@@ -229,8 +229,9 @@ export class CollectionImpl<
229229 private hasReceivedFirstCommit = false
230230 private isCommittingSyncTransactions = false
231231
232- // Array to store one-time commit listeners
233- private onFirstCommitCallbacks : Array < ( ) => void > = [ ]
232+ // Array to store one-time ready listeners
233+ private onFirstReadyCallbacks : Array < ( ) => void > = [ ]
234+ private hasBeenReady = false
234235
235236 // Event batching for preventing duplicate emissions during transaction flows
236237 private batchedEvents : Array < ChangeMessage < T , TKey > > = [ ]
@@ -244,17 +245,66 @@ export class CollectionImpl<
244245 private syncCleanupFn : ( ( ) => void ) | null = null
245246
246247 /**
247- * Register a callback to be executed on the next commit
248+ * Register a callback to be executed when the collection first becomes ready
248249 * Useful for preloading collections
249- * @param callback Function to call after the next commit
250+ * @param callback Function to call when the collection first becomes ready
250251 * @example
251- * collection.onFirstCommit (() => {
252- * console.log('Collection has received first data ')
252+ * collection.onFirstReady (() => {
253+ * console.log('Collection is ready for the first time ')
253254 * // Safe to access collection.state now
254255 * })
255256 */
256- public onFirstCommit ( callback : ( ) => void ) : void {
257- this . onFirstCommitCallbacks . push ( callback )
257+ public onFirstReady ( callback : ( ) => void ) : void {
258+ // If already ready, call immediately
259+ if ( this . hasBeenReady ) {
260+ callback ( )
261+ return
262+ }
263+
264+ this . onFirstReadyCallbacks . push ( callback )
265+ }
266+
267+ /**
268+ * Check if the collection is ready for use
269+ * Returns true if the collection has been marked as ready by its sync implementation
270+ * @returns true if the collection is ready, false otherwise
271+ * @example
272+ * if (collection.isReady()) {
273+ * console.log('Collection is ready, data is available')
274+ * // Safe to access collection.state
275+ * } else {
276+ * console.log('Collection is still loading')
277+ * }
278+ */
279+ public isReady ( ) : boolean {
280+ return this . _status === `ready`
281+ }
282+
283+ /**
284+ * Mark the collection as ready for use
285+ * This is called by sync implementations to explicitly signal that the collection is ready,
286+ * providing a more intuitive alternative to using commits for readiness signaling
287+ * @private - Should only be called by sync implementations
288+ */
289+ private markReady ( ) : void {
290+ // Can transition to ready from loading or initialCommit states
291+ if ( this . _status === `loading` || this . _status === `initialCommit` ) {
292+ this . setStatus ( `ready` )
293+
294+ // Call any registered first ready callbacks (only on first time becoming ready)
295+ if ( ! this . hasBeenReady ) {
296+ this . hasBeenReady = true
297+
298+ // Also mark as having received first commit for backwards compatibility
299+ if ( ! this . hasReceivedFirstCommit ) {
300+ this . hasReceivedFirstCommit = true
301+ }
302+
303+ const callbacks = [ ...this . onFirstReadyCallbacks ]
304+ this . onFirstReadyCallbacks = [ ]
305+ callbacks . forEach ( ( callback ) => callback ( ) )
306+ }
307+ }
258308 }
259309
260310 public id = ``
@@ -302,7 +352,7 @@ export class CollectionImpl<
302352 Array < CollectionStatus >
303353 > = {
304354 idle : [ `loading` , `error` , `cleaned-up` ] ,
305- loading : [ `initialCommit` , `error` , `cleaned-up` ] ,
355+ loading : [ `initialCommit` , `ready` , ` error`, `cleaned-up` ] ,
306356 initialCommit : [ `ready` , `error` , `cleaned-up` ] ,
307357 ready : [ `cleaned-up` , `error` ] ,
308358 error : [ `cleaned-up` , `idle` ] ,
@@ -455,11 +505,9 @@ export class CollectionImpl<
455505 }
456506
457507 this . commitPendingTransactions ( )
458-
459- // Transition from initialCommit to ready after the first commit is complete
460- if ( this . _status === `initialCommit` ) {
461- this . setStatus ( `ready` )
462- }
508+ } ,
509+ markReady : ( ) => {
510+ this . markReady ( )
463511 } ,
464512 } )
465513
@@ -492,7 +540,7 @@ export class CollectionImpl<
492540 }
493541
494542 // Register callback BEFORE starting sync to avoid race condition
495- this . onFirstCommit ( ( ) => {
543+ this . onFirstReady ( ( ) => {
496544 resolve ( )
497545 } )
498546
@@ -555,7 +603,8 @@ export class CollectionImpl<
555603 this . pendingSyncedTransactions = [ ]
556604 this . syncedKeys . clear ( )
557605 this . hasReceivedFirstCommit = false
558- this . onFirstCommitCallbacks = [ ]
606+ this . hasBeenReady = false
607+ this . onFirstReadyCallbacks = [ ]
559608 this . preloadPromise = null
560609 this . batchedEvents = [ ]
561610 this . shouldBatchEvents = false
@@ -1184,8 +1233,8 @@ export class CollectionImpl<
11841233 // Call any registered one-time commit listeners
11851234 if ( ! this . hasReceivedFirstCommit ) {
11861235 this . hasReceivedFirstCommit = true
1187- const callbacks = [ ...this . onFirstCommitCallbacks ]
1188- this . onFirstCommitCallbacks = [ ]
1236+ const callbacks = [ ...this . onFirstReadyCallbacks ]
1237+ this . onFirstReadyCallbacks = [ ]
11891238 callbacks . forEach ( ( callback ) => callback ( ) )
11901239 }
11911240 }
@@ -1812,14 +1861,14 @@ export class CollectionImpl<
18121861 * @returns Promise that resolves to a Map containing all items in the collection
18131862 */
18141863 stateWhenReady ( ) : Promise < Map < TKey , T > > {
1815- // If we already have data or there are no loading collections , resolve immediately
1816- if ( this . size > 0 || this . hasReceivedFirstCommit === true ) {
1864+ // If we already have data or collection is ready , resolve immediately
1865+ if ( this . size > 0 || this . isReady ( ) ) {
18171866 return Promise . resolve ( this . state )
18181867 }
18191868
1820- // Otherwise, wait for the first commit
1869+ // Otherwise, wait for the collection to be ready
18211870 return new Promise < Map < TKey , T > > ( ( resolve ) => {
1822- this . onFirstCommit ( ( ) => {
1871+ this . onFirstReady ( ( ) => {
18231872 resolve ( this . state )
18241873 } )
18251874 } )
@@ -1841,14 +1890,14 @@ export class CollectionImpl<
18411890 * @returns Promise that resolves to an Array containing all items in the collection
18421891 */
18431892 toArrayWhenReady ( ) : Promise < Array < T > > {
1844- // If we already have data or there are no loading collections , resolve immediately
1845- if ( this . size > 0 || this . hasReceivedFirstCommit === true ) {
1893+ // If we already have data or collection is ready , resolve immediately
1894+ if ( this . size > 0 || this . isReady ( ) ) {
18461895 return Promise . resolve ( this . toArray )
18471896 }
18481897
1849- // Otherwise, wait for the first commit
1898+ // Otherwise, wait for the collection to be ready
18501899 return new Promise < Array < T > > ( ( resolve ) => {
1851- this . onFirstCommit ( ( ) => {
1900+ this . onFirstReady ( ( ) => {
18521901 resolve ( this . toArray )
18531902 } )
18541903 } )
0 commit comments