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