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

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 (56) hide show
  1. package/dist/1807036ae51c10ee4d23.wasm +0 -0
  2. package/dist/{10072fe45f0a8fab0a0e.wasm → 307d8ce2280e3bae09d5.wasm} +0 -0
  3. package/dist/{6e435e51534839845554.wasm → cd8b9e8f4c87bf81c169.wasm} +0 -0
  4. package/dist/e797080f5ed0b5324166.wasm +0 -0
  5. package/dist/index.umd.js +336 -177
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/worker/SharedSyncImplementation.umd.js +310 -143
  8. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  9. package/dist/worker/WASQLiteDB.umd.js +19 -5
  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 +2 -2
  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 +2 -2
  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 +2 -2
  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 +2 -2
  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_IDBBatchAtomicVFS_js.umd.js +20 -23
  20. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js.map +1 -1
  21. package/lib/src/db/PowerSyncDatabase.d.ts +1 -1
  22. package/lib/src/db/PowerSyncDatabase.js +4 -4
  23. package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +8 -0
  24. package/lib/src/db/adapters/AsyncDatabaseConnection.js +11 -1
  25. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +14 -0
  26. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +61 -8
  27. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +5 -1
  28. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +15 -7
  29. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.d.ts +1 -4
  30. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +45 -31
  31. package/lib/src/worker/sync/MockSyncService.d.ts +2 -0
  32. package/lib/src/worker/sync/MockSyncService.js +3 -0
  33. package/lib/src/worker/sync/MockSyncServiceTypes.d.ts +101 -0
  34. package/lib/src/worker/sync/MockSyncServiceTypes.js +1 -0
  35. package/lib/src/worker/sync/MockSyncServiceWorker.d.ts +56 -0
  36. package/lib/src/worker/sync/MockSyncServiceWorker.js +369 -0
  37. package/lib/src/worker/sync/SharedSyncImplementation.d.ts +18 -8
  38. package/lib/src/worker/sync/SharedSyncImplementation.js +174 -108
  39. package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -1
  40. package/lib/src/worker/sync/WorkerClient.d.ts +4 -5
  41. package/lib/src/worker/sync/WorkerClient.js +5 -7
  42. package/lib/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +3 -3
  44. package/src/db/PowerSyncDatabase.ts +13 -15
  45. package/src/db/adapters/AsyncDatabaseConnection.ts +12 -0
  46. package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +71 -9
  47. package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +19 -6
  48. package/src/db/sync/SharedWebStreamingSyncImplementation.ts +56 -40
  49. package/src/worker/sync/MockSyncService.ts +3 -0
  50. package/src/worker/sync/MockSyncServiceTypes.ts +71 -0
  51. package/src/worker/sync/MockSyncServiceWorker.ts +406 -0
  52. package/src/worker/sync/SharedSyncImplementation.ts +201 -120
  53. package/src/worker/sync/SharedSyncImplementation.worker.ts +1 -1
  54. package/src/worker/sync/WorkerClient.ts +6 -11
  55. package/dist/a730f7ca717b02234beb.wasm +0 -0
  56. package/dist/aa2f408d64445fed090e.wasm +0 -0
@@ -1,4 +1,4 @@
1
- import { AbortOperation, BaseObserver, ConnectionManager, createLogger, SqliteBucketStorage, SyncStatus } from '@powersync/common';
1
+ import { AbortOperation, BaseObserver, ConnectionManager, SqliteBucketStorage, SyncStatus, createLogger } from '@powersync/common';
2
2
  import { Mutex } from 'async-mutex';
3
3
  import * as Comlink from 'comlink';
4
4
  import { WebRemote } from '../../db/sync/WebRemote';
@@ -35,7 +35,6 @@ export class SharedSyncImplementation extends BaseObserver {
35
35
  statusListener;
36
36
  fetchCredentialsController;
37
37
  uploadDataController;
38
- dbAdapter;
39
38
  syncParams;
40
39
  logger;
41
40
  lastConnectOptions;
@@ -44,10 +43,10 @@ export class SharedSyncImplementation extends BaseObserver {
44
43
  connectionManager;
45
44
  syncStatus;
46
45
  broadCastLogger;
46
+ distributedDB;
47
47
  constructor() {
48
48
  super();
49
49
  this.ports = [];
50
- this.dbAdapter = null;
51
50
  this.syncParams = null;
52
51
  this.logger = createLogger('shared-sync');
53
52
  this.lastConnectOptions = undefined;
@@ -60,26 +59,23 @@ export class SharedSyncImplementation extends BaseObserver {
60
59
  }
61
60
  });
62
61
  });
62
+ // Should be configured once we get params
63
+ this.distributedDB = null;
63
64
  this.syncStatus = new SyncStatus({});
64
65
  this.broadCastLogger = new BroadcastLogger(this.ports);
65
66
  this.connectionManager = new ConnectionManager({
66
67
  createSyncImplementation: async () => {
67
- return this.portMutex.runExclusive(async () => {
68
- await this.waitForReady();
69
- if (!this.dbAdapter) {
70
- await this.openInternalDB();
68
+ await this.waitForReady();
69
+ const sync = this.generateStreamingImplementation();
70
+ const onDispose = sync.registerListener({
71
+ statusChanged: (status) => {
72
+ this.updateAllStatuses(status.toJSON());
71
73
  }
72
- const sync = this.generateStreamingImplementation();
73
- const onDispose = sync.registerListener({
74
- statusChanged: (status) => {
75
- this.updateAllStatuses(status.toJSON());
76
- }
77
- });
78
- return {
79
- sync,
80
- onDispose
81
- };
82
74
  });
75
+ return {
76
+ sync,
77
+ onDispose
78
+ };
83
79
  },
84
80
  logger: this.logger
85
81
  });
@@ -90,6 +86,29 @@ export class SharedSyncImplementation extends BaseObserver {
90
86
  get isConnected() {
91
87
  return this.connectionManager.syncStreamImplementation?.isConnected ?? false;
92
88
  }
89
+ /**
90
+ * Gets the last client port which we know is safe from unexpected closes.
91
+ */
92
+ async getLastWrappedPort() {
93
+ // Find the last port which is not closing
94
+ return await this.portMutex.runExclusive(() => {
95
+ for (let i = this.ports.length - 1; i >= 0; i--) {
96
+ if (!this.ports[i].isClosing) {
97
+ return this.ports[i];
98
+ }
99
+ }
100
+ return;
101
+ });
102
+ }
103
+ /**
104
+ * In some very rare cases a specific tab might not respond to requests.
105
+ * This returns a random port which is not closing.
106
+ */
107
+ async getRandomWrappedPort() {
108
+ return await this.portMutex.runExclusive(() => {
109
+ return this.ports[Math.floor(Math.random() * this.ports.length)];
110
+ });
111
+ }
93
112
  async waitForStatus(status) {
94
113
  return this.withSyncImplementation(async (sync) => {
95
114
  return sync.waitForStatus(status);
@@ -130,28 +149,45 @@ export class SharedSyncImplementation extends BaseObserver {
130
149
  async setParams(params) {
131
150
  await this.portMutex.runExclusive(async () => {
132
151
  this.collectActiveSubscriptions();
133
- if (this.syncParams) {
134
- // Cannot modify already existing sync implementation params
135
- // But we can ask for a DB adapter, if required, at this point.
136
- if (!this.dbAdapter) {
137
- await this.openInternalDB();
138
- }
139
- return;
140
- }
141
- // First time setting params
142
- this.syncParams = params;
143
- if (params.streamOptions?.flags?.broadcastLogs) {
144
- this.logger = this.broadCastLogger;
145
- }
146
- self.onerror = (event) => {
147
- // Share any uncaught events on the broadcast logger
148
- this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
149
- };
150
- if (!this.dbAdapter) {
151
- await this.openInternalDB();
152
+ });
153
+ if (this.syncParams) {
154
+ // Cannot modify already existing sync implementation params
155
+ return;
156
+ }
157
+ // First time setting params
158
+ this.syncParams = params;
159
+ if (params.streamOptions?.flags?.broadcastLogs) {
160
+ this.logger = this.broadCastLogger;
161
+ }
162
+ const lockedAdapter = new LockedAsyncDatabaseAdapter({
163
+ name: params.dbParams.dbFilename,
164
+ openConnection: async () => {
165
+ // Gets a connection from the clients when a new connection is requested.
166
+ const db = await this.openInternalDB();
167
+ db.registerListener({
168
+ closing: () => {
169
+ lockedAdapter.reOpenInternalDB();
170
+ }
171
+ });
172
+ return db;
173
+ },
174
+ logger: this.logger,
175
+ reOpenOnConnectionClosed: true
176
+ });
177
+ this.distributedDB = lockedAdapter;
178
+ await lockedAdapter.init();
179
+ lockedAdapter.registerListener({
180
+ databaseReOpened: () => {
181
+ // We may have missed some table updates while the database was closed.
182
+ // We can poke the crud in case we missed any updates.
183
+ this.connectionManager.syncStreamImplementation?.triggerCrudUpload();
152
184
  }
153
- this.iterateListeners((l) => l.initialized?.());
154
185
  });
186
+ self.onerror = (event) => {
187
+ // Share any uncaught events on the broadcast logger
188
+ this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
189
+ };
190
+ this.iterateListeners((l) => l.initialized?.());
155
191
  }
156
192
  async dispose() {
157
193
  await this.waitForReady();
@@ -180,7 +216,8 @@ export class SharedSyncImplementation extends BaseObserver {
180
216
  port,
181
217
  clientProvider: Comlink.wrap(port),
182
218
  currentSubscriptions: [],
183
- closeListeners: []
219
+ closeListeners: [],
220
+ isClosing: false
184
221
  };
185
222
  this.ports.push(portProvider);
186
223
  // Give the newly connected client the latest status
@@ -196,14 +233,16 @@ export class SharedSyncImplementation extends BaseObserver {
196
233
  * clients.
197
234
  */
198
235
  async removePort(port) {
236
+ // Ports might be removed faster than we can process them.
237
+ port.isClosing = true;
199
238
  // Remove the port within a mutex context.
200
239
  // Warns if the port is not found. This should not happen in practice.
201
240
  // We return early if the port is not found.
202
- const { trackedPort, shouldReconnect } = await this.portMutex.runExclusive(async () => {
241
+ return await this.portMutex.runExclusive(async () => {
203
242
  const index = this.ports.findIndex((p) => p == port);
204
243
  if (index < 0) {
205
244
  this.logger.warn(`Could not remove port ${port} since it is not present in active ports.`);
206
- return {};
245
+ return () => { };
207
246
  }
208
247
  const trackedPort = this.ports[index];
209
248
  // Remove from the list of active ports
@@ -217,40 +256,13 @@ export class SharedSyncImplementation extends BaseObserver {
217
256
  abortController.controller.abort(new AbortOperation('Closing pending requests after client port is removed'));
218
257
  }
219
258
  });
220
- const shouldReconnect = !!this.connectionManager.syncStreamImplementation && this.ports.length > 0;
221
- /**
222
- * If the current database adapter is the one that is being closed, we need to disconnect from the backend.
223
- * We can disconnect in the portMutex lock. This ensures the disconnect is not affected by potential other
224
- * connect operations coming from other tabs.
225
- */
226
- if (this.dbAdapter && this.dbAdapter == trackedPort.db) {
227
- this.logger.debug(`Disconnecting due to closed database: should reconnect: ${shouldReconnect}`);
228
- this.dbAdapter = null;
229
- // Unconditionally close the connection because the database it's writing to has just been closed.
230
- // The connection has been closed previously, this might throw. We should be able to ignore it.
231
- await this.connectionManager
232
- .disconnect()
233
- .catch((ex) => this.logger.warn('Error while disconnecting. Will attempt to reconnect.', ex));
259
+ // Close the worker wrapped database connection, we can't accurately rely on this connection
260
+ for (const closeListener of trackedPort.closeListeners) {
261
+ await closeListener();
234
262
  }
235
- return {
236
- shouldReconnect,
237
- trackedPort
238
- };
263
+ this.collectActiveSubscriptions();
264
+ return () => trackedPort.clientProvider[Comlink.releaseProxy]();
239
265
  });
240
- if (!trackedPort) {
241
- // We could not find the port to remove
242
- return () => { };
243
- }
244
- for (const closeListener of trackedPort.closeListeners) {
245
- await closeListener();
246
- }
247
- if (shouldReconnect) {
248
- await this.connectionManager.connect(CONNECTOR_PLACEHOLDER, this.lastConnectOptions ?? {});
249
- }
250
- // Re-index subscriptions, the subscriptions of the removed port would no longer be considered.
251
- this.collectActiveSubscriptions();
252
- // Release proxy
253
- return () => trackedPort.clientProvider[Comlink.releaseProxy]();
254
266
  }
255
267
  triggerCrudUpload() {
256
268
  this.withSyncImplementation(async (sync) => {
@@ -287,10 +299,13 @@ export class SharedSyncImplementation extends BaseObserver {
287
299
  const syncParams = this.syncParams;
288
300
  // Create a new StreamingSyncImplementation for each connect call. This is usually done is all SDKs.
289
301
  return new WebStreamingSyncImplementation({
290
- adapter: new SqliteBucketStorage(this.dbAdapter, this.logger),
302
+ adapter: new SqliteBucketStorage(this.distributedDB, this.logger),
291
303
  remote: new WebRemote({
292
304
  invalidateCredentials: async () => {
293
- const lastPort = this.ports[this.ports.length - 1];
305
+ const lastPort = await this.getLastWrappedPort();
306
+ if (!lastPort) {
307
+ throw new Error('No client port found to invalidate credentials');
308
+ }
294
309
  try {
295
310
  this.logger.log('calling the last port client provider to invalidate credentials');
296
311
  lastPort.clientProvider.invalidateCredentials();
@@ -300,7 +315,10 @@ export class SharedSyncImplementation extends BaseObserver {
300
315
  }
301
316
  },
302
317
  fetchCredentials: async () => {
303
- const lastPort = this.ports[this.ports.length - 1];
318
+ const lastPort = await this.getLastWrappedPort();
319
+ if (!lastPort) {
320
+ throw new Error('No client port found to fetch credentials');
321
+ }
304
322
  return new Promise(async (resolve, reject) => {
305
323
  const abortController = new AbortController();
306
324
  this.fetchCredentialsController = {
@@ -322,7 +340,10 @@ export class SharedSyncImplementation extends BaseObserver {
322
340
  }
323
341
  }, this.logger),
324
342
  uploadCrud: async () => {
325
- const lastPort = this.ports[this.ports.length - 1];
343
+ const lastPort = await this.getLastWrappedPort();
344
+ if (!lastPort) {
345
+ throw new Error('No client port found to upload crud');
346
+ }
326
347
  return new Promise(async (resolve, reject) => {
327
348
  const abortController = new AbortController();
328
349
  this.uploadDataController = {
@@ -348,19 +369,52 @@ export class SharedSyncImplementation extends BaseObserver {
348
369
  logger: this.logger
349
370
  });
350
371
  }
372
+ /**
373
+ * Opens a worker wrapped database connection. Using the last connected client port.
374
+ */
351
375
  async openInternalDB() {
352
- const lastClient = this.ports[this.ports.length - 1];
353
- if (!lastClient) {
354
- // Should not really happen in practice
355
- throw new Error(`Could not open DB connection since no client is connected.`);
356
- }
357
- const workerPort = await lastClient.clientProvider.getDBWorkerPort();
358
- const remote = Comlink.wrap(workerPort);
359
- const identifier = this.syncParams.dbParams.dbFilename;
360
- const db = await remote(this.syncParams.dbParams);
361
- const locked = new LockedAsyncDatabaseAdapter({
362
- name: identifier,
363
- openConnection: async () => {
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);
364
418
  const wrapped = new WorkerWrappedAsyncDatabaseConnection({
365
419
  remote,
366
420
  baseConnection: db,
@@ -369,22 +423,23 @@ export class SharedSyncImplementation extends BaseObserver {
369
423
  // that and ensure pending requests are aborted when the tab is closed.
370
424
  remoteCanCloseUnexpectedly: true
371
425
  });
372
- lastClient.closeListeners.push(async () => {
426
+ client.closeListeners.push(async () => {
373
427
  this.logger.info('Aborting open connection because associated tab closed.');
374
428
  /**
375
429
  * Don't await this close operation. It might never resolve if the tab is closed.
376
- * We run the close operation first, before marking the remote as closed. This gives the database some chance
377
- * to close the connection.
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.
378
432
  */
379
- wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
380
433
  wrapped.markRemoteClosed();
434
+ wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
381
435
  });
382
436
  return wrapped;
383
- },
384
- logger: this.logger
385
- });
386
- await locked.init();
387
- this.dbAdapter = lastClient.db = locked;
437
+ }
438
+ catch (ex) {
439
+ this.logger.warn('Error opening internal DB', ex);
440
+ await new Promise((resolve) => setTimeout(resolve, 1000));
441
+ }
442
+ }
388
443
  }
389
444
  /**
390
445
  * A method to update the all shared statuses for each
@@ -394,16 +449,27 @@ export class SharedSyncImplementation extends BaseObserver {
394
449
  this.syncStatus = new SyncStatus(status);
395
450
  this.ports.forEach((p) => p.clientProvider.statusChanged(status));
396
451
  }
397
- /**
398
- * A function only used for unit tests which updates the internal
399
- * sync stream client and all tab client's sync status
400
- */
401
- async _testUpdateAllStatuses(status) {
402
- if (!this.connectionManager.syncStreamImplementation) {
403
- throw new Error('Cannot update status without a sync stream implementation');
452
+ }
453
+ /**
454
+ * Runs the action with an abort controller.
455
+ */
456
+ function withAbort(action, signal) {
457
+ return new Promise((resolve, reject) => {
458
+ if (signal.aborted) {
459
+ reject(new AbortOperation('Operation aborted by abort controller'));
460
+ return;
404
461
  }
405
- // Only assigning, don't call listeners for this test
406
- this.connectionManager.syncStreamImplementation.syncStatus = new SyncStatus(status);
407
- this.updateAllStatuses(status);
408
- }
462
+ function handleAbort() {
463
+ signal.removeEventListener('abort', handleAbort);
464
+ reject(new AbortOperation('Operation aborted by abort controller'));
465
+ }
466
+ signal.addEventListener('abort', handleAbort, { once: true });
467
+ function completePromise(action) {
468
+ signal.removeEventListener('abort', handleAbort);
469
+ action();
470
+ }
471
+ action()
472
+ .then((data) => completePromise(() => resolve(data)))
473
+ .catch((e) => completePromise(() => reject(e)));
474
+ });
409
475
  }
@@ -7,5 +7,5 @@ logger.useDefaults();
7
7
  const sharedSyncImplementation = new SharedSyncImplementation();
8
8
  _self.onconnect = async function (event) {
9
9
  const port = event.ports[0];
10
- await new WorkerClient(sharedSyncImplementation, port).initialize();
10
+ new WorkerClient(sharedSyncImplementation, port);
11
11
  };
@@ -1,5 +1,5 @@
1
- import { ILogLevel, PowerSyncConnectionOptions, SubscribedStream, SyncStatusOptions } from '@powersync/common';
2
- import { SharedSyncImplementation, SharedSyncInitOptions } from './SharedSyncImplementation';
1
+ import { ILogLevel, PowerSyncConnectionOptions, SubscribedStream } from '@powersync/common';
2
+ import { SharedSyncImplementation, SharedSyncInitOptions, WrappedSyncPort } from './SharedSyncImplementation';
3
3
  /**
4
4
  * A client to the shared sync worker.
5
5
  *
@@ -10,8 +10,8 @@ export declare class WorkerClient {
10
10
  private readonly sync;
11
11
  private readonly port;
12
12
  private resolvedPort;
13
+ protected resolvedPortPromise: Promise<WrappedSyncPort> | null;
13
14
  constructor(sync: SharedSyncImplementation, port: MessagePort);
14
- initialize(): Promise<void>;
15
15
  private removePort;
16
16
  /**
17
17
  * Called by a client after obtaining a lock with a random name.
@@ -19,7 +19,7 @@ export declare class WorkerClient {
19
19
  * When the client tab is closed, its lock will be returned. So when the shared worker attempts to acquire the lock,
20
20
  * it can consider the connection to be closed.
21
21
  */
22
- addLockBasedCloseSignal(name: string): void;
22
+ addLockBasedCloseSignal(name: string): Promise<void>;
23
23
  setLogLevel(level: ILogLevel): void;
24
24
  triggerCrudUpload(): void;
25
25
  setParams(params: SharedSyncInitOptions, subscriptions: SubscribedStream[]): Promise<void>;
@@ -28,5 +28,4 @@ export declare class WorkerClient {
28
28
  connect(options?: PowerSyncConnectionOptions): Promise<void>;
29
29
  updateSubscriptions(subscriptions: SubscribedStream[]): void;
30
30
  disconnect(): Promise<void>;
31
- _testUpdateAllStatuses(status: SyncStatusOptions): Promise<void>;
32
31
  }
@@ -11,12 +11,11 @@ export class WorkerClient {
11
11
  sync;
12
12
  port;
13
13
  resolvedPort = null;
14
+ resolvedPortPromise = null;
14
15
  constructor(sync, port) {
15
16
  this.sync = sync;
16
17
  this.port = port;
17
18
  Comlink.expose(this, this.port);
18
- }
19
- async initialize() {
20
19
  /**
21
20
  * Adds an extra listener which can remove this port
22
21
  * from the list of monitored ports.
@@ -27,7 +26,6 @@ export class WorkerClient {
27
26
  await this.removePort();
28
27
  }
29
28
  });
30
- this.resolvedPort = await this.sync.addPort(this.port);
31
29
  }
32
30
  async removePort() {
33
31
  if (this.resolvedPort) {
@@ -48,7 +46,10 @@ export class WorkerClient {
48
46
  * When the client tab is closed, its lock will be returned. So when the shared worker attempts to acquire the lock,
49
47
  * it can consider the connection to be closed.
50
48
  */
51
- addLockBasedCloseSignal(name) {
49
+ async addLockBasedCloseSignal(name) {
50
+ // Only add the port once the lock has been obtained on the client.
51
+ this.resolvedPort = await this.sync.addPort(this.port);
52
+ // Don't await this lock request
52
53
  getNavigatorLocks().request(name, async () => {
53
54
  await this.removePort();
54
55
  });
@@ -80,7 +81,4 @@ export class WorkerClient {
80
81
  disconnect() {
81
82
  return this.sync.disconnect();
82
83
  }
83
- async _testUpdateAllStatuses(status) {
84
- return this.sync._testUpdateAllStatuses(status);
85
- }
86
84
  }