@powersync/web 0.0.0-dev-20250526133243 → 0.0.0-dev-20250528152729
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 +155 -111
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +215 -171
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +59 -58
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/package.json +2 -2
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +15 -9
- package/lib/src/worker/sync/SharedSyncImplementation.d.ts +14 -11
- package/lib/src/worker/sync/SharedSyncImplementation.js +139 -100
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +11 -6
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powersync/web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "PowerSync web SDK. Sync Postgres, MongoDB or MySQL with SQLite in your web app",
|
|
5
5
|
"main": "lib/src/index.js",
|
|
6
6
|
"types": "lib/src/index.d.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"license": "Apache-2.0",
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"@journeyapps/wa-sqlite": "^1.2.4",
|
|
64
|
-
"@powersync/common": "workspace:^1.
|
|
64
|
+
"@powersync/common": "workspace:^1.31.0"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@powersync/common": "workspace:*",
|
|
@@ -153,9 +153,6 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
153
153
|
*/
|
|
154
154
|
async connect(options) {
|
|
155
155
|
await this.waitForReady();
|
|
156
|
-
// This is needed since a new tab won't have any reference to the
|
|
157
|
-
// shared worker sync implementation since that is only created on the first call to `connect`.
|
|
158
|
-
await this.disconnect();
|
|
159
156
|
return this.syncManager.connect(options);
|
|
160
157
|
}
|
|
161
158
|
async disconnect() {
|
|
@@ -171,12 +168,21 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
171
168
|
}
|
|
172
169
|
async dispose() {
|
|
173
170
|
await this.waitForReady();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
event
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
171
|
+
await new Promise((resolve) => {
|
|
172
|
+
// Listen for the close acknowledgment from the worker
|
|
173
|
+
this.messagePort.addEventListener('message', (event) => {
|
|
174
|
+
const payload = event.data;
|
|
175
|
+
if (payload?.event === SharedSyncClientEvent.CLOSE_ACK) {
|
|
176
|
+
resolve();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// Signal the shared worker that this client is closing its connection to the worker
|
|
180
|
+
const closeMessagePayload = {
|
|
181
|
+
event: SharedSyncClientEvent.CLOSE_CLIENT,
|
|
182
|
+
data: {}
|
|
183
|
+
};
|
|
184
|
+
this.messagePort.postMessage(closeMessagePayload);
|
|
185
|
+
});
|
|
180
186
|
// Release the proxy
|
|
181
187
|
this.syncManager[Comlink.releaseProxy]();
|
|
182
188
|
this.messagePort.close();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ILogger, type ILogLevel, type PowerSyncConnectionOptions, type StreamingSyncImplementation, type StreamingSyncImplementationListener, type SyncStatusOptions, BaseObserver, ConnectionManager, DBAdapter, SyncStatus } from '@powersync/common';
|
|
2
|
+
import { Mutex } from 'async-mutex';
|
|
2
3
|
import * as Comlink from 'comlink';
|
|
3
4
|
import { WebStreamingSyncImplementation, WebStreamingSyncImplementationOptions } from '../../db/sync/WebStreamingSyncImplementation';
|
|
4
5
|
import { ResolvedWebSQLOpenOptions } from '../../db/adapters/web-sql-flags';
|
|
@@ -11,7 +12,8 @@ export declare enum SharedSyncClientEvent {
|
|
|
11
12
|
* This client requests the shared sync manager should
|
|
12
13
|
* close it's connection to the client.
|
|
13
14
|
*/
|
|
14
|
-
CLOSE_CLIENT = "close-client"
|
|
15
|
+
CLOSE_CLIENT = "close-client",
|
|
16
|
+
CLOSE_ACK = "close-ack"
|
|
15
17
|
}
|
|
16
18
|
export type ManualSharedSyncPayload = {
|
|
17
19
|
event: SharedSyncClientEvent;
|
|
@@ -51,7 +53,6 @@ export type RemoteOperationAbortController = {
|
|
|
51
53
|
*/
|
|
52
54
|
export declare class SharedSyncImplementation extends BaseObserver<SharedSyncImplementationListener> implements StreamingSyncImplementation {
|
|
53
55
|
protected ports: WrappedSyncPort[];
|
|
54
|
-
protected syncStreamClient: AbstractStreamingSyncImplementation | null;
|
|
55
56
|
protected isInitialized: Promise<void>;
|
|
56
57
|
protected statusListener?: () => void;
|
|
57
58
|
protected fetchCredentialsController?: RemoteOperationAbortController;
|
|
@@ -60,41 +61,43 @@ export declare class SharedSyncImplementation extends BaseObserver<SharedSyncImp
|
|
|
60
61
|
protected syncParams: SharedSyncInitOptions | null;
|
|
61
62
|
protected logger: ILogger;
|
|
62
63
|
protected lastConnectOptions: PowerSyncConnectionOptions | undefined;
|
|
64
|
+
protected portMutex: Mutex;
|
|
65
|
+
protected connectionManager: ConnectionManager;
|
|
63
66
|
syncStatus: SyncStatus;
|
|
64
67
|
broadCastLogger: ILogger;
|
|
65
68
|
constructor();
|
|
66
|
-
waitForStatus(status: SyncStatusOptions): Promise<void>;
|
|
67
|
-
waitUntilStatusMatches(predicate: (status: SyncStatus) => boolean): Promise<void>;
|
|
68
69
|
get lastSyncedAt(): Date | undefined;
|
|
69
70
|
get isConnected(): boolean;
|
|
71
|
+
waitForStatus(status: SyncStatusOptions): Promise<void>;
|
|
72
|
+
waitUntilStatusMatches(predicate: (status: SyncStatus) => boolean): Promise<void>;
|
|
70
73
|
waitForReady(): Promise<void>;
|
|
71
74
|
setLogLevel(level: ILogLevel): void;
|
|
72
75
|
/**
|
|
73
76
|
* Configures the DBAdapter connection and a streaming sync client.
|
|
74
77
|
*/
|
|
75
78
|
setParams(params: SharedSyncInitOptions): Promise<void>;
|
|
76
|
-
dispose(): Promise<void
|
|
79
|
+
dispose(): Promise<void>;
|
|
77
80
|
/**
|
|
78
81
|
* Connects to the PowerSync backend instance.
|
|
79
82
|
* Multiple tabs can safely call this in their initialization.
|
|
80
83
|
* The connection will simply be reconnected whenever a new tab
|
|
81
84
|
* connects.
|
|
82
85
|
*/
|
|
83
|
-
connect(options?: PowerSyncConnectionOptions): Promise<
|
|
84
|
-
disconnect(): Promise<
|
|
86
|
+
connect(options?: PowerSyncConnectionOptions): Promise<void>;
|
|
87
|
+
disconnect(): Promise<void>;
|
|
85
88
|
/**
|
|
86
89
|
* Adds a new client tab's message port to the list of connected ports
|
|
87
90
|
*/
|
|
88
|
-
addPort(port: MessagePort): void
|
|
91
|
+
addPort(port: MessagePort): Promise<void>;
|
|
89
92
|
/**
|
|
90
93
|
* Removes a message port client from this manager's managed
|
|
91
94
|
* clients.
|
|
92
95
|
*/
|
|
93
|
-
removePort(port: MessagePort): Promise<void>;
|
|
96
|
+
removePort(port: MessagePort): Promise<(() => void) | undefined>;
|
|
94
97
|
triggerCrudUpload(): void;
|
|
95
|
-
obtainLock<T>(lockOptions: LockOptions<T>): Promise<T>;
|
|
96
98
|
hasCompletedSync(): Promise<boolean>;
|
|
97
99
|
getWriteCheckpoint(): Promise<string>;
|
|
100
|
+
protected withSyncImplementation<T>(callback: (sync: StreamingSyncImplementation) => Promise<T>): Promise<T>;
|
|
98
101
|
protected generateStreamingImplementation(): WebStreamingSyncImplementation;
|
|
99
102
|
protected openInternalDB(): Promise<void>;
|
|
100
103
|
/**
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { AbortOperation, BaseObserver, createLogger, SqliteBucketStorage, SyncStatus } from '@powersync/common';
|
|
1
|
+
import { AbortOperation, BaseObserver, ConnectionManager, createLogger, SqliteBucketStorage, SyncStatus } from '@powersync/common';
|
|
2
2
|
import { Mutex } from 'async-mutex';
|
|
3
3
|
import * as Comlink from 'comlink';
|
|
4
4
|
import { WebRemote } from '../../db/sync/WebRemote';
|
|
5
5
|
import { WebStreamingSyncImplementation } from '../../db/sync/WebStreamingSyncImplementation';
|
|
6
6
|
import { LockedAsyncDatabaseAdapter } from '../../db/adapters/LockedAsyncDatabaseAdapter';
|
|
7
7
|
import { WorkerWrappedAsyncDatabaseConnection } from '../../db/adapters/WorkerWrappedAsyncDatabaseConnection';
|
|
8
|
-
import { getNavigatorLocks } from '../../shared/navigator';
|
|
9
8
|
import { BroadcastLogger } from './BroadcastLogger';
|
|
10
9
|
/**
|
|
11
10
|
* Manual message events for shared sync clients
|
|
@@ -17,14 +16,20 @@ export var SharedSyncClientEvent;
|
|
|
17
16
|
* close it's connection to the client.
|
|
18
17
|
*/
|
|
19
18
|
SharedSyncClientEvent["CLOSE_CLIENT"] = "close-client";
|
|
19
|
+
SharedSyncClientEvent["CLOSE_ACK"] = "close-ack";
|
|
20
20
|
})(SharedSyncClientEvent || (SharedSyncClientEvent = {}));
|
|
21
|
+
/**
|
|
22
|
+
* HACK: The shared implementation wraps and provides its own
|
|
23
|
+
* PowerSyncBackendConnector when generating the streaming sync implementation.
|
|
24
|
+
* We provide this unused placeholder when connecting with the ConnectionManager.
|
|
25
|
+
*/
|
|
26
|
+
const CONNECTOR_PLACEHOLDER = {};
|
|
21
27
|
/**
|
|
22
28
|
* @internal
|
|
23
29
|
* Shared sync implementation which runs inside a shared webworker
|
|
24
30
|
*/
|
|
25
31
|
export class SharedSyncImplementation extends BaseObserver {
|
|
26
32
|
ports;
|
|
27
|
-
syncStreamClient;
|
|
28
33
|
isInitialized;
|
|
29
34
|
statusListener;
|
|
30
35
|
fetchCredentialsController;
|
|
@@ -33,6 +38,8 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
33
38
|
syncParams;
|
|
34
39
|
logger;
|
|
35
40
|
lastConnectOptions;
|
|
41
|
+
portMutex;
|
|
42
|
+
connectionManager;
|
|
36
43
|
syncStatus;
|
|
37
44
|
broadCastLogger;
|
|
38
45
|
constructor() {
|
|
@@ -40,9 +47,9 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
40
47
|
this.ports = [];
|
|
41
48
|
this.dbAdapter = null;
|
|
42
49
|
this.syncParams = null;
|
|
43
|
-
this.syncStreamClient = null;
|
|
44
50
|
this.logger = createLogger('shared-sync');
|
|
45
51
|
this.lastConnectOptions = undefined;
|
|
52
|
+
this.portMutex = new Mutex();
|
|
46
53
|
this.isInitialized = new Promise((resolve) => {
|
|
47
54
|
const callback = this.registerListener({
|
|
48
55
|
initialized: () => {
|
|
@@ -53,20 +60,41 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
53
60
|
});
|
|
54
61
|
this.syncStatus = new SyncStatus({});
|
|
55
62
|
this.broadCastLogger = new BroadcastLogger(this.ports);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
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());
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
sync,
|
|
77
|
+
onDispose
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
logger: this.logger
|
|
81
|
+
});
|
|
64
82
|
}
|
|
65
83
|
get lastSyncedAt() {
|
|
66
|
-
return this.
|
|
84
|
+
return this.connectionManager.syncStreamImplementation?.lastSyncedAt;
|
|
67
85
|
}
|
|
68
86
|
get isConnected() {
|
|
69
|
-
return this.
|
|
87
|
+
return this.connectionManager.syncStreamImplementation?.isConnected ?? false;
|
|
88
|
+
}
|
|
89
|
+
async waitForStatus(status) {
|
|
90
|
+
return this.withSyncImplementation(async (sync) => {
|
|
91
|
+
return sync.waitForStatus(status);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async waitUntilStatusMatches(predicate) {
|
|
95
|
+
return this.withSyncImplementation(async (sync) => {
|
|
96
|
+
return sync.waitUntilStatusMatches(predicate);
|
|
97
|
+
});
|
|
70
98
|
}
|
|
71
99
|
async waitForReady() {
|
|
72
100
|
return this.isInitialized;
|
|
@@ -79,25 +107,32 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
79
107
|
* Configures the DBAdapter connection and a streaming sync client.
|
|
80
108
|
*/
|
|
81
109
|
async setParams(params) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
await this.portMutex.runExclusive(async () => {
|
|
111
|
+
if (this.syncParams) {
|
|
112
|
+
if (!this.dbAdapter) {
|
|
113
|
+
await this.openInternalDB();
|
|
114
|
+
}
|
|
115
|
+
// Cannot modify already existing sync implementation
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.syncParams = params;
|
|
119
|
+
if (params.streamOptions?.flags?.broadcastLogs) {
|
|
120
|
+
this.logger = this.broadCastLogger;
|
|
121
|
+
}
|
|
122
|
+
self.onerror = (event) => {
|
|
123
|
+
// Share any uncaught events on the broadcast logger
|
|
124
|
+
this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
|
|
125
|
+
};
|
|
126
|
+
if (!this.dbAdapter) {
|
|
127
|
+
await this.openInternalDB();
|
|
128
|
+
}
|
|
129
|
+
this.iterateListeners((l) => l.initialized?.());
|
|
130
|
+
});
|
|
96
131
|
}
|
|
97
132
|
async dispose() {
|
|
98
133
|
await this.waitForReady();
|
|
99
134
|
this.statusListener?.();
|
|
100
|
-
return this.
|
|
135
|
+
return this.connectionManager.close();
|
|
101
136
|
}
|
|
102
137
|
/**
|
|
103
138
|
* Connects to the PowerSync backend instance.
|
|
@@ -106,99 +141,103 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
106
141
|
* connects.
|
|
107
142
|
*/
|
|
108
143
|
async connect(options) {
|
|
109
|
-
await this.
|
|
110
|
-
|
|
111
|
-
return getNavigatorLocks().request('shared-sync-connect', async () => {
|
|
112
|
-
if (!this.dbAdapter) {
|
|
113
|
-
await this.openInternalDB();
|
|
114
|
-
}
|
|
115
|
-
this.syncStreamClient = this.generateStreamingImplementation();
|
|
144
|
+
await this.portMutex.runExclusive(async () => {
|
|
145
|
+
// Keep track of the last connect options if we need to reconnect due to a lost client
|
|
116
146
|
this.lastConnectOptions = options;
|
|
117
|
-
this.
|
|
118
|
-
statusChanged: (status) => {
|
|
119
|
-
this.updateAllStatuses(status.toJSON());
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
await this.syncStreamClient.connect(options);
|
|
147
|
+
return this.connectionManager.connect(CONNECTOR_PLACEHOLDER, options);
|
|
123
148
|
});
|
|
124
149
|
}
|
|
125
150
|
async disconnect() {
|
|
126
|
-
await this.
|
|
127
|
-
|
|
128
|
-
return getNavigatorLocks().request('shared-sync-connect', async () => {
|
|
129
|
-
await this.syncStreamClient?.disconnect();
|
|
130
|
-
await this.syncStreamClient?.dispose();
|
|
131
|
-
this.syncStreamClient = null;
|
|
151
|
+
await this.portMutex.runExclusive(async () => {
|
|
152
|
+
await this.connectionManager.disconnect();
|
|
132
153
|
});
|
|
133
154
|
}
|
|
134
155
|
/**
|
|
135
156
|
* Adds a new client tab's message port to the list of connected ports
|
|
136
157
|
*/
|
|
137
|
-
addPort(port) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
async addPort(port) {
|
|
159
|
+
await this.portMutex.runExclusive(() => {
|
|
160
|
+
const portProvider = {
|
|
161
|
+
port,
|
|
162
|
+
clientProvider: Comlink.wrap(port)
|
|
163
|
+
};
|
|
164
|
+
this.ports.push(portProvider);
|
|
165
|
+
// Give the newly connected client the latest status
|
|
166
|
+
const status = this.connectionManager.syncStreamImplementation?.syncStatus;
|
|
167
|
+
if (status) {
|
|
168
|
+
portProvider.clientProvider.statusChanged(status.toJSON());
|
|
169
|
+
}
|
|
170
|
+
});
|
|
148
171
|
}
|
|
149
172
|
/**
|
|
150
173
|
* Removes a message port client from this manager's managed
|
|
151
174
|
* clients.
|
|
152
175
|
*/
|
|
153
176
|
async removePort(port) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const trackedPort = this.ports[index];
|
|
160
|
-
// Remove from the list of active ports
|
|
161
|
-
this.ports.splice(index, 1);
|
|
162
|
-
/**
|
|
163
|
-
* The port might currently be in use. Any active functions might
|
|
164
|
-
* not resolve. Abort them here.
|
|
165
|
-
*/
|
|
166
|
-
[this.fetchCredentialsController, this.uploadDataController].forEach((abortController) => {
|
|
167
|
-
if (abortController?.activePort.port == port) {
|
|
168
|
-
abortController.controller.abort(new AbortOperation('Closing pending requests after client port is removed'));
|
|
177
|
+
return await this.portMutex.runExclusive(async () => {
|
|
178
|
+
const index = this.ports.findIndex((p) => p.port == port);
|
|
179
|
+
if (index < 0) {
|
|
180
|
+
this.logger.warn(`Could not remove port ${port} since it is not present in active ports.`);
|
|
181
|
+
return;
|
|
169
182
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
183
|
+
const trackedPort = this.ports[index];
|
|
184
|
+
// Remove from the list of active ports
|
|
185
|
+
this.ports.splice(index, 1);
|
|
186
|
+
/**
|
|
187
|
+
* The port might currently be in use. Any active functions might
|
|
188
|
+
* not resolve. Abort them here.
|
|
189
|
+
*/
|
|
190
|
+
[this.fetchCredentialsController, this.uploadDataController].forEach((abortController) => {
|
|
191
|
+
if (abortController?.activePort.port == port) {
|
|
192
|
+
abortController.controller.abort(new AbortOperation('Closing pending requests after client port is removed'));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
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
|
+
}
|
|
175
205
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (shouldReconnect) {
|
|
179
|
-
await this.connect(this.lastConnectOptions);
|
|
206
|
+
if (trackedPort.db) {
|
|
207
|
+
await trackedPort.db.close();
|
|
180
208
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
trackedPort.
|
|
184
|
-
}
|
|
185
|
-
// Release proxy
|
|
186
|
-
trackedPort.clientProvider[Comlink.releaseProxy]();
|
|
209
|
+
this.logger.debug(`Port ${port} removed from shared sync implementation.`);
|
|
210
|
+
// Release proxy
|
|
211
|
+
return () => trackedPort.clientProvider[Comlink.releaseProxy]();
|
|
212
|
+
});
|
|
187
213
|
}
|
|
188
214
|
triggerCrudUpload() {
|
|
189
|
-
this.waitForReady().then(() => this.
|
|
190
|
-
}
|
|
191
|
-
async obtainLock(lockOptions) {
|
|
192
|
-
await this.waitForReady();
|
|
193
|
-
return this.syncStreamClient.obtainLock(lockOptions);
|
|
215
|
+
this.waitForReady().then(() => this.connectionManager.syncStreamImplementation?.triggerCrudUpload());
|
|
194
216
|
}
|
|
195
217
|
async hasCompletedSync() {
|
|
196
|
-
|
|
197
|
-
|
|
218
|
+
return this.withSyncImplementation(async (sync) => {
|
|
219
|
+
return sync.hasCompletedSync();
|
|
220
|
+
});
|
|
198
221
|
}
|
|
199
222
|
async getWriteCheckpoint() {
|
|
223
|
+
return this.withSyncImplementation(async (sync) => {
|
|
224
|
+
return sync.getWriteCheckpoint();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
async withSyncImplementation(callback) {
|
|
200
228
|
await this.waitForReady();
|
|
201
|
-
|
|
229
|
+
if (this.connectionManager.syncStreamImplementation) {
|
|
230
|
+
return callback(this.connectionManager.syncStreamImplementation);
|
|
231
|
+
}
|
|
232
|
+
const sync = await new Promise((resolve) => {
|
|
233
|
+
const dispose = this.connectionManager.registerListener({
|
|
234
|
+
syncStreamCreated: (sync) => {
|
|
235
|
+
resolve(sync);
|
|
236
|
+
dispose?.();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
return callback(sync);
|
|
202
241
|
}
|
|
203
242
|
generateStreamingImplementation() {
|
|
204
243
|
// This should only be called after initialization has completed
|
|
@@ -302,12 +341,12 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
302
341
|
* sync stream client and all tab client's sync status
|
|
303
342
|
*/
|
|
304
343
|
_testUpdateAllStatuses(status) {
|
|
305
|
-
if (!this.
|
|
344
|
+
if (!this.connectionManager.syncStreamImplementation) {
|
|
306
345
|
// This is just for testing purposes
|
|
307
|
-
this.
|
|
346
|
+
this.connectionManager.syncStreamImplementation = this.generateStreamingImplementation();
|
|
308
347
|
}
|
|
309
348
|
// Only assigning, don't call listeners for this test
|
|
310
|
-
this.
|
|
349
|
+
this.connectionManager.syncStreamImplementation.syncStatus = new SyncStatus(status);
|
|
311
350
|
this.updateAllStatuses(status);
|
|
312
351
|
}
|
|
313
352
|
}
|
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import * as Comlink from 'comlink';
|
|
2
|
-
import { SharedSyncImplementation, SharedSyncClientEvent } from './SharedSyncImplementation';
|
|
3
1
|
import { createBaseLogger } from '@powersync/common';
|
|
2
|
+
import * as Comlink from 'comlink';
|
|
3
|
+
import { SharedSyncClientEvent, SharedSyncImplementation } from './SharedSyncImplementation';
|
|
4
4
|
const _self = self;
|
|
5
5
|
const logger = createBaseLogger();
|
|
6
6
|
logger.useDefaults();
|
|
7
7
|
const sharedSyncImplementation = new SharedSyncImplementation();
|
|
8
|
-
_self.onconnect = function (event) {
|
|
8
|
+
_self.onconnect = async function (event) {
|
|
9
9
|
const port = event.ports[0];
|
|
10
10
|
/**
|
|
11
11
|
* Adds an extra listener which can remove this port
|
|
12
12
|
* from the list of monitored ports.
|
|
13
13
|
*/
|
|
14
|
-
port.addEventListener('message', (event) => {
|
|
14
|
+
port.addEventListener('message', async (event) => {
|
|
15
15
|
const payload = event.data;
|
|
16
16
|
if (payload?.event == SharedSyncClientEvent.CLOSE_CLIENT) {
|
|
17
|
-
sharedSyncImplementation.removePort(port);
|
|
17
|
+
const release = await sharedSyncImplementation.removePort(port);
|
|
18
|
+
port.postMessage({
|
|
19
|
+
event: SharedSyncClientEvent.CLOSE_ACK,
|
|
20
|
+
data: {}
|
|
21
|
+
});
|
|
22
|
+
release?.();
|
|
18
23
|
}
|
|
19
24
|
});
|
|
25
|
+
await sharedSyncImplementation.addPort(port);
|
|
20
26
|
Comlink.expose(sharedSyncImplementation, port);
|
|
21
|
-
sharedSyncImplementation.addPort(port);
|
|
22
27
|
};
|