@powersync/web 0.0.0-dev-20251201150812 → 0.0.0-dev-20251209082930

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 (47) hide show
  1. package/dist/0b19af1befc07ce338dd.wasm +0 -0
  2. package/dist/2632c3bda9473da74fd5.wasm +0 -0
  3. package/dist/64f5351ba3784bfe2f3e.wasm +0 -0
  4. package/dist/9318ca94aac4d0fe0135.wasm +0 -0
  5. package/dist/index.umd.js +219 -115
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/worker/SharedSyncImplementation.umd.js +164 -109
  8. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  9. package/dist/worker/WASQLiteDB.umd.js +24 -12
  10. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  11. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js +16 -3
  12. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js.map +1 -1
  13. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js +16 -3
  14. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js.map +1 -1
  15. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js +16 -3
  16. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js.map +1 -1
  17. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +16 -3
  18. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map +1 -1
  19. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js +18 -11
  20. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js.map +1 -1
  21. package/lib/package.json +2 -2
  22. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +4 -1
  23. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +60 -29
  24. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +11 -2
  25. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.d.ts +1 -1
  26. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +2 -2
  27. package/lib/src/worker/sync/SharedSyncImplementation.js +80 -68
  28. package/lib/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +5 -5
  30. package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +79 -48
  31. package/src/db/adapters/wa-sqlite/WASQLiteConnection.ts +11 -3
  32. package/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts +3 -3
  33. package/src/worker/db/WASQLiteDB.worker.ts +0 -1
  34. package/src/worker/sync/SharedSyncImplementation.ts +89 -74
  35. package/dist/1807036ae51c10ee4d23.wasm +0 -0
  36. package/dist/307d8ce2280e3bae09d5.wasm +0 -0
  37. package/dist/cd8b9e8f4c87bf81c169.wasm +0 -0
  38. package/dist/e797080f5ed0b5324166.wasm +0 -0
  39. package/lib/src/worker/sync/MockSyncService.d.ts +0 -2
  40. package/lib/src/worker/sync/MockSyncService.js +0 -3
  41. package/lib/src/worker/sync/MockSyncServiceTypes.d.ts +0 -101
  42. package/lib/src/worker/sync/MockSyncServiceTypes.js +0 -1
  43. package/lib/src/worker/sync/MockSyncServiceWorker.d.ts +0 -56
  44. package/lib/src/worker/sync/MockSyncServiceWorker.js +0 -369
  45. package/src/worker/sync/MockSyncService.ts +0 -3
  46. package/src/worker/sync/MockSyncServiceTypes.ts +0 -71
  47. package/src/worker/sync/MockSyncServiceWorker.ts +0 -406
@@ -106,7 +106,8 @@ export class SharedSyncImplementation extends BaseObserver {
106
106
  */
107
107
  async getRandomWrappedPort() {
108
108
  return await this.portMutex.runExclusive(() => {
109
- return this.ports[Math.floor(Math.random() * this.ports.length)];
109
+ const nonClosingPorts = this.ports.filter((p) => !p.isClosing);
110
+ return nonClosingPorts[Math.floor(Math.random() * nonClosingPorts.length)];
110
111
  });
111
112
  }
112
113
  async waitForStatus(status) {
@@ -373,73 +374,77 @@ export class SharedSyncImplementation extends BaseObserver {
373
374
  * Opens a worker wrapped database connection. Using the last connected client port.
374
375
  */
375
376
  async openInternalDB() {
376
- while (true) {
377
- try {
378
- const client = await this.getRandomWrappedPort();
379
- if (!client) {
380
- // Should not really happen in practice
381
- throw new Error(`Could not open DB connection since no client is connected.`);
382
- }
383
- // Fail-safe timeout for opening a database connection.
384
- const timeout = setTimeout(() => {
385
- abortController.abort();
386
- }, 10_000);
387
- /**
388
- * Handle cases where the client might close while opening a connection.
389
- */
390
- const abortController = new AbortController();
391
- const closeListener = () => {
392
- abortController.abort();
393
- };
394
- const removeCloseListener = () => {
395
- const index = client.closeListeners.indexOf(closeListener);
396
- if (index >= 0) {
397
- client.closeListeners.splice(index, 1);
398
- }
399
- };
400
- client.closeListeners.push(closeListener);
401
- const workerPort = await withAbort(() => client.clientProvider.getDBWorkerPort(), abortController.signal).catch((ex) => {
402
- removeCloseListener();
403
- throw ex;
404
- });
405
- const remote = Comlink.wrap(workerPort);
406
- const identifier = this.syncParams.dbParams.dbFilename;
407
- /**
408
- * The open could fail if the tab is closed while we're busy opening the database.
409
- * This operation is typically executed inside an exclusive portMutex lock.
410
- * We typically execute the closeListeners using the portMutex in a different context.
411
- * We can't rely on the closeListeners to abort the operation if the tab is closed.
412
- */
413
- const db = await withAbort(() => remote(this.syncParams.dbParams), abortController.signal).finally(() => {
414
- // We can remove the close listener here since we no longer need it past this point.
415
- removeCloseListener();
416
- });
417
- clearTimeout(timeout);
418
- const wrapped = new WorkerWrappedAsyncDatabaseConnection({
419
- remote,
420
- baseConnection: db,
421
- identifier,
422
- // It's possible for this worker to outlive the client hosting the database for us. We need to be prepared for
423
- // that and ensure pending requests are aborted when the tab is closed.
424
- remoteCanCloseUnexpectedly: true
425
- });
426
- client.closeListeners.push(async () => {
427
- this.logger.info('Aborting open connection because associated tab closed.');
428
- /**
429
- * Don't await this close operation. It might never resolve if the tab is closed.
430
- * We mark the remote as closed first, this will reject any pending requests.
431
- * We then call close. The close operation is configured to fire-and-forget, the main promise will reject immediately.
432
- */
433
- wrapped.markRemoteClosed();
434
- wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
435
- });
436
- return wrapped;
377
+ const client = await this.getRandomWrappedPort();
378
+ if (!client) {
379
+ // Should not really happen in practice
380
+ throw new Error(`Could not open DB connection since no client is connected.`);
381
+ }
382
+ // Fail-safe timeout for opening a database connection.
383
+ const timeout = setTimeout(() => {
384
+ abortController.abort();
385
+ }, 10_000);
386
+ /**
387
+ * Handle cases where the client might close while opening a connection.
388
+ */
389
+ const abortController = new AbortController();
390
+ const closeListener = () => {
391
+ abortController.abort();
392
+ };
393
+ const removeCloseListener = () => {
394
+ const index = client.closeListeners.indexOf(closeListener);
395
+ if (index >= 0) {
396
+ client.closeListeners.splice(index, 1);
437
397
  }
438
- catch (ex) {
439
- this.logger.warn('Error opening internal DB', ex);
440
- await new Promise((resolve) => setTimeout(resolve, 1000));
398
+ };
399
+ client.closeListeners.push(closeListener);
400
+ const workerPort = await withAbort({
401
+ action: () => client.clientProvider.getDBWorkerPort(),
402
+ signal: abortController.signal,
403
+ cleanupOnAbort: (port) => {
404
+ port.close();
441
405
  }
442
- }
406
+ }).catch((ex) => {
407
+ removeCloseListener();
408
+ throw ex;
409
+ });
410
+ const remote = Comlink.wrap(workerPort);
411
+ const identifier = this.syncParams.dbParams.dbFilename;
412
+ /**
413
+ * The open could fail if the tab is closed while we're busy opening the database.
414
+ * This operation is typically executed inside an exclusive portMutex lock.
415
+ * We typically execute the closeListeners using the portMutex in a different context.
416
+ * We can't rely on the closeListeners to abort the operation if the tab is closed.
417
+ */
418
+ const db = await withAbort({
419
+ action: () => remote(this.syncParams.dbParams),
420
+ signal: abortController.signal,
421
+ cleanupOnAbort: (db) => {
422
+ db.close();
423
+ }
424
+ }).finally(() => {
425
+ // We can remove the close listener here since we no longer need it past this point.
426
+ removeCloseListener();
427
+ });
428
+ clearTimeout(timeout);
429
+ const wrapped = new WorkerWrappedAsyncDatabaseConnection({
430
+ remote,
431
+ baseConnection: db,
432
+ identifier,
433
+ // It's possible for this worker to outlive the client hosting the database for us. We need to be prepared for
434
+ // that and ensure pending requests are aborted when the tab is closed.
435
+ remoteCanCloseUnexpectedly: true
436
+ });
437
+ client.closeListeners.push(async () => {
438
+ this.logger.info('Aborting open connection because associated tab closed.');
439
+ /**
440
+ * Don't await this close operation. It might never resolve if the tab is closed.
441
+ * We mark the remote as closed first, this will reject any pending requests.
442
+ * We then call close. The close operation is configured to fire-and-forget, the main promise will reject immediately.
443
+ */
444
+ wrapped.markRemoteClosed();
445
+ wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
446
+ });
447
+ return wrapped;
443
448
  }
444
449
  /**
445
450
  * A method to update the all shared statuses for each
@@ -453,7 +458,8 @@ export class SharedSyncImplementation extends BaseObserver {
453
458
  /**
454
459
  * Runs the action with an abort controller.
455
460
  */
456
- function withAbort(action, signal) {
461
+ function withAbort(options) {
462
+ const { action, signal, cleanupOnAbort } = options;
457
463
  return new Promise((resolve, reject) => {
458
464
  if (signal.aborted) {
459
465
  reject(new AbortOperation('Operation aborted by abort controller'));
@@ -469,7 +475,13 @@ function withAbort(action, signal) {
469
475
  action();
470
476
  }
471
477
  action()
472
- .then((data) => completePromise(() => resolve(data)))
478
+ .then((data) => {
479
+ // We already rejected due to the abort, allow for cleanup
480
+ if (signal.aborted) {
481
+ return completePromise(() => cleanupOnAbort?.(data));
482
+ }
483
+ completePromise(() => resolve(data));
484
+ })
473
485
  .catch((e) => completePromise(() => reject(e)));
474
486
  });
475
487
  }