@powersync/web 1.3.0 → 1.4.0
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/README.md +1 -25
- package/lib/src/db/PowerSyncDatabase.d.ts +2 -2
- package/lib/src/db/PowerSyncDatabase.js +35 -28
- package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js +7 -1
- package/lib/src/db/adapters/AbstractWebSQLOpenFactory.js +2 -0
- package/lib/src/db/adapters/SSRDBAdapter.js +28 -48
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +105 -124
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +4 -1
- package/lib/src/db/adapters/web-sql-flags.js +6 -3
- package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +2 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +66 -95
- package/lib/src/db/sync/WebRemote.d.ts +5 -1
- package/lib/src/db/sync/WebRemote.js +29 -21
- package/lib/src/shared/open-db.js +153 -184
- package/lib/src/worker/db/SharedWASQLiteDB.worker.js +15 -23
- package/lib/src/worker/db/WASQLiteDB.worker.js +1 -10
- package/lib/src/worker/sync/BroadcastLogger.js +16 -19
- package/lib/src/worker/sync/SharedSyncImplementation.js +96 -115
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -5
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -7
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import * as Comlink from 'comlink';
|
|
11
2
|
import Logger from 'js-logger';
|
|
12
3
|
import { BaseObserver, SqliteBucketStorage, SyncStatus, AbortOperation } from '@powersync/common';
|
|
@@ -30,6 +21,14 @@ export var SharedSyncClientEvent;
|
|
|
30
21
|
* Shared sync implementation which runs inside a shared webworker
|
|
31
22
|
*/
|
|
32
23
|
export class SharedSyncImplementation extends BaseObserver {
|
|
24
|
+
ports;
|
|
25
|
+
syncStreamClient;
|
|
26
|
+
isInitialized;
|
|
27
|
+
statusListener;
|
|
28
|
+
fetchCredentialsController;
|
|
29
|
+
uploadDataController;
|
|
30
|
+
syncStatus;
|
|
31
|
+
broadCastLogger;
|
|
33
32
|
constructor() {
|
|
34
33
|
super();
|
|
35
34
|
this.ports = [];
|
|
@@ -37,111 +36,104 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
37
36
|
const callback = this.registerListener({
|
|
38
37
|
initialized: () => {
|
|
39
38
|
resolve();
|
|
40
|
-
callback
|
|
39
|
+
callback?.();
|
|
41
40
|
}
|
|
42
41
|
});
|
|
43
42
|
});
|
|
44
43
|
this.syncStatus = new SyncStatus({});
|
|
45
44
|
this.broadCastLogger = new BroadcastLogger(this.ports);
|
|
46
45
|
}
|
|
47
|
-
waitForStatus(status) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return this.syncStreamClient.waitForStatus(status);
|
|
51
|
-
});
|
|
46
|
+
async waitForStatus(status) {
|
|
47
|
+
await this.waitForReady();
|
|
48
|
+
return this.syncStreamClient.waitForStatus(status);
|
|
52
49
|
}
|
|
53
50
|
get lastSyncedAt() {
|
|
54
|
-
|
|
55
|
-
return (_a = this.syncStreamClient) === null || _a === void 0 ? void 0 : _a.lastSyncedAt;
|
|
51
|
+
return this.syncStreamClient?.lastSyncedAt;
|
|
56
52
|
}
|
|
57
53
|
get isConnected() {
|
|
58
|
-
|
|
59
|
-
return (_b = (_a = this.syncStreamClient) === null || _a === void 0 ? void 0 : _a.isConnected) !== null && _b !== void 0 ? _b : false;
|
|
54
|
+
return this.syncStreamClient?.isConnected ?? false;
|
|
60
55
|
}
|
|
61
|
-
waitForReady() {
|
|
62
|
-
return
|
|
63
|
-
return this.isInitialized;
|
|
64
|
-
});
|
|
56
|
+
async waitForReady() {
|
|
57
|
+
return this.isInitialized;
|
|
65
58
|
}
|
|
66
59
|
/**
|
|
67
60
|
* Configures the DBAdapter connection and a streaming sync client.
|
|
68
61
|
*/
|
|
69
|
-
init(dbWorkerPort, params) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const lastPort = this.ports[this.ports.length - 1];
|
|
89
|
-
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
const abortController = new AbortController();
|
|
91
|
-
this.fetchCredentialsController = {
|
|
92
|
-
controller: abortController,
|
|
93
|
-
activePort: lastPort
|
|
94
|
-
};
|
|
95
|
-
abortController.signal.onabort = reject;
|
|
96
|
-
try {
|
|
97
|
-
resolve(yield lastPort.clientProvider.fetchCredentials());
|
|
98
|
-
}
|
|
99
|
-
catch (ex) {
|
|
100
|
-
reject(ex);
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
this.fetchCredentialsController = undefined;
|
|
104
|
-
}
|
|
105
|
-
}));
|
|
106
|
-
})
|
|
107
|
-
}), uploadCrud: () => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
async init(dbWorkerPort, params) {
|
|
63
|
+
if (this.syncStreamClient) {
|
|
64
|
+
// Cannot modify already existing sync implementation
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const logger = params.streamOptions?.flags?.broadcastLogs ? this.broadCastLogger : Logger.get('shared-sync');
|
|
68
|
+
self.onerror = (event) => {
|
|
69
|
+
// Share any uncaught events on the broadcast logger
|
|
70
|
+
logger.error('Uncaught exception in PowerSync shared sync worker', event);
|
|
71
|
+
};
|
|
72
|
+
this.syncStreamClient = new WebStreamingSyncImplementation({
|
|
73
|
+
adapter: new SqliteBucketStorage(new WASQLiteDBAdapter({
|
|
74
|
+
dbFilename: params.dbName,
|
|
75
|
+
workerPort: dbWorkerPort,
|
|
76
|
+
flags: { enableMultiTabs: true, useWebWorker: true },
|
|
77
|
+
logger
|
|
78
|
+
}), new Mutex(), logger),
|
|
79
|
+
remote: new WebRemote({
|
|
80
|
+
fetchCredentials: async () => {
|
|
108
81
|
const lastPort = this.ports[this.ports.length - 1];
|
|
109
|
-
return new Promise((resolve, reject) =>
|
|
82
|
+
return new Promise(async (resolve, reject) => {
|
|
110
83
|
const abortController = new AbortController();
|
|
111
|
-
this.
|
|
84
|
+
this.fetchCredentialsController = {
|
|
112
85
|
controller: abortController,
|
|
113
86
|
activePort: lastPort
|
|
114
87
|
};
|
|
115
|
-
|
|
116
|
-
abortController.signal.onabort = () => resolve();
|
|
88
|
+
abortController.signal.onabort = reject;
|
|
117
89
|
try {
|
|
118
|
-
resolve(
|
|
90
|
+
resolve(await lastPort.clientProvider.fetchCredentials());
|
|
119
91
|
}
|
|
120
92
|
catch (ex) {
|
|
121
93
|
reject(ex);
|
|
122
94
|
}
|
|
123
95
|
finally {
|
|
124
|
-
this.
|
|
96
|
+
this.fetchCredentialsController = undefined;
|
|
125
97
|
}
|
|
126
|
-
})
|
|
127
|
-
}) }, params.streamOptions), {
|
|
128
|
-
// Logger cannot be transferred just yet
|
|
129
|
-
logger }));
|
|
130
|
-
this.syncStreamClient.registerListener({
|
|
131
|
-
statusChanged: (status) => {
|
|
132
|
-
this.updateAllStatuses(status.toJSON());
|
|
98
|
+
});
|
|
133
99
|
}
|
|
134
|
-
})
|
|
135
|
-
|
|
100
|
+
}),
|
|
101
|
+
uploadCrud: async () => {
|
|
102
|
+
const lastPort = this.ports[this.ports.length - 1];
|
|
103
|
+
return new Promise(async (resolve, reject) => {
|
|
104
|
+
const abortController = new AbortController();
|
|
105
|
+
this.uploadDataController = {
|
|
106
|
+
controller: abortController,
|
|
107
|
+
activePort: lastPort
|
|
108
|
+
};
|
|
109
|
+
// Resolving will make it retry
|
|
110
|
+
abortController.signal.onabort = () => resolve();
|
|
111
|
+
try {
|
|
112
|
+
resolve(await lastPort.clientProvider.uploadCrud());
|
|
113
|
+
}
|
|
114
|
+
catch (ex) {
|
|
115
|
+
reject(ex);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
this.uploadDataController = undefined;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
...params.streamOptions,
|
|
123
|
+
// Logger cannot be transferred just yet
|
|
124
|
+
logger
|
|
136
125
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
yield this.waitForReady();
|
|
142
|
-
(_a = this.statusListener) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
143
|
-
return (_b = this.syncStreamClient) === null || _b === void 0 ? void 0 : _b.dispose();
|
|
126
|
+
this.syncStreamClient.registerListener({
|
|
127
|
+
statusChanged: (status) => {
|
|
128
|
+
this.updateAllStatuses(status.toJSON());
|
|
129
|
+
}
|
|
144
130
|
});
|
|
131
|
+
this.iterateListeners((l) => l.initialized?.());
|
|
132
|
+
}
|
|
133
|
+
async dispose() {
|
|
134
|
+
await this.waitForReady();
|
|
135
|
+
this.statusListener?.();
|
|
136
|
+
return this.syncStreamClient?.dispose();
|
|
145
137
|
}
|
|
146
138
|
/**
|
|
147
139
|
* Connects to the PowerSync backend instance.
|
|
@@ -149,32 +141,27 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
149
141
|
* The connection will simply be reconnected whenever a new tab
|
|
150
142
|
* connects.
|
|
151
143
|
*/
|
|
152
|
-
connect(options) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return navigator.locks.request('shared-sync-connect', () => { var _a; return (_a = this.syncStreamClient) === null || _a === void 0 ? void 0 : _a.connect(options); });
|
|
157
|
-
});
|
|
144
|
+
async connect(options) {
|
|
145
|
+
await this.waitForReady();
|
|
146
|
+
// This effectively queues connect and disconnect calls. Ensuring multiple tabs' requests are synchronized
|
|
147
|
+
return navigator.locks.request('shared-sync-connect', () => this.syncStreamClient?.connect(options));
|
|
158
148
|
}
|
|
159
|
-
disconnect() {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return navigator.locks.request('shared-sync-connect', () => { var _a; return (_a = this.syncStreamClient) === null || _a === void 0 ? void 0 : _a.disconnect(); });
|
|
164
|
-
});
|
|
149
|
+
async disconnect() {
|
|
150
|
+
await this.waitForReady();
|
|
151
|
+
// This effectively queues connect and disconnect calls. Ensuring multiple tabs' requests are synchronized
|
|
152
|
+
return navigator.locks.request('shared-sync-connect', () => this.syncStreamClient?.disconnect());
|
|
165
153
|
}
|
|
166
154
|
/**
|
|
167
155
|
* Adds a new client tab's message port to the list of connected ports
|
|
168
156
|
*/
|
|
169
157
|
addPort(port) {
|
|
170
|
-
var _a;
|
|
171
158
|
const portProvider = {
|
|
172
159
|
port,
|
|
173
160
|
clientProvider: Comlink.wrap(port)
|
|
174
161
|
};
|
|
175
162
|
this.ports.push(portProvider);
|
|
176
163
|
// Give the newly connected client the latest status
|
|
177
|
-
const status =
|
|
164
|
+
const status = this.syncStreamClient?.syncStatus;
|
|
178
165
|
if (status) {
|
|
179
166
|
portProvider.clientProvider.statusChanged(status.toJSON());
|
|
180
167
|
}
|
|
@@ -198,31 +185,25 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
198
185
|
* not resolve. Abort them here.
|
|
199
186
|
*/
|
|
200
187
|
[this.fetchCredentialsController, this.uploadDataController].forEach((abortController) => {
|
|
201
|
-
if (
|
|
188
|
+
if (abortController?.activePort.port == port) {
|
|
202
189
|
abortController.controller.abort(new AbortOperation('Closing pending requests after client port is removed'));
|
|
203
190
|
}
|
|
204
191
|
});
|
|
205
192
|
}
|
|
206
193
|
triggerCrudUpload() {
|
|
207
|
-
this.waitForReady().then(() =>
|
|
194
|
+
this.waitForReady().then(() => this.syncStreamClient?.triggerCrudUpload());
|
|
208
195
|
}
|
|
209
|
-
obtainLock(lockOptions) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return this.syncStreamClient.obtainLock(lockOptions);
|
|
213
|
-
});
|
|
196
|
+
async obtainLock(lockOptions) {
|
|
197
|
+
await this.waitForReady();
|
|
198
|
+
return this.syncStreamClient.obtainLock(lockOptions);
|
|
214
199
|
}
|
|
215
|
-
hasCompletedSync() {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return this.syncStreamClient.hasCompletedSync();
|
|
219
|
-
});
|
|
200
|
+
async hasCompletedSync() {
|
|
201
|
+
await this.waitForReady();
|
|
202
|
+
return this.syncStreamClient.hasCompletedSync();
|
|
220
203
|
}
|
|
221
|
-
getWriteCheckpoint() {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return this.syncStreamClient.getWriteCheckpoint();
|
|
225
|
-
});
|
|
204
|
+
async getWriteCheckpoint() {
|
|
205
|
+
await this.waitForReady();
|
|
206
|
+
return this.syncStreamClient.getWriteCheckpoint();
|
|
226
207
|
}
|
|
227
208
|
/**
|
|
228
209
|
* A method to update the all shared statuses for each
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import * as Comlink from 'comlink';
|
|
2
2
|
import { SharedSyncImplementation, SharedSyncClientEvent } from './SharedSyncImplementation';
|
|
3
3
|
import Logger from 'js-logger';
|
|
4
|
-
import { Buffer } from 'buffer';
|
|
5
|
-
if (typeof self.Buffer == 'undefined') {
|
|
6
|
-
self.Buffer = Buffer;
|
|
7
|
-
}
|
|
8
4
|
const _self = self;
|
|
9
5
|
Logger.useDefaults();
|
|
10
6
|
const sharedSyncImplementation = new SharedSyncImplementation();
|
|
@@ -16,7 +12,7 @@ _self.onconnect = function (event) {
|
|
|
16
12
|
*/
|
|
17
13
|
port.addEventListener('message', (event) => {
|
|
18
14
|
const payload = event.data;
|
|
19
|
-
if (
|
|
15
|
+
if (payload?.event == SharedSyncClientEvent.CLOSE_CLIENT) {
|
|
20
16
|
console.log('closing shared for port', port);
|
|
21
17
|
sharedSyncImplementation.removePort(port);
|
|
22
18
|
}
|