@powersync/web 1.38.1 → 1.38.3
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/worker/SharedSyncImplementation.umd.js +560 -353
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +560 -353
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/package.json +2 -2
- package/lib/src/attachments/IndexDBFileSystemAdapter.js +1 -0
- package/lib/src/attachments/IndexDBFileSystemAdapter.js.map +1 -0
- package/lib/src/db/NavigatorTriggerClaimManager.js +1 -0
- package/lib/src/db/NavigatorTriggerClaimManager.js.map +1 -0
- package/lib/src/db/PowerSyncDatabase.js +1 -0
- package/lib/src/db/PowerSyncDatabase.js.map +1 -0
- package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js +1 -0
- package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js.map +1 -0
- package/lib/src/db/adapters/AsyncWebAdapter.js +1 -0
- package/lib/src/db/adapters/AsyncWebAdapter.js.map +1 -0
- package/lib/src/db/adapters/SSRDBAdapter.js +1 -0
- package/lib/src/db/adapters/SSRDBAdapter.js.map +1 -0
- package/lib/src/db/adapters/WebDBAdapter.js +1 -0
- package/lib/src/db/adapters/WebDBAdapter.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/vfs.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/vfs.js.map +1 -0
- package/lib/src/db/adapters/web-sql-flags.js +1 -0
- package/lib/src/db/adapters/web-sql-flags.js.map +1 -0
- package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +1 -0
- package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js.map +1 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +1 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js.map +1 -0
- package/lib/src/db/sync/WebRemote.js +1 -0
- package/lib/src/db/sync/WebRemote.js.map +1 -0
- package/lib/src/db/sync/WebStreamingSyncImplementation.js +1 -0
- package/lib/src/db/sync/WebStreamingSyncImplementation.js.map +1 -0
- package/lib/src/db/sync/userAgent.js +1 -0
- package/lib/src/db/sync/userAgent.js.map +1 -0
- package/lib/src/index.js +1 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/shared/navigator.js +1 -0
- package/lib/src/shared/navigator.js.map +1 -0
- package/lib/src/shared/tab_close_signal.js +1 -0
- package/lib/src/shared/tab_close_signal.js.map +1 -0
- package/lib/src/worker/db/MultiDatabaseServer.js +1 -0
- package/lib/src/worker/db/MultiDatabaseServer.js.map +1 -0
- package/lib/src/worker/db/WASQLiteDB.worker.js +1 -0
- package/lib/src/worker/db/WASQLiteDB.worker.js.map +1 -0
- package/lib/src/worker/db/open-worker-database.js +1 -0
- package/lib/src/worker/db/open-worker-database.js.map +1 -0
- package/lib/src/worker/sync/AbstractSharedSyncClientProvider.js +1 -0
- package/lib/src/worker/sync/AbstractSharedSyncClientProvider.js.map +1 -0
- package/lib/src/worker/sync/BroadcastLogger.js +1 -0
- package/lib/src/worker/sync/BroadcastLogger.js.map +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.js +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.js.map +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js.map +1 -0
- package/lib/src/worker/sync/WorkerClient.js +1 -0
- package/lib/src/worker/sync/WorkerClient.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -2891,7 +2891,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2891
2891
|
/* harmony export */ sanitizeUUID: () => (/* binding */ sanitizeUUID),
|
|
2892
2892
|
/* harmony export */ timeoutSignal: () => (/* binding */ timeoutSignal)
|
|
2893
2893
|
/* harmony export */ });
|
|
2894
|
-
|
|
2894
|
+
/**
|
|
2895
|
+
* @see https://www.sqlite.org/lang_expr.html#castexpr
|
|
2896
|
+
* @public
|
|
2897
|
+
*/
|
|
2895
2898
|
var ColumnType;
|
|
2896
2899
|
(function (ColumnType) {
|
|
2897
2900
|
ColumnType["TEXT"] = "TEXT";
|
|
@@ -2907,14 +2910,24 @@ const integer = {
|
|
|
2907
2910
|
const real = {
|
|
2908
2911
|
type: ColumnType.REAL
|
|
2909
2912
|
};
|
|
2910
|
-
|
|
2911
|
-
|
|
2913
|
+
/**
|
|
2914
|
+
* powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
|
|
2915
|
+
* In earlier versions this was limited to 63.
|
|
2916
|
+
*
|
|
2917
|
+
* @internal
|
|
2918
|
+
*/
|
|
2912
2919
|
const MAX_AMOUNT_OF_COLUMNS = 1999;
|
|
2920
|
+
/**
|
|
2921
|
+
* @public
|
|
2922
|
+
*/
|
|
2913
2923
|
const column = {
|
|
2914
2924
|
text,
|
|
2915
2925
|
integer,
|
|
2916
2926
|
real
|
|
2917
2927
|
};
|
|
2928
|
+
/**
|
|
2929
|
+
* @public
|
|
2930
|
+
*/
|
|
2918
2931
|
class Column {
|
|
2919
2932
|
options;
|
|
2920
2933
|
constructor(options) {
|
|
@@ -2934,9 +2947,15 @@ class Column {
|
|
|
2934
2947
|
}
|
|
2935
2948
|
}
|
|
2936
2949
|
|
|
2950
|
+
/**
|
|
2951
|
+
* @internal
|
|
2952
|
+
*/
|
|
2937
2953
|
const DEFAULT_INDEX_COLUMN_OPTIONS = {
|
|
2938
2954
|
ascending: true
|
|
2939
2955
|
};
|
|
2956
|
+
/**
|
|
2957
|
+
* @public
|
|
2958
|
+
*/
|
|
2940
2959
|
class IndexedColumn {
|
|
2941
2960
|
options;
|
|
2942
2961
|
static createAscending(column) {
|
|
@@ -2963,9 +2982,15 @@ class IndexedColumn {
|
|
|
2963
2982
|
}
|
|
2964
2983
|
}
|
|
2965
2984
|
|
|
2985
|
+
/**
|
|
2986
|
+
* @internal
|
|
2987
|
+
*/
|
|
2966
2988
|
const DEFAULT_INDEX_OPTIONS = {
|
|
2967
2989
|
columns: []
|
|
2968
2990
|
};
|
|
2991
|
+
/**
|
|
2992
|
+
* @public
|
|
2993
|
+
*/
|
|
2969
2994
|
class Index {
|
|
2970
2995
|
options;
|
|
2971
2996
|
static createAscending(options, columnNames) {
|
|
@@ -3007,6 +3032,9 @@ function encodeTableOptions(options) {
|
|
|
3007
3032
|
};
|
|
3008
3033
|
}
|
|
3009
3034
|
|
|
3035
|
+
/**
|
|
3036
|
+
* @internal
|
|
3037
|
+
*/
|
|
3010
3038
|
const DEFAULT_TABLE_OPTIONS = {
|
|
3011
3039
|
indexes: [],
|
|
3012
3040
|
insertOnly: false,
|
|
@@ -3015,7 +3043,13 @@ const DEFAULT_TABLE_OPTIONS = {
|
|
|
3015
3043
|
trackMetadata: false,
|
|
3016
3044
|
ignoreEmptyUpdates: false
|
|
3017
3045
|
};
|
|
3046
|
+
/**
|
|
3047
|
+
* @internal
|
|
3048
|
+
*/
|
|
3018
3049
|
const InvalidSQLCharacters = /["'%,.#\s[\]]/;
|
|
3050
|
+
/**
|
|
3051
|
+
* @public
|
|
3052
|
+
*/
|
|
3019
3053
|
class Table {
|
|
3020
3054
|
options;
|
|
3021
3055
|
_mappedColumns;
|
|
@@ -3206,6 +3240,11 @@ class Table {
|
|
|
3206
3240
|
}
|
|
3207
3241
|
}
|
|
3208
3242
|
|
|
3243
|
+
/**
|
|
3244
|
+
* The default name of the local table storing attachment data.
|
|
3245
|
+
*
|
|
3246
|
+
* @alpha
|
|
3247
|
+
*/
|
|
3209
3248
|
const ATTACHMENT_TABLE = 'attachments';
|
|
3210
3249
|
/**
|
|
3211
3250
|
* Maps a database row to an AttachmentRecord.
|
|
@@ -3213,7 +3252,7 @@ const ATTACHMENT_TABLE = 'attachments';
|
|
|
3213
3252
|
* @param row - The database row object
|
|
3214
3253
|
* @returns The corresponding AttachmentRecord
|
|
3215
3254
|
*
|
|
3216
|
-
* @
|
|
3255
|
+
* @alpha
|
|
3217
3256
|
*/
|
|
3218
3257
|
function attachmentFromSql(row) {
|
|
3219
3258
|
return {
|
|
@@ -3231,7 +3270,7 @@ function attachmentFromSql(row) {
|
|
|
3231
3270
|
/**
|
|
3232
3271
|
* AttachmentState represents the current synchronization state of an attachment.
|
|
3233
3272
|
*
|
|
3234
|
-
* @
|
|
3273
|
+
* @alpha
|
|
3235
3274
|
*/
|
|
3236
3275
|
var AttachmentState;
|
|
3237
3276
|
(function (AttachmentState) {
|
|
@@ -3244,7 +3283,7 @@ var AttachmentState;
|
|
|
3244
3283
|
/**
|
|
3245
3284
|
* AttachmentTable defines the schema for the attachment queue table.
|
|
3246
3285
|
*
|
|
3247
|
-
* @
|
|
3286
|
+
* @alpha
|
|
3248
3287
|
*/
|
|
3249
3288
|
class AttachmentTable extends Table {
|
|
3250
3289
|
constructor(options) {
|
|
@@ -3272,7 +3311,8 @@ class AttachmentTable extends Table {
|
|
|
3272
3311
|
* Provides methods to query, insert, update, and delete attachment records with
|
|
3273
3312
|
* proper transaction management through PowerSync.
|
|
3274
3313
|
*
|
|
3275
|
-
* @
|
|
3314
|
+
* @experimental
|
|
3315
|
+
* @alpha
|
|
3276
3316
|
*/
|
|
3277
3317
|
class AttachmentContext {
|
|
3278
3318
|
/** PowerSync database instance for executing queries */
|
|
@@ -3494,6 +3534,9 @@ class AttachmentContext {
|
|
|
3494
3534
|
}
|
|
3495
3535
|
}
|
|
3496
3536
|
|
|
3537
|
+
/**
|
|
3538
|
+
* @public
|
|
3539
|
+
*/
|
|
3497
3540
|
var WatchedQueryListenerEvent;
|
|
3498
3541
|
(function (WatchedQueryListenerEvent) {
|
|
3499
3542
|
WatchedQueryListenerEvent["ON_DATA"] = "onData";
|
|
@@ -3502,176 +3545,18 @@ var WatchedQueryListenerEvent;
|
|
|
3502
3545
|
WatchedQueryListenerEvent["SETTINGS_WILL_UPDATE"] = "settingsWillUpdate";
|
|
3503
3546
|
WatchedQueryListenerEvent["CLOSED"] = "closed";
|
|
3504
3547
|
})(WatchedQueryListenerEvent || (WatchedQueryListenerEvent = {}));
|
|
3548
|
+
/**
|
|
3549
|
+
* @internal
|
|
3550
|
+
*/
|
|
3505
3551
|
const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
3552
|
+
/**
|
|
3553
|
+
* @internal
|
|
3554
|
+
*/
|
|
3506
3555
|
const DEFAULT_WATCH_QUERY_OPTIONS = {
|
|
3507
3556
|
throttleMs: DEFAULT_WATCH_THROTTLE_MS,
|
|
3508
3557
|
reportFetching: true
|
|
3509
3558
|
};
|
|
3510
3559
|
|
|
3511
|
-
/**
|
|
3512
|
-
* Orchestrates attachment synchronization between local and remote storage.
|
|
3513
|
-
* Handles uploads, downloads, deletions, and state transitions.
|
|
3514
|
-
*
|
|
3515
|
-
* @internal
|
|
3516
|
-
*/
|
|
3517
|
-
class SyncingService {
|
|
3518
|
-
attachmentService;
|
|
3519
|
-
localStorage;
|
|
3520
|
-
remoteStorage;
|
|
3521
|
-
logger;
|
|
3522
|
-
errorHandler;
|
|
3523
|
-
constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
|
|
3524
|
-
this.attachmentService = attachmentService;
|
|
3525
|
-
this.localStorage = localStorage;
|
|
3526
|
-
this.remoteStorage = remoteStorage;
|
|
3527
|
-
this.logger = logger;
|
|
3528
|
-
this.errorHandler = errorHandler;
|
|
3529
|
-
}
|
|
3530
|
-
/**
|
|
3531
|
-
* Processes attachments based on their state (upload, download, or delete).
|
|
3532
|
-
* All updates are saved in a single batch after processing.
|
|
3533
|
-
*
|
|
3534
|
-
* @param attachments - Array of attachment records to process
|
|
3535
|
-
* @param context - Attachment context for database operations
|
|
3536
|
-
* @returns Promise that resolves when all attachments have been processed and saved
|
|
3537
|
-
*/
|
|
3538
|
-
async processAttachments(attachments, context) {
|
|
3539
|
-
const updatedAttachments = [];
|
|
3540
|
-
for (const attachment of attachments) {
|
|
3541
|
-
switch (attachment.state) {
|
|
3542
|
-
case AttachmentState.QUEUED_UPLOAD:
|
|
3543
|
-
const uploaded = await this.uploadAttachment(attachment);
|
|
3544
|
-
updatedAttachments.push(uploaded);
|
|
3545
|
-
break;
|
|
3546
|
-
case AttachmentState.QUEUED_DOWNLOAD:
|
|
3547
|
-
const downloaded = await this.downloadAttachment(attachment);
|
|
3548
|
-
updatedAttachments.push(downloaded);
|
|
3549
|
-
break;
|
|
3550
|
-
case AttachmentState.QUEUED_DELETE:
|
|
3551
|
-
const deleted = await this.deleteAttachment(attachment, context);
|
|
3552
|
-
updatedAttachments.push(deleted);
|
|
3553
|
-
break;
|
|
3554
|
-
}
|
|
3555
|
-
}
|
|
3556
|
-
await context.saveAttachments(updatedAttachments);
|
|
3557
|
-
}
|
|
3558
|
-
/**
|
|
3559
|
-
* Uploads an attachment from local storage to remote storage.
|
|
3560
|
-
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
3561
|
-
*
|
|
3562
|
-
* @param attachment - The attachment record to upload
|
|
3563
|
-
* @returns Updated attachment record with new state
|
|
3564
|
-
* @throws Error if the attachment has no localUri
|
|
3565
|
-
*/
|
|
3566
|
-
async uploadAttachment(attachment) {
|
|
3567
|
-
this.logger.info(`Uploading attachment ${attachment.filename}`);
|
|
3568
|
-
try {
|
|
3569
|
-
if (attachment.localUri == null) {
|
|
3570
|
-
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
3571
|
-
}
|
|
3572
|
-
const fileBlob = await this.localStorage.readFile(attachment.localUri);
|
|
3573
|
-
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
3574
|
-
return {
|
|
3575
|
-
...attachment,
|
|
3576
|
-
state: AttachmentState.SYNCED,
|
|
3577
|
-
hasSynced: true
|
|
3578
|
-
};
|
|
3579
|
-
}
|
|
3580
|
-
catch (error) {
|
|
3581
|
-
const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
|
|
3582
|
-
if (!shouldRetry) {
|
|
3583
|
-
return {
|
|
3584
|
-
...attachment,
|
|
3585
|
-
state: AttachmentState.ARCHIVED
|
|
3586
|
-
};
|
|
3587
|
-
}
|
|
3588
|
-
return attachment;
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
/**
|
|
3592
|
-
* Downloads an attachment from remote storage to local storage.
|
|
3593
|
-
* Retrieves the file, converts to base64, and saves locally.
|
|
3594
|
-
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
3595
|
-
*
|
|
3596
|
-
* @param attachment - The attachment record to download
|
|
3597
|
-
* @returns Updated attachment record with local URI and new state
|
|
3598
|
-
*/
|
|
3599
|
-
async downloadAttachment(attachment) {
|
|
3600
|
-
this.logger.info(`Downloading attachment ${attachment.filename}`);
|
|
3601
|
-
try {
|
|
3602
|
-
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
3603
|
-
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
3604
|
-
await this.localStorage.saveFile(localUri, fileData);
|
|
3605
|
-
return {
|
|
3606
|
-
...attachment,
|
|
3607
|
-
state: AttachmentState.SYNCED,
|
|
3608
|
-
localUri: localUri,
|
|
3609
|
-
hasSynced: true
|
|
3610
|
-
};
|
|
3611
|
-
}
|
|
3612
|
-
catch (error) {
|
|
3613
|
-
const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
|
|
3614
|
-
if (!shouldRetry) {
|
|
3615
|
-
return {
|
|
3616
|
-
...attachment,
|
|
3617
|
-
state: AttachmentState.ARCHIVED
|
|
3618
|
-
};
|
|
3619
|
-
}
|
|
3620
|
-
return attachment;
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
/**
|
|
3624
|
-
* Deletes an attachment from both remote and local storage.
|
|
3625
|
-
* Removes the remote file, local file (if exists), and the attachment record.
|
|
3626
|
-
* On failure, defers to error handler or archives.
|
|
3627
|
-
*
|
|
3628
|
-
* @param attachment - The attachment record to delete
|
|
3629
|
-
* @param context - Attachment context for database operations
|
|
3630
|
-
* @returns Updated attachment record
|
|
3631
|
-
*/
|
|
3632
|
-
async deleteAttachment(attachment, context) {
|
|
3633
|
-
try {
|
|
3634
|
-
await this.remoteStorage.deleteFile(attachment);
|
|
3635
|
-
if (attachment.localUri) {
|
|
3636
|
-
await this.localStorage.deleteFile(attachment.localUri);
|
|
3637
|
-
}
|
|
3638
|
-
await context.deleteAttachment(attachment.id);
|
|
3639
|
-
return {
|
|
3640
|
-
...attachment,
|
|
3641
|
-
state: AttachmentState.ARCHIVED
|
|
3642
|
-
};
|
|
3643
|
-
}
|
|
3644
|
-
catch (error) {
|
|
3645
|
-
const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
|
|
3646
|
-
if (!shouldRetry) {
|
|
3647
|
-
return {
|
|
3648
|
-
...attachment,
|
|
3649
|
-
state: AttachmentState.ARCHIVED
|
|
3650
|
-
};
|
|
3651
|
-
}
|
|
3652
|
-
return attachment;
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
/**
|
|
3656
|
-
* Performs cleanup of archived attachments by removing their local files and records.
|
|
3657
|
-
* Errors during local file deletion are logged but do not prevent record deletion.
|
|
3658
|
-
*/
|
|
3659
|
-
async deleteArchivedAttachments(context) {
|
|
3660
|
-
return await context.deleteArchivedAttachments(async (archivedAttachments) => {
|
|
3661
|
-
for (const attachment of archivedAttachments) {
|
|
3662
|
-
if (attachment.localUri) {
|
|
3663
|
-
try {
|
|
3664
|
-
await this.localStorage.deleteFile(attachment.localUri);
|
|
3665
|
-
}
|
|
3666
|
-
catch (error) {
|
|
3667
|
-
this.logger.error('Error deleting local file for archived attachment', error);
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
}
|
|
3671
|
-
});
|
|
3672
|
-
}
|
|
3673
|
-
}
|
|
3674
|
-
|
|
3675
3560
|
/**
|
|
3676
3561
|
* A simple fixed-capacity queue implementation.
|
|
3677
3562
|
*
|
|
@@ -3857,6 +3742,9 @@ class Mutex {
|
|
|
3857
3742
|
}
|
|
3858
3743
|
}
|
|
3859
3744
|
}
|
|
3745
|
+
/**
|
|
3746
|
+
* @internal
|
|
3747
|
+
*/
|
|
3860
3748
|
function timeoutSignal(timeout) {
|
|
3861
3749
|
if (timeout == null)
|
|
3862
3750
|
return;
|
|
@@ -3885,36 +3773,200 @@ class AttachmentService {
|
|
|
3885
3773
|
this.context = new AttachmentContext(db, tableName, logger, archivedCacheLimit);
|
|
3886
3774
|
}
|
|
3887
3775
|
/**
|
|
3888
|
-
* Creates a differential watch query for active attachments requiring synchronization.
|
|
3889
|
-
* @returns Watch query that emits changes for queued uploads, downloads, and deletes
|
|
3776
|
+
* Creates a differential watch query for active attachments requiring synchronization.
|
|
3777
|
+
* @returns Watch query that emits changes for queued uploads, downloads, and deletes
|
|
3778
|
+
*/
|
|
3779
|
+
watchActiveAttachments({ throttleMs } = {}) {
|
|
3780
|
+
this.logger.info('Watching active attachments...');
|
|
3781
|
+
const watch = this.db
|
|
3782
|
+
.query({
|
|
3783
|
+
sql: /* sql */ `
|
|
3784
|
+
SELECT
|
|
3785
|
+
*
|
|
3786
|
+
FROM
|
|
3787
|
+
${this.tableName}
|
|
3788
|
+
WHERE
|
|
3789
|
+
state = ?
|
|
3790
|
+
OR state = ?
|
|
3791
|
+
OR state = ?
|
|
3792
|
+
ORDER BY
|
|
3793
|
+
timestamp ASC
|
|
3794
|
+
`,
|
|
3795
|
+
parameters: [AttachmentState.QUEUED_UPLOAD, AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_DELETE]
|
|
3796
|
+
})
|
|
3797
|
+
.differentialWatch({ throttleMs });
|
|
3798
|
+
return watch;
|
|
3799
|
+
}
|
|
3800
|
+
/**
|
|
3801
|
+
* Executes a callback with exclusive access to the attachment context.
|
|
3802
|
+
*/
|
|
3803
|
+
async withContext(callback) {
|
|
3804
|
+
return this.mutex.runExclusive(async () => {
|
|
3805
|
+
return callback(this.context);
|
|
3806
|
+
});
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
/**
|
|
3811
|
+
* Orchestrates attachment synchronization between local and remote storage.
|
|
3812
|
+
* Handles uploads, downloads, deletions, and state transitions.
|
|
3813
|
+
*
|
|
3814
|
+
* @internal
|
|
3815
|
+
*/
|
|
3816
|
+
class SyncingService {
|
|
3817
|
+
attachmentService;
|
|
3818
|
+
localStorage;
|
|
3819
|
+
remoteStorage;
|
|
3820
|
+
logger;
|
|
3821
|
+
errorHandler;
|
|
3822
|
+
constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
|
|
3823
|
+
this.attachmentService = attachmentService;
|
|
3824
|
+
this.localStorage = localStorage;
|
|
3825
|
+
this.remoteStorage = remoteStorage;
|
|
3826
|
+
this.logger = logger;
|
|
3827
|
+
this.errorHandler = errorHandler;
|
|
3828
|
+
}
|
|
3829
|
+
/**
|
|
3830
|
+
* Processes attachments based on their state (upload, download, or delete).
|
|
3831
|
+
* All updates are saved in a single batch after processing.
|
|
3832
|
+
*
|
|
3833
|
+
* @param attachments - Array of attachment records to process
|
|
3834
|
+
* @param context - Attachment context for database operations
|
|
3835
|
+
* @returns Promise that resolves when all attachments have been processed and saved
|
|
3836
|
+
*/
|
|
3837
|
+
async processAttachments(attachments, context) {
|
|
3838
|
+
const updatedAttachments = [];
|
|
3839
|
+
for (const attachment of attachments) {
|
|
3840
|
+
switch (attachment.state) {
|
|
3841
|
+
case AttachmentState.QUEUED_UPLOAD:
|
|
3842
|
+
const uploaded = await this.uploadAttachment(attachment);
|
|
3843
|
+
updatedAttachments.push(uploaded);
|
|
3844
|
+
break;
|
|
3845
|
+
case AttachmentState.QUEUED_DOWNLOAD:
|
|
3846
|
+
const downloaded = await this.downloadAttachment(attachment);
|
|
3847
|
+
updatedAttachments.push(downloaded);
|
|
3848
|
+
break;
|
|
3849
|
+
case AttachmentState.QUEUED_DELETE:
|
|
3850
|
+
const deleted = await this.deleteAttachment(attachment, context);
|
|
3851
|
+
updatedAttachments.push(deleted);
|
|
3852
|
+
break;
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
await context.saveAttachments(updatedAttachments);
|
|
3856
|
+
}
|
|
3857
|
+
/**
|
|
3858
|
+
* Uploads an attachment from local storage to remote storage.
|
|
3859
|
+
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
3860
|
+
*
|
|
3861
|
+
* @param attachment - The attachment record to upload
|
|
3862
|
+
* @returns Updated attachment record with new state
|
|
3863
|
+
* @throws Error if the attachment has no localUri
|
|
3864
|
+
*/
|
|
3865
|
+
async uploadAttachment(attachment) {
|
|
3866
|
+
this.logger.info(`Uploading attachment ${attachment.filename}`);
|
|
3867
|
+
try {
|
|
3868
|
+
if (attachment.localUri == null) {
|
|
3869
|
+
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
3870
|
+
}
|
|
3871
|
+
const fileBlob = await this.localStorage.readFile(attachment.localUri);
|
|
3872
|
+
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
3873
|
+
return {
|
|
3874
|
+
...attachment,
|
|
3875
|
+
state: AttachmentState.SYNCED,
|
|
3876
|
+
hasSynced: true
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
catch (error) {
|
|
3880
|
+
const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
|
|
3881
|
+
if (!shouldRetry) {
|
|
3882
|
+
return {
|
|
3883
|
+
...attachment,
|
|
3884
|
+
state: AttachmentState.ARCHIVED
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
return attachment;
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
/**
|
|
3891
|
+
* Downloads an attachment from remote storage to local storage.
|
|
3892
|
+
* Retrieves the file, converts to base64, and saves locally.
|
|
3893
|
+
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
3894
|
+
*
|
|
3895
|
+
* @param attachment - The attachment record to download
|
|
3896
|
+
* @returns Updated attachment record with local URI and new state
|
|
3897
|
+
*/
|
|
3898
|
+
async downloadAttachment(attachment) {
|
|
3899
|
+
this.logger.info(`Downloading attachment ${attachment.filename}`);
|
|
3900
|
+
try {
|
|
3901
|
+
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
3902
|
+
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
3903
|
+
await this.localStorage.saveFile(localUri, fileData);
|
|
3904
|
+
return {
|
|
3905
|
+
...attachment,
|
|
3906
|
+
state: AttachmentState.SYNCED,
|
|
3907
|
+
localUri: localUri,
|
|
3908
|
+
hasSynced: true
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
catch (error) {
|
|
3912
|
+
const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
|
|
3913
|
+
if (!shouldRetry) {
|
|
3914
|
+
return {
|
|
3915
|
+
...attachment,
|
|
3916
|
+
state: AttachmentState.ARCHIVED
|
|
3917
|
+
};
|
|
3918
|
+
}
|
|
3919
|
+
return attachment;
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
/**
|
|
3923
|
+
* Deletes an attachment from both remote and local storage.
|
|
3924
|
+
* Removes the remote file, local file (if exists), and the attachment record.
|
|
3925
|
+
* On failure, defers to error handler or archives.
|
|
3926
|
+
*
|
|
3927
|
+
* @param attachment - The attachment record to delete
|
|
3928
|
+
* @param context - Attachment context for database operations
|
|
3929
|
+
* @returns Updated attachment record
|
|
3890
3930
|
*/
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
.
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3931
|
+
async deleteAttachment(attachment, context) {
|
|
3932
|
+
try {
|
|
3933
|
+
await this.remoteStorage.deleteFile(attachment);
|
|
3934
|
+
if (attachment.localUri) {
|
|
3935
|
+
await this.localStorage.deleteFile(attachment.localUri);
|
|
3936
|
+
}
|
|
3937
|
+
await context.deleteAttachment(attachment.id);
|
|
3938
|
+
return {
|
|
3939
|
+
...attachment,
|
|
3940
|
+
state: AttachmentState.ARCHIVED
|
|
3941
|
+
};
|
|
3942
|
+
}
|
|
3943
|
+
catch (error) {
|
|
3944
|
+
const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
|
|
3945
|
+
if (!shouldRetry) {
|
|
3946
|
+
return {
|
|
3947
|
+
...attachment,
|
|
3948
|
+
state: AttachmentState.ARCHIVED
|
|
3949
|
+
};
|
|
3950
|
+
}
|
|
3951
|
+
return attachment;
|
|
3952
|
+
}
|
|
3911
3953
|
}
|
|
3912
3954
|
/**
|
|
3913
|
-
*
|
|
3955
|
+
* Performs cleanup of archived attachments by removing their local files and records.
|
|
3956
|
+
* Errors during local file deletion are logged but do not prevent record deletion.
|
|
3914
3957
|
*/
|
|
3915
|
-
async
|
|
3916
|
-
return
|
|
3917
|
-
|
|
3958
|
+
async deleteArchivedAttachments(context) {
|
|
3959
|
+
return await context.deleteArchivedAttachments(async (archivedAttachments) => {
|
|
3960
|
+
for (const attachment of archivedAttachments) {
|
|
3961
|
+
if (attachment.localUri) {
|
|
3962
|
+
try {
|
|
3963
|
+
await this.localStorage.deleteFile(attachment.localUri);
|
|
3964
|
+
}
|
|
3965
|
+
catch (error) {
|
|
3966
|
+
this.logger.error('Error deleting local file for archived attachment', error);
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3918
3970
|
});
|
|
3919
3971
|
}
|
|
3920
3972
|
}
|
|
@@ -3975,16 +4027,6 @@ class AttachmentQueue {
|
|
|
3975
4027
|
* Creates a new AttachmentQueue instance.
|
|
3976
4028
|
*
|
|
3977
4029
|
* @param options - Configuration options
|
|
3978
|
-
* @param options.db - PowerSync database instance
|
|
3979
|
-
* @param options.remoteStorage - Remote storage adapter for upload/download operations
|
|
3980
|
-
* @param options.localStorage - Local storage adapter for file persistence
|
|
3981
|
-
* @param options.watchAttachments - Callback for monitoring attachment changes in your data model
|
|
3982
|
-
* @param options.tableName - Name of the table to store attachment records. Default: 'ps_attachment_queue'
|
|
3983
|
-
* @param options.logger - Logger instance. Defaults to db.logger
|
|
3984
|
-
* @param options.syncIntervalMs - Periodic polling interval in milliseconds for retrying failed uploads/downloads. Default: 30000
|
|
3985
|
-
* @param options.syncThrottleDuration - Throttle duration in milliseconds for the reactive watch query that detects attachment changes. Prevents rapid-fire syncs during bulk changes. Default: 30
|
|
3986
|
-
* @param options.downloadAttachments - Whether to automatically download remote attachments. Default: true
|
|
3987
|
-
* @param options.archivedCacheLimit - Maximum archived attachments before cleanup. Default: 100
|
|
3988
4030
|
*/
|
|
3989
4031
|
constructor({ db, localStorage, remoteStorage, watchAttachments, logger, tableName = ATTACHMENT_TABLE, syncIntervalMs = 30 * 1000, syncThrottleDuration = DEFAULT_WATCH_THROTTLE_MS, downloadAttachments = true, archivedCacheLimit = 100, errorHandler }) {
|
|
3990
4032
|
this.db = db;
|
|
@@ -4073,6 +4115,7 @@ class AttachmentQueue {
|
|
|
4073
4115
|
state: AttachmentState.QUEUED_DOWNLOAD,
|
|
4074
4116
|
hasSynced: false,
|
|
4075
4117
|
metaData: watchedAttachment.metaData,
|
|
4118
|
+
mediaType: watchedAttachment.mediaType,
|
|
4076
4119
|
timestamp: new Date().getTime()
|
|
4077
4120
|
});
|
|
4078
4121
|
continue;
|
|
@@ -4160,17 +4203,24 @@ class AttachmentQueue {
|
|
|
4160
4203
|
this.statusListenerDispose = undefined;
|
|
4161
4204
|
}
|
|
4162
4205
|
}
|
|
4206
|
+
/**
|
|
4207
|
+
* Provides an {@link AttachmentContext} to a callback.
|
|
4208
|
+
*
|
|
4209
|
+
* The callback runs while the attachment queue mutex is held. Do not call
|
|
4210
|
+
* other {@link AttachmentQueue} methods from within the callback, as they may
|
|
4211
|
+
* attempt to acquire the same mutex and block indefinitely.
|
|
4212
|
+
*/
|
|
4213
|
+
withAttachmentContext(callback) {
|
|
4214
|
+
/**
|
|
4215
|
+
* AttachmentService is internal and private in this class.
|
|
4216
|
+
* We only need to expose its locking and context functionality for extending classes.
|
|
4217
|
+
*/
|
|
4218
|
+
return this.attachmentService.withContext(callback);
|
|
4219
|
+
}
|
|
4163
4220
|
/**
|
|
4164
4221
|
* Saves a file to local storage and queues it for upload to remote storage.
|
|
4165
4222
|
*
|
|
4166
4223
|
* @param options - File save options
|
|
4167
|
-
* @param options.data - The file data as ArrayBuffer, Blob, or base64 string
|
|
4168
|
-
* @param options.fileExtension - File extension (e.g., 'jpg', 'pdf')
|
|
4169
|
-
* @param options.mediaType - MIME type of the file (e.g., 'image/jpeg')
|
|
4170
|
-
* @param options.metaData - Optional metadata to associate with the attachment
|
|
4171
|
-
* @param options.id - Optional custom ID. If not provided, a UUID will be generated
|
|
4172
|
-
* @param options.updateHook - Optional callback to execute additional database operations
|
|
4173
|
-
* within the same transaction as the attachment creation
|
|
4174
4224
|
* @returns Promise resolving to the created attachment record
|
|
4175
4225
|
*/
|
|
4176
4226
|
async saveFile({ data, fileExtension, mediaType, metaData, id, updateHook }) {
|
|
@@ -4283,6 +4333,9 @@ class AttachmentQueue {
|
|
|
4283
4333
|
}
|
|
4284
4334
|
}
|
|
4285
4335
|
|
|
4336
|
+
/**
|
|
4337
|
+
* @alpha
|
|
4338
|
+
*/
|
|
4286
4339
|
var EncodingType;
|
|
4287
4340
|
(function (EncodingType) {
|
|
4288
4341
|
EncodingType["UTF8"] = "utf8";
|
|
@@ -4746,7 +4799,9 @@ var Logger = /*@__PURE__*/getDefaultExportFromCjs(loggerExports);
|
|
|
4746
4799
|
* different SQLite DB implementations.
|
|
4747
4800
|
*/
|
|
4748
4801
|
/**
|
|
4749
|
-
* Implements {@link DBGetUtils} on a {@link
|
|
4802
|
+
* Implements {@link DBGetUtils} on a {@link SqlExecutor}.
|
|
4803
|
+
*
|
|
4804
|
+
* @internal
|
|
4750
4805
|
*/
|
|
4751
4806
|
function DBGetUtilsDefaultMixin(Base) {
|
|
4752
4807
|
return class extends Base {
|
|
@@ -4790,6 +4845,8 @@ function DBGetUtilsDefaultMixin(Base) {
|
|
|
4790
4845
|
}
|
|
4791
4846
|
/**
|
|
4792
4847
|
* Update table operation numbers from SQLite
|
|
4848
|
+
*
|
|
4849
|
+
* @public
|
|
4793
4850
|
*/
|
|
4794
4851
|
var RowUpdateType;
|
|
4795
4852
|
(function (RowUpdateType) {
|
|
@@ -4798,8 +4855,10 @@ var RowUpdateType;
|
|
|
4798
4855
|
RowUpdateType[RowUpdateType["SQLITE_UPDATE"] = 23] = "SQLITE_UPDATE";
|
|
4799
4856
|
})(RowUpdateType || (RowUpdateType = {}));
|
|
4800
4857
|
/**
|
|
4801
|
-
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool
|
|
4802
|
-
* {@link ConnectionPool
|
|
4858
|
+
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool#readLock} and
|
|
4859
|
+
* {@link ConnectionPool#writeLock}.
|
|
4860
|
+
*
|
|
4861
|
+
* @internal
|
|
4803
4862
|
*/
|
|
4804
4863
|
function DBAdapterDefaultMixin(Base) {
|
|
4805
4864
|
return class extends Base {
|
|
@@ -4887,9 +4946,15 @@ class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction)
|
|
|
4887
4946
|
}
|
|
4888
4947
|
}
|
|
4889
4948
|
}
|
|
4949
|
+
/**
|
|
4950
|
+
* @internal
|
|
4951
|
+
*/
|
|
4890
4952
|
function isBatchedUpdateNotification(update) {
|
|
4891
4953
|
return 'tables' in update;
|
|
4892
4954
|
}
|
|
4955
|
+
/**
|
|
4956
|
+
* @internal
|
|
4957
|
+
*/
|
|
4893
4958
|
function extractTableUpdates(update) {
|
|
4894
4959
|
return isBatchedUpdateNotification(update) ? update.tables : [update.table];
|
|
4895
4960
|
}
|
|
@@ -4917,6 +4982,8 @@ const FULL_SYNC_PRIORITY = 2147483647;
|
|
|
4917
4982
|
*
|
|
4918
4983
|
* Also note that data is downloaded in bulk, which means that individual counters are unlikely
|
|
4919
4984
|
* to be updated one-by-one.
|
|
4985
|
+
*
|
|
4986
|
+
* @public
|
|
4920
4987
|
*/
|
|
4921
4988
|
class SyncProgress {
|
|
4922
4989
|
internal;
|
|
@@ -4955,6 +5022,9 @@ class SyncProgress {
|
|
|
4955
5022
|
}
|
|
4956
5023
|
}
|
|
4957
5024
|
|
|
5025
|
+
/**
|
|
5026
|
+
* @public
|
|
5027
|
+
*/
|
|
4958
5028
|
class SyncStatus {
|
|
4959
5029
|
options;
|
|
4960
5030
|
constructor(options) {
|
|
@@ -4965,6 +5035,8 @@ class SyncStatus {
|
|
|
4965
5035
|
* implementation).
|
|
4966
5036
|
*
|
|
4967
5037
|
* This information is only available after a connection has been requested.
|
|
5038
|
+
*
|
|
5039
|
+
* @deprecated This always returns the Rust client (the only option).
|
|
4968
5040
|
*/
|
|
4969
5041
|
get clientImplementation() {
|
|
4970
5042
|
return this.options.clientImplementation;
|
|
@@ -4972,7 +5044,7 @@ class SyncStatus {
|
|
|
4972
5044
|
/**
|
|
4973
5045
|
* Indicates if the client is currently connected to the PowerSync service.
|
|
4974
5046
|
*
|
|
4975
|
-
* @returns
|
|
5047
|
+
* @returns True if connected, false otherwise. Defaults to false if not specified.
|
|
4976
5048
|
*/
|
|
4977
5049
|
get connected() {
|
|
4978
5050
|
return this.options.connected ?? false;
|
|
@@ -4980,7 +5052,7 @@ class SyncStatus {
|
|
|
4980
5052
|
/**
|
|
4981
5053
|
* Indicates if the client is in the process of establishing a connection to the PowerSync service.
|
|
4982
5054
|
*
|
|
4983
|
-
* @returns
|
|
5055
|
+
* @returns True if connecting, false otherwise. Defaults to false if not specified.
|
|
4984
5056
|
*/
|
|
4985
5057
|
get connecting() {
|
|
4986
5058
|
return this.options.connecting ?? false;
|
|
@@ -4989,7 +5061,7 @@ class SyncStatus {
|
|
|
4989
5061
|
* Time that a last sync has fully completed, if any.
|
|
4990
5062
|
* This timestamp is reset to null after a restart of the PowerSync service.
|
|
4991
5063
|
*
|
|
4992
|
-
* @returns
|
|
5064
|
+
* @returns The timestamp of the last successful sync, or undefined if no sync has completed.
|
|
4993
5065
|
*/
|
|
4994
5066
|
get lastSyncedAt() {
|
|
4995
5067
|
return this.options.lastSyncedAt;
|
|
@@ -4997,7 +5069,7 @@ class SyncStatus {
|
|
|
4997
5069
|
/**
|
|
4998
5070
|
* Indicates whether there has been at least one full sync completed since initialization.
|
|
4999
5071
|
*
|
|
5000
|
-
* @returns
|
|
5072
|
+
* @returns True if at least one sync has completed, false if no sync has completed,
|
|
5001
5073
|
* or undefined when the state is still being loaded from the database.
|
|
5002
5074
|
*/
|
|
5003
5075
|
get hasSynced() {
|
|
@@ -5006,10 +5078,10 @@ class SyncStatus {
|
|
|
5006
5078
|
/**
|
|
5007
5079
|
* Provides the current data flow status regarding uploads and downloads.
|
|
5008
5080
|
*
|
|
5009
|
-
* @returns
|
|
5081
|
+
* @returns An object containing:
|
|
5010
5082
|
* - downloading: True if actively downloading changes (only when connected is also true)
|
|
5011
5083
|
* - uploading: True if actively uploading changes
|
|
5012
|
-
* Defaults to {downloading: false, uploading: false} if not specified.
|
|
5084
|
+
* Defaults to `{downloading: false, uploading: false}` if not specified.
|
|
5013
5085
|
*/
|
|
5014
5086
|
get dataFlowStatus() {
|
|
5015
5087
|
return (this.options.dataFlow ?? {
|
|
@@ -5034,7 +5106,7 @@ class SyncStatus {
|
|
|
5034
5106
|
return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
|
|
5035
5107
|
}
|
|
5036
5108
|
/**
|
|
5037
|
-
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
5109
|
+
* If the `stream` appears in {@link SyncStatus.syncStreams}, returns the current status for that stream.
|
|
5038
5110
|
*/
|
|
5039
5111
|
forStream(stream) {
|
|
5040
5112
|
const asJson = JSON.stringify(stream.parameters);
|
|
@@ -5044,7 +5116,7 @@ class SyncStatus {
|
|
|
5044
5116
|
/**
|
|
5045
5117
|
* Provides sync status information for all bucket priorities, sorted by priority (highest first).
|
|
5046
5118
|
*
|
|
5047
|
-
* @returns
|
|
5119
|
+
* @returns An array of status entries for different sync priority levels,
|
|
5048
5120
|
* sorted with highest priorities (lower numbers) first.
|
|
5049
5121
|
*/
|
|
5050
5122
|
get priorityStatusEntries() {
|
|
@@ -5079,8 +5151,8 @@ class SyncStatus {
|
|
|
5079
5151
|
* For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
|
|
5080
5152
|
* with a priority of 1 may return information for priority level 3.
|
|
5081
5153
|
*
|
|
5082
|
-
* @param
|
|
5083
|
-
* @returns
|
|
5154
|
+
* @param priority - The bucket priority for which the status should be reported
|
|
5155
|
+
* @returns Status information for the requested priority level or the next higher level with available status
|
|
5084
5156
|
*/
|
|
5085
5157
|
statusForPriority(priority) {
|
|
5086
5158
|
// priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
|
|
@@ -5101,8 +5173,8 @@ class SyncStatus {
|
|
|
5101
5173
|
* Compares this SyncStatus instance with another to determine if they are equal.
|
|
5102
5174
|
* Equality is determined by comparing the serialized JSON representation of both instances.
|
|
5103
5175
|
*
|
|
5104
|
-
* @param
|
|
5105
|
-
* @returns
|
|
5176
|
+
* @param status - The SyncStatus instance to compare against
|
|
5177
|
+
* @returns True if the instances are considered equal, false otherwise
|
|
5106
5178
|
*/
|
|
5107
5179
|
isEqual(status) {
|
|
5108
5180
|
/**
|
|
@@ -5125,7 +5197,7 @@ class SyncStatus {
|
|
|
5125
5197
|
* Creates a human-readable string representation of the current sync status.
|
|
5126
5198
|
* Includes information about connection state, sync completion, and data flow.
|
|
5127
5199
|
*
|
|
5128
|
-
* @returns
|
|
5200
|
+
* @returns A string representation of the sync status
|
|
5129
5201
|
*/
|
|
5130
5202
|
getMessage() {
|
|
5131
5203
|
const dataFlow = this.dataFlowStatus;
|
|
@@ -5134,7 +5206,7 @@ class SyncStatus {
|
|
|
5134
5206
|
/**
|
|
5135
5207
|
* Serializes the SyncStatus instance to a plain object.
|
|
5136
5208
|
*
|
|
5137
|
-
* @returns
|
|
5209
|
+
* @returns A plain object representation of the sync status
|
|
5138
5210
|
*/
|
|
5139
5211
|
toJSON() {
|
|
5140
5212
|
return {
|
|
@@ -5200,6 +5272,9 @@ class SyncStreamStatusView {
|
|
|
5200
5272
|
}
|
|
5201
5273
|
}
|
|
5202
5274
|
|
|
5275
|
+
/**
|
|
5276
|
+
* @public
|
|
5277
|
+
*/
|
|
5203
5278
|
class UploadQueueStats {
|
|
5204
5279
|
count;
|
|
5205
5280
|
size;
|
|
@@ -5225,6 +5300,9 @@ class UploadQueueStats {
|
|
|
5225
5300
|
}
|
|
5226
5301
|
}
|
|
5227
5302
|
|
|
5303
|
+
/**
|
|
5304
|
+
* @internal
|
|
5305
|
+
*/
|
|
5228
5306
|
class BaseObserver {
|
|
5229
5307
|
listeners = new Set();
|
|
5230
5308
|
constructor() { }
|
|
@@ -5252,6 +5330,9 @@ class BaseObserver {
|
|
|
5252
5330
|
}
|
|
5253
5331
|
}
|
|
5254
5332
|
|
|
5333
|
+
/**
|
|
5334
|
+
* @internal
|
|
5335
|
+
*/
|
|
5255
5336
|
class ControlledExecutor {
|
|
5256
5337
|
task;
|
|
5257
5338
|
/**
|
|
@@ -5321,30 +5402,44 @@ function throttleTrailing(func, wait) {
|
|
|
5321
5402
|
}
|
|
5322
5403
|
};
|
|
5323
5404
|
}
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5405
|
+
function asyncNotifier() {
|
|
5406
|
+
let waitingConsumer = null;
|
|
5407
|
+
let hasPendingNotification = false;
|
|
5408
|
+
return {
|
|
5409
|
+
notify() {
|
|
5410
|
+
if (waitingConsumer != null) {
|
|
5411
|
+
waitingConsumer();
|
|
5412
|
+
waitingConsumer = null;
|
|
5413
|
+
}
|
|
5414
|
+
else {
|
|
5415
|
+
hasPendingNotification = true;
|
|
5416
|
+
}
|
|
5417
|
+
},
|
|
5418
|
+
waitForNotification(signal) {
|
|
5419
|
+
return new Promise((resolve) => {
|
|
5420
|
+
if (waitingConsumer != null) {
|
|
5421
|
+
throw new Error('Illegal call to waitForNotification, already has a waiter.');
|
|
5422
|
+
}
|
|
5423
|
+
if (signal.aborted) {
|
|
5424
|
+
resolve();
|
|
5425
|
+
}
|
|
5426
|
+
else if (hasPendingNotification) {
|
|
5427
|
+
resolve();
|
|
5428
|
+
hasPendingNotification = false;
|
|
5429
|
+
}
|
|
5430
|
+
else {
|
|
5431
|
+
function complete() {
|
|
5432
|
+
signal.removeEventListener('abort', onAbort);
|
|
5433
|
+
resolve();
|
|
5434
|
+
}
|
|
5435
|
+
function onAbort() {
|
|
5436
|
+
waitingConsumer = null;
|
|
5437
|
+
resolve();
|
|
5438
|
+
}
|
|
5439
|
+
waitingConsumer = complete;
|
|
5440
|
+
signal.addEventListener('abort', onAbort);
|
|
5441
|
+
}
|
|
5442
|
+
});
|
|
5348
5443
|
}
|
|
5349
5444
|
};
|
|
5350
5445
|
}
|
|
@@ -5505,7 +5600,7 @@ class ConnectionManager extends BaseObserver {
|
|
|
5505
5600
|
/**
|
|
5506
5601
|
* Close the sync connection.
|
|
5507
5602
|
*
|
|
5508
|
-
* Use {@link connect} to connect again.
|
|
5603
|
+
* Use {@link ConnectionManager.connect} to connect again.
|
|
5509
5604
|
*/
|
|
5510
5605
|
async disconnect() {
|
|
5511
5606
|
// This will help abort pending connects
|
|
@@ -5645,6 +5740,8 @@ const _finalizer = 'FinalizationRegistry' in globalThis
|
|
|
5645
5740
|
/**
|
|
5646
5741
|
* An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
|
|
5647
5742
|
* result has changes without necessarily processing all items in the result.
|
|
5743
|
+
*
|
|
5744
|
+
* @public
|
|
5648
5745
|
*/
|
|
5649
5746
|
class ArrayComparator {
|
|
5650
5747
|
options;
|
|
@@ -5672,6 +5769,8 @@ class ArrayComparator {
|
|
|
5672
5769
|
}
|
|
5673
5770
|
/**
|
|
5674
5771
|
* Watched query comparator that always reports changed result sets.
|
|
5772
|
+
*
|
|
5773
|
+
* @public
|
|
5675
5774
|
*/
|
|
5676
5775
|
const FalsyComparator = {
|
|
5677
5776
|
checkEquality: () => false // Default comparator that always returns false
|
|
@@ -5879,6 +5978,8 @@ class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
5879
5978
|
/**
|
|
5880
5979
|
* An empty differential result set.
|
|
5881
5980
|
* This is used as the initial state for differential incrementally watched queries.
|
|
5981
|
+
*
|
|
5982
|
+
* @internal
|
|
5882
5983
|
*/
|
|
5883
5984
|
const EMPTY_DIFFERENTIAL = {
|
|
5884
5985
|
added: [],
|
|
@@ -5891,6 +5992,8 @@ const EMPTY_DIFFERENTIAL = {
|
|
|
5891
5992
|
* Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
|
|
5892
5993
|
* It keys items by their `id` property if available, alternatively it uses JSON stringification
|
|
5893
5994
|
* of the entire item for the key and comparison.
|
|
5995
|
+
*
|
|
5996
|
+
* @internal
|
|
5894
5997
|
*/
|
|
5895
5998
|
const DEFAULT_ROW_COMPARATOR = {
|
|
5896
5999
|
keyBy: (item) => {
|
|
@@ -6171,6 +6274,8 @@ class CustomQuery {
|
|
|
6171
6274
|
|
|
6172
6275
|
/**
|
|
6173
6276
|
* Tests if the input is a {@link SQLOpenOptions}
|
|
6277
|
+
*
|
|
6278
|
+
* @internal
|
|
6174
6279
|
*/
|
|
6175
6280
|
const isSQLOpenOptions = (test) => {
|
|
6176
6281
|
// typeof null is `object`, but you cannot use the `in` operator on `null.
|
|
@@ -6178,17 +6283,24 @@ const isSQLOpenOptions = (test) => {
|
|
|
6178
6283
|
};
|
|
6179
6284
|
/**
|
|
6180
6285
|
* Tests if input is a {@link SQLOpenFactory}
|
|
6286
|
+
*
|
|
6287
|
+
* @internal
|
|
6181
6288
|
*/
|
|
6182
6289
|
const isSQLOpenFactory = (test) => {
|
|
6183
6290
|
return typeof test?.openDB == 'function';
|
|
6184
6291
|
};
|
|
6185
6292
|
/**
|
|
6186
6293
|
* Tests if input is a {@link DBAdapter}
|
|
6294
|
+
*
|
|
6295
|
+
* @internal
|
|
6187
6296
|
*/
|
|
6188
6297
|
const isDBAdapter = (test) => {
|
|
6189
6298
|
return typeof test?.writeTransaction == 'function';
|
|
6190
6299
|
};
|
|
6191
6300
|
|
|
6301
|
+
/**
|
|
6302
|
+
* @internal
|
|
6303
|
+
*/
|
|
6192
6304
|
var PSInternalTable;
|
|
6193
6305
|
(function (PSInternalTable) {
|
|
6194
6306
|
PSInternalTable["DATA"] = "ps_data";
|
|
@@ -6197,6 +6309,9 @@ var PSInternalTable;
|
|
|
6197
6309
|
PSInternalTable["OPLOG"] = "ps_oplog";
|
|
6198
6310
|
PSInternalTable["UNTYPED"] = "ps_untyped";
|
|
6199
6311
|
})(PSInternalTable || (PSInternalTable = {}));
|
|
6312
|
+
/**
|
|
6313
|
+
* @internal
|
|
6314
|
+
*/
|
|
6200
6315
|
var PowerSyncControlCommand;
|
|
6201
6316
|
(function (PowerSyncControlCommand) {
|
|
6202
6317
|
PowerSyncControlCommand["PROCESS_TEXT_LINE"] = "line_text";
|
|
@@ -6214,6 +6329,8 @@ var PowerSyncControlCommand;
|
|
|
6214
6329
|
|
|
6215
6330
|
/**
|
|
6216
6331
|
* A batch of client-side changes.
|
|
6332
|
+
*
|
|
6333
|
+
* @public
|
|
6217
6334
|
*/
|
|
6218
6335
|
class CrudBatch {
|
|
6219
6336
|
crud;
|
|
@@ -6240,6 +6357,8 @@ class CrudBatch {
|
|
|
6240
6357
|
|
|
6241
6358
|
/**
|
|
6242
6359
|
* Type of local change.
|
|
6360
|
+
*
|
|
6361
|
+
* @public
|
|
6243
6362
|
*/
|
|
6244
6363
|
var UpdateType;
|
|
6245
6364
|
(function (UpdateType) {
|
|
@@ -6252,6 +6371,8 @@ var UpdateType;
|
|
|
6252
6371
|
})(UpdateType || (UpdateType = {}));
|
|
6253
6372
|
/**
|
|
6254
6373
|
* A single client-side change.
|
|
6374
|
+
*
|
|
6375
|
+
* @public
|
|
6255
6376
|
*/
|
|
6256
6377
|
class CrudEntry {
|
|
6257
6378
|
/**
|
|
@@ -6348,6 +6469,9 @@ class CrudEntry {
|
|
|
6348
6469
|
}
|
|
6349
6470
|
}
|
|
6350
6471
|
|
|
6472
|
+
/**
|
|
6473
|
+
* @public
|
|
6474
|
+
*/
|
|
6351
6475
|
class CrudTransaction extends CrudBatch {
|
|
6352
6476
|
crud;
|
|
6353
6477
|
complete;
|
|
@@ -6376,6 +6500,8 @@ class CrudTransaction extends CrudBatch {
|
|
|
6376
6500
|
* Calls to Abortcontroller.abort(reason: any) will result in the
|
|
6377
6501
|
* `reason` being thrown. This is not necessarily an error,
|
|
6378
6502
|
* but extends error for better logging purposes.
|
|
6503
|
+
*
|
|
6504
|
+
* @internal
|
|
6379
6505
|
*/
|
|
6380
6506
|
class AbortOperation extends Error {
|
|
6381
6507
|
reason;
|
|
@@ -13546,7 +13672,7 @@ function requireDist () {
|
|
|
13546
13672
|
|
|
13547
13673
|
var distExports = requireDist();
|
|
13548
13674
|
|
|
13549
|
-
var version = "1.
|
|
13675
|
+
var version = "1.54.0";
|
|
13550
13676
|
var PACKAGE = {
|
|
13551
13677
|
version: version};
|
|
13552
13678
|
|
|
@@ -13676,7 +13802,8 @@ class WebsocketClientTransport {
|
|
|
13676
13802
|
removeListeners();
|
|
13677
13803
|
resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
|
|
13678
13804
|
};
|
|
13679
|
-
const errorListener = (
|
|
13805
|
+
const errorListener = (event) => {
|
|
13806
|
+
const ev = event;
|
|
13680
13807
|
removeListeners();
|
|
13681
13808
|
// We add a default error in that case.
|
|
13682
13809
|
if (ev.error != null) {
|
|
@@ -13919,7 +14046,13 @@ const SOCKET_TIMEOUT_MS = 30_000;
|
|
|
13919
14046
|
// If there is a backlog of messages (for example on slow connections), keepalive messages could be delayed
|
|
13920
14047
|
// significantly. Therefore this is longer than the socket timeout.
|
|
13921
14048
|
const KEEP_ALIVE_LIFETIME_MS = 90_000;
|
|
14049
|
+
/**
|
|
14050
|
+
* @internal
|
|
14051
|
+
*/
|
|
13922
14052
|
const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
|
|
14053
|
+
/**
|
|
14054
|
+
* @public
|
|
14055
|
+
*/
|
|
13923
14056
|
var FetchStrategy;
|
|
13924
14057
|
(function (FetchStrategy) {
|
|
13925
14058
|
/**
|
|
@@ -13938,12 +14071,17 @@ var FetchStrategy;
|
|
|
13938
14071
|
* The class wrapper is used to distinguish the fetchImplementation
|
|
13939
14072
|
* option in [AbstractRemoteOptions] from the general fetch method
|
|
13940
14073
|
* which is typeof "function"
|
|
14074
|
+
*
|
|
14075
|
+
* @internal
|
|
13941
14076
|
*/
|
|
13942
14077
|
class FetchImplementationProvider {
|
|
13943
14078
|
getFetch() {
|
|
13944
14079
|
throw new Error('Unspecified fetch implementation');
|
|
13945
14080
|
}
|
|
13946
14081
|
}
|
|
14082
|
+
/**
|
|
14083
|
+
* @internal
|
|
14084
|
+
*/
|
|
13947
14085
|
const DEFAULT_REMOTE_OPTIONS = {
|
|
13948
14086
|
socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
|
|
13949
14087
|
return match === 'https://' ? 'wss://' : 'ws://';
|
|
@@ -13951,6 +14089,9 @@ const DEFAULT_REMOTE_OPTIONS = {
|
|
|
13951
14089
|
fetchImplementation: new FetchImplementationProvider(),
|
|
13952
14090
|
fetchOptions: {}
|
|
13953
14091
|
};
|
|
14092
|
+
/**
|
|
14093
|
+
* @internal
|
|
14094
|
+
*/
|
|
13954
14095
|
class AbstractRemote {
|
|
13955
14096
|
connector;
|
|
13956
14097
|
logger;
|
|
@@ -14360,7 +14501,7 @@ class AbstractRemote {
|
|
|
14360
14501
|
* Posts a `/sync/stream` request.
|
|
14361
14502
|
*
|
|
14362
14503
|
* Depending on the `Content-Type` of the response, this returns strings for sync lines or encoded BSON documents as
|
|
14363
|
-
*
|
|
14504
|
+
* `Uint8Array`s.
|
|
14364
14505
|
*/
|
|
14365
14506
|
async fetchStream(options) {
|
|
14366
14507
|
const { isBson, stream } = await this.fetchStreamRaw(options);
|
|
@@ -14402,16 +14543,26 @@ function isInterruptingInstruction(instruction) {
|
|
|
14402
14543
|
return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
|
|
14403
14544
|
}
|
|
14404
14545
|
|
|
14546
|
+
/**
|
|
14547
|
+
* @internal
|
|
14548
|
+
*/
|
|
14405
14549
|
var LockType;
|
|
14406
14550
|
(function (LockType) {
|
|
14407
14551
|
LockType["CRUD"] = "crud";
|
|
14408
14552
|
LockType["SYNC"] = "sync";
|
|
14409
14553
|
})(LockType || (LockType = {}));
|
|
14554
|
+
/**
|
|
14555
|
+
* @public
|
|
14556
|
+
*/
|
|
14410
14557
|
var SyncStreamConnectionMethod;
|
|
14411
14558
|
(function (SyncStreamConnectionMethod) {
|
|
14412
14559
|
SyncStreamConnectionMethod["HTTP"] = "http";
|
|
14413
14560
|
SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
|
|
14414
14561
|
})(SyncStreamConnectionMethod || (SyncStreamConnectionMethod = {}));
|
|
14562
|
+
/**
|
|
14563
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
14564
|
+
* @public
|
|
14565
|
+
*/
|
|
14415
14566
|
var SyncClientImplementation;
|
|
14416
14567
|
(function (SyncClientImplementation) {
|
|
14417
14568
|
/**
|
|
@@ -14423,8 +14574,8 @@ var SyncClientImplementation;
|
|
|
14423
14574
|
* ## Compatibility warning
|
|
14424
14575
|
*
|
|
14425
14576
|
* The Rust sync client stores sync data in a format that is slightly different than the one used
|
|
14426
|
-
* by the old JavaScript client. When adopting the {@link RUST} client on existing databases,
|
|
14427
|
-
* migrate the format automatically.
|
|
14577
|
+
* by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
|
|
14578
|
+
* the PowerSync SDK will migrate the format automatically.
|
|
14428
14579
|
*
|
|
14429
14580
|
* SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
|
|
14430
14581
|
* implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
|
|
@@ -14434,14 +14585,29 @@ var SyncClientImplementation;
|
|
|
14434
14585
|
})(SyncClientImplementation || (SyncClientImplementation = {}));
|
|
14435
14586
|
/**
|
|
14436
14587
|
* The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
|
|
14588
|
+
*
|
|
14589
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
14590
|
+
* @public
|
|
14437
14591
|
*/
|
|
14438
14592
|
const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
|
|
14593
|
+
/**
|
|
14594
|
+
* @internal
|
|
14595
|
+
*/
|
|
14439
14596
|
const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
14597
|
+
/**
|
|
14598
|
+
* @internal
|
|
14599
|
+
*/
|
|
14440
14600
|
const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
14601
|
+
/**
|
|
14602
|
+
* @internal
|
|
14603
|
+
*/
|
|
14441
14604
|
const DEFAULT_STREAMING_SYNC_OPTIONS = {
|
|
14442
14605
|
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
14443
14606
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
14444
14607
|
};
|
|
14608
|
+
/**
|
|
14609
|
+
* @internal
|
|
14610
|
+
*/
|
|
14445
14611
|
const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
14446
14612
|
appMetadata: {},
|
|
14447
14613
|
connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
|
|
@@ -14451,22 +14617,21 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
|
14451
14617
|
serializedSchema: undefined,
|
|
14452
14618
|
includeDefaultStreams: true
|
|
14453
14619
|
};
|
|
14620
|
+
/**
|
|
14621
|
+
* @internal
|
|
14622
|
+
*/
|
|
14454
14623
|
class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
14455
14624
|
options;
|
|
14456
14625
|
abortController;
|
|
14457
|
-
// In rare cases, mostly for tests, uploads can be triggered without being properly connected.
|
|
14458
|
-
// This allows ensuring that all upload processes can be aborted.
|
|
14459
|
-
uploadAbortController;
|
|
14460
14626
|
crudUpdateListener;
|
|
14461
14627
|
streamingSyncPromise;
|
|
14462
14628
|
logger;
|
|
14463
14629
|
activeStreams;
|
|
14464
14630
|
connectionMayHaveChanged = false;
|
|
14465
|
-
|
|
14631
|
+
crudUploadNotifier = asyncNotifier();
|
|
14466
14632
|
notifyCompletedUploads;
|
|
14467
14633
|
handleActiveStreamsChange;
|
|
14468
14634
|
syncStatus;
|
|
14469
|
-
triggerCrudUpload;
|
|
14470
14635
|
constructor(options) {
|
|
14471
14636
|
super();
|
|
14472
14637
|
this.options = options;
|
|
@@ -14482,16 +14647,9 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
14482
14647
|
}
|
|
14483
14648
|
});
|
|
14484
14649
|
this.abortController = null;
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
}
|
|
14489
|
-
this.isUploadingCrud = true;
|
|
14490
|
-
this._uploadAllCrud().finally(() => {
|
|
14491
|
-
this.notifyCompletedUploads?.();
|
|
14492
|
-
this.isUploadingCrud = false;
|
|
14493
|
-
});
|
|
14494
|
-
}, this.options.crudUploadThrottleMs);
|
|
14650
|
+
}
|
|
14651
|
+
triggerCrudUpload() {
|
|
14652
|
+
this.crudUploadNotifier.notify();
|
|
14495
14653
|
}
|
|
14496
14654
|
async waitForReady() { }
|
|
14497
14655
|
waitForStatus(status) {
|
|
@@ -14539,7 +14697,6 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
14539
14697
|
super.dispose();
|
|
14540
14698
|
this.crudUpdateListener?.();
|
|
14541
14699
|
this.crudUpdateListener = undefined;
|
|
14542
|
-
this.uploadAbortController?.abort();
|
|
14543
14700
|
}
|
|
14544
14701
|
async getWriteCheckpoint() {
|
|
14545
14702
|
const clientId = await this.options.adapter.getClientId();
|
|
@@ -14549,7 +14706,17 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
14549
14706
|
this.logger.debug(`Created write checkpoint: ${checkpoint}`);
|
|
14550
14707
|
return checkpoint;
|
|
14551
14708
|
}
|
|
14552
|
-
async
|
|
14709
|
+
async crudUploadLoop(signal) {
|
|
14710
|
+
while (!signal.aborted) {
|
|
14711
|
+
await Promise.all([
|
|
14712
|
+
// Start the initial CRUD upload on connect. Then, keep polling until we're done.
|
|
14713
|
+
this._uploadAllCrud(signal),
|
|
14714
|
+
this.delayRetry(signal, this.options.crudUploadThrottleMs)
|
|
14715
|
+
]);
|
|
14716
|
+
await this.crudUploadNotifier.waitForNotification(signal);
|
|
14717
|
+
}
|
|
14718
|
+
}
|
|
14719
|
+
async _uploadAllCrud(signal) {
|
|
14553
14720
|
return this.obtainLock({
|
|
14554
14721
|
type: LockType.CRUD,
|
|
14555
14722
|
callback: async () => {
|
|
@@ -14557,12 +14724,7 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
14557
14724
|
* Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
|
|
14558
14725
|
*/
|
|
14559
14726
|
let checkedCrudItem;
|
|
14560
|
-
|
|
14561
|
-
this.uploadAbortController = controller;
|
|
14562
|
-
this.abortController?.signal.addEventListener('abort', () => {
|
|
14563
|
-
controller.abort();
|
|
14564
|
-
}, { once: true });
|
|
14565
|
-
while (!controller.signal.aborted) {
|
|
14727
|
+
while (!signal.aborted) {
|
|
14566
14728
|
try {
|
|
14567
14729
|
/**
|
|
14568
14730
|
* This is the first item in the FIFO CRUD queue.
|
|
@@ -14592,7 +14754,10 @@ The next upload iteration will be delayed.`);
|
|
|
14592
14754
|
else {
|
|
14593
14755
|
// Uploading is completed
|
|
14594
14756
|
const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
|
|
14595
|
-
if (neededUpdate
|
|
14757
|
+
if (neededUpdate) {
|
|
14758
|
+
this.notifyCompletedUploads?.();
|
|
14759
|
+
}
|
|
14760
|
+
else if (checkedCrudItem != null) {
|
|
14596
14761
|
// Only log this if there was something to upload
|
|
14597
14762
|
this.logger.debug('Upload complete, no write checkpoint needed.');
|
|
14598
14763
|
}
|
|
@@ -14607,7 +14772,7 @@ The next upload iteration will be delayed.`);
|
|
|
14607
14772
|
uploadError: ex
|
|
14608
14773
|
}
|
|
14609
14774
|
});
|
|
14610
|
-
await this.delayRetry(
|
|
14775
|
+
await this.delayRetry(signal);
|
|
14611
14776
|
if (!this.isConnected) {
|
|
14612
14777
|
// Exit the upload loop if the sync stream is no longer connected
|
|
14613
14778
|
break;
|
|
@@ -14622,7 +14787,6 @@ The next upload iteration will be delayed.`);
|
|
|
14622
14787
|
});
|
|
14623
14788
|
}
|
|
14624
14789
|
}
|
|
14625
|
-
this.uploadAbortController = undefined;
|
|
14626
14790
|
}
|
|
14627
14791
|
});
|
|
14628
14792
|
}
|
|
@@ -14632,7 +14796,10 @@ The next upload iteration will be delayed.`);
|
|
|
14632
14796
|
}
|
|
14633
14797
|
const controller = new AbortController();
|
|
14634
14798
|
this.abortController = controller;
|
|
14635
|
-
this.streamingSyncPromise =
|
|
14799
|
+
this.streamingSyncPromise = Promise.all([
|
|
14800
|
+
this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
|
|
14801
|
+
this.streamingSync(controller.signal, options)
|
|
14802
|
+
]);
|
|
14636
14803
|
// Return a promise that resolves when the connection status is updated to indicate that we're connected.
|
|
14637
14804
|
return new Promise((resolve) => {
|
|
14638
14805
|
const disposer = this.registerListener({
|
|
@@ -14670,14 +14837,7 @@ The next upload iteration will be delayed.`);
|
|
|
14670
14837
|
this.abortController = null;
|
|
14671
14838
|
this.updateSyncStatus({ connected: false, connecting: false });
|
|
14672
14839
|
}
|
|
14673
|
-
/**
|
|
14674
|
-
* @deprecated use [connect instead]
|
|
14675
|
-
*/
|
|
14676
14840
|
async streamingSync(signal, options) {
|
|
14677
|
-
if (!signal) {
|
|
14678
|
-
this.abortController = new AbortController();
|
|
14679
|
-
signal = this.abortController.signal;
|
|
14680
|
-
}
|
|
14681
14841
|
/**
|
|
14682
14842
|
* Listen for CRUD updates and trigger upstream uploads
|
|
14683
14843
|
*/
|
|
@@ -14783,7 +14943,7 @@ The next upload iteration will be delayed.`);
|
|
|
14783
14943
|
this.handleActiveStreamsChange?.();
|
|
14784
14944
|
}
|
|
14785
14945
|
/**
|
|
14786
|
-
* Older versions of the JS SDK used to encode subkeys as JSON in
|
|
14946
|
+
* Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
|
|
14787
14947
|
* Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
|
|
14788
14948
|
* While this is not a problem as long as it's done consistently, it causes issues when a database
|
|
14789
14949
|
* created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
|
|
@@ -14793,7 +14953,7 @@ The next upload iteration will be delayed.`);
|
|
|
14793
14953
|
* migration is only triggered when necessary (for now). The function returns whether the new format
|
|
14794
14954
|
* should be used, so that the JS SDK is able to write to updated databases.
|
|
14795
14955
|
*
|
|
14796
|
-
* @param requireFixedKeyFormat Whether we require the new format or also support the old one.
|
|
14956
|
+
* @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
|
|
14797
14957
|
* The Rust client requires the new subkey format.
|
|
14798
14958
|
* @returns Whether the database is now using the new, fixed subkey format.
|
|
14799
14959
|
*/
|
|
@@ -15051,14 +15211,13 @@ The next upload iteration will be delayed.`);
|
|
|
15051
15211
|
// trigger this for all updates
|
|
15052
15212
|
this.iterateListeners((cb) => cb.statusUpdated?.(options));
|
|
15053
15213
|
}
|
|
15054
|
-
async delayRetry(signal) {
|
|
15214
|
+
async delayRetry(signal, delay = this.options.retryDelayMs) {
|
|
15055
15215
|
return new Promise((resolve) => {
|
|
15056
15216
|
if (signal?.aborted) {
|
|
15057
15217
|
// If the signal is already aborted, resolve immediately
|
|
15058
15218
|
resolve();
|
|
15059
15219
|
return;
|
|
15060
15220
|
}
|
|
15061
|
-
const { retryDelayMs } = this.options;
|
|
15062
15221
|
let timeoutId;
|
|
15063
15222
|
const endDelay = () => {
|
|
15064
15223
|
resolve();
|
|
@@ -15069,7 +15228,7 @@ The next upload iteration will be delayed.`);
|
|
|
15069
15228
|
signal?.removeEventListener('abort', endDelay);
|
|
15070
15229
|
};
|
|
15071
15230
|
signal?.addEventListener('abort', endDelay, { once: true });
|
|
15072
|
-
timeoutId = setTimeout(endDelay,
|
|
15231
|
+
timeoutId = setTimeout(endDelay, delay);
|
|
15073
15232
|
});
|
|
15074
15233
|
}
|
|
15075
15234
|
updateSubscriptions(subscriptions) {
|
|
@@ -15101,7 +15260,8 @@ const MEMORY_TRIGGER_CLAIM_MANAGER = {
|
|
|
15101
15260
|
|
|
15102
15261
|
/**
|
|
15103
15262
|
* SQLite operations to track changes for with {@link TriggerManager}
|
|
15104
|
-
*
|
|
15263
|
+
*
|
|
15264
|
+
* @experimental @alpha
|
|
15105
15265
|
*/
|
|
15106
15266
|
var DiffTriggerOperation;
|
|
15107
15267
|
(function (DiffTriggerOperation) {
|
|
@@ -15163,8 +15323,8 @@ class TriggerManagerImpl {
|
|
|
15163
15323
|
get db() {
|
|
15164
15324
|
return this.options.db;
|
|
15165
15325
|
}
|
|
15166
|
-
async getUUID() {
|
|
15167
|
-
const { id: uuid } = await this.db.get(/* sql */ `
|
|
15326
|
+
async getUUID(ctx) {
|
|
15327
|
+
const { id: uuid } = await (ctx ?? this.db).get(/* sql */ `
|
|
15168
15328
|
SELECT
|
|
15169
15329
|
uuid () as id
|
|
15170
15330
|
`);
|
|
@@ -15277,7 +15437,7 @@ class TriggerManagerImpl {
|
|
|
15277
15437
|
const replicatedColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
|
|
15278
15438
|
const internalSource = sourceDefinition.internalName;
|
|
15279
15439
|
const triggerIds = [];
|
|
15280
|
-
const id = await this.getUUID();
|
|
15440
|
+
const id = await this.getUUID(setupContext);
|
|
15281
15441
|
const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
|
|
15282
15442
|
/**
|
|
15283
15443
|
* We default to replicating all columns if no columns array is provided.
|
|
@@ -15517,18 +15677,29 @@ const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
|
|
|
15517
15677
|
const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
|
|
15518
15678
|
clearLocal: true
|
|
15519
15679
|
};
|
|
15680
|
+
/**
|
|
15681
|
+
* @internal
|
|
15682
|
+
*/
|
|
15520
15683
|
const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
|
|
15521
15684
|
disconnect: true
|
|
15522
15685
|
};
|
|
15686
|
+
/**
|
|
15687
|
+
* @internal
|
|
15688
|
+
*/
|
|
15523
15689
|
const DEFAULT_POWERSYNC_DB_OPTIONS = {
|
|
15524
15690
|
retryDelayMs: 5000,
|
|
15525
15691
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
15526
15692
|
};
|
|
15693
|
+
/**
|
|
15694
|
+
* @internal
|
|
15695
|
+
*/
|
|
15527
15696
|
const DEFAULT_CRUD_BATCH_LIMIT = 100;
|
|
15528
15697
|
/**
|
|
15529
15698
|
* Requesting nested or recursive locks can block the application in some circumstances.
|
|
15530
15699
|
* This default lock timeout will act as a failsafe to throw an error if a lock cannot
|
|
15531
15700
|
* be obtained.
|
|
15701
|
+
*
|
|
15702
|
+
* @internal
|
|
15532
15703
|
*/
|
|
15533
15704
|
const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
15534
15705
|
/**
|
|
@@ -15538,6 +15709,9 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
|
15538
15709
|
const isPowerSyncDatabaseOptionsWithSettings = (test) => {
|
|
15539
15710
|
return typeof test == 'object' && isSQLOpenOptions(test.database);
|
|
15540
15711
|
};
|
|
15712
|
+
/**
|
|
15713
|
+
* @public
|
|
15714
|
+
*/
|
|
15541
15715
|
class AbstractPowerSyncDatabase extends BaseObserver {
|
|
15542
15716
|
options;
|
|
15543
15717
|
/**
|
|
@@ -15695,7 +15869,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15695
15869
|
/**
|
|
15696
15870
|
* Wait for the first sync operation to complete.
|
|
15697
15871
|
*
|
|
15698
|
-
* @param request Either an abort signal (after which the promise will complete regardless of
|
|
15872
|
+
* @param request - Either an abort signal (after which the promise will complete regardless of
|
|
15699
15873
|
* whether a full sync was completed) or an object providing an abort signal and a priority target.
|
|
15700
15874
|
* When a priority target is set, the promise may complete when all buckets with the given (or higher)
|
|
15701
15875
|
* priorities have been synchronized. This can be earlier than a complete sync.
|
|
@@ -15850,7 +16024,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15850
16024
|
/**
|
|
15851
16025
|
* Close the sync connection.
|
|
15852
16026
|
*
|
|
15853
|
-
* Use {@link connect} to connect again.
|
|
16027
|
+
* Use {@link AbstractPowerSyncDatabase.connect} to connect again.
|
|
15854
16028
|
*/
|
|
15855
16029
|
async disconnect() {
|
|
15856
16030
|
return this.connectionManager.disconnect();
|
|
@@ -15877,8 +16051,8 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15877
16051
|
/**
|
|
15878
16052
|
* Create a sync stream to query its status or to subscribe to it.
|
|
15879
16053
|
*
|
|
15880
|
-
* @param name The name of the stream to subscribe to.
|
|
15881
|
-
* @param params Optional parameters for the stream subscription.
|
|
16054
|
+
* @param name - The name of the stream to subscribe to.
|
|
16055
|
+
* @param params - Optional parameters for the stream subscription.
|
|
15882
16056
|
* @returns A {@link SyncStream} instance that can be subscribed to.
|
|
15883
16057
|
* @experimental Sync streams are currently in alpha.
|
|
15884
16058
|
*/
|
|
@@ -15936,14 +16110,14 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15936
16110
|
* Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
|
|
15937
16111
|
* requesting the next batch.
|
|
15938
16112
|
*
|
|
15939
|
-
* Use
|
|
16113
|
+
* Use the `limit` parameter to specify the maximum number of updates to return in a single
|
|
15940
16114
|
* batch.
|
|
15941
16115
|
*
|
|
15942
16116
|
* This method does include transaction ids in the result, but does not group
|
|
15943
16117
|
* data by transaction. One batch may contain data from multiple transactions,
|
|
15944
16118
|
* and a single transaction may be split over multiple batches.
|
|
15945
16119
|
*
|
|
15946
|
-
* @param limit Maximum number of CRUD entries to include in the batch
|
|
16120
|
+
* @param limit - Maximum number of CRUD entries to include in the batch
|
|
15947
16121
|
* @returns A batch of CRUD operations to upload, or null if there are none
|
|
15948
16122
|
*/
|
|
15949
16123
|
async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
|
|
@@ -15970,7 +16144,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15970
16144
|
* Once the data have been successfully uploaded, call {@link CrudTransaction.complete} before
|
|
15971
16145
|
* requesting the next transaction.
|
|
15972
16146
|
*
|
|
15973
|
-
* Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
|
|
16147
|
+
* Unlike {@link AbstractPowerSyncDatabase.getCrudBatch}, this only returns data from a single transaction at a time.
|
|
15974
16148
|
* All data for the transaction is loaded into memory.
|
|
15975
16149
|
*
|
|
15976
16150
|
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
@@ -15985,7 +16159,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15985
16159
|
* This is typically used from the {@link PowerSyncBackendConnector.uploadData} callback. Each entry emitted by the
|
|
15986
16160
|
* returned iterator is a full transaction containing all local writes made while that transaction was active.
|
|
15987
16161
|
*
|
|
15988
|
-
* Unlike {@link getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
16162
|
+
* Unlike {@link AbstractPowerSyncDatabase.getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
15989
16163
|
* {@link CrudTransaction.complete}d yet, this iterator can be used to receive multiple transactions. Calling
|
|
15990
16164
|
* {@link CrudTransaction.complete} will mark that and all prior transactions emitted by the iterator as completed.
|
|
15991
16165
|
*
|
|
@@ -16079,8 +16253,8 @@ SELECT * FROM crud_entries;
|
|
|
16079
16253
|
* the returned result's `rowsAffected` may be `0` for successful `UPDATE` and `DELETE` statements.
|
|
16080
16254
|
* Use a `RETURNING` clause and inspect `result.rows` when you need to confirm which rows changed.
|
|
16081
16255
|
*
|
|
16082
|
-
* @param sql The SQL query to execute
|
|
16083
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16256
|
+
* @param sql - The SQL query to execute
|
|
16257
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16084
16258
|
* @returns The query result as an object with structured key-value pairs
|
|
16085
16259
|
*/
|
|
16086
16260
|
async execute(sql, parameters) {
|
|
@@ -16090,8 +16264,8 @@ SELECT * FROM crud_entries;
|
|
|
16090
16264
|
* Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
|
|
16091
16265
|
* This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
|
|
16092
16266
|
*
|
|
16093
|
-
* @param sql The SQL query to execute
|
|
16094
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16267
|
+
* @param sql - The SQL query to execute
|
|
16268
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16095
16269
|
* @returns The raw query result from the underlying database as a nested array of raw values, where each row is
|
|
16096
16270
|
* represented as an array of column values without field names.
|
|
16097
16271
|
*/
|
|
@@ -16104,8 +16278,8 @@ SELECT * FROM crud_entries;
|
|
|
16104
16278
|
* and optionally return results.
|
|
16105
16279
|
* This is faster than executing separately with each parameter set.
|
|
16106
16280
|
*
|
|
16107
|
-
* @param sql The SQL query to execute
|
|
16108
|
-
* @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
16281
|
+
* @param sql - The SQL query to execute
|
|
16282
|
+
* @param parameters - Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
16109
16283
|
* @returns The query result
|
|
16110
16284
|
*/
|
|
16111
16285
|
async executeBatch(sql, parameters) {
|
|
@@ -16115,8 +16289,8 @@ SELECT * FROM crud_entries;
|
|
|
16115
16289
|
/**
|
|
16116
16290
|
* Execute a read-only query and return results.
|
|
16117
16291
|
*
|
|
16118
|
-
* @param sql The SQL query to execute
|
|
16119
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16292
|
+
* @param sql - The SQL query to execute
|
|
16293
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16120
16294
|
* @returns An array of results
|
|
16121
16295
|
*/
|
|
16122
16296
|
async getAll(sql, parameters) {
|
|
@@ -16126,8 +16300,8 @@ SELECT * FROM crud_entries;
|
|
|
16126
16300
|
/**
|
|
16127
16301
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
16128
16302
|
*
|
|
16129
|
-
* @param sql The SQL query to execute
|
|
16130
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16303
|
+
* @param sql - The SQL query to execute
|
|
16304
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16131
16305
|
* @returns The first result if found, or null if no results are returned
|
|
16132
16306
|
*/
|
|
16133
16307
|
async getOptional(sql, parameters) {
|
|
@@ -16137,8 +16311,8 @@ SELECT * FROM crud_entries;
|
|
|
16137
16311
|
/**
|
|
16138
16312
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
16139
16313
|
*
|
|
16140
|
-
* @param sql The SQL query to execute
|
|
16141
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16314
|
+
* @param sql - The SQL query to execute
|
|
16315
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16142
16316
|
* @returns The first result matching the query
|
|
16143
16317
|
* @throws Error if no rows are returned
|
|
16144
16318
|
*/
|
|
@@ -16148,7 +16322,7 @@ SELECT * FROM crud_entries;
|
|
|
16148
16322
|
}
|
|
16149
16323
|
/**
|
|
16150
16324
|
* Takes a read lock, without starting a transaction.
|
|
16151
|
-
* In most cases, {@link readTransaction} should be used instead.
|
|
16325
|
+
* In most cases, {@link AbstractPowerSyncDatabase.readTransaction} should be used instead.
|
|
16152
16326
|
*/
|
|
16153
16327
|
async readLock(callback) {
|
|
16154
16328
|
await this.waitForReady();
|
|
@@ -16156,7 +16330,7 @@ SELECT * FROM crud_entries;
|
|
|
16156
16330
|
}
|
|
16157
16331
|
/**
|
|
16158
16332
|
* Takes a global lock, without starting a transaction.
|
|
16159
|
-
* In most cases, {@link writeTransaction} should be used instead.
|
|
16333
|
+
* In most cases, {@link AbstractPowerSyncDatabase.writeTransaction} should be used instead.
|
|
16160
16334
|
*/
|
|
16161
16335
|
async writeLock(callback) {
|
|
16162
16336
|
await this.waitForReady();
|
|
@@ -16167,8 +16341,8 @@ SELECT * FROM crud_entries;
|
|
|
16167
16341
|
* Read transactions can run concurrently to a write transaction.
|
|
16168
16342
|
* Changes from any write transaction are not visible to read transactions started before it.
|
|
16169
16343
|
*
|
|
16170
|
-
* @param callback Function to execute within the transaction
|
|
16171
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
16344
|
+
* @param callback - Function to execute within the transaction
|
|
16345
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
16172
16346
|
* @returns The result of the callback
|
|
16173
16347
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
16174
16348
|
*/
|
|
@@ -16185,8 +16359,8 @@ SELECT * FROM crud_entries;
|
|
|
16185
16359
|
* This takes a global lock - only one write transaction can execute against the database at a time.
|
|
16186
16360
|
* Statements within the transaction must be done on the provided {@link Transaction} interface.
|
|
16187
16361
|
*
|
|
16188
|
-
* @param callback Function to execute within the transaction
|
|
16189
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
16362
|
+
* @param callback - Function to execute within the transaction
|
|
16363
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
16190
16364
|
* @returns The result of the callback
|
|
16191
16365
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
16192
16366
|
*/
|
|
@@ -16263,15 +16437,15 @@ SELECT * FROM crud_entries;
|
|
|
16263
16437
|
}
|
|
16264
16438
|
/**
|
|
16265
16439
|
* Execute a read query every time the source tables are modified.
|
|
16266
|
-
* Use {@link
|
|
16440
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
16267
16441
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
16268
16442
|
*
|
|
16269
16443
|
* Note that the `onChange` callback member of the handler is required.
|
|
16270
16444
|
*
|
|
16271
|
-
* @param sql The SQL query to execute
|
|
16272
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16273
|
-
* @param handler Callbacks for handling results and errors
|
|
16274
|
-
* @param options Options for configuring watch behavior
|
|
16445
|
+
* @param sql - The SQL query to execute
|
|
16446
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16447
|
+
* @param handler - Callbacks for handling results and errors
|
|
16448
|
+
* @param options - Options for configuring watch behavior
|
|
16275
16449
|
*/
|
|
16276
16450
|
watchWithCallback(sql, parameters, handler, options) {
|
|
16277
16451
|
const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
|
|
@@ -16284,7 +16458,7 @@ SELECT * FROM crud_entries;
|
|
|
16284
16458
|
const watchedQuery = new OnChangeQueryProcessor({
|
|
16285
16459
|
db: this,
|
|
16286
16460
|
comparator,
|
|
16287
|
-
placeholderData: null,
|
|
16461
|
+
placeholderData: null, // FIXME
|
|
16288
16462
|
watchOptions: {
|
|
16289
16463
|
query: {
|
|
16290
16464
|
compile: () => ({
|
|
@@ -16317,12 +16491,12 @@ SELECT * FROM crud_entries;
|
|
|
16317
16491
|
}
|
|
16318
16492
|
/**
|
|
16319
16493
|
* Execute a read query every time the source tables are modified.
|
|
16320
|
-
* Use {@link
|
|
16494
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
16321
16495
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
16322
16496
|
*
|
|
16323
|
-
* @param sql The SQL query to execute
|
|
16324
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16325
|
-
* @param options Options for configuring watch behavior
|
|
16497
|
+
* @param sql - The SQL query to execute
|
|
16498
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16499
|
+
* @param options - Options for configuring watch behavior
|
|
16326
16500
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
16327
16501
|
*/
|
|
16328
16502
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
@@ -16346,9 +16520,9 @@ SELECT * FROM crud_entries;
|
|
|
16346
16520
|
* If tables are specified in the options, those are used directly.
|
|
16347
16521
|
* Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
|
|
16348
16522
|
*
|
|
16349
|
-
* @param sql The SQL query to analyze
|
|
16350
|
-
* @param parameters Optional parameters for the SQL query
|
|
16351
|
-
* @param options Optional watch options that may contain explicit table list
|
|
16523
|
+
* @param sql - The SQL query to analyze
|
|
16524
|
+
* @param parameters - Optional parameters for the SQL query
|
|
16525
|
+
* @param options - Optional watch options that may contain explicit table list
|
|
16352
16526
|
* @returns Array of table names that the query depends on
|
|
16353
16527
|
*/
|
|
16354
16528
|
async resolveTables(sql, parameters, options) {
|
|
@@ -16377,13 +16551,13 @@ SELECT * FROM crud_entries;
|
|
|
16377
16551
|
/**
|
|
16378
16552
|
* Invoke the provided callback on any changes to any of the specified tables.
|
|
16379
16553
|
*
|
|
16380
|
-
* This is preferred over {@link watchWithCallback} when multiple queries need to be performed
|
|
16554
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithCallback} when multiple queries need to be performed
|
|
16381
16555
|
* together when data is changed.
|
|
16382
16556
|
*
|
|
16383
16557
|
* Note that the `onChange` callback member of the handler is required.
|
|
16384
16558
|
*
|
|
16385
|
-
* @param handler Callbacks for handling change events and errors
|
|
16386
|
-
* @param options Options for configuring watch behavior
|
|
16559
|
+
* @param handler - Callbacks for handling change events and errors
|
|
16560
|
+
* @param options - Options for configuring watch behavior
|
|
16387
16561
|
* @returns A dispose function to stop watching for changes
|
|
16388
16562
|
*/
|
|
16389
16563
|
onChangeWithCallback(handler, options) {
|
|
@@ -16426,12 +16600,12 @@ SELECT * FROM crud_entries;
|
|
|
16426
16600
|
/**
|
|
16427
16601
|
* Create a Stream of changes to any of the specified tables.
|
|
16428
16602
|
*
|
|
16429
|
-
* This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be
|
|
16430
|
-
* together when data is changed.
|
|
16603
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
16604
|
+
* performed together when data is changed.
|
|
16431
16605
|
*
|
|
16432
16606
|
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
16433
16607
|
*
|
|
16434
|
-
* @param options Options for configuring watch behavior
|
|
16608
|
+
* @param options - Options for configuring watch behavior
|
|
16435
16609
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
16436
16610
|
*/
|
|
16437
16611
|
onChangeWithAsyncGenerator(options) {
|
|
@@ -16469,15 +16643,15 @@ SELECT * FROM crud_entries;
|
|
|
16469
16643
|
changedTables.add(table);
|
|
16470
16644
|
}
|
|
16471
16645
|
}
|
|
16472
|
-
/**
|
|
16473
|
-
* @ignore
|
|
16474
|
-
*/
|
|
16475
16646
|
async executeReadOnly(sql, params) {
|
|
16476
16647
|
await this.waitForReady();
|
|
16477
16648
|
return this.database.readLock((tx) => tx.execute(sql, params));
|
|
16478
16649
|
}
|
|
16479
16650
|
}
|
|
16480
16651
|
|
|
16652
|
+
/**
|
|
16653
|
+
* @internal
|
|
16654
|
+
*/
|
|
16481
16655
|
class AbstractPowerSyncDatabaseOpenFactory {
|
|
16482
16656
|
options;
|
|
16483
16657
|
constructor(options) {
|
|
@@ -16502,6 +16676,9 @@ class AbstractPowerSyncDatabaseOpenFactory {
|
|
|
16502
16676
|
}
|
|
16503
16677
|
}
|
|
16504
16678
|
|
|
16679
|
+
/**
|
|
16680
|
+
* @internal
|
|
16681
|
+
*/
|
|
16505
16682
|
function runOnSchemaChange(callback, db, options) {
|
|
16506
16683
|
const triggerWatchedQuery = () => {
|
|
16507
16684
|
const abortController = new AbortController();
|
|
@@ -16526,6 +16703,9 @@ function runOnSchemaChange(callback, db, options) {
|
|
|
16526
16703
|
triggerWatchedQuery();
|
|
16527
16704
|
}
|
|
16528
16705
|
|
|
16706
|
+
/**
|
|
16707
|
+
* @public
|
|
16708
|
+
*/
|
|
16529
16709
|
function compilableQueryWatch(db, query, handler, options) {
|
|
16530
16710
|
const { onResult, onError = (e) => { } } = handler ?? {};
|
|
16531
16711
|
if (!onResult) {
|
|
@@ -16563,8 +16743,14 @@ function compilableQueryWatch(db, query, handler, options) {
|
|
|
16563
16743
|
runOnSchemaChange(watchQuery, db, options);
|
|
16564
16744
|
}
|
|
16565
16745
|
|
|
16746
|
+
/**
|
|
16747
|
+
* @internal
|
|
16748
|
+
*/
|
|
16566
16749
|
const MAX_OP_ID = '9223372036854775807';
|
|
16567
16750
|
|
|
16751
|
+
/**
|
|
16752
|
+
* @internal
|
|
16753
|
+
*/
|
|
16568
16754
|
class SqliteBucketStorage extends BaseObserver {
|
|
16569
16755
|
db;
|
|
16570
16756
|
logger;
|
|
@@ -16725,6 +16911,8 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
16725
16911
|
* Thrown when an underlying database connection is closed.
|
|
16726
16912
|
* This is particularly relevant when worker connections are marked as closed while
|
|
16727
16913
|
* operations are still in progress.
|
|
16914
|
+
*
|
|
16915
|
+
* @internal
|
|
16728
16916
|
*/
|
|
16729
16917
|
class ConnectionClosedError extends Error {
|
|
16730
16918
|
static NAME = 'ConnectionClosedError';
|
|
@@ -16744,6 +16932,8 @@ class ConnectionClosedError extends Error {
|
|
|
16744
16932
|
|
|
16745
16933
|
/**
|
|
16746
16934
|
* A schema is a collection of tables. It is used to define the structure of a database.
|
|
16935
|
+
*
|
|
16936
|
+
* @public
|
|
16747
16937
|
*/
|
|
16748
16938
|
class Schema {
|
|
16749
16939
|
/*
|
|
@@ -16780,7 +16970,7 @@ class Schema {
|
|
|
16780
16970
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
16781
16971
|
* using client-side table and column constraints.
|
|
16782
16972
|
*
|
|
16783
|
-
* @param tables An object of (table name, raw table definition) entries.
|
|
16973
|
+
* @param tables - An object of (table name, raw table definition) entries.
|
|
16784
16974
|
*/
|
|
16785
16975
|
withRawTables(tables) {
|
|
16786
16976
|
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
@@ -16826,6 +17016,8 @@ class Schema {
|
|
|
16826
17016
|
Generate a new table from the columns and indexes
|
|
16827
17017
|
@deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
|
|
16828
17018
|
This will be removed in the next major release.
|
|
17019
|
+
|
|
17020
|
+
@public
|
|
16829
17021
|
*/
|
|
16830
17022
|
class TableV2 extends Table {
|
|
16831
17023
|
}
|
|
@@ -16836,6 +17028,8 @@ function sanitizeString(input) {
|
|
|
16836
17028
|
/**
|
|
16837
17029
|
* Helper function for sanitizing UUID input strings.
|
|
16838
17030
|
* Typically used with {@link sanitizeSQL}.
|
|
17031
|
+
*
|
|
17032
|
+
* @alpha
|
|
16839
17033
|
*/
|
|
16840
17034
|
function sanitizeUUID(uuid) {
|
|
16841
17035
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -16872,6 +17066,8 @@ function sanitizeUUID(uuid) {
|
|
|
16872
17066
|
* // Incorrect:
|
|
16873
17067
|
* sanitizeSQL`New.id = '${myID}'` // Produces double quotes: New.id = ''O''Reilly''
|
|
16874
17068
|
* ```
|
|
17069
|
+
*
|
|
17070
|
+
* @alpha
|
|
16875
17071
|
*/
|
|
16876
17072
|
function sanitizeSQL(strings, ...values) {
|
|
16877
17073
|
let result = '';
|
|
@@ -16901,6 +17097,8 @@ function sanitizeSQL(strings, ...values) {
|
|
|
16901
17097
|
|
|
16902
17098
|
/**
|
|
16903
17099
|
* Performs a {@link AbstractPowerSyncDatabase.getAll} operation for a watched query.
|
|
17100
|
+
*
|
|
17101
|
+
* @public
|
|
16904
17102
|
*/
|
|
16905
17103
|
class GetAllQuery {
|
|
16906
17104
|
options;
|
|
@@ -16925,6 +17123,9 @@ class GetAllQuery {
|
|
|
16925
17123
|
}
|
|
16926
17124
|
|
|
16927
17125
|
const TypedLogger = Logger;
|
|
17126
|
+
/**
|
|
17127
|
+
* @public
|
|
17128
|
+
*/
|
|
16928
17129
|
const LogLevel = {
|
|
16929
17130
|
TRACE: TypedLogger.TRACE,
|
|
16930
17131
|
DEBUG: TypedLogger.DEBUG,
|
|
@@ -16941,6 +17142,7 @@ const LogLevel = {
|
|
|
16941
17142
|
* across all loggers created with `createLogger`. Adjusting settings on this
|
|
16942
17143
|
* base logger affects all loggers derived from it unless explicitly overridden.
|
|
16943
17144
|
*
|
|
17145
|
+
* @public
|
|
16944
17146
|
*/
|
|
16945
17147
|
function createBaseLogger() {
|
|
16946
17148
|
return Logger;
|
|
@@ -16951,6 +17153,8 @@ function createBaseLogger() {
|
|
|
16951
17153
|
* Named loggers allow specific modules or areas of your application to have
|
|
16952
17154
|
* their own logging levels and behaviors. These loggers inherit configuration
|
|
16953
17155
|
* from the base logger by default but can override settings independently.
|
|
17156
|
+
*
|
|
17157
|
+
* @public
|
|
16954
17158
|
*/
|
|
16955
17159
|
function createLogger(name, options = {}) {
|
|
16956
17160
|
const logger = Logger.get(name);
|
|
@@ -16960,6 +17164,9 @@ function createLogger(name, options = {}) {
|
|
|
16960
17164
|
return logger;
|
|
16961
17165
|
}
|
|
16962
17166
|
|
|
17167
|
+
/**
|
|
17168
|
+
* @internal
|
|
17169
|
+
*/
|
|
16963
17170
|
const parseQuery = (query, parameters) => {
|
|
16964
17171
|
let sqlStatement;
|
|
16965
17172
|
if (typeof query == 'string') {
|