@lunora/replica 1.0.0-alpha.5 → 1.0.0-alpha.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE.md +214 -0
  2. package/dist/adapters/better-sqlite3.d.mts +14 -14
  3. package/dist/adapters/better-sqlite3.d.ts +14 -14
  4. package/dist/adapters/better-sqlite3.mjs +1 -29
  5. package/dist/adapters/sqlite-wasm.d.mts +25 -25
  6. package/dist/adapters/sqlite-wasm.d.ts +25 -25
  7. package/dist/adapters/sqlite-wasm.mjs +1 -44
  8. package/dist/adapters/sqljs.d.mts +10 -10
  9. package/dist/adapters/sqljs.d.ts +10 -10
  10. package/dist/adapters/sqljs.mjs +1 -55
  11. package/dist/index.d.mts +649 -623
  12. package/dist/index.d.ts +649 -623
  13. package/dist/index.mjs +1 -20
  14. package/dist/packem_shared/EventEmitter-uo75adUL.mjs +1 -0
  15. package/dist/packem_shared/EventLog-B1-yhArT.mjs +1 -0
  16. package/dist/packem_shared/EventLogDO-CaZvpgJN.mjs +1 -0
  17. package/dist/packem_shared/EventLogDOClient-DGDMj5L5.mjs +1 -0
  18. package/dist/packem_shared/EventSource-BC0hKJSA.mjs +1 -0
  19. package/dist/packem_shared/EventsSync-DWyGGZQZ.mjs +1 -0
  20. package/dist/packem_shared/InMemorySnapshotStore-C4taIG5K.mjs +1 -0
  21. package/dist/packem_shared/LocalMirror-DMzMW0Mc.mjs +4 -0
  22. package/dist/packem_shared/MaterializerRuntime-D0HOAe24.mjs +1 -0
  23. package/dist/packem_shared/SubscriptionManager-AhPw3lFc.mjs +1 -0
  24. package/dist/packem_shared/applyDiff-Da160K7_.mjs +1 -0
  25. package/dist/packem_shared/applyDiffToDb-BEot1Jiz.mjs +1 -0
  26. package/dist/packem_shared/classifyChanges-ioMfjjbU.mjs +1 -0
  27. package/dist/packem_shared/defineEvents-DHo-VK7G.mjs +1 -0
  28. package/dist/packem_shared/eventsContext-Dxow9Y7S.mjs +1 -0
  29. package/dist/packem_shared/isClientSeq-D2Xm0_lj.mjs +1 -0
  30. package/dist/packem_shared/local-mirror.d-ByrYTq4z.d.ts +530 -0
  31. package/dist/packem_shared/local-mirror.d-Cip3QuMf.d.mts +530 -0
  32. package/dist/packem_shared/subscribeToMirror-CX10AaP3.mjs +1 -0
  33. package/dist/packem_shared/{types.d-BuAWjEY5.d.mts → types.d-CkMkSwLJ.d.mts} +10 -10
  34. package/dist/packem_shared/{types.d-BuAWjEY5.d.ts → types.d-CkMkSwLJ.d.ts} +10 -10
  35. package/dist/react.d.mts +88 -61
  36. package/dist/react.d.ts +88 -61
  37. package/dist/react.mjs +1 -15
  38. package/package.json +1 -1
  39. package/dist/packem_shared/EventEmitter-CMZfct03.mjs +0 -92
  40. package/dist/packem_shared/EventLog-CnK-3Wge.mjs +0 -264
  41. package/dist/packem_shared/EventLogDO-DqlsVx0H.mjs +0 -381
  42. package/dist/packem_shared/EventLogDOClient-F4FO8Si4.mjs +0 -92
  43. package/dist/packem_shared/EventSource-D5yO9_aI.mjs +0 -221
  44. package/dist/packem_shared/EventsSync-BP36tC9O.mjs +0 -123
  45. package/dist/packem_shared/InMemorySnapshotStore-BHVAD-Bp.mjs +0 -24
  46. package/dist/packem_shared/LocalMirror-a-5jEqFN.mjs +0 -219
  47. package/dist/packem_shared/MaterializerRuntime-BoIrsMYB.mjs +0 -224
  48. package/dist/packem_shared/SubscriptionManager-C5xbw0pg.mjs +0 -75
  49. package/dist/packem_shared/applyDiff-98tKzmiW.mjs +0 -67
  50. package/dist/packem_shared/applyDiffToDb-DQ1xZp5J.mjs +0 -58
  51. package/dist/packem_shared/classifyChanges-RcqLBpLs.mjs +0 -41
  52. package/dist/packem_shared/defineEvents-DiBkPTh_.mjs +0 -28
  53. package/dist/packem_shared/eventsContext-Bk_p48hj.mjs +0 -6
  54. package/dist/packem_shared/isClientSeq-C46BkzqJ.mjs +0 -5
  55. package/dist/packem_shared/local-mirror.d-ByIjd7sW.d.ts +0 -530
  56. package/dist/packem_shared/local-mirror.d-DL1XJBB3.d.mts +0 -530
  57. package/dist/packem_shared/subscribeToMirror-CiaM-nQ7.mjs +0 -45
package/dist/index.d.mts CHANGED
@@ -1,130 +1,146 @@
1
- export { createBetterSqlite3Adapter } from "./adapters/better-sqlite3.mjs";
1
+ export {
2
+ /**
3
+ * \@lunora/replica — Event sourcing runtime + local SQLite mirror for Lunora
4
+ *
5
+ * ## Modules
6
+ *
7
+ * - **EventEmitter** — type-safe event emitter with typed + wildcard listeners.
8
+ * - **EventSource** — event-sourcing runtime that derives state from an
9
+ * append-only log via a reducer.
10
+ * - **SubscriptionManager** — manages state and event-type subscriptions.
11
+ * - **SnapshotStore** — interface + in-memory store for persisting
12
+ * event-sourced state snapshots.
13
+ * - **LocalMirror** — local SQLite mirror that applies typed table diffs.
14
+ * - **EventLog** — append-only log for events and catch-up replication.
15
+ */
16
+ createBetterSqlite3Adapter } from "./adapters/better-sqlite3.mjs";
2
17
  export { createSqliteWasmAdapter } from "./adapters/sqlite-wasm.mjs";
3
18
  export { createSqlJsAdapter } from "./adapters/sqljs.mjs";
4
- import { S as SqliteAdapter } from "./packem_shared/types.d-BuAWjEY5.mjs";
5
- import { T as TableDiff, I as InputEvent, S as Seq, E as EventLogEntry, a as EventLog, A as AppendOptions, L as LocalMirror } from "./packem_shared/local-mirror.d-DL1XJBB3.mjs";
6
- export { type C as ClientSeq, type b as EventLogOptions, type c as EventLogSnapshot, type G as GlobalSeq, type d as LocalMirrorOptions, type M as MirrorTableDef, type R as RowChange, e as classifyChanges, f as createTableDiff, g as diffSize, i as isClientSeq, h as isDiffEmpty, j as isGlobalSeq, k as isInputEvent, m as mergeDiffs } from "./packem_shared/local-mirror.d-DL1XJBB3.mjs";
7
- /**
8
- * Apply a single {@link TableDiff} to an in-memory row map and return
9
- * the updated map.
10
- *
11
- * The function creates a **shallow copy** of the input map so the caller's
12
- * reference stays untouched unless they choose to replace it.
13
- * @example
14
- * ```ts
15
- * const rows = new Map<string, Record<string, unknown>>();
16
- * rows.set("id-1", { name: "alice" });
17
- *
18
- * const diff = createTableDiff("users", [
19
- * { type: "insert", data: { id: "id-2", name: "bob" } },
20
- * { type: "update", id: "id-1", data: { name: "alice-updated" } },
21
- * ]);
22
- *
23
- * const updated = applyDiff(rows, diff);
24
- * updated.get("id-1")?.name // "alice-updated"
25
- * updated.get("id-2")?.name // "bob"
26
- * ```
27
- * @experimental
28
- */
19
+ import { S as SqliteAdapter } from "./packem_shared/types.d-CkMkSwLJ.mjs";
20
+ import { T as TableDiff, I as InputEvent, S as Seq, E as EventLogEntry, a as EventLog, A as AppendOptions, L as LocalMirror } from "./packem_shared/local-mirror.d-Cip3QuMf.mjs";
21
+ export { type C as ClientSeq, type b as EventLogOptions, type c as EventLogSnapshot, type G as GlobalSeq, type d as LocalMirrorOptions, type M as MirrorTableDef, type R as RowChange, e as classifyChanges, f as createTableDiff, g as diffSize, i as isClientSeq, h as isDiffEmpty, j as isGlobalSeq, k as isInputEvent, m as mergeDiffs } from "./packem_shared/local-mirror.d-Cip3QuMf.mjs";
22
+ /**
23
+ * Apply a single {@link TableDiff} to an in-memory row map and return
24
+ * the updated map.
25
+ *
26
+ * The function creates a **shallow copy** of the input map so the caller's
27
+ * reference stays untouched unless they choose to replace it.
28
+ * @example
29
+ * ```ts
30
+ * const rows = new Map<string, Record<string, unknown>>();
31
+ * rows.set("id-1", { name: "alice" });
32
+ *
33
+ * const diff = createTableDiff("users", [
34
+ * { type: "insert", data: { id: "id-2", name: "bob" } },
35
+ * { type: "update", id: "id-1", data: { name: "alice-updated" } },
36
+ * ]);
37
+ *
38
+ * const updated = applyDiff(rows, diff);
39
+ * updated.get("id-1")?.name // "alice-updated"
40
+ * updated.get("id-2")?.name // "bob"
41
+ * ```
42
+ * @experimental
43
+ */
29
44
  declare const applyDiff: (current: ReadonlyMap<string, Record<string, unknown>>, diff: TableDiff) => Map<string, Record<string, unknown>>;
30
45
  /**
31
- * Apply an array of diffs **in order**, returning the final row map.
32
- *
33
- * This is equivalent to calling {@link applyDiff} repeatedly but avoids
34
- * intermediate map copies.
35
- * @experimental
36
- */
46
+ * Apply an array of diffs **in order**, returning the final row map.
47
+ *
48
+ * This is equivalent to calling {@link applyDiff} repeatedly but copies the
49
+ * input map exactly once rather than once per diff — catch-up replay of an
50
+ * N-diff backlog is a single copy, not N+1.
51
+ * @experimental
52
+ */
37
53
  declare const applyDiffs: (current: ReadonlyMap<string, Record<string, unknown>>, diffs: ReadonlyArray<TableDiff>) => Map<string, Record<string, unknown>>;
38
54
  /**
39
- * Merge the row-level effect of a {@link TableDiff} into plain JSON
40
- * state keyed by table name, returning a new snapshot.
41
- * @param snapshot Current snapshot, e.g. `{ users: Map&lt;id, row>, posts: Map&lt;id, row> }`.
42
- * @param diff Contains the target table name and the row-level changes to merge.
43
- * @returns A shallow copy of `snapshot` with `diff.table`'s map updated.
44
- * @experimental
45
- */
55
+ * Merge the row-level effect of a {@link TableDiff} into plain JSON
56
+ * state keyed by table name, returning a new snapshot.
57
+ * @param snapshot Current snapshot, e.g. `{ users: Map<id, row>, posts: Map<id, row> }`.
58
+ * @param diff Contains the target table name and the row-level changes to merge.
59
+ * @returns A shallow copy of `snapshot` with `diff.table`'s map updated.
60
+ * @experimental
61
+ */
46
62
  declare const applyDiffToSnapshot: (snapshot: ReadonlyMap<string, ReadonlyMap<string, Record<string, unknown>>>, diff: TableDiff) => Map<string, Map<string, Record<string, unknown>>>;
47
63
  /** Map a namespace-and-name pair to a qualified event type string. */
48
64
  type QualifiedType<Ns extends string, Name extends string> = `${Ns}.${Name}`;
49
65
  /**
50
- * Extract the payload type from an event schema.
51
- *
52
- * A `@lunora/values` validator (e.g. `v.object(...)`) carries its output type on
53
- * the phantom `__type` field (the same hook `Infer` reads), so match that FIRST
54
- * — otherwise a validator, being object-shaped, would fall through to the
55
- * `Record` branch and resolve to the validator instance itself rather than its
56
- * validated `{ … }` output. A bare factory function or plain descriptor object
57
- * is still supported as a fallback.
58
- */
66
+ * Extract the payload type from an event schema.
67
+ *
68
+ * A `@lunora/values` validator (e.g. `v.object(...)`) carries its output type on
69
+ * the phantom `__type` field (the same hook `Infer` reads), so match that FIRST
70
+ * — otherwise a validator, being object-shaped, would fall through to the
71
+ * `Record` branch and resolve to the validator instance itself rather than its
72
+ * validated `{ … }` output. A bare factory function or plain descriptor object
73
+ * is still supported as a fallback.
74
+ */
59
75
  type PayloadOf<T> = T extends {
60
76
  readonly __type: infer P;
61
77
  } ? P : T extends ((payload: infer P) => unknown) ? P : T extends Record<string, unknown> ? T : never;
62
78
  type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
63
79
  /**
64
- * Produce `{ "ns.name": Payload }` for every event, merged.
65
- */
66
- type EventTypeMap<TDefinition extends Record<string, Record<string, unknown>>> = UnionToIntersection<{ [Ns in keyof TDefinition & string]: { [Name in keyof TDefinition[Ns] & string]: { [K in QualifiedType<Ns, Name>]: PayloadOf<TDefinition[Ns][Name]> } }[keyof TDefinition[Ns] & string] }[keyof TDefinition & string]>;
80
+ * Produce `{ "ns.name": Payload }` for every event, merged.
81
+ */
82
+ type EventTypeMap<TDefinition extends Record<string, Record<string, unknown>>> = UnionToIntersection<{ [Ns in keyof TDefinition & string]: { [Name in keyof TDefinition[Ns] & string]: { [K in QualifiedType<Ns, Name>]: PayloadOf<TDefinition[Ns][Name]>; }; }[keyof TDefinition[Ns] & string]; }[keyof TDefinition & string]>;
67
83
  /**
68
- * A factory function that creates an {@link InputEvent} for a specific event type.
69
- *
70
- * The returned event has no `seq` — it is an optimistic / command payload
71
- * that the event log will assign a sequence number to on append.
72
- * @experimental
73
- */
84
+ * A factory function that creates an {@link InputEvent} for a specific event type.
85
+ *
86
+ * The returned event has no `seq` — it is an optimistic / command payload
87
+ * that the event log will assign a sequence number to on append.
88
+ * @experimental
89
+ */
74
90
  interface EventFactory<Type extends string, Payload> {
75
91
  (payload: Payload): InputEvent<Type, Payload>;
76
92
  /** The fully qualified event type string (e.g. `"chat.messageSent"`). */
77
93
  readonly type: Type;
78
94
  }
79
95
  /**
80
- * The namespace object returned for each group of events.
81
- * @experimental
82
- */
83
- type EventNamespace<Ns extends string, TDefinition extends Record<string, unknown>> = { [Name in keyof TDefinition & string]: EventFactory<QualifiedType<Ns, Name>, PayloadOf<TDefinition[Name]>> };
96
+ * The namespace object returned for each group of events.
97
+ * @experimental
98
+ */
99
+ type EventNamespace<Ns extends string, TDefinition extends Record<string, unknown>> = { [Name in keyof TDefinition & string]: EventFactory<QualifiedType<Ns, Name>, PayloadOf<TDefinition[Name]>>; };
84
100
  /**
85
- * The full result of {@link defineEvents}.
86
- * @experimental
87
- */
88
- type EventsDefinition<TDefinition extends Record<string, Record<string, unknown>>> = { [Ns in keyof TDefinition & string]: EventNamespace<Ns, TDefinition[Ns]> } & {
101
+ * The full result of {@link defineEvents}.
102
+ * @experimental
103
+ */
104
+ type EventsDefinition<TDefinition extends Record<string, Record<string, unknown>>> = { [Ns in keyof TDefinition & string]: EventNamespace<Ns, TDefinition[Ns]>; } & {
89
105
  /** Type-level map of event type → payload shape. Useful for generic code. */
90
106
  readonly _types: EventTypeMap<TDefinition>;
91
107
  };
92
108
  /**
93
- * Declare typed event types for event sourcing.
94
- *
95
- * Each key under a namespace becomes a factory function that produces
96
- * an {@link InputEvent} — an optimistic / command event that the event
97
- * log will assign a sequence number to on append.
98
- * @param definition A nested object where the outer keys are namespaces
99
- * and the inner keys are event names mapped to their
100
- * payload schemas (or simple type-descriptor objects).
101
- * @returns An object with the same nesting structure, where each leaf is
102
- * a factory function plus a `.type` property.
103
- */
109
+ * Declare typed event types for event sourcing.
110
+ *
111
+ * Each key under a namespace becomes a factory function that produces
112
+ * an {@link InputEvent} — an optimistic / command event that the event
113
+ * log will assign a sequence number to on append.
114
+ * @param definition A nested object where the outer keys are namespaces
115
+ * and the inner keys are event names mapped to their
116
+ * payload schemas (or simple type-descriptor objects).
117
+ * @returns An object with the same nesting structure, where each leaf is
118
+ * a factory function plus a `.type` property.
119
+ */
104
120
  interface DefineEventsOptions {
105
121
  /**
106
- * Optional version prefix for all event types.
107
- *
108
- * When set, every qualified event type is prefixed with `"v&lt;N>."`, enabling
109
- * versioned event naming like `"v1.chat.messageSent"` or `"v2.chat.messageSent"`.
110
- * This allows materializers to evolve their handling logic based on the event
111
- * version without breaking backward compatibility.
112
- * @example "v1" → event type becomes "v1.chat.messageSent"
113
- */
122
+ * Optional version prefix for all event types.
123
+ *
124
+ * When set, every qualified event type is prefixed with `"v<N>."`, enabling
125
+ * versioned event naming like `"v1.chat.messageSent"` or `"v2.chat.messageSent"`.
126
+ * This allows materializers to evolve their handling logic based on the event
127
+ * version without breaking backward compatibility.
128
+ * @example "v1" → event type becomes "v1.chat.messageSent"
129
+ */
114
130
  readonly version?: string;
115
131
  }
116
132
  /**
117
- * `defineEvents` is part of the experimental `@lunora/replica` API and may change without a major version bump.
118
- * @experimental
119
- */
133
+ * `defineEvents` is part of the experimental `@lunora/replica` API and may change without a major version bump.
134
+ * @experimental
135
+ */
120
136
  declare const defineEvents: <TDefinition extends Record<string, Record<string, unknown>>>(definition: TDefinition, options?: DefineEventsOptions) => EventsDefinition<TDefinition>;
121
137
  /**
122
- * Shape of the `events[]` items sent in a POST `/append` body.
123
- *
124
- * Like {@link InputEvent} but with `timestamp` optional — omit it to
125
- * let the server assign the timestamp.
126
- * @experimental
127
- */
138
+ * Shape of the `events[]` items sent in a POST `/append` body.
139
+ *
140
+ * Like {@link InputEvent} but with `timestamp` optional — omit it to
141
+ * let the server assign the timestamp.
142
+ * @experimental
143
+ */
128
144
  interface AppendEventInput {
129
145
  /** Globally-unique client identifier (for offline/optimistic support). */
130
146
  readonly clientId?: string;
@@ -140,141 +156,141 @@ interface AppendEventInput {
140
156
  readonly type: string;
141
157
  }
142
158
  /**
143
- * Options for constructing an {@link EventLogDOClient}.
144
- * @experimental
145
- */
159
+ * Options for constructing an {@link EventLogDOClient}.
160
+ * @experimental
161
+ */
146
162
  interface EventLogDOClientOptions {
147
163
  /**
148
- * A function that dispatches an HTTP request to the target EventLogDO
149
- * instance. In a Cloudflare Worker this is:
150
- *
151
- * ```ts
152
- * (req) => env.MY_DO_NAMESPACE.get(id).fetch(req)
153
- * ```
154
- */
164
+ * A function that dispatches an HTTP request to the target EventLogDO
165
+ * instance. In a Cloudflare Worker this is:
166
+ *
167
+ * ```ts
168
+ * (req) => env.MY_DO_NAMESPACE.get(id).fetch(req)
169
+ * ```
170
+ */
155
171
  fetch: (request: Request) => Promise<Response>;
156
172
  }
157
173
  /**
158
- * Lightweight HTTP client for EventLogDO's RPC surface.
159
- *
160
- * Each method maps to one of the DO's endpoints, throws on non-OK status,
161
- * and returns the parsed response body.
162
- * @experimental
163
- */
174
+ * Lightweight HTTP client for EventLogDO's RPC surface.
175
+ *
176
+ * Each method maps to one of the DO's endpoints, throws on non-OK status,
177
+ * and returns the parsed response body.
178
+ * @experimental
179
+ */
164
180
  declare class EventLogDOClient {
165
181
  #private;
166
182
  constructor(options: EventLogDOClientOptions);
167
183
  /**
168
- * Append one or more events to the log.
169
- * @param events The events to append.
170
- * @param options Idempotency controls for the batch.
171
- * @param options.batchId Optional idempotency key for the whole batch — a
172
- * retried `append` call with the same `batchId` (e.g. after a network
173
- * timeout that hid a successful response) returns the originally-persisted
174
- * entries instead of inserting duplicates.
175
- * @returns The persisted entries with their assigned `seq` numbers.
176
- */
184
+ * Append one or more events to the log.
185
+ * @param events The events to append.
186
+ * @param options Idempotency controls for the batch.
187
+ * @param options.batchId Optional idempotency key for the whole batch — a
188
+ * retried `append` call with the same `batchId` (e.g. after a network
189
+ * timeout that hid a successful response) returns the originally-persisted
190
+ * entries instead of inserting duplicates.
191
+ * @returns The persisted entries with their assigned `seq` numbers.
192
+ */
177
193
  append(events: AppendEventInput[], options?: {
178
194
  batchId?: string;
179
195
  }): Promise<EventLogEntry[]>;
180
196
  /**
181
- * Fetch all entries with `seq >= sinceSeq`.
182
- *
183
- * Pass `sinceSeq = 0` to fetch the entire log.
184
- */
197
+ * Fetch all entries with `seq >= sinceSeq`.
198
+ *
199
+ * Pass `sinceSeq = 0` to fetch the entire log.
200
+ */
185
201
  getSince(sinceSeq: number): Promise<EventLogEntry[]>;
186
202
  /**
187
- * Fetch a paginated range of entries.
188
- * @returns `{ entries, hasMore }` — `hasMore` is `true` when another
189
- * page exists (i.e. the DO returned `limit + 1` rows).
190
- */
203
+ * Fetch a paginated range of entries.
204
+ * @returns `{ entries, hasMore }` — `hasMore` is `true` when another
205
+ * page exists (i.e. the DO returned `limit + 1` rows).
206
+ */
191
207
  getRange(fromSeq: number, limit?: number): Promise<{
192
208
  entries: EventLogEntry[];
193
209
  hasMore: boolean;
194
210
  }>;
195
211
  /**
196
- * Return the total number of entries currently in the log.
197
- */
212
+ * Return the total number of entries currently in the log.
213
+ */
198
214
  getSize(): Promise<number>;
199
215
  /**
200
- * Return the full log state — all entries plus the next seq number.
201
- */
216
+ * Return the full log state — all entries plus the next seq number.
217
+ */
202
218
  getState(): Promise<{
203
219
  entries: EventLogEntry[];
204
220
  nextSeq: number;
205
221
  }>;
206
222
  }
207
223
  /**
208
- * Type-safe event emitter that powers the event-sourcing runtime.
209
- * @example
210
- * ```ts
211
- * type MyEvents = { userCreated: { id: string; name: string }; error: { message: string } };
212
- *
213
- * const emitter = new EventEmitter<MyEvents>();
214
- * emitter.on("userCreated", (payload) => console.log(payload.name));
215
- * emitter.emit("userCreated", { id: "1", name: "alice" });
216
- * ```
217
- * @experimental
218
- */
224
+ * Type-safe event emitter that powers the event-sourcing runtime.
225
+ * @example
226
+ * ```ts
227
+ * type MyEvents = { userCreated: { id: string; name: string }; error: { message: string } };
228
+ *
229
+ * const emitter = new EventEmitter<MyEvents>();
230
+ * emitter.on("userCreated", (payload) => console.log(payload.name));
231
+ * emitter.emit("userCreated", { id: "1", name: "alice" });
232
+ * ```
233
+ * @experimental
234
+ */
219
235
  declare class EventEmitter<EventMap extends Record<string, unknown>> {
220
236
  #private;
221
237
  /**
222
- * Register a handler for a specific event type.
223
- * @returns An unsubscribe function (equivalent to calling {@link off}).
224
- */
238
+ * Register a handler for a specific event type.
239
+ * @returns An unsubscribe function (equivalent to calling {@link off}).
240
+ */
225
241
  on<K extends keyof EventMap>(event: K, handler: (payload: EventMap[K]) => void): () => void;
226
242
  /**
227
- * Remove a previously registered handler.
228
- */
243
+ * Remove a previously registered handler.
244
+ */
229
245
  off<K extends keyof EventMap>(event: K, handler: (payload: EventMap[K]) => void): void;
230
246
  /**
231
- * Register a wildcard handler that fires for **every** event type.
232
- * @returns An unsubscribe function.
233
- */
247
+ * Register a wildcard handler that fires for **every** event type.
248
+ * @returns An unsubscribe function.
249
+ */
234
250
  onAny(handler: (event: keyof EventMap, payload: unknown) => void): () => void;
235
251
  /**
236
- * Remove a wildcard handler.
237
- */
252
+ * Remove a wildcard handler.
253
+ */
238
254
  offAny(handler: (event: keyof EventMap, payload: unknown) => void): void;
239
255
  /**
240
- * Emit an event. All registered handlers (typed + wildcard) are invoked
241
- * synchronously. Exceptions from handlers are caught and silently
242
- * swallowed — they **must not** break the emitter loop.
243
- * @returns `true` if at least one handler was called.
244
- */
256
+ * Emit an event. All registered handlers (typed + wildcard) are invoked
257
+ * synchronously. Exceptions from handlers are caught and silently
258
+ * swallowed — they **must not** break the emitter loop.
259
+ * @returns `true` if at least one handler was called.
260
+ */
245
261
  emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): boolean;
246
262
  /**
247
- * Return `true` when at least one listener is registered for `event`.
248
- */
263
+ * Return `true` when at least one listener is registered for `event`.
264
+ */
249
265
  hasListeners(event: keyof EventMap): boolean;
250
266
  /**
251
- * Return the number of typed listeners for a specific event.
252
- */
267
+ * Return the number of typed listeners for a specific event.
268
+ */
253
269
  listenerCount(event: keyof EventMap): number;
254
270
  /**
255
- * Remove all listeners.
256
- */
271
+ * Remove all listeners.
272
+ */
257
273
  clear(): void;
258
274
  }
259
275
  /**
260
- * Strategy for handling events whose `type` the reducer does not recognise.
261
- *
262
- * - `"warn"` _(default)_ — log a warning and skip the event (state unchanged).
263
- * - `"ignore"` — skip silently (no warning, no error).
264
- * - `"fail"` — throw an error, halting the apply / replay cycle.
265
- * - A **callback** — invoked with the entry; return truthy to mark it as
266
- * handled (no warning), falsy to fall through to the configured fallback.
267
- * @experimental
268
- */
276
+ * Strategy for handling events whose `type` the reducer does not recognise.
277
+ *
278
+ * - `"warn"` _(default)_ — log a warning and skip the event (state unchanged).
279
+ * - `"ignore"` — skip silently (no warning, no error).
280
+ * - `"fail"` — throw an error, halting the apply / replay cycle.
281
+ * - A **callback** — invoked with the entry; return truthy to mark it as
282
+ * handled (no warning), falsy to fall through to the configured fallback.
283
+ * @experimental
284
+ */
269
285
  type UnknownEventHandling = "warn" | "ignore" | "fail" | ((entry: EventLogEntry) => boolean);
270
286
  /**
271
- * Events emitted by the {@link EventSource} runtime.
272
- *
273
- * A `type` (not `interface`) so it satisfies `EventEmitter`'s
274
- * `Record&lt;string, unknown>` constraint — interfaces have no implicit index
275
- * signature and aren't assignable to `Record&lt;string, unknown>`.
276
- * @experimental
277
- */
287
+ * Events emitted by the {@link EventSource} runtime.
288
+ *
289
+ * A `type` (not `interface`) so it satisfies `EventEmitter`'s
290
+ * `Record<string, unknown>` constraint — interfaces have no implicit index
291
+ * signature and aren't assignable to `Record<string, unknown>`.
292
+ * @experimental
293
+ */
278
294
  type EventSourceEvents = {
279
295
  /** Fired (once) after the initial replay completes. */
280
296
  ready: {
@@ -292,143 +308,145 @@ type EventSourceEvents = {
292
308
  };
293
309
  };
294
310
  /**
295
- * Sentinel a reducer can return to EXPLICITLY signal it does not handle a
296
- * given event's `type` — as opposed to returning the current `state`
297
- * reference unchanged to represent a legitimate, idempotent no-op for a type
298
- * it DOES recognise.
299
- *
300
- * Reference equality alone can't tell these two cases apart (REPLICA-07): a
301
- * reducer that intentionally returns `state` for a type it fully understands
302
- * (e.g. "already applied this event, nothing to do") would otherwise be
303
- * misclassified as "unhandled" and trigger {@link UnknownEventHandling} — a
304
- * spurious warning, or worse, a thrown error under `"fail"`. Return `UNHANDLED`
305
- * only for a `type` your reducer truly does not recognise; every other return
306
- * (including a `state` returned by reference) is treated as handled.
307
- *
308
- * Reducers that always recognise every event they're given (a single
309
- * always-matching type, or a catch-all) can ignore this entirely.
310
- * @experimental
311
- */
311
+ * Sentinel a reducer can return to EXPLICITLY signal it does not handle a
312
+ * given event's `type` — as opposed to returning the current `state`
313
+ * reference unchanged to represent a legitimate, idempotent no-op for a type
314
+ * it DOES recognise.
315
+ *
316
+ * Reference equality alone can't tell these two cases apart (REPLICA-07): a
317
+ * reducer that intentionally returns `state` for a type it fully understands
318
+ * (e.g. "already applied this event, nothing to do") would otherwise be
319
+ * misclassified as "unhandled" and trigger {@link UnknownEventHandling} — a
320
+ * spurious warning, or worse, a thrown error under `"fail"`. Return `UNHANDLED`
321
+ * only for a `type` your reducer truly does not recognise; every other return
322
+ * (including a `state` returned by reference) is treated as handled.
323
+ *
324
+ * Reducers that always recognise every event they're given (a single
325
+ * always-matching type, or a catch-all) can ignore this entirely.
326
+ * @experimental
327
+ */
312
328
  declare const UNHANDLED: unique symbol;
313
329
  /**
314
- * A function that reduces an event into a state mutation.
315
- *
316
- * Pure functions are strongly encouraged: given the same event payload
317
- * and state, they must produce the same next state. Return {@link UNHANDLED}
318
- * to explicitly mark an event `type` this reducer does not process — see
319
- * {@link UNHANDLED} for why reference equality against the input `state`
320
- * cannot be used for this instead.
321
- * @experimental
322
- */
330
+ * A function that reduces an event into a state mutation.
331
+ *
332
+ * Pure functions are strongly encouraged: given the same event payload
333
+ * and state, they must produce the same next state. Return {@link UNHANDLED}
334
+ * to explicitly mark an event `type` this reducer does not process — see
335
+ * {@link UNHANDLED} for why reference equality against the input `state`
336
+ * cannot be used for this instead.
337
+ * @experimental
338
+ */
323
339
  type EventReducer<S> = (state: S, entry: EventLogEntry) => S | typeof UNHANDLED;
324
340
  /**
325
- * Options for constructing an {@link EventSource}.
326
- * @experimental
327
- */
341
+ * Options for constructing an {@link EventSource}.
342
+ * @experimental
343
+ */
328
344
  interface EventSourceOptions {
329
345
  /**
330
- * Cap this runtime's internal `log` to this many entries (REPLICA-06).
331
- * `replayFromLog` copies every entry it replays from the source log into
332
- * `this.log` too — a second, uncapped copy of the same history — so a
333
- * long-lived `EventSource` fed by repeated replay accumulates entries in
334
- * both places forever without a cap.
335
- *
336
- * `undefined` (the default) preserves unbounded retention.
337
- */
346
+ * Cap this runtime's internal `log` to this many entries (REPLICA-06).
347
+ * `replayFromLog` copies every entry it replays from the source log into
348
+ * `this.log` too — a second, uncapped copy of the same history — so a
349
+ * long-lived `EventSource` fed by repeated replay accumulates entries in
350
+ * both places forever without a cap.
351
+ *
352
+ * `undefined` (the default) preserves unbounded retention.
353
+ */
338
354
  maxLogEntries?: number;
339
355
  /**
340
- * How to handle events whose `type` is not recognised by the reducer.
341
- * @default "warn"
342
- */
356
+ * How to handle events whose `type` is not recognised by the reducer.
357
+ * @default "warn"
358
+ */
343
359
  unknownEventHandling?: UnknownEventHandling;
344
360
  }
345
361
  /**
346
- * Event-sourcing runtime that maintains a derived state by replaying an
347
- * append-only {@link EventLog}.
348
- *
349
- * Usage:
350
- * ```ts
351
- * const source = new EventSource(initialState, myReducer);
352
- * await source.replayFromLog(existingLog);
353
- *
354
- * // Later, when a new event arrives:
355
- * const entry = source.applyEvent("user-created", { id: "1", name: "alice" });
356
- * console.log(source.state); // updated state
357
- * ```
358
- * @experimental
359
- */
362
+ * Event-sourcing runtime that maintains a derived state by replaying an
363
+ * append-only {@link EventLog}.
364
+ *
365
+ * Usage:
366
+ * ```ts
367
+ * const source = new EventSource(initialState, myReducer);
368
+ * await source.replayFromLog(existingLog);
369
+ *
370
+ * // Later, when a new event arrives:
371
+ * const entry = source.applyEvent("user-created", { id: "1", name: "alice" });
372
+ * console.log(source.state); // updated state
373
+ * ```
374
+ * @experimental
375
+ */
360
376
  declare class EventSource<S extends Record<string, unknown> = Record<string, unknown>> {
361
377
  #private;
362
378
  readonly emitter: EventEmitter<EventSourceEvents>;
363
379
  readonly log: EventLog;
364
380
  constructor(initialState: S, reducer: EventReducer<S>, options?: EventSourceOptions);
365
381
  /**
366
- * The current derived state. Read-only snapshot; mutate through events.
367
- */
382
+ * The current derived state. Read-only snapshot; mutate through events.
383
+ */
368
384
  get state(): Readonly<S>;
369
385
  /**
370
- * Whether the initial replay from an existing log has completed.
371
- */
386
+ * Whether the initial replay from an existing log has completed.
387
+ */
372
388
  get replayed(): boolean;
373
389
  /**
374
- * Append a new event to the log and apply it to the current state.
375
- *
376
- * Accepts either an {@link InputEvent} (e.g. from a `defineEvents` factory)
377
- * or the traditional `(type, payload)` pair.
378
- * @returns The newly created log entry (with its assigned `seq`).
379
- */
390
+ * Append a new event to the log and apply it to the current state.
391
+ *
392
+ * Accepts either an {@link InputEvent} (e.g. from a `defineEvents` factory)
393
+ * or the traditional `(type, payload)` pair.
394
+ * @returns The newly created log entry (with its assigned `seq`).
395
+ */
380
396
  applyEvent(event: InputEvent, options?: AppendOptions): EventLogEntry;
381
397
  applyEvent(type: string, payload: unknown, options?: AppendOptions): EventLogEntry;
382
398
  /**
383
- * Replay all entries from an existing {@link EventLog} to bootstrap
384
- * the current state.
385
- *
386
- * Idempotent across calls: only source entries past the `#lastAppliedSeq`
387
- * watermark are applied, so re-invoking picks up just the new entries.
388
- * @param log The external log to replay from.
389
- */
399
+ * Replay all entries from an existing {@link EventLog} to bootstrap
400
+ * the current state.
401
+ *
402
+ * Idempotent across calls: only source entries past the `#lastAppliedSeq`
403
+ * watermark are applied, so re-invoking picks up just the new entries.
404
+ * @param log The external log to replay from.
405
+ */
390
406
  replayFromLog(log: EventLog): void;
391
407
  /**
392
- * Reset the runtime to a base state, optionally resuming from a watermark.
393
- *
394
- * Useful after loading a snapshot from the DO: pass the snapshot's state as
395
- * `initialState` and its highest applied source `seq` as `resumeFromSeq`, so
396
- * the next {@link replayFromLog} applies ONLY the events after the snapshot
397
- * (`getSince(resumeFromSeq + 1)`) rather than replaying the whole log on top
398
- * of the snapshot — which would double-apply non-idempotent reducers.
399
- *
400
- * Omit `resumeFromSeq` (default `-1`) for a full reset that replays from the
401
- * beginning.
402
- * @param initialState The base state to reset to (e.g. a loaded snapshot).
403
- * @param resumeFromSeq Highest source `seq` already baked into `initialState`, or `-1` to replay all.
404
- */
408
+ * Reset the runtime to a base state, optionally resuming from a watermark.
409
+ *
410
+ * Useful after loading a snapshot from the DO: pass the snapshot's state as
411
+ * `initialState` and its highest applied source `seq` as `resumeFromSeq`, so
412
+ * the next {@link replayFromLog} applies ONLY the events after the snapshot
413
+ * (`getSince(resumeFromSeq + 1)`) rather than replaying the whole log on top
414
+ * of the snapshot — which would double-apply non-idempotent reducers.
415
+ *
416
+ * Omit `resumeFromSeq` (default `-1`) for a full reset that replays from the
417
+ * beginning.
418
+ * @param initialState The base state to reset to (e.g. a loaded snapshot).
419
+ * @param resumeFromSeq Highest source `seq` already baked into `initialState`, or `-1` to replay all.
420
+ */
405
421
  reset(initialState: S, resumeFromSeq?: number): void;
406
422
  /**
407
- * Return an async generator that yields every event as it is applied,
408
- * starting from the events currently in the log and continuing with
409
- * every future `applyEvent` / `replayFromLog` call.
410
- *
411
- * The generator runs indefinitely — it never returns. Callers should
412
- * break out of the `for await` loop or use an `AbortSignal` to stop.
413
- * @example
414
- * ```ts
415
- * for await (const entry of source.events()) {
416
- * console.log("event applied:", entry);
417
- * }
418
- * ```
419
- */
423
+ * Return an async generator that yields every event as it is applied,
424
+ * starting from the events currently in the log and continuing with
425
+ * every future `applyEvent` / `replayFromLog` call.
426
+ *
427
+ * The generator runs indefinitely unless given a `signal` — callers
428
+ * should break out of the `for await` loop or pass an `AbortSignal` to
429
+ * stop it (an abort settles the generator, `done: true`, on its next
430
+ * iteration step; it does not throw).
431
+ * @example
432
+ * ```ts
433
+ * for await (const entry of source.events()) {
434
+ * console.log("event applied:", entry);
435
+ * }
436
+ * ```
437
+ */
420
438
  events(signal?: AbortSignal): AsyncGenerator<EventLogEntry>;
421
439
  }
422
440
  /**
423
- * Interface for persisting event-sourced state snapshots.
424
- *
425
- * In a Lunora app the primary implementation is backed by the
426
- * SnapshotDO (a Durable Object) on the server side. On the client
427
- * the {@link InMemorySnapshotStore} is used for the offline-first
428
- * local mirror, while a production client would implement this
429
- * over IndexedDB or OPFS.
430
- * @experimental
431
- */
441
+ * Interface for persisting event-sourced state snapshots.
442
+ *
443
+ * In a Lunora app the primary implementation is backed by the
444
+ * SnapshotDO (a Durable Object) on the server side. On the client
445
+ * the {@link InMemorySnapshotStore} is used for the offline-first
446
+ * local mirror, while a production client would implement this
447
+ * over IndexedDB or OPFS.
448
+ * @experimental
449
+ */
432
450
  interface SnapshotStore {
433
451
  /** Delete all snapshots. */
434
452
  clear: () => Promise<void>;
@@ -442,11 +460,11 @@ interface SnapshotStore {
442
460
  save: (key: string, snapshot: unknown) => Promise<void>;
443
461
  }
444
462
  /**
445
- * In-memory snapshot store. Useful for testing and for the local
446
- * offline-first mirror where persistence is handled at a higher
447
- * layer (IndexedDB adapter).
448
- * @experimental
449
- */
463
+ * In-memory snapshot store. Useful for testing and for the local
464
+ * offline-first mirror where persistence is handled at a higher
465
+ * layer (IndexedDB adapter).
466
+ * @experimental
467
+ */
450
468
  declare class InMemorySnapshotStore implements SnapshotStore {
451
469
  #private;
452
470
  save(key: string, snapshot: unknown): Promise<void>;
@@ -456,23 +474,23 @@ declare class InMemorySnapshotStore implements SnapshotStore {
456
474
  clear(): Promise<void>;
457
475
  }
458
476
  /**
459
- * A function that reduces an event entry into a state mutation.
460
- *
461
- * Pure functions are strongly encouraged: given the same event and state,
462
- * they must produce the same next state for deterministic replay.
463
- * @experimental
464
- */
477
+ * A function that reduces an event entry into a state mutation.
478
+ *
479
+ * Pure functions are strongly encouraged: given the same event and state,
480
+ * they must produce the same next state for deterministic replay.
481
+ * @experimental
482
+ */
465
483
  type MaterializerReducer<S> = (state: S, entry: EventLogEntry) => S;
466
484
  /**
467
- * Options for defining a single materializer.
468
- * @experimental
469
- */
485
+ * Options for defining a single materializer.
486
+ * @experimental
487
+ */
470
488
  interface MaterializerDef<S> {
471
489
  /**
472
- * Reducer invoked for every event in the log.
473
- *
474
- * Return the current state unchanged to skip the event.
475
- */
490
+ * Reducer invoked for every event in the log.
491
+ *
492
+ * Return the current state unchanged to skip the event.
493
+ */
476
494
  handle: MaterializerReducer<S>;
477
495
  /** Factory for the initial (empty) state. */
478
496
  initial: () => S;
@@ -480,9 +498,9 @@ interface MaterializerDef<S> {
480
498
  readonly name: string;
481
499
  }
482
500
  /**
483
- * A constructed materializer ready to be used with a {@link MaterializerRuntime}.
484
- * @experimental
485
- */
501
+ * A constructed materializer ready to be used with a {@link MaterializerRuntime}.
502
+ * @experimental
503
+ */
486
504
  interface Materializer<S> {
487
505
  /** Apply a single event entry through the reducer. */
488
506
  apply: (entry: EventLogEntry) => void;
@@ -495,143 +513,143 @@ interface Materializer<S> {
495
513
  readonly state: Readonly<S>;
496
514
  }
497
515
  /**
498
- * Declare a materializer — a named reducer that derives state from events.
499
- *
500
- * The returned {@link Materializer} object can be used standalone or passed
501
- * to a {@link MaterializerRuntime} for automatic log subscription.
502
- * @experimental
503
- */
516
+ * Declare a materializer — a named reducer that derives state from events.
517
+ *
518
+ * The returned {@link Materializer} object can be used standalone or passed
519
+ * to a {@link MaterializerRuntime} for automatic log subscription.
520
+ * @experimental
521
+ */
504
522
  declare const defineMaterializer: <S>(definition: MaterializerDef<S>) => Materializer<S>;
505
523
  /**
506
- * A materializer of any state shape. The {@link MaterializerRuntime} holds a
507
- * heterogeneous collection and only ever calls `apply(entry)` / `setState(...)`
508
- * (with cast values) / reads `def.name` — it never needs the concrete state
509
- * type. `Materializer&lt;unknown>` won't do: `setState(state: S)` makes
510
- * `Materializer&lt;S>` invariant in `S`, so `Materializer&lt;number>` isn't assignable
511
- * to `Materializer&lt;unknown>`. Erasing the type param is the idiomatic fix.
512
- */
524
+ * A materializer of any state shape. The {@link MaterializerRuntime} holds a
525
+ * heterogeneous collection and only ever calls `apply(entry)` / `setState(...)`
526
+ * (with cast values) / reads `def.name` — it never needs the concrete state
527
+ * type. `Materializer<unknown>` won't do: `setState(state: S)` makes
528
+ * `Materializer<S>` invariant in `S`, so `Materializer<number>` isn't assignable
529
+ * to `Materializer<unknown>`. Erasing the type param is the idiomatic fix.
530
+ */
513
531
  type AnyMaterializer = Materializer<any>;
514
532
  /**
515
- * Options for constructing a {@link MaterializerRuntime}.
516
- * @experimental
517
- */
533
+ * Options for constructing a {@link MaterializerRuntime}.
534
+ * @experimental
535
+ */
518
536
  interface MaterializerRuntimeOptions {
519
537
  /**
520
- * Optional EventLogDO client for persistent event log integration.
521
- *
522
- * When provided, the runtime can bootstrap from the DO on startup
523
- * (recover from snapshots → catch up via `getSince`) and append
524
- * new events through the DO automatically.
525
- */
538
+ * Optional EventLogDO client for persistent event log integration.
539
+ *
540
+ * When provided, the runtime can bootstrap from the DO on startup
541
+ * (recover from snapshots → catch up via `getSince`) and append
542
+ * new events through the DO automatically.
543
+ */
526
544
  doClient?: EventLogDOClient;
527
545
  /** Optional snapshot store for persisting/recovering materialized state. */
528
546
  snapshotStore?: SnapshotStore;
529
547
  /**
530
- * How to handle events whose type no materializer handles.
531
- * @default "warn"
532
- */
548
+ * How to handle events whose type no materializer handles.
549
+ * @default "warn"
550
+ */
533
551
  unknownEventHandling?: UnknownEventHandling;
534
552
  }
535
553
  /**
536
- * Runtime that drives one or more materializers from an event log.
537
- *
538
- * Handles:
539
- * - Replaying the full log on startup
540
- * - Applying new events as they arrive
541
- * - Periodic snapshot persistence
542
- * - Recovery from snapshots (replay only what's missing)
543
- * @experimental
544
- */
554
+ * Runtime that drives one or more materializers from an event log.
555
+ *
556
+ * Handles:
557
+ * - Replaying the full log on startup
558
+ * - Applying new events as they arrive
559
+ * - Periodic snapshot persistence
560
+ * - Recovery from snapshots (replay only what's missing)
561
+ * @experimental
562
+ */
545
563
  declare class MaterializerRuntime {
546
564
  #private;
547
565
  constructor(materializers: AnyMaterializer[], options?: MaterializerRuntimeOptions);
548
566
  /**
549
- * The lowest per-materializer watermark — the seq of the next event that
550
- * at least one materializer has not yet applied. `0` when there are no
551
- * materializers.
552
- */
567
+ * The lowest per-materializer watermark — the seq of the next event that
568
+ * at least one materializer has not yet applied. `0` when there are no
569
+ * materializers.
570
+ */
553
571
  get appliedSeq(): number;
554
572
  /**
555
- * Replay a batch of entries, applying each entry only to the
556
- * materializers whose own watermark is behind it — a materializer at or
557
- * past an entry's seq (e.g. recovered from a snapshot, or already caught
558
- * up) skips it, so no materializer ever double-applies an event.
559
- * @returns The number of entries applied to at least one materializer.
560
- */
573
+ * Replay a batch of entries, applying each entry only to the
574
+ * materializers whose own watermark is behind it — a materializer at or
575
+ * past an entry's seq (e.g. recovered from a snapshot, or already caught
576
+ * up) skips it, so no materializer ever double-applies an event.
577
+ * @returns The number of entries applied to at least one materializer.
578
+ */
561
579
  applyEntries(entries: ReadonlyArray<EventLogEntry>): number;
562
580
  /**
563
- * Attempt to recover materialized state from a snapshot store.
564
- *
565
- * When a snapshot is found for a materializer, its state AND its own
566
- * watermark are restored from that snapshot. A materializer with no
567
- * snapshot keeps its current watermark (`0` for a fresh runtime) — it
568
- * does NOT inherit another materializer's watermark, so it still catches
569
- * up from the very beginning (REPLICA-04: previously a shared watermark
570
- * was bumped to the MAX across snapshots, permanently skipping events 0..N
571
- * for any un-snapshotted or lagging materializer).
572
- * @returns The highest snapshot `appliedSeq` across all materializers, or
573
- * `0` — kept for backward compatibility; callers that need the fetch
574
- * watermark for catch-up should use the per-materializer minimum instead
575
- * (see `initialize`).
576
- */
581
+ * Attempt to recover materialized state from a snapshot store.
582
+ *
583
+ * When a snapshot is found for a materializer, its state AND its own
584
+ * watermark are restored from that snapshot. A materializer with no
585
+ * snapshot keeps its current watermark (`0` for a fresh runtime) — it
586
+ * does NOT inherit another materializer's watermark, so it still catches
587
+ * up from the very beginning (REPLICA-04: previously a shared watermark
588
+ * was bumped to the MAX across snapshots, permanently skipping events 0..N
589
+ * for any un-snapshotted or lagging materializer).
590
+ * @returns The highest snapshot `appliedSeq` across all materializers, or
591
+ * `0` — kept for backward compatibility; callers that need the fetch
592
+ * watermark for catch-up should use the per-materializer minimum instead
593
+ * (see `initialize`).
594
+ */
577
595
  recoverFromSnapshots(): Promise<number>;
578
596
  /**
579
- * Persist the current state of all materializers as snapshots, each
580
- * tagged with ITS OWN watermark (not a shared one).
581
- */
597
+ * Persist the current state of all materializers as snapshots, each
598
+ * tagged with ITS OWN watermark (not a shared one).
599
+ */
582
600
  persistSnapshots(): Promise<void>;
583
601
  /**
584
- * Bootstrap the runtime from the EventLogDO.
585
- *
586
- * 1. Recover materialized state from snapshots (if a snapshotStore is
587
- * configured).
588
- * 2. Fetch all entries since the MINIMUM per-materializer watermark from
589
- * the DO — not the maximum — so a materializer with no snapshot (or a
590
- * lower one) still receives every event it hasn't seen (REPLICA-04).
591
- * 3. Apply them through the materializers; `applyEntries` skips each
592
- * entry for any materializer already past it, so nothing is double-applied.
593
- *
594
- * Call this once on startup / after the DO binding is available.
595
- * @returns The number of entries applied during catch-up.
596
- */
602
+ * Bootstrap the runtime from the EventLogDO.
603
+ *
604
+ * 1. Recover materialized state from snapshots (if a snapshotStore is
605
+ * configured).
606
+ * 2. Fetch all entries since the MINIMUM per-materializer watermark from
607
+ * the DO — not the maximum — so a materializer with no snapshot (or a
608
+ * lower one) still receives every event it hasn't seen (REPLICA-04).
609
+ * 3. Apply them through the materializers; `applyEntries` skips each
610
+ * entry for any materializer already past it, so nothing is double-applied.
611
+ *
612
+ * Call this once on startup / after the DO binding is available.
613
+ * @returns The number of entries applied during catch-up.
614
+ */
597
615
  initialize(): Promise<number>;
598
616
  /**
599
- * Append an event to the EventLogDO and apply it through all
600
- * materializers.
601
- *
602
- * This is a convenience over calling `doClient.append(...)` +
603
- * `runtime.applyEntries(...)` yourself — it persists the event
604
- * **then** applies the returned entry (with its assigned seq).
605
- * @returns The persisted entry with its DO-assigned `seq`.
606
- */
617
+ * Append an event to the EventLogDO and apply it through all
618
+ * materializers.
619
+ *
620
+ * This is a convenience over calling `doClient.append(...)` +
621
+ * `runtime.applyEntries(...)` yourself — it persists the event
622
+ * **then** applies the returned entry (with its assigned seq).
623
+ * @returns The persisted entry with its DO-assigned `seq`.
624
+ */
607
625
  appendEvent(input: AppendEventInput): Promise<EventLogEntry>;
608
626
  /**
609
- * Reset all materializers to their initial state and clear snapshots.
610
- */
627
+ * Reset all materializers to their initial state and clear snapshots.
628
+ */
611
629
  reset(): void;
612
630
  /**
613
- * The list of registered materializers.
614
- */
631
+ * The list of registered materializers.
632
+ */
615
633
  get materializers(): ReadonlyArray<Materializer<unknown>>;
616
634
  }
617
635
  /**
618
- * Apply a {@link TableDiff} to the given SQLite database by translating
619
- * each row change into an INSERT, UPDATE, or DELETE statement.
620
- *
621
- * All statements are wrapped in a single transaction.
622
- * @param database SQLite adapter the statements run against.
623
- * @param diff The table diff to apply.
624
- * @param pkColumn Primary key column for DELETE/UPDATE (default `"id"`).
625
- * @experimental
626
- */
636
+ * Apply a {@link TableDiff} to the given SQLite database by translating
637
+ * each row change into an INSERT, UPDATE, or DELETE statement.
638
+ *
639
+ * All statements are wrapped in a single transaction.
640
+ * @param database SQLite adapter the statements run against.
641
+ * @param diff The table diff to apply.
642
+ * @param pkColumn Primary key column for DELETE/UPDATE (default `"id"`).
643
+ * @experimental
644
+ */
627
645
  declare const applyDiffToDatabase: (database: SqliteAdapter, diff: TableDiff, pkColumn?: string) => void;
628
646
  /**
629
- * Apply multiple diffs **in order** within a single transaction.
630
- *
631
- * Each diff uses `"id"` as the primary key column. For tables with a custom
632
- * PK, use {@link applyDiffToDatabase} per-diff and pass the PK explicitly.
633
- * @experimental
634
- */
647
+ * Apply multiple diffs **in order** within a single transaction.
648
+ *
649
+ * Each diff uses `"id"` as the primary key column. For tables with a custom
650
+ * PK, use {@link applyDiffToDatabase} per-diff and pass the PK explicitly.
651
+ * @experimental
652
+ */
635
653
  declare const applyDiffsToDatabase: (database: SqliteAdapter, diffs: ReadonlyArray<TableDiff>) => void;
636
654
  interface EventLogDOState {
637
655
  storage: {
@@ -639,18 +657,18 @@ interface EventLogDOState {
639
657
  exec: (query: string, ...params: unknown[]) => unknown;
640
658
  };
641
659
  /**
642
- * The DO platform's native atomic-transaction primitive (async;
643
- * commits on resolve, rolls back on throw/reject). Test doubles that
644
- * omit it fall back to a bare (non-transactional) call — see
645
- * `#handleAppend`.
646
- */
660
+ * The DO platform's native atomic-transaction primitive (async;
661
+ * commits on resolve, rolls back on throw/reject). Test doubles that
662
+ * omit it fall back to a bare (non-transactional) call — see
663
+ * `#handleAppend`.
664
+ */
647
665
  transaction?: <T>(closure: () => Promise<T> | T) => Promise<T>;
648
666
  };
649
667
  }
650
668
  /**
651
- * `EventLogDO` is part of the experimental `@lunora/replica` API and may change without a major version bump.
652
- * @experimental
653
- */
669
+ * `EventLogDO` is part of the experimental `@lunora/replica` API and may change without a major version bump.
670
+ * @experimental
671
+ */
654
672
  declare class EventLogDO {
655
673
  #private;
656
674
  protected state: EventLogDOState;
@@ -659,10 +677,10 @@ declare class EventLogDO {
659
677
  fetch(request: Request): Promise<Response>;
660
678
  }
661
679
  /**
662
- * `next()` advances the middleware chain. Called with no argument it forwards
663
- * the current context unchanged; called with `{ ctx }` it shallow-merges the
664
- * extension, and the result type reflects the widened context.
665
- */
680
+ * `next()` advances the middleware chain. Called with no argument it forwards
681
+ * the current context unchanged; called with `{ ctx }` it shallow-merges the
682
+ * extension, and the result type reflects the widened context.
683
+ */
666
684
  interface MiddlewareNext<ContextIn> {
667
685
  (): Promise<ContextIn>;
668
686
  <Extension extends Record<string, unknown>>(options: {
@@ -670,46 +688,45 @@ interface MiddlewareNext<ContextIn> {
670
688
  }): Promise<ContextIn & Extension>;
671
689
  }
672
690
  /**
673
- * A middleware receives the current context and a `next` continuation. Its
674
- * return type becomes the builder's new context, so `return next({ ctx })`
675
- * propagates the extension into every downstream `.use()` and the handler.
676
- */
691
+ * A middleware receives the current context and a `next` continuation. Its
692
+ * return type becomes the builder's new context, so `return next({ ctx })`
693
+ * propagates the extension into every downstream `.use()` and the handler.
694
+ */
677
695
  type Middleware<ContextIn, ContextOut> = (options: {
678
696
  ctx: ContextIn;
679
697
  next: MiddlewareNext<ContextIn>;
680
698
  }) => ContextOut | Promise<ContextOut>;
681
- /** Options accepted by `initLunora.dataModel&lt;DM>().create(...)`. Reserved for transformer/error-formatter wiring. */
682
- /**
683
- * The per-request `ctx.events` facade that {@link eventsContext} attaches.
684
- *
685
- * Each method delegates to the corresponding {@link EventLogDOClient} method,
686
- * so handlers never need to import or reference the DO client directly.
687
- * @experimental
688
- */
699
+ /**
700
+ * The per-request `ctx.events` facade that {@link eventsContext} attaches.
701
+ *
702
+ * Each method delegates to the corresponding {@link EventLogDOClient} method,
703
+ * so handlers never need to import or reference the DO client directly.
704
+ * @experimental
705
+ */
689
706
  interface EventsFacade {
690
707
  /**
691
- * Append one or more events to the log.
692
- * @returns The persisted entries with their assigned `seq` numbers.
693
- */
708
+ * Append one or more events to the log.
709
+ * @returns The persisted entries with their assigned `seq` numbers.
710
+ */
694
711
  append: (events: {
695
712
  payload: unknown;
696
713
  timestamp?: number;
697
714
  type: string;
698
715
  }[]) => Promise<EventLogEntry[]>;
699
716
  /**
700
- * Fetch a paginated range of entries.
701
- * @returns `{ entries, hasMore }` — `hasMore` is `true` when another
702
- * page exists.
703
- */
717
+ * Fetch a paginated range of entries.
718
+ * @returns `{ entries, hasMore }` — `hasMore` is `true` when another
719
+ * page exists.
720
+ */
704
721
  getRange: (fromSeq: number, limit?: number) => Promise<{
705
722
  entries: EventLogEntry[];
706
723
  hasMore: boolean;
707
724
  }>;
708
725
  /**
709
- * Fetch all entries with `seq >= sinceSeq`.
710
- *
711
- * Pass `sinceSeq = 0` to fetch the entire log.
712
- */
726
+ * Fetch all entries with `seq >= sinceSeq`.
727
+ *
728
+ * Pass `sinceSeq = 0` to fetch the entire log.
729
+ */
713
730
  getSince: (sinceSeq: number) => Promise<EventLogEntry[]>;
714
731
  /** Return the total number of entries currently in the log. */
715
732
  getSize: () => Promise<number>;
@@ -720,47 +737,47 @@ interface EventsFacade {
720
737
  }>;
721
738
  }
722
739
  /**
723
- * The context shape produced by {@link eventsContext}.
724
- * @experimental
725
- */
740
+ * The context shape produced by {@link eventsContext}.
741
+ * @experimental
742
+ */
726
743
  interface EventsContextOutput {
727
744
  /** Typed event log facade backed by an {@link EventLogDOClient}. */
728
745
  readonly events: EventsFacade;
729
746
  }
730
747
  /**
731
- * Create a middleware that attaches a typed `ctx.events` facade backed by
732
- * the given {@link EventLogDOClient}.
733
- *
734
- * The facade surfaces `append`, `getSince`, `getRange`, `getSize`, and
735
- * `getState` — every method the DO client exposes — so handlers can read
736
- * and write the event log without reaching for the DO stub directly.
737
- *
738
- * The middleware is unopinionated about which context it extends — it works
739
- * with `MutationCtx`, `ActionCtx`, or `QueryCtx` equally.
740
- * @param client A configured {@link EventLogDOClient} instance.
741
- * @returns A Lunora middleware that injects `ctx.events`.
742
- *
743
- * ```ts
744
- * const client = new EventLogDOClient({
745
- * fetch: (req) => env.EVENTS.get(id).fetch(req),
746
- * });
747
- *
748
- * export const logEvent = mutation
749
- * .use(eventsContext(client))
750
- * .mutation(async ({ ctx, args }) => {
751
- * const [entry] = await ctx.events.append([{ type: "order.placed", payload: args }]);
752
- * return entry;
753
- * });
754
- * ```
755
- * @experimental
756
- */
748
+ * Create a middleware that attaches a typed `ctx.events` facade backed by
749
+ * the given {@link EventLogDOClient}.
750
+ *
751
+ * The facade surfaces `append`, `getSince`, `getRange`, `getSize`, and
752
+ * `getState` — every method the DO client exposes — so handlers can read
753
+ * and write the event log without reaching for the DO stub directly.
754
+ *
755
+ * The middleware is unopinionated about which context it extends — it works
756
+ * with `MutationCtx`, `ActionCtx`, or `QueryCtx` equally.
757
+ * @param client A configured {@link EventLogDOClient} instance.
758
+ * @returns A Lunora middleware that injects `ctx.events`.
759
+ *
760
+ * ```ts
761
+ * const client = new EventLogDOClient({
762
+ * fetch: (req) => env.EVENTS.get(id).fetch(req),
763
+ * });
764
+ *
765
+ * export const logEvent = mutation
766
+ * .use(eventsContext(client))
767
+ * .mutation(async ({ ctx, args }) => {
768
+ * const [entry] = await ctx.events.append([{ type: "order.placed", payload: args }]);
769
+ * return entry;
770
+ * });
771
+ * ```
772
+ * @experimental
773
+ */
757
774
  declare const eventsContext: <Context>(client: EventLogDOClient) => Middleware<Context, Context & EventsContextOutput>;
758
775
  /**
759
- * A dependency-light subscription sink interface that mirrors what
760
- * `LunoraClient.subscribe` expects, so the mirror helper doesn't
761
- * need to import `@lunora/client`.
762
- * @experimental
763
- */
776
+ * A dependency-light subscription sink interface that mirrors what
777
+ * `LunoraClient.subscribe` expects, so the mirror helper doesn't
778
+ * need to import `@lunora/client`.
779
+ * @experimental
780
+ */
764
781
  interface SubscriptionClient {
765
782
  subscribe: (functionRef: {
766
783
  __lunoraRef: string;
@@ -769,203 +786,212 @@ interface SubscriptionClient {
769
786
  }) => () => void;
770
787
  }
771
788
  /**
772
- * Subscribe a Lunora-query to the local mirror so every server push
773
- * is applied to the local SQLite store.
774
- *
775
- * Each frame from a Lunora live query is the FULL current result set, so the
776
- * callback treats it as a snapshot: it upserts every row present and emits a
777
- * `delete` for any id that was mirrored on a previous frame but is absent now —
778
- * otherwise rows that drop out of the server result would linger stale in the
779
- * local mirror. Rows are keyed by their `id` field (the mirror's default primary
780
- * key); a row without an `id` can't be reconciled on removal, and — because the
781
- * mirror table's `id` column is `NOT NULL` — will fail the insert.
782
- *
783
- * The mirror table name is derived from the function ref alone (not `args`), so
784
- * do NOT mirror two subscriptions to the same function with different `args`
785
- * into the same mirror: they'd share one table and the snapshot-delete pass of
786
- * one could remove rows still live in the other.
787
- *
788
- * Call the returned unsubscribe function to tear down both the client
789
- * subscription and future mirror writes.
790
- * @example
791
- * ```ts
792
- * const unsub = subscribeToMirror(client, mirror, api.todos.list, { userId });
793
- * // Later:
794
- * unsub();
795
- * ```
796
- * @experimental
797
- */
789
+ * Subscribe a Lunora-query to the local mirror so every server push
790
+ * is applied to the local SQLite store.
791
+ *
792
+ * Each frame from a Lunora live query is the FULL current result set, so the
793
+ * callback treats it as a snapshot: it upserts every row present and emits a
794
+ * `delete` for any id that was mirrored on a previous frame but is absent now —
795
+ * otherwise rows that drop out of the server result would linger stale in the
796
+ * local mirror. Rows are keyed by their `id` field (the mirror's default primary
797
+ * key); a row without an `id` can't be reconciled on removal, and — because the
798
+ * mirror table's `id` column is `NOT NULL` — will fail the insert.
799
+ *
800
+ * The mirror table name is derived from the function ref alone (not `args`), so
801
+ * do NOT mirror two subscriptions to the same function with different `args`
802
+ * into the same mirror: they'd share one table and the snapshot-delete pass of
803
+ * one could remove rows still live in the other.
804
+ *
805
+ * Call the returned unsubscribe function to tear down both the client
806
+ * subscription and future mirror writes.
807
+ * @example
808
+ * ```ts
809
+ * const unsub = subscribeToMirror(client, mirror, api.todos.list, { userId });
810
+ * // Later:
811
+ * unsub();
812
+ * ```
813
+ * @experimental
814
+ */
798
815
  declare const subscribeToMirror: (client: SubscriptionClient, mirror: LocalMirror, functionRef: {
799
816
  __lunoraRef: string;
800
817
  }, args: Record<string, unknown>, shardKey?: string) => (() => void);
801
818
  /**
802
- * Callback signature for state-change subscriptions.
803
- * @experimental
804
- */
819
+ * Callback signature for state-change subscriptions.
820
+ * @experimental
821
+ */
805
822
  type StateChangeCallback = (state: Readonly<Record<string, unknown>>) => void;
806
823
  /**
807
- * Callback signature for event-type subscriptions.
808
- * @experimental
809
- */
824
+ * Callback signature for event-type subscriptions.
825
+ * @experimental
826
+ */
810
827
  type EventCallback = (entry: EventLogEntry) => void;
811
828
  /**
812
- * Manages subscriptions to state changes and individual event types
813
- * for the event-sourcing runtime.
814
- *
815
- * Each subscription returns an unsubscribe function — the caller is
816
- * expected to call it during cleanup (e.g. in a React `useEffect`
817
- * return or a Svelte `onDestroy`).
818
- * @example
819
- * ```ts
820
- * const subs = new SubscriptionManager();
821
- *
822
- * // Subscribe to every state change
823
- * const unsub1 = subs.onStateChange((state) => console.log("new state", state));
824
- *
825
- * // Subscribe to a specific event type
826
- * const unsub2 = subs.onEvent("user-created", (entry) => console.log("user created", entry.payload));
827
- *
828
- * // Later, when state or events arrive:
829
- * subs.notifyState({ users: [] });
830
- * subs.notifyEvent({ seq: 1, type: "user-created", payload: { id: "1" }, timestamp: 100 });
831
- *
832
- * // Cleanup
833
- * unsub1();
834
- * unsub2();
835
- * ```
836
- * @experimental
837
- */
829
+ * Manages subscriptions to state changes and individual event types
830
+ * for the event-sourcing runtime.
831
+ *
832
+ * Each subscription returns an unsubscribe function — the caller is
833
+ * expected to call it during cleanup (e.g. in a React `useEffect`
834
+ * return or a Svelte `onDestroy`).
835
+ * @example
836
+ * ```ts
837
+ * const subs = new SubscriptionManager();
838
+ *
839
+ * // Subscribe to every state change
840
+ * const unsub1 = subs.onStateChange((state) => console.log("new state", state));
841
+ *
842
+ * // Subscribe to a specific event type
843
+ * const unsub2 = subs.onEvent("user-created", (entry) => console.log("user created", entry.payload));
844
+ *
845
+ * // Later, when state or events arrive:
846
+ * subs.notifyState({ users: [] });
847
+ * subs.notifyEvent({ seq: 1, type: "user-created", payload: { id: "1" }, timestamp: 100 });
848
+ *
849
+ * // Cleanup
850
+ * unsub1();
851
+ * unsub2();
852
+ * ```
853
+ * @experimental
854
+ */
838
855
  declare class SubscriptionManager {
839
856
  #private;
840
857
  /**
841
- * Subscribe to every state change emitted by the event source.
842
- * @returns Unsubscribe function.
843
- */
858
+ * Subscribe to every state change emitted by the event source.
859
+ * @returns Unsubscribe function.
860
+ */
844
861
  onStateChange(callback: StateChangeCallback): () => void;
845
862
  /**
846
- * Subscribe to a specific event type.
847
- * @param eventType The event type to listen for (matches `entry.type`).
848
- * @param callback Invoked with each matching entry.
849
- * @returns Unsubscribe function.
850
- */
863
+ * Subscribe to a specific event type.
864
+ * @param eventType The event type to listen for (matches `entry.type`).
865
+ * @param callback Invoked with each matching entry.
866
+ * @returns Unsubscribe function.
867
+ */
851
868
  onEvent(eventType: string, callback: EventCallback): () => void;
852
869
  /**
853
- * Notify all state-change subscribers with the current state.
854
- */
870
+ * Notify all state-change subscribers with the current state.
871
+ */
855
872
  notifyState(state: Readonly<Record<string, unknown>>): void;
856
873
  /**
857
- * Notify event-type subscribers whose `eventType` matches.
858
- */
874
+ * Notify event-type subscribers whose `eventType` matches.
875
+ */
859
876
  notifyEvent(entry: EventLogEntry): void;
860
877
  /**
861
- * Return the total number of active subscriptions.
862
- */
878
+ * Return the total number of active subscriptions.
879
+ */
863
880
  get size(): number;
864
881
  /**
865
- * Remove all subscriptions.
866
- */
882
+ * Remove all subscriptions.
883
+ */
867
884
  clear(): void;
868
885
  }
869
886
  /**
870
- * Options for constructing an {@link EventsSync}.
871
- * @experimental
872
- */
887
+ * Options for constructing an {@link EventsSync}.
888
+ * @experimental
889
+ */
873
890
  interface EventsSyncOptions {
874
891
  /**
875
- * Replay a batch of events through the derived-state machine.
876
- *
877
- * Called with every batch of new events fetched from the log. The
878
- * consumer should feed these events into their state machine
879
- * (e.g. an {@link import("@lunora/replica").EventSource | EventSource})
880
- * so that the machine's state reflects the latest log position.
881
- */
892
+ * Replay a batch of events through the derived-state machine.
893
+ *
894
+ * Called with every batch of new events fetched from the log. The
895
+ * consumer should feed these events into their state machine
896
+ * (e.g. an {@link import("@lunora/replica").EventSource | EventSource})
897
+ * so that the machine's state reflects the latest log position.
898
+ */
882
899
  applyEvents: (events: ReadonlyArray<EventLogEntry>) => void;
883
900
  /**
884
- * Fetch all events whose `seq >= sinceSeq`.
885
- *
886
- * In a server-side context, this typically wraps
887
- * {@link import("@lunora/replica").EventLogDOClient.getSince |
888
- * EventLogDOClient.getSince()}.
889
- * In a client context it could call a Lunora action that proxies to the
890
- * event log, or read from an IndexedDB cache.
891
- *
892
- * Return an empty array when there are no new events.
893
- */
901
+ * Fetch all events whose `seq >= sinceSeq`.
902
+ *
903
+ * In a server-side context, this typically wraps
904
+ * {@link import("@lunora/replica").EventLogDOClient.getSince |
905
+ * EventLogDOClient.getSince()}.
906
+ * In a client context it could call a Lunora action that proxies to the
907
+ * event log, or read from an IndexedDB cache.
908
+ *
909
+ * Return an empty array when there are no new events.
910
+ */
894
911
  fetchEventsSince: (sinceSeq: number) => Promise<ReadonlyArray<EventLogEntry>>;
895
912
  /**
896
- * Produce {@link TableDiff | TableDiffs} from the current derived state.
897
- *
898
- * Called after every batch of events has been applied. The consumer
899
- * compares the state _before_ and _after_ the batch and returns the
900
- * diffs needed to bring the LocalMirror up to date.
901
- *
902
- * Return an empty array when there are no changes to push to the mirror.
903
- */
913
+ * Produce {@link TableDiff | TableDiffs} from the current derived state.
914
+ *
915
+ * Called after every batch of events has been applied. The consumer
916
+ * **recomputes a full diff from the current mirror-vs-source state** and
917
+ * returns the diffs needed to bring the LocalMirror up to date.
918
+ *
919
+ * **MUST be idempotent** — it must NOT advance a one-shot cursor as a side
920
+ * effect. A batch that fails partway (a `mirror.applyDiff` throws) is
921
+ * retried on the next poll from the same watermark; if this call consumed a
922
+ * cursor on the first attempt it would return `[]` on the retry and the
923
+ * un-mirrored diffs would be lost forever. Recompute-from-current-state has
924
+ * no such hazard: calling it again with no new events returns the same
925
+ * diffs, and calling it after a partial mirror write returns exactly the
926
+ * diffs still missing from the mirror.
927
+ *
928
+ * Return an empty array when there are no changes to push to the mirror.
929
+ */
904
930
  getTableDiffs: () => TableDiff[];
905
931
  /**
906
- * The local SQLite mirror to apply diffs to.
907
- */
932
+ * The local SQLite mirror to apply diffs to.
933
+ */
908
934
  mirror: LocalMirror;
909
935
  /**
910
- * Called when an error occurs during a poll cycle.
911
- *
912
- * Defaults to `console.error`. Set to a no-op to suppress error logging.
913
- */
936
+ * Called when an error occurs during a poll cycle.
937
+ *
938
+ * Defaults to `console.error`. Set to a no-op to suppress error logging.
939
+ */
914
940
  onError?: (error: unknown) => void;
915
941
  /**
916
- * How often to poll for new events (in milliseconds).
917
- * @default 5000
918
- */
942
+ * How often to poll for new events (in milliseconds).
943
+ * @default 5000
944
+ */
919
945
  pollInterval?: number;
920
946
  }
921
947
  /**
922
- * Periodically polls an event log, replays events through a state machine,
923
- * converts the resulting state into {@link TableDiff | TableDiffs}, and
924
- * applies them to a {@link LocalMirror}.
925
- *
926
- * The class is **transport-agnostic** — it accepts a generic
927
- * `fetchEventsSince` function rather than coupling to a specific source
928
- * (EventLogDO, WebSocket push, IndexedDB, etc.).
929
- *
930
- * ## Lifecycle
931
- *
932
- * 1. Call `start()` to begin periodic polling.
933
- * 2. Call `sync()` to perform an immediate one-shot sync.
934
- * 3. Call `stop()` to halt polling.
935
- *
936
- * The current watermark is exposed via `watermark` and advances
937
- * monotonically as events are applied.
938
- * @experimental
939
- */
948
+ * Periodically polls an event log, replays events through a state machine,
949
+ * converts the resulting state into {@link TableDiff | TableDiffs}, and
950
+ * applies them to a {@link LocalMirror}.
951
+ *
952
+ * The class is **transport-agnostic** — it accepts a generic
953
+ * `fetchEventsSince` function rather than coupling to a specific source
954
+ * (EventLogDO, WebSocket push, IndexedDB, etc.).
955
+ *
956
+ * ## Lifecycle
957
+ *
958
+ * 1. Call `start()` to begin periodic polling.
959
+ * 2. Call `sync()` to perform an immediate one-shot sync.
960
+ * 3. Call `stop()` to halt polling.
961
+ *
962
+ * The current watermark is exposed via `watermark` and advances
963
+ * monotonically as events are applied.
964
+ * @experimental
965
+ */
940
966
  declare class EventsSync {
941
967
  #private;
942
968
  constructor(options: EventsSyncOptions);
943
969
  /**
944
- * The current watermark — the next `seq` the sync will fetch from.
945
- *
946
- * Starts at `0` (fetch everything). Advances to `max(seq) + 1` after
947
- * each successful poll cycle.
948
- */
970
+ * The current watermark — the next `seq` the sync will fetch from.
971
+ *
972
+ * Starts at `0` (fetch everything). Advances to `max(seq) + 1` after
973
+ * each successful poll cycle.
974
+ */
949
975
  get watermark(): number;
950
976
  /**
951
- * Start polling for new events on the configured interval.
952
- *
953
- * Does nothing if polling is already active.
954
- * Does **not** perform an initial sync — call {@link sync} once if you
955
- * need to catch up immediately.
956
- */
977
+ * Start polling for new events on the configured interval.
978
+ *
979
+ * Does nothing if polling is already active.
980
+ * Does **not** perform an initial sync — call {@link sync} once if you
981
+ * need to catch up immediately.
982
+ */
957
983
  start(): void;
958
984
  /**
959
- * Stop polling for new events.
960
- *
961
- * Safe to call when not started.
962
- */
985
+ * Stop polling for new events.
986
+ *
987
+ * Safe to call when not started.
988
+ */
963
989
  stop(): void;
964
990
  /**
965
- * Perform a one-shot sync: fetch events since the current watermark,
966
- * apply them through the state machine, and push diffs to the mirror.
967
- * @returns The number of events that were fetched and applied.
968
- */
991
+ * Perform a one-shot sync: fetch events since the current watermark,
992
+ * apply them through the state machine, and push diffs to the mirror.
993
+ * @returns The number of events that were fetched and applied.
994
+ */
969
995
  sync(): Promise<number>;
970
996
  }
971
997
  export { type AppendEventInput, type AppendOptions, type EventCallback, EventEmitter, type EventFactory, EventLog, EventLogDO, EventLogDOClient, type EventLogDOClientOptions, type EventLogEntry, type EventNamespace, type EventReducer, EventSource, type EventSourceEvents, type EventSourceOptions, type EventsContextOutput, type EventsDefinition, type EventsFacade, EventsSync, type EventsSyncOptions, InMemorySnapshotStore, type InputEvent, LocalMirror, type Materializer, type MaterializerDef, type MaterializerReducer, MaterializerRuntime, type MaterializerRuntimeOptions, type Seq, type SnapshotStore, type SqliteAdapter, type StateChangeCallback, type SubscriptionClient, SubscriptionManager, type TableDiff, UNHANDLED, type UnknownEventHandling, applyDiff, applyDiffToDatabase as applyDiffToDb, applyDiffToSnapshot, applyDiffs, applyDiffsToDatabase as applyDiffsToDb, defineEvents, defineMaterializer, eventsContext, subscribeToMirror };