9
9
UpdateNotification ,
10
10
isBatchedUpdateNotification
11
11
} from '../db/DBAdapter.js' ;
12
- import { SyncStatus } from '../db/crud/SyncStatus.js' ;
12
+ import { SyncPriorityStatus , SyncStatus } from '../db/crud/SyncStatus.js' ;
13
13
import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js' ;
14
14
import { Schema } from '../db/schema/Schema.js' ;
15
15
import { BaseObserver } from '../utils/BaseObserver.js' ;
@@ -146,6 +146,11 @@ export const isPowerSyncDatabaseOptionsWithSettings = (test: any): test is Power
146
146
return typeof test == 'object' && isSQLOpenOptions ( test . database ) ;
147
147
} ;
148
148
149
+ /**
150
+ * The priority used by the core extension to indicate that a full sync was completed.
151
+ */
152
+ const FULL_SYNC_PRIORITY = 2147483647 ;
153
+
149
154
export abstract class AbstractPowerSyncDatabase extends BaseObserver < PowerSyncDBListener > {
150
155
/**
151
156
* Transactions should be queued in the DBAdapter, but we also want to prevent
@@ -260,16 +265,30 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
260
265
}
261
266
262
267
/**
268
+ * Wait for the first sync operation to complete.
269
+ *
270
+ * @argument request Either an abort signal (after which the promise will complete regardless of
271
+ * whether a full sync was completed) or an object providing an abort signal and a priority target.
272
+ * When a priority target is set, the promise may complete when all buckets with the given (or higher)
273
+ * priorities have been synchronized. This can be earlier than a complete sync.
263
274
* @returns A promise which will resolve once the first full sync has completed.
264
275
*/
265
- async waitForFirstSync ( signal ?: AbortSignal ) : Promise < void > {
266
- if ( this . currentStatus . hasSynced ) {
276
+ async waitForFirstSync ( request ?: AbortSignal | { signal ?: AbortSignal ; priority ?: number } ) : Promise < void > {
277
+ const signal = request instanceof AbortSignal ? request : request ?. signal ;
278
+ const priority = request && 'priority' in request ? request . priority : undefined ;
279
+
280
+ const statusMatches =
281
+ priority === undefined
282
+ ? ( status : SyncStatus ) => status . hasSynced
283
+ : ( status : SyncStatus ) => status . statusForPriority ( priority ) . hasSynced ;
284
+
285
+ if ( statusMatches ( this . currentStatus ) ) {
267
286
return ;
268
287
}
269
288
return new Promise ( ( resolve ) => {
270
289
const dispose = this . registerListener ( {
271
290
statusChanged : ( status ) => {
272
- if ( status . hasSynced ) {
291
+ if ( statusMatches ( status ) ) {
273
292
dispose ( ) ;
274
293
resolve ( ) ;
275
294
}
@@ -329,14 +348,33 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
329
348
}
330
349
331
350
protected async updateHasSynced ( ) {
332
- const result = await this . database . get < { synced_at : string | null } > (
333
- 'SELECT powersync_last_synced_at() as synced_at '
351
+ const result = await this . database . getAll < { priority : number ; last_synced_at : string } > (
352
+ 'SELECT priority, last_synced_at FROM ps_sync_state ORDER BY priority DESC '
334
353
) ;
335
- const hasSynced = result . synced_at != null ;
336
- const syncedAt = result . synced_at != null ? new Date ( result . synced_at ! + 'Z' ) : undefined ;
354
+ let lastCompleteSync : Date | undefined ;
355
+ const priorityStatuses : SyncPriorityStatus [ ] = [ ] ;
356
+
357
+ for ( const { priority, last_synced_at } of result ) {
358
+ const parsedDate = new Date ( last_synced_at + 'Z' ) ;
359
+
360
+ if ( priority == FULL_SYNC_PRIORITY ) {
361
+ // This lowest-possible priority represents a complete sync.
362
+ lastCompleteSync = parsedDate ;
363
+ } else {
364
+ priorityStatuses . push ( { priority, hasSynced : true , lastSyncedAt : parsedDate } ) ;
365
+ }
366
+ }
367
+
368
+ const hasSynced = lastCompleteSync != null ;
369
+ const updatedStatus = new SyncStatus ( {
370
+ ...this . currentStatus . toJSON ( ) ,
371
+ hasSynced,
372
+ priorityStatuses,
373
+ lastSyncedAt : lastCompleteSync
374
+ } ) ;
337
375
338
- if ( hasSynced != this . currentStatus . hasSynced ) {
339
- this . currentStatus = new SyncStatus ( { ... this . currentStatus . toJSON ( ) , hasSynced , lastSyncedAt : syncedAt } ) ;
376
+ if ( ! updatedStatus . isEqual ( this . currentStatus ) ) {
377
+ this . currentStatus = updatedStatus ;
340
378
this . iterateListeners ( ( l ) => l . statusChanged ?.( this . currentStatus ) ) ;
341
379
}
342
380
}
@@ -379,7 +417,8 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
379
417
// Use the options passed in during connect, or fallback to the options set during database creation or fallback to the default options
380
418
resolvedConnectionOptions ( options ?: PowerSyncConnectionOptions ) : RequiredAdditionalConnectionOptions {
381
419
return {
382
- retryDelayMs : options ?. retryDelayMs ?? this . options . retryDelayMs ?? this . options . retryDelay ?? DEFAULT_RETRY_DELAY_MS ,
420
+ retryDelayMs :
421
+ options ?. retryDelayMs ?? this . options . retryDelayMs ?? this . options . retryDelay ?? DEFAULT_RETRY_DELAY_MS ,
383
422
crudUploadThrottleMs :
384
423
options ?. crudUploadThrottleMs ?? this . options . crudUploadThrottleMs ?? DEFAULT_CRUD_UPLOAD_THROTTLE_MS
385
424
} ;
@@ -401,7 +440,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
401
440
402
441
this . syncStreamImplementation = this . generateSyncStreamImplementation ( connector , {
403
442
retryDelayMs,
404
- crudUploadThrottleMs,
443
+ crudUploadThrottleMs
405
444
} ) ;
406
445
this . syncStatusListenerDisposer = this . syncStreamImplementation . registerListener ( {
407
446
statusChanged : ( status ) => {
0 commit comments