@powersync/web 0.0.0-dev-20251129133952 → 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.
- package/dist/1807036ae51c10ee4d23.wasm +0 -0
- package/dist/{10072fe45f0a8fab0a0e.wasm → 307d8ce2280e3bae09d5.wasm} +0 -0
- package/dist/{6e435e51534839845554.wasm → cd8b9e8f4c87bf81c169.wasm} +0 -0
- package/dist/e797080f5ed0b5324166.wasm +0 -0
- package/dist/index.umd.js +137 -104
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +137 -110
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +15 -1
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js +2 -2
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js +2 -2
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js +2 -2
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +2 -2
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js +20 -23
- package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js.map +1 -1
- package/lib/src/db/PowerSyncDatabase.d.ts +1 -1
- package/lib/src/db/PowerSyncDatabase.js +4 -4
- package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +5 -0
- package/lib/src/db/adapters/AsyncDatabaseConnection.js +5 -0
- package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +6 -1
- package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +20 -5
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +5 -1
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +12 -5
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.d.ts +0 -4
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +3 -8
- package/lib/src/worker/sync/MockSyncService.d.ts +2 -0
- package/lib/src/worker/sync/MockSyncService.js +3 -0
- package/lib/src/worker/sync/MockSyncServiceTypes.d.ts +101 -0
- package/lib/src/worker/sync/MockSyncServiceTypes.js +1 -0
- package/lib/src/worker/sync/MockSyncServiceWorker.d.ts +56 -0
- package/lib/src/worker/sync/MockSyncServiceWorker.js +369 -0
- package/lib/src/worker/sync/SharedSyncImplementation.d.ts +6 -11
- package/lib/src/worker/sync/SharedSyncImplementation.js +73 -64
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -1
- package/lib/src/worker/sync/WorkerClient.d.ts +1 -3
- package/lib/src/worker/sync/WorkerClient.js +3 -27
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/db/PowerSyncDatabase.ts +13 -15
- package/src/db/adapters/AsyncDatabaseConnection.ts +5 -0
- package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +22 -5
- package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +16 -4
- package/src/db/sync/SharedWebStreamingSyncImplementation.ts +5 -11
- package/src/worker/sync/MockSyncService.ts +3 -0
- package/src/worker/sync/MockSyncServiceTypes.ts +71 -0
- package/src/worker/sync/MockSyncServiceWorker.ts +406 -0
- package/src/worker/sync/SharedSyncImplementation.ts +85 -78
- package/src/worker/sync/SharedSyncImplementation.worker.ts +1 -1
- package/src/worker/sync/WorkerClient.ts +4 -30
- package/dist/a730f7ca717b02234beb.wasm +0 -0
- package/dist/aa2f408d64445fed090e.wasm +0 -0
|
@@ -76,10 +76,6 @@ export type WrappedSyncPort = {
|
|
|
76
76
|
db?: DBAdapter;
|
|
77
77
|
currentSubscriptions: SubscribedStream[];
|
|
78
78
|
closeListeners: (() => void | Promise<void>)[];
|
|
79
|
-
/**
|
|
80
|
-
* If we can use Navigator locks to detect if the client has closed.
|
|
81
|
-
*/
|
|
82
|
-
isProtectedFromClose: boolean;
|
|
83
79
|
isClosing: boolean;
|
|
84
80
|
};
|
|
85
81
|
|
|
@@ -176,14 +172,26 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
176
172
|
/**
|
|
177
173
|
* Gets the last client port which we know is safe from unexpected closes.
|
|
178
174
|
*/
|
|
179
|
-
protected
|
|
180
|
-
// Find the last port which is
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
175
|
+
protected async getLastWrappedPort(): Promise<WrappedSyncPort | undefined> {
|
|
176
|
+
// Find the last port which is not closing
|
|
177
|
+
return await this.portMutex.runExclusive(() => {
|
|
178
|
+
for (let i = this.ports.length - 1; i >= 0; i--) {
|
|
179
|
+
if (!this.ports[i].isClosing) {
|
|
180
|
+
return this.ports[i];
|
|
181
|
+
}
|
|
184
182
|
}
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
return;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* In some very rare cases a specific tab might not respond to requests.
|
|
189
|
+
* This returns a random port which is not closing.
|
|
190
|
+
*/
|
|
191
|
+
protected async getRandomWrappedPort(): Promise<WrappedSyncPort | undefined> {
|
|
192
|
+
return await this.portMutex.runExclusive(() => {
|
|
193
|
+
return this.ports[Math.floor(Math.random() * this.ports.length)];
|
|
194
|
+
});
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
async waitForStatus(status: SyncStatusOptions): Promise<void> {
|
|
@@ -232,44 +240,51 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
232
240
|
async setParams(params: SharedSyncInitOptions) {
|
|
233
241
|
await this.portMutex.runExclusive(async () => {
|
|
234
242
|
this.collectActiveSubscriptions();
|
|
235
|
-
|
|
236
|
-
// Cannot modify already existing sync implementation params
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// First time setting params
|
|
241
|
-
this.syncParams = params;
|
|
242
|
-
if (params.streamOptions?.flags?.broadcastLogs) {
|
|
243
|
-
this.logger = this.broadCastLogger;
|
|
244
|
-
}
|
|
243
|
+
});
|
|
245
244
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return await this.openInternalDB();
|
|
251
|
-
},
|
|
252
|
-
logger: this.logger,
|
|
253
|
-
reOpenOnConnectionClosed: true
|
|
254
|
-
});
|
|
255
|
-
this.distributedDB = lockedAdapter;
|
|
256
|
-
await lockedAdapter.init();
|
|
257
|
-
|
|
258
|
-
lockedAdapter.registerListener({
|
|
259
|
-
databaseReOpened: () => {
|
|
260
|
-
// We may have missed some table updates while the database was closed.
|
|
261
|
-
// We can poke the crud in case we missed any updates.
|
|
262
|
-
this.connectionManager.syncStreamImplementation?.triggerCrudUpload();
|
|
263
|
-
}
|
|
264
|
-
});
|
|
245
|
+
if (this.syncParams) {
|
|
246
|
+
// Cannot modify already existing sync implementation params
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
265
249
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
250
|
+
// First time setting params
|
|
251
|
+
this.syncParams = params;
|
|
252
|
+
if (params.streamOptions?.flags?.broadcastLogs) {
|
|
253
|
+
this.logger = this.broadCastLogger;
|
|
254
|
+
}
|
|
270
255
|
|
|
271
|
-
|
|
256
|
+
const lockedAdapter = new LockedAsyncDatabaseAdapter({
|
|
257
|
+
name: params.dbParams.dbFilename,
|
|
258
|
+
openConnection: async () => {
|
|
259
|
+
// Gets a connection from the clients when a new connection is requested.
|
|
260
|
+
const db = await this.openInternalDB();
|
|
261
|
+
db.registerListener({
|
|
262
|
+
closing: () => {
|
|
263
|
+
lockedAdapter.reOpenInternalDB();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
return db;
|
|
267
|
+
},
|
|
268
|
+
logger: this.logger,
|
|
269
|
+
reOpenOnConnectionClosed: true
|
|
272
270
|
});
|
|
271
|
+
this.distributedDB = lockedAdapter;
|
|
272
|
+
await lockedAdapter.init();
|
|
273
|
+
|
|
274
|
+
lockedAdapter.registerListener({
|
|
275
|
+
databaseReOpened: () => {
|
|
276
|
+
// We may have missed some table updates while the database was closed.
|
|
277
|
+
// We can poke the crud in case we missed any updates.
|
|
278
|
+
this.connectionManager.syncStreamImplementation?.triggerCrudUpload();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
self.onerror = (event) => {
|
|
283
|
+
// Share any uncaught events on the broadcast logger
|
|
284
|
+
this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
this.iterateListeners((l) => l.initialized?.());
|
|
273
288
|
}
|
|
274
289
|
|
|
275
290
|
async dispose() {
|
|
@@ -303,7 +318,6 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
303
318
|
clientProvider: Comlink.wrap<AbstractSharedSyncClientProvider>(port),
|
|
304
319
|
currentSubscriptions: [],
|
|
305
320
|
closeListeners: [],
|
|
306
|
-
isProtectedFromClose: false,
|
|
307
321
|
isClosing: false
|
|
308
322
|
} satisfies WrappedSyncPort;
|
|
309
323
|
this.ports.push(portProvider);
|
|
@@ -409,7 +423,7 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
409
423
|
remote: new WebRemote(
|
|
410
424
|
{
|
|
411
425
|
invalidateCredentials: async () => {
|
|
412
|
-
const lastPort = this.
|
|
426
|
+
const lastPort = await this.getLastWrappedPort();
|
|
413
427
|
if (!lastPort) {
|
|
414
428
|
throw new Error('No client port found to invalidate credentials');
|
|
415
429
|
}
|
|
@@ -421,7 +435,7 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
421
435
|
}
|
|
422
436
|
},
|
|
423
437
|
fetchCredentials: async () => {
|
|
424
|
-
const lastPort = this.
|
|
438
|
+
const lastPort = await this.getLastWrappedPort();
|
|
425
439
|
if (!lastPort) {
|
|
426
440
|
throw new Error('No client port found to fetch credentials');
|
|
427
441
|
}
|
|
@@ -447,7 +461,7 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
447
461
|
this.logger
|
|
448
462
|
),
|
|
449
463
|
uploadCrud: async () => {
|
|
450
|
-
const lastPort = this.
|
|
464
|
+
const lastPort = await this.getLastWrappedPort();
|
|
451
465
|
if (!lastPort) {
|
|
452
466
|
throw new Error('No client port found to upload crud');
|
|
453
467
|
}
|
|
@@ -483,12 +497,17 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
483
497
|
protected async openInternalDB() {
|
|
484
498
|
while (true) {
|
|
485
499
|
try {
|
|
486
|
-
const
|
|
487
|
-
if (!
|
|
500
|
+
const client = await this.getRandomWrappedPort();
|
|
501
|
+
if (!client) {
|
|
488
502
|
// Should not really happen in practice
|
|
489
503
|
throw new Error(`Could not open DB connection since no client is connected.`);
|
|
490
504
|
}
|
|
491
505
|
|
|
506
|
+
// Fail-safe timeout for opening a database connection.
|
|
507
|
+
const timeout = setTimeout(() => {
|
|
508
|
+
abortController.abort();
|
|
509
|
+
}, 10_000);
|
|
510
|
+
|
|
492
511
|
/**
|
|
493
512
|
* Handle cases where the client might close while opening a connection.
|
|
494
513
|
*/
|
|
@@ -498,21 +517,20 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
498
517
|
};
|
|
499
518
|
|
|
500
519
|
const removeCloseListener = () => {
|
|
501
|
-
const index =
|
|
520
|
+
const index = client.closeListeners.indexOf(closeListener);
|
|
502
521
|
if (index >= 0) {
|
|
503
|
-
|
|
522
|
+
client.closeListeners.splice(index, 1);
|
|
504
523
|
}
|
|
505
524
|
};
|
|
506
525
|
|
|
507
|
-
|
|
526
|
+
client.closeListeners.push(closeListener);
|
|
508
527
|
|
|
509
|
-
const workerPort = await withAbort(
|
|
510
|
-
() =>
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
});
|
|
528
|
+
const workerPort = await withAbort(() => client.clientProvider.getDBWorkerPort(), abortController.signal).catch(
|
|
529
|
+
(ex) => {
|
|
530
|
+
removeCloseListener();
|
|
531
|
+
throw ex;
|
|
532
|
+
}
|
|
533
|
+
);
|
|
516
534
|
|
|
517
535
|
const remote = Comlink.wrap<OpenAsyncDatabaseConnection>(workerPort);
|
|
518
536
|
const identifier = this.syncParams!.dbParams.dbFilename;
|
|
@@ -528,6 +546,8 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
528
546
|
removeCloseListener();
|
|
529
547
|
});
|
|
530
548
|
|
|
549
|
+
clearTimeout(timeout);
|
|
550
|
+
|
|
531
551
|
const wrapped = new WorkerWrappedAsyncDatabaseConnection({
|
|
532
552
|
remote,
|
|
533
553
|
baseConnection: db,
|
|
@@ -536,15 +556,15 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
536
556
|
// that and ensure pending requests are aborted when the tab is closed.
|
|
537
557
|
remoteCanCloseUnexpectedly: true
|
|
538
558
|
});
|
|
539
|
-
|
|
559
|
+
client.closeListeners.push(async () => {
|
|
540
560
|
this.logger.info('Aborting open connection because associated tab closed.');
|
|
541
561
|
/**
|
|
542
562
|
* Don't await this close operation. It might never resolve if the tab is closed.
|
|
543
|
-
* We
|
|
544
|
-
*
|
|
563
|
+
* We mark the remote as closed first, this will reject any pending requests.
|
|
564
|
+
* We then call close. The close operation is configured to fire-and-forget, the main promise will reject immediately.
|
|
545
565
|
*/
|
|
546
|
-
wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
|
|
547
566
|
wrapped.markRemoteClosed();
|
|
567
|
+
wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
|
|
548
568
|
});
|
|
549
569
|
|
|
550
570
|
return wrapped;
|
|
@@ -563,19 +583,6 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
|
|
|
563
583
|
this.syncStatus = new SyncStatus(status);
|
|
564
584
|
this.ports.forEach((p) => p.clientProvider.statusChanged(status));
|
|
565
585
|
}
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* A function only used for unit tests which updates the internal
|
|
569
|
-
* sync stream client and all tab client's sync status
|
|
570
|
-
*/
|
|
571
|
-
async _testUpdateAllStatuses(status: SyncStatusOptions) {
|
|
572
|
-
if (!this.connectionManager.syncStreamImplementation) {
|
|
573
|
-
throw new Error('Cannot update status without a sync stream implementation');
|
|
574
|
-
}
|
|
575
|
-
// Only assigning, don't call listeners for this test
|
|
576
|
-
this.connectionManager.syncStreamImplementation!.syncStatus = new SyncStatus(status);
|
|
577
|
-
this.updateAllStatuses(status);
|
|
578
|
-
}
|
|
579
586
|
}
|
|
580
587
|
|
|
581
588
|
/**
|
|
@@ -10,5 +10,5 @@ const sharedSyncImplementation = new SharedSyncImplementation();
|
|
|
10
10
|
|
|
11
11
|
_self.onconnect = async function (event: MessageEvent<string>) {
|
|
12
12
|
const port = event.ports[0];
|
|
13
|
-
|
|
13
|
+
new WorkerClient(sharedSyncImplementation, port);
|
|
14
14
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILogLevel, PowerSyncConnectionOptions, SubscribedStream
|
|
1
|
+
import { ILogLevel, PowerSyncConnectionOptions, SubscribedStream } from '@powersync/common';
|
|
2
2
|
import * as Comlink from 'comlink';
|
|
3
3
|
import { getNavigatorLocks } from '../../shared/navigator';
|
|
4
4
|
import {
|
|
@@ -24,9 +24,6 @@ export class WorkerClient {
|
|
|
24
24
|
private readonly port: MessagePort
|
|
25
25
|
) {
|
|
26
26
|
Comlink.expose(this, this.port);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async initialize() {
|
|
30
27
|
/**
|
|
31
28
|
* Adds an extra listener which can remove this port
|
|
32
29
|
* from the list of monitored ports.
|
|
@@ -37,16 +34,6 @@ export class WorkerClient {
|
|
|
37
34
|
await this.removePort();
|
|
38
35
|
}
|
|
39
36
|
});
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Keep a reference to the resolved port promise.
|
|
43
|
-
* The init timing is difficult to predict due to the async message passing.
|
|
44
|
-
* We only want to use a port if we are know it's been protected from being closed.
|
|
45
|
-
* The lock based close signal will be added asynchronously. We need to use the
|
|
46
|
-
* added port once the lock is configured.
|
|
47
|
-
*/
|
|
48
|
-
this.resolvedPortPromise = this.sync.addPort(this.port);
|
|
49
|
-
this.resolvedPort = await this.resolvedPortPromise;
|
|
50
37
|
}
|
|
51
38
|
|
|
52
39
|
private async removePort() {
|
|
@@ -70,18 +57,9 @@ export class WorkerClient {
|
|
|
70
57
|
* it can consider the connection to be closed.
|
|
71
58
|
*/
|
|
72
59
|
async addLockBasedCloseSignal(name: string) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} else {
|
|
77
|
-
const wrappedPort = await this.resolvedPortPromise;
|
|
78
|
-
/**
|
|
79
|
-
* The client registered a navigator lock. We now can guarantee detecting if the client has closed.
|
|
80
|
-
* E.g. before this point: It's possible some ports might have been created and closed before the
|
|
81
|
-
* lock based close signal is added. We should not trust those ports.
|
|
82
|
-
*/
|
|
83
|
-
wrappedPort.isProtectedFromClose = true;
|
|
84
|
-
}
|
|
60
|
+
// Only add the port once the lock has been obtained on the client.
|
|
61
|
+
this.resolvedPort = await this.sync.addPort(this.port);
|
|
62
|
+
// Don't await this lock request
|
|
85
63
|
getNavigatorLocks().request(name, async () => {
|
|
86
64
|
await this.removePort();
|
|
87
65
|
});
|
|
@@ -121,8 +99,4 @@ export class WorkerClient {
|
|
|
121
99
|
disconnect() {
|
|
122
100
|
return this.sync.disconnect();
|
|
123
101
|
}
|
|
124
|
-
|
|
125
|
-
async _testUpdateAllStatuses(status: SyncStatusOptions) {
|
|
126
|
-
return this.sync._testUpdateAllStatuses(status);
|
|
127
|
-
}
|
|
128
102
|
}
|
|
Binary file
|
|
Binary file
|