@sqlite-sync/core 0.0.1 → 0.0.3
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/chunk-627DSM2Q.js +1410 -0
- package/dist/chunk-627DSM2Q.js.map +1 -0
- package/dist/chunk-UGF5IU53.js +132 -0
- package/dist/chunk-UGF5IU53.js.map +1 -0
- package/dist/crdt-schema-DQ1cYsFE.d.ts +63 -0
- package/dist/crdt-sync-remote-source-idoIjMcs.d.ts +479 -0
- package/dist/index.d.ts +117 -207
- package/dist/index.js +614 -357
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +50 -26
- package/dist/server.js +28 -36
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +13 -11
- package/dist/worker.js +275 -140
- package/dist/worker.js.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-LK5FJCUD.js +0 -522
- package/dist/chunk-LK5FJCUD.js.map +0 -1
- package/dist/chunk-YLXMST5Z.js +0 -490
- package/dist/chunk-YLXMST5Z.js.map +0 -1
- package/dist/crdt-sync-producer-0toEpGf0.d.ts +0 -15
- package/dist/crdt-sync-remote-source-rrqinqLn.d.ts +0 -271
package/dist/worker.js
CHANGED
|
@@ -1,24 +1,110 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SQLiteDbWrapper,
|
|
3
|
+
applyKyselyEventsBatchFilters,
|
|
3
4
|
applyWorkerDbSchema,
|
|
4
5
|
createBroadcastChannels,
|
|
6
|
+
createCrdtStorage,
|
|
7
|
+
createCrdtSyncProducer,
|
|
5
8
|
createCrdtSyncRemoteSource,
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
createMigrator,
|
|
10
|
+
createSQLiteCrdtApplyFunction,
|
|
11
|
+
createSQLiteKvStore,
|
|
12
|
+
createStoredValue,
|
|
8
13
|
isWorkerInitMessage,
|
|
9
14
|
isWorkerRequestMessage,
|
|
15
|
+
syncDbClientLockName,
|
|
10
16
|
syncDbWorkerLockName
|
|
11
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-627DSM2Q.js";
|
|
12
18
|
import {
|
|
13
|
-
applyCrdtEventMutations,
|
|
14
|
-
createCrdtStorage,
|
|
15
|
-
createCrdtSyncProducer,
|
|
16
19
|
createDeferredPromise,
|
|
17
|
-
|
|
18
|
-
} from "./chunk-
|
|
20
|
+
jsonSafeParse
|
|
21
|
+
} from "./chunk-UGF5IU53.js";
|
|
22
|
+
|
|
23
|
+
// src/web-socket/ws-remote-source.ts
|
|
24
|
+
var createWsRemoteSource = ({ createWebSocket }) => {
|
|
25
|
+
return async ({ onEventsAvailable }) => {
|
|
26
|
+
const socket = createWebSocket();
|
|
27
|
+
const openPromise = createDeferredPromise({
|
|
28
|
+
timeout: 5e3,
|
|
29
|
+
onTimeout: () => {
|
|
30
|
+
socket.close();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
socket.addEventListener("open", () => {
|
|
34
|
+
openPromise.resolve(void 0);
|
|
35
|
+
});
|
|
36
|
+
await openPromise.promise;
|
|
37
|
+
const requestsMap = /* @__PURE__ */ new Map();
|
|
38
|
+
const pushEvents = async (request) => {
|
|
39
|
+
const requestId = crypto.randomUUID();
|
|
40
|
+
const promise = createDeferredPromise({ timeout: 5e3 });
|
|
41
|
+
requestsMap.set(requestId, promise);
|
|
42
|
+
const wsRequest = {
|
|
43
|
+
type: "push-events",
|
|
44
|
+
requestId,
|
|
45
|
+
nodeId: request.nodeId,
|
|
46
|
+
events: request.events
|
|
47
|
+
};
|
|
48
|
+
socket.send(JSON.stringify(wsRequest));
|
|
49
|
+
return promise.promise;
|
|
50
|
+
};
|
|
51
|
+
const pullEvents = async (request) => {
|
|
52
|
+
const requestId = crypto.randomUUID();
|
|
53
|
+
const promise = createDeferredPromise({ timeout: 2e3 });
|
|
54
|
+
requestsMap.set(requestId, promise);
|
|
55
|
+
const wsRequest = {
|
|
56
|
+
type: "pull-events",
|
|
57
|
+
requestId,
|
|
58
|
+
afterSyncId: request.afterSyncId,
|
|
59
|
+
excludeNodeId: request.excludeNodeId
|
|
60
|
+
};
|
|
61
|
+
socket.send(JSON.stringify(wsRequest));
|
|
62
|
+
return promise.promise;
|
|
63
|
+
};
|
|
64
|
+
socket.onmessage = (event) => {
|
|
65
|
+
const result = jsonSafeParse(event.data);
|
|
66
|
+
if (!result.success || !("type" in result.data) || !result.data.type) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const message = result.data;
|
|
70
|
+
switch (message.type) {
|
|
71
|
+
case "events-pull-response": {
|
|
72
|
+
const promise = requestsMap.get(message.requestId);
|
|
73
|
+
if (!promise) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
promise.resolve(message.data);
|
|
77
|
+
requestsMap.delete(message.requestId);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "events-push-response": {
|
|
81
|
+
const promise = requestsMap.get(message.requestId);
|
|
82
|
+
if (!promise) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
promise.resolve(message.data);
|
|
86
|
+
requestsMap.delete(message.requestId);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case "events-applied":
|
|
90
|
+
onEventsAvailable(message.newSyncId);
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
message;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
pushEvents,
|
|
99
|
+
pullEvents,
|
|
100
|
+
disconnect: () => {
|
|
101
|
+
socket.close();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
19
106
|
|
|
20
107
|
// src/worker-db/db-worker.ts
|
|
21
|
-
import { sql } from "kysely";
|
|
22
108
|
import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
|
|
23
109
|
var defaultLogger = (type, message, level = "info") => {
|
|
24
110
|
const logMessage = `[${type}] ${message}`;
|
|
@@ -38,46 +124,69 @@ var defaultLogger = (type, message, level = "info") => {
|
|
|
38
124
|
}
|
|
39
125
|
};
|
|
40
126
|
async function createDbWorker(config, opts) {
|
|
41
|
-
const broadcastChannels = createBroadcastChannels();
|
|
127
|
+
const broadcastChannels = createBroadcastChannels(config.dbId);
|
|
42
128
|
const logger = opts.logger ?? defaultLogger;
|
|
43
129
|
const sqlite3 = await sqlite3InitModule();
|
|
44
130
|
const pool = await sqlite3.installOpfsSAHPoolVfs({
|
|
45
|
-
name:
|
|
46
|
-
|
|
131
|
+
name: config.dbId,
|
|
132
|
+
directory: `.${config.dbId}`,
|
|
133
|
+
clearOnInit: config.clearOnInit,
|
|
134
|
+
initialCapacity: 8
|
|
47
135
|
});
|
|
48
136
|
const db = new SQLiteDbWrapper({
|
|
49
|
-
db: new pool.OpfsSAHPoolDb(config.
|
|
137
|
+
db: () => new pool.OpfsSAHPoolDb(`/${config.dbId}-main.db`),
|
|
50
138
|
logger,
|
|
51
139
|
loggerPrefix: "worker",
|
|
52
140
|
sqlite3
|
|
53
141
|
});
|
|
54
|
-
db.execute("PRAGMA locking_mode=exclusive");
|
|
55
|
-
db.execute("PRAGMA journal_mode=WAL");
|
|
56
|
-
db.execute(
|
|
142
|
+
db.execute("PRAGMA locking_mode=exclusive", { loggerLevel: "system" });
|
|
143
|
+
db.execute("PRAGMA journal_mode=WAL", { loggerLevel: "system" });
|
|
144
|
+
db.execute("PRAGMA synchronous=NORMAL", { loggerLevel: "system" });
|
|
145
|
+
db.execute(`ATTACH DATABASE '/${config.dbId}-worker.db' as worker`, { loggerLevel: "system" });
|
|
146
|
+
db.execute("PRAGMA worker.locking_mode=exclusive", { loggerLevel: "system" });
|
|
147
|
+
db.execute("PRAGMA worker.journal_mode=WAL", { loggerLevel: "system" });
|
|
148
|
+
db.execute("PRAGMA worker.synchronous=NORMAL", { loggerLevel: "system" });
|
|
57
149
|
applyWorkerDbSchema(db);
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
150
|
+
const kvStore = createSQLiteKvStore({
|
|
151
|
+
db,
|
|
152
|
+
metaTableName: "worker.kv"
|
|
153
|
+
});
|
|
154
|
+
const migrator = createMigrator({
|
|
155
|
+
migrations: opts.syncDbSchema.migrations,
|
|
156
|
+
schemaVersion: kvStore.createNumberStoredValue("schema-version", -1),
|
|
157
|
+
updateLogTableName: '"crdt_update_log"'
|
|
158
|
+
});
|
|
159
|
+
migrator.migrateDbToLatest({
|
|
160
|
+
startTransaction: (callback) => {
|
|
161
|
+
db.executeTransaction((tx) => callback({ execute: (sql, parameters) => tx.execute({ sql, parameters }) }));
|
|
162
|
+
}
|
|
62
163
|
});
|
|
63
|
-
await migrator.migrateToLatest();
|
|
64
164
|
db.invalidateDbSchema();
|
|
65
|
-
const localSyncId =
|
|
66
|
-
|
|
165
|
+
const localSyncId = createStoredValue({
|
|
166
|
+
initialValue: getLatestSyncId(db)
|
|
67
167
|
});
|
|
68
168
|
const crdtStorage = createCrdtStorage({
|
|
169
|
+
nodeId: config.clientId,
|
|
69
170
|
syncId: localSyncId,
|
|
70
|
-
|
|
171
|
+
migrator,
|
|
172
|
+
hlc: {
|
|
173
|
+
getNextHLC() {
|
|
174
|
+
throw new Error("Worker DB should not call getNextHLC");
|
|
175
|
+
},
|
|
176
|
+
mergeHLC() {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
transaction: (callback) => db.executeTransaction(callback),
|
|
181
|
+
handleCrdtEventApply: createSQLiteCrdtApplyFunction({
|
|
71
182
|
db,
|
|
72
|
-
event,
|
|
73
183
|
updateLogTableName: "crdt_update_log"
|
|
74
184
|
}),
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
185
|
+
persistEvent: (event) => persistEvent(db, event),
|
|
186
|
+
getEventsBatch: (opts2) => getEventsBatch(db, opts2),
|
|
187
|
+
updateEvent: (syncId, update) => updateEvent(db, syncId, update)
|
|
78
188
|
});
|
|
79
189
|
createCrdtSyncProducer({
|
|
80
|
-
bufferSize: 100,
|
|
81
190
|
storage: crdtStorage,
|
|
82
191
|
broadcastEvents: (chunk) => {
|
|
83
192
|
broadcastChannels.responses.postMessage({
|
|
@@ -86,89 +195,112 @@ async function createDbWorker(config, opts) {
|
|
|
86
195
|
});
|
|
87
196
|
}
|
|
88
197
|
});
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
198
|
+
const postState = () => {
|
|
199
|
+
broadcastChannels.responses.postMessage({
|
|
200
|
+
notificationType: "state-changed",
|
|
201
|
+
state: {
|
|
202
|
+
remoteState: remoteSource.getState()
|
|
94
203
|
}
|
|
95
204
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
pullEvents: remoteSource.pullEvents,
|
|
109
|
-
pushEvents: remoteSource.pushEvents
|
|
110
|
-
});
|
|
111
|
-
await crdtSyncRemoteSource.pullEvents({ includeSelf: true });
|
|
112
|
-
}
|
|
205
|
+
};
|
|
206
|
+
const remoteSource = createRemoteSource({
|
|
207
|
+
kvStore,
|
|
208
|
+
crdtStorage,
|
|
209
|
+
migrator,
|
|
210
|
+
clientId: config.clientId,
|
|
211
|
+
remoteFactory: opts.createRemoteSource
|
|
212
|
+
});
|
|
213
|
+
remoteSource.goOnline();
|
|
214
|
+
remoteSource.addEventListener("state-changed", () => {
|
|
215
|
+
postState();
|
|
216
|
+
});
|
|
113
217
|
const rpcTarget = {
|
|
114
218
|
execute: (query) => db.execute(query),
|
|
115
219
|
getSnapshot: () => {
|
|
116
|
-
db.execute("PRAGMA journal_mode=off");
|
|
220
|
+
db.execute("PRAGMA journal_mode=off", { loggerLevel: "system" });
|
|
117
221
|
const file = db.createSnapshot();
|
|
118
|
-
db.execute("PRAGMA journal_mode=WAL");
|
|
222
|
+
db.execute("PRAGMA journal_mode=WAL", { loggerLevel: "system" });
|
|
119
223
|
return {
|
|
120
224
|
file,
|
|
121
|
-
syncId: localSyncId.current
|
|
225
|
+
syncId: localSyncId.current,
|
|
226
|
+
schemaVersion: migrator.currentSchemaVersion
|
|
122
227
|
};
|
|
123
228
|
},
|
|
124
|
-
|
|
125
|
-
broadcastChannels.responses.postMessage({
|
|
126
|
-
type: "init-ready"
|
|
127
|
-
});
|
|
128
|
-
},
|
|
229
|
+
postState,
|
|
129
230
|
pushTabEvents: (request) => {
|
|
130
|
-
crdtStorage.
|
|
131
|
-
request.events.map((event) => ({
|
|
132
|
-
...event,
|
|
133
|
-
origin: request.nodeId
|
|
134
|
-
}))
|
|
135
|
-
);
|
|
231
|
+
crdtStorage.enqueueLocalEvents(request.events, request.nodeId);
|
|
136
232
|
return {
|
|
137
233
|
ok: true
|
|
138
234
|
};
|
|
139
235
|
},
|
|
140
236
|
pullEvents: (request) => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
hasMore: false,
|
|
151
|
-
newSyncId: events[events.length - 1]?.sync_id ?? request.afterSyncId
|
|
152
|
-
};
|
|
153
|
-
}
|
|
237
|
+
return crdtStorage.getEventsBatch({
|
|
238
|
+
afterSyncId: request.afterSyncId,
|
|
239
|
+
status: "applied",
|
|
240
|
+
excludeNodeId: request.excludeNodeId,
|
|
241
|
+
limit: 100
|
|
242
|
+
});
|
|
243
|
+
},
|
|
244
|
+
goOnline: () => remoteSource.goOnline(),
|
|
245
|
+
goOffline: () => remoteSource.goOffline("DISCONNECTED")
|
|
154
246
|
};
|
|
155
247
|
broadcastChannels.requests.onmessage = (event) => {
|
|
156
248
|
const message = event.data;
|
|
157
249
|
if (!isWorkerRequestMessage(message)) {
|
|
158
250
|
return;
|
|
159
251
|
}
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
252
|
+
const sendError = (error) => {
|
|
253
|
+
const response = {
|
|
254
|
+
type: "error-response",
|
|
255
|
+
requestId: message.requestId,
|
|
256
|
+
error: error instanceof Error ? error.message : String(error)
|
|
257
|
+
};
|
|
258
|
+
broadcastChannels.responses.postMessage(response);
|
|
166
259
|
};
|
|
167
|
-
|
|
260
|
+
try {
|
|
261
|
+
const method = rpcTarget[message.method];
|
|
262
|
+
const data = method.apply(null, message.args);
|
|
263
|
+
if (data instanceof Promise) {
|
|
264
|
+
data.then((result) => {
|
|
265
|
+
const response = {
|
|
266
|
+
type: "response",
|
|
267
|
+
requestId: message.requestId,
|
|
268
|
+
data: result
|
|
269
|
+
};
|
|
270
|
+
broadcastChannels.responses.postMessage(response);
|
|
271
|
+
}).catch(sendError);
|
|
272
|
+
} else {
|
|
273
|
+
const response = {
|
|
274
|
+
type: "response",
|
|
275
|
+
requestId: message.requestId,
|
|
276
|
+
data
|
|
277
|
+
};
|
|
278
|
+
broadcastChannels.responses.postMessage(response);
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
sendError(error);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
rpcTarget.postState();
|
|
285
|
+
return async () => {
|
|
286
|
+
await remoteSource.dispose();
|
|
287
|
+
broadcastChannels.requests.close();
|
|
288
|
+
broadcastChannels.responses.close();
|
|
289
|
+
db.close();
|
|
168
290
|
};
|
|
169
|
-
rpcTarget.postInitReady();
|
|
170
291
|
}
|
|
171
|
-
|
|
292
|
+
function createRemoteSource({ kvStore, clientId, crdtStorage, migrator, remoteFactory }) {
|
|
293
|
+
return createCrdtSyncRemoteSource({
|
|
294
|
+
bufferSize: 50,
|
|
295
|
+
pullSyncId: kvStore.createNumberStoredValue("pull-sync-id", -1),
|
|
296
|
+
pushSyncId: kvStore.createNumberStoredValue("push-sync-id", -1),
|
|
297
|
+
nodeId: clientId,
|
|
298
|
+
storage: crdtStorage,
|
|
299
|
+
migrator,
|
|
300
|
+
remoteFactory
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
async function getWorkerConfig() {
|
|
172
304
|
let configSet = false;
|
|
173
305
|
const responsePromise = createDeferredPromise();
|
|
174
306
|
self.onmessage = (event) => {
|
|
@@ -186,76 +318,79 @@ async function getConfig() {
|
|
|
186
318
|
return responsePromise.promise;
|
|
187
319
|
}
|
|
188
320
|
async function startDbWorker(opts) {
|
|
189
|
-
const config = await
|
|
190
|
-
await navigator.locks.request(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
async (lock) => {
|
|
194
|
-
if (!lock) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
await createDbWorker(config, opts);
|
|
198
|
-
await new Promise(() => {
|
|
199
|
-
});
|
|
321
|
+
const config = opts.workerConfig ?? await getWorkerConfig();
|
|
322
|
+
await navigator.locks.request(`${syncDbWorkerLockName}-${config.dbId}`, { mode: "exclusive" }, async (lock) => {
|
|
323
|
+
if (!lock) {
|
|
324
|
+
return;
|
|
200
325
|
}
|
|
201
|
-
|
|
202
|
-
|
|
326
|
+
const cleanup = await createDbWorker(config, opts);
|
|
327
|
+
const clientLockName = `${syncDbClientLockName}-${config.dbId}`;
|
|
328
|
+
await new Promise((resolve) => {
|
|
329
|
+
const interval = setInterval(async () => {
|
|
330
|
+
const { held } = await navigator.locks.query();
|
|
331
|
+
const hasClients = held?.some((l) => l.name === clientLockName && l.mode === "shared");
|
|
332
|
+
if (!hasClients) {
|
|
333
|
+
clearInterval(interval);
|
|
334
|
+
resolve();
|
|
335
|
+
}
|
|
336
|
+
}, 5e3);
|
|
337
|
+
});
|
|
338
|
+
await cleanup();
|
|
339
|
+
});
|
|
340
|
+
self.close();
|
|
203
341
|
}
|
|
204
342
|
function getLatestSyncId(db) {
|
|
205
343
|
const result = db.executePrepared(
|
|
206
344
|
"get-latest-sync-id",
|
|
207
345
|
{},
|
|
208
|
-
(db2) => db2.selectFrom("worker.crdt_events").select((eb) => eb.fn.max("sync_id").as("sync_id"))
|
|
346
|
+
(db2) => db2.selectFrom("worker.crdt_events").select((eb) => eb.fn.max("sync_id").as("sync_id")),
|
|
347
|
+
{ loggerLevel: "system" }
|
|
209
348
|
);
|
|
210
349
|
return result[0]?.sync_id ?? 0;
|
|
211
350
|
}
|
|
212
|
-
function
|
|
213
|
-
db.executeTransaction((db2) => {
|
|
214
|
-
const chunkSize = 100;
|
|
215
|
-
for (let i = 0; i < events.length; i += chunkSize) {
|
|
216
|
-
const chunk = events.slice(i, i + chunkSize);
|
|
217
|
-
db2.executeKysely(
|
|
218
|
-
(db3) => db3.insertInto("worker.crdt_events").values(chunk)
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
function popPendingEventsBatch(db, limit) {
|
|
224
|
-
const events = db.executePrepared(
|
|
225
|
-
"pop-enqueued-crdt-events",
|
|
226
|
-
{
|
|
227
|
-
limit
|
|
228
|
-
},
|
|
229
|
-
(db2, param) => db2.selectFrom("worker.crdt_events").where("status", "=", sql.lit("pending")).limit(param("limit")).orderBy("sync_id", "asc").selectAll()
|
|
230
|
-
);
|
|
231
|
-
return {
|
|
232
|
-
events,
|
|
233
|
-
hasMore: events.length === limit
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
function updateEventStatus(db, syncId, status) {
|
|
351
|
+
function persistEvent(db, event) {
|
|
237
352
|
db.executePrepared(
|
|
238
|
-
"
|
|
239
|
-
|
|
240
|
-
(db2, params) => db2.
|
|
353
|
+
"persist-crdt-event",
|
|
354
|
+
event,
|
|
355
|
+
(db2, params) => db2.insertInto("worker.crdt_events").values({
|
|
356
|
+
type: params("type"),
|
|
357
|
+
dataset: params("dataset"),
|
|
358
|
+
item_id: params("item_id"),
|
|
359
|
+
payload: params("payload"),
|
|
360
|
+
schema_version: params("schema_version"),
|
|
361
|
+
sync_id: params("sync_id"),
|
|
362
|
+
status: params("status"),
|
|
363
|
+
timestamp: params("timestamp"),
|
|
364
|
+
origin: params("origin"),
|
|
365
|
+
source_node_id: params("source_node_id")
|
|
366
|
+
}),
|
|
367
|
+
{ loggerLevel: "system" }
|
|
241
368
|
);
|
|
242
369
|
}
|
|
243
|
-
function
|
|
244
|
-
|
|
245
|
-
"
|
|
246
|
-
{
|
|
247
|
-
|
|
248
|
-
);
|
|
249
|
-
return result?.value ?? null;
|
|
370
|
+
function getEventsBatch(db, opts) {
|
|
371
|
+
return db.executeKysely(
|
|
372
|
+
(db2) => applyKyselyEventsBatchFilters(db2.selectFrom("worker.crdt_events").selectAll(), opts),
|
|
373
|
+
{ loggerLevel: "system" }
|
|
374
|
+
).rows;
|
|
250
375
|
}
|
|
251
|
-
function
|
|
376
|
+
function updateEvent(db, syncId, update) {
|
|
252
377
|
db.executePrepared(
|
|
253
|
-
"
|
|
254
|
-
{
|
|
255
|
-
(db2, params) => db2.
|
|
378
|
+
"update-crdt-event",
|
|
379
|
+
{ syncId, ...update },
|
|
380
|
+
(db2, params) => db2.updateTable("worker.crdt_events").set({
|
|
381
|
+
status: params("status"),
|
|
382
|
+
schema_version: params("schema_version"),
|
|
383
|
+
type: params("type"),
|
|
384
|
+
dataset: params("dataset"),
|
|
385
|
+
item_id: params("item_id"),
|
|
386
|
+
payload: params("payload")
|
|
387
|
+
}).where("sync_id", "=", params("syncId")),
|
|
388
|
+
{ loggerLevel: "system" }
|
|
256
389
|
);
|
|
257
390
|
}
|
|
258
391
|
export {
|
|
392
|
+
createWsRemoteSource,
|
|
393
|
+
getWorkerConfig,
|
|
259
394
|
startDbWorker
|
|
260
395
|
};
|
|
261
396
|
//# sourceMappingURL=worker.js.map
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker-db/db-worker.ts"],"sourcesContent":["/* eslint-disable prefer-spread */\nimport { sql, type Kysely, type Migration } from \"kysely\";\nimport { createDeferredPromise } from \"../utils\";\nimport {\n createBroadcastChannels,\n isWorkerInitMessage,\n isWorkerRequestMessage,\n syncDbWorkerLockName,\n type WorkerConfig,\n type WorkerResponseMessage,\n type WorkerRpc,\n} from \"./worker-common\";\nimport sqlite3InitModule from \"@sqlite.org/sqlite-wasm\";\nimport { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport {\n applyWorkerDbSchema,\n type WorkerDbSchema,\n} from \"../migrations/system-schema\";\nimport { createSQLiteKysely } from \"../sqlite-kysely\";\nimport { createSyncDbMigrator } from \"../migrations/migrator\";\nimport { createSyncIdCounter } from \"../sqlite-crdt/sync-id-counter\";\nimport { applyCrdtEventMutations } from \"../sqlite-crdt/apply-crdt-event\";\nimport { createCrdtStorage } from \"../sqlite-crdt/crdt-storage\";\nimport type {\n CrdtEventStatus,\n PersistedCrdtEvent,\n} from \"../sqlite-crdt/crdt-table-schema\";\nimport { createCrdtSyncProducer } from \"../sqlite-crdt/crdt-sync-producer\";\nimport {\n createCrdtSyncRemoteSource,\n type CrdtSyncRemoteSource,\n type EventsPullRequest,\n type EventsPullResponse,\n type EventsPushRequest,\n type EventsPushResponse,\n} from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { Logger } from \"../logger\";\n\nconst defaultLogger: Logger = (type, message, level = \"info\") => {\n const logMessage = `[${type}] ${message}`;\n switch (level) {\n case \"info\":\n console.log(logMessage);\n break;\n case \"warning\":\n console.warn(logMessage);\n break;\n case \"error\":\n console.error(logMessage);\n break;\n case \"trace\":\n console.trace(logMessage);\n break;\n }\n};\n\nasync function createDbWorker(config: WorkerConfig, opts: WorkerOptions) {\n const broadcastChannels = createBroadcastChannels();\n const logger = opts.logger ?? defaultLogger;\n\n const sqlite3 = await sqlite3InitModule();\n\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: \"sync-db-storage\",\n clearOnInit: config.clearOnInit,\n });\n\n const db = new SQLiteDbWrapper<WorkerDbSchema>({\n db: new pool.OpfsSAHPoolDb(config.dbPath),\n logger: logger,\n loggerPrefix: \"worker\",\n sqlite3,\n });\n\n db.execute(\"PRAGMA locking_mode=exclusive\");\n db.execute(\"PRAGMA journal_mode=WAL\");\n db.execute(`ATTACH DATABASE '${config.dbPath}-worker' as worker`);\n applyWorkerDbSchema(db);\n const kysely = createSQLiteKysely<WorkerDbSchema>(db);\n const migrator = createSyncDbMigrator({\n db: kysely as Kysely<unknown>,\n migrations: opts.migrations,\n });\n await migrator.migrateToLatest();\n db.invalidateDbSchema();\n\n const localSyncId = createSyncIdCounter({\n initialSyncId: getLatestSyncId(db),\n });\n\n const crdtStorage = createCrdtStorage({\n syncId: localSyncId,\n applyCrdtEventMutations: (event) =>\n applyCrdtEventMutations({\n db,\n event,\n updateLogTableName: \"crdt_update_log\",\n }),\n persistEvents: (events) => persistEvents(db, events),\n popPendingEventsBatch: () => popPendingEventsBatch(db, 50),\n updateEventStatus: (syncId, status) =>\n updateEventStatus(db, syncId, status),\n });\n\n createCrdtSyncProducer({\n bufferSize: 100,\n storage: crdtStorage,\n broadcastEvents: (chunk) => {\n broadcastChannels.responses.postMessage({\n notificationType: \"new-event-chunk-applied\",\n newSyncId: chunk.newSyncId,\n });\n },\n });\n\n if (opts.createRemoteSource) {\n let crdtSyncRemoteSource: CrdtSyncRemoteSource | null = null;\n const remoteSource = opts.createRemoteSource?.({\n onEventsAvailable: () => {\n crdtSyncRemoteSource?.pullEvents();\n },\n });\n\n const storedRemoteSyncId = Number.parseInt(\n getMetaValue(db, \"remote-sync-id\")\n );\n const remoteSyncId = createSyncIdCounter({\n initialSyncId: Number.isNaN(storedRemoteSyncId) ? -1 : storedRemoteSyncId,\n saveToStorage: (syncId) =>\n setMetaValue(db, \"remote-sync-id\", syncId.toString()),\n });\n crdtSyncRemoteSource = createCrdtSyncRemoteSource({\n bufferSize: 50,\n syncId: remoteSyncId,\n nodeId: config.clientId,\n storage: crdtStorage,\n pullEvents: remoteSource.pullEvents,\n pushEvents: remoteSource.pushEvents,\n });\n await crdtSyncRemoteSource.pullEvents({ includeSelf: true });\n }\n\n const rpcTarget: WorkerRpc = {\n execute: (query) => db.execute(query),\n getSnapshot: () => {\n db.execute(\"PRAGMA journal_mode=off\");\n const file = db.createSnapshot();\n db.execute(\"PRAGMA journal_mode=WAL\");\n return {\n file,\n syncId: localSyncId.current,\n };\n },\n postInitReady: () => {\n broadcastChannels.responses.postMessage({\n type: \"init-ready\",\n });\n },\n pushTabEvents: (request) => {\n crdtStorage.enqueueEvents(\n request.events.map((event) => ({\n ...event,\n origin: request.nodeId,\n }))\n );\n return {\n ok: true,\n };\n },\n pullEvents: (request) => {\n const events = db.executeKysely((db) => {\n const query = db\n .selectFrom(\"worker.crdt_events\")\n .where(\"sync_id\", \">\", request.afterSyncId)\n .where(\"status\", \"=\", \"applied\")\n .orderBy(\"sync_id\", \"asc\")\n .selectAll();\n if (request.excludeNodeId) {\n query.where(\"origin\", \"!=\", request.excludeNodeId);\n }\n return query;\n }).rows;\n\n return {\n events,\n hasMore: false,\n newSyncId: events[events.length - 1]?.sync_id ?? request.afterSyncId,\n };\n },\n };\n\n broadcastChannels.requests.onmessage = (event) => {\n const message = event.data;\n\n if (!isWorkerRequestMessage(message)) {\n return;\n }\n\n const method = rpcTarget[message.method] as () => ReturnType<\n WorkerRpc[keyof WorkerRpc]\n >;\n const data = method.apply(null, message.args as []);\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data,\n };\n broadcastChannels.responses.postMessage(response);\n };\n\n rpcTarget.postInitReady();\n}\n\nasync function getConfig(): Promise<WorkerConfig> {\n let configSet = false;\n const responsePromise = createDeferredPromise<WorkerConfig>();\n\n self.onmessage = (event: MessageEvent<unknown>) => {\n if (configSet) {\n console.error(\"Worker config already set\");\n return;\n }\n\n const message = event.data;\n if (!isWorkerInitMessage(message)) {\n return;\n }\n\n responsePromise.resolve(message.config);\n configSet = true;\n };\n\n return responsePromise.promise;\n}\n\ntype WorkerOptions = {\n migrations: Record<number, Migration>;\n logger?: Logger;\n createRemoteSource?: CreateRemoteSourceFactory;\n};\n\ntype CreateRemoteSourceFactory = (opts: {\n onEventsAvailable: (newSyncId: number) => void;\n}) => {\n pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;\n pushEvents: (request: EventsPushRequest) => Promise<EventsPushResponse>;\n};\n\nexport async function startDbWorker(opts: WorkerOptions) {\n const config = await getConfig();\n\n await navigator.locks.request(\n syncDbWorkerLockName,\n { mode: \"exclusive\" },\n async (lock) => {\n if (!lock) {\n return;\n }\n\n await createDbWorker(config, opts);\n\n await new Promise<void>(() => {});\n }\n );\n\n console.error(\"Failed to acquire lock\");\n}\n\nfunction getLatestSyncId(db: SQLiteDbWrapper<WorkerDbSchema>) {\n const result = db.executePrepared(\"get-latest-sync-id\", {}, (db) =>\n db\n .selectFrom(\"worker.crdt_events\")\n .select((eb) => eb.fn.max(\"sync_id\").as(\"sync_id\"))\n );\n return result[0]?.sync_id ?? 0;\n}\n\nfunction persistEvents(\n db: SQLiteDbWrapper<WorkerDbSchema>,\n events: PersistedCrdtEvent[]\n) {\n db.executeTransaction((db) => {\n const chunkSize = 100;\n for (let i = 0; i < events.length; i += chunkSize) {\n const chunk = events.slice(i, i + chunkSize);\n db.executeKysely((db) =>\n db.insertInto(\"worker.crdt_events\").values(chunk)\n );\n }\n });\n}\n\nfunction popPendingEventsBatch(\n db: SQLiteDbWrapper<WorkerDbSchema>,\n limit: number\n) {\n const events = db.executePrepared(\n \"pop-enqueued-crdt-events\",\n {\n limit: limit,\n },\n (db, param) =>\n db\n .selectFrom(\"worker.crdt_events\")\n .where(\"status\", \"=\", sql.lit(\"pending\"))\n .limit(param(\"limit\"))\n .orderBy(\"sync_id\", \"asc\")\n .selectAll()\n );\n return {\n events,\n hasMore: events.length === limit,\n };\n}\n\nfunction updateEventStatus(\n db: SQLiteDbWrapper<WorkerDbSchema>,\n syncId: number,\n status: CrdtEventStatus\n) {\n db.executePrepared(\n \"update-crdt-event-status\",\n { syncId, status },\n (db, params) =>\n db\n .updateTable(\"worker.crdt_events\")\n .set({ status: params(\"status\") })\n .where(\"sync_id\", \"=\", params(\"syncId\"))\n );\n}\n\nfunction getMetaValue(db: SQLiteDbWrapper<WorkerDbSchema>, key: string) {\n const [result] = db.executePrepared(\"get-meta-value\", { key }, (db, params) =>\n db\n .selectFrom(\"worker.meta\")\n .where(\"key\", \"=\", params(\"key\"))\n .select(\"value\")\n );\n return result?.value ?? null;\n}\n\nfunction setMetaValue(\n db: SQLiteDbWrapper<WorkerDbSchema>,\n key: string,\n value: string\n) {\n db.executePrepared(\"set-meta-value\", { key, value }, (db, params) =>\n db\n .insertInto(\"worker.meta\")\n .values({ key: params(\"key\"), value: params(\"value\") })\n .onConflict((oc) => oc.doUpdateSet({ value: params(\"value\") }))\n );\n}\n\n"],"mappings":"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","names":["db"]}
|
|
1
|
+
{"version":3,"sources":["../src/web-socket/ws-remote-source.ts","../src/worker-db/db-worker.ts"],"sourcesContent":["import type { SyncServerMessage, SyncServerRequest } from \"../server\";\nimport type { CreateRemoteSourceFactory } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport { createDeferredPromise, type DeferredPromise, jsonSafeParse } from \"../utils\";\nimport type { EventsPullRequest, EventsPushRequest, EventsPushResponse, GetEventsBatch } from \"../worker\";\n\ntype WsRemoteSourceConfig = {\n createWebSocket: () => Pick<WebSocket, \"send\" | \"onmessage\" | \"close\" | \"addEventListener\">;\n};\n\nexport const createWsRemoteSource = ({ createWebSocket }: WsRemoteSourceConfig): CreateRemoteSourceFactory => {\n return async ({ onEventsAvailable }) => {\n const socket = createWebSocket();\n\n const openPromise = createDeferredPromise<void>({\n timeout: 5000,\n onTimeout: () => {\n socket.close();\n },\n });\n socket.addEventListener(\"open\", () => {\n openPromise.resolve(undefined);\n });\n await openPromise.promise;\n\n const requestsMap = new Map<string, DeferredPromise<unknown>>();\n\n const pushEvents = async (request: EventsPushRequest): Promise<EventsPushResponse> => {\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<EventsPushResponse>({ timeout: 5000 });\n requestsMap.set(requestId, promise as DeferredPromise<unknown>);\n\n const wsRequest: SyncServerRequest = {\n type: \"push-events\",\n requestId,\n nodeId: request.nodeId,\n events: request.events,\n };\n socket.send(JSON.stringify(wsRequest));\n\n return promise.promise;\n };\n\n const pullEvents = async (request: EventsPullRequest): Promise<GetEventsBatch> => {\n const requestId = crypto.randomUUID();\n const promise = createDeferredPromise<GetEventsBatch>({ timeout: 2000 });\n requestsMap.set(requestId, promise as DeferredPromise<unknown>);\n\n const wsRequest: SyncServerRequest = {\n type: \"pull-events\",\n requestId,\n afterSyncId: request.afterSyncId,\n excludeNodeId: request.excludeNodeId,\n };\n socket.send(JSON.stringify(wsRequest));\n\n return promise.promise;\n };\n\n socket.onmessage = (event) => {\n const result = jsonSafeParse<SyncServerMessage>(event.data);\n\n if (!result.success || !(\"type\" in result.data) || !result.data.type) {\n return;\n }\n\n const message = result.data;\n\n switch (message.type) {\n case \"events-pull-response\": {\n const promise = requestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n promise.resolve(message.data);\n requestsMap.delete(message.requestId);\n break;\n }\n case \"events-push-response\": {\n const promise = requestsMap.get(message.requestId);\n if (!promise) {\n return;\n }\n promise.resolve(message.data);\n requestsMap.delete(message.requestId);\n break;\n }\n case \"events-applied\":\n onEventsAvailable(message.newSyncId);\n break;\n default:\n message satisfies never;\n return;\n }\n };\n\n return {\n pushEvents,\n pullEvents,\n disconnect: () => {\n socket.close();\n },\n };\n };\n};\n","import sqlite3InitModule from \"@sqlite.org/sqlite-wasm\";\nimport type { Logger } from \"../logger\";\nimport { createMigrator, type SyncDbMigrator } from \"../migrations/migrator\";\nimport { applyWorkerDbSchema, type WorkerDbSchema } from \"../migrations/system-schema\";\nimport { createSQLiteCrdtApplyFunction } from \"../sqlite-crdt/apply-crdt-event\";\nimport type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport {\n type CrdtStorage,\n createCrdtStorage,\n type EventUpdate,\n type GetEventsOptions,\n} from \"../sqlite-crdt/crdt-storage\";\nimport { createCrdtSyncProducer } from \"../sqlite-crdt/crdt-sync-producer\";\nimport { type CreateRemoteSourceFactory, createCrdtSyncRemoteSource } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { PersistedCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport { applyKyselyEventsBatchFilters } from \"../sqlite-crdt/events-batch-filters\";\nimport { createStoredValue } from \"../sqlite-crdt/stored-value\";\nimport { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { createSQLiteKvStore, type KvStore } from \"../sqlite-kv-store\";\nimport { createDeferredPromise } from \"../utils\";\nimport {\n createBroadcastChannels,\n isWorkerInitMessage,\n isWorkerRequestMessage,\n syncDbClientLockName,\n syncDbWorkerLockName,\n type WorkerConfig,\n type WorkerErrorResponseMessage,\n type WorkerResponseMessage,\n type WorkerRpc,\n} from \"./worker-common\";\n\nconst defaultLogger: Logger = (type, message, level = \"info\") => {\n const logMessage = `[${type}] ${message}`;\n switch (level) {\n case \"info\":\n console.log(logMessage);\n break;\n case \"warning\":\n console.warn(logMessage);\n break;\n case \"error\":\n console.error(logMessage);\n break;\n case \"trace\":\n console.trace(logMessage);\n break;\n }\n};\n\nasync function createDbWorker(config: WorkerConfig, opts: WorkerOptions) {\n const broadcastChannels = createBroadcastChannels(config.dbId);\n const logger = opts.logger ?? defaultLogger;\n\n const sqlite3 = await sqlite3InitModule();\n\n const pool = await sqlite3.installOpfsSAHPoolVfs({\n name: config.dbId,\n directory: `.${config.dbId}`,\n clearOnInit: config.clearOnInit,\n initialCapacity: 8,\n });\n\n const db = new SQLiteDbWrapper<WorkerDbSchema>({\n db: () => new pool.OpfsSAHPoolDb(`/${config.dbId}-main.db`),\n logger: logger,\n loggerPrefix: \"worker\",\n sqlite3,\n });\n\n db.execute(\"PRAGMA locking_mode=exclusive\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA journal_mode=WAL\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA synchronous=NORMAL\", { loggerLevel: \"system\" });\n\n db.execute(`ATTACH DATABASE '/${config.dbId}-worker.db' as worker`, { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.locking_mode=exclusive\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.journal_mode=WAL\", { loggerLevel: \"system\" });\n db.execute(\"PRAGMA worker.synchronous=NORMAL\", { loggerLevel: \"system\" });\n\n applyWorkerDbSchema(db);\n\n const kvStore = createSQLiteKvStore({\n db,\n metaTableName: \"worker.kv\",\n });\n\n const migrator = createMigrator({\n migrations: opts.syncDbSchema.migrations,\n schemaVersion: kvStore.createNumberStoredValue(\"schema-version\", -1),\n updateLogTableName: '\"crdt_update_log\"',\n });\n migrator.migrateDbToLatest({\n startTransaction: (callback) => {\n db.executeTransaction((tx) => callback({ execute: (sql, parameters) => tx.execute({ sql, parameters }) }));\n },\n });\n db.invalidateDbSchema();\n\n const localSyncId = createStoredValue({\n initialValue: getLatestSyncId(db),\n });\n\n const crdtStorage = createCrdtStorage({\n nodeId: config.clientId,\n syncId: localSyncId,\n migrator,\n hlc: {\n getNextHLC() {\n throw new Error(\"Worker DB should not call getNextHLC\");\n },\n mergeHLC() {\n return;\n },\n },\n transaction: (callback) => db.executeTransaction(callback),\n handleCrdtEventApply: createSQLiteCrdtApplyFunction({\n db,\n updateLogTableName: \"crdt_update_log\",\n }),\n persistEvent: (event) => persistEvent(db, event),\n getEventsBatch: (opts) => getEventsBatch(db, opts),\n updateEvent: (syncId, update) => updateEvent(db, syncId, update),\n });\n\n createCrdtSyncProducer({\n storage: crdtStorage,\n broadcastEvents: (chunk) => {\n broadcastChannels.responses.postMessage({\n notificationType: \"new-event-chunk-applied\",\n newSyncId: chunk.newSyncId,\n });\n },\n });\n\n const postState = () => {\n broadcastChannels.responses.postMessage({\n notificationType: \"state-changed\",\n state: {\n remoteState: remoteSource.getState(),\n },\n });\n };\n\n const remoteSource = createRemoteSource({\n kvStore,\n crdtStorage,\n migrator,\n clientId: config.clientId,\n remoteFactory: opts.createRemoteSource,\n });\n remoteSource.goOnline();\n\n remoteSource.addEventListener(\"state-changed\", () => {\n postState();\n });\n\n const rpcTarget: WorkerRpc = {\n execute: (query) => db.execute(query),\n getSnapshot: () => {\n db.execute(\"PRAGMA journal_mode=off\", { loggerLevel: \"system\" });\n const file = db.createSnapshot();\n db.execute(\"PRAGMA journal_mode=WAL\", { loggerLevel: \"system\" });\n return {\n file,\n syncId: localSyncId.current,\n schemaVersion: migrator.currentSchemaVersion,\n };\n },\n postState,\n pushTabEvents: (request) => {\n crdtStorage.enqueueLocalEvents(request.events, request.nodeId);\n return {\n ok: true,\n };\n },\n pullEvents: (request) => {\n return crdtStorage.getEventsBatch({\n afterSyncId: request.afterSyncId,\n status: \"applied\",\n excludeNodeId: request.excludeNodeId,\n limit: 100,\n });\n },\n goOnline: () => remoteSource.goOnline(),\n goOffline: () => remoteSource.goOffline(\"DISCONNECTED\"),\n };\n\n broadcastChannels.requests.onmessage = (event) => {\n const message = event.data;\n\n if (!isWorkerRequestMessage(message)) {\n return;\n }\n\n const sendError = (error: unknown) => {\n const response: WorkerErrorResponseMessage = {\n type: \"error-response\",\n requestId: message.requestId,\n error: error instanceof Error ? error.message : String(error),\n };\n broadcastChannels.responses.postMessage(response);\n };\n\n try {\n const method = rpcTarget[message.method] as () => ReturnType<WorkerRpc[keyof WorkerRpc]>;\n const data = method.apply(null, message.args as []);\n\n if (data instanceof Promise) {\n data\n .then((result) => {\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data: result,\n };\n broadcastChannels.responses.postMessage(response);\n })\n .catch(sendError);\n } else {\n const response: WorkerResponseMessage = {\n type: \"response\",\n requestId: message.requestId,\n data,\n };\n broadcastChannels.responses.postMessage(response);\n }\n } catch (error) {\n sendError(error);\n }\n };\n\n rpcTarget.postState();\n\n return async () => {\n await remoteSource.dispose();\n broadcastChannels.requests.close();\n broadcastChannels.responses.close();\n db.close();\n };\n}\n\ntype InitRemoteOptions = {\n kvStore: KvStore;\n clientId: string;\n crdtStorage: CrdtStorage;\n migrator: SyncDbMigrator;\n remoteFactory?: CreateRemoteSourceFactory;\n};\n\nfunction createRemoteSource({ kvStore, clientId, crdtStorage, migrator, remoteFactory }: InitRemoteOptions) {\n return createCrdtSyncRemoteSource({\n bufferSize: 50,\n pullSyncId: kvStore.createNumberStoredValue(\"pull-sync-id\", -1),\n pushSyncId: kvStore.createNumberStoredValue(\"push-sync-id\", -1),\n nodeId: clientId,\n storage: crdtStorage,\n migrator,\n remoteFactory,\n });\n}\n\nexport async function getWorkerConfig<Props = never>(): Promise<WorkerConfig<Props>> {\n let configSet = false;\n const responsePromise = createDeferredPromise<WorkerConfig>();\n\n self.onmessage = (event: MessageEvent<unknown>) => {\n if (configSet) {\n console.error(\"Worker config already set\");\n return;\n }\n\n const message = event.data;\n if (!isWorkerInitMessage(message)) {\n return;\n }\n\n responsePromise.resolve(message.config);\n configSet = true;\n };\n\n return responsePromise.promise;\n}\n\ntype WorkerOptions = {\n syncDbSchema: SyncDbSchema;\n logger?: Logger;\n createRemoteSource?: CreateRemoteSourceFactory;\n workerConfig?: WorkerConfig;\n};\n\nexport async function startDbWorker(opts: WorkerOptions) {\n const config = opts.workerConfig ?? (await getWorkerConfig());\n\n await navigator.locks.request(`${syncDbWorkerLockName}-${config.dbId}`, { mode: \"exclusive\" }, async (lock) => {\n if (!lock) {\n return;\n }\n\n const cleanup = await createDbWorker(config, opts);\n\n const clientLockName = `${syncDbClientLockName}-${config.dbId}`;\n await new Promise<void>((resolve) => {\n const interval = setInterval(async () => {\n const { held } = await navigator.locks.query();\n const hasClients = held?.some((l) => l.name === clientLockName && l.mode === \"shared\");\n if (!hasClients) {\n clearInterval(interval);\n resolve();\n }\n }, 5_000);\n });\n\n await cleanup();\n });\n\n self.close();\n}\n\nfunction getLatestSyncId(db: SQLiteDbWrapper<WorkerDbSchema>) {\n const result = db.executePrepared(\n \"get-latest-sync-id\",\n {},\n (db) => db.selectFrom(\"worker.crdt_events\").select((eb) => eb.fn.max(\"sync_id\").as(\"sync_id\")),\n { loggerLevel: \"system\" },\n );\n return result[0]?.sync_id ?? 0;\n}\n\nfunction persistEvent(db: SQLiteDbWrapper<WorkerDbSchema>, event: PersistedCrdtEvent) {\n db.executePrepared(\n \"persist-crdt-event\",\n event,\n (db, params) =>\n db.insertInto(\"worker.crdt_events\").values({\n type: params(\"type\"),\n dataset: params(\"dataset\"),\n item_id: params(\"item_id\"),\n payload: params(\"payload\"),\n schema_version: params(\"schema_version\"),\n sync_id: params(\"sync_id\"),\n status: params(\"status\"),\n timestamp: params(\"timestamp\"),\n origin: params(\"origin\"),\n source_node_id: params(\"source_node_id\"),\n }),\n { loggerLevel: \"system\" },\n );\n}\n\nfunction getEventsBatch(db: SQLiteDbWrapper<WorkerDbSchema>, opts: GetEventsOptions) {\n return db.executeKysely(\n (db) => applyKyselyEventsBatchFilters(db.selectFrom(\"worker.crdt_events\").selectAll(), opts),\n { loggerLevel: \"system\" },\n ).rows;\n}\n\nfunction updateEvent(db: SQLiteDbWrapper<WorkerDbSchema>, syncId: number, update: EventUpdate) {\n db.executePrepared(\n \"update-crdt-event\",\n { syncId, ...update },\n (db, params) =>\n db\n .updateTable(\"worker.crdt_events\")\n .set({\n status: params(\"status\"),\n schema_version: params(\"schema_version\"),\n type: params(\"type\"),\n dataset: params(\"dataset\"),\n item_id: params(\"item_id\"),\n payload: params(\"payload\"),\n })\n .where(\"sync_id\", \"=\", params(\"syncId\")),\n { loggerLevel: \"system\" },\n );\n}\n"],"mappings":"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","names":["opts","db"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlite-sync/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "SQLite synchronization library with CRDT support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/krolebord/sqlite-sync
|
|
9
|
+
"url": "https://github.com/krolebord-dev/sqlite-sync",
|
|
10
10
|
"directory": "packages/core"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@sqlite.org/sqlite-wasm": "3.51.
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"zod": "^4.
|
|
40
|
+
"@sqlite.org/sqlite-wasm": "^3.51.2-build5",
|
|
41
|
+
"kysely": "^0.28.10",
|
|
42
|
+
"retry-as-promised": "^7.1.1",
|
|
43
|
+
"zod": "^4.3.6"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^22.10.2",
|