@powersync/web 2.2.0 → 2.3.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 (29) hide show
  1. package/README.md +1 -1
  2. package/dist/index.react_native_web.js +12 -29
  3. package/dist/index.react_native_web.js.map +1 -1
  4. package/dist/worker/worker.js +1 -1
  5. package/dist/worker/worker.js.map +1 -1
  6. package/lib/db/PowerSyncDatabase.d.ts +1 -2
  7. package/lib/db/PowerSyncDatabase.js +3 -13
  8. package/lib/db/PowerSyncDatabase.js.map +1 -1
  9. package/lib/db/sync/SSRWebStreamingSyncImplementation.d.ts +1 -8
  10. package/lib/db/sync/SSRWebStreamingSyncImplementation.js +3 -12
  11. package/lib/db/sync/SSRWebStreamingSyncImplementation.js.map +1 -1
  12. package/lib/db/sync/SharedWebStreamingSyncImplementation.d.ts +2 -1
  13. package/lib/db/sync/SharedWebStreamingSyncImplementation.js +6 -4
  14. package/lib/db/sync/SharedWebStreamingSyncImplementation.js.map +1 -1
  15. package/lib/worker/sync/AbstractSharedSyncClientProvider.d.ts +1 -0
  16. package/lib/worker/sync/AbstractSharedSyncClientProvider.js.map +1 -1
  17. package/lib/worker/sync/SharedSyncImplementation.d.ts +3 -11
  18. package/lib/worker/sync/SharedSyncImplementation.js +32 -64
  19. package/lib/worker/sync/SharedSyncImplementation.js.map +1 -1
  20. package/lib/worker/sync/WorkerClient.d.ts +1 -1
  21. package/lib/worker/sync/WorkerClient.js +3 -3
  22. package/lib/worker/sync/WorkerClient.js.map +1 -1
  23. package/package.json +5 -5
  24. package/src/db/PowerSyncDatabase.ts +2 -13
  25. package/src/db/sync/SSRWebStreamingSyncImplementation.ts +4 -14
  26. package/src/db/sync/SharedWebStreamingSyncImplementation.ts +8 -5
  27. package/src/worker/sync/AbstractSharedSyncClientProvider.ts +1 -0
  28. package/src/worker/sync/SharedSyncImplementation.ts +41 -76
  29. package/src/worker/sync/WorkerClient.ts +4 -4
@@ -8,6 +8,7 @@ export abstract class AbstractSharedSyncClientProvider {
8
8
  abstract fetchCredentials(): Promise<PowerSyncCredentials | null>;
9
9
  abstract invalidateCredentials(): void;
10
10
  abstract uploadCrud(): Promise<void>;
11
+ abstract postCheckpointRequest(clientId: string, requestId: string): Promise<string | null>;
11
12
  abstract statusChanged(status: SyncStatusJson): void;
12
13
  abstract getDBWorkerPort(): Promise<MessagePort>;
13
14
 
@@ -60,7 +60,7 @@ export type ManualSharedSyncPayload = {
60
60
  export type SharedSyncInitOptions = {
61
61
  streamOptions: Omit<
62
62
  WebStreamingSyncImplementationOptions,
63
- 'adapter' | 'uploadCrud' | 'remote' | 'subscriptions' | 'logger'
63
+ 'adapter' | 'uploadCrud' | 'postCheckpointRequest' | 'remote' | 'subscriptions' | 'logger'
64
64
  >;
65
65
  dbParams: ResolvedWebSQLOpenOptions;
66
66
  enableBroadcastLogs: boolean;
@@ -85,14 +85,6 @@ export type WrappedSyncPort = {
85
85
  isClosing: boolean;
86
86
  };
87
87
 
88
- /**
89
- * @internal
90
- */
91
- export type RemoteOperationAbortController = {
92
- controller: AbortController;
93
- activePort: WrappedSyncPort;
94
- };
95
-
96
88
  /**
97
89
  * HACK: The shared implementation wraps and provides its own
98
90
  * PowerSyncBackendConnector when generating the streaming sync implementation.
@@ -110,9 +102,6 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
110
102
  protected isInitialized: Promise<void>;
111
103
  protected statusListener?: () => void;
112
104
 
113
- protected fetchCredentialsController?: RemoteOperationAbortController;
114
- protected uploadDataController?: RemoteOperationAbortController;
115
-
116
105
  protected syncParams: SharedSyncInitOptions | null;
117
106
  protected lastConnectOptions: ResolvedSyncOptions | undefined;
118
107
  protected portMutex: Mutex;
@@ -334,18 +323,6 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
334
323
  // Remove from the list of active ports
335
324
  this.ports.splice(index, 1);
336
325
 
337
- /**
338
- * The port might currently be in use. Any active functions might
339
- * not resolve. Abort them here.
340
- */
341
- [this.fetchCredentialsController, this.uploadDataController].forEach((abortController) => {
342
- if (abortController?.activePort == port) {
343
- abortController!.controller.abort(
344
- new AbortOperation('Closing pending requests after client port is removed')
345
- );
346
- }
347
- });
348
-
349
326
  // Close the worker wrapped database connection, we can't accurately rely on this connection
350
327
  for (const closeListener of trackedPort.closeListeners) {
351
328
  await closeListener();
@@ -363,10 +340,8 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
363
340
  });
364
341
  }
365
342
 
366
- async getWriteCheckpoint(): Promise<string> {
367
- return this.withSyncImplementation(async (sync) => {
368
- return sync.getWriteCheckpoint();
369
- });
343
+ requestCheckpoint() {
344
+ return this.withSyncImplementation((sync) => sync.requestCheckpoint());
370
345
  }
371
346
 
372
347
  protected async withSyncImplementation<T>(callback: (sync: StreamingSyncImplementation) => Promise<T>): Promise<T> {
@@ -411,58 +386,27 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
411
386
  this.logger.log({ level: LogLevels.error, message: 'error invalidating credentials', error });
412
387
  }
413
388
  },
414
- fetchCredentials: async () => {
415
- const lastPort = await this.getLastWrappedPort();
416
- if (!lastPort) {
417
- throw new Error('No client port found to fetch credentials');
418
- }
419
- return new Promise(async (resolve, reject) => {
420
- const abortController = new AbortController();
421
- this.fetchCredentialsController = {
422
- controller: abortController,
423
- activePort: lastPort
424
- };
425
-
426
- abortController.signal.onabort = reject;
427
- try {
428
- this.logger.log({
429
- level: LogLevels.info,
430
- message: 'calling the last port client provider for credentials'
431
- });
432
- resolve(await lastPort.clientProvider.fetchCredentials());
433
- } catch (ex) {
434
- reject(ex);
435
- } finally {
436
- this.fetchCredentialsController = undefined;
437
- }
438
- });
389
+ fetchCredentials: () => {
390
+ return this.#useConnector((port) => {
391
+ this.logger.log({
392
+ level: LogLevels.info,
393
+ message: 'calling the last port client provider for credentials'
394
+ });
395
+
396
+ return port.clientProvider.fetchCredentials();
397
+ }, 'fetchCredentials');
439
398
  }
440
399
  },
441
400
  this.logger
442
401
  ),
443
- uploadCrud: async () => {
444
- const lastPort = await this.getLastWrappedPort();
445
- if (!lastPort) {
446
- throw new Error('No client port found to upload crud');
447
- }
448
-
449
- return new Promise(async (resolve, reject) => {
450
- const abortController = new AbortController();
451
- this.uploadDataController = {
452
- controller: abortController,
453
- activePort: lastPort
454
- };
455
-
456
- // Resolving will make it retry
457
- abortController.signal.onabort = () => resolve();
458
- try {
459
- resolve(await lastPort.clientProvider.uploadCrud());
460
- } catch (ex) {
461
- reject(ex);
462
- } finally {
463
- this.uploadDataController = undefined;
464
- }
465
- });
402
+ uploadCrud: () => {
403
+ return this.#useConnector((port) => port.clientProvider.uploadCrud(), 'uploadCrud');
404
+ },
405
+ postCheckpointRequest: (clientId, requestId) => {
406
+ return this.#useConnector(
407
+ (port) => port.clientProvider.postCheckpointRequest(clientId, requestId),
408
+ 'postCheckpointRequest'
409
+ );
466
410
  },
467
411
  ...syncParams.streamOptions,
468
412
  subscriptions: this.subscriptions,
@@ -471,6 +415,27 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
471
415
  });
472
416
  }
473
417
 
418
+ async #useConnector<T>(inner: (port: WrappedSyncPort) => Promise<T>, debugContext: string): Promise<T> {
419
+ const lastPort = await this.getLastWrappedPort();
420
+ if (!lastPort) {
421
+ throw new Error(`No client port found for ${debugContext}`);
422
+ }
423
+
424
+ return new Promise((resolve, reject) => {
425
+ function portClosed() {
426
+ reject(new Error(`Tab closed while handling ${debugContext}`));
427
+ }
428
+
429
+ lastPort.closeListeners.push(portClosed);
430
+
431
+ inner(lastPort)
432
+ .then(resolve, reject)
433
+ .finally(() => {
434
+ lastPort.closeListeners.splice(lastPort.closeListeners.indexOf(portClosed), 1);
435
+ });
436
+ });
437
+ }
438
+
474
439
  /**
475
440
  * Requests a random client to share its database connection with us.
476
441
  */
@@ -78,10 +78,6 @@ export class WorkerClient {
78
78
  return this.sync.setParams(params);
79
79
  }
80
80
 
81
- getWriteCheckpoint() {
82
- return this.sync.getWriteCheckpoint();
83
- }
84
-
85
81
  connect(options: ResolvedSyncOptions, schema: any) {
86
82
  return this.sync.connect(options, schema);
87
83
  }
@@ -92,6 +88,10 @@ export class WorkerClient {
92
88
  }
93
89
  }
94
90
 
91
+ requestCheckpoint(): Promise<bigint> {
92
+ return this.sync.requestCheckpoint();
93
+ }
94
+
95
95
  disconnect() {
96
96
  return this.sync.disconnect();
97
97
  }