@powersync/common 1.53.2 → 1.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.cjs +922 -772
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +922 -772
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +923 -619
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +923 -619
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +749 -205
- package/lib/attachments/AttachmentContext.d.ts +7 -6
- package/lib/attachments/AttachmentContext.js +2 -1
- package/lib/attachments/AttachmentContext.js.map +1 -1
- package/lib/attachments/AttachmentErrorHandler.d.ts +6 -6
- package/lib/attachments/AttachmentQueue.d.ts +82 -33
- package/lib/attachments/AttachmentQueue.js +16 -18
- package/lib/attachments/AttachmentQueue.js.map +1 -1
- package/lib/attachments/LocalStorageAdapter.d.ts +14 -8
- package/lib/attachments/LocalStorageAdapter.js +3 -0
- package/lib/attachments/LocalStorageAdapter.js.map +1 -1
- package/lib/attachments/RemoteStorageAdapter.d.ts +4 -4
- package/lib/attachments/Schema.d.ts +12 -4
- package/lib/attachments/Schema.js +8 -3
- package/lib/attachments/Schema.js.map +1 -1
- package/lib/attachments/WatchedAttachmentItem.d.ts +3 -1
- package/lib/client/AbstractPowerSyncDatabase.d.ts +110 -60
- package/lib/client/AbstractPowerSyncDatabase.js +77 -74
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
- package/lib/client/AbstractPowerSyncOpenFactory.d.ts +6 -0
- package/lib/client/AbstractPowerSyncOpenFactory.js +3 -0
- package/lib/client/AbstractPowerSyncOpenFactory.js.map +1 -1
- package/lib/client/ConnectionManager.d.ts +4 -1
- package/lib/client/ConnectionManager.js +1 -1
- package/lib/client/ConnectionManager.js.map +1 -1
- package/lib/client/Query.d.ts +9 -0
- package/lib/client/SQLOpenFactory.d.ts +12 -0
- package/lib/client/SQLOpenFactory.js +6 -0
- package/lib/client/SQLOpenFactory.js.map +1 -1
- package/lib/client/compilableQueryWatch.d.ts +6 -0
- package/lib/client/compilableQueryWatch.js +3 -0
- package/lib/client/compilableQueryWatch.js.map +1 -1
- package/lib/client/connection/PowerSyncBackendConnector.d.ts +3 -0
- package/lib/client/connection/PowerSyncCredentials.d.ts +3 -0
- package/lib/client/constants.d.ts +3 -0
- package/lib/client/constants.js +3 -0
- package/lib/client/constants.js.map +1 -1
- package/lib/client/runOnSchemaChange.d.ts +3 -0
- package/lib/client/runOnSchemaChange.js +3 -0
- package/lib/client/runOnSchemaChange.js.map +1 -1
- package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +12 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js +6 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -1
- package/lib/client/sync/bucket/CrudBatch.d.ts +2 -0
- package/lib/client/sync/bucket/CrudBatch.js +2 -0
- package/lib/client/sync/bucket/CrudBatch.js.map +1 -1
- package/lib/client/sync/bucket/CrudEntry.d.ts +9 -0
- package/lib/client/sync/bucket/CrudEntry.js +4 -0
- package/lib/client/sync/bucket/CrudEntry.js.map +1 -1
- package/lib/client/sync/bucket/CrudTransaction.d.ts +3 -0
- package/lib/client/sync/bucket/CrudTransaction.js +3 -0
- package/lib/client/sync/bucket/CrudTransaction.js.map +1 -1
- package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +3 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js +3 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.d.ts +30 -1
- package/lib/client/sync/stream/AbstractRemote.js +59 -27
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +55 -5
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +32 -4
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
- package/lib/client/sync/stream/JsonValue.d.ts +3 -0
- package/lib/client/sync/stream/WebsocketClientTransport.js +2 -1
- package/lib/client/sync/stream/WebsocketClientTransport.js.map +1 -1
- package/lib/client/sync/sync-streams.d.ts +22 -7
- package/lib/client/triggers/TriggerManager.d.ts +19 -18
- package/lib/client/triggers/TriggerManager.js +2 -1
- package/lib/client/triggers/TriggerManager.js.map +1 -1
- package/lib/client/triggers/TriggerManagerImpl.d.ts +1 -1
- package/lib/client/triggers/TriggerManagerImpl.js +3 -3
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
- package/lib/client/triggers/sanitizeSQL.d.ts +4 -0
- package/lib/client/triggers/sanitizeSQL.js +4 -0
- package/lib/client/triggers/sanitizeSQL.js.map +1 -1
- package/lib/client/watched/GetAllQuery.d.ts +4 -0
- package/lib/client/watched/GetAllQuery.js +2 -0
- package/lib/client/watched/GetAllQuery.js.map +1 -1
- package/lib/client/watched/WatchedQuery.d.ts +24 -2
- package/lib/client/watched/WatchedQuery.js +9 -0
- package/lib/client/watched/WatchedQuery.js.map +1 -1
- package/lib/client/watched/processors/AbstractQueryProcessor.d.ts +1 -1
- package/lib/client/watched/processors/AbstractQueryProcessor.js.map +1 -1
- package/lib/client/watched/processors/DifferentialQueryProcessor.d.ts +20 -0
- package/lib/client/watched/processors/DifferentialQueryProcessor.js +4 -0
- package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -1
- package/lib/client/watched/processors/OnChangeQueryProcessor.d.ts +4 -0
- package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -1
- package/lib/client/watched/processors/comparators.d.ts +8 -0
- package/lib/client/watched/processors/comparators.js +4 -0
- package/lib/client/watched/processors/comparators.js.map +1 -1
- package/lib/db/ConnectionClosedError.d.ts +2 -0
- package/lib/db/ConnectionClosedError.js +2 -0
- package/lib/db/ConnectionClosedError.js.map +1 -1
- package/lib/db/DBAdapter.d.ts +56 -6
- package/lib/db/DBAdapter.js +15 -3
- package/lib/db/DBAdapter.js.map +1 -1
- package/lib/db/crud/SyncProgress.d.ts +6 -1
- package/lib/db/crud/SyncProgress.js +2 -0
- package/lib/db/crud/SyncProgress.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +36 -38
- package/lib/db/crud/SyncStatus.js +19 -14
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/lib/db/crud/UploadQueueStatus.d.ts +3 -0
- package/lib/db/crud/UploadQueueStatus.js +3 -0
- package/lib/db/crud/UploadQueueStatus.js.map +1 -1
- package/lib/db/schema/Column.d.ts +28 -0
- package/lib/db/schema/Column.js +16 -3
- package/lib/db/schema/Column.js.map +1 -1
- package/lib/db/schema/Index.d.ts +9 -0
- package/lib/db/schema/Index.js +6 -0
- package/lib/db/schema/Index.js.map +1 -1
- package/lib/db/schema/IndexedColumn.d.ts +9 -0
- package/lib/db/schema/IndexedColumn.js +6 -0
- package/lib/db/schema/IndexedColumn.js.map +1 -1
- package/lib/db/schema/RawTable.d.ts +7 -1
- package/lib/db/schema/Schema.d.ts +6 -1
- package/lib/db/schema/Schema.js +3 -1
- package/lib/db/schema/Schema.js.map +1 -1
- package/lib/db/schema/Table.d.ts +27 -3
- package/lib/db/schema/Table.js +9 -0
- package/lib/db/schema/Table.js.map +1 -1
- package/lib/db/schema/TableV2.d.ts +2 -0
- package/lib/db/schema/TableV2.js +2 -0
- package/lib/db/schema/TableV2.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/types/types.d.ts +6 -0
- package/lib/utils/AbortOperation.d.ts +2 -0
- package/lib/utils/AbortOperation.js +2 -0
- package/lib/utils/AbortOperation.js.map +1 -1
- package/lib/utils/BaseObserver.d.ts +12 -0
- package/lib/utils/BaseObserver.js +3 -0
- package/lib/utils/BaseObserver.js.map +1 -1
- package/lib/utils/ControlledExecutor.d.ts +6 -0
- package/lib/utils/ControlledExecutor.js +3 -0
- package/lib/utils/ControlledExecutor.js.map +1 -1
- package/lib/utils/Logger.d.ts +9 -0
- package/lib/utils/Logger.js +6 -0
- package/lib/utils/Logger.js.map +1 -1
- package/lib/utils/async.d.ts +26 -0
- package/lib/utils/async.js +114 -27
- package/lib/utils/async.js.map +1 -1
- package/lib/utils/compatibility.d.ts +8 -0
- package/lib/utils/compatibility.js +9 -0
- package/lib/utils/compatibility.js.map +1 -0
- package/lib/utils/mutex.d.ts +8 -0
- package/lib/utils/mutex.js +3 -0
- package/lib/utils/mutex.js.map +1 -1
- package/lib/utils/parseQuery.d.ts +6 -0
- package/lib/utils/parseQuery.js +3 -0
- package/lib/utils/parseQuery.js.map +1 -1
- package/lib/utils/stream_transform.d.ts +3 -1
- package/lib/utils/stream_transform.js.map +1 -1
- package/package.json +3 -3
- package/src/attachments/AttachmentContext.ts +7 -6
- package/src/attachments/AttachmentErrorHandler.ts +6 -6
- package/src/attachments/AttachmentQueue.ts +93 -35
- package/src/attachments/LocalStorageAdapter.ts +14 -8
- package/src/attachments/README.md +2 -0
- package/src/attachments/RemoteStorageAdapter.ts +4 -4
- package/src/attachments/Schema.ts +12 -4
- package/src/attachments/WatchedAttachmentItem.ts +3 -1
- package/src/client/AbstractPowerSyncDatabase.ts +135 -91
- package/src/client/AbstractPowerSyncOpenFactory.ts +6 -0
- package/src/client/ConnectionManager.ts +4 -1
- package/src/client/Query.ts +9 -0
- package/src/client/SQLOpenFactory.ts +12 -0
- package/src/client/compilableQueryWatch.ts +6 -0
- package/src/client/connection/PowerSyncBackendConnector.ts +3 -0
- package/src/client/connection/PowerSyncCredentials.ts +3 -0
- package/src/client/constants.ts +3 -0
- package/src/client/runOnSchemaChange.ts +3 -0
- package/src/client/sync/bucket/BucketStorageAdapter.ts +12 -0
- package/src/client/sync/bucket/CrudBatch.ts +2 -0
- package/src/client/sync/bucket/CrudEntry.ts +9 -0
- package/src/client/sync/bucket/CrudTransaction.ts +3 -0
- package/src/client/sync/bucket/SqliteBucketStorage.ts +3 -0
- package/src/client/sync/stream/AbstractRemote.ts +76 -34
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +55 -5
- package/src/client/sync/stream/JsonValue.ts +3 -0
- package/src/client/sync/stream/WebsocketClientTransport.ts +3 -1
- package/src/client/sync/sync-streams.ts +22 -9
- package/src/client/triggers/TriggerManager.ts +19 -18
- package/src/client/triggers/TriggerManagerImpl.ts +5 -5
- package/src/client/triggers/sanitizeSQL.ts +5 -0
- package/src/client/watched/GetAllQuery.ts +5 -1
- package/src/client/watched/WatchedQuery.ts +24 -2
- package/src/client/watched/processors/AbstractQueryProcessor.ts +6 -6
- package/src/client/watched/processors/DifferentialQueryProcessor.ts +28 -5
- package/src/client/watched/processors/OnChangeQueryProcessor.ts +9 -3
- package/src/client/watched/processors/comparators.ts +8 -0
- package/src/db/ConnectionClosedError.ts +2 -0
- package/src/db/DBAdapter.ts +58 -6
- package/src/db/crud/SyncProgress.ts +6 -1
- package/src/db/crud/SyncStatus.ts +40 -21
- package/src/db/crud/UploadQueueStatus.ts +3 -0
- package/src/db/schema/Column.ts +28 -3
- package/src/db/schema/Index.ts +9 -0
- package/src/db/schema/IndexedColumn.ts +9 -0
- package/src/db/schema/RawTable.ts +7 -1
- package/src/db/schema/Schema.ts +8 -3
- package/src/db/schema/Table.ts +30 -5
- package/src/db/schema/TableV2.ts +2 -0
- package/src/index.ts +1 -1
- package/src/types/types.ts +6 -0
- package/src/utils/AbortOperation.ts +2 -0
- package/src/utils/BaseObserver.ts +12 -0
- package/src/utils/ControlledExecutor.ts +6 -0
- package/src/utils/Logger.ts +9 -0
- package/src/utils/async.ts +136 -28
- package/src/utils/compatibility.ts +9 -0
- package/src/utils/mutex.ts +12 -0
- package/src/utils/parseQuery.ts +6 -0
- package/src/utils/stream_transform.ts +3 -1
package/dist/bundle.cjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @see https://www.sqlite.org/lang_expr.html#castexpr
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
4
7
|
exports.ColumnType = void 0;
|
|
5
8
|
(function (ColumnType) {
|
|
6
9
|
ColumnType["TEXT"] = "TEXT";
|
|
@@ -16,14 +19,24 @@ const integer = {
|
|
|
16
19
|
const real = {
|
|
17
20
|
type: exports.ColumnType.REAL
|
|
18
21
|
};
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
/**
|
|
23
|
+
* powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
|
|
24
|
+
* In earlier versions this was limited to 63.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
21
28
|
const MAX_AMOUNT_OF_COLUMNS = 1999;
|
|
29
|
+
/**
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
22
32
|
const column = {
|
|
23
33
|
text,
|
|
24
34
|
integer,
|
|
25
35
|
real
|
|
26
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
27
40
|
class Column {
|
|
28
41
|
options;
|
|
29
42
|
constructor(options) {
|
|
@@ -43,9 +56,15 @@ class Column {
|
|
|
43
56
|
}
|
|
44
57
|
}
|
|
45
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
46
62
|
const DEFAULT_INDEX_COLUMN_OPTIONS = {
|
|
47
63
|
ascending: true
|
|
48
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
49
68
|
class IndexedColumn {
|
|
50
69
|
options;
|
|
51
70
|
static createAscending(column) {
|
|
@@ -72,9 +91,15 @@ class IndexedColumn {
|
|
|
72
91
|
}
|
|
73
92
|
}
|
|
74
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
75
97
|
const DEFAULT_INDEX_OPTIONS = {
|
|
76
98
|
columns: []
|
|
77
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* @public
|
|
102
|
+
*/
|
|
78
103
|
class Index {
|
|
79
104
|
options;
|
|
80
105
|
static createAscending(options, columnNames) {
|
|
@@ -116,6 +141,9 @@ function encodeTableOptions(options) {
|
|
|
116
141
|
};
|
|
117
142
|
}
|
|
118
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
119
147
|
const DEFAULT_TABLE_OPTIONS = {
|
|
120
148
|
indexes: [],
|
|
121
149
|
insertOnly: false,
|
|
@@ -124,7 +152,13 @@ const DEFAULT_TABLE_OPTIONS = {
|
|
|
124
152
|
trackMetadata: false,
|
|
125
153
|
ignoreEmptyUpdates: false
|
|
126
154
|
};
|
|
155
|
+
/**
|
|
156
|
+
* @internal
|
|
157
|
+
*/
|
|
127
158
|
const InvalidSQLCharacters = /["'%,.#\s[\]]/;
|
|
159
|
+
/**
|
|
160
|
+
* @public
|
|
161
|
+
*/
|
|
128
162
|
class Table {
|
|
129
163
|
options;
|
|
130
164
|
_mappedColumns;
|
|
@@ -315,6 +349,11 @@ class Table {
|
|
|
315
349
|
}
|
|
316
350
|
}
|
|
317
351
|
|
|
352
|
+
/**
|
|
353
|
+
* The default name of the local table storing attachment data.
|
|
354
|
+
*
|
|
355
|
+
* @alpha
|
|
356
|
+
*/
|
|
318
357
|
const ATTACHMENT_TABLE = 'attachments';
|
|
319
358
|
/**
|
|
320
359
|
* Maps a database row to an AttachmentRecord.
|
|
@@ -322,7 +361,7 @@ const ATTACHMENT_TABLE = 'attachments';
|
|
|
322
361
|
* @param row - The database row object
|
|
323
362
|
* @returns The corresponding AttachmentRecord
|
|
324
363
|
*
|
|
325
|
-
* @
|
|
364
|
+
* @alpha
|
|
326
365
|
*/
|
|
327
366
|
function attachmentFromSql(row) {
|
|
328
367
|
return {
|
|
@@ -340,7 +379,7 @@ function attachmentFromSql(row) {
|
|
|
340
379
|
/**
|
|
341
380
|
* AttachmentState represents the current synchronization state of an attachment.
|
|
342
381
|
*
|
|
343
|
-
* @
|
|
382
|
+
* @alpha
|
|
344
383
|
*/
|
|
345
384
|
exports.AttachmentState = void 0;
|
|
346
385
|
(function (AttachmentState) {
|
|
@@ -353,7 +392,7 @@ exports.AttachmentState = void 0;
|
|
|
353
392
|
/**
|
|
354
393
|
* AttachmentTable defines the schema for the attachment queue table.
|
|
355
394
|
*
|
|
356
|
-
* @
|
|
395
|
+
* @alpha
|
|
357
396
|
*/
|
|
358
397
|
class AttachmentTable extends Table {
|
|
359
398
|
constructor(options) {
|
|
@@ -381,7 +420,8 @@ class AttachmentTable extends Table {
|
|
|
381
420
|
* Provides methods to query, insert, update, and delete attachment records with
|
|
382
421
|
* proper transaction management through PowerSync.
|
|
383
422
|
*
|
|
384
|
-
* @
|
|
423
|
+
* @experimental
|
|
424
|
+
* @alpha
|
|
385
425
|
*/
|
|
386
426
|
class AttachmentContext {
|
|
387
427
|
/** PowerSync database instance for executing queries */
|
|
@@ -603,6 +643,9 @@ class AttachmentContext {
|
|
|
603
643
|
}
|
|
604
644
|
}
|
|
605
645
|
|
|
646
|
+
/**
|
|
647
|
+
* @public
|
|
648
|
+
*/
|
|
606
649
|
exports.WatchedQueryListenerEvent = void 0;
|
|
607
650
|
(function (WatchedQueryListenerEvent) {
|
|
608
651
|
WatchedQueryListenerEvent["ON_DATA"] = "onData";
|
|
@@ -611,176 +654,18 @@ exports.WatchedQueryListenerEvent = void 0;
|
|
|
611
654
|
WatchedQueryListenerEvent["SETTINGS_WILL_UPDATE"] = "settingsWillUpdate";
|
|
612
655
|
WatchedQueryListenerEvent["CLOSED"] = "closed";
|
|
613
656
|
})(exports.WatchedQueryListenerEvent || (exports.WatchedQueryListenerEvent = {}));
|
|
657
|
+
/**
|
|
658
|
+
* @internal
|
|
659
|
+
*/
|
|
614
660
|
const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
661
|
+
/**
|
|
662
|
+
* @internal
|
|
663
|
+
*/
|
|
615
664
|
const DEFAULT_WATCH_QUERY_OPTIONS = {
|
|
616
665
|
throttleMs: DEFAULT_WATCH_THROTTLE_MS,
|
|
617
666
|
reportFetching: true
|
|
618
667
|
};
|
|
619
668
|
|
|
620
|
-
/**
|
|
621
|
-
* Orchestrates attachment synchronization between local and remote storage.
|
|
622
|
-
* Handles uploads, downloads, deletions, and state transitions.
|
|
623
|
-
*
|
|
624
|
-
* @internal
|
|
625
|
-
*/
|
|
626
|
-
class SyncingService {
|
|
627
|
-
attachmentService;
|
|
628
|
-
localStorage;
|
|
629
|
-
remoteStorage;
|
|
630
|
-
logger;
|
|
631
|
-
errorHandler;
|
|
632
|
-
constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
|
|
633
|
-
this.attachmentService = attachmentService;
|
|
634
|
-
this.localStorage = localStorage;
|
|
635
|
-
this.remoteStorage = remoteStorage;
|
|
636
|
-
this.logger = logger;
|
|
637
|
-
this.errorHandler = errorHandler;
|
|
638
|
-
}
|
|
639
|
-
/**
|
|
640
|
-
* Processes attachments based on their state (upload, download, or delete).
|
|
641
|
-
* All updates are saved in a single batch after processing.
|
|
642
|
-
*
|
|
643
|
-
* @param attachments - Array of attachment records to process
|
|
644
|
-
* @param context - Attachment context for database operations
|
|
645
|
-
* @returns Promise that resolves when all attachments have been processed and saved
|
|
646
|
-
*/
|
|
647
|
-
async processAttachments(attachments, context) {
|
|
648
|
-
const updatedAttachments = [];
|
|
649
|
-
for (const attachment of attachments) {
|
|
650
|
-
switch (attachment.state) {
|
|
651
|
-
case exports.AttachmentState.QUEUED_UPLOAD:
|
|
652
|
-
const uploaded = await this.uploadAttachment(attachment);
|
|
653
|
-
updatedAttachments.push(uploaded);
|
|
654
|
-
break;
|
|
655
|
-
case exports.AttachmentState.QUEUED_DOWNLOAD:
|
|
656
|
-
const downloaded = await this.downloadAttachment(attachment);
|
|
657
|
-
updatedAttachments.push(downloaded);
|
|
658
|
-
break;
|
|
659
|
-
case exports.AttachmentState.QUEUED_DELETE:
|
|
660
|
-
const deleted = await this.deleteAttachment(attachment, context);
|
|
661
|
-
updatedAttachments.push(deleted);
|
|
662
|
-
break;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
await context.saveAttachments(updatedAttachments);
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Uploads an attachment from local storage to remote storage.
|
|
669
|
-
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
670
|
-
*
|
|
671
|
-
* @param attachment - The attachment record to upload
|
|
672
|
-
* @returns Updated attachment record with new state
|
|
673
|
-
* @throws Error if the attachment has no localUri
|
|
674
|
-
*/
|
|
675
|
-
async uploadAttachment(attachment) {
|
|
676
|
-
this.logger.info(`Uploading attachment ${attachment.filename}`);
|
|
677
|
-
try {
|
|
678
|
-
if (attachment.localUri == null) {
|
|
679
|
-
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
680
|
-
}
|
|
681
|
-
const fileBlob = await this.localStorage.readFile(attachment.localUri);
|
|
682
|
-
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
683
|
-
return {
|
|
684
|
-
...attachment,
|
|
685
|
-
state: exports.AttachmentState.SYNCED,
|
|
686
|
-
hasSynced: true
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
catch (error) {
|
|
690
|
-
const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
|
|
691
|
-
if (!shouldRetry) {
|
|
692
|
-
return {
|
|
693
|
-
...attachment,
|
|
694
|
-
state: exports.AttachmentState.ARCHIVED
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
return attachment;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* Downloads an attachment from remote storage to local storage.
|
|
702
|
-
* Retrieves the file, converts to base64, and saves locally.
|
|
703
|
-
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
704
|
-
*
|
|
705
|
-
* @param attachment - The attachment record to download
|
|
706
|
-
* @returns Updated attachment record with local URI and new state
|
|
707
|
-
*/
|
|
708
|
-
async downloadAttachment(attachment) {
|
|
709
|
-
this.logger.info(`Downloading attachment ${attachment.filename}`);
|
|
710
|
-
try {
|
|
711
|
-
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
712
|
-
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
713
|
-
await this.localStorage.saveFile(localUri, fileData);
|
|
714
|
-
return {
|
|
715
|
-
...attachment,
|
|
716
|
-
state: exports.AttachmentState.SYNCED,
|
|
717
|
-
localUri: localUri,
|
|
718
|
-
hasSynced: true
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
catch (error) {
|
|
722
|
-
const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
|
|
723
|
-
if (!shouldRetry) {
|
|
724
|
-
return {
|
|
725
|
-
...attachment,
|
|
726
|
-
state: exports.AttachmentState.ARCHIVED
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
return attachment;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* Deletes an attachment from both remote and local storage.
|
|
734
|
-
* Removes the remote file, local file (if exists), and the attachment record.
|
|
735
|
-
* On failure, defers to error handler or archives.
|
|
736
|
-
*
|
|
737
|
-
* @param attachment - The attachment record to delete
|
|
738
|
-
* @param context - Attachment context for database operations
|
|
739
|
-
* @returns Updated attachment record
|
|
740
|
-
*/
|
|
741
|
-
async deleteAttachment(attachment, context) {
|
|
742
|
-
try {
|
|
743
|
-
await this.remoteStorage.deleteFile(attachment);
|
|
744
|
-
if (attachment.localUri) {
|
|
745
|
-
await this.localStorage.deleteFile(attachment.localUri);
|
|
746
|
-
}
|
|
747
|
-
await context.deleteAttachment(attachment.id);
|
|
748
|
-
return {
|
|
749
|
-
...attachment,
|
|
750
|
-
state: exports.AttachmentState.ARCHIVED
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
catch (error) {
|
|
754
|
-
const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
|
|
755
|
-
if (!shouldRetry) {
|
|
756
|
-
return {
|
|
757
|
-
...attachment,
|
|
758
|
-
state: exports.AttachmentState.ARCHIVED
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
return attachment;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
|
-
* Performs cleanup of archived attachments by removing their local files and records.
|
|
766
|
-
* Errors during local file deletion are logged but do not prevent record deletion.
|
|
767
|
-
*/
|
|
768
|
-
async deleteArchivedAttachments(context) {
|
|
769
|
-
return await context.deleteArchivedAttachments(async (archivedAttachments) => {
|
|
770
|
-
for (const attachment of archivedAttachments) {
|
|
771
|
-
if (attachment.localUri) {
|
|
772
|
-
try {
|
|
773
|
-
await this.localStorage.deleteFile(attachment.localUri);
|
|
774
|
-
}
|
|
775
|
-
catch (error) {
|
|
776
|
-
this.logger.error('Error deleting local file for archived attachment', error);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
|
|
784
669
|
/**
|
|
785
670
|
* A simple fixed-capacity queue implementation.
|
|
786
671
|
*
|
|
@@ -966,6 +851,9 @@ class Mutex {
|
|
|
966
851
|
}
|
|
967
852
|
}
|
|
968
853
|
}
|
|
854
|
+
/**
|
|
855
|
+
* @internal
|
|
856
|
+
*/
|
|
969
857
|
function timeoutSignal(timeout) {
|
|
970
858
|
if (timeout == null)
|
|
971
859
|
return;
|
|
@@ -1028,6 +916,170 @@ class AttachmentService {
|
|
|
1028
916
|
}
|
|
1029
917
|
}
|
|
1030
918
|
|
|
919
|
+
/**
|
|
920
|
+
* Orchestrates attachment synchronization between local and remote storage.
|
|
921
|
+
* Handles uploads, downloads, deletions, and state transitions.
|
|
922
|
+
*
|
|
923
|
+
* @internal
|
|
924
|
+
*/
|
|
925
|
+
class SyncingService {
|
|
926
|
+
attachmentService;
|
|
927
|
+
localStorage;
|
|
928
|
+
remoteStorage;
|
|
929
|
+
logger;
|
|
930
|
+
errorHandler;
|
|
931
|
+
constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
|
|
932
|
+
this.attachmentService = attachmentService;
|
|
933
|
+
this.localStorage = localStorage;
|
|
934
|
+
this.remoteStorage = remoteStorage;
|
|
935
|
+
this.logger = logger;
|
|
936
|
+
this.errorHandler = errorHandler;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Processes attachments based on their state (upload, download, or delete).
|
|
940
|
+
* All updates are saved in a single batch after processing.
|
|
941
|
+
*
|
|
942
|
+
* @param attachments - Array of attachment records to process
|
|
943
|
+
* @param context - Attachment context for database operations
|
|
944
|
+
* @returns Promise that resolves when all attachments have been processed and saved
|
|
945
|
+
*/
|
|
946
|
+
async processAttachments(attachments, context) {
|
|
947
|
+
const updatedAttachments = [];
|
|
948
|
+
for (const attachment of attachments) {
|
|
949
|
+
switch (attachment.state) {
|
|
950
|
+
case exports.AttachmentState.QUEUED_UPLOAD:
|
|
951
|
+
const uploaded = await this.uploadAttachment(attachment);
|
|
952
|
+
updatedAttachments.push(uploaded);
|
|
953
|
+
break;
|
|
954
|
+
case exports.AttachmentState.QUEUED_DOWNLOAD:
|
|
955
|
+
const downloaded = await this.downloadAttachment(attachment);
|
|
956
|
+
updatedAttachments.push(downloaded);
|
|
957
|
+
break;
|
|
958
|
+
case exports.AttachmentState.QUEUED_DELETE:
|
|
959
|
+
const deleted = await this.deleteAttachment(attachment, context);
|
|
960
|
+
updatedAttachments.push(deleted);
|
|
961
|
+
break;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
await context.saveAttachments(updatedAttachments);
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Uploads an attachment from local storage to remote storage.
|
|
968
|
+
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
969
|
+
*
|
|
970
|
+
* @param attachment - The attachment record to upload
|
|
971
|
+
* @returns Updated attachment record with new state
|
|
972
|
+
* @throws Error if the attachment has no localUri
|
|
973
|
+
*/
|
|
974
|
+
async uploadAttachment(attachment) {
|
|
975
|
+
this.logger.info(`Uploading attachment ${attachment.filename}`);
|
|
976
|
+
try {
|
|
977
|
+
if (attachment.localUri == null) {
|
|
978
|
+
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
979
|
+
}
|
|
980
|
+
const fileBlob = await this.localStorage.readFile(attachment.localUri);
|
|
981
|
+
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
982
|
+
return {
|
|
983
|
+
...attachment,
|
|
984
|
+
state: exports.AttachmentState.SYNCED,
|
|
985
|
+
hasSynced: true
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
catch (error) {
|
|
989
|
+
const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
|
|
990
|
+
if (!shouldRetry) {
|
|
991
|
+
return {
|
|
992
|
+
...attachment,
|
|
993
|
+
state: exports.AttachmentState.ARCHIVED
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
return attachment;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Downloads an attachment from remote storage to local storage.
|
|
1001
|
+
* Retrieves the file, converts to base64, and saves locally.
|
|
1002
|
+
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
1003
|
+
*
|
|
1004
|
+
* @param attachment - The attachment record to download
|
|
1005
|
+
* @returns Updated attachment record with local URI and new state
|
|
1006
|
+
*/
|
|
1007
|
+
async downloadAttachment(attachment) {
|
|
1008
|
+
this.logger.info(`Downloading attachment ${attachment.filename}`);
|
|
1009
|
+
try {
|
|
1010
|
+
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
1011
|
+
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
1012
|
+
await this.localStorage.saveFile(localUri, fileData);
|
|
1013
|
+
return {
|
|
1014
|
+
...attachment,
|
|
1015
|
+
state: exports.AttachmentState.SYNCED,
|
|
1016
|
+
localUri: localUri,
|
|
1017
|
+
hasSynced: true
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
catch (error) {
|
|
1021
|
+
const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
|
|
1022
|
+
if (!shouldRetry) {
|
|
1023
|
+
return {
|
|
1024
|
+
...attachment,
|
|
1025
|
+
state: exports.AttachmentState.ARCHIVED
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
return attachment;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Deletes an attachment from both remote and local storage.
|
|
1033
|
+
* Removes the remote file, local file (if exists), and the attachment record.
|
|
1034
|
+
* On failure, defers to error handler or archives.
|
|
1035
|
+
*
|
|
1036
|
+
* @param attachment - The attachment record to delete
|
|
1037
|
+
* @param context - Attachment context for database operations
|
|
1038
|
+
* @returns Updated attachment record
|
|
1039
|
+
*/
|
|
1040
|
+
async deleteAttachment(attachment, context) {
|
|
1041
|
+
try {
|
|
1042
|
+
await this.remoteStorage.deleteFile(attachment);
|
|
1043
|
+
if (attachment.localUri) {
|
|
1044
|
+
await this.localStorage.deleteFile(attachment.localUri);
|
|
1045
|
+
}
|
|
1046
|
+
await context.deleteAttachment(attachment.id);
|
|
1047
|
+
return {
|
|
1048
|
+
...attachment,
|
|
1049
|
+
state: exports.AttachmentState.ARCHIVED
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
catch (error) {
|
|
1053
|
+
const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
|
|
1054
|
+
if (!shouldRetry) {
|
|
1055
|
+
return {
|
|
1056
|
+
...attachment,
|
|
1057
|
+
state: exports.AttachmentState.ARCHIVED
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
return attachment;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Performs cleanup of archived attachments by removing their local files and records.
|
|
1065
|
+
* Errors during local file deletion are logged but do not prevent record deletion.
|
|
1066
|
+
*/
|
|
1067
|
+
async deleteArchivedAttachments(context) {
|
|
1068
|
+
return await context.deleteArchivedAttachments(async (archivedAttachments) => {
|
|
1069
|
+
for (const attachment of archivedAttachments) {
|
|
1070
|
+
if (attachment.localUri) {
|
|
1071
|
+
try {
|
|
1072
|
+
await this.localStorage.deleteFile(attachment.localUri);
|
|
1073
|
+
}
|
|
1074
|
+
catch (error) {
|
|
1075
|
+
this.logger.error('Error deleting local file for archived attachment', error);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1031
1083
|
/**
|
|
1032
1084
|
* AttachmentQueue manages the lifecycle and synchronization of attachments
|
|
1033
1085
|
* between local and remote storage.
|
|
@@ -1084,16 +1136,6 @@ class AttachmentQueue {
|
|
|
1084
1136
|
* Creates a new AttachmentQueue instance.
|
|
1085
1137
|
*
|
|
1086
1138
|
* @param options - Configuration options
|
|
1087
|
-
* @param options.db - PowerSync database instance
|
|
1088
|
-
* @param options.remoteStorage - Remote storage adapter for upload/download operations
|
|
1089
|
-
* @param options.localStorage - Local storage adapter for file persistence
|
|
1090
|
-
* @param options.watchAttachments - Callback for monitoring attachment changes in your data model
|
|
1091
|
-
* @param options.tableName - Name of the table to store attachment records. Default: 'ps_attachment_queue'
|
|
1092
|
-
* @param options.logger - Logger instance. Defaults to db.logger
|
|
1093
|
-
* @param options.syncIntervalMs - Periodic polling interval in milliseconds for retrying failed uploads/downloads. Default: 30000
|
|
1094
|
-
* @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
|
|
1095
|
-
* @param options.downloadAttachments - Whether to automatically download remote attachments. Default: true
|
|
1096
|
-
* @param options.archivedCacheLimit - Maximum archived attachments before cleanup. Default: 100
|
|
1097
1139
|
*/
|
|
1098
1140
|
constructor({ db, localStorage, remoteStorage, watchAttachments, logger, tableName = ATTACHMENT_TABLE, syncIntervalMs = 30 * 1000, syncThrottleDuration = DEFAULT_WATCH_THROTTLE_MS, downloadAttachments = true, archivedCacheLimit = 100, errorHandler }) {
|
|
1099
1141
|
this.db = db;
|
|
@@ -1182,6 +1224,7 @@ class AttachmentQueue {
|
|
|
1182
1224
|
state: exports.AttachmentState.QUEUED_DOWNLOAD,
|
|
1183
1225
|
hasSynced: false,
|
|
1184
1226
|
metaData: watchedAttachment.metaData,
|
|
1227
|
+
mediaType: watchedAttachment.mediaType,
|
|
1185
1228
|
timestamp: new Date().getTime()
|
|
1186
1229
|
});
|
|
1187
1230
|
continue;
|
|
@@ -1269,17 +1312,24 @@ class AttachmentQueue {
|
|
|
1269
1312
|
this.statusListenerDispose = undefined;
|
|
1270
1313
|
}
|
|
1271
1314
|
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Provides an {@link AttachmentContext} to a callback.
|
|
1317
|
+
*
|
|
1318
|
+
* The callback runs while the attachment queue mutex is held. Do not call
|
|
1319
|
+
* other {@link AttachmentQueue} methods from within the callback, as they may
|
|
1320
|
+
* attempt to acquire the same mutex and block indefinitely.
|
|
1321
|
+
*/
|
|
1322
|
+
withAttachmentContext(callback) {
|
|
1323
|
+
/**
|
|
1324
|
+
* AttachmentService is internal and private in this class.
|
|
1325
|
+
* We only need to expose its locking and context functionality for extending classes.
|
|
1326
|
+
*/
|
|
1327
|
+
return this.attachmentService.withContext(callback);
|
|
1328
|
+
}
|
|
1272
1329
|
/**
|
|
1273
1330
|
* Saves a file to local storage and queues it for upload to remote storage.
|
|
1274
1331
|
*
|
|
1275
1332
|
* @param options - File save options
|
|
1276
|
-
* @param options.data - The file data as ArrayBuffer, Blob, or base64 string
|
|
1277
|
-
* @param options.fileExtension - File extension (e.g., 'jpg', 'pdf')
|
|
1278
|
-
* @param options.mediaType - MIME type of the file (e.g., 'image/jpeg')
|
|
1279
|
-
* @param options.metaData - Optional metadata to associate with the attachment
|
|
1280
|
-
* @param options.id - Optional custom ID. If not provided, a UUID will be generated
|
|
1281
|
-
* @param options.updateHook - Optional callback to execute additional database operations
|
|
1282
|
-
* within the same transaction as the attachment creation
|
|
1283
1333
|
* @returns Promise resolving to the created attachment record
|
|
1284
1334
|
*/
|
|
1285
1335
|
async saveFile({ data, fileExtension, mediaType, metaData, id, updateHook }) {
|
|
@@ -1392,171 +1442,19 @@ class AttachmentQueue {
|
|
|
1392
1442
|
}
|
|
1393
1443
|
}
|
|
1394
1444
|
|
|
1445
|
+
/**
|
|
1446
|
+
* @alpha
|
|
1447
|
+
*/
|
|
1395
1448
|
exports.EncodingType = void 0;
|
|
1396
1449
|
(function (EncodingType) {
|
|
1397
1450
|
EncodingType["UTF8"] = "utf8";
|
|
1398
1451
|
EncodingType["Base64"] = "base64";
|
|
1399
1452
|
})(exports.EncodingType || (exports.EncodingType = {}));
|
|
1400
1453
|
|
|
1401
|
-
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
1402
|
-
|
|
1403
1454
|
function getDefaultExportFromCjs (x) {
|
|
1404
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
var dom = {};
|
|
1408
|
-
|
|
1409
|
-
var eventIterator = {};
|
|
1410
|
-
|
|
1411
|
-
var hasRequiredEventIterator;
|
|
1412
|
-
|
|
1413
|
-
function requireEventIterator () {
|
|
1414
|
-
if (hasRequiredEventIterator) return eventIterator;
|
|
1415
|
-
hasRequiredEventIterator = 1;
|
|
1416
|
-
Object.defineProperty(eventIterator, "__esModule", { value: true });
|
|
1417
|
-
class EventQueue {
|
|
1418
|
-
constructor() {
|
|
1419
|
-
this.pullQueue = [];
|
|
1420
|
-
this.pushQueue = [];
|
|
1421
|
-
this.eventHandlers = {};
|
|
1422
|
-
this.isPaused = false;
|
|
1423
|
-
this.isStopped = false;
|
|
1424
|
-
}
|
|
1425
|
-
push(value) {
|
|
1426
|
-
if (this.isStopped)
|
|
1427
|
-
return;
|
|
1428
|
-
const resolution = { value, done: false };
|
|
1429
|
-
if (this.pullQueue.length) {
|
|
1430
|
-
const placeholder = this.pullQueue.shift();
|
|
1431
|
-
if (placeholder)
|
|
1432
|
-
placeholder.resolve(resolution);
|
|
1433
|
-
}
|
|
1434
|
-
else {
|
|
1435
|
-
this.pushQueue.push(Promise.resolve(resolution));
|
|
1436
|
-
if (this.highWaterMark !== undefined &&
|
|
1437
|
-
this.pushQueue.length >= this.highWaterMark &&
|
|
1438
|
-
!this.isPaused) {
|
|
1439
|
-
this.isPaused = true;
|
|
1440
|
-
if (this.eventHandlers.highWater) {
|
|
1441
|
-
this.eventHandlers.highWater();
|
|
1442
|
-
}
|
|
1443
|
-
else if (console) {
|
|
1444
|
-
console.warn(`EventIterator queue reached ${this.pushQueue.length} items`);
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
stop() {
|
|
1450
|
-
if (this.isStopped)
|
|
1451
|
-
return;
|
|
1452
|
-
this.isStopped = true;
|
|
1453
|
-
this.remove();
|
|
1454
|
-
for (const placeholder of this.pullQueue) {
|
|
1455
|
-
placeholder.resolve({ value: undefined, done: true });
|
|
1456
|
-
}
|
|
1457
|
-
this.pullQueue.length = 0;
|
|
1458
|
-
}
|
|
1459
|
-
fail(error) {
|
|
1460
|
-
if (this.isStopped)
|
|
1461
|
-
return;
|
|
1462
|
-
this.isStopped = true;
|
|
1463
|
-
this.remove();
|
|
1464
|
-
if (this.pullQueue.length) {
|
|
1465
|
-
for (const placeholder of this.pullQueue) {
|
|
1466
|
-
placeholder.reject(error);
|
|
1467
|
-
}
|
|
1468
|
-
this.pullQueue.length = 0;
|
|
1469
|
-
}
|
|
1470
|
-
else {
|
|
1471
|
-
const rejection = Promise.reject(error);
|
|
1472
|
-
/* Attach error handler to avoid leaking an unhandled promise rejection. */
|
|
1473
|
-
rejection.catch(() => { });
|
|
1474
|
-
this.pushQueue.push(rejection);
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
remove() {
|
|
1478
|
-
Promise.resolve().then(() => {
|
|
1479
|
-
if (this.removeCallback)
|
|
1480
|
-
this.removeCallback();
|
|
1481
|
-
});
|
|
1482
|
-
}
|
|
1483
|
-
[symbolAsyncIterator]() {
|
|
1484
|
-
return {
|
|
1485
|
-
next: (value) => {
|
|
1486
|
-
const result = this.pushQueue.shift();
|
|
1487
|
-
if (result) {
|
|
1488
|
-
if (this.lowWaterMark !== undefined &&
|
|
1489
|
-
this.pushQueue.length <= this.lowWaterMark &&
|
|
1490
|
-
this.isPaused) {
|
|
1491
|
-
this.isPaused = false;
|
|
1492
|
-
if (this.eventHandlers.lowWater) {
|
|
1493
|
-
this.eventHandlers.lowWater();
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
return result;
|
|
1497
|
-
}
|
|
1498
|
-
else if (this.isStopped) {
|
|
1499
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
1500
|
-
}
|
|
1501
|
-
else {
|
|
1502
|
-
return new Promise((resolve, reject) => {
|
|
1503
|
-
this.pullQueue.push({ resolve, reject });
|
|
1504
|
-
});
|
|
1505
|
-
}
|
|
1506
|
-
},
|
|
1507
|
-
return: () => {
|
|
1508
|
-
this.isStopped = true;
|
|
1509
|
-
this.pushQueue.length = 0;
|
|
1510
|
-
this.remove();
|
|
1511
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
1512
|
-
},
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
class EventIterator {
|
|
1517
|
-
constructor(listen, { highWaterMark = 100, lowWaterMark = 1 } = {}) {
|
|
1518
|
-
const queue = new EventQueue();
|
|
1519
|
-
queue.highWaterMark = highWaterMark;
|
|
1520
|
-
queue.lowWaterMark = lowWaterMark;
|
|
1521
|
-
queue.removeCallback =
|
|
1522
|
-
listen({
|
|
1523
|
-
push: value => queue.push(value),
|
|
1524
|
-
stop: () => queue.stop(),
|
|
1525
|
-
fail: error => queue.fail(error),
|
|
1526
|
-
on: (event, fn) => {
|
|
1527
|
-
queue.eventHandlers[event] = fn;
|
|
1528
|
-
},
|
|
1529
|
-
}) || (() => { });
|
|
1530
|
-
this[symbolAsyncIterator] = () => queue[symbolAsyncIterator]();
|
|
1531
|
-
Object.freeze(this);
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
eventIterator.EventIterator = EventIterator;
|
|
1535
|
-
eventIterator.default = EventIterator;
|
|
1536
|
-
return eventIterator;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
var hasRequiredDom;
|
|
1540
|
-
|
|
1541
|
-
function requireDom () {
|
|
1542
|
-
if (hasRequiredDom) return dom;
|
|
1543
|
-
hasRequiredDom = 1;
|
|
1544
|
-
Object.defineProperty(dom, "__esModule", { value: true });
|
|
1545
|
-
const event_iterator_1 = requireEventIterator();
|
|
1546
|
-
dom.EventIterator = event_iterator_1.EventIterator;
|
|
1547
|
-
function subscribe(event, options, evOptions) {
|
|
1548
|
-
return new event_iterator_1.EventIterator(({ push }) => {
|
|
1549
|
-
this.addEventListener(event, push, options);
|
|
1550
|
-
return () => this.removeEventListener(event, push, options);
|
|
1551
|
-
}, evOptions);
|
|
1552
|
-
}
|
|
1553
|
-
dom.subscribe = subscribe;
|
|
1554
|
-
dom.default = event_iterator_1.EventIterator;
|
|
1555
|
-
return dom;
|
|
1455
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1556
1456
|
}
|
|
1557
1457
|
|
|
1558
|
-
var domExports = requireDom();
|
|
1559
|
-
|
|
1560
1458
|
var logger$1 = {exports: {}};
|
|
1561
1459
|
|
|
1562
1460
|
/*!
|
|
@@ -1855,7 +1753,9 @@ var Logger = /*@__PURE__*/getDefaultExportFromCjs(loggerExports);
|
|
|
1855
1753
|
* different SQLite DB implementations.
|
|
1856
1754
|
*/
|
|
1857
1755
|
/**
|
|
1858
|
-
* Implements {@link DBGetUtils} on a {@link
|
|
1756
|
+
* Implements {@link DBGetUtils} on a {@link SqlExecutor}.
|
|
1757
|
+
*
|
|
1758
|
+
* @internal
|
|
1859
1759
|
*/
|
|
1860
1760
|
function DBGetUtilsDefaultMixin(Base) {
|
|
1861
1761
|
return class extends Base {
|
|
@@ -1899,6 +1799,8 @@ function DBGetUtilsDefaultMixin(Base) {
|
|
|
1899
1799
|
}
|
|
1900
1800
|
/**
|
|
1901
1801
|
* Update table operation numbers from SQLite
|
|
1802
|
+
*
|
|
1803
|
+
* @public
|
|
1902
1804
|
*/
|
|
1903
1805
|
exports.RowUpdateType = void 0;
|
|
1904
1806
|
(function (RowUpdateType) {
|
|
@@ -1907,8 +1809,10 @@ exports.RowUpdateType = void 0;
|
|
|
1907
1809
|
RowUpdateType[RowUpdateType["SQLITE_UPDATE"] = 23] = "SQLITE_UPDATE";
|
|
1908
1810
|
})(exports.RowUpdateType || (exports.RowUpdateType = {}));
|
|
1909
1811
|
/**
|
|
1910
|
-
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool
|
|
1911
|
-
* {@link ConnectionPool
|
|
1812
|
+
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool#readLock} and
|
|
1813
|
+
* {@link ConnectionPool#writeLock}.
|
|
1814
|
+
*
|
|
1815
|
+
* @internal
|
|
1912
1816
|
*/
|
|
1913
1817
|
function DBAdapterDefaultMixin(Base) {
|
|
1914
1818
|
return class extends Base {
|
|
@@ -1996,9 +1900,15 @@ class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction)
|
|
|
1996
1900
|
}
|
|
1997
1901
|
}
|
|
1998
1902
|
}
|
|
1903
|
+
/**
|
|
1904
|
+
* @internal
|
|
1905
|
+
*/
|
|
1999
1906
|
function isBatchedUpdateNotification(update) {
|
|
2000
1907
|
return 'tables' in update;
|
|
2001
1908
|
}
|
|
1909
|
+
/**
|
|
1910
|
+
* @internal
|
|
1911
|
+
*/
|
|
2002
1912
|
function extractTableUpdates(update) {
|
|
2003
1913
|
return isBatchedUpdateNotification(update) ? update.tables : [update.table];
|
|
2004
1914
|
}
|
|
@@ -2026,6 +1936,8 @@ const FULL_SYNC_PRIORITY = 2147483647;
|
|
|
2026
1936
|
*
|
|
2027
1937
|
* Also note that data is downloaded in bulk, which means that individual counters are unlikely
|
|
2028
1938
|
* to be updated one-by-one.
|
|
1939
|
+
*
|
|
1940
|
+
* @public
|
|
2029
1941
|
*/
|
|
2030
1942
|
class SyncProgress {
|
|
2031
1943
|
internal;
|
|
@@ -2064,6 +1976,9 @@ class SyncProgress {
|
|
|
2064
1976
|
}
|
|
2065
1977
|
}
|
|
2066
1978
|
|
|
1979
|
+
/**
|
|
1980
|
+
* @public
|
|
1981
|
+
*/
|
|
2067
1982
|
class SyncStatus {
|
|
2068
1983
|
options;
|
|
2069
1984
|
constructor(options) {
|
|
@@ -2074,6 +1989,8 @@ class SyncStatus {
|
|
|
2074
1989
|
* implementation).
|
|
2075
1990
|
*
|
|
2076
1991
|
* This information is only available after a connection has been requested.
|
|
1992
|
+
*
|
|
1993
|
+
* @deprecated This always returns the Rust client (the only option).
|
|
2077
1994
|
*/
|
|
2078
1995
|
get clientImplementation() {
|
|
2079
1996
|
return this.options.clientImplementation;
|
|
@@ -2081,7 +1998,7 @@ class SyncStatus {
|
|
|
2081
1998
|
/**
|
|
2082
1999
|
* Indicates if the client is currently connected to the PowerSync service.
|
|
2083
2000
|
*
|
|
2084
|
-
* @returns
|
|
2001
|
+
* @returns True if connected, false otherwise. Defaults to false if not specified.
|
|
2085
2002
|
*/
|
|
2086
2003
|
get connected() {
|
|
2087
2004
|
return this.options.connected ?? false;
|
|
@@ -2089,7 +2006,7 @@ class SyncStatus {
|
|
|
2089
2006
|
/**
|
|
2090
2007
|
* Indicates if the client is in the process of establishing a connection to the PowerSync service.
|
|
2091
2008
|
*
|
|
2092
|
-
* @returns
|
|
2009
|
+
* @returns True if connecting, false otherwise. Defaults to false if not specified.
|
|
2093
2010
|
*/
|
|
2094
2011
|
get connecting() {
|
|
2095
2012
|
return this.options.connecting ?? false;
|
|
@@ -2098,7 +2015,7 @@ class SyncStatus {
|
|
|
2098
2015
|
* Time that a last sync has fully completed, if any.
|
|
2099
2016
|
* This timestamp is reset to null after a restart of the PowerSync service.
|
|
2100
2017
|
*
|
|
2101
|
-
* @returns
|
|
2018
|
+
* @returns The timestamp of the last successful sync, or undefined if no sync has completed.
|
|
2102
2019
|
*/
|
|
2103
2020
|
get lastSyncedAt() {
|
|
2104
2021
|
return this.options.lastSyncedAt;
|
|
@@ -2106,7 +2023,7 @@ class SyncStatus {
|
|
|
2106
2023
|
/**
|
|
2107
2024
|
* Indicates whether there has been at least one full sync completed since initialization.
|
|
2108
2025
|
*
|
|
2109
|
-
* @returns
|
|
2026
|
+
* @returns True if at least one sync has completed, false if no sync has completed,
|
|
2110
2027
|
* or undefined when the state is still being loaded from the database.
|
|
2111
2028
|
*/
|
|
2112
2029
|
get hasSynced() {
|
|
@@ -2115,10 +2032,10 @@ class SyncStatus {
|
|
|
2115
2032
|
/**
|
|
2116
2033
|
* Provides the current data flow status regarding uploads and downloads.
|
|
2117
2034
|
*
|
|
2118
|
-
* @returns
|
|
2035
|
+
* @returns An object containing:
|
|
2119
2036
|
* - downloading: True if actively downloading changes (only when connected is also true)
|
|
2120
2037
|
* - uploading: True if actively uploading changes
|
|
2121
|
-
* Defaults to {downloading: false, uploading: false} if not specified.
|
|
2038
|
+
* Defaults to `{downloading: false, uploading: false}` if not specified.
|
|
2122
2039
|
*/
|
|
2123
2040
|
get dataFlowStatus() {
|
|
2124
2041
|
return (this.options.dataFlow ?? {
|
|
@@ -2143,7 +2060,7 @@ class SyncStatus {
|
|
|
2143
2060
|
return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
|
|
2144
2061
|
}
|
|
2145
2062
|
/**
|
|
2146
|
-
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
2063
|
+
* If the `stream` appears in {@link SyncStatus.syncStreams}, returns the current status for that stream.
|
|
2147
2064
|
*/
|
|
2148
2065
|
forStream(stream) {
|
|
2149
2066
|
const asJson = JSON.stringify(stream.parameters);
|
|
@@ -2153,7 +2070,7 @@ class SyncStatus {
|
|
|
2153
2070
|
/**
|
|
2154
2071
|
* Provides sync status information for all bucket priorities, sorted by priority (highest first).
|
|
2155
2072
|
*
|
|
2156
|
-
* @returns
|
|
2073
|
+
* @returns An array of status entries for different sync priority levels,
|
|
2157
2074
|
* sorted with highest priorities (lower numbers) first.
|
|
2158
2075
|
*/
|
|
2159
2076
|
get priorityStatusEntries() {
|
|
@@ -2188,8 +2105,8 @@ class SyncStatus {
|
|
|
2188
2105
|
* For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
|
|
2189
2106
|
* with a priority of 1 may return information for priority level 3.
|
|
2190
2107
|
*
|
|
2191
|
-
* @param
|
|
2192
|
-
* @returns
|
|
2108
|
+
* @param priority - The bucket priority for which the status should be reported
|
|
2109
|
+
* @returns Status information for the requested priority level or the next higher level with available status
|
|
2193
2110
|
*/
|
|
2194
2111
|
statusForPriority(priority) {
|
|
2195
2112
|
// priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
|
|
@@ -2210,8 +2127,8 @@ class SyncStatus {
|
|
|
2210
2127
|
* Compares this SyncStatus instance with another to determine if they are equal.
|
|
2211
2128
|
* Equality is determined by comparing the serialized JSON representation of both instances.
|
|
2212
2129
|
*
|
|
2213
|
-
* @param
|
|
2214
|
-
* @returns
|
|
2130
|
+
* @param status - The SyncStatus instance to compare against
|
|
2131
|
+
* @returns True if the instances are considered equal, false otherwise
|
|
2215
2132
|
*/
|
|
2216
2133
|
isEqual(status) {
|
|
2217
2134
|
/**
|
|
@@ -2234,7 +2151,7 @@ class SyncStatus {
|
|
|
2234
2151
|
* Creates a human-readable string representation of the current sync status.
|
|
2235
2152
|
* Includes information about connection state, sync completion, and data flow.
|
|
2236
2153
|
*
|
|
2237
|
-
* @returns
|
|
2154
|
+
* @returns A string representation of the sync status
|
|
2238
2155
|
*/
|
|
2239
2156
|
getMessage() {
|
|
2240
2157
|
const dataFlow = this.dataFlowStatus;
|
|
@@ -2243,7 +2160,7 @@ class SyncStatus {
|
|
|
2243
2160
|
/**
|
|
2244
2161
|
* Serializes the SyncStatus instance to a plain object.
|
|
2245
2162
|
*
|
|
2246
|
-
* @returns
|
|
2163
|
+
* @returns A plain object representation of the sync status
|
|
2247
2164
|
*/
|
|
2248
2165
|
toJSON() {
|
|
2249
2166
|
return {
|
|
@@ -2309,6 +2226,9 @@ class SyncStreamStatusView {
|
|
|
2309
2226
|
}
|
|
2310
2227
|
}
|
|
2311
2228
|
|
|
2229
|
+
/**
|
|
2230
|
+
* @public
|
|
2231
|
+
*/
|
|
2312
2232
|
class UploadQueueStats {
|
|
2313
2233
|
count;
|
|
2314
2234
|
size;
|
|
@@ -2334,6 +2254,9 @@ class UploadQueueStats {
|
|
|
2334
2254
|
}
|
|
2335
2255
|
}
|
|
2336
2256
|
|
|
2257
|
+
/**
|
|
2258
|
+
* @internal
|
|
2259
|
+
*/
|
|
2337
2260
|
class BaseObserver {
|
|
2338
2261
|
listeners = new Set();
|
|
2339
2262
|
constructor() { }
|
|
@@ -2361,6 +2284,9 @@ class BaseObserver {
|
|
|
2361
2284
|
}
|
|
2362
2285
|
}
|
|
2363
2286
|
|
|
2287
|
+
/**
|
|
2288
|
+
* @internal
|
|
2289
|
+
*/
|
|
2364
2290
|
class ControlledExecutor {
|
|
2365
2291
|
task;
|
|
2366
2292
|
/**
|
|
@@ -2412,6 +2338,210 @@ class ControlledExecutor {
|
|
|
2412
2338
|
}
|
|
2413
2339
|
}
|
|
2414
2340
|
|
|
2341
|
+
/**
|
|
2342
|
+
* Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
|
|
2343
|
+
*
|
|
2344
|
+
* 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).
|
|
2345
|
+
* This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
|
|
2346
|
+
* SDK.
|
|
2347
|
+
*/
|
|
2348
|
+
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
2349
|
+
|
|
2350
|
+
const doneResult = { done: true, value: undefined };
|
|
2351
|
+
function valueResult(value) {
|
|
2352
|
+
return { done: false, value };
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
2356
|
+
*
|
|
2357
|
+
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
2358
|
+
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
2359
|
+
*
|
|
2360
|
+
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
2361
|
+
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
2362
|
+
*/
|
|
2363
|
+
function injectable(source) {
|
|
2364
|
+
let sourceIsDone = false;
|
|
2365
|
+
let waiter = undefined; // An active, waiting next() call.
|
|
2366
|
+
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
2367
|
+
let pendingSourceEvent = null;
|
|
2368
|
+
let sourceFetchInFlight = false;
|
|
2369
|
+
let pendingInjectedEvents = [];
|
|
2370
|
+
const consumeWaiter = () => {
|
|
2371
|
+
const pending = waiter;
|
|
2372
|
+
waiter = undefined;
|
|
2373
|
+
return pending;
|
|
2374
|
+
};
|
|
2375
|
+
const fetchFromSource = () => {
|
|
2376
|
+
const resolveWaiter = (propagate) => {
|
|
2377
|
+
sourceFetchInFlight = false;
|
|
2378
|
+
const active = consumeWaiter();
|
|
2379
|
+
if (active) {
|
|
2380
|
+
propagate(active);
|
|
2381
|
+
}
|
|
2382
|
+
else {
|
|
2383
|
+
pendingSourceEvent = propagate;
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
sourceFetchInFlight = true;
|
|
2387
|
+
const nextFromSource = source.next();
|
|
2388
|
+
nextFromSource.then((value) => {
|
|
2389
|
+
sourceIsDone = value.done == true;
|
|
2390
|
+
resolveWaiter((w) => w.resolve(value));
|
|
2391
|
+
}, (error) => {
|
|
2392
|
+
resolveWaiter((w) => w.reject(error));
|
|
2393
|
+
});
|
|
2394
|
+
};
|
|
2395
|
+
return {
|
|
2396
|
+
next: () => {
|
|
2397
|
+
return new Promise((resolve, reject) => {
|
|
2398
|
+
// First priority: Dispatch ready upstream events.
|
|
2399
|
+
if (sourceIsDone) {
|
|
2400
|
+
return resolve(doneResult);
|
|
2401
|
+
}
|
|
2402
|
+
if (pendingSourceEvent) {
|
|
2403
|
+
pendingSourceEvent({ resolve, reject });
|
|
2404
|
+
pendingSourceEvent = null;
|
|
2405
|
+
return;
|
|
2406
|
+
}
|
|
2407
|
+
// Second priority: Dispatch injected events
|
|
2408
|
+
if (pendingInjectedEvents.length) {
|
|
2409
|
+
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
2410
|
+
}
|
|
2411
|
+
// Nothing pending? Fetch from source
|
|
2412
|
+
waiter = { resolve, reject };
|
|
2413
|
+
if (!sourceFetchInFlight) {
|
|
2414
|
+
fetchFromSource();
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
},
|
|
2418
|
+
inject: (event) => {
|
|
2419
|
+
const pending = consumeWaiter();
|
|
2420
|
+
if (pending != null) {
|
|
2421
|
+
pending.resolve(valueResult(event));
|
|
2422
|
+
}
|
|
2423
|
+
else {
|
|
2424
|
+
pendingInjectedEvents.push(event);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Splits a byte stream at line endings, emitting each line as a string.
|
|
2431
|
+
*/
|
|
2432
|
+
function extractJsonLines(source, decoder) {
|
|
2433
|
+
let buffer = '';
|
|
2434
|
+
const pendingLines = [];
|
|
2435
|
+
let isFinalEvent = false;
|
|
2436
|
+
return {
|
|
2437
|
+
next: async () => {
|
|
2438
|
+
while (true) {
|
|
2439
|
+
if (isFinalEvent) {
|
|
2440
|
+
return doneResult;
|
|
2441
|
+
}
|
|
2442
|
+
{
|
|
2443
|
+
const first = pendingLines.shift();
|
|
2444
|
+
if (first) {
|
|
2445
|
+
return { done: false, value: first };
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
const { done, value } = await source.next();
|
|
2449
|
+
if (done) {
|
|
2450
|
+
const remaining = buffer.trim();
|
|
2451
|
+
if (remaining.length != 0) {
|
|
2452
|
+
isFinalEvent = true;
|
|
2453
|
+
return { done: false, value: remaining };
|
|
2454
|
+
}
|
|
2455
|
+
return doneResult;
|
|
2456
|
+
}
|
|
2457
|
+
const data = decoder.decode(value, { stream: true });
|
|
2458
|
+
buffer += data;
|
|
2459
|
+
const lines = buffer.split('\n');
|
|
2460
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
2461
|
+
const l = lines[i].trim();
|
|
2462
|
+
if (l.length > 0) {
|
|
2463
|
+
pendingLines.push(l);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
buffer = lines[lines.length - 1];
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* Splits a concatenated stream of BSON objects by emitting individual objects.
|
|
2473
|
+
*/
|
|
2474
|
+
function extractBsonObjects(source) {
|
|
2475
|
+
// Fully read but not emitted yet.
|
|
2476
|
+
const completedObjects = [];
|
|
2477
|
+
// Whether source has returned { done: true }. We do the same once completed objects have been emitted.
|
|
2478
|
+
let isDone = false;
|
|
2479
|
+
const lengthBuffer = new DataView(new ArrayBuffer(4));
|
|
2480
|
+
let objectBody = null;
|
|
2481
|
+
// If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
|
|
2482
|
+
// If we're consuming a document, the bytes remaining.
|
|
2483
|
+
let remainingLength = 4;
|
|
2484
|
+
return {
|
|
2485
|
+
async next() {
|
|
2486
|
+
while (true) {
|
|
2487
|
+
// Before fetching new data from upstream, return completed objects.
|
|
2488
|
+
if (completedObjects.length) {
|
|
2489
|
+
return valueResult(completedObjects.shift());
|
|
2490
|
+
}
|
|
2491
|
+
if (isDone) {
|
|
2492
|
+
return doneResult;
|
|
2493
|
+
}
|
|
2494
|
+
const upstreamEvent = await source.next();
|
|
2495
|
+
if (upstreamEvent.done) {
|
|
2496
|
+
isDone = true;
|
|
2497
|
+
if (objectBody || remainingLength != 4) {
|
|
2498
|
+
throw new Error('illegal end of stream in BSON object');
|
|
2499
|
+
}
|
|
2500
|
+
return doneResult;
|
|
2501
|
+
}
|
|
2502
|
+
const chunk = upstreamEvent.value;
|
|
2503
|
+
for (let i = 0; i < chunk.length;) {
|
|
2504
|
+
const availableInData = chunk.length - i;
|
|
2505
|
+
if (objectBody) {
|
|
2506
|
+
// We're in the middle of reading a BSON document.
|
|
2507
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
2508
|
+
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
2509
|
+
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
2510
|
+
i += bytesToRead;
|
|
2511
|
+
remainingLength -= bytesToRead;
|
|
2512
|
+
if (remainingLength == 0) {
|
|
2513
|
+
completedObjects.push(objectBody);
|
|
2514
|
+
// Prepare to read another document, starting with its length
|
|
2515
|
+
objectBody = null;
|
|
2516
|
+
remainingLength = 4;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
else {
|
|
2520
|
+
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
2521
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
2522
|
+
for (let j = 0; j < bytesToRead; j++) {
|
|
2523
|
+
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
2524
|
+
}
|
|
2525
|
+
i += bytesToRead;
|
|
2526
|
+
remainingLength -= bytesToRead;
|
|
2527
|
+
if (remainingLength == 0) {
|
|
2528
|
+
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
2529
|
+
// header is included in length.
|
|
2530
|
+
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
2531
|
+
remainingLength = length - 4;
|
|
2532
|
+
if (remainingLength < 1) {
|
|
2533
|
+
throw new Error(`invalid length for bson: ${length}`);
|
|
2534
|
+
}
|
|
2535
|
+
objectBody = new Uint8Array(length);
|
|
2536
|
+
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2415
2545
|
/**
|
|
2416
2546
|
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
2417
2547
|
* on the trailing edge.
|
|
@@ -2431,45 +2561,128 @@ function throttleTrailing(func, wait) {
|
|
|
2431
2561
|
};
|
|
2432
2562
|
}
|
|
2433
2563
|
function asyncNotifier() {
|
|
2434
|
-
|
|
2435
|
-
let hasPendingNotification = false;
|
|
2564
|
+
const queue = new EventQueue();
|
|
2436
2565
|
return {
|
|
2437
2566
|
notify() {
|
|
2438
|
-
if (
|
|
2439
|
-
waitingConsumer();
|
|
2440
|
-
waitingConsumer = null;
|
|
2441
|
-
}
|
|
2567
|
+
if (queue.countOutstandingEvents > 0) ;
|
|
2442
2568
|
else {
|
|
2443
|
-
|
|
2569
|
+
queue.notify();
|
|
2444
2570
|
}
|
|
2445
2571
|
},
|
|
2446
2572
|
waitForNotification(signal) {
|
|
2447
|
-
return
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2573
|
+
return queue.waitForEvent(signal);
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
class EventQueue {
|
|
2578
|
+
options;
|
|
2579
|
+
waitingConsumer;
|
|
2580
|
+
outstandingEvents;
|
|
2581
|
+
constructor(options = {}) {
|
|
2582
|
+
this.options = options;
|
|
2583
|
+
this.outstandingEvents = [];
|
|
2584
|
+
}
|
|
2585
|
+
/**
|
|
2586
|
+
* The amount of buffered events not yet dispatched to listeners.
|
|
2587
|
+
*/
|
|
2588
|
+
get countOutstandingEvents() {
|
|
2589
|
+
return this.outstandingEvents.length;
|
|
2590
|
+
}
|
|
2591
|
+
notifyInner(dispatch) {
|
|
2592
|
+
const existing = this.waitingConsumer;
|
|
2593
|
+
this.waitingConsumer = undefined;
|
|
2594
|
+
const dispatchAndNotifyListeners = (waiter) => {
|
|
2595
|
+
dispatch(waiter);
|
|
2596
|
+
this.options.eventDelivered?.();
|
|
2597
|
+
};
|
|
2598
|
+
if (existing) {
|
|
2599
|
+
dispatchAndNotifyListeners(existing);
|
|
2600
|
+
}
|
|
2601
|
+
else {
|
|
2602
|
+
this.outstandingEvents.push(dispatchAndNotifyListeners);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
notify(value) {
|
|
2606
|
+
this.notifyInner((l) => l.resolve(value));
|
|
2607
|
+
}
|
|
2608
|
+
notifyError(error) {
|
|
2609
|
+
this.notifyInner((l) => l.reject(error));
|
|
2610
|
+
}
|
|
2611
|
+
waitForEvent(signal) {
|
|
2612
|
+
return new Promise((resolve, reject) => {
|
|
2613
|
+
if (this.waitingConsumer != null) {
|
|
2614
|
+
throw new Error('Illegal call to waitForEvent, already has a waiter.');
|
|
2615
|
+
}
|
|
2616
|
+
const complete = () => {
|
|
2617
|
+
signal?.removeEventListener('abort', onAbort);
|
|
2618
|
+
};
|
|
2619
|
+
const onAbort = () => {
|
|
2620
|
+
complete();
|
|
2621
|
+
this.waitingConsumer = undefined;
|
|
2622
|
+
resolve(undefined);
|
|
2623
|
+
};
|
|
2624
|
+
const waiter = {
|
|
2625
|
+
resolve: (value) => {
|
|
2626
|
+
complete();
|
|
2627
|
+
resolve(value);
|
|
2628
|
+
},
|
|
2629
|
+
reject: (error) => {
|
|
2630
|
+
complete();
|
|
2631
|
+
reject(error);
|
|
2453
2632
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2633
|
+
};
|
|
2634
|
+
if (signal.aborted) {
|
|
2635
|
+
resolve(undefined);
|
|
2636
|
+
}
|
|
2637
|
+
else if (this.countOutstandingEvents > 0) {
|
|
2638
|
+
const [event] = this.outstandingEvents.splice(0, 1);
|
|
2639
|
+
event(waiter);
|
|
2640
|
+
}
|
|
2641
|
+
else {
|
|
2642
|
+
this.waitingConsumer = waiter;
|
|
2643
|
+
signal.addEventListener('abort', onAbort);
|
|
2644
|
+
}
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* Creates an async iterable backed by event queues.
|
|
2649
|
+
*
|
|
2650
|
+
* @param run A function invoked for every new listener. It receives a queue backing the async iterator.
|
|
2651
|
+
* @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
|
|
2652
|
+
* called.
|
|
2653
|
+
* @returns An object conforming to the async iterable protocol.
|
|
2654
|
+
*/
|
|
2655
|
+
static queueBasedAsyncIterable(run, abort) {
|
|
2656
|
+
return {
|
|
2657
|
+
[symbolAsyncIterator]: () => {
|
|
2658
|
+
const queue = new EventQueue();
|
|
2659
|
+
const controller = new AbortController();
|
|
2660
|
+
function dispose() {
|
|
2661
|
+
controller.abort();
|
|
2662
|
+
abort?.removeEventListener('abort', dispose);
|
|
2457
2663
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
resolve();
|
|
2664
|
+
if (abort) {
|
|
2665
|
+
if (abort.aborted) {
|
|
2666
|
+
controller.abort();
|
|
2462
2667
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
resolve();
|
|
2668
|
+
else {
|
|
2669
|
+
abort.addEventListener('abort', dispose);
|
|
2466
2670
|
}
|
|
2467
|
-
waitingConsumer = complete;
|
|
2468
|
-
signal.addEventListener('abort', onAbort);
|
|
2469
2671
|
}
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2672
|
+
run(queue, controller.signal);
|
|
2673
|
+
return {
|
|
2674
|
+
async next() {
|
|
2675
|
+
const event = await queue.waitForEvent(controller.signal);
|
|
2676
|
+
return event == null ? doneResult : valueResult(event);
|
|
2677
|
+
},
|
|
2678
|
+
async return() {
|
|
2679
|
+
dispose();
|
|
2680
|
+
return doneResult;
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2473
2686
|
}
|
|
2474
2687
|
|
|
2475
2688
|
/**
|
|
@@ -2628,7 +2841,7 @@ class ConnectionManager extends BaseObserver {
|
|
|
2628
2841
|
/**
|
|
2629
2842
|
* Close the sync connection.
|
|
2630
2843
|
*
|
|
2631
|
-
* Use {@link connect} to connect again.
|
|
2844
|
+
* Use {@link ConnectionManager.connect} to connect again.
|
|
2632
2845
|
*/
|
|
2633
2846
|
async disconnect() {
|
|
2634
2847
|
// This will help abort pending connects
|
|
@@ -2768,6 +2981,8 @@ const _finalizer = 'FinalizationRegistry' in globalThis
|
|
|
2768
2981
|
/**
|
|
2769
2982
|
* An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
|
|
2770
2983
|
* result has changes without necessarily processing all items in the result.
|
|
2984
|
+
*
|
|
2985
|
+
* @public
|
|
2771
2986
|
*/
|
|
2772
2987
|
class ArrayComparator {
|
|
2773
2988
|
options;
|
|
@@ -2795,6 +3010,8 @@ class ArrayComparator {
|
|
|
2795
3010
|
}
|
|
2796
3011
|
/**
|
|
2797
3012
|
* Watched query comparator that always reports changed result sets.
|
|
3013
|
+
*
|
|
3014
|
+
* @public
|
|
2798
3015
|
*/
|
|
2799
3016
|
const FalsyComparator = {
|
|
2800
3017
|
checkEquality: () => false // Default comparator that always returns false
|
|
@@ -3002,6 +3219,8 @@ class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
3002
3219
|
/**
|
|
3003
3220
|
* An empty differential result set.
|
|
3004
3221
|
* This is used as the initial state for differential incrementally watched queries.
|
|
3222
|
+
*
|
|
3223
|
+
* @internal
|
|
3005
3224
|
*/
|
|
3006
3225
|
const EMPTY_DIFFERENTIAL = {
|
|
3007
3226
|
added: [],
|
|
@@ -3014,6 +3233,8 @@ const EMPTY_DIFFERENTIAL = {
|
|
|
3014
3233
|
* Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
|
|
3015
3234
|
* It keys items by their `id` property if available, alternatively it uses JSON stringification
|
|
3016
3235
|
* of the entire item for the key and comparison.
|
|
3236
|
+
*
|
|
3237
|
+
* @internal
|
|
3017
3238
|
*/
|
|
3018
3239
|
const DEFAULT_ROW_COMPARATOR = {
|
|
3019
3240
|
keyBy: (item) => {
|
|
@@ -3294,6 +3515,8 @@ class CustomQuery {
|
|
|
3294
3515
|
|
|
3295
3516
|
/**
|
|
3296
3517
|
* Tests if the input is a {@link SQLOpenOptions}
|
|
3518
|
+
*
|
|
3519
|
+
* @internal
|
|
3297
3520
|
*/
|
|
3298
3521
|
const isSQLOpenOptions = (test) => {
|
|
3299
3522
|
// typeof null is `object`, but you cannot use the `in` operator on `null.
|
|
@@ -3301,17 +3524,24 @@ const isSQLOpenOptions = (test) => {
|
|
|
3301
3524
|
};
|
|
3302
3525
|
/**
|
|
3303
3526
|
* Tests if input is a {@link SQLOpenFactory}
|
|
3527
|
+
*
|
|
3528
|
+
* @internal
|
|
3304
3529
|
*/
|
|
3305
3530
|
const isSQLOpenFactory = (test) => {
|
|
3306
3531
|
return typeof test?.openDB == 'function';
|
|
3307
3532
|
};
|
|
3308
3533
|
/**
|
|
3309
3534
|
* Tests if input is a {@link DBAdapter}
|
|
3535
|
+
*
|
|
3536
|
+
* @internal
|
|
3310
3537
|
*/
|
|
3311
3538
|
const isDBAdapter = (test) => {
|
|
3312
3539
|
return typeof test?.writeTransaction == 'function';
|
|
3313
3540
|
};
|
|
3314
3541
|
|
|
3542
|
+
/**
|
|
3543
|
+
* @internal
|
|
3544
|
+
*/
|
|
3315
3545
|
exports.PSInternalTable = void 0;
|
|
3316
3546
|
(function (PSInternalTable) {
|
|
3317
3547
|
PSInternalTable["DATA"] = "ps_data";
|
|
@@ -3320,6 +3550,9 @@ exports.PSInternalTable = void 0;
|
|
|
3320
3550
|
PSInternalTable["OPLOG"] = "ps_oplog";
|
|
3321
3551
|
PSInternalTable["UNTYPED"] = "ps_untyped";
|
|
3322
3552
|
})(exports.PSInternalTable || (exports.PSInternalTable = {}));
|
|
3553
|
+
/**
|
|
3554
|
+
* @internal
|
|
3555
|
+
*/
|
|
3323
3556
|
exports.PowerSyncControlCommand = void 0;
|
|
3324
3557
|
(function (PowerSyncControlCommand) {
|
|
3325
3558
|
PowerSyncControlCommand["PROCESS_TEXT_LINE"] = "line_text";
|
|
@@ -3337,6 +3570,8 @@ exports.PowerSyncControlCommand = void 0;
|
|
|
3337
3570
|
|
|
3338
3571
|
/**
|
|
3339
3572
|
* A batch of client-side changes.
|
|
3573
|
+
*
|
|
3574
|
+
* @public
|
|
3340
3575
|
*/
|
|
3341
3576
|
class CrudBatch {
|
|
3342
3577
|
crud;
|
|
@@ -3363,6 +3598,8 @@ class CrudBatch {
|
|
|
3363
3598
|
|
|
3364
3599
|
/**
|
|
3365
3600
|
* Type of local change.
|
|
3601
|
+
*
|
|
3602
|
+
* @public
|
|
3366
3603
|
*/
|
|
3367
3604
|
exports.UpdateType = void 0;
|
|
3368
3605
|
(function (UpdateType) {
|
|
@@ -3375,6 +3612,8 @@ exports.UpdateType = void 0;
|
|
|
3375
3612
|
})(exports.UpdateType || (exports.UpdateType = {}));
|
|
3376
3613
|
/**
|
|
3377
3614
|
* A single client-side change.
|
|
3615
|
+
*
|
|
3616
|
+
* @public
|
|
3378
3617
|
*/
|
|
3379
3618
|
class CrudEntry {
|
|
3380
3619
|
/**
|
|
@@ -3471,6 +3710,9 @@ class CrudEntry {
|
|
|
3471
3710
|
}
|
|
3472
3711
|
}
|
|
3473
3712
|
|
|
3713
|
+
/**
|
|
3714
|
+
* @public
|
|
3715
|
+
*/
|
|
3474
3716
|
class CrudTransaction extends CrudBatch {
|
|
3475
3717
|
crud;
|
|
3476
3718
|
complete;
|
|
@@ -3499,6 +3741,8 @@ class CrudTransaction extends CrudBatch {
|
|
|
3499
3741
|
* Calls to Abortcontroller.abort(reason: any) will result in the
|
|
3500
3742
|
* `reason` being thrown. This is not necessarily an error,
|
|
3501
3743
|
* but extends error for better logging purposes.
|
|
3744
|
+
*
|
|
3745
|
+
* @internal
|
|
3502
3746
|
*/
|
|
3503
3747
|
class AbortOperation extends Error {
|
|
3504
3748
|
reason;
|
|
@@ -10669,7 +10913,7 @@ function requireDist () {
|
|
|
10669
10913
|
|
|
10670
10914
|
var distExports = requireDist();
|
|
10671
10915
|
|
|
10672
|
-
var version = "1.
|
|
10916
|
+
var version = "1.55.0";
|
|
10673
10917
|
var PACKAGE = {
|
|
10674
10918
|
version: version};
|
|
10675
10919
|
|
|
@@ -10745,289 +10989,95 @@ function requireWebsocketDuplexConnection () {
|
|
|
10745
10989
|
get: function () {
|
|
10746
10990
|
return this.done ? 0 : 1;
|
|
10747
10991
|
},
|
|
10748
|
-
enumerable: false,
|
|
10749
|
-
configurable: true
|
|
10750
|
-
});
|
|
10751
|
-
WebsocketDuplexConnection.prototype.close = function (error) {
|
|
10752
|
-
if (this.done) {
|
|
10753
|
-
_super.prototype.close.call(this, error);
|
|
10754
|
-
return;
|
|
10755
|
-
}
|
|
10756
|
-
this.websocket.removeEventListener("close", this.handleClosed);
|
|
10757
|
-
this.websocket.removeEventListener("error", this.handleError);
|
|
10758
|
-
this.websocket.removeEventListener("message", this.handleMessage);
|
|
10759
|
-
this.websocket.close();
|
|
10760
|
-
delete this.websocket;
|
|
10761
|
-
_super.prototype.close.call(this, error);
|
|
10762
|
-
};
|
|
10763
|
-
WebsocketDuplexConnection.prototype.send = function (frame) {
|
|
10764
|
-
if (this.done) {
|
|
10765
|
-
return;
|
|
10766
|
-
}
|
|
10767
|
-
var buffer = (0, rsocket_core_1.serializeFrame)(frame);
|
|
10768
|
-
this.websocket.send(buffer);
|
|
10769
|
-
};
|
|
10770
|
-
return WebsocketDuplexConnection;
|
|
10771
|
-
}(rsocket_core_1.Deferred));
|
|
10772
|
-
WebsocketDuplexConnection.WebsocketDuplexConnection = WebsocketDuplexConnection$1;
|
|
10773
|
-
|
|
10774
|
-
return WebsocketDuplexConnection;
|
|
10775
|
-
}
|
|
10776
|
-
|
|
10777
|
-
var WebsocketDuplexConnectionExports = requireWebsocketDuplexConnection();
|
|
10778
|
-
|
|
10779
|
-
/**
|
|
10780
|
-
* Adapted from rsocket-websocket-client
|
|
10781
|
-
* https://github.com/rsocket/rsocket-js/blob/e224cf379e747c4f1ddc4f2fa111854626cc8575/packages/rsocket-websocket-client/src/WebsocketClientTransport.ts#L17
|
|
10782
|
-
* This adds additional error handling for React Native iOS.
|
|
10783
|
-
* This particularly adds a close listener to handle cases where the WebSocket
|
|
10784
|
-
* connection closes immediately after opening without emitting an error.
|
|
10785
|
-
*/
|
|
10786
|
-
class WebsocketClientTransport {
|
|
10787
|
-
url;
|
|
10788
|
-
factory;
|
|
10789
|
-
constructor(options) {
|
|
10790
|
-
this.url = options.url;
|
|
10791
|
-
this.factory = options.wsCreator ?? ((url) => new WebSocket(url));
|
|
10792
|
-
}
|
|
10793
|
-
connect(multiplexerDemultiplexerFactory) {
|
|
10794
|
-
return new Promise((resolve, reject) => {
|
|
10795
|
-
const websocket = this.factory(this.url);
|
|
10796
|
-
websocket.binaryType = 'arraybuffer';
|
|
10797
|
-
let removeListeners;
|
|
10798
|
-
const openListener = () => {
|
|
10799
|
-
removeListeners();
|
|
10800
|
-
resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
|
|
10801
|
-
};
|
|
10802
|
-
const errorListener = (ev) => {
|
|
10803
|
-
removeListeners();
|
|
10804
|
-
// We add a default error in that case.
|
|
10805
|
-
if (ev.error != null) {
|
|
10806
|
-
// undici typically provides an error object
|
|
10807
|
-
reject(ev.error);
|
|
10808
|
-
}
|
|
10809
|
-
else if (ev.message != null) {
|
|
10810
|
-
// React Native typically does not provide an error object, but does provide a message
|
|
10811
|
-
reject(new Error(`Failed to create websocket connection: ${ev.message}`));
|
|
10812
|
-
}
|
|
10813
|
-
else {
|
|
10814
|
-
// Browsers often provide no details at all
|
|
10815
|
-
reject(new Error(`Failed to create websocket connection to ${this.url}`));
|
|
10816
|
-
}
|
|
10817
|
-
};
|
|
10818
|
-
/**
|
|
10819
|
-
* In some cases, such as React Native iOS, the WebSocket connection may close immediately after opening
|
|
10820
|
-
* without and error. In such cases, we need to handle the close event to reject the promise.
|
|
10821
|
-
*/
|
|
10822
|
-
const closeListener = () => {
|
|
10823
|
-
removeListeners();
|
|
10824
|
-
reject(new Error('WebSocket connection closed while opening'));
|
|
10825
|
-
};
|
|
10826
|
-
removeListeners = () => {
|
|
10827
|
-
websocket.removeEventListener('open', openListener);
|
|
10828
|
-
websocket.removeEventListener('error', errorListener);
|
|
10829
|
-
websocket.removeEventListener('close', closeListener);
|
|
10830
|
-
};
|
|
10831
|
-
websocket.addEventListener('open', openListener);
|
|
10832
|
-
websocket.addEventListener('error', errorListener);
|
|
10833
|
-
websocket.addEventListener('close', closeListener);
|
|
10834
|
-
});
|
|
10835
|
-
}
|
|
10836
|
-
}
|
|
10837
|
-
|
|
10838
|
-
const doneResult = { done: true, value: undefined };
|
|
10839
|
-
function valueResult(value) {
|
|
10840
|
-
return { done: false, value };
|
|
10841
|
-
}
|
|
10842
|
-
/**
|
|
10843
|
-
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
10844
|
-
*
|
|
10845
|
-
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
10846
|
-
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
10847
|
-
*
|
|
10848
|
-
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
10849
|
-
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
10850
|
-
*/
|
|
10851
|
-
function injectable(source) {
|
|
10852
|
-
let sourceIsDone = false;
|
|
10853
|
-
let waiter = undefined; // An active, waiting next() call.
|
|
10854
|
-
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
10855
|
-
let pendingSourceEvent = null;
|
|
10856
|
-
let sourceFetchInFlight = false;
|
|
10857
|
-
let pendingInjectedEvents = [];
|
|
10858
|
-
const consumeWaiter = () => {
|
|
10859
|
-
const pending = waiter;
|
|
10860
|
-
waiter = undefined;
|
|
10861
|
-
return pending;
|
|
10862
|
-
};
|
|
10863
|
-
const fetchFromSource = () => {
|
|
10864
|
-
const resolveWaiter = (propagate) => {
|
|
10865
|
-
sourceFetchInFlight = false;
|
|
10866
|
-
const active = consumeWaiter();
|
|
10867
|
-
if (active) {
|
|
10868
|
-
propagate(active);
|
|
10869
|
-
}
|
|
10870
|
-
else {
|
|
10871
|
-
pendingSourceEvent = propagate;
|
|
10872
|
-
}
|
|
10873
|
-
};
|
|
10874
|
-
sourceFetchInFlight = true;
|
|
10875
|
-
const nextFromSource = source.next();
|
|
10876
|
-
nextFromSource.then((value) => {
|
|
10877
|
-
sourceIsDone = value.done == true;
|
|
10878
|
-
resolveWaiter((w) => w.resolve(value));
|
|
10879
|
-
}, (error) => {
|
|
10880
|
-
resolveWaiter((w) => w.reject(error));
|
|
10881
|
-
});
|
|
10882
|
-
};
|
|
10883
|
-
return {
|
|
10884
|
-
next: () => {
|
|
10885
|
-
return new Promise((resolve, reject) => {
|
|
10886
|
-
// First priority: Dispatch ready upstream events.
|
|
10887
|
-
if (sourceIsDone) {
|
|
10888
|
-
return resolve(doneResult);
|
|
10889
|
-
}
|
|
10890
|
-
if (pendingSourceEvent) {
|
|
10891
|
-
pendingSourceEvent({ resolve, reject });
|
|
10892
|
-
pendingSourceEvent = null;
|
|
10893
|
-
return;
|
|
10894
|
-
}
|
|
10895
|
-
// Second priority: Dispatch injected events
|
|
10896
|
-
if (pendingInjectedEvents.length) {
|
|
10897
|
-
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
10898
|
-
}
|
|
10899
|
-
// Nothing pending? Fetch from source
|
|
10900
|
-
waiter = { resolve, reject };
|
|
10901
|
-
if (!sourceFetchInFlight) {
|
|
10902
|
-
fetchFromSource();
|
|
10903
|
-
}
|
|
10904
|
-
});
|
|
10905
|
-
},
|
|
10906
|
-
inject: (event) => {
|
|
10907
|
-
const pending = consumeWaiter();
|
|
10908
|
-
if (pending != null) {
|
|
10909
|
-
pending.resolve(valueResult(event));
|
|
10910
|
-
}
|
|
10911
|
-
else {
|
|
10912
|
-
pendingInjectedEvents.push(event);
|
|
10913
|
-
}
|
|
10914
|
-
}
|
|
10915
|
-
};
|
|
10916
|
-
}
|
|
10917
|
-
/**
|
|
10918
|
-
* Splits a byte stream at line endings, emitting each line as a string.
|
|
10919
|
-
*/
|
|
10920
|
-
function extractJsonLines(source, decoder) {
|
|
10921
|
-
let buffer = '';
|
|
10922
|
-
const pendingLines = [];
|
|
10923
|
-
let isFinalEvent = false;
|
|
10924
|
-
return {
|
|
10925
|
-
next: async () => {
|
|
10926
|
-
while (true) {
|
|
10927
|
-
if (isFinalEvent) {
|
|
10928
|
-
return doneResult;
|
|
10929
|
-
}
|
|
10930
|
-
{
|
|
10931
|
-
const first = pendingLines.shift();
|
|
10932
|
-
if (first) {
|
|
10933
|
-
return { done: false, value: first };
|
|
10934
|
-
}
|
|
10935
|
-
}
|
|
10936
|
-
const { done, value } = await source.next();
|
|
10937
|
-
if (done) {
|
|
10938
|
-
const remaining = buffer.trim();
|
|
10939
|
-
if (remaining.length != 0) {
|
|
10940
|
-
isFinalEvent = true;
|
|
10941
|
-
return { done: false, value: remaining };
|
|
10942
|
-
}
|
|
10943
|
-
return doneResult;
|
|
10944
|
-
}
|
|
10945
|
-
const data = decoder.decode(value, { stream: true });
|
|
10946
|
-
buffer += data;
|
|
10947
|
-
const lines = buffer.split('\n');
|
|
10948
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
10949
|
-
const l = lines[i].trim();
|
|
10950
|
-
if (l.length > 0) {
|
|
10951
|
-
pendingLines.push(l);
|
|
10952
|
-
}
|
|
10953
|
-
}
|
|
10954
|
-
buffer = lines[lines.length - 1];
|
|
10955
|
-
}
|
|
10956
|
-
}
|
|
10957
|
-
};
|
|
10992
|
+
enumerable: false,
|
|
10993
|
+
configurable: true
|
|
10994
|
+
});
|
|
10995
|
+
WebsocketDuplexConnection.prototype.close = function (error) {
|
|
10996
|
+
if (this.done) {
|
|
10997
|
+
_super.prototype.close.call(this, error);
|
|
10998
|
+
return;
|
|
10999
|
+
}
|
|
11000
|
+
this.websocket.removeEventListener("close", this.handleClosed);
|
|
11001
|
+
this.websocket.removeEventListener("error", this.handleError);
|
|
11002
|
+
this.websocket.removeEventListener("message", this.handleMessage);
|
|
11003
|
+
this.websocket.close();
|
|
11004
|
+
delete this.websocket;
|
|
11005
|
+
_super.prototype.close.call(this, error);
|
|
11006
|
+
};
|
|
11007
|
+
WebsocketDuplexConnection.prototype.send = function (frame) {
|
|
11008
|
+
if (this.done) {
|
|
11009
|
+
return;
|
|
11010
|
+
}
|
|
11011
|
+
var buffer = (0, rsocket_core_1.serializeFrame)(frame);
|
|
11012
|
+
this.websocket.send(buffer);
|
|
11013
|
+
};
|
|
11014
|
+
return WebsocketDuplexConnection;
|
|
11015
|
+
}(rsocket_core_1.Deferred));
|
|
11016
|
+
WebsocketDuplexConnection.WebsocketDuplexConnection = WebsocketDuplexConnection$1;
|
|
11017
|
+
|
|
11018
|
+
return WebsocketDuplexConnection;
|
|
10958
11019
|
}
|
|
11020
|
+
|
|
11021
|
+
var WebsocketDuplexConnectionExports = requireWebsocketDuplexConnection();
|
|
11022
|
+
|
|
10959
11023
|
/**
|
|
10960
|
-
*
|
|
11024
|
+
* Adapted from rsocket-websocket-client
|
|
11025
|
+
* https://github.com/rsocket/rsocket-js/blob/e224cf379e747c4f1ddc4f2fa111854626cc8575/packages/rsocket-websocket-client/src/WebsocketClientTransport.ts#L17
|
|
11026
|
+
* This adds additional error handling for React Native iOS.
|
|
11027
|
+
* This particularly adds a close listener to handle cases where the WebSocket
|
|
11028
|
+
* connection closes immediately after opening without emitting an error.
|
|
10961
11029
|
*/
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
11030
|
+
class WebsocketClientTransport {
|
|
11031
|
+
url;
|
|
11032
|
+
factory;
|
|
11033
|
+
constructor(options) {
|
|
11034
|
+
this.url = options.url;
|
|
11035
|
+
this.factory = options.wsCreator ?? ((url) => new WebSocket(url));
|
|
11036
|
+
}
|
|
11037
|
+
connect(multiplexerDemultiplexerFactory) {
|
|
11038
|
+
return new Promise((resolve, reject) => {
|
|
11039
|
+
const websocket = this.factory(this.url);
|
|
11040
|
+
websocket.binaryType = 'arraybuffer';
|
|
11041
|
+
let removeListeners;
|
|
11042
|
+
const openListener = () => {
|
|
11043
|
+
removeListeners();
|
|
11044
|
+
resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
|
|
11045
|
+
};
|
|
11046
|
+
const errorListener = (event) => {
|
|
11047
|
+
const ev = event;
|
|
11048
|
+
removeListeners();
|
|
11049
|
+
// We add a default error in that case.
|
|
11050
|
+
if (ev.error != null) {
|
|
11051
|
+
// undici typically provides an error object
|
|
11052
|
+
reject(ev.error);
|
|
10981
11053
|
}
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
if (objectBody || remainingLength != 4) {
|
|
10986
|
-
throw new Error('illegal end of stream in BSON object');
|
|
10987
|
-
}
|
|
10988
|
-
return doneResult;
|
|
11054
|
+
else if (ev.message != null) {
|
|
11055
|
+
// React Native typically does not provide an error object, but does provide a message
|
|
11056
|
+
reject(new Error(`Failed to create websocket connection: ${ev.message}`));
|
|
10989
11057
|
}
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
if (objectBody) {
|
|
10994
|
-
// We're in the middle of reading a BSON document.
|
|
10995
|
-
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
10996
|
-
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
10997
|
-
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
10998
|
-
i += bytesToRead;
|
|
10999
|
-
remainingLength -= bytesToRead;
|
|
11000
|
-
if (remainingLength == 0) {
|
|
11001
|
-
completedObjects.push(objectBody);
|
|
11002
|
-
// Prepare to read another document, starting with its length
|
|
11003
|
-
objectBody = null;
|
|
11004
|
-
remainingLength = 4;
|
|
11005
|
-
}
|
|
11006
|
-
}
|
|
11007
|
-
else {
|
|
11008
|
-
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
11009
|
-
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
11010
|
-
for (let j = 0; j < bytesToRead; j++) {
|
|
11011
|
-
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
11012
|
-
}
|
|
11013
|
-
i += bytesToRead;
|
|
11014
|
-
remainingLength -= bytesToRead;
|
|
11015
|
-
if (remainingLength == 0) {
|
|
11016
|
-
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
11017
|
-
// header is included in length.
|
|
11018
|
-
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
11019
|
-
remainingLength = length - 4;
|
|
11020
|
-
if (remainingLength < 1) {
|
|
11021
|
-
throw new Error(`invalid length for bson: ${length}`);
|
|
11022
|
-
}
|
|
11023
|
-
objectBody = new Uint8Array(length);
|
|
11024
|
-
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
11025
|
-
}
|
|
11026
|
-
}
|
|
11058
|
+
else {
|
|
11059
|
+
// Browsers often provide no details at all
|
|
11060
|
+
reject(new Error(`Failed to create websocket connection to ${this.url}`));
|
|
11027
11061
|
}
|
|
11028
|
-
}
|
|
11029
|
-
|
|
11030
|
-
|
|
11062
|
+
};
|
|
11063
|
+
/**
|
|
11064
|
+
* In some cases, such as React Native iOS, the WebSocket connection may close immediately after opening
|
|
11065
|
+
* without and error. In such cases, we need to handle the close event to reject the promise.
|
|
11066
|
+
*/
|
|
11067
|
+
const closeListener = () => {
|
|
11068
|
+
removeListeners();
|
|
11069
|
+
reject(new Error('WebSocket connection closed while opening'));
|
|
11070
|
+
};
|
|
11071
|
+
removeListeners = () => {
|
|
11072
|
+
websocket.removeEventListener('open', openListener);
|
|
11073
|
+
websocket.removeEventListener('error', errorListener);
|
|
11074
|
+
websocket.removeEventListener('close', closeListener);
|
|
11075
|
+
};
|
|
11076
|
+
websocket.addEventListener('open', openListener);
|
|
11077
|
+
websocket.addEventListener('error', errorListener);
|
|
11078
|
+
websocket.addEventListener('close', closeListener);
|
|
11079
|
+
});
|
|
11080
|
+
}
|
|
11031
11081
|
}
|
|
11032
11082
|
|
|
11033
11083
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
@@ -11042,7 +11092,13 @@ const SOCKET_TIMEOUT_MS = 30_000;
|
|
|
11042
11092
|
// If there is a backlog of messages (for example on slow connections), keepalive messages could be delayed
|
|
11043
11093
|
// significantly. Therefore this is longer than the socket timeout.
|
|
11044
11094
|
const KEEP_ALIVE_LIFETIME_MS = 90_000;
|
|
11095
|
+
/**
|
|
11096
|
+
* @internal
|
|
11097
|
+
*/
|
|
11045
11098
|
const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
|
|
11099
|
+
/**
|
|
11100
|
+
* @public
|
|
11101
|
+
*/
|
|
11046
11102
|
exports.FetchStrategy = void 0;
|
|
11047
11103
|
(function (FetchStrategy) {
|
|
11048
11104
|
/**
|
|
@@ -11061,12 +11117,17 @@ exports.FetchStrategy = void 0;
|
|
|
11061
11117
|
* The class wrapper is used to distinguish the fetchImplementation
|
|
11062
11118
|
* option in [AbstractRemoteOptions] from the general fetch method
|
|
11063
11119
|
* which is typeof "function"
|
|
11120
|
+
*
|
|
11121
|
+
* @internal
|
|
11064
11122
|
*/
|
|
11065
11123
|
class FetchImplementationProvider {
|
|
11066
11124
|
getFetch() {
|
|
11067
11125
|
throw new Error('Unspecified fetch implementation');
|
|
11068
11126
|
}
|
|
11069
11127
|
}
|
|
11128
|
+
/**
|
|
11129
|
+
* @internal
|
|
11130
|
+
*/
|
|
11070
11131
|
const DEFAULT_REMOTE_OPTIONS = {
|
|
11071
11132
|
socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
|
|
11072
11133
|
return match === 'https://' ? 'wss://' : 'ws://';
|
|
@@ -11074,6 +11135,9 @@ const DEFAULT_REMOTE_OPTIONS = {
|
|
|
11074
11135
|
fetchImplementation: new FetchImplementationProvider(),
|
|
11075
11136
|
fetchOptions: {}
|
|
11076
11137
|
};
|
|
11138
|
+
/**
|
|
11139
|
+
* @internal
|
|
11140
|
+
*/
|
|
11077
11141
|
class AbstractRemote {
|
|
11078
11142
|
connector;
|
|
11079
11143
|
logger;
|
|
@@ -11234,8 +11298,19 @@ class AbstractRemote {
|
|
|
11234
11298
|
let pendingSocket = null;
|
|
11235
11299
|
let keepAliveTimeout;
|
|
11236
11300
|
let rsocket = null;
|
|
11237
|
-
let
|
|
11301
|
+
let paused = false;
|
|
11302
|
+
const queue = new EventQueue({
|
|
11303
|
+
eventDelivered: () => {
|
|
11304
|
+
if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
|
|
11305
|
+
paused = false;
|
|
11306
|
+
requestMore();
|
|
11307
|
+
}
|
|
11308
|
+
}
|
|
11309
|
+
});
|
|
11238
11310
|
let didClose = false;
|
|
11311
|
+
let connectionEstablished = false;
|
|
11312
|
+
let pendingEventsCount = syncQueueRequestSize;
|
|
11313
|
+
let res = null;
|
|
11239
11314
|
const abortRequest = () => {
|
|
11240
11315
|
if (didClose) {
|
|
11241
11316
|
return;
|
|
@@ -11248,10 +11323,23 @@ class AbstractRemote {
|
|
|
11248
11323
|
if (rsocket) {
|
|
11249
11324
|
rsocket.close();
|
|
11250
11325
|
}
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11326
|
+
// Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
|
|
11327
|
+
// return a doneEvent.
|
|
11328
|
+
queue.notify(null);
|
|
11254
11329
|
};
|
|
11330
|
+
function push(event) {
|
|
11331
|
+
queue.notify(event);
|
|
11332
|
+
if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
|
|
11333
|
+
paused = true;
|
|
11334
|
+
}
|
|
11335
|
+
}
|
|
11336
|
+
function requestMore() {
|
|
11337
|
+
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
11338
|
+
if (!paused && delta > 0) {
|
|
11339
|
+
res?.request(delta);
|
|
11340
|
+
pendingEventsCount = syncQueueRequestSize;
|
|
11341
|
+
}
|
|
11342
|
+
}
|
|
11255
11343
|
// Handle upstream abort
|
|
11256
11344
|
if (options.abortSignal.aborted) {
|
|
11257
11345
|
throw new AbortOperation('Connection request aborted');
|
|
@@ -11306,25 +11394,19 @@ class AbstractRemote {
|
|
|
11306
11394
|
// Helps to prevent double close scenarios
|
|
11307
11395
|
rsocket.onClose(() => (rsocket = null));
|
|
11308
11396
|
return await new Promise((resolve, reject) => {
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11397
|
+
const queueAsIterator = {
|
|
11398
|
+
next: async () => {
|
|
11399
|
+
if (didClose)
|
|
11400
|
+
return doneResult;
|
|
11401
|
+
const notification = await queue.waitForEvent(options.abortSignal);
|
|
11402
|
+
if (didClose) {
|
|
11403
|
+
return doneResult;
|
|
11404
|
+
}
|
|
11405
|
+
else {
|
|
11406
|
+
return valueResult(notification);
|
|
11407
|
+
}
|
|
11318
11408
|
}
|
|
11319
|
-
}
|
|
11320
|
-
const events = new domExports.EventIterator((q) => {
|
|
11321
|
-
queue = q;
|
|
11322
|
-
q.on('highWater', () => (paused = true));
|
|
11323
|
-
q.on('lowWater', () => {
|
|
11324
|
-
paused = false;
|
|
11325
|
-
requestMore();
|
|
11326
|
-
});
|
|
11327
|
-
}, { highWaterMark: SYNC_QUEUE_REQUEST_HIGH_WATER, lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER })[symbolAsyncIterator]();
|
|
11409
|
+
};
|
|
11328
11410
|
res = rsocket.requestStream({
|
|
11329
11411
|
data: toBuffer(options.data),
|
|
11330
11412
|
metadata: toBuffer({
|
|
@@ -11360,11 +11442,11 @@ class AbstractRemote {
|
|
|
11360
11442
|
// The connection is active
|
|
11361
11443
|
if (!connectionEstablished) {
|
|
11362
11444
|
connectionEstablished = true;
|
|
11363
|
-
resolve(
|
|
11445
|
+
resolve(queueAsIterator);
|
|
11364
11446
|
}
|
|
11365
11447
|
const { data } = payload;
|
|
11366
11448
|
if (data) {
|
|
11367
|
-
|
|
11449
|
+
push(data);
|
|
11368
11450
|
}
|
|
11369
11451
|
// Less events are now pending
|
|
11370
11452
|
pendingEventsCount--;
|
|
@@ -11483,7 +11565,7 @@ class AbstractRemote {
|
|
|
11483
11565
|
* Posts a `/sync/stream` request.
|
|
11484
11566
|
*
|
|
11485
11567
|
* Depending on the `Content-Type` of the response, this returns strings for sync lines or encoded BSON documents as
|
|
11486
|
-
*
|
|
11568
|
+
* `Uint8Array`s.
|
|
11487
11569
|
*/
|
|
11488
11570
|
async fetchStream(options) {
|
|
11489
11571
|
const { isBson, stream } = await this.fetchStreamRaw(options);
|
|
@@ -11525,16 +11607,26 @@ function isInterruptingInstruction(instruction) {
|
|
|
11525
11607
|
return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
|
|
11526
11608
|
}
|
|
11527
11609
|
|
|
11610
|
+
/**
|
|
11611
|
+
* @internal
|
|
11612
|
+
*/
|
|
11528
11613
|
exports.LockType = void 0;
|
|
11529
11614
|
(function (LockType) {
|
|
11530
11615
|
LockType["CRUD"] = "crud";
|
|
11531
11616
|
LockType["SYNC"] = "sync";
|
|
11532
11617
|
})(exports.LockType || (exports.LockType = {}));
|
|
11618
|
+
/**
|
|
11619
|
+
* @public
|
|
11620
|
+
*/
|
|
11533
11621
|
exports.SyncStreamConnectionMethod = void 0;
|
|
11534
11622
|
(function (SyncStreamConnectionMethod) {
|
|
11535
11623
|
SyncStreamConnectionMethod["HTTP"] = "http";
|
|
11536
11624
|
SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
|
|
11537
11625
|
})(exports.SyncStreamConnectionMethod || (exports.SyncStreamConnectionMethod = {}));
|
|
11626
|
+
/**
|
|
11627
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
11628
|
+
* @public
|
|
11629
|
+
*/
|
|
11538
11630
|
exports.SyncClientImplementation = void 0;
|
|
11539
11631
|
(function (SyncClientImplementation) {
|
|
11540
11632
|
/**
|
|
@@ -11546,8 +11638,8 @@ exports.SyncClientImplementation = void 0;
|
|
|
11546
11638
|
* ## Compatibility warning
|
|
11547
11639
|
*
|
|
11548
11640
|
* The Rust sync client stores sync data in a format that is slightly different than the one used
|
|
11549
|
-
* by the old JavaScript client. When adopting the {@link RUST} client on existing databases,
|
|
11550
|
-
* migrate the format automatically.
|
|
11641
|
+
* by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
|
|
11642
|
+
* the PowerSync SDK will migrate the format automatically.
|
|
11551
11643
|
*
|
|
11552
11644
|
* SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
|
|
11553
11645
|
* implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
|
|
@@ -11557,14 +11649,29 @@ exports.SyncClientImplementation = void 0;
|
|
|
11557
11649
|
})(exports.SyncClientImplementation || (exports.SyncClientImplementation = {}));
|
|
11558
11650
|
/**
|
|
11559
11651
|
* The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
|
|
11652
|
+
*
|
|
11653
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
11654
|
+
* @public
|
|
11560
11655
|
*/
|
|
11561
11656
|
const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = exports.SyncClientImplementation.RUST;
|
|
11657
|
+
/**
|
|
11658
|
+
* @internal
|
|
11659
|
+
*/
|
|
11562
11660
|
const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
11661
|
+
/**
|
|
11662
|
+
* @internal
|
|
11663
|
+
*/
|
|
11563
11664
|
const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
11665
|
+
/**
|
|
11666
|
+
* @internal
|
|
11667
|
+
*/
|
|
11564
11668
|
const DEFAULT_STREAMING_SYNC_OPTIONS = {
|
|
11565
11669
|
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
11566
11670
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
11567
11671
|
};
|
|
11672
|
+
/**
|
|
11673
|
+
* @internal
|
|
11674
|
+
*/
|
|
11568
11675
|
const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
11569
11676
|
appMetadata: {},
|
|
11570
11677
|
connectionMethod: exports.SyncStreamConnectionMethod.WEB_SOCKET,
|
|
@@ -11574,6 +11681,9 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
|
11574
11681
|
serializedSchema: undefined,
|
|
11575
11682
|
includeDefaultStreams: true
|
|
11576
11683
|
};
|
|
11684
|
+
/**
|
|
11685
|
+
* @internal
|
|
11686
|
+
*/
|
|
11577
11687
|
class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
11578
11688
|
options;
|
|
11579
11689
|
abortController;
|
|
@@ -11897,7 +12007,7 @@ The next upload iteration will be delayed.`);
|
|
|
11897
12007
|
this.handleActiveStreamsChange?.();
|
|
11898
12008
|
}
|
|
11899
12009
|
/**
|
|
11900
|
-
* Older versions of the JS SDK used to encode subkeys as JSON in
|
|
12010
|
+
* Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
|
|
11901
12011
|
* Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
|
|
11902
12012
|
* While this is not a problem as long as it's done consistently, it causes issues when a database
|
|
11903
12013
|
* created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
|
|
@@ -11907,7 +12017,7 @@ The next upload iteration will be delayed.`);
|
|
|
11907
12017
|
* migration is only triggered when necessary (for now). The function returns whether the new format
|
|
11908
12018
|
* should be used, so that the JS SDK is able to write to updated databases.
|
|
11909
12019
|
*
|
|
11910
|
-
* @param requireFixedKeyFormat Whether we require the new format or also support the old one.
|
|
12020
|
+
* @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
|
|
11911
12021
|
* The Rust client requires the new subkey format.
|
|
11912
12022
|
* @returns Whether the database is now using the new, fixed subkey format.
|
|
11913
12023
|
*/
|
|
@@ -12214,7 +12324,8 @@ const MEMORY_TRIGGER_CLAIM_MANAGER = {
|
|
|
12214
12324
|
|
|
12215
12325
|
/**
|
|
12216
12326
|
* SQLite operations to track changes for with {@link TriggerManager}
|
|
12217
|
-
*
|
|
12327
|
+
*
|
|
12328
|
+
* @experimental @alpha
|
|
12218
12329
|
*/
|
|
12219
12330
|
exports.DiffTriggerOperation = void 0;
|
|
12220
12331
|
(function (DiffTriggerOperation) {
|
|
@@ -12276,8 +12387,8 @@ class TriggerManagerImpl {
|
|
|
12276
12387
|
get db() {
|
|
12277
12388
|
return this.options.db;
|
|
12278
12389
|
}
|
|
12279
|
-
async getUUID() {
|
|
12280
|
-
const { id: uuid } = await this.db.get(/* sql */ `
|
|
12390
|
+
async getUUID(ctx) {
|
|
12391
|
+
const { id: uuid } = await (ctx ?? this.db).get(/* sql */ `
|
|
12281
12392
|
SELECT
|
|
12282
12393
|
uuid () as id
|
|
12283
12394
|
`);
|
|
@@ -12390,7 +12501,7 @@ class TriggerManagerImpl {
|
|
|
12390
12501
|
const replicatedColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
|
|
12391
12502
|
const internalSource = sourceDefinition.internalName;
|
|
12392
12503
|
const triggerIds = [];
|
|
12393
|
-
const id = await this.getUUID();
|
|
12504
|
+
const id = await this.getUUID(setupContext);
|
|
12394
12505
|
const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
|
|
12395
12506
|
/**
|
|
12396
12507
|
* We default to replicating all columns if no columns array is provided.
|
|
@@ -12630,18 +12741,29 @@ const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
|
|
|
12630
12741
|
const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
|
|
12631
12742
|
clearLocal: true
|
|
12632
12743
|
};
|
|
12744
|
+
/**
|
|
12745
|
+
* @internal
|
|
12746
|
+
*/
|
|
12633
12747
|
const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
|
|
12634
12748
|
disconnect: true
|
|
12635
12749
|
};
|
|
12750
|
+
/**
|
|
12751
|
+
* @internal
|
|
12752
|
+
*/
|
|
12636
12753
|
const DEFAULT_POWERSYNC_DB_OPTIONS = {
|
|
12637
12754
|
retryDelayMs: 5000,
|
|
12638
12755
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
12639
12756
|
};
|
|
12757
|
+
/**
|
|
12758
|
+
* @internal
|
|
12759
|
+
*/
|
|
12640
12760
|
const DEFAULT_CRUD_BATCH_LIMIT = 100;
|
|
12641
12761
|
/**
|
|
12642
12762
|
* Requesting nested or recursive locks can block the application in some circumstances.
|
|
12643
12763
|
* This default lock timeout will act as a failsafe to throw an error if a lock cannot
|
|
12644
12764
|
* be obtained.
|
|
12765
|
+
*
|
|
12766
|
+
* @internal
|
|
12645
12767
|
*/
|
|
12646
12768
|
const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
12647
12769
|
/**
|
|
@@ -12651,6 +12773,9 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
|
12651
12773
|
const isPowerSyncDatabaseOptionsWithSettings = (test) => {
|
|
12652
12774
|
return typeof test == 'object' && isSQLOpenOptions(test.database);
|
|
12653
12775
|
};
|
|
12776
|
+
/**
|
|
12777
|
+
* @public
|
|
12778
|
+
*/
|
|
12654
12779
|
class AbstractPowerSyncDatabase extends BaseObserver {
|
|
12655
12780
|
options;
|
|
12656
12781
|
/**
|
|
@@ -12808,7 +12933,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
12808
12933
|
/**
|
|
12809
12934
|
* Wait for the first sync operation to complete.
|
|
12810
12935
|
*
|
|
12811
|
-
* @param request Either an abort signal (after which the promise will complete regardless of
|
|
12936
|
+
* @param request - Either an abort signal (after which the promise will complete regardless of
|
|
12812
12937
|
* whether a full sync was completed) or an object providing an abort signal and a priority target.
|
|
12813
12938
|
* When a priority target is set, the promise may complete when all buckets with the given (or higher)
|
|
12814
12939
|
* priorities have been synchronized. This can be earlier than a complete sync.
|
|
@@ -12963,7 +13088,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
12963
13088
|
/**
|
|
12964
13089
|
* Close the sync connection.
|
|
12965
13090
|
*
|
|
12966
|
-
* Use {@link connect} to connect again.
|
|
13091
|
+
* Use {@link AbstractPowerSyncDatabase.connect} to connect again.
|
|
12967
13092
|
*/
|
|
12968
13093
|
async disconnect() {
|
|
12969
13094
|
return this.connectionManager.disconnect();
|
|
@@ -12990,8 +13115,8 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
12990
13115
|
/**
|
|
12991
13116
|
* Create a sync stream to query its status or to subscribe to it.
|
|
12992
13117
|
*
|
|
12993
|
-
* @param name The name of the stream to subscribe to.
|
|
12994
|
-
* @param params Optional parameters for the stream subscription.
|
|
13118
|
+
* @param name - The name of the stream to subscribe to.
|
|
13119
|
+
* @param params - Optional parameters for the stream subscription.
|
|
12995
13120
|
* @returns A {@link SyncStream} instance that can be subscribed to.
|
|
12996
13121
|
* @experimental Sync streams are currently in alpha.
|
|
12997
13122
|
*/
|
|
@@ -13049,14 +13174,14 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
13049
13174
|
* Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
|
|
13050
13175
|
* requesting the next batch.
|
|
13051
13176
|
*
|
|
13052
|
-
* Use
|
|
13177
|
+
* Use the `limit` parameter to specify the maximum number of updates to return in a single
|
|
13053
13178
|
* batch.
|
|
13054
13179
|
*
|
|
13055
13180
|
* This method does include transaction ids in the result, but does not group
|
|
13056
13181
|
* data by transaction. One batch may contain data from multiple transactions,
|
|
13057
13182
|
* and a single transaction may be split over multiple batches.
|
|
13058
13183
|
*
|
|
13059
|
-
* @param limit Maximum number of CRUD entries to include in the batch
|
|
13184
|
+
* @param limit - Maximum number of CRUD entries to include in the batch
|
|
13060
13185
|
* @returns A batch of CRUD operations to upload, or null if there are none
|
|
13061
13186
|
*/
|
|
13062
13187
|
async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
|
|
@@ -13083,7 +13208,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
13083
13208
|
* Once the data have been successfully uploaded, call {@link CrudTransaction.complete} before
|
|
13084
13209
|
* requesting the next transaction.
|
|
13085
13210
|
*
|
|
13086
|
-
* Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
|
|
13211
|
+
* Unlike {@link AbstractPowerSyncDatabase.getCrudBatch}, this only returns data from a single transaction at a time.
|
|
13087
13212
|
* All data for the transaction is loaded into memory.
|
|
13088
13213
|
*
|
|
13089
13214
|
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
@@ -13098,7 +13223,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
13098
13223
|
* This is typically used from the {@link PowerSyncBackendConnector.uploadData} callback. Each entry emitted by the
|
|
13099
13224
|
* returned iterator is a full transaction containing all local writes made while that transaction was active.
|
|
13100
13225
|
*
|
|
13101
|
-
* Unlike {@link getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
13226
|
+
* Unlike {@link AbstractPowerSyncDatabase.getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
13102
13227
|
* {@link CrudTransaction.complete}d yet, this iterator can be used to receive multiple transactions. Calling
|
|
13103
13228
|
* {@link CrudTransaction.complete} will mark that and all prior transactions emitted by the iterator as completed.
|
|
13104
13229
|
*
|
|
@@ -13192,8 +13317,8 @@ SELECT * FROM crud_entries;
|
|
|
13192
13317
|
* the returned result's `rowsAffected` may be `0` for successful `UPDATE` and `DELETE` statements.
|
|
13193
13318
|
* Use a `RETURNING` clause and inspect `result.rows` when you need to confirm which rows changed.
|
|
13194
13319
|
*
|
|
13195
|
-
* @param sql The SQL query to execute
|
|
13196
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13320
|
+
* @param sql - The SQL query to execute
|
|
13321
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13197
13322
|
* @returns The query result as an object with structured key-value pairs
|
|
13198
13323
|
*/
|
|
13199
13324
|
async execute(sql, parameters) {
|
|
@@ -13203,8 +13328,8 @@ SELECT * FROM crud_entries;
|
|
|
13203
13328
|
* Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
|
|
13204
13329
|
* This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
|
|
13205
13330
|
*
|
|
13206
|
-
* @param sql The SQL query to execute
|
|
13207
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13331
|
+
* @param sql - The SQL query to execute
|
|
13332
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13208
13333
|
* @returns The raw query result from the underlying database as a nested array of raw values, where each row is
|
|
13209
13334
|
* represented as an array of column values without field names.
|
|
13210
13335
|
*/
|
|
@@ -13217,8 +13342,8 @@ SELECT * FROM crud_entries;
|
|
|
13217
13342
|
* and optionally return results.
|
|
13218
13343
|
* This is faster than executing separately with each parameter set.
|
|
13219
13344
|
*
|
|
13220
|
-
* @param sql The SQL query to execute
|
|
13221
|
-
* @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
13345
|
+
* @param sql - The SQL query to execute
|
|
13346
|
+
* @param parameters - Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
13222
13347
|
* @returns The query result
|
|
13223
13348
|
*/
|
|
13224
13349
|
async executeBatch(sql, parameters) {
|
|
@@ -13228,8 +13353,8 @@ SELECT * FROM crud_entries;
|
|
|
13228
13353
|
/**
|
|
13229
13354
|
* Execute a read-only query and return results.
|
|
13230
13355
|
*
|
|
13231
|
-
* @param sql The SQL query to execute
|
|
13232
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13356
|
+
* @param sql - The SQL query to execute
|
|
13357
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13233
13358
|
* @returns An array of results
|
|
13234
13359
|
*/
|
|
13235
13360
|
async getAll(sql, parameters) {
|
|
@@ -13239,8 +13364,8 @@ SELECT * FROM crud_entries;
|
|
|
13239
13364
|
/**
|
|
13240
13365
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
13241
13366
|
*
|
|
13242
|
-
* @param sql The SQL query to execute
|
|
13243
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13367
|
+
* @param sql - The SQL query to execute
|
|
13368
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13244
13369
|
* @returns The first result if found, or null if no results are returned
|
|
13245
13370
|
*/
|
|
13246
13371
|
async getOptional(sql, parameters) {
|
|
@@ -13250,8 +13375,8 @@ SELECT * FROM crud_entries;
|
|
|
13250
13375
|
/**
|
|
13251
13376
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
13252
13377
|
*
|
|
13253
|
-
* @param sql The SQL query to execute
|
|
13254
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13378
|
+
* @param sql - The SQL query to execute
|
|
13379
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13255
13380
|
* @returns The first result matching the query
|
|
13256
13381
|
* @throws Error if no rows are returned
|
|
13257
13382
|
*/
|
|
@@ -13261,7 +13386,7 @@ SELECT * FROM crud_entries;
|
|
|
13261
13386
|
}
|
|
13262
13387
|
/**
|
|
13263
13388
|
* Takes a read lock, without starting a transaction.
|
|
13264
|
-
* In most cases, {@link readTransaction} should be used instead.
|
|
13389
|
+
* In most cases, {@link AbstractPowerSyncDatabase.readTransaction} should be used instead.
|
|
13265
13390
|
*/
|
|
13266
13391
|
async readLock(callback) {
|
|
13267
13392
|
await this.waitForReady();
|
|
@@ -13269,7 +13394,7 @@ SELECT * FROM crud_entries;
|
|
|
13269
13394
|
}
|
|
13270
13395
|
/**
|
|
13271
13396
|
* Takes a global lock, without starting a transaction.
|
|
13272
|
-
* In most cases, {@link writeTransaction} should be used instead.
|
|
13397
|
+
* In most cases, {@link AbstractPowerSyncDatabase.writeTransaction} should be used instead.
|
|
13273
13398
|
*/
|
|
13274
13399
|
async writeLock(callback) {
|
|
13275
13400
|
await this.waitForReady();
|
|
@@ -13280,8 +13405,8 @@ SELECT * FROM crud_entries;
|
|
|
13280
13405
|
* Read transactions can run concurrently to a write transaction.
|
|
13281
13406
|
* Changes from any write transaction are not visible to read transactions started before it.
|
|
13282
13407
|
*
|
|
13283
|
-
* @param callback Function to execute within the transaction
|
|
13284
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
13408
|
+
* @param callback - Function to execute within the transaction
|
|
13409
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
13285
13410
|
* @returns The result of the callback
|
|
13286
13411
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
13287
13412
|
*/
|
|
@@ -13298,8 +13423,8 @@ SELECT * FROM crud_entries;
|
|
|
13298
13423
|
* This takes a global lock - only one write transaction can execute against the database at a time.
|
|
13299
13424
|
* Statements within the transaction must be done on the provided {@link Transaction} interface.
|
|
13300
13425
|
*
|
|
13301
|
-
* @param callback Function to execute within the transaction
|
|
13302
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
13426
|
+
* @param callback - Function to execute within the transaction
|
|
13427
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
13303
13428
|
* @returns The result of the callback
|
|
13304
13429
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
13305
13430
|
*/
|
|
@@ -13376,15 +13501,15 @@ SELECT * FROM crud_entries;
|
|
|
13376
13501
|
}
|
|
13377
13502
|
/**
|
|
13378
13503
|
* Execute a read query every time the source tables are modified.
|
|
13379
|
-
* Use {@link
|
|
13504
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
13380
13505
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
13381
13506
|
*
|
|
13382
13507
|
* Note that the `onChange` callback member of the handler is required.
|
|
13383
13508
|
*
|
|
13384
|
-
* @param sql The SQL query to execute
|
|
13385
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13386
|
-
* @param handler Callbacks for handling results and errors
|
|
13387
|
-
* @param options Options for configuring watch behavior
|
|
13509
|
+
* @param sql - The SQL query to execute
|
|
13510
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13511
|
+
* @param handler - Callbacks for handling results and errors
|
|
13512
|
+
* @param options - Options for configuring watch behavior
|
|
13388
13513
|
*/
|
|
13389
13514
|
watchWithCallback(sql, parameters, handler, options) {
|
|
13390
13515
|
const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
|
|
@@ -13397,7 +13522,7 @@ SELECT * FROM crud_entries;
|
|
|
13397
13522
|
const watchedQuery = new OnChangeQueryProcessor({
|
|
13398
13523
|
db: this,
|
|
13399
13524
|
comparator,
|
|
13400
|
-
placeholderData: null,
|
|
13525
|
+
placeholderData: null, // FIXME
|
|
13401
13526
|
watchOptions: {
|
|
13402
13527
|
query: {
|
|
13403
13528
|
compile: () => ({
|
|
@@ -13430,38 +13555,35 @@ SELECT * FROM crud_entries;
|
|
|
13430
13555
|
}
|
|
13431
13556
|
/**
|
|
13432
13557
|
* Execute a read query every time the source tables are modified.
|
|
13433
|
-
* Use {@link
|
|
13558
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
13434
13559
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
13435
13560
|
*
|
|
13436
|
-
* @param sql The SQL query to execute
|
|
13437
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
13438
|
-
* @param options Options for configuring watch behavior
|
|
13561
|
+
* @param sql - The SQL query to execute
|
|
13562
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
13563
|
+
* @param options - Options for configuring watch behavior
|
|
13439
13564
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
13440
13565
|
*/
|
|
13441
13566
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
13442
|
-
return
|
|
13567
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
13443
13568
|
const handler = {
|
|
13444
13569
|
onResult: (result) => {
|
|
13445
|
-
|
|
13570
|
+
queue.notify(result);
|
|
13446
13571
|
},
|
|
13447
13572
|
onError: (error) => {
|
|
13448
|
-
|
|
13573
|
+
queue.notifyError(error);
|
|
13449
13574
|
}
|
|
13450
13575
|
};
|
|
13451
|
-
this.watchWithCallback(sql, parameters, handler, options);
|
|
13452
|
-
|
|
13453
|
-
eventOptions.stop();
|
|
13454
|
-
});
|
|
13455
|
-
});
|
|
13576
|
+
this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
|
|
13577
|
+
}, options?.signal);
|
|
13456
13578
|
}
|
|
13457
13579
|
/**
|
|
13458
13580
|
* Resolves the list of tables that are used in a SQL query.
|
|
13459
13581
|
* If tables are specified in the options, those are used directly.
|
|
13460
13582
|
* Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
|
|
13461
13583
|
*
|
|
13462
|
-
* @param sql The SQL query to analyze
|
|
13463
|
-
* @param parameters Optional parameters for the SQL query
|
|
13464
|
-
* @param options Optional watch options that may contain explicit table list
|
|
13584
|
+
* @param sql - The SQL query to analyze
|
|
13585
|
+
* @param parameters - Optional parameters for the SQL query
|
|
13586
|
+
* @param options - Optional watch options that may contain explicit table list
|
|
13465
13587
|
* @returns Array of table names that the query depends on
|
|
13466
13588
|
*/
|
|
13467
13589
|
async resolveTables(sql, parameters, options) {
|
|
@@ -13490,13 +13612,13 @@ SELECT * FROM crud_entries;
|
|
|
13490
13612
|
/**
|
|
13491
13613
|
* Invoke the provided callback on any changes to any of the specified tables.
|
|
13492
13614
|
*
|
|
13493
|
-
* This is preferred over {@link watchWithCallback} when multiple queries need to be performed
|
|
13615
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithCallback} when multiple queries need to be performed
|
|
13494
13616
|
* together when data is changed.
|
|
13495
13617
|
*
|
|
13496
13618
|
* Note that the `onChange` callback member of the handler is required.
|
|
13497
13619
|
*
|
|
13498
|
-
* @param handler Callbacks for handling change events and errors
|
|
13499
|
-
* @param options Options for configuring watch behavior
|
|
13620
|
+
* @param handler - Callbacks for handling change events and errors
|
|
13621
|
+
* @param options - Options for configuring watch behavior
|
|
13500
13622
|
* @returns A dispose function to stop watching for changes
|
|
13501
13623
|
*/
|
|
13502
13624
|
onChangeWithCallback(handler, options) {
|
|
@@ -13539,31 +13661,26 @@ SELECT * FROM crud_entries;
|
|
|
13539
13661
|
/**
|
|
13540
13662
|
* Create a Stream of changes to any of the specified tables.
|
|
13541
13663
|
*
|
|
13542
|
-
* This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be
|
|
13543
|
-
* together when data is changed.
|
|
13544
|
-
*
|
|
13545
|
-
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
13664
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
13665
|
+
* performed together when data is changed.
|
|
13546
13666
|
*
|
|
13547
|
-
* @param options Options for configuring watch behavior
|
|
13667
|
+
* @param options - Options for configuring watch behavior
|
|
13548
13668
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
13549
13669
|
*/
|
|
13670
|
+
// Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
13550
13671
|
onChangeWithAsyncGenerator(options) {
|
|
13551
|
-
|
|
13552
|
-
|
|
13553
|
-
const dispose = this.onChangeWithCallback({
|
|
13672
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
13673
|
+
this.onChangeWithCallback({
|
|
13554
13674
|
onChange: (event) => {
|
|
13555
|
-
|
|
13675
|
+
queue.notify(event);
|
|
13556
13676
|
},
|
|
13557
13677
|
onError: (error) => {
|
|
13558
|
-
|
|
13678
|
+
queue.notifyError(error);
|
|
13559
13679
|
}
|
|
13560
|
-
}, options);
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
|
|
13564
|
-
});
|
|
13565
|
-
return () => dispose();
|
|
13566
|
-
});
|
|
13680
|
+
}, { ...options, signal: abort });
|
|
13681
|
+
// Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
|
|
13682
|
+
// after the abort signal completes.
|
|
13683
|
+
}, options?.signal);
|
|
13567
13684
|
}
|
|
13568
13685
|
handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
|
|
13569
13686
|
if (changedTables.size > 0) {
|
|
@@ -13582,15 +13699,15 @@ SELECT * FROM crud_entries;
|
|
|
13582
13699
|
changedTables.add(table);
|
|
13583
13700
|
}
|
|
13584
13701
|
}
|
|
13585
|
-
/**
|
|
13586
|
-
* @ignore
|
|
13587
|
-
*/
|
|
13588
13702
|
async executeReadOnly(sql, params) {
|
|
13589
13703
|
await this.waitForReady();
|
|
13590
13704
|
return this.database.readLock((tx) => tx.execute(sql, params));
|
|
13591
13705
|
}
|
|
13592
13706
|
}
|
|
13593
13707
|
|
|
13708
|
+
/**
|
|
13709
|
+
* @internal
|
|
13710
|
+
*/
|
|
13594
13711
|
class AbstractPowerSyncDatabaseOpenFactory {
|
|
13595
13712
|
options;
|
|
13596
13713
|
constructor(options) {
|
|
@@ -13615,6 +13732,9 @@ class AbstractPowerSyncDatabaseOpenFactory {
|
|
|
13615
13732
|
}
|
|
13616
13733
|
}
|
|
13617
13734
|
|
|
13735
|
+
/**
|
|
13736
|
+
* @internal
|
|
13737
|
+
*/
|
|
13618
13738
|
function runOnSchemaChange(callback, db, options) {
|
|
13619
13739
|
const triggerWatchedQuery = () => {
|
|
13620
13740
|
const abortController = new AbortController();
|
|
@@ -13639,6 +13759,9 @@ function runOnSchemaChange(callback, db, options) {
|
|
|
13639
13759
|
triggerWatchedQuery();
|
|
13640
13760
|
}
|
|
13641
13761
|
|
|
13762
|
+
/**
|
|
13763
|
+
* @public
|
|
13764
|
+
*/
|
|
13642
13765
|
function compilableQueryWatch(db, query, handler, options) {
|
|
13643
13766
|
const { onResult, onError = (e) => { } } = handler ?? {};
|
|
13644
13767
|
if (!onResult) {
|
|
@@ -13676,8 +13799,14 @@ function compilableQueryWatch(db, query, handler, options) {
|
|
|
13676
13799
|
runOnSchemaChange(watchQuery, db, options);
|
|
13677
13800
|
}
|
|
13678
13801
|
|
|
13802
|
+
/**
|
|
13803
|
+
* @internal
|
|
13804
|
+
*/
|
|
13679
13805
|
const MAX_OP_ID = '9223372036854775807';
|
|
13680
13806
|
|
|
13807
|
+
/**
|
|
13808
|
+
* @internal
|
|
13809
|
+
*/
|
|
13681
13810
|
class SqliteBucketStorage extends BaseObserver {
|
|
13682
13811
|
db;
|
|
13683
13812
|
logger;
|
|
@@ -13838,6 +13967,8 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
13838
13967
|
* Thrown when an underlying database connection is closed.
|
|
13839
13968
|
* This is particularly relevant when worker connections are marked as closed while
|
|
13840
13969
|
* operations are still in progress.
|
|
13970
|
+
*
|
|
13971
|
+
* @internal
|
|
13841
13972
|
*/
|
|
13842
13973
|
class ConnectionClosedError extends Error {
|
|
13843
13974
|
static NAME = 'ConnectionClosedError';
|
|
@@ -13857,6 +13988,8 @@ class ConnectionClosedError extends Error {
|
|
|
13857
13988
|
|
|
13858
13989
|
/**
|
|
13859
13990
|
* A schema is a collection of tables. It is used to define the structure of a database.
|
|
13991
|
+
*
|
|
13992
|
+
* @public
|
|
13860
13993
|
*/
|
|
13861
13994
|
class Schema {
|
|
13862
13995
|
/*
|
|
@@ -13893,7 +14026,7 @@ class Schema {
|
|
|
13893
14026
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
13894
14027
|
* using client-side table and column constraints.
|
|
13895
14028
|
*
|
|
13896
|
-
* @param tables An object of (table name, raw table definition) entries.
|
|
14029
|
+
* @param tables - An object of (table name, raw table definition) entries.
|
|
13897
14030
|
*/
|
|
13898
14031
|
withRawTables(tables) {
|
|
13899
14032
|
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
@@ -13939,6 +14072,8 @@ class Schema {
|
|
|
13939
14072
|
Generate a new table from the columns and indexes
|
|
13940
14073
|
@deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
|
|
13941
14074
|
This will be removed in the next major release.
|
|
14075
|
+
|
|
14076
|
+
@public
|
|
13942
14077
|
*/
|
|
13943
14078
|
class TableV2 extends Table {
|
|
13944
14079
|
}
|
|
@@ -13949,6 +14084,8 @@ function sanitizeString(input) {
|
|
|
13949
14084
|
/**
|
|
13950
14085
|
* Helper function for sanitizing UUID input strings.
|
|
13951
14086
|
* Typically used with {@link sanitizeSQL}.
|
|
14087
|
+
*
|
|
14088
|
+
* @alpha
|
|
13952
14089
|
*/
|
|
13953
14090
|
function sanitizeUUID(uuid) {
|
|
13954
14091
|
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;
|
|
@@ -13985,6 +14122,8 @@ function sanitizeUUID(uuid) {
|
|
|
13985
14122
|
* // Incorrect:
|
|
13986
14123
|
* sanitizeSQL`New.id = '${myID}'` // Produces double quotes: New.id = ''O''Reilly''
|
|
13987
14124
|
* ```
|
|
14125
|
+
*
|
|
14126
|
+
* @alpha
|
|
13988
14127
|
*/
|
|
13989
14128
|
function sanitizeSQL(strings, ...values) {
|
|
13990
14129
|
let result = '';
|
|
@@ -14014,6 +14153,8 @@ function sanitizeSQL(strings, ...values) {
|
|
|
14014
14153
|
|
|
14015
14154
|
/**
|
|
14016
14155
|
* Performs a {@link AbstractPowerSyncDatabase.getAll} operation for a watched query.
|
|
14156
|
+
*
|
|
14157
|
+
* @public
|
|
14017
14158
|
*/
|
|
14018
14159
|
class GetAllQuery {
|
|
14019
14160
|
options;
|
|
@@ -14038,6 +14179,9 @@ class GetAllQuery {
|
|
|
14038
14179
|
}
|
|
14039
14180
|
|
|
14040
14181
|
const TypedLogger = Logger;
|
|
14182
|
+
/**
|
|
14183
|
+
* @public
|
|
14184
|
+
*/
|
|
14041
14185
|
const LogLevel = {
|
|
14042
14186
|
TRACE: TypedLogger.TRACE,
|
|
14043
14187
|
DEBUG: TypedLogger.DEBUG,
|
|
@@ -14054,6 +14198,7 @@ const LogLevel = {
|
|
|
14054
14198
|
* across all loggers created with `createLogger`. Adjusting settings on this
|
|
14055
14199
|
* base logger affects all loggers derived from it unless explicitly overridden.
|
|
14056
14200
|
*
|
|
14201
|
+
* @public
|
|
14057
14202
|
*/
|
|
14058
14203
|
function createBaseLogger() {
|
|
14059
14204
|
return Logger;
|
|
@@ -14064,6 +14209,8 @@ function createBaseLogger() {
|
|
|
14064
14209
|
* Named loggers allow specific modules or areas of your application to have
|
|
14065
14210
|
* their own logging levels and behaviors. These loggers inherit configuration
|
|
14066
14211
|
* from the base logger by default but can override settings independently.
|
|
14212
|
+
*
|
|
14213
|
+
* @public
|
|
14067
14214
|
*/
|
|
14068
14215
|
function createLogger(name, options = {}) {
|
|
14069
14216
|
const logger = Logger.get(name);
|
|
@@ -14073,6 +14220,9 @@ function createLogger(name, options = {}) {
|
|
|
14073
14220
|
return logger;
|
|
14074
14221
|
}
|
|
14075
14222
|
|
|
14223
|
+
/**
|
|
14224
|
+
* @internal
|
|
14225
|
+
*/
|
|
14076
14226
|
const parseQuery = (query, parameters) => {
|
|
14077
14227
|
let sqlStatement;
|
|
14078
14228
|
if (typeof query == 'string') {
|