@sqlite-sync/core 0.14.1 → 0.14.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.
Files changed (29) hide show
  1. package/dist/{crdt-sync-producer-BQkuzCBM.js → crdt-sync-producer-CUXiu4X3.js} +2 -2
  2. package/dist/{crdt-sync-producer-BQkuzCBM.js.map → crdt-sync-producer-CUXiu4X3.js.map} +1 -1
  3. package/dist/{crdt-sync-remote-source-yeTEkOjH.js → crdt-sync-remote-source-CH2bsr-x.js} +63 -16
  4. package/dist/crdt-sync-remote-source-CH2bsr-x.js.map +1 -0
  5. package/dist/{index-BgkTUdlX.d.ts → index-C1E8xMH1.d.ts} +21 -2
  6. package/dist/{index.common-DIN0mZxR.js → index.common-c-Je9hVa.js} +3 -3
  7. package/dist/{index.common-DIN0mZxR.js.map → index.common-c-Je9hVa.js.map} +1 -1
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +48 -5
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.native.d.ts +2 -2
  12. package/dist/index.native.js +3 -3
  13. package/dist/internal.d.ts +1 -1
  14. package/dist/local.d.ts +6 -6
  15. package/dist/local.js +6 -5
  16. package/dist/local.js.map +1 -1
  17. package/dist/{reset-state-DAPyJglN.js → reset-state-BeJZaX-W.js} +2 -2
  18. package/dist/reset-state-BeJZaX-W.js.map +1 -0
  19. package/dist/server.d.ts +1 -1
  20. package/dist/server.js +1 -1
  21. package/dist/worker.d.ts +4 -4
  22. package/dist/worker.js +6 -5
  23. package/dist/worker.js.map +1 -1
  24. package/dist/{ws-remote-source-BJFkwEyQ.d.ts → ws-remote-source-Bk6NYwML.d.ts} +2 -2
  25. package/dist/{ws-remote-source-BbPWrHB6.js → ws-remote-source-CliRqkIW.js} +60 -14
  26. package/dist/{ws-remote-source-BbPWrHB6.js.map → ws-remote-source-CliRqkIW.js.map} +1 -1
  27. package/package.json +1 -1
  28. package/dist/crdt-sync-remote-source-yeTEkOjH.js.map +0 -1
  29. package/dist/reset-state-DAPyJglN.js.map +0 -1
@@ -505,6 +505,7 @@ limit ?`,
505
505
  }
506
506
  //#endregion
507
507
  //#region src/sqlite-crdt/crdt-sync-remote-source.ts
508
+ const CATCH_UP_TIMEOUT_MS = 1e4;
508
509
  const DISCONNECT_WATCHDOG_MS = 15e3;
509
510
  const REQUEST_TIMEOUT_MS = 1e4;
510
511
  const RECONNECT_BASE_DELAY_MS = 1e3;
@@ -585,6 +586,7 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
585
586
  let establishedAt = null;
586
587
  let lastActivityAt = Date.now();
587
588
  let heartbeatTimer;
589
+ let initialPull = null;
588
590
  const untilClosed = (operation, timeoutMs) => new Promise((resolve, reject) => {
589
591
  const onAbort = () => reject(/* @__PURE__ */ new Error("Remote connection closed"));
590
592
  if (signal.aborted) {
@@ -646,7 +648,12 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
646
648
  return;
647
649
  }
648
650
  source = created;
649
- sync();
651
+ const firstPull = pull();
652
+ initialPull = firstPull;
653
+ firstPull.then(() => {
654
+ initialPull = null;
655
+ });
656
+ firstPull.then(() => push.promise()).then(() => push()).catch((error) => console.warn("Initial sync failed", error));
650
657
  };
651
658
  const scheduleHeartbeat = () => {
652
659
  const ping = source?.ping?.bind(source);
@@ -666,28 +673,30 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
666
673
  };
667
674
  let activePull = null;
668
675
  const pull = (request) => {
669
- if (!source || signal.aborted) return Promise.resolve();
676
+ if (!source || signal.aborted) return Promise.resolve(false);
670
677
  const activeSource = source;
671
678
  const remoteSyncId = request?.remoteSyncId;
672
679
  if (remoteSyncId !== void 0 && remoteSyncId <= pullSyncId.current) {
673
680
  checkRemoteConsistency(remoteSyncId, request?.remoteStateHash ?? null);
674
- return Promise.resolve();
681
+ return Promise.resolve(true);
675
682
  }
676
683
  if (activePull) {
677
684
  if (remoteSyncId !== void 0 && (!activePull.requestedSyncId || activePull.requestedSyncId < remoteSyncId)) activePull.requestedSyncId = remoteSyncId;
678
685
  return activePull.promise;
679
686
  }
680
687
  const nextPull = {
681
- promise: Promise.resolve(),
688
+ promise: Promise.resolve(false),
682
689
  requestedSyncId: null
683
690
  };
684
691
  nextPull.promise = pullAllEvents(activeSource, {
685
692
  afterSyncId: pullSyncId.current,
686
693
  excludeNodeId: request?.includeSelf ? void 0 : nodeId
687
694
  }).catch((error) => {
688
- if (signal.aborted) return;
689
- console.error("Error pulling events. Going offline.", error);
690
- fail("REMOTE_PULL_ERROR");
695
+ if (!signal.aborted) {
696
+ console.error("Error pulling events. Going offline.", error);
697
+ fail("REMOTE_PULL_ERROR");
698
+ }
699
+ return false;
691
700
  }).finally(() => {
692
701
  if (activePull === nextPull) activePull = null;
693
702
  const nextTarget = nextPull.requestedSyncId;
@@ -704,7 +713,7 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
704
713
  ...opts,
705
714
  afterSyncId
706
715
  }));
707
- if (signal.aborted) return;
716
+ if (signal.aborted) return false;
708
717
  hasMore = response.hasMore;
709
718
  afterSyncId = response.nextSyncId;
710
719
  if (response.events) {
@@ -720,11 +729,12 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
720
729
  return x;
721
730
  }));
722
731
  await untilClosed(() => processed);
723
- if (signal.aborted) return;
732
+ if (signal.aborted) return false;
724
733
  }
725
734
  if (response.nextSyncId <= pullSyncId.current) break;
726
735
  if (response.nextSyncId > pullSyncId.current) pullSyncId.current = response.nextSyncId;
727
736
  }
737
+ return !hasMore;
728
738
  };
729
739
  const push = ensureSingletonExecution(async () => {
730
740
  while (source && !signal.aborted) {
@@ -735,7 +745,7 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
735
745
  excludeOrigin: "remote",
736
746
  limit: bufferSize
737
747
  });
738
- if (eventsBatch.events.length === 0) break;
748
+ if (eventsBatch.events.length === 0) return true;
739
749
  let response;
740
750
  try {
741
751
  response = await request(() => activeSource.pushEvents({
@@ -754,18 +764,19 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
754
764
  console.error("Error pushing events. Going offline.", error);
755
765
  fail("REMOTE_PUSH_ERROR");
756
766
  }
757
- return;
767
+ return false;
758
768
  }
759
- if (signal.aborted) return;
769
+ if (signal.aborted) return false;
760
770
  if (!response.ok) {
761
771
  console.error("Remote rejected pushed events. Going offline.");
762
772
  fail("REMOTE_PUSH_ERROR", { reconnect: false });
763
- return;
773
+ return false;
764
774
  }
765
775
  pushSyncId.current = eventsBatch.nextSyncId;
766
776
  if (response.beforeSyncId !== void 0 && response.afterSyncId !== void 0 && response.beforeSyncId <= pullSyncId.current && response.afterSyncId > pullSyncId.current) pullSyncId.current = response.afterSyncId;
767
- if (!eventsBatch.hasMore) break;
777
+ if (!eventsBatch.hasMore) return true;
768
778
  }
779
+ return false;
769
780
  });
770
781
  const sync = async () => {
771
782
  await activePull?.promise;
@@ -773,6 +784,15 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
773
784
  await push.promise();
774
785
  await push();
775
786
  };
787
+ const catchUp = async (progress) => {
788
+ if (initialPull) progress.pulled = await initialPull;
789
+ else {
790
+ await activePull?.promise;
791
+ progress.pulled = await pull();
792
+ }
793
+ await push.promise();
794
+ progress.pushed = await push();
795
+ };
776
796
  const close = () => {
777
797
  controller.abort();
778
798
  clearTimeout(heartbeatTimer);
@@ -784,7 +804,8 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
784
804
  start,
785
805
  close,
786
806
  push,
787
- sync
807
+ sync,
808
+ catchUp
788
809
  };
789
810
  return connection;
790
811
  };
@@ -827,6 +848,31 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
827
848
  if (current) await current.sync();
828
849
  else if (wantsOnline && !remoteState.schemaVersionMismatched) await goOnline();
829
850
  };
851
+ const catchUp = async ({ timeoutMs = CATCH_UP_TIMEOUT_MS } = {}, prepare) => {
852
+ const progress = {
853
+ pulled: false,
854
+ pushed: false
855
+ };
856
+ const run = async () => {
857
+ await prepare?.();
858
+ if (!current && wantsOnline && !remoteState.schemaVersionMismatched) goOnline();
859
+ await current?.catchUp(progress);
860
+ };
861
+ let timer;
862
+ const timeout = new Promise((resolve) => {
863
+ timer = setTimeout(() => resolve(true), timeoutMs);
864
+ });
865
+ const finished = run().then(() => false, (error) => {
866
+ console.warn("Failed to catch up with the remote", error);
867
+ return false;
868
+ });
869
+ const timedOut = await Promise.race([finished, timeout]);
870
+ clearTimeout(timer);
871
+ return {
872
+ ...progress,
873
+ timedOut
874
+ };
875
+ };
830
876
  const checkRemoteConsistency = (remoteSyncId, remoteStateHash) => {
831
877
  if (remoteStateHash == null) return;
832
878
  if (remoteSyncId !== pullSyncId.current) return;
@@ -863,6 +909,7 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
863
909
  goOffline,
864
910
  dropConnection,
865
911
  syncWithRemote,
912
+ catchUp,
866
913
  getState,
867
914
  dispose,
868
915
  addEventListener: eventTarget.addEventListener,
@@ -872,4 +919,4 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
872
919
  //#endregion
873
920
  export { DEFAULT_MAX_DRIFT_MS as a, deserializeHLC as c, validateNewCrdtEvent as i, serializeHLC as l, createStateHashTracker as n, HLCCounter as o, CrdtEventValidationError as r, compareHLC as s, createCrdtSyncRemoteSource as t, xxhash as u };
874
921
 
875
- //# sourceMappingURL=crdt-sync-remote-source-yeTEkOjH.js.map
922
+ //# sourceMappingURL=crdt-sync-remote-source-CH2bsr-x.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt-sync-remote-source-CH2bsr-x.js","names":[],"sources":["../src/xxh64.ts","../src/hash.ts","../src/hlc.ts","../src/schema/validate-crdt-event.ts","../src/sqlite-crdt/state-hash.ts","../src/sqlite-crdt/crdt-sync-remote-source.ts"],"sourcesContent":["// XXH64 over 32-bit halves kept in locals: on Hermes both BigInt arithmetic and allocating per step are slow.\n// Must stay bit-for-bit compatible with xxhash-wasm, since replicas compare checksums across platforms.\n\nconst P1_HI = 0x9e3779b1;\nconst P1_LO = 0x85ebca87;\nconst P2_HI = 0xc2b2ae3d;\nconst P2_LO = 0x27d4eb4f;\nconst P3_HI = 0x165667b1;\nconst P3_LO = 0x9e3779f9;\nconst P4_HI = 0x85ebca77;\nconst P4_LO = 0xc2b2ae63;\nconst P5_HI = 0x27d4eb2f;\nconst P5_LO = 0x165667c5;\n\nconst TWO_32 = 0x100000000;\n\n// The 64-bit helpers below return their result through these instead of allocating.\nlet hi = 0;\nlet lo = 0;\n\nfunction add(aHi: number, aLo: number, bHi: number, bLo: number) {\n const sum = aLo + bLo;\n hi = (aHi + bHi + (sum >= TWO_32 ? 1 : 0)) >>> 0;\n lo = sum >>> 0;\n}\n\nfunction sub(aHi: number, aLo: number, bHi: number, bLo: number) {\n const difference = aLo - bLo;\n hi = (aHi - bHi - (difference < 0 ? 1 : 0)) >>> 0;\n lo = difference >>> 0;\n}\n\nfunction mul(aHi: number, aLo: number, bHi: number, bLo: number) {\n const a0 = aLo & 0xffff;\n const a1 = aLo >>> 16;\n const b0 = bLo & 0xffff;\n const b1 = bLo >>> 16;\n\n const mid = a0 * b1 + a1 * b0;\n const low = a0 * b0 + (mid % 0x10000) * 0x10000;\n const carry = Math.floor(low / TWO_32);\n hi = (a1 * b1 + Math.floor(mid / 0x10000) + carry + Math.imul(aHi, bLo) + Math.imul(aLo, bHi)) >>> 0;\n lo = low >>> 0;\n}\n\n/** `bits` must be between 1 and 31. */\nfunction rotl(h: number, l: number, bits: number) {\n hi = ((h << bits) | (l >>> (32 - bits))) >>> 0;\n lo = ((l << bits) | (h >>> (32 - bits))) >>> 0;\n}\n\nfunction round(accHi: number, accLo: number, inputHi: number, inputLo: number) {\n mul(inputHi, inputLo, P2_HI, P2_LO);\n add(accHi, accLo, hi, lo);\n rotl(hi, lo, 31);\n mul(hi, lo, P1_HI, P1_LO);\n}\n\nfunction mergeRound(accHi: number, accLo: number, valueHi: number, valueLo: number) {\n round(0, 0, valueHi, valueLo);\n mul((accHi ^ hi) >>> 0, (accLo ^ lo) >>> 0, P1_HI, P1_LO);\n add(hi, lo, P4_HI, P4_LO);\n}\n\nfunction read32(bytes: Uint8Array, offset: number) {\n return (bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24)) >>> 0;\n}\n\nexport function xxh64(bytes: Uint8Array, seed: bigint): bigint {\n const seedHi = Number((seed >> 32n) & 0xffffffffn);\n const seedLo = Number(seed & 0xffffffffn);\n const length = bytes.length;\n let offset = 0;\n let hashHi: number;\n let hashLo: number;\n\n if (length >= 32) {\n add(seedHi, seedLo, P1_HI, P1_LO);\n add(hi, lo, P2_HI, P2_LO);\n let v1Hi = hi;\n let v1Lo = lo;\n add(seedHi, seedLo, P2_HI, P2_LO);\n let v2Hi = hi;\n let v2Lo = lo;\n let v3Hi = seedHi;\n let v3Lo = seedLo;\n sub(seedHi, seedLo, P1_HI, P1_LO);\n let v4Hi = hi;\n let v4Lo = lo;\n\n const limit = length - 32;\n while (offset <= limit) {\n round(v1Hi, v1Lo, read32(bytes, offset + 4), read32(bytes, offset));\n v1Hi = hi;\n v1Lo = lo;\n round(v2Hi, v2Lo, read32(bytes, offset + 12), read32(bytes, offset + 8));\n v2Hi = hi;\n v2Lo = lo;\n round(v3Hi, v3Lo, read32(bytes, offset + 20), read32(bytes, offset + 16));\n v3Hi = hi;\n v3Lo = lo;\n round(v4Hi, v4Lo, read32(bytes, offset + 28), read32(bytes, offset + 24));\n v4Hi = hi;\n v4Lo = lo;\n offset += 32;\n }\n\n rotl(v1Hi, v1Lo, 1);\n hashHi = hi;\n hashLo = lo;\n rotl(v2Hi, v2Lo, 7);\n add(hashHi, hashLo, hi, lo);\n hashHi = hi;\n hashLo = lo;\n rotl(v3Hi, v3Lo, 12);\n add(hashHi, hashLo, hi, lo);\n hashHi = hi;\n hashLo = lo;\n rotl(v4Hi, v4Lo, 18);\n add(hashHi, hashLo, hi, lo);\n\n mergeRound(hi, lo, v1Hi, v1Lo);\n mergeRound(hi, lo, v2Hi, v2Lo);\n mergeRound(hi, lo, v3Hi, v3Lo);\n mergeRound(hi, lo, v4Hi, v4Lo);\n hashHi = hi;\n hashLo = lo;\n } else {\n add(seedHi, seedLo, P5_HI, P5_LO);\n hashHi = hi;\n hashLo = lo;\n }\n\n add(hashHi, hashLo, Math.floor(length / TWO_32), length >>> 0);\n hashHi = hi;\n hashLo = lo;\n\n while (offset + 8 <= length) {\n round(0, 0, read32(bytes, offset + 4), read32(bytes, offset));\n rotl((hashHi ^ hi) >>> 0, (hashLo ^ lo) >>> 0, 27);\n mul(hi, lo, P1_HI, P1_LO);\n add(hi, lo, P4_HI, P4_LO);\n hashHi = hi;\n hashLo = lo;\n offset += 8;\n }\n\n if (offset + 4 <= length) {\n mul(0, read32(bytes, offset), P1_HI, P1_LO);\n rotl((hashHi ^ hi) >>> 0, (hashLo ^ lo) >>> 0, 23);\n mul(hi, lo, P2_HI, P2_LO);\n add(hi, lo, P3_HI, P3_LO);\n hashHi = hi;\n hashLo = lo;\n offset += 4;\n }\n\n while (offset < length) {\n mul(0, bytes[offset], P5_HI, P5_LO);\n rotl((hashHi ^ hi) >>> 0, (hashLo ^ lo) >>> 0, 11);\n mul(hi, lo, P1_HI, P1_LO);\n hashHi = hi;\n hashLo = lo;\n offset++;\n }\n\n mul(hashHi, (hashLo ^ (hashHi >>> 1)) >>> 0, P2_HI, P2_LO);\n hashHi = hi;\n hashLo = lo;\n mul((hashHi ^ (hashHi >>> 29)) >>> 0, (hashLo ^ ((hashLo >>> 29) | (hashHi << 3))) >>> 0, P3_HI, P3_LO);\n hashHi = hi;\n hashLo = (lo ^ hi) >>> 0;\n\n return (BigInt(hashHi) << 32n) | BigInt(hashLo);\n}\n\nconst textEncoder = typeof TextEncoder === \"undefined\" ? null : new TextEncoder();\n\nexport function encodeUtf8(value: string): Uint8Array {\n return textEncoder ? textEncoder.encode(value) : encodeUtf8Fallback(value);\n}\n\n/** Matches TextEncoder, including U+FFFD for lone surrogates. */\nexport function encodeUtf8Fallback(value: string): Uint8Array {\n const bytes: number[] = [];\n for (let index = 0; index < value.length; index++) {\n let codePoint = value.charCodeAt(index);\n\n if (codePoint >= 0xd800 && codePoint <= 0xdbff) {\n const next = index + 1 < value.length ? value.charCodeAt(index + 1) : 0;\n if (next >= 0xdc00 && next <= 0xdfff) {\n codePoint = 0x10000 + ((codePoint - 0xd800) << 10) + (next - 0xdc00);\n index++;\n } else {\n codePoint = 0xfffd;\n }\n } else if (codePoint >= 0xdc00 && codePoint <= 0xdfff) {\n codePoint = 0xfffd;\n }\n\n if (codePoint < 0x80) {\n bytes.push(codePoint);\n } else if (codePoint < 0x800) {\n bytes.push(0xc0 | (codePoint >> 6), 0x80 | (codePoint & 0x3f));\n } else if (codePoint < 0x10000) {\n bytes.push(0xe0 | (codePoint >> 12), 0x80 | ((codePoint >> 6) & 0x3f), 0x80 | (codePoint & 0x3f));\n } else {\n bytes.push(\n 0xf0 | (codePoint >> 18),\n 0x80 | ((codePoint >> 12) & 0x3f),\n 0x80 | ((codePoint >> 6) & 0x3f),\n 0x80 | (codePoint & 0x3f),\n );\n }\n }\n return Uint8Array.from(bytes);\n}\n","import initXxhash, { type XXHashAPI } from \"xxhash-wasm\";\nimport { encodeUtf8, xxh64 } from \"./xxh64\";\n\ntype Hasher = {\n h64: (input: string, seed: bigint) => bigint;\n};\n\nlet loadPromise: Promise<void> | null = null;\nlet hasher: Hasher | null = null;\n\nconst jsHasher: Hasher = {\n h64: (input, seed) => xxh64(encodeUtf8(input), seed),\n};\n\nfunction createWasmHasher(api: XXHashAPI): Hasher {\n return {\n h64: (input, seed) => api.h64(input, seed),\n };\n}\n\nfunction ensureLoaded(): Promise<void> {\n if (!loadPromise) {\n if (typeof WebAssembly === \"undefined\") {\n hasher = jsHasher;\n loadPromise = Promise.resolve();\n } else {\n // Instantiation can still fail, e.g. under a CSP without 'wasm-unsafe-eval'; the JS hasher gives the same hashes.\n loadPromise = initXxhash().then(\n (api) => {\n hasher = createWasmHasher(api);\n },\n () => {\n hasher = jsHasher;\n },\n );\n }\n }\n return loadPromise;\n}\n\nfunction getHasher() {\n if (!hasher) {\n throw new Error(\"xxhash is not initialized; call xxhash.ensureLoaded() first\");\n }\n return hasher;\n}\n\nfunction h64(input: string, seed = 0n): bigint {\n return getHasher().h64(input, seed);\n}\n\nexport const xxhash = {\n ensureLoaded,\n h64,\n};\n","export interface HLC {\n timestamp: number;\n counter: number;\n nodeId: string;\n}\n\nconst MAX_COUNTER = 36 ** 5 - 1; // 60,466,175 — max value that fits in 5-char base36\nexport const DEFAULT_MAX_DRIFT_MS = 6 * 60 * 60 * 1000; // 6 hours\n\nexport class HLCCounter {\n private timestamp: number;\n private counter: number;\n private nodeId: string;\n\n private readonly getTimestamp: () => number;\n private readonly maxDrift: number;\n\n constructor(nodeId: string, getTimestamp: () => number, maxDrift: number = DEFAULT_MAX_DRIFT_MS) {\n this.timestamp = getTimestamp();\n this.counter = 0;\n this.nodeId = nodeId;\n this.getTimestamp = getTimestamp;\n this.maxDrift = maxDrift;\n }\n\n getCurrentHLC(): HLC {\n return {\n timestamp: this.timestamp,\n counter: this.counter,\n nodeId: this.nodeId,\n };\n }\n\n getNextHLC(): HLC {\n const now = this.getTimestamp();\n\n if (now > this.timestamp) {\n this.timestamp = now;\n this.counter = 0;\n return this.getCurrentHLC();\n }\n\n this.counter++;\n this.rollOverCounter();\n return this.getCurrentHLC();\n }\n\n /** Restore already-persisted causal state, regardless of wall-clock drift. */\n restoreHLC(hlc: HLC) {\n if (hlc.timestamp > this.timestamp) {\n this.timestamp = hlc.timestamp;\n this.counter = hlc.counter;\n } else if (hlc.timestamp === this.timestamp) {\n this.counter = Math.max(this.counter, hlc.counter);\n }\n }\n\n mergeHLC(hlc: HLC) {\n const now = this.getTimestamp();\n if (hlc.timestamp - now > this.maxDrift) {\n console.warn(\n `HLC: ignoring far-future timestamp (remote=${hlc.timestamp}, local=${now}, drift=${hlc.timestamp - now}ms)`,\n );\n return;\n }\n\n if (this.timestamp === hlc.timestamp) {\n this.counter = Math.max(this.counter, hlc.counter) + 1;\n } else if (this.timestamp > hlc.timestamp) {\n this.counter++;\n } else {\n this.timestamp = hlc.timestamp;\n this.counter = hlc.counter + 1;\n }\n this.rollOverCounter();\n }\n\n private rollOverCounter() {\n if (this.counter > MAX_COUNTER) {\n this.timestamp += 1;\n this.counter = 0;\n }\n }\n}\n\nexport function serializeHLC(hlc: HLC) {\n return `${hlc.timestamp.toString().padStart(15, \"0\")}:${hlc.counter.toString(36).padStart(5, \"0\")}:${hlc.nodeId}`;\n}\n\nexport function deserializeHLC(serialized: string): HLC {\n const parts = serialized.split(\":\");\n if (parts.length < 3) {\n throw new Error(`Invalid HLC format: expected at least 3 colon-separated segments, got ${parts.length}`);\n }\n\n const timestamp = parseInt(parts[0], 10);\n const counter = parseInt(parts[1], 36);\n\n if (Number.isNaN(timestamp) || Number.isNaN(counter)) {\n throw new Error(`Invalid HLC values: timestamp=${parts[0]}, counter=${parts[1]}`);\n }\n\n return {\n timestamp,\n counter,\n nodeId: parts.slice(2).join(\":\"),\n };\n}\n\nexport function compareHLC(one: HLC, two: HLC) {\n if (one.timestamp === two.timestamp) {\n if (one.counter === two.counter) {\n if (one.nodeId === two.nodeId) {\n return 0;\n }\n return one.nodeId < two.nodeId ? -1 : 1;\n }\n return one.counter - two.counter;\n }\n return one.timestamp - two.timestamp;\n}\n","import type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport type { CrdtEventType, NewCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport type { AnyTableBuilder } from \"./table-builder\";\n\nexport type NewCrdtEventValidationResult =\n | { success: true; event: NewCrdtEvent }\n | { success: false; errors: string[] };\n\nexport class CrdtEventValidationError extends Error {\n constructor(readonly errors: string[]) {\n super(`Invalid CRDT events: ${errors.join(\"; \")}`);\n this.name = \"CrdtEventValidationError\";\n }\n}\n\nconst crdtEventTypes = [\"item-created\", \"item-updated\", \"item-deleted\"] as const;\n\nexport function validateNewCrdtEvent(\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">,\n input: unknown,\n): NewCrdtEventValidationResult {\n const errors: string[] = [];\n\n if (!isRecord(input)) {\n return { success: false, errors: [\"Event must be an object\"] } satisfies NewCrdtEventValidationResult;\n }\n\n const type = input.type;\n const dataset = input.dataset;\n const itemId = input.item_id;\n\n let payload = input.payload;\n let payloadParseFailed = false;\n if (typeof payload === \"string\") {\n try {\n payload = JSON.parse(payload);\n } catch {\n payloadParseFailed = true;\n errors.push(`Event payload is not valid JSON`);\n }\n }\n\n if (!isCrdtEventType(type)) {\n errors.push(`Invalid event type \"${String(type)}\"`);\n }\n if (typeof dataset !== \"string\") {\n errors.push(`Event dataset must be a string`);\n }\n if (typeof itemId !== \"string\") {\n errors.push(`Event item_id must be a string`);\n }\n if (!payloadParseFailed && !isRecord(payload)) {\n errors.push(`Event payload must be an object`);\n }\n\n const datasetMatch = typeof dataset === \"string\" ? findTableForDataset(schema, dataset) : null;\n if (typeof dataset === \"string\" && !datasetMatch) {\n errors.push(`Unknown dataset \"${dataset}\"`);\n }\n\n if (isCrdtEventType(type) && datasetMatch && isRecord(payload)) {\n switch (type) {\n case \"item-created\": {\n const result = datasetMatch.table.validatePayload(payload, { event: \"item-created\" });\n if (!result.success) {\n errors.push(...result.errors.map((error) => `payload: ${error}`));\n }\n if (typeof itemId === \"string\" && \"id\" in payload && payload.id !== itemId) {\n errors.push(`payload: Column \"id\" must match item_id \"${itemId}\"`);\n }\n break;\n }\n case \"item-updated\": {\n const result = datasetMatch.table.validatePayload(payload, { event: \"item-updated\" });\n if (!result.success) {\n errors.push(...result.errors.map((error) => `payload: ${error}`));\n }\n break;\n }\n case \"item-deleted\":\n if (Object.keys(payload).length > 0) {\n errors.push(`payload: item-deleted events must have an empty payload`);\n }\n break;\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors } satisfies NewCrdtEventValidationResult;\n }\n\n return {\n success: true,\n event: {\n type: type as CrdtEventType,\n dataset: datasetMatch?.baseTableName ?? (dataset as string),\n item_id: itemId as string,\n payload: payload as Record<string, unknown>,\n },\n } satisfies NewCrdtEventValidationResult;\n}\n\nfunction findTableForDataset(\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">,\n dataset: string,\n): { table: AnyTableBuilder; baseTableName: string } | null {\n const config = schema.tablesConfig.find(\n (tableConfig) => tableConfig.baseTableName === dataset || tableConfig.crdtTableName === dataset,\n );\n if (config) {\n const table = schema.tables[config.crdtTableName];\n return table ? { table, baseTableName: config.baseTableName } : null;\n }\n const table = Object.hasOwn(schema.tables, dataset) ? schema.tables[dataset] : undefined;\n return table ? { table, baseTableName: dataset } : null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCrdtEventType(value: unknown): value is CrdtEventType {\n return typeof value === \"string\" && crdtEventTypes.includes(value as CrdtEventType);\n}\n","import { xxhash } from \"../hash\";\nimport type { InternalSQLiteWrapper } from \"../sqlite-db-wrapper\";\nimport { quoteId } from \"../utils\";\nimport type { SyncDbSchema } from \"./crdt-schema\";\nimport type { CrdtUpdateLogItem, CrdtUpdateLogPayload } from \"./crdt-table-schema\";\n\nconst STATE_HASH_PATTERN = /^(-?\\d+):([0-9a-f]{16})$/;\n\n/**\n * Persistence for the state hash. Reads and writes must go through the same\n * transaction that changes the update log, so a rollback reverts both.\n */\nexport type StateHashStore = {\n read: () => string | null;\n write: (value: string) => void;\n};\n\nexport function formatStateHash(schemaVersion: number, sum: bigint) {\n return `${schemaVersion}:${sum.toString(16).padStart(16, \"0\")}`;\n}\n\nexport function parseStateHash(value: string | null | undefined) {\n const match = value ? STATE_HASH_PATTERN.exec(value) : null;\n if (!match) {\n return null;\n }\n return { schemaVersion: Number(match[1]), sum: BigInt(`0x${match[2]}`) };\n}\n\n/** Returns the hashed columns for a dataset, or null to hash whichever cells the update log holds. */\nexport type StateHashColumns = (dataset: string) => readonly string[] | null;\n\nexport type StateHashAccumulator = ReturnType<typeof createStateHashAccumulator>;\n\n/**\n * The state hash is a sum of per-cell hashes over the CRDT update log, tagged with\n * the schema version it was computed for. It depends only on current state, not on\n * history, so replicas that converged agree on it no matter how they got there.\n *\n * Cells are the dataset's schema columns except `id`. A cell missing from the\n * update log hashes as written by the item's latest create (the HLC stored under\n * `id`). Older builds filled column defaults into creates migrated through an\n * add-column migration, so replicas that replayed history hold such cells while\n * replicas that applied the creates earlier never stored them.\n */\nexport function createStateHashAccumulator({ sum, getColumns }: { sum: bigint; getColumns: StateHashColumns }) {\n let current = sum;\n\n const cellHash = (dataset: string, itemId: string, column: string, hlc: string) =>\n xxhash.h64(`${dataset}\\u0000${itemId}\\u0000${column}\\u0000${hlc}`);\n\n const columnsOf = (dataset: string, meta: CrdtUpdateLogPayload) =>\n getColumns(dataset) ?? Object.keys(meta).filter((column) => column !== \"id\");\n\n return {\n get sum() {\n return current;\n },\n addItem(dataset: string, itemId: string, meta: CrdtUpdateLogPayload) {\n const createHlc = meta.id ?? \"\";\n for (const column of columnsOf(dataset, meta)) {\n current = BigInt.asUintN(64, current + cellHash(dataset, itemId, column, meta[column] ?? createHlc));\n }\n },\n /**\n * `meta` is the item's update log after the write. `replacedCells` holds each written\n * cell's HLC from before it, and `previousCreateHlc` the item's create HLC from before.\n */\n updateItem(\n dataset: string,\n itemId: string,\n meta: CrdtUpdateLogPayload,\n previousCreateHlc: string | undefined,\n replacedCells: readonly [column: string, previousHlc: string | undefined][],\n ) {\n const columns = getColumns(dataset);\n for (const [column, previousHlc] of replacedCells) {\n if (columns ? !columns.includes(column) : column === \"id\") {\n continue;\n }\n const previous = previousHlc ?? (columns ? (previousCreateHlc ?? \"\") : undefined);\n if (previous !== undefined) {\n current = BigInt.asUintN(64, current - cellHash(dataset, itemId, column, previous));\n }\n current = BigInt.asUintN(64, current + cellHash(dataset, itemId, column, meta[column]));\n }\n\n if (!columns || meta.id === previousCreateHlc) {\n return;\n }\n for (const column of columns) {\n if (meta[column] === undefined) {\n current = BigInt.asUintN(\n 64,\n current -\n cellHash(dataset, itemId, column, previousCreateHlc ?? \"\") +\n cellHash(dataset, itemId, column, meta.id ?? \"\"),\n );\n }\n }\n },\n };\n}\n\nexport type StateHashTracker = ReturnType<typeof createStateHashTracker>;\n\n/** Keeps the stored state hash in step with the update log. Without a store it does nothing. */\nexport function createStateHashTracker({\n db,\n updateLogTable,\n schema,\n getSchemaVersion,\n store,\n}: {\n db: InternalSQLiteWrapper<any>;\n updateLogTable: string;\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\"> | undefined;\n getSchemaVersion: () => number;\n store: StateHashStore | null;\n}) {\n const columnsByDataset = new Map(\n (schema?.tablesConfig ?? []).flatMap((config) => {\n const table = schema?.tables[config.crdtTableName];\n return table ? [[config.baseTableName, Object.keys(table.columns).filter((column) => column !== \"id\")]] : [];\n }),\n );\n const getColumns = (dataset: string) => columnsByDataset.get(dataset) ?? null;\n let active: StateHashAccumulator | null = null;\n\n const rebuild = () => {\n const accumulator = createStateHashAccumulator({ sum: 0n, getColumns });\n const batchSize = 1000;\n let after: [string, string] = [\"\", \"\"];\n for (;;) {\n const rows = db.executePreparedRaw<[string, string, number], CrdtUpdateLogItem>({\n key: \"get-update-log-batch\",\n sql: `select \"dataset\", \"item_id\", \"payload\" from ${quoteId(updateLogTable)}\nwhere (\"item_id\", \"dataset\") > (?, ?)\norder by \"item_id\", \"dataset\"\nlimit ?`,\n params: [...after, batchSize],\n meta: { loggerLevel: \"system\" },\n });\n\n for (const row of rows) {\n accumulator.addItem(row.dataset, row.item_id, JSON.parse(row.payload));\n }\n\n const lastRow = rows[rows.length - 1];\n if (!lastRow || rows.length < batchSize) {\n break;\n }\n after = [lastRow.item_id, lastRow.dataset];\n }\n return accumulator.sum;\n };\n\n // Hashes computed for another schema version cover a different set of columns,\n // and migrations rewrite the update log directly, so those are rebuilt.\n const readSum = (store: StateHashStore) => {\n const stored = parseStateHash(store.read());\n if (stored && stored.schemaVersion === getSchemaVersion()) {\n return stored.sum;\n }\n const sum = rebuild();\n store.write(formatStateHash(getSchemaVersion(), sum));\n return sum;\n };\n\n return {\n /** The accumulator of the running `track` call, for apply functions to add cells to. */\n get active() {\n return active;\n },\n read: () => store?.read() ?? null,\n /** Rebuilds a missing or outdated hash. Must run inside a transaction. */\n initialize: () => {\n if (store) {\n readSum(store);\n }\n },\n /** Must run inside the transaction that changes the update log, so a rollback reverts the hash too. */\n track: <T>(callback: () => T): T => {\n if (!store || active) {\n return callback();\n }\n const initialSum = readSum(store);\n const accumulator = createStateHashAccumulator({ sum: initialSum, getColumns });\n active = accumulator;\n try {\n const result = callback();\n if (accumulator.sum !== initialSum) {\n store.write(formatStateHash(getSchemaVersion(), accumulator.sum));\n }\n return result;\n } finally {\n active = null;\n }\n },\n };\n}\n","import type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { createDeferredPromise, createTypedEventTarget, ensureSingletonExecution, tryCatchAsync } from \"../utils\";\nimport type { EventsPullResponse, WorkerState } from \"../worker-db/worker-common\";\nimport type { PendingCrdtEvent } from \"./apply-crdt-event\";\nimport type { CrdtStorage } from \"./crdt-storage\";\nimport { parseStateHash } from \"./state-hash\";\nimport type { StoredValue } from \"./stored-value\";\n\ntype CrdtSyncRemoteSourceConfig = {\n bufferSize: number;\n storage: CrdtStorage;\n migrator: SyncDbMigrator;\n pullSyncId: StoredValue<number>;\n pushSyncId: StoredValue<number>;\n nodeId: string;\n remoteFactory?: CreateRemoteSourceFactory;\n};\n\nexport type EventsPullRequest = {\n afterSyncId: number;\n excludeNodeId?: string;\n};\n\nexport const PUSH_BATCH_SIZE = 75;\nexport const MAX_PUSH_EVENTS = 100;\n\nexport type EventsPushRequest = {\n nodeId: string;\n events: (PendingCrdtEvent & { schema_version: number })[];\n};\nexport type EventsPushResponse = {\n ok: boolean;\n /** Remote sync_id right before the pushed events were enqueued. */\n beforeSyncId?: number;\n /** Remote sync_id right after the pushed events were enqueued. */\n afterSyncId?: number;\n};\n\nexport type CatchUpOptions = {\n /** How long to wait before giving up. Defaults to 10 seconds. */\n timeoutMs?: number;\n};\n\nexport type CatchUpResult = {\n /** The pull reached the end of what the remote had, and those events were processed locally. */\n pulled: boolean;\n /** The remote accepted every local event that was waiting to be pushed. */\n pushed: boolean;\n /** The timeout ran out first. The sync keeps going in the background. */\n timedOut: boolean;\n};\n\nconst CATCH_UP_TIMEOUT_MS = 10_000;\n\ntype SyncProgress = Pick<CatchUpResult, \"pulled\" | \"pushed\">;\n\nconst DISCONNECT_WATCHDOG_MS = 15_000;\nconst REQUEST_TIMEOUT_MS = 10_000;\nconst RECONNECT_BASE_DELAY_MS = 1_000;\nconst RECONNECT_MAX_DELAY_MS = 30_000;\n// Backoff restarts only after a connection stays up this long, so a remote that accepts and then drops\n// connections right away is not hammered.\nconst RECONNECT_MIN_UPTIME_MS = 5_000;\n// Pings a remote that has sent nothing for this long, to catch connections that died without closing.\nconst HEARTBEAT_IDLE_MS = 25_000;\n\nexport type CrdtSyncRemoteSource = ReturnType<typeof createCrdtSyncRemoteSource>;\n\nexport type EventsAvailable = {\n newSyncId: number;\n remoteStateHash: string | null;\n};\n\n/**\n * Opens one connection to the remote. Each call must return a new source: the library calls `disconnect` on every\n * returned source exactly once. Call `onClosed` when the connection ends on its own, at any time including during\n * this call; the library then reconnects with backoff. Requests may wait for the connection to open and should\n * reject once it closes. The library times requests out and shows the remote as online after the first one succeeds.\n * With `ping`, the library also checks idle connections and replaces one whose ping fails or times out. It calls `ping`\n * only on an online connection, one at a time, and never after the connection closes; a ping does not need to settle\n * once the source has closed.\n */\nexport type CreateRemoteSourceFactory = (opts: {\n onEventsAvailable: (event: EventsAvailable) => void;\n onClosed: (error?: unknown) => void;\n}) => RemoteSource;\n\ntype RemoteSource = {\n pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;\n pushEvents: (request: EventsPushRequest) => Promise<EventsPushResponse>;\n ping?: () => Promise<void>;\n disconnect?: () => void | Promise<void>;\n};\n\nexport class SchemaVersionMismatchError extends Error {\n constructor(\n public remoteSchemaVersion: number,\n public localSchemaVersion: number,\n ) {\n super(`Schema version mismatch: remote ${remoteSchemaVersion} != local ${localSchemaVersion}`);\n this.name = \"SchemaVersionMismatchError\";\n }\n}\n\ntype RemoteSourceState =\n | {\n type: \"connecting\";\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n }\n | {\n type: \"offline\";\n reason: OfflineReason;\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n }\n | {\n type: \"online\";\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n };\n\nexport type OfflineReason =\n | \"NOT_INITIALIZED\"\n | \"INITIALIZATION_FAILED\"\n | \"REMOTE_PUSH_ERROR\"\n | \"REMOTE_PULL_ERROR\"\n | \"CONNECTION_LOST\"\n | \"NETWORK_OFFLINE\"\n | \"DISCONNECTED\";\n\nexport type DeSyncDetectedReason = \"CHECKSUM_MISMATCH\" | \"ERROR_APPLYING_REMOTE_EVENT\";\n\nexport const createCrdtSyncRemoteSource = ({\n bufferSize,\n storage,\n migrator,\n pullSyncId,\n pushSyncId,\n nodeId,\n remoteFactory,\n}: CrdtSyncRemoteSourceConfig) => {\n const eventTarget = createTypedEventTarget<{\n \"state-changed\": RemoteSourceState[\"type\"];\n \"de-sync-detected\": {\n reason: DeSyncDetectedReason;\n };\n \"remote-schema-version-mismatch\": {\n remoteSchemaVersion: number;\n localSchemaVersion: number;\n };\n }>();\n\n let remoteState: RemoteSourceState = {\n type: \"offline\",\n reason: \"NOT_INITIALIZED\",\n deSynced: false,\n schemaVersionMismatched: false,\n };\n\n const patchRemoteState = (state: Partial<RemoteSourceState>) => {\n const previous = remoteState;\n remoteState = { ...remoteState, ...state } as RemoteSourceState;\n if (\n remoteState.type !== previous.type ||\n remoteState.deSynced !== previous.deSynced ||\n remoteState.schemaVersionMismatched !== previous.schemaVersionMismatched\n ) {\n eventTarget.dispatchEvent(\"state-changed\", remoteState.type);\n }\n };\n\n let current: Connection | null = null;\n let disposed = false;\n let wantsOnline = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n let reconnectAttempt = 0;\n\n const disconnectSource = (source: RemoteSource) => {\n const watchdog = setTimeout(() => {\n console.error(`Remote source did not disconnect within ${DISCONNECT_WATCHDOG_MS}ms`);\n }, DISCONNECT_WATCHDOG_MS);\n void tryCatchAsync(async () => await source.disconnect?.()).then((result) => {\n clearTimeout(watchdog);\n if (!result.success) {\n console.warn(\"Error while disconnecting from remote source\", result.error);\n }\n });\n };\n\n const closeConnection = (connection: Connection, reason: OfflineReason, { reconnect = false } = {}) => {\n if (current !== connection) {\n return false;\n }\n current = null;\n if (reconnect && scheduleReconnect()) {\n patchRemoteState({ type: \"connecting\" });\n } else {\n patchRemoteState({ type: \"offline\", reason });\n }\n connection.close();\n return true;\n };\n\n const cancelReconnect = () => {\n clearTimeout(reconnectTimer);\n reconnectTimer = undefined;\n };\n\n // Retrying after a schema mismatch would fail the same way.\n const scheduleReconnect = () => {\n if (!wantsOnline || current || reconnectTimer || remoteState.schemaVersionMismatched) {\n return false;\n }\n const maxDelay = Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** reconnectAttempt);\n reconnectAttempt++;\n const delay = maxDelay / 2 + (Math.random() * maxDelay) / 2;\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined;\n void connect();\n }, delay);\n return true;\n };\n\n type Connection = ReturnType<typeof createConnection>;\n const createConnection = (factory: CreateRemoteSourceFactory) => {\n const controller = new AbortController();\n const { signal } = controller;\n const ready = createDeferredPromise<void>();\n let source: RemoteSource | null = null;\n let establishedAt: number | null = null;\n let lastActivityAt = Date.now();\n let heartbeatTimer: ReturnType<typeof setTimeout> | undefined;\n let initialPull: Promise<boolean> | null = null;\n\n const untilClosed = <T>(operation: () => Promise<T>, timeoutMs?: number) =>\n new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(new Error(\"Remote connection closed\"));\n if (signal.aborted) {\n onAbort();\n return;\n }\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const timeout =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => reject(new Error(`Remote request timed out after ${timeoutMs}ms`)), timeoutMs);\n operation()\n .then(resolve, reject)\n .finally(() => {\n clearTimeout(timeout);\n signal.removeEventListener(\"abort\", onAbort);\n });\n });\n\n const request = async <T>(operation: () => Promise<T>) => {\n const response = await untilClosed(operation, REQUEST_TIMEOUT_MS);\n lastActivityAt = Date.now();\n if (establishedAt === null && !signal.aborted) {\n establishedAt = Date.now();\n patchRemoteState({ type: \"online\", schemaVersionMismatched: false });\n ready.resolve();\n scheduleHeartbeat();\n }\n return response;\n };\n\n const fail = (reason: OfflineReason, { reconnect = true } = {}) => {\n if (current !== connection) {\n return;\n }\n if (establishedAt !== null && Date.now() - establishedAt >= RECONNECT_MIN_UPTIME_MS) {\n reconnectAttempt = 0;\n }\n closeConnection(connection, establishedAt === null ? \"INITIALIZATION_FAILED\" : reason, { reconnect });\n };\n\n // A close during the factory call aborts before `source` is set, so the abort check below disconnects it.\n const start = () => {\n if (signal.aborted) {\n return;\n }\n\n let created: RemoteSource;\n try {\n created = factory({\n onEventsAvailable: ({ newSyncId, remoteStateHash }) => {\n lastActivityAt = Date.now();\n pull({ remoteSyncId: newSyncId, remoteStateHash, includeSelf: false });\n },\n onClosed: (error) => {\n if (signal.aborted) {\n return;\n }\n if (error !== undefined) {\n console.warn(\"Remote connection closed\", error);\n }\n fail(\"CONNECTION_LOST\");\n },\n });\n } catch (error) {\n console.warn(\"Failed to create remote source\", error);\n fail(\"INITIALIZATION_FAILED\");\n return;\n }\n\n if (signal.aborted) {\n disconnectSource(created);\n return;\n }\n source = created;\n const firstPull = pull();\n initialPull = firstPull;\n void firstPull.then(() => {\n initialPull = null;\n });\n firstPull\n .then(() => push.promise())\n .then(() => push())\n .catch((error) => console.warn(\"Initial sync failed\", error));\n };\n\n const scheduleHeartbeat = () => {\n const ping = source?.ping?.bind(source);\n if (!ping || signal.aborted) {\n return;\n }\n heartbeatTimer = setTimeout(\n async () => {\n if (Date.now() - lastActivityAt >= HEARTBEAT_IDLE_MS) {\n try {\n await request(ping);\n } catch (error) {\n if (!signal.aborted) {\n console.warn(\"Remote did not answer a ping\", error);\n fail(\"CONNECTION_LOST\");\n }\n return;\n }\n }\n scheduleHeartbeat();\n },\n Math.max(0, lastActivityAt + HEARTBEAT_IDLE_MS - Date.now()),\n );\n };\n\n // The promise tells whether the pull reached the end of the remote's events.\n type ActivePull = { promise: Promise<boolean>; requestedSyncId: number | null };\n let activePull: ActivePull | null = null;\n const pull = (request?: {\n remoteSyncId?: number;\n remoteStateHash?: string | null;\n includeSelf?: boolean;\n }): Promise<boolean> => {\n if (!source || signal.aborted) {\n return Promise.resolve(false);\n }\n const activeSource = source;\n\n const remoteSyncId = request?.remoteSyncId;\n\n if (remoteSyncId !== undefined && remoteSyncId <= pullSyncId.current) {\n // We are already caught up to this broadcast, so there is nothing to pull.\n // This is the quiescent moment to verify we have not diverged from the\n // remote (the check is a no-op unless we are exactly aligned: remoteSyncId\n // === pullSyncId.current).\n checkRemoteConsistency(remoteSyncId, request?.remoteStateHash ?? null);\n return Promise.resolve(true);\n }\n\n if (activePull) {\n if (remoteSyncId !== undefined && (!activePull.requestedSyncId || activePull.requestedSyncId < remoteSyncId)) {\n activePull.requestedSyncId = remoteSyncId;\n }\n return activePull.promise;\n }\n\n const nextPull: ActivePull = { promise: Promise.resolve(false), requestedSyncId: null };\n nextPull.promise = pullAllEvents(activeSource, {\n afterSyncId: pullSyncId.current,\n excludeNodeId: request?.includeSelf ? undefined : nodeId,\n })\n .catch((error) => {\n if (!signal.aborted) {\n console.error(\"Error pulling events. Going offline.\", error);\n fail(\"REMOTE_PULL_ERROR\");\n }\n return false;\n })\n .finally(() => {\n if (activePull === nextPull) {\n activePull = null;\n }\n\n const nextTarget = nextPull.requestedSyncId;\n if (nextTarget && nextTarget > pullSyncId.current) {\n pull({ remoteSyncId: nextTarget });\n }\n });\n activePull = nextPull;\n return nextPull.promise;\n };\n\n const pullAllEvents = async (activeSource: RemoteSource, opts: EventsPullRequest) => {\n let hasMore = true;\n let afterSyncId = opts.afterSyncId;\n while (hasMore) {\n const response = await request(() =>\n activeSource.pullEvents({\n ...opts,\n afterSyncId,\n }),\n );\n if (signal.aborted) {\n return false;\n }\n hasMore = response.hasMore;\n afterSyncId = response.nextSyncId;\n\n if (response.events) {\n const { processed } = storage.enqueueRemoteEvents(\n response.events.map((x) => {\n if (x.schema_version > migrator.currentSchemaVersion) {\n eventTarget.dispatchEvent(\"remote-schema-version-mismatch\", {\n remoteSchemaVersion: x.schema_version,\n localSchemaVersion: migrator.currentSchemaVersion,\n });\n if (!remoteState.schemaVersionMismatched) {\n patchRemoteState({ schemaVersionMismatched: true });\n }\n throw new SchemaVersionMismatchError(x.schema_version, migrator.currentSchemaVersion);\n }\n return x;\n }),\n );\n await untilClosed(() => processed);\n if (signal.aborted) {\n return false;\n }\n }\n if (response.nextSyncId <= pullSyncId.current) {\n break;\n }\n if (response.nextSyncId > pullSyncId.current) {\n pullSyncId.current = response.nextSyncId;\n }\n }\n return !hasMore;\n };\n\n const push = ensureSingletonExecution(async () => {\n while (source && !signal.aborted) {\n const activeSource = source;\n const eventsBatch = storage.getEventsBatch({\n status: \"applied\",\n afterSyncId: pushSyncId.current,\n excludeOrigin: \"remote\",\n limit: bufferSize,\n });\n if (eventsBatch.events.length === 0) {\n return true;\n }\n\n let response: EventsPushResponse;\n try {\n response = await request(() =>\n activeSource.pushEvents({\n nodeId,\n events: eventsBatch.events.map((event) => ({\n schema_version: event.schema_version,\n timestamp: event.timestamp,\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n payload: event.payload,\n })),\n }),\n );\n } catch (error) {\n if (!signal.aborted) {\n console.error(\"Error pushing events. Going offline.\", error);\n fail(\"REMOTE_PUSH_ERROR\");\n }\n return false;\n }\n\n if (signal.aborted) {\n return false;\n }\n\n if (!response.ok) {\n // The remote would reject the same batch again, so only an explicit sync retries it.\n console.error(\"Remote rejected pushed events. Going offline.\");\n fail(\"REMOTE_PUSH_ERROR\", { reconnect: false });\n return false;\n }\n\n pushSyncId.current = eventsBatch.nextSyncId;\n\n // Fast-forward the pull cursor: the remote assigns sync ids for the pushed\n // events synchronously, so (beforeSyncId, afterSyncId] contains only this\n // node's own events. If we are caught up to at least beforeSyncId, the skipped\n // range (pullSyncId, afterSyncId] contains only our own events, so there is\n // nothing to pull up to afterSyncId.\n if (\n response.beforeSyncId !== undefined &&\n response.afterSyncId !== undefined &&\n response.beforeSyncId <= pullSyncId.current &&\n response.afterSyncId > pullSyncId.current\n ) {\n pullSyncId.current = response.afterSyncId;\n }\n if (!eventsBatch.hasMore) {\n return true;\n }\n }\n return false;\n });\n\n // Waits out in-flight operations first: they may have started before the caller's request.\n const sync = async () => {\n await activePull?.promise;\n await pull();\n await push.promise();\n await push();\n };\n\n // Records each step in `progress` as it finishes, so a caller that stops waiting sees how far it got.\n // While the connection's first pull is running nothing has been pulled yet, so that pull is recent\n // enough and another one would only cost a round trip.\n const catchUp = async (progress: SyncProgress) => {\n if (initialPull) {\n progress.pulled = await initialPull;\n } else {\n await activePull?.promise;\n progress.pulled = await pull();\n }\n await push.promise();\n progress.pushed = await push();\n };\n\n const close = () => {\n controller.abort();\n clearTimeout(heartbeatTimer);\n ready.resolve();\n if (source) {\n disconnectSource(source);\n }\n };\n\n const connection = { ready: ready.promise, start, close, push, sync, catchUp };\n return connection;\n };\n\n const connect = (): Promise<void> => {\n if (disposed) {\n return Promise.resolve();\n }\n if (current) {\n return current.ready;\n }\n if (!remoteFactory) {\n console.warn(\"Remote source factory not provided. Going offline.\");\n patchRemoteState({ type: \"offline\", reason: \"NOT_INITIALIZED\" });\n return Promise.resolve();\n }\n\n cancelReconnect();\n wantsOnline = true;\n const connection = createConnection(remoteFactory);\n current = connection;\n patchRemoteState({ type: \"connecting\" });\n connection.start();\n return connection.ready;\n };\n\n const goOnline = () => {\n reconnectAttempt = 0;\n return connect();\n };\n\n // Unlike goOffline, leaves syncWithRemote free to reconnect, but does not retry on its own: the caller\n // knows the network is gone and calls syncWithRemote once it is back.\n const dropConnection = (reason: OfflineReason) => {\n cancelReconnect();\n if (current) {\n closeConnection(current, reason);\n } else if (remoteState.type === \"connecting\") {\n patchRemoteState({ type: \"offline\", reason });\n }\n };\n\n const goOffline = async (reason: OfflineReason) => {\n wantsOnline = false;\n dropConnection(reason);\n };\n\n // Reconnects right away, skipping any backoff, unless goOffline or a schema mismatch took us offline.\n const syncWithRemote = async () => {\n if (current) {\n await current.sync();\n } else if (wantsOnline && !remoteState.schemaVersionMismatched) {\n await goOnline();\n }\n };\n\n // Reconnects like syncWithRemote. Never rejects: a failed sync shows up in the result and in the state.\n // `prepare` runs first, within the same timeout, for storage work that makes local events ready to push.\n const catchUp = async (\n { timeoutMs = CATCH_UP_TIMEOUT_MS }: CatchUpOptions = {},\n prepare?: () => Promise<void>,\n ): Promise<CatchUpResult> => {\n const progress: SyncProgress = { pulled: false, pushed: false };\n const run = async () => {\n await prepare?.();\n if (!current && wantsOnline && !remoteState.schemaVersionMismatched) {\n void goOnline();\n }\n await current?.catchUp(progress);\n };\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<boolean>((resolve) => {\n timer = setTimeout(() => resolve(true), timeoutMs);\n });\n const finished = run().then(\n () => false,\n (error) => {\n console.warn(\"Failed to catch up with the remote\", error);\n return false;\n },\n );\n const timedOut = await Promise.race([finished, timeout]);\n clearTimeout(timer);\n return { ...progress, timedOut };\n };\n\n // De-sync detection: when we are exactly caught up to the remote's broadcast\n // sync id and fully quiescent, we hold the same events as the remote, so our\n // materialized CRDT state and its hash must match. A mismatch means the nodes\n // have diverged.\n const checkRemoteConsistency = (remoteSyncId: number, remoteStateHash: string | null) => {\n // A remote that does not report a state hash gives us nothing to compare against.\n if (remoteStateHash == null) {\n return;\n }\n\n // Only meaningful when we are exactly caught up: if we are behind we still\n // need to pull; if we are ahead our state covers events the remote hash\n // does not.\n if (remoteSyncId !== pullSyncId.current) {\n return;\n }\n\n // Quiescence: the hashes only match when nothing is left to apply locally and\n // no local applied events are still waiting to be pushed (those are in our\n // state but the remote has not seen them yet).\n if (!storage.checkIsQuiescent(pushSyncId.current)) {\n return;\n }\n\n const localStateHash = storage.getStateHash();\n if (localStateHash === null || localStateHash === remoteStateHash) {\n return;\n }\n\n // Hashes for different schema versions cover different columns, e.g. while\n // the remote has been migrated by a deploy that this client has not loaded yet.\n const local = parseStateHash(localStateHash);\n const remote = parseStateHash(remoteStateHash);\n if (!local || !remote || local.schemaVersion !== remote.schemaVersion) {\n return;\n }\n\n eventTarget.dispatchEvent(\"de-sync-detected\", { reason: \"CHECKSUM_MISMATCH\" });\n console.warn(\n `[sqlite-sync] De-sync detected at syncId ${remoteSyncId}: local state hash ${localStateHash} != remote ${remoteStateHash}. Local and remote have diverged despite being caught up.`,\n );\n if (!remoteState.deSynced) {\n patchRemoteState({ deSynced: true });\n }\n };\n\n const eventsAppliedSubscription = storage.addEventListener(\"events-applied\", () => {\n current?.push();\n });\n\n const remoteEventApplyFailedSubscription = storage.addEventListener(\"remote-event-apply-failed\", () => {\n eventTarget.dispatchEvent(\"de-sync-detected\", { reason: \"ERROR_APPLYING_REMOTE_EVENT\" });\n if (!remoteState.deSynced) {\n patchRemoteState({ deSynced: true });\n }\n });\n\n const getState = (): WorkerState => ({\n remoteState: remoteState.type,\n deSynced: remoteState.deSynced,\n schemaVersionMismatched: remoteState.schemaVersionMismatched,\n });\n\n const dispose = async () => {\n disposed = true;\n eventsAppliedSubscription.unsubscribe();\n remoteEventApplyFailedSubscription.unsubscribe();\n await goOffline(\"DISCONNECTED\");\n };\n\n return {\n goOnline,\n goOffline,\n dropConnection,\n syncWithRemote,\n catchUp,\n getState,\n dispose,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n};\n"],"mappings":"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"}
@@ -1111,6 +1111,18 @@ type EventsPushResponse = {
1111
1111
  /** Remote sync_id right after the pushed events were enqueued. */
1112
1112
  afterSyncId?: number;
1113
1113
  };
1114
+ type CatchUpOptions = {
1115
+ /** How long to wait before giving up. Defaults to 10 seconds. */
1116
+ timeoutMs?: number;
1117
+ };
1118
+ type CatchUpResult = {
1119
+ /** The pull reached the end of what the remote had, and those events were processed locally. */
1120
+ pulled: boolean;
1121
+ /** The remote accepted every local event that was waiting to be pushed. */
1122
+ pushed: boolean;
1123
+ /** The timeout ran out first. The sync keeps going in the background. */
1124
+ timedOut: boolean;
1125
+ };
1114
1126
  type CrdtSyncRemoteSource = ReturnType<typeof createCrdtSyncRemoteSource>;
1115
1127
  type EventsAvailable = {
1116
1128
  newSyncId: number;
@@ -1156,6 +1168,7 @@ declare const createCrdtSyncRemoteSource: ({ bufferSize, storage, migrator, pull
1156
1168
  goOffline: (reason: OfflineReason) => Promise<void>;
1157
1169
  dropConnection: (reason: OfflineReason) => void;
1158
1170
  syncWithRemote: () => Promise<void>;
1171
+ catchUp: ({ timeoutMs }?: CatchUpOptions, prepare?: () => Promise<void>) => Promise<CatchUpResult>;
1159
1172
  getState: () => WorkerState;
1160
1173
  dispose: () => Promise<void>;
1161
1174
  addEventListener: <K extends "state-changed" | "de-sync-detected" | "remote-schema-version-mismatch">(type: K, listener: (event: TypedEvent<{
@@ -1262,6 +1275,12 @@ declare function createSyncedDb<Database, Props = undefined>(options: SyncedDbOp
1262
1275
  goOnline: () => Promise<void>;
1263
1276
  goOffline: () => Promise<void>;
1264
1277
  sync: () => void;
1278
+ /**
1279
+ * Pulls what the remote has now and pushes pending local events, and resolves once that
1280
+ * is done or `timeoutMs` (10 seconds by default) runs out. Best effort: it never rejects,
1281
+ * does not wait for other clients to stop writing, and the result tells how far it got.
1282
+ */
1283
+ catchUp: ({ timeoutMs }?: CatchUpOptions) => Promise<CatchUpResult>;
1265
1284
  };
1266
1285
  /**
1267
1286
  * Ask the elected worker to broadcast a page reload to all tabs for this dbId.
@@ -1332,5 +1351,5 @@ declare function loadSqliteWasm(): Promise<Sqlite3Static>;
1332
1351
  declare function createWasmMemoryDriver(): Promise<SqliteDriver>;
1333
1352
  declare function createWasmSqliteDriver(sqlite3: Sqlite3Static, db: Database): SqliteDriver;
1334
1353
  //#endregion
1335
- export { parametersAreEqual as $, startPerformanceLogger as $t, WorkerState as A, MigrationsDb as An, tryCatch as At, verifySyncSchemaWithDriver as B, CrdtUpdateLogItem as Bn, KyselyStatementFactory as Bt, EventsPullRequest as C, TableBuilder as Cn, TypedEvent as Ct, EventsPullResponse as D, t as Dn, generateId as Dt, createCrdtSyncRemoteSource as E, WriteOrigin as En, createTypedEventTarget as Et, PendingCrdtEvent as F, createStoredValue as Fn, createSQLiteKvStore as Ft, DefinedSyncSchema as G, compileKyselyStatement as Gt, NewCrdtEventValidationResult as H, PersistedCrdtEvent as Hn, QueryBuilderOutput as Ht, createCrdtApplyFunction as I, CRDT_EVENT_NO_OP_PAYLOAD as In, ExecuteParams as It, ClientDatasetRejectionReason as J, SqliteDriverRows as Jt, defineSyncSchema as K, SqliteDriver as Kt, createSQLiteCrdtApplyFunction as L, CrdtEventOrigin as Ln, ExecuteResult as Lt, CrdtStorageMutator as M, createMigrations as Mn, KvStoreItem as Mt, SnapshotOptions as N, createMigrator as Nn, STATE_HASH_KV_KEY as Nt, WorkerConfig as O, MigratableEvent as On, jsonSafeParse as Ot, createCrdtStorageMutator as P, StoredValue as Pn, createKvStoreTableQuery as Pt, SharedLiveQuerySnapshot as Q, Logger as Qt, SchemaVerificationIssue as R, CrdtEventStatus as Rn, InternalSQLiteWrapper as Rt, CreateRemoteSourceFactory as S, SyncSchemaTables as Sn, TypedBroadcastChannel as St, EventsPushResponse as T, TableOptions as Tn, createDeferredPromise as Tt, validateNewCrdtEvent as U, isNoOpCrdtEventPayload as Un, SQLiteDbWrapper as Ut, CrdtEventValidationError as V, CrdtUpdateLogPayload as Vn, PreparedStatement as Vt, DefineSyncSchemaConfig as W, dummyKysely as Wn, SQLiteTransactionWrapper as Wt, SQLiteReactiveDb as X, SqliteDriverValue as Xt, admitClientEvents as Y, SqliteDriverStatement as Yt, SharedLiveQuery as Z, LogLevel as Zt, crdtViewColumnsFromTable as _, ColumnMeta as _n, baseSystemMigrations as _t, createSQLiteReactiveDb as a, HLCCounter as an, createImportData as at, makeCrdtTable as b, PayloadValidationResult as bn, DeferredPromise as bt, ResetStore as c, serializeHLC as cn, GetEventsBatch as ct, createSyncedDb as d, SyncDbSchema as dn, createCrdtStorage as dt, ColumnMetadata as en, xxhash as et, SyncedDbDatabase as f, AiAccess as fn, SystemDbConfig as ft, CRDT_CHANGE_INTENTS_TABLE as g, ColumnKind as gn, applyWorkerDbSchema as gt, UnsafeSyncedDbSqlExecutor as h, ColumnBuilder as hn, applyMemoryDbSchema as ht, verifySyncSchema as i, HLC as in, createExportData as it, createCrdtSyncProducer as j, SyncDbMigrator as jn, tryCatchAsync as jt, WorkerNotificationMessage as k, Migrations as kn, quoteId as kt, SyncedDb as l, CrdtTableConfig as ln, OwnCrdtEvent as lt, SyncedDbTransaction as m, AnyTableBuilder as mn, SystemMigrationContext as mt, createWasmSqliteDriver as n, TableMetadata as nn, ImportDataResult as nt, RESET_REQUEST_TTL_MS as o, compareHLC as on, CrdtChangeIntent as ot, SyncedDbSqlExecutor as p, AnyColumnBuilder as pn, SystemMigration as pt, AdmitClientEventsResult as q, SqliteDriverHooks as qt, loadSqliteWasm as r, introspectDb as rn, SyncedDbExport as rt, ResetRequest as s, deserializeHLC as sn, CrdtStorage as st, createWasmMemoryDriver as t, DatabaseIntrospection as tn, ImportDataOptions as tt, SyncedDbOptions as u, ReadonlyTable as un, OwnCrdtSnapshot as ut, createCrdtViewStatements as v, ExportImportMode as vn, createSystemDbConfig as vt, EventsPushRequest as w, TableColumns as wn, TypedEventTarget as wt, CrdtSyncRemoteSource as x, SqliteStorageType as xn, DistributiveOmit as xt, drainCrdtChangeIntents as y, InferRow as yn, runSystemMigrations as yt, formatSchemaVerificationIssues as z, CrdtEventType as zn, KyselyQueryFactory as zt };
1336
- //# sourceMappingURL=index-BgkTUdlX.d.ts.map
1354
+ export { SharedLiveQuery as $, LogLevel as $t, WorkerConfig as A, MigratableEvent as An, jsonSafeParse as At, SchemaVerificationIssue as B, CrdtEventStatus as Bn, InternalSQLiteWrapper as Bt, CrdtSyncRemoteSource as C, SqliteStorageType as Cn, DistributiveOmit as Ct, EventsPushResponse as D, TableOptions as Dn, createDeferredPromise as Dt, EventsPushRequest as E, TableColumns as En, TypedEventTarget as Et, SnapshotOptions as F, createMigrator as Fn, STATE_HASH_KV_KEY as Ft, validateNewCrdtEvent as G, isNoOpCrdtEventPayload as Gn, SQLiteDbWrapper as Gt, verifySyncSchemaWithDriver as H, CrdtUpdateLogItem as Hn, KyselyStatementFactory as Ht, createCrdtStorageMutator as I, StoredValue as In, createKvStoreTableQuery as It, defineSyncSchema as J, SqliteDriver as Jt, DefineSyncSchemaConfig as K, dummyKysely as Kn, SQLiteTransactionWrapper as Kt, PendingCrdtEvent as L, createStoredValue as Ln, createSQLiteKvStore as Lt, WorkerState as M, MigrationsDb as Mn, tryCatch as Mt, createCrdtSyncProducer as N, SyncDbMigrator as Nn, tryCatchAsync as Nt, createCrdtSyncRemoteSource as O, WriteOrigin as On, createTypedEventTarget as Ot, CrdtStorageMutator as P, createMigrations as Pn, KvStoreItem as Pt, SQLiteReactiveDb as Q, SqliteDriverValue as Qt, createCrdtApplyFunction as R, CRDT_EVENT_NO_OP_PAYLOAD as Rn, ExecuteParams as Rt, CatchUpResult as S, PayloadValidationResult as Sn, DeferredPromise as St, EventsPullRequest as T, TableBuilder as Tn, TypedEvent as Tt, CrdtEventValidationError as U, CrdtUpdateLogPayload as Un, PreparedStatement as Ut, formatSchemaVerificationIssues as V, CrdtEventType as Vn, KyselyQueryFactory as Vt, NewCrdtEventValidationResult as W, PersistedCrdtEvent as Wn, QueryBuilderOutput as Wt, ClientDatasetRejectionReason as X, SqliteDriverRows as Xt, AdmitClientEventsResult as Y, SqliteDriverHooks as Yt, admitClientEvents as Z, SqliteDriverStatement as Zt, crdtViewColumnsFromTable as _, ColumnBuilder as _n, applyMemoryDbSchema as _t, createSQLiteReactiveDb as a, introspectDb as an, SyncedDbExport as at, makeCrdtTable as b, ExportImportMode as bn, createSystemDbConfig as bt, ResetStore as c, compareHLC as cn, CrdtChangeIntent as ct, createSyncedDb as d, CrdtTableConfig as dn, OwnCrdtEvent as dt, Logger as en, SharedLiveQuerySnapshot as et, SyncedDbDatabase as f, ReadonlyTable as fn, OwnCrdtSnapshot as ft, CRDT_CHANGE_INTENTS_TABLE as g, AnyTableBuilder as gn, SystemMigrationContext as gt, UnsafeSyncedDbSqlExecutor as h, AnyColumnBuilder as hn, SystemMigration as ht, verifySyncSchema as i, TableMetadata as in, ImportDataResult as it, WorkerNotificationMessage as j, Migrations as jn, quoteId as jt, EventsPullResponse as k, t as kn, generateId as kt, SyncedDb as l, deserializeHLC as ln, CrdtStorage as lt, SyncedDbTransaction as m, AiAccess as mn, SystemDbConfig as mt, createWasmSqliteDriver as n, ColumnMetadata as nn, xxhash as nt, RESET_REQUEST_TTL_MS as o, HLC as on, createExportData as ot, SyncedDbSqlExecutor as p, SyncDbSchema as pn, createCrdtStorage as pt, DefinedSyncSchema as q, compileKyselyStatement as qt, loadSqliteWasm as r, DatabaseIntrospection as rn, ImportDataOptions as rt, ResetRequest as s, HLCCounter as sn, createImportData as st, createWasmMemoryDriver as t, startPerformanceLogger as tn, parametersAreEqual as tt, SyncedDbOptions as u, serializeHLC as un, GetEventsBatch as ut, createCrdtViewStatements as v, ColumnKind as vn, applyWorkerDbSchema as vt, CreateRemoteSourceFactory as w, SyncSchemaTables as wn, TypedBroadcastChannel as wt, CatchUpOptions as x, InferRow as xn, runSystemMigrations as xt, drainCrdtChangeIntents as y, ColumnMeta as yn, baseSystemMigrations as yt, createSQLiteCrdtApplyFunction as z, CrdtEventOrigin as zn, ExecuteResult as zt };
1355
+ //# sourceMappingURL=index-C1E8xMH1.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import "./system-schema-B8hbFl7o.js";
2
- import "./crdt-sync-producer-BQkuzCBM.js";
3
- import "./crdt-sync-remote-source-yeTEkOjH.js";
2
+ import "./crdt-sync-producer-CUXiu4X3.js";
3
+ import "./crdt-sync-remote-source-CH2bsr-x.js";
4
4
  import "./sqlite-reactive-db-DLTvpGzO.js";
5
5
  import { n as buildWriteOriginByName } from "./admit-client-events-BDrJw4SK.js";
6
6
  //#region src/schema/define-sync-schema.ts
@@ -273,4 +273,4 @@ function createCrdtStorageMutator({ storage }) {
273
273
  //#endregion
274
274
  export { defineSyncSchema as a, t as i, ColumnBuilder as n, TableBuilder as r, createCrdtStorageMutator as t };
275
275
 
276
- //# sourceMappingURL=index.common-DIN0mZxR.js.map
276
+ //# sourceMappingURL=index.common-c-Je9hVa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.common-DIN0mZxR.js","names":[],"sources":["../src/schema/define-sync-schema.ts","../src/schema/table-builder.ts","../src/sqlite-crdt/crdt-storage-mutator.ts"],"sourcesContent":["import type { Migrations } from \"../migrations/migrator\";\nimport type { CrdtTableConfig, ReadonlyTable, SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport { buildWriteOriginByName } from \"./admit-client-events\";\nimport type {\n AnyTableBuilder,\n InferRow,\n SyncSchemaTables,\n TableBuilder,\n TableColumns,\n WriteOrigin,\n} from \"./table-builder\";\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// Names sqlite-sync uses for its own tables/views, so a user schema can't shadow them.\nconst RESERVED_TABLE_NAMES = new Set<string>([\n \"change_history\",\n \"crdt_events\",\n \"worker.crdt_events\",\n \"persisted_crdt_events\",\n \"crdt_update_log\",\n \"crdt_change_intents\",\n \"worker.kv\",\n \"sync_kv\",\n]);\n\ntype RowOf<Table extends AnyTableBuilder> =\n Table extends TableBuilder<infer Cols extends TableColumns, any, WriteOrigin> ? InferRow<Cols> : never;\n\n// Falls back to the \"_\" convention unless ~baseName is a string *literal* — inference can\n// resolve an omitted override to its constraint (string | undefined) rather than undefined.\ntype BaseNameOf<CrdtName, Table extends AnyTableBuilder> = string extends Table[\"~baseName\"]\n ? `_${CrdtName & string}`\n : [Table[\"~baseName\"]] extends [string]\n ? Table[\"~baseName\"]\n : `_${CrdtName & string}`;\n\ntype ClientSchemaOf<Tables extends SyncSchemaTables> = Simplify<\n {\n [K in keyof Tables]: Tables[K][\"~writeOrigin\"] extends \"server\"\n ? ReadonlyTable<RowOf<Tables[K]>>\n : RowOf<Tables[K]>;\n } & {\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;\n }\n>;\n\ntype ServerSchemaOf<Tables extends SyncSchemaTables> = Simplify<\n { [K in keyof Tables]: RowOf<Tables[K]> } & {\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;\n }\n>;\n\ntype MutationsSchemaOf<Tables extends SyncSchemaTables> = Simplify<{\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: RowOf<Tables[K]>;\n}>;\n\nexport interface DefinedSyncSchema<Tables extends SyncSchemaTables>\n extends SyncDbSchema<ClientSchemaOf<Tables>, ServerSchemaOf<Tables>, MutationsSchemaOf<Tables>> {\n /** The table builders, for type extraction (`typeof schema.tables.item.$row`) and runtime metadata. */\n tables: Tables;\n /** Write origin keyed by crdt and base table name. */\n writeOriginByName: ReadonlyMap<string, WriteOrigin>;\n}\n\nexport type DefineSyncSchemaConfig<Tables extends SyncSchemaTables> = {\n tables: Tables;\n /** Full migration history (including the initial createTable version), built with `createMigrations`. */\n migrations: Migrations;\n};\n\n/**\n * Define a sync database schema from `t.table()` builders.\n *\n * Record keys are the crdt (view) table names; base table names default to the key\n * prefixed with \"_\", overridable per table via `t.table(cols, { baseName })`.\n */\nexport function defineSyncSchema<Tables extends SyncSchemaTables>({\n tables,\n migrations,\n}: DefineSyncSchemaConfig<Tables>): DefinedSyncSchema<Tables> {\n const tablesConfig: CrdtTableConfig[] = Object.entries(tables).map(([crdtTableName, table]) => ({\n crdtTableName,\n baseTableName: table.baseName ?? `_${crdtTableName}`,\n ...(table.exportImport === \"ignore\" ? { exportImport: \"ignore\" } : {}),\n }));\n\n const seenNames = new Set<string>();\n for (const { crdtTableName, baseTableName } of tablesConfig) {\n for (const name of [crdtTableName, baseTableName]) {\n const normalizedName = name.toLowerCase();\n if (RESERVED_TABLE_NAMES.has(normalizedName)) {\n throw new Error(`Table name \"${name}\" is reserved by sqlite-sync and cannot be used in a sync schema`);\n }\n if (seenNames.has(normalizedName)) {\n throw new Error(`Duplicate table name \"${name}\" in sync schema`);\n }\n seenNames.add(normalizedName);\n }\n }\n\n const writeOriginByName = buildWriteOriginByName({ tables, tablesConfig });\n\n return {\n tables,\n get tablesConfig() {\n return tablesConfig;\n },\n get writeOriginByName() {\n return writeOriginByName;\n },\n get migrations() {\n return migrations;\n },\n get \"~clientSchema\"(): never {\n throw new Error(\"~clientSchema is type-only and cannot be accessed at runtime\");\n },\n get \"~serverSchema\"(): never {\n throw new Error(\"~serverSchema is type-only and cannot be accessed at runtime\");\n },\n get \"~mutationsSchema\"(): never {\n throw new Error(\"~mutationsSchema is type-only and cannot be accessed at runtime\");\n },\n };\n}\n","export type ColumnKind = \"text\" | \"integer\" | \"real\" | \"boolean\" | \"enum\";\n\nexport type SqliteStorageType = \"text\" | \"integer\" | \"real\";\n\n/**\n * What `@sqlite-sync/ai` may do with a table.\n * `read-only`: documented and queryable; mutations rejected.\n * `hidden`: omitted from the schema doc; reads rejected.\n */\nexport type AiAccess = \"read-write\" | \"read-only\" | \"hidden\";\n\n/**\n * Who may author CRDT events for a table.\n * `\"any\"` (default): every replica. `\"server\"`: client pushes drop these events.\n */\nexport type WriteOrigin = \"any\" | \"server\";\n\n/** Whether backup and restore include a table. */\nexport type ExportImportMode = \"include\" | \"ignore\";\n\nexport type ColumnMeta = {\n kind: ColumnKind;\n /** Storage type used in generated DDL. Booleans store as integer 0/1. */\n sqlType: SqliteStorageType;\n nullable: boolean;\n hasDefault: boolean;\n /** Declared default in JS form (booleans as booleans). */\n defaultValue?: unknown;\n /** Allowed values for `enum` columns. */\n enumValues?: readonly string[];\n description?: string;\n};\n\nexport class ColumnBuilder<Value, HasDefault extends boolean = false> {\n declare readonly \"~types\": { value: Value; hasDefault: HasDefault };\n\n constructor(readonly meta: ColumnMeta) {}\n\n /** Allow SQL NULL; widens the value type with `null`. */\n nullable(): ColumnBuilder<Value | null, HasDefault> {\n return new ColumnBuilder({ ...this.meta, nullable: true });\n }\n\n /** Declare a column default. Creates may omit the column; it must match the migrated DDL default. */\n default(value: Value): ColumnBuilder<Value, true> {\n return new ColumnBuilder({ ...this.meta, hasDefault: true, defaultValue: value });\n }\n\n /** Narrow the TypeScript value type without changing storage, e.g. `t.text().$type<\"movie\" | \"tv\">()`. */\n $type<Narrowed extends Value>(): ColumnBuilder<Narrowed, HasDefault> {\n return this as unknown as ColumnBuilder<Narrowed, HasDefault>;\n }\n\n /** Human/AI-readable description, surfaced in generated schema docs. */\n describe(description: string): ColumnBuilder<Value, HasDefault> {\n return new ColumnBuilder({ ...this.meta, description });\n }\n}\n\nexport type AnyColumnBuilder = ColumnBuilder<any, boolean>;\n\nexport type TableColumns = Record<string, AnyColumnBuilder>;\n\nexport type AnyTableBuilder = TableBuilder<any, any, WriteOrigin>;\n\nexport type SyncSchemaTables = Record<string, AnyTableBuilder>;\n\nexport type TableOptions<\n BaseName extends string | undefined = string | undefined,\n Writes extends WriteOrigin = \"any\",\n> = {\n /** Override the materialized base table name (defaults to the crdt table name prefixed with \"_\"). */\n baseName?: BaseName;\n description?: string;\n /** Defaults to `\"read-write\"`. See {@link AiAccess}. */\n ai?: AiAccess;\n /** Defaults to `\"any\"`. See {@link WriteOrigin}. */\n writes?: Writes;\n /** Defaults to `\"include\"`. Ignored tables are omitted from export and skipped on import. */\n exportImport?: ExportImportMode;\n};\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\ntype ColumnValue<Column> = Column extends ColumnBuilder<infer Value, boolean> ? Value : never;\n\nexport type InferRow<Cols extends TableColumns> = Simplify<\n { id: string } & { [K in keyof Cols]: ColumnValue<Cols[K]> } & { tombstone?: boolean }\n>;\n\nexport type PayloadValidationResult = { success: true } | { success: false; errors: string[] };\n\nconst RESERVED_COLUMNS = [\"id\", \"tombstone\"] as const;\n\nconst idColumnMeta: ColumnMeta = {\n kind: \"text\",\n sqlType: \"text\",\n nullable: false,\n hasDefault: false,\n description: \"Unique immutable item id\",\n};\n\nconst tombstoneColumnMeta: ColumnMeta = {\n kind: \"boolean\",\n sqlType: \"integer\",\n nullable: false,\n hasDefault: true,\n defaultValue: false,\n description: \"Soft-delete marker\",\n};\n\nexport class TableBuilder<\n Cols extends TableColumns,\n BaseName extends string | undefined = undefined,\n Writes extends WriteOrigin = \"any\",\n> {\n declare readonly \"~baseName\": BaseName;\n declare readonly \"~writeOrigin\": Writes;\n\n /** All columns in DDL order, including the auto-injected `id` and `tombstone`. */\n readonly columns: Record<string, ColumnMeta>;\n readonly baseName: BaseName;\n readonly description: string | undefined;\n /** Resolved {@link AiAccess}; defaults to `\"read-write\"`. */\n readonly aiAccess: AiAccess;\n /** Resolved {@link WriteOrigin}; defaults to `\"any\"`. */\n readonly writeOrigin: Writes;\n /** Resolved {@link ExportImportMode}; defaults to `\"include\"`. */\n readonly exportImport: ExportImportMode;\n\n constructor(\n readonly userColumns: Cols,\n options?: TableOptions<BaseName, Writes>,\n ) {\n for (const reserved of RESERVED_COLUMNS) {\n if (reserved in userColumns) {\n throw new Error(`Column \"${reserved}\" is added automatically to every synced table and cannot be declared`);\n }\n }\n\n this.columns = {\n id: idColumnMeta,\n ...Object.fromEntries(Object.entries(userColumns).map(([name, column]) => [name, column.meta])),\n tombstone: tombstoneColumnMeta,\n };\n this.baseName = options?.baseName as BaseName;\n this.description = options?.description;\n this.aiAccess = options?.ai ?? \"read-write\";\n this.writeOrigin = (options?.writes ?? \"any\") as Writes;\n this.exportImport = options?.exportImport ?? \"include\";\n }\n\n describe(description: string): TableBuilder<Cols, BaseName, Writes> {\n return new TableBuilder<Cols, BaseName, Writes>(this.userColumns, {\n baseName: this.baseName,\n description,\n ai: this.aiAccess,\n writes: this.writeOrigin,\n exportImport: this.exportImport,\n });\n }\n\n /** Type-only: the row shape returned by queries. Do not access at runtime. */\n get $row(): InferRow<Cols> {\n return null as never;\n }\n\n /**\n * Validate a CRDT event payload against the declared columns.\n * `item-created` payloads must contain every column without a default (nullable columns may be omitted)\n * and their `tombstone` value is ignored, since applying a create always sets it to 0;\n * `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.\n */\n validatePayload(\n payload: Record<string, unknown>,\n options?: { event?: \"item-created\" | \"item-updated\" },\n ): PayloadValidationResult {\n const event = options?.event ?? \"item-created\";\n const errors: string[] = [];\n\n for (const key of Object.keys(payload)) {\n if (!Object.hasOwn(this.columns, key)) {\n errors.push(`Unknown column \"${key}\"`);\n }\n }\n\n if (event === \"item-updated\") {\n if (\"id\" in payload) {\n errors.push(`Column \"id\" is immutable and cannot appear in an update payload`);\n }\n if (payload.tombstone === true || payload.tombstone === 1) {\n errors.push(`Column \"tombstone\" cannot be set to true/1 in an update payload; use an item-deleted event`);\n }\n }\n\n if (event === \"item-created\") {\n for (const [name, meta] of Object.entries(this.columns)) {\n if (Object.hasOwn(payload, name) || name === \"tombstone\" || meta.nullable || meta.hasDefault) {\n continue;\n }\n errors.push(`Missing required column \"${name}\"`);\n }\n }\n\n for (const [name, value] of Object.entries(payload)) {\n const meta = Object.hasOwn(this.columns, name) ? this.columns[name] : undefined;\n if (!meta || (event === \"item-updated\" && name === \"id\") || (event === \"item-created\" && name === \"tombstone\")) {\n continue;\n }\n const error = validateValue(name, value, meta);\n if (error) {\n errors.push(error);\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n }\n}\n\nfunction describeValue(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n return typeof value;\n}\n\nfunction validateValue(name: string, value: unknown, meta: ColumnMeta): string | null {\n if (value === null) {\n return meta.nullable ? null : `Column \"${name}\" is not nullable, got null`;\n }\n\n switch (meta.kind) {\n case \"text\":\n return typeof value === \"string\" ? null : `Column \"${name}\" expects text, got ${describeValue(value)}`;\n case \"integer\":\n return typeof value === \"number\" && Number.isInteger(value)\n ? null\n : `Column \"${name}\" expects an integer, got ${describeValue(value)}`;\n case \"real\":\n return typeof value === \"number\" && Number.isFinite(value)\n ? null\n : `Column \"${name}\" expects a number, got ${describeValue(value)}`;\n case \"boolean\":\n return typeof value === \"boolean\" || value === 0 || value === 1\n ? null\n : `Column \"${name}\" expects a boolean (true/false or 0/1), got ${describeValue(value)}`;\n case \"enum\": {\n if (typeof value !== \"string\") {\n return `Column \"${name}\" expects one of ${formatEnumValues(meta)}, got ${describeValue(value)}`;\n }\n return meta.enumValues?.includes(value)\n ? null\n : `Column \"${name}\" expects one of ${formatEnumValues(meta)}, got \"${value}\"`;\n }\n }\n}\n\nfunction formatEnumValues(meta: ColumnMeta): string {\n return (meta.enumValues ?? []).map((value) => `\"${value}\"`).join(\" | \");\n}\n\nfunction column<Value>(kind: ColumnKind, sqlType: SqliteStorageType): ColumnBuilder<Value> {\n return new ColumnBuilder<Value>({ kind, sqlType, nullable: false, hasDefault: false });\n}\n\nexport const t = {\n text: () => column<string>(\"text\", \"text\"),\n integer: () => column<number>(\"integer\", \"integer\"),\n real: () => column<number>(\"real\", \"real\"),\n /** Stored as INTEGER 0/1; always bind booleans as 0/1 when writing raw SQL. */\n boolean: () => column<boolean>(\"boolean\", \"integer\"),\n /**\n * Union of string literals stored as TEXT, e.g. `t.enum([\"movie\", \"tv\"])`. Unlike `$type`, the\n * allowed values are kept as runtime metadata, so payloads are validated against them.\n * For an open-ended set, use `t.text().$type<\"idle\" | (string & {})>()` instead.\n */\n enum: <const Values extends readonly [string, ...string[]]>(values: Values) =>\n new ColumnBuilder<Values[number]>({\n kind: \"enum\",\n sqlType: \"text\",\n nullable: false,\n hasDefault: false,\n enumValues: values,\n }),\n table: <\n Cols extends TableColumns,\n const BaseName extends string | undefined = undefined,\n const Writes extends WriteOrigin = \"any\",\n >(\n columns: Cols,\n options?: TableOptions<BaseName, Writes>,\n ) => new TableBuilder(columns, options),\n};\n","import type { CrdtStorage, OwnCrdtEvent } from \"./crdt-storage\";\n\nexport type CrdtStorageMutator<Database> = ReturnType<typeof createCrdtStorageMutator<Database>>;\n\ntype CommitEventOptions<Database, Table extends keyof Database & string> =\n | {\n type: \"item-created\";\n dataset: Table;\n item_id: string;\n payload: CreateEventPayload<Database, Table>;\n }\n | {\n type: \"item-updated\";\n dataset: Table;\n item_id: string;\n payload: UpdateEventPayload<Database, Table>;\n }\n | {\n type: \"item-deleted\";\n dataset: Table;\n item_id: string;\n };\n\ntype AnyCommitEventOptions<Database> = {\n [Table in keyof Database & string]: CommitEventOptions<Database, Table>;\n}[keyof Database & string];\n\ntype CreateEventPayload<Database, Table extends keyof Database> = Omit<Database[Table], \"tombstone\">;\ntype UpdateEventPayload<Database, Table extends keyof Database> = Omit<Partial<Database[Table]>, \"id\" | \"tombstone\">;\n\nexport type SnapshotOptions<Database, Table extends keyof Database & string> = {\n dataset: Table;\n id: string;\n patch: UpdateEventPayload<Database, Table>;\n};\n\nexport function createCrdtStorageMutator<Database>({ storage }: { storage: CrdtStorage }) {\n const mapToStorageEvent = (event: CommitEventOptions<Database, keyof Database & string>): OwnCrdtEvent => {\n switch (event.type) {\n case \"item-created\":\n return {\n type: \"item-created\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-updated\":\n return {\n type: \"item-updated\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-deleted\":\n return {\n type: \"item-deleted\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: \"{}\",\n };\n }\n };\n\n const applyEvents = (events: AnyCommitEventOptions<Database>[]) => {\n storage.applyOwnEvents(events.map(mapToStorageEvent));\n };\n\n const applyEvent = <Table extends keyof Database & string>(event: CommitEventOptions<Database, Table>) => {\n storage.applyOwnEvents([mapToStorageEvent(event)]);\n };\n\n const applySnapshot = <Table extends keyof Database & string>(snapshot: SnapshotOptions<Database, Table>) => {\n storage.applyOwnSnapshot({\n dataset: snapshot.dataset,\n item_id: snapshot.id,\n patch: { ...snapshot.patch },\n });\n };\n\n return {\n applyEvent,\n applyEvents,\n applySnapshot,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.common-c-Je9hVa.js","names":[],"sources":["../src/schema/define-sync-schema.ts","../src/schema/table-builder.ts","../src/sqlite-crdt/crdt-storage-mutator.ts"],"sourcesContent":["import type { Migrations } from \"../migrations/migrator\";\nimport type { CrdtTableConfig, ReadonlyTable, SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport { buildWriteOriginByName } from \"./admit-client-events\";\nimport type {\n AnyTableBuilder,\n InferRow,\n SyncSchemaTables,\n TableBuilder,\n TableColumns,\n WriteOrigin,\n} from \"./table-builder\";\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\n// Names sqlite-sync uses for its own tables/views, so a user schema can't shadow them.\nconst RESERVED_TABLE_NAMES = new Set<string>([\n \"change_history\",\n \"crdt_events\",\n \"worker.crdt_events\",\n \"persisted_crdt_events\",\n \"crdt_update_log\",\n \"crdt_change_intents\",\n \"worker.kv\",\n \"sync_kv\",\n]);\n\ntype RowOf<Table extends AnyTableBuilder> =\n Table extends TableBuilder<infer Cols extends TableColumns, any, WriteOrigin> ? InferRow<Cols> : never;\n\n// Falls back to the \"_\" convention unless ~baseName is a string *literal* — inference can\n// resolve an omitted override to its constraint (string | undefined) rather than undefined.\ntype BaseNameOf<CrdtName, Table extends AnyTableBuilder> = string extends Table[\"~baseName\"]\n ? `_${CrdtName & string}`\n : [Table[\"~baseName\"]] extends [string]\n ? Table[\"~baseName\"]\n : `_${CrdtName & string}`;\n\ntype ClientSchemaOf<Tables extends SyncSchemaTables> = Simplify<\n {\n [K in keyof Tables]: Tables[K][\"~writeOrigin\"] extends \"server\"\n ? ReadonlyTable<RowOf<Tables[K]>>\n : RowOf<Tables[K]>;\n } & {\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;\n }\n>;\n\ntype ServerSchemaOf<Tables extends SyncSchemaTables> = Simplify<\n { [K in keyof Tables]: RowOf<Tables[K]> } & {\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;\n }\n>;\n\ntype MutationsSchemaOf<Tables extends SyncSchemaTables> = Simplify<{\n [K in keyof Tables as BaseNameOf<K, Tables[K]>]: RowOf<Tables[K]>;\n}>;\n\nexport interface DefinedSyncSchema<Tables extends SyncSchemaTables>\n extends SyncDbSchema<ClientSchemaOf<Tables>, ServerSchemaOf<Tables>, MutationsSchemaOf<Tables>> {\n /** The table builders, for type extraction (`typeof schema.tables.item.$row`) and runtime metadata. */\n tables: Tables;\n /** Write origin keyed by crdt and base table name. */\n writeOriginByName: ReadonlyMap<string, WriteOrigin>;\n}\n\nexport type DefineSyncSchemaConfig<Tables extends SyncSchemaTables> = {\n tables: Tables;\n /** Full migration history (including the initial createTable version), built with `createMigrations`. */\n migrations: Migrations;\n};\n\n/**\n * Define a sync database schema from `t.table()` builders.\n *\n * Record keys are the crdt (view) table names; base table names default to the key\n * prefixed with \"_\", overridable per table via `t.table(cols, { baseName })`.\n */\nexport function defineSyncSchema<Tables extends SyncSchemaTables>({\n tables,\n migrations,\n}: DefineSyncSchemaConfig<Tables>): DefinedSyncSchema<Tables> {\n const tablesConfig: CrdtTableConfig[] = Object.entries(tables).map(([crdtTableName, table]) => ({\n crdtTableName,\n baseTableName: table.baseName ?? `_${crdtTableName}`,\n ...(table.exportImport === \"ignore\" ? { exportImport: \"ignore\" } : {}),\n }));\n\n const seenNames = new Set<string>();\n for (const { crdtTableName, baseTableName } of tablesConfig) {\n for (const name of [crdtTableName, baseTableName]) {\n const normalizedName = name.toLowerCase();\n if (RESERVED_TABLE_NAMES.has(normalizedName)) {\n throw new Error(`Table name \"${name}\" is reserved by sqlite-sync and cannot be used in a sync schema`);\n }\n if (seenNames.has(normalizedName)) {\n throw new Error(`Duplicate table name \"${name}\" in sync schema`);\n }\n seenNames.add(normalizedName);\n }\n }\n\n const writeOriginByName = buildWriteOriginByName({ tables, tablesConfig });\n\n return {\n tables,\n get tablesConfig() {\n return tablesConfig;\n },\n get writeOriginByName() {\n return writeOriginByName;\n },\n get migrations() {\n return migrations;\n },\n get \"~clientSchema\"(): never {\n throw new Error(\"~clientSchema is type-only and cannot be accessed at runtime\");\n },\n get \"~serverSchema\"(): never {\n throw new Error(\"~serverSchema is type-only and cannot be accessed at runtime\");\n },\n get \"~mutationsSchema\"(): never {\n throw new Error(\"~mutationsSchema is type-only and cannot be accessed at runtime\");\n },\n };\n}\n","export type ColumnKind = \"text\" | \"integer\" | \"real\" | \"boolean\" | \"enum\";\n\nexport type SqliteStorageType = \"text\" | \"integer\" | \"real\";\n\n/**\n * What `@sqlite-sync/ai` may do with a table.\n * `read-only`: documented and queryable; mutations rejected.\n * `hidden`: omitted from the schema doc; reads rejected.\n */\nexport type AiAccess = \"read-write\" | \"read-only\" | \"hidden\";\n\n/**\n * Who may author CRDT events for a table.\n * `\"any\"` (default): every replica. `\"server\"`: client pushes drop these events.\n */\nexport type WriteOrigin = \"any\" | \"server\";\n\n/** Whether backup and restore include a table. */\nexport type ExportImportMode = \"include\" | \"ignore\";\n\nexport type ColumnMeta = {\n kind: ColumnKind;\n /** Storage type used in generated DDL. Booleans store as integer 0/1. */\n sqlType: SqliteStorageType;\n nullable: boolean;\n hasDefault: boolean;\n /** Declared default in JS form (booleans as booleans). */\n defaultValue?: unknown;\n /** Allowed values for `enum` columns. */\n enumValues?: readonly string[];\n description?: string;\n};\n\nexport class ColumnBuilder<Value, HasDefault extends boolean = false> {\n declare readonly \"~types\": { value: Value; hasDefault: HasDefault };\n\n constructor(readonly meta: ColumnMeta) {}\n\n /** Allow SQL NULL; widens the value type with `null`. */\n nullable(): ColumnBuilder<Value | null, HasDefault> {\n return new ColumnBuilder({ ...this.meta, nullable: true });\n }\n\n /** Declare a column default. Creates may omit the column; it must match the migrated DDL default. */\n default(value: Value): ColumnBuilder<Value, true> {\n return new ColumnBuilder({ ...this.meta, hasDefault: true, defaultValue: value });\n }\n\n /** Narrow the TypeScript value type without changing storage, e.g. `t.text().$type<\"movie\" | \"tv\">()`. */\n $type<Narrowed extends Value>(): ColumnBuilder<Narrowed, HasDefault> {\n return this as unknown as ColumnBuilder<Narrowed, HasDefault>;\n }\n\n /** Human/AI-readable description, surfaced in generated schema docs. */\n describe(description: string): ColumnBuilder<Value, HasDefault> {\n return new ColumnBuilder({ ...this.meta, description });\n }\n}\n\nexport type AnyColumnBuilder = ColumnBuilder<any, boolean>;\n\nexport type TableColumns = Record<string, AnyColumnBuilder>;\n\nexport type AnyTableBuilder = TableBuilder<any, any, WriteOrigin>;\n\nexport type SyncSchemaTables = Record<string, AnyTableBuilder>;\n\nexport type TableOptions<\n BaseName extends string | undefined = string | undefined,\n Writes extends WriteOrigin = \"any\",\n> = {\n /** Override the materialized base table name (defaults to the crdt table name prefixed with \"_\"). */\n baseName?: BaseName;\n description?: string;\n /** Defaults to `\"read-write\"`. See {@link AiAccess}. */\n ai?: AiAccess;\n /** Defaults to `\"any\"`. See {@link WriteOrigin}. */\n writes?: Writes;\n /** Defaults to `\"include\"`. Ignored tables are omitted from export and skipped on import. */\n exportImport?: ExportImportMode;\n};\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\ntype ColumnValue<Column> = Column extends ColumnBuilder<infer Value, boolean> ? Value : never;\n\nexport type InferRow<Cols extends TableColumns> = Simplify<\n { id: string } & { [K in keyof Cols]: ColumnValue<Cols[K]> } & { tombstone?: boolean }\n>;\n\nexport type PayloadValidationResult = { success: true } | { success: false; errors: string[] };\n\nconst RESERVED_COLUMNS = [\"id\", \"tombstone\"] as const;\n\nconst idColumnMeta: ColumnMeta = {\n kind: \"text\",\n sqlType: \"text\",\n nullable: false,\n hasDefault: false,\n description: \"Unique immutable item id\",\n};\n\nconst tombstoneColumnMeta: ColumnMeta = {\n kind: \"boolean\",\n sqlType: \"integer\",\n nullable: false,\n hasDefault: true,\n defaultValue: false,\n description: \"Soft-delete marker\",\n};\n\nexport class TableBuilder<\n Cols extends TableColumns,\n BaseName extends string | undefined = undefined,\n Writes extends WriteOrigin = \"any\",\n> {\n declare readonly \"~baseName\": BaseName;\n declare readonly \"~writeOrigin\": Writes;\n\n /** All columns in DDL order, including the auto-injected `id` and `tombstone`. */\n readonly columns: Record<string, ColumnMeta>;\n readonly baseName: BaseName;\n readonly description: string | undefined;\n /** Resolved {@link AiAccess}; defaults to `\"read-write\"`. */\n readonly aiAccess: AiAccess;\n /** Resolved {@link WriteOrigin}; defaults to `\"any\"`. */\n readonly writeOrigin: Writes;\n /** Resolved {@link ExportImportMode}; defaults to `\"include\"`. */\n readonly exportImport: ExportImportMode;\n\n constructor(\n readonly userColumns: Cols,\n options?: TableOptions<BaseName, Writes>,\n ) {\n for (const reserved of RESERVED_COLUMNS) {\n if (reserved in userColumns) {\n throw new Error(`Column \"${reserved}\" is added automatically to every synced table and cannot be declared`);\n }\n }\n\n this.columns = {\n id: idColumnMeta,\n ...Object.fromEntries(Object.entries(userColumns).map(([name, column]) => [name, column.meta])),\n tombstone: tombstoneColumnMeta,\n };\n this.baseName = options?.baseName as BaseName;\n this.description = options?.description;\n this.aiAccess = options?.ai ?? \"read-write\";\n this.writeOrigin = (options?.writes ?? \"any\") as Writes;\n this.exportImport = options?.exportImport ?? \"include\";\n }\n\n describe(description: string): TableBuilder<Cols, BaseName, Writes> {\n return new TableBuilder<Cols, BaseName, Writes>(this.userColumns, {\n baseName: this.baseName,\n description,\n ai: this.aiAccess,\n writes: this.writeOrigin,\n exportImport: this.exportImport,\n });\n }\n\n /** Type-only: the row shape returned by queries. Do not access at runtime. */\n get $row(): InferRow<Cols> {\n return null as never;\n }\n\n /**\n * Validate a CRDT event payload against the declared columns.\n * `item-created` payloads must contain every column without a default (nullable columns may be omitted)\n * and their `tombstone` value is ignored, since applying a create always sets it to 0;\n * `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.\n */\n validatePayload(\n payload: Record<string, unknown>,\n options?: { event?: \"item-created\" | \"item-updated\" },\n ): PayloadValidationResult {\n const event = options?.event ?? \"item-created\";\n const errors: string[] = [];\n\n for (const key of Object.keys(payload)) {\n if (!Object.hasOwn(this.columns, key)) {\n errors.push(`Unknown column \"${key}\"`);\n }\n }\n\n if (event === \"item-updated\") {\n if (\"id\" in payload) {\n errors.push(`Column \"id\" is immutable and cannot appear in an update payload`);\n }\n if (payload.tombstone === true || payload.tombstone === 1) {\n errors.push(`Column \"tombstone\" cannot be set to true/1 in an update payload; use an item-deleted event`);\n }\n }\n\n if (event === \"item-created\") {\n for (const [name, meta] of Object.entries(this.columns)) {\n if (Object.hasOwn(payload, name) || name === \"tombstone\" || meta.nullable || meta.hasDefault) {\n continue;\n }\n errors.push(`Missing required column \"${name}\"`);\n }\n }\n\n for (const [name, value] of Object.entries(payload)) {\n const meta = Object.hasOwn(this.columns, name) ? this.columns[name] : undefined;\n if (!meta || (event === \"item-updated\" && name === \"id\") || (event === \"item-created\" && name === \"tombstone\")) {\n continue;\n }\n const error = validateValue(name, value, meta);\n if (error) {\n errors.push(error);\n }\n }\n\n return errors.length === 0 ? { success: true } : { success: false, errors };\n }\n}\n\nfunction describeValue(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"array\";\n return typeof value;\n}\n\nfunction validateValue(name: string, value: unknown, meta: ColumnMeta): string | null {\n if (value === null) {\n return meta.nullable ? null : `Column \"${name}\" is not nullable, got null`;\n }\n\n switch (meta.kind) {\n case \"text\":\n return typeof value === \"string\" ? null : `Column \"${name}\" expects text, got ${describeValue(value)}`;\n case \"integer\":\n return typeof value === \"number\" && Number.isInteger(value)\n ? null\n : `Column \"${name}\" expects an integer, got ${describeValue(value)}`;\n case \"real\":\n return typeof value === \"number\" && Number.isFinite(value)\n ? null\n : `Column \"${name}\" expects a number, got ${describeValue(value)}`;\n case \"boolean\":\n return typeof value === \"boolean\" || value === 0 || value === 1\n ? null\n : `Column \"${name}\" expects a boolean (true/false or 0/1), got ${describeValue(value)}`;\n case \"enum\": {\n if (typeof value !== \"string\") {\n return `Column \"${name}\" expects one of ${formatEnumValues(meta)}, got ${describeValue(value)}`;\n }\n return meta.enumValues?.includes(value)\n ? null\n : `Column \"${name}\" expects one of ${formatEnumValues(meta)}, got \"${value}\"`;\n }\n }\n}\n\nfunction formatEnumValues(meta: ColumnMeta): string {\n return (meta.enumValues ?? []).map((value) => `\"${value}\"`).join(\" | \");\n}\n\nfunction column<Value>(kind: ColumnKind, sqlType: SqliteStorageType): ColumnBuilder<Value> {\n return new ColumnBuilder<Value>({ kind, sqlType, nullable: false, hasDefault: false });\n}\n\nexport const t = {\n text: () => column<string>(\"text\", \"text\"),\n integer: () => column<number>(\"integer\", \"integer\"),\n real: () => column<number>(\"real\", \"real\"),\n /** Stored as INTEGER 0/1; always bind booleans as 0/1 when writing raw SQL. */\n boolean: () => column<boolean>(\"boolean\", \"integer\"),\n /**\n * Union of string literals stored as TEXT, e.g. `t.enum([\"movie\", \"tv\"])`. Unlike `$type`, the\n * allowed values are kept as runtime metadata, so payloads are validated against them.\n * For an open-ended set, use `t.text().$type<\"idle\" | (string & {})>()` instead.\n */\n enum: <const Values extends readonly [string, ...string[]]>(values: Values) =>\n new ColumnBuilder<Values[number]>({\n kind: \"enum\",\n sqlType: \"text\",\n nullable: false,\n hasDefault: false,\n enumValues: values,\n }),\n table: <\n Cols extends TableColumns,\n const BaseName extends string | undefined = undefined,\n const Writes extends WriteOrigin = \"any\",\n >(\n columns: Cols,\n options?: TableOptions<BaseName, Writes>,\n ) => new TableBuilder(columns, options),\n};\n","import type { CrdtStorage, OwnCrdtEvent } from \"./crdt-storage\";\n\nexport type CrdtStorageMutator<Database> = ReturnType<typeof createCrdtStorageMutator<Database>>;\n\ntype CommitEventOptions<Database, Table extends keyof Database & string> =\n | {\n type: \"item-created\";\n dataset: Table;\n item_id: string;\n payload: CreateEventPayload<Database, Table>;\n }\n | {\n type: \"item-updated\";\n dataset: Table;\n item_id: string;\n payload: UpdateEventPayload<Database, Table>;\n }\n | {\n type: \"item-deleted\";\n dataset: Table;\n item_id: string;\n };\n\ntype AnyCommitEventOptions<Database> = {\n [Table in keyof Database & string]: CommitEventOptions<Database, Table>;\n}[keyof Database & string];\n\ntype CreateEventPayload<Database, Table extends keyof Database> = Omit<Database[Table], \"tombstone\">;\ntype UpdateEventPayload<Database, Table extends keyof Database> = Omit<Partial<Database[Table]>, \"id\" | \"tombstone\">;\n\nexport type SnapshotOptions<Database, Table extends keyof Database & string> = {\n dataset: Table;\n id: string;\n patch: UpdateEventPayload<Database, Table>;\n};\n\nexport function createCrdtStorageMutator<Database>({ storage }: { storage: CrdtStorage }) {\n const mapToStorageEvent = (event: CommitEventOptions<Database, keyof Database & string>): OwnCrdtEvent => {\n switch (event.type) {\n case \"item-created\":\n return {\n type: \"item-created\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-updated\":\n return {\n type: \"item-updated\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: JSON.stringify(event.payload),\n };\n case \"item-deleted\":\n return {\n type: \"item-deleted\",\n dataset: event.dataset,\n item_id: event.item_id,\n payload: \"{}\",\n };\n }\n };\n\n const applyEvents = (events: AnyCommitEventOptions<Database>[]) => {\n storage.applyOwnEvents(events.map(mapToStorageEvent));\n };\n\n const applyEvent = <Table extends keyof Database & string>(event: CommitEventOptions<Database, Table>) => {\n storage.applyOwnEvents([mapToStorageEvent(event)]);\n };\n\n const applySnapshot = <Table extends keyof Database & string>(snapshot: SnapshotOptions<Database, Table>) => {\n storage.applyOwnSnapshot({\n dataset: snapshot.dataset,\n item_id: snapshot.id,\n patch: { ...snapshot.patch },\n });\n };\n\n return {\n applyEvent,\n applyEvents,\n applySnapshot,\n };\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as parametersAreEqual, $t as startPerformanceLogger, A as WorkerState, An as MigrationsDb, At as tryCatch, B as verifySyncSchemaWithDriver, Bn as CrdtUpdateLogItem, C as EventsPullRequest, Cn as TableBuilder, Ct as TypedEvent, Dn as t, Dt as generateId, E as createCrdtSyncRemoteSource, En as WriteOrigin, Et as createTypedEventTarget, F as PendingCrdtEvent, Fn as createStoredValue, Ft as createSQLiteKvStore, G as DefinedSyncSchema, H as NewCrdtEventValidationResult, Hn as PersistedCrdtEvent, Ht as QueryBuilderOutput, I as createCrdtApplyFunction, In as CRDT_EVENT_NO_OP_PAYLOAD, It as ExecuteParams, Jt as SqliteDriverRows, K as defineSyncSchema, Kt as SqliteDriver, L as createSQLiteCrdtApplyFunction, Ln as CrdtEventOrigin, Lt as ExecuteResult, M as CrdtStorageMutator, Mn as createMigrations, Mt as KvStoreItem, N as SnapshotOptions, Nn as createMigrator, O as WorkerConfig, On as MigratableEvent, Ot as jsonSafeParse, P as createCrdtStorageMutator, Pn as StoredValue, Pt as createKvStoreTableQuery, Q as SharedLiveQuerySnapshot, Qt as Logger, R as SchemaVerificationIssue, Rn as CrdtEventStatus, Sn as SyncSchemaTables, St as TypedBroadcastChannel, T as EventsPushResponse, Tn as TableOptions, Tt as createDeferredPromise, U as validateNewCrdtEvent, Un as isNoOpCrdtEventPayload, Ut as SQLiteDbWrapper, V as CrdtEventValidationError, Vn as CrdtUpdateLogPayload, Vt as PreparedStatement, W as DefineSyncSchemaConfig, Wn as dummyKysely, Wt as SQLiteTransactionWrapper, X as SQLiteReactiveDb, Xt as SqliteDriverValue, Y as admitClientEvents, Yt as SqliteDriverStatement, Z as SharedLiveQuery, Zt as LogLevel, _n as ColumnMeta, a as createSQLiteReactiveDb, an as HLCCounter, at as createImportData, b as makeCrdtTable, bn as PayloadValidationResult, bt as DeferredPromise, c as ResetStore, cn as serializeHLC, d as createSyncedDb, dn as SyncDbSchema, dt as createCrdtStorage, en as ColumnMetadata, et as xxhash, f as SyncedDbDatabase, fn as AiAccess, ft as SystemDbConfig, gn as ColumnKind, gt as applyWorkerDbSchema, h as UnsafeSyncedDbSqlExecutor, hn as ColumnBuilder, ht as applyMemoryDbSchema, i as verifySyncSchema, in as HLC, it as createExportData, j as createCrdtSyncProducer, jn as SyncDbMigrator, jt as tryCatchAsync, k as WorkerNotificationMessage, kn as Migrations, kt as quoteId, l as SyncedDb, ln as CrdtTableConfig, lt as OwnCrdtEvent, m as SyncedDbTransaction, mn as AnyTableBuilder, mt as SystemMigrationContext, n as createWasmSqliteDriver, nn as TableMetadata, nt as ImportDataResult, o as RESET_REQUEST_TTL_MS, on as compareHLC, p as SyncedDbSqlExecutor, pn as AnyColumnBuilder, pt as SystemMigration, q as AdmitClientEventsResult, qt as SqliteDriverHooks, r as loadSqliteWasm, rn as introspectDb, rt as SyncedDbExport, s as ResetRequest, sn as deserializeHLC, st as CrdtStorage, t as createWasmMemoryDriver, tn as DatabaseIntrospection, tt as ImportDataOptions, u as SyncedDbOptions, un as ReadonlyTable, ut as OwnCrdtSnapshot, vn as ExportImportMode, w as EventsPushRequest, wn as TableColumns, wt as TypedEventTarget, x as CrdtSyncRemoteSource, xn as SqliteStorageType, xt as DistributiveOmit, yn as InferRow, z as formatSchemaVerificationIssues, zn as CrdtEventType, zt as KyselyQueryFactory } from "./index-BgkTUdlX.js";
2
- export { type AdmitClientEventsResult, type AiAccess, type AnyColumnBuilder, type AnyTableBuilder, CRDT_EVENT_NO_OP_PAYLOAD, ColumnBuilder, type ColumnKind, type ColumnMeta, type ColumnMetadata, type CrdtEventOrigin, type CrdtEventStatus, type CrdtEventType, CrdtEventValidationError, type CrdtStorage, type CrdtStorageMutator, type CrdtSyncRemoteSource, type CrdtTableConfig, type CrdtUpdateLogItem, type CrdtUpdateLogPayload, type DatabaseIntrospection, type DeferredPromise, type DefineSyncSchemaConfig, type DefinedSyncSchema, type DistributiveOmit, type EventsPullRequest, type EventsPushRequest, type EventsPushResponse, type ExecuteParams, type ExecuteResult, type ExportImportMode, type HLC, HLCCounter, type ImportDataOptions, type ImportDataResult, type InferRow, type KvStoreItem, type KyselyQueryFactory, type LogLevel, type Logger, type MigratableEvent, type Migrations, type MigrationsDb, type NewCrdtEventValidationResult, type OwnCrdtEvent, type OwnCrdtSnapshot, type PayloadValidationResult, type PendingCrdtEvent, type PersistedCrdtEvent, type PreparedStatement, type QueryBuilderOutput, RESET_REQUEST_TTL_MS, type ReadonlyTable, type ResetRequest, type ResetStore, SQLiteDbWrapper, SQLiteReactiveDb, type SQLiteTransactionWrapper, type SchemaVerificationIssue, type SharedLiveQuery, type SharedLiveQuerySnapshot, type SnapshotOptions, type SqliteDriver, type SqliteDriverHooks, type SqliteDriverRows, type SqliteDriverStatement, type SqliteDriverValue, type SqliteStorageType, type StoredValue, type SyncDbMigrator, type SyncDbSchema, type SyncSchemaTables, type SyncedDb, type SyncedDbDatabase, type SyncedDbExport, type SyncedDbOptions, type SyncedDbSqlExecutor, type SyncedDbTransaction, type SystemDbConfig, type SystemMigration, type SystemMigrationContext, TableBuilder, type TableColumns, type TableMetadata, type TableOptions, TypedBroadcastChannel, TypedEvent, type TypedEventTarget, type UnsafeSyncedDbSqlExecutor, type WorkerConfig, type WorkerNotificationMessage, type WorkerState, type WriteOrigin, admitClientEvents, applyMemoryDbSchema, applyWorkerDbSchema, compareHLC, createCrdtApplyFunction, createCrdtStorage, createCrdtStorageMutator, createCrdtSyncProducer, createCrdtSyncRemoteSource, createDeferredPromise, createExportData, createImportData, createKvStoreTableQuery, createMigrations, createMigrator, createSQLiteCrdtApplyFunction, createSQLiteKvStore, createSQLiteReactiveDb, createStoredValue, createSyncedDb, createTypedEventTarget, createWasmMemoryDriver, createWasmSqliteDriver, defineSyncSchema, deserializeHLC, dummyKysely, formatSchemaVerificationIssues, generateId, introspectDb, isNoOpCrdtEventPayload, jsonSafeParse, loadSqliteWasm, makeCrdtTable, parametersAreEqual, quoteId, serializeHLC, startPerformanceLogger, t, tryCatch, tryCatchAsync, validateNewCrdtEvent, verifySyncSchema, verifySyncSchemaWithDriver, xxhash };
1
+ import { $ as SharedLiveQuery, $t as LogLevel, A as WorkerConfig, An as MigratableEvent, At as jsonSafeParse, B as SchemaVerificationIssue, Bn as CrdtEventStatus, C as CrdtSyncRemoteSource, Cn as SqliteStorageType, Ct as DistributiveOmit, D as EventsPushResponse, Dn as TableOptions, Dt as createDeferredPromise, E as EventsPushRequest, En as TableColumns, Et as TypedEventTarget, F as SnapshotOptions, Fn as createMigrator, G as validateNewCrdtEvent, Gn as isNoOpCrdtEventPayload, Gt as SQLiteDbWrapper, H as verifySyncSchemaWithDriver, Hn as CrdtUpdateLogItem, I as createCrdtStorageMutator, In as StoredValue, It as createKvStoreTableQuery, J as defineSyncSchema, Jt as SqliteDriver, K as DefineSyncSchemaConfig, Kn as dummyKysely, Kt as SQLiteTransactionWrapper, L as PendingCrdtEvent, Ln as createStoredValue, Lt as createSQLiteKvStore, M as WorkerState, Mn as MigrationsDb, Mt as tryCatch, N as createCrdtSyncProducer, Nn as SyncDbMigrator, Nt as tryCatchAsync, O as createCrdtSyncRemoteSource, On as WriteOrigin, Ot as createTypedEventTarget, P as CrdtStorageMutator, Pn as createMigrations, Pt as KvStoreItem, Q as SQLiteReactiveDb, Qt as SqliteDriverValue, R as createCrdtApplyFunction, Rn as CRDT_EVENT_NO_OP_PAYLOAD, Rt as ExecuteParams, S as CatchUpResult, Sn as PayloadValidationResult, St as DeferredPromise, T as EventsPullRequest, Tn as TableBuilder, Tt as TypedEvent, U as CrdtEventValidationError, Un as CrdtUpdateLogPayload, Ut as PreparedStatement, V as formatSchemaVerificationIssues, Vn as CrdtEventType, Vt as KyselyQueryFactory, W as NewCrdtEventValidationResult, Wn as PersistedCrdtEvent, Wt as QueryBuilderOutput, Xt as SqliteDriverRows, Y as AdmitClientEventsResult, Yt as SqliteDriverHooks, Z as admitClientEvents, Zt as SqliteDriverStatement, _n as ColumnBuilder, _t as applyMemoryDbSchema, a as createSQLiteReactiveDb, an as introspectDb, at as SyncedDbExport, b as makeCrdtTable, bn as ExportImportMode, c as ResetStore, cn as compareHLC, d as createSyncedDb, dn as CrdtTableConfig, dt as OwnCrdtEvent, en as Logger, et as SharedLiveQuerySnapshot, f as SyncedDbDatabase, fn as ReadonlyTable, ft as OwnCrdtSnapshot, gn as AnyTableBuilder, gt as SystemMigrationContext, h as UnsafeSyncedDbSqlExecutor, hn as AnyColumnBuilder, ht as SystemMigration, i as verifySyncSchema, in as TableMetadata, it as ImportDataResult, j as WorkerNotificationMessage, jn as Migrations, jt as quoteId, kn as t, kt as generateId, l as SyncedDb, ln as deserializeHLC, lt as CrdtStorage, m as SyncedDbTransaction, mn as AiAccess, mt as SystemDbConfig, n as createWasmSqliteDriver, nn as ColumnMetadata, nt as xxhash, o as RESET_REQUEST_TTL_MS, on as HLC, ot as createExportData, p as SyncedDbSqlExecutor, pn as SyncDbSchema, pt as createCrdtStorage, q as DefinedSyncSchema, r as loadSqliteWasm, rn as DatabaseIntrospection, rt as ImportDataOptions, s as ResetRequest, sn as HLCCounter, st as createImportData, t as createWasmMemoryDriver, tn as startPerformanceLogger, tt as parametersAreEqual, u as SyncedDbOptions, un as serializeHLC, vn as ColumnKind, vt as applyWorkerDbSchema, wn as SyncSchemaTables, wt as TypedBroadcastChannel, x as CatchUpOptions, xn as InferRow, yn as ColumnMeta, z as createSQLiteCrdtApplyFunction, zn as CrdtEventOrigin, zt as ExecuteResult } from "./index-C1E8xMH1.js";
2
+ export { type AdmitClientEventsResult, type AiAccess, type AnyColumnBuilder, type AnyTableBuilder, CRDT_EVENT_NO_OP_PAYLOAD, type CatchUpOptions, type CatchUpResult, ColumnBuilder, type ColumnKind, type ColumnMeta, type ColumnMetadata, type CrdtEventOrigin, type CrdtEventStatus, type CrdtEventType, CrdtEventValidationError, type CrdtStorage, type CrdtStorageMutator, type CrdtSyncRemoteSource, type CrdtTableConfig, type CrdtUpdateLogItem, type CrdtUpdateLogPayload, type DatabaseIntrospection, type DeferredPromise, type DefineSyncSchemaConfig, type DefinedSyncSchema, type DistributiveOmit, type EventsPullRequest, type EventsPushRequest, type EventsPushResponse, type ExecuteParams, type ExecuteResult, type ExportImportMode, type HLC, HLCCounter, type ImportDataOptions, type ImportDataResult, type InferRow, type KvStoreItem, type KyselyQueryFactory, type LogLevel, type Logger, type MigratableEvent, type Migrations, type MigrationsDb, type NewCrdtEventValidationResult, type OwnCrdtEvent, type OwnCrdtSnapshot, type PayloadValidationResult, type PendingCrdtEvent, type PersistedCrdtEvent, type PreparedStatement, type QueryBuilderOutput, RESET_REQUEST_TTL_MS, type ReadonlyTable, type ResetRequest, type ResetStore, SQLiteDbWrapper, SQLiteReactiveDb, type SQLiteTransactionWrapper, type SchemaVerificationIssue, type SharedLiveQuery, type SharedLiveQuerySnapshot, type SnapshotOptions, type SqliteDriver, type SqliteDriverHooks, type SqliteDriverRows, type SqliteDriverStatement, type SqliteDriverValue, type SqliteStorageType, type StoredValue, type SyncDbMigrator, type SyncDbSchema, type SyncSchemaTables, type SyncedDb, type SyncedDbDatabase, type SyncedDbExport, type SyncedDbOptions, type SyncedDbSqlExecutor, type SyncedDbTransaction, type SystemDbConfig, type SystemMigration, type SystemMigrationContext, TableBuilder, type TableColumns, type TableMetadata, type TableOptions, TypedBroadcastChannel, TypedEvent, type TypedEventTarget, type UnsafeSyncedDbSqlExecutor, type WorkerConfig, type WorkerNotificationMessage, type WorkerState, type WriteOrigin, admitClientEvents, applyMemoryDbSchema, applyWorkerDbSchema, compareHLC, createCrdtApplyFunction, createCrdtStorage, createCrdtStorageMutator, createCrdtSyncProducer, createCrdtSyncRemoteSource, createDeferredPromise, createExportData, createImportData, createKvStoreTableQuery, createMigrations, createMigrator, createSQLiteCrdtApplyFunction, createSQLiteKvStore, createSQLiteReactiveDb, createStoredValue, createSyncedDb, createTypedEventTarget, createWasmMemoryDriver, createWasmSqliteDriver, defineSyncSchema, deserializeHLC, dummyKysely, formatSchemaVerificationIssues, generateId, introspectDb, isNoOpCrdtEventPayload, jsonSafeParse, loadSqliteWasm, makeCrdtTable, parametersAreEqual, quoteId, serializeHLC, startPerformanceLogger, t, tryCatch, tryCatchAsync, validateNewCrdtEvent, verifySyncSchema, verifySyncSchemaWithDriver, xxhash };