@powersync/web 1.38.2 → 1.38.4
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 +921 -771
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +924 -774
- 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;
|
|
@@ -3919,6 +3807,170 @@ class AttachmentService {
|
|
|
3919
3807
|
}
|
|
3920
3808
|
}
|
|
3921
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
|
|
3930
|
+
*/
|
|
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
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
/**
|
|
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.
|
|
3957
|
+
*/
|
|
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
|
+
}
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
|
|
3922
3974
|
/**
|
|
3923
3975
|
* AttachmentQueue manages the lifecycle and synchronization of attachments
|
|
3924
3976
|
* between local and remote storage.
|
|
@@ -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,170 +4333,18 @@ 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";
|
|
4289
4342
|
EncodingType["Base64"] = "base64";
|
|
4290
4343
|
})(EncodingType || (EncodingType = {}));
|
|
4291
4344
|
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
4296
|
-
}
|
|
4297
|
-
|
|
4298
|
-
var dom = {};
|
|
4299
|
-
|
|
4300
|
-
var eventIterator = {};
|
|
4301
|
-
|
|
4302
|
-
var hasRequiredEventIterator;
|
|
4303
|
-
|
|
4304
|
-
function requireEventIterator () {
|
|
4305
|
-
if (hasRequiredEventIterator) return eventIterator;
|
|
4306
|
-
hasRequiredEventIterator = 1;
|
|
4307
|
-
Object.defineProperty(eventIterator, "__esModule", { value: true });
|
|
4308
|
-
class EventQueue {
|
|
4309
|
-
constructor() {
|
|
4310
|
-
this.pullQueue = [];
|
|
4311
|
-
this.pushQueue = [];
|
|
4312
|
-
this.eventHandlers = {};
|
|
4313
|
-
this.isPaused = false;
|
|
4314
|
-
this.isStopped = false;
|
|
4315
|
-
}
|
|
4316
|
-
push(value) {
|
|
4317
|
-
if (this.isStopped)
|
|
4318
|
-
return;
|
|
4319
|
-
const resolution = { value, done: false };
|
|
4320
|
-
if (this.pullQueue.length) {
|
|
4321
|
-
const placeholder = this.pullQueue.shift();
|
|
4322
|
-
if (placeholder)
|
|
4323
|
-
placeholder.resolve(resolution);
|
|
4324
|
-
}
|
|
4325
|
-
else {
|
|
4326
|
-
this.pushQueue.push(Promise.resolve(resolution));
|
|
4327
|
-
if (this.highWaterMark !== undefined &&
|
|
4328
|
-
this.pushQueue.length >= this.highWaterMark &&
|
|
4329
|
-
!this.isPaused) {
|
|
4330
|
-
this.isPaused = true;
|
|
4331
|
-
if (this.eventHandlers.highWater) {
|
|
4332
|
-
this.eventHandlers.highWater();
|
|
4333
|
-
}
|
|
4334
|
-
else if (console) {
|
|
4335
|
-
console.warn(`EventIterator queue reached ${this.pushQueue.length} items`);
|
|
4336
|
-
}
|
|
4337
|
-
}
|
|
4338
|
-
}
|
|
4339
|
-
}
|
|
4340
|
-
stop() {
|
|
4341
|
-
if (this.isStopped)
|
|
4342
|
-
return;
|
|
4343
|
-
this.isStopped = true;
|
|
4344
|
-
this.remove();
|
|
4345
|
-
for (const placeholder of this.pullQueue) {
|
|
4346
|
-
placeholder.resolve({ value: undefined, done: true });
|
|
4347
|
-
}
|
|
4348
|
-
this.pullQueue.length = 0;
|
|
4349
|
-
}
|
|
4350
|
-
fail(error) {
|
|
4351
|
-
if (this.isStopped)
|
|
4352
|
-
return;
|
|
4353
|
-
this.isStopped = true;
|
|
4354
|
-
this.remove();
|
|
4355
|
-
if (this.pullQueue.length) {
|
|
4356
|
-
for (const placeholder of this.pullQueue) {
|
|
4357
|
-
placeholder.reject(error);
|
|
4358
|
-
}
|
|
4359
|
-
this.pullQueue.length = 0;
|
|
4360
|
-
}
|
|
4361
|
-
else {
|
|
4362
|
-
const rejection = Promise.reject(error);
|
|
4363
|
-
/* Attach error handler to avoid leaking an unhandled promise rejection. */
|
|
4364
|
-
rejection.catch(() => { });
|
|
4365
|
-
this.pushQueue.push(rejection);
|
|
4366
|
-
}
|
|
4367
|
-
}
|
|
4368
|
-
remove() {
|
|
4369
|
-
Promise.resolve().then(() => {
|
|
4370
|
-
if (this.removeCallback)
|
|
4371
|
-
this.removeCallback();
|
|
4372
|
-
});
|
|
4373
|
-
}
|
|
4374
|
-
[symbolAsyncIterator]() {
|
|
4375
|
-
return {
|
|
4376
|
-
next: (value) => {
|
|
4377
|
-
const result = this.pushQueue.shift();
|
|
4378
|
-
if (result) {
|
|
4379
|
-
if (this.lowWaterMark !== undefined &&
|
|
4380
|
-
this.pushQueue.length <= this.lowWaterMark &&
|
|
4381
|
-
this.isPaused) {
|
|
4382
|
-
this.isPaused = false;
|
|
4383
|
-
if (this.eventHandlers.lowWater) {
|
|
4384
|
-
this.eventHandlers.lowWater();
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4387
|
-
return result;
|
|
4388
|
-
}
|
|
4389
|
-
else if (this.isStopped) {
|
|
4390
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
4391
|
-
}
|
|
4392
|
-
else {
|
|
4393
|
-
return new Promise((resolve, reject) => {
|
|
4394
|
-
this.pullQueue.push({ resolve, reject });
|
|
4395
|
-
});
|
|
4396
|
-
}
|
|
4397
|
-
},
|
|
4398
|
-
return: () => {
|
|
4399
|
-
this.isStopped = true;
|
|
4400
|
-
this.pushQueue.length = 0;
|
|
4401
|
-
this.remove();
|
|
4402
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
4403
|
-
},
|
|
4404
|
-
};
|
|
4405
|
-
}
|
|
4406
|
-
}
|
|
4407
|
-
class EventIterator {
|
|
4408
|
-
constructor(listen, { highWaterMark = 100, lowWaterMark = 1 } = {}) {
|
|
4409
|
-
const queue = new EventQueue();
|
|
4410
|
-
queue.highWaterMark = highWaterMark;
|
|
4411
|
-
queue.lowWaterMark = lowWaterMark;
|
|
4412
|
-
queue.removeCallback =
|
|
4413
|
-
listen({
|
|
4414
|
-
push: value => queue.push(value),
|
|
4415
|
-
stop: () => queue.stop(),
|
|
4416
|
-
fail: error => queue.fail(error),
|
|
4417
|
-
on: (event, fn) => {
|
|
4418
|
-
queue.eventHandlers[event] = fn;
|
|
4419
|
-
},
|
|
4420
|
-
}) || (() => { });
|
|
4421
|
-
this[symbolAsyncIterator] = () => queue[symbolAsyncIterator]();
|
|
4422
|
-
Object.freeze(this);
|
|
4423
|
-
}
|
|
4424
|
-
}
|
|
4425
|
-
eventIterator.EventIterator = EventIterator;
|
|
4426
|
-
eventIterator.default = EventIterator;
|
|
4427
|
-
return eventIterator;
|
|
4428
|
-
}
|
|
4429
|
-
|
|
4430
|
-
var hasRequiredDom;
|
|
4431
|
-
|
|
4432
|
-
function requireDom () {
|
|
4433
|
-
if (hasRequiredDom) return dom;
|
|
4434
|
-
hasRequiredDom = 1;
|
|
4435
|
-
Object.defineProperty(dom, "__esModule", { value: true });
|
|
4436
|
-
const event_iterator_1 = requireEventIterator();
|
|
4437
|
-
dom.EventIterator = event_iterator_1.EventIterator;
|
|
4438
|
-
function subscribe(event, options, evOptions) {
|
|
4439
|
-
return new event_iterator_1.EventIterator(({ push }) => {
|
|
4440
|
-
this.addEventListener(event, push, options);
|
|
4441
|
-
return () => this.removeEventListener(event, push, options);
|
|
4442
|
-
}, evOptions);
|
|
4443
|
-
}
|
|
4444
|
-
dom.subscribe = subscribe;
|
|
4445
|
-
dom.default = event_iterator_1.EventIterator;
|
|
4446
|
-
return dom;
|
|
4447
|
-
}
|
|
4448
|
-
|
|
4449
|
-
var domExports = requireDom();
|
|
4345
|
+
function getDefaultExportFromCjs (x) {
|
|
4346
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
4347
|
+
}
|
|
4450
4348
|
|
|
4451
4349
|
var logger$1 = {exports: {}};
|
|
4452
4350
|
|
|
@@ -4746,7 +4644,9 @@ var Logger = /*@__PURE__*/getDefaultExportFromCjs(loggerExports);
|
|
|
4746
4644
|
* different SQLite DB implementations.
|
|
4747
4645
|
*/
|
|
4748
4646
|
/**
|
|
4749
|
-
* Implements {@link DBGetUtils} on a {@link
|
|
4647
|
+
* Implements {@link DBGetUtils} on a {@link SqlExecutor}.
|
|
4648
|
+
*
|
|
4649
|
+
* @internal
|
|
4750
4650
|
*/
|
|
4751
4651
|
function DBGetUtilsDefaultMixin(Base) {
|
|
4752
4652
|
return class extends Base {
|
|
@@ -4790,6 +4690,8 @@ function DBGetUtilsDefaultMixin(Base) {
|
|
|
4790
4690
|
}
|
|
4791
4691
|
/**
|
|
4792
4692
|
* Update table operation numbers from SQLite
|
|
4693
|
+
*
|
|
4694
|
+
* @public
|
|
4793
4695
|
*/
|
|
4794
4696
|
var RowUpdateType;
|
|
4795
4697
|
(function (RowUpdateType) {
|
|
@@ -4798,8 +4700,10 @@ var RowUpdateType;
|
|
|
4798
4700
|
RowUpdateType[RowUpdateType["SQLITE_UPDATE"] = 23] = "SQLITE_UPDATE";
|
|
4799
4701
|
})(RowUpdateType || (RowUpdateType = {}));
|
|
4800
4702
|
/**
|
|
4801
|
-
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool
|
|
4802
|
-
* {@link ConnectionPool
|
|
4703
|
+
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool#readLock} and
|
|
4704
|
+
* {@link ConnectionPool#writeLock}.
|
|
4705
|
+
*
|
|
4706
|
+
* @internal
|
|
4803
4707
|
*/
|
|
4804
4708
|
function DBAdapterDefaultMixin(Base) {
|
|
4805
4709
|
return class extends Base {
|
|
@@ -4887,9 +4791,15 @@ class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction)
|
|
|
4887
4791
|
}
|
|
4888
4792
|
}
|
|
4889
4793
|
}
|
|
4794
|
+
/**
|
|
4795
|
+
* @internal
|
|
4796
|
+
*/
|
|
4890
4797
|
function isBatchedUpdateNotification(update) {
|
|
4891
4798
|
return 'tables' in update;
|
|
4892
4799
|
}
|
|
4800
|
+
/**
|
|
4801
|
+
* @internal
|
|
4802
|
+
*/
|
|
4893
4803
|
function extractTableUpdates(update) {
|
|
4894
4804
|
return isBatchedUpdateNotification(update) ? update.tables : [update.table];
|
|
4895
4805
|
}
|
|
@@ -4917,6 +4827,8 @@ const FULL_SYNC_PRIORITY = 2147483647;
|
|
|
4917
4827
|
*
|
|
4918
4828
|
* Also note that data is downloaded in bulk, which means that individual counters are unlikely
|
|
4919
4829
|
* to be updated one-by-one.
|
|
4830
|
+
*
|
|
4831
|
+
* @public
|
|
4920
4832
|
*/
|
|
4921
4833
|
class SyncProgress {
|
|
4922
4834
|
internal;
|
|
@@ -4955,6 +4867,9 @@ class SyncProgress {
|
|
|
4955
4867
|
}
|
|
4956
4868
|
}
|
|
4957
4869
|
|
|
4870
|
+
/**
|
|
4871
|
+
* @public
|
|
4872
|
+
*/
|
|
4958
4873
|
class SyncStatus {
|
|
4959
4874
|
options;
|
|
4960
4875
|
constructor(options) {
|
|
@@ -4965,6 +4880,8 @@ class SyncStatus {
|
|
|
4965
4880
|
* implementation).
|
|
4966
4881
|
*
|
|
4967
4882
|
* This information is only available after a connection has been requested.
|
|
4883
|
+
*
|
|
4884
|
+
* @deprecated This always returns the Rust client (the only option).
|
|
4968
4885
|
*/
|
|
4969
4886
|
get clientImplementation() {
|
|
4970
4887
|
return this.options.clientImplementation;
|
|
@@ -4972,7 +4889,7 @@ class SyncStatus {
|
|
|
4972
4889
|
/**
|
|
4973
4890
|
* Indicates if the client is currently connected to the PowerSync service.
|
|
4974
4891
|
*
|
|
4975
|
-
* @returns
|
|
4892
|
+
* @returns True if connected, false otherwise. Defaults to false if not specified.
|
|
4976
4893
|
*/
|
|
4977
4894
|
get connected() {
|
|
4978
4895
|
return this.options.connected ?? false;
|
|
@@ -4980,7 +4897,7 @@ class SyncStatus {
|
|
|
4980
4897
|
/**
|
|
4981
4898
|
* Indicates if the client is in the process of establishing a connection to the PowerSync service.
|
|
4982
4899
|
*
|
|
4983
|
-
* @returns
|
|
4900
|
+
* @returns True if connecting, false otherwise. Defaults to false if not specified.
|
|
4984
4901
|
*/
|
|
4985
4902
|
get connecting() {
|
|
4986
4903
|
return this.options.connecting ?? false;
|
|
@@ -4989,7 +4906,7 @@ class SyncStatus {
|
|
|
4989
4906
|
* Time that a last sync has fully completed, if any.
|
|
4990
4907
|
* This timestamp is reset to null after a restart of the PowerSync service.
|
|
4991
4908
|
*
|
|
4992
|
-
* @returns
|
|
4909
|
+
* @returns The timestamp of the last successful sync, or undefined if no sync has completed.
|
|
4993
4910
|
*/
|
|
4994
4911
|
get lastSyncedAt() {
|
|
4995
4912
|
return this.options.lastSyncedAt;
|
|
@@ -4997,7 +4914,7 @@ class SyncStatus {
|
|
|
4997
4914
|
/**
|
|
4998
4915
|
* Indicates whether there has been at least one full sync completed since initialization.
|
|
4999
4916
|
*
|
|
5000
|
-
* @returns
|
|
4917
|
+
* @returns True if at least one sync has completed, false if no sync has completed,
|
|
5001
4918
|
* or undefined when the state is still being loaded from the database.
|
|
5002
4919
|
*/
|
|
5003
4920
|
get hasSynced() {
|
|
@@ -5006,10 +4923,10 @@ class SyncStatus {
|
|
|
5006
4923
|
/**
|
|
5007
4924
|
* Provides the current data flow status regarding uploads and downloads.
|
|
5008
4925
|
*
|
|
5009
|
-
* @returns
|
|
4926
|
+
* @returns An object containing:
|
|
5010
4927
|
* - downloading: True if actively downloading changes (only when connected is also true)
|
|
5011
4928
|
* - uploading: True if actively uploading changes
|
|
5012
|
-
* Defaults to {downloading: false, uploading: false} if not specified.
|
|
4929
|
+
* Defaults to `{downloading: false, uploading: false}` if not specified.
|
|
5013
4930
|
*/
|
|
5014
4931
|
get dataFlowStatus() {
|
|
5015
4932
|
return (this.options.dataFlow ?? {
|
|
@@ -5034,7 +4951,7 @@ class SyncStatus {
|
|
|
5034
4951
|
return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
|
|
5035
4952
|
}
|
|
5036
4953
|
/**
|
|
5037
|
-
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
4954
|
+
* If the `stream` appears in {@link SyncStatus.syncStreams}, returns the current status for that stream.
|
|
5038
4955
|
*/
|
|
5039
4956
|
forStream(stream) {
|
|
5040
4957
|
const asJson = JSON.stringify(stream.parameters);
|
|
@@ -5044,7 +4961,7 @@ class SyncStatus {
|
|
|
5044
4961
|
/**
|
|
5045
4962
|
* Provides sync status information for all bucket priorities, sorted by priority (highest first).
|
|
5046
4963
|
*
|
|
5047
|
-
* @returns
|
|
4964
|
+
* @returns An array of status entries for different sync priority levels,
|
|
5048
4965
|
* sorted with highest priorities (lower numbers) first.
|
|
5049
4966
|
*/
|
|
5050
4967
|
get priorityStatusEntries() {
|
|
@@ -5079,8 +4996,8 @@ class SyncStatus {
|
|
|
5079
4996
|
* For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
|
|
5080
4997
|
* with a priority of 1 may return information for priority level 3.
|
|
5081
4998
|
*
|
|
5082
|
-
* @param
|
|
5083
|
-
* @returns
|
|
4999
|
+
* @param priority - The bucket priority for which the status should be reported
|
|
5000
|
+
* @returns Status information for the requested priority level or the next higher level with available status
|
|
5084
5001
|
*/
|
|
5085
5002
|
statusForPriority(priority) {
|
|
5086
5003
|
// priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
|
|
@@ -5101,8 +5018,8 @@ class SyncStatus {
|
|
|
5101
5018
|
* Compares this SyncStatus instance with another to determine if they are equal.
|
|
5102
5019
|
* Equality is determined by comparing the serialized JSON representation of both instances.
|
|
5103
5020
|
*
|
|
5104
|
-
* @param
|
|
5105
|
-
* @returns
|
|
5021
|
+
* @param status - The SyncStatus instance to compare against
|
|
5022
|
+
* @returns True if the instances are considered equal, false otherwise
|
|
5106
5023
|
*/
|
|
5107
5024
|
isEqual(status) {
|
|
5108
5025
|
/**
|
|
@@ -5125,7 +5042,7 @@ class SyncStatus {
|
|
|
5125
5042
|
* Creates a human-readable string representation of the current sync status.
|
|
5126
5043
|
* Includes information about connection state, sync completion, and data flow.
|
|
5127
5044
|
*
|
|
5128
|
-
* @returns
|
|
5045
|
+
* @returns A string representation of the sync status
|
|
5129
5046
|
*/
|
|
5130
5047
|
getMessage() {
|
|
5131
5048
|
const dataFlow = this.dataFlowStatus;
|
|
@@ -5134,7 +5051,7 @@ class SyncStatus {
|
|
|
5134
5051
|
/**
|
|
5135
5052
|
* Serializes the SyncStatus instance to a plain object.
|
|
5136
5053
|
*
|
|
5137
|
-
* @returns
|
|
5054
|
+
* @returns A plain object representation of the sync status
|
|
5138
5055
|
*/
|
|
5139
5056
|
toJSON() {
|
|
5140
5057
|
return {
|
|
@@ -5200,6 +5117,9 @@ class SyncStreamStatusView {
|
|
|
5200
5117
|
}
|
|
5201
5118
|
}
|
|
5202
5119
|
|
|
5120
|
+
/**
|
|
5121
|
+
* @public
|
|
5122
|
+
*/
|
|
5203
5123
|
class UploadQueueStats {
|
|
5204
5124
|
count;
|
|
5205
5125
|
size;
|
|
@@ -5225,6 +5145,9 @@ class UploadQueueStats {
|
|
|
5225
5145
|
}
|
|
5226
5146
|
}
|
|
5227
5147
|
|
|
5148
|
+
/**
|
|
5149
|
+
* @internal
|
|
5150
|
+
*/
|
|
5228
5151
|
class BaseObserver {
|
|
5229
5152
|
listeners = new Set();
|
|
5230
5153
|
constructor() { }
|
|
@@ -5252,6 +5175,9 @@ class BaseObserver {
|
|
|
5252
5175
|
}
|
|
5253
5176
|
}
|
|
5254
5177
|
|
|
5178
|
+
/**
|
|
5179
|
+
* @internal
|
|
5180
|
+
*/
|
|
5255
5181
|
class ControlledExecutor {
|
|
5256
5182
|
task;
|
|
5257
5183
|
/**
|
|
@@ -5303,6 +5229,210 @@ class ControlledExecutor {
|
|
|
5303
5229
|
}
|
|
5304
5230
|
}
|
|
5305
5231
|
|
|
5232
|
+
/**
|
|
5233
|
+
* Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
|
|
5234
|
+
*
|
|
5235
|
+
* For those, users relying on async generators typically lower them with a transpiler and [this polyfill](https://github.com/Azure/azure-sdk-for-js/blob/%40azure/core-asynciterator-polyfill_1.0.2/sdk/core/core-asynciterator-polyfill/src/index.ts#L4-L6).
|
|
5236
|
+
* This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
|
|
5237
|
+
* SDK.
|
|
5238
|
+
*/
|
|
5239
|
+
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
5240
|
+
|
|
5241
|
+
const doneResult = { done: true, value: undefined };
|
|
5242
|
+
function valueResult(value) {
|
|
5243
|
+
return { done: false, value };
|
|
5244
|
+
}
|
|
5245
|
+
/**
|
|
5246
|
+
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
5247
|
+
*
|
|
5248
|
+
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
5249
|
+
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
5250
|
+
*
|
|
5251
|
+
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
5252
|
+
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
5253
|
+
*/
|
|
5254
|
+
function injectable(source) {
|
|
5255
|
+
let sourceIsDone = false;
|
|
5256
|
+
let waiter = undefined; // An active, waiting next() call.
|
|
5257
|
+
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
5258
|
+
let pendingSourceEvent = null;
|
|
5259
|
+
let sourceFetchInFlight = false;
|
|
5260
|
+
let pendingInjectedEvents = [];
|
|
5261
|
+
const consumeWaiter = () => {
|
|
5262
|
+
const pending = waiter;
|
|
5263
|
+
waiter = undefined;
|
|
5264
|
+
return pending;
|
|
5265
|
+
};
|
|
5266
|
+
const fetchFromSource = () => {
|
|
5267
|
+
const resolveWaiter = (propagate) => {
|
|
5268
|
+
sourceFetchInFlight = false;
|
|
5269
|
+
const active = consumeWaiter();
|
|
5270
|
+
if (active) {
|
|
5271
|
+
propagate(active);
|
|
5272
|
+
}
|
|
5273
|
+
else {
|
|
5274
|
+
pendingSourceEvent = propagate;
|
|
5275
|
+
}
|
|
5276
|
+
};
|
|
5277
|
+
sourceFetchInFlight = true;
|
|
5278
|
+
const nextFromSource = source.next();
|
|
5279
|
+
nextFromSource.then((value) => {
|
|
5280
|
+
sourceIsDone = value.done == true;
|
|
5281
|
+
resolveWaiter((w) => w.resolve(value));
|
|
5282
|
+
}, (error) => {
|
|
5283
|
+
resolveWaiter((w) => w.reject(error));
|
|
5284
|
+
});
|
|
5285
|
+
};
|
|
5286
|
+
return {
|
|
5287
|
+
next: () => {
|
|
5288
|
+
return new Promise((resolve, reject) => {
|
|
5289
|
+
// First priority: Dispatch ready upstream events.
|
|
5290
|
+
if (sourceIsDone) {
|
|
5291
|
+
return resolve(doneResult);
|
|
5292
|
+
}
|
|
5293
|
+
if (pendingSourceEvent) {
|
|
5294
|
+
pendingSourceEvent({ resolve, reject });
|
|
5295
|
+
pendingSourceEvent = null;
|
|
5296
|
+
return;
|
|
5297
|
+
}
|
|
5298
|
+
// Second priority: Dispatch injected events
|
|
5299
|
+
if (pendingInjectedEvents.length) {
|
|
5300
|
+
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
5301
|
+
}
|
|
5302
|
+
// Nothing pending? Fetch from source
|
|
5303
|
+
waiter = { resolve, reject };
|
|
5304
|
+
if (!sourceFetchInFlight) {
|
|
5305
|
+
fetchFromSource();
|
|
5306
|
+
}
|
|
5307
|
+
});
|
|
5308
|
+
},
|
|
5309
|
+
inject: (event) => {
|
|
5310
|
+
const pending = consumeWaiter();
|
|
5311
|
+
if (pending != null) {
|
|
5312
|
+
pending.resolve(valueResult(event));
|
|
5313
|
+
}
|
|
5314
|
+
else {
|
|
5315
|
+
pendingInjectedEvents.push(event);
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5320
|
+
/**
|
|
5321
|
+
* Splits a byte stream at line endings, emitting each line as a string.
|
|
5322
|
+
*/
|
|
5323
|
+
function extractJsonLines(source, decoder) {
|
|
5324
|
+
let buffer = '';
|
|
5325
|
+
const pendingLines = [];
|
|
5326
|
+
let isFinalEvent = false;
|
|
5327
|
+
return {
|
|
5328
|
+
next: async () => {
|
|
5329
|
+
while (true) {
|
|
5330
|
+
if (isFinalEvent) {
|
|
5331
|
+
return doneResult;
|
|
5332
|
+
}
|
|
5333
|
+
{
|
|
5334
|
+
const first = pendingLines.shift();
|
|
5335
|
+
if (first) {
|
|
5336
|
+
return { done: false, value: first };
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
const { done, value } = await source.next();
|
|
5340
|
+
if (done) {
|
|
5341
|
+
const remaining = buffer.trim();
|
|
5342
|
+
if (remaining.length != 0) {
|
|
5343
|
+
isFinalEvent = true;
|
|
5344
|
+
return { done: false, value: remaining };
|
|
5345
|
+
}
|
|
5346
|
+
return doneResult;
|
|
5347
|
+
}
|
|
5348
|
+
const data = decoder.decode(value, { stream: true });
|
|
5349
|
+
buffer += data;
|
|
5350
|
+
const lines = buffer.split('\n');
|
|
5351
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
5352
|
+
const l = lines[i].trim();
|
|
5353
|
+
if (l.length > 0) {
|
|
5354
|
+
pendingLines.push(l);
|
|
5355
|
+
}
|
|
5356
|
+
}
|
|
5357
|
+
buffer = lines[lines.length - 1];
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
};
|
|
5361
|
+
}
|
|
5362
|
+
/**
|
|
5363
|
+
* Splits a concatenated stream of BSON objects by emitting individual objects.
|
|
5364
|
+
*/
|
|
5365
|
+
function extractBsonObjects(source) {
|
|
5366
|
+
// Fully read but not emitted yet.
|
|
5367
|
+
const completedObjects = [];
|
|
5368
|
+
// Whether source has returned { done: true }. We do the same once completed objects have been emitted.
|
|
5369
|
+
let isDone = false;
|
|
5370
|
+
const lengthBuffer = new DataView(new ArrayBuffer(4));
|
|
5371
|
+
let objectBody = null;
|
|
5372
|
+
// If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
|
|
5373
|
+
// If we're consuming a document, the bytes remaining.
|
|
5374
|
+
let remainingLength = 4;
|
|
5375
|
+
return {
|
|
5376
|
+
async next() {
|
|
5377
|
+
while (true) {
|
|
5378
|
+
// Before fetching new data from upstream, return completed objects.
|
|
5379
|
+
if (completedObjects.length) {
|
|
5380
|
+
return valueResult(completedObjects.shift());
|
|
5381
|
+
}
|
|
5382
|
+
if (isDone) {
|
|
5383
|
+
return doneResult;
|
|
5384
|
+
}
|
|
5385
|
+
const upstreamEvent = await source.next();
|
|
5386
|
+
if (upstreamEvent.done) {
|
|
5387
|
+
isDone = true;
|
|
5388
|
+
if (objectBody || remainingLength != 4) {
|
|
5389
|
+
throw new Error('illegal end of stream in BSON object');
|
|
5390
|
+
}
|
|
5391
|
+
return doneResult;
|
|
5392
|
+
}
|
|
5393
|
+
const chunk = upstreamEvent.value;
|
|
5394
|
+
for (let i = 0; i < chunk.length;) {
|
|
5395
|
+
const availableInData = chunk.length - i;
|
|
5396
|
+
if (objectBody) {
|
|
5397
|
+
// We're in the middle of reading a BSON document.
|
|
5398
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
5399
|
+
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
5400
|
+
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
5401
|
+
i += bytesToRead;
|
|
5402
|
+
remainingLength -= bytesToRead;
|
|
5403
|
+
if (remainingLength == 0) {
|
|
5404
|
+
completedObjects.push(objectBody);
|
|
5405
|
+
// Prepare to read another document, starting with its length
|
|
5406
|
+
objectBody = null;
|
|
5407
|
+
remainingLength = 4;
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
else {
|
|
5411
|
+
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
5412
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
5413
|
+
for (let j = 0; j < bytesToRead; j++) {
|
|
5414
|
+
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
5415
|
+
}
|
|
5416
|
+
i += bytesToRead;
|
|
5417
|
+
remainingLength -= bytesToRead;
|
|
5418
|
+
if (remainingLength == 0) {
|
|
5419
|
+
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
5420
|
+
// header is included in length.
|
|
5421
|
+
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
5422
|
+
remainingLength = length - 4;
|
|
5423
|
+
if (remainingLength < 1) {
|
|
5424
|
+
throw new Error(`invalid length for bson: ${length}`);
|
|
5425
|
+
}
|
|
5426
|
+
objectBody = new Uint8Array(length);
|
|
5427
|
+
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
};
|
|
5434
|
+
}
|
|
5435
|
+
|
|
5306
5436
|
/**
|
|
5307
5437
|
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
5308
5438
|
* on the trailing edge.
|
|
@@ -5322,45 +5452,128 @@ function throttleTrailing(func, wait) {
|
|
|
5322
5452
|
};
|
|
5323
5453
|
}
|
|
5324
5454
|
function asyncNotifier() {
|
|
5325
|
-
|
|
5326
|
-
let hasPendingNotification = false;
|
|
5455
|
+
const queue = new EventQueue();
|
|
5327
5456
|
return {
|
|
5328
5457
|
notify() {
|
|
5329
|
-
if (
|
|
5330
|
-
waitingConsumer();
|
|
5331
|
-
waitingConsumer = null;
|
|
5332
|
-
}
|
|
5458
|
+
if (queue.countOutstandingEvents > 0) ;
|
|
5333
5459
|
else {
|
|
5334
|
-
|
|
5460
|
+
queue.notify();
|
|
5335
5461
|
}
|
|
5336
5462
|
},
|
|
5337
5463
|
waitForNotification(signal) {
|
|
5338
|
-
return
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5464
|
+
return queue.waitForEvent(signal);
|
|
5465
|
+
}
|
|
5466
|
+
};
|
|
5467
|
+
}
|
|
5468
|
+
class EventQueue {
|
|
5469
|
+
options;
|
|
5470
|
+
waitingConsumer;
|
|
5471
|
+
outstandingEvents;
|
|
5472
|
+
constructor(options = {}) {
|
|
5473
|
+
this.options = options;
|
|
5474
|
+
this.outstandingEvents = [];
|
|
5475
|
+
}
|
|
5476
|
+
/**
|
|
5477
|
+
* The amount of buffered events not yet dispatched to listeners.
|
|
5478
|
+
*/
|
|
5479
|
+
get countOutstandingEvents() {
|
|
5480
|
+
return this.outstandingEvents.length;
|
|
5481
|
+
}
|
|
5482
|
+
notifyInner(dispatch) {
|
|
5483
|
+
const existing = this.waitingConsumer;
|
|
5484
|
+
this.waitingConsumer = undefined;
|
|
5485
|
+
const dispatchAndNotifyListeners = (waiter) => {
|
|
5486
|
+
dispatch(waiter);
|
|
5487
|
+
this.options.eventDelivered?.();
|
|
5488
|
+
};
|
|
5489
|
+
if (existing) {
|
|
5490
|
+
dispatchAndNotifyListeners(existing);
|
|
5491
|
+
}
|
|
5492
|
+
else {
|
|
5493
|
+
this.outstandingEvents.push(dispatchAndNotifyListeners);
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
notify(value) {
|
|
5497
|
+
this.notifyInner((l) => l.resolve(value));
|
|
5498
|
+
}
|
|
5499
|
+
notifyError(error) {
|
|
5500
|
+
this.notifyInner((l) => l.reject(error));
|
|
5501
|
+
}
|
|
5502
|
+
waitForEvent(signal) {
|
|
5503
|
+
return new Promise((resolve, reject) => {
|
|
5504
|
+
if (this.waitingConsumer != null) {
|
|
5505
|
+
throw new Error('Illegal call to waitForEvent, already has a waiter.');
|
|
5506
|
+
}
|
|
5507
|
+
const complete = () => {
|
|
5508
|
+
signal?.removeEventListener('abort', onAbort);
|
|
5509
|
+
};
|
|
5510
|
+
const onAbort = () => {
|
|
5511
|
+
complete();
|
|
5512
|
+
this.waitingConsumer = undefined;
|
|
5513
|
+
resolve(undefined);
|
|
5514
|
+
};
|
|
5515
|
+
const waiter = {
|
|
5516
|
+
resolve: (value) => {
|
|
5517
|
+
complete();
|
|
5518
|
+
resolve(value);
|
|
5519
|
+
},
|
|
5520
|
+
reject: (error) => {
|
|
5521
|
+
complete();
|
|
5522
|
+
reject(error);
|
|
5344
5523
|
}
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5524
|
+
};
|
|
5525
|
+
if (signal.aborted) {
|
|
5526
|
+
resolve(undefined);
|
|
5527
|
+
}
|
|
5528
|
+
else if (this.countOutstandingEvents > 0) {
|
|
5529
|
+
const [event] = this.outstandingEvents.splice(0, 1);
|
|
5530
|
+
event(waiter);
|
|
5531
|
+
}
|
|
5532
|
+
else {
|
|
5533
|
+
this.waitingConsumer = waiter;
|
|
5534
|
+
signal.addEventListener('abort', onAbort);
|
|
5535
|
+
}
|
|
5536
|
+
});
|
|
5537
|
+
}
|
|
5538
|
+
/**
|
|
5539
|
+
* Creates an async iterable backed by event queues.
|
|
5540
|
+
*
|
|
5541
|
+
* @param run A function invoked for every new listener. It receives a queue backing the async iterator.
|
|
5542
|
+
* @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
|
|
5543
|
+
* called.
|
|
5544
|
+
* @returns An object conforming to the async iterable protocol.
|
|
5545
|
+
*/
|
|
5546
|
+
static queueBasedAsyncIterable(run, abort) {
|
|
5547
|
+
return {
|
|
5548
|
+
[symbolAsyncIterator]: () => {
|
|
5549
|
+
const queue = new EventQueue();
|
|
5550
|
+
const controller = new AbortController();
|
|
5551
|
+
function dispose() {
|
|
5552
|
+
controller.abort();
|
|
5553
|
+
abort?.removeEventListener('abort', dispose);
|
|
5348
5554
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
resolve();
|
|
5555
|
+
if (abort) {
|
|
5556
|
+
if (abort.aborted) {
|
|
5557
|
+
controller.abort();
|
|
5353
5558
|
}
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
resolve();
|
|
5559
|
+
else {
|
|
5560
|
+
abort.addEventListener('abort', dispose);
|
|
5357
5561
|
}
|
|
5358
|
-
waitingConsumer = complete;
|
|
5359
|
-
signal.addEventListener('abort', onAbort);
|
|
5360
5562
|
}
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5563
|
+
run(queue, controller.signal);
|
|
5564
|
+
return {
|
|
5565
|
+
async next() {
|
|
5566
|
+
const event = await queue.waitForEvent(controller.signal);
|
|
5567
|
+
return event == null ? doneResult : valueResult(event);
|
|
5568
|
+
},
|
|
5569
|
+
async return() {
|
|
5570
|
+
dispose();
|
|
5571
|
+
return doneResult;
|
|
5572
|
+
}
|
|
5573
|
+
};
|
|
5574
|
+
}
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5364
5577
|
}
|
|
5365
5578
|
|
|
5366
5579
|
/**
|
|
@@ -5519,7 +5732,7 @@ class ConnectionManager extends BaseObserver {
|
|
|
5519
5732
|
/**
|
|
5520
5733
|
* Close the sync connection.
|
|
5521
5734
|
*
|
|
5522
|
-
* Use {@link connect} to connect again.
|
|
5735
|
+
* Use {@link ConnectionManager.connect} to connect again.
|
|
5523
5736
|
*/
|
|
5524
5737
|
async disconnect() {
|
|
5525
5738
|
// This will help abort pending connects
|
|
@@ -5659,6 +5872,8 @@ const _finalizer = 'FinalizationRegistry' in globalThis
|
|
|
5659
5872
|
/**
|
|
5660
5873
|
* An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
|
|
5661
5874
|
* result has changes without necessarily processing all items in the result.
|
|
5875
|
+
*
|
|
5876
|
+
* @public
|
|
5662
5877
|
*/
|
|
5663
5878
|
class ArrayComparator {
|
|
5664
5879
|
options;
|
|
@@ -5686,6 +5901,8 @@ class ArrayComparator {
|
|
|
5686
5901
|
}
|
|
5687
5902
|
/**
|
|
5688
5903
|
* Watched query comparator that always reports changed result sets.
|
|
5904
|
+
*
|
|
5905
|
+
* @public
|
|
5689
5906
|
*/
|
|
5690
5907
|
const FalsyComparator = {
|
|
5691
5908
|
checkEquality: () => false // Default comparator that always returns false
|
|
@@ -5893,6 +6110,8 @@ class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
5893
6110
|
/**
|
|
5894
6111
|
* An empty differential result set.
|
|
5895
6112
|
* This is used as the initial state for differential incrementally watched queries.
|
|
6113
|
+
*
|
|
6114
|
+
* @internal
|
|
5896
6115
|
*/
|
|
5897
6116
|
const EMPTY_DIFFERENTIAL = {
|
|
5898
6117
|
added: [],
|
|
@@ -5905,6 +6124,8 @@ const EMPTY_DIFFERENTIAL = {
|
|
|
5905
6124
|
* Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
|
|
5906
6125
|
* It keys items by their `id` property if available, alternatively it uses JSON stringification
|
|
5907
6126
|
* of the entire item for the key and comparison.
|
|
6127
|
+
*
|
|
6128
|
+
* @internal
|
|
5908
6129
|
*/
|
|
5909
6130
|
const DEFAULT_ROW_COMPARATOR = {
|
|
5910
6131
|
keyBy: (item) => {
|
|
@@ -6185,6 +6406,8 @@ class CustomQuery {
|
|
|
6185
6406
|
|
|
6186
6407
|
/**
|
|
6187
6408
|
* Tests if the input is a {@link SQLOpenOptions}
|
|
6409
|
+
*
|
|
6410
|
+
* @internal
|
|
6188
6411
|
*/
|
|
6189
6412
|
const isSQLOpenOptions = (test) => {
|
|
6190
6413
|
// typeof null is `object`, but you cannot use the `in` operator on `null.
|
|
@@ -6192,17 +6415,24 @@ const isSQLOpenOptions = (test) => {
|
|
|
6192
6415
|
};
|
|
6193
6416
|
/**
|
|
6194
6417
|
* Tests if input is a {@link SQLOpenFactory}
|
|
6418
|
+
*
|
|
6419
|
+
* @internal
|
|
6195
6420
|
*/
|
|
6196
6421
|
const isSQLOpenFactory = (test) => {
|
|
6197
6422
|
return typeof test?.openDB == 'function';
|
|
6198
6423
|
};
|
|
6199
6424
|
/**
|
|
6200
6425
|
* Tests if input is a {@link DBAdapter}
|
|
6426
|
+
*
|
|
6427
|
+
* @internal
|
|
6201
6428
|
*/
|
|
6202
6429
|
const isDBAdapter = (test) => {
|
|
6203
6430
|
return typeof test?.writeTransaction == 'function';
|
|
6204
6431
|
};
|
|
6205
6432
|
|
|
6433
|
+
/**
|
|
6434
|
+
* @internal
|
|
6435
|
+
*/
|
|
6206
6436
|
var PSInternalTable;
|
|
6207
6437
|
(function (PSInternalTable) {
|
|
6208
6438
|
PSInternalTable["DATA"] = "ps_data";
|
|
@@ -6211,6 +6441,9 @@ var PSInternalTable;
|
|
|
6211
6441
|
PSInternalTable["OPLOG"] = "ps_oplog";
|
|
6212
6442
|
PSInternalTable["UNTYPED"] = "ps_untyped";
|
|
6213
6443
|
})(PSInternalTable || (PSInternalTable = {}));
|
|
6444
|
+
/**
|
|
6445
|
+
* @internal
|
|
6446
|
+
*/
|
|
6214
6447
|
var PowerSyncControlCommand;
|
|
6215
6448
|
(function (PowerSyncControlCommand) {
|
|
6216
6449
|
PowerSyncControlCommand["PROCESS_TEXT_LINE"] = "line_text";
|
|
@@ -6228,6 +6461,8 @@ var PowerSyncControlCommand;
|
|
|
6228
6461
|
|
|
6229
6462
|
/**
|
|
6230
6463
|
* A batch of client-side changes.
|
|
6464
|
+
*
|
|
6465
|
+
* @public
|
|
6231
6466
|
*/
|
|
6232
6467
|
class CrudBatch {
|
|
6233
6468
|
crud;
|
|
@@ -6254,6 +6489,8 @@ class CrudBatch {
|
|
|
6254
6489
|
|
|
6255
6490
|
/**
|
|
6256
6491
|
* Type of local change.
|
|
6492
|
+
*
|
|
6493
|
+
* @public
|
|
6257
6494
|
*/
|
|
6258
6495
|
var UpdateType;
|
|
6259
6496
|
(function (UpdateType) {
|
|
@@ -6266,6 +6503,8 @@ var UpdateType;
|
|
|
6266
6503
|
})(UpdateType || (UpdateType = {}));
|
|
6267
6504
|
/**
|
|
6268
6505
|
* A single client-side change.
|
|
6506
|
+
*
|
|
6507
|
+
* @public
|
|
6269
6508
|
*/
|
|
6270
6509
|
class CrudEntry {
|
|
6271
6510
|
/**
|
|
@@ -6362,6 +6601,9 @@ class CrudEntry {
|
|
|
6362
6601
|
}
|
|
6363
6602
|
}
|
|
6364
6603
|
|
|
6604
|
+
/**
|
|
6605
|
+
* @public
|
|
6606
|
+
*/
|
|
6365
6607
|
class CrudTransaction extends CrudBatch {
|
|
6366
6608
|
crud;
|
|
6367
6609
|
complete;
|
|
@@ -6390,6 +6632,8 @@ class CrudTransaction extends CrudBatch {
|
|
|
6390
6632
|
* Calls to Abortcontroller.abort(reason: any) will result in the
|
|
6391
6633
|
* `reason` being thrown. This is not necessarily an error,
|
|
6392
6634
|
* but extends error for better logging purposes.
|
|
6635
|
+
*
|
|
6636
|
+
* @internal
|
|
6393
6637
|
*/
|
|
6394
6638
|
class AbortOperation extends Error {
|
|
6395
6639
|
reason;
|
|
@@ -13560,7 +13804,7 @@ function requireDist () {
|
|
|
13560
13804
|
|
|
13561
13805
|
var distExports = requireDist();
|
|
13562
13806
|
|
|
13563
|
-
var version = "1.
|
|
13807
|
+
var version = "1.55.0";
|
|
13564
13808
|
var PACKAGE = {
|
|
13565
13809
|
version: version};
|
|
13566
13810
|
|
|
@@ -13636,289 +13880,95 @@ function requireWebsocketDuplexConnection () {
|
|
|
13636
13880
|
get: function () {
|
|
13637
13881
|
return this.done ? 0 : 1;
|
|
13638
13882
|
},
|
|
13639
|
-
enumerable: false,
|
|
13640
|
-
configurable: true
|
|
13641
|
-
});
|
|
13642
|
-
WebsocketDuplexConnection.prototype.close = function (error) {
|
|
13643
|
-
if (this.done) {
|
|
13644
|
-
_super.prototype.close.call(this, error);
|
|
13645
|
-
return;
|
|
13646
|
-
}
|
|
13647
|
-
this.websocket.removeEventListener("close", this.handleClosed);
|
|
13648
|
-
this.websocket.removeEventListener("error", this.handleError);
|
|
13649
|
-
this.websocket.removeEventListener("message", this.handleMessage);
|
|
13650
|
-
this.websocket.close();
|
|
13651
|
-
delete this.websocket;
|
|
13652
|
-
_super.prototype.close.call(this, error);
|
|
13653
|
-
};
|
|
13654
|
-
WebsocketDuplexConnection.prototype.send = function (frame) {
|
|
13655
|
-
if (this.done) {
|
|
13656
|
-
return;
|
|
13657
|
-
}
|
|
13658
|
-
var buffer = (0, rsocket_core_1.serializeFrame)(frame);
|
|
13659
|
-
this.websocket.send(buffer);
|
|
13660
|
-
};
|
|
13661
|
-
return WebsocketDuplexConnection;
|
|
13662
|
-
}(rsocket_core_1.Deferred));
|
|
13663
|
-
WebsocketDuplexConnection.WebsocketDuplexConnection = WebsocketDuplexConnection$1;
|
|
13664
|
-
|
|
13665
|
-
return WebsocketDuplexConnection;
|
|
13666
|
-
}
|
|
13667
|
-
|
|
13668
|
-
var WebsocketDuplexConnectionExports = requireWebsocketDuplexConnection();
|
|
13669
|
-
|
|
13670
|
-
/**
|
|
13671
|
-
* Adapted from rsocket-websocket-client
|
|
13672
|
-
* https://github.com/rsocket/rsocket-js/blob/e224cf379e747c4f1ddc4f2fa111854626cc8575/packages/rsocket-websocket-client/src/WebsocketClientTransport.ts#L17
|
|
13673
|
-
* This adds additional error handling for React Native iOS.
|
|
13674
|
-
* This particularly adds a close listener to handle cases where the WebSocket
|
|
13675
|
-
* connection closes immediately after opening without emitting an error.
|
|
13676
|
-
*/
|
|
13677
|
-
class WebsocketClientTransport {
|
|
13678
|
-
url;
|
|
13679
|
-
factory;
|
|
13680
|
-
constructor(options) {
|
|
13681
|
-
this.url = options.url;
|
|
13682
|
-
this.factory = options.wsCreator ?? ((url) => new WebSocket(url));
|
|
13683
|
-
}
|
|
13684
|
-
connect(multiplexerDemultiplexerFactory) {
|
|
13685
|
-
return new Promise((resolve, reject) => {
|
|
13686
|
-
const websocket = this.factory(this.url);
|
|
13687
|
-
websocket.binaryType = 'arraybuffer';
|
|
13688
|
-
let removeListeners;
|
|
13689
|
-
const openListener = () => {
|
|
13690
|
-
removeListeners();
|
|
13691
|
-
resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
|
|
13692
|
-
};
|
|
13693
|
-
const errorListener = (ev) => {
|
|
13694
|
-
removeListeners();
|
|
13695
|
-
// We add a default error in that case.
|
|
13696
|
-
if (ev.error != null) {
|
|
13697
|
-
// undici typically provides an error object
|
|
13698
|
-
reject(ev.error);
|
|
13699
|
-
}
|
|
13700
|
-
else if (ev.message != null) {
|
|
13701
|
-
// React Native typically does not provide an error object, but does provide a message
|
|
13702
|
-
reject(new Error(`Failed to create websocket connection: ${ev.message}`));
|
|
13703
|
-
}
|
|
13704
|
-
else {
|
|
13705
|
-
// Browsers often provide no details at all
|
|
13706
|
-
reject(new Error(`Failed to create websocket connection to ${this.url}`));
|
|
13707
|
-
}
|
|
13708
|
-
};
|
|
13709
|
-
/**
|
|
13710
|
-
* In some cases, such as React Native iOS, the WebSocket connection may close immediately after opening
|
|
13711
|
-
* without and error. In such cases, we need to handle the close event to reject the promise.
|
|
13712
|
-
*/
|
|
13713
|
-
const closeListener = () => {
|
|
13714
|
-
removeListeners();
|
|
13715
|
-
reject(new Error('WebSocket connection closed while opening'));
|
|
13716
|
-
};
|
|
13717
|
-
removeListeners = () => {
|
|
13718
|
-
websocket.removeEventListener('open', openListener);
|
|
13719
|
-
websocket.removeEventListener('error', errorListener);
|
|
13720
|
-
websocket.removeEventListener('close', closeListener);
|
|
13721
|
-
};
|
|
13722
|
-
websocket.addEventListener('open', openListener);
|
|
13723
|
-
websocket.addEventListener('error', errorListener);
|
|
13724
|
-
websocket.addEventListener('close', closeListener);
|
|
13725
|
-
});
|
|
13726
|
-
}
|
|
13727
|
-
}
|
|
13728
|
-
|
|
13729
|
-
const doneResult = { done: true, value: undefined };
|
|
13730
|
-
function valueResult(value) {
|
|
13731
|
-
return { done: false, value };
|
|
13732
|
-
}
|
|
13733
|
-
/**
|
|
13734
|
-
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
13735
|
-
*
|
|
13736
|
-
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
13737
|
-
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
13738
|
-
*
|
|
13739
|
-
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
13740
|
-
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
13741
|
-
*/
|
|
13742
|
-
function injectable(source) {
|
|
13743
|
-
let sourceIsDone = false;
|
|
13744
|
-
let waiter = undefined; // An active, waiting next() call.
|
|
13745
|
-
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
13746
|
-
let pendingSourceEvent = null;
|
|
13747
|
-
let sourceFetchInFlight = false;
|
|
13748
|
-
let pendingInjectedEvents = [];
|
|
13749
|
-
const consumeWaiter = () => {
|
|
13750
|
-
const pending = waiter;
|
|
13751
|
-
waiter = undefined;
|
|
13752
|
-
return pending;
|
|
13753
|
-
};
|
|
13754
|
-
const fetchFromSource = () => {
|
|
13755
|
-
const resolveWaiter = (propagate) => {
|
|
13756
|
-
sourceFetchInFlight = false;
|
|
13757
|
-
const active = consumeWaiter();
|
|
13758
|
-
if (active) {
|
|
13759
|
-
propagate(active);
|
|
13760
|
-
}
|
|
13761
|
-
else {
|
|
13762
|
-
pendingSourceEvent = propagate;
|
|
13763
|
-
}
|
|
13764
|
-
};
|
|
13765
|
-
sourceFetchInFlight = true;
|
|
13766
|
-
const nextFromSource = source.next();
|
|
13767
|
-
nextFromSource.then((value) => {
|
|
13768
|
-
sourceIsDone = value.done == true;
|
|
13769
|
-
resolveWaiter((w) => w.resolve(value));
|
|
13770
|
-
}, (error) => {
|
|
13771
|
-
resolveWaiter((w) => w.reject(error));
|
|
13772
|
-
});
|
|
13773
|
-
};
|
|
13774
|
-
return {
|
|
13775
|
-
next: () => {
|
|
13776
|
-
return new Promise((resolve, reject) => {
|
|
13777
|
-
// First priority: Dispatch ready upstream events.
|
|
13778
|
-
if (sourceIsDone) {
|
|
13779
|
-
return resolve(doneResult);
|
|
13780
|
-
}
|
|
13781
|
-
if (pendingSourceEvent) {
|
|
13782
|
-
pendingSourceEvent({ resolve, reject });
|
|
13783
|
-
pendingSourceEvent = null;
|
|
13784
|
-
return;
|
|
13785
|
-
}
|
|
13786
|
-
// Second priority: Dispatch injected events
|
|
13787
|
-
if (pendingInjectedEvents.length) {
|
|
13788
|
-
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
13789
|
-
}
|
|
13790
|
-
// Nothing pending? Fetch from source
|
|
13791
|
-
waiter = { resolve, reject };
|
|
13792
|
-
if (!sourceFetchInFlight) {
|
|
13793
|
-
fetchFromSource();
|
|
13794
|
-
}
|
|
13795
|
-
});
|
|
13796
|
-
},
|
|
13797
|
-
inject: (event) => {
|
|
13798
|
-
const pending = consumeWaiter();
|
|
13799
|
-
if (pending != null) {
|
|
13800
|
-
pending.resolve(valueResult(event));
|
|
13801
|
-
}
|
|
13802
|
-
else {
|
|
13803
|
-
pendingInjectedEvents.push(event);
|
|
13804
|
-
}
|
|
13805
|
-
}
|
|
13806
|
-
};
|
|
13807
|
-
}
|
|
13808
|
-
/**
|
|
13809
|
-
* Splits a byte stream at line endings, emitting each line as a string.
|
|
13810
|
-
*/
|
|
13811
|
-
function extractJsonLines(source, decoder) {
|
|
13812
|
-
let buffer = '';
|
|
13813
|
-
const pendingLines = [];
|
|
13814
|
-
let isFinalEvent = false;
|
|
13815
|
-
return {
|
|
13816
|
-
next: async () => {
|
|
13817
|
-
while (true) {
|
|
13818
|
-
if (isFinalEvent) {
|
|
13819
|
-
return doneResult;
|
|
13820
|
-
}
|
|
13821
|
-
{
|
|
13822
|
-
const first = pendingLines.shift();
|
|
13823
|
-
if (first) {
|
|
13824
|
-
return { done: false, value: first };
|
|
13825
|
-
}
|
|
13826
|
-
}
|
|
13827
|
-
const { done, value } = await source.next();
|
|
13828
|
-
if (done) {
|
|
13829
|
-
const remaining = buffer.trim();
|
|
13830
|
-
if (remaining.length != 0) {
|
|
13831
|
-
isFinalEvent = true;
|
|
13832
|
-
return { done: false, value: remaining };
|
|
13833
|
-
}
|
|
13834
|
-
return doneResult;
|
|
13835
|
-
}
|
|
13836
|
-
const data = decoder.decode(value, { stream: true });
|
|
13837
|
-
buffer += data;
|
|
13838
|
-
const lines = buffer.split('\n');
|
|
13839
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
13840
|
-
const l = lines[i].trim();
|
|
13841
|
-
if (l.length > 0) {
|
|
13842
|
-
pendingLines.push(l);
|
|
13843
|
-
}
|
|
13844
|
-
}
|
|
13845
|
-
buffer = lines[lines.length - 1];
|
|
13846
|
-
}
|
|
13847
|
-
}
|
|
13848
|
-
};
|
|
13883
|
+
enumerable: false,
|
|
13884
|
+
configurable: true
|
|
13885
|
+
});
|
|
13886
|
+
WebsocketDuplexConnection.prototype.close = function (error) {
|
|
13887
|
+
if (this.done) {
|
|
13888
|
+
_super.prototype.close.call(this, error);
|
|
13889
|
+
return;
|
|
13890
|
+
}
|
|
13891
|
+
this.websocket.removeEventListener("close", this.handleClosed);
|
|
13892
|
+
this.websocket.removeEventListener("error", this.handleError);
|
|
13893
|
+
this.websocket.removeEventListener("message", this.handleMessage);
|
|
13894
|
+
this.websocket.close();
|
|
13895
|
+
delete this.websocket;
|
|
13896
|
+
_super.prototype.close.call(this, error);
|
|
13897
|
+
};
|
|
13898
|
+
WebsocketDuplexConnection.prototype.send = function (frame) {
|
|
13899
|
+
if (this.done) {
|
|
13900
|
+
return;
|
|
13901
|
+
}
|
|
13902
|
+
var buffer = (0, rsocket_core_1.serializeFrame)(frame);
|
|
13903
|
+
this.websocket.send(buffer);
|
|
13904
|
+
};
|
|
13905
|
+
return WebsocketDuplexConnection;
|
|
13906
|
+
}(rsocket_core_1.Deferred));
|
|
13907
|
+
WebsocketDuplexConnection.WebsocketDuplexConnection = WebsocketDuplexConnection$1;
|
|
13908
|
+
|
|
13909
|
+
return WebsocketDuplexConnection;
|
|
13849
13910
|
}
|
|
13911
|
+
|
|
13912
|
+
var WebsocketDuplexConnectionExports = requireWebsocketDuplexConnection();
|
|
13913
|
+
|
|
13850
13914
|
/**
|
|
13851
|
-
*
|
|
13915
|
+
* Adapted from rsocket-websocket-client
|
|
13916
|
+
* https://github.com/rsocket/rsocket-js/blob/e224cf379e747c4f1ddc4f2fa111854626cc8575/packages/rsocket-websocket-client/src/WebsocketClientTransport.ts#L17
|
|
13917
|
+
* This adds additional error handling for React Native iOS.
|
|
13918
|
+
* This particularly adds a close listener to handle cases where the WebSocket
|
|
13919
|
+
* connection closes immediately after opening without emitting an error.
|
|
13852
13920
|
*/
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13921
|
+
class WebsocketClientTransport {
|
|
13922
|
+
url;
|
|
13923
|
+
factory;
|
|
13924
|
+
constructor(options) {
|
|
13925
|
+
this.url = options.url;
|
|
13926
|
+
this.factory = options.wsCreator ?? ((url) => new WebSocket(url));
|
|
13927
|
+
}
|
|
13928
|
+
connect(multiplexerDemultiplexerFactory) {
|
|
13929
|
+
return new Promise((resolve, reject) => {
|
|
13930
|
+
const websocket = this.factory(this.url);
|
|
13931
|
+
websocket.binaryType = 'arraybuffer';
|
|
13932
|
+
let removeListeners;
|
|
13933
|
+
const openListener = () => {
|
|
13934
|
+
removeListeners();
|
|
13935
|
+
resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
|
|
13936
|
+
};
|
|
13937
|
+
const errorListener = (event) => {
|
|
13938
|
+
const ev = event;
|
|
13939
|
+
removeListeners();
|
|
13940
|
+
// We add a default error in that case.
|
|
13941
|
+
if (ev.error != null) {
|
|
13942
|
+
// undici typically provides an error object
|
|
13943
|
+
reject(ev.error);
|
|
13872
13944
|
}
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
if (objectBody || remainingLength != 4) {
|
|
13877
|
-
throw new Error('illegal end of stream in BSON object');
|
|
13878
|
-
}
|
|
13879
|
-
return doneResult;
|
|
13945
|
+
else if (ev.message != null) {
|
|
13946
|
+
// React Native typically does not provide an error object, but does provide a message
|
|
13947
|
+
reject(new Error(`Failed to create websocket connection: ${ev.message}`));
|
|
13880
13948
|
}
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
if (objectBody) {
|
|
13885
|
-
// We're in the middle of reading a BSON document.
|
|
13886
|
-
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
13887
|
-
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
13888
|
-
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
13889
|
-
i += bytesToRead;
|
|
13890
|
-
remainingLength -= bytesToRead;
|
|
13891
|
-
if (remainingLength == 0) {
|
|
13892
|
-
completedObjects.push(objectBody);
|
|
13893
|
-
// Prepare to read another document, starting with its length
|
|
13894
|
-
objectBody = null;
|
|
13895
|
-
remainingLength = 4;
|
|
13896
|
-
}
|
|
13897
|
-
}
|
|
13898
|
-
else {
|
|
13899
|
-
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
13900
|
-
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
13901
|
-
for (let j = 0; j < bytesToRead; j++) {
|
|
13902
|
-
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
13903
|
-
}
|
|
13904
|
-
i += bytesToRead;
|
|
13905
|
-
remainingLength -= bytesToRead;
|
|
13906
|
-
if (remainingLength == 0) {
|
|
13907
|
-
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
13908
|
-
// header is included in length.
|
|
13909
|
-
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
13910
|
-
remainingLength = length - 4;
|
|
13911
|
-
if (remainingLength < 1) {
|
|
13912
|
-
throw new Error(`invalid length for bson: ${length}`);
|
|
13913
|
-
}
|
|
13914
|
-
objectBody = new Uint8Array(length);
|
|
13915
|
-
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
13916
|
-
}
|
|
13917
|
-
}
|
|
13949
|
+
else {
|
|
13950
|
+
// Browsers often provide no details at all
|
|
13951
|
+
reject(new Error(`Failed to create websocket connection to ${this.url}`));
|
|
13918
13952
|
}
|
|
13919
|
-
}
|
|
13920
|
-
|
|
13921
|
-
|
|
13953
|
+
};
|
|
13954
|
+
/**
|
|
13955
|
+
* In some cases, such as React Native iOS, the WebSocket connection may close immediately after opening
|
|
13956
|
+
* without and error. In such cases, we need to handle the close event to reject the promise.
|
|
13957
|
+
*/
|
|
13958
|
+
const closeListener = () => {
|
|
13959
|
+
removeListeners();
|
|
13960
|
+
reject(new Error('WebSocket connection closed while opening'));
|
|
13961
|
+
};
|
|
13962
|
+
removeListeners = () => {
|
|
13963
|
+
websocket.removeEventListener('open', openListener);
|
|
13964
|
+
websocket.removeEventListener('error', errorListener);
|
|
13965
|
+
websocket.removeEventListener('close', closeListener);
|
|
13966
|
+
};
|
|
13967
|
+
websocket.addEventListener('open', openListener);
|
|
13968
|
+
websocket.addEventListener('error', errorListener);
|
|
13969
|
+
websocket.addEventListener('close', closeListener);
|
|
13970
|
+
});
|
|
13971
|
+
}
|
|
13922
13972
|
}
|
|
13923
13973
|
|
|
13924
13974
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
@@ -13933,7 +13983,13 @@ const SOCKET_TIMEOUT_MS = 30_000;
|
|
|
13933
13983
|
// If there is a backlog of messages (for example on slow connections), keepalive messages could be delayed
|
|
13934
13984
|
// significantly. Therefore this is longer than the socket timeout.
|
|
13935
13985
|
const KEEP_ALIVE_LIFETIME_MS = 90_000;
|
|
13986
|
+
/**
|
|
13987
|
+
* @internal
|
|
13988
|
+
*/
|
|
13936
13989
|
const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
|
|
13990
|
+
/**
|
|
13991
|
+
* @public
|
|
13992
|
+
*/
|
|
13937
13993
|
var FetchStrategy;
|
|
13938
13994
|
(function (FetchStrategy) {
|
|
13939
13995
|
/**
|
|
@@ -13952,12 +14008,17 @@ var FetchStrategy;
|
|
|
13952
14008
|
* The class wrapper is used to distinguish the fetchImplementation
|
|
13953
14009
|
* option in [AbstractRemoteOptions] from the general fetch method
|
|
13954
14010
|
* which is typeof "function"
|
|
14011
|
+
*
|
|
14012
|
+
* @internal
|
|
13955
14013
|
*/
|
|
13956
14014
|
class FetchImplementationProvider {
|
|
13957
14015
|
getFetch() {
|
|
13958
14016
|
throw new Error('Unspecified fetch implementation');
|
|
13959
14017
|
}
|
|
13960
14018
|
}
|
|
14019
|
+
/**
|
|
14020
|
+
* @internal
|
|
14021
|
+
*/
|
|
13961
14022
|
const DEFAULT_REMOTE_OPTIONS = {
|
|
13962
14023
|
socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
|
|
13963
14024
|
return match === 'https://' ? 'wss://' : 'ws://';
|
|
@@ -13965,6 +14026,9 @@ const DEFAULT_REMOTE_OPTIONS = {
|
|
|
13965
14026
|
fetchImplementation: new FetchImplementationProvider(),
|
|
13966
14027
|
fetchOptions: {}
|
|
13967
14028
|
};
|
|
14029
|
+
/**
|
|
14030
|
+
* @internal
|
|
14031
|
+
*/
|
|
13968
14032
|
class AbstractRemote {
|
|
13969
14033
|
connector;
|
|
13970
14034
|
logger;
|
|
@@ -14125,8 +14189,19 @@ class AbstractRemote {
|
|
|
14125
14189
|
let pendingSocket = null;
|
|
14126
14190
|
let keepAliveTimeout;
|
|
14127
14191
|
let rsocket = null;
|
|
14128
|
-
let
|
|
14192
|
+
let paused = false;
|
|
14193
|
+
const queue = new EventQueue({
|
|
14194
|
+
eventDelivered: () => {
|
|
14195
|
+
if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
|
|
14196
|
+
paused = false;
|
|
14197
|
+
requestMore();
|
|
14198
|
+
}
|
|
14199
|
+
}
|
|
14200
|
+
});
|
|
14129
14201
|
let didClose = false;
|
|
14202
|
+
let connectionEstablished = false;
|
|
14203
|
+
let pendingEventsCount = syncQueueRequestSize;
|
|
14204
|
+
let res = null;
|
|
14130
14205
|
const abortRequest = () => {
|
|
14131
14206
|
if (didClose) {
|
|
14132
14207
|
return;
|
|
@@ -14139,10 +14214,23 @@ class AbstractRemote {
|
|
|
14139
14214
|
if (rsocket) {
|
|
14140
14215
|
rsocket.close();
|
|
14141
14216
|
}
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14217
|
+
// Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
|
|
14218
|
+
// return a doneEvent.
|
|
14219
|
+
queue.notify(null);
|
|
14145
14220
|
};
|
|
14221
|
+
function push(event) {
|
|
14222
|
+
queue.notify(event);
|
|
14223
|
+
if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
|
|
14224
|
+
paused = true;
|
|
14225
|
+
}
|
|
14226
|
+
}
|
|
14227
|
+
function requestMore() {
|
|
14228
|
+
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
14229
|
+
if (!paused && delta > 0) {
|
|
14230
|
+
res?.request(delta);
|
|
14231
|
+
pendingEventsCount = syncQueueRequestSize;
|
|
14232
|
+
}
|
|
14233
|
+
}
|
|
14146
14234
|
// Handle upstream abort
|
|
14147
14235
|
if (options.abortSignal.aborted) {
|
|
14148
14236
|
throw new AbortOperation('Connection request aborted');
|
|
@@ -14197,25 +14285,19 @@ class AbstractRemote {
|
|
|
14197
14285
|
// Helps to prevent double close scenarios
|
|
14198
14286
|
rsocket.onClose(() => (rsocket = null));
|
|
14199
14287
|
return await new Promise((resolve, reject) => {
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14288
|
+
const queueAsIterator = {
|
|
14289
|
+
next: async () => {
|
|
14290
|
+
if (didClose)
|
|
14291
|
+
return doneResult;
|
|
14292
|
+
const notification = await queue.waitForEvent(options.abortSignal);
|
|
14293
|
+
if (didClose) {
|
|
14294
|
+
return doneResult;
|
|
14295
|
+
}
|
|
14296
|
+
else {
|
|
14297
|
+
return valueResult(notification);
|
|
14298
|
+
}
|
|
14209
14299
|
}
|
|
14210
|
-
}
|
|
14211
|
-
const events = new domExports.EventIterator((q) => {
|
|
14212
|
-
queue = q;
|
|
14213
|
-
q.on('highWater', () => (paused = true));
|
|
14214
|
-
q.on('lowWater', () => {
|
|
14215
|
-
paused = false;
|
|
14216
|
-
requestMore();
|
|
14217
|
-
});
|
|
14218
|
-
}, { highWaterMark: SYNC_QUEUE_REQUEST_HIGH_WATER, lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER })[symbolAsyncIterator]();
|
|
14300
|
+
};
|
|
14219
14301
|
res = rsocket.requestStream({
|
|
14220
14302
|
data: toBuffer(options.data),
|
|
14221
14303
|
metadata: toBuffer({
|
|
@@ -14251,11 +14333,11 @@ class AbstractRemote {
|
|
|
14251
14333
|
// The connection is active
|
|
14252
14334
|
if (!connectionEstablished) {
|
|
14253
14335
|
connectionEstablished = true;
|
|
14254
|
-
resolve(
|
|
14336
|
+
resolve(queueAsIterator);
|
|
14255
14337
|
}
|
|
14256
14338
|
const { data } = payload;
|
|
14257
14339
|
if (data) {
|
|
14258
|
-
|
|
14340
|
+
push(data);
|
|
14259
14341
|
}
|
|
14260
14342
|
// Less events are now pending
|
|
14261
14343
|
pendingEventsCount--;
|
|
@@ -14374,7 +14456,7 @@ class AbstractRemote {
|
|
|
14374
14456
|
* Posts a `/sync/stream` request.
|
|
14375
14457
|
*
|
|
14376
14458
|
* Depending on the `Content-Type` of the response, this returns strings for sync lines or encoded BSON documents as
|
|
14377
|
-
*
|
|
14459
|
+
* `Uint8Array`s.
|
|
14378
14460
|
*/
|
|
14379
14461
|
async fetchStream(options) {
|
|
14380
14462
|
const { isBson, stream } = await this.fetchStreamRaw(options);
|
|
@@ -14416,16 +14498,26 @@ function isInterruptingInstruction(instruction) {
|
|
|
14416
14498
|
return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
|
|
14417
14499
|
}
|
|
14418
14500
|
|
|
14501
|
+
/**
|
|
14502
|
+
* @internal
|
|
14503
|
+
*/
|
|
14419
14504
|
var LockType;
|
|
14420
14505
|
(function (LockType) {
|
|
14421
14506
|
LockType["CRUD"] = "crud";
|
|
14422
14507
|
LockType["SYNC"] = "sync";
|
|
14423
14508
|
})(LockType || (LockType = {}));
|
|
14509
|
+
/**
|
|
14510
|
+
* @public
|
|
14511
|
+
*/
|
|
14424
14512
|
var SyncStreamConnectionMethod;
|
|
14425
14513
|
(function (SyncStreamConnectionMethod) {
|
|
14426
14514
|
SyncStreamConnectionMethod["HTTP"] = "http";
|
|
14427
14515
|
SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
|
|
14428
14516
|
})(SyncStreamConnectionMethod || (SyncStreamConnectionMethod = {}));
|
|
14517
|
+
/**
|
|
14518
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
14519
|
+
* @public
|
|
14520
|
+
*/
|
|
14429
14521
|
var SyncClientImplementation;
|
|
14430
14522
|
(function (SyncClientImplementation) {
|
|
14431
14523
|
/**
|
|
@@ -14437,8 +14529,8 @@ var SyncClientImplementation;
|
|
|
14437
14529
|
* ## Compatibility warning
|
|
14438
14530
|
*
|
|
14439
14531
|
* The Rust sync client stores sync data in a format that is slightly different than the one used
|
|
14440
|
-
* by the old JavaScript client. When adopting the {@link RUST} client on existing databases,
|
|
14441
|
-
* migrate the format automatically.
|
|
14532
|
+
* by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
|
|
14533
|
+
* the PowerSync SDK will migrate the format automatically.
|
|
14442
14534
|
*
|
|
14443
14535
|
* SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
|
|
14444
14536
|
* implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
|
|
@@ -14448,14 +14540,29 @@ var SyncClientImplementation;
|
|
|
14448
14540
|
})(SyncClientImplementation || (SyncClientImplementation = {}));
|
|
14449
14541
|
/**
|
|
14450
14542
|
* The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
|
|
14543
|
+
*
|
|
14544
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
14545
|
+
* @public
|
|
14451
14546
|
*/
|
|
14452
14547
|
const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
|
|
14548
|
+
/**
|
|
14549
|
+
* @internal
|
|
14550
|
+
*/
|
|
14453
14551
|
const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
14552
|
+
/**
|
|
14553
|
+
* @internal
|
|
14554
|
+
*/
|
|
14454
14555
|
const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
14556
|
+
/**
|
|
14557
|
+
* @internal
|
|
14558
|
+
*/
|
|
14455
14559
|
const DEFAULT_STREAMING_SYNC_OPTIONS = {
|
|
14456
14560
|
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
14457
14561
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
14458
14562
|
};
|
|
14563
|
+
/**
|
|
14564
|
+
* @internal
|
|
14565
|
+
*/
|
|
14459
14566
|
const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
14460
14567
|
appMetadata: {},
|
|
14461
14568
|
connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
|
|
@@ -14465,6 +14572,9 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
|
14465
14572
|
serializedSchema: undefined,
|
|
14466
14573
|
includeDefaultStreams: true
|
|
14467
14574
|
};
|
|
14575
|
+
/**
|
|
14576
|
+
* @internal
|
|
14577
|
+
*/
|
|
14468
14578
|
class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
14469
14579
|
options;
|
|
14470
14580
|
abortController;
|
|
@@ -14788,7 +14898,7 @@ The next upload iteration will be delayed.`);
|
|
|
14788
14898
|
this.handleActiveStreamsChange?.();
|
|
14789
14899
|
}
|
|
14790
14900
|
/**
|
|
14791
|
-
* Older versions of the JS SDK used to encode subkeys as JSON in
|
|
14901
|
+
* Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
|
|
14792
14902
|
* Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
|
|
14793
14903
|
* While this is not a problem as long as it's done consistently, it causes issues when a database
|
|
14794
14904
|
* created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
|
|
@@ -14798,7 +14908,7 @@ The next upload iteration will be delayed.`);
|
|
|
14798
14908
|
* migration is only triggered when necessary (for now). The function returns whether the new format
|
|
14799
14909
|
* should be used, so that the JS SDK is able to write to updated databases.
|
|
14800
14910
|
*
|
|
14801
|
-
* @param requireFixedKeyFormat Whether we require the new format or also support the old one.
|
|
14911
|
+
* @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
|
|
14802
14912
|
* The Rust client requires the new subkey format.
|
|
14803
14913
|
* @returns Whether the database is now using the new, fixed subkey format.
|
|
14804
14914
|
*/
|
|
@@ -15105,7 +15215,8 @@ const MEMORY_TRIGGER_CLAIM_MANAGER = {
|
|
|
15105
15215
|
|
|
15106
15216
|
/**
|
|
15107
15217
|
* SQLite operations to track changes for with {@link TriggerManager}
|
|
15108
|
-
*
|
|
15218
|
+
*
|
|
15219
|
+
* @experimental @alpha
|
|
15109
15220
|
*/
|
|
15110
15221
|
var DiffTriggerOperation;
|
|
15111
15222
|
(function (DiffTriggerOperation) {
|
|
@@ -15167,8 +15278,8 @@ class TriggerManagerImpl {
|
|
|
15167
15278
|
get db() {
|
|
15168
15279
|
return this.options.db;
|
|
15169
15280
|
}
|
|
15170
|
-
async getUUID() {
|
|
15171
|
-
const { id: uuid } = await this.db.get(/* sql */ `
|
|
15281
|
+
async getUUID(ctx) {
|
|
15282
|
+
const { id: uuid } = await (ctx ?? this.db).get(/* sql */ `
|
|
15172
15283
|
SELECT
|
|
15173
15284
|
uuid () as id
|
|
15174
15285
|
`);
|
|
@@ -15281,7 +15392,7 @@ class TriggerManagerImpl {
|
|
|
15281
15392
|
const replicatedColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
|
|
15282
15393
|
const internalSource = sourceDefinition.internalName;
|
|
15283
15394
|
const triggerIds = [];
|
|
15284
|
-
const id = await this.getUUID();
|
|
15395
|
+
const id = await this.getUUID(setupContext);
|
|
15285
15396
|
const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
|
|
15286
15397
|
/**
|
|
15287
15398
|
* We default to replicating all columns if no columns array is provided.
|
|
@@ -15521,18 +15632,29 @@ const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
|
|
|
15521
15632
|
const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
|
|
15522
15633
|
clearLocal: true
|
|
15523
15634
|
};
|
|
15635
|
+
/**
|
|
15636
|
+
* @internal
|
|
15637
|
+
*/
|
|
15524
15638
|
const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
|
|
15525
15639
|
disconnect: true
|
|
15526
15640
|
};
|
|
15641
|
+
/**
|
|
15642
|
+
* @internal
|
|
15643
|
+
*/
|
|
15527
15644
|
const DEFAULT_POWERSYNC_DB_OPTIONS = {
|
|
15528
15645
|
retryDelayMs: 5000,
|
|
15529
15646
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
15530
15647
|
};
|
|
15648
|
+
/**
|
|
15649
|
+
* @internal
|
|
15650
|
+
*/
|
|
15531
15651
|
const DEFAULT_CRUD_BATCH_LIMIT = 100;
|
|
15532
15652
|
/**
|
|
15533
15653
|
* Requesting nested or recursive locks can block the application in some circumstances.
|
|
15534
15654
|
* This default lock timeout will act as a failsafe to throw an error if a lock cannot
|
|
15535
15655
|
* be obtained.
|
|
15656
|
+
*
|
|
15657
|
+
* @internal
|
|
15536
15658
|
*/
|
|
15537
15659
|
const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
15538
15660
|
/**
|
|
@@ -15542,6 +15664,9 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
|
15542
15664
|
const isPowerSyncDatabaseOptionsWithSettings = (test) => {
|
|
15543
15665
|
return typeof test == 'object' && isSQLOpenOptions(test.database);
|
|
15544
15666
|
};
|
|
15667
|
+
/**
|
|
15668
|
+
* @public
|
|
15669
|
+
*/
|
|
15545
15670
|
class AbstractPowerSyncDatabase extends BaseObserver {
|
|
15546
15671
|
options;
|
|
15547
15672
|
/**
|
|
@@ -15699,7 +15824,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15699
15824
|
/**
|
|
15700
15825
|
* Wait for the first sync operation to complete.
|
|
15701
15826
|
*
|
|
15702
|
-
* @param request Either an abort signal (after which the promise will complete regardless of
|
|
15827
|
+
* @param request - Either an abort signal (after which the promise will complete regardless of
|
|
15703
15828
|
* whether a full sync was completed) or an object providing an abort signal and a priority target.
|
|
15704
15829
|
* When a priority target is set, the promise may complete when all buckets with the given (or higher)
|
|
15705
15830
|
* priorities have been synchronized. This can be earlier than a complete sync.
|
|
@@ -15854,7 +15979,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15854
15979
|
/**
|
|
15855
15980
|
* Close the sync connection.
|
|
15856
15981
|
*
|
|
15857
|
-
* Use {@link connect} to connect again.
|
|
15982
|
+
* Use {@link AbstractPowerSyncDatabase.connect} to connect again.
|
|
15858
15983
|
*/
|
|
15859
15984
|
async disconnect() {
|
|
15860
15985
|
return this.connectionManager.disconnect();
|
|
@@ -15881,8 +16006,8 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15881
16006
|
/**
|
|
15882
16007
|
* Create a sync stream to query its status or to subscribe to it.
|
|
15883
16008
|
*
|
|
15884
|
-
* @param name The name of the stream to subscribe to.
|
|
15885
|
-
* @param params Optional parameters for the stream subscription.
|
|
16009
|
+
* @param name - The name of the stream to subscribe to.
|
|
16010
|
+
* @param params - Optional parameters for the stream subscription.
|
|
15886
16011
|
* @returns A {@link SyncStream} instance that can be subscribed to.
|
|
15887
16012
|
* @experimental Sync streams are currently in alpha.
|
|
15888
16013
|
*/
|
|
@@ -15940,14 +16065,14 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15940
16065
|
* Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
|
|
15941
16066
|
* requesting the next batch.
|
|
15942
16067
|
*
|
|
15943
|
-
* Use
|
|
16068
|
+
* Use the `limit` parameter to specify the maximum number of updates to return in a single
|
|
15944
16069
|
* batch.
|
|
15945
16070
|
*
|
|
15946
16071
|
* This method does include transaction ids in the result, but does not group
|
|
15947
16072
|
* data by transaction. One batch may contain data from multiple transactions,
|
|
15948
16073
|
* and a single transaction may be split over multiple batches.
|
|
15949
16074
|
*
|
|
15950
|
-
* @param limit Maximum number of CRUD entries to include in the batch
|
|
16075
|
+
* @param limit - Maximum number of CRUD entries to include in the batch
|
|
15951
16076
|
* @returns A batch of CRUD operations to upload, or null if there are none
|
|
15952
16077
|
*/
|
|
15953
16078
|
async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
|
|
@@ -15974,7 +16099,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15974
16099
|
* Once the data have been successfully uploaded, call {@link CrudTransaction.complete} before
|
|
15975
16100
|
* requesting the next transaction.
|
|
15976
16101
|
*
|
|
15977
|
-
* Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
|
|
16102
|
+
* Unlike {@link AbstractPowerSyncDatabase.getCrudBatch}, this only returns data from a single transaction at a time.
|
|
15978
16103
|
* All data for the transaction is loaded into memory.
|
|
15979
16104
|
*
|
|
15980
16105
|
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
@@ -15989,7 +16114,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15989
16114
|
* This is typically used from the {@link PowerSyncBackendConnector.uploadData} callback. Each entry emitted by the
|
|
15990
16115
|
* returned iterator is a full transaction containing all local writes made while that transaction was active.
|
|
15991
16116
|
*
|
|
15992
|
-
* Unlike {@link getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
16117
|
+
* Unlike {@link AbstractPowerSyncDatabase.getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
15993
16118
|
* {@link CrudTransaction.complete}d yet, this iterator can be used to receive multiple transactions. Calling
|
|
15994
16119
|
* {@link CrudTransaction.complete} will mark that and all prior transactions emitted by the iterator as completed.
|
|
15995
16120
|
*
|
|
@@ -16083,8 +16208,8 @@ SELECT * FROM crud_entries;
|
|
|
16083
16208
|
* the returned result's `rowsAffected` may be `0` for successful `UPDATE` and `DELETE` statements.
|
|
16084
16209
|
* Use a `RETURNING` clause and inspect `result.rows` when you need to confirm which rows changed.
|
|
16085
16210
|
*
|
|
16086
|
-
* @param sql The SQL query to execute
|
|
16087
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16211
|
+
* @param sql - The SQL query to execute
|
|
16212
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16088
16213
|
* @returns The query result as an object with structured key-value pairs
|
|
16089
16214
|
*/
|
|
16090
16215
|
async execute(sql, parameters) {
|
|
@@ -16094,8 +16219,8 @@ SELECT * FROM crud_entries;
|
|
|
16094
16219
|
* Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
|
|
16095
16220
|
* This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
|
|
16096
16221
|
*
|
|
16097
|
-
* @param sql The SQL query to execute
|
|
16098
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16222
|
+
* @param sql - The SQL query to execute
|
|
16223
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16099
16224
|
* @returns The raw query result from the underlying database as a nested array of raw values, where each row is
|
|
16100
16225
|
* represented as an array of column values without field names.
|
|
16101
16226
|
*/
|
|
@@ -16108,8 +16233,8 @@ SELECT * FROM crud_entries;
|
|
|
16108
16233
|
* and optionally return results.
|
|
16109
16234
|
* This is faster than executing separately with each parameter set.
|
|
16110
16235
|
*
|
|
16111
|
-
* @param sql The SQL query to execute
|
|
16112
|
-
* @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
16236
|
+
* @param sql - The SQL query to execute
|
|
16237
|
+
* @param parameters - Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
16113
16238
|
* @returns The query result
|
|
16114
16239
|
*/
|
|
16115
16240
|
async executeBatch(sql, parameters) {
|
|
@@ -16119,8 +16244,8 @@ SELECT * FROM crud_entries;
|
|
|
16119
16244
|
/**
|
|
16120
16245
|
* Execute a read-only query and return results.
|
|
16121
16246
|
*
|
|
16122
|
-
* @param sql The SQL query to execute
|
|
16123
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16247
|
+
* @param sql - The SQL query to execute
|
|
16248
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16124
16249
|
* @returns An array of results
|
|
16125
16250
|
*/
|
|
16126
16251
|
async getAll(sql, parameters) {
|
|
@@ -16130,8 +16255,8 @@ SELECT * FROM crud_entries;
|
|
|
16130
16255
|
/**
|
|
16131
16256
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
16132
16257
|
*
|
|
16133
|
-
* @param sql The SQL query to execute
|
|
16134
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16258
|
+
* @param sql - The SQL query to execute
|
|
16259
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16135
16260
|
* @returns The first result if found, or null if no results are returned
|
|
16136
16261
|
*/
|
|
16137
16262
|
async getOptional(sql, parameters) {
|
|
@@ -16141,8 +16266,8 @@ SELECT * FROM crud_entries;
|
|
|
16141
16266
|
/**
|
|
16142
16267
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
16143
16268
|
*
|
|
16144
|
-
* @param sql The SQL query to execute
|
|
16145
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16269
|
+
* @param sql - The SQL query to execute
|
|
16270
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16146
16271
|
* @returns The first result matching the query
|
|
16147
16272
|
* @throws Error if no rows are returned
|
|
16148
16273
|
*/
|
|
@@ -16152,7 +16277,7 @@ SELECT * FROM crud_entries;
|
|
|
16152
16277
|
}
|
|
16153
16278
|
/**
|
|
16154
16279
|
* Takes a read lock, without starting a transaction.
|
|
16155
|
-
* In most cases, {@link readTransaction} should be used instead.
|
|
16280
|
+
* In most cases, {@link AbstractPowerSyncDatabase.readTransaction} should be used instead.
|
|
16156
16281
|
*/
|
|
16157
16282
|
async readLock(callback) {
|
|
16158
16283
|
await this.waitForReady();
|
|
@@ -16160,7 +16285,7 @@ SELECT * FROM crud_entries;
|
|
|
16160
16285
|
}
|
|
16161
16286
|
/**
|
|
16162
16287
|
* Takes a global lock, without starting a transaction.
|
|
16163
|
-
* In most cases, {@link writeTransaction} should be used instead.
|
|
16288
|
+
* In most cases, {@link AbstractPowerSyncDatabase.writeTransaction} should be used instead.
|
|
16164
16289
|
*/
|
|
16165
16290
|
async writeLock(callback) {
|
|
16166
16291
|
await this.waitForReady();
|
|
@@ -16171,8 +16296,8 @@ SELECT * FROM crud_entries;
|
|
|
16171
16296
|
* Read transactions can run concurrently to a write transaction.
|
|
16172
16297
|
* Changes from any write transaction are not visible to read transactions started before it.
|
|
16173
16298
|
*
|
|
16174
|
-
* @param callback Function to execute within the transaction
|
|
16175
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
16299
|
+
* @param callback - Function to execute within the transaction
|
|
16300
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
16176
16301
|
* @returns The result of the callback
|
|
16177
16302
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
16178
16303
|
*/
|
|
@@ -16189,8 +16314,8 @@ SELECT * FROM crud_entries;
|
|
|
16189
16314
|
* This takes a global lock - only one write transaction can execute against the database at a time.
|
|
16190
16315
|
* Statements within the transaction must be done on the provided {@link Transaction} interface.
|
|
16191
16316
|
*
|
|
16192
|
-
* @param callback Function to execute within the transaction
|
|
16193
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
16317
|
+
* @param callback - Function to execute within the transaction
|
|
16318
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
16194
16319
|
* @returns The result of the callback
|
|
16195
16320
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
16196
16321
|
*/
|
|
@@ -16267,15 +16392,15 @@ SELECT * FROM crud_entries;
|
|
|
16267
16392
|
}
|
|
16268
16393
|
/**
|
|
16269
16394
|
* Execute a read query every time the source tables are modified.
|
|
16270
|
-
* Use {@link
|
|
16395
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
16271
16396
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
16272
16397
|
*
|
|
16273
16398
|
* Note that the `onChange` callback member of the handler is required.
|
|
16274
16399
|
*
|
|
16275
|
-
* @param sql The SQL query to execute
|
|
16276
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16277
|
-
* @param handler Callbacks for handling results and errors
|
|
16278
|
-
* @param options Options for configuring watch behavior
|
|
16400
|
+
* @param sql - The SQL query to execute
|
|
16401
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16402
|
+
* @param handler - Callbacks for handling results and errors
|
|
16403
|
+
* @param options - Options for configuring watch behavior
|
|
16279
16404
|
*/
|
|
16280
16405
|
watchWithCallback(sql, parameters, handler, options) {
|
|
16281
16406
|
const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
|
|
@@ -16288,7 +16413,7 @@ SELECT * FROM crud_entries;
|
|
|
16288
16413
|
const watchedQuery = new OnChangeQueryProcessor({
|
|
16289
16414
|
db: this,
|
|
16290
16415
|
comparator,
|
|
16291
|
-
placeholderData: null,
|
|
16416
|
+
placeholderData: null, // FIXME
|
|
16292
16417
|
watchOptions: {
|
|
16293
16418
|
query: {
|
|
16294
16419
|
compile: () => ({
|
|
@@ -16321,38 +16446,35 @@ SELECT * FROM crud_entries;
|
|
|
16321
16446
|
}
|
|
16322
16447
|
/**
|
|
16323
16448
|
* Execute a read query every time the source tables are modified.
|
|
16324
|
-
* Use {@link
|
|
16449
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
16325
16450
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
16326
16451
|
*
|
|
16327
|
-
* @param sql The SQL query to execute
|
|
16328
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
16329
|
-
* @param options Options for configuring watch behavior
|
|
16452
|
+
* @param sql - The SQL query to execute
|
|
16453
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
16454
|
+
* @param options - Options for configuring watch behavior
|
|
16330
16455
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
16331
16456
|
*/
|
|
16332
16457
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
16333
|
-
return
|
|
16458
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
16334
16459
|
const handler = {
|
|
16335
16460
|
onResult: (result) => {
|
|
16336
|
-
|
|
16461
|
+
queue.notify(result);
|
|
16337
16462
|
},
|
|
16338
16463
|
onError: (error) => {
|
|
16339
|
-
|
|
16464
|
+
queue.notifyError(error);
|
|
16340
16465
|
}
|
|
16341
16466
|
};
|
|
16342
|
-
this.watchWithCallback(sql, parameters, handler, options);
|
|
16343
|
-
|
|
16344
|
-
eventOptions.stop();
|
|
16345
|
-
});
|
|
16346
|
-
});
|
|
16467
|
+
this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
|
|
16468
|
+
}, options?.signal);
|
|
16347
16469
|
}
|
|
16348
16470
|
/**
|
|
16349
16471
|
* Resolves the list of tables that are used in a SQL query.
|
|
16350
16472
|
* If tables are specified in the options, those are used directly.
|
|
16351
16473
|
* Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
|
|
16352
16474
|
*
|
|
16353
|
-
* @param sql The SQL query to analyze
|
|
16354
|
-
* @param parameters Optional parameters for the SQL query
|
|
16355
|
-
* @param options Optional watch options that may contain explicit table list
|
|
16475
|
+
* @param sql - The SQL query to analyze
|
|
16476
|
+
* @param parameters - Optional parameters for the SQL query
|
|
16477
|
+
* @param options - Optional watch options that may contain explicit table list
|
|
16356
16478
|
* @returns Array of table names that the query depends on
|
|
16357
16479
|
*/
|
|
16358
16480
|
async resolveTables(sql, parameters, options) {
|
|
@@ -16381,13 +16503,13 @@ SELECT * FROM crud_entries;
|
|
|
16381
16503
|
/**
|
|
16382
16504
|
* Invoke the provided callback on any changes to any of the specified tables.
|
|
16383
16505
|
*
|
|
16384
|
-
* This is preferred over {@link watchWithCallback} when multiple queries need to be performed
|
|
16506
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithCallback} when multiple queries need to be performed
|
|
16385
16507
|
* together when data is changed.
|
|
16386
16508
|
*
|
|
16387
16509
|
* Note that the `onChange` callback member of the handler is required.
|
|
16388
16510
|
*
|
|
16389
|
-
* @param handler Callbacks for handling change events and errors
|
|
16390
|
-
* @param options Options for configuring watch behavior
|
|
16511
|
+
* @param handler - Callbacks for handling change events and errors
|
|
16512
|
+
* @param options - Options for configuring watch behavior
|
|
16391
16513
|
* @returns A dispose function to stop watching for changes
|
|
16392
16514
|
*/
|
|
16393
16515
|
onChangeWithCallback(handler, options) {
|
|
@@ -16430,31 +16552,26 @@ SELECT * FROM crud_entries;
|
|
|
16430
16552
|
/**
|
|
16431
16553
|
* Create a Stream of changes to any of the specified tables.
|
|
16432
16554
|
*
|
|
16433
|
-
* This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be
|
|
16434
|
-
* together when data is changed.
|
|
16435
|
-
*
|
|
16436
|
-
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
16555
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
16556
|
+
* performed together when data is changed.
|
|
16437
16557
|
*
|
|
16438
|
-
* @param options Options for configuring watch behavior
|
|
16558
|
+
* @param options - Options for configuring watch behavior
|
|
16439
16559
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
16440
16560
|
*/
|
|
16561
|
+
// Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
16441
16562
|
onChangeWithAsyncGenerator(options) {
|
|
16442
|
-
|
|
16443
|
-
|
|
16444
|
-
const dispose = this.onChangeWithCallback({
|
|
16563
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
16564
|
+
this.onChangeWithCallback({
|
|
16445
16565
|
onChange: (event) => {
|
|
16446
|
-
|
|
16566
|
+
queue.notify(event);
|
|
16447
16567
|
},
|
|
16448
16568
|
onError: (error) => {
|
|
16449
|
-
|
|
16569
|
+
queue.notifyError(error);
|
|
16450
16570
|
}
|
|
16451
|
-
}, options);
|
|
16452
|
-
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
});
|
|
16456
|
-
return () => dispose();
|
|
16457
|
-
});
|
|
16571
|
+
}, { ...options, signal: abort });
|
|
16572
|
+
// Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
|
|
16573
|
+
// after the abort signal completes.
|
|
16574
|
+
}, options?.signal);
|
|
16458
16575
|
}
|
|
16459
16576
|
handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
|
|
16460
16577
|
if (changedTables.size > 0) {
|
|
@@ -16473,15 +16590,15 @@ SELECT * FROM crud_entries;
|
|
|
16473
16590
|
changedTables.add(table);
|
|
16474
16591
|
}
|
|
16475
16592
|
}
|
|
16476
|
-
/**
|
|
16477
|
-
* @ignore
|
|
16478
|
-
*/
|
|
16479
16593
|
async executeReadOnly(sql, params) {
|
|
16480
16594
|
await this.waitForReady();
|
|
16481
16595
|
return this.database.readLock((tx) => tx.execute(sql, params));
|
|
16482
16596
|
}
|
|
16483
16597
|
}
|
|
16484
16598
|
|
|
16599
|
+
/**
|
|
16600
|
+
* @internal
|
|
16601
|
+
*/
|
|
16485
16602
|
class AbstractPowerSyncDatabaseOpenFactory {
|
|
16486
16603
|
options;
|
|
16487
16604
|
constructor(options) {
|
|
@@ -16506,6 +16623,9 @@ class AbstractPowerSyncDatabaseOpenFactory {
|
|
|
16506
16623
|
}
|
|
16507
16624
|
}
|
|
16508
16625
|
|
|
16626
|
+
/**
|
|
16627
|
+
* @internal
|
|
16628
|
+
*/
|
|
16509
16629
|
function runOnSchemaChange(callback, db, options) {
|
|
16510
16630
|
const triggerWatchedQuery = () => {
|
|
16511
16631
|
const abortController = new AbortController();
|
|
@@ -16530,6 +16650,9 @@ function runOnSchemaChange(callback, db, options) {
|
|
|
16530
16650
|
triggerWatchedQuery();
|
|
16531
16651
|
}
|
|
16532
16652
|
|
|
16653
|
+
/**
|
|
16654
|
+
* @public
|
|
16655
|
+
*/
|
|
16533
16656
|
function compilableQueryWatch(db, query, handler, options) {
|
|
16534
16657
|
const { onResult, onError = (e) => { } } = handler ?? {};
|
|
16535
16658
|
if (!onResult) {
|
|
@@ -16567,8 +16690,14 @@ function compilableQueryWatch(db, query, handler, options) {
|
|
|
16567
16690
|
runOnSchemaChange(watchQuery, db, options);
|
|
16568
16691
|
}
|
|
16569
16692
|
|
|
16693
|
+
/**
|
|
16694
|
+
* @internal
|
|
16695
|
+
*/
|
|
16570
16696
|
const MAX_OP_ID = '9223372036854775807';
|
|
16571
16697
|
|
|
16698
|
+
/**
|
|
16699
|
+
* @internal
|
|
16700
|
+
*/
|
|
16572
16701
|
class SqliteBucketStorage extends BaseObserver {
|
|
16573
16702
|
db;
|
|
16574
16703
|
logger;
|
|
@@ -16729,6 +16858,8 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
16729
16858
|
* Thrown when an underlying database connection is closed.
|
|
16730
16859
|
* This is particularly relevant when worker connections are marked as closed while
|
|
16731
16860
|
* operations are still in progress.
|
|
16861
|
+
*
|
|
16862
|
+
* @internal
|
|
16732
16863
|
*/
|
|
16733
16864
|
class ConnectionClosedError extends Error {
|
|
16734
16865
|
static NAME = 'ConnectionClosedError';
|
|
@@ -16748,6 +16879,8 @@ class ConnectionClosedError extends Error {
|
|
|
16748
16879
|
|
|
16749
16880
|
/**
|
|
16750
16881
|
* A schema is a collection of tables. It is used to define the structure of a database.
|
|
16882
|
+
*
|
|
16883
|
+
* @public
|
|
16751
16884
|
*/
|
|
16752
16885
|
class Schema {
|
|
16753
16886
|
/*
|
|
@@ -16784,7 +16917,7 @@ class Schema {
|
|
|
16784
16917
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
16785
16918
|
* using client-side table and column constraints.
|
|
16786
16919
|
*
|
|
16787
|
-
* @param tables An object of (table name, raw table definition) entries.
|
|
16920
|
+
* @param tables - An object of (table name, raw table definition) entries.
|
|
16788
16921
|
*/
|
|
16789
16922
|
withRawTables(tables) {
|
|
16790
16923
|
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
@@ -16830,6 +16963,8 @@ class Schema {
|
|
|
16830
16963
|
Generate a new table from the columns and indexes
|
|
16831
16964
|
@deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
|
|
16832
16965
|
This will be removed in the next major release.
|
|
16966
|
+
|
|
16967
|
+
@public
|
|
16833
16968
|
*/
|
|
16834
16969
|
class TableV2 extends Table {
|
|
16835
16970
|
}
|
|
@@ -16840,6 +16975,8 @@ function sanitizeString(input) {
|
|
|
16840
16975
|
/**
|
|
16841
16976
|
* Helper function for sanitizing UUID input strings.
|
|
16842
16977
|
* Typically used with {@link sanitizeSQL}.
|
|
16978
|
+
*
|
|
16979
|
+
* @alpha
|
|
16843
16980
|
*/
|
|
16844
16981
|
function sanitizeUUID(uuid) {
|
|
16845
16982
|
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;
|
|
@@ -16876,6 +17013,8 @@ function sanitizeUUID(uuid) {
|
|
|
16876
17013
|
* // Incorrect:
|
|
16877
17014
|
* sanitizeSQL`New.id = '${myID}'` // Produces double quotes: New.id = ''O''Reilly''
|
|
16878
17015
|
* ```
|
|
17016
|
+
*
|
|
17017
|
+
* @alpha
|
|
16879
17018
|
*/
|
|
16880
17019
|
function sanitizeSQL(strings, ...values) {
|
|
16881
17020
|
let result = '';
|
|
@@ -16905,6 +17044,8 @@ function sanitizeSQL(strings, ...values) {
|
|
|
16905
17044
|
|
|
16906
17045
|
/**
|
|
16907
17046
|
* Performs a {@link AbstractPowerSyncDatabase.getAll} operation for a watched query.
|
|
17047
|
+
*
|
|
17048
|
+
* @public
|
|
16908
17049
|
*/
|
|
16909
17050
|
class GetAllQuery {
|
|
16910
17051
|
options;
|
|
@@ -16929,6 +17070,9 @@ class GetAllQuery {
|
|
|
16929
17070
|
}
|
|
16930
17071
|
|
|
16931
17072
|
const TypedLogger = Logger;
|
|
17073
|
+
/**
|
|
17074
|
+
* @public
|
|
17075
|
+
*/
|
|
16932
17076
|
const LogLevel = {
|
|
16933
17077
|
TRACE: TypedLogger.TRACE,
|
|
16934
17078
|
DEBUG: TypedLogger.DEBUG,
|
|
@@ -16945,6 +17089,7 @@ const LogLevel = {
|
|
|
16945
17089
|
* across all loggers created with `createLogger`. Adjusting settings on this
|
|
16946
17090
|
* base logger affects all loggers derived from it unless explicitly overridden.
|
|
16947
17091
|
*
|
|
17092
|
+
* @public
|
|
16948
17093
|
*/
|
|
16949
17094
|
function createBaseLogger() {
|
|
16950
17095
|
return Logger;
|
|
@@ -16955,6 +17100,8 @@ function createBaseLogger() {
|
|
|
16955
17100
|
* Named loggers allow specific modules or areas of your application to have
|
|
16956
17101
|
* their own logging levels and behaviors. These loggers inherit configuration
|
|
16957
17102
|
* from the base logger by default but can override settings independently.
|
|
17103
|
+
*
|
|
17104
|
+
* @public
|
|
16958
17105
|
*/
|
|
16959
17106
|
function createLogger(name, options = {}) {
|
|
16960
17107
|
const logger = Logger.get(name);
|
|
@@ -16964,6 +17111,9 @@ function createLogger(name, options = {}) {
|
|
|
16964
17111
|
return logger;
|
|
16965
17112
|
}
|
|
16966
17113
|
|
|
17114
|
+
/**
|
|
17115
|
+
* @internal
|
|
17116
|
+
*/
|
|
16967
17117
|
const parseQuery = (query, parameters) => {
|
|
16968
17118
|
let sqlStatement;
|
|
16969
17119
|
if (typeof query == 'string') {
|