@lunora/db 1.0.0-alpha.4 → 1.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/README.md +12 -0
- package/dist/collections/index.d.mts +5 -0
- package/dist/collections/index.d.ts +5 -0
- package/dist/collections/index.mjs +1 -0
- package/dist/index.d.mts +75 -135
- package/dist/index.d.ts +75 -135
- package/dist/index.mjs +1 -6
- package/dist/mutators/index.d.mts +5 -0
- package/dist/mutators/index.d.ts +5 -0
- package/dist/mutators/index.mjs +1 -0
- package/dist/packem_shared/CHECKPOINT_FALLBACK_MS-6GmTGWsH.mjs +1 -0
- package/dist/packem_shared/DIRECT_TRANSACTION_METADATA_KEY-D1XuUJtq.mjs +1 -0
- package/dist/packem_shared/OUTBOX_MUTATION_FN_NAME-CebgkYw2.mjs +1 -0
- package/dist/packem_shared/applyPlanToCollections-C29s6OoX.mjs +1 -0
- package/dist/packem_shared/collection-options.d-B_2IXvdU.d.mts +347 -0
- package/dist/packem_shared/collection-options.d-B_2IXvdU.d.ts +347 -0
- package/dist/packem_shared/define-collections.d-BcQg0ibQ.d.ts +166 -0
- package/dist/packem_shared/define-collections.d-CK7jEXQJ.d.mts +166 -0
- package/dist/packem_shared/define-mutators.d-D51n_r1n.d.mts +235 -0
- package/dist/packem_shared/define-mutators.d-DuAmdkYR.d.ts +235 -0
- package/dist/packem_shared/defineCollections-4CPSF9OJ.mjs +1 -0
- package/package.json +11 -2
- package/dist/packem_shared/createOptimisticOnlineDetector-CRulWqZ7.mjs +0 -65
- package/dist/packem_shared/defineCollections-BAmslSrF.mjs +0 -109
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { OutboxSink, LunoraClient, FunctionReference, SubscriptionError } from '@lunora/client';
|
|
2
|
+
import { CollectionConfig } from '@tanstack/db';
|
|
3
|
+
import { OnlineDetector } from '@tanstack/offline-transactions';
|
|
4
|
+
/**
|
|
5
|
+
* Reserved `mutationFns` key the unified outbox routes raw `client.mutation`
|
|
6
|
+
* offline writes through. `defineCollections` registers a handler under this
|
|
7
|
+
* name that reads `transaction.metadata` (functionPath + args) and replays the
|
|
8
|
+
* write, so a db app's direct mutations ride the same durable executor as its
|
|
9
|
+
* collection inserts instead of the standalone {@link OutboxSink} fallback.
|
|
10
|
+
*/
|
|
11
|
+
declare const OUTBOX_MUTATION_FN_NAME = "__lunora_outbox__";
|
|
12
|
+
/** The metadata an outbox-routed transaction carries so its replay can call `client.mutation`. */
|
|
13
|
+
interface OutboxMutationMetadata extends Record<string, unknown> {
|
|
14
|
+
args: Record<string, unknown>;
|
|
15
|
+
clientId: string;
|
|
16
|
+
functionPath: string;
|
|
17
|
+
/** Stable `${clientId}:${mutationId}` replay key; passed back as the mutation id so a committed-but-unacked replay is server-idempotent. */
|
|
18
|
+
idempotencyKey: string;
|
|
19
|
+
/** Issuing identity fingerprint; the replay handler drops the write when it no longer matches. */
|
|
20
|
+
identity: string | null;
|
|
21
|
+
mutationId: number;
|
|
22
|
+
shardKey?: string;
|
|
23
|
+
}
|
|
24
|
+
/** A committable outbox transaction handle (the `OfflineTransaction` the executor mints). */
|
|
25
|
+
interface OutboxTransaction {
|
|
26
|
+
commit?: () => Promise<unknown>;
|
|
27
|
+
mutate: (callback: () => void) => unknown;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The slice of the TanStack `OfflineExecutor` the {@link createExecutorOutboxSink}
|
|
31
|
+
* drives. Declared structurally so `@lunora/db`'s outbox glue doesn't widen its
|
|
32
|
+
* coupling to the executor's full surface (and stays unit-testable with a fake).
|
|
33
|
+
*/
|
|
34
|
+
interface OutboxExecutor {
|
|
35
|
+
createOfflineTransaction: (options: {
|
|
36
|
+
autoCommit?: boolean;
|
|
37
|
+
idempotencyKey?: string;
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
mutationFnName: string;
|
|
40
|
+
}) => OutboxTransaction;
|
|
41
|
+
getPendingCount: () => number;
|
|
42
|
+
}
|
|
43
|
+
/** Tuning for {@link createExecutorOutboxSink}. */
|
|
44
|
+
interface ExecutorOutboxSinkOptions {
|
|
45
|
+
/** Max persisted-but-unconfirmed writes before `enqueue` rejects with `OFFLINE_QUEUE_OVERFLOW` (default 1000, matching `OfflineQueue`). */
|
|
46
|
+
maxItems?: number;
|
|
47
|
+
/** `mutationFns` key the replay handler is registered under (default {@link OUTBOX_MUTATION_FN_NAME}). */
|
|
48
|
+
mutationFnName?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The blessed {@link OutboxSink} over the TanStack `OfflineExecutor` — the single
|
|
52
|
+
* durable write path for a `@lunora/db` app. It persists each offline write as an
|
|
53
|
+
* executor transaction carrying the `client.mutation` target in `metadata`, then
|
|
54
|
+
* ports the two semantics the executor lacks vs the built-in `OfflineQueue`.
|
|
55
|
+
*
|
|
56
|
+
* First: a `maxItems` cap that **rejects** a new write at capacity with an
|
|
57
|
+
* `OFFLINE_QUEUE_OVERFLOW`-coded error — the {@link OutboxSink} contract, matching
|
|
58
|
+
* `OfflineQueue`. Rejecting (rather than evicting the oldest) preserves the
|
|
59
|
+
* at-least-once promise: an already-persisted write is never silently dropped, and
|
|
60
|
+
* the caller surfaces back-pressure to the issuing mutation (which rolls its
|
|
61
|
+
* optimistic write back).
|
|
62
|
+
* Second: the identity guard lives in the replay handler (`defineCollections`), which drops a write whose captured `identity` no longer matches — see {@link OutboxMutationMetadata}.
|
|
63
|
+
*/
|
|
64
|
+
declare const createExecutorOutboxSink: (executor: OutboxExecutor, options?: ExecutorOutboxSinkOptions) => OutboxSink;
|
|
65
|
+
/** A row carrying the Lunora document id. */
|
|
66
|
+
type Row = Record<string, unknown> & {
|
|
67
|
+
_id: string;
|
|
68
|
+
};
|
|
69
|
+
/** The subset of a TanStack DB sync write channel that {@link makeDiffEmit} drives. */
|
|
70
|
+
interface SyncWriter<T extends object> {
|
|
71
|
+
begin: () => void;
|
|
72
|
+
commit: () => void;
|
|
73
|
+
write: (message: {
|
|
74
|
+
type: "insert" | "update";
|
|
75
|
+
value: T;
|
|
76
|
+
} | {
|
|
77
|
+
key: string;
|
|
78
|
+
type: "delete";
|
|
79
|
+
}) => void;
|
|
80
|
+
}
|
|
81
|
+
/** Index a row list into a keyed map. */
|
|
82
|
+
declare const toMap: <T extends object>(rows: ReadonlyArray<T>, getKey: (row: T) => string) => Map<string, T>;
|
|
83
|
+
/**
|
|
84
|
+
* Build an `emit(next)` that diffs a desired keyed snapshot into a collection's
|
|
85
|
+
* sync channel — only changed rows are written, so a reconnect snapshot or a
|
|
86
|
+
* scope change never churns the synced view out from under a pending optimistic
|
|
87
|
+
* row. The last-synced base is tracked in `syncedJson`.
|
|
88
|
+
*
|
|
89
|
+
* Change detection compares rows by `JSON.stringify`, which is key-order
|
|
90
|
+
* sensitive — safe here because server snapshots have stable column order
|
|
91
|
+
* across reconnects (same query projection). A sync source with unstable key
|
|
92
|
+
* ordering would need a structural compare instead.
|
|
93
|
+
*
|
|
94
|
+
* `syncedJson` holds the JSON-serialized form of each last-synced row, keyed
|
|
95
|
+
* by row id. It is the **sole** synced-state map — no parallel row-object map
|
|
96
|
+
* is kept. Each incoming value is serialized exactly once per tick (for both
|
|
97
|
+
* comparison and cache update), so the previous value is never re-serialized.
|
|
98
|
+
*
|
|
99
|
+
* Lifecycle: `syncedJson` must be owned by the caller at the same scope as any
|
|
100
|
+
* other per-collection state (e.g. outside the `sync.sync` callback), so the
|
|
101
|
+
* cache persists correctly across sync restarts. A new `makeDiffEmit` closure
|
|
102
|
+
* created on restart receives the same map reference and starts from the
|
|
103
|
+
* committed synced state — no spurious diffs on reconnect.
|
|
104
|
+
*/
|
|
105
|
+
declare const makeDiffEmit: <T extends object>(syncedJson: Map<string, string>, writer: SyncWriter<T>) => (next: Map<string, T>) => void;
|
|
106
|
+
/**
|
|
107
|
+
* Run a Lunora mutation under the outbox's retry policy.
|
|
108
|
+
*
|
|
109
|
+
* The retryable/permanent split keys on whether the failure carries a server
|
|
110
|
+
* application error `code` (set by `@lunora/client`'s rpc when the server returns
|
|
111
|
+
* a `{ error: { code, … } }` envelope — validation, conflict, etc.). A coded
|
|
112
|
+
* error is a definite verdict: surface it as a `NonRetriableError` so the executor
|
|
113
|
+
* stops and TanStack DB rolls the optimistic insert back. Everything without a
|
|
114
|
+
* code is transient — a `fetch` network failure (`TypeError`) or an HTTP/infra
|
|
115
|
+
* blip the rpc surfaces as a code-less `Error` (a 5xx gateway page, a non-JSON
|
|
116
|
+
* body) — so it's rethrown as-is and the durable outbox replays it. Keying on
|
|
117
|
+
* `error instanceof TypeError` alone would wrongly drop the latter.
|
|
118
|
+
*/
|
|
119
|
+
declare const runOutboxMutation: (mutate: () => Promise<unknown>) => Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* An "always attempt" online detector. We deliberately don't trust
|
|
122
|
+
* `navigator.onLine`: some environments (and Playwright's `setOffline` under
|
|
123
|
+
* Firefox) leave it stuck, which would freeze the outbox. Instead the executor
|
|
124
|
+
* always tries the send and {@link runOutboxMutation}'s transient-error retry
|
|
125
|
+
* handles real offline; the periodic tick nudges the executor to drain the outbox
|
|
126
|
+
* so a queued write replays promptly once connectivity returns.
|
|
127
|
+
*
|
|
128
|
+
* `isOnline` is therefore intentionally always `true` — it gates the executor's
|
|
129
|
+
* attempts, not a UI signal. A consumer that wants to show real connectivity
|
|
130
|
+
* should read `navigator.onLine` itself, separately from this detector.
|
|
131
|
+
*/
|
|
132
|
+
declare const createOptimisticOnlineDetector: () => OnlineDetector;
|
|
133
|
+
/** A watermark pair — the two monotonic lines a checkpoint registry gates on. */
|
|
134
|
+
interface CheckpointWatermark {
|
|
135
|
+
/** Op-log cursor the server has durably applied. */
|
|
136
|
+
checkpoint?: number;
|
|
137
|
+
/** Highest `clientSeq` the server has echoed back for this client. */
|
|
138
|
+
mutationId?: number;
|
|
139
|
+
}
|
|
140
|
+
/** Reported when the fallback releases an overlay the sync stream never confirmed. */
|
|
141
|
+
interface CheckpointFallbackEvent {
|
|
142
|
+
/** Which gate released. */
|
|
143
|
+
kind: "checkpoint" | "mutationId";
|
|
144
|
+
/** How long the release waited past the server acknowledgement, in ms. */
|
|
145
|
+
waitedMs: number;
|
|
146
|
+
/** The watermark that was acknowledged but never confirmed by a sync frame. */
|
|
147
|
+
watermark: number;
|
|
148
|
+
}
|
|
149
|
+
/** Counters for {@link CheckpointRegistry.stats} — feeds a debug/diagnostics surface. */
|
|
150
|
+
interface CheckpointRegistryStats {
|
|
151
|
+
/** How many times the fallback timer released an overlay (a non-zero value means sync frames are being lost). */
|
|
152
|
+
fallbacks: number;
|
|
153
|
+
/** Overlays currently waiting on a checkpoint cursor. */
|
|
154
|
+
pendingCheckpointWaiters: number;
|
|
155
|
+
/** Overlays currently waiting on a mutation id. */
|
|
156
|
+
pendingMutationWaiters: number;
|
|
157
|
+
}
|
|
158
|
+
/** Tuning for {@link createCheckpointRegistry}. */
|
|
159
|
+
interface CheckpointRegistryOptions {
|
|
160
|
+
/**
|
|
161
|
+
* How long an {@link CheckpointRegistry.acknowledge}d watermark waits for the
|
|
162
|
+
* authoritative sync frame before the overlay is released anyway. Default 3000.
|
|
163
|
+
* `0` disables the fallback (an overlay then waits forever for the frame — the
|
|
164
|
+
* pre-fallback behavior, which hangs on a dropped poke).
|
|
165
|
+
*/
|
|
166
|
+
fallbackMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* Notified each time the fallback fires. A fallback is never *correct* — it
|
|
169
|
+
* means a poke or `settled` frame that should have confirmed the write never
|
|
170
|
+
* arrived — so this is the hook for a warning or a metric. Defaults to a
|
|
171
|
+
* one-shot `console.warn`.
|
|
172
|
+
*/
|
|
173
|
+
onFallback?: (event: CheckpointFallbackEvent) => void;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Resolves the TanStack optimistic-overlay drop against the server's confirmed
|
|
177
|
+
* watermarks. A mutator's optimistic transaction returns `awaitMutationId(id)`
|
|
178
|
+
* (or `awaitCheckpoint(cursor)`); TanStack keeps the overlay until that promise
|
|
179
|
+
* settles, so the row de-duplicates exactly as the synced server value lands — no
|
|
180
|
+
* flash of the optimistic row disappearing then reappearing.
|
|
181
|
+
*
|
|
182
|
+
* Two inputs, deliberately distinct:
|
|
183
|
+
*
|
|
184
|
+
* - {@link resolve} is the **authoritative** advance, called by whoever owns the
|
|
185
|
+
* watermark stream — a `data`/`delta` frame's `lastMutationId`, or a shape poke's
|
|
186
|
+
* `checkpoint`. The synced rows have landed, so gates open immediately.
|
|
187
|
+
* - {@link acknowledge} is the **provisional** advance, called when the server has
|
|
188
|
+
* accepted the write (the mutator RPC ack) but the matching rows have not
|
|
189
|
+
* necessarily been delivered yet. Releasing here would drop the overlay before
|
|
190
|
+
* the synced row exists — a visible flicker — so instead it arms a bounded
|
|
191
|
+
* fallback. If the authoritative frame lands first the fallback is cancelled;
|
|
192
|
+
* if it never lands, the overlay is released after `fallbackMs` and the event is
|
|
193
|
+
* reported rather than hanging forever.
|
|
194
|
+
*
|
|
195
|
+
* That pairing is why a lost poke degrades to a late overlay drop instead of a
|
|
196
|
+
* permanently stuck `isPersisted` promise.
|
|
197
|
+
*/
|
|
198
|
+
interface CheckpointRegistry {
|
|
199
|
+
/**
|
|
200
|
+
* Record a server-accepted watermark whose rows may not have synced yet: arms
|
|
201
|
+
* the bounded fallback described on {@link CheckpointRegistry}. Safe to call
|
|
202
|
+
* repeatedly; a watermark already passed is a no-op.
|
|
203
|
+
*/
|
|
204
|
+
acknowledge: (watermark: CheckpointWatermark) => void;
|
|
205
|
+
/** Resolve once the server has acknowledged the op-log `cursor`. */
|
|
206
|
+
awaitCheckpoint: (cursor: number) => Promise<void>;
|
|
207
|
+
/** Resolve once the server has echoed a `lastMutationId >= id` for this client. */
|
|
208
|
+
awaitMutationId: (id: number) => Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Tear the registry down: `clearTimeout` every armed fallback timer and empty
|
|
211
|
+
* the armed set. Idempotent. A discarded registry (Vite HMR dispose, sign-out)
|
|
212
|
+
* can otherwise hold up to `fallbackMs` of pending `setTimeout`s alive through
|
|
213
|
+
* their closures — keeping a Node/SSR event loop from draining. Distinct from
|
|
214
|
+
* {@link resolve}: `resolve` settles parked *waiters* (and disarms the timers it
|
|
215
|
+
* subsumes as a side effect); `dispose` guarantees no armed timer survives,
|
|
216
|
+
* independent of any watermark.
|
|
217
|
+
*/
|
|
218
|
+
dispose: () => void;
|
|
219
|
+
/** Advance the gates from a sync frame's watermark; later callers past the mark settle immediately. */
|
|
220
|
+
resolve: (watermark: CheckpointWatermark) => void;
|
|
221
|
+
/** Diagnostics counters — notably how often the fallback had to fire. */
|
|
222
|
+
stats: () => CheckpointRegistryStats;
|
|
223
|
+
}
|
|
224
|
+
/** Default fallback window: long enough that a slow-but-arriving poke wins, short enough that a UI isn't visibly stuck. */
|
|
225
|
+
declare const CHECKPOINT_FALLBACK_MS = 3e3;
|
|
226
|
+
/**
|
|
227
|
+
* A standalone checkpoint/mutation-id registry. Prefer {@link getShardCheckpoints}
|
|
228
|
+
* unless you are wiring a bespoke watermark stream — a registry must be shared by
|
|
229
|
+
* every collection on a shard (see that function for why).
|
|
230
|
+
*/
|
|
231
|
+
declare const createCheckpointRegistry: (options?: CheckpointRegistryOptions) => CheckpointRegistry;
|
|
232
|
+
/**
|
|
233
|
+
* The shared checkpoint registry for `client` + `shardKey` — created on first use.
|
|
234
|
+
* This is the registry {@link lunoraCollectionOptions} and
|
|
235
|
+
* {@link import("./define-mutators").bindMutators} default to, which is what makes
|
|
236
|
+
* a multi-collection shard work without the caller relaying pokes between
|
|
237
|
+
* registries by hand.
|
|
238
|
+
*
|
|
239
|
+
* `options` applies **only when the registry is created**. Because the point is that
|
|
240
|
+
* every collection and mutator on a shard shares one gate, a later call cannot
|
|
241
|
+
* retune an existing registry — it returns the existing one and `options` is ignored.
|
|
242
|
+
* To control `fallbackMs` / `onFallback`, build the registry yourself with
|
|
243
|
+
* {@link createCheckpointRegistry} and pass it explicitly to every
|
|
244
|
+
* `lunoraCollectionOptions` and `bindMutators` call for that shard.
|
|
245
|
+
*/
|
|
246
|
+
declare const getShardCheckpoints: (client: LunoraClient, shardKey?: string, options?: CheckpointRegistryOptions) => CheckpointRegistry;
|
|
247
|
+
/**
|
|
248
|
+
* Release every pending overlay gate for `client` and drop its shard registries.
|
|
249
|
+
*
|
|
250
|
+
* The hot-reload / teardown escape hatch. When a module that owns collections and
|
|
251
|
+
* mutators is replaced — a Vite HMR update, a sign-out that rebuilds the data layer
|
|
252
|
+
* — the *old* bindings may still have transactions parked in `awaitMutationId`. The
|
|
253
|
+
* subscriptions that would have resolved them are gone with the old module, so
|
|
254
|
+
* without this those promises never settle and every one of their
|
|
255
|
+
* `transaction.isPersisted` waiters hangs forever.
|
|
256
|
+
*
|
|
257
|
+
* Resolving to `Infinity` settles the parked waiters (the writes were already sent;
|
|
258
|
+
* the server is authoritative regardless), and dropping the registries means the
|
|
259
|
+
* replacement module's bindings start from a clean per-shard gate.
|
|
260
|
+
*
|
|
261
|
+
* ```ts
|
|
262
|
+
* // In the module that owns the data layer:
|
|
263
|
+
* import.meta.hot?.dispose(() => releaseShardCheckpoints(client));
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare const releaseShardCheckpoints: (client: LunoraClient) => void;
|
|
267
|
+
/** Every live registry for `client`, keyed by shard (`""` = unsharded) — for a debug surface. */
|
|
268
|
+
declare const shardCheckpointStats: (client: LunoraClient) => Record<string, CheckpointRegistryStats>;
|
|
269
|
+
/**
|
|
270
|
+
* A replication-shape sync source (the local-first partial-replication path).
|
|
271
|
+
* Mutually exclusive with {@link LunoraCollectionConfig.list}: the collection
|
|
272
|
+
* live-syncs the named shape's rowset via the client's poke protocol
|
|
273
|
+
* (`subscribeShape`) instead of a full-table `list` query subscription.
|
|
274
|
+
*/
|
|
275
|
+
interface ShapeSource {
|
|
276
|
+
/** Validated shape parameters (the partition selector — e.g. `{ channelId }`). */
|
|
277
|
+
args?: Record<string, unknown>;
|
|
278
|
+
/** The `defineShape` export name registered in `LUNORA_SHAPES`. */
|
|
279
|
+
name: string;
|
|
280
|
+
/** Routes the subscription to a specific shard's DO when the table is sharded. */
|
|
281
|
+
shardKey?: string;
|
|
282
|
+
}
|
|
283
|
+
/** Declarative inputs for {@link lunoraCollectionOptions}. */
|
|
284
|
+
interface LunoraCollectionConfig<TRow extends Row> {
|
|
285
|
+
/**
|
|
286
|
+
* The registry optimistic overlays are gated on. Defaults to the shared
|
|
287
|
+
* per-shard registry ({@link getShardCheckpoints}), which is what a
|
|
288
|
+
* multi-collection shard needs — pass one explicitly only to isolate a
|
|
289
|
+
* collection's gate (tests) or to supply custom {@link CheckpointRegistryOptions}.
|
|
290
|
+
*/
|
|
291
|
+
checkpoints?: CheckpointRegistry;
|
|
292
|
+
/** The Lunora client to subscribe through. */
|
|
293
|
+
client: LunoraClient;
|
|
294
|
+
/** Row key extractor — defaults to `row._id`. */
|
|
295
|
+
getKey?: (row: TRow) => string;
|
|
296
|
+
/** Collection id (TanStack identity) — defaults to the `list` function path (or `shape:` + the shape name). */
|
|
297
|
+
id?: string;
|
|
298
|
+
/** The Lunora query that lists the rows (the full-table sync source). Mutually exclusive with {@link LunoraCollectionConfig.shape}. */
|
|
299
|
+
list?: FunctionReference;
|
|
300
|
+
/**
|
|
301
|
+
* When the collection starts syncing. `"lazy"` (default) starts on the first
|
|
302
|
+
* `useLiveQuery` subscriber; `"eager"` starts at creation (TanStack's
|
|
303
|
+
* `startSync`) — for small "instant" reference data you want warm at boot.
|
|
304
|
+
* No effect on a `scopeBy` collection, which has nothing to sync until scoped.
|
|
305
|
+
* (Even eager, TanStack pauses sync while there are no subscribers, per its
|
|
306
|
+
* `gcTime` lifecycle — "warm while referenced", not pinned forever.)
|
|
307
|
+
*/
|
|
308
|
+
load?: "eager" | "lazy";
|
|
309
|
+
/** Notified when the underlying subscription errors; the collection always leaves `loading` regardless. */
|
|
310
|
+
onError?: (error: SubscriptionError) => void;
|
|
311
|
+
/** When set, the collection stays empty until {@link LunoraCollectionOptions.scope} points it at args (sharded). */
|
|
312
|
+
scopeBy?: string;
|
|
313
|
+
/** A replication shape as the sync source (partial replication). Mutually exclusive with {@link LunoraCollectionConfig.list}. */
|
|
314
|
+
shape?: ShapeSource;
|
|
315
|
+
/**
|
|
316
|
+
* Routes the `list` subscription — and the confirmed-mutation watermark its
|
|
317
|
+
* data/`settled` frames advance the checkpoint gate from — to a specific
|
|
318
|
+
* shard's DO. Applies to the `list` source; a `shape` carries its own
|
|
319
|
+
* {@link ShapeSource.shardKey}. Without it the list path falls back to the
|
|
320
|
+
* default ("") watermark bucket, which must not be compared against a
|
|
321
|
+
* per-shard mutator's sequence line (it would drop a sharded overlay early or
|
|
322
|
+
* hang it forever).
|
|
323
|
+
*/
|
|
324
|
+
shardKey?: string;
|
|
325
|
+
}
|
|
326
|
+
/** The result of {@link lunoraCollectionOptions}: a TanStack collection config plus its sync controls. */
|
|
327
|
+
interface LunoraCollectionOptions<TRow extends Row> {
|
|
328
|
+
/** Resolves optimistic-overlay drops against the server's confirmed watermarks. */
|
|
329
|
+
checkpoints: CheckpointRegistry;
|
|
330
|
+
/** Pass to TanStack's `createCollection`. */
|
|
331
|
+
config: CollectionConfig<TRow, string>;
|
|
332
|
+
/** Re-point a `scopeBy` collection's subscription (omit `args` to detach). No-op for unscoped collections. */
|
|
333
|
+
scope: (args?: Record<string, unknown>) => void;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Build a TanStack DB collection config (+ sync controls) that live-syncs a
|
|
337
|
+
* Lunora `list` query through the client. This is the reusable core lifted out of
|
|
338
|
+
* {@link import("./define-collections").defineCollections}: the same `makeDiffEmit`
|
|
339
|
+
* diff-into-channel, `autoIndex:"eager"` + B-tree indexes, scoped-resubscribe, and
|
|
340
|
+
* fail-safe `markReady`-on-error behavior, exposed as a standalone
|
|
341
|
+
* collection-options creator so apps (and codegen) can compose it directly.
|
|
342
|
+
*
|
|
343
|
+
* The returned `checkpoints` registry lets a mutator runtime resolve optimistic
|
|
344
|
+
* overlays against confirmed server watermarks (see {@link CheckpointRegistry}).
|
|
345
|
+
*/
|
|
346
|
+
declare const lunoraCollectionOptions: <TRow extends Row>(options: LunoraCollectionConfig<TRow>) => LunoraCollectionOptions<TRow>;
|
|
347
|
+
export { CheckpointRegistry as C, ExecutorOutboxSinkOptions as E, LunoraCollectionConfig as L, OUTBOX_MUTATION_FN_NAME as O, Row as R, SyncWriter as S, LunoraCollectionOptions as a, CHECKPOINT_FALLBACK_MS as b, createCheckpointRegistry as c, CheckpointFallbackEvent as d, CheckpointRegistryOptions as e, CheckpointRegistryStats as f, CheckpointWatermark as g, OutboxExecutor as h, OutboxMutationMetadata as i, createExecutorOutboxSink as j, createOptimisticOnlineDetector as k, lunoraCollectionOptions as l, getShardCheckpoints as m, makeDiffEmit as n, runOutboxMutation as o, releaseShardCheckpoints as r, shardCheckpointStats as s, toMap as t };
|