@@ -14,10 +14,13 @@ import type { DataStore } from "../data/datastore.ts";
1414 * - page:deleted (string)
1515 */
1616export class EventedSpacePrimitives implements SpacePrimitives {
17- // Various operations may be going on at the same time, and we don't want to trigger events unnessarily.
18- // Therefore, we use this variable to track if any operation is in flight, and if so, we skip event triggering.
19- // This is ok, because any event will be picked up in a following iteration.
20- operationInProgress = false ;
17+ // Various operations may be going on at the same time, and we don't want to trigger events unnecessarily.
18+ // Therefore, we use this counter to track how many operations are in flight, and if so, we skip event triggering.
19+ private operationCount = 0 ;
20+
21+ // When a fetchFileList is requested while operations are in flight, we defer it
22+ // so that synced changes are not missed.
23+ private deferredFetchFileList = false ;
2124
2225 private enabled = false ;
2326
@@ -65,6 +68,20 @@ export class EventedSpacePrimitives implements SpacePrimitives {
6568 }
6669 }
6770
71+ /**
72+ * Called when an operation completes. If a fetchFileList was deferred
73+ * because operations were in flight, trigger it now.
74+ */
75+ private checkDeferredFetchFileList ( ) {
76+ if ( this . deferredFetchFileList && this . operationCount === 0 ) {
77+ this . deferredFetchFileList = false ;
78+ // Schedule on next tick to avoid reentrancy
79+ setTimeout ( ( ) => {
80+ void this . fetchFileList ( ) ;
81+ } ) ;
82+ }
83+ }
84+
6885 dispatchEvent ( name : string , ...args : any [ ] ) : Promise < any [ ] > {
6986 if ( ! this . enabled ) {
7087 return Promise . resolve ( [ ] ) ;
@@ -74,27 +91,22 @@ export class EventedSpacePrimitives implements SpacePrimitives {
7491 }
7592
7693 async fetchFileList ( ) : Promise < FileMeta [ ] > {
77- if ( this . operationInProgress ) {
94+ if ( this . operationCount > 0 ) {
7895 // Some other operation (read, write, list, meta) is already going on
7996 // this will likely trigger events, so let's not worry about any of that and avoid race condition and inconsistent data.
97+ // We mark a deferred flag so the next operation completion will trigger a fetchFileList.
8098 console . info (
81- "operationInProgress, deferring event processing for fetchFileList." ,
99+ "deferredFetchFileList: skipping event triggering for fetchFileList." ,
82100 ) ;
83- const result = await this . wrapped . fetchFileList ( ) ;
84- // Schedule a retry after current operation completes
85- setTimeout ( ( ) => {
86- if ( ! this . operationInProgress ) {
87- void this . fetchFileList ( ) ;
88- }
89- } , 50 ) ;
90- return result ;
101+ this . deferredFetchFileList = true ;
102+ return this . wrapped . fetchFileList ( ) ;
91103 }
92104 if ( ! this . enabled ) {
93105 return this . wrapped . fetchFileList ( ) ;
94106 }
95107 // console.log("Fetching file list");
96108 // Fetching mutex
97- this . operationInProgress = true ;
109+ this . operationCount ++ ;
98110 try {
99111 // Fetch the list
100112 const newFileList = await this . wrapped . fetchFileList ( ) ;
@@ -140,27 +152,25 @@ export class EventedSpacePrimitives implements SpacePrimitives {
140152 return newFileList ;
141153 } finally {
142154 await this . saveSnapshot ( ) ;
143- this . operationInProgress = false ;
155+ this . operationCount -- ;
144156 }
145157 }
146158
147159 async readFile ( path : string ) : Promise < { data : Uint8Array ; meta : FileMeta } > {
148160 if ( ! this . enabled ) {
149161 return this . wrapped . readFile ( path ) ;
150162 }
163+ this . operationCount ++ ;
151164 try {
152- // Fetching mutex
153- const wasFetching = this . operationInProgress ;
154- this . operationInProgress = true ;
155-
156165 // Fetch file
157166 const data = await this . wrapped . readFile ( path ) ;
158- if ( ! wasFetching ) {
167+ if ( this . operationCount === 1 ) {
159168 await this . triggerEventsAndCache ( path , data . meta . lastModified ) ;
160169 }
161170 return data ;
162171 } finally {
163- this . operationInProgress = false ;
172+ this . operationCount -- ;
173+ this . checkDeferredFetchFileList ( ) ;
164174 }
165175 }
166176
@@ -173,11 +183,10 @@ export class EventedSpacePrimitives implements SpacePrimitives {
173183 return this . wrapped . writeFile ( path , data , meta ) ;
174184 }
175185
176- const wasFetching = this . operationInProgress ;
177- this . operationInProgress = true ;
186+ this . operationCount ++ ;
178187 try {
179188 const newMeta = await this . wrapped . writeFile ( path , data , meta ) ;
180- if ( ! wasFetching ) {
189+ if ( this . operationCount === 1 ) {
181190 await this . triggerEventsAndCache ( path , newMeta . lastModified ) ;
182191 }
183192 if ( path . endsWith ( ".md" ) ) {
@@ -187,7 +196,8 @@ export class EventedSpacePrimitives implements SpacePrimitives {
187196
188197 return newMeta ;
189198 } finally {
190- this . operationInProgress = false ;
199+ this . operationCount -- ;
200+ this . checkDeferredFetchFileList ( ) ;
191201 }
192202 }
193203
@@ -212,16 +222,16 @@ export class EventedSpacePrimitives implements SpacePrimitives {
212222 return this . wrapped . getFileMeta ( path , observing ) ;
213223 }
214224
225+ this . operationCount ++ ;
215226 try {
216- const wasFetching = this . operationInProgress ;
217- this . operationInProgress = true ;
218227 const newMeta = await this . wrapped . getFileMeta ( path , observing ) ;
219- if ( ! wasFetching ) {
228+ if ( this . operationCount === 1 ) {
220229 await this . triggerEventsAndCache ( path , newMeta . lastModified ) ;
221230 }
222231 return newMeta ;
223232 } finally {
224- this . operationInProgress = false ;
233+ this . operationCount -- ;
234+ this . checkDeferredFetchFileList ( ) ;
225235 }
226236 }
227237
@@ -230,19 +240,19 @@ export class EventedSpacePrimitives implements SpacePrimitives {
230240 return this . wrapped . deleteFile ( path ) ;
231241 }
232242
243+ this . operationCount ++ ;
233244 try {
234- this . operationInProgress = true ;
235245 if ( path . endsWith ( ".md" ) ) {
236246 const pageName = path . substring ( 0 , path . length - 3 ) ;
237247 await this . dispatchEvent ( "page:deleted" , pageName ) ;
238248 }
239- // await this.getPageMeta(path); // Check if page exists, if not throws Error
240249 await this . wrapped . deleteFile ( path ) ;
241250 this . deleteFromSnapshot ( path ) ;
242251 await this . dispatchEvent ( "file:deleted" , path ) ;
243252 } finally {
244253 await this . saveSnapshot ( ) ;
245- this . operationInProgress = false ;
254+ this . operationCount -- ;
255+ this . checkDeferredFetchFileList ( ) ;
246256 }
247257 }
248258}
0 commit comments