@powersync/web 0.0.0-dev-20250528152729 → 0.0.0-dev-20250529141956

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.
@@ -69,7 +69,6 @@ export declare class PowerSyncDatabase extends AbstractPowerSyncDatabase {
69
69
  * multiple tabs are not enabled.
70
70
  */
71
71
  close(options?: PowerSyncCloseOptions): Promise<void>;
72
- protected connectExclusive(callback: () => Promise<void>): Promise<void>;
73
72
  protected generateBucketStorageAdapter(): BucketStorageAdapter;
74
73
  protected runExclusive<T>(cb: () => Promise<T>): Promise<any>;
75
74
  protected generateSyncStreamImplementation(connector: PowerSyncBackendConnector, options: RequiredAdditionalConnectionOptions): StreamingSyncImplementation;
@@ -81,9 +81,6 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
81
81
  disconnect: options.disconnect ?? !this.resolvedFlags.enableMultiTabs
82
82
  });
83
83
  }
84
- async connectExclusive(callback) {
85
- await this.runExclusive(callback);
86
- }
87
84
  generateBucketStorageAdapter() {
88
85
  return new SqliteBucketStorage(this.database, AbstractPowerSyncDatabase.transactionMutex);
89
86
  }
@@ -5,6 +5,7 @@ import { WebStreamingSyncImplementation, WebStreamingSyncImplementationOptions }
5
5
  import { ResolvedWebSQLOpenOptions } from '../../db/adapters/web-sql-flags';
6
6
  import { AbstractSharedSyncClientProvider } from './AbstractSharedSyncClientProvider';
7
7
  /**
8
+ * @internal
8
9
  * Manual message events for shared sync clients
9
10
  */
10
11
  export declare enum SharedSyncClientEvent {
@@ -15,6 +16,9 @@ export declare enum SharedSyncClientEvent {
15
16
  CLOSE_CLIENT = "close-client",
16
17
  CLOSE_ACK = "close-ack"
17
18
  }
19
+ /**
20
+ * @internal
21
+ */
18
22
  export type ManualSharedSyncPayload = {
19
23
  event: SharedSyncClientEvent;
20
24
  data: any;
@@ -93,7 +97,7 @@ export declare class SharedSyncImplementation extends BaseObserver<SharedSyncImp
93
97
  * Removes a message port client from this manager's managed
94
98
  * clients.
95
99
  */
96
- removePort(port: MessagePort): Promise<(() => void) | undefined>;
100
+ removePort(port: MessagePort): Promise<() => void>;
97
101
  triggerCrudUpload(): void;
98
102
  hasCompletedSync(): Promise<boolean>;
99
103
  getWriteCheckpoint(): Promise<string>;
@@ -7,6 +7,7 @@ import { LockedAsyncDatabaseAdapter } from '../../db/adapters/LockedAsyncDatabas
7
7
  import { WorkerWrappedAsyncDatabaseConnection } from '../../db/adapters/WorkerWrappedAsyncDatabaseConnection';
8
8
  import { BroadcastLogger } from './BroadcastLogger';
9
9
  /**
10
+ * @internal
10
11
  * Manual message events for shared sync clients
11
12
  */
12
13
  export var SharedSyncClientEvent;
@@ -61,21 +62,23 @@ export class SharedSyncImplementation extends BaseObserver {
61
62
  this.syncStatus = new SyncStatus({});
62
63
  this.broadCastLogger = new BroadcastLogger(this.ports);
63
64
  this.connectionManager = new ConnectionManager({
64
- createSyncImplementation: async (connector, options) => {
65
- await this.waitForReady();
66
- if (!this.dbAdapter) {
67
- await this.openInternalDB();
68
- }
69
- const sync = this.generateStreamingImplementation();
70
- const onDispose = sync.registerListener({
71
- statusChanged: (status) => {
72
- this.updateAllStatuses(status.toJSON());
65
+ createSyncImplementation: async () => {
66
+ return this.portMutex.runExclusive(async () => {
67
+ await this.waitForReady();
68
+ if (!this.dbAdapter) {
69
+ await this.openInternalDB();
73
70
  }
71
+ const sync = this.generateStreamingImplementation();
72
+ const onDispose = sync.registerListener({
73
+ statusChanged: (status) => {
74
+ this.updateAllStatuses(status.toJSON());
75
+ }
76
+ });
77
+ return {
78
+ sync,
79
+ onDispose
80
+ };
74
81
  });
75
- return {
76
- sync,
77
- onDispose
78
- };
79
82
  },
80
83
  logger: this.logger
81
84
  });
@@ -109,12 +112,14 @@ export class SharedSyncImplementation extends BaseObserver {
109
112
  async setParams(params) {
110
113
  await this.portMutex.runExclusive(async () => {
111
114
  if (this.syncParams) {
115
+ // Cannot modify already existing sync implementation params
116
+ // But we can ask for a DB adapter, if required, at this point.
112
117
  if (!this.dbAdapter) {
113
118
  await this.openInternalDB();
114
119
  }
115
- // Cannot modify already existing sync implementation
116
120
  return;
117
121
  }
122
+ // First time setting params
118
123
  this.syncParams = params;
119
124
  if (params.streamOptions?.flags?.broadcastLogs) {
120
125
  this.logger = this.broadCastLogger;
@@ -141,16 +146,11 @@ export class SharedSyncImplementation extends BaseObserver {
141
146
  * connects.
142
147
  */
143
148
  async connect(options) {
144
- await this.portMutex.runExclusive(async () => {
145
- // Keep track of the last connect options if we need to reconnect due to a lost client
146
- this.lastConnectOptions = options;
147
- return this.connectionManager.connect(CONNECTOR_PLACEHOLDER, options);
148
- });
149
+ this.lastConnectOptions = options;
150
+ return this.connectionManager.connect(CONNECTOR_PLACEHOLDER, options);
149
151
  }
150
152
  async disconnect() {
151
- await this.portMutex.runExclusive(async () => {
152
- await this.connectionManager.disconnect();
153
- });
153
+ return this.connectionManager.disconnect();
154
154
  }
155
155
  /**
156
156
  * Adds a new client tab's message port to the list of connected ports
@@ -174,11 +174,14 @@ export class SharedSyncImplementation extends BaseObserver {
174
174
  * clients.
175
175
  */
176
176
  async removePort(port) {
177
- return await this.portMutex.runExclusive(async () => {
177
+ // Remove the port within a mutex context.
178
+ // Warns if the port is not found. This should not happen in practice.
179
+ // We return early if the port is not found.
180
+ const { trackedPort, shouldReconnect } = await this.portMutex.runExclusive(async () => {
178
181
  const index = this.ports.findIndex((p) => p.port == port);
179
182
  if (index < 0) {
180
183
  this.logger.warn(`Could not remove port ${port} since it is not present in active ports.`);
181
- return;
184
+ return {};
182
185
  }
183
186
  const trackedPort = this.ports[index];
184
187
  // Remove from the list of active ports
@@ -193,26 +196,35 @@ export class SharedSyncImplementation extends BaseObserver {
193
196
  }
194
197
  });
195
198
  const shouldReconnect = !!this.connectionManager.syncStreamImplementation && this.ports.length > 0;
196
- if (this.dbAdapter && this.dbAdapter == trackedPort.db) {
197
- if (shouldReconnect) {
198
- await this.connectionManager.disconnect();
199
- }
200
- // Clearing the adapter will result in a new one being opened in connect
201
- this.dbAdapter = null;
202
- if (shouldReconnect) {
203
- await this.connectionManager.connect(CONNECTOR_PLACEHOLDER, this.lastConnectOptions);
204
- }
199
+ return {
200
+ shouldReconnect,
201
+ trackedPort
202
+ };
203
+ });
204
+ if (!trackedPort) {
205
+ // We could not find the port to remove
206
+ return () => { };
207
+ }
208
+ if (this.dbAdapter && this.dbAdapter == trackedPort.db) {
209
+ if (shouldReconnect) {
210
+ await this.connectionManager.disconnect();
205
211
  }
206
- if (trackedPort.db) {
207
- await trackedPort.db.close();
212
+ // Clearing the adapter will result in a new one being opened in connect
213
+ this.dbAdapter = null;
214
+ if (shouldReconnect) {
215
+ await this.connectionManager.connect(CONNECTOR_PLACEHOLDER, this.lastConnectOptions);
208
216
  }
209
- this.logger.debug(`Port ${port} removed from shared sync implementation.`);
210
- // Release proxy
211
- return () => trackedPort.clientProvider[Comlink.releaseProxy]();
212
- });
217
+ }
218
+ if (trackedPort.db) {
219
+ await trackedPort.db.close();
220
+ }
221
+ // Release proxy
222
+ return () => trackedPort.clientProvider[Comlink.releaseProxy]();
213
223
  }
214
224
  triggerCrudUpload() {
215
- this.waitForReady().then(() => this.connectionManager.syncStreamImplementation?.triggerCrudUpload());
225
+ this.withSyncImplementation(async (sync) => {
226
+ sync.triggerCrudUpload();
227
+ });
216
228
  }
217
229
  async hasCompletedSync() {
218
230
  return this.withSyncImplementation(async (sync) => {
@@ -340,7 +352,7 @@ export class SharedSyncImplementation extends BaseObserver {
340
352
  * A function only used for unit tests which updates the internal
341
353
  * sync stream client and all tab client's sync status
342
354
  */
343
- _testUpdateAllStatuses(status) {
355
+ async _testUpdateAllStatuses(status) {
344
356
  if (!this.connectionManager.syncStreamImplementation) {
345
357
  // This is just for testing purposes
346
358
  this.connectionManager.syncStreamImplementation = this.generateStreamingImplementation();