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