@powersync/web 0.0.0-dev-20251127205344 → 0.0.0-dev-20251129133952
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/index.umd.js +243 -117
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +243 -103
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +4 -4
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +3 -0
- package/lib/src/db/adapters/AsyncDatabaseConnection.js +6 -1
- package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +9 -0
- package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +46 -8
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +3 -2
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.d.ts +1 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +42 -23
- package/lib/src/worker/sync/SharedSyncImplementation.d.ts +18 -3
- package/lib/src/worker/sync/SharedSyncImplementation.js +137 -80
- package/lib/src/worker/sync/WorkerClient.d.ts +3 -2
- package/lib/src/worker/sync/WorkerClient.js +24 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/db/adapters/AsyncDatabaseConnection.ts +7 -0
- package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +54 -9
- package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +3 -2
- package/src/db/sync/SharedWebStreamingSyncImplementation.ts +51 -29
- package/src/worker/sync/SharedSyncImplementation.ts +166 -92
- package/src/worker/sync/WorkerClient.ts +23 -2
package/dist/index.umd.js
CHANGED
|
@@ -38680,7 +38680,15 @@ class AbstractWebSQLOpenFactory {
|
|
|
38680
38680
|
|
|
38681
38681
|
"use strict";
|
|
38682
38682
|
__webpack_require__.r(__webpack_exports__);
|
|
38683
|
-
|
|
38683
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
38684
|
+
/* harmony export */ ConnectionClosedError: () => (/* binding */ ConnectionClosedError)
|
|
38685
|
+
/* harmony export */ });
|
|
38686
|
+
class ConnectionClosedError extends Error {
|
|
38687
|
+
constructor(message) {
|
|
38688
|
+
super(message);
|
|
38689
|
+
this.name = 'ConnectionClosedError';
|
|
38690
|
+
}
|
|
38691
|
+
}
|
|
38684
38692
|
|
|
38685
38693
|
|
|
38686
38694
|
/***/ }),
|
|
@@ -38699,8 +38707,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
38699
38707
|
/* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
|
|
38700
38708
|
/* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_powersync_common__WEBPACK_IMPORTED_MODULE_0__);
|
|
38701
38709
|
/* harmony import */ var _shared_navigator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../..//shared/navigator */ "./lib/src/shared/navigator.js");
|
|
38702
|
-
/* harmony import */ var
|
|
38703
|
-
/* harmony import */ var
|
|
38710
|
+
/* harmony import */ var _AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./AsyncDatabaseConnection */ "./lib/src/db/adapters/AsyncDatabaseConnection.js");
|
|
38711
|
+
/* harmony import */ var _WorkerWrappedAsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./WorkerWrappedAsyncDatabaseConnection */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
|
|
38712
|
+
/* harmony import */ var _wa_sqlite_WASQLiteConnection__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./wa-sqlite/WASQLiteConnection */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
|
|
38713
|
+
|
|
38704
38714
|
|
|
38705
38715
|
|
|
38706
38716
|
|
|
@@ -38722,6 +38732,8 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38722
38732
|
_config = null;
|
|
38723
38733
|
pendingAbortControllers;
|
|
38724
38734
|
requiresHolds;
|
|
38735
|
+
requiresReOpen;
|
|
38736
|
+
databaseOpenPromise = null;
|
|
38725
38737
|
closing;
|
|
38726
38738
|
closed;
|
|
38727
38739
|
constructor(options) {
|
|
@@ -38733,6 +38745,7 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38733
38745
|
this.closed = false;
|
|
38734
38746
|
this.closing = false;
|
|
38735
38747
|
this.requiresHolds = null;
|
|
38748
|
+
this.requiresReOpen = false;
|
|
38736
38749
|
// Set the name if provided. We can query for the name if not available yet
|
|
38737
38750
|
this.debugMode = options.debugMode ?? false;
|
|
38738
38751
|
if (this.debugMode) {
|
|
@@ -38771,16 +38784,26 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38771
38784
|
async init() {
|
|
38772
38785
|
return this.initPromise;
|
|
38773
38786
|
}
|
|
38774
|
-
async
|
|
38787
|
+
async openInternalDB() {
|
|
38788
|
+
// Dispose any previous table change listener.
|
|
38789
|
+
this._disposeTableChangeListener?.();
|
|
38790
|
+
this._disposeTableChangeListener = null;
|
|
38791
|
+
const isReOpen = !!this._db;
|
|
38775
38792
|
this._db = await this.options.openConnection();
|
|
38776
38793
|
await this._db.init();
|
|
38777
38794
|
this._config = await this._db.getConfig();
|
|
38778
38795
|
await this.registerOnChangeListener(this._db);
|
|
38779
|
-
|
|
38796
|
+
if (isReOpen) {
|
|
38797
|
+
this.iterateListeners((cb) => cb.databaseReOpened?.());
|
|
38798
|
+
}
|
|
38780
38799
|
/**
|
|
38781
38800
|
* This is only required for the long-lived shared IndexedDB connections.
|
|
38782
38801
|
*/
|
|
38783
|
-
this.requiresHolds = this._config.vfs ==
|
|
38802
|
+
this.requiresHolds = this._config.vfs == _wa_sqlite_WASQLiteConnection__WEBPACK_IMPORTED_MODULE_4__.WASQLiteVFS.IDBBatchAtomicVFS;
|
|
38803
|
+
}
|
|
38804
|
+
async _init() {
|
|
38805
|
+
await this.openInternalDB();
|
|
38806
|
+
this.iterateListeners((cb) => cb.initialized?.());
|
|
38784
38807
|
}
|
|
38785
38808
|
getConfiguration() {
|
|
38786
38809
|
if (!this._config) {
|
|
@@ -38793,7 +38816,7 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38793
38816
|
await this.initPromise;
|
|
38794
38817
|
}
|
|
38795
38818
|
async shareConnection() {
|
|
38796
|
-
if (false == this._db instanceof
|
|
38819
|
+
if (false == this._db instanceof _WorkerWrappedAsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_3__.WorkerWrappedAsyncDatabaseConnection) {
|
|
38797
38820
|
throw new Error(`Only worker connections can be shared`);
|
|
38798
38821
|
}
|
|
38799
38822
|
return this._db.shareConnection();
|
|
@@ -38847,13 +38870,13 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38847
38870
|
async readLock(fn, options) {
|
|
38848
38871
|
await this.waitForInitialized();
|
|
38849
38872
|
return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })), {
|
|
38850
|
-
timeoutMs: options?.timeoutMs
|
|
38873
|
+
timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
|
|
38851
38874
|
});
|
|
38852
38875
|
}
|
|
38853
38876
|
async writeLock(fn, options) {
|
|
38854
38877
|
await this.waitForInitialized();
|
|
38855
38878
|
return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })), {
|
|
38856
|
-
timeoutMs: options?.timeoutMs
|
|
38879
|
+
timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
|
|
38857
38880
|
});
|
|
38858
38881
|
}
|
|
38859
38882
|
async acquireLock(callback, options) {
|
|
@@ -38864,7 +38887,7 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38864
38887
|
const abortController = new AbortController();
|
|
38865
38888
|
this.pendingAbortControllers.add(abortController);
|
|
38866
38889
|
const { timeoutMs } = options ?? {};
|
|
38867
|
-
const
|
|
38890
|
+
const timeoutId = timeoutMs
|
|
38868
38891
|
? setTimeout(() => {
|
|
38869
38892
|
abortController.abort(`Timeout after ${timeoutMs}ms`);
|
|
38870
38893
|
this.pendingAbortControllers.delete(abortController);
|
|
@@ -38872,13 +38895,37 @@ class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MOD
|
|
|
38872
38895
|
: null;
|
|
38873
38896
|
return (0,_shared_navigator__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`db-lock-${this._dbIdentifier}`, { signal: abortController.signal }, async () => {
|
|
38874
38897
|
this.pendingAbortControllers.delete(abortController);
|
|
38875
|
-
if (
|
|
38876
|
-
clearTimeout(
|
|
38898
|
+
if (timeoutId) {
|
|
38899
|
+
clearTimeout(timeoutId);
|
|
38877
38900
|
}
|
|
38878
|
-
|
|
38901
|
+
let holdId = null;
|
|
38879
38902
|
try {
|
|
38903
|
+
// The database is being opened in the background. Wait for it here.
|
|
38904
|
+
if (this.databaseOpenPromise) {
|
|
38905
|
+
try {
|
|
38906
|
+
await this.databaseOpenPromise;
|
|
38907
|
+
}
|
|
38908
|
+
catch (ex) {
|
|
38909
|
+
// This will cause a retry of opening the database.
|
|
38910
|
+
const wrappedError = new _AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_2__.ConnectionClosedError('Could not open database');
|
|
38911
|
+
wrappedError.cause = ex;
|
|
38912
|
+
throw wrappedError;
|
|
38913
|
+
}
|
|
38914
|
+
}
|
|
38915
|
+
holdId = this.requiresHolds ? await this.baseDB.markHold() : null;
|
|
38880
38916
|
return await callback();
|
|
38881
38917
|
}
|
|
38918
|
+
catch (ex) {
|
|
38919
|
+
if (ex instanceof _AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_2__.ConnectionClosedError) {
|
|
38920
|
+
if (this.options.reOpenOnConnectionClosed && !this.databaseOpenPromise && !this.closing) {
|
|
38921
|
+
// Immediately re-open the database. We need to miss as little table updates as possible.
|
|
38922
|
+
this.databaseOpenPromise = this.openInternalDB().finally(() => {
|
|
38923
|
+
this.databaseOpenPromise = null;
|
|
38924
|
+
});
|
|
38925
|
+
}
|
|
38926
|
+
}
|
|
38927
|
+
throw ex;
|
|
38928
|
+
}
|
|
38882
38929
|
finally {
|
|
38883
38930
|
if (holdId) {
|
|
38884
38931
|
await this.baseDB.releaseHold(holdId);
|
|
@@ -39102,6 +39149,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
39102
39149
|
/* harmony export */ });
|
|
39103
39150
|
/* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
|
|
39104
39151
|
/* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(comlink__WEBPACK_IMPORTED_MODULE_0__);
|
|
39152
|
+
/* harmony import */ var _AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./AsyncDatabaseConnection */ "./lib/src/db/adapters/AsyncDatabaseConnection.js");
|
|
39153
|
+
|
|
39105
39154
|
|
|
39106
39155
|
/**
|
|
39107
39156
|
* Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
|
|
@@ -39149,12 +39198,12 @@ class WorkerWrappedAsyncDatabaseConnection {
|
|
|
39149
39198
|
if (controller) {
|
|
39150
39199
|
return new Promise((resolve, reject) => {
|
|
39151
39200
|
if (controller.signal.aborted) {
|
|
39152
|
-
reject(new
|
|
39201
|
+
reject(new _AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_1__.ConnectionClosedError('Called operation on closed remote'));
|
|
39153
39202
|
// Don't run the operation if we're going to reject
|
|
39154
39203
|
return;
|
|
39155
39204
|
}
|
|
39156
39205
|
function handleAbort() {
|
|
39157
|
-
reject(new
|
|
39206
|
+
reject(new _AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_1__.ConnectionClosedError('Remote peer closed with request in flight'));
|
|
39158
39207
|
}
|
|
39159
39208
|
function completePromise(action) {
|
|
39160
39209
|
controller.signal.removeEventListener('abort', handleAbort);
|
|
@@ -40172,7 +40221,19 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
|
|
|
40172
40221
|
type: undefined
|
|
40173
40222
|
}).port;
|
|
40174
40223
|
}
|
|
40224
|
+
/**
|
|
40225
|
+
* Pass along any sync status updates to this listener
|
|
40226
|
+
*/
|
|
40227
|
+
this.clientProvider = new SharedSyncClientProvider(this.webOptions, (status) => {
|
|
40228
|
+
this.updateSyncStatus(status);
|
|
40229
|
+
}, options.db);
|
|
40175
40230
|
this.syncManager = comlink__WEBPACK_IMPORTED_MODULE_0__.wrap(this.messagePort);
|
|
40231
|
+
/**
|
|
40232
|
+
* The sync worker will call this client provider when it needs
|
|
40233
|
+
* to fetch credentials or upload data.
|
|
40234
|
+
* This performs bi-directional method calling.
|
|
40235
|
+
*/
|
|
40236
|
+
comlink__WEBPACK_IMPORTED_MODULE_0__.expose(this.clientProvider, this.messagePort);
|
|
40176
40237
|
this.syncManager.setLogLevel(this.logger.getLevel());
|
|
40177
40238
|
this.triggerCrudUpload = this.syncManager.triggerCrudUpload;
|
|
40178
40239
|
/**
|
|
@@ -40181,22 +40242,41 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
|
|
|
40181
40242
|
* DB worker, but a port to the DB worker can be transferred to the
|
|
40182
40243
|
* sync worker.
|
|
40183
40244
|
*/
|
|
40184
|
-
|
|
40185
|
-
|
|
40186
|
-
|
|
40187
|
-
|
|
40188
|
-
|
|
40189
|
-
|
|
40190
|
-
|
|
40191
|
-
|
|
40245
|
+
this.isInitialized = this._init();
|
|
40246
|
+
}
|
|
40247
|
+
async _init() {
|
|
40248
|
+
/**
|
|
40249
|
+
* The general flow of initialization is:
|
|
40250
|
+
* - The client requests a unique navigator lock.
|
|
40251
|
+
* - Once the lock is acquired, we register the lock with the shared worker.
|
|
40252
|
+
* - The shared worker can then request the same lock. The client has been closed if the shared worker can acquire the lock.
|
|
40253
|
+
* - Once the shared worker knows the client's lock, we can guarentee that the shared worker will detect if the client has been closed.
|
|
40254
|
+
* - This makes the client safe for the shared worker to use.
|
|
40255
|
+
* - The client side lock is held until the client is disposed.
|
|
40256
|
+
* - We resolve the top-level promise after the lock has been registered with the shared worker.
|
|
40257
|
+
* - The client sends the params to the shared worker after locks have been registered.
|
|
40258
|
+
*/
|
|
40259
|
+
await new Promise((resolve) => {
|
|
40260
|
+
// Request a random lock until this client is disposed. The name of the lock is sent to the shared worker, which
|
|
40261
|
+
// will also attempt to acquire it. Since the lock is returned when the tab is closed, this allows the share worker
|
|
40262
|
+
// to free resources associated with this tab.
|
|
40263
|
+
// We take hold of this lock as soon-as-possible in order to cater for potentially closed tabs.
|
|
40264
|
+
(0,_shared_navigator__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`tab-close-signal-${crypto.randomUUID()}`, async (lock) => {
|
|
40265
|
+
if (this.abortOnClose.signal.aborted) {
|
|
40266
|
+
return;
|
|
40267
|
+
}
|
|
40192
40268
|
// Awaiting here ensures the worker is waiting for the lock
|
|
40193
40269
|
await this.syncManager.addLockBasedCloseSignal(lock.name);
|
|
40270
|
+
// The lock has been registered, we can continue with the initialization
|
|
40271
|
+
resolve();
|
|
40194
40272
|
await new Promise((r) => {
|
|
40195
40273
|
this.abortOnClose.signal.onabort = () => r();
|
|
40196
40274
|
});
|
|
40197
|
-
}
|
|
40275
|
+
});
|
|
40198
40276
|
});
|
|
40199
|
-
|
|
40277
|
+
const { crudUploadThrottleMs, identifier, retryDelayMs } = this.options;
|
|
40278
|
+
const flags = { ...this.webOptions.flags, workers: undefined };
|
|
40279
|
+
await this.syncManager.setParams({
|
|
40200
40280
|
dbParams: this.dbAdapter.getConfiguration(),
|
|
40201
40281
|
streamOptions: {
|
|
40202
40282
|
crudUploadThrottleMs,
|
|
@@ -40204,19 +40284,7 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
|
|
|
40204
40284
|
retryDelayMs,
|
|
40205
40285
|
flags: flags
|
|
40206
40286
|
}
|
|
40207
|
-
}, options.subscriptions);
|
|
40208
|
-
/**
|
|
40209
|
-
* Pass along any sync status updates to this listener
|
|
40210
|
-
*/
|
|
40211
|
-
this.clientProvider = new SharedSyncClientProvider(this.webOptions, (status) => {
|
|
40212
|
-
this.iterateListeners((l) => this.updateSyncStatus(status));
|
|
40213
|
-
}, options.db);
|
|
40214
|
-
/**
|
|
40215
|
-
* The sync worker will call this client provider when it needs
|
|
40216
|
-
* to fetch credentials or upload data.
|
|
40217
|
-
* This performs bi-directional method calling.
|
|
40218
|
-
*/
|
|
40219
|
-
comlink__WEBPACK_IMPORTED_MODULE_0__.expose(this.clientProvider, this.messagePort);
|
|
40287
|
+
}, this.options.subscriptions);
|
|
40220
40288
|
}
|
|
40221
40289
|
/**
|
|
40222
40290
|
* Starts the sync process, this effectively acts as a call to
|
|
@@ -40774,7 +40842,6 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40774
40842
|
statusListener;
|
|
40775
40843
|
fetchCredentialsController;
|
|
40776
40844
|
uploadDataController;
|
|
40777
|
-
dbAdapter;
|
|
40778
40845
|
syncParams;
|
|
40779
40846
|
logger;
|
|
40780
40847
|
lastConnectOptions;
|
|
@@ -40783,10 +40850,10 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40783
40850
|
connectionManager;
|
|
40784
40851
|
syncStatus;
|
|
40785
40852
|
broadCastLogger;
|
|
40853
|
+
distributedDB;
|
|
40786
40854
|
constructor() {
|
|
40787
40855
|
super();
|
|
40788
40856
|
this.ports = [];
|
|
40789
|
-
this.dbAdapter = null;
|
|
40790
40857
|
this.syncParams = null;
|
|
40791
40858
|
this.logger = (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.createLogger)('shared-sync');
|
|
40792
40859
|
this.lastConnectOptions = undefined;
|
|
@@ -40799,26 +40866,23 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40799
40866
|
}
|
|
40800
40867
|
});
|
|
40801
40868
|
});
|
|
40869
|
+
// Should be configured once we get params
|
|
40870
|
+
this.distributedDB = null;
|
|
40802
40871
|
this.syncStatus = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SyncStatus({});
|
|
40803
40872
|
this.broadCastLogger = new _BroadcastLogger__WEBPACK_IMPORTED_MODULE_7__.BroadcastLogger(this.ports);
|
|
40804
40873
|
this.connectionManager = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionManager({
|
|
40805
40874
|
createSyncImplementation: async () => {
|
|
40806
|
-
|
|
40807
|
-
|
|
40808
|
-
|
|
40809
|
-
|
|
40875
|
+
await this.waitForReady();
|
|
40876
|
+
const sync = this.generateStreamingImplementation();
|
|
40877
|
+
const onDispose = sync.registerListener({
|
|
40878
|
+
statusChanged: (status) => {
|
|
40879
|
+
this.updateAllStatuses(status.toJSON());
|
|
40810
40880
|
}
|
|
40811
|
-
const sync = this.generateStreamingImplementation();
|
|
40812
|
-
const onDispose = sync.registerListener({
|
|
40813
|
-
statusChanged: (status) => {
|
|
40814
|
-
this.updateAllStatuses(status.toJSON());
|
|
40815
|
-
}
|
|
40816
|
-
});
|
|
40817
|
-
return {
|
|
40818
|
-
sync,
|
|
40819
|
-
onDispose
|
|
40820
|
-
};
|
|
40821
40881
|
});
|
|
40882
|
+
return {
|
|
40883
|
+
sync,
|
|
40884
|
+
onDispose
|
|
40885
|
+
};
|
|
40822
40886
|
},
|
|
40823
40887
|
logger: this.logger
|
|
40824
40888
|
});
|
|
@@ -40829,6 +40893,18 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40829
40893
|
get isConnected() {
|
|
40830
40894
|
return this.connectionManager.syncStreamImplementation?.isConnected ?? false;
|
|
40831
40895
|
}
|
|
40896
|
+
/**
|
|
40897
|
+
* Gets the last client port which we know is safe from unexpected closes.
|
|
40898
|
+
*/
|
|
40899
|
+
get lastWrappedPort() {
|
|
40900
|
+
// Find the last port which is protected from close
|
|
40901
|
+
for (let i = this.ports.length - 1; i >= 0; i--) {
|
|
40902
|
+
if (this.ports[i].isProtectedFromClose && !this.ports[i].isClosing) {
|
|
40903
|
+
return this.ports[i];
|
|
40904
|
+
}
|
|
40905
|
+
}
|
|
40906
|
+
return;
|
|
40907
|
+
}
|
|
40832
40908
|
async waitForStatus(status) {
|
|
40833
40909
|
return this.withSyncImplementation(async (sync) => {
|
|
40834
40910
|
return sync.waitForStatus(status);
|
|
@@ -40871,10 +40947,6 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40871
40947
|
this.collectActiveSubscriptions();
|
|
40872
40948
|
if (this.syncParams) {
|
|
40873
40949
|
// Cannot modify already existing sync implementation params
|
|
40874
|
-
// But we can ask for a DB adapter, if required, at this point.
|
|
40875
|
-
if (!this.dbAdapter) {
|
|
40876
|
-
await this.openInternalDB();
|
|
40877
|
-
}
|
|
40878
40950
|
return;
|
|
40879
40951
|
}
|
|
40880
40952
|
// First time setting params
|
|
@@ -40882,13 +40954,28 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40882
40954
|
if (params.streamOptions?.flags?.broadcastLogs) {
|
|
40883
40955
|
this.logger = this.broadCastLogger;
|
|
40884
40956
|
}
|
|
40957
|
+
const lockedAdapter = new _db_adapters_LockedAsyncDatabaseAdapter__WEBPACK_IMPORTED_MODULE_5__.LockedAsyncDatabaseAdapter({
|
|
40958
|
+
name: params.dbParams.dbFilename,
|
|
40959
|
+
openConnection: async () => {
|
|
40960
|
+
// Gets a connection from the clients when a new connection is requested.
|
|
40961
|
+
return await this.openInternalDB();
|
|
40962
|
+
},
|
|
40963
|
+
logger: this.logger,
|
|
40964
|
+
reOpenOnConnectionClosed: true
|
|
40965
|
+
});
|
|
40966
|
+
this.distributedDB = lockedAdapter;
|
|
40967
|
+
await lockedAdapter.init();
|
|
40968
|
+
lockedAdapter.registerListener({
|
|
40969
|
+
databaseReOpened: () => {
|
|
40970
|
+
// We may have missed some table updates while the database was closed.
|
|
40971
|
+
// We can poke the crud in case we missed any updates.
|
|
40972
|
+
this.connectionManager.syncStreamImplementation?.triggerCrudUpload();
|
|
40973
|
+
}
|
|
40974
|
+
});
|
|
40885
40975
|
self.onerror = (event) => {
|
|
40886
40976
|
// Share any uncaught events on the broadcast logger
|
|
40887
40977
|
this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
|
|
40888
40978
|
};
|
|
40889
|
-
if (!this.dbAdapter) {
|
|
40890
|
-
await this.openInternalDB();
|
|
40891
|
-
}
|
|
40892
40979
|
this.iterateListeners((l) => l.initialized?.());
|
|
40893
40980
|
});
|
|
40894
40981
|
}
|
|
@@ -40919,7 +41006,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40919
41006
|
port,
|
|
40920
41007
|
clientProvider: comlink__WEBPACK_IMPORTED_MODULE_2__.wrap(port),
|
|
40921
41008
|
currentSubscriptions: [],
|
|
40922
|
-
closeListeners: []
|
|
41009
|
+
closeListeners: [],
|
|
41010
|
+
isProtectedFromClose: false,
|
|
41011
|
+
isClosing: false
|
|
40923
41012
|
};
|
|
40924
41013
|
this.ports.push(portProvider);
|
|
40925
41014
|
// Give the newly connected client the latest status
|
|
@@ -40935,14 +41024,16 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40935
41024
|
* clients.
|
|
40936
41025
|
*/
|
|
40937
41026
|
async removePort(port) {
|
|
41027
|
+
// Ports might be removed faster than we can process them.
|
|
41028
|
+
port.isClosing = true;
|
|
40938
41029
|
// Remove the port within a mutex context.
|
|
40939
41030
|
// Warns if the port is not found. This should not happen in practice.
|
|
40940
41031
|
// We return early if the port is not found.
|
|
40941
|
-
|
|
41032
|
+
return await this.portMutex.runExclusive(async () => {
|
|
40942
41033
|
const index = this.ports.findIndex((p) => p == port);
|
|
40943
41034
|
if (index < 0) {
|
|
40944
41035
|
this.logger.warn(`Could not remove port ${port} since it is not present in active ports.`);
|
|
40945
|
-
return {};
|
|
41036
|
+
return () => { };
|
|
40946
41037
|
}
|
|
40947
41038
|
const trackedPort = this.ports[index];
|
|
40948
41039
|
// Remove from the list of active ports
|
|
@@ -40956,40 +41047,13 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
40956
41047
|
abortController.controller.abort(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.AbortOperation('Closing pending requests after client port is removed'));
|
|
40957
41048
|
}
|
|
40958
41049
|
});
|
|
40959
|
-
|
|
40960
|
-
|
|
40961
|
-
|
|
40962
|
-
* We can disconnect in the portMutex lock. This ensures the disconnect is not affected by potential other
|
|
40963
|
-
* connect operations coming from other tabs.
|
|
40964
|
-
*/
|
|
40965
|
-
if (this.dbAdapter && this.dbAdapter == trackedPort.db) {
|
|
40966
|
-
this.logger.debug(`Disconnecting due to closed database: should reconnect: ${shouldReconnect}`);
|
|
40967
|
-
this.dbAdapter = null;
|
|
40968
|
-
// Unconditionally close the connection because the database it's writing to has just been closed.
|
|
40969
|
-
// The connection has been closed previously, this might throw. We should be able to ignore it.
|
|
40970
|
-
await this.connectionManager
|
|
40971
|
-
.disconnect()
|
|
40972
|
-
.catch((ex) => this.logger.warn('Error while disconnecting. Will attempt to reconnect.', ex));
|
|
41050
|
+
// Close the worker wrapped database connection, we can't accurately rely on this connection
|
|
41051
|
+
for (const closeListener of trackedPort.closeListeners) {
|
|
41052
|
+
await closeListener();
|
|
40973
41053
|
}
|
|
40974
|
-
|
|
40975
|
-
|
|
40976
|
-
trackedPort
|
|
40977
|
-
};
|
|
41054
|
+
this.collectActiveSubscriptions();
|
|
41055
|
+
return () => trackedPort.clientProvider[comlink__WEBPACK_IMPORTED_MODULE_2__.releaseProxy]();
|
|
40978
41056
|
});
|
|
40979
|
-
if (!trackedPort) {
|
|
40980
|
-
// We could not find the port to remove
|
|
40981
|
-
return () => { };
|
|
40982
|
-
}
|
|
40983
|
-
for (const closeListener of trackedPort.closeListeners) {
|
|
40984
|
-
await closeListener();
|
|
40985
|
-
}
|
|
40986
|
-
if (shouldReconnect) {
|
|
40987
|
-
await this.connectionManager.connect(CONNECTOR_PLACEHOLDER, this.lastConnectOptions ?? {});
|
|
40988
|
-
}
|
|
40989
|
-
// Re-index subscriptions, the subscriptions of the removed port would no longer be considered.
|
|
40990
|
-
this.collectActiveSubscriptions();
|
|
40991
|
-
// Release proxy
|
|
40992
|
-
return () => trackedPort.clientProvider[comlink__WEBPACK_IMPORTED_MODULE_2__.releaseProxy]();
|
|
40993
41057
|
}
|
|
40994
41058
|
triggerCrudUpload() {
|
|
40995
41059
|
this.withSyncImplementation(async (sync) => {
|
|
@@ -41026,10 +41090,13 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
41026
41090
|
const syncParams = this.syncParams;
|
|
41027
41091
|
// Create a new StreamingSyncImplementation for each connect call. This is usually done is all SDKs.
|
|
41028
41092
|
return new _db_sync_WebStreamingSyncImplementation__WEBPACK_IMPORTED_MODULE_4__.WebStreamingSyncImplementation({
|
|
41029
|
-
adapter: new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SqliteBucketStorage(this.
|
|
41093
|
+
adapter: new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SqliteBucketStorage(this.distributedDB, this.logger),
|
|
41030
41094
|
remote: new _db_sync_WebRemote__WEBPACK_IMPORTED_MODULE_3__.WebRemote({
|
|
41031
41095
|
invalidateCredentials: async () => {
|
|
41032
|
-
const lastPort = this.
|
|
41096
|
+
const lastPort = this.lastWrappedPort;
|
|
41097
|
+
if (!lastPort) {
|
|
41098
|
+
throw new Error('No client port found to invalidate credentials');
|
|
41099
|
+
}
|
|
41033
41100
|
try {
|
|
41034
41101
|
this.logger.log('calling the last port client provider to invalidate credentials');
|
|
41035
41102
|
lastPort.clientProvider.invalidateCredentials();
|
|
@@ -41039,7 +41106,10 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
41039
41106
|
}
|
|
41040
41107
|
},
|
|
41041
41108
|
fetchCredentials: async () => {
|
|
41042
|
-
const lastPort = this.
|
|
41109
|
+
const lastPort = this.lastWrappedPort;
|
|
41110
|
+
if (!lastPort) {
|
|
41111
|
+
throw new Error('No client port found to fetch credentials');
|
|
41112
|
+
}
|
|
41043
41113
|
return new Promise(async (resolve, reject) => {
|
|
41044
41114
|
const abortController = new AbortController();
|
|
41045
41115
|
this.fetchCredentialsController = {
|
|
@@ -41061,7 +41131,10 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
41061
41131
|
}
|
|
41062
41132
|
}, this.logger),
|
|
41063
41133
|
uploadCrud: async () => {
|
|
41064
|
-
const lastPort = this.
|
|
41134
|
+
const lastPort = this.lastWrappedPort;
|
|
41135
|
+
if (!lastPort) {
|
|
41136
|
+
throw new Error('No client port found to upload crud');
|
|
41137
|
+
}
|
|
41065
41138
|
return new Promise(async (resolve, reject) => {
|
|
41066
41139
|
const abortController = new AbortController();
|
|
41067
41140
|
this.uploadDataController = {
|
|
@@ -41087,19 +41160,47 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
41087
41160
|
logger: this.logger
|
|
41088
41161
|
});
|
|
41089
41162
|
}
|
|
41163
|
+
/**
|
|
41164
|
+
* Opens a worker wrapped database connection. Using the last connected client port.
|
|
41165
|
+
*/
|
|
41090
41166
|
async openInternalDB() {
|
|
41091
|
-
|
|
41092
|
-
|
|
41093
|
-
|
|
41094
|
-
|
|
41095
|
-
|
|
41096
|
-
|
|
41097
|
-
|
|
41098
|
-
|
|
41099
|
-
|
|
41100
|
-
|
|
41101
|
-
|
|
41102
|
-
|
|
41167
|
+
while (true) {
|
|
41168
|
+
try {
|
|
41169
|
+
const lastClient = this.lastWrappedPort;
|
|
41170
|
+
if (!lastClient) {
|
|
41171
|
+
// Should not really happen in practice
|
|
41172
|
+
throw new Error(`Could not open DB connection since no client is connected.`);
|
|
41173
|
+
}
|
|
41174
|
+
/**
|
|
41175
|
+
* Handle cases where the client might close while opening a connection.
|
|
41176
|
+
*/
|
|
41177
|
+
const abortController = new AbortController();
|
|
41178
|
+
const closeListener = () => {
|
|
41179
|
+
abortController.abort();
|
|
41180
|
+
};
|
|
41181
|
+
const removeCloseListener = () => {
|
|
41182
|
+
const index = lastClient.closeListeners.indexOf(closeListener);
|
|
41183
|
+
if (index >= 0) {
|
|
41184
|
+
lastClient.closeListeners.splice(index, 1);
|
|
41185
|
+
}
|
|
41186
|
+
};
|
|
41187
|
+
lastClient.closeListeners.push(closeListener);
|
|
41188
|
+
const workerPort = await withAbort(() => lastClient.clientProvider.getDBWorkerPort(), abortController.signal).catch((ex) => {
|
|
41189
|
+
removeCloseListener();
|
|
41190
|
+
throw ex;
|
|
41191
|
+
});
|
|
41192
|
+
const remote = comlink__WEBPACK_IMPORTED_MODULE_2__.wrap(workerPort);
|
|
41193
|
+
const identifier = this.syncParams.dbParams.dbFilename;
|
|
41194
|
+
/**
|
|
41195
|
+
* The open could fail if the tab is closed while we're busy opening the database.
|
|
41196
|
+
* This operation is typically executed inside an exclusive portMutex lock.
|
|
41197
|
+
* We typically execute the closeListeners using the portMutex in a different context.
|
|
41198
|
+
* We can't rely on the closeListeners to abort the operation if the tab is closed.
|
|
41199
|
+
*/
|
|
41200
|
+
const db = await withAbort(() => remote(this.syncParams.dbParams), abortController.signal).finally(() => {
|
|
41201
|
+
// We can remove the close listener here since we no longer need it past this point.
|
|
41202
|
+
removeCloseListener();
|
|
41203
|
+
});
|
|
41103
41204
|
const wrapped = new _db_adapters_WorkerWrappedAsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_6__.WorkerWrappedAsyncDatabaseConnection({
|
|
41104
41205
|
remote,
|
|
41105
41206
|
baseConnection: db,
|
|
@@ -41119,11 +41220,12 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
41119
41220
|
wrapped.markRemoteClosed();
|
|
41120
41221
|
});
|
|
41121
41222
|
return wrapped;
|
|
41122
|
-
}
|
|
41123
|
-
|
|
41124
|
-
|
|
41125
|
-
|
|
41126
|
-
|
|
41223
|
+
}
|
|
41224
|
+
catch (ex) {
|
|
41225
|
+
this.logger.warn('Error opening internal DB', ex);
|
|
41226
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
41227
|
+
}
|
|
41228
|
+
}
|
|
41127
41229
|
}
|
|
41128
41230
|
/**
|
|
41129
41231
|
* A method to update the all shared statuses for each
|
|
@@ -41146,6 +41248,29 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
|
|
|
41146
41248
|
this.updateAllStatuses(status);
|
|
41147
41249
|
}
|
|
41148
41250
|
}
|
|
41251
|
+
/**
|
|
41252
|
+
* Runs the action with an abort controller.
|
|
41253
|
+
*/
|
|
41254
|
+
function withAbort(action, signal) {
|
|
41255
|
+
return new Promise((resolve, reject) => {
|
|
41256
|
+
if (signal.aborted) {
|
|
41257
|
+
reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.AbortOperation('Operation aborted by abort controller'));
|
|
41258
|
+
return;
|
|
41259
|
+
}
|
|
41260
|
+
function handleAbort() {
|
|
41261
|
+
signal.removeEventListener('abort', handleAbort);
|
|
41262
|
+
reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.AbortOperation('Operation aborted by abort controller'));
|
|
41263
|
+
}
|
|
41264
|
+
signal.addEventListener('abort', handleAbort, { once: true });
|
|
41265
|
+
function completePromise(action) {
|
|
41266
|
+
signal.removeEventListener('abort', handleAbort);
|
|
41267
|
+
action();
|
|
41268
|
+
}
|
|
41269
|
+
action()
|
|
41270
|
+
.then((data) => completePromise(() => resolve(data)))
|
|
41271
|
+
.catch((e) => completePromise(() => reject(e)));
|
|
41272
|
+
});
|
|
41273
|
+
}
|
|
41149
41274
|
|
|
41150
41275
|
|
|
41151
41276
|
/***/ }),
|
|
@@ -41502,6 +41627,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
41502
41627
|
/* harmony export */ AbstractWebPowerSyncDatabaseOpenFactory: () => (/* reexport safe */ _db_adapters_AbstractWebPowerSyncDatabaseOpenFactory__WEBPACK_IMPORTED_MODULE_2__.AbstractWebPowerSyncDatabaseOpenFactory),
|
|
41503
41628
|
/* harmony export */ AbstractWebSQLOpenFactory: () => (/* reexport safe */ _db_adapters_AbstractWebSQLOpenFactory__WEBPACK_IMPORTED_MODULE_3__.AbstractWebSQLOpenFactory),
|
|
41504
41629
|
/* harmony export */ AsyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection__WEBPACK_IMPORTED_MODULE_4__.AsyncWASQLiteModuleFactory),
|
|
41630
|
+
/* harmony export */ ConnectionClosedError: () => (/* reexport safe */ _db_adapters_AsyncDatabaseConnection__WEBPACK_IMPORTED_MODULE_1__.ConnectionClosedError),
|
|
41505
41631
|
/* harmony export */ DEFAULT_CACHE_SIZE_KB: () => (/* reexport safe */ _db_adapters_web_sql_flags__WEBPACK_IMPORTED_MODULE_8__.DEFAULT_CACHE_SIZE_KB),
|
|
41506
41632
|
/* harmony export */ DEFAULT_MODULE_FACTORIES: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection__WEBPACK_IMPORTED_MODULE_4__.DEFAULT_MODULE_FACTORIES),
|
|
41507
41633
|
/* harmony export */ DEFAULT_POWERSYNC_FLAGS: () => (/* reexport safe */ _db_PowerSyncDatabase__WEBPACK_IMPORTED_MODULE_9__.DEFAULT_POWERSYNC_FLAGS),
|