@powersync/common 1.53.1 → 1.54.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.
Files changed (217) hide show
  1. package/dist/bundle.cjs +560 -353
  2. package/dist/bundle.cjs.map +1 -1
  3. package/dist/bundle.mjs +560 -353
  4. package/dist/bundle.mjs.map +1 -1
  5. package/dist/bundle.node.cjs +560 -353
  6. package/dist/bundle.node.cjs.map +1 -1
  7. package/dist/bundle.node.mjs +560 -353
  8. package/dist/bundle.node.mjs.map +1 -1
  9. package/dist/index.d.cts +733 -198
  10. package/lib/attachments/AttachmentContext.d.ts +7 -6
  11. package/lib/attachments/AttachmentContext.js +2 -1
  12. package/lib/attachments/AttachmentContext.js.map +1 -1
  13. package/lib/attachments/AttachmentErrorHandler.d.ts +6 -6
  14. package/lib/attachments/AttachmentQueue.d.ts +61 -20
  15. package/lib/attachments/AttachmentQueue.js +16 -18
  16. package/lib/attachments/AttachmentQueue.js.map +1 -1
  17. package/lib/attachments/LocalStorageAdapter.d.ts +14 -8
  18. package/lib/attachments/LocalStorageAdapter.js +3 -0
  19. package/lib/attachments/LocalStorageAdapter.js.map +1 -1
  20. package/lib/attachments/RemoteStorageAdapter.d.ts +4 -4
  21. package/lib/attachments/Schema.d.ts +12 -4
  22. package/lib/attachments/Schema.js +8 -3
  23. package/lib/attachments/Schema.js.map +1 -1
  24. package/lib/attachments/WatchedAttachmentItem.d.ts +3 -1
  25. package/lib/client/AbstractPowerSyncDatabase.d.ts +110 -58
  26. package/lib/client/AbstractPowerSyncDatabase.js +59 -48
  27. package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
  28. package/lib/client/AbstractPowerSyncOpenFactory.d.ts +6 -0
  29. package/lib/client/AbstractPowerSyncOpenFactory.js +3 -0
  30. package/lib/client/AbstractPowerSyncOpenFactory.js.map +1 -1
  31. package/lib/client/ConnectionManager.d.ts +4 -1
  32. package/lib/client/ConnectionManager.js +1 -1
  33. package/lib/client/ConnectionManager.js.map +1 -1
  34. package/lib/client/Query.d.ts +9 -0
  35. package/lib/client/SQLOpenFactory.d.ts +12 -0
  36. package/lib/client/SQLOpenFactory.js +6 -0
  37. package/lib/client/SQLOpenFactory.js.map +1 -1
  38. package/lib/client/compilableQueryWatch.d.ts +6 -0
  39. package/lib/client/compilableQueryWatch.js +3 -0
  40. package/lib/client/compilableQueryWatch.js.map +1 -1
  41. package/lib/client/connection/PowerSyncBackendConnector.d.ts +3 -0
  42. package/lib/client/connection/PowerSyncCredentials.d.ts +3 -0
  43. package/lib/client/constants.d.ts +3 -0
  44. package/lib/client/constants.js +3 -0
  45. package/lib/client/constants.js.map +1 -1
  46. package/lib/client/runOnSchemaChange.d.ts +3 -0
  47. package/lib/client/runOnSchemaChange.js +3 -0
  48. package/lib/client/runOnSchemaChange.js.map +1 -1
  49. package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +12 -0
  50. package/lib/client/sync/bucket/BucketStorageAdapter.js +6 -0
  51. package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -1
  52. package/lib/client/sync/bucket/CrudBatch.d.ts +2 -0
  53. package/lib/client/sync/bucket/CrudBatch.js +2 -0
  54. package/lib/client/sync/bucket/CrudBatch.js.map +1 -1
  55. package/lib/client/sync/bucket/CrudEntry.d.ts +9 -0
  56. package/lib/client/sync/bucket/CrudEntry.js +4 -0
  57. package/lib/client/sync/bucket/CrudEntry.js.map +1 -1
  58. package/lib/client/sync/bucket/CrudTransaction.d.ts +3 -0
  59. package/lib/client/sync/bucket/CrudTransaction.js +3 -0
  60. package/lib/client/sync/bucket/CrudTransaction.js.map +1 -1
  61. package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +3 -0
  62. package/lib/client/sync/bucket/SqliteBucketStorage.js +3 -0
  63. package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -1
  64. package/lib/client/sync/stream/AbstractRemote.d.ts +30 -1
  65. package/lib/client/sync/stream/AbstractRemote.js +15 -1
  66. package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
  67. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +61 -14
  68. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +60 -47
  69. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
  70. package/lib/client/sync/stream/JsonValue.d.ts +3 -0
  71. package/lib/client/sync/stream/WebsocketClientTransport.js +2 -1
  72. package/lib/client/sync/stream/WebsocketClientTransport.js.map +1 -1
  73. package/lib/client/sync/sync-streams.d.ts +22 -7
  74. package/lib/client/triggers/TriggerManager.d.ts +19 -18
  75. package/lib/client/triggers/TriggerManager.js +2 -1
  76. package/lib/client/triggers/TriggerManager.js.map +1 -1
  77. package/lib/client/triggers/TriggerManagerImpl.d.ts +1 -1
  78. package/lib/client/triggers/TriggerManagerImpl.js +3 -3
  79. package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
  80. package/lib/client/triggers/sanitizeSQL.d.ts +4 -0
  81. package/lib/client/triggers/sanitizeSQL.js +4 -0
  82. package/lib/client/triggers/sanitizeSQL.js.map +1 -1
  83. package/lib/client/watched/GetAllQuery.d.ts +4 -0
  84. package/lib/client/watched/GetAllQuery.js +2 -0
  85. package/lib/client/watched/GetAllQuery.js.map +1 -1
  86. package/lib/client/watched/WatchedQuery.d.ts +24 -2
  87. package/lib/client/watched/WatchedQuery.js +9 -0
  88. package/lib/client/watched/WatchedQuery.js.map +1 -1
  89. package/lib/client/watched/processors/AbstractQueryProcessor.d.ts +1 -1
  90. package/lib/client/watched/processors/AbstractQueryProcessor.js.map +1 -1
  91. package/lib/client/watched/processors/DifferentialQueryProcessor.d.ts +20 -0
  92. package/lib/client/watched/processors/DifferentialQueryProcessor.js +4 -0
  93. package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -1
  94. package/lib/client/watched/processors/OnChangeQueryProcessor.d.ts +4 -0
  95. package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -1
  96. package/lib/client/watched/processors/comparators.d.ts +8 -0
  97. package/lib/client/watched/processors/comparators.js +4 -0
  98. package/lib/client/watched/processors/comparators.js.map +1 -1
  99. package/lib/db/ConnectionClosedError.d.ts +2 -0
  100. package/lib/db/ConnectionClosedError.js +2 -0
  101. package/lib/db/ConnectionClosedError.js.map +1 -1
  102. package/lib/db/DBAdapter.d.ts +56 -6
  103. package/lib/db/DBAdapter.js +15 -3
  104. package/lib/db/DBAdapter.js.map +1 -1
  105. package/lib/db/crud/SyncProgress.d.ts +6 -1
  106. package/lib/db/crud/SyncProgress.js +2 -0
  107. package/lib/db/crud/SyncProgress.js.map +1 -1
  108. package/lib/db/crud/SyncStatus.d.ts +36 -38
  109. package/lib/db/crud/SyncStatus.js +19 -14
  110. package/lib/db/crud/SyncStatus.js.map +1 -1
  111. package/lib/db/crud/UploadQueueStatus.d.ts +3 -0
  112. package/lib/db/crud/UploadQueueStatus.js +3 -0
  113. package/lib/db/crud/UploadQueueStatus.js.map +1 -1
  114. package/lib/db/schema/Column.d.ts +28 -0
  115. package/lib/db/schema/Column.js +16 -3
  116. package/lib/db/schema/Column.js.map +1 -1
  117. package/lib/db/schema/Index.d.ts +9 -0
  118. package/lib/db/schema/Index.js +6 -0
  119. package/lib/db/schema/Index.js.map +1 -1
  120. package/lib/db/schema/IndexedColumn.d.ts +9 -0
  121. package/lib/db/schema/IndexedColumn.js +6 -0
  122. package/lib/db/schema/IndexedColumn.js.map +1 -1
  123. package/lib/db/schema/RawTable.d.ts +7 -1
  124. package/lib/db/schema/Schema.d.ts +6 -1
  125. package/lib/db/schema/Schema.js +3 -1
  126. package/lib/db/schema/Schema.js.map +1 -1
  127. package/lib/db/schema/Table.d.ts +27 -3
  128. package/lib/db/schema/Table.js +9 -0
  129. package/lib/db/schema/Table.js.map +1 -1
  130. package/lib/db/schema/TableV2.d.ts +2 -0
  131. package/lib/db/schema/TableV2.js +2 -0
  132. package/lib/db/schema/TableV2.js.map +1 -1
  133. package/lib/index.d.ts +1 -1
  134. package/lib/types/types.d.ts +6 -0
  135. package/lib/utils/AbortOperation.d.ts +2 -0
  136. package/lib/utils/AbortOperation.js +2 -0
  137. package/lib/utils/AbortOperation.js.map +1 -1
  138. package/lib/utils/BaseObserver.d.ts +12 -0
  139. package/lib/utils/BaseObserver.js +3 -0
  140. package/lib/utils/BaseObserver.js.map +1 -1
  141. package/lib/utils/ControlledExecutor.d.ts +6 -0
  142. package/lib/utils/ControlledExecutor.js +3 -0
  143. package/lib/utils/ControlledExecutor.js.map +1 -1
  144. package/lib/utils/Logger.d.ts +9 -0
  145. package/lib/utils/Logger.js +6 -0
  146. package/lib/utils/Logger.js.map +1 -1
  147. package/lib/utils/async.d.ts +13 -7
  148. package/lib/utils/async.js +38 -24
  149. package/lib/utils/async.js.map +1 -1
  150. package/lib/utils/mutex.d.ts +8 -0
  151. package/lib/utils/mutex.js +3 -0
  152. package/lib/utils/mutex.js.map +1 -1
  153. package/lib/utils/parseQuery.d.ts +6 -0
  154. package/lib/utils/parseQuery.js +3 -0
  155. package/lib/utils/parseQuery.js.map +1 -1
  156. package/lib/utils/stream_transform.d.ts +3 -1
  157. package/lib/utils/stream_transform.js.map +1 -1
  158. package/package.json +3 -2
  159. package/src/attachments/AttachmentContext.ts +7 -6
  160. package/src/attachments/AttachmentErrorHandler.ts +6 -6
  161. package/src/attachments/AttachmentQueue.ts +71 -23
  162. package/src/attachments/LocalStorageAdapter.ts +14 -8
  163. package/src/attachments/README.md +2 -0
  164. package/src/attachments/RemoteStorageAdapter.ts +4 -4
  165. package/src/attachments/Schema.ts +12 -4
  166. package/src/attachments/WatchedAttachmentItem.ts +3 -1
  167. package/src/client/AbstractPowerSyncDatabase.ts +117 -62
  168. package/src/client/AbstractPowerSyncOpenFactory.ts +6 -0
  169. package/src/client/ConnectionManager.ts +4 -1
  170. package/src/client/Query.ts +9 -0
  171. package/src/client/SQLOpenFactory.ts +12 -0
  172. package/src/client/compilableQueryWatch.ts +6 -0
  173. package/src/client/connection/PowerSyncBackendConnector.ts +3 -0
  174. package/src/client/connection/PowerSyncCredentials.ts +3 -0
  175. package/src/client/constants.ts +3 -0
  176. package/src/client/runOnSchemaChange.ts +3 -0
  177. package/src/client/sync/bucket/BucketStorageAdapter.ts +12 -0
  178. package/src/client/sync/bucket/CrudBatch.ts +2 -0
  179. package/src/client/sync/bucket/CrudEntry.ts +9 -0
  180. package/src/client/sync/bucket/CrudTransaction.ts +3 -0
  181. package/src/client/sync/bucket/SqliteBucketStorage.ts +3 -0
  182. package/src/client/sync/stream/AbstractRemote.ts +30 -1
  183. package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +86 -59
  184. package/src/client/sync/stream/JsonValue.ts +3 -0
  185. package/src/client/sync/stream/WebsocketClientTransport.ts +3 -1
  186. package/src/client/sync/sync-streams.ts +22 -9
  187. package/src/client/triggers/TriggerManager.ts +19 -18
  188. package/src/client/triggers/TriggerManagerImpl.ts +5 -5
  189. package/src/client/triggers/sanitizeSQL.ts +5 -0
  190. package/src/client/watched/GetAllQuery.ts +5 -1
  191. package/src/client/watched/WatchedQuery.ts +24 -2
  192. package/src/client/watched/processors/AbstractQueryProcessor.ts +6 -6
  193. package/src/client/watched/processors/DifferentialQueryProcessor.ts +28 -5
  194. package/src/client/watched/processors/OnChangeQueryProcessor.ts +9 -3
  195. package/src/client/watched/processors/comparators.ts +8 -0
  196. package/src/db/ConnectionClosedError.ts +2 -0
  197. package/src/db/DBAdapter.ts +58 -6
  198. package/src/db/crud/SyncProgress.ts +6 -1
  199. package/src/db/crud/SyncStatus.ts +40 -21
  200. package/src/db/crud/UploadQueueStatus.ts +3 -0
  201. package/src/db/schema/Column.ts +28 -3
  202. package/src/db/schema/Index.ts +9 -0
  203. package/src/db/schema/IndexedColumn.ts +9 -0
  204. package/src/db/schema/RawTable.ts +7 -1
  205. package/src/db/schema/Schema.ts +8 -3
  206. package/src/db/schema/Table.ts +30 -5
  207. package/src/db/schema/TableV2.ts +2 -0
  208. package/src/index.ts +1 -1
  209. package/src/types/types.ts +6 -0
  210. package/src/utils/AbortOperation.ts +2 -0
  211. package/src/utils/BaseObserver.ts +12 -0
  212. package/src/utils/ControlledExecutor.ts +6 -0
  213. package/src/utils/Logger.ts +9 -0
  214. package/src/utils/async.ts +51 -24
  215. package/src/utils/mutex.ts +12 -0
  216. package/src/utils/parseQuery.ts +6 -0
  217. package/src/utils/stream_transform.ts +3 -1
@@ -3,7 +3,10 @@
3
3
  var eventIterator = require('event-iterator');
4
4
  var node_buffer = require('node:buffer');
5
5
 
6
- // https://www.sqlite.org/lang_expr.html#castexpr
6
+ /**
7
+ * @see https://www.sqlite.org/lang_expr.html#castexpr
8
+ * @public
9
+ */
7
10
  exports.ColumnType = void 0;
8
11
  (function (ColumnType) {
9
12
  ColumnType["TEXT"] = "TEXT";
@@ -19,14 +22,24 @@ const integer = {
19
22
  const real = {
20
23
  type: exports.ColumnType.REAL
21
24
  };
22
- // powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
23
- // In earlier versions this was limited to 63.
25
+ /**
26
+ * powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
27
+ * In earlier versions this was limited to 63.
28
+ *
29
+ * @internal
30
+ */
24
31
  const MAX_AMOUNT_OF_COLUMNS = 1999;
32
+ /**
33
+ * @public
34
+ */
25
35
  const column = {
26
36
  text,
27
37
  integer,
28
38
  real
29
39
  };
40
+ /**
41
+ * @public
42
+ */
30
43
  class Column {
31
44
  options;
32
45
  constructor(options) {
@@ -46,9 +59,15 @@ class Column {
46
59
  }
47
60
  }
48
61
 
62
+ /**
63
+ * @internal
64
+ */
49
65
  const DEFAULT_INDEX_COLUMN_OPTIONS = {
50
66
  ascending: true
51
67
  };
68
+ /**
69
+ * @public
70
+ */
52
71
  class IndexedColumn {
53
72
  options;
54
73
  static createAscending(column) {
@@ -75,9 +94,15 @@ class IndexedColumn {
75
94
  }
76
95
  }
77
96
 
97
+ /**
98
+ * @internal
99
+ */
78
100
  const DEFAULT_INDEX_OPTIONS = {
79
101
  columns: []
80
102
  };
103
+ /**
104
+ * @public
105
+ */
81
106
  class Index {
82
107
  options;
83
108
  static createAscending(options, columnNames) {
@@ -119,6 +144,9 @@ function encodeTableOptions(options) {
119
144
  };
120
145
  }
121
146
 
147
+ /**
148
+ * @internal
149
+ */
122
150
  const DEFAULT_TABLE_OPTIONS = {
123
151
  indexes: [],
124
152
  insertOnly: false,
@@ -127,7 +155,13 @@ const DEFAULT_TABLE_OPTIONS = {
127
155
  trackMetadata: false,
128
156
  ignoreEmptyUpdates: false
129
157
  };
158
+ /**
159
+ * @internal
160
+ */
130
161
  const InvalidSQLCharacters = /["'%,.#\s[\]]/;
162
+ /**
163
+ * @public
164
+ */
131
165
  class Table {
132
166
  options;
133
167
  _mappedColumns;
@@ -318,6 +352,11 @@ class Table {
318
352
  }
319
353
  }
320
354
 
355
+ /**
356
+ * The default name of the local table storing attachment data.
357
+ *
358
+ * @alpha
359
+ */
321
360
  const ATTACHMENT_TABLE = 'attachments';
322
361
  /**
323
362
  * Maps a database row to an AttachmentRecord.
@@ -325,7 +364,7 @@ const ATTACHMENT_TABLE = 'attachments';
325
364
  * @param row - The database row object
326
365
  * @returns The corresponding AttachmentRecord
327
366
  *
328
- * @experimental
367
+ * @alpha
329
368
  */
330
369
  function attachmentFromSql(row) {
331
370
  return {
@@ -343,7 +382,7 @@ function attachmentFromSql(row) {
343
382
  /**
344
383
  * AttachmentState represents the current synchronization state of an attachment.
345
384
  *
346
- * @experimental
385
+ * @alpha
347
386
  */
348
387
  exports.AttachmentState = void 0;
349
388
  (function (AttachmentState) {
@@ -356,7 +395,7 @@ exports.AttachmentState = void 0;
356
395
  /**
357
396
  * AttachmentTable defines the schema for the attachment queue table.
358
397
  *
359
- * @internal
398
+ * @alpha
360
399
  */
361
400
  class AttachmentTable extends Table {
362
401
  constructor(options) {
@@ -384,7 +423,8 @@ class AttachmentTable extends Table {
384
423
  * Provides methods to query, insert, update, and delete attachment records with
385
424
  * proper transaction management through PowerSync.
386
425
  *
387
- * @internal
426
+ * @experimental
427
+ * @alpha
388
428
  */
389
429
  class AttachmentContext {
390
430
  /** PowerSync database instance for executing queries */
@@ -606,6 +646,9 @@ class AttachmentContext {
606
646
  }
607
647
  }
608
648
 
649
+ /**
650
+ * @public
651
+ */
609
652
  exports.WatchedQueryListenerEvent = void 0;
610
653
  (function (WatchedQueryListenerEvent) {
611
654
  WatchedQueryListenerEvent["ON_DATA"] = "onData";
@@ -614,176 +657,18 @@ exports.WatchedQueryListenerEvent = void 0;
614
657
  WatchedQueryListenerEvent["SETTINGS_WILL_UPDATE"] = "settingsWillUpdate";
615
658
  WatchedQueryListenerEvent["CLOSED"] = "closed";
616
659
  })(exports.WatchedQueryListenerEvent || (exports.WatchedQueryListenerEvent = {}));
660
+ /**
661
+ * @internal
662
+ */
617
663
  const DEFAULT_WATCH_THROTTLE_MS = 30;
664
+ /**
665
+ * @internal
666
+ */
618
667
  const DEFAULT_WATCH_QUERY_OPTIONS = {
619
668
  throttleMs: DEFAULT_WATCH_THROTTLE_MS,
620
669
  reportFetching: true
621
670
  };
622
671
 
623
- /**
624
- * Orchestrates attachment synchronization between local and remote storage.
625
- * Handles uploads, downloads, deletions, and state transitions.
626
- *
627
- * @internal
628
- */
629
- class SyncingService {
630
- attachmentService;
631
- localStorage;
632
- remoteStorage;
633
- logger;
634
- errorHandler;
635
- constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
636
- this.attachmentService = attachmentService;
637
- this.localStorage = localStorage;
638
- this.remoteStorage = remoteStorage;
639
- this.logger = logger;
640
- this.errorHandler = errorHandler;
641
- }
642
- /**
643
- * Processes attachments based on their state (upload, download, or delete).
644
- * All updates are saved in a single batch after processing.
645
- *
646
- * @param attachments - Array of attachment records to process
647
- * @param context - Attachment context for database operations
648
- * @returns Promise that resolves when all attachments have been processed and saved
649
- */
650
- async processAttachments(attachments, context) {
651
- const updatedAttachments = [];
652
- for (const attachment of attachments) {
653
- switch (attachment.state) {
654
- case exports.AttachmentState.QUEUED_UPLOAD:
655
- const uploaded = await this.uploadAttachment(attachment);
656
- updatedAttachments.push(uploaded);
657
- break;
658
- case exports.AttachmentState.QUEUED_DOWNLOAD:
659
- const downloaded = await this.downloadAttachment(attachment);
660
- updatedAttachments.push(downloaded);
661
- break;
662
- case exports.AttachmentState.QUEUED_DELETE:
663
- const deleted = await this.deleteAttachment(attachment, context);
664
- updatedAttachments.push(deleted);
665
- break;
666
- }
667
- }
668
- await context.saveAttachments(updatedAttachments);
669
- }
670
- /**
671
- * Uploads an attachment from local storage to remote storage.
672
- * On success, marks as SYNCED. On failure, defers to error handler or archives.
673
- *
674
- * @param attachment - The attachment record to upload
675
- * @returns Updated attachment record with new state
676
- * @throws Error if the attachment has no localUri
677
- */
678
- async uploadAttachment(attachment) {
679
- this.logger.info(`Uploading attachment ${attachment.filename}`);
680
- try {
681
- if (attachment.localUri == null) {
682
- throw new Error(`No localUri for attachment ${attachment.id}`);
683
- }
684
- const fileBlob = await this.localStorage.readFile(attachment.localUri);
685
- await this.remoteStorage.uploadFile(fileBlob, attachment);
686
- return {
687
- ...attachment,
688
- state: exports.AttachmentState.SYNCED,
689
- hasSynced: true
690
- };
691
- }
692
- catch (error) {
693
- const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
694
- if (!shouldRetry) {
695
- return {
696
- ...attachment,
697
- state: exports.AttachmentState.ARCHIVED
698
- };
699
- }
700
- return attachment;
701
- }
702
- }
703
- /**
704
- * Downloads an attachment from remote storage to local storage.
705
- * Retrieves the file, converts to base64, and saves locally.
706
- * On success, marks as SYNCED. On failure, defers to error handler or archives.
707
- *
708
- * @param attachment - The attachment record to download
709
- * @returns Updated attachment record with local URI and new state
710
- */
711
- async downloadAttachment(attachment) {
712
- this.logger.info(`Downloading attachment ${attachment.filename}`);
713
- try {
714
- const fileData = await this.remoteStorage.downloadFile(attachment);
715
- const localUri = this.localStorage.getLocalUri(attachment.filename);
716
- await this.localStorage.saveFile(localUri, fileData);
717
- return {
718
- ...attachment,
719
- state: exports.AttachmentState.SYNCED,
720
- localUri: localUri,
721
- hasSynced: true
722
- };
723
- }
724
- catch (error) {
725
- const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
726
- if (!shouldRetry) {
727
- return {
728
- ...attachment,
729
- state: exports.AttachmentState.ARCHIVED
730
- };
731
- }
732
- return attachment;
733
- }
734
- }
735
- /**
736
- * Deletes an attachment from both remote and local storage.
737
- * Removes the remote file, local file (if exists), and the attachment record.
738
- * On failure, defers to error handler or archives.
739
- *
740
- * @param attachment - The attachment record to delete
741
- * @param context - Attachment context for database operations
742
- * @returns Updated attachment record
743
- */
744
- async deleteAttachment(attachment, context) {
745
- try {
746
- await this.remoteStorage.deleteFile(attachment);
747
- if (attachment.localUri) {
748
- await this.localStorage.deleteFile(attachment.localUri);
749
- }
750
- await context.deleteAttachment(attachment.id);
751
- return {
752
- ...attachment,
753
- state: exports.AttachmentState.ARCHIVED
754
- };
755
- }
756
- catch (error) {
757
- const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
758
- if (!shouldRetry) {
759
- return {
760
- ...attachment,
761
- state: exports.AttachmentState.ARCHIVED
762
- };
763
- }
764
- return attachment;
765
- }
766
- }
767
- /**
768
- * Performs cleanup of archived attachments by removing their local files and records.
769
- * Errors during local file deletion are logged but do not prevent record deletion.
770
- */
771
- async deleteArchivedAttachments(context) {
772
- return await context.deleteArchivedAttachments(async (archivedAttachments) => {
773
- for (const attachment of archivedAttachments) {
774
- if (attachment.localUri) {
775
- try {
776
- await this.localStorage.deleteFile(attachment.localUri);
777
- }
778
- catch (error) {
779
- this.logger.error('Error deleting local file for archived attachment', error);
780
- }
781
- }
782
- }
783
- });
784
- }
785
- }
786
-
787
672
  /**
788
673
  * A simple fixed-capacity queue implementation.
789
674
  *
@@ -969,6 +854,9 @@ class Mutex {
969
854
  }
970
855
  }
971
856
  }
857
+ /**
858
+ * @internal
859
+ */
972
860
  function timeoutSignal(timeout) {
973
861
  if (timeout == null)
974
862
  return;
@@ -997,36 +885,200 @@ class AttachmentService {
997
885
  this.context = new AttachmentContext(db, tableName, logger, archivedCacheLimit);
998
886
  }
999
887
  /**
1000
- * Creates a differential watch query for active attachments requiring synchronization.
1001
- * @returns Watch query that emits changes for queued uploads, downloads, and deletes
888
+ * Creates a differential watch query for active attachments requiring synchronization.
889
+ * @returns Watch query that emits changes for queued uploads, downloads, and deletes
890
+ */
891
+ watchActiveAttachments({ throttleMs } = {}) {
892
+ this.logger.info('Watching active attachments...');
893
+ const watch = this.db
894
+ .query({
895
+ sql: /* sql */ `
896
+ SELECT
897
+ *
898
+ FROM
899
+ ${this.tableName}
900
+ WHERE
901
+ state = ?
902
+ OR state = ?
903
+ OR state = ?
904
+ ORDER BY
905
+ timestamp ASC
906
+ `,
907
+ parameters: [exports.AttachmentState.QUEUED_UPLOAD, exports.AttachmentState.QUEUED_DOWNLOAD, exports.AttachmentState.QUEUED_DELETE]
908
+ })
909
+ .differentialWatch({ throttleMs });
910
+ return watch;
911
+ }
912
+ /**
913
+ * Executes a callback with exclusive access to the attachment context.
914
+ */
915
+ async withContext(callback) {
916
+ return this.mutex.runExclusive(async () => {
917
+ return callback(this.context);
918
+ });
919
+ }
920
+ }
921
+
922
+ /**
923
+ * Orchestrates attachment synchronization between local and remote storage.
924
+ * Handles uploads, downloads, deletions, and state transitions.
925
+ *
926
+ * @internal
927
+ */
928
+ class SyncingService {
929
+ attachmentService;
930
+ localStorage;
931
+ remoteStorage;
932
+ logger;
933
+ errorHandler;
934
+ constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
935
+ this.attachmentService = attachmentService;
936
+ this.localStorage = localStorage;
937
+ this.remoteStorage = remoteStorage;
938
+ this.logger = logger;
939
+ this.errorHandler = errorHandler;
940
+ }
941
+ /**
942
+ * Processes attachments based on their state (upload, download, or delete).
943
+ * All updates are saved in a single batch after processing.
944
+ *
945
+ * @param attachments - Array of attachment records to process
946
+ * @param context - Attachment context for database operations
947
+ * @returns Promise that resolves when all attachments have been processed and saved
948
+ */
949
+ async processAttachments(attachments, context) {
950
+ const updatedAttachments = [];
951
+ for (const attachment of attachments) {
952
+ switch (attachment.state) {
953
+ case exports.AttachmentState.QUEUED_UPLOAD:
954
+ const uploaded = await this.uploadAttachment(attachment);
955
+ updatedAttachments.push(uploaded);
956
+ break;
957
+ case exports.AttachmentState.QUEUED_DOWNLOAD:
958
+ const downloaded = await this.downloadAttachment(attachment);
959
+ updatedAttachments.push(downloaded);
960
+ break;
961
+ case exports.AttachmentState.QUEUED_DELETE:
962
+ const deleted = await this.deleteAttachment(attachment, context);
963
+ updatedAttachments.push(deleted);
964
+ break;
965
+ }
966
+ }
967
+ await context.saveAttachments(updatedAttachments);
968
+ }
969
+ /**
970
+ * Uploads an attachment from local storage to remote storage.
971
+ * On success, marks as SYNCED. On failure, defers to error handler or archives.
972
+ *
973
+ * @param attachment - The attachment record to upload
974
+ * @returns Updated attachment record with new state
975
+ * @throws Error if the attachment has no localUri
976
+ */
977
+ async uploadAttachment(attachment) {
978
+ this.logger.info(`Uploading attachment ${attachment.filename}`);
979
+ try {
980
+ if (attachment.localUri == null) {
981
+ throw new Error(`No localUri for attachment ${attachment.id}`);
982
+ }
983
+ const fileBlob = await this.localStorage.readFile(attachment.localUri);
984
+ await this.remoteStorage.uploadFile(fileBlob, attachment);
985
+ return {
986
+ ...attachment,
987
+ state: exports.AttachmentState.SYNCED,
988
+ hasSynced: true
989
+ };
990
+ }
991
+ catch (error) {
992
+ const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
993
+ if (!shouldRetry) {
994
+ return {
995
+ ...attachment,
996
+ state: exports.AttachmentState.ARCHIVED
997
+ };
998
+ }
999
+ return attachment;
1000
+ }
1001
+ }
1002
+ /**
1003
+ * Downloads an attachment from remote storage to local storage.
1004
+ * Retrieves the file, converts to base64, and saves locally.
1005
+ * On success, marks as SYNCED. On failure, defers to error handler or archives.
1006
+ *
1007
+ * @param attachment - The attachment record to download
1008
+ * @returns Updated attachment record with local URI and new state
1009
+ */
1010
+ async downloadAttachment(attachment) {
1011
+ this.logger.info(`Downloading attachment ${attachment.filename}`);
1012
+ try {
1013
+ const fileData = await this.remoteStorage.downloadFile(attachment);
1014
+ const localUri = this.localStorage.getLocalUri(attachment.filename);
1015
+ await this.localStorage.saveFile(localUri, fileData);
1016
+ return {
1017
+ ...attachment,
1018
+ state: exports.AttachmentState.SYNCED,
1019
+ localUri: localUri,
1020
+ hasSynced: true
1021
+ };
1022
+ }
1023
+ catch (error) {
1024
+ const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
1025
+ if (!shouldRetry) {
1026
+ return {
1027
+ ...attachment,
1028
+ state: exports.AttachmentState.ARCHIVED
1029
+ };
1030
+ }
1031
+ return attachment;
1032
+ }
1033
+ }
1034
+ /**
1035
+ * Deletes an attachment from both remote and local storage.
1036
+ * Removes the remote file, local file (if exists), and the attachment record.
1037
+ * On failure, defers to error handler or archives.
1038
+ *
1039
+ * @param attachment - The attachment record to delete
1040
+ * @param context - Attachment context for database operations
1041
+ * @returns Updated attachment record
1002
1042
  */
1003
- watchActiveAttachments({ throttleMs } = {}) {
1004
- this.logger.info('Watching active attachments...');
1005
- const watch = this.db
1006
- .query({
1007
- sql: /* sql */ `
1008
- SELECT
1009
- *
1010
- FROM
1011
- ${this.tableName}
1012
- WHERE
1013
- state = ?
1014
- OR state = ?
1015
- OR state = ?
1016
- ORDER BY
1017
- timestamp ASC
1018
- `,
1019
- parameters: [exports.AttachmentState.QUEUED_UPLOAD, exports.AttachmentState.QUEUED_DOWNLOAD, exports.AttachmentState.QUEUED_DELETE]
1020
- })
1021
- .differentialWatch({ throttleMs });
1022
- return watch;
1043
+ async deleteAttachment(attachment, context) {
1044
+ try {
1045
+ await this.remoteStorage.deleteFile(attachment);
1046
+ if (attachment.localUri) {
1047
+ await this.localStorage.deleteFile(attachment.localUri);
1048
+ }
1049
+ await context.deleteAttachment(attachment.id);
1050
+ return {
1051
+ ...attachment,
1052
+ state: exports.AttachmentState.ARCHIVED
1053
+ };
1054
+ }
1055
+ catch (error) {
1056
+ const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
1057
+ if (!shouldRetry) {
1058
+ return {
1059
+ ...attachment,
1060
+ state: exports.AttachmentState.ARCHIVED
1061
+ };
1062
+ }
1063
+ return attachment;
1064
+ }
1023
1065
  }
1024
1066
  /**
1025
- * Executes a callback with exclusive access to the attachment context.
1067
+ * Performs cleanup of archived attachments by removing their local files and records.
1068
+ * Errors during local file deletion are logged but do not prevent record deletion.
1026
1069
  */
1027
- async withContext(callback) {
1028
- return this.mutex.runExclusive(async () => {
1029
- return callback(this.context);
1070
+ async deleteArchivedAttachments(context) {
1071
+ return await context.deleteArchivedAttachments(async (archivedAttachments) => {
1072
+ for (const attachment of archivedAttachments) {
1073
+ if (attachment.localUri) {
1074
+ try {
1075
+ await this.localStorage.deleteFile(attachment.localUri);
1076
+ }
1077
+ catch (error) {
1078
+ this.logger.error('Error deleting local file for archived attachment', error);
1079
+ }
1080
+ }
1081
+ }
1030
1082
  });
1031
1083
  }
1032
1084
  }
@@ -1087,16 +1139,6 @@ class AttachmentQueue {
1087
1139
  * Creates a new AttachmentQueue instance.
1088
1140
  *
1089
1141
  * @param options - Configuration options
1090
- * @param options.db - PowerSync database instance
1091
- * @param options.remoteStorage - Remote storage adapter for upload/download operations
1092
- * @param options.localStorage - Local storage adapter for file persistence
1093
- * @param options.watchAttachments - Callback for monitoring attachment changes in your data model
1094
- * @param options.tableName - Name of the table to store attachment records. Default: 'ps_attachment_queue'
1095
- * @param options.logger - Logger instance. Defaults to db.logger
1096
- * @param options.syncIntervalMs - Periodic polling interval in milliseconds for retrying failed uploads/downloads. Default: 30000
1097
- * @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
1098
- * @param options.downloadAttachments - Whether to automatically download remote attachments. Default: true
1099
- * @param options.archivedCacheLimit - Maximum archived attachments before cleanup. Default: 100
1100
1142
  */
1101
1143
  constructor({ db, localStorage, remoteStorage, watchAttachments, logger, tableName = ATTACHMENT_TABLE, syncIntervalMs = 30 * 1000, syncThrottleDuration = DEFAULT_WATCH_THROTTLE_MS, downloadAttachments = true, archivedCacheLimit = 100, errorHandler }) {
1102
1144
  this.db = db;
@@ -1185,6 +1227,7 @@ class AttachmentQueue {
1185
1227
  state: exports.AttachmentState.QUEUED_DOWNLOAD,
1186
1228
  hasSynced: false,
1187
1229
  metaData: watchedAttachment.metaData,
1230
+ mediaType: watchedAttachment.mediaType,
1188
1231
  timestamp: new Date().getTime()
1189
1232
  });
1190
1233
  continue;
@@ -1272,17 +1315,24 @@ class AttachmentQueue {
1272
1315
  this.statusListenerDispose = undefined;
1273
1316
  }
1274
1317
  }
1318
+ /**
1319
+ * Provides an {@link AttachmentContext} to a callback.
1320
+ *
1321
+ * The callback runs while the attachment queue mutex is held. Do not call
1322
+ * other {@link AttachmentQueue} methods from within the callback, as they may
1323
+ * attempt to acquire the same mutex and block indefinitely.
1324
+ */
1325
+ withAttachmentContext(callback) {
1326
+ /**
1327
+ * AttachmentService is internal and private in this class.
1328
+ * We only need to expose its locking and context functionality for extending classes.
1329
+ */
1330
+ return this.attachmentService.withContext(callback);
1331
+ }
1275
1332
  /**
1276
1333
  * Saves a file to local storage and queues it for upload to remote storage.
1277
1334
  *
1278
1335
  * @param options - File save options
1279
- * @param options.data - The file data as ArrayBuffer, Blob, or base64 string
1280
- * @param options.fileExtension - File extension (e.g., 'jpg', 'pdf')
1281
- * @param options.mediaType - MIME type of the file (e.g., 'image/jpeg')
1282
- * @param options.metaData - Optional metadata to associate with the attachment
1283
- * @param options.id - Optional custom ID. If not provided, a UUID will be generated
1284
- * @param options.updateHook - Optional callback to execute additional database operations
1285
- * within the same transaction as the attachment creation
1286
1336
  * @returns Promise resolving to the created attachment record
1287
1337
  */
1288
1338
  async saveFile({ data, fileExtension, mediaType, metaData, id, updateHook }) {
@@ -1395,6 +1445,9 @@ class AttachmentQueue {
1395
1445
  }
1396
1446
  }
1397
1447
 
1448
+ /**
1449
+ * @alpha
1450
+ */
1398
1451
  exports.EncodingType = void 0;
1399
1452
  (function (EncodingType) {
1400
1453
  EncodingType["UTF8"] = "utf8";
@@ -1703,7 +1756,9 @@ var Logger = /*@__PURE__*/getDefaultExportFromCjs(loggerExports);
1703
1756
  * different SQLite DB implementations.
1704
1757
  */
1705
1758
  /**
1706
- * Implements {@link DBGetUtils} on a {@link SqlRunner}.
1759
+ * Implements {@link DBGetUtils} on a {@link SqlExecutor}.
1760
+ *
1761
+ * @internal
1707
1762
  */
1708
1763
  function DBGetUtilsDefaultMixin(Base) {
1709
1764
  return class extends Base {
@@ -1747,6 +1802,8 @@ function DBGetUtilsDefaultMixin(Base) {
1747
1802
  }
1748
1803
  /**
1749
1804
  * Update table operation numbers from SQLite
1805
+ *
1806
+ * @public
1750
1807
  */
1751
1808
  exports.RowUpdateType = void 0;
1752
1809
  (function (RowUpdateType) {
@@ -1755,8 +1812,10 @@ exports.RowUpdateType = void 0;
1755
1812
  RowUpdateType[RowUpdateType["SQLITE_UPDATE"] = 23] = "SQLITE_UPDATE";
1756
1813
  })(exports.RowUpdateType || (exports.RowUpdateType = {}));
1757
1814
  /**
1758
- * A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool.readLock} and
1759
- * {@link ConnectionPool.writeLock}.
1815
+ * A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool#readLock} and
1816
+ * {@link ConnectionPool#writeLock}.
1817
+ *
1818
+ * @internal
1760
1819
  */
1761
1820
  function DBAdapterDefaultMixin(Base) {
1762
1821
  return class extends Base {
@@ -1844,9 +1903,15 @@ class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction)
1844
1903
  }
1845
1904
  }
1846
1905
  }
1906
+ /**
1907
+ * @internal
1908
+ */
1847
1909
  function isBatchedUpdateNotification(update) {
1848
1910
  return 'tables' in update;
1849
1911
  }
1912
+ /**
1913
+ * @internal
1914
+ */
1850
1915
  function extractTableUpdates(update) {
1851
1916
  return isBatchedUpdateNotification(update) ? update.tables : [update.table];
1852
1917
  }
@@ -1874,6 +1939,8 @@ const FULL_SYNC_PRIORITY = 2147483647;
1874
1939
  *
1875
1940
  * Also note that data is downloaded in bulk, which means that individual counters are unlikely
1876
1941
  * to be updated one-by-one.
1942
+ *
1943
+ * @public
1877
1944
  */
1878
1945
  class SyncProgress {
1879
1946
  internal;
@@ -1912,6 +1979,9 @@ class SyncProgress {
1912
1979
  }
1913
1980
  }
1914
1981
 
1982
+ /**
1983
+ * @public
1984
+ */
1915
1985
  class SyncStatus {
1916
1986
  options;
1917
1987
  constructor(options) {
@@ -1922,6 +1992,8 @@ class SyncStatus {
1922
1992
  * implementation).
1923
1993
  *
1924
1994
  * This information is only available after a connection has been requested.
1995
+ *
1996
+ * @deprecated This always returns the Rust client (the only option).
1925
1997
  */
1926
1998
  get clientImplementation() {
1927
1999
  return this.options.clientImplementation;
@@ -1929,7 +2001,7 @@ class SyncStatus {
1929
2001
  /**
1930
2002
  * Indicates if the client is currently connected to the PowerSync service.
1931
2003
  *
1932
- * @returns {boolean} True if connected, false otherwise. Defaults to false if not specified.
2004
+ * @returns True if connected, false otherwise. Defaults to false if not specified.
1933
2005
  */
1934
2006
  get connected() {
1935
2007
  return this.options.connected ?? false;
@@ -1937,7 +2009,7 @@ class SyncStatus {
1937
2009
  /**
1938
2010
  * Indicates if the client is in the process of establishing a connection to the PowerSync service.
1939
2011
  *
1940
- * @returns {boolean} True if connecting, false otherwise. Defaults to false if not specified.
2012
+ * @returns True if connecting, false otherwise. Defaults to false if not specified.
1941
2013
  */
1942
2014
  get connecting() {
1943
2015
  return this.options.connecting ?? false;
@@ -1946,7 +2018,7 @@ class SyncStatus {
1946
2018
  * Time that a last sync has fully completed, if any.
1947
2019
  * This timestamp is reset to null after a restart of the PowerSync service.
1948
2020
  *
1949
- * @returns {Date | undefined} The timestamp of the last successful sync, or undefined if no sync has completed.
2021
+ * @returns The timestamp of the last successful sync, or undefined if no sync has completed.
1950
2022
  */
1951
2023
  get lastSyncedAt() {
1952
2024
  return this.options.lastSyncedAt;
@@ -1954,7 +2026,7 @@ class SyncStatus {
1954
2026
  /**
1955
2027
  * Indicates whether there has been at least one full sync completed since initialization.
1956
2028
  *
1957
- * @returns {boolean | undefined} True if at least one sync has completed, false if no sync has completed,
2029
+ * @returns True if at least one sync has completed, false if no sync has completed,
1958
2030
  * or undefined when the state is still being loaded from the database.
1959
2031
  */
1960
2032
  get hasSynced() {
@@ -1963,10 +2035,10 @@ class SyncStatus {
1963
2035
  /**
1964
2036
  * Provides the current data flow status regarding uploads and downloads.
1965
2037
  *
1966
- * @returns {SyncDataFlowStatus} An object containing:
2038
+ * @returns An object containing:
1967
2039
  * - downloading: True if actively downloading changes (only when connected is also true)
1968
2040
  * - uploading: True if actively uploading changes
1969
- * Defaults to {downloading: false, uploading: false} if not specified.
2041
+ * Defaults to `{downloading: false, uploading: false}` if not specified.
1970
2042
  */
1971
2043
  get dataFlowStatus() {
1972
2044
  return (this.options.dataFlow ?? {
@@ -1991,7 +2063,7 @@ class SyncStatus {
1991
2063
  return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
1992
2064
  }
1993
2065
  /**
1994
- * If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
2066
+ * If the `stream` appears in {@link SyncStatus.syncStreams}, returns the current status for that stream.
1995
2067
  */
1996
2068
  forStream(stream) {
1997
2069
  const asJson = JSON.stringify(stream.parameters);
@@ -2001,7 +2073,7 @@ class SyncStatus {
2001
2073
  /**
2002
2074
  * Provides sync status information for all bucket priorities, sorted by priority (highest first).
2003
2075
  *
2004
- * @returns {SyncPriorityStatus[]} An array of status entries for different sync priority levels,
2076
+ * @returns An array of status entries for different sync priority levels,
2005
2077
  * sorted with highest priorities (lower numbers) first.
2006
2078
  */
2007
2079
  get priorityStatusEntries() {
@@ -2036,8 +2108,8 @@ class SyncStatus {
2036
2108
  * For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
2037
2109
  * with a priority of 1 may return information for priority level 3.
2038
2110
  *
2039
- * @param {number} priority The bucket priority for which the status should be reported
2040
- * @returns {SyncPriorityStatus} Status information for the requested priority level or the next higher level with available status
2111
+ * @param priority - The bucket priority for which the status should be reported
2112
+ * @returns Status information for the requested priority level or the next higher level with available status
2041
2113
  */
2042
2114
  statusForPriority(priority) {
2043
2115
  // priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
@@ -2058,8 +2130,8 @@ class SyncStatus {
2058
2130
  * Compares this SyncStatus instance with another to determine if they are equal.
2059
2131
  * Equality is determined by comparing the serialized JSON representation of both instances.
2060
2132
  *
2061
- * @param {SyncStatus} status The SyncStatus instance to compare against
2062
- * @returns {boolean} True if the instances are considered equal, false otherwise
2133
+ * @param status - The SyncStatus instance to compare against
2134
+ * @returns True if the instances are considered equal, false otherwise
2063
2135
  */
2064
2136
  isEqual(status) {
2065
2137
  /**
@@ -2082,7 +2154,7 @@ class SyncStatus {
2082
2154
  * Creates a human-readable string representation of the current sync status.
2083
2155
  * Includes information about connection state, sync completion, and data flow.
2084
2156
  *
2085
- * @returns {string} A string representation of the sync status
2157
+ * @returns A string representation of the sync status
2086
2158
  */
2087
2159
  getMessage() {
2088
2160
  const dataFlow = this.dataFlowStatus;
@@ -2091,7 +2163,7 @@ class SyncStatus {
2091
2163
  /**
2092
2164
  * Serializes the SyncStatus instance to a plain object.
2093
2165
  *
2094
- * @returns {SyncStatusOptions} A plain object representation of the sync status
2166
+ * @returns A plain object representation of the sync status
2095
2167
  */
2096
2168
  toJSON() {
2097
2169
  return {
@@ -2157,6 +2229,9 @@ class SyncStreamStatusView {
2157
2229
  }
2158
2230
  }
2159
2231
 
2232
+ /**
2233
+ * @public
2234
+ */
2160
2235
  class UploadQueueStats {
2161
2236
  count;
2162
2237
  size;
@@ -2182,6 +2257,9 @@ class UploadQueueStats {
2182
2257
  }
2183
2258
  }
2184
2259
 
2260
+ /**
2261
+ * @internal
2262
+ */
2185
2263
  class BaseObserver {
2186
2264
  listeners = new Set();
2187
2265
  constructor() { }
@@ -2209,6 +2287,9 @@ class BaseObserver {
2209
2287
  }
2210
2288
  }
2211
2289
 
2290
+ /**
2291
+ * @internal
2292
+ */
2212
2293
  class ControlledExecutor {
2213
2294
  task;
2214
2295
  /**
@@ -2278,30 +2359,44 @@ function throttleTrailing(func, wait) {
2278
2359
  }
2279
2360
  };
2280
2361
  }
2281
- /**
2282
- * Throttle a function to be called at most once every "wait" milliseconds,
2283
- * on the leading and trailing edge.
2284
- *
2285
- * Roughly equivalent to lodash/throttle with {leading: true, trailing: true}
2286
- */
2287
- function throttleLeadingTrailing(func, wait) {
2288
- let timeoutId = null;
2289
- let lastCallTime = 0;
2290
- const invokeFunction = () => {
2291
- func();
2292
- lastCallTime = Date.now();
2293
- timeoutId = null;
2294
- };
2295
- return function () {
2296
- const now = Date.now();
2297
- const timeToWait = wait - (now - lastCallTime);
2298
- if (timeToWait <= 0) {
2299
- // Leading edge: Call the function immediately if enough time has passed
2300
- invokeFunction();
2301
- }
2302
- else if (!timeoutId) {
2303
- // Set a timeout for the trailing edge if not already set
2304
- timeoutId = setTimeout(invokeFunction, timeToWait);
2362
+ function asyncNotifier() {
2363
+ let waitingConsumer = null;
2364
+ let hasPendingNotification = false;
2365
+ return {
2366
+ notify() {
2367
+ if (waitingConsumer != null) {
2368
+ waitingConsumer();
2369
+ waitingConsumer = null;
2370
+ }
2371
+ else {
2372
+ hasPendingNotification = true;
2373
+ }
2374
+ },
2375
+ waitForNotification(signal) {
2376
+ return new Promise((resolve) => {
2377
+ if (waitingConsumer != null) {
2378
+ throw new Error('Illegal call to waitForNotification, already has a waiter.');
2379
+ }
2380
+ if (signal.aborted) {
2381
+ resolve();
2382
+ }
2383
+ else if (hasPendingNotification) {
2384
+ resolve();
2385
+ hasPendingNotification = false;
2386
+ }
2387
+ else {
2388
+ function complete() {
2389
+ signal.removeEventListener('abort', onAbort);
2390
+ resolve();
2391
+ }
2392
+ function onAbort() {
2393
+ waitingConsumer = null;
2394
+ resolve();
2395
+ }
2396
+ waitingConsumer = complete;
2397
+ signal.addEventListener('abort', onAbort);
2398
+ }
2399
+ });
2305
2400
  }
2306
2401
  };
2307
2402
  }
@@ -2462,7 +2557,7 @@ class ConnectionManager extends BaseObserver {
2462
2557
  /**
2463
2558
  * Close the sync connection.
2464
2559
  *
2465
- * Use {@link connect} to connect again.
2560
+ * Use {@link ConnectionManager.connect} to connect again.
2466
2561
  */
2467
2562
  async disconnect() {
2468
2563
  // This will help abort pending connects
@@ -2602,6 +2697,8 @@ const _finalizer = 'FinalizationRegistry' in globalThis
2602
2697
  /**
2603
2698
  * An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
2604
2699
  * result has changes without necessarily processing all items in the result.
2700
+ *
2701
+ * @public
2605
2702
  */
2606
2703
  class ArrayComparator {
2607
2704
  options;
@@ -2629,6 +2726,8 @@ class ArrayComparator {
2629
2726
  }
2630
2727
  /**
2631
2728
  * Watched query comparator that always reports changed result sets.
2729
+ *
2730
+ * @public
2632
2731
  */
2633
2732
  const FalsyComparator = {
2634
2733
  checkEquality: () => false // Default comparator that always returns false
@@ -2836,6 +2935,8 @@ class AbstractQueryProcessor extends MetaBaseObserver {
2836
2935
  /**
2837
2936
  * An empty differential result set.
2838
2937
  * This is used as the initial state for differential incrementally watched queries.
2938
+ *
2939
+ * @internal
2839
2940
  */
2840
2941
  const EMPTY_DIFFERENTIAL = {
2841
2942
  added: [],
@@ -2848,6 +2949,8 @@ const EMPTY_DIFFERENTIAL = {
2848
2949
  * Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
2849
2950
  * It keys items by their `id` property if available, alternatively it uses JSON stringification
2850
2951
  * of the entire item for the key and comparison.
2952
+ *
2953
+ * @internal
2851
2954
  */
2852
2955
  const DEFAULT_ROW_COMPARATOR = {
2853
2956
  keyBy: (item) => {
@@ -3128,6 +3231,8 @@ class CustomQuery {
3128
3231
 
3129
3232
  /**
3130
3233
  * Tests if the input is a {@link SQLOpenOptions}
3234
+ *
3235
+ * @internal
3131
3236
  */
3132
3237
  const isSQLOpenOptions = (test) => {
3133
3238
  // typeof null is `object`, but you cannot use the `in` operator on `null.
@@ -3135,17 +3240,24 @@ const isSQLOpenOptions = (test) => {
3135
3240
  };
3136
3241
  /**
3137
3242
  * Tests if input is a {@link SQLOpenFactory}
3243
+ *
3244
+ * @internal
3138
3245
  */
3139
3246
  const isSQLOpenFactory = (test) => {
3140
3247
  return typeof test?.openDB == 'function';
3141
3248
  };
3142
3249
  /**
3143
3250
  * Tests if input is a {@link DBAdapter}
3251
+ *
3252
+ * @internal
3144
3253
  */
3145
3254
  const isDBAdapter = (test) => {
3146
3255
  return typeof test?.writeTransaction == 'function';
3147
3256
  };
3148
3257
 
3258
+ /**
3259
+ * @internal
3260
+ */
3149
3261
  exports.PSInternalTable = void 0;
3150
3262
  (function (PSInternalTable) {
3151
3263
  PSInternalTable["DATA"] = "ps_data";
@@ -3154,6 +3266,9 @@ exports.PSInternalTable = void 0;
3154
3266
  PSInternalTable["OPLOG"] = "ps_oplog";
3155
3267
  PSInternalTable["UNTYPED"] = "ps_untyped";
3156
3268
  })(exports.PSInternalTable || (exports.PSInternalTable = {}));
3269
+ /**
3270
+ * @internal
3271
+ */
3157
3272
  exports.PowerSyncControlCommand = void 0;
3158
3273
  (function (PowerSyncControlCommand) {
3159
3274
  PowerSyncControlCommand["PROCESS_TEXT_LINE"] = "line_text";
@@ -3171,6 +3286,8 @@ exports.PowerSyncControlCommand = void 0;
3171
3286
 
3172
3287
  /**
3173
3288
  * A batch of client-side changes.
3289
+ *
3290
+ * @public
3174
3291
  */
3175
3292
  class CrudBatch {
3176
3293
  crud;
@@ -3197,6 +3314,8 @@ class CrudBatch {
3197
3314
 
3198
3315
  /**
3199
3316
  * Type of local change.
3317
+ *
3318
+ * @public
3200
3319
  */
3201
3320
  exports.UpdateType = void 0;
3202
3321
  (function (UpdateType) {
@@ -3209,6 +3328,8 @@ exports.UpdateType = void 0;
3209
3328
  })(exports.UpdateType || (exports.UpdateType = {}));
3210
3329
  /**
3211
3330
  * A single client-side change.
3331
+ *
3332
+ * @public
3212
3333
  */
3213
3334
  class CrudEntry {
3214
3335
  /**
@@ -3305,6 +3426,9 @@ class CrudEntry {
3305
3426
  }
3306
3427
  }
3307
3428
 
3429
+ /**
3430
+ * @public
3431
+ */
3308
3432
  class CrudTransaction extends CrudBatch {
3309
3433
  crud;
3310
3434
  complete;
@@ -3333,6 +3457,8 @@ class CrudTransaction extends CrudBatch {
3333
3457
  * Calls to Abortcontroller.abort(reason: any) will result in the
3334
3458
  * `reason` being thrown. This is not necessarily an error,
3335
3459
  * but extends error for better logging purposes.
3460
+ *
3461
+ * @internal
3336
3462
  */
3337
3463
  class AbortOperation extends Error {
3338
3464
  reason;
@@ -8132,7 +8258,7 @@ function requireDist () {
8132
8258
 
8133
8259
  var distExports = requireDist();
8134
8260
 
8135
- var version = "1.53.1";
8261
+ var version = "1.54.0";
8136
8262
  var PACKAGE = {
8137
8263
  version: version};
8138
8264
 
@@ -8262,7 +8388,8 @@ class WebsocketClientTransport {
8262
8388
  removeListeners();
8263
8389
  resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
8264
8390
  };
8265
- const errorListener = (ev) => {
8391
+ const errorListener = (event) => {
8392
+ const ev = event;
8266
8393
  removeListeners();
8267
8394
  // We add a default error in that case.
8268
8395
  if (ev.error != null) {
@@ -8505,7 +8632,13 @@ const SOCKET_TIMEOUT_MS = 30_000;
8505
8632
  // If there is a backlog of messages (for example on slow connections), keepalive messages could be delayed
8506
8633
  // significantly. Therefore this is longer than the socket timeout.
8507
8634
  const KEEP_ALIVE_LIFETIME_MS = 90_000;
8635
+ /**
8636
+ * @internal
8637
+ */
8508
8638
  const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
8639
+ /**
8640
+ * @public
8641
+ */
8509
8642
  exports.FetchStrategy = void 0;
8510
8643
  (function (FetchStrategy) {
8511
8644
  /**
@@ -8524,12 +8657,17 @@ exports.FetchStrategy = void 0;
8524
8657
  * The class wrapper is used to distinguish the fetchImplementation
8525
8658
  * option in [AbstractRemoteOptions] from the general fetch method
8526
8659
  * which is typeof "function"
8660
+ *
8661
+ * @internal
8527
8662
  */
8528
8663
  class FetchImplementationProvider {
8529
8664
  getFetch() {
8530
8665
  throw new Error('Unspecified fetch implementation');
8531
8666
  }
8532
8667
  }
8668
+ /**
8669
+ * @internal
8670
+ */
8533
8671
  const DEFAULT_REMOTE_OPTIONS = {
8534
8672
  socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
8535
8673
  return match === 'https://' ? 'wss://' : 'ws://';
@@ -8537,6 +8675,9 @@ const DEFAULT_REMOTE_OPTIONS = {
8537
8675
  fetchImplementation: new FetchImplementationProvider(),
8538
8676
  fetchOptions: {}
8539
8677
  };
8678
+ /**
8679
+ * @internal
8680
+ */
8540
8681
  class AbstractRemote {
8541
8682
  connector;
8542
8683
  logger;
@@ -8946,7 +9087,7 @@ class AbstractRemote {
8946
9087
  * Posts a `/sync/stream` request.
8947
9088
  *
8948
9089
  * Depending on the `Content-Type` of the response, this returns strings for sync lines or encoded BSON documents as
8949
- * {@link Uint8Array}s.
9090
+ * `Uint8Array`s.
8950
9091
  */
8951
9092
  async fetchStream(options) {
8952
9093
  const { isBson, stream } = await this.fetchStreamRaw(options);
@@ -8988,16 +9129,26 @@ function isInterruptingInstruction(instruction) {
8988
9129
  return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
8989
9130
  }
8990
9131
 
9132
+ /**
9133
+ * @internal
9134
+ */
8991
9135
  exports.LockType = void 0;
8992
9136
  (function (LockType) {
8993
9137
  LockType["CRUD"] = "crud";
8994
9138
  LockType["SYNC"] = "sync";
8995
9139
  })(exports.LockType || (exports.LockType = {}));
9140
+ /**
9141
+ * @public
9142
+ */
8996
9143
  exports.SyncStreamConnectionMethod = void 0;
8997
9144
  (function (SyncStreamConnectionMethod) {
8998
9145
  SyncStreamConnectionMethod["HTTP"] = "http";
8999
9146
  SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
9000
9147
  })(exports.SyncStreamConnectionMethod || (exports.SyncStreamConnectionMethod = {}));
9148
+ /**
9149
+ * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
9150
+ * @public
9151
+ */
9001
9152
  exports.SyncClientImplementation = void 0;
9002
9153
  (function (SyncClientImplementation) {
9003
9154
  /**
@@ -9009,8 +9160,8 @@ exports.SyncClientImplementation = void 0;
9009
9160
  * ## Compatibility warning
9010
9161
  *
9011
9162
  * The Rust sync client stores sync data in a format that is slightly different than the one used
9012
- * by the old JavaScript client. When adopting the {@link RUST} client on existing databases, the PowerSync SDK will
9013
- * migrate the format automatically.
9163
+ * by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
9164
+ * the PowerSync SDK will migrate the format automatically.
9014
9165
  *
9015
9166
  * SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
9016
9167
  * implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
@@ -9020,14 +9171,29 @@ exports.SyncClientImplementation = void 0;
9020
9171
  })(exports.SyncClientImplementation || (exports.SyncClientImplementation = {}));
9021
9172
  /**
9022
9173
  * The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
9174
+ *
9175
+ * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
9176
+ * @public
9023
9177
  */
9024
9178
  const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = exports.SyncClientImplementation.RUST;
9179
+ /**
9180
+ * @internal
9181
+ */
9025
9182
  const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
9183
+ /**
9184
+ * @internal
9185
+ */
9026
9186
  const DEFAULT_RETRY_DELAY_MS = 5000;
9187
+ /**
9188
+ * @internal
9189
+ */
9027
9190
  const DEFAULT_STREAMING_SYNC_OPTIONS = {
9028
9191
  retryDelayMs: DEFAULT_RETRY_DELAY_MS,
9029
9192
  crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
9030
9193
  };
9194
+ /**
9195
+ * @internal
9196
+ */
9031
9197
  const DEFAULT_STREAM_CONNECTION_OPTIONS = {
9032
9198
  appMetadata: {},
9033
9199
  connectionMethod: exports.SyncStreamConnectionMethod.WEB_SOCKET,
@@ -9037,22 +9203,21 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
9037
9203
  serializedSchema: undefined,
9038
9204
  includeDefaultStreams: true
9039
9205
  };
9206
+ /**
9207
+ * @internal
9208
+ */
9040
9209
  class AbstractStreamingSyncImplementation extends BaseObserver {
9041
9210
  options;
9042
9211
  abortController;
9043
- // In rare cases, mostly for tests, uploads can be triggered without being properly connected.
9044
- // This allows ensuring that all upload processes can be aborted.
9045
- uploadAbortController;
9046
9212
  crudUpdateListener;
9047
9213
  streamingSyncPromise;
9048
9214
  logger;
9049
9215
  activeStreams;
9050
9216
  connectionMayHaveChanged = false;
9051
- isUploadingCrud = false;
9217
+ crudUploadNotifier = asyncNotifier();
9052
9218
  notifyCompletedUploads;
9053
9219
  handleActiveStreamsChange;
9054
9220
  syncStatus;
9055
- triggerCrudUpload;
9056
9221
  constructor(options) {
9057
9222
  super();
9058
9223
  this.options = options;
@@ -9068,16 +9233,9 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
9068
9233
  }
9069
9234
  });
9070
9235
  this.abortController = null;
9071
- this.triggerCrudUpload = throttleLeadingTrailing(() => {
9072
- if (!this.syncStatus.connected || this.isUploadingCrud) {
9073
- return;
9074
- }
9075
- this.isUploadingCrud = true;
9076
- this._uploadAllCrud().finally(() => {
9077
- this.notifyCompletedUploads?.();
9078
- this.isUploadingCrud = false;
9079
- });
9080
- }, this.options.crudUploadThrottleMs);
9236
+ }
9237
+ triggerCrudUpload() {
9238
+ this.crudUploadNotifier.notify();
9081
9239
  }
9082
9240
  async waitForReady() { }
9083
9241
  waitForStatus(status) {
@@ -9125,7 +9283,6 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
9125
9283
  super.dispose();
9126
9284
  this.crudUpdateListener?.();
9127
9285
  this.crudUpdateListener = undefined;
9128
- this.uploadAbortController?.abort();
9129
9286
  }
9130
9287
  async getWriteCheckpoint() {
9131
9288
  const clientId = await this.options.adapter.getClientId();
@@ -9135,7 +9292,17 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
9135
9292
  this.logger.debug(`Created write checkpoint: ${checkpoint}`);
9136
9293
  return checkpoint;
9137
9294
  }
9138
- async _uploadAllCrud() {
9295
+ async crudUploadLoop(signal) {
9296
+ while (!signal.aborted) {
9297
+ await Promise.all([
9298
+ // Start the initial CRUD upload on connect. Then, keep polling until we're done.
9299
+ this._uploadAllCrud(signal),
9300
+ this.delayRetry(signal, this.options.crudUploadThrottleMs)
9301
+ ]);
9302
+ await this.crudUploadNotifier.waitForNotification(signal);
9303
+ }
9304
+ }
9305
+ async _uploadAllCrud(signal) {
9139
9306
  return this.obtainLock({
9140
9307
  type: exports.LockType.CRUD,
9141
9308
  callback: async () => {
@@ -9143,12 +9310,7 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
9143
9310
  * Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
9144
9311
  */
9145
9312
  let checkedCrudItem;
9146
- const controller = new AbortController();
9147
- this.uploadAbortController = controller;
9148
- this.abortController?.signal.addEventListener('abort', () => {
9149
- controller.abort();
9150
- }, { once: true });
9151
- while (!controller.signal.aborted) {
9313
+ while (!signal.aborted) {
9152
9314
  try {
9153
9315
  /**
9154
9316
  * This is the first item in the FIFO CRUD queue.
@@ -9178,7 +9340,10 @@ The next upload iteration will be delayed.`);
9178
9340
  else {
9179
9341
  // Uploading is completed
9180
9342
  const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
9181
- if (neededUpdate == false && checkedCrudItem != null) {
9343
+ if (neededUpdate) {
9344
+ this.notifyCompletedUploads?.();
9345
+ }
9346
+ else if (checkedCrudItem != null) {
9182
9347
  // Only log this if there was something to upload
9183
9348
  this.logger.debug('Upload complete, no write checkpoint needed.');
9184
9349
  }
@@ -9193,7 +9358,7 @@ The next upload iteration will be delayed.`);
9193
9358
  uploadError: ex
9194
9359
  }
9195
9360
  });
9196
- await this.delayRetry(controller.signal);
9361
+ await this.delayRetry(signal);
9197
9362
  if (!this.isConnected) {
9198
9363
  // Exit the upload loop if the sync stream is no longer connected
9199
9364
  break;
@@ -9208,7 +9373,6 @@ The next upload iteration will be delayed.`);
9208
9373
  });
9209
9374
  }
9210
9375
  }
9211
- this.uploadAbortController = undefined;
9212
9376
  }
9213
9377
  });
9214
9378
  }
@@ -9218,7 +9382,10 @@ The next upload iteration will be delayed.`);
9218
9382
  }
9219
9383
  const controller = new AbortController();
9220
9384
  this.abortController = controller;
9221
- this.streamingSyncPromise = this.streamingSync(this.abortController.signal, options);
9385
+ this.streamingSyncPromise = Promise.all([
9386
+ this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
9387
+ this.streamingSync(controller.signal, options)
9388
+ ]);
9222
9389
  // Return a promise that resolves when the connection status is updated to indicate that we're connected.
9223
9390
  return new Promise((resolve) => {
9224
9391
  const disposer = this.registerListener({
@@ -9256,14 +9423,7 @@ The next upload iteration will be delayed.`);
9256
9423
  this.abortController = null;
9257
9424
  this.updateSyncStatus({ connected: false, connecting: false });
9258
9425
  }
9259
- /**
9260
- * @deprecated use [connect instead]
9261
- */
9262
9426
  async streamingSync(signal, options) {
9263
- if (!signal) {
9264
- this.abortController = new AbortController();
9265
- signal = this.abortController.signal;
9266
- }
9267
9427
  /**
9268
9428
  * Listen for CRUD updates and trigger upstream uploads
9269
9429
  */
@@ -9369,7 +9529,7 @@ The next upload iteration will be delayed.`);
9369
9529
  this.handleActiveStreamsChange?.();
9370
9530
  }
9371
9531
  /**
9372
- * Older versions of the JS SDK used to encode subkeys as JSON in {@link OplogEntry.toJSON}.
9532
+ * Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
9373
9533
  * Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
9374
9534
  * While this is not a problem as long as it's done consistently, it causes issues when a database
9375
9535
  * created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
@@ -9379,7 +9539,7 @@ The next upload iteration will be delayed.`);
9379
9539
  * migration is only triggered when necessary (for now). The function returns whether the new format
9380
9540
  * should be used, so that the JS SDK is able to write to updated databases.
9381
9541
  *
9382
- * @param requireFixedKeyFormat Whether we require the new format or also support the old one.
9542
+ * @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
9383
9543
  * The Rust client requires the new subkey format.
9384
9544
  * @returns Whether the database is now using the new, fixed subkey format.
9385
9545
  */
@@ -9637,14 +9797,13 @@ The next upload iteration will be delayed.`);
9637
9797
  // trigger this for all updates
9638
9798
  this.iterateListeners((cb) => cb.statusUpdated?.(options));
9639
9799
  }
9640
- async delayRetry(signal) {
9800
+ async delayRetry(signal, delay = this.options.retryDelayMs) {
9641
9801
  return new Promise((resolve) => {
9642
9802
  if (signal?.aborted) {
9643
9803
  // If the signal is already aborted, resolve immediately
9644
9804
  resolve();
9645
9805
  return;
9646
9806
  }
9647
- const { retryDelayMs } = this.options;
9648
9807
  let timeoutId;
9649
9808
  const endDelay = () => {
9650
9809
  resolve();
@@ -9655,7 +9814,7 @@ The next upload iteration will be delayed.`);
9655
9814
  signal?.removeEventListener('abort', endDelay);
9656
9815
  };
9657
9816
  signal?.addEventListener('abort', endDelay, { once: true });
9658
- timeoutId = setTimeout(endDelay, retryDelayMs);
9817
+ timeoutId = setTimeout(endDelay, delay);
9659
9818
  });
9660
9819
  }
9661
9820
  updateSubscriptions(subscriptions) {
@@ -9687,7 +9846,8 @@ const MEMORY_TRIGGER_CLAIM_MANAGER = {
9687
9846
 
9688
9847
  /**
9689
9848
  * SQLite operations to track changes for with {@link TriggerManager}
9690
- * @experimental
9849
+ *
9850
+ * @experimental @alpha
9691
9851
  */
9692
9852
  exports.DiffTriggerOperation = void 0;
9693
9853
  (function (DiffTriggerOperation) {
@@ -9749,8 +9909,8 @@ class TriggerManagerImpl {
9749
9909
  get db() {
9750
9910
  return this.options.db;
9751
9911
  }
9752
- async getUUID() {
9753
- const { id: uuid } = await this.db.get(/* sql */ `
9912
+ async getUUID(ctx) {
9913
+ const { id: uuid } = await (ctx ?? this.db).get(/* sql */ `
9754
9914
  SELECT
9755
9915
  uuid () as id
9756
9916
  `);
@@ -9863,7 +10023,7 @@ class TriggerManagerImpl {
9863
10023
  const replicatedColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
9864
10024
  const internalSource = sourceDefinition.internalName;
9865
10025
  const triggerIds = [];
9866
- const id = await this.getUUID();
10026
+ const id = await this.getUUID(setupContext);
9867
10027
  const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
9868
10028
  /**
9869
10029
  * We default to replicating all columns if no columns array is provided.
@@ -10103,18 +10263,29 @@ const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
10103
10263
  const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
10104
10264
  clearLocal: true
10105
10265
  };
10266
+ /**
10267
+ * @internal
10268
+ */
10106
10269
  const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
10107
10270
  disconnect: true
10108
10271
  };
10272
+ /**
10273
+ * @internal
10274
+ */
10109
10275
  const DEFAULT_POWERSYNC_DB_OPTIONS = {
10110
10276
  retryDelayMs: 5000,
10111
10277
  crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
10112
10278
  };
10279
+ /**
10280
+ * @internal
10281
+ */
10113
10282
  const DEFAULT_CRUD_BATCH_LIMIT = 100;
10114
10283
  /**
10115
10284
  * Requesting nested or recursive locks can block the application in some circumstances.
10116
10285
  * This default lock timeout will act as a failsafe to throw an error if a lock cannot
10117
10286
  * be obtained.
10287
+ *
10288
+ * @internal
10118
10289
  */
10119
10290
  const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
10120
10291
  /**
@@ -10124,6 +10295,9 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
10124
10295
  const isPowerSyncDatabaseOptionsWithSettings = (test) => {
10125
10296
  return typeof test == 'object' && isSQLOpenOptions(test.database);
10126
10297
  };
10298
+ /**
10299
+ * @public
10300
+ */
10127
10301
  class AbstractPowerSyncDatabase extends BaseObserver {
10128
10302
  options;
10129
10303
  /**
@@ -10281,7 +10455,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
10281
10455
  /**
10282
10456
  * Wait for the first sync operation to complete.
10283
10457
  *
10284
- * @param request Either an abort signal (after which the promise will complete regardless of
10458
+ * @param request - Either an abort signal (after which the promise will complete regardless of
10285
10459
  * whether a full sync was completed) or an object providing an abort signal and a priority target.
10286
10460
  * When a priority target is set, the promise may complete when all buckets with the given (or higher)
10287
10461
  * priorities have been synchronized. This can be earlier than a complete sync.
@@ -10436,7 +10610,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
10436
10610
  /**
10437
10611
  * Close the sync connection.
10438
10612
  *
10439
- * Use {@link connect} to connect again.
10613
+ * Use {@link AbstractPowerSyncDatabase.connect} to connect again.
10440
10614
  */
10441
10615
  async disconnect() {
10442
10616
  return this.connectionManager.disconnect();
@@ -10463,8 +10637,8 @@ class AbstractPowerSyncDatabase extends BaseObserver {
10463
10637
  /**
10464
10638
  * Create a sync stream to query its status or to subscribe to it.
10465
10639
  *
10466
- * @param name The name of the stream to subscribe to.
10467
- * @param params Optional parameters for the stream subscription.
10640
+ * @param name - The name of the stream to subscribe to.
10641
+ * @param params - Optional parameters for the stream subscription.
10468
10642
  * @returns A {@link SyncStream} instance that can be subscribed to.
10469
10643
  * @experimental Sync streams are currently in alpha.
10470
10644
  */
@@ -10522,14 +10696,14 @@ class AbstractPowerSyncDatabase extends BaseObserver {
10522
10696
  * Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
10523
10697
  * requesting the next batch.
10524
10698
  *
10525
- * Use {@link limit} to specify the maximum number of updates to return in a single
10699
+ * Use the `limit` parameter to specify the maximum number of updates to return in a single
10526
10700
  * batch.
10527
10701
  *
10528
10702
  * This method does include transaction ids in the result, but does not group
10529
10703
  * data by transaction. One batch may contain data from multiple transactions,
10530
10704
  * and a single transaction may be split over multiple batches.
10531
10705
  *
10532
- * @param limit Maximum number of CRUD entries to include in the batch
10706
+ * @param limit - Maximum number of CRUD entries to include in the batch
10533
10707
  * @returns A batch of CRUD operations to upload, or null if there are none
10534
10708
  */
10535
10709
  async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
@@ -10556,7 +10730,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
10556
10730
  * Once the data have been successfully uploaded, call {@link CrudTransaction.complete} before
10557
10731
  * requesting the next transaction.
10558
10732
  *
10559
- * Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
10733
+ * Unlike {@link AbstractPowerSyncDatabase.getCrudBatch}, this only returns data from a single transaction at a time.
10560
10734
  * All data for the transaction is loaded into memory.
10561
10735
  *
10562
10736
  * @returns A transaction of CRUD operations to upload, or null if there are none
@@ -10571,7 +10745,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
10571
10745
  * This is typically used from the {@link PowerSyncBackendConnector.uploadData} callback. Each entry emitted by the
10572
10746
  * returned iterator is a full transaction containing all local writes made while that transaction was active.
10573
10747
  *
10574
- * Unlike {@link getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
10748
+ * Unlike {@link AbstractPowerSyncDatabase.getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
10575
10749
  * {@link CrudTransaction.complete}d yet, this iterator can be used to receive multiple transactions. Calling
10576
10750
  * {@link CrudTransaction.complete} will mark that and all prior transactions emitted by the iterator as completed.
10577
10751
  *
@@ -10665,8 +10839,8 @@ SELECT * FROM crud_entries;
10665
10839
  * the returned result's `rowsAffected` may be `0` for successful `UPDATE` and `DELETE` statements.
10666
10840
  * Use a `RETURNING` clause and inspect `result.rows` when you need to confirm which rows changed.
10667
10841
  *
10668
- * @param sql The SQL query to execute
10669
- * @param parameters Optional array of parameters to bind to the query
10842
+ * @param sql - The SQL query to execute
10843
+ * @param parameters - Optional array of parameters to bind to the query
10670
10844
  * @returns The query result as an object with structured key-value pairs
10671
10845
  */
10672
10846
  async execute(sql, parameters) {
@@ -10676,8 +10850,8 @@ SELECT * FROM crud_entries;
10676
10850
  * Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
10677
10851
  * This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
10678
10852
  *
10679
- * @param sql The SQL query to execute
10680
- * @param parameters Optional array of parameters to bind to the query
10853
+ * @param sql - The SQL query to execute
10854
+ * @param parameters - Optional array of parameters to bind to the query
10681
10855
  * @returns The raw query result from the underlying database as a nested array of raw values, where each row is
10682
10856
  * represented as an array of column values without field names.
10683
10857
  */
@@ -10690,8 +10864,8 @@ SELECT * FROM crud_entries;
10690
10864
  * and optionally return results.
10691
10865
  * This is faster than executing separately with each parameter set.
10692
10866
  *
10693
- * @param sql The SQL query to execute
10694
- * @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
10867
+ * @param sql - The SQL query to execute
10868
+ * @param parameters - Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
10695
10869
  * @returns The query result
10696
10870
  */
10697
10871
  async executeBatch(sql, parameters) {
@@ -10701,8 +10875,8 @@ SELECT * FROM crud_entries;
10701
10875
  /**
10702
10876
  * Execute a read-only query and return results.
10703
10877
  *
10704
- * @param sql The SQL query to execute
10705
- * @param parameters Optional array of parameters to bind to the query
10878
+ * @param sql - The SQL query to execute
10879
+ * @param parameters - Optional array of parameters to bind to the query
10706
10880
  * @returns An array of results
10707
10881
  */
10708
10882
  async getAll(sql, parameters) {
@@ -10712,8 +10886,8 @@ SELECT * FROM crud_entries;
10712
10886
  /**
10713
10887
  * Execute a read-only query and return the first result, or null if the ResultSet is empty.
10714
10888
  *
10715
- * @param sql The SQL query to execute
10716
- * @param parameters Optional array of parameters to bind to the query
10889
+ * @param sql - The SQL query to execute
10890
+ * @param parameters - Optional array of parameters to bind to the query
10717
10891
  * @returns The first result if found, or null if no results are returned
10718
10892
  */
10719
10893
  async getOptional(sql, parameters) {
@@ -10723,8 +10897,8 @@ SELECT * FROM crud_entries;
10723
10897
  /**
10724
10898
  * Execute a read-only query and return the first result, error if the ResultSet is empty.
10725
10899
  *
10726
- * @param sql The SQL query to execute
10727
- * @param parameters Optional array of parameters to bind to the query
10900
+ * @param sql - The SQL query to execute
10901
+ * @param parameters - Optional array of parameters to bind to the query
10728
10902
  * @returns The first result matching the query
10729
10903
  * @throws Error if no rows are returned
10730
10904
  */
@@ -10734,7 +10908,7 @@ SELECT * FROM crud_entries;
10734
10908
  }
10735
10909
  /**
10736
10910
  * Takes a read lock, without starting a transaction.
10737
- * In most cases, {@link readTransaction} should be used instead.
10911
+ * In most cases, {@link AbstractPowerSyncDatabase.readTransaction} should be used instead.
10738
10912
  */
10739
10913
  async readLock(callback) {
10740
10914
  await this.waitForReady();
@@ -10742,7 +10916,7 @@ SELECT * FROM crud_entries;
10742
10916
  }
10743
10917
  /**
10744
10918
  * Takes a global lock, without starting a transaction.
10745
- * In most cases, {@link writeTransaction} should be used instead.
10919
+ * In most cases, {@link AbstractPowerSyncDatabase.writeTransaction} should be used instead.
10746
10920
  */
10747
10921
  async writeLock(callback) {
10748
10922
  await this.waitForReady();
@@ -10753,8 +10927,8 @@ SELECT * FROM crud_entries;
10753
10927
  * Read transactions can run concurrently to a write transaction.
10754
10928
  * Changes from any write transaction are not visible to read transactions started before it.
10755
10929
  *
10756
- * @param callback Function to execute within the transaction
10757
- * @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
10930
+ * @param callback - Function to execute within the transaction
10931
+ * @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
10758
10932
  * @returns The result of the callback
10759
10933
  * @throws Error if the lock cannot be obtained within the timeout period
10760
10934
  */
@@ -10771,8 +10945,8 @@ SELECT * FROM crud_entries;
10771
10945
  * This takes a global lock - only one write transaction can execute against the database at a time.
10772
10946
  * Statements within the transaction must be done on the provided {@link Transaction} interface.
10773
10947
  *
10774
- * @param callback Function to execute within the transaction
10775
- * @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
10948
+ * @param callback - Function to execute within the transaction
10949
+ * @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
10776
10950
  * @returns The result of the callback
10777
10951
  * @throws Error if the lock cannot be obtained within the timeout period
10778
10952
  */
@@ -10849,15 +11023,15 @@ SELECT * FROM crud_entries;
10849
11023
  }
10850
11024
  /**
10851
11025
  * Execute a read query every time the source tables are modified.
10852
- * Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
11026
+ * Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
10853
11027
  * Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
10854
11028
  *
10855
11029
  * Note that the `onChange` callback member of the handler is required.
10856
11030
  *
10857
- * @param sql The SQL query to execute
10858
- * @param parameters Optional array of parameters to bind to the query
10859
- * @param handler Callbacks for handling results and errors
10860
- * @param options Options for configuring watch behavior
11031
+ * @param sql - The SQL query to execute
11032
+ * @param parameters - Optional array of parameters to bind to the query
11033
+ * @param handler - Callbacks for handling results and errors
11034
+ * @param options - Options for configuring watch behavior
10861
11035
  */
10862
11036
  watchWithCallback(sql, parameters, handler, options) {
10863
11037
  const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
@@ -10870,7 +11044,7 @@ SELECT * FROM crud_entries;
10870
11044
  const watchedQuery = new OnChangeQueryProcessor({
10871
11045
  db: this,
10872
11046
  comparator,
10873
- placeholderData: null,
11047
+ placeholderData: null, // FIXME
10874
11048
  watchOptions: {
10875
11049
  query: {
10876
11050
  compile: () => ({
@@ -10903,12 +11077,12 @@ SELECT * FROM crud_entries;
10903
11077
  }
10904
11078
  /**
10905
11079
  * Execute a read query every time the source tables are modified.
10906
- * Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
11080
+ * Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
10907
11081
  * Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
10908
11082
  *
10909
- * @param sql The SQL query to execute
10910
- * @param parameters Optional array of parameters to bind to the query
10911
- * @param options Options for configuring watch behavior
11083
+ * @param sql - The SQL query to execute
11084
+ * @param parameters - Optional array of parameters to bind to the query
11085
+ * @param options - Options for configuring watch behavior
10912
11086
  * @returns An AsyncIterable that yields QueryResults whenever the data changes
10913
11087
  */
10914
11088
  watchWithAsyncGenerator(sql, parameters, options) {
@@ -10932,9 +11106,9 @@ SELECT * FROM crud_entries;
10932
11106
  * If tables are specified in the options, those are used directly.
10933
11107
  * Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
10934
11108
  *
10935
- * @param sql The SQL query to analyze
10936
- * @param parameters Optional parameters for the SQL query
10937
- * @param options Optional watch options that may contain explicit table list
11109
+ * @param sql - The SQL query to analyze
11110
+ * @param parameters - Optional parameters for the SQL query
11111
+ * @param options - Optional watch options that may contain explicit table list
10938
11112
  * @returns Array of table names that the query depends on
10939
11113
  */
10940
11114
  async resolveTables(sql, parameters, options) {
@@ -10963,13 +11137,13 @@ SELECT * FROM crud_entries;
10963
11137
  /**
10964
11138
  * Invoke the provided callback on any changes to any of the specified tables.
10965
11139
  *
10966
- * This is preferred over {@link watchWithCallback} when multiple queries need to be performed
11140
+ * This is preferred over {@link AbstractPowerSyncDatabase.watchWithCallback} when multiple queries need to be performed
10967
11141
  * together when data is changed.
10968
11142
  *
10969
11143
  * Note that the `onChange` callback member of the handler is required.
10970
11144
  *
10971
- * @param handler Callbacks for handling change events and errors
10972
- * @param options Options for configuring watch behavior
11145
+ * @param handler - Callbacks for handling change events and errors
11146
+ * @param options - Options for configuring watch behavior
10973
11147
  * @returns A dispose function to stop watching for changes
10974
11148
  */
10975
11149
  onChangeWithCallback(handler, options) {
@@ -11012,12 +11186,12 @@ SELECT * FROM crud_entries;
11012
11186
  /**
11013
11187
  * Create a Stream of changes to any of the specified tables.
11014
11188
  *
11015
- * This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be performed
11016
- * together when data is changed.
11189
+ * This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
11190
+ * performed together when data is changed.
11017
11191
  *
11018
11192
  * Note: do not declare this as `async *onChange` as it will not work in React Native.
11019
11193
  *
11020
- * @param options Options for configuring watch behavior
11194
+ * @param options - Options for configuring watch behavior
11021
11195
  * @returns An AsyncIterable that yields change events whenever the specified tables change
11022
11196
  */
11023
11197
  onChangeWithAsyncGenerator(options) {
@@ -11055,15 +11229,15 @@ SELECT * FROM crud_entries;
11055
11229
  changedTables.add(table);
11056
11230
  }
11057
11231
  }
11058
- /**
11059
- * @ignore
11060
- */
11061
11232
  async executeReadOnly(sql, params) {
11062
11233
  await this.waitForReady();
11063
11234
  return this.database.readLock((tx) => tx.execute(sql, params));
11064
11235
  }
11065
11236
  }
11066
11237
 
11238
+ /**
11239
+ * @internal
11240
+ */
11067
11241
  class AbstractPowerSyncDatabaseOpenFactory {
11068
11242
  options;
11069
11243
  constructor(options) {
@@ -11088,6 +11262,9 @@ class AbstractPowerSyncDatabaseOpenFactory {
11088
11262
  }
11089
11263
  }
11090
11264
 
11265
+ /**
11266
+ * @internal
11267
+ */
11091
11268
  function runOnSchemaChange(callback, db, options) {
11092
11269
  const triggerWatchedQuery = () => {
11093
11270
  const abortController = new AbortController();
@@ -11112,6 +11289,9 @@ function runOnSchemaChange(callback, db, options) {
11112
11289
  triggerWatchedQuery();
11113
11290
  }
11114
11291
 
11292
+ /**
11293
+ * @public
11294
+ */
11115
11295
  function compilableQueryWatch(db, query, handler, options) {
11116
11296
  const { onResult, onError = (e) => { } } = handler ?? {};
11117
11297
  if (!onResult) {
@@ -11149,8 +11329,14 @@ function compilableQueryWatch(db, query, handler, options) {
11149
11329
  runOnSchemaChange(watchQuery, db, options);
11150
11330
  }
11151
11331
 
11332
+ /**
11333
+ * @internal
11334
+ */
11152
11335
  const MAX_OP_ID = '9223372036854775807';
11153
11336
 
11337
+ /**
11338
+ * @internal
11339
+ */
11154
11340
  class SqliteBucketStorage extends BaseObserver {
11155
11341
  db;
11156
11342
  logger;
@@ -11311,6 +11497,8 @@ class SqliteBucketStorage extends BaseObserver {
11311
11497
  * Thrown when an underlying database connection is closed.
11312
11498
  * This is particularly relevant when worker connections are marked as closed while
11313
11499
  * operations are still in progress.
11500
+ *
11501
+ * @internal
11314
11502
  */
11315
11503
  class ConnectionClosedError extends Error {
11316
11504
  static NAME = 'ConnectionClosedError';
@@ -11330,6 +11518,8 @@ class ConnectionClosedError extends Error {
11330
11518
 
11331
11519
  /**
11332
11520
  * A schema is a collection of tables. It is used to define the structure of a database.
11521
+ *
11522
+ * @public
11333
11523
  */
11334
11524
  class Schema {
11335
11525
  /*
@@ -11366,7 +11556,7 @@ class Schema {
11366
11556
  * Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
11367
11557
  * using client-side table and column constraints.
11368
11558
  *
11369
- * @param tables An object of (table name, raw table definition) entries.
11559
+ * @param tables - An object of (table name, raw table definition) entries.
11370
11560
  */
11371
11561
  withRawTables(tables) {
11372
11562
  for (const [name, rawTableDefinition] of Object.entries(tables)) {
@@ -11412,6 +11602,8 @@ class Schema {
11412
11602
  Generate a new table from the columns and indexes
11413
11603
  @deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
11414
11604
  This will be removed in the next major release.
11605
+
11606
+ @public
11415
11607
  */
11416
11608
  class TableV2 extends Table {
11417
11609
  }
@@ -11422,6 +11614,8 @@ function sanitizeString(input) {
11422
11614
  /**
11423
11615
  * Helper function for sanitizing UUID input strings.
11424
11616
  * Typically used with {@link sanitizeSQL}.
11617
+ *
11618
+ * @alpha
11425
11619
  */
11426
11620
  function sanitizeUUID(uuid) {
11427
11621
  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;
@@ -11458,6 +11652,8 @@ function sanitizeUUID(uuid) {
11458
11652
  * // Incorrect:
11459
11653
  * sanitizeSQL`New.id = '${myID}'` // Produces double quotes: New.id = ''O''Reilly''
11460
11654
  * ```
11655
+ *
11656
+ * @alpha
11461
11657
  */
11462
11658
  function sanitizeSQL(strings, ...values) {
11463
11659
  let result = '';
@@ -11487,6 +11683,8 @@ function sanitizeSQL(strings, ...values) {
11487
11683
 
11488
11684
  /**
11489
11685
  * Performs a {@link AbstractPowerSyncDatabase.getAll} operation for a watched query.
11686
+ *
11687
+ * @public
11490
11688
  */
11491
11689
  class GetAllQuery {
11492
11690
  options;
@@ -11511,6 +11709,9 @@ class GetAllQuery {
11511
11709
  }
11512
11710
 
11513
11711
  const TypedLogger = Logger;
11712
+ /**
11713
+ * @public
11714
+ */
11514
11715
  const LogLevel = {
11515
11716
  TRACE: TypedLogger.TRACE,
11516
11717
  DEBUG: TypedLogger.DEBUG,
@@ -11527,6 +11728,7 @@ const LogLevel = {
11527
11728
  * across all loggers created with `createLogger`. Adjusting settings on this
11528
11729
  * base logger affects all loggers derived from it unless explicitly overridden.
11529
11730
  *
11731
+ * @public
11530
11732
  */
11531
11733
  function createBaseLogger() {
11532
11734
  return Logger;
@@ -11537,6 +11739,8 @@ function createBaseLogger() {
11537
11739
  * Named loggers allow specific modules or areas of your application to have
11538
11740
  * their own logging levels and behaviors. These loggers inherit configuration
11539
11741
  * from the base logger by default but can override settings independently.
11742
+ *
11743
+ * @public
11540
11744
  */
11541
11745
  function createLogger(name, options = {}) {
11542
11746
  const logger = Logger.get(name);
@@ -11546,6 +11750,9 @@ function createLogger(name, options = {}) {
11546
11750
  return logger;
11547
11751
  }
11548
11752
 
11753
+ /**
11754
+ * @internal
11755
+ */
11549
11756
  const parseQuery = (query, parameters) => {
11550
11757
  let sqlStatement;
11551
11758
  if (typeof query == 'string') {