@powersync/web 2.5.0 → 2.6.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 (82) hide show
  1. package/dist/index.react_native_web.js +80 -21
  2. package/dist/index.react_native_web.js.map +1 -1
  3. package/dist/worker/AccessHandlePoolVFS-Do8lc5Ck.js +2 -0
  4. package/dist/worker/AccessHandlePoolVFS-Do8lc5Ck.js.map +1 -0
  5. package/dist/worker/FacadeVFS-d1ZDvud7.js.map +1 -1
  6. package/dist/worker/IDBBatchAtomicVFS-CDb4d4wN.js +2 -0
  7. package/dist/worker/IDBBatchAtomicVFS-CDb4d4wN.js.map +1 -0
  8. package/dist/worker/MemoryVFS-DVJL5F8j.js.map +1 -1
  9. package/dist/worker/OPFSCoopSyncVFS-CDNBPKfy.js +2 -0
  10. package/dist/worker/OPFSCoopSyncVFS-CDNBPKfy.js.map +1 -0
  11. package/dist/worker/OPFSWriteAheadVFS-V9yBrLWl.js +2 -0
  12. package/dist/worker/OPFSWriteAheadVFS-V9yBrLWl.js.map +1 -0
  13. package/dist/worker/assets/mc-wa-sqlite-Cq3AePIW.wasm +0 -0
  14. package/dist/worker/assets/{mc-wa-sqlite-async-DdlQmAUV.wasm → mc-wa-sqlite-async-aRcIGeKj.wasm} +0 -0
  15. package/dist/worker/assets/wa-sqlite-CePoIZt_.wasm +0 -0
  16. package/dist/worker/assets/wa-sqlite-async-B3FiUnt1.wasm +0 -0
  17. package/dist/worker/{mc-wa-sqlite-PubB6u8c.js → mc-wa-sqlite-DRR2R_uj.js} +2 -2
  18. package/dist/worker/mc-wa-sqlite-DRR2R_uj.js.map +1 -0
  19. package/dist/worker/{mc-wa-sqlite-async-DJG_ANKO.js → mc-wa-sqlite-async-C40l6kIz.js} +2 -2
  20. package/dist/worker/mc-wa-sqlite-async-C40l6kIz.js.map +1 -0
  21. package/dist/worker/{wa-sqlite-D1_4mGs8.js → wa-sqlite-_a9CU4MM.js} +2 -2
  22. package/dist/worker/wa-sqlite-_a9CU4MM.js.map +1 -0
  23. package/dist/worker/wa-sqlite-async-Da0d0GJg.js +2 -0
  24. package/dist/worker/wa-sqlite-async-Da0d0GJg.js.map +1 -0
  25. package/dist/worker/{websockets-Q8W_lerF.js → websockets-Dx9ExO-W.js} +2 -2
  26. package/dist/worker/{websockets-Q8W_lerF.js.map → websockets-Dx9ExO-W.js.map} +1 -1
  27. package/dist/worker/worker.js +1 -1
  28. package/dist/worker/worker.js.map +1 -1
  29. package/lib/db/PowerSyncDatabase.d.ts +4 -3
  30. package/lib/db/PowerSyncDatabase.js +3 -0
  31. package/lib/db/PowerSyncDatabase.js.map +1 -1
  32. package/lib/db/adapters/options.d.ts +30 -0
  33. package/lib/db/adapters/options.js.map +1 -1
  34. package/lib/db/adapters/resolveAndValidateOptions.js +6 -1
  35. package/lib/db/adapters/resolveAndValidateOptions.js.map +1 -1
  36. package/lib/db/adapters/wa-sqlite/RawSqliteConnection.d.ts +5 -0
  37. package/lib/db/adapters/wa-sqlite/RawSqliteConnection.js +7 -2
  38. package/lib/db/adapters/wa-sqlite/RawSqliteConnection.js.map +1 -1
  39. package/lib/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +4 -2
  40. package/lib/db/adapters/wa-sqlite/WASQLiteOpenFactory.js.map +1 -1
  41. package/lib/db/adapters/wa-sqlite/vfs.d.ts +24 -1
  42. package/lib/db/adapters/wa-sqlite/vfs.js +33 -4
  43. package/lib/db/adapters/wa-sqlite/vfs.js.map +1 -1
  44. package/lib/db/sync/SharedWebStreamingSyncImplementation.d.ts +1 -0
  45. package/lib/db/sync/SharedWebStreamingSyncImplementation.js +28 -11
  46. package/lib/db/sync/SharedWebStreamingSyncImplementation.js.map +1 -1
  47. package/lib/db/sync/WebRemote.d.ts +2 -3
  48. package/lib/db/sync/WebRemote.js +0 -2
  49. package/lib/db/sync/WebRemote.js.map +1 -1
  50. package/lib/worker/sync/SharedSyncImplementation.d.ts +9 -2
  51. package/lib/worker/sync/SharedSyncImplementation.js +66 -37
  52. package/lib/worker/sync/SharedSyncImplementation.js.map +1 -1
  53. package/lib/worker/sync/WorkerClient.d.ts +3 -3
  54. package/lib/worker/sync/WorkerClient.js +3 -3
  55. package/lib/worker/sync/WorkerClient.js.map +1 -1
  56. package/package.json +6 -6
  57. package/src/db/PowerSyncDatabase.ts +6 -3
  58. package/src/db/adapters/options.ts +32 -0
  59. package/src/db/adapters/resolveAndValidateOptions.ts +9 -1
  60. package/src/db/adapters/wa-sqlite/RawSqliteConnection.ts +12 -2
  61. package/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts +6 -2
  62. package/src/db/adapters/wa-sqlite/vfs.ts +51 -4
  63. package/src/db/sync/SharedWebStreamingSyncImplementation.ts +34 -15
  64. package/src/db/sync/WebRemote.ts +2 -5
  65. package/src/worker/sync/SharedSyncImplementation.ts +89 -48
  66. package/src/worker/sync/WorkerClient.ts +4 -3
  67. package/dist/worker/AccessHandlePoolVFS-B9CKqcQ3.js +0 -2
  68. package/dist/worker/AccessHandlePoolVFS-B9CKqcQ3.js.map +0 -1
  69. package/dist/worker/IDBBatchAtomicVFS-BfRzbRoV.js +0 -2
  70. package/dist/worker/IDBBatchAtomicVFS-BfRzbRoV.js.map +0 -1
  71. package/dist/worker/OPFSCoopSyncVFS-BxXAVpVQ.js +0 -2
  72. package/dist/worker/OPFSCoopSyncVFS-BxXAVpVQ.js.map +0 -1
  73. package/dist/worker/OPFSWriteAheadVFS-DuqXaOct.js +0 -2
  74. package/dist/worker/OPFSWriteAheadVFS-DuqXaOct.js.map +0 -1
  75. package/dist/worker/assets/mc-wa-sqlite-BHScYYZw.wasm +0 -0
  76. package/dist/worker/assets/wa-sqlite-CIxTmRHQ.wasm +0 -0
  77. package/dist/worker/assets/wa-sqlite-async-zc68fxQq.wasm +0 -0
  78. package/dist/worker/mc-wa-sqlite-PubB6u8c.js.map +0 -1
  79. package/dist/worker/mc-wa-sqlite-async-DJG_ANKO.js.map +0 -1
  80. package/dist/worker/wa-sqlite-D1_4mGs8.js.map +0 -1
  81. package/dist/worker/wa-sqlite-async-BxkwNY-2.js +0 -2
  82. package/dist/worker/wa-sqlite-async-BxkwNY-2.js.map +0 -1
@@ -139,6 +139,35 @@ var WASQLiteVFS;
139
139
  function vfsRequiresDedicatedWorkers(vfs) {
140
140
  return vfs != WASQLiteVFS.IDBBatchAtomicVFS && vfs != WASQLiteVFS.InMemoryVfs;
141
141
  }
142
+ /**
143
+ * Whether the VFS can keep its files in a Storage Bucket, see {@link WebSpecificOpenOptions.storageBucket}.
144
+ */
145
+ function vfsSupportsStorageBuckets(vfs) {
146
+ return (vfs == WASQLiteVFS.OPFSCoopSyncVFS || vfs == WASQLiteVFS.OPFSWriteAheadVFS || vfs == WASQLiteVFS.AccessHandlePoolVFS);
147
+ }
148
+ /**
149
+ * Options for an OPFS-based VFS that keep its files in the named Storage Bucket, see
150
+ * {@link WebSpecificOpenOptions.storageBucket}. Without a bucket name, no options: the VFS uses the root of the
151
+ * origin private file system.
152
+ *
153
+ * @internal
154
+ */
155
+ function vfsOptionsForStorageBucket(storageBucket, storageBuckets = navigator.storageBuckets) {
156
+ if (storageBucket == null) {
157
+ return {};
158
+ }
159
+ if (storageBuckets == null) {
160
+ throw new Error(`The 'storageBucket' option needs the Storage Buckets API (navigator.storageBuckets), which this browser does not have. Check for it before setting the option.`);
161
+ }
162
+ return {
163
+ // Called by the VFS each time it needs its root directory. Opening the bucket again creates it when the
164
+ // browser deleted it with the rest of the site's data.
165
+ getRoot: () => storageBuckets.open(storageBucket).then((bucket) => bucket.getDirectory()),
166
+ // Keeps the locks and channels of a file apart from those of a file of the same name in another bucket.
167
+ // Bucket names cannot contain ':', so the names of two buckets never overlap.
168
+ prefix: `${storageBucket}:`
169
+ };
170
+ }
142
171
  async function asyncModuleFactory(encryptionKey) {
143
172
  if (encryptionKey) {
144
173
  const { default: factory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs');
@@ -162,7 +191,7 @@ async function syncModuleFactory(encryptionKey) {
162
191
  /**
163
192
  * @internal
164
193
  */
165
- async function loadModuleAndVfs({ vfs, filename, encryptionKey }) {
194
+ async function loadModuleAndVfs({ vfs, filename, encryptionKey, storageBucket }) {
166
195
  let moduleFactory = syncModuleFactory;
167
196
  let resolveVfs;
168
197
  switch (vfs) {
@@ -178,19 +207,19 @@ async function loadModuleAndVfs({ vfs, filename, encryptionKey }) {
178
207
  case WASQLiteVFS.AccessHandlePoolVFS: {
179
208
  // @ts-expect-error The types for this import are missing upstream
180
209
  const { AccessHandlePoolVFS } = await import('@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js');
181
- resolveVfs = (module) => AccessHandlePoolVFS.create(filename, module);
210
+ resolveVfs = (module) => AccessHandlePoolVFS.create(filename, module, vfsOptionsForStorageBucket(storageBucket));
182
211
  break;
183
212
  }
184
213
  case WASQLiteVFS.OPFSCoopSyncVFS: {
185
214
  // @ts-expect-error The types for this import are missing upstream
186
215
  const { OPFSCoopSyncVFS } = await import('@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js');
187
- resolveVfs = (module) => OPFSCoopSyncVFS.create(filename, module);
216
+ resolveVfs = (module) => OPFSCoopSyncVFS.create(filename, module, vfsOptionsForStorageBucket(storageBucket));
188
217
  break;
189
218
  }
190
219
  case WASQLiteVFS.OPFSWriteAheadVFS: {
191
220
  // @ts-expect-error The types for this import are missing upstream
192
221
  const { OPFSWriteAheadVFS } = await import('@journeyapps/wa-sqlite/src/examples/OPFSWriteAheadVFS.js');
193
- resolveVfs = (module) => OPFSWriteAheadVFS.create(filename, module, {});
222
+ resolveVfs = (module) => OPFSWriteAheadVFS.create(filename, module, vfsOptionsForStorageBucket(storageBucket));
194
223
  break;
195
224
  }
196
225
  case WASQLiteVFS.InMemoryVfs: {
@@ -439,6 +468,11 @@ function resolveAndValidateOptions(options) {
439
468
  if (vfsRequiresDedicatedWorkers(resolved.vfs) && !resolved.useWebWorker) {
440
469
  throw new Error(`Invalid configuration: The 'useWebWorker' flag must be true when using an OPFS-based VFS (${resolved.vfs}).`);
441
470
  }
471
+ if (resolved.storageBucket != null) {
472
+ if (!vfsSupportsStorageBuckets(resolved.vfs)) {
473
+ throw new Error(`Invalid configuration: The 'storageBucket' option needs an OPFS-based VFS, which ${resolved.vfs} is not.`);
474
+ }
475
+ }
442
476
  return resolved;
443
477
  }
444
478
 
@@ -530,9 +564,14 @@ class RawSqliteConnection {
530
564
  const sqlite3 = Factory(module);
531
565
  sqlite3.vfs_register(vfs, true);
532
566
  /**
533
- * Register the PowerSync core SQLite extension
567
+ * Register the PowerSync core and additional statically linked extensions
534
568
  */
535
- module.ccall('powersync_init_static', 'int', []);
569
+ for (const name of ['powersync_init_static', ...(this.options.additionalInitializationFunctions ?? [])]) {
570
+ const result = module.ccall(name, 'int', []);
571
+ if (result !== 0) {
572
+ throw new Error(`SQLite extension initializer ${name} failed with code ${result}`);
573
+ }
574
+ }
536
575
  /**
537
576
  * Create the multiple cipher vfs if an encryption key is provided
538
577
  */
@@ -1421,7 +1460,7 @@ class WASQLiteOpenFactory {
1421
1460
  return this.openAdapter();
1422
1461
  }
1423
1462
  async openConnection() {
1424
- const { enableMultiTabs, useWebWorker, vfs, dbFilename, encryptionKey, temporaryStorage, cacheSizeKb, preparedStatementsCache } = this.options;
1463
+ const { enableMultiTabs, useWebWorker, vfs, dbFilename, encryptionKey, temporaryStorage, cacheSizeKb, preparedStatementsCache, additionalInitializationFunctions, storageBucket } = this.options;
1425
1464
  if (!enableMultiTabs) {
1426
1465
  this.logger.log({ level: LogLevels.warn, message: 'Multiple tabs are not enabled in this browser' });
1427
1466
  }
@@ -1437,7 +1476,9 @@ class WASQLiteOpenFactory {
1437
1476
  temporaryStorage,
1438
1477
  cacheSizeKb,
1439
1478
  // TODO: Enable prepared statement cache by default?
1440
- preparedStatementsCache: preparedStatementsCache ?? 0
1479
+ preparedStatementsCache: preparedStatementsCache ?? 0,
1480
+ additionalInitializationFunctions,
1481
+ storageBucket
1441
1482
  };
1442
1483
  }
1443
1484
  if (useWebWorker) {
@@ -1653,10 +1694,8 @@ function getOsInfo(nav) {
1653
1694
  }
1654
1695
 
1655
1696
  class WebRemote extends AbstractRemote {
1656
- connector;
1657
1697
  constructor(connector, logger) {
1658
1698
  super(connector, logger);
1659
- this.connector = connector;
1660
1699
  }
1661
1700
  fetch({ resource, request }) {
1662
1701
  return fetch(resource, request);
@@ -1782,10 +1821,10 @@ class SharedSyncClientProvider extends AbstractSharedSyncClientProvider {
1782
1821
  * Don't return anything here, just incase something which is not
1783
1822
  * serializable is returned from the `uploadCrud` function.
1784
1823
  */
1785
- await this.#relay(() => this.options.uploadCrud());
1824
+ await this.#relay(() => this.options.connector.uploadCrud());
1786
1825
  }
1787
1826
  async postCheckpointRequest(clientId, requestId) {
1788
- return await this.#relay(async () => (await this.options.postCheckpointRequest(clientId, requestId)) ?? null);
1827
+ return await this.#relay(async () => (await this.options.connector.postCheckpointRequest(clientId, requestId)) ?? null);
1789
1828
  }
1790
1829
  get logger() {
1791
1830
  return this.options.logger;
@@ -1862,9 +1901,13 @@ class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplementatio
1862
1901
  * - The client sends the params to the shared worker after locks have been registered.
1863
1902
  */
1864
1903
  const closeSignal = await generateTabCloseSignal(this.abortOnClose.signal);
1904
+ const { identifier, connector } = this.options;
1865
1905
  // Awaiting here ensures the worker is waiting for the lock
1866
- await this.syncManager.addLockBasedCloseSignal(closeSignal);
1867
- const { identifier } = this.options;
1906
+ await this.syncManager.initialize(closeSignal, {
1907
+ hasFetchCredentials: !!connector.fetchCredentials,
1908
+ hasPostCheckpointRequest: !!connector.postCheckpointRequest,
1909
+ hasUploadCrud: !!connector.uploadCrud
1910
+ });
1868
1911
  await this.syncManager.setParams({
1869
1912
  dbParams: this.dbAdapter.getConfiguration(),
1870
1913
  streamOptions: {
@@ -1887,8 +1930,26 @@ class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplementatio
1887
1930
  return this.syncManager.disconnect();
1888
1931
  }
1889
1932
  async dispose() {
1890
- await this.waitForReady();
1891
- await new Promise((resolve) => {
1933
+ let registeredWithWorker = true;
1934
+ try {
1935
+ await this.waitForReady();
1936
+ }
1937
+ catch {
1938
+ // The worker rejected this client (e.g. because it connects in a mode incompatible with other tabs), so it won't
1939
+ // acknowledge a close request either.
1940
+ registeredWithWorker = false;
1941
+ }
1942
+ if (registeredWithWorker) {
1943
+ await this.#closeWorkerClient();
1944
+ }
1945
+ await super.dispose();
1946
+ this.abortOnClose.abort();
1947
+ // Release the proxy
1948
+ this.syncManager[Comlink.releaseProxy]();
1949
+ this.worker.close();
1950
+ }
1951
+ #closeWorkerClient() {
1952
+ return new Promise((resolve) => {
1892
1953
  // This will always be a message port since we use shared workers.
1893
1954
  const messagePort = this.worker.endpoint;
1894
1955
  // Listen for the close acknowledgment from the worker
@@ -1905,11 +1966,6 @@ class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplementatio
1905
1966
  };
1906
1967
  messagePort.postMessage(closeMessagePayload);
1907
1968
  });
1908
- await super.dispose();
1909
- this.abortOnClose.abort();
1910
- // Release the proxy
1911
- this.syncManager[Comlink.releaseProxy]();
1912
- this.worker.close();
1913
1969
  }
1914
1970
  async waitForReady() {
1915
1971
  return this.isInitialized;
@@ -2033,6 +2089,9 @@ class WebPowerSyncDatabase extends BasePowerSyncDatabase {
2033
2089
  // Lets development tooling in this page find the live databases.
2034
2090
  registerDatabase(this);
2035
2091
  }
2092
+ get sdkName() {
2093
+ return '@powersync/web';
2094
+ }
2036
2095
  async _initialize() {
2037
2096
  if (this.database instanceof AsyncDbAdapter) {
2038
2097
  /**