99 UpdateNotification ,
1010 isBatchedUpdateNotification
1111} from '../db/DBAdapter.js' ;
12- import { SyncStatus } from '../db/crud/SyncStatus.js' ;
12+ import { SyncPriorityStatus , SyncStatus } from '../db/crud/SyncStatus.js' ;
1313import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js' ;
1414import { Schema } from '../db/schema/Schema.js' ;
1515import { BaseObserver } from '../utils/BaseObserver.js' ;
@@ -146,6 +146,11 @@ export const isPowerSyncDatabaseOptionsWithSettings = (test: any): test is Power
146146 return typeof test == 'object' && isSQLOpenOptions ( test . database ) ;
147147} ;
148148
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+
149154export abstract class AbstractPowerSyncDatabase extends BaseObserver < PowerSyncDBListener > {
150155 /**
151156 * Transactions should be queued in the DBAdapter, but we also want to prevent
@@ -260,16 +265,30 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
260265 }
261266
262267 /**
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.
263274 * @returns A promise which will resolve once the first full sync has completed.
264275 */
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 ) ) {
267286 return ;
268287 }
269288 return new Promise ( ( resolve ) => {
270289 const dispose = this . registerListener ( {
271290 statusChanged : ( status ) => {
272- if ( status . hasSynced ) {
291+ if ( statusMatches ( status ) ) {
273292 dispose ( ) ;
274293 resolve ( ) ;
275294 }
@@ -329,14 +348,33 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
329348 }
330349
331350 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 '
334353 ) ;
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+ } ) ;
337375
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 ;
340378 this . iterateListeners ( ( l ) => l . statusChanged ?.( this . currentStatus ) ) ;
341379 }
342380 }
@@ -379,7 +417,8 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
379417 // Use the options passed in during connect, or fallback to the options set during database creation or fallback to the default options
380418 resolvedConnectionOptions ( options ?: PowerSyncConnectionOptions ) : RequiredAdditionalConnectionOptions {
381419 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 ,
383422 crudUploadThrottleMs :
384423 options ?. crudUploadThrottleMs ?? this . options . crudUploadThrottleMs ?? DEFAULT_CRUD_UPLOAD_THROTTLE_MS
385424 } ;
@@ -401,7 +440,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
401440
402441 this . syncStreamImplementation = this . generateSyncStreamImplementation ( connector , {
403442 retryDelayMs,
404- crudUploadThrottleMs,
443+ crudUploadThrottleMs
405444 } ) ;
406445 this . syncStatusListenerDisposer = this . syncStreamImplementation . registerListener ( {
407446 statusChanged : ( status ) => {
0 commit comments