@sqlite-sync/core 0.0.1 → 0.0.2

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/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
- createSQLiteKysely,
7
- createSyncDbMigrator,
9
+ createMigrator,
10
+ createSQLiteCrdtApplyFunction,
11
+ createSQLiteKvStore,
12
+ createStoredValue,
8
13
  isWorkerInitMessage,
9
14
  isWorkerRequestMessage,
15
+ syncDbClientLockName,
10
16
  syncDbWorkerLockName
11
- } from "./chunk-LK5FJCUD.js";
17
+ } from "./chunk-627DSM2Q.js";
12
18
  import {
13
- applyCrdtEventMutations,
14
- createCrdtStorage,
15
- createCrdtSyncProducer,
16
19
  createDeferredPromise,
17
- createSyncIdCounter
18
- } from "./chunk-YLXMST5Z.js";
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: "sync-db-storage",
46
- clearOnInit: config.clearOnInit
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.dbPath),
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(`ATTACH DATABASE '${config.dbPath}-worker' as worker`);
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 kysely = createSQLiteKysely(db);
59
- const migrator = createSyncDbMigrator({
60
- db: kysely,
61
- migrations: opts.migrations
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 = createSyncIdCounter({
66
- initialSyncId: getLatestSyncId(db)
165
+ const localSyncId = createStoredValue({
166
+ initialValue: getLatestSyncId(db)
67
167
  });
68
168
  const crdtStorage = createCrdtStorage({
169
+ nodeId: config.clientId,
69
170
  syncId: localSyncId,
70
- applyCrdtEventMutations: (event) => applyCrdtEventMutations({
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
- persistEvents: (events) => persistEvents(db, events),
76
- popPendingEventsBatch: () => popPendingEventsBatch(db, 50),
77
- updateEventStatus: (syncId, status) => updateEventStatus(db, syncId, status)
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
- if (opts.createRemoteSource) {
90
- let crdtSyncRemoteSource = null;
91
- const remoteSource = opts.createRemoteSource?.({
92
- onEventsAvailable: () => {
93
- crdtSyncRemoteSource?.pullEvents();
198
+ const postState = () => {
199
+ broadcastChannels.responses.postMessage({
200
+ notificationType: "state-changed",
201
+ state: {
202
+ remoteState: remoteSource.getState()
94
203
  }
95
204
  });
96
- const storedRemoteSyncId = Number.parseInt(
97
- getMetaValue(db, "remote-sync-id")
98
- );
99
- const remoteSyncId = createSyncIdCounter({
100
- initialSyncId: Number.isNaN(storedRemoteSyncId) ? -1 : storedRemoteSyncId,
101
- saveToStorage: (syncId) => setMetaValue(db, "remote-sync-id", syncId.toString())
102
- });
103
- crdtSyncRemoteSource = createCrdtSyncRemoteSource({
104
- bufferSize: 50,
105
- syncId: remoteSyncId,
106
- nodeId: config.clientId,
107
- storage: crdtStorage,
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
- postInitReady: () => {
125
- broadcastChannels.responses.postMessage({
126
- type: "init-ready"
127
- });
128
- },
229
+ postState,
129
230
  pushTabEvents: (request) => {
130
- crdtStorage.enqueueEvents(
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
- const events = db.executeKysely((db2) => {
142
- const query = db2.selectFrom("worker.crdt_events").where("sync_id", ">", request.afterSyncId).where("status", "=", "applied").orderBy("sync_id", "asc").selectAll();
143
- if (request.excludeNodeId) {
144
- query.where("origin", "!=", request.excludeNodeId);
145
- }
146
- return query;
147
- }).rows;
148
- return {
149
- events,
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 method = rpcTarget[message.method];
161
- const data = method.apply(null, message.args);
162
- const response = {
163
- type: "response",
164
- requestId: message.requestId,
165
- data
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
- broadcastChannels.responses.postMessage(response);
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
- async function getConfig() {
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 getConfig();
190
- await navigator.locks.request(
191
- syncDbWorkerLockName,
192
- { mode: "exclusive" },
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
- console.error("Failed to acquire lock");
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 persistEvents(db, events) {
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
- "update-crdt-event-status",
239
- { syncId, status },
240
- (db2, params) => db2.updateTable("worker.crdt_events").set({ status: params("status") }).where("sync_id", "=", params("syncId"))
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 getMetaValue(db, key) {
244
- const [result] = db.executePrepared(
245
- "get-meta-value",
246
- { key },
247
- (db2, params) => db2.selectFrom("worker.meta").where("key", "=", params("key")).select("value")
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 setMetaValue(db, key, value) {
376
+ function updateEvent(db, syncId, update) {
252
377
  db.executePrepared(
253
- "set-meta-value",
254
- { key, value },
255
- (db2, params) => db2.insertInto("worker.meta").values({ key: params("key"), value: params("value") }).onConflict((oc) => oc.doUpdateSet({ value: params("value") }))
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
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlite-sync/core",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "SQLite synchronization library with CRDT support",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,14 +21,20 @@
21
21
  "types": "./dist/index.d.ts",
22
22
  "exports": {
23
23
  ".": {
24
+ "@sqlite-sync/source": "./src/index.ts",
25
+ "workerd": "./src/index.ts",
24
26
  "types": "./dist/index.d.ts",
25
27
  "import": "./dist/index.js"
26
28
  },
27
29
  "./worker": {
30
+ "@sqlite-sync/source": "./src/worker.ts",
31
+ "workerd": "./src/worker.ts",
28
32
  "types": "./dist/worker.d.ts",
29
33
  "import": "./dist/worker.js"
30
34
  },
31
35
  "./server": {
36
+ "@sqlite-sync/source": "./src/server/index.ts",
37
+ "workerd": "./src/server/index.ts",
32
38
  "types": "./dist/server.d.ts",
33
39
  "import": "./dist/server.js"
34
40
  }
@@ -37,10 +43,10 @@
37
43
  "dist"
38
44
  ],
39
45
  "dependencies": {
40
- "@sqlite.org/sqlite-wasm": "3.51.1-build2",
41
- "broadcast-channel": "^7.2.0",
42
- "kysely": "^0.28.8",
43
- "zod": "^4.1.13"
46
+ "@sqlite.org/sqlite-wasm": "^3.51.2-build5",
47
+ "kysely": "^0.28.10",
48
+ "retry-as-promised": "^7.1.1",
49
+ "zod": "^4.3.6"
44
50
  },
45
51
  "devDependencies": {
46
52
  "@types/node": "^22.10.2",