@lunora/db 1.0.0-alpha.9 → 1.0.0-alpha.90

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 (33) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +7 -1
  3. package/dist/collections/index.d.mts +2 -2
  4. package/dist/collections/index.d.ts +2 -2
  5. package/dist/collections/index.mjs +1 -2
  6. package/dist/index.d.mts +144 -5
  7. package/dist/index.d.ts +144 -5
  8. package/dist/index.mjs +1 -8
  9. package/dist/mutators/index.d.mts +2 -2
  10. package/dist/mutators/index.d.ts +2 -2
  11. package/dist/mutators/index.mjs +1 -1
  12. package/dist/packem_shared/CHECKPOINT_FALLBACK_MS-DVkxHO2j.mjs +1 -0
  13. package/dist/packem_shared/DIRECT_TRANSACTION_METADATA_KEY-O-hmqO42.mjs +1 -0
  14. package/dist/packem_shared/OUTBOX_MUTATION_FN_NAME-BfFCughR.mjs +1 -0
  15. package/dist/packem_shared/applyPlanToCollections-C_eyqNF9.mjs +1 -0
  16. package/dist/packem_shared/collection-options.d-bmH3BTGq.d.mts +364 -0
  17. package/dist/packem_shared/collection-options.d-bmH3BTGq.d.ts +364 -0
  18. package/dist/packem_shared/defineCollections-Cz4-m1Mp.mjs +1 -0
  19. package/dist/packem_shared/index.d-BzXEOiP5.d.mts +174 -0
  20. package/dist/packem_shared/index.d-DQsjGcjP.d.ts +235 -0
  21. package/dist/packem_shared/index.d-DlfHbDas.d.ts +174 -0
  22. package/dist/packem_shared/index.d-DrpzHIoj.d.mts +235 -0
  23. package/package.json +3 -2
  24. package/dist/packem_shared/OUTBOX_MUTATION_FN_NAME-Cf8iP6Wa.mjs +0 -102
  25. package/dist/packem_shared/bindMutators-DNhICkoy.mjs +0 -66
  26. package/dist/packem_shared/collection-options.d-lJBOVJgq.d.mts +0 -216
  27. package/dist/packem_shared/collection-options.d-lJBOVJgq.d.ts +0 -216
  28. package/dist/packem_shared/createCheckpointRegistry-SvwBczzv.mjs +0 -127
  29. package/dist/packem_shared/define-collections.d-CdORnl3S.d.mts +0 -150
  30. package/dist/packem_shared/define-collections.d-X2MfU5Es.d.ts +0 -150
  31. package/dist/packem_shared/define-mutators.d-DhO8mWz0.d.ts +0 -80
  32. package/dist/packem_shared/define-mutators.d-HWj94_nL.d.mts +0 -80
  33. package/dist/packem_shared/defineCollections-BJNtgnWB.mjs +0 -105
@@ -1,127 +0,0 @@
1
- import { BTreeIndex } from '@tanstack/db';
2
- import { toMap, makeDiffEmit } from './OUTBOX_MUTATION_FN_NAME-Cf8iP6Wa.mjs';
3
-
4
- const createGate = () => {
5
- let highest = Number.NEGATIVE_INFINITY;
6
- const waiters = [];
7
- return {
8
- advance: (value) => {
9
- if (value <= highest) {
10
- return;
11
- }
12
- highest = value;
13
- for (let index = waiters.length - 1; index >= 0; index -= 1) {
14
- const waiter = waiters[index];
15
- if (waiter && waiter.threshold <= highest) {
16
- waiter.resolve();
17
- waiters.splice(index, 1);
18
- }
19
- }
20
- },
21
- await: (threshold) => {
22
- if (threshold <= highest) {
23
- return Promise.resolve();
24
- }
25
- return new Promise((resolve) => {
26
- waiters.push({ resolve, threshold });
27
- });
28
- }
29
- };
30
- };
31
- const createCheckpointRegistry = () => {
32
- const checkpointGate = createGate();
33
- const mutationGate = createGate();
34
- return {
35
- awaitCheckpoint: (cursor) => checkpointGate.await(cursor),
36
- awaitMutationId: (id) => mutationGate.await(id),
37
- resolve: ({ checkpoint, mutationId }) => {
38
- if (checkpoint !== void 0) {
39
- checkpointGate.advance(checkpoint);
40
- }
41
- if (mutationId !== void 0) {
42
- mutationGate.advance(mutationId);
43
- }
44
- }
45
- };
46
- };
47
- const lunoraCollectionOptions = (options) => {
48
- if (options.list === void 0 === (options.shape === void 0)) {
49
- throw new Error("lunoraCollectionOptions: pass exactly one of `list` or `shape`");
50
- }
51
- const getKey = options.getKey ?? ((row) => row._id);
52
- const checkpoints = createCheckpointRegistry();
53
- const syncedJson = /* @__PURE__ */ new Map();
54
- let emit;
55
- let unsubscribe;
56
- let onErrorHandler;
57
- const openSubscription = (args, onReady) => {
58
- const onRows = (data) => {
59
- emit?.(toMap(data, getKey));
60
- onReady?.();
61
- if (options.shape === void 0) {
62
- checkpoints.resolve({ mutationId: options.client.confirmedMutationWatermark() });
63
- }
64
- };
65
- const onError = (error) => onErrorHandler?.(error);
66
- const onCheckpoint = (watermark) => {
67
- checkpoints.resolve(watermark);
68
- };
69
- if (options.shape !== void 0) {
70
- return options.client.subscribeShape({ args, name: options.shape.name }, onRows, {
71
- onCheckpoint,
72
- onError,
73
- shardKey: options.shape.shardKey
74
- });
75
- }
76
- return options.client.subscribe(options.list, args, onRows, { onCheckpoint, onError });
77
- };
78
- const config = {
79
- // Auto-build ordered (B-tree) indexes for whatever the app's live queries
80
- // join / filter / sort on, so they stay fast as the dataset grows.
81
- autoIndex: "eager",
82
- defaultIndexType: BTreeIndex,
83
- getKey,
84
- id: options.id ?? options.list?.__lunoraRef ?? `shape:${options.shape?.name ?? ""}`,
85
- // `"eager"` syncs at creation; omitted otherwise so the wire stays
86
- // byte-identical to the lazy default (sync on first subscriber).
87
- ...options.load === "eager" ? { startSync: true } : {},
88
- sync: {
89
- sync: (writer) => {
90
- emit = makeDiffEmit(syncedJson, writer);
91
- const onError = (error) => {
92
- writer.markReady();
93
- options.onError?.(error);
94
- };
95
- onErrorHandler = onError;
96
- if (options.scopeBy === void 0) {
97
- unsubscribe = openSubscription(options.shape?.args ?? {}, () => {
98
- writer.markReady();
99
- });
100
- } else {
101
- writer.markReady();
102
- }
103
- return () => {
104
- emit = void 0;
105
- onErrorHandler = void 0;
106
- unsubscribe?.();
107
- unsubscribe = void 0;
108
- };
109
- }
110
- }
111
- };
112
- const scope = (args) => {
113
- if (options.scopeBy === void 0) {
114
- return;
115
- }
116
- unsubscribe?.();
117
- unsubscribe = void 0;
118
- emit?.(/* @__PURE__ */ new Map());
119
- if (args === void 0) {
120
- return;
121
- }
122
- unsubscribe = openSubscription(args, void 0);
123
- };
124
- return { checkpoints, config, scope };
125
- };
126
-
127
- export { createCheckpointRegistry, lunoraCollectionOptions };
@@ -1,150 +0,0 @@
1
- import { FunctionReference, SubscriptionError, LunoraClient } from '@lunora/client';
2
- import { Transaction, Collection } from '@tanstack/db';
3
- import { OfflineExecutor, StorageDiagnostic } from '@tanstack/offline-transactions';
4
- import { R as Row } from "./collection-options.d-lJBOVJgq.mjs";
5
- /** Element type of an array (the row type a `list` query returns). */
6
- type Element<T> = T extends ReadonlyArray<infer E> ? E : never;
7
- /** `true` for the `any` type, `false` otherwise. */
8
- type IsAny<T> = 0 extends 1 & T ? true : false;
9
- /**
10
- * The row type a `list` query syncs. For `TList = any` (the heterogeneous-map
11
- * constraint) it resolves to the permissive {@link Row}, not `never` — otherwise
12
- * the constraint would force every `optimistic` to return `never`. For a concrete
13
- * `FunctionReference` it's the element type of the query's array return.
14
- */
15
- type RowOfList<TList> = IsAny<TList> extends true ? Row : TList extends FunctionReference<infer _K, infer _A, infer R> ? Element<R> & Row : never;
16
- /** Maps a write through the durable outbox: optimistic insert + a retried mutation. */
17
- interface InsertBinding<TRow extends Row, TInput> {
18
- /** The Lunora mutation that persists the row. */
19
- mutation: FunctionReference;
20
- /** Build the optimistic row to insert from the action input + the generated client id. */
21
- optimistic: (input: TInput, id: string) => TRow;
22
- /** Build the mutation args from the persisted optimistic row (forward `_id` as the `clientId`). */
23
- toArgs: (row: TRow) => Record<string, unknown>;
24
- }
25
- /** Declarative binding of a Lunora table to a live collection (+ optional write action). */
26
- interface CollectionDef<TList extends FunctionReference, TInput = never> {
27
- /** Row key extractor — defaults to `row._id`. */
28
- getKey?: (row: RowOfList<TList>) => string;
29
- /** Optional write binding — present iff this collection is written through the outbox. */
30
- insert?: InsertBinding<RowOfList<TList>, TInput>;
31
- /** The Lunora query that lists the rows (the sync source). */
32
- list: TList;
33
- /**
34
- * When this collection starts syncing — `"lazy"` (default) on the first
35
- * `useLiveQuery` subscriber, or `"eager"` at creation, for small "instant"
36
- * reference data you want warm at boot. Pairs with `scopeBy` for partial
37
- * (per-scope) loading — together they give the full lazy/partial/eager
38
- * (Linear `lazy`/`partial`/`instant`) load taxonomy declaratively. No effect
39
- * on a `scopeBy` collection (nothing to sync until scoped).
40
- */
41
- load?: "eager" | "lazy";
42
- /**
43
- * Notified when the underlying `list` subscription errors (e.g. the server
44
- * rejects it). Without this the error would be swallowed and the collection
45
- * could hang in `loading`; the binding always moves the collection out of
46
- * `loading` on error, and forwards the error here if supplied.
47
- */
48
- onError?: (error: SubscriptionError) => void;
49
- /** A field that scopes the list (e.g. a shard key); makes the collection re-pointable via `scope`. */
50
- scopeBy?: string;
51
- }
52
- type AnyDef = CollectionDef<any, any>;
53
- /**
54
- * The public row type a collection exposes — the element type of its `list`
55
- * query's return, with no `& Row`: a `Collection&lt;T>` is invariant in `T`, so the
56
- * exposed type must be exactly the document type, not a subtype.
57
- */
58
- type RowOf<C extends AnyDef> = C["list"] extends FunctionReference<infer _K, infer _A, infer R> ? Element<R> : never;
59
- /** The action input type, inferred structurally from the def's optimistic insert. */
60
- type InputOf<C> = C extends {
61
- insert: {
62
- optimistic: (input: infer I, id: string) => unknown;
63
- };
64
- } ? I : never;
65
- /** A queued write that was permanently dropped, passed to {@link DefineCollectionsOptions.onWriteRejected}. */
66
- interface WriteRejectedEvent {
67
- /**
68
- * The machine-readable reason — the server's error `code` (e.g. `CONFLICT`,
69
- * `FORBIDDEN`), or `UNKNOWN_MUTATION_FN` when the write referenced a collection
70
- * that no longer exists (removed in a deploy). Mirrors the client's
71
- * `MutationSettledEvent.code` so a consumer can branch on the verdict.
72
- */
73
- code?: string;
74
- /** The collection/table name the write targeted. */
75
- collection: string;
76
- /** The error that dropped the write (message carried by the underlying `NonRetriableError`). */
77
- error: Error;
78
- /**
79
- * The optimistic row being rolled back (the rollback follows the callback).
80
- * Absent only if the dropped transaction carried no recoverable row (e.g. some
81
- * `UNKNOWN_MUTATION_FN` cases).
82
- */
83
- row?: Row;
84
- }
85
- /** Options for {@link defineCollections}. */
86
- interface DefineCollectionsOptions {
87
- /**
88
- * Invoked when a leadership change occurs across tabs (only the leader tab
89
- * drains the durable outbox). Informational — useful for diagnostics; the
90
- * library handles the election itself.
91
- */
92
- onLeadershipChange?: (isLeader: boolean) => void;
93
- /**
94
- * Invoked when the durable outbox's storage layer fails — IndexedDB
95
- * unavailable (private mode), blocked, or quota exceeded. The standalone
96
- * client surfaces this via `offlineQueue.onPersistenceError`; this is the
97
- * collection-layer counterpart. A storage failure means a write is NOT durable
98
- * and won't survive a reload, so surface it (e.g. "your change may not be
99
- * saved if you close this tab").
100
- */
101
- onStorageFailure?: (diagnostic: StorageDiagnostic) => void;
102
- /**
103
- * Invoked as a queued write is permanently dropped: a coded application error
104
- * from the server (validation, RLS denial, conflict, surfaced as a
105
- * `NonRetriableError`), OR a write whose target collection no longer exists —
106
- * removed/renamed in a deploy (`code: "UNKNOWN_MUTATION_FN"`). This is the
107
- * aggregate, fire-and-forget-safe channel: unlike awaiting the per-action
108
- * `transaction` returned by `actions[name](...)`, it fires even when the caller
109
- * never retained that handle, so a UI can surface "couldn't save" instead of a
110
- * silently vanishing row. Transient failures (offline, 5xx) are retried by the
111
- * outbox, not reported here.
112
- *
113
- * Timing: the callback runs at the point of rejection; the executor's
114
- * optimistic-row rollback follows immediately after. The event's `row` is the
115
- * (about-to-be-removed) optimistic row, so don't depend on the collection
116
- * already reflecting the removal from inside the handler — use `row`/`error`
117
- * directly (e.g. for a toast).
118
- */
119
- onWriteRejected?: (event: WriteRejectedEvent) => void;
120
- }
121
- /** The wired data layer `defineCollections` returns. */
122
- interface LunoraDb<D extends Record<string, AnyDef>> {
123
- /** Optimistic, durable, retried write actions — present for `insert` collections. */
124
- actions: { [K in keyof D]: D[K] extends {
125
- insert: object;
126
- } ? (input: InputOf<D[K]>) => {
127
- id: string;
128
- transaction: Transaction;
129
- } : never };
130
- /** The live, synced collections — feed these to `useLiveQuery`. */
131
- collections: { [K in keyof D]: Collection<RowOf<D[K]>, string> };
132
- /** The shared offline executor (the outbox). */
133
- executor: OfflineExecutor;
134
- /** Re-point a `scopeBy` collection's subscription (omit `args` to detach) — present for scoped collections. */
135
- scope: { [K in keyof D]: D[K] extends {
136
- scopeBy: string;
137
- } ? (args?: Record<string, unknown>) => void : never };
138
- }
139
- /**
140
- * Wire a set of Lunora tables into a TanStack DB data layer in one declaration:
141
- * each entry becomes a live, auto-indexed collection synced from its `list` query,
142
- * and `insert` entries get an optimistic write action backed by the
143
- * offline-transactions outbox (durable, retried, client-id-keyed). Scoped
144
- * (`scopeBy`) collections are re-pointable for sharded queries.
145
- *
146
- * This is the hand-written form; `@lunora/codegen` can emit a fully-typed call to
147
- * it from `schema.ts`, so an app writes nothing.
148
- */
149
- declare const defineCollections: <D extends Record<string, AnyDef>>(client: LunoraClient, defs: D, options?: DefineCollectionsOptions) => LunoraDb<D>;
150
- export { CollectionDef as C, DefineCollectionsOptions as D, InsertBinding as I, LunoraDb as L, WriteRejectedEvent as W, defineCollections as d };
@@ -1,150 +0,0 @@
1
- import { FunctionReference, SubscriptionError, LunoraClient } from '@lunora/client';
2
- import { Transaction, Collection } from '@tanstack/db';
3
- import { OfflineExecutor, StorageDiagnostic } from '@tanstack/offline-transactions';
4
- import { R as Row } from "./collection-options.d-lJBOVJgq.js";
5
- /** Element type of an array (the row type a `list` query returns). */
6
- type Element<T> = T extends ReadonlyArray<infer E> ? E : never;
7
- /** `true` for the `any` type, `false` otherwise. */
8
- type IsAny<T> = 0 extends 1 & T ? true : false;
9
- /**
10
- * The row type a `list` query syncs. For `TList = any` (the heterogeneous-map
11
- * constraint) it resolves to the permissive {@link Row}, not `never` — otherwise
12
- * the constraint would force every `optimistic` to return `never`. For a concrete
13
- * `FunctionReference` it's the element type of the query's array return.
14
- */
15
- type RowOfList<TList> = IsAny<TList> extends true ? Row : TList extends FunctionReference<infer _K, infer _A, infer R> ? Element<R> & Row : never;
16
- /** Maps a write through the durable outbox: optimistic insert + a retried mutation. */
17
- interface InsertBinding<TRow extends Row, TInput> {
18
- /** The Lunora mutation that persists the row. */
19
- mutation: FunctionReference;
20
- /** Build the optimistic row to insert from the action input + the generated client id. */
21
- optimistic: (input: TInput, id: string) => TRow;
22
- /** Build the mutation args from the persisted optimistic row (forward `_id` as the `clientId`). */
23
- toArgs: (row: TRow) => Record<string, unknown>;
24
- }
25
- /** Declarative binding of a Lunora table to a live collection (+ optional write action). */
26
- interface CollectionDef<TList extends FunctionReference, TInput = never> {
27
- /** Row key extractor — defaults to `row._id`. */
28
- getKey?: (row: RowOfList<TList>) => string;
29
- /** Optional write binding — present iff this collection is written through the outbox. */
30
- insert?: InsertBinding<RowOfList<TList>, TInput>;
31
- /** The Lunora query that lists the rows (the sync source). */
32
- list: TList;
33
- /**
34
- * When this collection starts syncing — `"lazy"` (default) on the first
35
- * `useLiveQuery` subscriber, or `"eager"` at creation, for small "instant"
36
- * reference data you want warm at boot. Pairs with `scopeBy` for partial
37
- * (per-scope) loading — together they give the full lazy/partial/eager
38
- * (Linear `lazy`/`partial`/`instant`) load taxonomy declaratively. No effect
39
- * on a `scopeBy` collection (nothing to sync until scoped).
40
- */
41
- load?: "eager" | "lazy";
42
- /**
43
- * Notified when the underlying `list` subscription errors (e.g. the server
44
- * rejects it). Without this the error would be swallowed and the collection
45
- * could hang in `loading`; the binding always moves the collection out of
46
- * `loading` on error, and forwards the error here if supplied.
47
- */
48
- onError?: (error: SubscriptionError) => void;
49
- /** A field that scopes the list (e.g. a shard key); makes the collection re-pointable via `scope`. */
50
- scopeBy?: string;
51
- }
52
- type AnyDef = CollectionDef<any, any>;
53
- /**
54
- * The public row type a collection exposes — the element type of its `list`
55
- * query's return, with no `& Row`: a `Collection&lt;T>` is invariant in `T`, so the
56
- * exposed type must be exactly the document type, not a subtype.
57
- */
58
- type RowOf<C extends AnyDef> = C["list"] extends FunctionReference<infer _K, infer _A, infer R> ? Element<R> : never;
59
- /** The action input type, inferred structurally from the def's optimistic insert. */
60
- type InputOf<C> = C extends {
61
- insert: {
62
- optimistic: (input: infer I, id: string) => unknown;
63
- };
64
- } ? I : never;
65
- /** A queued write that was permanently dropped, passed to {@link DefineCollectionsOptions.onWriteRejected}. */
66
- interface WriteRejectedEvent {
67
- /**
68
- * The machine-readable reason — the server's error `code` (e.g. `CONFLICT`,
69
- * `FORBIDDEN`), or `UNKNOWN_MUTATION_FN` when the write referenced a collection
70
- * that no longer exists (removed in a deploy). Mirrors the client's
71
- * `MutationSettledEvent.code` so a consumer can branch on the verdict.
72
- */
73
- code?: string;
74
- /** The collection/table name the write targeted. */
75
- collection: string;
76
- /** The error that dropped the write (message carried by the underlying `NonRetriableError`). */
77
- error: Error;
78
- /**
79
- * The optimistic row being rolled back (the rollback follows the callback).
80
- * Absent only if the dropped transaction carried no recoverable row (e.g. some
81
- * `UNKNOWN_MUTATION_FN` cases).
82
- */
83
- row?: Row;
84
- }
85
- /** Options for {@link defineCollections}. */
86
- interface DefineCollectionsOptions {
87
- /**
88
- * Invoked when a leadership change occurs across tabs (only the leader tab
89
- * drains the durable outbox). Informational — useful for diagnostics; the
90
- * library handles the election itself.
91
- */
92
- onLeadershipChange?: (isLeader: boolean) => void;
93
- /**
94
- * Invoked when the durable outbox's storage layer fails — IndexedDB
95
- * unavailable (private mode), blocked, or quota exceeded. The standalone
96
- * client surfaces this via `offlineQueue.onPersistenceError`; this is the
97
- * collection-layer counterpart. A storage failure means a write is NOT durable
98
- * and won't survive a reload, so surface it (e.g. "your change may not be
99
- * saved if you close this tab").
100
- */
101
- onStorageFailure?: (diagnostic: StorageDiagnostic) => void;
102
- /**
103
- * Invoked as a queued write is permanently dropped: a coded application error
104
- * from the server (validation, RLS denial, conflict, surfaced as a
105
- * `NonRetriableError`), OR a write whose target collection no longer exists —
106
- * removed/renamed in a deploy (`code: "UNKNOWN_MUTATION_FN"`). This is the
107
- * aggregate, fire-and-forget-safe channel: unlike awaiting the per-action
108
- * `transaction` returned by `actions[name](...)`, it fires even when the caller
109
- * never retained that handle, so a UI can surface "couldn't save" instead of a
110
- * silently vanishing row. Transient failures (offline, 5xx) are retried by the
111
- * outbox, not reported here.
112
- *
113
- * Timing: the callback runs at the point of rejection; the executor's
114
- * optimistic-row rollback follows immediately after. The event's `row` is the
115
- * (about-to-be-removed) optimistic row, so don't depend on the collection
116
- * already reflecting the removal from inside the handler — use `row`/`error`
117
- * directly (e.g. for a toast).
118
- */
119
- onWriteRejected?: (event: WriteRejectedEvent) => void;
120
- }
121
- /** The wired data layer `defineCollections` returns. */
122
- interface LunoraDb<D extends Record<string, AnyDef>> {
123
- /** Optimistic, durable, retried write actions — present for `insert` collections. */
124
- actions: { [K in keyof D]: D[K] extends {
125
- insert: object;
126
- } ? (input: InputOf<D[K]>) => {
127
- id: string;
128
- transaction: Transaction;
129
- } : never };
130
- /** The live, synced collections — feed these to `useLiveQuery`. */
131
- collections: { [K in keyof D]: Collection<RowOf<D[K]>, string> };
132
- /** The shared offline executor (the outbox). */
133
- executor: OfflineExecutor;
134
- /** Re-point a `scopeBy` collection's subscription (omit `args` to detach) — present for scoped collections. */
135
- scope: { [K in keyof D]: D[K] extends {
136
- scopeBy: string;
137
- } ? (args?: Record<string, unknown>) => void : never };
138
- }
139
- /**
140
- * Wire a set of Lunora tables into a TanStack DB data layer in one declaration:
141
- * each entry becomes a live, auto-indexed collection synced from its `list` query,
142
- * and `insert` entries get an optimistic write action backed by the
143
- * offline-transactions outbox (durable, retried, client-id-keyed). Scoped
144
- * (`scopeBy`) collections are re-pointable for sharded queries.
145
- *
146
- * This is the hand-written form; `@lunora/codegen` can emit a fully-typed call to
147
- * it from `schema.ts`, so an app writes nothing.
148
- */
149
- declare const defineCollections: <D extends Record<string, AnyDef>>(client: LunoraClient, defs: D, options?: DefineCollectionsOptions) => LunoraDb<D>;
150
- export { CollectionDef as C, DefineCollectionsOptions as D, InsertBinding as I, LunoraDb as L, WriteRejectedEvent as W, defineCollections as d };
@@ -1,80 +0,0 @@
1
- import { LunoraClient } from '@lunora/client';
2
- import { Collection, Transaction } from '@tanstack/db';
3
- import { C as CheckpointRegistry, R as Row } from "./collection-options.d-lJBOVJgq.js";
4
- /** The local store a client mutator's optimistic body writes against. */
5
- interface ClientMutatorContext {
6
- /** The wired collections, keyed by name — apply optimistic inserts/updates/deletes here. */
7
- collections: Record<string, Collection<Row, string>>;
8
- }
9
- /** A client-side custom mutator: an optimistic body plus the path of its authoritative server impl. */
10
- interface ClientMutatorDef<TArgs> {
11
- /** Brand so codegen / `bindMutators` can recognize a mutator definition. */
12
- __lunoraClientMutator: true;
13
- /** The optimistic update applied to the local collections before the server confirms. */
14
- apply: (context: ClientMutatorContext, args: TArgs) => void;
15
- /** The Lunora function path of the server-authoritative mutator (`defineMutator` on the server). */
16
- serverRef: string;
17
- }
18
- /**
19
- * Declare a client-side custom mutator. `apply` runs optimistically against the
20
- * local TanStack collections; `serverRef` names the authoritative server mutator
21
- * the write is pushed to over the watermark protocol. The server impl is the
22
- * linearization point — this body is a prediction the server can override.
23
- */
24
- declare const defineMutator: <TArgs = Record<string, unknown>>(definition: {
25
- apply: (context: ClientMutatorContext, args: TArgs) => void;
26
- serverRef: string;
27
- }) => ClientMutatorDef<TArgs>;
28
- type AnyMutatorMap = Record<string, ClientMutatorDef<any>>;
29
- /** Args type of a mutator definition. */
30
- type ArgsOf<M> = M extends ClientMutatorDef<infer A> ? A : never;
31
- /** Inputs `bindMutators` needs to run a mutator: the local store + how the overlay drops. */
32
- interface BindMutatorsContext {
33
- /**
34
- * Resolves the optimistic-overlay drop against confirmed server watermarks.
35
- * When supplied, a mutation's overlay is held until the sync stream echoes
36
- * `lastMutationId >= clientSeq` (via {@link CheckpointRegistry.resolve}) — no
37
- * flicker. When omitted, the overlay drops as soon as the server accepts the
38
- * write (the by-value sync diff then converges the synced row in place).
39
- */
40
- checkpoints?: CheckpointRegistry;
41
- /** The wired collections the optimistic bodies write against. */
42
- collections: Record<string, Collection<Row, string>>;
43
- /** Optional shard key the mutator's server push is routed to. */
44
- shardKey?: string;
45
- }
46
- /** Calling a bound mutator runs the optimistic body + pushes the server write; returns the TanStack transaction. */
47
- type BoundMutators<M extends AnyMutatorMap> = { [K in keyof M]: (args: ArgsOf<M[K]>) => Transaction };
48
- /**
49
- * Bind a set of client mutators to a client + local store. Each returned handle,
50
- * when called, opens a TanStack optimistic transaction: the mutator's `apply`
51
- * body writes the predicted rows into the collections, and the transaction's
52
- * `mutationFn` pushes the authoritative write through
53
- * {@link LunoraClient.callMutator} under a monotonic per-client `clientSeq`.
54
- *
55
- * Rebase-on-poke is free — TanStack DB re-derives every pending optimistic overlay
56
- * over the latest synced base on each sync tick. The overlay is dropped when the
57
- * server confirms the write (and, if `checkpoints` is supplied, once it echoes the
58
- * matching watermark so the synced row has landed).
59
- *
60
- * The `clientSeq` generator is seeded from the server's echoed watermark
61
- * ({@link LunoraClient.confirmedMutationWatermark}) on every issue, so a reload —
62
- * which resets this in-memory counter while the server keeps a durable per-client
63
- * watermark — never reissues a sequence the DO has already applied. As a backstop
64
- * for the very first push of a fresh session (before any ack has taught the client
65
- * the watermark), a push the DO swallows as a replay (`applied === false`) is
66
- * reissued above the now-known watermark instead of being mistaken for a confirmed
67
- * write — closing the silent-drop window without risking a double-apply (a fresh
68
- * session's first stale push provably cannot be an honest replay).
69
- *
70
- * Pushes are **serialized per binding** (a FIFO chain): the DO rejects any push
71
- * with `clientSeq > watermark + 1` as `OUT_OF_ORDER` and drops the write, so two
72
- * mutators fired concurrently must not race the network into a gap. Each push
73
- * waits for the previous one's ack and assigns its `clientSeq` *inside* the
74
- * critical section — from the live watermark — so the sequence is always exactly
75
- * `watermark + 1`. Because a failed mutation never advances the server watermark,
76
- * a permanently-rejected predecessor can't wedge the chain: the next push simply
77
- * reclaims the same `watermark + 1` instead of leaving a hole the DO waits on.
78
- */
79
- declare const bindMutators: <M extends AnyMutatorMap>(client: LunoraClient, context: BindMutatorsContext, mutators: M) => BoundMutators<M>;
80
- export { BindMutatorsContext as B, ClientMutatorContext as C, BoundMutators as a, ClientMutatorDef as b, bindMutators as c, defineMutator as d };
@@ -1,80 +0,0 @@
1
- import { LunoraClient } from '@lunora/client';
2
- import { Collection, Transaction } from '@tanstack/db';
3
- import { C as CheckpointRegistry, R as Row } from "./collection-options.d-lJBOVJgq.mjs";
4
- /** The local store a client mutator's optimistic body writes against. */
5
- interface ClientMutatorContext {
6
- /** The wired collections, keyed by name — apply optimistic inserts/updates/deletes here. */
7
- collections: Record<string, Collection<Row, string>>;
8
- }
9
- /** A client-side custom mutator: an optimistic body plus the path of its authoritative server impl. */
10
- interface ClientMutatorDef<TArgs> {
11
- /** Brand so codegen / `bindMutators` can recognize a mutator definition. */
12
- __lunoraClientMutator: true;
13
- /** The optimistic update applied to the local collections before the server confirms. */
14
- apply: (context: ClientMutatorContext, args: TArgs) => void;
15
- /** The Lunora function path of the server-authoritative mutator (`defineMutator` on the server). */
16
- serverRef: string;
17
- }
18
- /**
19
- * Declare a client-side custom mutator. `apply` runs optimistically against the
20
- * local TanStack collections; `serverRef` names the authoritative server mutator
21
- * the write is pushed to over the watermark protocol. The server impl is the
22
- * linearization point — this body is a prediction the server can override.
23
- */
24
- declare const defineMutator: <TArgs = Record<string, unknown>>(definition: {
25
- apply: (context: ClientMutatorContext, args: TArgs) => void;
26
- serverRef: string;
27
- }) => ClientMutatorDef<TArgs>;
28
- type AnyMutatorMap = Record<string, ClientMutatorDef<any>>;
29
- /** Args type of a mutator definition. */
30
- type ArgsOf<M> = M extends ClientMutatorDef<infer A> ? A : never;
31
- /** Inputs `bindMutators` needs to run a mutator: the local store + how the overlay drops. */
32
- interface BindMutatorsContext {
33
- /**
34
- * Resolves the optimistic-overlay drop against confirmed server watermarks.
35
- * When supplied, a mutation's overlay is held until the sync stream echoes
36
- * `lastMutationId >= clientSeq` (via {@link CheckpointRegistry.resolve}) — no
37
- * flicker. When omitted, the overlay drops as soon as the server accepts the
38
- * write (the by-value sync diff then converges the synced row in place).
39
- */
40
- checkpoints?: CheckpointRegistry;
41
- /** The wired collections the optimistic bodies write against. */
42
- collections: Record<string, Collection<Row, string>>;
43
- /** Optional shard key the mutator's server push is routed to. */
44
- shardKey?: string;
45
- }
46
- /** Calling a bound mutator runs the optimistic body + pushes the server write; returns the TanStack transaction. */
47
- type BoundMutators<M extends AnyMutatorMap> = { [K in keyof M]: (args: ArgsOf<M[K]>) => Transaction };
48
- /**
49
- * Bind a set of client mutators to a client + local store. Each returned handle,
50
- * when called, opens a TanStack optimistic transaction: the mutator's `apply`
51
- * body writes the predicted rows into the collections, and the transaction's
52
- * `mutationFn` pushes the authoritative write through
53
- * {@link LunoraClient.callMutator} under a monotonic per-client `clientSeq`.
54
- *
55
- * Rebase-on-poke is free — TanStack DB re-derives every pending optimistic overlay
56
- * over the latest synced base on each sync tick. The overlay is dropped when the
57
- * server confirms the write (and, if `checkpoints` is supplied, once it echoes the
58
- * matching watermark so the synced row has landed).
59
- *
60
- * The `clientSeq` generator is seeded from the server's echoed watermark
61
- * ({@link LunoraClient.confirmedMutationWatermark}) on every issue, so a reload —
62
- * which resets this in-memory counter while the server keeps a durable per-client
63
- * watermark — never reissues a sequence the DO has already applied. As a backstop
64
- * for the very first push of a fresh session (before any ack has taught the client
65
- * the watermark), a push the DO swallows as a replay (`applied === false`) is
66
- * reissued above the now-known watermark instead of being mistaken for a confirmed
67
- * write — closing the silent-drop window without risking a double-apply (a fresh
68
- * session's first stale push provably cannot be an honest replay).
69
- *
70
- * Pushes are **serialized per binding** (a FIFO chain): the DO rejects any push
71
- * with `clientSeq > watermark + 1` as `OUT_OF_ORDER` and drops the write, so two
72
- * mutators fired concurrently must not race the network into a gap. Each push
73
- * waits for the previous one's ack and assigns its `clientSeq` *inside* the
74
- * critical section — from the live watermark — so the sequence is always exactly
75
- * `watermark + 1`. Because a failed mutation never advances the server watermark,
76
- * a permanently-rejected predecessor can't wedge the chain: the next push simply
77
- * reclaims the same `watermark + 1` instead of leaving a hole the DO waits on.
78
- */
79
- declare const bindMutators: <M extends AnyMutatorMap>(client: LunoraClient, context: BindMutatorsContext, mutators: M) => BoundMutators<M>;
80
- export { BindMutatorsContext as B, ClientMutatorContext as C, BoundMutators as a, ClientMutatorDef as b, bindMutators as c, defineMutator as d };