@powersync/web 1.38.1 → 1.38.3

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 (69) hide show
  1. package/dist/worker/SharedSyncImplementation.umd.js +560 -353
  2. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  3. package/dist/worker/WASQLiteDB.umd.js +560 -353
  4. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  5. package/lib/package.json +2 -2
  6. package/lib/src/attachments/IndexDBFileSystemAdapter.js +1 -0
  7. package/lib/src/attachments/IndexDBFileSystemAdapter.js.map +1 -0
  8. package/lib/src/db/NavigatorTriggerClaimManager.js +1 -0
  9. package/lib/src/db/NavigatorTriggerClaimManager.js.map +1 -0
  10. package/lib/src/db/PowerSyncDatabase.js +1 -0
  11. package/lib/src/db/PowerSyncDatabase.js.map +1 -0
  12. package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js +1 -0
  13. package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js.map +1 -0
  14. package/lib/src/db/adapters/AsyncWebAdapter.js +1 -0
  15. package/lib/src/db/adapters/AsyncWebAdapter.js.map +1 -0
  16. package/lib/src/db/adapters/SSRDBAdapter.js +1 -0
  17. package/lib/src/db/adapters/SSRDBAdapter.js.map +1 -0
  18. package/lib/src/db/adapters/WebDBAdapter.js +1 -0
  19. package/lib/src/db/adapters/WebDBAdapter.js.map +1 -0
  20. package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js +1 -0
  21. package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js.map +1 -0
  22. package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js +1 -0
  23. package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js.map +1 -0
  24. package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js +1 -0
  25. package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js.map +1 -0
  26. package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js +1 -0
  27. package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js.map +1 -0
  28. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +1 -0
  29. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js.map +1 -0
  30. package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js +1 -0
  31. package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js.map +1 -0
  32. package/lib/src/db/adapters/wa-sqlite/vfs.js +1 -0
  33. package/lib/src/db/adapters/wa-sqlite/vfs.js.map +1 -0
  34. package/lib/src/db/adapters/web-sql-flags.js +1 -0
  35. package/lib/src/db/adapters/web-sql-flags.js.map +1 -0
  36. package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +1 -0
  37. package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js.map +1 -0
  38. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +1 -0
  39. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js.map +1 -0
  40. package/lib/src/db/sync/WebRemote.js +1 -0
  41. package/lib/src/db/sync/WebRemote.js.map +1 -0
  42. package/lib/src/db/sync/WebStreamingSyncImplementation.js +1 -0
  43. package/lib/src/db/sync/WebStreamingSyncImplementation.js.map +1 -0
  44. package/lib/src/db/sync/userAgent.js +1 -0
  45. package/lib/src/db/sync/userAgent.js.map +1 -0
  46. package/lib/src/index.js +1 -0
  47. package/lib/src/index.js.map +1 -0
  48. package/lib/src/shared/navigator.js +1 -0
  49. package/lib/src/shared/navigator.js.map +1 -0
  50. package/lib/src/shared/tab_close_signal.js +1 -0
  51. package/lib/src/shared/tab_close_signal.js.map +1 -0
  52. package/lib/src/worker/db/MultiDatabaseServer.js +1 -0
  53. package/lib/src/worker/db/MultiDatabaseServer.js.map +1 -0
  54. package/lib/src/worker/db/WASQLiteDB.worker.js +1 -0
  55. package/lib/src/worker/db/WASQLiteDB.worker.js.map +1 -0
  56. package/lib/src/worker/db/open-worker-database.js +1 -0
  57. package/lib/src/worker/db/open-worker-database.js.map +1 -0
  58. package/lib/src/worker/sync/AbstractSharedSyncClientProvider.js +1 -0
  59. package/lib/src/worker/sync/AbstractSharedSyncClientProvider.js.map +1 -0
  60. package/lib/src/worker/sync/BroadcastLogger.js +1 -0
  61. package/lib/src/worker/sync/BroadcastLogger.js.map +1 -0
  62. package/lib/src/worker/sync/SharedSyncImplementation.js +1 -0
  63. package/lib/src/worker/sync/SharedSyncImplementation.js.map +1 -0
  64. package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -0
  65. package/lib/src/worker/sync/SharedSyncImplementation.worker.js.map +1 -0
  66. package/lib/src/worker/sync/WorkerClient.js +1 -0
  67. package/lib/src/worker/sync/WorkerClient.js.map +1 -0
  68. package/lib/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +3 -3
@@ -1733,7 +1733,10 @@ __webpack_require__.r(__webpack_exports__);
1733
1733
  /* harmony export */ sanitizeUUID: () => (/* binding */ sanitizeUUID),
1734
1734
  /* harmony export */ timeoutSignal: () => (/* binding */ timeoutSignal)
1735
1735
  /* harmony export */ });
1736
- // https://www.sqlite.org/lang_expr.html#castexpr
1736
+ /**
1737
+ * @see https://www.sqlite.org/lang_expr.html#castexpr
1738
+ * @public
1739
+ */
1737
1740
  var ColumnType;
1738
1741
  (function (ColumnType) {
1739
1742
  ColumnType["TEXT"] = "TEXT";
@@ -1749,14 +1752,24 @@ const integer = {
1749
1752
  const real = {
1750
1753
  type: ColumnType.REAL
1751
1754
  };
1752
- // powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
1753
- // In earlier versions this was limited to 63.
1755
+ /**
1756
+ * powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
1757
+ * In earlier versions this was limited to 63.
1758
+ *
1759
+ * @internal
1760
+ */
1754
1761
  const MAX_AMOUNT_OF_COLUMNS = 1999;
1762
+ /**
1763
+ * @public
1764
+ */
1755
1765
  const column = {
1756
1766
  text,
1757
1767
  integer,
1758
1768
  real
1759
1769
  };
1770
+ /**
1771
+ * @public
1772
+ */
1760
1773
  class Column {
1761
1774
  options;
1762
1775
  constructor(options) {
@@ -1776,9 +1789,15 @@ class Column {
1776
1789
  }
1777
1790
  }
1778
1791
 
1792
+ /**
1793
+ * @internal
1794
+ */
1779
1795
  const DEFAULT_INDEX_COLUMN_OPTIONS = {
1780
1796
  ascending: true
1781
1797
  };
1798
+ /**
1799
+ * @public
1800
+ */
1782
1801
  class IndexedColumn {
1783
1802
  options;
1784
1803
  static createAscending(column) {
@@ -1805,9 +1824,15 @@ class IndexedColumn {
1805
1824
  }
1806
1825
  }
1807
1826
 
1827
+ /**
1828
+ * @internal
1829
+ */
1808
1830
  const DEFAULT_INDEX_OPTIONS = {
1809
1831
  columns: []
1810
1832
  };
1833
+ /**
1834
+ * @public
1835
+ */
1811
1836
  class Index {
1812
1837
  options;
1813
1838
  static createAscending(options, columnNames) {
@@ -1849,6 +1874,9 @@ function encodeTableOptions(options) {
1849
1874
  };
1850
1875
  }
1851
1876
 
1877
+ /**
1878
+ * @internal
1879
+ */
1852
1880
  const DEFAULT_TABLE_OPTIONS = {
1853
1881
  indexes: [],
1854
1882
  insertOnly: false,
@@ -1857,7 +1885,13 @@ const DEFAULT_TABLE_OPTIONS = {
1857
1885
  trackMetadata: false,
1858
1886
  ignoreEmptyUpdates: false
1859
1887
  };
1888
+ /**
1889
+ * @internal
1890
+ */
1860
1891
  const InvalidSQLCharacters = /["'%,.#\s[\]]/;
1892
+ /**
1893
+ * @public
1894
+ */
1861
1895
  class Table {
1862
1896
  options;
1863
1897
  _mappedColumns;
@@ -2048,6 +2082,11 @@ class Table {
2048
2082
  }
2049
2083
  }
2050
2084
 
2085
+ /**
2086
+ * The default name of the local table storing attachment data.
2087
+ *
2088
+ * @alpha
2089
+ */
2051
2090
  const ATTACHMENT_TABLE = 'attachments';
2052
2091
  /**
2053
2092
  * Maps a database row to an AttachmentRecord.
@@ -2055,7 +2094,7 @@ const ATTACHMENT_TABLE = 'attachments';
2055
2094
  * @param row - The database row object
2056
2095
  * @returns The corresponding AttachmentRecord
2057
2096
  *
2058
- * @experimental
2097
+ * @alpha
2059
2098
  */
2060
2099
  function attachmentFromSql(row) {
2061
2100
  return {
@@ -2073,7 +2112,7 @@ function attachmentFromSql(row) {
2073
2112
  /**
2074
2113
  * AttachmentState represents the current synchronization state of an attachment.
2075
2114
  *
2076
- * @experimental
2115
+ * @alpha
2077
2116
  */
2078
2117
  var AttachmentState;
2079
2118
  (function (AttachmentState) {
@@ -2086,7 +2125,7 @@ var AttachmentState;
2086
2125
  /**
2087
2126
  * AttachmentTable defines the schema for the attachment queue table.
2088
2127
  *
2089
- * @internal
2128
+ * @alpha
2090
2129
  */
2091
2130
  class AttachmentTable extends Table {
2092
2131
  constructor(options) {
@@ -2114,7 +2153,8 @@ class AttachmentTable extends Table {
2114
2153
  * Provides methods to query, insert, update, and delete attachment records with
2115
2154
  * proper transaction management through PowerSync.
2116
2155
  *
2117
- * @internal
2156
+ * @experimental
2157
+ * @alpha
2118
2158
  */
2119
2159
  class AttachmentContext {
2120
2160
  /** PowerSync database instance for executing queries */
@@ -2336,6 +2376,9 @@ class AttachmentContext {
2336
2376
  }
2337
2377
  }
2338
2378
 
2379
+ /**
2380
+ * @public
2381
+ */
2339
2382
  var WatchedQueryListenerEvent;
2340
2383
  (function (WatchedQueryListenerEvent) {
2341
2384
  WatchedQueryListenerEvent["ON_DATA"] = "onData";
@@ -2344,176 +2387,18 @@ var WatchedQueryListenerEvent;
2344
2387
  WatchedQueryListenerEvent["SETTINGS_WILL_UPDATE"] = "settingsWillUpdate";
2345
2388
  WatchedQueryListenerEvent["CLOSED"] = "closed";
2346
2389
  })(WatchedQueryListenerEvent || (WatchedQueryListenerEvent = {}));
2390
+ /**
2391
+ * @internal
2392
+ */
2347
2393
  const DEFAULT_WATCH_THROTTLE_MS = 30;
2394
+ /**
2395
+ * @internal
2396
+ */
2348
2397
  const DEFAULT_WATCH_QUERY_OPTIONS = {
2349
2398
  throttleMs: DEFAULT_WATCH_THROTTLE_MS,
2350
2399
  reportFetching: true
2351
2400
  };
2352
2401
 
2353
- /**
2354
- * Orchestrates attachment synchronization between local and remote storage.
2355
- * Handles uploads, downloads, deletions, and state transitions.
2356
- *
2357
- * @internal
2358
- */
2359
- class SyncingService {
2360
- attachmentService;
2361
- localStorage;
2362
- remoteStorage;
2363
- logger;
2364
- errorHandler;
2365
- constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
2366
- this.attachmentService = attachmentService;
2367
- this.localStorage = localStorage;
2368
- this.remoteStorage = remoteStorage;
2369
- this.logger = logger;
2370
- this.errorHandler = errorHandler;
2371
- }
2372
- /**
2373
- * Processes attachments based on their state (upload, download, or delete).
2374
- * All updates are saved in a single batch after processing.
2375
- *
2376
- * @param attachments - Array of attachment records to process
2377
- * @param context - Attachment context for database operations
2378
- * @returns Promise that resolves when all attachments have been processed and saved
2379
- */
2380
- async processAttachments(attachments, context) {
2381
- const updatedAttachments = [];
2382
- for (const attachment of attachments) {
2383
- switch (attachment.state) {
2384
- case AttachmentState.QUEUED_UPLOAD:
2385
- const uploaded = await this.uploadAttachment(attachment);
2386
- updatedAttachments.push(uploaded);
2387
- break;
2388
- case AttachmentState.QUEUED_DOWNLOAD:
2389
- const downloaded = await this.downloadAttachment(attachment);
2390
- updatedAttachments.push(downloaded);
2391
- break;
2392
- case AttachmentState.QUEUED_DELETE:
2393
- const deleted = await this.deleteAttachment(attachment, context);
2394
- updatedAttachments.push(deleted);
2395
- break;
2396
- }
2397
- }
2398
- await context.saveAttachments(updatedAttachments);
2399
- }
2400
- /**
2401
- * Uploads an attachment from local storage to remote storage.
2402
- * On success, marks as SYNCED. On failure, defers to error handler or archives.
2403
- *
2404
- * @param attachment - The attachment record to upload
2405
- * @returns Updated attachment record with new state
2406
- * @throws Error if the attachment has no localUri
2407
- */
2408
- async uploadAttachment(attachment) {
2409
- this.logger.info(`Uploading attachment ${attachment.filename}`);
2410
- try {
2411
- if (attachment.localUri == null) {
2412
- throw new Error(`No localUri for attachment ${attachment.id}`);
2413
- }
2414
- const fileBlob = await this.localStorage.readFile(attachment.localUri);
2415
- await this.remoteStorage.uploadFile(fileBlob, attachment);
2416
- return {
2417
- ...attachment,
2418
- state: AttachmentState.SYNCED,
2419
- hasSynced: true
2420
- };
2421
- }
2422
- catch (error) {
2423
- const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
2424
- if (!shouldRetry) {
2425
- return {
2426
- ...attachment,
2427
- state: AttachmentState.ARCHIVED
2428
- };
2429
- }
2430
- return attachment;
2431
- }
2432
- }
2433
- /**
2434
- * Downloads an attachment from remote storage to local storage.
2435
- * Retrieves the file, converts to base64, and saves locally.
2436
- * On success, marks as SYNCED. On failure, defers to error handler or archives.
2437
- *
2438
- * @param attachment - The attachment record to download
2439
- * @returns Updated attachment record with local URI and new state
2440
- */
2441
- async downloadAttachment(attachment) {
2442
- this.logger.info(`Downloading attachment ${attachment.filename}`);
2443
- try {
2444
- const fileData = await this.remoteStorage.downloadFile(attachment);
2445
- const localUri = this.localStorage.getLocalUri(attachment.filename);
2446
- await this.localStorage.saveFile(localUri, fileData);
2447
- return {
2448
- ...attachment,
2449
- state: AttachmentState.SYNCED,
2450
- localUri: localUri,
2451
- hasSynced: true
2452
- };
2453
- }
2454
- catch (error) {
2455
- const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
2456
- if (!shouldRetry) {
2457
- return {
2458
- ...attachment,
2459
- state: AttachmentState.ARCHIVED
2460
- };
2461
- }
2462
- return attachment;
2463
- }
2464
- }
2465
- /**
2466
- * Deletes an attachment from both remote and local storage.
2467
- * Removes the remote file, local file (if exists), and the attachment record.
2468
- * On failure, defers to error handler or archives.
2469
- *
2470
- * @param attachment - The attachment record to delete
2471
- * @param context - Attachment context for database operations
2472
- * @returns Updated attachment record
2473
- */
2474
- async deleteAttachment(attachment, context) {
2475
- try {
2476
- await this.remoteStorage.deleteFile(attachment);
2477
- if (attachment.localUri) {
2478
- await this.localStorage.deleteFile(attachment.localUri);
2479
- }
2480
- await context.deleteAttachment(attachment.id);
2481
- return {
2482
- ...attachment,
2483
- state: AttachmentState.ARCHIVED
2484
- };
2485
- }
2486
- catch (error) {
2487
- const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
2488
- if (!shouldRetry) {
2489
- return {
2490
- ...attachment,
2491
- state: AttachmentState.ARCHIVED
2492
- };
2493
- }
2494
- return attachment;
2495
- }
2496
- }
2497
- /**
2498
- * Performs cleanup of archived attachments by removing their local files and records.
2499
- * Errors during local file deletion are logged but do not prevent record deletion.
2500
- */
2501
- async deleteArchivedAttachments(context) {
2502
- return await context.deleteArchivedAttachments(async (archivedAttachments) => {
2503
- for (const attachment of archivedAttachments) {
2504
- if (attachment.localUri) {
2505
- try {
2506
- await this.localStorage.deleteFile(attachment.localUri);
2507
- }
2508
- catch (error) {
2509
- this.logger.error('Error deleting local file for archived attachment', error);
2510
- }
2511
- }
2512
- }
2513
- });
2514
- }
2515
- }
2516
-
2517
2402
  /**
2518
2403
  * A simple fixed-capacity queue implementation.
2519
2404
  *
@@ -2699,6 +2584,9 @@ class Mutex {
2699
2584
  }
2700
2585
  }
2701
2586
  }
2587
+ /**
2588
+ * @internal
2589
+ */
2702
2590
  function timeoutSignal(timeout) {
2703
2591
  if (timeout == null)
2704
2592
  return;
@@ -2727,36 +2615,200 @@ class AttachmentService {
2727
2615
  this.context = new AttachmentContext(db, tableName, logger, archivedCacheLimit);
2728
2616
  }
2729
2617
  /**
2730
- * Creates a differential watch query for active attachments requiring synchronization.
2731
- * @returns Watch query that emits changes for queued uploads, downloads, and deletes
2618
+ * Creates a differential watch query for active attachments requiring synchronization.
2619
+ * @returns Watch query that emits changes for queued uploads, downloads, and deletes
2620
+ */
2621
+ watchActiveAttachments({ throttleMs } = {}) {
2622
+ this.logger.info('Watching active attachments...');
2623
+ const watch = this.db
2624
+ .query({
2625
+ sql: /* sql */ `
2626
+ SELECT
2627
+ *
2628
+ FROM
2629
+ ${this.tableName}
2630
+ WHERE
2631
+ state = ?
2632
+ OR state = ?
2633
+ OR state = ?
2634
+ ORDER BY
2635
+ timestamp ASC
2636
+ `,
2637
+ parameters: [AttachmentState.QUEUED_UPLOAD, AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_DELETE]
2638
+ })
2639
+ .differentialWatch({ throttleMs });
2640
+ return watch;
2641
+ }
2642
+ /**
2643
+ * Executes a callback with exclusive access to the attachment context.
2644
+ */
2645
+ async withContext(callback) {
2646
+ return this.mutex.runExclusive(async () => {
2647
+ return callback(this.context);
2648
+ });
2649
+ }
2650
+ }
2651
+
2652
+ /**
2653
+ * Orchestrates attachment synchronization between local and remote storage.
2654
+ * Handles uploads, downloads, deletions, and state transitions.
2655
+ *
2656
+ * @internal
2657
+ */
2658
+ class SyncingService {
2659
+ attachmentService;
2660
+ localStorage;
2661
+ remoteStorage;
2662
+ logger;
2663
+ errorHandler;
2664
+ constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
2665
+ this.attachmentService = attachmentService;
2666
+ this.localStorage = localStorage;
2667
+ this.remoteStorage = remoteStorage;
2668
+ this.logger = logger;
2669
+ this.errorHandler = errorHandler;
2670
+ }
2671
+ /**
2672
+ * Processes attachments based on their state (upload, download, or delete).
2673
+ * All updates are saved in a single batch after processing.
2674
+ *
2675
+ * @param attachments - Array of attachment records to process
2676
+ * @param context - Attachment context for database operations
2677
+ * @returns Promise that resolves when all attachments have been processed and saved
2678
+ */
2679
+ async processAttachments(attachments, context) {
2680
+ const updatedAttachments = [];
2681
+ for (const attachment of attachments) {
2682
+ switch (attachment.state) {
2683
+ case AttachmentState.QUEUED_UPLOAD:
2684
+ const uploaded = await this.uploadAttachment(attachment);
2685
+ updatedAttachments.push(uploaded);
2686
+ break;
2687
+ case AttachmentState.QUEUED_DOWNLOAD:
2688
+ const downloaded = await this.downloadAttachment(attachment);
2689
+ updatedAttachments.push(downloaded);
2690
+ break;
2691
+ case AttachmentState.QUEUED_DELETE:
2692
+ const deleted = await this.deleteAttachment(attachment, context);
2693
+ updatedAttachments.push(deleted);
2694
+ break;
2695
+ }
2696
+ }
2697
+ await context.saveAttachments(updatedAttachments);
2698
+ }
2699
+ /**
2700
+ * Uploads an attachment from local storage to remote storage.
2701
+ * On success, marks as SYNCED. On failure, defers to error handler or archives.
2702
+ *
2703
+ * @param attachment - The attachment record to upload
2704
+ * @returns Updated attachment record with new state
2705
+ * @throws Error if the attachment has no localUri
2706
+ */
2707
+ async uploadAttachment(attachment) {
2708
+ this.logger.info(`Uploading attachment ${attachment.filename}`);
2709
+ try {
2710
+ if (attachment.localUri == null) {
2711
+ throw new Error(`No localUri for attachment ${attachment.id}`);
2712
+ }
2713
+ const fileBlob = await this.localStorage.readFile(attachment.localUri);
2714
+ await this.remoteStorage.uploadFile(fileBlob, attachment);
2715
+ return {
2716
+ ...attachment,
2717
+ state: AttachmentState.SYNCED,
2718
+ hasSynced: true
2719
+ };
2720
+ }
2721
+ catch (error) {
2722
+ const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
2723
+ if (!shouldRetry) {
2724
+ return {
2725
+ ...attachment,
2726
+ state: AttachmentState.ARCHIVED
2727
+ };
2728
+ }
2729
+ return attachment;
2730
+ }
2731
+ }
2732
+ /**
2733
+ * Downloads an attachment from remote storage to local storage.
2734
+ * Retrieves the file, converts to base64, and saves locally.
2735
+ * On success, marks as SYNCED. On failure, defers to error handler or archives.
2736
+ *
2737
+ * @param attachment - The attachment record to download
2738
+ * @returns Updated attachment record with local URI and new state
2739
+ */
2740
+ async downloadAttachment(attachment) {
2741
+ this.logger.info(`Downloading attachment ${attachment.filename}`);
2742
+ try {
2743
+ const fileData = await this.remoteStorage.downloadFile(attachment);
2744
+ const localUri = this.localStorage.getLocalUri(attachment.filename);
2745
+ await this.localStorage.saveFile(localUri, fileData);
2746
+ return {
2747
+ ...attachment,
2748
+ state: AttachmentState.SYNCED,
2749
+ localUri: localUri,
2750
+ hasSynced: true
2751
+ };
2752
+ }
2753
+ catch (error) {
2754
+ const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
2755
+ if (!shouldRetry) {
2756
+ return {
2757
+ ...attachment,
2758
+ state: AttachmentState.ARCHIVED
2759
+ };
2760
+ }
2761
+ return attachment;
2762
+ }
2763
+ }
2764
+ /**
2765
+ * Deletes an attachment from both remote and local storage.
2766
+ * Removes the remote file, local file (if exists), and the attachment record.
2767
+ * On failure, defers to error handler or archives.
2768
+ *
2769
+ * @param attachment - The attachment record to delete
2770
+ * @param context - Attachment context for database operations
2771
+ * @returns Updated attachment record
2732
2772
  */
2733
- watchActiveAttachments({ throttleMs } = {}) {
2734
- this.logger.info('Watching active attachments...');
2735
- const watch = this.db
2736
- .query({
2737
- sql: /* sql */ `
2738
- SELECT
2739
- *
2740
- FROM
2741
- ${this.tableName}
2742
- WHERE
2743
- state = ?
2744
- OR state = ?
2745
- OR state = ?
2746
- ORDER BY
2747
- timestamp ASC
2748
- `,
2749
- parameters: [AttachmentState.QUEUED_UPLOAD, AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_DELETE]
2750
- })
2751
- .differentialWatch({ throttleMs });
2752
- return watch;
2773
+ async deleteAttachment(attachment, context) {
2774
+ try {
2775
+ await this.remoteStorage.deleteFile(attachment);
2776
+ if (attachment.localUri) {
2777
+ await this.localStorage.deleteFile(attachment.localUri);
2778
+ }
2779
+ await context.deleteAttachment(attachment.id);
2780
+ return {
2781
+ ...attachment,
2782
+ state: AttachmentState.ARCHIVED
2783
+ };
2784
+ }
2785
+ catch (error) {
2786
+ const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
2787
+ if (!shouldRetry) {
2788
+ return {
2789
+ ...attachment,
2790
+ state: AttachmentState.ARCHIVED
2791
+ };
2792
+ }
2793
+ return attachment;
2794
+ }
2753
2795
  }
2754
2796
  /**
2755
- * Executes a callback with exclusive access to the attachment context.
2797
+ * Performs cleanup of archived attachments by removing their local files and records.
2798
+ * Errors during local file deletion are logged but do not prevent record deletion.
2756
2799
  */
2757
- async withContext(callback) {
2758
- return this.mutex.runExclusive(async () => {
2759
- return callback(this.context);
2800
+ async deleteArchivedAttachments(context) {
2801
+ return await context.deleteArchivedAttachments(async (archivedAttachments) => {
2802
+ for (const attachment of archivedAttachments) {
2803
+ if (attachment.localUri) {
2804
+ try {
2805
+ await this.localStorage.deleteFile(attachment.localUri);
2806
+ }
2807
+ catch (error) {
2808
+ this.logger.error('Error deleting local file for archived attachment', error);
2809
+ }
2810
+ }
2811
+ }
2760
2812
  });
2761
2813
  }
2762
2814
  }
@@ -2817,16 +2869,6 @@ class AttachmentQueue {
2817
2869
  * Creates a new AttachmentQueue instance.
2818
2870
  *
2819
2871
  * @param options - Configuration options
2820
- * @param options.db - PowerSync database instance
2821
- * @param options.remoteStorage - Remote storage adapter for upload/download operations
2822
- * @param options.localStorage - Local storage adapter for file persistence
2823
- * @param options.watchAttachments - Callback for monitoring attachment changes in your data model
2824
- * @param options.tableName - Name of the table to store attachment records. Default: 'ps_attachment_queue'
2825
- * @param options.logger - Logger instance. Defaults to db.logger
2826
- * @param options.syncIntervalMs - Periodic polling interval in milliseconds for retrying failed uploads/downloads. Default: 30000
2827
- * @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
2828
- * @param options.downloadAttachments - Whether to automatically download remote attachments. Default: true
2829
- * @param options.archivedCacheLimit - Maximum archived attachments before cleanup. Default: 100
2830
2872
  */
2831
2873
  constructor({ db, localStorage, remoteStorage, watchAttachments, logger, tableName = ATTACHMENT_TABLE, syncIntervalMs = 30 * 1000, syncThrottleDuration = DEFAULT_WATCH_THROTTLE_MS, downloadAttachments = true, archivedCacheLimit = 100, errorHandler }) {
2832
2874
  this.db = db;
@@ -2915,6 +2957,7 @@ class AttachmentQueue {
2915
2957
  state: AttachmentState.QUEUED_DOWNLOAD,
2916
2958
  hasSynced: false,
2917
2959
  metaData: watchedAttachment.metaData,
2960
+ mediaType: watchedAttachment.mediaType,
2918
2961
  timestamp: new Date().getTime()
2919
2962
  });
2920
2963
  continue;
@@ -3002,17 +3045,24 @@ class AttachmentQueue {
3002
3045
  this.statusListenerDispose = undefined;
3003
3046
  }
3004
3047
  }
3048
+ /**
3049
+ * Provides an {@link AttachmentContext} to a callback.
3050
+ *
3051
+ * The callback runs while the attachment queue mutex is held. Do not call
3052
+ * other {@link AttachmentQueue} methods from within the callback, as they may
3053
+ * attempt to acquire the same mutex and block indefinitely.
3054
+ */
3055
+ withAttachmentContext(callback) {
3056
+ /**
3057
+ * AttachmentService is internal and private in this class.
3058
+ * We only need to expose its locking and context functionality for extending classes.
3059
+ */
3060
+ return this.attachmentService.withContext(callback);
3061
+ }
3005
3062
  /**
3006
3063
  * Saves a file to local storage and queues it for upload to remote storage.
3007
3064
  *
3008
3065
  * @param options - File save options
3009
- * @param options.data - The file data as ArrayBuffer, Blob, or base64 string
3010
- * @param options.fileExtension - File extension (e.g., 'jpg', 'pdf')
3011
- * @param options.mediaType - MIME type of the file (e.g., 'image/jpeg')
3012
- * @param options.metaData - Optional metadata to associate with the attachment
3013
- * @param options.id - Optional custom ID. If not provided, a UUID will be generated
3014
- * @param options.updateHook - Optional callback to execute additional database operations
3015
- * within the same transaction as the attachment creation
3016
3066
  * @returns Promise resolving to the created attachment record
3017
3067
  */
3018
3068
  async saveFile({ data, fileExtension, mediaType, metaData, id, updateHook }) {
@@ -3125,6 +3175,9 @@ class AttachmentQueue {
3125
3175
  }
3126
3176
  }
3127
3177
 
3178
+ /**
3179
+ * @alpha
3180
+ */
3128
3181
  var EncodingType;
3129
3182
  (function (EncodingType) {
3130
3183
  EncodingType["UTF8"] = "utf8";
@@ -3588,7 +3641,9 @@ var Logger = /*@__PURE__*/getDefaultExportFromCjs(loggerExports);
3588
3641
  * different SQLite DB implementations.
3589
3642
  */
3590
3643
  /**
3591
- * Implements {@link DBGetUtils} on a {@link SqlRunner}.
3644
+ * Implements {@link DBGetUtils} on a {@link SqlExecutor}.
3645
+ *
3646
+ * @internal
3592
3647
  */
3593
3648
  function DBGetUtilsDefaultMixin(Base) {
3594
3649
  return class extends Base {
@@ -3632,6 +3687,8 @@ function DBGetUtilsDefaultMixin(Base) {
3632
3687
  }
3633
3688
  /**
3634
3689
  * Update table operation numbers from SQLite
3690
+ *
3691
+ * @public
3635
3692
  */
3636
3693
  var RowUpdateType;
3637
3694
  (function (RowUpdateType) {
@@ -3640,8 +3697,10 @@ var RowUpdateType;
3640
3697
  RowUpdateType[RowUpdateType["SQLITE_UPDATE"] = 23] = "SQLITE_UPDATE";
3641
3698
  })(RowUpdateType || (RowUpdateType = {}));
3642
3699
  /**
3643
- * A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool.readLock} and
3644
- * {@link ConnectionPool.writeLock}.
3700
+ * A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool#readLock} and
3701
+ * {@link ConnectionPool#writeLock}.
3702
+ *
3703
+ * @internal
3645
3704
  */
3646
3705
  function DBAdapterDefaultMixin(Base) {
3647
3706
  return class extends Base {
@@ -3729,9 +3788,15 @@ class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction)
3729
3788
  }
3730
3789
  }
3731
3790
  }
3791
+ /**
3792
+ * @internal
3793
+ */
3732
3794
  function isBatchedUpdateNotification(update) {
3733
3795
  return 'tables' in update;
3734
3796
  }
3797
+ /**
3798
+ * @internal
3799
+ */
3735
3800
  function extractTableUpdates(update) {
3736
3801
  return isBatchedUpdateNotification(update) ? update.tables : [update.table];
3737
3802
  }
@@ -3759,6 +3824,8 @@ const FULL_SYNC_PRIORITY = 2147483647;
3759
3824
  *
3760
3825
  * Also note that data is downloaded in bulk, which means that individual counters are unlikely
3761
3826
  * to be updated one-by-one.
3827
+ *
3828
+ * @public
3762
3829
  */
3763
3830
  class SyncProgress {
3764
3831
  internal;
@@ -3797,6 +3864,9 @@ class SyncProgress {
3797
3864
  }
3798
3865
  }
3799
3866
 
3867
+ /**
3868
+ * @public
3869
+ */
3800
3870
  class SyncStatus {
3801
3871
  options;
3802
3872
  constructor(options) {
@@ -3807,6 +3877,8 @@ class SyncStatus {
3807
3877
  * implementation).
3808
3878
  *
3809
3879
  * This information is only available after a connection has been requested.
3880
+ *
3881
+ * @deprecated This always returns the Rust client (the only option).
3810
3882
  */
3811
3883
  get clientImplementation() {
3812
3884
  return this.options.clientImplementation;
@@ -3814,7 +3886,7 @@ class SyncStatus {
3814
3886
  /**
3815
3887
  * Indicates if the client is currently connected to the PowerSync service.
3816
3888
  *
3817
- * @returns {boolean} True if connected, false otherwise. Defaults to false if not specified.
3889
+ * @returns True if connected, false otherwise. Defaults to false if not specified.
3818
3890
  */
3819
3891
  get connected() {
3820
3892
  return this.options.connected ?? false;
@@ -3822,7 +3894,7 @@ class SyncStatus {
3822
3894
  /**
3823
3895
  * Indicates if the client is in the process of establishing a connection to the PowerSync service.
3824
3896
  *
3825
- * @returns {boolean} True if connecting, false otherwise. Defaults to false if not specified.
3897
+ * @returns True if connecting, false otherwise. Defaults to false if not specified.
3826
3898
  */
3827
3899
  get connecting() {
3828
3900
  return this.options.connecting ?? false;
@@ -3831,7 +3903,7 @@ class SyncStatus {
3831
3903
  * Time that a last sync has fully completed, if any.
3832
3904
  * This timestamp is reset to null after a restart of the PowerSync service.
3833
3905
  *
3834
- * @returns {Date | undefined} The timestamp of the last successful sync, or undefined if no sync has completed.
3906
+ * @returns The timestamp of the last successful sync, or undefined if no sync has completed.
3835
3907
  */
3836
3908
  get lastSyncedAt() {
3837
3909
  return this.options.lastSyncedAt;
@@ -3839,7 +3911,7 @@ class SyncStatus {
3839
3911
  /**
3840
3912
  * Indicates whether there has been at least one full sync completed since initialization.
3841
3913
  *
3842
- * @returns {boolean | undefined} True if at least one sync has completed, false if no sync has completed,
3914
+ * @returns True if at least one sync has completed, false if no sync has completed,
3843
3915
  * or undefined when the state is still being loaded from the database.
3844
3916
  */
3845
3917
  get hasSynced() {
@@ -3848,10 +3920,10 @@ class SyncStatus {
3848
3920
  /**
3849
3921
  * Provides the current data flow status regarding uploads and downloads.
3850
3922
  *
3851
- * @returns {SyncDataFlowStatus} An object containing:
3923
+ * @returns An object containing:
3852
3924
  * - downloading: True if actively downloading changes (only when connected is also true)
3853
3925
  * - uploading: True if actively uploading changes
3854
- * Defaults to {downloading: false, uploading: false} if not specified.
3926
+ * Defaults to `{downloading: false, uploading: false}` if not specified.
3855
3927
  */
3856
3928
  get dataFlowStatus() {
3857
3929
  return (this.options.dataFlow ?? {
@@ -3876,7 +3948,7 @@ class SyncStatus {
3876
3948
  return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
3877
3949
  }
3878
3950
  /**
3879
- * If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
3951
+ * If the `stream` appears in {@link SyncStatus.syncStreams}, returns the current status for that stream.
3880
3952
  */
3881
3953
  forStream(stream) {
3882
3954
  const asJson = JSON.stringify(stream.parameters);
@@ -3886,7 +3958,7 @@ class SyncStatus {
3886
3958
  /**
3887
3959
  * Provides sync status information for all bucket priorities, sorted by priority (highest first).
3888
3960
  *
3889
- * @returns {SyncPriorityStatus[]} An array of status entries for different sync priority levels,
3961
+ * @returns An array of status entries for different sync priority levels,
3890
3962
  * sorted with highest priorities (lower numbers) first.
3891
3963
  */
3892
3964
  get priorityStatusEntries() {
@@ -3921,8 +3993,8 @@ class SyncStatus {
3921
3993
  * For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
3922
3994
  * with a priority of 1 may return information for priority level 3.
3923
3995
  *
3924
- * @param {number} priority The bucket priority for which the status should be reported
3925
- * @returns {SyncPriorityStatus} Status information for the requested priority level or the next higher level with available status
3996
+ * @param priority - The bucket priority for which the status should be reported
3997
+ * @returns Status information for the requested priority level or the next higher level with available status
3926
3998
  */
3927
3999
  statusForPriority(priority) {
3928
4000
  // priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
@@ -3943,8 +4015,8 @@ class SyncStatus {
3943
4015
  * Compares this SyncStatus instance with another to determine if they are equal.
3944
4016
  * Equality is determined by comparing the serialized JSON representation of both instances.
3945
4017
  *
3946
- * @param {SyncStatus} status The SyncStatus instance to compare against
3947
- * @returns {boolean} True if the instances are considered equal, false otherwise
4018
+ * @param status - The SyncStatus instance to compare against
4019
+ * @returns True if the instances are considered equal, false otherwise
3948
4020
  */
3949
4021
  isEqual(status) {
3950
4022
  /**
@@ -3967,7 +4039,7 @@ class SyncStatus {
3967
4039
  * Creates a human-readable string representation of the current sync status.
3968
4040
  * Includes information about connection state, sync completion, and data flow.
3969
4041
  *
3970
- * @returns {string} A string representation of the sync status
4042
+ * @returns A string representation of the sync status
3971
4043
  */
3972
4044
  getMessage() {
3973
4045
  const dataFlow = this.dataFlowStatus;
@@ -3976,7 +4048,7 @@ class SyncStatus {
3976
4048
  /**
3977
4049
  * Serializes the SyncStatus instance to a plain object.
3978
4050
  *
3979
- * @returns {SyncStatusOptions} A plain object representation of the sync status
4051
+ * @returns A plain object representation of the sync status
3980
4052
  */
3981
4053
  toJSON() {
3982
4054
  return {
@@ -4042,6 +4114,9 @@ class SyncStreamStatusView {
4042
4114
  }
4043
4115
  }
4044
4116
 
4117
+ /**
4118
+ * @public
4119
+ */
4045
4120
  class UploadQueueStats {
4046
4121
  count;
4047
4122
  size;
@@ -4067,6 +4142,9 @@ class UploadQueueStats {
4067
4142
  }
4068
4143
  }
4069
4144
 
4145
+ /**
4146
+ * @internal
4147
+ */
4070
4148
  class BaseObserver {
4071
4149
  listeners = new Set();
4072
4150
  constructor() { }
@@ -4094,6 +4172,9 @@ class BaseObserver {
4094
4172
  }
4095
4173
  }
4096
4174
 
4175
+ /**
4176
+ * @internal
4177
+ */
4097
4178
  class ControlledExecutor {
4098
4179
  task;
4099
4180
  /**
@@ -4163,30 +4244,44 @@ function throttleTrailing(func, wait) {
4163
4244
  }
4164
4245
  };
4165
4246
  }
4166
- /**
4167
- * Throttle a function to be called at most once every "wait" milliseconds,
4168
- * on the leading and trailing edge.
4169
- *
4170
- * Roughly equivalent to lodash/throttle with {leading: true, trailing: true}
4171
- */
4172
- function throttleLeadingTrailing(func, wait) {
4173
- let timeoutId = null;
4174
- let lastCallTime = 0;
4175
- const invokeFunction = () => {
4176
- func();
4177
- lastCallTime = Date.now();
4178
- timeoutId = null;
4179
- };
4180
- return function () {
4181
- const now = Date.now();
4182
- const timeToWait = wait - (now - lastCallTime);
4183
- if (timeToWait <= 0) {
4184
- // Leading edge: Call the function immediately if enough time has passed
4185
- invokeFunction();
4186
- }
4187
- else if (!timeoutId) {
4188
- // Set a timeout for the trailing edge if not already set
4189
- timeoutId = setTimeout(invokeFunction, timeToWait);
4247
+ function asyncNotifier() {
4248
+ let waitingConsumer = null;
4249
+ let hasPendingNotification = false;
4250
+ return {
4251
+ notify() {
4252
+ if (waitingConsumer != null) {
4253
+ waitingConsumer();
4254
+ waitingConsumer = null;
4255
+ }
4256
+ else {
4257
+ hasPendingNotification = true;
4258
+ }
4259
+ },
4260
+ waitForNotification(signal) {
4261
+ return new Promise((resolve) => {
4262
+ if (waitingConsumer != null) {
4263
+ throw new Error('Illegal call to waitForNotification, already has a waiter.');
4264
+ }
4265
+ if (signal.aborted) {
4266
+ resolve();
4267
+ }
4268
+ else if (hasPendingNotification) {
4269
+ resolve();
4270
+ hasPendingNotification = false;
4271
+ }
4272
+ else {
4273
+ function complete() {
4274
+ signal.removeEventListener('abort', onAbort);
4275
+ resolve();
4276
+ }
4277
+ function onAbort() {
4278
+ waitingConsumer = null;
4279
+ resolve();
4280
+ }
4281
+ waitingConsumer = complete;
4282
+ signal.addEventListener('abort', onAbort);
4283
+ }
4284
+ });
4190
4285
  }
4191
4286
  };
4192
4287
  }
@@ -4347,7 +4442,7 @@ class ConnectionManager extends BaseObserver {
4347
4442
  /**
4348
4443
  * Close the sync connection.
4349
4444
  *
4350
- * Use {@link connect} to connect again.
4445
+ * Use {@link ConnectionManager.connect} to connect again.
4351
4446
  */
4352
4447
  async disconnect() {
4353
4448
  // This will help abort pending connects
@@ -4487,6 +4582,8 @@ const _finalizer = 'FinalizationRegistry' in globalThis
4487
4582
  /**
4488
4583
  * An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
4489
4584
  * result has changes without necessarily processing all items in the result.
4585
+ *
4586
+ * @public
4490
4587
  */
4491
4588
  class ArrayComparator {
4492
4589
  options;
@@ -4514,6 +4611,8 @@ class ArrayComparator {
4514
4611
  }
4515
4612
  /**
4516
4613
  * Watched query comparator that always reports changed result sets.
4614
+ *
4615
+ * @public
4517
4616
  */
4518
4617
  const FalsyComparator = {
4519
4618
  checkEquality: () => false // Default comparator that always returns false
@@ -4721,6 +4820,8 @@ class AbstractQueryProcessor extends MetaBaseObserver {
4721
4820
  /**
4722
4821
  * An empty differential result set.
4723
4822
  * This is used as the initial state for differential incrementally watched queries.
4823
+ *
4824
+ * @internal
4724
4825
  */
4725
4826
  const EMPTY_DIFFERENTIAL = {
4726
4827
  added: [],
@@ -4733,6 +4834,8 @@ const EMPTY_DIFFERENTIAL = {
4733
4834
  * Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
4734
4835
  * It keys items by their `id` property if available, alternatively it uses JSON stringification
4735
4836
  * of the entire item for the key and comparison.
4837
+ *
4838
+ * @internal
4736
4839
  */
4737
4840
  const DEFAULT_ROW_COMPARATOR = {
4738
4841
  keyBy: (item) => {
@@ -5013,6 +5116,8 @@ class CustomQuery {
5013
5116
 
5014
5117
  /**
5015
5118
  * Tests if the input is a {@link SQLOpenOptions}
5119
+ *
5120
+ * @internal
5016
5121
  */
5017
5122
  const isSQLOpenOptions = (test) => {
5018
5123
  // typeof null is `object`, but you cannot use the `in` operator on `null.
@@ -5020,17 +5125,24 @@ const isSQLOpenOptions = (test) => {
5020
5125
  };
5021
5126
  /**
5022
5127
  * Tests if input is a {@link SQLOpenFactory}
5128
+ *
5129
+ * @internal
5023
5130
  */
5024
5131
  const isSQLOpenFactory = (test) => {
5025
5132
  return typeof test?.openDB == 'function';
5026
5133
  };
5027
5134
  /**
5028
5135
  * Tests if input is a {@link DBAdapter}
5136
+ *
5137
+ * @internal
5029
5138
  */
5030
5139
  const isDBAdapter = (test) => {
5031
5140
  return typeof test?.writeTransaction == 'function';
5032
5141
  };
5033
5142
 
5143
+ /**
5144
+ * @internal
5145
+ */
5034
5146
  var PSInternalTable;
5035
5147
  (function (PSInternalTable) {
5036
5148
  PSInternalTable["DATA"] = "ps_data";
@@ -5039,6 +5151,9 @@ var PSInternalTable;
5039
5151
  PSInternalTable["OPLOG"] = "ps_oplog";
5040
5152
  PSInternalTable["UNTYPED"] = "ps_untyped";
5041
5153
  })(PSInternalTable || (PSInternalTable = {}));
5154
+ /**
5155
+ * @internal
5156
+ */
5042
5157
  var PowerSyncControlCommand;
5043
5158
  (function (PowerSyncControlCommand) {
5044
5159
  PowerSyncControlCommand["PROCESS_TEXT_LINE"] = "line_text";
@@ -5056,6 +5171,8 @@ var PowerSyncControlCommand;
5056
5171
 
5057
5172
  /**
5058
5173
  * A batch of client-side changes.
5174
+ *
5175
+ * @public
5059
5176
  */
5060
5177
  class CrudBatch {
5061
5178
  crud;
@@ -5082,6 +5199,8 @@ class CrudBatch {
5082
5199
 
5083
5200
  /**
5084
5201
  * Type of local change.
5202
+ *
5203
+ * @public
5085
5204
  */
5086
5205
  var UpdateType;
5087
5206
  (function (UpdateType) {
@@ -5094,6 +5213,8 @@ var UpdateType;
5094
5213
  })(UpdateType || (UpdateType = {}));
5095
5214
  /**
5096
5215
  * A single client-side change.
5216
+ *
5217
+ * @public
5097
5218
  */
5098
5219
  class CrudEntry {
5099
5220
  /**
@@ -5190,6 +5311,9 @@ class CrudEntry {
5190
5311
  }
5191
5312
  }
5192
5313
 
5314
+ /**
5315
+ * @public
5316
+ */
5193
5317
  class CrudTransaction extends CrudBatch {
5194
5318
  crud;
5195
5319
  complete;
@@ -5218,6 +5342,8 @@ class CrudTransaction extends CrudBatch {
5218
5342
  * Calls to Abortcontroller.abort(reason: any) will result in the
5219
5343
  * `reason` being thrown. This is not necessarily an error,
5220
5344
  * but extends error for better logging purposes.
5345
+ *
5346
+ * @internal
5221
5347
  */
5222
5348
  class AbortOperation extends Error {
5223
5349
  reason;
@@ -12388,7 +12514,7 @@ function requireDist () {
12388
12514
 
12389
12515
  var distExports = requireDist();
12390
12516
 
12391
- var version = "1.53.1";
12517
+ var version = "1.54.0";
12392
12518
  var PACKAGE = {
12393
12519
  version: version};
12394
12520
 
@@ -12518,7 +12644,8 @@ class WebsocketClientTransport {
12518
12644
  removeListeners();
12519
12645
  resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
12520
12646
  };
12521
- const errorListener = (ev) => {
12647
+ const errorListener = (event) => {
12648
+ const ev = event;
12522
12649
  removeListeners();
12523
12650
  // We add a default error in that case.
12524
12651
  if (ev.error != null) {
@@ -12761,7 +12888,13 @@ const SOCKET_TIMEOUT_MS = 30_000;
12761
12888
  // If there is a backlog of messages (for example on slow connections), keepalive messages could be delayed
12762
12889
  // significantly. Therefore this is longer than the socket timeout.
12763
12890
  const KEEP_ALIVE_LIFETIME_MS = 90_000;
12891
+ /**
12892
+ * @internal
12893
+ */
12764
12894
  const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
12895
+ /**
12896
+ * @public
12897
+ */
12765
12898
  var FetchStrategy;
12766
12899
  (function (FetchStrategy) {
12767
12900
  /**
@@ -12780,12 +12913,17 @@ var FetchStrategy;
12780
12913
  * The class wrapper is used to distinguish the fetchImplementation
12781
12914
  * option in [AbstractRemoteOptions] from the general fetch method
12782
12915
  * which is typeof "function"
12916
+ *
12917
+ * @internal
12783
12918
  */
12784
12919
  class FetchImplementationProvider {
12785
12920
  getFetch() {
12786
12921
  throw new Error('Unspecified fetch implementation');
12787
12922
  }
12788
12923
  }
12924
+ /**
12925
+ * @internal
12926
+ */
12789
12927
  const DEFAULT_REMOTE_OPTIONS = {
12790
12928
  socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
12791
12929
  return match === 'https://' ? 'wss://' : 'ws://';
@@ -12793,6 +12931,9 @@ const DEFAULT_REMOTE_OPTIONS = {
12793
12931
  fetchImplementation: new FetchImplementationProvider(),
12794
12932
  fetchOptions: {}
12795
12933
  };
12934
+ /**
12935
+ * @internal
12936
+ */
12796
12937
  class AbstractRemote {
12797
12938
  connector;
12798
12939
  logger;
@@ -13202,7 +13343,7 @@ class AbstractRemote {
13202
13343
  * Posts a `/sync/stream` request.
13203
13344
  *
13204
13345
  * Depending on the `Content-Type` of the response, this returns strings for sync lines or encoded BSON documents as
13205
- * {@link Uint8Array}s.
13346
+ * `Uint8Array`s.
13206
13347
  */
13207
13348
  async fetchStream(options) {
13208
13349
  const { isBson, stream } = await this.fetchStreamRaw(options);
@@ -13244,16 +13385,26 @@ function isInterruptingInstruction(instruction) {
13244
13385
  return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
13245
13386
  }
13246
13387
 
13388
+ /**
13389
+ * @internal
13390
+ */
13247
13391
  var LockType;
13248
13392
  (function (LockType) {
13249
13393
  LockType["CRUD"] = "crud";
13250
13394
  LockType["SYNC"] = "sync";
13251
13395
  })(LockType || (LockType = {}));
13396
+ /**
13397
+ * @public
13398
+ */
13252
13399
  var SyncStreamConnectionMethod;
13253
13400
  (function (SyncStreamConnectionMethod) {
13254
13401
  SyncStreamConnectionMethod["HTTP"] = "http";
13255
13402
  SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
13256
13403
  })(SyncStreamConnectionMethod || (SyncStreamConnectionMethod = {}));
13404
+ /**
13405
+ * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
13406
+ * @public
13407
+ */
13257
13408
  var SyncClientImplementation;
13258
13409
  (function (SyncClientImplementation) {
13259
13410
  /**
@@ -13265,8 +13416,8 @@ var SyncClientImplementation;
13265
13416
  * ## Compatibility warning
13266
13417
  *
13267
13418
  * The Rust sync client stores sync data in a format that is slightly different than the one used
13268
- * by the old JavaScript client. When adopting the {@link RUST} client on existing databases, the PowerSync SDK will
13269
- * migrate the format automatically.
13419
+ * by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
13420
+ * the PowerSync SDK will migrate the format automatically.
13270
13421
  *
13271
13422
  * SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
13272
13423
  * implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
@@ -13276,14 +13427,29 @@ var SyncClientImplementation;
13276
13427
  })(SyncClientImplementation || (SyncClientImplementation = {}));
13277
13428
  /**
13278
13429
  * The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
13430
+ *
13431
+ * @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
13432
+ * @public
13279
13433
  */
13280
13434
  const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
13435
+ /**
13436
+ * @internal
13437
+ */
13281
13438
  const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
13439
+ /**
13440
+ * @internal
13441
+ */
13282
13442
  const DEFAULT_RETRY_DELAY_MS = 5000;
13443
+ /**
13444
+ * @internal
13445
+ */
13283
13446
  const DEFAULT_STREAMING_SYNC_OPTIONS = {
13284
13447
  retryDelayMs: DEFAULT_RETRY_DELAY_MS,
13285
13448
  crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
13286
13449
  };
13450
+ /**
13451
+ * @internal
13452
+ */
13287
13453
  const DEFAULT_STREAM_CONNECTION_OPTIONS = {
13288
13454
  appMetadata: {},
13289
13455
  connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
@@ -13293,22 +13459,21 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
13293
13459
  serializedSchema: undefined,
13294
13460
  includeDefaultStreams: true
13295
13461
  };
13462
+ /**
13463
+ * @internal
13464
+ */
13296
13465
  class AbstractStreamingSyncImplementation extends BaseObserver {
13297
13466
  options;
13298
13467
  abortController;
13299
- // In rare cases, mostly for tests, uploads can be triggered without being properly connected.
13300
- // This allows ensuring that all upload processes can be aborted.
13301
- uploadAbortController;
13302
13468
  crudUpdateListener;
13303
13469
  streamingSyncPromise;
13304
13470
  logger;
13305
13471
  activeStreams;
13306
13472
  connectionMayHaveChanged = false;
13307
- isUploadingCrud = false;
13473
+ crudUploadNotifier = asyncNotifier();
13308
13474
  notifyCompletedUploads;
13309
13475
  handleActiveStreamsChange;
13310
13476
  syncStatus;
13311
- triggerCrudUpload;
13312
13477
  constructor(options) {
13313
13478
  super();
13314
13479
  this.options = options;
@@ -13324,16 +13489,9 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
13324
13489
  }
13325
13490
  });
13326
13491
  this.abortController = null;
13327
- this.triggerCrudUpload = throttleLeadingTrailing(() => {
13328
- if (!this.syncStatus.connected || this.isUploadingCrud) {
13329
- return;
13330
- }
13331
- this.isUploadingCrud = true;
13332
- this._uploadAllCrud().finally(() => {
13333
- this.notifyCompletedUploads?.();
13334
- this.isUploadingCrud = false;
13335
- });
13336
- }, this.options.crudUploadThrottleMs);
13492
+ }
13493
+ triggerCrudUpload() {
13494
+ this.crudUploadNotifier.notify();
13337
13495
  }
13338
13496
  async waitForReady() { }
13339
13497
  waitForStatus(status) {
@@ -13381,7 +13539,6 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
13381
13539
  super.dispose();
13382
13540
  this.crudUpdateListener?.();
13383
13541
  this.crudUpdateListener = undefined;
13384
- this.uploadAbortController?.abort();
13385
13542
  }
13386
13543
  async getWriteCheckpoint() {
13387
13544
  const clientId = await this.options.adapter.getClientId();
@@ -13391,7 +13548,17 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
13391
13548
  this.logger.debug(`Created write checkpoint: ${checkpoint}`);
13392
13549
  return checkpoint;
13393
13550
  }
13394
- async _uploadAllCrud() {
13551
+ async crudUploadLoop(signal) {
13552
+ while (!signal.aborted) {
13553
+ await Promise.all([
13554
+ // Start the initial CRUD upload on connect. Then, keep polling until we're done.
13555
+ this._uploadAllCrud(signal),
13556
+ this.delayRetry(signal, this.options.crudUploadThrottleMs)
13557
+ ]);
13558
+ await this.crudUploadNotifier.waitForNotification(signal);
13559
+ }
13560
+ }
13561
+ async _uploadAllCrud(signal) {
13395
13562
  return this.obtainLock({
13396
13563
  type: LockType.CRUD,
13397
13564
  callback: async () => {
@@ -13399,12 +13566,7 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
13399
13566
  * Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
13400
13567
  */
13401
13568
  let checkedCrudItem;
13402
- const controller = new AbortController();
13403
- this.uploadAbortController = controller;
13404
- this.abortController?.signal.addEventListener('abort', () => {
13405
- controller.abort();
13406
- }, { once: true });
13407
- while (!controller.signal.aborted) {
13569
+ while (!signal.aborted) {
13408
13570
  try {
13409
13571
  /**
13410
13572
  * This is the first item in the FIFO CRUD queue.
@@ -13434,7 +13596,10 @@ The next upload iteration will be delayed.`);
13434
13596
  else {
13435
13597
  // Uploading is completed
13436
13598
  const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
13437
- if (neededUpdate == false && checkedCrudItem != null) {
13599
+ if (neededUpdate) {
13600
+ this.notifyCompletedUploads?.();
13601
+ }
13602
+ else if (checkedCrudItem != null) {
13438
13603
  // Only log this if there was something to upload
13439
13604
  this.logger.debug('Upload complete, no write checkpoint needed.');
13440
13605
  }
@@ -13449,7 +13614,7 @@ The next upload iteration will be delayed.`);
13449
13614
  uploadError: ex
13450
13615
  }
13451
13616
  });
13452
- await this.delayRetry(controller.signal);
13617
+ await this.delayRetry(signal);
13453
13618
  if (!this.isConnected) {
13454
13619
  // Exit the upload loop if the sync stream is no longer connected
13455
13620
  break;
@@ -13464,7 +13629,6 @@ The next upload iteration will be delayed.`);
13464
13629
  });
13465
13630
  }
13466
13631
  }
13467
- this.uploadAbortController = undefined;
13468
13632
  }
13469
13633
  });
13470
13634
  }
@@ -13474,7 +13638,10 @@ The next upload iteration will be delayed.`);
13474
13638
  }
13475
13639
  const controller = new AbortController();
13476
13640
  this.abortController = controller;
13477
- this.streamingSyncPromise = this.streamingSync(this.abortController.signal, options);
13641
+ this.streamingSyncPromise = Promise.all([
13642
+ this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
13643
+ this.streamingSync(controller.signal, options)
13644
+ ]);
13478
13645
  // Return a promise that resolves when the connection status is updated to indicate that we're connected.
13479
13646
  return new Promise((resolve) => {
13480
13647
  const disposer = this.registerListener({
@@ -13512,14 +13679,7 @@ The next upload iteration will be delayed.`);
13512
13679
  this.abortController = null;
13513
13680
  this.updateSyncStatus({ connected: false, connecting: false });
13514
13681
  }
13515
- /**
13516
- * @deprecated use [connect instead]
13517
- */
13518
13682
  async streamingSync(signal, options) {
13519
- if (!signal) {
13520
- this.abortController = new AbortController();
13521
- signal = this.abortController.signal;
13522
- }
13523
13683
  /**
13524
13684
  * Listen for CRUD updates and trigger upstream uploads
13525
13685
  */
@@ -13625,7 +13785,7 @@ The next upload iteration will be delayed.`);
13625
13785
  this.handleActiveStreamsChange?.();
13626
13786
  }
13627
13787
  /**
13628
- * Older versions of the JS SDK used to encode subkeys as JSON in {@link OplogEntry.toJSON}.
13788
+ * Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
13629
13789
  * Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
13630
13790
  * While this is not a problem as long as it's done consistently, it causes issues when a database
13631
13791
  * created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
@@ -13635,7 +13795,7 @@ The next upload iteration will be delayed.`);
13635
13795
  * migration is only triggered when necessary (for now). The function returns whether the new format
13636
13796
  * should be used, so that the JS SDK is able to write to updated databases.
13637
13797
  *
13638
- * @param requireFixedKeyFormat Whether we require the new format or also support the old one.
13798
+ * @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
13639
13799
  * The Rust client requires the new subkey format.
13640
13800
  * @returns Whether the database is now using the new, fixed subkey format.
13641
13801
  */
@@ -13893,14 +14053,13 @@ The next upload iteration will be delayed.`);
13893
14053
  // trigger this for all updates
13894
14054
  this.iterateListeners((cb) => cb.statusUpdated?.(options));
13895
14055
  }
13896
- async delayRetry(signal) {
14056
+ async delayRetry(signal, delay = this.options.retryDelayMs) {
13897
14057
  return new Promise((resolve) => {
13898
14058
  if (signal?.aborted) {
13899
14059
  // If the signal is already aborted, resolve immediately
13900
14060
  resolve();
13901
14061
  return;
13902
14062
  }
13903
- const { retryDelayMs } = this.options;
13904
14063
  let timeoutId;
13905
14064
  const endDelay = () => {
13906
14065
  resolve();
@@ -13911,7 +14070,7 @@ The next upload iteration will be delayed.`);
13911
14070
  signal?.removeEventListener('abort', endDelay);
13912
14071
  };
13913
14072
  signal?.addEventListener('abort', endDelay, { once: true });
13914
- timeoutId = setTimeout(endDelay, retryDelayMs);
14073
+ timeoutId = setTimeout(endDelay, delay);
13915
14074
  });
13916
14075
  }
13917
14076
  updateSubscriptions(subscriptions) {
@@ -13943,7 +14102,8 @@ const MEMORY_TRIGGER_CLAIM_MANAGER = {
13943
14102
 
13944
14103
  /**
13945
14104
  * SQLite operations to track changes for with {@link TriggerManager}
13946
- * @experimental
14105
+ *
14106
+ * @experimental @alpha
13947
14107
  */
13948
14108
  var DiffTriggerOperation;
13949
14109
  (function (DiffTriggerOperation) {
@@ -14005,8 +14165,8 @@ class TriggerManagerImpl {
14005
14165
  get db() {
14006
14166
  return this.options.db;
14007
14167
  }
14008
- async getUUID() {
14009
- const { id: uuid } = await this.db.get(/* sql */ `
14168
+ async getUUID(ctx) {
14169
+ const { id: uuid } = await (ctx ?? this.db).get(/* sql */ `
14010
14170
  SELECT
14011
14171
  uuid () as id
14012
14172
  `);
@@ -14119,7 +14279,7 @@ class TriggerManagerImpl {
14119
14279
  const replicatedColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
14120
14280
  const internalSource = sourceDefinition.internalName;
14121
14281
  const triggerIds = [];
14122
- const id = await this.getUUID();
14282
+ const id = await this.getUUID(setupContext);
14123
14283
  const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
14124
14284
  /**
14125
14285
  * We default to replicating all columns if no columns array is provided.
@@ -14359,18 +14519,29 @@ const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
14359
14519
  const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
14360
14520
  clearLocal: true
14361
14521
  };
14522
+ /**
14523
+ * @internal
14524
+ */
14362
14525
  const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
14363
14526
  disconnect: true
14364
14527
  };
14528
+ /**
14529
+ * @internal
14530
+ */
14365
14531
  const DEFAULT_POWERSYNC_DB_OPTIONS = {
14366
14532
  retryDelayMs: 5000,
14367
14533
  crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
14368
14534
  };
14535
+ /**
14536
+ * @internal
14537
+ */
14369
14538
  const DEFAULT_CRUD_BATCH_LIMIT = 100;
14370
14539
  /**
14371
14540
  * Requesting nested or recursive locks can block the application in some circumstances.
14372
14541
  * This default lock timeout will act as a failsafe to throw an error if a lock cannot
14373
14542
  * be obtained.
14543
+ *
14544
+ * @internal
14374
14545
  */
14375
14546
  const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
14376
14547
  /**
@@ -14380,6 +14551,9 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
14380
14551
  const isPowerSyncDatabaseOptionsWithSettings = (test) => {
14381
14552
  return typeof test == 'object' && isSQLOpenOptions(test.database);
14382
14553
  };
14554
+ /**
14555
+ * @public
14556
+ */
14383
14557
  class AbstractPowerSyncDatabase extends BaseObserver {
14384
14558
  options;
14385
14559
  /**
@@ -14537,7 +14711,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
14537
14711
  /**
14538
14712
  * Wait for the first sync operation to complete.
14539
14713
  *
14540
- * @param request Either an abort signal (after which the promise will complete regardless of
14714
+ * @param request - Either an abort signal (after which the promise will complete regardless of
14541
14715
  * whether a full sync was completed) or an object providing an abort signal and a priority target.
14542
14716
  * When a priority target is set, the promise may complete when all buckets with the given (or higher)
14543
14717
  * priorities have been synchronized. This can be earlier than a complete sync.
@@ -14692,7 +14866,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
14692
14866
  /**
14693
14867
  * Close the sync connection.
14694
14868
  *
14695
- * Use {@link connect} to connect again.
14869
+ * Use {@link AbstractPowerSyncDatabase.connect} to connect again.
14696
14870
  */
14697
14871
  async disconnect() {
14698
14872
  return this.connectionManager.disconnect();
@@ -14719,8 +14893,8 @@ class AbstractPowerSyncDatabase extends BaseObserver {
14719
14893
  /**
14720
14894
  * Create a sync stream to query its status or to subscribe to it.
14721
14895
  *
14722
- * @param name The name of the stream to subscribe to.
14723
- * @param params Optional parameters for the stream subscription.
14896
+ * @param name - The name of the stream to subscribe to.
14897
+ * @param params - Optional parameters for the stream subscription.
14724
14898
  * @returns A {@link SyncStream} instance that can be subscribed to.
14725
14899
  * @experimental Sync streams are currently in alpha.
14726
14900
  */
@@ -14778,14 +14952,14 @@ class AbstractPowerSyncDatabase extends BaseObserver {
14778
14952
  * Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
14779
14953
  * requesting the next batch.
14780
14954
  *
14781
- * Use {@link limit} to specify the maximum number of updates to return in a single
14955
+ * Use the `limit` parameter to specify the maximum number of updates to return in a single
14782
14956
  * batch.
14783
14957
  *
14784
14958
  * This method does include transaction ids in the result, but does not group
14785
14959
  * data by transaction. One batch may contain data from multiple transactions,
14786
14960
  * and a single transaction may be split over multiple batches.
14787
14961
  *
14788
- * @param limit Maximum number of CRUD entries to include in the batch
14962
+ * @param limit - Maximum number of CRUD entries to include in the batch
14789
14963
  * @returns A batch of CRUD operations to upload, or null if there are none
14790
14964
  */
14791
14965
  async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
@@ -14812,7 +14986,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
14812
14986
  * Once the data have been successfully uploaded, call {@link CrudTransaction.complete} before
14813
14987
  * requesting the next transaction.
14814
14988
  *
14815
- * Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
14989
+ * Unlike {@link AbstractPowerSyncDatabase.getCrudBatch}, this only returns data from a single transaction at a time.
14816
14990
  * All data for the transaction is loaded into memory.
14817
14991
  *
14818
14992
  * @returns A transaction of CRUD operations to upload, or null if there are none
@@ -14827,7 +15001,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
14827
15001
  * This is typically used from the {@link PowerSyncBackendConnector.uploadData} callback. Each entry emitted by the
14828
15002
  * returned iterator is a full transaction containing all local writes made while that transaction was active.
14829
15003
  *
14830
- * Unlike {@link getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
15004
+ * Unlike {@link AbstractPowerSyncDatabase.getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
14831
15005
  * {@link CrudTransaction.complete}d yet, this iterator can be used to receive multiple transactions. Calling
14832
15006
  * {@link CrudTransaction.complete} will mark that and all prior transactions emitted by the iterator as completed.
14833
15007
  *
@@ -14921,8 +15095,8 @@ SELECT * FROM crud_entries;
14921
15095
  * the returned result's `rowsAffected` may be `0` for successful `UPDATE` and `DELETE` statements.
14922
15096
  * Use a `RETURNING` clause and inspect `result.rows` when you need to confirm which rows changed.
14923
15097
  *
14924
- * @param sql The SQL query to execute
14925
- * @param parameters Optional array of parameters to bind to the query
15098
+ * @param sql - The SQL query to execute
15099
+ * @param parameters - Optional array of parameters to bind to the query
14926
15100
  * @returns The query result as an object with structured key-value pairs
14927
15101
  */
14928
15102
  async execute(sql, parameters) {
@@ -14932,8 +15106,8 @@ SELECT * FROM crud_entries;
14932
15106
  * Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
14933
15107
  * This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
14934
15108
  *
14935
- * @param sql The SQL query to execute
14936
- * @param parameters Optional array of parameters to bind to the query
15109
+ * @param sql - The SQL query to execute
15110
+ * @param parameters - Optional array of parameters to bind to the query
14937
15111
  * @returns The raw query result from the underlying database as a nested array of raw values, where each row is
14938
15112
  * represented as an array of column values without field names.
14939
15113
  */
@@ -14946,8 +15120,8 @@ SELECT * FROM crud_entries;
14946
15120
  * and optionally return results.
14947
15121
  * This is faster than executing separately with each parameter set.
14948
15122
  *
14949
- * @param sql The SQL query to execute
14950
- * @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
15123
+ * @param sql - The SQL query to execute
15124
+ * @param parameters - Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
14951
15125
  * @returns The query result
14952
15126
  */
14953
15127
  async executeBatch(sql, parameters) {
@@ -14957,8 +15131,8 @@ SELECT * FROM crud_entries;
14957
15131
  /**
14958
15132
  * Execute a read-only query and return results.
14959
15133
  *
14960
- * @param sql The SQL query to execute
14961
- * @param parameters Optional array of parameters to bind to the query
15134
+ * @param sql - The SQL query to execute
15135
+ * @param parameters - Optional array of parameters to bind to the query
14962
15136
  * @returns An array of results
14963
15137
  */
14964
15138
  async getAll(sql, parameters) {
@@ -14968,8 +15142,8 @@ SELECT * FROM crud_entries;
14968
15142
  /**
14969
15143
  * Execute a read-only query and return the first result, or null if the ResultSet is empty.
14970
15144
  *
14971
- * @param sql The SQL query to execute
14972
- * @param parameters Optional array of parameters to bind to the query
15145
+ * @param sql - The SQL query to execute
15146
+ * @param parameters - Optional array of parameters to bind to the query
14973
15147
  * @returns The first result if found, or null if no results are returned
14974
15148
  */
14975
15149
  async getOptional(sql, parameters) {
@@ -14979,8 +15153,8 @@ SELECT * FROM crud_entries;
14979
15153
  /**
14980
15154
  * Execute a read-only query and return the first result, error if the ResultSet is empty.
14981
15155
  *
14982
- * @param sql The SQL query to execute
14983
- * @param parameters Optional array of parameters to bind to the query
15156
+ * @param sql - The SQL query to execute
15157
+ * @param parameters - Optional array of parameters to bind to the query
14984
15158
  * @returns The first result matching the query
14985
15159
  * @throws Error if no rows are returned
14986
15160
  */
@@ -14990,7 +15164,7 @@ SELECT * FROM crud_entries;
14990
15164
  }
14991
15165
  /**
14992
15166
  * Takes a read lock, without starting a transaction.
14993
- * In most cases, {@link readTransaction} should be used instead.
15167
+ * In most cases, {@link AbstractPowerSyncDatabase.readTransaction} should be used instead.
14994
15168
  */
14995
15169
  async readLock(callback) {
14996
15170
  await this.waitForReady();
@@ -14998,7 +15172,7 @@ SELECT * FROM crud_entries;
14998
15172
  }
14999
15173
  /**
15000
15174
  * Takes a global lock, without starting a transaction.
15001
- * In most cases, {@link writeTransaction} should be used instead.
15175
+ * In most cases, {@link AbstractPowerSyncDatabase.writeTransaction} should be used instead.
15002
15176
  */
15003
15177
  async writeLock(callback) {
15004
15178
  await this.waitForReady();
@@ -15009,8 +15183,8 @@ SELECT * FROM crud_entries;
15009
15183
  * Read transactions can run concurrently to a write transaction.
15010
15184
  * Changes from any write transaction are not visible to read transactions started before it.
15011
15185
  *
15012
- * @param callback Function to execute within the transaction
15013
- * @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
15186
+ * @param callback - Function to execute within the transaction
15187
+ * @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
15014
15188
  * @returns The result of the callback
15015
15189
  * @throws Error if the lock cannot be obtained within the timeout period
15016
15190
  */
@@ -15027,8 +15201,8 @@ SELECT * FROM crud_entries;
15027
15201
  * This takes a global lock - only one write transaction can execute against the database at a time.
15028
15202
  * Statements within the transaction must be done on the provided {@link Transaction} interface.
15029
15203
  *
15030
- * @param callback Function to execute within the transaction
15031
- * @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
15204
+ * @param callback - Function to execute within the transaction
15205
+ * @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
15032
15206
  * @returns The result of the callback
15033
15207
  * @throws Error if the lock cannot be obtained within the timeout period
15034
15208
  */
@@ -15105,15 +15279,15 @@ SELECT * FROM crud_entries;
15105
15279
  }
15106
15280
  /**
15107
15281
  * Execute a read query every time the source tables are modified.
15108
- * Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
15282
+ * Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
15109
15283
  * Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
15110
15284
  *
15111
15285
  * Note that the `onChange` callback member of the handler is required.
15112
15286
  *
15113
- * @param sql The SQL query to execute
15114
- * @param parameters Optional array of parameters to bind to the query
15115
- * @param handler Callbacks for handling results and errors
15116
- * @param options Options for configuring watch behavior
15287
+ * @param sql - The SQL query to execute
15288
+ * @param parameters - Optional array of parameters to bind to the query
15289
+ * @param handler - Callbacks for handling results and errors
15290
+ * @param options - Options for configuring watch behavior
15117
15291
  */
15118
15292
  watchWithCallback(sql, parameters, handler, options) {
15119
15293
  const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
@@ -15126,7 +15300,7 @@ SELECT * FROM crud_entries;
15126
15300
  const watchedQuery = new OnChangeQueryProcessor({
15127
15301
  db: this,
15128
15302
  comparator,
15129
- placeholderData: null,
15303
+ placeholderData: null, // FIXME
15130
15304
  watchOptions: {
15131
15305
  query: {
15132
15306
  compile: () => ({
@@ -15159,12 +15333,12 @@ SELECT * FROM crud_entries;
15159
15333
  }
15160
15334
  /**
15161
15335
  * Execute a read query every time the source tables are modified.
15162
- * Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
15336
+ * Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
15163
15337
  * Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
15164
15338
  *
15165
- * @param sql The SQL query to execute
15166
- * @param parameters Optional array of parameters to bind to the query
15167
- * @param options Options for configuring watch behavior
15339
+ * @param sql - The SQL query to execute
15340
+ * @param parameters - Optional array of parameters to bind to the query
15341
+ * @param options - Options for configuring watch behavior
15168
15342
  * @returns An AsyncIterable that yields QueryResults whenever the data changes
15169
15343
  */
15170
15344
  watchWithAsyncGenerator(sql, parameters, options) {
@@ -15188,9 +15362,9 @@ SELECT * FROM crud_entries;
15188
15362
  * If tables are specified in the options, those are used directly.
15189
15363
  * Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
15190
15364
  *
15191
- * @param sql The SQL query to analyze
15192
- * @param parameters Optional parameters for the SQL query
15193
- * @param options Optional watch options that may contain explicit table list
15365
+ * @param sql - The SQL query to analyze
15366
+ * @param parameters - Optional parameters for the SQL query
15367
+ * @param options - Optional watch options that may contain explicit table list
15194
15368
  * @returns Array of table names that the query depends on
15195
15369
  */
15196
15370
  async resolveTables(sql, parameters, options) {
@@ -15219,13 +15393,13 @@ SELECT * FROM crud_entries;
15219
15393
  /**
15220
15394
  * Invoke the provided callback on any changes to any of the specified tables.
15221
15395
  *
15222
- * This is preferred over {@link watchWithCallback} when multiple queries need to be performed
15396
+ * This is preferred over {@link AbstractPowerSyncDatabase.watchWithCallback} when multiple queries need to be performed
15223
15397
  * together when data is changed.
15224
15398
  *
15225
15399
  * Note that the `onChange` callback member of the handler is required.
15226
15400
  *
15227
- * @param handler Callbacks for handling change events and errors
15228
- * @param options Options for configuring watch behavior
15401
+ * @param handler - Callbacks for handling change events and errors
15402
+ * @param options - Options for configuring watch behavior
15229
15403
  * @returns A dispose function to stop watching for changes
15230
15404
  */
15231
15405
  onChangeWithCallback(handler, options) {
@@ -15268,12 +15442,12 @@ SELECT * FROM crud_entries;
15268
15442
  /**
15269
15443
  * Create a Stream of changes to any of the specified tables.
15270
15444
  *
15271
- * This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be performed
15272
- * together when data is changed.
15445
+ * This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
15446
+ * performed together when data is changed.
15273
15447
  *
15274
15448
  * Note: do not declare this as `async *onChange` as it will not work in React Native.
15275
15449
  *
15276
- * @param options Options for configuring watch behavior
15450
+ * @param options - Options for configuring watch behavior
15277
15451
  * @returns An AsyncIterable that yields change events whenever the specified tables change
15278
15452
  */
15279
15453
  onChangeWithAsyncGenerator(options) {
@@ -15311,15 +15485,15 @@ SELECT * FROM crud_entries;
15311
15485
  changedTables.add(table);
15312
15486
  }
15313
15487
  }
15314
- /**
15315
- * @ignore
15316
- */
15317
15488
  async executeReadOnly(sql, params) {
15318
15489
  await this.waitForReady();
15319
15490
  return this.database.readLock((tx) => tx.execute(sql, params));
15320
15491
  }
15321
15492
  }
15322
15493
 
15494
+ /**
15495
+ * @internal
15496
+ */
15323
15497
  class AbstractPowerSyncDatabaseOpenFactory {
15324
15498
  options;
15325
15499
  constructor(options) {
@@ -15344,6 +15518,9 @@ class AbstractPowerSyncDatabaseOpenFactory {
15344
15518
  }
15345
15519
  }
15346
15520
 
15521
+ /**
15522
+ * @internal
15523
+ */
15347
15524
  function runOnSchemaChange(callback, db, options) {
15348
15525
  const triggerWatchedQuery = () => {
15349
15526
  const abortController = new AbortController();
@@ -15368,6 +15545,9 @@ function runOnSchemaChange(callback, db, options) {
15368
15545
  triggerWatchedQuery();
15369
15546
  }
15370
15547
 
15548
+ /**
15549
+ * @public
15550
+ */
15371
15551
  function compilableQueryWatch(db, query, handler, options) {
15372
15552
  const { onResult, onError = (e) => { } } = handler ?? {};
15373
15553
  if (!onResult) {
@@ -15405,8 +15585,14 @@ function compilableQueryWatch(db, query, handler, options) {
15405
15585
  runOnSchemaChange(watchQuery, db, options);
15406
15586
  }
15407
15587
 
15588
+ /**
15589
+ * @internal
15590
+ */
15408
15591
  const MAX_OP_ID = '9223372036854775807';
15409
15592
 
15593
+ /**
15594
+ * @internal
15595
+ */
15410
15596
  class SqliteBucketStorage extends BaseObserver {
15411
15597
  db;
15412
15598
  logger;
@@ -15567,6 +15753,8 @@ class SqliteBucketStorage extends BaseObserver {
15567
15753
  * Thrown when an underlying database connection is closed.
15568
15754
  * This is particularly relevant when worker connections are marked as closed while
15569
15755
  * operations are still in progress.
15756
+ *
15757
+ * @internal
15570
15758
  */
15571
15759
  class ConnectionClosedError extends Error {
15572
15760
  static NAME = 'ConnectionClosedError';
@@ -15586,6 +15774,8 @@ class ConnectionClosedError extends Error {
15586
15774
 
15587
15775
  /**
15588
15776
  * A schema is a collection of tables. It is used to define the structure of a database.
15777
+ *
15778
+ * @public
15589
15779
  */
15590
15780
  class Schema {
15591
15781
  /*
@@ -15622,7 +15812,7 @@ class Schema {
15622
15812
  * Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
15623
15813
  * using client-side table and column constraints.
15624
15814
  *
15625
- * @param tables An object of (table name, raw table definition) entries.
15815
+ * @param tables - An object of (table name, raw table definition) entries.
15626
15816
  */
15627
15817
  withRawTables(tables) {
15628
15818
  for (const [name, rawTableDefinition] of Object.entries(tables)) {
@@ -15668,6 +15858,8 @@ class Schema {
15668
15858
  Generate a new table from the columns and indexes
15669
15859
  @deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
15670
15860
  This will be removed in the next major release.
15861
+
15862
+ @public
15671
15863
  */
15672
15864
  class TableV2 extends Table {
15673
15865
  }
@@ -15678,6 +15870,8 @@ function sanitizeString(input) {
15678
15870
  /**
15679
15871
  * Helper function for sanitizing UUID input strings.
15680
15872
  * Typically used with {@link sanitizeSQL}.
15873
+ *
15874
+ * @alpha
15681
15875
  */
15682
15876
  function sanitizeUUID(uuid) {
15683
15877
  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;
@@ -15714,6 +15908,8 @@ function sanitizeUUID(uuid) {
15714
15908
  * // Incorrect:
15715
15909
  * sanitizeSQL`New.id = '${myID}'` // Produces double quotes: New.id = ''O''Reilly''
15716
15910
  * ```
15911
+ *
15912
+ * @alpha
15717
15913
  */
15718
15914
  function sanitizeSQL(strings, ...values) {
15719
15915
  let result = '';
@@ -15743,6 +15939,8 @@ function sanitizeSQL(strings, ...values) {
15743
15939
 
15744
15940
  /**
15745
15941
  * Performs a {@link AbstractPowerSyncDatabase.getAll} operation for a watched query.
15942
+ *
15943
+ * @public
15746
15944
  */
15747
15945
  class GetAllQuery {
15748
15946
  options;
@@ -15767,6 +15965,9 @@ class GetAllQuery {
15767
15965
  }
15768
15966
 
15769
15967
  const TypedLogger = Logger;
15968
+ /**
15969
+ * @public
15970
+ */
15770
15971
  const LogLevel = {
15771
15972
  TRACE: TypedLogger.TRACE,
15772
15973
  DEBUG: TypedLogger.DEBUG,
@@ -15783,6 +15984,7 @@ const LogLevel = {
15783
15984
  * across all loggers created with `createLogger`. Adjusting settings on this
15784
15985
  * base logger affects all loggers derived from it unless explicitly overridden.
15785
15986
  *
15987
+ * @public
15786
15988
  */
15787
15989
  function createBaseLogger() {
15788
15990
  return Logger;
@@ -15793,6 +15995,8 @@ function createBaseLogger() {
15793
15995
  * Named loggers allow specific modules or areas of your application to have
15794
15996
  * their own logging levels and behaviors. These loggers inherit configuration
15795
15997
  * from the base logger by default but can override settings independently.
15998
+ *
15999
+ * @public
15796
16000
  */
15797
16001
  function createLogger(name, options = {}) {
15798
16002
  const logger = Logger.get(name);
@@ -15802,6 +16006,9 @@ function createLogger(name, options = {}) {
15802
16006
  return logger;
15803
16007
  }
15804
16008
 
16009
+ /**
16010
+ * @internal
16011
+ */
15805
16012
  const parseQuery = (query, parameters) => {
15806
16013
  let sqlStatement;
15807
16014
  if (typeof query == 'string') {