@sqlite-sync/core 0.12.2 → 0.13.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.
Files changed (44) hide show
  1. package/dist/{reset-state-t0tTGqoh.js → crdt-sync-producer-BY-WobgO.js} +55 -241
  2. package/dist/crdt-sync-producer-BY-WobgO.js.map +1 -0
  3. package/dist/{crdt-sync-remote-source-CPXzmSIS.js → crdt-sync-remote-source-yeTEkOjH.js} +344 -12
  4. package/dist/crdt-sync-remote-source-yeTEkOjH.js.map +1 -0
  5. package/dist/{crdt-table-schema-BRm_WdcW.js → crdt-table-schema-D6Yf61Ex.js} +37 -13
  6. package/dist/crdt-table-schema-D6Yf61Ex.js.map +1 -0
  7. package/dist/{index-BHsKLqcK.d.ts → index-CH4lf3iX.d.ts} +166 -72
  8. package/dist/index.common-C8W9VKSY.js +276 -0
  9. package/dist/index.common-C8W9VKSY.js.map +1 -0
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +20 -677
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.native.d.ts +2 -0
  14. package/dist/index.native.js +9 -0
  15. package/dist/internal.d.ts +1 -1
  16. package/dist/internal.js +2 -2
  17. package/dist/local.d.ts +48 -0
  18. package/dist/local.js +255 -0
  19. package/dist/local.js.map +1 -0
  20. package/dist/{make-crdt-table-C6l0F-o_.js → make-crdt-table-DYCXOYB7.js} +13 -14
  21. package/dist/make-crdt-table-DYCXOYB7.js.map +1 -0
  22. package/dist/reset-state-CPOi_45B.js +220 -0
  23. package/dist/reset-state-CPOi_45B.js.map +1 -0
  24. package/dist/server.d.ts +8 -3
  25. package/dist/server.js +2 -2
  26. package/dist/server.js.map +1 -1
  27. package/dist/sqlite-reactive-db-DLS1xRZp.js +296 -0
  28. package/dist/sqlite-reactive-db-DLS1xRZp.js.map +1 -0
  29. package/dist/synced-db-database-B5M7MrBD.js +81 -0
  30. package/dist/synced-db-database-B5M7MrBD.js.map +1 -0
  31. package/dist/{system-schema-Dvl_BLId.js → system-schema-CXf3FmxN.js} +142 -58
  32. package/dist/system-schema-CXf3FmxN.js.map +1 -0
  33. package/dist/worker.d.ts +2 -10
  34. package/dist/worker.js +11 -177
  35. package/dist/worker.js.map +1 -1
  36. package/dist/ws-remote-source-BbPWrHB6.js +161 -0
  37. package/dist/ws-remote-source-BbPWrHB6.js.map +1 -0
  38. package/dist/ws-remote-source-DFBhDc6Z.d.ts +12 -0
  39. package/package.json +9 -1
  40. package/dist/crdt-sync-remote-source-CPXzmSIS.js.map +0 -1
  41. package/dist/crdt-table-schema-BRm_WdcW.js.map +0 -1
  42. package/dist/make-crdt-table-C6l0F-o_.js.map +0 -1
  43. package/dist/reset-state-t0tTGqoh.js.map +0 -1
  44. package/dist/system-schema-Dvl_BLId.js.map +0 -1
@@ -1,10 +1,7 @@
1
- import { d as SQLiteDbWrapper, h as dummyKysely, m as introspectDb, u as createStoredValue } from "./system-schema-Dvl_BLId.js";
2
- import { a as acquireLock, c as ensureSingletonExecution, l as generateId, n as isNoOpCrdtEventPayload, p as quoteId, r as TypedBroadcastChannel, s as createTypedEventTarget, t as CRDT_EVENT_NO_OP_PAYLOAD } from "./crdt-table-schema-BRm_WdcW.js";
3
- import { c as serializeHLC, n as CrdtEventValidationError, r as validateNewCrdtEvent, s as deserializeHLC } from "./crdt-sync-remote-source-CPXzmSIS.js";
1
+ import { b as dummyKysely, h as SQLiteDbWrapper, m as createStoredValue, y as introspectDb } from "./system-schema-CXf3FmxN.js";
2
+ import { c as ensureSingletonExecution, n as isNoOpCrdtEventPayload, p as quoteId, s as createTypedEventTarget, t as CRDT_EVENT_NO_OP_PAYLOAD } from "./crdt-table-schema-D6Yf61Ex.js";
3
+ import { c as deserializeHLC, i as validateNewCrdtEvent, l as serializeHLC, n as createStateHashTracker, r as CrdtEventValidationError } from "./crdt-sync-remote-source-yeTEkOjH.js";
4
4
  import { sql } from "kysely";
5
- import initXxhash from "xxhash-wasm";
6
- import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
7
- import { createStore, del, get, set } from "idb-keyval";
8
5
  //#region src/export-import.ts
9
6
  function createTableNameNormalizer(tablesConfig) {
10
7
  const tablesByAlias = /* @__PURE__ */ new Map();
@@ -107,24 +104,6 @@ function createExportData({ reactiveDb, tablesConfig, schemaVersion }) {
107
104
  };
108
105
  }
109
106
  //#endregion
110
- //#region src/hash.ts
111
- let loadPromise = null;
112
- let api = null;
113
- function ensureLoaded() {
114
- if (!loadPromise) loadPromise = initXxhash().then((hasher) => {
115
- api = hasher;
116
- });
117
- return loadPromise;
118
- }
119
- function h64(input, seed = 0n) {
120
- if (!api) throw new Error("xxhash is not initialized; call xxhash.ensureLoaded() first");
121
- return api.h64(input, seed);
122
- }
123
- const xxhash = {
124
- ensureLoaded,
125
- h64
126
- };
127
- //#endregion
128
107
  //#region src/migrations/migrator.ts
129
108
  const protectedColumns = ["id", "tombstone"];
130
109
  function assertColumnNotProtected(column, operation) {
@@ -176,11 +155,6 @@ const migrationSteps = {
176
155
  },
177
156
  addColumn: ({ table, column, type, defaultValue, build = (e) => e }) => ({
178
157
  sql: (db) => db.schema.alterTable(table).addColumn(column, type, (x) => build(x).defaultTo(defaultValue)),
179
- eventTransformer: { [table]: (event) => {
180
- if (event.type !== "item-created") return event;
181
- event.payload[column] = defaultValue;
182
- return event;
183
- } },
184
158
  updateLogMutations: [{
185
159
  type: "add-column",
186
160
  table,
@@ -384,25 +358,21 @@ const acceptedDataTypes = {
384
358
  ],
385
359
  enum: ["TEXT"]
386
360
  };
387
- let sqliteModule = null;
388
361
  /**
389
362
  * Verify that replaying the full migration history on a fresh database produces
390
- * exactly the declared table schema. Creates a throwaway in-memory SQLite db,
391
- * applies all migrations, introspects the result, and diffs it against the
392
- * `t.table()` declarations.
363
+ * exactly the declared table schema. Applies all migrations to `driver`, which
364
+ * must be an empty throwaway database (it is closed afterwards), introspects the
365
+ * result, and diffs it against the `t.table()` declarations.
393
366
  *
394
367
  * Returns an empty array when migrations and schema agree. Intended for dev-time
395
368
  * checks (`startDbWorker({ verifySchema: true })`) and tests:
396
369
  * `expect(await verifySyncSchema(syncDbSchema)).toEqual([])`.
397
370
  */
398
- async function verifySyncSchema(schema) {
399
- if (!sqliteModule) sqliteModule = await sqlite3InitModule();
400
- const sqlite3 = sqliteModule;
371
+ function verifySyncSchemaWithDriver(schema, driver) {
401
372
  const db = new SQLiteDbWrapper({
402
- db: () => new sqlite3.oo1.DB({ filename: ":memory:" }),
373
+ driver,
403
374
  logger: () => {},
404
- loggerPrefix: "schema-verify",
405
- sqlite3
375
+ loggerPrefix: "schema-verify"
406
376
  });
407
377
  try {
408
378
  const migrator = createMigrator({
@@ -538,8 +508,9 @@ function formatExpected(meta) {
538
508
  }
539
509
  //#endregion
540
510
  //#region src/sqlite-crdt/apply-crdt-event.ts
541
- const createSQLiteCrdtApplyFunction = ({ db, dbConfig }) => {
511
+ const createSQLiteCrdtApplyFunction = ({ db, dbConfig, getStateHash }) => {
542
512
  return createCrdtApplyFunction({
513
+ getStateHash,
543
514
  getCrdtUpdateLog(opts) {
544
515
  const [metaRow] = db.executePrepared("get-item-crdt-meta", {
545
516
  item_id: opts.itemId,
@@ -583,7 +554,7 @@ const createSQLiteCrdtApplyFunction = ({ db, dbConfig }) => {
583
554
  }
584
555
  });
585
556
  };
586
- function createCrdtApplyFunction({ getCrdtUpdateLog, insertItem, insertCrdtUpdateLog, updateItem, updateCrdtUpdateLog }) {
557
+ function createCrdtApplyFunction({ getStateHash, getCrdtUpdateLog, insertItem, insertCrdtUpdateLog, updateItem, updateCrdtUpdateLog }) {
587
558
  const applyItemCreated = ({ event }) => {
588
559
  const eventPayload = JSON.parse(event.payload);
589
560
  eventPayload.tombstone = 0;
@@ -598,25 +569,29 @@ function createCrdtApplyFunction({ getCrdtUpdateLog, insertItem, insertCrdtUpdat
598
569
  itemId: event.item_id,
599
570
  payload: JSON.stringify(newUpdateLog)
600
571
  });
572
+ getStateHash?.()?.addItem(event.dataset, event.item_id, newUpdateLog);
601
573
  };
602
574
  const applyItemUpdated = ({ event, meta }) => {
603
575
  if (!meta) throw new Error(`Item ${event.item_id} in dataset ${event.dataset} not found`);
604
576
  const eventPayload = event.type === "item-deleted" ? { tombstone: 1 } : JSON.parse(event.payload);
605
577
  if (event.type === "item-created") eventPayload.tombstone = 0;
606
578
  const updatePayload = {};
607
- let hasUpdates = false;
579
+ const replacedCells = [];
580
+ const previousCreateHlc = meta.id;
608
581
  for (const [key, value] of Object.entries(eventPayload)) {
609
582
  if (key === "id") continue;
610
583
  const lastUpdateTimestamp = meta[key];
611
584
  const currentUpdateTimestamp = event.timestamp;
612
585
  if (!lastUpdateTimestamp || !currentUpdateTimestamp || currentUpdateTimestamp > lastUpdateTimestamp) {
613
586
  updatePayload[key] = value;
587
+ replacedCells.push([key, lastUpdateTimestamp]);
614
588
  meta[key] = currentUpdateTimestamp;
615
- hasUpdates = true;
616
589
  }
617
590
  }
618
- if (!hasUpdates) return;
619
- updateItem({
591
+ const isLatestCreate = event.type === "item-created" && (!previousCreateHlc || event.timestamp > previousCreateHlc);
592
+ if (isLatestCreate) meta.id = event.timestamp;
593
+ if (replacedCells.length === 0 && !isLatestCreate) return;
594
+ if (replacedCells.length > 0) updateItem({
620
595
  dataset: event.dataset,
621
596
  itemId: event.item_id,
622
597
  payload: updatePayload
@@ -626,6 +601,7 @@ function createCrdtApplyFunction({ getCrdtUpdateLog, insertItem, insertCrdtUpdat
626
601
  itemId: event.item_id,
627
602
  payload: JSON.stringify(meta)
628
603
  });
604
+ getStateHash?.()?.updateItem(event.dataset, event.item_id, meta, previousCreateHlc, replacedCells);
629
605
  };
630
606
  return (event) => {
631
607
  if (isNoOpCrdtEventPayload(event.payload)) return;
@@ -649,33 +625,6 @@ function createCrdtApplyFunction({ getCrdtUpdateLog, insertItem, insertCrdtUpdat
649
625
  };
650
626
  }
651
627
  //#endregion
652
- //#region src/sqlite-crdt/event-consistency.ts
653
- const MASK_128 = (1n << 128n) - 1n;
654
- const HEX_128_PATTERN = /^[0-9a-f]{32}$/;
655
- function createEventHlcAccumulator(initialValue) {
656
- let current = parseHex128(initialValue);
657
- return {
658
- add(timestamp) {
659
- current = current + hash128BigInt(timestamp) & MASK_128;
660
- },
661
- get current() {
662
- return toHex128(current);
663
- }
664
- };
665
- }
666
- function parseHex128(value) {
667
- if (value === "") return 0n;
668
- const normalized = value.toLowerCase();
669
- if (!HEX_128_PATTERN.test(normalized)) throw new Error(`Invalid event HLC accumulator value: ${value}`);
670
- return BigInt(`0x${normalized}`);
671
- }
672
- function toHex128(value) {
673
- return value.toString(16).padStart(32, "0");
674
- }
675
- function hash128BigInt(value) {
676
- return xxhash.h64(value, 0n) | xxhash.h64(value, 1n) << 64n;
677
- }
678
- //#endregion
679
628
  //#region src/sqlite-crdt/crdt-storage.ts
680
629
  function createCrdtStorage(storage) {
681
630
  let localSyncId = storage.initialLocalSyncId;
@@ -848,7 +797,9 @@ function createCrdtStorage(storage) {
848
797
  status: "pending"
849
798
  }));
850
799
  persistEvents(tx, persistedEvents);
851
- for (const persistedEvent of persistedEvents) applyCrdtEvent(persistedEvent);
800
+ withStateHash(() => {
801
+ for (const persistedEvent of persistedEvents) applyCrdtEvent(persistedEvent);
802
+ });
852
803
  });
853
804
  processEnqueuedEvents();
854
805
  };
@@ -924,7 +875,7 @@ where "dataset" = ? and "item_id" = ? and "payload" <> ${noOpPayloadSqlLiteral}`
924
875
  persistEvents(tx, [persistedEvent]);
925
876
  applyCrdtEvent(persistedEvent);
926
877
  };
927
- const applyOwnIntentsFromTransaction = (tx, intents) => {
878
+ const applyOwnIntentsFromTransaction = (tx, intents) => withStateHash(() => {
928
879
  let appliedEvents = 0;
929
880
  for (const intent of intents) {
930
881
  if (intent.type === "item-updated") {
@@ -940,12 +891,12 @@ where "dataset" = ? and "item_id" = ? and "payload" <> ${noOpPayloadSqlLiteral}`
940
891
  appliedEvents++;
941
892
  }
942
893
  return { appliedEvents };
943
- };
894
+ });
944
895
  const dispatchEventsApplied = (previousSequentialSyncId) => {
945
896
  if (sequentialSyncId <= previousSequentialSyncId) return;
946
897
  eventTarget.dispatchEvent("events-applied", {
947
898
  syncId: sequentialSyncId,
948
- eventHlcSum: sequentialSyncId === localSyncId ? eventHlcAccumulator?.current ?? null : null
899
+ stateHash: sequentialSyncId === localSyncId ? getStateHash() : null
949
900
  });
950
901
  };
951
902
  const publishProcessedBatch = ({ previousSequentialSyncId, nextSequentialSyncId, appliedEvents }) => {
@@ -988,34 +939,21 @@ where "dataset" = ? and "item_id" = ? and "payload" <> ${noOpPayloadSqlLiteral}`
988
939
  limit: 1
989
940
  }).events.length === 0;
990
941
  };
991
- const applyCrdtEvent = createSQLiteCrdtApplyFunction({
942
+ const stateHash = createStateHashTracker({
992
943
  db,
993
- dbConfig: storage.dbConfig
944
+ updateLogTable: storage.dbConfig.updateLogTable.fullIdentifier,
945
+ schema: storage.schema,
946
+ getSchemaVersion: () => storage.migrator.currentSchemaVersion,
947
+ store: storage.stateHash ?? null
994
948
  });
995
- const eventHlcAccumulator = storage.eventHlcAccumulator ? createEventHlcAccumulator(storage.eventHlcAccumulator.current) : null;
996
- const persistEventHlcAccumulator = () => {
997
- if (eventHlcAccumulator && storage.eventHlcAccumulator) storage.eventHlcAccumulator.current = eventHlcAccumulator.current;
998
- };
999
- const recomputeEventHlcAccumulatorIfNeeded = () => {
1000
- if (!eventHlcAccumulator || !storage.eventHlcAccumulator) return;
1001
- if (storage.eventHlcAccumulator.current !== "") return;
1002
- const batchSize = 1e3;
1003
- let afterSyncId = 0;
1004
- for (;;) {
1005
- const rows = db.executePrepared("get-applied-event-timestamps", {
1006
- status: "applied",
1007
- afterSyncId,
1008
- limit: batchSize
1009
- }, (db, params) => db.selectFrom(crdtEventsTable).select(["sync_id", "timestamp"]).where("status", "=", params("status")).where("sync_id", ">", params("afterSyncId")).orderBy("sync_id", "asc").limit(params("limit")), { loggerLevel: "system" });
1010
- if (rows.length === 0) break;
1011
- for (const row of rows) {
1012
- eventHlcAccumulator.add(row.timestamp);
1013
- afterSyncId = row.sync_id;
1014
- }
1015
- if (rows.length < batchSize) break;
1016
- }
1017
- persistEventHlcAccumulator();
1018
- };
949
+ const withStateHash = stateHash.track;
950
+ const getStateHash = stateHash.read;
951
+ const applyCrdtEventWithoutStateHash = createSQLiteCrdtApplyFunction({
952
+ db,
953
+ dbConfig: storage.dbConfig,
954
+ getStateHash: () => stateHash.active
955
+ });
956
+ const applyCrdtEvent = (event) => withStateHash(() => applyCrdtEventWithoutStateHash(event));
1019
957
  const getFirstAcceptedSyncIdsByTimestamp = (tx, events) => {
1020
958
  const timestamps = Array.from(new Set(events.map((event) => event.timestamp)));
1021
959
  if (timestamps.length === 0) return /* @__PURE__ */ new Map();
@@ -1135,18 +1073,18 @@ where "dataset" = ? and "item_id" = ? and "payload" <> ${noOpPayloadSqlLiteral}`
1135
1073
  db.executeTransaction((tx) => {
1136
1074
  const firstAcceptedSyncIdByTimestamp = getFirstAcceptedSyncIdsByTimestamp(tx, events.filter((event) => event.origin !== "own-applied"));
1137
1075
  const statusOnlyEvents = [];
1138
- for (const event of events) {
1139
- processPersistedEvent(tx, event, firstAcceptedSyncIdByTimestamp, statusOnlyEvents);
1140
- if (event.status === "applied") {
1141
- const firstAcceptedSyncId = firstAcceptedSyncIdByTimestamp.get(event.timestamp);
1142
- if (firstAcceptedSyncId === void 0 || event.sync_id < firstAcceptedSyncId) firstAcceptedSyncIdByTimestamp.set(event.timestamp, event.sync_id);
1143
- eventHlcAccumulator?.add(event.timestamp);
1144
- if (storage.onEventApplied) appliedEventsToNotify.push(event);
1076
+ withStateHash(() => {
1077
+ for (const event of events) {
1078
+ processPersistedEvent(tx, event, firstAcceptedSyncIdByTimestamp, statusOnlyEvents);
1079
+ if (event.status === "applied") {
1080
+ const firstAcceptedSyncId = firstAcceptedSyncIdByTimestamp.get(event.timestamp);
1081
+ if (firstAcceptedSyncId === void 0 || event.sync_id < firstAcceptedSyncId) firstAcceptedSyncIdByTimestamp.set(event.timestamp, event.sync_id);
1082
+ if (storage.onEventApplied) appliedEventsToNotify.push(event);
1083
+ }
1084
+ if (event.status === "failed" && event.origin === "remote") failedRemoteSyncIds.push(event.sync_id);
1145
1085
  }
1146
- if (event.status === "failed" && event.origin === "remote") failedRemoteSyncIds.push(event.sync_id);
1147
- }
1086
+ });
1148
1087
  updateEventStatuses(tx, statusOnlyEvents);
1149
- persistEventHlcAccumulator();
1150
1088
  });
1151
1089
  publishProcessedBatch({
1152
1090
  previousSequentialSyncId,
@@ -1156,7 +1094,7 @@ where "dataset" = ? and "item_id" = ? and "payload" <> ${noOpPayloadSqlLiteral}`
1156
1094
  for (const syncId of failedRemoteSyncIds) eventTarget.dispatchEvent("remote-event-apply-failed", { syncId });
1157
1095
  }
1158
1096
  });
1159
- recomputeEventHlcAccumulatorIfNeeded();
1097
+ if (storage.stateHash) db.executeTransaction(stateHash.initialize);
1160
1098
  processEnqueuedEvents();
1161
1099
  return {
1162
1100
  getEventsBatch,
@@ -1165,7 +1103,7 @@ where "dataset" = ? and "item_id" = ? and "payload" <> ${noOpPayloadSqlLiteral}`
1165
1103
  applyOwnEvents,
1166
1104
  applyOwnSnapshot,
1167
1105
  checkIsQuiescent,
1168
- getEventHlcAccumulator: () => eventHlcAccumulator?.current ?? null,
1106
+ getStateHash,
1169
1107
  addEventListener: eventTarget.addEventListener,
1170
1108
  removeEventListener: eventTarget.removeEventListener,
1171
1109
  internal: {
@@ -1181,135 +1119,11 @@ const createCrdtSyncProducer = ({ storage, broadcastEvents }) => {
1181
1119
  storage.addEventListener("events-applied", (event) => {
1182
1120
  broadcastEvents({
1183
1121
  newSyncId: event.payload.syncId,
1184
- eventHlcSum: event.payload.eventHlcSum
1122
+ stateHash: event.payload.stateHash
1185
1123
  });
1186
1124
  });
1187
1125
  };
1188
1126
  //#endregion
1189
- //#region src/worker-db/worker-common.ts
1190
- const syncDbWorkerLockName = "sync-db-worker-lock";
1191
- const syncDbClientLockName = "sync-db-client-lock";
1192
- const broadcastChannelNames = {
1193
- requests: "sync-db-worker-requests",
1194
- responses: "sync-db-worker-responses"
1195
- };
1196
- const createBroadcastChannels = (prefix) => {
1197
- return {
1198
- requests: new TypedBroadcastChannel(`${prefix}-${broadcastChannelNames.requests}`),
1199
- responses: new TypedBroadcastChannel(`${prefix}-${broadcastChannelNames.responses}`)
1200
- };
1201
- };
1202
- function isWorkerInitMessage(message) {
1203
- return typeof message === "object" && message !== null && "type" in message && message.type === "init";
1204
- }
1205
- function isWorkerRequestMessage(message) {
1206
- return typeof message === "object" && message !== null && "type" in message && message.type === "request";
1207
- }
1208
- function isWorkerResponseMessage(message) {
1209
- return typeof message === "object" && message !== null && "type" in message && "requestId" in message && "data" in message;
1210
- }
1211
- function isWorkerErrorResponseMessage(message) {
1212
- return typeof message === "object" && message !== null && "type" in message && message.type === "error-response";
1213
- }
1214
- function isWorkerNotificationMessage(message) {
1215
- return typeof message === "object" && message !== null && "notificationType" in message && !!message.notificationType;
1216
- }
1217
- //#endregion
1218
- //#region src/worker-db/replica-lock.ts
1219
- const replicaLockName = (dbId) => `sync-db-replica-lock-${dbId}`;
1220
- function acquireReplicaLock({ dbId }) {
1221
- return acquireLock(replicaLockName(dbId), { mode: "shared" });
1222
- }
1223
- function runWithNoActiveReplicas({ dbId, run }) {
1224
- return navigator.locks.request(replicaLockName(dbId), {
1225
- mode: "exclusive",
1226
- ifAvailable: true
1227
- }, async (lock) => {
1228
- if (!lock) return false;
1229
- await run();
1230
- return true;
1231
- });
1232
- }
1233
- //#endregion
1234
- //#region src/worker-db/reset-state.ts
1235
- /** Default IndexedDB-backed reset store (idb-keyval over a dedicated database). */
1236
- function createIdbResetStore() {
1237
- const store = createStore("sqlite-sync", "kv");
1238
- return {
1239
- get: (key) => get(key, store),
1240
- set: (key, value) => set(key, value, store),
1241
- delete: (key) => del(key, store)
1242
- };
1243
- }
1244
- /**
1245
- * A clean reset is a recovery action for a de-sync detected now. If the reload
1246
- * never happens (broadcast lost, tab crashed mid-reload, browser killed the page),
1247
- * the request must not fire on an arbitrary later cold start and silently wipe
1248
- * local-only writes accumulated since.
1249
- */
1250
- const RESET_REQUEST_TTL_MS = 6e5;
1251
- const resetRequestKey = (dbId) => `sqlite-sync-reset-request-${dbId}`;
1252
- const resetAppliedKey = (dbId) => `sqlite-sync-reset-applied-${dbId}`;
1253
- /**
1254
- * Worker-owned durable reset state. The reset decision must be owned by the
1255
- * elected worker, not by tabs during `createSyncedDb` — otherwise a later
1256
- * worker election could repeat an already-applied wipe.
1257
- */
1258
- function createResetStateStore({ store, dbId, now = () => Date.now() }) {
1259
- const requestKey = resetRequestKey(dbId);
1260
- const appliedKey = resetAppliedKey(dbId);
1261
- return {
1262
- async writeResetRequest(epoch) {
1263
- const request = {
1264
- epoch,
1265
- requestedAt: now()
1266
- };
1267
- await store.set(requestKey, request);
1268
- return request;
1269
- },
1270
- /**
1271
- * Read the pending reset request after winning the worker election.
1272
- * Returns the request only when it has not been applied yet and is within
1273
- * the TTL. Stale requests are deleted so they cannot fire on a later cold start.
1274
- */
1275
- async resolvePendingReset() {
1276
- const request = await store.get(requestKey);
1277
- if (!request) return;
1278
- if (now() - request.requestedAt > 6e5) {
1279
- await store.delete(requestKey);
1280
- return;
1281
- }
1282
- const appliedEpoch = await store.get(appliedKey);
1283
- if (request.epoch === appliedEpoch) return;
1284
- return request;
1285
- },
1286
- /**
1287
- * Record the epoch as applied. Must be called only after the worker has
1288
- * successfully initialized with `clearOnInit: true`, so a failed init can
1289
- * be retried by a later elected worker.
1290
- */
1291
- async markResetApplied(epoch) {
1292
- await store.set(appliedKey, epoch);
1293
- }
1294
- };
1295
- }
1296
- /**
1297
- * Worker-side `requestReload` RPC implementation. For `clean: true` the reset
1298
- * request is durably stored before broadcasting and before the RPC resolves,
1299
- * so the epoch survives no matter which path triggers the reload.
1300
- */
1301
- function createReloadRequestHandler({ resetState, broadcast, generateEpoch = generateId }) {
1302
- return async (options) => {
1303
- const reloadEpoch = generateEpoch();
1304
- if (options.clean) await resetState.writeResetRequest(reloadEpoch);
1305
- broadcast({
1306
- notificationType: "reload-requested",
1307
- reloadEpoch,
1308
- clean: options.clean
1309
- });
1310
- };
1311
- }
1312
- //#endregion
1313
- export { xxhash as C, createMigrator as S, createImportData as T, createCrdtApplyFunction as _, acquireReplicaLock as a, verifySyncSchema as b, isWorkerErrorResponseMessage as c, isWorkerRequestMessage as d, isWorkerResponseMessage as f, createCrdtStorage as g, createCrdtSyncProducer as h, createResetStateStore as i, isWorkerInitMessage as l, syncDbWorkerLockName as m, createIdbResetStore as n, runWithNoActiveReplicas as o, syncDbClientLockName as p, createReloadRequestHandler as r, createBroadcastChannels as s, RESET_REQUEST_TTL_MS as t, isWorkerNotificationMessage as u, createSQLiteCrdtApplyFunction as v, createExportData as w, createMigrations as x, formatSchemaVerificationIssues as y };
1127
+ export { formatSchemaVerificationIssues as a, createMigrator as c, createSQLiteCrdtApplyFunction as i, createExportData as l, createCrdtStorage as n, verifySyncSchemaWithDriver as o, createCrdtApplyFunction as r, createMigrations as s, createCrdtSyncProducer as t, createImportData as u };
1314
1128
 
1315
- //# sourceMappingURL=reset-state-t0tTGqoh.js.map
1129
+ //# sourceMappingURL=crdt-sync-producer-BY-WobgO.js.map