event-sourced-collection 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to `event-sourced-collection` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.10] - 2026-09-08
9
+
10
+ ### Added
11
+
12
+ - `recordLocalEchoes` option (default `true`). Set `false` to advance the pull
13
+ cursor past this device's own events without writing resolved inbox echo rows.
14
+
15
+ ## [0.0.9] - 2026-09-08
16
+
17
+ ### Added
18
+
19
+ - `useSyncStatus` / `useEnsureDb` on `event-sourced-collection/react`
20
+ - `useManualSync({ db })` so `syncing` follows `subscribeSyncStatus` (load sync + manual)
21
+
8
22
  ## [0.0.7] - 2026-08-09
9
23
 
10
24
  Reliability-focused release: dead-lettering, backend identity, retries, conflict detection, multi-tab locking, React helpers, and published examples.
@@ -1,5 +1,5 @@
1
- import { L as SyncLock, P as SQLiteDriver, Q as InjectedModuleFn, Z as InjectedCreateCollection, p as EventSourcedSharedOptions } from "./types-BSdudVLh.mjs";
2
- import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-BAos-9pS.mjs";
1
+ import { L as SyncLock, P as SQLiteDriver, Q as InjectedModuleFn, Z as InjectedCreateCollection, p as EventSourcedSharedOptions } from "./types-DChF-gGL.mjs";
2
+ import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-B_uCsLJC.mjs";
3
3
  import { PersistedCollectionPersistence } from "@tanstack/db-sqlite-persistence-core";
4
4
 
5
5
  //#region src/platforms/browser-wa-sqlite-driver.d.ts
@@ -118,4 +118,4 @@ type BrowserEventSourcedDBConfig<TDefs extends Record<string, CollectionDefConst
118
118
  declare function createBrowserEventSourcedDB<const TDefs extends Record<string, CollectionDefConstraint>>(config: BrowserEventSourcedDBConfig<TDefs>): EventSourcedDBHandle<TDefs>;
119
119
  //#endregion
120
120
  export { BrowserPlatformConfig as a, BrowserWASQLiteDatabase as c, BrowserCoordinatorInstance as i, BrowserEventSourcedModules as n, BrowserPlatformDeps as o, createBrowserEventSourcedDB as r, BrowserPlatformResult as s, BrowserEventSourcedDBConfig as t };
121
- //# sourceMappingURL=browser-event-sourced-db-CCOAgE31.d.mts.map
121
+ //# sourceMappingURL=browser-event-sourced-db-YBmcmWDR.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-event-sourced-db-CCOAgE31.d.mts","names":[],"sources":["../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser-types.ts","../src/platforms/browser-event-sourced-db.ts"],"mappings":";;;;;KAEY,uBAAA;EACV,OAAA,mBACE,GAAA,UACA,MAAA,GAAS,aAAA,cACN,OAAA,CAAQ,aAAA,CAAc,IAAA;EAC3B,KAAA,SAAc,OAAA;AAAA;;;KCJJ,0BAAA;EACV,OAAO;AAAA;ADFT;;;;AAAA,KCSY,mBAAA;EACV,+BAAA,GAAkC,OAAA,YAAmB,OAAA,CAAQ,uBAAA;EAC7D,gCAAA,GAAmC,OAAA,YAAmB,8BAAA;EACtD,4BAAA,OAAmC,OAAA,YAAmB,0BAAA;AAAA;AAAA,KAG5C,qBAAA;EACV,YAAA;EACA,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EACV,MAAA,EAD+B,YAAA;EAE/B,WAAA,EAAa,8BAAA;EACb,KAAA,QAAa,OAAA;AAAA;;;KCTV,uBAAA;EACH,MAAA,GAAS,KAAY;AAAA;AAAA,KAGX,0BAAA,GAA6B,mBAAA;EACvC,gBAAA,EAAkB,wBAAA;EAClB,0BAAA,EAA4B,gBAAA;AAAA;;;;;;KAQlB,2BAAA,eAA0C,MAAA,SAAe,uBAAA,KACnE,IAAA,CAAK,yBAAA;EF3BH;;;;EEgCA,WAAA,EAAa,KAAA;EF9BY;;;;EEmCzB,YAAA;;;;ADtCJ;EC2CI,iBAAA;;;AD1CK;AAOT;;;EC0CI,OAAA,EAAS,YAAA,CAAa,0BAAA;EDzC6B;;;;EC8CnD,IAAA,GAAO,QAAA;AAAA;;;;;;;;;;AD5CuE;AAGlF;;;;AAEmB;AAGnB;;;;;;;;;;;;;;;AAGsB;;;;ACZqC;;;;AAIpC;AAGvB;;;iBAqFgB,2BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,MAAA,EAAQ,2BAAA,CAA4B,KAAA,IAAM,oBAAA,CAAA,KAAA"}
1
+ {"version":3,"file":"browser-event-sourced-db-YBmcmWDR.d.mts","names":[],"sources":["../src/platforms/browser-wa-sqlite-driver.ts","../src/platforms/browser-types.ts","../src/platforms/browser-event-sourced-db.ts"],"mappings":";;;;;KAEY,uBAAA;EACV,OAAA,mBACE,GAAA,UACA,MAAA,GAAS,aAAA,cACN,OAAA,CAAQ,aAAA,CAAc,IAAA;EAC3B,KAAA,SAAc,OAAA;AAAA;;;KCJJ,0BAAA;EACV,OAAO;AAAA;ADFT;;;;AAAA,KCSY,mBAAA;EACV,+BAAA,GAAkC,OAAA,YAAmB,OAAA,CAAQ,uBAAA;EAC7D,gCAAA,GAAmC,OAAA,YAAmB,8BAAA;EACtD,4BAAA,OAAmC,OAAA,YAAmB,0BAAA;AAAA;AAAA,KAG5C,qBAAA;EACV,YAAA;EACA,iBAAiB;AAAA;AAAA,KAGP,qBAAA;EACV,MAAA,EAD+B,YAAA;EAE/B,WAAA,EAAa,8BAAA;EACb,KAAA,QAAa,OAAA;AAAA;;;KCTV,uBAAA;EACH,MAAA,GAAS,KAAY;AAAA;AAAA,KAGX,0BAAA,GAA6B,mBAAA;EACvC,gBAAA,EAAkB,wBAAA;EAClB,0BAAA,EAA4B,gBAAA;AAAA;;;;;;KAQlB,2BAAA,eAA0C,MAAA,SAAe,uBAAA,KACnE,IAAA,CAAK,yBAAA;EF3BH;;;;EEgCA,WAAA,EAAa,KAAA;EF9BY;;;;EEmCzB,YAAA;;;;ADtCJ;EC2CI,iBAAA;;;AD1CK;AAOT;;;EC0CI,OAAA,EAAS,YAAA,CAAa,0BAAA;EDzC6B;;;;EC8CnD,IAAA,GAAO,QAAA;AAAA;;;;;;;;;;AD5CuE;AAGlF;;;;AAEmB;AAGnB;;;;;;;;;;;;;;;AAGsB;;;;ACZqC;;;;AAIpC;AAGvB;;;iBAqFgB,2BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,MAAA,EAAQ,2BAAA,CAA4B,KAAA,IAAM,oBAAA,CAAA,KAAA"}
@@ -1,5 +1,5 @@
1
- import { a as BrowserPlatformConfig, c as BrowserWASQLiteDatabase, i as BrowserCoordinatorInstance, n as BrowserEventSourcedModules, o as BrowserPlatformDeps, r as createBrowserEventSourcedDB, s as BrowserPlatformResult, t as BrowserEventSourcedDBConfig } from "./browser-event-sourced-db-CCOAgE31.mjs";
2
- import { t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-BAos-9pS.mjs";
1
+ import { a as BrowserPlatformConfig, c as BrowserWASQLiteDatabase, i as BrowserCoordinatorInstance, n as BrowserEventSourcedModules, o as BrowserPlatformDeps, r as createBrowserEventSourcedDB, s as BrowserPlatformResult, t as BrowserEventSourcedDBConfig } from "./browser-event-sourced-db-YBmcmWDR.mjs";
2
+ import { t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-B_uCsLJC.mjs";
3
3
 
4
4
  //#region src/platforms/browser.d.ts
5
5
  /**
package/dist/browser.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-BPsVV1x6.mjs";
1
+ import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-D-fHKczr.mjs";
2
2
  import { t as createWebLocksSyncLock } from "./web-locks-Bi2f732X.mjs";
3
3
  //#region src/platforms/browser-wa-sqlite-driver.ts
4
4
  function assertTransactionCallbackHasDriverArg(fn) {
@@ -1,4 +1,4 @@
1
- import { l as EventSourcedDB, u as EventSourcedDBConfig } from "./types-BSdudVLh.mjs";
1
+ import { l as EventSourcedDB, u as EventSourcedDBConfig } from "./types-DChF-gGL.mjs";
2
2
 
3
3
  //#region src/core/create-event-sourced-db-handle.d.ts
4
4
  type CollectionDefConstraint = {
@@ -102,4 +102,4 @@ type ModulesInput<T> = T | (() => T | Promise<T>);
102
102
  declare function resolveModules<T>(modules: ModulesInput<T>): Promise<T>;
103
103
  //#endregion
104
104
  export { resolveModules as a, createEventSourcedDBHandle as i, EventSourcedDBHandleSetup as n, ModulesInput as r, EventSourcedDBHandle as t };
105
- //# sourceMappingURL=create-event-sourced-db-handle-BAos-9pS.d.mts.map
105
+ //# sourceMappingURL=create-event-sourced-db-handle-B_uCsLJC.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-event-sourced-db-handle-BAos-9pS.d.mts","names":[],"sources":["../src/core/create-event-sourced-db-handle.ts"],"mappings":";;;KAIK,uBAAA;EACH,MAAA,GAAS,KAAY;AAAA;;;;AAAA;AAyBvB;;;;;;;;;;;;;;;;;;KAAY,oBAAA,eAAmC,MAAA,SAAe,uBAAA;EAErB,uFAAvC,QAAA,QAAgB,OAAA,CAAQ,cAAA,CAAe,KAAA;EAKnC;;;;EAAJ,EAAA,EAAI,cAAA,CAAe,KAAA,GAEC;EAApB,KAAA,QAAa,OAAA;AAAA;AAAA,KAGH,yBAAA,eAAwC,MAAA,SAAe,uBAAA,KACjE,oBAAA,CAAqB,KAAA;EAD4C,uEAG/D,KAAA,SAAc,OAAA;AAAA;;;;;;;;;;;;;AAAO;AAsCzB;;;;;;;;;;;;;;;;;;;;;;iBAAgB,0BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,OAAA;EACA,KAAA,QAAa,OAAA,CAAQ,yBAAA,CAA0B,KAAA;EAC/C,KAAA;EACA,qBAAA;AAAA,IACE,oBAAA,CAAqB,KAAA;;KAuCb,YAAA,MAAkB,CAAA,UAAW,CAAA,GAAI,OAAA,CAAQ,CAAA;;;;;;;;;;;;;AAAC;AAqBtD;;;;;;iBAAsB,cAAA,IAAkB,OAAA,EAAS,YAAA,CAAa,CAAA,IAAK,OAAA,CAAQ,CAAA"}
1
+ {"version":3,"file":"create-event-sourced-db-handle-B_uCsLJC.d.mts","names":[],"sources":["../src/core/create-event-sourced-db-handle.ts"],"mappings":";;;KAIK,uBAAA;EACH,MAAA,GAAS,KAAY;AAAA;;;;AAAA;AAyBvB;;;;;;;;;;;;;;;;;;KAAY,oBAAA,eAAmC,MAAA,SAAe,uBAAA;EAErB,uFAAvC,QAAA,QAAgB,OAAA,CAAQ,cAAA,CAAe,KAAA;EAKnC;;;;EAAJ,EAAA,EAAI,cAAA,CAAe,KAAA,GAEC;EAApB,KAAA,QAAa,OAAA;AAAA;AAAA,KAGH,yBAAA,eAAwC,MAAA,SAAe,uBAAA,KACjE,oBAAA,CAAqB,KAAA;EAD4C,uEAG/D,KAAA,SAAc,OAAA;AAAA;;;;;;;;;;;;;AAAO;AAsCzB;;;;;;;;;;;;;;;;;;;;;;iBAAgB,0BAAA,qBACM,MAAA,SAAe,uBAAA,GACnC,OAAA;EACA,KAAA,QAAa,OAAA,CAAQ,yBAAA,CAA0B,KAAA;EAC/C,KAAA;EACA,qBAAA;AAAA,IACE,oBAAA,CAAqB,KAAA;;KAuCb,YAAA,MAAkB,CAAA,UAAW,CAAA,GAAI,OAAA,CAAQ,CAAA;;;;;;;;;;;;;AAAC;AAqBtD;;;;;;iBAAsB,cAAA,IAAkB,OAAA,EAAS,YAAA,CAAa,CAAA,IAAK,OAAA,CAAQ,CAAA"}
@@ -480,7 +480,7 @@ var BackendMismatchError = class extends Error {
480
480
  }
481
481
  };
482
482
  async function pullInbox(args) {
483
- const { outbox, inbox, syncmeta, pull, clientId, pullOverlap, context } = args;
483
+ const { outbox, inbox, syncmeta, pull, clientId, pullOverlap, recordLocalEchoes, context } = args;
484
484
  const { log } = context;
485
485
  let pulled = 0;
486
486
  let skipped = 0;
@@ -513,10 +513,11 @@ async function pullInbox(args) {
513
513
  let halted = false;
514
514
  for (const event of sorted) {
515
515
  if (isLocalOrigin(event, outbox, clientId)) {
516
- await markInboxEventResolved(inbox, event);
516
+ if (recordLocalEchoes) await markInboxEventResolved(inbox, event);
517
517
  log.debug("pull skipped: event originated locally", {
518
518
  eventId: event.eventId,
519
- globalSeq: event.globalSeq
519
+ globalSeq: event.globalSeq,
520
+ recorded: recordLocalEchoes
520
521
  });
521
522
  continue;
522
523
  }
@@ -1280,6 +1281,7 @@ async function createEventSourcedDB(config) {
1280
1281
  const clientId = { value: config.clientId ?? generateEventId() };
1281
1282
  const unknownEventHandling = config.unknownEventHandling ?? "skip";
1282
1283
  const pullOverlap = Math.max(0, config.pullOverlap ?? 0);
1284
+ const recordLocalEchoes = config.recordLocalEchoes ?? true;
1283
1285
  const eventSchemaVersion = config.eventSchemaVersion ?? 1;
1284
1286
  const pushBatchSize = Math.max(1, config.pushBatchSize ?? 100);
1285
1287
  const backendMismatch = config.backendMismatch ?? "resetCursor";
@@ -1297,6 +1299,7 @@ async function createEventSourcedDB(config) {
1297
1299
  syncEnabled,
1298
1300
  unknownEventHandling,
1299
1301
  pullOverlap,
1302
+ recordLocalEchoes,
1300
1303
  pushBatchSize,
1301
1304
  backendMismatch,
1302
1305
  conflictDetection,
@@ -1456,6 +1459,7 @@ async function createEventSourcedDB(config) {
1456
1459
  pull: transport.pull,
1457
1460
  clientId: clientId.value,
1458
1461
  pullOverlap,
1462
+ recordLocalEchoes,
1459
1463
  backendMismatch,
1460
1464
  context: replayContext
1461
1465
  });
@@ -2026,4 +2030,4 @@ async function resolveModules(modules) {
2026
2030
  //#endregion
2027
2031
  export { generateEventId as a, SyncPushError as c, createEventSourcedDB as i, createHttpTransport as l, resolveModules as n, createEventSourcedLogger as o, createLazySingleton as r, SyncPullError as s, createEventSourcedDBHandle as t, BackendMismatchError as u };
2028
2032
 
2029
- //# sourceMappingURL=create-event-sourced-db-handle-BPsVV1x6.mjs.map
2033
+ //# sourceMappingURL=create-event-sourced-db-handle-D-fHKczr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-event-sourced-db-handle-D-fHKczr.mjs","names":[],"sources":["../src/internal/constants.ts","../src/internal/hooks.ts","../src/internal/replay.ts","../src/internal/sync-meta.ts","../src/internal/pull.ts","../src/core/sync.ts","../src/internal/push.ts","../src/internal/serial-queue.ts","../src/utils/logger.ts","../src/utils/uuid.ts","../src/core/create-event-sourced-db.ts","../src/core/lazy-singleton.ts","../src/core/create-event-sourced-db-handle.ts"],"sourcesContent":["export const OUTBOX_ID = \"outbox\";\nexport const INBOX_ID = \"inbox\";\nexport const DEADLETTER_ID = \"deadletter\";\nexport const SYNCMETA_ID = \"syncmeta\";\nexport const ROWVERSIONS_ID = \"rowversions\";\n\n/** `syncmeta` holds exactly one row; this is its key. */\nexport const SYNCMETA_KEY = \"singleton\";\n\nexport const RESERVED_IDS: ReadonlySet<string> = new Set([\n OUTBOX_ID,\n INBOX_ID,\n DEADLETTER_ID,\n SYNCMETA_ID,\n ROWVERSIONS_ID,\n]);\n\nexport const DEFAULT_PUSH_BATCH_SIZE = 100;\nexport const DEFAULT_MAX_ATTEMPTS = 8;\nexport const DEFAULT_BASE_DELAY_MS = 1_000;\nexport const DEFAULT_MAX_DELAY_MS = 5 * 60 * 1_000;\nexport const DEFAULT_EVENT_SCHEMA_VERSION = 1;\n\nexport const SYNC_LOCK_PREFIX = \"event-sourced-sync\";\n\n/** Server error code that routes a rejected event to the dead-letter queue as a conflict. */\nexport const CONFLICT_ERROR_CODE = \"CONFLICT\";\n","import type { EventSourcedHooks } from \"../core/types\";\nimport type { EventSourcedLogger } from \"../utils/logger\";\n\n/**\n * Invokes a lifecycle hook. A hook that throws is logged and swallowed — user\n * observation code must never be able to fail a sync.\n *\n * @example\n * ```ts\n * const emit: EmitHook = createHookEmitter(\n * { onSyncStart: ({ trigger }) => console.info(\"sync\", trigger) },\n * log,\n * )\n *\n * emit(\"onSyncStart\", { trigger: \"manualSync\" })\n * emit(\"onDeadLetter\", deadLetterEntry) // no-op when that hook is omitted\n * ```\n */\nexport type EmitHook = <TName extends keyof EventSourcedHooks>(\n name: TName,\n argument: Parameters<NonNullable<EventSourcedHooks[TName]>>[0],\n) => void;\n\n/**\n * Builds an {@link EmitHook} bound to the user-supplied {@link EventSourcedHooks}.\n *\n * @example A throwing hook is logged, then ignored\n * ```ts\n * import { createHookEmitter } from \"./hooks\"\n *\n * const emit = createHookEmitter(\n * {\n * onDeadLetter: () => {\n * throw new Error(\"toast failed\")\n * },\n * },\n * log,\n * )\n *\n * emit(\"onDeadLetter\", entry) // sync continues; log.error(\"lifecycle hook threw\", …)\n * ```\n */\nexport function createHookEmitter(\n hooks: EventSourcedHooks | undefined,\n log: EventSourcedLogger,\n): EmitHook {\n if (!hooks) return () => {};\n\n return (name, argument) => {\n const hook = hooks[name] as ((value: unknown) => void) | undefined;\n if (!hook) return;\n\n try {\n hook(argument);\n } catch (err) {\n log.error(\"lifecycle hook threw\", {\n hook: name,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n };\n}\n","import type { Collection } from \"@tanstack/db\";\n\nimport type { DeadLetterEntry, InboxEntry, RowVersionEntry, UpcastableEvent } from \"../core/types\";\nimport { DEFAULT_EVENT_SCHEMA_VERSION, RESERVED_IDS } from \"./constants\";\nimport type { ReplayableEvent, ReplayContext, ReplayOutcome } from \"./types\";\n\nexport type ReplaySummary = { applied: number; skipped: number; deadLettered: number };\n\nexport function rowVersionId(collectionId: string, key: string | number): string {\n return `${collectionId}::${String(key)}`;\n}\n\n/**\n * Records the version a row is now at. Used to stamp `baseVersion` on future\n * local mutations so the server can detect writes authored against stale state.\n */\nexport async function recordRowVersion(\n rowversions: Collection<RowVersionEntry, string>,\n collectionId: string,\n key: string | number,\n version: string,\n globalSeq: number | null,\n): Promise<void> {\n const id = rowVersionId(collectionId, key);\n\n if (rowversions.has(id)) {\n await rowversions.update(id, (draft) => {\n draft.version = version;\n draft.globalSeq = globalSeq;\n }).isPersisted.promise;\n return;\n }\n\n await rowversions.insert({ id, collectionId, key, version, globalSeq }).isPersisted.promise;\n}\n\nexport function readRowVersion(\n rowversions: Collection<RowVersionEntry, string>,\n collectionId: string,\n key: string | number,\n): string | null {\n return rowversions.get(rowVersionId(collectionId, key))?.version ?? null;\n}\n\n/**\n * After a CONFLICT dead-letter, undo the optimistic `recordRowVersion` that\n * stamped the rejected event id as head. Restores `baseVersion`, or deletes\n * the index row when the mutation was authored against an unknown row.\n */\nexport async function restoreRowVersionAfterConflict(\n rowversions: Collection<RowVersionEntry, string>,\n collectionId: string,\n key: string | number,\n baseVersion: string | null,\n): Promise<void> {\n const id = rowVersionId(collectionId, key);\n\n if (baseVersion === null) {\n if (rowversions.has(id)) {\n await rowversions.delete(id).isPersisted.promise;\n }\n return;\n }\n\n await recordRowVersion(rowversions, collectionId, key, baseVersion, null);\n}\n\nexport async function replayEvent(\n context: ReplayContext,\n event: ReplayableEvent,\n): Promise<ReplayOutcome> {\n const { targets, log } = context;\n const { eventId, collectionId } = event;\n\n // A reserved target can never become valid, so retrying it would wedge the\n // pipeline forever. Unknown collections can become valid after a client\n // upgrade, so those honour unknownEventHandling.\n if (RESERVED_IDS.has(collectionId)) {\n const reason = `reserved collection \"${collectionId}\"`;\n log.warn(\"replay skipped: reserved collection\", {\n eventId,\n collectionId,\n type: event.type,\n key: event.key,\n });\n context.emit(\"onEventSkipped\", { eventId, collectionId, reason });\n return { status: \"skipped\", reason };\n }\n\n const migrated = upcast(context, event);\n if (migrated.status !== \"ok\") return migrated.outcome;\n\n const target = targets[collectionId];\n if (!target) {\n return unresolvable(context, event, `unknown collection \"${collectionId}\"`, {\n knownCollections: Object.keys(targets).filter((id) => !RESERVED_IDS.has(id)),\n });\n }\n\n if (!target.utils.acceptMutations) {\n return unresolvable(context, event, `collection \"${collectionId}\" is missing acceptMutations`, {\n targetId: target.id,\n });\n }\n\n const { type, key, payload } = migrated.event;\n\n log.debug(\"replay applying mutation\", { eventId, collectionId, type, key });\n\n try {\n await target.utils.acceptMutations({\n mutations: [\n {\n mutationId: eventId,\n type,\n key,\n modified: payload,\n original: payload,\n changes: payload,\n collection: target,\n },\n ],\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n log.error(\"replay mutation failed\", {\n eventId,\n collectionId,\n type,\n key,\n message: error.message,\n });\n // Reported rather than thrown: one unapplicable event must not be able to\n // wedge the pull pipeline behind it forever.\n return { status: \"failed\", error };\n }\n\n if (context.conflictDetection) {\n await recordRowVersion(\n context.rowversions,\n collectionId,\n key,\n eventId,\n event.globalSeq ?? null,\n );\n }\n\n log.info(\"replay mutation accepted\", { eventId, collectionId, type, key });\n context.emit(\"onEventApplied\", { eventId, collectionId, type, key });\n\n return { status: \"applied\" };\n}\n\ntype UpcastResult =\n | { status: \"ok\"; event: UpcastableEvent }\n | { status: \"blocked\"; outcome: ReplayOutcome };\n\n/**\n * Brings an event authored under a different schema version into the current\n * shape. Without an upcaster, older events are applied as-is (best effort) but\n * newer ones are refused, since this build cannot know what the fields mean.\n */\nfunction upcast(context: ReplayContext, event: ReplayableEvent): UpcastResult {\n const version = event.schemaVersion ?? DEFAULT_EVENT_SCHEMA_VERSION;\n\n const current: UpcastableEvent = {\n eventId: event.eventId,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n payload: event.payload,\n previous: event.previous ?? null,\n schemaVersion: version,\n };\n\n if (version === context.eventSchemaVersion) {\n return { status: \"ok\", event: current };\n }\n\n if (context.upcastEvent) {\n const migrated = context.upcastEvent(current);\n\n if (!migrated) {\n const reason = `upcaster dropped schema version ${version}`;\n context.log.warn(\"replay skipped: upcaster dropped event\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n schemaVersion: version,\n });\n context.emit(\"onEventSkipped\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n reason,\n });\n return { status: \"blocked\", outcome: { status: \"skipped\", reason } };\n }\n\n return { status: \"ok\", event: migrated };\n }\n\n if (version > context.eventSchemaVersion) {\n return {\n status: \"blocked\",\n outcome: unresolvable(\n context,\n event,\n `event schema version ${version} is newer than supported version ${context.eventSchemaVersion}`,\n {},\n ),\n };\n }\n\n context.log.warn(\"replaying older event schema version without an upcaster\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n schemaVersion: version,\n currentVersion: context.eventSchemaVersion,\n });\n\n return { status: \"ok\", event: current };\n}\n\nfunction unresolvable(\n context: ReplayContext,\n event: ReplayableEvent,\n reason: string,\n details: Record<string, unknown>,\n): ReplayOutcome {\n const halt = context.unknownEventHandling === \"fail\";\n\n context.log.warn(halt ? \"replay halted\" : \"replay skipped\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n reason,\n ...details,\n });\n\n if (halt) return { status: \"halted\", reason };\n\n context.emit(\"onEventSkipped\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n reason,\n });\n\n return { status: \"skipped\", reason };\n}\n\n/**\n * What to do after a replay threw: come back to it next sync, or give up and\n * park it in the dead-letter queue.\n */\nexport type ReplayFailureResolution = \"retry\" | \"deadLettered\";\n\n/**\n * Records a failed replay against its inbox row and, once the attempt budget is\n * spent, moves the event to the dead-letter queue and resolves the inbox row so\n * the cursor can advance past it. Otherwise a single event the local schema\n * cannot accept blocks every event behind it, permanently.\n */\nexport async function handleReplayFailure(\n context: ReplayContext,\n inbox: Collection<InboxEntry, string>,\n event: ReplayableEvent,\n error: Error,\n now: number,\n): Promise<ReplayFailureResolution> {\n const { deadletter, maxReplayAttempts, log } = context;\n const existing = inbox.get(event.eventId);\n const attemptCount = (existing?.attemptCount ?? 0) + 1;\n\n if (existing) {\n await inbox.update(event.eventId, (draft) => {\n draft.attemptCount = attemptCount;\n draft.lastError = error.message;\n }).isPersisted.promise;\n }\n\n if (attemptCount < maxReplayAttempts) {\n log.warn(\"replay failed; retrying on a later sync\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n attemptCount,\n maxReplayAttempts,\n message: error.message,\n });\n\n return \"retry\";\n }\n\n const reason = `replay failed after ${attemptCount} attempts: ${error.message}`;\n\n if (!deadletter.has(event.eventId)) {\n const record: DeadLetterEntry = {\n eventId: event.eventId,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n payload: event.payload,\n previous: event.previous ?? null,\n txId: null,\n clientId: existing?.clientId ?? null,\n schemaVersion: event.schemaVersion ?? DEFAULT_EVENT_SCHEMA_VERSION,\n timestamp: existing?.timestamp ?? now,\n localSeq: null,\n globalSeq: event.globalSeq ?? null,\n direction: \"inbound\",\n reason: \"replayFailed\",\n message: error.message,\n code: null,\n attemptCount,\n failedAt: now,\n };\n\n await deadletter.insert(record).isPersisted.promise;\n context.emit(\"onDeadLetter\", record);\n }\n\n if (existing) {\n await resolveInboxEntry(inbox, event.eventId, { status: \"skipped\", reason });\n }\n\n log.warn(\"inbound event dead-lettered\", {\n eventId: event.eventId,\n collectionId: event.collectionId,\n attemptCount,\n message: error.message,\n });\n\n return \"deadLettered\";\n}\n\n/**\n * Marks an inbox row as resolved — either applied or deliberately skipped.\n * Skipped rows still count as resolved so the pull cursor can move past them.\n */\nexport async function resolveInboxEntry(\n inbox: Collection<InboxEntry, string>,\n eventId: string,\n outcome: Extract<ReplayOutcome, { status: \"applied\" | \"skipped\" }>,\n): Promise<void> {\n await inbox.update(eventId, (draft) => {\n draft.sync = true;\n draft.skipped = outcome.status === \"skipped\";\n draft.skipReason = outcome.status === \"skipped\" ? outcome.reason : null;\n }).isPersisted.promise;\n}\n\nexport async function replayInbox(\n inbox: Collection<InboxEntry, string>,\n context: ReplayContext,\n): Promise<ReplaySummary> {\n const { log } = context;\n\n const pending = [...inbox.state.values()]\n .filter((entry) => !entry.sync)\n .sort((a, b) => a.globalSeq - b.globalSeq);\n\n log.info(\"pending inbox replay started\", { pendingCount: pending.length });\n\n let applied = 0;\n let skipped = 0;\n let deadLettered = 0;\n\n for (const entry of pending) {\n const outcome = await replayEvent(context, entry);\n\n if (outcome.status === \"halted\") break;\n\n if (outcome.status === \"failed\") {\n const resolution = await handleReplayFailure(\n context,\n inbox,\n entry,\n outcome.error,\n Date.now(),\n );\n\n // Still retryable: stop here so events stay in order until it resolves.\n if (resolution === \"retry\") break;\n\n deadLettered++;\n continue;\n }\n\n await resolveInboxEntry(inbox, entry.eventId, outcome);\n\n if (outcome.status === \"skipped\") {\n skipped++;\n continue;\n }\n\n applied++;\n\n log.info(\"pending inbox replay applied\", {\n eventId: entry.eventId,\n globalSeq: entry.globalSeq,\n collectionId: entry.collectionId,\n });\n }\n\n log.info(\"pending inbox replay finished\", { applied, skipped, deadLettered });\n\n return { applied, skipped, deadLettered };\n}\n","import type { Collection } from \"@tanstack/db\";\n\nimport type { InboxEntry, SyncMetaEntry } from \"../core/types\";\nimport { SYNCMETA_KEY } from \"./constants\";\n\nconst EMPTY: SyncMetaEntry = {\n id: SYNCMETA_KEY,\n backendId: null,\n clientId: null,\n pullCursor: 0,\n lastSyncAt: null,\n lastError: null,\n};\n\nexport function readSyncMeta(syncmeta: Collection<SyncMetaEntry, string>): SyncMetaEntry {\n return syncmeta.get(SYNCMETA_KEY) ?? EMPTY;\n}\n\nexport async function ensureSyncMeta(syncmeta: Collection<SyncMetaEntry, string>): Promise<void> {\n if (syncmeta.has(SYNCMETA_KEY)) return;\n await syncmeta.insert({ ...EMPTY }).isPersisted.promise;\n}\n\nasync function patch(\n syncmeta: Collection<SyncMetaEntry, string>,\n apply: (draft: SyncMetaEntry) => void,\n): Promise<void> {\n await ensureSyncMeta(syncmeta);\n await syncmeta.update(SYNCMETA_KEY, apply).isPersisted.promise;\n}\n\nexport function readBackendId(syncmeta: Collection<SyncMetaEntry, string>): string | null {\n return readSyncMeta(syncmeta).backendId;\n}\n\n/**\n * Resolves this device's identity, persisting it on first run so it survives\n * reloads. Without that, a client stops recognising its own pulled-back events\n * as soon as the outbox is pruned and will replay its own history as if it were\n * someone else's.\n *\n * Preference: explicit `configured` id, then the stored `syncmeta.clientId`,\n * then `generate()`.\n *\n * @example First open generates and stores an id; the next open reuses it\n * ```ts\n * import { resolveClientId } from \"./sync-meta\"\n *\n * const clientId = await resolveClientId(\n * db.collections.syncmeta,\n * undefined,\n * () => crypto.randomUUID(),\n * )\n * // later, after close + reopen on the same file:\n * const again = await resolveClientId(db.collections.syncmeta, undefined, () => {\n * throw new Error(\"should not generate a second id\")\n * })\n * // again === clientId\n * ```\n */\nexport async function resolveClientId(\n syncmeta: Collection<SyncMetaEntry, string>,\n configured: string | undefined,\n generate: () => string,\n): Promise<string> {\n const stored = readSyncMeta(syncmeta).clientId ?? null;\n const clientId = configured ?? stored ?? generate();\n\n if (stored !== clientId) {\n await patch(syncmeta, (draft) => {\n draft.clientId = clientId;\n });\n }\n\n return clientId;\n}\n\n/**\n * The cursor lives in `syncmeta` so the inbox can be pruned freely. Older\n * databases predate that row, so fall back to the highest resolved inbox\n * sequence and let the first successful pull persist it.\n *\n * @example\n * ```ts\n * import { readPullCursor } from \"./sync-meta\"\n *\n * const since = readPullCursor(db.collections.syncmeta, db.collections.inbox)\n * const page = await transport.pull(since)\n * ```\n */\nexport function readPullCursor(\n syncmeta: Collection<SyncMetaEntry, string>,\n inbox: Collection<InboxEntry, string>,\n): number {\n let cursor = readSyncMeta(syncmeta).pullCursor;\n\n for (const entry of inbox.state.values()) {\n if (entry.sync && entry.globalSeq > cursor) cursor = entry.globalSeq;\n }\n\n return cursor;\n}\n\nexport async function writePullCursor(\n syncmeta: Collection<SyncMetaEntry, string>,\n cursor: number,\n): Promise<void> {\n await patch(syncmeta, (draft) => {\n draft.pullCursor = cursor;\n });\n}\n\nexport async function writeBackendId(\n syncmeta: Collection<SyncMetaEntry, string>,\n backendId: string,\n): Promise<void> {\n await patch(syncmeta, (draft) => {\n draft.backendId = backendId;\n });\n}\n\nexport async function writeSyncOutcome(\n syncmeta: Collection<SyncMetaEntry, string>,\n lastSyncAt: number,\n lastError: string | null,\n): Promise<void> {\n await patch(syncmeta, (draft) => {\n draft.lastSyncAt = lastSyncAt;\n draft.lastError = lastError;\n });\n}\n","import type { Collection } from \"@tanstack/db\";\n\nimport type { NormalizedSyncTransport } from \"../core/sync\";\nimport type {\n BackendMismatchPolicy,\n InboxEntry,\n OutboxEntry,\n ServerEvent,\n SyncMetaEntry,\n} from \"../core/types\";\nimport { DEFAULT_EVENT_SCHEMA_VERSION } from \"./constants\";\nimport { handleReplayFailure, replayEvent, resolveInboxEntry } from \"./replay\";\nimport { readBackendId, readPullCursor, writeBackendId, writePullCursor } from \"./sync-meta\";\nimport type { ReplayContext } from \"./types\";\n\nexport type PullOutcome = {\n pulled: number;\n skipped: number;\n deadLettered: number;\n /** Outbox events put back in the queue by a backend reset, awaiting a push. */\n requeued: number;\n};\n\n/**\n * Thrown when `backendMismatch` is `\"fail\"` and the server reports a different\n * `backendId` than this client last synced with (wiped or swapped event store).\n *\n * @example\n * ```ts\n * import { BackendMismatchError } from \"event-sourced-collection\"\n *\n * const result = await db.sync()\n * const mismatch = result.errors.find((error) => error instanceof BackendMismatchError)\n * if (mismatch) {\n * console.error(\"backend changed\", mismatch.expected, mismatch.received)\n * }\n * ```\n */\nexport class BackendMismatchError extends Error {\n constructor(\n readonly expected: string | null,\n readonly received: string,\n ) {\n super(\n `Sync backend identity changed (was \"${expected ?? \"unknown\"}\", now \"${received}\"). ` +\n `The local pull cursor no longer refers to this backend.`,\n );\n this.name = \"BackendMismatchError\";\n }\n}\n\nexport type PullArgs = {\n outbox: Collection<OutboxEntry, string>;\n inbox: Collection<InboxEntry, string>;\n syncmeta: Collection<SyncMetaEntry, string>;\n pull: NonNullable<NormalizedSyncTransport[\"pull\"]>;\n clientId: string;\n pullOverlap: number;\n /** When false, local-origin pulls advance the cursor without inbox rows. */\n recordLocalEchoes: boolean;\n backendMismatch: BackendMismatchPolicy;\n context: ReplayContext;\n};\n\nexport async function pullInbox(args: PullArgs): Promise<PullOutcome> {\n const { outbox, inbox, syncmeta, pull, clientId, pullOverlap, recordLocalEchoes, context } =\n args;\n const { log } = context;\n\n let pulled = 0;\n let skipped = 0;\n let deadLettered = 0;\n let requeued = 0;\n let hasMore = true;\n let cursor = Math.max(0, readPullCursor(syncmeta, inbox) - pullOverlap);\n let identityChecked = false;\n\n while (hasMore) {\n log.debug(\"pull inbox page\", { since: cursor });\n\n const response = await pull(cursor);\n\n log.debug(\"pull inbox response\", {\n since: cursor,\n eventCount: response.events.length,\n hasMore: response.hasMore,\n cursor: response.cursor,\n backendId: response.backendId,\n });\n\n if (!identityChecked && response.backendId !== undefined) {\n identityChecked = true;\n\n const reconciled = await reconcileBackendIdentity(args, response.backendId);\n if (reconciled.reset) {\n requeued += reconciled.requeued;\n cursor = 0;\n continue;\n }\n }\n\n if (response.events.length === 0) break;\n\n const sorted = [...response.events].sort((a, b) => a.globalSeq - b.globalSeq);\n let halted = false;\n\n for (const event of sorted) {\n if (isLocalOrigin(event, outbox, clientId)) {\n if (recordLocalEchoes) {\n await markInboxEventResolved(inbox, event);\n }\n\n log.debug(\"pull skipped: event originated locally\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n recorded: recordLocalEchoes,\n });\n continue;\n }\n\n const existing = inbox.get(event.eventId);\n if (existing?.sync) {\n log.debug(\"pull skipped: inbox already resolved\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n });\n continue;\n }\n\n if (!existing) {\n await inbox.insert(toInboxEntry(event, false)).isPersisted.promise;\n log.debug(\"inbox entry inserted\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n });\n }\n\n const outcome = await replayEvent(context, event);\n\n if (outcome.status === \"halted\") {\n halted = true;\n break;\n }\n\n if (outcome.status === \"failed\") {\n const resolution = await handleReplayFailure(\n context,\n inbox,\n event,\n outcome.error,\n Date.now(),\n );\n\n if (resolution === \"retry\") {\n halted = true;\n break;\n }\n\n deadLettered++;\n continue;\n }\n\n await resolveInboxEntry(inbox, event.eventId, outcome);\n\n if (outcome.status === \"skipped\") {\n skipped++;\n continue;\n }\n\n log.info(\"pull replay applied\", {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n });\n\n pulled++;\n }\n\n // Leaving the cursor where it is means the halted event is retried on the\n // next sync rather than being stepped over.\n if (halted) break;\n\n const nextCursor = resolveNextCursor(cursor, sorted, response.cursor);\n\n if (nextCursor <= cursor) {\n log.warn(\"pull stopped: cursor did not advance\", {\n cursor,\n nextCursor,\n eventCount: sorted.length,\n });\n break;\n }\n\n cursor = nextCursor;\n await writePullCursor(syncmeta, cursor);\n hasMore = response.hasMore;\n }\n\n log.info(\"pull inbox finished\", { pulled, skipped, deadLettered, requeued, cursor });\n\n return { pulled, skipped, deadLettered, requeued };\n}\n\ntype ReconcileOutcome = { reset: false } | { reset: true; requeued: number };\n\n/**\n * Detects a wiped or swapped backend. Reports `reset` when the caller should\n * restart the pull from zero.\n */\nasync function reconcileBackendIdentity(\n args: PullArgs,\n received: string,\n): Promise<ReconcileOutcome> {\n const { syncmeta, inbox, backendMismatch, context } = args;\n const { log } = context;\n\n const stored = readBackendId(syncmeta);\n\n if (stored === received) return { reset: false };\n\n if (stored === null) {\n await writeBackendId(syncmeta, received);\n log.info(\"sync backend identity recorded\", { backendId: received });\n return { reset: false };\n }\n\n context.emit(\"onBackendMismatch\", {\n expected: stored,\n received,\n policy: backendMismatch,\n });\n\n if (backendMismatch === \"fail\") {\n throw new BackendMismatchError(stored, received);\n }\n\n if (backendMismatch === \"ignore\") {\n log.warn(\"sync backend identity changed; ignoring per configuration\", {\n expected: stored,\n received,\n });\n return { reset: false };\n }\n\n log.warn(\"sync backend identity changed; resetting pull cursor\", {\n expected: stored,\n received,\n });\n\n for (const entry of inbox.state.values()) {\n await inbox.delete(entry.eventId).isPersisted.promise;\n }\n\n const requeued = await requeueSyncedOutbox(args.outbox);\n\n await writeBackendId(syncmeta, received);\n await writePullCursor(syncmeta, 0);\n\n log.info(\"sync backend identity reset complete\", {\n backendId: received,\n inboxCleared: true,\n outboxRequeued: requeued,\n });\n\n return { reset: true, requeued };\n}\n\n/**\n * Marks previously confirmed outbox events as pending again. The new backend\n * has never seen them, so without this the client keeps local rows it believes\n * are safely synced and never uploads them again. Servers deduplicate by\n * `eventId`, so re-pushing to a *restored* backend is a no-op.\n *\n * `baseVersion` is cleared because it refers to event ids from the old\n * backend's history, which the new one cannot resolve.\n */\nasync function requeueSyncedOutbox(outbox: Collection<OutboxEntry, string>): Promise<number> {\n const synced = [...outbox.state.values()].filter((entry) => entry.sync);\n\n for (const entry of synced) {\n await outbox.update(entry.eventId, (draft) => {\n draft.sync = false;\n draft.syncStatus = \"pending\";\n draft.globalSeq = null;\n draft.baseVersion = null;\n draft.attemptCount = 0;\n draft.nextAttemptAt = null;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n }).isPersisted.promise;\n }\n\n return synced.length;\n}\n\nfunction isLocalOrigin(\n event: ServerEvent,\n outbox: Collection<OutboxEntry, string>,\n clientId: string,\n): boolean {\n // clientId keeps origin detection working after the outbox has been pruned.\n return outbox.has(event.eventId) || event.clientId === clientId;\n}\n\nfunction resolveNextCursor(\n cursor: number,\n events: ReadonlyArray<ServerEvent>,\n serverCursor: string | undefined,\n): number {\n let next = cursor;\n\n for (const event of events) {\n if (event.globalSeq > next) next = event.globalSeq;\n }\n\n const parsed = Number(serverCursor);\n if (serverCursor !== undefined && Number.isFinite(parsed) && parsed > next) {\n next = parsed;\n }\n\n return next;\n}\n\nasync function markInboxEventResolved(\n inbox: Collection<InboxEntry, string>,\n event: ServerEvent,\n): Promise<void> {\n const existing = inbox.get(event.eventId);\n\n if (!existing) {\n await inbox.insert(toInboxEntry(event, true)).isPersisted.promise;\n return;\n }\n\n if (!existing.sync || existing.globalSeq !== event.globalSeq) {\n await inbox.update(event.eventId, (draft) => {\n draft.globalSeq = event.globalSeq;\n draft.sync = true;\n }).isPersisted.promise;\n }\n}\n\nexport function toInboxEntry(event: ServerEvent, sync: boolean): InboxEntry {\n return {\n eventId: event.eventId,\n globalSeq: event.globalSeq,\n collectionId: event.collectionId,\n type: event.type,\n key: event.key,\n payload: event.payload,\n previous: event.previous ?? null,\n clientId: event.clientId ?? null,\n schemaVersion: event.schemaVersion ?? DEFAULT_EVENT_SCHEMA_VERSION,\n timestamp: event.timestamp,\n sync,\n skipped: false,\n skipReason: null,\n attemptCount: 0,\n lastError: null,\n };\n}\n","import type {\n OutboundEvent,\n PullEventsFn,\n PullResponse,\n PushConfirmation,\n PushEventsFn,\n PushResponse,\n SyncHandlersConfig,\n SyncTransport,\n SyncUrlConfig,\n} from \"./types\";\n\nexport type NormalizedSyncTransport = {\n push?: PushEventsFn;\n pull?: (since: number) => Promise<PullResponse>;\n};\n\ntype HeaderConfig = SyncHandlersConfig[\"headers\"];\n\n/**\n * Builds a {@link SyncTransport} that POSTs outbound events and GETs\n * `pull?since=N`. Pass this as `sync` when you already have REST endpoints.\n *\n * @example\n * ```ts\n * import { createHttpTransport } from \"event-sourced-collection\"\n * import { createBrowserEventSourcedDB } from \"event-sourced-collection/browser\"\n *\n * const sync = createHttpTransport({\n * push: \"/api/sync/events\",\n * pull: \"/api/sync/events\",\n * headers: () => ({ Authorization: `Bearer ${localStorage.getItem(\"token\") ?? \"\"}` }),\n * })\n *\n * const { ensureDb } = createBrowserEventSourcedDB({\n * databaseName: \"app.sqlite\",\n * collections: { todos: { getKey: (todo: { id: string }) => todo.id } },\n * sync,\n * modules: async () => {\n * const { createCollection } = await import(\"@tanstack/db\")\n * const persistence = await import(\"@tanstack/browser-db-sqlite-persistence\")\n * return { createCollection, ...persistence }\n * },\n * })\n * ```\n */\nexport function createHttpTransport(config: SyncUrlConfig): SyncTransport {\n return {\n push: createHttpPushEvents(config.push, config.headers),\n pull: createHttpPullEvents(config.pull, config.headers),\n };\n}\n\nexport function createSyncTransport(\n config?: SyncHandlersConfig | SyncUrlConfig | SyncTransport,\n): NormalizedSyncTransport | null {\n if (!config) {\n return null;\n }\n\n if (isTransport(config)) {\n return {\n push: config.push,\n pull: config.pull,\n };\n }\n\n const pushUrl = getPushUrl(config);\n const pullUrl = getPullUrl(config);\n\n const push =\n \"pushEvents\" in config && config.pushEvents\n ? config.pushEvents\n : pushUrl\n ? createHttpPushEvents(pushUrl, config.headers)\n : undefined;\n\n const pullEvents = \"pullEvents\" in config ? config.pullEvents : undefined;\n const pull = pullEvents\n ? createPullFromHandler(pullEvents)\n : pullUrl\n ? createHttpPullEvents(pullUrl, config.headers)\n : undefined;\n\n if (!push && !pull) {\n return null;\n }\n\n return { push, pull };\n}\n\nexport function normalizePushResponse(\n response: PushResponse | ReadonlyArray<PushConfirmation>,\n): PushResponse {\n if (isConfirmationArray(response)) {\n return { confirmed: response };\n }\n\n return {\n confirmed: response.confirmed,\n failed: response.failed,\n };\n}\n\nfunction isConfirmationArray(\n response: PushResponse | ReadonlyArray<PushConfirmation>,\n): response is ReadonlyArray<PushConfirmation> {\n return Array.isArray(response);\n}\n\nfunction createHttpPushEvents(url: string, headers: HeaderConfig): PushEventsFn {\n return async (events: ReadonlyArray<OutboundEvent>): Promise<PushResponse> => {\n if (events.length === 0) return { confirmed: [] };\n\n const resolvedHeaders = await resolveHeaders(headers);\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...resolvedHeaders },\n body: JSON.stringify(events),\n });\n\n if (!response.ok) {\n throw new SyncPushError(response.status, await response.text());\n }\n\n return response.json() as Promise<PushResponse>;\n };\n}\n\nfunction createPullFromHandler(pullEvents: PullEventsFn): (since: number) => Promise<PullResponse> {\n return (since: number) => pullEvents({ since });\n}\n\nfunction createHttpPullEvents(\n url: string,\n headers: HeaderConfig,\n): (since: number) => Promise<PullResponse> {\n return async (since: number): Promise<PullResponse> => {\n const resolvedHeaders = await resolveHeaders(headers);\n const pullUrl = appendSince(url, since);\n\n const response = await fetch(pullUrl, {\n headers: { Accept: \"application/json\", ...resolvedHeaders },\n });\n\n if (!response.ok) {\n throw new SyncPullError(response.status, await response.text());\n }\n\n return response.json() as Promise<PullResponse>;\n };\n}\n\nasync function resolveHeaders(headers: HeaderConfig): Promise<Record<string, string>> {\n if (!headers) return {};\n if (typeof headers === \"function\") return headers();\n return headers;\n}\n\nfunction appendSince(url: string, since: number): string {\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}since=${encodeURIComponent(String(since))}`;\n}\n\nfunction getPushUrl(config: SyncHandlersConfig | SyncUrlConfig): string | undefined {\n if (\"pushUrl\" in config && config.pushUrl) {\n return config.pushUrl;\n }\n\n if (\"push\" in config && typeof config.push === \"string\") {\n return config.push;\n }\n\n return undefined;\n}\n\nfunction getPullUrl(config: SyncHandlersConfig | SyncUrlConfig): string | undefined {\n if (\"pullUrl\" in config && config.pullUrl) {\n return config.pullUrl;\n }\n\n if (\"pull\" in config && typeof config.pull === \"string\") {\n return config.pull;\n }\n\n return undefined;\n}\n\n/**\n * Thrown when the HTTP push endpoint returns a non-OK status.\n *\n * @example\n * ```ts\n * import { createHttpTransport, SyncPushError } from \"event-sourced-collection\"\n *\n * const sync = createHttpTransport({\n * push: \"/api/sync/events\",\n * pull: \"/api/sync/events\",\n * })\n *\n * try {\n * await sync.push(events)\n * } catch (error) {\n * if (error instanceof SyncPushError) {\n * console.error(error.status, error.body)\n * }\n * }\n * ```\n */\nexport class SyncPushError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n ) {\n super(`Event push failed: HTTP ${status}`);\n this.name = \"SyncPushError\";\n }\n}\n\n/**\n * Thrown when the HTTP pull endpoint returns a non-OK status.\n *\n * @example\n * ```ts\n * import { createHttpTransport, SyncPullError } from \"event-sourced-collection\"\n *\n * const sync = createHttpTransport({\n * push: \"/api/sync/events\",\n * pull: \"/api/sync/events\",\n * })\n *\n * try {\n * await sync.pull(0)\n * } catch (error) {\n * if (error instanceof SyncPullError) {\n * console.error(error.status, error.body)\n * }\n * }\n * ```\n */\nexport class SyncPullError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n ) {\n super(`Event pull failed: HTTP ${status}`);\n this.name = \"SyncPullError\";\n }\n}\n\nexport function isTransport(\n value: SyncHandlersConfig | SyncUrlConfig | SyncTransport,\n): value is SyncTransport {\n return \"push\" in value && typeof value.push === \"function\";\n}\n","import type { Collection } from \"@tanstack/db\";\n\nimport type { NormalizedSyncTransport } from \"../core/sync\";\nimport { normalizePushResponse } from \"../core/sync\";\nimport type {\n DeadLetterEntry,\n DeadLetterReason,\n OutboundEvent,\n OutboxEntry,\n PushFailure,\n RowVersionEntry,\n} from \"../core/types\";\nimport type { EventSourcedLogger } from \"../utils/logger\";\nimport { CONFLICT_ERROR_CODE, DEFAULT_EVENT_SCHEMA_VERSION } from \"./constants\";\nimport type { EmitHook } from \"./hooks\";\nimport { restoreRowVersionAfterConflict } from \"./replay\";\nimport type { ResolvedRetryConfig } from \"./types\";\n\nexport type PushOutcome = {\n pushed: number;\n deadLettered: number;\n /**\n * Set when a batch failed at the transport level. Reported rather than thrown\n * so the counts from batches that already succeeded are not lost.\n */\n error?: Error;\n};\n\nexport type PushArgs = {\n outbox: Collection<OutboxEntry, string>;\n deadletter: Collection<DeadLetterEntry, string>;\n rowversions: Collection<RowVersionEntry, string>;\n push: NonNullable<NormalizedSyncTransport[\"push\"]>;\n batchSize: number;\n retry: ResolvedRetryConfig;\n now: number;\n emit: EmitHook;\n log: EventSourcedLogger;\n};\n\n/**\n * Backoff for attempt N, doubling from `baseDelayMs` and capped at `maxDelayMs`.\n * Deliberately jitter-free so retry scheduling stays reproducible in tests; if\n * you have many clients hitting one server, add jitter in your push handler.\n */\nexport function backoffDelay(attemptCount: number, retry: ResolvedRetryConfig): number {\n const exponent = Math.max(0, attemptCount - 1);\n const raw = retry.baseDelayMs * 2 ** exponent;\n return Math.min(retry.maxDelayMs, raw);\n}\n\nfunction isDue(entry: OutboxEntry, now: number): boolean {\n if (entry.sync) return false;\n // A permanent rejection is dead-lettered rather than parked, so anything still\n // marked failed here is retryable and just waiting for its backoff window.\n if (entry.syncStatus === \"failed\" && entry.retryable !== true) return false;\n return (entry.nextAttemptAt ?? 0) <= now;\n}\n\nfunction byLocalSeq(a: OutboxEntry, b: OutboxEntry): number {\n if (a.localSeq !== b.localSeq) return a.localSeq - b.localSeq;\n // localSeq can collide across tabs; eventId is a uuidv7 so it breaks the tie\n // in creation order.\n return a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0;\n}\n\nexport function toOutboundEvent(entry: OutboxEntry): OutboundEvent {\n return {\n eventId: entry.eventId,\n collectionId: entry.collectionId,\n type: entry.type,\n key: entry.key,\n payload: entry.payload,\n previous: entry.previous ?? null,\n txId: entry.txId,\n clientId: entry.clientId,\n // Rows written before these fields existed are still in persisted outboxes.\n schemaVersion: entry.schemaVersion ?? DEFAULT_EVENT_SCHEMA_VERSION,\n baseVersion: entry.baseVersion ?? null,\n timestamp: entry.timestamp,\n };\n}\n\nexport async function deadLetter(\n deadletter: Collection<DeadLetterEntry, string>,\n outbox: Collection<OutboxEntry, string>,\n entry: OutboxEntry,\n reason: DeadLetterReason,\n message: string,\n code: string | null,\n now: number,\n emit: EmitHook,\n log: EventSourcedLogger,\n rowversions?: Collection<RowVersionEntry, string>,\n): Promise<void> {\n const record: DeadLetterEntry = {\n eventId: entry.eventId,\n collectionId: entry.collectionId,\n type: entry.type,\n key: entry.key,\n payload: entry.payload,\n previous: entry.previous ?? null,\n txId: entry.txId,\n clientId: entry.clientId,\n schemaVersion: entry.schemaVersion ?? DEFAULT_EVENT_SCHEMA_VERSION,\n baseVersion: entry.baseVersion ?? null,\n timestamp: entry.timestamp,\n localSeq: entry.localSeq,\n globalSeq: entry.globalSeq ?? null,\n direction: \"outbound\",\n reason,\n message,\n code,\n attemptCount: entry.attemptCount,\n failedAt: now,\n };\n\n if (!deadletter.has(entry.eventId)) {\n await deadletter.insert(record).isPersisted.promise;\n }\n\n if (outbox.has(entry.eventId)) {\n await outbox.delete(entry.eventId).isPersisted.promise;\n }\n\n if (reason === \"conflict\" && rowversions) {\n await restoreRowVersionAfterConflict(\n rowversions,\n entry.collectionId,\n entry.key,\n entry.baseVersion ?? null,\n );\n }\n\n log.warn(\"event dead-lettered\", {\n eventId: entry.eventId,\n collectionId: entry.collectionId,\n reason,\n message,\n code,\n attemptCount: entry.attemptCount,\n });\n\n emit(\"onDeadLetter\", record);\n}\n\n/**\n * Splits events into batches of at most `size`, without ever separating events\n * that share a `txId`. The server is asked to commit each transaction\n * atomically, which it cannot do if we deliver half of one now and half later.\n * A single transaction larger than `size` is sent whole rather than split.\n */\nfunction batchByTransaction(events: ReadonlyArray<OutboxEntry>, size: number): OutboxEntry[][] {\n const batches: OutboxEntry[][] = [];\n let current: OutboxEntry[] = [];\n\n for (let index = 0; index < events.length;) {\n const txId = events[index]!.txId;\n const group: OutboxEntry[] = [];\n\n // Events are ordered by localSeq, so one transaction's events are adjacent.\n while (index < events.length && events[index]!.txId === txId) {\n group.push(events[index]!);\n index++;\n }\n\n if (current.length > 0 && current.length + group.length > size) {\n batches.push(current);\n current = [];\n }\n\n current.push(...group);\n\n if (current.length >= size) {\n batches.push(current);\n current = [];\n }\n }\n\n if (current.length > 0) batches.push(current);\n\n return batches;\n}\n\n/**\n * Pushes due outbox events in batches. Each batch's result is persisted before\n * the next is sent, so a failure part-way through keeps the progress already\n * made instead of restarting from zero.\n */\nexport async function pushOutbox(args: PushArgs): Promise<PushOutcome> {\n const { outbox, push, batchSize, now, log } = args;\n\n const due = [...outbox.state.values()].filter((entry) => isDue(entry, now)).sort(byLocalSeq);\n\n log.debug(\"push outbox\", { dueCount: due.length, batchSize });\n\n if (due.length === 0) return { pushed: 0, deadLettered: 0 };\n\n let pushed = 0;\n let deadLettered = 0;\n\n for (const batch of batchByTransaction(due, batchSize)) {\n const attempted: OutboxEntry[] = [];\n\n for (const entry of batch) {\n const attemptCount = (entry.attemptCount ?? 0) + 1;\n\n await outbox.update(entry.eventId, (draft) => {\n draft.syncStatus = \"pending\";\n draft.attemptCount = attemptCount;\n draft.lastAttemptAt = now;\n draft.nextAttemptAt = null;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n }).isPersisted.promise;\n\n attempted.push({ ...entry, attemptCount });\n }\n\n let response;\n try {\n response = normalizePushResponse(await push(attempted.map(toOutboundEvent)));\n } catch (err) {\n // Transport-level failure: nothing in this batch was acknowledged, so back\n // every event off together and stop. Batches already confirmed stay synced.\n deadLettered += await backoffBatch(args, attempted, err);\n const error = err instanceof Error ? err : new Error(String(err));\n log.warn(\"push stopped after transport failure\", {\n message: error.message,\n pushed,\n remaining: due.length - pushed,\n });\n return { pushed, deadLettered, error };\n }\n\n log.info(\"push batch confirmed\", {\n sent: attempted.length,\n confirmed: response.confirmed.length,\n failed: response.failed?.length ?? 0,\n });\n\n for (const confirmation of response.confirmed) {\n if (!outbox.has(confirmation.eventId)) continue;\n\n await outbox.update(confirmation.eventId, (draft) => {\n draft.sync = true;\n draft.syncStatus = \"synced\";\n draft.globalSeq = confirmation.globalSeq;\n draft.nextAttemptAt = null;\n draft.lastError = null;\n draft.lastErrorCode = null;\n draft.retryable = null;\n }).isPersisted.promise;\n\n pushed++;\n args.emit(\"onEventPushed\", {\n eventId: confirmation.eventId,\n globalSeq: confirmation.globalSeq,\n });\n }\n\n const byId = new Map(attempted.map((entry) => [entry.eventId, entry]));\n\n for (const failure of response.failed ?? []) {\n const entry = byId.get(failure.eventId);\n if (!entry) continue;\n\n deadLettered += await handleFailure(args, entry, failure);\n }\n }\n\n return { pushed, deadLettered };\n}\n\n/** Returns 1 when the event was dead-lettered, 0 when it stays queued for retry. */\nasync function handleFailure(\n args: PushArgs,\n entry: OutboxEntry,\n failure: PushFailure,\n): Promise<number> {\n const { outbox, deadletter, rowversions, retry, now, emit, log } = args;\n const code = failure.code ?? null;\n\n if (failure.retryable !== true) {\n const reason: DeadLetterReason = code === CONFLICT_ERROR_CODE ? \"conflict\" : \"rejected\";\n await deadLetter(\n deadletter,\n outbox,\n entry,\n reason,\n failure.message,\n code,\n now,\n emit,\n log,\n rowversions,\n );\n return 1;\n }\n\n if (entry.attemptCount >= retry.maxAttempts) {\n await deadLetter(\n deadletter,\n outbox,\n entry,\n \"maxAttemptsExceeded\",\n failure.message,\n code,\n now,\n emit,\n log,\n rowversions,\n );\n return 1;\n }\n\n const delay = backoffDelay(entry.attemptCount, retry);\n\n await outbox.update(entry.eventId, (draft) => {\n draft.sync = false;\n draft.syncStatus = \"failed\";\n draft.retryable = true;\n draft.lastError = failure.message;\n draft.lastErrorCode = code;\n draft.nextAttemptAt = now + delay;\n }).isPersisted.promise;\n\n log.warn(\"push retry scheduled\", {\n eventId: entry.eventId,\n attemptCount: entry.attemptCount,\n retryInMs: delay,\n message: failure.message,\n });\n\n return 0;\n}\n\n/** Applies retry backoff to a whole batch after a transport-level failure. */\nasync function backoffBatch(\n args: PushArgs,\n batch: ReadonlyArray<OutboxEntry>,\n err: unknown,\n): Promise<number> {\n const message = err instanceof Error ? err.message : String(err);\n let deadLettered = 0;\n\n for (const entry of batch) {\n deadLettered += await handleFailure(args, entry, {\n eventId: entry.eventId,\n message,\n retryable: true,\n });\n }\n\n return deadLettered;\n}\n","/**\n * Serializes async work so overlapping callers run one after another instead of\n * concurrently. Used to stop two `sync()` calls from reading the same pending\n * set and pushing it twice.\n *\n * A rejected job does not stall the queue: the next caller still runs.\n *\n * @example Overlapping syncs run in order\n * ```ts\n * import { createSerialQueue } from \"./serial-queue\"\n *\n * const runExclusive = createSerialQueue()\n *\n * const first = runExclusive(async () => {\n * await pushPending()\n * return \"first\"\n * })\n * const second = runExclusive(async () => {\n * await pushPending()\n * return \"second\"\n * })\n *\n * await Promise.all([first, second]) // \"first\" finishes before \"second\" starts\n * ```\n */\nexport function createSerialQueue(): <T>(fn: () => Promise<T>) => Promise<T> {\n let tail: Promise<unknown> = Promise.resolve();\n\n return <T>(fn: () => Promise<T>): Promise<T> => {\n const result = tail.then(fn, fn);\n tail = result.catch(() => undefined);\n return result;\n };\n}\n","export type EventSourcedLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\n/**\n * Structured logger used when `debug` is enabled on a DB factory.\n *\n * @example Custom sink\n * ```ts\n * import type { EventSourcedLogger } from \"event-sourced-collection\"\n *\n * const logger: EventSourcedLogger = {\n * debug: (message, data) => console.debug(message, data),\n * info: (message, data) => console.info(message, data),\n * warn: (message, data) => console.warn(message, data),\n * error: (message, data) => console.error(message, data),\n * }\n * ```\n */\nexport type EventSourcedLogger = {\n debug: (message: string, data?: Record<string, unknown>) => void;\n info: (message: string, data?: Record<string, unknown>) => void;\n warn: (message: string, data?: Record<string, unknown>) => void;\n error: (message: string, data?: Record<string, unknown>) => void;\n};\n\nconst noopLogger: EventSourcedLogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nconst LOG_PREFIX = \"[event-sourced]\";\n\n/**\n * Builds the logger used internally when you pass `debug` to a DB factory.\n * `false` / omitted is a no-op logger; `true` logs to `console`; an object is\n * used as-is.\n *\n * @example\n * ```ts\n * import { createEventSourcedLogger } from \"event-sourced-collection\"\n * import { createBrowserEventSourcedDB } from \"event-sourced-collection/browser\"\n *\n * const debug = createEventSourcedLogger(import.meta.env.DEV)\n *\n * createBrowserEventSourcedDB({\n * databaseName: \"app.sqlite\",\n * debug,\n * collections: { todos: { getKey: (todo: { id: string }) => todo.id } },\n * modules: async () => {\n * const { createCollection } = await import(\"@tanstack/db\")\n * const persistence = await import(\"@tanstack/browser-db-sqlite-persistence\")\n * return { createCollection, ...persistence }\n * },\n * })\n * ```\n */\nexport function createEventSourcedLogger(debug?: boolean | EventSourcedLogger): EventSourcedLogger {\n if (debug === undefined || debug === false) {\n return noopLogger;\n }\n\n if (typeof debug === \"object\") {\n return debug;\n }\n\n return {\n debug: (message, data) => {\n if (data === undefined) {\n console.debug(LOG_PREFIX, message);\n return;\n }\n console.debug(LOG_PREFIX, message, data);\n },\n info: (message, data) => {\n if (data === undefined) {\n console.info(LOG_PREFIX, message);\n return;\n }\n console.info(LOG_PREFIX, message, data);\n },\n warn: (message, data) => {\n if (data === undefined) {\n console.warn(LOG_PREFIX, message);\n return;\n }\n console.warn(LOG_PREFIX, message, data);\n },\n error: (message, data) => {\n if (data === undefined) {\n console.error(LOG_PREFIX, message);\n return;\n }\n console.error(LOG_PREFIX, message, data);\n },\n };\n}\n","import { uuidv7 } from \"uuidv7\";\n\n/**\n * Time-ordered event id (UUIDv7). Used as `eventId` on outbox rows and as the\n * default device `clientId` when you do not pass one.\n *\n * @example\n * ```ts\n * import { generateEventId } from \"event-sourced-collection\"\n *\n * const eventId = generateEventId()\n * const clientId = generateEventId()\n * ```\n */\nexport function generateEventId(): string {\n return uuidv7();\n}\n","import type { Collection } from \"@tanstack/db\";\n\nimport {\n DEADLETTER_ID,\n DEFAULT_BASE_DELAY_MS,\n DEFAULT_EVENT_SCHEMA_VERSION,\n DEFAULT_MAX_ATTEMPTS,\n DEFAULT_MAX_DELAY_MS,\n DEFAULT_PUSH_BATCH_SIZE,\n INBOX_ID,\n OUTBOX_ID,\n RESERVED_IDS,\n ROWVERSIONS_ID,\n SYNCMETA_ID,\n SYNC_LOCK_PREFIX,\n} from \"../internal/constants\";\nimport { createHookEmitter } from \"../internal/hooks\";\nimport type { EmitHook } from \"../internal/hooks\";\nimport { pullInbox } from \"../internal/pull\";\nimport { pushOutbox, toOutboundEvent } from \"../internal/push\";\nimport { readRowVersion, recordRowVersion, replayInbox } from \"../internal/replay\";\nimport { createSerialQueue } from \"../internal/serial-queue\";\nimport {\n ensureSyncMeta,\n readPullCursor,\n readSyncMeta,\n resolveClientId,\n writePullCursor,\n writeSyncOutcome,\n} from \"../internal/sync-meta\";\nimport type {\n AcceptMutationsCollection,\n MetaCollections,\n MutationHookParams,\n ReplayContext,\n ResolvedRetryConfig,\n} from \"../internal/types\";\nimport { createSyncTransport, normalizePushResponse } from \"./sync\";\nimport type {\n CollectionMap,\n DeadLetterEntry,\n EventSourcedDB,\n EventSourcedDBConfig,\n InboxEntry,\n ManualSyncResult,\n MutationType,\n OutboundEvent,\n OutboxEntry,\n PruneOptions,\n PruneResult,\n RowVersionEntry,\n SyncMetaEntry,\n SyncResult,\n SyncStatus,\n SyncTrigger,\n} from \"./types\";\nimport type { EventSourcedLogger } from \"../utils/logger\";\nimport { createEventSourcedLogger } from \"../utils/logger\";\nimport { generateEventId } from \"../utils/uuid\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n schemaVersion?: number;\n indexes?: ReadonlyArray<{\n select: (row: never) => unknown;\n name?: string;\n indexType?: new (id: number, expression: never, name?: string, options?: never) => unknown;\n }>;\n};\n\ntype SeqCounter = { value: number };\n\n/**\n * Lets the mutation hooks, which are built before `syncmeta` has been\n * preloaded, read the persisted client id once it is known.\n */\ntype ClientIdRef = { value: string };\n\ntype MetaCollectionFactory = Pick<\n EventSourcedDBConfig<Record<string, CollectionDefConstraint>>,\n \"createCollection\" | \"persistedCollectionOptions\" | \"persistence\"\n>;\n\ntype MutationContext = {\n outbox: Collection<OutboxEntry, string>;\n rowversions: Collection<RowVersionEntry, string>;\n collectionId: string;\n type: MutationType;\n seq: SeqCounter;\n clientId: ClientIdRef;\n eventSchemaVersion: number;\n conflictDetection: boolean;\n emit: EmitHook;\n log: EventSourcedLogger;\n};\n\nfunction emptyResult(overrides: Partial<SyncResult> = {}): SyncResult {\n return {\n pushed: 0,\n pulled: 0,\n skipped: 0,\n deadLettered: 0,\n deferred: false,\n errors: [],\n ...overrides,\n };\n}\n\n/**\n * Low-level factory: wires persistence, collections, outbox/inbox, and sync.\n * Prefer a platform helper (`createBrowserEventSourcedDB`,\n * `createNodeEventSourcedDB`, `createReactNativeEventSourcedDB`) unless you are\n * supplying your own persistence.\n *\n * @param config - See {@link EventSourcedDBConfig} for every option.\n *\n * @example Node SQLite, opened immediately\n * ```ts\n * import { createCollection } from \"@tanstack/db\"\n * import {\n * createNodeSQLitePersistence,\n * persistedCollectionOptions,\n * } from \"@tanstack/node-db-sqlite-persistence\"\n * import Database from \"better-sqlite3\"\n * import { createEventSourcedDB } from \"event-sourced-collection\"\n *\n * type Todo = { id: string; title: string }\n *\n * const sqlite = new Database(\"app.sqlite\")\n * const db = await createEventSourcedDB({\n * persistence: createNodeSQLitePersistence({ database: sqlite }),\n * createCollection,\n * persistedCollectionOptions,\n * collections: {\n * todos: { getKey: (todo: Todo) => todo.id },\n * },\n * sync: {\n * push: \"/api/sync/events\",\n * pull: \"/api/sync/events\",\n * },\n * })\n *\n * await db.collections.todos.insert({ id: \"t1\", title: \"Ship\" }).isPersisted.promise\n * await db.sync()\n * ```\n */\nexport async function createEventSourcedDB<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(config: EventSourcedDBConfig<TDefs>): Promise<EventSourcedDB<TDefs>> {\n assertReservedNamesAvailable(config.collections);\n\n const log = createEventSourcedLogger(config.debug);\n const emit = createHookEmitter(config.hooks, log);\n\n const transport = createSyncTransport(config.sync);\n let syncEnabled = config.syncEnabled ?? true;\n\n // Provisional until `syncmeta` is preloaded and the persisted id is known.\n const clientId: ClientIdRef = { value: config.clientId ?? generateEventId() };\n const unknownEventHandling = config.unknownEventHandling ?? \"skip\";\n const pullOverlap = Math.max(0, config.pullOverlap ?? 0);\n const recordLocalEchoes = config.recordLocalEchoes ?? true;\n const eventSchemaVersion = config.eventSchemaVersion ?? DEFAULT_EVENT_SCHEMA_VERSION;\n const pushBatchSize = Math.max(1, config.pushBatchSize ?? DEFAULT_PUSH_BATCH_SIZE);\n const backendMismatch = config.backendMismatch ?? \"resetCursor\";\n const conflictDetection = config.conflictDetection ?? false;\n const lockName = `${SYNC_LOCK_PREFIX}:${config.lockName ?? \"default\"}`;\n\n const retry: ResolvedRetryConfig = {\n maxAttempts: Math.max(1, config.retry?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS),\n baseDelayMs: Math.max(0, config.retry?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS),\n maxDelayMs: Math.max(0, config.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS),\n };\n\n log.info(\"creating event-sourced db\", {\n collectionIds: Object.keys(config.collections),\n hasTransport: transport !== null,\n hasLock: Boolean(config.lock),\n syncEnabled,\n unknownEventHandling,\n pullOverlap,\n recordLocalEchoes,\n pushBatchSize,\n backendMismatch,\n conflictDetection,\n eventSchemaVersion,\n });\n\n const defaultSchemaVersion = config.schemaVersion ?? 1;\n const seq: SeqCounter = { value: 0 };\n\n const meta: MetaCollections = {\n outbox: createMetaCollection<OutboxEntry>(\n config,\n OUTBOX_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n ),\n inbox: createMetaCollection<InboxEntry>(\n config,\n INBOX_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n ),\n deadletter: createMetaCollection<DeadLetterEntry>(\n config,\n DEADLETTER_ID,\n (entry) => entry.eventId,\n defaultSchemaVersion,\n ),\n syncmeta: createMetaCollection<SyncMetaEntry>(\n config,\n SYNCMETA_ID,\n (entry) => entry.id,\n defaultSchemaVersion,\n ),\n rowversions: createMetaCollection<RowVersionEntry>(\n config,\n ROWVERSIONS_ID,\n (entry) => entry.id,\n defaultSchemaVersion,\n ),\n };\n\n const { outbox, inbox, deadletter, syncmeta, rowversions } = meta;\n const userCollections = {} as CollectionMap<TDefs>;\n\n for (const collectionId of Object.keys(config.collections)) {\n const def = config.collections[collectionId]!;\n const getKey = def.getKey as (item: Record<string, unknown>) => string | number;\n\n const mutationContext = {\n outbox,\n rowversions,\n collectionId,\n seq,\n clientId,\n eventSchemaVersion,\n conflictDetection,\n emit,\n log,\n };\n\n const options = config.persistedCollectionOptions<Record<string, unknown>, string | number>({\n id: collectionId,\n getKey,\n persistence: config.persistence,\n schemaVersion: def.schemaVersion ?? defaultSchemaVersion,\n gcTime: Number.POSITIVE_INFINITY,\n onInsert: createMutationHook({ ...mutationContext, type: \"insert\" }),\n onUpdate: createMutationHook({ ...mutationContext, type: \"update\" }),\n onDelete: createMutationHook({ ...mutationContext, type: \"delete\" }),\n });\n\n const collection = config.createCollection(options);\n applyCollectionIndexes(collection, collectionId, def.indexes, log);\n\n log.debug(\"registered collection\", {\n collectionId,\n hasAcceptMutations: Boolean((collection as AcceptMutationsCollection).utils?.acceptMutations),\n });\n\n (userCollections as Record<string, unknown>)[collectionId] = collection;\n }\n\n const collections = {\n ...(userCollections as CollectionMap<TDefs>),\n ...meta,\n } as EventSourcedDB<TDefs>[\"collections\"];\n\n const replayTargets = collections as unknown as Record<string, AcceptMutationsCollection>;\n\n let isSyncing = false;\n const statusListeners = new Set<(status: SyncStatus) => void>();\n\n const notifyStatus = (): void => {\n if (statusListeners.size === 0) return;\n const status = getSyncStatus();\n for (const listener of statusListeners) listener(status);\n };\n\n const subscriptions = [\n outbox.subscribeChanges(notifyStatus),\n inbox.subscribeChanges(() => {}),\n deadletter.subscribeChanges(notifyStatus),\n syncmeta.subscribeChanges(notifyStatus),\n ];\n\n for (const collection of Object.values(meta)) {\n await collection.preload();\n }\n\n await ensureSyncMeta(syncmeta);\n\n clientId.value = await resolveClientId(syncmeta, config.clientId, generateEventId);\n\n seq.value = nextLocalSeq(outbox);\n\n // User collections must be hydrated before we hand the DB to the app.\n // Otherwise seeders like `ensureAppSettings` see an empty `get()` and\n // re-insert rows that already exist on disk / on the server.\n for (const collection of Object.values(userCollections)) {\n await collection.preload();\n }\n\n log.info(\"preloaded collections\", {\n outboxCount: outbox.state.size,\n inboxCount: inbox.state.size,\n deadLetterCount: deadletter.state.size,\n userCollectionIds: Object.keys(userCollections),\n nextLocalSeq: seq.value,\n clientId: clientId.value,\n pullCursor: readPullCursor(syncmeta, inbox),\n });\n\n const replayContext: ReplayContext = {\n targets: replayTargets,\n rowversions,\n deadletter,\n unknownEventHandling,\n eventSchemaVersion,\n upcastEvent: config.upcastEvent,\n conflictDetection,\n maxReplayAttempts: retry.maxAttempts,\n emit,\n log,\n };\n\n await replayInbox(inbox, replayContext);\n\n emit(\"onReady\", { clientId: clientId.value, pullCursor: readPullCursor(syncmeta, inbox) });\n\n // Serializes callers within this context; the optional lock extends that\n // across tabs so only one of them syncs at a time.\n const runExclusive = createSerialQueue();\n\n function recordError(phase: \"push\" | \"pull\" | \"replay\", label: string, err: unknown): Error {\n const error = toError(err);\n log.error(`${label} ${phase} failed`, { message: error.message });\n emit(\"onSyncError\", { phase, error });\n return error;\n }\n\n async function pushPull(label: string): Promise<SyncResult> {\n await outbox.preload();\n await inbox.preload();\n\n const errors: Error[] = [];\n let pushed = 0;\n let pulled = 0;\n let skipped = 0;\n let deadLettered = 0;\n\n async function runPush(): Promise<void> {\n if (!transport?.push) {\n log.debug(`${label} push skipped: no push transport configured`);\n return;\n }\n\n try {\n const outcome = await pushOutbox({\n outbox,\n deadletter,\n rowversions,\n push: transport.push,\n batchSize: pushBatchSize,\n retry,\n now: Date.now(),\n emit,\n log,\n });\n pushed += outcome.pushed;\n deadLettered += outcome.deadLettered;\n if (outcome.error) {\n errors.push(recordError(\"push\", label, outcome.error));\n }\n } catch (err) {\n errors.push(recordError(\"push\", label, err));\n }\n }\n\n await runPush();\n\n let requeued = 0;\n\n try {\n if (transport?.pull) {\n const outcome = await pullInbox({\n outbox,\n inbox,\n syncmeta,\n pull: transport.pull,\n clientId: clientId.value,\n pullOverlap,\n recordLocalEchoes,\n backendMismatch,\n context: replayContext,\n });\n pulled = outcome.pulled;\n skipped = outcome.skipped;\n deadLettered += outcome.deadLettered;\n requeued = outcome.requeued;\n } else {\n log.debug(`${label} pull skipped: no pull transport configured`);\n }\n } catch (err) {\n errors.push(recordError(\"pull\", label, err));\n }\n\n // A backend reset is only discovered during pull, by which point this\n // sync's push has already run. Upload the requeued history now so a single\n // sync is enough to recover rather than leaving the gap open until the next.\n if (requeued > 0) {\n log.info(`${label} re-pushing history after backend reset`, { requeued });\n await runPush();\n }\n\n await writeSyncOutcome(syncmeta, Date.now(), errors[0]?.message ?? null);\n\n return { pushed, pulled, skipped, deadLettered, deferred: false, errors };\n }\n\n /** Runs `fn` under the cross-context lock when one is configured. */\n async function withLock<T>(fn: () => Promise<T>, deferred: T): Promise<T> {\n if (!config.lock) return fn();\n\n const outcome = await config.lock.tryRun(lockName, fn);\n\n if (!outcome.acquired) {\n log.debug(\"sync deferred: lock held by another context\", { lockName });\n return deferred;\n }\n\n return outcome.result;\n }\n\n async function tracked<T extends SyncResult>(\n trigger: SyncTrigger,\n fn: () => Promise<T>,\n ): Promise<T> {\n isSyncing = true;\n notifyStatus();\n emit(\"onSyncStart\", { trigger });\n\n try {\n const result = await fn();\n emit(\"onSyncComplete\", { trigger, result });\n return result;\n } finally {\n isSyncing = false;\n notifyStatus();\n }\n }\n\n function sync(): Promise<SyncResult> {\n return runExclusive(async () => {\n if (!syncEnabled) {\n log.debug(\"sync skipped: sync disabled\");\n return emptyResult();\n }\n\n if (!transport) {\n log.warn(\"sync skipped: no transport configured\");\n return emptyResult();\n }\n\n return withLock(\n () =>\n tracked(\"sync\", async () => {\n log.info(\"sync started\");\n const result = await pushPull(\"sync\");\n log.info(\"sync finished\", {\n pushed: result.pushed,\n pulled: result.pulled,\n skipped: result.skipped,\n deadLettered: result.deadLettered,\n errorCount: result.errors.length,\n });\n return result;\n }),\n emptyResult({ deferred: true }),\n );\n });\n }\n\n function manualSync(): Promise<ManualSyncResult> {\n return runExclusive(() =>\n withLock<ManualSyncResult>(\n () =>\n tracked(\"manualSync\", async () => {\n log.info(\"manual sync started\");\n\n await outbox.preload();\n await inbox.preload();\n\n let result: SyncResult = emptyResult();\n\n if (syncEnabled && transport) {\n result = await pushPull(\"manual sync\");\n } else if (!syncEnabled) {\n log.debug(\"manual sync push/pull skipped: sync disabled\");\n } else {\n log.warn(\"manual sync: no transport configured, skipping push/pull\");\n }\n\n const errors = [...result.errors];\n let replayed = 0;\n\n try {\n const outcome = await replayInbox(inbox, replayContext);\n replayed = outcome.applied;\n result = { ...result, skipped: result.skipped + outcome.skipped };\n } catch (err) {\n errors.push(recordError(\"replay\", \"manual sync\", err));\n }\n\n log.info(\"manual sync finished\", {\n pushed: result.pushed,\n pulled: result.pulled,\n skipped: result.skipped,\n deadLettered: result.deadLettered,\n replayed,\n errorCount: errors.length,\n });\n\n return { ...result, replayed, errors };\n }),\n { ...emptyResult({ deferred: true }), replayed: 0 },\n ),\n );\n }\n\n function getSyncStatus(): SyncStatus {\n let pendingCount = 0;\n let failedCount = 0;\n\n for (const entry of outbox.state.values()) {\n if (entry.sync) continue;\n pendingCount++;\n if (entry.syncStatus === \"failed\") failedCount++;\n }\n\n const stored = readSyncMeta(syncmeta);\n\n return {\n isSyncing,\n isSynced: pendingCount === 0,\n pendingCount,\n failedCount,\n deadLetterCount: deadletter.state.size,\n pullCursor: readPullCursor(syncmeta, inbox),\n backendId: stored.backendId,\n lastSyncAt: stored.lastSyncAt,\n lastError: stored.lastError,\n };\n }\n\n function subscribeSyncStatus(listener: (status: SyncStatus) => void): () => void {\n statusListeners.add(listener);\n listener(getSyncStatus());\n return () => statusListeners.delete(listener);\n }\n\n /**\n * Retries dead-lettered events.\n * - Outbound: pushes directly to the sync transport (does not requeue the outbox).\n * - Inbound: requeues into the inbox and replays immediately.\n * Omit `eventId` to retry everything. Returns how many were successfully\n * cleared from the dead-letter queue.\n */\n function retryDeadLetter(eventId?: string): Promise<number> {\n return runExclusive(async () => {\n const targets = selectDeadLetters(deadletter, eventId);\n const outbound: DeadLetterEntry[] = [];\n let cleared = 0;\n let inboundRequeued = 0;\n\n for (const entry of targets) {\n if ((entry.direction ?? \"outbound\") === \"inbound\") {\n await requeueInboundDeadLetter(inbox, entry);\n await deadletter.delete(entry.eventId).isPersisted.promise;\n inboundRequeued++;\n cleared++;\n } else {\n outbound.push(entry);\n }\n }\n\n if (inboundRequeued > 0) {\n await replayInbox(inbox, replayContext);\n }\n\n if (outbound.length > 0) {\n if (!transport?.push) {\n throw new Error(\n \"Cannot retry outbound dead-letter events: no push transport is configured.\",\n );\n }\n\n const events: OutboundEvent[] = outbound.map((entry) =>\n toOutboundEvent({\n eventId: entry.eventId,\n collectionId: entry.collectionId,\n type: entry.type,\n key: entry.key,\n payload: entry.payload,\n previous: entry.previous,\n txId: entry.txId ?? entry.eventId,\n clientId: entry.clientId ?? clientId.value,\n schemaVersion: entry.schemaVersion,\n baseVersion: entry.baseVersion ?? null,\n timestamp: entry.timestamp,\n localSeq: entry.localSeq ?? 0,\n globalSeq: null,\n sync: false,\n syncStatus: \"pending\",\n attemptCount: entry.attemptCount,\n lastAttemptAt: null,\n nextAttemptAt: null,\n lastError: null,\n lastErrorCode: null,\n retryable: null,\n }),\n );\n\n const response = normalizePushResponse(await transport.push(events));\n const confirmed = new Set(response.confirmed.map((item) => item.eventId));\n const failedById = new Map((response.failed ?? []).map((item) => [item.eventId, item]));\n const now = Date.now();\n\n for (const confirmation of response.confirmed) {\n const entry = outbound.find((row) => row.eventId === confirmation.eventId);\n if (!entry) continue;\n\n if (deadletter.has(entry.eventId)) {\n await deadletter.delete(entry.eventId).isPersisted.promise;\n }\n\n if (conflictDetection) {\n await recordRowVersion(\n rowversions,\n entry.collectionId,\n entry.key,\n entry.eventId,\n confirmation.globalSeq,\n );\n }\n\n emit(\"onEventPushed\", {\n eventId: confirmation.eventId,\n globalSeq: confirmation.globalSeq,\n });\n cleared++;\n }\n\n for (const entry of outbound) {\n if (confirmed.has(entry.eventId)) continue;\n\n const failure = failedById.get(entry.eventId);\n const message = failure?.message ?? \"push failed during dead-letter retry\";\n const code = failure?.code ?? null;\n\n if (deadletter.has(entry.eventId)) {\n await deadletter.update(entry.eventId, (draft) => {\n draft.attemptCount = (draft.attemptCount ?? 0) + 1;\n draft.message = message;\n draft.code = code;\n draft.failedAt = now;\n }).isPersisted.promise;\n }\n }\n\n log.info(\"outbound dead-letter events pushed\", {\n attempted: outbound.length,\n confirmed: response.confirmed.length,\n failed: response.failed?.length ?? 0,\n });\n } else {\n log.info(\"dead-letter events retried\", {\n count: targets.length,\n inbound: inboundRequeued,\n });\n }\n\n return cleared;\n });\n }\n\n function discardDeadLetter(eventId?: string): Promise<number> {\n return runExclusive(async () => {\n const targets = selectDeadLetters(deadletter, eventId);\n\n for (const entry of targets) {\n await deadletter.delete(entry.eventId).isPersisted.promise;\n }\n\n log.info(\"dead-letter events discarded\", { count: targets.length });\n\n return targets.length;\n });\n }\n\n function pruneSyncedEvents(options: PruneOptions = {}): Promise<PruneResult> {\n return runExclusive(async () => {\n const olderThanMs = Math.max(0, options.olderThanMs ?? 0);\n const keepLast = Math.max(0, options.keepLast ?? 0);\n const cutoff = Date.now() - olderThanMs;\n\n // The cursor is derived partly from resolved inbox rows, so persist it\n // before those rows are removed.\n await writePullCursor(syncmeta, readPullCursor(syncmeta, inbox));\n\n const outboxTargets = trimKeepLast(\n [...outbox.state.values()]\n .filter((entry) => entry.sync && entry.timestamp <= cutoff)\n .sort((a, b) => a.localSeq - b.localSeq),\n keepLast,\n );\n\n const inboxTargets = trimKeepLast(\n [...inbox.state.values()]\n .filter((entry) => entry.sync && entry.timestamp <= cutoff)\n .sort((a, b) => a.globalSeq - b.globalSeq),\n keepLast,\n );\n\n for (const entry of outboxTargets) {\n await outbox.delete(entry.eventId).isPersisted.promise;\n }\n\n for (const entry of inboxTargets) {\n await inbox.delete(entry.eventId).isPersisted.promise;\n }\n\n log.info(\"pruned synced events\", {\n outbox: outboxTargets.length,\n inbox: inboxTargets.length,\n });\n\n return { outbox: outboxTargets.length, inbox: inboxTargets.length };\n });\n }\n\n function getSyncEnabled(): boolean {\n return syncEnabled;\n }\n\n function setSyncEnabled(enabled: boolean): void {\n syncEnabled = enabled;\n log.debug(\"sync enabled updated\", { syncEnabled: enabled });\n }\n\n function dispose(): void {\n log.debug(\"disposing event-sourced db\");\n statusListeners.clear();\n for (const subscription of subscriptions) {\n subscription.unsubscribe();\n }\n }\n\n return {\n collections,\n sync,\n manualSync,\n getSyncEnabled,\n setSyncEnabled,\n getSyncStatus,\n subscribeSyncStatus,\n retryDeadLetter,\n discardDeadLetter,\n pruneSyncedEvents,\n dispose,\n };\n}\n\nfunction selectDeadLetters(\n deadletter: Collection<DeadLetterEntry, string>,\n eventId?: string,\n): DeadLetterEntry[] {\n if (eventId === undefined) {\n return [...deadletter.state.values()].sort(\n (a, b) => (a.localSeq ?? a.globalSeq ?? 0) - (b.localSeq ?? b.globalSeq ?? 0),\n );\n }\n\n const entry = deadletter.get(eventId);\n return entry ? [entry] : [];\n}\n\n/** Puts a parked inbound event back in the inbox so replay can pick it up again. */\nasync function requeueInboundDeadLetter(\n inbox: Collection<InboxEntry, string>,\n entry: DeadLetterEntry,\n): Promise<void> {\n if (inbox.has(entry.eventId)) {\n await inbox.update(entry.eventId, (draft) => {\n draft.sync = false;\n draft.skipped = false;\n draft.skipReason = null;\n draft.attemptCount = 0;\n draft.lastError = null;\n }).isPersisted.promise;\n return;\n }\n\n await inbox.insert({\n eventId: entry.eventId,\n globalSeq: entry.globalSeq ?? 0,\n collectionId: entry.collectionId,\n type: entry.type,\n key: entry.key,\n payload: entry.payload,\n previous: entry.previous,\n clientId: entry.clientId,\n schemaVersion: entry.schemaVersion,\n timestamp: entry.timestamp,\n sync: false,\n skipped: false,\n skipReason: null,\n attemptCount: 0,\n lastError: null,\n }).isPersisted.promise;\n}\n\n/** Drops the newest `keepLast` items from an ascending-sorted removal list. */\nfunction trimKeepLast<T>(items: ReadonlyArray<T>, keepLast: number): T[] {\n if (keepLast <= 0) return [...items];\n return items.slice(0, Math.max(0, items.length - keepLast));\n}\n\nfunction createMetaCollection<TEntry extends object>(\n config: MetaCollectionFactory,\n id: string,\n getKey: (entry: TEntry) => string,\n schemaVersion: number,\n): Collection<TEntry, string> {\n const options = config.persistedCollectionOptions<TEntry, string>({\n id,\n getKey,\n persistence: config.persistence,\n schemaVersion,\n });\n\n return config.createCollection(options);\n}\n\ntype IndexableCollection = {\n createIndex: (\n indexCallback: (row: Record<string, unknown>) => unknown,\n config?: {\n name?: string;\n indexType?: new (id: number, expression: never, name?: string, options?: never) => unknown;\n },\n ) => unknown;\n getIndexMetadata?: () => ReadonlyArray<unknown>;\n on?: (event: \"status:ready\", callback: () => void) => () => void;\n};\n\nfunction applyCollectionIndexes(\n collection: Collection<Record<string, unknown>, string | number>,\n collectionId: string,\n indexes: CollectionDefConstraint[\"indexes\"],\n log: EventSourcedLogger,\n): void {\n if (!indexes?.length) return;\n\n const indexable = collection as IndexableCollection;\n\n const register = (): void => {\n if ((indexable.getIndexMetadata?.().length ?? 0) > 0) return;\n\n for (const indexDef of indexes) {\n indexable.createIndex(indexDef.select as (row: Record<string, unknown>) => unknown, {\n name: indexDef.name,\n indexType: indexDef.indexType,\n });\n\n log.debug(\"registered collection index\", { collectionId, name: indexDef.name });\n }\n };\n\n register();\n\n indexable.on?.(\"status:ready\", register);\n}\n\nfunction createMutationHook(context: MutationContext) {\n const {\n outbox,\n rowversions,\n collectionId,\n type,\n seq,\n clientId,\n eventSchemaVersion,\n conflictDetection,\n emit,\n log,\n } = context;\n\n return async (params: MutationHookParams): Promise<Record<string, unknown>> => {\n const txId = generateEventId();\n const timestamp = Date.now();\n\n for (const mutation of params.transaction.mutations) {\n const payload = type === \"delete\" ? mutation.original : mutation.modified;\n const eventId = generateEventId();\n\n const entry: OutboxEntry = {\n eventId,\n collectionId,\n type,\n key: mutation.key,\n payload,\n previous: type === \"insert\" ? null : mutation.original,\n txId,\n clientId: clientId.value,\n schemaVersion: eventSchemaVersion,\n baseVersion: conflictDetection\n ? readRowVersion(rowversions, collectionId, mutation.key)\n : null,\n timestamp,\n localSeq: allocateLocalSeq(outbox, seq),\n globalSeq: null,\n sync: false,\n syncStatus: \"pending\",\n attemptCount: 0,\n lastAttemptAt: null,\n nextAttemptAt: null,\n lastError: null,\n lastErrorCode: null,\n retryable: null,\n };\n\n await outbox.insert(entry).isPersisted.promise;\n\n if (conflictDetection) {\n await recordRowVersion(rowversions, collectionId, mutation.key, eventId, null);\n }\n\n log.debug(\"outbox entry created\", {\n eventId,\n collectionId,\n type,\n key: entry.key,\n localSeq: entry.localSeq,\n txId,\n });\n\n emit(\"onMutation\", entry);\n }\n\n return {};\n };\n}\n\n/**\n * Reads the high-water mark from live outbox state rather than trusting the\n * cached counter alone, so a second tab writing to the same persisted store\n * cannot hand out a localSeq that has already been used.\n */\nfunction allocateLocalSeq(outbox: Collection<OutboxEntry, string>, seq: SeqCounter): number {\n const allocated = Math.max(seq.value, nextLocalSeq(outbox));\n seq.value = allocated + 1;\n return allocated;\n}\n\nfunction nextLocalSeq(outbox: Collection<OutboxEntry, string>): number {\n let max = -1;\n\n for (const entry of outbox.state.values()) {\n if (entry.localSeq > max) max = entry.localSeq;\n }\n\n return max + 1;\n}\n\nfunction assertReservedNamesAvailable(collections: Record<string, unknown>): void {\n for (const id of Object.keys(collections)) {\n if (RESERVED_IDS.has(id)) {\n throw new Error(\n `Collection id \"${id}\" is reserved. Built-in collections are: ${[...RESERVED_IDS]\n .map((name) => `\"${name}\"`)\n .join(\", \")}.`,\n );\n }\n }\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err));\n}\n","export type LazySingletonOptions = {\n /** Runs on every `ensure()` / proxy access (e.g. `typeof window` checks). */\n guard?: () => void;\n /** Thrown when `proxy` is used before `ensure()` resolves. */\n notInitializedMessage?: string;\n};\n\n/**\n * Result of {@link createLazySingleton}.\n *\n * @example\n * ```ts\n * import { createLazySingleton } from \"event-sourced-collection\"\n *\n * const { ensure, proxy, reset } = createLazySingleton(async () => ({ ready: true }))\n * await ensure()\n * console.log(proxy.ready) // true\n * reset()\n * ```\n */\nexport type LazySingleton<T extends object> = {\n ensure: () => Promise<T>;\n proxy: T;\n reset: () => void;\n};\n\n/**\n * One-shot async factory with a proxy that is safe to export as a module\n * singleton. Platform DB helpers are built on this.\n *\n * @example Export a proxy, initialize at app startup\n * ```ts\n * import { createLazySingleton } from \"event-sourced-collection\"\n *\n * type Client = { ping: () => Promise<string> }\n *\n * const { ensure, proxy: client } = createLazySingleton<Client>(async () => ({\n * ping: async () => \"ok\",\n * }))\n *\n * export async function start() {\n * await ensure()\n * }\n *\n * export { client }\n * // client.ping() throws until start() has run\n * ```\n */\nexport function createLazySingleton<T extends object>(\n factory: () => Promise<T>,\n options: LazySingletonOptions = {},\n): LazySingleton<T> {\n let instance: T | null = null;\n let initPromise: Promise<T> | null = null;\n\n const ensure = async (): Promise<T> => {\n options.guard?.();\n\n if (instance) {\n return instance;\n }\n\n if (!initPromise) {\n initPromise = factory()\n .then((resolved) => {\n instance = resolved;\n return resolved;\n })\n .catch((error: unknown) => {\n initPromise = null;\n throw error;\n });\n }\n\n return initPromise;\n };\n\n const message =\n options.notInitializedMessage ?? \"Instance is not initialized. Call ensure() first.\";\n\n const proxy = new Proxy({} as T, {\n get(_target, prop, receiver) {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.get(instance, prop, receiver);\n },\n has(_target, prop) {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.has(instance, prop);\n },\n getPrototypeOf() {\n if (!instance) {\n throw new Error(message);\n }\n return Reflect.getPrototypeOf(instance);\n },\n });\n\n const reset = (): void => {\n instance = null;\n initPromise = null;\n };\n\n return { ensure, proxy, reset };\n}\n","import { createEventSourcedDB } from \"./create-event-sourced-db\";\nimport { createLazySingleton } from \"./lazy-singleton\";\nimport type { EventSourcedDB, EventSourcedDBConfig } from \"./types\";\n\ntype CollectionDefConstraint = {\n getKey: (state: never) => string | number;\n};\n\n/**\n * Lazy handle returned by platform helpers and {@link createEventSourcedDBHandle}.\n *\n * @example\n * ```ts\n * import { createBrowserEventSourcedDB } from \"event-sourced-collection/browser\"\n *\n * const { ensureDb, db, close } = createBrowserEventSourcedDB({\n * databaseName: \"app.sqlite\",\n * collections: { todos: { getKey: (todo: { id: string }) => todo.id } },\n * modules: async () => {\n * const { createCollection } = await import(\"@tanstack/db\")\n * const persistence = await import(\"@tanstack/browser-db-sqlite-persistence\")\n * return { createCollection, ...persistence }\n * },\n * })\n *\n * await ensureDb()\n * await db.collections.todos.insert({ id: \"t1\" }).isPersisted.promise\n * await close()\n * ```\n */\nexport type EventSourcedDBHandle<TDefs extends Record<string, CollectionDefConstraint>> = {\n /** Opens (or returns) the singleton DB. Call once at app startup before using `db`. */\n ensureDb: () => Promise<EventSourcedDB<TDefs>>;\n /**\n * Proxy that forwards to the live DB after `ensureDb()` resolves. Accessing a\n * property before initialization throws.\n */\n db: EventSourcedDB<TDefs>;\n /** Disposes the DB, runs platform cleanup, and resets the singleton. */\n close: () => Promise<void>;\n};\n\nexport type EventSourcedDBHandleSetup<TDefs extends Record<string, CollectionDefConstraint>> =\n EventSourcedDBConfig<TDefs> & {\n /** Optional platform teardown (close SQLite, dispose coordinators). */\n close?: () => Promise<void> | void;\n };\n\n/**\n * Lazy singleton around {@link createEventSourcedDB}. Platform helpers use this\n * after they have turned TanStack persistence modules into `persistence`.\n * Call this yourself only when you need a custom open/close path.\n *\n * @example Custom persistence, still lazy like the platform helpers\n * ```ts\n * import { createCollection } from \"@tanstack/db\"\n * import {\n * createNodeSQLitePersistence,\n * persistedCollectionOptions,\n * } from \"@tanstack/node-db-sqlite-persistence\"\n * import Database from \"better-sqlite3\"\n * import { createEventSourcedDBHandle } from \"event-sourced-collection\"\n *\n * type Todo = { id: string; title: string }\n *\n * const { ensureDb, db, close } = createEventSourcedDBHandle({\n * setup: async () => {\n * const sqlite = new Database(\"app.sqlite\")\n * return {\n * persistence: createNodeSQLitePersistence({ database: sqlite }),\n * createCollection,\n * persistedCollectionOptions,\n * collections: { todos: { getKey: (todo: Todo) => todo.id } },\n * close: () => sqlite.close(),\n * }\n * },\n * })\n *\n * await ensureDb()\n * console.log(db.collections.todos.size)\n * await close()\n * ```\n */\nexport function createEventSourcedDBHandle<\n const TDefs extends Record<string, CollectionDefConstraint>,\n>(options: {\n setup: () => Promise<EventSourcedDBHandleSetup<TDefs>>;\n guard?: () => void;\n notInitializedMessage?: string;\n}): EventSourcedDBHandle<TDefs> {\n let activeDb: EventSourcedDB<TDefs> | null = null;\n let closePlatform: (() => Promise<void> | void) | null = null;\n\n const factory = async (): Promise<EventSourcedDB<TDefs>> => {\n const { close, ...dbConfig } = await options.setup();\n closePlatform = close ?? null;\n const database = await createEventSourcedDB<TDefs>(dbConfig);\n activeDb = database;\n return database;\n };\n\n const singleton = createLazySingleton<EventSourcedDB<TDefs>>(factory, {\n guard: options.guard,\n notInitializedMessage:\n options.notInitializedMessage ??\n \"Event-sourced DB is not initialized. Call ensureDb() before using db.\",\n });\n\n const close = async (): Promise<void> => {\n if (activeDb) {\n activeDb.dispose();\n activeDb = null;\n }\n if (closePlatform) {\n await closePlatform();\n closePlatform = null;\n }\n singleton.reset();\n };\n\n return {\n ensureDb: singleton.ensure,\n db: singleton.proxy,\n close,\n };\n}\n\n/** Bindings object, or a function that loads them on first `ensureDb()`. */\nexport type ModulesInput<T> = T | (() => T | Promise<T>);\n\n/**\n * Resolves {@link ModulesInput}: returns the object, or awaits the loader.\n *\n * @example Keep WASM off the SSR bundle until the first `ensureDb()`\n * ```ts\n * import { resolveModules, type ModulesInput } from \"event-sourced-collection\"\n *\n * type BrowserModules = {\n * createCollection: typeof import(\"@tanstack/db\").createCollection\n * }\n *\n * const modules: ModulesInput<BrowserModules> = async () => {\n * const { createCollection } = await import(\"@tanstack/db\")\n * return { createCollection }\n * }\n *\n * const resolved = await resolveModules(modules)\n * ```\n */\nexport async function resolveModules<T>(modules: ModulesInput<T>): Promise<T> {\n return typeof modules === \"function\" ? await (modules as () => T | Promise<T>)() : modules;\n}\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as PersistedCollectionOptionsFn, A as PushResponse, B as SyncResult, C as PruneOptions, D as PushConfirmation, E as PullResponse, F as ServerEvent, G as UnknownEventHandling, H as SyncTransport, I as SyncHandlersConfig, J as EventSourcedLogger, K as UpcastEventFn, L as SyncLock, M as RetryConfig, N as RowVersionEntry, O as PushEventsFn, P as SQLiteDriver, Q as InjectedModuleFn, R as SyncMetaEntry, S as PersistedCollectionPersistence, T as PullEventsFn, U as SyncTrigger, V as SyncStatus, W as SyncUrlConfig, X as CreateCollectionFn, Y as createEventSourcedLogger, Z as InjectedCreateCollection, _ as ManualSyncResult, a as CollectionMap, b as OutboxEntry, c as DeadLetterReason, d as EventSourcedHooks, f as EventSourcedOptions, g as InferState, h as InferKey, i as CollectionIndexDef, j as ReservedCollections, k as PushFailure, l as EventSourcedDB, m as InboxEntry, n as CollectionDef, o as DeadLetterDirection, p as EventSourcedSharedOptions, q as UpcastableEvent, r as CollectionIndexConstructor, s as DeadLetterEntry, t as BackendMismatchPolicy, u as EventSourcedDBConfig, v as MutationType, w as PruneResult, x as OutboxSyncStatus, y as OutboundEvent, z as SyncPhase } from "./types-BSdudVLh.mjs";
2
- import { a as resolveModules, i as createEventSourcedDBHandle, n as EventSourcedDBHandleSetup, r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-BAos-9pS.mjs";
1
+ import { $ as PersistedCollectionOptionsFn, A as PushResponse, B as SyncResult, C as PruneOptions, D as PushConfirmation, E as PullResponse, F as ServerEvent, G as UnknownEventHandling, H as SyncTransport, I as SyncHandlersConfig, J as EventSourcedLogger, K as UpcastEventFn, L as SyncLock, M as RetryConfig, N as RowVersionEntry, O as PushEventsFn, P as SQLiteDriver, Q as InjectedModuleFn, R as SyncMetaEntry, S as PersistedCollectionPersistence, T as PullEventsFn, U as SyncTrigger, V as SyncStatus, W as SyncUrlConfig, X as CreateCollectionFn, Y as createEventSourcedLogger, Z as InjectedCreateCollection, _ as ManualSyncResult, a as CollectionMap, b as OutboxEntry, c as DeadLetterReason, d as EventSourcedHooks, f as EventSourcedOptions, g as InferState, h as InferKey, i as CollectionIndexDef, j as ReservedCollections, k as PushFailure, l as EventSourcedDB, m as InboxEntry, n as CollectionDef, o as DeadLetterDirection, p as EventSourcedSharedOptions, q as UpcastableEvent, r as CollectionIndexConstructor, s as DeadLetterEntry, t as BackendMismatchPolicy, u as EventSourcedDBConfig, v as MutationType, w as PruneResult, x as OutboxSyncStatus, y as OutboundEvent, z as SyncPhase } from "./types-DChF-gGL.mjs";
2
+ import { a as resolveModules, i as createEventSourcedDBHandle, n as EventSourcedDBHandleSetup, r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-B_uCsLJC.mjs";
3
3
  import { Collection } from "@tanstack/db";
4
4
 
5
5
  //#region src/core/create-event-sourced-db.d.ts
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as generateEventId, c as SyncPushError, i as createEventSourcedDB, l as createHttpTransport, n as resolveModules, o as createEventSourcedLogger, r as createLazySingleton, s as SyncPullError, t as createEventSourcedDBHandle, u as BackendMismatchError } from "./create-event-sourced-db-handle-BPsVV1x6.mjs";
1
+ import { a as generateEventId, c as SyncPushError, i as createEventSourcedDB, l as createHttpTransport, n as resolveModules, o as createEventSourcedLogger, r as createLazySingleton, s as SyncPullError, t as createEventSourcedDBHandle, u as BackendMismatchError } from "./create-event-sourced-db-handle-D-fHKczr.mjs";
2
2
  import { n as supportsWebLocks, t as createWebLocksSyncLock } from "./web-locks-Bi2f732X.mjs";
3
3
  //#region src/testing/mock-sync-backend.ts
4
4
  /**
package/dist/node.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { Q as InjectedModuleFn, S as PersistedCollectionPersistence, Z as InjectedCreateCollection, p as EventSourcedSharedOptions } from "./types-BSdudVLh.mjs";
2
- import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-BAos-9pS.mjs";
1
+ import { Q as InjectedModuleFn, S as PersistedCollectionPersistence, Z as InjectedCreateCollection, p as EventSourcedSharedOptions } from "./types-DChF-gGL.mjs";
2
+ import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-B_uCsLJC.mjs";
3
3
 
4
4
  //#region src/platforms/node.d.ts
5
5
  type NodePlatformDeps = {
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-BPsVV1x6.mjs";
1
+ import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-D-fHKczr.mjs";
2
2
  //#region src/platforms/node.ts
3
3
  /**
4
4
  * Wraps an already-opened better-sqlite3 database as TanStack persistence.
@@ -1,5 +1,5 @@
1
- import { Q as InjectedModuleFn, S as PersistedCollectionPersistence, Z as InjectedCreateCollection, p as EventSourcedSharedOptions } from "./types-BSdudVLh.mjs";
2
- import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-BAos-9pS.mjs";
1
+ import { Q as InjectedModuleFn, S as PersistedCollectionPersistence, Z as InjectedCreateCollection, p as EventSourcedSharedOptions } from "./types-DChF-gGL.mjs";
2
+ import { r as ModulesInput, t as EventSourcedDBHandle } from "./create-event-sourced-db-handle-B_uCsLJC.mjs";
3
3
 
4
4
  //#region src/platforms/react-native.d.ts
5
5
  type ReactNativePlatformDeps = {
@@ -1,4 +1,4 @@
1
- import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-BPsVV1x6.mjs";
1
+ import { n as resolveModules, t as createEventSourcedDBHandle } from "./create-event-sourced-db-handle-D-fHKczr.mjs";
2
2
  //#region src/platforms/react-native.ts
3
3
  /**
4
4
  * Wraps an already-opened native SQLite database as TanStack persistence.