@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.
- 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 +336 -177
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +310 -143
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +19 -5
- 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 +8 -0
- package/lib/src/db/adapters/AsyncDatabaseConnection.js +11 -1
- package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +14 -0
- package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +61 -8
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +5 -1
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +15 -7
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.d.ts +1 -4
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +45 -31
- 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 +18 -8
- package/lib/src/worker/sync/SharedSyncImplementation.js +174 -108
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -1
- package/lib/src/worker/sync/WorkerClient.d.ts +4 -5
- package/lib/src/worker/sync/WorkerClient.js +5 -7
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/db/PowerSyncDatabase.ts +13 -15
- package/src/db/adapters/AsyncDatabaseConnection.ts +12 -0
- package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +71 -9
- package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +19 -6
- package/src/db/sync/SharedWebStreamingSyncImplementation.ts +56 -40
- 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 +201 -120
- package/src/worker/sync/SharedSyncImplementation.worker.ts +1 -1
- package/src/worker/sync/WorkerClient.ts +6 -11
- package/dist/a730f7ca717b02234beb.wasm +0 -0
- package/dist/aa2f408d64445fed090e.wasm +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type ILogger
|
|
12
12
|
} from '@powersync/common';
|
|
13
13
|
import { getNavigatorLocks } from '../..//shared/navigator';
|
|
14
|
-
import { AsyncDatabaseConnection } from './AsyncDatabaseConnection';
|
|
14
|
+
import { AsyncDatabaseConnection, ConnectionClosedError } from './AsyncDatabaseConnection';
|
|
15
15
|
import { SharedConnectionWorker, WebDBAdapter } from './WebDBAdapter';
|
|
16
16
|
import { WorkerWrappedAsyncDatabaseConnection } from './WorkerWrappedAsyncDatabaseConnection';
|
|
17
17
|
import { WASQLiteVFS } from './wa-sqlite/WASQLiteConnection';
|
|
@@ -26,10 +26,16 @@ export interface LockedAsyncDatabaseAdapterOptions {
|
|
|
26
26
|
openConnection: () => Promise<AsyncDatabaseConnection>;
|
|
27
27
|
debugMode?: boolean;
|
|
28
28
|
logger?: ILogger;
|
|
29
|
+
defaultLockTimeoutMs?: number;
|
|
30
|
+
reOpenOnConnectionClosed?: boolean;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export type LockedAsyncDatabaseAdapterListener = DBAdapterListener & {
|
|
32
34
|
initialized?: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Fired when the database is re-opened after being closed.
|
|
37
|
+
*/
|
|
38
|
+
databaseReOpened?: () => void;
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
/**
|
|
@@ -51,6 +57,7 @@ export class LockedAsyncDatabaseAdapter
|
|
|
51
57
|
private _config: ResolvedWebSQLOpenOptions | null = null;
|
|
52
58
|
protected pendingAbortControllers: Set<AbortController>;
|
|
53
59
|
protected requiresHolds: boolean | null;
|
|
60
|
+
protected databaseOpenPromise: Promise<void> | null = null;
|
|
54
61
|
|
|
55
62
|
closing: boolean;
|
|
56
63
|
closed: boolean;
|
|
@@ -105,18 +112,52 @@ export class LockedAsyncDatabaseAdapter
|
|
|
105
112
|
return this.initPromise;
|
|
106
113
|
}
|
|
107
114
|
|
|
108
|
-
protected async
|
|
115
|
+
protected async openInternalDB() {
|
|
116
|
+
// Dispose any previous table change listener.
|
|
117
|
+
this._disposeTableChangeListener?.();
|
|
118
|
+
this._disposeTableChangeListener = null;
|
|
119
|
+
|
|
120
|
+
const isReOpen = !!this._db;
|
|
121
|
+
|
|
109
122
|
this._db = await this.options.openConnection();
|
|
110
123
|
await this._db.init();
|
|
111
124
|
this._config = await this._db.getConfig();
|
|
112
125
|
await this.registerOnChangeListener(this._db);
|
|
113
|
-
|
|
126
|
+
if (isReOpen) {
|
|
127
|
+
this.iterateListeners((cb) => cb.databaseReOpened?.());
|
|
128
|
+
}
|
|
114
129
|
/**
|
|
115
130
|
* This is only required for the long-lived shared IndexedDB connections.
|
|
116
131
|
*/
|
|
117
132
|
this.requiresHolds = (this._config as ResolvedWASQLiteOpenFactoryOptions).vfs == WASQLiteVFS.IDBBatchAtomicVFS;
|
|
118
133
|
}
|
|
119
134
|
|
|
135
|
+
protected _reOpen() {
|
|
136
|
+
this.databaseOpenPromise = this.openInternalDB().finally(() => {
|
|
137
|
+
this.databaseOpenPromise = null;
|
|
138
|
+
});
|
|
139
|
+
return this.databaseOpenPromise;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Re-opens the underlying database.
|
|
144
|
+
* Returns a pending operation if one is already in progress.
|
|
145
|
+
*/
|
|
146
|
+
async reOpenInternalDB(): Promise<void> {
|
|
147
|
+
if (!this.options.reOpenOnConnectionClosed) {
|
|
148
|
+
throw new Error(`Cannot re-open underlying database, reOpenOnConnectionClosed is not enabled`);
|
|
149
|
+
}
|
|
150
|
+
if (this.databaseOpenPromise) {
|
|
151
|
+
return this.databaseOpenPromise;
|
|
152
|
+
}
|
|
153
|
+
return this._reOpen();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected async _init() {
|
|
157
|
+
await this.openInternalDB();
|
|
158
|
+
this.iterateListeners((cb) => cb.initialized?.());
|
|
159
|
+
}
|
|
160
|
+
|
|
120
161
|
getConfiguration(): ResolvedWebSQLOpenOptions {
|
|
121
162
|
if (!this._config) {
|
|
122
163
|
throw new Error(`Cannot get config before initialization is completed`);
|
|
@@ -196,7 +237,7 @@ export class LockedAsyncDatabaseAdapter
|
|
|
196
237
|
return this.acquireLock(
|
|
197
238
|
async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })),
|
|
198
239
|
{
|
|
199
|
-
timeoutMs: options?.timeoutMs
|
|
240
|
+
timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
|
|
200
241
|
}
|
|
201
242
|
);
|
|
202
243
|
}
|
|
@@ -206,7 +247,7 @@ export class LockedAsyncDatabaseAdapter
|
|
|
206
247
|
return this.acquireLock(
|
|
207
248
|
async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })),
|
|
208
249
|
{
|
|
209
|
-
timeoutMs: options?.timeoutMs
|
|
250
|
+
timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
|
|
210
251
|
}
|
|
211
252
|
);
|
|
212
253
|
}
|
|
@@ -222,7 +263,7 @@ export class LockedAsyncDatabaseAdapter
|
|
|
222
263
|
this.pendingAbortControllers.add(abortController);
|
|
223
264
|
const { timeoutMs } = options ?? {};
|
|
224
265
|
|
|
225
|
-
const
|
|
266
|
+
const timeoutId = timeoutMs
|
|
226
267
|
? setTimeout(() => {
|
|
227
268
|
abortController.abort(`Timeout after ${timeoutMs}ms`);
|
|
228
269
|
this.pendingAbortControllers.delete(abortController);
|
|
@@ -234,12 +275,33 @@ export class LockedAsyncDatabaseAdapter
|
|
|
234
275
|
{ signal: abortController.signal },
|
|
235
276
|
async () => {
|
|
236
277
|
this.pendingAbortControllers.delete(abortController);
|
|
237
|
-
if (
|
|
238
|
-
clearTimeout(
|
|
278
|
+
if (timeoutId) {
|
|
279
|
+
clearTimeout(timeoutId);
|
|
239
280
|
}
|
|
240
|
-
|
|
281
|
+
let holdId: string | null = null;
|
|
241
282
|
try {
|
|
283
|
+
// The database is being opened in the background. Wait for it here.
|
|
284
|
+
if (this.databaseOpenPromise) {
|
|
285
|
+
try {
|
|
286
|
+
await this.databaseOpenPromise;
|
|
287
|
+
} catch (ex) {
|
|
288
|
+
// This will cause a retry of opening the database.
|
|
289
|
+
const wrappedError = new ConnectionClosedError('Could not open database');
|
|
290
|
+
wrappedError.cause = ex;
|
|
291
|
+
throw wrappedError;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
holdId = this.requiresHolds ? await this.baseDB.markHold() : null;
|
|
242
296
|
return await callback();
|
|
297
|
+
} catch (ex) {
|
|
298
|
+
if (ex instanceof ConnectionClosedError) {
|
|
299
|
+
if (this.options.reOpenOnConnectionClosed && !this.databaseOpenPromise && !this.closing) {
|
|
300
|
+
// Immediately re-open the database. We need to miss as little table updates as possible.
|
|
301
|
+
this.reOpenInternalDB();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
throw ex;
|
|
243
305
|
} finally {
|
|
244
306
|
if (holdId) {
|
|
245
307
|
await this.baseDB.releaseHold(holdId);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { BaseObserver } from '@powersync/common';
|
|
1
2
|
import * as Comlink from 'comlink';
|
|
2
3
|
import {
|
|
3
4
|
AsyncDatabaseConnection,
|
|
5
|
+
ConnectionClosedError,
|
|
4
6
|
OnTableChangeCallback,
|
|
5
7
|
OpenAsyncDatabaseConnection,
|
|
6
8
|
ProxiedQueryResult
|
|
@@ -23,17 +25,23 @@ export type WrappedWorkerConnectionOptions<Config extends ResolvedWebSQLOpenOpti
|
|
|
23
25
|
onClose?: () => void;
|
|
24
26
|
};
|
|
25
27
|
|
|
28
|
+
export type WorkerWrappedAsyncDatabaseConnectionListener = {
|
|
29
|
+
closing: () => void;
|
|
30
|
+
};
|
|
26
31
|
/**
|
|
27
32
|
* Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
|
|
28
33
|
* functions for worker listeners.
|
|
29
34
|
*/
|
|
30
35
|
export class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLOpenOptions = ResolvedWebSQLOpenOptions>
|
|
36
|
+
extends BaseObserver<WorkerWrappedAsyncDatabaseConnectionListener>
|
|
31
37
|
implements AsyncDatabaseConnection
|
|
32
38
|
{
|
|
33
39
|
protected lockAbortController = new AbortController();
|
|
34
40
|
protected notifyRemoteClosed: AbortController | undefined;
|
|
35
41
|
|
|
36
42
|
constructor(protected options: WrappedWorkerConnectionOptions<Config>) {
|
|
43
|
+
super();
|
|
44
|
+
|
|
37
45
|
if (options.remoteCanCloseUnexpectedly) {
|
|
38
46
|
this.notifyRemoteClosed = new AbortController();
|
|
39
47
|
}
|
|
@@ -72,18 +80,21 @@ export class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLO
|
|
|
72
80
|
return this.withRemote(() => this.baseConnection.isAutoCommit());
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
private withRemote<T>(workerPromise: () => Promise<T
|
|
83
|
+
private withRemote<T>(workerPromise: () => Promise<T>, fireActionOnAbort = false): Promise<T> {
|
|
76
84
|
const controller = this.notifyRemoteClosed;
|
|
77
85
|
if (controller) {
|
|
78
86
|
return new Promise((resolve, reject) => {
|
|
79
87
|
if (controller.signal.aborted) {
|
|
80
|
-
reject(new
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
reject(new ConnectionClosedError('Called operation on closed remote'));
|
|
89
|
+
if (!fireActionOnAbort) {
|
|
90
|
+
// Don't run the operation if we're going to reject
|
|
91
|
+
// We might want to fire-and-forget the operation in some cases (like a close operation)
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
function handleAbort() {
|
|
86
|
-
reject(new
|
|
97
|
+
reject(new ConnectionClosedError('Remote peer closed with request in flight'));
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
function completePromise(action: () => void) {
|
|
@@ -164,10 +175,12 @@ export class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLO
|
|
|
164
175
|
// Abort any pending lock requests.
|
|
165
176
|
this.lockAbortController.abort();
|
|
166
177
|
try {
|
|
167
|
-
|
|
178
|
+
// fire and forget the close operation
|
|
179
|
+
await this.withRemote(() => this.baseConnection.close(), true);
|
|
168
180
|
} finally {
|
|
169
181
|
this.options.remote[Comlink.releaseProxy]();
|
|
170
182
|
this.options.onClose?.();
|
|
183
|
+
this.iterateListeners((l) => l.closing?.());
|
|
171
184
|
}
|
|
172
185
|
}
|
|
173
186
|
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
PowerSyncConnectionOptions,
|
|
3
3
|
PowerSyncCredentials,
|
|
4
4
|
SubscribedStream,
|
|
5
|
-
SyncStatus,
|
|
6
5
|
SyncStatusOptions
|
|
7
6
|
} from '@powersync/common';
|
|
8
7
|
import * as Comlink from 'comlink';
|
|
@@ -146,7 +145,25 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
146
145
|
).port;
|
|
147
146
|
}
|
|
148
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Pass along any sync status updates to this listener
|
|
150
|
+
*/
|
|
151
|
+
this.clientProvider = new SharedSyncClientProvider(
|
|
152
|
+
this.webOptions,
|
|
153
|
+
(status) => {
|
|
154
|
+
this.updateSyncStatus(status);
|
|
155
|
+
},
|
|
156
|
+
options.db
|
|
157
|
+
);
|
|
158
|
+
|
|
149
159
|
this.syncManager = Comlink.wrap<WorkerClient>(this.messagePort);
|
|
160
|
+
/**
|
|
161
|
+
* The sync worker will call this client provider when it needs
|
|
162
|
+
* to fetch credentials or upload data.
|
|
163
|
+
* This performs bi-directional method calling.
|
|
164
|
+
*/
|
|
165
|
+
Comlink.expose(this.clientProvider, this.messagePort);
|
|
166
|
+
|
|
150
167
|
this.syncManager.setLogLevel(this.logger.getLevel());
|
|
151
168
|
|
|
152
169
|
this.triggerCrudUpload = this.syncManager.triggerCrudUpload;
|
|
@@ -157,25 +174,49 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
157
174
|
* DB worker, but a port to the DB worker can be transferred to the
|
|
158
175
|
* sync worker.
|
|
159
176
|
*/
|
|
160
|
-
const { crudUploadThrottleMs, identifier, retryDelayMs } = this.options;
|
|
161
|
-
const flags = { ...this.webOptions.flags, workers: undefined };
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
this.isInitialized = this._init();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected async _init() {
|
|
182
|
+
/**
|
|
183
|
+
* The general flow of initialization is:
|
|
184
|
+
* - The client requests a unique navigator lock.
|
|
185
|
+
* - Once the lock is acquired, we register the lock with the shared worker.
|
|
186
|
+
* - The shared worker can then request the same lock. The client has been closed if the shared worker can acquire the lock.
|
|
187
|
+
* - Once the shared worker knows the client's lock, we can guarentee that the shared worker will detect if the client has been closed.
|
|
188
|
+
* - This makes the client safe for the shared worker to use.
|
|
189
|
+
* - The client is only added to the SharedSyncImplementation once the lock has been registered.
|
|
190
|
+
* This ensures we don't ever keep track of dead clients (tabs that closed before the lock was registered).
|
|
191
|
+
* - The client side lock is held until the client is disposed.
|
|
192
|
+
* - We resolve the top-level promise after the lock has been registered with the shared worker.
|
|
193
|
+
* - The client sends the params to the shared worker after locks have been registered.
|
|
194
|
+
*/
|
|
195
|
+
await new Promise<void>((resolve) => {
|
|
196
|
+
// Request a random lock until this client is disposed. The name of the lock is sent to the shared worker, which
|
|
197
|
+
// will also attempt to acquire it. Since the lock is returned when the tab is closed, this allows the share worker
|
|
198
|
+
// to free resources associated with this tab.
|
|
199
|
+
// We take hold of this lock as soon-as-possible in order to cater for potentially closed tabs.
|
|
200
|
+
getNavigatorLocks().request(`tab-close-signal-${crypto.randomUUID()}`, async (lock) => {
|
|
201
|
+
if (this.abortOnClose.signal.aborted) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
169
204
|
// Awaiting here ensures the worker is waiting for the lock
|
|
170
205
|
await this.syncManager.addLockBasedCloseSignal(lock!.name);
|
|
171
206
|
|
|
207
|
+
// The lock has been registered, we can continue with the initialization
|
|
208
|
+
resolve();
|
|
209
|
+
|
|
172
210
|
await new Promise<void>((r) => {
|
|
173
211
|
this.abortOnClose.signal.onabort = () => r();
|
|
174
212
|
});
|
|
175
|
-
}
|
|
213
|
+
});
|
|
176
214
|
});
|
|
177
215
|
|
|
178
|
-
|
|
216
|
+
const { crudUploadThrottleMs, identifier, retryDelayMs } = this.options;
|
|
217
|
+
const flags = { ...this.webOptions.flags, workers: undefined };
|
|
218
|
+
|
|
219
|
+
await this.syncManager.setParams(
|
|
179
220
|
{
|
|
180
221
|
dbParams: this.dbAdapter.getConfiguration(),
|
|
181
222
|
streamOptions: {
|
|
@@ -185,26 +226,8 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
185
226
|
flags: flags
|
|
186
227
|
}
|
|
187
228
|
},
|
|
188
|
-
options.subscriptions
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Pass along any sync status updates to this listener
|
|
193
|
-
*/
|
|
194
|
-
this.clientProvider = new SharedSyncClientProvider(
|
|
195
|
-
this.webOptions,
|
|
196
|
-
(status) => {
|
|
197
|
-
this.iterateListeners((l) => this.updateSyncStatus(status));
|
|
198
|
-
},
|
|
199
|
-
options.db
|
|
229
|
+
this.options.subscriptions
|
|
200
230
|
);
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* The sync worker will call this client provider when it needs
|
|
204
|
-
* to fetch credentials or upload data.
|
|
205
|
-
* This performs bi-directional method calling.
|
|
206
|
-
*/
|
|
207
|
-
Comlink.expose(this.clientProvider, this.messagePort);
|
|
208
231
|
}
|
|
209
232
|
|
|
210
233
|
/**
|
|
@@ -233,8 +256,6 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
233
256
|
async dispose(): Promise<void> {
|
|
234
257
|
await this.waitForReady();
|
|
235
258
|
|
|
236
|
-
await super.dispose();
|
|
237
|
-
|
|
238
259
|
await new Promise<void>((resolve) => {
|
|
239
260
|
// Listen for the close acknowledgment from the worker
|
|
240
261
|
this.messagePort.addEventListener('message', (event) => {
|
|
@@ -251,6 +272,9 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
251
272
|
};
|
|
252
273
|
this.messagePort.postMessage(closeMessagePayload);
|
|
253
274
|
});
|
|
275
|
+
|
|
276
|
+
await super.dispose();
|
|
277
|
+
|
|
254
278
|
this.abortOnClose.abort();
|
|
255
279
|
|
|
256
280
|
// Release the proxy
|
|
@@ -265,12 +289,4 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
265
289
|
updateSubscriptions(subscriptions: SubscribedStream[]): void {
|
|
266
290
|
this.syncManager.updateSubscriptions(subscriptions);
|
|
267
291
|
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Used in tests to force a connection states
|
|
271
|
-
*/
|
|
272
|
-
private async _testUpdateStatus(status: SyncStatus) {
|
|
273
|
-
await this.isInitialized;
|
|
274
|
-
return this.syncManager._testUpdateAllStatuses(status.toJSON());
|
|
275
|
-
}
|
|
276
292
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Representation of a pending request
|
|
3
|
+
*/
|
|
4
|
+
export interface PendingRequest {
|
|
5
|
+
id: string;
|
|
6
|
+
url: string;
|
|
7
|
+
method: string;
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
body: any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Automatic response configuration
|
|
14
|
+
*/
|
|
15
|
+
export interface AutomaticResponseConfig {
|
|
16
|
+
status: number;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
bodyLines?: any[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Message types for communication via MessagePort
|
|
23
|
+
*/
|
|
24
|
+
export type MockSyncServiceMessage =
|
|
25
|
+
| { type: 'getPendingRequests'; requestId: string }
|
|
26
|
+
| {
|
|
27
|
+
type: 'createResponse';
|
|
28
|
+
requestId: string;
|
|
29
|
+
pendingRequestId: string;
|
|
30
|
+
status: number;
|
|
31
|
+
headers: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
| { type: 'pushBodyData'; requestId: string; pendingRequestId: string; data: string | ArrayBuffer | Uint8Array }
|
|
34
|
+
| { type: 'completeResponse'; requestId: string; pendingRequestId: string }
|
|
35
|
+
| { type: 'setAutomaticResponse'; requestId: string; config: AutomaticResponseConfig | null }
|
|
36
|
+
| { type: 'replyToAllPendingRequests'; requestId: string };
|
|
37
|
+
|
|
38
|
+
export type MockSyncServiceResponse =
|
|
39
|
+
| { type: 'getPendingRequests'; requestId: string; requests: PendingRequest[] }
|
|
40
|
+
| { type: 'createResponse'; requestId: string; success: boolean }
|
|
41
|
+
| { type: 'pushBodyData'; requestId: string; success: boolean }
|
|
42
|
+
| { type: 'completeResponse'; requestId: string; success: boolean }
|
|
43
|
+
| { type: 'setAutomaticResponse'; requestId: string; success: boolean }
|
|
44
|
+
| { type: 'replyToAllPendingRequests'; requestId: string; success: boolean; count: number }
|
|
45
|
+
| { type: 'error'; requestId?: string; error: string };
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Internal representation of a pending request with response promise
|
|
49
|
+
*/
|
|
50
|
+
export interface PendingRequestInternal {
|
|
51
|
+
id: string;
|
|
52
|
+
url: string;
|
|
53
|
+
method: string;
|
|
54
|
+
headers: Record<string, string>;
|
|
55
|
+
body: any;
|
|
56
|
+
responsePromise: {
|
|
57
|
+
resolve: (response: Response) => void;
|
|
58
|
+
reject: (error: Error) => void;
|
|
59
|
+
};
|
|
60
|
+
streamController?: ReadableStreamDefaultController<Uint8Array>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Internal representation of an active response
|
|
65
|
+
*/
|
|
66
|
+
export interface ActiveResponse {
|
|
67
|
+
id: string;
|
|
68
|
+
status: number;
|
|
69
|
+
headers: Record<string, string>;
|
|
70
|
+
stream: ReadableStreamDefaultController<Uint8Array>;
|
|
71
|
+
}
|