@lunora/db 1.0.0-alpha.31 → 1.0.0-alpha.32
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/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/mutators/index.d.mts +1 -1
- package/dist/mutators/index.d.ts +1 -1
- package/dist/mutators/index.mjs +1 -1
- package/dist/packem_shared/DIRECT_TRANSACTION_METADATA_KEY-DDE7m9p9.mjs +1 -0
- package/dist/packem_shared/{define-mutators.d-C_E1q1_B.d.ts → define-mutators.d-BXQN7fju.d.mts} +106 -19
- package/dist/packem_shared/{define-mutators.d--WcIPEJa.d.mts → define-mutators.d-DLP_vYu9.d.ts} +106 -19
- package/package.json +1 -1
- package/dist/packem_shared/DIRECT_TRANSACTION_METADATA_KEY-BxF71ve7.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { Collection } from '@tanstack/db';
|
|
|
2
2
|
import { R as Row } from "./packem_shared/collection-options.d-B_2IXvdU.mjs";
|
|
3
3
|
export { b as CHECKPOINT_FALLBACK_MS, type d as CheckpointFallbackEvent, type C as CheckpointRegistry, type e as CheckpointRegistryOptions, type f as CheckpointRegistryStats, type g as CheckpointWatermark, type E as ExecutorOutboxSinkOptions, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, O as OUTBOX_MUTATION_FN_NAME, type h as OutboxExecutor, type i as OutboxMutationMetadata, type S as SyncWriter, c as createCheckpointRegistry, j as createExecutorOutboxSink, k as createOptimisticOnlineDetector, m as getShardCheckpoints, l as lunoraCollectionOptions, n as makeDiffEmit, r as releaseShardCheckpoints, o as runOutboxMutation, s as shardCheckpointStats, t as toMap } from "./packem_shared/collection-options.d-B_2IXvdU.mjs";
|
|
4
4
|
export { type C as CollectionDef, type D as DefineCollectionsOptions, type I as InsertBinding, type L as LunoraDb, type W as WriteRejectedEvent, d as defineCollections } from "./packem_shared/define-collections.d-C6J1Y4Q_.mjs";
|
|
5
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type
|
|
5
|
+
export { type B as BindMutatorsContext, type a as BoundMutatorApi, type b as BoundMutators, type C as ClientMutatorContext, type c as ClientMutatorDef, type d as CollectionMap, D as DIRECT_TRANSACTION_METADATA_KEY, type g as MutatorReference, type M as MutatorRejectedEvent, e as bindMutators, f as defineMutator, i as initMutators } from "./packem_shared/define-mutators.d-BXQN7fju.mjs";
|
|
6
6
|
import '@lunora/client';
|
|
7
7
|
import '@tanstack/offline-transactions';
|
|
8
8
|
/** One row to insert. `_id` may be pre-minted client-side so the optimistic row keys match the persisted one. */
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Collection } from '@tanstack/db';
|
|
|
2
2
|
import { R as Row } from "./packem_shared/collection-options.d-B_2IXvdU.js";
|
|
3
3
|
export { b as CHECKPOINT_FALLBACK_MS, type d as CheckpointFallbackEvent, type C as CheckpointRegistry, type e as CheckpointRegistryOptions, type f as CheckpointRegistryStats, type g as CheckpointWatermark, type E as ExecutorOutboxSinkOptions, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, O as OUTBOX_MUTATION_FN_NAME, type h as OutboxExecutor, type i as OutboxMutationMetadata, type S as SyncWriter, c as createCheckpointRegistry, j as createExecutorOutboxSink, k as createOptimisticOnlineDetector, m as getShardCheckpoints, l as lunoraCollectionOptions, n as makeDiffEmit, r as releaseShardCheckpoints, o as runOutboxMutation, s as shardCheckpointStats, t as toMap } from "./packem_shared/collection-options.d-B_2IXvdU.js";
|
|
4
4
|
export { type C as CollectionDef, type D as DefineCollectionsOptions, type I as InsertBinding, type L as LunoraDb, type W as WriteRejectedEvent, d as defineCollections } from "./packem_shared/define-collections.d-DAFPHFxr.js";
|
|
5
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type
|
|
5
|
+
export { type B as BindMutatorsContext, type a as BoundMutatorApi, type b as BoundMutators, type C as ClientMutatorContext, type c as ClientMutatorDef, type d as CollectionMap, D as DIRECT_TRANSACTION_METADATA_KEY, type g as MutatorReference, type M as MutatorRejectedEvent, e as bindMutators, f as defineMutator, i as initMutators } from "./packem_shared/define-mutators.d-DLP_vYu9.js";
|
|
6
6
|
import '@lunora/client';
|
|
7
7
|
import '@tanstack/offline-transactions';
|
|
8
8
|
/** One row to insert. `_id` may be pre-minted client-side so the optimistic row keys match the persisted one. */
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{applyPlanToCollections as r,applyPlanToDb as i}from"./packem_shared/applyPlanToCollections-C29s6OoX.mjs";import{CHECKPOINT_FALLBACK_MS as a,createCheckpointRegistry as p,getShardCheckpoints as c,lunoraCollectionOptions as l,releaseShardCheckpoints as s,shardCheckpointStats as C}from"./packem_shared/CHECKPOINT_FALLBACK_MS-vBJLOBjX.mjs";import{defineCollections as T}from"./packem_shared/defineCollections-BCgkk3A6.mjs";import{DIRECT_TRANSACTION_METADATA_KEY as
|
|
1
|
+
import{applyPlanToCollections as r,applyPlanToDb as i}from"./packem_shared/applyPlanToCollections-C29s6OoX.mjs";import{CHECKPOINT_FALLBACK_MS as a,createCheckpointRegistry as p,getShardCheckpoints as c,lunoraCollectionOptions as l,releaseShardCheckpoints as s,shardCheckpointStats as C}from"./packem_shared/CHECKPOINT_FALLBACK_MS-vBJLOBjX.mjs";import{defineCollections as T}from"./packem_shared/defineCollections-BCgkk3A6.mjs";import{DIRECT_TRANSACTION_METADATA_KEY as u,bindMutators as x,defineMutator as A,initMutators as M}from"./packem_shared/DIRECT_TRANSACTION_METADATA_KEY-DDE7m9p9.mjs";import{OUTBOX_MUTATION_FN_NAME as E,createExecutorOutboxSink as _,createOptimisticOnlineDetector as h,makeDiffEmit as N,runOutboxMutation as S,toMap as d}from"./packem_shared/OUTBOX_MUTATION_FN_NAME-CebgkYw2.mjs";const t="0.0.0";export{a as CHECKPOINT_FALLBACK_MS,u as DIRECT_TRANSACTION_METADATA_KEY,E as OUTBOX_MUTATION_FN_NAME,t as VERSION,r as applyPlanToCollections,i as applyPlanToDb,x as bindMutators,p as createCheckpointRegistry,_ as createExecutorOutboxSink,h as createOptimisticOnlineDetector,T as defineCollections,A as defineMutator,c as getShardCheckpoints,M as initMutators,l as lunoraCollectionOptions,N as makeDiffEmit,s as releaseShardCheckpoints,S as runOutboxMutation,C as shardCheckpointStats,d as toMap};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type
|
|
1
|
+
export { type B as BindMutatorsContext, type a as BoundMutatorApi, type b as BoundMutators, type C as ClientMutatorContext, type c as ClientMutatorDef, type d as CollectionMap, type M as MutatorRejectedEvent, e as bindMutators, f as defineMutator, i as initMutators } from "../packem_shared/define-mutators.d-BXQN7fju.mjs";
|
|
2
2
|
import '@lunora/client';
|
|
3
3
|
import '@tanstack/db';
|
|
4
4
|
import "../packem_shared/collection-options.d-B_2IXvdU.mjs";
|
package/dist/mutators/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type
|
|
1
|
+
export { type B as BindMutatorsContext, type a as BoundMutatorApi, type b as BoundMutators, type C as ClientMutatorContext, type c as ClientMutatorDef, type d as CollectionMap, type M as MutatorRejectedEvent, e as bindMutators, f as defineMutator, i as initMutators } from "../packem_shared/define-mutators.d-DLP_vYu9.js";
|
|
2
2
|
import '@lunora/client';
|
|
3
3
|
import '@tanstack/db';
|
|
4
4
|
import "../packem_shared/collection-options.d-B_2IXvdU.js";
|
package/dist/mutators/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{bindMutators as r,defineMutator as
|
|
1
|
+
import{bindMutators as r,defineMutator as i,initMutators as a}from"../packem_shared/DIRECT_TRANSACTION_METADATA_KEY-DDE7m9p9.mjs";export{r as bindMutators,i as defineMutator,a as initMutators};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as l}from"@lunora/errors";import{createTransaction as g}from"@tanstack/db";import{getShardCheckpoints as w,hasCheckpointsAttached as M}from"./CHECKPOINT_FALLBACK_MS-vBJLOBjX.mjs";import{runOutboxMutation as _}from"./OUTBOX_MUTATION_FN_NAME-CebgkYw2.mjs";const v="__tanstack_db_direct",T="defineMutator: `serverRef` must be a generated mutator reference (api.mutators.*) or a 'namespace:fn' string",k=e=>{const t=typeof e=="string"?e:e?.__lunoraRef;if(typeof t!="string"||t.length===0)throw new l("INTERNAL",T);return t},A=e=>({__lunoraClientMutator:!0,apply:e.apply,serverRef:k(e.serverRef)}),E=(e,t,p)=>{let i=0;const d=()=>(i=Math.max(i,e.confirmedMutationWatermark(t.shardKey))+1,i);let u=Promise.resolve();const h=(r,a)=>{const s=u.then(async()=>{for(let c=0;;c+=1){const o=d(),{applied:n}=await e.callMutator(r,a,{clientSeq:o,shardKey:t.shardKey});if(n)return o;if(c>=32)throw new l("INTERNAL",`lunora: custom mutator "${r}" could not claim a fresh client sequence after ${String(32)} attempts`)}});return u=s.then(()=>{},()=>{}),s},R=()=>{if(t.checkpoints===!1)return;if(t.checkpoints)return t.checkpoints;const r=w(e,t.shardKey);return M(r)?r:void 0},y=(r,a,s,c)=>{const{onWriteRejected:o}=t;o&&r.isPersisted.promise.catch(n=>{const m=n instanceof Error?n:new Error(String(n));try{o({args:c,code:m.code,error:m,mutator:a,serverRef:s})}catch{}})},f={};for(const[r,a]of Object.entries(p))f[r]=s=>{const c=g({autoCommit:!0,metadata:{[v]:!0,serverRef:a.serverRef},mutationFn:async()=>{let o=0;await _(async()=>{o=await h(a.serverRef,s)});const n=R();n&&(n.acknowledge({mutationId:o}),await n.awaitMutationId(o))}});return c.mutate(()=>{a.apply({collections:t.collections},s)}),y(c,r,a.serverRef,s),c};return f},K=()=>({bindMutators:E,defineMutator:A});export{v as DIRECT_TRANSACTION_METADATA_KEY,E as bindMutators,A as defineMutator,K as initMutators};
|
package/dist/packem_shared/{define-mutators.d-C_E1q1_B.d.ts → define-mutators.d-BXQN7fju.d.mts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LunoraClient } from '@lunora/client';
|
|
2
2
|
import { Collection, Transaction } from '@tanstack/db';
|
|
3
|
-
import { C as CheckpointRegistry
|
|
3
|
+
import { C as CheckpointRegistry } from "./collection-options.d-B_2IXvdU.mjs";
|
|
4
4
|
/**
|
|
5
5
|
* TanStack DB's "direct transaction" marker.
|
|
6
6
|
*
|
|
@@ -19,10 +19,23 @@ import { C as CheckpointRegistry, R as Row } from "./collection-options.d-B_2IXv
|
|
|
19
19
|
* reads that module off disk and fails if the upstream value ever changes.
|
|
20
20
|
*/
|
|
21
21
|
declare const DIRECT_TRANSACTION_METADATA_KEY = "__tanstack_db_direct";
|
|
22
|
+
/**
|
|
23
|
+
* A map of wired collections, keyed by the name the optimistic bodies address them
|
|
24
|
+
* by.
|
|
25
|
+
*
|
|
26
|
+
* The row type is `any` on purpose. `Collection<T, string>` is **invariant** in
|
|
27
|
+
* `T` (its methods both consume and produce rows), so a concrete
|
|
28
|
+
* `Collection<Doc<"nodes"> & Row>` is NOT assignable to `Collection<Row>` — which
|
|
29
|
+
* is why the previous `Record<string, Collection<Row, string>>` forced an
|
|
30
|
+
* `as never` cast on every entry, and left `context.collections.<name>` too
|
|
31
|
+
* untyped to be worth using. Projects recover the concrete types by binding them
|
|
32
|
+
* once through {@link initMutators}.
|
|
33
|
+
*/
|
|
34
|
+
type CollectionMap = Record<string, Collection<any, string>>;
|
|
22
35
|
/** The local store a client mutator's optimistic body writes against. */
|
|
23
|
-
interface ClientMutatorContext {
|
|
36
|
+
interface ClientMutatorContext<TCollections extends CollectionMap = CollectionMap> {
|
|
24
37
|
/** The wired collections, keyed by name — apply optimistic inserts/updates/deletes here. */
|
|
25
|
-
collections:
|
|
38
|
+
collections: TCollections;
|
|
26
39
|
}
|
|
27
40
|
/**
|
|
28
41
|
* A generated mutator reference (`api.mutators.sendMessage`), accepted by
|
|
@@ -41,14 +54,12 @@ interface MutatorReference<TArgs = unknown> {
|
|
|
41
54
|
};
|
|
42
55
|
readonly __lunoraRef: string;
|
|
43
56
|
}
|
|
44
|
-
/** Args type carried by a {@link MutatorReference}. */
|
|
45
|
-
type ArgsOfReference<R> = R extends MutatorReference<infer A> ? A : never;
|
|
46
57
|
/** A client-side custom mutator: an optimistic body plus the path of its authoritative server impl. */
|
|
47
|
-
interface ClientMutatorDef<TArgs> {
|
|
58
|
+
interface ClientMutatorDef<TArgs, TCollections extends CollectionMap = CollectionMap> {
|
|
48
59
|
/** Brand so codegen / `bindMutators` can recognize a mutator definition. */
|
|
49
60
|
__lunoraClientMutator: true;
|
|
50
61
|
/** The optimistic update applied to the local collections before the server confirms. */
|
|
51
|
-
apply: (context: ClientMutatorContext
|
|
62
|
+
apply: (context: ClientMutatorContext<TCollections>, args: TArgs) => void;
|
|
52
63
|
/** The Lunora function path of the server-authoritative mutator (`defineMutator` on the server). */
|
|
53
64
|
serverRef: string;
|
|
54
65
|
}
|
|
@@ -75,22 +86,47 @@ interface ClientMutatorDef<TArgs> {
|
|
|
75
86
|
* // restate the args yourself.
|
|
76
87
|
* defineMutator<{ text: string }>({ apply, serverRef: "mutators:sendMessage" });
|
|
77
88
|
* ```
|
|
89
|
+
*
|
|
90
|
+
* `context.collections` is typed by whatever map it is bound to — which for this
|
|
91
|
+
* standalone form is the widest one. Bind the concrete collections once with
|
|
92
|
+
* {@link initMutators} to get `collections.<name>` typed inside `apply`.
|
|
78
93
|
*/
|
|
79
94
|
declare const defineMutator: {
|
|
80
95
|
<TArgs = Record<string, unknown>>(definition: {
|
|
81
96
|
apply: (context: ClientMutatorContext, args: TArgs) => void;
|
|
82
97
|
serverRef: string;
|
|
83
98
|
}): ClientMutatorDef<TArgs>;
|
|
84
|
-
<
|
|
85
|
-
apply: (context: ClientMutatorContext, args:
|
|
86
|
-
serverRef:
|
|
87
|
-
}): ClientMutatorDef<
|
|
99
|
+
<TArgs>(definition: {
|
|
100
|
+
apply: (context: ClientMutatorContext, args: TArgs) => void;
|
|
101
|
+
serverRef: MutatorReference<TArgs>;
|
|
102
|
+
}): ClientMutatorDef<TArgs>;
|
|
88
103
|
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
/**
|
|
105
|
+
* A mutator map whose defs are bound to `TCollections`, arg types erased — the
|
|
106
|
+
* `TCollections`-pinned counterpart of {@link AnyMutatorMap}, and `any` for the
|
|
107
|
+
* same variance reason.
|
|
108
|
+
*/
|
|
109
|
+
type MutatorMapFor<TCollections extends CollectionMap> = Record<string, ClientMutatorDef<any, TCollections>>;
|
|
110
|
+
type AnyMutatorMap = Record<string, ClientMutatorDef<any, any>>;
|
|
111
|
+
/** Args type of a mutator definition, whatever collections map it was bound to. */
|
|
112
|
+
type ArgsOf<M> = M extends ClientMutatorDef<infer A, infer _C> ? A : never;
|
|
113
|
+
/** A mutator write that was permanently rejected, passed to {@link BindMutatorsContext.onWriteRejected}. */
|
|
114
|
+
interface MutatorRejectedEvent {
|
|
115
|
+
/** The args the rejected call was made with. */
|
|
116
|
+
args: unknown;
|
|
117
|
+
/**
|
|
118
|
+
* The machine-readable reason when the server supplied one (e.g. `CONFLICT`,
|
|
119
|
+
* `FORBIDDEN`). Mirrors {@link import("./define-collections").WriteRejectedEvent.code}.
|
|
120
|
+
*/
|
|
121
|
+
code?: string;
|
|
122
|
+
/** The error that rejected the write. */
|
|
123
|
+
error: Error;
|
|
124
|
+
/** The bound mutator's key in the map passed to `bindMutators`. */
|
|
125
|
+
mutator: string;
|
|
126
|
+
/** The `namespace:fn` path the push targeted. */
|
|
127
|
+
serverRef: string;
|
|
128
|
+
}
|
|
129
|
+
interface BindMutatorsContext<TCollections extends CollectionMap = CollectionMap> {
|
|
94
130
|
/**
|
|
95
131
|
* Resolves the optimistic-overlay drop against confirmed server watermarks: a
|
|
96
132
|
* mutation's overlay is held until the sync stream echoes
|
|
@@ -107,7 +143,24 @@ interface BindMutatorsContext {
|
|
|
107
143
|
*/
|
|
108
144
|
checkpoints?: CheckpointRegistry | false;
|
|
109
145
|
/** The wired collections the optimistic bodies write against. */
|
|
110
|
-
collections:
|
|
146
|
+
collections: TCollections;
|
|
147
|
+
/**
|
|
148
|
+
* Called when a mutator's server push is permanently rejected — the aggregate
|
|
149
|
+
* failure channel for writes, symmetric with
|
|
150
|
+
* {@link import("./define-collections").DefineCollectionsOptions.onWriteRejected}
|
|
151
|
+
* on the outbox path.
|
|
152
|
+
*
|
|
153
|
+
* Supplying it also makes a **fire-and-forget** call safe: a bound handle
|
|
154
|
+
* returns a `Transaction` whose `isPersisted` promise rejects on failure, so a
|
|
155
|
+
* caller that neither awaits it nor attaches a `.catch` leaves an unhandled
|
|
156
|
+
* rejection. With this hook set, `bindMutators` consumes that rejection itself
|
|
157
|
+
* after reporting it — a caller that DOES await still sees the rejection, so
|
|
158
|
+
* per-call handling is unaffected.
|
|
159
|
+
*
|
|
160
|
+
* A throwing listener is swallowed: reporting a failure must not manufacture a
|
|
161
|
+
* second one.
|
|
162
|
+
*/
|
|
163
|
+
onWriteRejected?: (event: MutatorRejectedEvent) => void;
|
|
111
164
|
/** Optional shard key the mutator's server push is routed to. */
|
|
112
165
|
shardKey?: string;
|
|
113
166
|
}
|
|
@@ -144,5 +197,39 @@ type BoundMutators<M extends AnyMutatorMap> = { [K in keyof M]: (args: ArgsOf<M[
|
|
|
144
197
|
* a permanently-rejected predecessor can't wedge the chain: the next push simply
|
|
145
198
|
* reclaims the same `watermark + 1` instead of leaving a hole the DO waits on.
|
|
146
199
|
*/
|
|
147
|
-
declare const bindMutators: <M extends AnyMutatorMap>(client: LunoraClient, context: BindMutatorsContext
|
|
148
|
-
|
|
200
|
+
declare const bindMutators: <M extends AnyMutatorMap, TCollections extends CollectionMap = CollectionMap>(client: LunoraClient, context: BindMutatorsContext<TCollections>, mutators: M) => BoundMutators<M>;
|
|
201
|
+
/** `defineMutator` + `bindMutators`, both bound to one project's collections map. */
|
|
202
|
+
interface BoundMutatorApi<TCollections extends CollectionMap> {
|
|
203
|
+
/** {@link bindMutators}, with `collections` pinned to `TCollections`. */
|
|
204
|
+
bindMutators: <M extends MutatorMapFor<TCollections>>(client: LunoraClient, context: BindMutatorsContext<TCollections>, mutators: M) => BoundMutators<M>;
|
|
205
|
+
/** {@link defineMutator}, with `context.collections` typed as `TCollections`. */
|
|
206
|
+
defineMutator: {
|
|
207
|
+
<TArgs = Record<string, unknown>>(definition: {
|
|
208
|
+
apply: (context: ClientMutatorContext<TCollections>, args: TArgs) => void;
|
|
209
|
+
serverRef: string;
|
|
210
|
+
}): ClientMutatorDef<TArgs, TCollections>;
|
|
211
|
+
<TArgs>(definition: {
|
|
212
|
+
apply: (context: ClientMutatorContext<TCollections>, args: TArgs) => void;
|
|
213
|
+
serverRef: MutatorReference<TArgs>;
|
|
214
|
+
}): ClientMutatorDef<TArgs, TCollections>;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Bind the mutator surface to **this project's** collections map, once.
|
|
219
|
+
*
|
|
220
|
+
* `Collection` is invariant in its row type, so a shared
|
|
221
|
+
* `Record<string, Collection<Row, string>>` could neither accept a generated
|
|
222
|
+
* collection without an `as never` cast nor hand a usable type back to `apply` —
|
|
223
|
+
* which is why optimistic bodies tended to ignore `context.collections` and close
|
|
224
|
+
* over module-scope collection variables instead. Declaring the map once here
|
|
225
|
+
* fixes both ends: `bindMutators` takes the concrete collections cast-free, and
|
|
226
|
+
* `apply` reads `collections.<name>` at its real row type.
|
|
227
|
+
*
|
|
228
|
+
* Runtime-identical to the standalone {@link defineMutator} / {@link bindMutators}
|
|
229
|
+
* (this returns those very functions); only the types narrow.
|
|
230
|
+
* @example
|
|
231
|
+
* const { bindMutators, defineMutator } = initMutators<{ nodes: typeof wholeOutlineCollection }>();
|
|
232
|
+
* const setText = defineMutator({ apply: ({ collections }, args) => collections.nodes.update(args.id, setter), serverRef: api.mutators.setText });
|
|
233
|
+
*/
|
|
234
|
+
declare const initMutators: <TCollections extends CollectionMap>() => BoundMutatorApi<TCollections>;
|
|
235
|
+
export { BindMutatorsContext as B, ClientMutatorContext as C, DIRECT_TRANSACTION_METADATA_KEY as D, MutatorRejectedEvent as M, BoundMutatorApi as a, BoundMutators as b, ClientMutatorDef as c, CollectionMap as d, bindMutators as e, defineMutator as f, MutatorReference as g, initMutators as i };
|
package/dist/packem_shared/{define-mutators.d--WcIPEJa.d.mts → define-mutators.d-DLP_vYu9.d.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LunoraClient } from '@lunora/client';
|
|
2
2
|
import { Collection, Transaction } from '@tanstack/db';
|
|
3
|
-
import { C as CheckpointRegistry
|
|
3
|
+
import { C as CheckpointRegistry } from "./collection-options.d-B_2IXvdU.js";
|
|
4
4
|
/**
|
|
5
5
|
* TanStack DB's "direct transaction" marker.
|
|
6
6
|
*
|
|
@@ -19,10 +19,23 @@ import { C as CheckpointRegistry, R as Row } from "./collection-options.d-B_2IXv
|
|
|
19
19
|
* reads that module off disk and fails if the upstream value ever changes.
|
|
20
20
|
*/
|
|
21
21
|
declare const DIRECT_TRANSACTION_METADATA_KEY = "__tanstack_db_direct";
|
|
22
|
+
/**
|
|
23
|
+
* A map of wired collections, keyed by the name the optimistic bodies address them
|
|
24
|
+
* by.
|
|
25
|
+
*
|
|
26
|
+
* The row type is `any` on purpose. `Collection<T, string>` is **invariant** in
|
|
27
|
+
* `T` (its methods both consume and produce rows), so a concrete
|
|
28
|
+
* `Collection<Doc<"nodes"> & Row>` is NOT assignable to `Collection<Row>` — which
|
|
29
|
+
* is why the previous `Record<string, Collection<Row, string>>` forced an
|
|
30
|
+
* `as never` cast on every entry, and left `context.collections.<name>` too
|
|
31
|
+
* untyped to be worth using. Projects recover the concrete types by binding them
|
|
32
|
+
* once through {@link initMutators}.
|
|
33
|
+
*/
|
|
34
|
+
type CollectionMap = Record<string, Collection<any, string>>;
|
|
22
35
|
/** The local store a client mutator's optimistic body writes against. */
|
|
23
|
-
interface ClientMutatorContext {
|
|
36
|
+
interface ClientMutatorContext<TCollections extends CollectionMap = CollectionMap> {
|
|
24
37
|
/** The wired collections, keyed by name — apply optimistic inserts/updates/deletes here. */
|
|
25
|
-
collections:
|
|
38
|
+
collections: TCollections;
|
|
26
39
|
}
|
|
27
40
|
/**
|
|
28
41
|
* A generated mutator reference (`api.mutators.sendMessage`), accepted by
|
|
@@ -41,14 +54,12 @@ interface MutatorReference<TArgs = unknown> {
|
|
|
41
54
|
};
|
|
42
55
|
readonly __lunoraRef: string;
|
|
43
56
|
}
|
|
44
|
-
/** Args type carried by a {@link MutatorReference}. */
|
|
45
|
-
type ArgsOfReference<R> = R extends MutatorReference<infer A> ? A : never;
|
|
46
57
|
/** A client-side custom mutator: an optimistic body plus the path of its authoritative server impl. */
|
|
47
|
-
interface ClientMutatorDef<TArgs> {
|
|
58
|
+
interface ClientMutatorDef<TArgs, TCollections extends CollectionMap = CollectionMap> {
|
|
48
59
|
/** Brand so codegen / `bindMutators` can recognize a mutator definition. */
|
|
49
60
|
__lunoraClientMutator: true;
|
|
50
61
|
/** The optimistic update applied to the local collections before the server confirms. */
|
|
51
|
-
apply: (context: ClientMutatorContext
|
|
62
|
+
apply: (context: ClientMutatorContext<TCollections>, args: TArgs) => void;
|
|
52
63
|
/** The Lunora function path of the server-authoritative mutator (`defineMutator` on the server). */
|
|
53
64
|
serverRef: string;
|
|
54
65
|
}
|
|
@@ -75,22 +86,47 @@ interface ClientMutatorDef<TArgs> {
|
|
|
75
86
|
* // restate the args yourself.
|
|
76
87
|
* defineMutator<{ text: string }>({ apply, serverRef: "mutators:sendMessage" });
|
|
77
88
|
* ```
|
|
89
|
+
*
|
|
90
|
+
* `context.collections` is typed by whatever map it is bound to — which for this
|
|
91
|
+
* standalone form is the widest one. Bind the concrete collections once with
|
|
92
|
+
* {@link initMutators} to get `collections.<name>` typed inside `apply`.
|
|
78
93
|
*/
|
|
79
94
|
declare const defineMutator: {
|
|
80
95
|
<TArgs = Record<string, unknown>>(definition: {
|
|
81
96
|
apply: (context: ClientMutatorContext, args: TArgs) => void;
|
|
82
97
|
serverRef: string;
|
|
83
98
|
}): ClientMutatorDef<TArgs>;
|
|
84
|
-
<
|
|
85
|
-
apply: (context: ClientMutatorContext, args:
|
|
86
|
-
serverRef:
|
|
87
|
-
}): ClientMutatorDef<
|
|
99
|
+
<TArgs>(definition: {
|
|
100
|
+
apply: (context: ClientMutatorContext, args: TArgs) => void;
|
|
101
|
+
serverRef: MutatorReference<TArgs>;
|
|
102
|
+
}): ClientMutatorDef<TArgs>;
|
|
88
103
|
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
/**
|
|
105
|
+
* A mutator map whose defs are bound to `TCollections`, arg types erased — the
|
|
106
|
+
* `TCollections`-pinned counterpart of {@link AnyMutatorMap}, and `any` for the
|
|
107
|
+
* same variance reason.
|
|
108
|
+
*/
|
|
109
|
+
type MutatorMapFor<TCollections extends CollectionMap> = Record<string, ClientMutatorDef<any, TCollections>>;
|
|
110
|
+
type AnyMutatorMap = Record<string, ClientMutatorDef<any, any>>;
|
|
111
|
+
/** Args type of a mutator definition, whatever collections map it was bound to. */
|
|
112
|
+
type ArgsOf<M> = M extends ClientMutatorDef<infer A, infer _C> ? A : never;
|
|
113
|
+
/** A mutator write that was permanently rejected, passed to {@link BindMutatorsContext.onWriteRejected}. */
|
|
114
|
+
interface MutatorRejectedEvent {
|
|
115
|
+
/** The args the rejected call was made with. */
|
|
116
|
+
args: unknown;
|
|
117
|
+
/**
|
|
118
|
+
* The machine-readable reason when the server supplied one (e.g. `CONFLICT`,
|
|
119
|
+
* `FORBIDDEN`). Mirrors {@link import("./define-collections").WriteRejectedEvent.code}.
|
|
120
|
+
*/
|
|
121
|
+
code?: string;
|
|
122
|
+
/** The error that rejected the write. */
|
|
123
|
+
error: Error;
|
|
124
|
+
/** The bound mutator's key in the map passed to `bindMutators`. */
|
|
125
|
+
mutator: string;
|
|
126
|
+
/** The `namespace:fn` path the push targeted. */
|
|
127
|
+
serverRef: string;
|
|
128
|
+
}
|
|
129
|
+
interface BindMutatorsContext<TCollections extends CollectionMap = CollectionMap> {
|
|
94
130
|
/**
|
|
95
131
|
* Resolves the optimistic-overlay drop against confirmed server watermarks: a
|
|
96
132
|
* mutation's overlay is held until the sync stream echoes
|
|
@@ -107,7 +143,24 @@ interface BindMutatorsContext {
|
|
|
107
143
|
*/
|
|
108
144
|
checkpoints?: CheckpointRegistry | false;
|
|
109
145
|
/** The wired collections the optimistic bodies write against. */
|
|
110
|
-
collections:
|
|
146
|
+
collections: TCollections;
|
|
147
|
+
/**
|
|
148
|
+
* Called when a mutator's server push is permanently rejected — the aggregate
|
|
149
|
+
* failure channel for writes, symmetric with
|
|
150
|
+
* {@link import("./define-collections").DefineCollectionsOptions.onWriteRejected}
|
|
151
|
+
* on the outbox path.
|
|
152
|
+
*
|
|
153
|
+
* Supplying it also makes a **fire-and-forget** call safe: a bound handle
|
|
154
|
+
* returns a `Transaction` whose `isPersisted` promise rejects on failure, so a
|
|
155
|
+
* caller that neither awaits it nor attaches a `.catch` leaves an unhandled
|
|
156
|
+
* rejection. With this hook set, `bindMutators` consumes that rejection itself
|
|
157
|
+
* after reporting it — a caller that DOES await still sees the rejection, so
|
|
158
|
+
* per-call handling is unaffected.
|
|
159
|
+
*
|
|
160
|
+
* A throwing listener is swallowed: reporting a failure must not manufacture a
|
|
161
|
+
* second one.
|
|
162
|
+
*/
|
|
163
|
+
onWriteRejected?: (event: MutatorRejectedEvent) => void;
|
|
111
164
|
/** Optional shard key the mutator's server push is routed to. */
|
|
112
165
|
shardKey?: string;
|
|
113
166
|
}
|
|
@@ -144,5 +197,39 @@ type BoundMutators<M extends AnyMutatorMap> = { [K in keyof M]: (args: ArgsOf<M[
|
|
|
144
197
|
* a permanently-rejected predecessor can't wedge the chain: the next push simply
|
|
145
198
|
* reclaims the same `watermark + 1` instead of leaving a hole the DO waits on.
|
|
146
199
|
*/
|
|
147
|
-
declare const bindMutators: <M extends AnyMutatorMap>(client: LunoraClient, context: BindMutatorsContext
|
|
148
|
-
|
|
200
|
+
declare const bindMutators: <M extends AnyMutatorMap, TCollections extends CollectionMap = CollectionMap>(client: LunoraClient, context: BindMutatorsContext<TCollections>, mutators: M) => BoundMutators<M>;
|
|
201
|
+
/** `defineMutator` + `bindMutators`, both bound to one project's collections map. */
|
|
202
|
+
interface BoundMutatorApi<TCollections extends CollectionMap> {
|
|
203
|
+
/** {@link bindMutators}, with `collections` pinned to `TCollections`. */
|
|
204
|
+
bindMutators: <M extends MutatorMapFor<TCollections>>(client: LunoraClient, context: BindMutatorsContext<TCollections>, mutators: M) => BoundMutators<M>;
|
|
205
|
+
/** {@link defineMutator}, with `context.collections` typed as `TCollections`. */
|
|
206
|
+
defineMutator: {
|
|
207
|
+
<TArgs = Record<string, unknown>>(definition: {
|
|
208
|
+
apply: (context: ClientMutatorContext<TCollections>, args: TArgs) => void;
|
|
209
|
+
serverRef: string;
|
|
210
|
+
}): ClientMutatorDef<TArgs, TCollections>;
|
|
211
|
+
<TArgs>(definition: {
|
|
212
|
+
apply: (context: ClientMutatorContext<TCollections>, args: TArgs) => void;
|
|
213
|
+
serverRef: MutatorReference<TArgs>;
|
|
214
|
+
}): ClientMutatorDef<TArgs, TCollections>;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Bind the mutator surface to **this project's** collections map, once.
|
|
219
|
+
*
|
|
220
|
+
* `Collection` is invariant in its row type, so a shared
|
|
221
|
+
* `Record<string, Collection<Row, string>>` could neither accept a generated
|
|
222
|
+
* collection without an `as never` cast nor hand a usable type back to `apply` —
|
|
223
|
+
* which is why optimistic bodies tended to ignore `context.collections` and close
|
|
224
|
+
* over module-scope collection variables instead. Declaring the map once here
|
|
225
|
+
* fixes both ends: `bindMutators` takes the concrete collections cast-free, and
|
|
226
|
+
* `apply` reads `collections.<name>` at its real row type.
|
|
227
|
+
*
|
|
228
|
+
* Runtime-identical to the standalone {@link defineMutator} / {@link bindMutators}
|
|
229
|
+
* (this returns those very functions); only the types narrow.
|
|
230
|
+
* @example
|
|
231
|
+
* const { bindMutators, defineMutator } = initMutators<{ nodes: typeof wholeOutlineCollection }>();
|
|
232
|
+
* const setText = defineMutator({ apply: ({ collections }, args) => collections.nodes.update(args.id, setter), serverRef: api.mutators.setText });
|
|
233
|
+
*/
|
|
234
|
+
declare const initMutators: <TCollections extends CollectionMap>() => BoundMutatorApi<TCollections>;
|
|
235
|
+
export { BindMutatorsContext as B, ClientMutatorContext as C, DIRECT_TRANSACTION_METADATA_KEY as D, MutatorRejectedEvent as M, BoundMutatorApi as a, BoundMutators as b, ClientMutatorDef as c, CollectionMap as d, bindMutators as e, defineMutator as f, MutatorReference as g, initMutators as i };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as l}from"@lunora/errors";import{createTransaction as y}from"@tanstack/db";import{getShardCheckpoints as R,hasCheckpointsAttached as M}from"./CHECKPOINT_FALLBACK_MS-vBJLOBjX.mjs";import{runOutboxMutation as _}from"./OUTBOX_MUTATION_FN_NAME-CebgkYw2.mjs";const w="__tanstack_db_direct",A="defineMutator: `serverRef` must be a generated mutator reference (api.mutators.*) or a 'namespace:fn' string",g=e=>{const t=typeof e=="string"?e:e?.__lunoraRef;if(typeof t!="string"||t.length===0)throw new l("INTERNAL",A);return t},E=e=>({__lunoraClientMutator:!0,apply:e.apply,serverRef:g(e.serverRef)}),I=(e,t,m)=>{let c=0;const p=()=>(c=Math.max(c,e.confirmedMutationWatermark(t.shardKey))+1,c);let u=Promise.resolve();const d=(r,o)=>{const a=u.then(async()=>{for(let s=0;;s+=1){const n=p(),{applied:i}=await e.callMutator(r,o,{clientSeq:n,shardKey:t.shardKey});if(i)return n;if(s>=32)throw new l("INTERNAL",`lunora: custom mutator "${r}" could not claim a fresh client sequence after ${String(32)} attempts`)}});return u=a.then(()=>{},()=>{}),a},h=()=>{if(t.checkpoints===!1)return;if(t.checkpoints)return t.checkpoints;const r=R(e,t.shardKey);return M(r)?r:void 0},f={};for(const[r,o]of Object.entries(m))f[r]=a=>{const s=y({autoCommit:!0,metadata:{[w]:!0,serverRef:o.serverRef},mutationFn:async()=>{let n=0;await _(async()=>{n=await d(o.serverRef,a)});const i=h();i&&(i.acknowledge({mutationId:n}),await i.awaitMutationId(n))}});return s.mutate(()=>{o.apply({collections:t.collections},a)}),s};return f};export{w as DIRECT_TRANSACTION_METADATA_KEY,I as bindMutators,E as defineMutator};
|