@lunora/client 1.0.0-alpha.23 → 1.0.0-alpha.25

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 (28) hide show
  1. package/dist/auth/index.d.mts +10 -10
  2. package/dist/auth/index.d.ts +10 -10
  3. package/dist/index.d.mts +344 -357
  4. package/dist/index.d.ts +344 -357
  5. package/dist/index.mjs +5 -5
  6. package/dist/packem_shared/{LunoraClient-BBCQjjbl.mjs → LunoraClient-D3h4P7hg.mjs} +18 -4
  7. package/dist/packem_shared/{OfflineQueue-B4HUF7rt.mjs → OfflineQueue-BgarnAub.mjs} +1 -1
  8. package/dist/packem_shared/{TabCoordinator-BwRR8H06.mjs → TabCoordinator-D_5oNTTt.mjs} +48 -12
  9. package/dist/packem_shared/{createClientQuery-CQ51bWAE.mjs → createClientQuery-dJZg1ohm.mjs} +15 -6
  10. package/dist/packem_shared/{createServerClient-CTTAmvMx.mjs → createServerClient-DzeC2J3A.mjs} +1 -1
  11. package/dist/packem_shared/{httpStream-BJU-aflc.mjs → httpStream-DIdL8NEw.mjs} +33 -24
  12. package/dist/packem_shared/lunora-client.d-C4ud8bej.d.mts +2834 -0
  13. package/dist/packem_shared/lunora-client.d-C4ud8bej.d.ts +2834 -0
  14. package/dist/packem_shared/{offline-queue-CF4_Co5k.mjs → offline-queue-N-1JvYb4.mjs} +14 -2
  15. package/dist/packem_shared/preload.d-B6-lqUf2.d.ts +20 -0
  16. package/dist/packem_shared/preload.d-Dvk8zg6m.d.mts +20 -0
  17. package/dist/pagination/index.d.mts +42 -42
  18. package/dist/pagination/index.d.ts +42 -42
  19. package/dist/query/index.d.mts +42 -42
  20. package/dist/query/index.d.ts +42 -42
  21. package/dist/ssr/index.d.mts +79 -79
  22. package/dist/ssr/index.d.ts +79 -79
  23. package/dist/ssr/index.mjs +1 -1
  24. package/package.json +2 -2
  25. package/dist/packem_shared/lunora-client.d-JvtVpf8A.d.mts +0 -2824
  26. package/dist/packem_shared/lunora-client.d-JvtVpf8A.d.ts +0 -2824
  27. package/dist/packem_shared/preload.d-C4_d_l5v.d.ts +0 -20
  28. package/dist/packem_shared/preload.d-DKbjGN5O.d.mts +0 -20
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { c as PersistenceAdapter, B as BookmarkStorage, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, g as ReconnectOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-JvtVpf8A.js";
2
- export { type h as BatchSlot, C as CONFLICT_ERROR_CODE, type i as CachedQuery, type j as ClientMessage, type k as ClientQueryRef, type l as ClientShapeSubscribeMessage, type m as ClientShapeUnsubscribeMessage, type n as ConnectionStatus, D as DEFAULT_MAX_BUFFER, type o as FunctionArgumentDescriptor, type p as FunctionDescriptor, type G as GlobalFacetResult, type q as GlobalFacetValue, type r as GlobalFilterClause, type s as GlobalTableInfo, type t as GlobalTablePage, type u as HttpStreamCallArgs, type v as LunoraClientError, type w as LunoraClientOptions, type x as LunoraErrorCode, type y as MutationSettledEvent, type z as OptimisticLocalStore, type E as OptimisticUpdate, type I as OutboxMutation, type J as OutboxSink, type K as PersistedMutation, type P as Preloaded, type N as RowOp, type T as RpcEnvelope, type V as RpcResponseBody, type W as ScheduleRecord, type X as SchedulerPoolStatus, type Y as SchedulerStatus, type Z as ServerMessage, type _ as ServerPokeEndMessage, type $ as ServerPokePartMessage, type a0 as ServerPokeStartMessage, type a1 as ShardTrafficEntry, type a2 as ShardTrafficResult, type a3 as StorageListPage, type a4 as StorageObject, type a5 as StreamHandle, type a6 as SubscriptionCallback, type b as SubscriptionErrorCallback, a7 as SubscriptionRegistry, type a8 as SubscriptionState, type a9 as SyncWatermark, type a as Unsubscribe, type U as User, type aa as WorkflowInstanceAction, type ab as WorkflowInstanceDetail, type ac as WorkflowInstancePage, type ad as WorkflowInstanceStatus, type ae as WorkflowInstanceSummary, type af as WorkflowStepDetail, type ag as WsTokenProvider, ah as createClientQuery, ai as createLocalStore, aj as createStream, ak as getErrorCode, al as getRetryAfterMs, am as isConflictError, an as isForbiddenError, ao as isRateLimitedError, ap as isUnauthorizedError } from "./packem_shared/lunora-client.d-JvtVpf8A.js";
3
- export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-C4_d_l5v.js";
1
+ import { c as PersistenceAdapter, B as BookmarkStorage, S as SubscriptionError, H as HttpStreamRef, d as HttpStreamArgsOf, e as HttpStreamChunkOf, f as StreamIterable, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, g as ReconnectOptions, L as LunoraClient } from "./packem_shared/lunora-client.d-C4ud8bej.js";
2
+ export { type h as BatchSlot, C as CONFLICT_ERROR_CODE, type i as CachedQuery, type j as ClientMessage, type k as ClientQueryRef, type l as ClientShapeSubscribeMessage, type m as ClientShapeUnsubscribeMessage, type n as ConnectionStatus, D as DEFAULT_MAX_BUFFER, type o as FunctionArgumentDescriptor, type p as FunctionDescriptor, type G as GlobalFacetResult, type q as GlobalFacetValue, type r as GlobalFilterClause, type s as GlobalTableInfo, type t as GlobalTablePage, type u as HttpStreamCallArgs, type v as LunoraClientError, type w as LunoraClientOptions, type x as LunoraErrorCode, type y as MutationSettledEvent, type z as OptimisticLocalStore, type E as OptimisticUpdate, type I as OutboxMutation, type J as OutboxSink, type K as PersistedMutation, type P as Preloaded, type N as RowOp, type T as RpcEnvelope, type V as RpcResponseBody, type W as ScheduleRecord, type X as SchedulerPoolStatus, type Y as SchedulerStatus, type Z as ServerMessage, type _ as ServerPokeEndMessage, type $ as ServerPokePartMessage, type a0 as ServerPokeStartMessage, type a1 as ShardTrafficEntry, type a2 as ShardTrafficResult, type a3 as StorageListPage, type a4 as StorageObject, type a5 as StreamHandle, type a6 as SubscriptionCallback, type b as SubscriptionErrorCallback, a7 as SubscriptionRegistry, type a8 as SubscriptionState, type a9 as SyncWatermark, type a as Unsubscribe, type U as User, type aa as WorkflowInstanceAction, type ab as WorkflowInstanceDetail, type ac as WorkflowInstancePage, type ad as WorkflowInstanceStatus, type ae as WorkflowInstanceSummary, type af as WorkflowStepDetail, type ag as WsTokenProvider, ah as createClientQuery, ai as createLocalStore, aj as createStream, ak as getErrorCode, al as getRetryAfterMs, am as isConflictError, an as isForbiddenError, ao as isRateLimitedError, ap as isUnauthorizedError } from "./packem_shared/lunora-client.d-C4ud8bej.js";
3
+ export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-B6-lqUf2.js";
4
4
  export type { AuthCapabilities, AuthConfigInfo, AuthImpersonation, AuthPage, AuthSession, AuthUser, AuthUserFieldSpec, CronJobInfo, KvKeyEntry, KvKeyListResult, KvNamespaceSummary, KvValueResult, VectorIndexSummary, VectorQueryMatch } from '@lunora/runtime';
5
5
  /**
6
- * The slice of React Native's `AsyncStorage` (or any async key/value store —
7
- * Expo `SecureStore`, a wrapped `localForage`, an in-memory map in tests) this
8
- * adapter needs. Matches `@react-native-async-storage/async-storage`'s core
9
- * surface, so you can pass the module straight in.
10
- */
6
+ * The slice of React Native's `AsyncStorage` (or any async key/value store —
7
+ * Expo `SecureStore`, a wrapped `localForage`, an in-memory map in tests) this
8
+ * adapter needs. Matches `@react-native-async-storage/async-storage`'s core
9
+ * surface, so you can pass the module straight in.
10
+ */
11
11
  interface AsyncStorageLike {
12
12
  getItem: (key: string) => Promise<string | null>;
13
13
  removeItem: (key: string) => Promise<void>;
@@ -20,48 +20,48 @@ interface AsyncStoragePersistenceOptions {
20
20
  storage: AsyncStorageLike;
21
21
  }
22
22
  /**
23
- * Builds a {@link PersistenceAdapter} over an async key/value store — the React
24
- * Native / Expo counterpart to the IndexedDB adapter (`createIndexedDbPersistence`).
25
- * The whole FIFO mutation log is serialized to JSON under a single key (`key`),
26
- * so enqueue order is preserved and `load()` returns freshly-parsed records that
27
- * callers can't alias.
28
- *
29
- * AsyncStorage has no transactions, so every read-modify-write is funnelled
30
- * through a single promise chain — concurrent `append`/`remove` calls run one at
31
- * a time and can't clobber each other's writes.
32
- */
23
+ * Builds a {@link PersistenceAdapter} over an async key/value store — the React
24
+ * Native / Expo counterpart to the IndexedDB adapter (`createIndexedDbPersistence`).
25
+ * The whole FIFO mutation log is serialized to JSON under a single key (`key`),
26
+ * so enqueue order is preserved and `load()` returns freshly-parsed records that
27
+ * callers can't alias.
28
+ *
29
+ * AsyncStorage has no transactions, so every read-modify-write is funnelled
30
+ * through a single promise chain — concurrent `append`/`remove` calls run one at
31
+ * a time and can't clobber each other's writes.
32
+ */
33
33
  declare const createAsyncStoragePersistence: (options: AsyncStoragePersistenceOptions) => PersistenceAdapter;
34
34
  /** Default in-memory bookmark store. Survives the lifetime of the client. */
35
35
  declare const createInMemoryBookmarkStorage: () => BookmarkStorage;
36
36
  interface TabCoordinatorOptions {
37
37
  /**
38
- * BroadcastChannel name. Defaults to `"lunora-bridge"`.
39
- */
38
+ * BroadcastChannel name. Defaults to `"lunora-bridge"`.
39
+ */
40
40
  channelName?: string;
41
41
  /**
42
- * Interval (ms) between leader heartbeats. Defaults to 1000.
43
- */
42
+ * Interval (ms) between leader heartbeats. Defaults to 1000.
43
+ */
44
44
  heartbeatInterval?: number;
45
45
  /**
46
- * Milliseconds without a heartbeat to consider the leader dead. Must be
47
- * larger than `heartbeatInterval`. Defaults to 3000.
48
- */
46
+ * Milliseconds without a heartbeat to consider the leader dead. Must be
47
+ * larger than `heartbeatInterval`. Defaults to 3000.
48
+ */
49
49
  leaderTimeout?: number;
50
50
  /**
51
- * Called when this tab becomes the leader (should open WS connections).
52
- */
51
+ * Called when this tab becomes the leader (should open WS connections).
52
+ */
53
53
  onBecomeLeader?: () => void;
54
54
  /**
55
- * Called when this tab loses leadership (should close WS connections).
56
- */
55
+ * Called when this tab loses leadership (should close WS connections).
56
+ */
57
57
  onStopBeingLeader?: () => void;
58
58
  /**
59
- * Called when the leader broadcasts subscription data.
60
- */
59
+ * Called when the leader broadcasts subscription data.
60
+ */
61
61
  onSubscriptionData?: (key: string, data: unknown) => void;
62
62
  /**
63
- * Called when the leader broadcasts a subscription error.
64
- */
63
+ * Called when the leader broadcasts a subscription error.
64
+ */
65
65
  onSubscriptionError?: (key: string, error: SubscriptionError) => void;
66
66
  }
67
67
  declare class TabCoordinator {
@@ -86,14 +86,14 @@ declare class TabCoordinator {
86
86
  private readonly onSubscriptionError;
87
87
  constructor(options?: TabCoordinatorOptions);
88
88
  /**
89
- * Start the coordinator: attempt to claim leadership and begin the
90
- * heartbeat/leader-check cycle. Safe to call multiple times.
91
- */
89
+ * Start the coordinator: attempt to claim leadership and begin the
90
+ * heartbeat/leader-check cycle. Safe to call multiple times.
91
+ */
92
92
  start(): void;
93
93
  /**
94
- * Stop the coordinator: yield leadership (if held), close the channel, and
95
- * clear all timers. Safe to call multiple times.
96
- */
94
+ * Stop the coordinator: yield leadership (if held), close the channel, and
95
+ * clear all timers. Safe to call multiple times.
96
+ */
97
97
  stop(): void;
98
98
  /** `true` when this tab is the current WebSocket leader. */
99
99
  isLeader(): boolean;
@@ -104,48 +104,63 @@ declare class TabCoordinator {
104
104
  /** `true` when the coordinator has been started and is not yet stopped. */
105
105
  get isRunning(): boolean;
106
106
  /**
107
- * Broadcast subscription data to all follower tabs. Only the leader should
108
- * call this.
109
- */
107
+ * Broadcast subscription data to all follower tabs. Only the leader should
108
+ * call this.
109
+ */
110
110
  broadcastSubscriptionData(key: string, data: unknown): void;
111
111
  /**
112
- * Broadcast a subscription error to all follower tabs. Only the leader
113
- * should call this.
114
- */
112
+ * Broadcast a subscription error to all follower tabs. Only the leader
113
+ * should call this.
114
+ */
115
115
  broadcastSubscriptionError(key: string, error: SubscriptionError): void;
116
116
  private broadcast;
117
117
  private handleMessage;
118
+ private handleClaimLeadership;
119
+ private handleHeartbeat;
120
+ /**
121
+ * Resolve two leaders existing at once — e.g. this tab was backgrounded
122
+ * and its heartbeat/health timers were throttled while a foreground
123
+ * follower's were not, so the follower's `checkLeaderHealth` timed out
124
+ * the (still-alive) leader and self-promoted. `BroadcastChannel` message
125
+ * delivery isn't subject to the same timer-throttling clamp, so even a
126
+ * backgrounded leader eventually observes the pretender's heartbeat here
127
+ * — resolve the split-brain deterministically with the same
128
+ * lexicographically-smaller-tabId rule used at claim-adoption. If the
129
+ * other tab wins, step down; if we win, reassert immediately so the
130
+ * pretender demotes itself the moment it processes our heartbeat.
131
+ */
132
+ private resolveLeaderVsLeaderTieBreak;
118
133
  private becomeLeader;
119
134
  private sendHeartbeat;
120
135
  private checkLeaderHealth;
121
136
  }
122
137
  /**
123
- * Client-side incremental merging of structured mutation deltas.
124
- *
125
- * Lunora's live-query fan-out has two server paths:
126
- *
127
- * 1. Server re-execution (subscriptions carrying a `functionPath`) pushes a
128
- * full `data` snapshot whenever a write touches a table the query reads. These
129
- * already carry the authoritative result and are applied wholesale.
130
- * 2. Legacy delta fan-out (`broadcastDelta`) pushes a structured `MutationDelta`
131
- * as a `delta` frame to subscribers matched by table + args. The delta describes
132
- * a single row change (`insert` / `update` / `delete`) keyed by row id, so the
133
- * client can splice it into the cached list result without a full re-send.
134
- *
135
- * Historically the client treated the `delta` field as an opaque blob and
136
- * replaced the whole cached value with it on every message — which only made
137
- * sense for the rare delta payloads that already carried the full result. This
138
- * module lets the client recognise a structured delta and merge it into the
139
- * existing array (preserving order, no dup/loss), falling back to full
140
- * replacement when the payload isn't a recognisable row delta or can't be
141
- * applied cleanly against the current cached shape.
142
- */
143
- /**
144
- * One row change as emitted by `@lunora/do`'s `broadcastDelta`. Mirrors
145
- * `MutationDelta` in `@lunora/do` structurally so the client carries no
146
- * dependency on it. `row` is absent on `delete` events (and may be absent on
147
- * older servers for any op).
148
- */
138
+ * Client-side incremental merging of structured mutation deltas.
139
+ *
140
+ * Lunora's live-query fan-out has two server paths:
141
+ *
142
+ * 1. Server re-execution (subscriptions carrying a `functionPath`) pushes a
143
+ * full `data` snapshot whenever a write touches a table the query reads. These
144
+ * already carry the authoritative result and are applied wholesale.
145
+ * 2. Legacy delta fan-out (`broadcastDelta`) pushes a structured `MutationDelta`
146
+ * as a `delta` frame to subscribers matched by table + args. The delta describes
147
+ * a single row change (`insert` / `update` / `delete`) keyed by row id, so the
148
+ * client can splice it into the cached list result without a full re-send.
149
+ *
150
+ * Historically the client treated the `delta` field as an opaque blob and
151
+ * replaced the whole cached value with it on every message — which only made
152
+ * sense for the rare delta payloads that already carried the full result. This
153
+ * module lets the client recognise a structured delta and merge it into the
154
+ * existing array (preserving order, no dup/loss), falling back to full
155
+ * replacement when the payload isn't a recognisable row delta or can't be
156
+ * applied cleanly against the current cached shape.
157
+ */
158
+ /**
159
+ * One row change as emitted by `@lunora/do`'s `broadcastDelta`. Mirrors
160
+ * `MutationDelta` in `@lunora/do` structurally so the client carries no
161
+ * dependency on it. `row` is absent on `delete` events (and may be absent on
162
+ * older servers for any op).
163
+ */
149
164
  interface MutationDelta {
150
165
  /** Row id (`_id`) the change applies to. */
151
166
  key: string;
@@ -154,43 +169,43 @@ interface MutationDelta {
154
169
  table: string;
155
170
  }
156
171
  /**
157
- * Structural guard: is `value` a `MutationDelta` the client knows how to merge?
158
- * We require `op`, `table`, and a string `key` so opaque payloads that merely
159
- * happen to be objects (e.g. an aggregate `{ count: 1 }` a query returns
160
- * verbatim) are never mistaken for a row delta and keep replacing the cached
161
- * value wholesale.
162
- */
172
+ * Structural guard: is `value` a `MutationDelta` the client knows how to merge?
173
+ * We require `op`, `table`, and a string `key` so opaque payloads that merely
174
+ * happen to be objects (e.g. an aggregate `{ count: 1 }` a query returns
175
+ * verbatim) are never mistaken for a row delta and keep replacing the cached
176
+ * value wholesale.
177
+ */
163
178
  declare const isMutationDelta: (value: unknown) => value is MutationDelta;
164
179
  /**
165
- * Apply a structured `MutationDelta` to a cached array result, returning a new
166
- * array (never mutating the input). Returns `undefined` when the delta can't be
167
- * applied cleanly — the caller should then fall back to the existing
168
- * full-replacement behaviour (or trust the next snapshot to reconcile).
169
- *
170
- * Mergeable shape: a plain array of id-bearing row objects, e.g. the result of
171
- * `db.query().collect()`.
172
- *
173
- * Insert / update / delete are matched by row `_id`:
174
- * - `insert`: appended (or placed by `_creationTime` order) if absent; treated
175
- * as an update if a row with the same id already exists (idempotent — guards
176
- * against a delta replayed after a snapshot already included it).
177
- * - `update`: replaces the matching row in place, preserving its position.
178
- * - `delete`: removes the matching row.
179
- *
180
- * Returns `undefined` when `current` isn't an array of id-keyable objects, or
181
- * when an `insert`/`update` delta carries no `row` to splice in.
182
- */
180
+ * Apply a structured `MutationDelta` to a cached array result, returning a new
181
+ * array (never mutating the input). Returns `undefined` when the delta can't be
182
+ * applied cleanly — the caller should then fall back to the existing
183
+ * full-replacement behaviour (or trust the next snapshot to reconcile).
184
+ *
185
+ * Mergeable shape: a plain array of id-bearing row objects, e.g. the result of
186
+ * `db.query().collect()`.
187
+ *
188
+ * Insert / update / delete are matched by row `_id`:
189
+ * - `insert`: appended (or placed by `_creationTime` order) if absent; treated
190
+ * as an update if a row with the same id already exists (idempotent — guards
191
+ * against a delta replayed after a snapshot already included it).
192
+ * - `update`: replaces the matching row in place, preserving its position.
193
+ * - `delete`: removes the matching row.
194
+ *
195
+ * Returns `undefined` when `current` isn't an array of id-keyable objects, or
196
+ * when an `insert`/`update` delta carries no `row` to splice in.
197
+ */
183
198
  declare const applyDelta: (current: unknown, delta: MutationDelta) => undefined | unknown[];
184
199
  /**
185
- * Options accepted by {@link httpStream}.
186
- * @experimental Part of the HTTP-SSE stream surface.
187
- */
200
+ * Options accepted by {@link httpStream}.
201
+ * @experimental Part of the HTTP-SSE stream surface.
202
+ */
188
203
  interface HttpStreamOptions {
189
204
  /**
190
- * Origin (or origin + prefix) the route path is appended to, e.g.
191
- * `https://my-app.example.com`. Defaults to `""` — a relative URL, which
192
- * resolves against the page origin in a browser.
193
- */
205
+ * Origin (or origin + prefix) the route path is appended to, e.g.
206
+ * `https://my-app.example.com`. Defaults to `""` — a relative URL, which
207
+ * resolves against the page origin in a browser.
208
+ */
194
209
  baseUrl?: string;
195
210
  /** `fetch` implementation override; defaults to the global `fetch`. */
196
211
  fetch?: typeof fetch;
@@ -202,32 +217,32 @@ interface HttpStreamOptions {
202
217
  signal?: AbortSignal;
203
218
  }
204
219
  /**
205
- * Open a typed HTTP-SSE stream route and iterate its chunks:
206
- *
207
- * ```ts
208
- * const stream = httpStream(httpStreams.http.tokens, { searchParams: { prompt } }, { baseUrl });
209
- * for await (const token of stream) {
210
- * render(token); // typed as the route handler's yielded chunk
211
- * }
212
- * ```
213
- *
214
- * The returned iterable terminates when the server writes `event: complete`;
215
- * an `event: error` frame (or a transport failure) surfaces as a coded
216
- * rejection on the next `next()`. `.cancel()` — or aborting `options.signal` —
217
- * aborts the underlying fetch, which the server observes via `request.signal`.
218
- * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
219
- */
220
+ * Open a typed HTTP-SSE stream route and iterate its chunks:
221
+ *
222
+ * ```ts
223
+ * const stream = httpStream(httpStreams.http.tokens, { searchParams: { prompt } }, { baseUrl });
224
+ * for await (const token of stream) {
225
+ * render(token); // typed as the route handler's yielded chunk
226
+ * }
227
+ * ```
228
+ *
229
+ * The returned iterable terminates when the server writes `event: complete`;
230
+ * an `event: error` frame (or a transport failure) surfaces as a coded
231
+ * rejection on the next `next()`. `.cancel()` — or aborting `options.signal` —
232
+ * aborts the underlying fetch, which the server observes via `request.signal`.
233
+ * @experimental Reconnect/POST-body/wire-fidelity design questions are still open, so the shape may change.
234
+ */
220
235
  declare const httpStream: <Ref extends HttpStreamRef>(route: Ref, args?: HttpStreamArgsOf<Ref>, options?: HttpStreamOptions) => StreamIterable<HttpStreamChunkOf<Ref>>;
221
236
  /** The single transport method a mutation runner needs — narrowed so adapters can test against a stub. */
222
237
  interface MutationCapableClient<F extends FunctionReference> {
223
238
  mutation: (function_: F, args: ArgsOf<F>, options?: MutationCallOptions<unknown, unknown, ArgsOf<F>>) => Promise<ReturnOf<F>>;
224
239
  }
225
240
  /**
226
- * Reactive sinks an adapter binds to its own primitive's setters (a Solid
227
- * signal, a Vue ref, a Svelte store). The runner pushes into them; how they
228
- * store the value is the adapter's concern (e.g. Solid wraps function-valued
229
- * results in a thunk).
230
- */
241
+ * Reactive sinks an adapter binds to its own primitive's setters (a Solid
242
+ * signal, a Vue ref, a Svelte store). The runner pushes into them; how they
243
+ * store the value is the adapter's concern (e.g. Solid wraps function-valued
244
+ * results in a thunk).
245
+ */
231
246
  interface MutationRunnerSinks<R> {
232
247
  /** Receives the normalized {@link Error} when an invocation rejects. */
233
248
  setError: (error: Error) => void;
@@ -237,41 +252,41 @@ interface MutationRunnerSinks<R> {
237
252
  setResult: (result: R) => void;
238
253
  }
239
254
  /**
240
- * Build the framework-neutral `mutate` half of an adapter's mutation hook.
241
- *
242
- * Owns the orchestration every adapter otherwise copy-pastes: ref-counts
243
- * overlapping invocations into `setPending` (so it only clears once the last
244
- * settles), normalizes a thrown non-`Error`, and routes success/failure to
245
- * `setResult`/`setError` before re-throwing. Each adapter (`@lunora/react`,
246
- * `/solid`, `/svelte`, `/vue`) binds the three sinks to its own reactive
247
- * setters, so this logic lives in exactly one place. Optimistic-update options
248
- * pass straight through to `client.mutation`.
249
- */
255
+ * Build the framework-neutral `mutate` half of an adapter's mutation hook.
256
+ *
257
+ * Owns the orchestration every adapter otherwise copy-pastes: ref-counts
258
+ * overlapping invocations into `setPending` (so it only clears once the last
259
+ * settles), normalizes a thrown non-`Error`, and routes success/failure to
260
+ * `setResult`/`setError` before re-throwing. Each adapter (`@lunora/react`,
261
+ * `/solid`, `/svelte`, `/vue`) binds the three sinks to its own reactive
262
+ * setters, so this logic lives in exactly one place. Optimistic-update options
263
+ * pass straight through to `client.mutation`.
264
+ */
250
265
  declare const createMutationRunner: <F extends FunctionReference>(client: MutationCapableClient<F>, function_: F, sinks: MutationRunnerSinks<ReturnOf<F>>) => ((args: ArgsOf<F>, options?: MutationCallOptions<unknown, unknown, ArgsOf<F>>) => Promise<ReturnOf<F>>);
251
266
  /**
252
- * The structural surface of a TanStack `Transaction` a bound custom mutator
253
- * returns — its `isPersisted.promise` resolves once the write is persisted and
254
- * rejects on failure. Typed structurally so the framework adapters need not
255
- * depend on `@tanstack/db` or `@lunora/db` (the handle is created app-side by
256
- * `bindMutators`).
257
- */
267
+ * The structural surface of a TanStack `Transaction` a bound custom mutator
268
+ * returns — its `isPersisted.promise` resolves once the write is persisted and
269
+ * rejects on failure. Typed structurally so the framework adapters need not
270
+ * depend on `@tanstack/db` or `@lunora/db` (the handle is created app-side by
271
+ * `bindMutators`).
272
+ */
258
273
  interface MutatorTransaction {
259
274
  isPersisted: {
260
275
  promise: Promise<unknown>;
261
276
  };
262
277
  }
263
278
  /**
264
- * A bound custom-mutator handle produced by `bindMutators(client, ctx, mutators)`
265
- * in `@lunora/db`. Calling it applies the optimistic overlay to the local
266
- * collections and pushes the authoritative server write; it returns the TanStack
267
- * transaction whose `isPersisted` promise tracks completion.
268
- */
279
+ * A bound custom-mutator handle produced by `bindMutators(client, ctx, mutators)`
280
+ * in `@lunora/db`. Calling it applies the optimistic overlay to the local
281
+ * collections and pushes the authoritative server write; it returns the TanStack
282
+ * transaction whose `isPersisted` promise tracks completion.
283
+ */
269
284
  type MutatorHandle<TArgs> = (args: TArgs) => MutatorTransaction;
270
285
  /**
271
- * Reactive sinks an adapter binds to its own primitive's setters (a React
272
- * `useState`, a Solid signal, a Vue ref, a Svelte store). The runner pushes into
273
- * them; how they store the value is the adapter's concern.
274
- */
286
+ * Reactive sinks an adapter binds to its own primitive's setters (a React
287
+ * `useState`, a Solid signal, a Vue ref, a Svelte store). The runner pushes into
288
+ * them; how they store the value is the adapter's concern.
289
+ */
275
290
  interface MutatorRunnerSinks {
276
291
  /** Receives the normalized {@link Error} when an invocation rejects, or `undefined` on success / reset. */
277
292
  setError: (error: Error | undefined) => void;
@@ -279,24 +294,24 @@ interface MutatorRunnerSinks {
279
294
  setPending: (pending: boolean) => void;
280
295
  }
281
296
  /**
282
- * Build the framework-neutral `mutate` / `reset` pair of an adapter's
283
- * custom-mutator hook (`useMutator` / `createMutator` / `mutator`).
284
- *
285
- * Owns the orchestration every adapter otherwise copy-pastes: ref-counts
286
- * overlapping invocations into `setPending` (so it only clears once the last
287
- * settles), awaits the bound handle's `isPersisted` promise, normalizes a thrown
288
- * non-`Error`, and routes failure to `setError` (clearing it on success) before
289
- * re-throwing. Each adapter (`@lunora/react`, `/solid`, `/svelte`, `/vue`) binds
290
- * the two sinks to its own reactive setters, so this logic lives in exactly one
291
- * place. The optimistic overlay + server push are owned by the bound handle.
292
- *
293
- * `error` tracks the LATEST invocation, not the last to settle: overlapping
294
- * calls can resolve out of order, so an earlier call that finishes later must
295
- * not clobber a newer call's outcome. Each invocation takes a monotonic token
296
- * and only writes `setError` while it is still the most recent one — otherwise
297
- * `error`/`isError` could surface a stale success or failure (the documented
298
- * "latest invocation's error" contract every adapter advertises).
299
- */
297
+ * Build the framework-neutral `mutate` / `reset` pair of an adapter's
298
+ * custom-mutator hook (`useMutator` / `createMutator` / `mutator`).
299
+ *
300
+ * Owns the orchestration every adapter otherwise copy-pastes: ref-counts
301
+ * overlapping invocations into `setPending` (so it only clears once the last
302
+ * settles), awaits the bound handle's `isPersisted` promise, normalizes a thrown
303
+ * non-`Error`, and routes failure to `setError` (clearing it on success) before
304
+ * re-throwing. Each adapter (`@lunora/react`, `/solid`, `/svelte`, `/vue`) binds
305
+ * the two sinks to its own reactive setters, so this logic lives in exactly one
306
+ * place. The optimistic overlay + server push are owned by the bound handle.
307
+ *
308
+ * `error` tracks the LATEST invocation, not the last to settle: overlapping
309
+ * calls can resolve out of order, so an earlier call that finishes later must
310
+ * not clobber a newer call's outcome. Each invocation takes a monotonic token
311
+ * and only writes `setError` while it is still the most recent one — otherwise
312
+ * `error`/`isError` could surface a stale success or failure (the documented
313
+ * "latest invocation's error" contract every adapter advertises).
314
+ */
300
315
  declare const createMutatorRunner: <TArgs>(handle: MutatorHandle<TArgs>, sinks: MutatorRunnerSinks) => {
301
316
  mutate: (args: TArgs) => Promise<void>;
302
317
  reset: () => void;
@@ -307,31 +322,31 @@ interface QueuedMutation<T = unknown> {
307
322
  /** Stable id used to remove the entry from durable storage once replayed; assigned by the queue when absent. */
308
323
  id?: string;
309
324
  /**
310
- * Issuing identity fingerprint carried through to durable storage (`null` =
311
- * signed out). Absent on hydrated legacy records, which replay ambiently.
312
- */
325
+ * Issuing identity fingerprint carried through to durable storage (`null` =
326
+ * signed out). Absent on hydrated legacy records, which replay ambiently.
327
+ */
313
328
  readonly identity?: string | null;
314
329
  /**
315
- * `true` when a live caller is still awaiting this write's `mutation()`
316
- * Promise; `false`/absent for a write restored from durable storage after a
317
- * reload (its original awaiter is gone). Carried so terminal-verdict
318
- * observers can distinguish "the caller already saw this" from "nothing else
319
- * will report this". Maps to the public `MutationSettledEvent.hadAwaiter`.
320
- */
330
+ * `true` when a live caller is still awaiting this write's `mutation()`
331
+ * Promise; `false`/absent for a write restored from durable storage after a
332
+ * reload (its original awaiter is gone). Carried so terminal-verdict
333
+ * observers can distinguish "the caller already saw this" from "nothing else
334
+ * will report this". Maps to the public `MutationSettledEvent.hadAwaiter`.
335
+ */
321
336
  liveAwaiter?: boolean;
322
337
  /**
323
- * Invoked on a successful replay with the server's echoed commit CDC cursor,
324
- * so a live per-call optimistic layer drops gaplessly once a frame reaches it.
325
- * Absent on hydrated records (the optimistic write lived in a prior session).
326
- */
338
+ * Invoked on a successful replay with the server's echoed commit CDC cursor,
339
+ * so a live per-call optimistic layer drops gaplessly once a frame reaches it.
340
+ * Absent on hydrated records (the optimistic write lived in a prior session).
341
+ */
327
342
  readonly onCommit?: (commitCursor: number | undefined) => void;
328
343
  /**
329
- * Optional sync predicate evaluated just before replay. When it returns
330
- * `false` the write is dropped instead of replaying, handling the case
331
- * where the mutation's preconditions are no longer valid (e.g. the
332
- * document it referred to was deleted while offline). Absent or `true`
333
- * means "ok to replay".
334
- */
344
+ * Optional sync predicate evaluated just before replay. When it returns
345
+ * `false` the write is dropped instead of replaying, handling the case
346
+ * where the mutation's preconditions are no longer valid (e.g. the
347
+ * document it referred to was deleted while offline). Absent or `true`
348
+ * means "ok to replay".
349
+ */
335
350
  readonly precondition?: () => boolean;
336
351
  /** Rejects if the mutation can no longer be replayed. */
337
352
  readonly reject: (error: unknown) => void;
@@ -340,11 +355,11 @@ interface QueuedMutation<T = unknown> {
340
355
  readonly shardKey?: string;
341
356
  }
342
357
  /**
343
- * Invoked when the queue itself discards an entry on overflow (capacity
344
- * eviction), so the client can surface the dropped write on its
345
- * terminal-verdict observer even when the entry has no live awaiter (a hydrated
346
- * record). The `error` carries the `OFFLINE_QUEUE_OVERFLOW` code.
347
- */
358
+ * Invoked when the queue itself discards an entry on overflow (capacity
359
+ * eviction), so the client can surface the dropped write on its
360
+ * terminal-verdict observer even when the entry has no live awaiter (a hydrated
361
+ * record). The `error` carries the `OFFLINE_QUEUE_OVERFLOW` code.
362
+ */
348
363
  type EvictHandler = (entry: QueuedMutation, error: Error & {
349
364
  code?: string;
350
365
  }) => void;
@@ -360,28 +375,17 @@ interface OfflineQueueDeps {
360
375
  version?: string;
361
376
  }
362
377
  /**
363
- * A process-unique id, used both per-mutation and as the fallback `clientId`. It
364
- * MUST be globally unique: the server scopes a custom mutator's replay watermark
365
- * by `(verifiedIdentity, clientId)`, and an anonymous push has no verified
366
- * identity so two anonymous clients that collide on `clientId` would share one
367
- * watermark namespace, letting one stall/suppress the other's ordered mutations.
368
- * `crypto.randomUUID` covers every modern runtime; the fallback still mixes
369
- * crypto-quality (or `Math.random`) entropy with the timestamp + counter so it
370
- * can't collide across two clients started in the same millisecond.
371
- */
372
-
373
- /**
374
- * Bounded FIFO queue. Mutations issued while the client is offline are
375
- * enqueued and replayed in the order they were submitted once the WS
376
- * reconnects and identifies. If the queue exceeds `maxItems` the oldest
377
- * entry is rejected with `OFFLINE_QUEUE_OVERFLOW`.
378
- *
379
- * When a {@link PersistenceAdapter} is supplied, enqueued mutations are mirrored
380
- * to durable storage so they survive a reload — {@link OfflineQueue.hydrate} restores them on
381
- * the next startup and the client replays them on reconnect. Durable removal is
382
- * the caller's responsibility *after* a successful replay (see `LunoraClient`);
383
- * the queue only persists on enqueue and un-persists on overflow.
384
- */
378
+ * Bounded FIFO queue. Mutations issued while the client is offline are
379
+ * enqueued and replayed in the order they were submitted once the WS
380
+ * reconnects and identifies. If the queue exceeds `maxItems` the oldest
381
+ * entry is rejected with `OFFLINE_QUEUE_OVERFLOW`.
382
+ *
383
+ * When a {@link PersistenceAdapter} is supplied, enqueued mutations are mirrored
384
+ * to durable storage so they survive a reload {@link OfflineQueue.hydrate} restores them on
385
+ * the next startup and the client replays them on reconnect. Durable removal is
386
+ * the caller's responsibility *after* a successful replay (see `LunoraClient`);
387
+ * the queue only persists on enqueue and un-persists on overflow.
388
+ */
385
389
  declare class OfflineQueue {
386
390
  /** Opt-in to queueing mutations before the targeted shard's first connect. */
387
391
  readonly queueBeforeFirstConnect: boolean;
@@ -397,48 +401,58 @@ declare class OfflineQueue {
397
401
  get size(): number;
398
402
  enqueue<T>(entry: QueuedMutation<T>): void;
399
403
  /**
400
- * Restore mutations persisted in a prior session and re-queue them in FIFO
401
- * order. Restored entries already live in durable storage, so they are not
402
- * re-appended; they carry no-op `resolve`/`reject` (the original awaiter is
403
- * gone after a reload). No-op when no persistence adapter is configured.
404
- * Returns the distinct shard keys of the restored writes so the caller can
405
- * open their sockets to trigger a flush.
406
- */
404
+ * Restore mutations persisted in a prior session and re-queue them in FIFO
405
+ * order. Restored entries already live in durable storage, so they are not
406
+ * re-appended; they carry no-op `resolve`/`reject` (the original awaiter is
407
+ * gone after a reload). No-op when no persistence adapter is configured.
408
+ * Returns the distinct shard keys of the restored writes so the caller can
409
+ * open their sockets to trigger a flush.
410
+ *
411
+ * `hydrate()` runs post-construction (the caller awaits an async durable-store
412
+ * load), so a mutation issued while offline during that boot window is
413
+ * enqueued into `items` *before* this method's `await` resolves. Restored
414
+ * records are therefore `unshift`-ed ahead of whatever is already queued
415
+ * rather than `push`-ed to the end: the durable store's persist order is
416
+ * authoritative (a prior-session write is always older than anything from
417
+ * this session), so replaying a same-session boot-time write before an
418
+ * older restored write on the same document would let last-writer-wins
419
+ * silently clobber the newer data with the stale one.
420
+ */
407
421
  hydrate(): Promise<(string | undefined)[]>;
408
422
  /**
409
- * Remove and return queued mutations. With no `predicate`, drains the whole
410
- * queue. With one, drains only matching entries (preserving FIFO order) and
411
- * leaves the rest queued — used to flush a single shard's writes when its
412
- * socket reconnects while other shards are still down.
413
- */
423
+ * Remove and return queued mutations. With no `predicate`, drains the whole
424
+ * queue. With one, drains only matching entries (preserving FIFO order) and
425
+ * leaves the rest queued — used to flush a single shard's writes when its
426
+ * socket reconnects while other shards are still down.
427
+ */
414
428
  drain(predicate?: (item: QueuedMutation) => boolean): QueuedMutation[];
415
429
  /**
416
- * Return previously-drained mutations to the front of the queue, preserving
417
- * their FIFO order, without re-persisting them — they were never unpersisted,
418
- * so durable storage still holds them. Used when a flush aborts on a transient
419
- * transport failure: the unreplayed writes stay queued for the next reconnect.
420
- */
430
+ * Return previously-drained mutations to the front of the queue, preserving
431
+ * their FIFO order, without re-persisting them — they were never unpersisted,
432
+ * so durable storage still holds them. Used when a flush aborts on a transient
433
+ * transport failure: the unreplayed writes stay queued for the next reconnect.
434
+ */
421
435
  requeue(items: QueuedMutation[]): void;
422
436
  /**
423
- * Remove mutations whose precondition evaluates to `false` (stale/dirty
424
- * writes that should not replay) and reject each with an
425
- * `OFFLINE_PRECONDITION_FAILED` error. The valid (admitted) mutations stay
426
- * queued in FIFO order. Returns the drained stale entries.
427
- *
428
- * Called during reconnect before the flush cycle to weed out writes whose
429
- * assumptions no longer hold (e.g. a document was deleted by another client).
430
- */
437
+ * Remove mutations whose precondition evaluates to `false` (stale/dirty
438
+ * writes that should not replay) and reject each with an
439
+ * `OFFLINE_PRECONDITION_FAILED` error. The valid (admitted) mutations stay
440
+ * queued in FIFO order. Returns the drained stale entries.
441
+ *
442
+ * Called during reconnect before the flush cycle to weed out writes whose
443
+ * assumptions no longer hold (e.g. a document was deleted by another client).
444
+ */
431
445
  drainConflict(): QueuedMutation[];
432
446
  clear(): void;
433
447
  /** Notify the size observer (the client's pending-sync count) after any change. */
434
448
  private notifySize;
435
449
  }
436
450
  /**
437
- * In-memory {@link PersistenceAdapter}. Doesn't survive a reload — it exists so
438
- * the persistence wiring can be exercised without IndexedDB (tests, SSR, or as
439
- * a deliberate "no durable store" choice that still satisfies the interface).
440
- * Preserves enqueue order; `clone` keeps callers from mutating stored args.
441
- */
451
+ * In-memory {@link PersistenceAdapter}. Doesn't survive a reload — it exists so
452
+ * the persistence wiring can be exercised without IndexedDB (tests, SSR, or as
453
+ * a deliberate "no durable store" choice that still satisfies the interface).
454
+ * Preserves enqueue order; `clone` keeps callers from mutating stored args.
455
+ */
442
456
  declare const createInMemoryPersistence: () => PersistenceAdapter;
443
457
  interface IndexedDbPersistenceOptions {
444
458
  /** Database name; defaults to `"lunora-outbox"` (its own DB, separate from the read cache). */
@@ -449,47 +463,29 @@ interface IndexedDbPersistenceOptions {
449
463
  storeName?: string;
450
464
  }
451
465
  /**
452
- * IndexedDB-backed {@link PersistenceAdapter}. Each mutation is stored under an
453
- * autoincrementing key (so `load()` returns them in enqueue order regardless of
454
- * the string ids) with a unique secondary index on `id` for `remove()`.
455
- *
456
- * The store handle is opened lazily and the open promise is cached, so repeated
457
- * ops reuse one connection. Throws eagerly if no `IDBFactory` is available —
458
- * callers in non-browser environments should use {@link createInMemoryPersistence}.
459
- */
466
+ * IndexedDB-backed {@link PersistenceAdapter}. Each mutation is stored under an
467
+ * autoincrementing key (so `load()` returns them in enqueue order regardless of
468
+ * the string ids) with a unique secondary index on `id` for `remove()`.
469
+ *
470
+ * The store handle is opened lazily and the open promise is cached, so repeated
471
+ * ops reuse one connection. Throws eagerly if no `IDBFactory` is available —
472
+ * callers in non-browser environments should use {@link createInMemoryPersistence}.
473
+ */
460
474
  declare const createIndexedDbPersistence: (options?: IndexedDbPersistenceOptions) => PersistenceAdapter;
461
475
  /**
462
- * Resolve the effective offline-queue persistence from the user option,
463
- * defaulting to a durable IndexedDB store when the environment supports one.
464
- *
465
- * An explicit adapter is used as-is; `false` opts out (the caller keeps an
466
- * in-memory queue, lost on reload); `undefined` (the default) auto-probes
467
- * IndexedDB when the global is present (browsers) and `autoProbe` is set,
468
- * otherwise `undefined` — so SSR/Node/React-Native keep today's in-memory
469
- * behaviour and only environments that can persist do.
470
- *
471
- * `autoProbe` is `false` when the `@lunora/db` outbox is wired: that sink is the
472
- * single durable write path, so the built-in queue must stay in memory rather
473
- * than persist a second, never-flushed copy. An explicit adapter is still
474
- * honoured (the caller asked for it); only the implicit default is suppressed.
475
- *
476
- * The IndexedDB adapter opens its connection lazily, so constructing it here is
477
- * cheap and never throws (the `indexedDB` global is verified present first).
478
- */
479
- /**
480
- * Compose the read-cache key for a subscription. Mirrors how
481
- * `SubscriptionRegistry` keys live subscriptions so a hydrated value lines up
482
- * with the subscription that will consume it. `shardKey` defaults to `""` (the
483
- * root shard) exactly as the registry does.
484
- */
476
+ * Compose the read-cache key for a subscription. Mirrors how
477
+ * `SubscriptionRegistry` keys live subscriptions so a hydrated value lines up
478
+ * with the subscription that will consume it. `shardKey` defaults to `""` (the
479
+ * root shard) exactly as the registry does.
480
+ */
485
481
  declare const queryCacheKey: (functionPath: string, argsKey: string, shardKey?: string) => string;
486
482
  /**
487
- * In-memory {@link QueryCacheAdapter}. Doesn't survive a reload — it exists so
488
- * the read-cache wiring can be exercised without IndexedDB (tests, SSR, or as a
489
- * deliberate "no durable store" choice that still satisfies the interface).
490
- * Enforces the same LRU row cap as the IndexedDB adapter; `clone` keeps callers
491
- * from mutating stored values.
492
- */
483
+ * In-memory {@link QueryCacheAdapter}. Doesn't survive a reload — it exists so
484
+ * the read-cache wiring can be exercised without IndexedDB (tests, SSR, or as a
485
+ * deliberate "no durable store" choice that still satisfies the interface).
486
+ * Enforces the same LRU row cap as the IndexedDB adapter; `clone` keeps callers
487
+ * from mutating stored values.
488
+ */
493
489
  declare const createInMemoryQueryCache: (options?: {
494
490
  maxEntries?: number;
495
491
  }) => QueryCacheAdapter;
@@ -504,34 +500,25 @@ interface IndexedDbQueryCacheOptions {
504
500
  storeName?: string;
505
501
  }
506
502
  /**
507
- * IndexedDB-backed {@link QueryCacheAdapter}. Each query is stored under its
508
- * composite key (`functionPath::argsKey::shardKey`) with a `ts` index driving
509
- * LRU eviction. The store handle is opened lazily and cached, so repeated ops
510
- * reuse one connection.
511
- *
512
- * The store lives in its own `lunora-query-cache` database — deliberately
513
- * separate from the offline-mutation outbox's `lunora-outbox` database so the two
514
- * independently-toggleable adapters never share (and drift on) a schema version.
515
- * Throws eagerly if no `IDBFactory` is available — callers in non-browser
516
- * environments should use {@link createInMemoryQueryCache}.
517
- */
503
+ * IndexedDB-backed {@link QueryCacheAdapter}. Each query is stored under its
504
+ * composite key (`functionPath::argsKey::shardKey`) with a `ts` index driving
505
+ * LRU eviction. The store handle is opened lazily and cached, so repeated ops
506
+ * reuse one connection.
507
+ *
508
+ * The store lives in its own `lunora-query-cache` database — deliberately
509
+ * separate from the offline-mutation outbox's `lunora-outbox` database so the two
510
+ * independently-toggleable adapters never share (and drift on) a schema version.
511
+ * Throws eagerly if no `IDBFactory` is available — callers in non-browser
512
+ * environments should use {@link createInMemoryQueryCache}.
513
+ */
518
514
  declare const createIndexedDbQueryCache: (options?: IndexedDbQueryCacheOptions) => QueryCacheAdapter;
519
515
  /**
520
- * Resolve the effective read-cache from the user option, defaulting to a durable
521
- * IndexedDB store when the environment supports one. Same tri-state semantics as
522
- * `resolvePersistenceAdapter` in `./persistence`:
523
- *
524
- * - an explicit adapter is used as-is;
525
- * - `false` opts out — reads stay in memory only;
526
- * - `undefined` (the default) auto-probes IndexedDB (browsers), else `undefined`.
527
- */
528
- /**
529
- * Exponential backoff calculator with optional jitter.
530
- *
531
- * `next()` doubles the delay each call up to `maxDelayMs`. When `jitter` is
532
- * enabled the returned value is randomized in `[delay/2, delay]` so a fleet
533
- * of clients reconnecting at the same time spread out their retries.
534
- */
516
+ * Exponential backoff calculator with optional jitter.
517
+ *
518
+ * `next()` doubles the delay each call up to `maxDelayMs`. When `jitter` is
519
+ * enabled the returned value is randomized in `[delay/2, delay]` so a fleet
520
+ * of clients reconnecting at the same time spread out their retries.
521
+ */
535
522
  interface ReconnectCalculator {
536
523
  /** Returns the delay to wait before the next reconnect attempt. */
537
524
  next: () => number;
@@ -540,34 +527,34 @@ interface ReconnectCalculator {
540
527
  }
541
528
  declare const createReconnect: (options?: ReconnectOptions, random?: () => number) => ReconnectCalculator;
542
529
  /**
543
- * Capture a snapshot of the current live query value at call time and produce a
544
- * `() => boolean` precondition that compares it against the value at replay time.
545
- *
546
- * When the precondition is checked (on queue drain / reconnect) it re-reads the
547
- * query's current value via {@link LunoraClient.peekActiveQueryValue}. If the
548
- * value differs from what was captured at call time the precondition returns
549
- * `false` and the offline mutation is dropped as stale.
550
- * @example
551
- * ```ts
552
- * client.mutation(api.todos.update, { id, text }, {
553
- * precondition: createSnapshotPrecondition(client, api.todos.list, { userId }),
554
- * });
555
- * ```
556
- */
530
+ * Capture a snapshot of the current live query value at call time and produce a
531
+ * `() => boolean` precondition that compares it against the value at replay time.
532
+ *
533
+ * When the precondition is checked (on queue drain / reconnect) it re-reads the
534
+ * query's current value via {@link LunoraClient.peekActiveQueryValue}. If the
535
+ * value differs from what was captured at call time the precondition returns
536
+ * `false` and the offline mutation is dropped as stale.
537
+ * @example
538
+ * ```ts
539
+ * client.mutation(api.todos.update, { id, text }, {
540
+ * precondition: createSnapshotPrecondition(client, api.todos.list, { userId }),
541
+ * });
542
+ * ```
543
+ */
557
544
  declare const createSnapshotPrecondition: (client: LunoraClient, functionRef: FunctionReference, args: Record<string, unknown>, shardKey?: string) => (() => boolean);
558
545
  /**
559
- * Client-side service-worker registration and lifecycle management.
560
- *
561
- * Usage:
562
- * ```ts
563
- * const sw = new ClientServiceWorker({ swUrl: "/sw.js" });
564
- * await sw.register();
565
- *
566
- * if (sw.active) {
567
- * sw.postMessage({ type: "sync" });
568
- * }
569
- * ```
570
- */
546
+ * Client-side service-worker registration and lifecycle management.
547
+ *
548
+ * Usage:
549
+ * ```ts
550
+ * const sw = new ClientServiceWorker({ swUrl: "/sw.js" });
551
+ * await sw.register();
552
+ *
553
+ * if (sw.active) {
554
+ * sw.postMessage({ type: "sync" });
555
+ * }
556
+ * ```
557
+ */
571
558
  type ServiceWorkerStatus = "unsupported" | "unregistered" | "registering" | "active" | "error";
572
559
  interface ClientSwOptions {
573
560
  /** Called when the status changes. */
@@ -578,9 +565,9 @@ interface ClientSwOptions {
578
565
  swUrl: string;
579
566
  }
580
567
  /**
581
- * Manages service-worker registration and provides a simple API for
582
- * sending messages and listening for responses.
583
- */
568
+ * Manages service-worker registration and provides a simple API for
569
+ * sending messages and listening for responses.
570
+ */
584
571
  declare class ClientServiceWorker {
585
572
  #private;
586
573
  readonly swUrl: string;
@@ -593,30 +580,30 @@ declare class ClientServiceWorker {
593
580
  /** Whether the SW is currently controlling this page. */
594
581
  get active(): boolean;
595
582
  /**
596
- * Register the service worker.
597
- *
598
- * Returns `false` when the browser does not support service workers.
599
- */
583
+ * Register the service worker.
584
+ *
585
+ * Returns `false` when the browser does not support service workers.
586
+ */
600
587
  register(): Promise<boolean>;
601
588
  /**
602
- * Remove the active service worker registration and clear listeners.
603
- *
604
- * Returns `false` when no registration is currently held.
605
- */
589
+ * Remove the active service worker registration and clear listeners.
590
+ *
591
+ * Returns `false` when no registration is currently held.
592
+ */
606
593
  unregister(): Promise<boolean>;
607
594
  /**
608
- * Send a message to the active service worker.
609
- */
595
+ * Send a message to the active service worker.
596
+ */
610
597
  postMessage(message: unknown): void;
611
598
  /**
612
- * Register a handler for messages **from** the service worker.
613
- * @returns Unsubscribe function.
614
- */
599
+ * Register a handler for messages **from** the service worker.
600
+ * @returns Unsubscribe function.
601
+ */
615
602
  onMessage(handler: (event: MessageEvent) => void): () => void;
616
603
  }
617
604
  /**
618
- * Outbound message from the client to the SW.
619
- */
605
+ * Outbound message from the client to the SW.
606
+ */
620
607
  interface ClientToSwMessage {
621
608
  /** Opaque correlation ID for request/response patterns. */
622
609
  correlationId?: string;
@@ -624,8 +611,8 @@ interface ClientToSwMessage {
624
611
  type: string;
625
612
  }
626
613
  /**
627
- * Inbound message from the SW to the client.
628
- */
614
+ * Inbound message from the SW to the client.
615
+ */
629
616
  interface SwToClientMessage {
630
617
  /** Echoes the correlation ID from the client request, if any. */
631
618
  correlationId?: string;
@@ -633,14 +620,14 @@ interface SwToClientMessage {
633
620
  type: string;
634
621
  }
635
622
  /**
636
- * Send a typed message to the service worker and optionally await a
637
- * matching response.
638
- * @returns A promise that resolves when the SW sends a reply with the
639
- * same `correlationId` (if `expectResponse` is true).
640
- */
623
+ * Send a typed message to the service worker and optionally await a
624
+ * matching response.
625
+ * @returns A promise that resolves when the SW sends a reply with the
626
+ * same `correlationId` (if `expectResponse` is true).
627
+ */
641
628
  declare const sendToSw: (sw: ServiceWorker | null, message: ClientToSwMessage, expectResponse?: boolean) => Promise<unknown>;
642
629
  /**
643
- * Create a reply for a client message (call from inside the SW).
644
- */
630
+ * Create a reply for a client message (call from inside the SW).
631
+ */
645
632
  declare const createReply: (original: ClientToSwMessage, payload?: unknown) => SwToClientMessage;
646
633
  export { type ArgsOf, type AsyncStorageLike, type AsyncStoragePersistenceOptions, type BookmarkStorage, ClientServiceWorker, type ClientSwOptions, type ClientToSwMessage, type FunctionReference, type HttpStreamArgsOf, type HttpStreamChunkOf, type HttpStreamOptions, type HttpStreamRef, type IndexedDbPersistenceOptions, type IndexedDbQueryCacheOptions, LunoraClient, type MutationCallOptions, type MutationDelta, type MutationRunnerSinks, type MutatorHandle, type MutatorRunnerSinks, type MutatorTransaction, OfflineQueue, type OfflineQueueOptions, type PersistenceAdapter, type QueryCacheAdapter, type QueuedMutation, type ReconnectCalculator, type ReconnectOptions, type ReturnOf, type ServiceWorkerStatus, type StreamIterable, type SubscriptionError, type SwToClientMessage, TabCoordinator, applyDelta, createAsyncStoragePersistence, createInMemoryBookmarkStorage, createInMemoryPersistence, createInMemoryQueryCache, createIndexedDbPersistence, createIndexedDbQueryCache, createMutationRunner, createMutatorRunner, createReconnect, createReply, createSnapshotPrecondition, httpStream, isMutationDelta, queryCacheKey, sendToSw };