@powersync/common 1.27.0 → 1.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.mjs +1 -1
- package/lib/client/AbstractPowerSyncDatabase.d.ts +77 -5
- package/lib/client/AbstractPowerSyncDatabase.js +78 -6
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +3 -3
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +65 -61
- package/lib/db/crud/SyncStatus.d.ts +55 -14
- package/lib/db/crud/SyncStatus.js +55 -14
- package/lib/utils/{throttle.d.ts → async.d.ts} +1 -0
- package/lib/utils/{throttle.js → async.js} +10 -0
- package/package.json +1 -1
|
@@ -155,7 +155,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
155
155
|
/**
|
|
156
156
|
* Wait for the first sync operation to complete.
|
|
157
157
|
*
|
|
158
|
-
* @
|
|
158
|
+
* @param request Either an abort signal (after which the promise will complete regardless of
|
|
159
159
|
* whether a full sync was completed) or an object providing an abort signal and a priority target.
|
|
160
160
|
* When a priority target is set, the promise may complete when all buckets with the given (or higher)
|
|
161
161
|
* priorities have been synchronized. This can be earlier than a complete sync.
|
|
@@ -222,7 +222,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
222
222
|
*/
|
|
223
223
|
getUploadQueueStats(includeSize?: boolean): Promise<UploadQueueStats>;
|
|
224
224
|
/**
|
|
225
|
-
* Get a batch of
|
|
225
|
+
* Get a batch of CRUD data to upload.
|
|
226
226
|
*
|
|
227
227
|
* Returns null if there is no data to upload.
|
|
228
228
|
*
|
|
@@ -237,6 +237,9 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
237
237
|
* This method does include transaction ids in the result, but does not group
|
|
238
238
|
* data by transaction. One batch may contain data from multiple transactions,
|
|
239
239
|
* and a single transaction may be split over multiple batches.
|
|
240
|
+
*
|
|
241
|
+
* @param limit Maximum number of CRUD entries to include in the batch
|
|
242
|
+
* @returns A batch of CRUD operations to upload, or null if there are none
|
|
240
243
|
*/
|
|
241
244
|
getCrudBatch(limit?: number): Promise<CrudBatch | null>;
|
|
242
245
|
/**
|
|
@@ -251,37 +254,71 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
251
254
|
*
|
|
252
255
|
* Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
|
|
253
256
|
* All data for the transaction is loaded into memory.
|
|
257
|
+
*
|
|
258
|
+
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
254
259
|
*/
|
|
255
260
|
getNextCrudTransaction(): Promise<CrudTransaction | null>;
|
|
256
261
|
/**
|
|
257
262
|
* Get an unique client id for this database.
|
|
258
263
|
*
|
|
259
264
|
* The id is not reset when the database is cleared, only when the database is deleted.
|
|
265
|
+
*
|
|
266
|
+
* @returns A unique identifier for the database instance
|
|
260
267
|
*/
|
|
261
268
|
getClientId(): Promise<string>;
|
|
262
269
|
private handleCrudCheckpoint;
|
|
263
270
|
/**
|
|
264
|
-
* Execute a write (INSERT/UPDATE/DELETE) query
|
|
271
|
+
* Execute a SQL write (INSERT/UPDATE/DELETE) query
|
|
265
272
|
* and optionally return results.
|
|
273
|
+
*
|
|
274
|
+
* @param sql The SQL query to execute
|
|
275
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
276
|
+
* @returns The query result as an object with structured key-value pairs
|
|
266
277
|
*/
|
|
267
278
|
execute(sql: string, parameters?: any[]): Promise<QueryResult>;
|
|
279
|
+
/**
|
|
280
|
+
* Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
|
|
281
|
+
* This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
|
|
282
|
+
*
|
|
283
|
+
* @param sql The SQL query to execute
|
|
284
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
285
|
+
* @returns The raw query result from the underlying database as a nested array of raw values, where each row is
|
|
286
|
+
* represented as an array of column values without field names.
|
|
287
|
+
*/
|
|
268
288
|
executeRaw(sql: string, parameters?: any[]): Promise<any[][]>;
|
|
269
289
|
/**
|
|
270
290
|
* Execute a write query (INSERT/UPDATE/DELETE) multiple times with each parameter set
|
|
271
291
|
* and optionally return results.
|
|
272
292
|
* This is faster than executing separately with each parameter set.
|
|
293
|
+
*
|
|
294
|
+
* @param sql The SQL query to execute
|
|
295
|
+
* @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
296
|
+
* @returns The query result
|
|
273
297
|
*/
|
|
274
298
|
executeBatch(sql: string, parameters?: any[][]): Promise<QueryResult>;
|
|
275
299
|
/**
|
|
276
300
|
* Execute a read-only query and return results.
|
|
301
|
+
*
|
|
302
|
+
* @param sql The SQL query to execute
|
|
303
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
304
|
+
* @returns An array of results
|
|
277
305
|
*/
|
|
278
306
|
getAll<T>(sql: string, parameters?: any[]): Promise<T[]>;
|
|
279
307
|
/**
|
|
280
308
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
309
|
+
*
|
|
310
|
+
* @param sql The SQL query to execute
|
|
311
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
312
|
+
* @returns The first result if found, or null if no results are returned
|
|
281
313
|
*/
|
|
282
314
|
getOptional<T>(sql: string, parameters?: any[]): Promise<T | null>;
|
|
283
315
|
/**
|
|
284
316
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
317
|
+
*
|
|
318
|
+
* @param sql The SQL query to execute
|
|
319
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
320
|
+
* @returns The first result matching the query
|
|
321
|
+
* @throws Error if no rows are returned
|
|
285
322
|
*/
|
|
286
323
|
get<T>(sql: string, parameters?: any[]): Promise<T>;
|
|
287
324
|
/**
|
|
@@ -298,12 +335,22 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
298
335
|
* Open a read-only transaction.
|
|
299
336
|
* Read transactions can run concurrently to a write transaction.
|
|
300
337
|
* Changes from any write transaction are not visible to read transactions started before it.
|
|
338
|
+
*
|
|
339
|
+
* @param callback Function to execute within the transaction
|
|
340
|
+
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
341
|
+
* @returns The result of the callback
|
|
342
|
+
* @throws Error if the lock cannot be obtained within the timeout period
|
|
301
343
|
*/
|
|
302
344
|
readTransaction<T>(callback: (tx: Transaction) => Promise<T>, lockTimeout?: number): Promise<T>;
|
|
303
345
|
/**
|
|
304
346
|
* Open a read-write transaction.
|
|
305
347
|
* This takes a global lock - only one write transaction can execute against the database at a time.
|
|
306
348
|
* Statements within the transaction must be done on the provided {@link Transaction} interface.
|
|
349
|
+
*
|
|
350
|
+
* @param callback Function to execute within the transaction
|
|
351
|
+
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
352
|
+
* @returns The result of the callback
|
|
353
|
+
* @throws Error if the lock cannot be obtained within the timeout period
|
|
307
354
|
*/
|
|
308
355
|
writeTransaction<T>(callback: (tx: Transaction) => Promise<T>, lockTimeout?: number): Promise<T>;
|
|
309
356
|
/**
|
|
@@ -346,14 +393,34 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
346
393
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
347
394
|
*
|
|
348
395
|
* Note that the `onChange` callback member of the handler is required.
|
|
396
|
+
*
|
|
397
|
+
* @param sql The SQL query to execute
|
|
398
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
399
|
+
* @param handler Callbacks for handling results and errors
|
|
400
|
+
* @param options Options for configuring watch behavior
|
|
349
401
|
*/
|
|
350
402
|
watchWithCallback(sql: string, parameters?: any[], handler?: WatchHandler, options?: SQLWatchOptions): void;
|
|
351
403
|
/**
|
|
352
404
|
* Execute a read query every time the source tables are modified.
|
|
353
405
|
* Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
|
|
354
406
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
407
|
+
*
|
|
408
|
+
* @param sql The SQL query to execute
|
|
409
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
410
|
+
* @param options Options for configuring watch behavior
|
|
411
|
+
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
355
412
|
*/
|
|
356
413
|
watchWithAsyncGenerator(sql: string, parameters?: any[], options?: SQLWatchOptions): AsyncIterable<QueryResult>;
|
|
414
|
+
/**
|
|
415
|
+
* Resolves the list of tables that are used in a SQL query.
|
|
416
|
+
* If tables are specified in the options, those are used directly.
|
|
417
|
+
* Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
|
|
418
|
+
*
|
|
419
|
+
* @param sql The SQL query to analyze
|
|
420
|
+
* @param parameters Optional parameters for the SQL query
|
|
421
|
+
* @param options Optional watch options that may contain explicit table list
|
|
422
|
+
* @returns Array of table names that the query depends on
|
|
423
|
+
*/
|
|
357
424
|
resolveTables(sql: string, parameters?: any[], options?: SQLWatchOptions): Promise<string[]>;
|
|
358
425
|
/**
|
|
359
426
|
* This version of `onChange` uses {@link AsyncGenerator}, for documentation see {@link onChangeWithAsyncGenerator}.
|
|
@@ -392,7 +459,9 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
392
459
|
*
|
|
393
460
|
* Note that the `onChange` callback member of the handler is required.
|
|
394
461
|
*
|
|
395
|
-
*
|
|
462
|
+
* @param handler Callbacks for handling change events and errors
|
|
463
|
+
* @param options Options for configuring watch behavior
|
|
464
|
+
* @returns A dispose function to stop watching for changes
|
|
396
465
|
*/
|
|
397
466
|
onChangeWithCallback(handler?: WatchOnChangeHandler, options?: SQLWatchOptions): () => void;
|
|
398
467
|
/**
|
|
@@ -401,7 +470,10 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
401
470
|
* This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be performed
|
|
402
471
|
* together when data is changed.
|
|
403
472
|
*
|
|
404
|
-
* Note
|
|
473
|
+
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
474
|
+
*
|
|
475
|
+
* @param options Options for configuring watch behavior
|
|
476
|
+
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
405
477
|
*/
|
|
406
478
|
onChangeWithAsyncGenerator(options?: SQLWatchOptions): AsyncIterable<WatchOnChangeEvent>;
|
|
407
479
|
private handleTableChanges;
|
|
@@ -7,7 +7,7 @@ import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
|
|
|
7
7
|
import { BaseObserver } from '../utils/BaseObserver.js';
|
|
8
8
|
import { ControlledExecutor } from '../utils/ControlledExecutor.js';
|
|
9
9
|
import { mutexRunExclusive } from '../utils/mutex.js';
|
|
10
|
-
import { throttleTrailing } from '../utils/
|
|
10
|
+
import { throttleTrailing } from '../utils/async.js';
|
|
11
11
|
import { isDBAdapter, isSQLOpenFactory, isSQLOpenOptions } from './SQLOpenFactory.js';
|
|
12
12
|
import { runOnSchemaChange } from './runOnSchemaChange.js';
|
|
13
13
|
import { PSInternalTable } from './sync/bucket/BucketStorageAdapter.js';
|
|
@@ -133,7 +133,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
133
133
|
/**
|
|
134
134
|
* Wait for the first sync operation to complete.
|
|
135
135
|
*
|
|
136
|
-
* @
|
|
136
|
+
* @param request Either an abort signal (after which the promise will complete regardless of
|
|
137
137
|
* whether a full sync was completed) or an object providing an abort signal and a priority target.
|
|
138
138
|
* When a priority target is set, the promise may complete when all buckets with the given (or higher)
|
|
139
139
|
* priorities have been synchronized. This can be earlier than a complete sync.
|
|
@@ -363,7 +363,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
363
363
|
});
|
|
364
364
|
}
|
|
365
365
|
/**
|
|
366
|
-
* Get a batch of
|
|
366
|
+
* Get a batch of CRUD data to upload.
|
|
367
367
|
*
|
|
368
368
|
* Returns null if there is no data to upload.
|
|
369
369
|
*
|
|
@@ -378,6 +378,9 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
378
378
|
* This method does include transaction ids in the result, but does not group
|
|
379
379
|
* data by transaction. One batch may contain data from multiple transactions,
|
|
380
380
|
* and a single transaction may be split over multiple batches.
|
|
381
|
+
*
|
|
382
|
+
* @param limit Maximum number of CRUD entries to include in the batch
|
|
383
|
+
* @returns A batch of CRUD operations to upload, or null if there are none
|
|
381
384
|
*/
|
|
382
385
|
async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
|
|
383
386
|
const result = await this.getAll(`SELECT id, tx_id, data FROM ${PSInternalTable.CRUD} ORDER BY id ASC LIMIT ?`, [limit + 1]);
|
|
@@ -405,6 +408,8 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
405
408
|
*
|
|
406
409
|
* Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
|
|
407
410
|
* All data for the transaction is loaded into memory.
|
|
411
|
+
*
|
|
412
|
+
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
408
413
|
*/
|
|
409
414
|
async getNextCrudTransaction() {
|
|
410
415
|
return await this.readTransaction(async (tx) => {
|
|
@@ -429,6 +434,8 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
429
434
|
* Get an unique client id for this database.
|
|
430
435
|
*
|
|
431
436
|
* The id is not reset when the database is cleared, only when the database is deleted.
|
|
437
|
+
*
|
|
438
|
+
* @returns A unique identifier for the database instance
|
|
432
439
|
*/
|
|
433
440
|
async getClientId() {
|
|
434
441
|
return this.bucketStorageAdapter.getClientId();
|
|
@@ -452,13 +459,26 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
452
459
|
});
|
|
453
460
|
}
|
|
454
461
|
/**
|
|
455
|
-
* Execute a write (INSERT/UPDATE/DELETE) query
|
|
462
|
+
* Execute a SQL write (INSERT/UPDATE/DELETE) query
|
|
456
463
|
* and optionally return results.
|
|
464
|
+
*
|
|
465
|
+
* @param sql The SQL query to execute
|
|
466
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
467
|
+
* @returns The query result as an object with structured key-value pairs
|
|
457
468
|
*/
|
|
458
469
|
async execute(sql, parameters) {
|
|
459
470
|
await this.waitForReady();
|
|
460
471
|
return this.database.execute(sql, parameters);
|
|
461
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
|
|
475
|
+
* This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
|
|
476
|
+
*
|
|
477
|
+
* @param sql The SQL query to execute
|
|
478
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
479
|
+
* @returns The raw query result from the underlying database as a nested array of raw values, where each row is
|
|
480
|
+
* represented as an array of column values without field names.
|
|
481
|
+
*/
|
|
462
482
|
async executeRaw(sql, parameters) {
|
|
463
483
|
await this.waitForReady();
|
|
464
484
|
return this.database.executeRaw(sql, parameters);
|
|
@@ -467,6 +487,10 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
467
487
|
* Execute a write query (INSERT/UPDATE/DELETE) multiple times with each parameter set
|
|
468
488
|
* and optionally return results.
|
|
469
489
|
* This is faster than executing separately with each parameter set.
|
|
490
|
+
*
|
|
491
|
+
* @param sql The SQL query to execute
|
|
492
|
+
* @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
493
|
+
* @returns The query result
|
|
470
494
|
*/
|
|
471
495
|
async executeBatch(sql, parameters) {
|
|
472
496
|
await this.waitForReady();
|
|
@@ -474,6 +498,10 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
474
498
|
}
|
|
475
499
|
/**
|
|
476
500
|
* Execute a read-only query and return results.
|
|
501
|
+
*
|
|
502
|
+
* @param sql The SQL query to execute
|
|
503
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
504
|
+
* @returns An array of results
|
|
477
505
|
*/
|
|
478
506
|
async getAll(sql, parameters) {
|
|
479
507
|
await this.waitForReady();
|
|
@@ -481,6 +509,10 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
481
509
|
}
|
|
482
510
|
/**
|
|
483
511
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
512
|
+
*
|
|
513
|
+
* @param sql The SQL query to execute
|
|
514
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
515
|
+
* @returns The first result if found, or null if no results are returned
|
|
484
516
|
*/
|
|
485
517
|
async getOptional(sql, parameters) {
|
|
486
518
|
await this.waitForReady();
|
|
@@ -488,6 +520,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
488
520
|
}
|
|
489
521
|
/**
|
|
490
522
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
523
|
+
*
|
|
524
|
+
* @param sql The SQL query to execute
|
|
525
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
526
|
+
* @returns The first result matching the query
|
|
527
|
+
* @throws Error if no rows are returned
|
|
491
528
|
*/
|
|
492
529
|
async get(sql, parameters) {
|
|
493
530
|
await this.waitForReady();
|
|
@@ -516,6 +553,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
516
553
|
* Open a read-only transaction.
|
|
517
554
|
* Read transactions can run concurrently to a write transaction.
|
|
518
555
|
* Changes from any write transaction are not visible to read transactions started before it.
|
|
556
|
+
*
|
|
557
|
+
* @param callback Function to execute within the transaction
|
|
558
|
+
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
559
|
+
* @returns The result of the callback
|
|
560
|
+
* @throws Error if the lock cannot be obtained within the timeout period
|
|
519
561
|
*/
|
|
520
562
|
async readTransaction(callback, lockTimeout = DEFAULT_LOCK_TIMEOUT_MS) {
|
|
521
563
|
await this.waitForReady();
|
|
@@ -529,6 +571,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
529
571
|
* Open a read-write transaction.
|
|
530
572
|
* This takes a global lock - only one write transaction can execute against the database at a time.
|
|
531
573
|
* Statements within the transaction must be done on the provided {@link Transaction} interface.
|
|
574
|
+
*
|
|
575
|
+
* @param callback Function to execute within the transaction
|
|
576
|
+
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
577
|
+
* @returns The result of the callback
|
|
578
|
+
* @throws Error if the lock cannot be obtained within the timeout period
|
|
532
579
|
*/
|
|
533
580
|
async writeTransaction(callback, lockTimeout = DEFAULT_LOCK_TIMEOUT_MS) {
|
|
534
581
|
await this.waitForReady();
|
|
@@ -553,6 +600,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
553
600
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
554
601
|
*
|
|
555
602
|
* Note that the `onChange` callback member of the handler is required.
|
|
603
|
+
*
|
|
604
|
+
* @param sql The SQL query to execute
|
|
605
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
606
|
+
* @param handler Callbacks for handling results and errors
|
|
607
|
+
* @param options Options for configuring watch behavior
|
|
556
608
|
*/
|
|
557
609
|
watchWithCallback(sql, parameters, handler, options) {
|
|
558
610
|
const { onResult, onError = (e) => this.options.logger?.error(e) } = handler ?? {};
|
|
@@ -593,6 +645,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
593
645
|
* Execute a read query every time the source tables are modified.
|
|
594
646
|
* Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
|
|
595
647
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
648
|
+
*
|
|
649
|
+
* @param sql The SQL query to execute
|
|
650
|
+
* @param parameters Optional array of parameters to bind to the query
|
|
651
|
+
* @param options Options for configuring watch behavior
|
|
652
|
+
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
596
653
|
*/
|
|
597
654
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
598
655
|
return new EventIterator((eventOptions) => {
|
|
@@ -610,6 +667,16 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
610
667
|
});
|
|
611
668
|
});
|
|
612
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* Resolves the list of tables that are used in a SQL query.
|
|
672
|
+
* If tables are specified in the options, those are used directly.
|
|
673
|
+
* Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
|
|
674
|
+
*
|
|
675
|
+
* @param sql The SQL query to analyze
|
|
676
|
+
* @param parameters Optional parameters for the SQL query
|
|
677
|
+
* @param options Optional watch options that may contain explicit table list
|
|
678
|
+
* @returns Array of table names that the query depends on
|
|
679
|
+
*/
|
|
613
680
|
async resolveTables(sql, parameters, options) {
|
|
614
681
|
const resolvedTables = options?.tables ? [...options.tables] : [];
|
|
615
682
|
if (!options?.tables) {
|
|
@@ -641,7 +708,9 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
641
708
|
*
|
|
642
709
|
* Note that the `onChange` callback member of the handler is required.
|
|
643
710
|
*
|
|
644
|
-
*
|
|
711
|
+
* @param handler Callbacks for handling change events and errors
|
|
712
|
+
* @param options Options for configuring watch behavior
|
|
713
|
+
* @returns A dispose function to stop watching for changes
|
|
645
714
|
*/
|
|
646
715
|
onChangeWithCallback(handler, options) {
|
|
647
716
|
const { onChange, onError = (e) => this.options.logger?.error(e) } = handler ?? {};
|
|
@@ -683,7 +752,10 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
683
752
|
* This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be performed
|
|
684
753
|
* together when data is changed.
|
|
685
754
|
*
|
|
686
|
-
* Note
|
|
755
|
+
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
756
|
+
*
|
|
757
|
+
* @param options Options for configuring watch behavior
|
|
758
|
+
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
687
759
|
*/
|
|
688
760
|
onChangeWithAsyncGenerator(options) {
|
|
689
761
|
const resolvedOptions = options ?? {};
|
|
@@ -116,6 +116,7 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
|
|
|
116
116
|
protected abortController: AbortController | null;
|
|
117
117
|
protected crudUpdateListener?: () => void;
|
|
118
118
|
protected streamingSyncPromise?: Promise<void>;
|
|
119
|
+
private pendingCrudUpload?;
|
|
119
120
|
syncStatus: SyncStatus;
|
|
120
121
|
triggerCrudUpload: () => void;
|
|
121
122
|
constructor(options: AbstractStreamingSyncImplementationOptions);
|
|
@@ -137,9 +138,8 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
|
|
|
137
138
|
*/
|
|
138
139
|
streamingSync(signal?: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void>;
|
|
139
140
|
private collectLocalBucketState;
|
|
140
|
-
protected streamingSyncIteration(signal: AbortSignal, options?: PowerSyncConnectionOptions): Promise<
|
|
141
|
-
|
|
142
|
-
}>;
|
|
141
|
+
protected streamingSyncIteration(signal: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void>;
|
|
142
|
+
private applyCheckpoint;
|
|
143
143
|
protected updateSyncStatus(options: SyncStatusOptions): void;
|
|
144
144
|
private delayRetry;
|
|
145
145
|
}
|
|
@@ -2,7 +2,7 @@ import Logger from 'js-logger';
|
|
|
2
2
|
import { SyncStatus } from '../../../db/crud/SyncStatus.js';
|
|
3
3
|
import { AbortOperation } from '../../../utils/AbortOperation.js';
|
|
4
4
|
import { BaseObserver } from '../../../utils/BaseObserver.js';
|
|
5
|
-
import { throttleLeadingTrailing } from '../../../utils/
|
|
5
|
+
import { onAbortPromise, throttleLeadingTrailing } from '../../../utils/async.js';
|
|
6
6
|
import { SyncDataBucket } from '../bucket/SyncDataBucket.js';
|
|
7
7
|
import { FetchStrategy } from './AbstractRemote.js';
|
|
8
8
|
import { isStreamingKeepalive, isStreamingSyncCheckpoint, isStreamingSyncCheckpointComplete, isStreamingSyncCheckpointDiff, isStreamingSyncCheckpointPartiallyComplete, isStreamingSyncData } from './streaming-sync-types.js';
|
|
@@ -39,6 +39,7 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
39
39
|
abortController;
|
|
40
40
|
crudUpdateListener;
|
|
41
41
|
streamingSyncPromise;
|
|
42
|
+
pendingCrudUpload;
|
|
42
43
|
syncStatus;
|
|
43
44
|
triggerCrudUpload;
|
|
44
45
|
constructor(options) {
|
|
@@ -55,10 +56,15 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
55
56
|
});
|
|
56
57
|
this.abortController = null;
|
|
57
58
|
this.triggerCrudUpload = throttleLeadingTrailing(() => {
|
|
58
|
-
if (!this.syncStatus.connected || this.
|
|
59
|
+
if (!this.syncStatus.connected || this.pendingCrudUpload != null) {
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
|
-
this.
|
|
62
|
+
this.pendingCrudUpload = new Promise((resolve) => {
|
|
63
|
+
this._uploadAllCrud().finally(() => {
|
|
64
|
+
this.pendingCrudUpload = undefined;
|
|
65
|
+
resolve();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
62
68
|
}, this.options.crudUploadThrottleMs);
|
|
63
69
|
}
|
|
64
70
|
async waitForReady() { }
|
|
@@ -280,16 +286,8 @@ The next upload iteration will be delayed.`);
|
|
|
280
286
|
if (signal?.aborted) {
|
|
281
287
|
break;
|
|
282
288
|
}
|
|
283
|
-
|
|
284
|
-
if
|
|
285
|
-
/**
|
|
286
|
-
* A sync error ocurred that we cannot recover from here.
|
|
287
|
-
* This loop must terminate.
|
|
288
|
-
* The nestedAbortController will close any open network requests and streams below.
|
|
289
|
-
*/
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
// Continue immediately
|
|
289
|
+
await this.streamingSyncIteration(nestedAbortController.signal, options);
|
|
290
|
+
// Continue immediately, streamingSyncIteration will wait before completing if necessary.
|
|
293
291
|
}
|
|
294
292
|
catch (ex) {
|
|
295
293
|
/**
|
|
@@ -341,7 +339,7 @@ The next upload iteration will be delayed.`);
|
|
|
341
339
|
return [req, localDescriptions];
|
|
342
340
|
}
|
|
343
341
|
async streamingSyncIteration(signal, options) {
|
|
344
|
-
|
|
342
|
+
await this.obtainLock({
|
|
345
343
|
type: LockType.SYNC,
|
|
346
344
|
signal,
|
|
347
345
|
callback: async () => {
|
|
@@ -384,7 +382,7 @@ The next upload iteration will be delayed.`);
|
|
|
384
382
|
const line = await stream.read();
|
|
385
383
|
if (!line) {
|
|
386
384
|
// The stream has closed while waiting
|
|
387
|
-
return
|
|
385
|
+
return;
|
|
388
386
|
}
|
|
389
387
|
// A connection is active and messages are being received
|
|
390
388
|
if (!this.syncStatus.connected) {
|
|
@@ -413,30 +411,12 @@ The next upload iteration will be delayed.`);
|
|
|
413
411
|
await this.options.adapter.setTargetCheckpoint(targetCheckpoint);
|
|
414
412
|
}
|
|
415
413
|
else if (isStreamingSyncCheckpointComplete(line)) {
|
|
416
|
-
this.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// This means checksums failed. Start again with a new checkpoint.
|
|
420
|
-
// TODO: better back-off
|
|
421
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
422
|
-
return { retry: true };
|
|
423
|
-
}
|
|
424
|
-
else if (!result.ready) {
|
|
425
|
-
// Checksums valid, but need more data for a consistent checkpoint.
|
|
426
|
-
// Continue waiting.
|
|
427
|
-
// landing here the whole time
|
|
414
|
+
const result = await this.applyCheckpoint(targetCheckpoint, signal);
|
|
415
|
+
if (result.endIteration) {
|
|
416
|
+
return;
|
|
428
417
|
}
|
|
429
|
-
else {
|
|
418
|
+
else if (result.applied) {
|
|
430
419
|
appliedCheckpoint = targetCheckpoint;
|
|
431
|
-
this.logger.debug('validated checkpoint', appliedCheckpoint);
|
|
432
|
-
this.updateSyncStatus({
|
|
433
|
-
connected: true,
|
|
434
|
-
lastSyncedAt: new Date(),
|
|
435
|
-
dataFlow: {
|
|
436
|
-
downloading: false,
|
|
437
|
-
downloadError: undefined
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
420
|
}
|
|
441
421
|
validatedCheckpoint = targetCheckpoint;
|
|
442
422
|
}
|
|
@@ -448,10 +428,11 @@ The next upload iteration will be delayed.`);
|
|
|
448
428
|
// This means checksums failed. Start again with a new checkpoint.
|
|
449
429
|
// TODO: better back-off
|
|
450
430
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
451
|
-
return
|
|
431
|
+
return;
|
|
452
432
|
}
|
|
453
433
|
else if (!result.ready) {
|
|
454
|
-
//
|
|
434
|
+
// If we have pending uploads, we can't complete new checkpoints outside of priority 0.
|
|
435
|
+
// We'll resolve this for a complete checkpoint.
|
|
455
436
|
}
|
|
456
437
|
else {
|
|
457
438
|
// We'll keep on downloading, but can report that this priority is synced now.
|
|
@@ -522,7 +503,7 @@ The next upload iteration will be delayed.`);
|
|
|
522
503
|
* (uses the same one), this should have some delay.
|
|
523
504
|
*/
|
|
524
505
|
await this.delayRetry();
|
|
525
|
-
return
|
|
506
|
+
return;
|
|
526
507
|
}
|
|
527
508
|
this.triggerCrudUpload();
|
|
528
509
|
}
|
|
@@ -539,38 +520,61 @@ The next upload iteration will be delayed.`);
|
|
|
539
520
|
});
|
|
540
521
|
}
|
|
541
522
|
else if (validatedCheckpoint === targetCheckpoint) {
|
|
542
|
-
const result = await this.
|
|
543
|
-
if (
|
|
544
|
-
|
|
545
|
-
// TODO: better back-off
|
|
546
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
547
|
-
return { retry: false };
|
|
523
|
+
const result = await this.applyCheckpoint(targetCheckpoint, signal);
|
|
524
|
+
if (result.endIteration) {
|
|
525
|
+
return;
|
|
548
526
|
}
|
|
549
|
-
else if (
|
|
550
|
-
// Checksums valid, but need more data for a consistent checkpoint.
|
|
551
|
-
// Continue waiting.
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
527
|
+
else if (result.applied) {
|
|
554
528
|
appliedCheckpoint = targetCheckpoint;
|
|
555
|
-
this.updateSyncStatus({
|
|
556
|
-
connected: true,
|
|
557
|
-
lastSyncedAt: new Date(),
|
|
558
|
-
priorityStatusEntries: [],
|
|
559
|
-
dataFlow: {
|
|
560
|
-
downloading: false,
|
|
561
|
-
downloadError: undefined
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
529
|
}
|
|
565
530
|
}
|
|
566
531
|
}
|
|
567
532
|
}
|
|
568
533
|
this.logger.debug('Stream input empty');
|
|
569
534
|
// Connection closed. Likely due to auth issue.
|
|
570
|
-
return
|
|
535
|
+
return;
|
|
571
536
|
}
|
|
572
537
|
});
|
|
573
538
|
}
|
|
539
|
+
async applyCheckpoint(checkpoint, abort) {
|
|
540
|
+
let result = await this.options.adapter.syncLocalDatabase(checkpoint);
|
|
541
|
+
const pending = this.pendingCrudUpload;
|
|
542
|
+
if (!result.checkpointValid) {
|
|
543
|
+
this.logger.debug('Checksum mismatch in checkpoint, will reconnect');
|
|
544
|
+
// This means checksums failed. Start again with a new checkpoint.
|
|
545
|
+
// TODO: better back-off
|
|
546
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
547
|
+
return { applied: false, endIteration: true };
|
|
548
|
+
}
|
|
549
|
+
else if (!result.ready && pending != null) {
|
|
550
|
+
// We have pending entries in the local upload queue or are waiting to confirm a write
|
|
551
|
+
// checkpoint, which prevented this checkpoint from applying. Wait for that to complete and
|
|
552
|
+
// try again.
|
|
553
|
+
this.logger.debug('Could not apply checkpoint due to local data. Waiting for in-progress upload before retrying.');
|
|
554
|
+
await Promise.race([pending, onAbortPromise(abort)]);
|
|
555
|
+
if (abort.aborted) {
|
|
556
|
+
return { applied: false, endIteration: true };
|
|
557
|
+
}
|
|
558
|
+
// Try again now that uploads have completed.
|
|
559
|
+
result = await this.options.adapter.syncLocalDatabase(checkpoint);
|
|
560
|
+
}
|
|
561
|
+
if (result.checkpointValid && result.ready) {
|
|
562
|
+
this.logger.debug('validated checkpoint', checkpoint);
|
|
563
|
+
this.updateSyncStatus({
|
|
564
|
+
connected: true,
|
|
565
|
+
lastSyncedAt: new Date(),
|
|
566
|
+
dataFlow: {
|
|
567
|
+
downloading: false,
|
|
568
|
+
downloadError: undefined
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
return { applied: true, endIteration: false };
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
this.logger.debug('Could not apply checkpoint. Waiting for next sync complete line.');
|
|
575
|
+
return { applied: false, endIteration: false };
|
|
576
|
+
}
|
|
577
|
+
}
|
|
574
578
|
updateSyncStatus(options) {
|
|
575
579
|
const updatedStatus = new SyncStatus({
|
|
576
580
|
connected: options.connected ?? this.syncStatus.connected,
|