@rindle/client 0.3.1 → 0.4.2

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.ts CHANGED
@@ -6,6 +6,9 @@ export * from "./store.ts";
6
6
  export * from "./ssr.ts";
7
7
  export { resolveChange, subRow } from "./resolve.ts";
8
8
  export type { NamedRow, ResolvedChange } from "./resolve.ts";
9
+ export type { KeyedRow, MutationOp, ServerWriteTx } from "./mutation-ops.ts";
10
+ export { driveMutationAsync, driveMutationSync, isGeneratorMutator, isoTx, shared, } from "./mutation-ops.ts";
11
+ export type { ArgSchema, AsyncEffectExec, BatchEffect, IsoTx, MutationGen, MutatorCtx, ReadEffect, SharedMutator, SharedMutatorWithArgs, SyncEffectExec, YieldEffect, } from "./mutation-ops.ts";
9
12
  export { stableKey } from "./key.ts";
10
13
  export { COMPARATOR_VERSION, compareNumber, compareRows, compareString, compareValue } from "./compare.ts";
11
14
  export type { Backend, BackendDevObserver, BackendServerDelta, ChangeEvent, ColType, FlatChange, FlatOp, Mutation, MutationEnvelope, NormalizedEvent, NormalizedOp, NormalizedSource, NormalizedTableSchema, OptimisticSource, PathSeg, ProgressFrame, QueryId, RemoteQuery, ResultType, WireNode, WireProjection, WireRel, WireSchema, WireValue, } from "./types.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3G,YAAY,EACV,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,UAAU,EACV,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,OAAO,EACP,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9F,YAAY,EACV,SAAS,EACT,GAAG,EACH,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAa,GACd,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE7D,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,EACL,MAAM,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,KAAK,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3G,YAAY,EACV,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,UAAU,EACV,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,OAAO,EACP,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9F,YAAY,EACV,SAAS,EACT,GAAG,EACH,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAa,GACd,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  // @rindle/client — the backend-agnostic flat-change client core: typed schema, query builder,
2
2
  // comparator, Backend seam, and the ArrayView contract. (The ArrayView folding impl + the
3
3
  // Store/backend wiring land in later slices.)
4
- export * from "./schema.js"; // table/columns/primaryKey, createSchema/extendSchema, string/number/boolean/json, Col, RowOf, Row, TableDef, tableSpec, SCHEMA
4
+ export * from "./schema.js"; // table/columns/primaryKey, createSchema/extendSchema, refineTable/refineSchema, string/number/boolean/json, Col, RowOf, Row, TableDef, tableSpec, SCHEMA
5
5
  export * from "./operators.js"; // eq/ne/gt/ge/lt/le/like/notLike/ilike/notIlike/inList/notInList/is/isNot, and/or, Pred, Cond, Arg
6
6
  export * from "./query.js"; // Query, QueryRoot, queries/newQueryBuilder, defineQuery/NamedQuery, exists, notExists, defineFragment/Fragment/FragmentRef/isFragment
7
7
  export * from "./view.js"; // ArrayView, SingularArrayView, FlatArrayView, SingularView, ViewTypes
8
8
  export * from "./store.js"; // Store, WriteTx, AssembledNode, DehydratedQuery, DehydratedState
9
9
  export * from "./ssr.js"; // createServerStore, ServerStore, OneShotBackend, OneShotQueryFn, OneShotResult, ServerStoreOptions
10
10
  export { resolveChange, subRow } from "./resolve.js"; // positional FlatChange → named rows (inverse of the wire encoder)
11
+ // shared (generator) mutator seam — one body, sync on the client + async on the server
12
+ export { driveMutationAsync, driveMutationSync, isGeneratorMutator, isoTx, shared, } from "./mutation-ops.js";
11
13
  export { stableKey } from "./key.js"; // canonical viewKey for an AST (shared with @rindle/react)
12
14
  export { COMPARATOR_VERSION, compareNumber, compareRows, compareString, compareValue } from "./compare.js";
13
15
  export { CLIENT_MUTATIONS_SCHEMA, CLIENT_MUTATIONS_TABLE, LMID_QUERY_NAME } from "./types.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0FAA0F;AAC1F,8CAA8C;AAE9C,cAAc,aAAa,CAAC,CAAC,gIAAgI;AAC7J,cAAc,gBAAgB,CAAC,CAAC,mGAAmG;AACnI,cAAc,YAAY,CAAC,CAAC,uIAAuI;AACnK,cAAc,WAAW,CAAC,CAAC,uEAAuE;AAClG,cAAc,YAAY,CAAC,CAAC,kEAAkE;AAC9F,cAAc,UAAU,CAAC,CAAC,oGAAoG;AAE9H,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,mEAAmE;AAGzH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC,CAAC,2DAA2D;AAEjG,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA6B3G,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0FAA0F;AAC1F,8CAA8C;AAE9C,cAAc,aAAa,CAAC,CAAC,0JAA0J;AACvL,cAAc,gBAAgB,CAAC,CAAC,mGAAmG;AACnI,cAAc,YAAY,CAAC,CAAC,uIAAuI;AACnK,cAAc,WAAW,CAAC,CAAC,uEAAuE;AAClG,cAAc,YAAY,CAAC,CAAC,kEAAkE;AAC9F,cAAc,UAAU,CAAC,CAAC,oGAAoG;AAE9H,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,mEAAmE;AAIzH,uFAAuF;AACvF,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,EACL,MAAM,GACP,MAAM,mBAAmB,CAAC;AAe3B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC,CAAC,2DAA2D;AAEjG,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA6B3G,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,140 @@
1
+ import type { WireValue } from "./types.ts";
2
+ /** A keyed row: column name → cell. The ergonomic write shape (validated against the schema at
3
+ * runtime). JSON columns carry their raw JSON string (a {@link WireValue}), never a parsed object. */
4
+ export type KeyedRow = Record<string, WireValue>;
5
+ /** One structured write intent — the discriminated union that mirrors the write half of the client
6
+ * `MutationTx` 1:1. Keyed (column-name addressed), so it is independent of column order. */
7
+ export type MutationOp = {
8
+ kind: "insert";
9
+ table: string;
10
+ row: KeyedRow;
11
+ } | {
12
+ kind: "upsert";
13
+ table: string;
14
+ row: KeyedRow;
15
+ } | {
16
+ kind: "insertIgnore";
17
+ table: string;
18
+ row: KeyedRow;
19
+ } | {
20
+ kind: "update";
21
+ table: string;
22
+ row: KeyedRow;
23
+ } | {
24
+ kind: "delete";
25
+ table: string;
26
+ pk: KeyedRow;
27
+ };
28
+ /** The ASYNC server-side write surface a mutator runs against. Semantically the twin of the client's
29
+ * synchronous `MutationTx` write methods — same names, same keyed-row arguments — but every op is a
30
+ * Promise so a Postgres backend can execute it live against an open transaction (read-your-writes).
31
+ * A rindle/daemon backend resolves writes immediately (accumulate) and serves `row` from committed
32
+ * state. `insert` requires every column; `update`/`delete` require the pk columns. */
33
+ export interface ServerWriteTx {
34
+ /** Insert a FULL row (every column named; missing or unknown columns throw). */
35
+ insert(table: string, row: KeyedRow): Promise<void>;
36
+ /** Update the row identified by the pk columns; only the named non-pk columns change. A missing
37
+ * row is a NO-OP. */
38
+ update(table: string, row: KeyedRow): Promise<void>;
39
+ /** Insert, or fully replace when the pk already exists (a FULL row, like `insert`). */
40
+ upsert(table: string, row: KeyedRow): Promise<void>;
41
+ /** Insert a FULL row, or do nothing if the pk already exists (the SQL twin of the client
42
+ * `if (!tx.row(pk)) tx.insert(row)` pattern). */
43
+ insertIgnore(table: string, row: KeyedRow): Promise<void>;
44
+ /** Delete the row identified by the pk columns. A missing row is a NO-OP. */
45
+ delete(table: string, pk: KeyedRow): Promise<void>;
46
+ /** Read one row by primary key, through the OPEN transaction on both backends
47
+ * (read-your-writes: live on Postgres; via an interactive mutation session on the daemon,
48
+ * DAEMON-INTERACTIVE-TXN-DESIGN.md §5.3). */
49
+ row(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
50
+ }
51
+ /** A point read a generator mutator yields; the driver resolves it and feeds the row back through
52
+ * `gen.next(row)`. Read-your-writes on every tier: the client reads its local engine, and both
53
+ * server backends read the open transaction (the daemon via an interactive mutation session). */
54
+ export type ReadEffect = {
55
+ kind: "row";
56
+ table: string;
57
+ pk: KeyedRow;
58
+ };
59
+ /** A fan-out a generator mutator yields: run several effects "together". The server driver resolves
60
+ * them with `Promise.all`; the client driver runs them in array order (already synchronous). Results
61
+ * return in the SAME order on both tiers, so the mutator stays deterministic. The `yield` evaluates
62
+ * to `(KeyedRow | undefined)[]` at runtime (cast it — the generator's single next-type is a row). */
63
+ export type BatchEffect = {
64
+ kind: "all";
65
+ effects: readonly YieldEffect[];
66
+ };
67
+ /** Everything a generator mutator may `yield`: a write {@link MutationOp}, a {@link ReadEffect}, or a
68
+ * {@link BatchEffect}. */
69
+ export type YieldEffect = MutationOp | ReadEffect | BatchEffect;
70
+ /** The tier-AGNOSTIC effect factory a generator mutator writes against. Every method just BUILDS an
71
+ * effect to `yield` — it performs no I/O and holds no state, so the single {@link isoTx} instance is
72
+ * shared by every mutator on both tiers; only the driver differs. `insert`/`upsert`/`insertIgnore`
73
+ * take a FULL row; `update`/`delete` take the pk columns (`update` adds the columns to change). */
74
+ export interface IsoTx {
75
+ insert(table: string, row: KeyedRow): MutationOp;
76
+ update(table: string, row: KeyedRow): MutationOp;
77
+ upsert(table: string, row: KeyedRow): MutationOp;
78
+ insertIgnore(table: string, row: KeyedRow): MutationOp;
79
+ delete(table: string, pk: KeyedRow): MutationOp;
80
+ row(table: string, pk: KeyedRow): ReadEffect;
81
+ all(effects: readonly YieldEffect[]): BatchEffect;
82
+ }
83
+ /** The one shared effect factory (stateless — see {@link IsoTx}). */
84
+ export declare const isoTx: IsoTx;
85
+ /** The minimal per-invocation context a shared mutator sees on BOTH tiers: the acting principal. The
86
+ * server injects its AUTHENTICATED user; the client injects its local user. (The server's own
87
+ * `MutationContext` is a superset of this.) */
88
+ export interface MutatorCtx {
89
+ user: string;
90
+ }
91
+ /** What a generator mutator IS: `yield tx.<op>()` on every side effect; a `yield tx.row()` expression
92
+ * evaluates to the row. Neither sync nor async — the tier's driver decides, which is what lets one
93
+ * body run synchronously on the client and against a live async transaction on the server. */
94
+ export type MutationGen = Generator<YieldEffect, void, KeyedRow | undefined>;
95
+ /** A generator (isomorphic) mutator, shared verbatim by both tiers: the client trusts typed `args`,
96
+ * the server parses untrusted args into `Args` before invoking. */
97
+ export type SharedMutator<Args, Ctx extends MutatorCtx = MutatorCtx> = (tx: IsoTx, args: Args, ctx: Ctx) => MutationGen;
98
+ /** The minimal arg validator a shared mutator can carry so the SERVER can parse UNTRUSTED wire args
99
+ * before driving it (the client trusts its typed callsites and never calls this). Structural on
100
+ * purpose — a zod schema satisfies it as-is — so `@rindle/client` stays validator-library-agnostic. */
101
+ export interface ArgSchema<Args> {
102
+ parse(raw: unknown): Args;
103
+ }
104
+ /** A shared mutator that CARRIES its own arg validator, co-located at the def site (`shared(schema,
105
+ * gen)`). The client registers it exactly like a bare generator mutator — the `.args` validator is
106
+ * inert there (typed callsites skip the parse); the server ({@link runSharedMutation}, via the
107
+ * api-server's `sharedApiMutators`) reads `.args` to parse untrusted wire args before driving the
108
+ * SAME body. */
109
+ export type SharedMutatorWithArgs<Args, Ctx extends MutatorCtx = MutatorCtx> = SharedMutator<Args, Ctx> & {
110
+ args: ArgSchema<Args>;
111
+ };
112
+ /** Co-locate a shared (generator) mutator with the validator for its args — pairing the arg SHAPE
113
+ * with the body that consumes it at ONE site, so neither tier restates it (the client derives its
114
+ * callsite type from `Args`; the server parses untrusted args through `.args`). Returns the SAME
115
+ * generator function with an `args` property attached (`Object.assign` mutates + returns it), so the
116
+ * registered value is byte-for-byte what the client drove before: {@link isGeneratorMutator} still
117
+ * detects it and it still `satisfies ClientRegistry`. */
118
+ export declare function shared<Args, Ctx extends MutatorCtx = MutatorCtx>(args: ArgSchema<Args>, run: SharedMutator<Args, Ctx>): SharedMutatorWithArgs<Args, Ctx>;
119
+ /** True iff `fn` is a generator function (a shared/isomorphic mutator) rather than a plain function —
120
+ * the driver accepts both forms. Detected structurally (native `GeneratorFunction`). */
121
+ export declare function isGeneratorMutator(fn: unknown): fn is (...args: never[]) => MutationGen;
122
+ /** A tier's SYNCHRONOUS effect executor (the browser wasm engine): apply a write, resolve a read —
123
+ * both immediate. */
124
+ export interface SyncEffectExec {
125
+ apply(op: MutationOp): void;
126
+ read(table: string, pk: KeyedRow): KeyedRow | undefined;
127
+ }
128
+ /** Drive a generator mutator SYNCHRONOUSLY (the client, inside the wasm write transaction). Each
129
+ * yielded write applies immediately; each read is resolved and fed back; a batch runs in order. */
130
+ export declare function driveMutationSync(gen: MutationGen, exec: SyncEffectExec): void;
131
+ /** A tier's ASYNCHRONOUS effect executor (the server transaction): every op is a Promise. */
132
+ export interface AsyncEffectExec {
133
+ apply(op: MutationOp): Promise<void>;
134
+ read(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
135
+ }
136
+ /** Drive a generator mutator ASYNCHRONOUSLY (the server, against the open transaction). Writes are
137
+ * awaited (harmless: a single interactive Postgres connection serializes statements anyway, and the
138
+ * daemon backend resolves them instantly), reads suspend, and a batch fans out with `Promise.all`. */
139
+ export declare function driveMutationAsync(gen: MutationGen, exec: AsyncEffectExec): Promise<void>;
140
+ //# sourceMappingURL=mutation-ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-ops.d.ts","sourceRoot":"","sources":["../src/mutation-ops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ // Isomorphic mutation ops — the backend-agnostic "rindle mutation" vocabulary
2
+ // (MUTATORS-ISOMORPHIC plan). A mutator names WHAT it writes (insert/update/upsert/
3
+ // insertIgnore/delete over a keyed row), not HOW — the server renders dialect SQL and the
4
+ // client applies to its local engine. This lives in `@rindle/client` (the leaf both tiers
5
+ // import); the SQL renderer that consumes it is server-only (`@rindle/api-server`).
6
+ /** The one shared effect factory (stateless — see {@link IsoTx}). */
7
+ export const isoTx = {
8
+ insert: (table, row) => ({ kind: "insert", table, row }),
9
+ update: (table, row) => ({ kind: "update", table, row }),
10
+ upsert: (table, row) => ({ kind: "upsert", table, row }),
11
+ insertIgnore: (table, row) => ({ kind: "insertIgnore", table, row }),
12
+ delete: (table, pk) => ({ kind: "delete", table, pk }),
13
+ row: (table, pk) => ({ kind: "row", table, pk }),
14
+ all: (effects) => ({ kind: "all", effects }),
15
+ };
16
+ /** Co-locate a shared (generator) mutator with the validator for its args — pairing the arg SHAPE
17
+ * with the body that consumes it at ONE site, so neither tier restates it (the client derives its
18
+ * callsite type from `Args`; the server parses untrusted args through `.args`). Returns the SAME
19
+ * generator function with an `args` property attached (`Object.assign` mutates + returns it), so the
20
+ * registered value is byte-for-byte what the client drove before: {@link isGeneratorMutator} still
21
+ * detects it and it still `satisfies ClientRegistry`. */
22
+ export function shared(args, run) {
23
+ return Object.assign(run, { args });
24
+ }
25
+ /** True iff `fn` is a generator function (a shared/isomorphic mutator) rather than a plain function —
26
+ * the driver accepts both forms. Detected structurally (native `GeneratorFunction`). */
27
+ export function isGeneratorMutator(fn) {
28
+ return (typeof fn === "function" &&
29
+ fn.constructor?.name === "GeneratorFunction");
30
+ }
31
+ /** Drive a generator mutator SYNCHRONOUSLY (the client, inside the wasm write transaction). Each
32
+ * yielded write applies immediately; each read is resolved and fed back; a batch runs in order. */
33
+ export function driveMutationSync(gen, exec) {
34
+ const run = (eff) => {
35
+ if (eff.kind === "row")
36
+ return exec.read(eff.table, eff.pk);
37
+ if (eff.kind === "all")
38
+ return eff.effects.map(run);
39
+ exec.apply(eff);
40
+ return undefined;
41
+ };
42
+ for (let step = gen.next(); !step.done; step = gen.next(run(step.value)))
43
+ ;
44
+ }
45
+ /** Drive a generator mutator ASYNCHRONOUSLY (the server, against the open transaction). Writes are
46
+ * awaited (harmless: a single interactive Postgres connection serializes statements anyway, and the
47
+ * daemon backend resolves them instantly), reads suspend, and a batch fans out with `Promise.all`. */
48
+ export async function driveMutationAsync(gen, exec) {
49
+ const run = async (eff) => {
50
+ if (eff.kind === "row")
51
+ return exec.read(eff.table, eff.pk);
52
+ if (eff.kind === "all")
53
+ return (await Promise.all(eff.effects.map(run)));
54
+ await exec.apply(eff);
55
+ return undefined;
56
+ };
57
+ for (let step = gen.next(); !step.done; step = gen.next(await run(step.value)))
58
+ ;
59
+ }
60
+ //# sourceMappingURL=mutation-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-ops.js","sourceRoot":"","sources":["../src/mutation-ops.ts"],"names":[],"mappings":"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"}
package/dist/schema.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { Arg, Cond } from "./operators.ts";
2
2
  import type { ColType } from "./types.ts";
3
- /** A column descriptor. `type` drives the comparator + JSON parsing; `__t` is a phantom. */
3
+ /** A column descriptor. `type` drives the comparator + JSON parsing; `__t` is a phantom. A
4
+ * nullable column is a `Col<T | null>` — that is the ONLY thing nullability changes at the type
5
+ * level, so `RowOf` (and the field-condition factory) widen automatically. */
4
6
  export interface Col<T> {
5
7
  readonly type: ColType;
6
8
  readonly __t?: T;
@@ -10,10 +12,19 @@ export type AnyCols = Record<string, Col<unknown>>;
10
12
  export type RowOf<C extends AnyCols> = {
11
13
  [K in keyof C]: ColT<C[K]>;
12
14
  };
13
- export declare const string: () => Col<string>;
14
- export declare const number: () => Col<number>;
15
- export declare const boolean: () => Col<boolean>;
16
- export declare const json: <T = unknown>() => Col<T>;
15
+ /** The chainable form returned by the column factories: a {@link Col} plus `.nullable()`.
16
+ *
17
+ * `.nullable()` widens the column's value type to `T | null` — its `Row<…>` field becomes
18
+ * `T | null`, and (design 206 Phase 2) it may be omitted from an insert. `rindle schema gen` emits
19
+ * `.nullable()` for every nullable (non-`NOT NULL`) SQL column; you can also call it by hand on a
20
+ * local-only table's columns. It is idempotent and stays chainable. */
21
+ export interface ColBuilder<T> extends Col<T> {
22
+ nullable(): ColBuilder<T | null>;
23
+ }
24
+ export declare const string: <T extends string = string>() => ColBuilder<T>;
25
+ export declare const number: <T extends number = number>() => ColBuilder<T>;
26
+ export declare const boolean: () => ColBuilder<boolean>;
27
+ export declare const json: <T = unknown>() => ColBuilder<T>;
17
28
  /** Metadata key on a {@link TableDef} (a `unique symbol`, so no column name collides). */
18
29
  export declare const SCHEMA: unique symbol;
19
30
  export interface TableMeta<N extends string = string, C extends AnyCols = AnyCols> {
@@ -92,6 +103,43 @@ export declare function createSchema<const T extends readonly AnyTable[]>(opts:
92
103
  export declare function extendSchema<S extends ColsMap, const T extends readonly AnyTable[]>(base: Schema<S>, opts: {
93
104
  tables: T;
94
105
  }): Schema<S & SchemaOf<T>>;
106
+ /** Per-column narrowings for {@link refineTable}: each entry must keep the column's kind and narrow
107
+ * its TS type (`Col<T2>` with `T2 extends T`) — `json<Meta>()` on a json column, a literal-union
108
+ * `string<"a" | "b">()` on a string column. Kind changes are rejected (cross-kind at compile time,
109
+ * same-phantom kind flips like `string()` on a json column at runtime). */
110
+ export type ColRefinements<C extends AnyCols> = {
111
+ readonly [K in keyof C]?: Col<ColT<C[K]>>;
112
+ };
113
+ /** `C` with the columns named in `R` re-typed to their refined `Col`s. */
114
+ export type RefinedCols<C extends AnyCols, R extends ColRefinements<C>> = {
115
+ [K in keyof C]: K extends keyof R ? (R[K] extends Col<unknown> ? R[K] : C[K]) : C[K];
116
+ };
117
+ /** Narrow a generated table's column TYPES without touching its runtime shape.
118
+ *
119
+ * Returns the SAME def, re-typed (identity, after validating that every refined column exists and
120
+ * keeps its kind) — so conditions built from it, `rel(...)`s anchored on it, and `Row<typeof t>`
121
+ * all see the narrowed types. Pass the result to {@link refineSchema} so query roots narrow too. */
122
+ export declare function refineTable<N extends string, C extends AnyCols, R extends ColRefinements<C>>(base: TableDef<N, C>, cols: R): TableDef<N, RefinedCols<C, R>>;
123
+ /** A table def acceptable to {@link refineSchema} over `Schema<S>`: its NAME must be one of `S`'s
124
+ * tables (a def for an unknown table is a compile error at the call site). */
125
+ export type RefinableTable<S extends ColsMap> = {
126
+ readonly [SCHEMA]: TableMeta<Extract<keyof S, string>, AnyCols>;
127
+ };
128
+ /** `S` with each table named in `T` re-typed to that def's (refined) columns. (The inner
129
+ * `extends AnyCols` guard is how the checker proves the remapped `SchemaOf<T>[K]` is a column
130
+ * map while `T` is still generic; it always holds for a concrete `T`.) */
131
+ export type RefinedColsMap<S extends ColsMap, T extends readonly AnyTable[]> = {
132
+ [K in keyof S]: K extends keyof SchemaOf<T> ? (SchemaOf<T>[K] extends AnyCols ? SchemaOf<T>[K] : S[K]) : S[K];
133
+ };
134
+ /** Swap {@link refineTable}-narrowed table defs into a generated schema, re-typing those tables for
135
+ * everything downstream of the schema (`newQueryBuilder`/`queries` roots, store row types).
136
+ *
137
+ * Runtime-validated identity: each def must name a table already in the schema and match its
138
+ * runtime shape exactly (same columns, kinds, primary key, and locality) — refinement narrows TS
139
+ * types, never what's on the wire. Composes with {@link extendSchema} in either order. */
140
+ export declare function refineSchema<S extends ColsMap, const T extends readonly RefinableTable<S>[]>(base: Schema<S>, opts: {
141
+ tables: T;
142
+ }): Schema<RefinedColsMap<S, T>>;
95
143
  /** Whether `table` is a {@link TableMeta.local local-only} table in `schema` (an unknown table
96
144
  * reads as non-local). The single locality predicate the backends key off. */
97
145
  export declare function isLocalTable<S extends ColsMap>(schema: Schema<S>, table: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/schema.js CHANGED
@@ -6,10 +6,11 @@
6
6
  // (WASM-CLIENT-DESIGN.md §6). Schema metadata is stored under a `unique symbol` key so it
7
7
  // never collides with a column name.
8
8
  import { fieldCondition } from "./operators.js";
9
- export const string = () => ({ type: "string" });
10
- export const number = () => ({ type: "number" });
11
- export const boolean = () => ({ type: "boolean" });
12
- export const json = () => ({ type: "json" });
9
+ const makeCol = (type) => ({ type, nullable: () => makeCol(type) });
10
+ export const string = () => makeCol("string");
11
+ export const number = () => makeCol("number");
12
+ export const boolean = () => makeCol("boolean");
13
+ export const json = () => makeCol("json");
13
14
  /** Metadata key on a {@link TableDef} (a `unique symbol`, so no column name collides). */
14
15
  export const SCHEMA = Symbol("rindle.schema");
15
16
  /** `table("issue").columns({ id: string(), … }).primaryKey("id")`. Pass `{ local: true }` for a
@@ -92,6 +93,66 @@ function addTableMeta(tables, m, caller) {
92
93
  }
93
94
  tables[m.name] = m;
94
95
  }
96
+ /** Narrow a generated table's column TYPES without touching its runtime shape.
97
+ *
98
+ * Returns the SAME def, re-typed (identity, after validating that every refined column exists and
99
+ * keeps its kind) — so conditions built from it, `rel(...)`s anchored on it, and `Row<typeof t>`
100
+ * all see the narrowed types. Pass the result to {@link refineSchema} so query roots narrow too. */
101
+ export function refineTable(base, cols) {
102
+ const meta = base[SCHEMA];
103
+ for (const [name, col] of Object.entries(cols)) {
104
+ if (col === undefined)
105
+ continue;
106
+ const baseCol = meta.columns[name];
107
+ if (baseCol === undefined) {
108
+ throw new Error(`refineTable: table "${meta.name}" has no column "${name}".`);
109
+ }
110
+ if (col.type !== baseCol.type) {
111
+ throw new Error(`refineTable: column "${meta.name}.${name}" is ${baseCol.type}, not ${col.type} — a refinement ` +
112
+ `may only narrow the TS type WITHIN a column's kind, never change the kind itself.`);
113
+ }
114
+ }
115
+ return base;
116
+ }
117
+ /** Swap {@link refineTable}-narrowed table defs into a generated schema, re-typing those tables for
118
+ * everything downstream of the schema (`newQueryBuilder`/`queries` roots, store row types).
119
+ *
120
+ * Runtime-validated identity: each def must name a table already in the schema and match its
121
+ * runtime shape exactly (same columns, kinds, primary key, and locality) — refinement narrows TS
122
+ * types, never what's on the wire. Composes with {@link extendSchema} in either order. */
123
+ export function refineSchema(base, opts) {
124
+ const seen = new Set();
125
+ for (const t of opts.tables) {
126
+ const m = t[SCHEMA];
127
+ const baseMeta = base.tables[m.name];
128
+ if (baseMeta === undefined) {
129
+ throw new Error(`refineSchema: schema has no table "${m.name}" — refinement re-types existing tables only ` +
130
+ `(add local-only tables with extendSchema).`);
131
+ }
132
+ if (seen.has(m.name)) {
133
+ throw new Error(`refineSchema: table "${m.name}" is refined twice.`);
134
+ }
135
+ seen.add(m.name);
136
+ assertRefinementMatches(m, baseMeta);
137
+ }
138
+ return base;
139
+ }
140
+ /** The refined def must be the same table the schema already carries — identical column set, kinds,
141
+ * primary key, and locality — so swapping its TYPE in cannot change any runtime behavior. */
142
+ function assertRefinementMatches(m, baseMeta) {
143
+ const cols = Object.keys(m.columns);
144
+ const baseCols = Object.keys(baseMeta.columns);
145
+ const matches = cols.length === baseCols.length &&
146
+ cols.every((c) => baseMeta.columns[c] !== undefined && m.columns[c].type === baseMeta.columns[c].type) &&
147
+ m.primaryKey.length === baseMeta.primaryKey.length &&
148
+ m.primaryKey.every((k, i) => baseMeta.primaryKey[i] === k) &&
149
+ (m.local === true) === (baseMeta.local === true);
150
+ if (!matches) {
151
+ throw new Error(`refineSchema: table "${m.name}" does not match the schema's table of that name — pass the ` +
152
+ `output of refineTable over the SAME generated def (identical columns, kinds, primary key, ` +
153
+ `and locality).`);
154
+ }
155
+ }
95
156
  /** Whether `table` is a {@link TableMeta.local local-only} table in `schema` (an unknown table
96
157
  * reads as non-local). The single locality predicate the backends key off. */
97
158
  export function isLocalTable(schema, table) {
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/ssr.d.ts CHANGED
@@ -68,6 +68,20 @@ export declare class ServerStore<S extends ColsMap> {
68
68
  /** The dehydrated first-paint cache for every preloaded query — embed in the HTML, then
69
69
  * `store.hydrate(...)` it in the browser. */
70
70
  dehydrate(): DehydratedState;
71
+ /**
72
+ * Loader-phase convenience over {@link preload} + {@link dehydrate}: preload EVERY query (reads run
73
+ * concurrently) and return the dehydrated first-paint cache. Composition keeps this to one read per
74
+ * composed root query — no request waterfall (SSR-DESIGN.md §6.2).
75
+ *
76
+ * A failed read degrades to NO seed for that one query — `onError` fires and the browser's live
77
+ * engine fills it in right after hydration — rather than rejecting the whole batch (which would trip
78
+ * the route's error boundary). The seed is a first-paint optimization; the live `subscribe` is the
79
+ * source of truth, so a missing seed never affects correctness. Without `onError` a failed read is
80
+ * swallowed silently — pass one to log.
81
+ */
82
+ preloadAll(queries: Array<Query<any, any, any>>, opts?: {
83
+ onError?: (query: Query<any, any, any>, err: unknown) => void;
84
+ }): Promise<DehydratedState>;
71
85
  }
72
86
  /** Construct a {@link ServerStore} — the one-shot REST Store for server-side rendering. */
73
87
  export declare function createServerStore<S extends ColsMap>(schema: Schema<S>, opts: ServerStoreOptions): ServerStore<S>;
package/dist/ssr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAYA,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvF;;wFAEwF;AACxF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;4EAU4E;AAC5E,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAC3F,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5B;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,OAAO;IAC5C,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IACxE,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IACpC,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAG7C,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC,oGAAoG;IACpG,KAAK,EAAE,cAAc,CAAC;IACtB;;+EAE2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;6DAGyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,OAAO;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAE9B,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB;IAMvD;kFAC8E;IACxE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBzD;kDAC8C;IAC9C,SAAS,IAAI,eAAe;CAG7B;AAED,2FAA2F;AAC3F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,OAAO,EACjD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,IAAI,EAAE,kBAAkB,GACvB,WAAW,CAAC,CAAC,CAAC,CAEhB"}
1
+ {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAYA,OAAO,EAAuB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvF;;wFAEwF;AACxF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;4EAU4E;AAC5E,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAC3F,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5B;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,OAAO;IAC5C,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IACxE,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IACpC,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAG7C,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;CACxE;AAED,MAAM,WAAW,kBAAkB;IACjC,oGAAoG;IACpG,KAAK,EAAE,cAAc,CAAC;IACtB;;+EAE2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;6DAGyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,WAAW,CAAC,CAAC,SAAS,OAAO;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAE9B,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB;IAMvD;kFAC8E;IACxE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBzD;kDAC8C;IAC9C,SAAS,IAAI,eAAe;IAI5B;;;;;;;;;;OAUG;IACG,UAAU,CACd,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EACpC,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;KAAO,GAC3E,OAAO,CAAC,eAAe,CAAC;CAM5B;AAED,2FAA2F;AAC3F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,OAAO,EACjD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,IAAI,EAAE,kBAAkB,GACvB,WAAW,CAAC,CAAC,CAAC,CAEhB"}
package/dist/ssr.js CHANGED
@@ -66,6 +66,21 @@ export class ServerStore {
66
66
  dehydrate() {
67
67
  return this.store.dehydrate();
68
68
  }
69
+ /**
70
+ * Loader-phase convenience over {@link preload} + {@link dehydrate}: preload EVERY query (reads run
71
+ * concurrently) and return the dehydrated first-paint cache. Composition keeps this to one read per
72
+ * composed root query — no request waterfall (SSR-DESIGN.md §6.2).
73
+ *
74
+ * A failed read degrades to NO seed for that one query — `onError` fires and the browser's live
75
+ * engine fills it in right after hydration — rather than rejecting the whole batch (which would trip
76
+ * the route's error boundary). The seed is a first-paint optimization; the live `subscribe` is the
77
+ * source of truth, so a missing seed never affects correctness. Without `onError` a failed read is
78
+ * swallowed silently — pass one to log.
79
+ */
80
+ async preloadAll(queries, opts = {}) {
81
+ await Promise.all(queries.map((query) => this.preload(query).catch((err) => opts.onError?.(query, err))));
82
+ return this.dehydrate();
83
+ }
69
84
  }
70
85
  /** Construct a {@link ServerStore} — the one-shot REST Store for server-side rendering. */
71
86
  export function createServerStore(schema, opts) {
package/dist/ssr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ssr.js","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,gGAAgG;AAChG,EAAE;AACF,+EAA+E;AAC/E,iGAAiG;AACjG,mGAAmG;AACnG,kGAAkG;AAClG,4FAA4F;AAC5F,EAAE;AACF,gGAAgG;AAChG,mCAAmC;AAEnC,OAAO,EAAE,mBAAmB,EAAc,MAAM,YAAY,CAAC;AAE7D,OAAO,EAA4C,KAAK,EAAE,MAAM,YAAY,CAAC;AA0B7E;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IACzB,aAAa,CAAC,IAAa,EAAE,IAAa,EAAE,OAAqB,IAAS,CAAC;IAC3E,eAAe,CAAC,IAAa,IAAS,CAAC;IACvC,MAAM,CAAC,UAAsB;QAC3B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,CAAC,QAAwD,IAAS,CAAC;CAC3E;AAgBD;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACb,KAAK,CAAW;IACR,MAAM,CAAY;IAClB,IAAI,CAAqB;IAE1C,YAAY,MAAiB,EAAE,IAAwB;QACrD,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;kFAC8E;IAC9E,KAAK,CAAC,OAAO,CAAC,KAA2B;QACvC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QACxB,gGAAgG;QAChG,+FAA+F;QAC/F,gGAAgG;QAChG,qDAAqD;QACrD,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,+FAA+F;QAC/F,+FAA+F;QAC/F,uFAAuF;QACvF,qDAAqD;QACrD,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAClG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;YACnC,GAAG;YACH,GAAG,KAAK;YACR,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa;YACtC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IAChE,CAAC;IAED;kDAC8C;IAC9C,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IAChC,CAAC;CACF;AAED,2FAA2F;AAC3F,MAAM,UAAU,iBAAiB,CAC/B,MAAiB,EACjB,IAAwB;IAExB,OAAO,IAAI,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"ssr.js","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,gGAAgG;AAChG,EAAE;AACF,+EAA+E;AAC/E,iGAAiG;AACjG,mGAAmG;AACnG,kGAAkG;AAClG,4FAA4F;AAC5F,EAAE;AACF,gGAAgG;AAChG,mCAAmC;AAEnC,OAAO,EAAE,mBAAmB,EAAc,MAAM,YAAY,CAAC;AAE7D,OAAO,EAA4C,KAAK,EAAE,MAAM,YAAY,CAAC;AA0B7E;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IACzB,aAAa,CAAC,IAAa,EAAE,IAAa,EAAE,OAAqB,IAAS,CAAC;IAC3E,eAAe,CAAC,IAAa,IAAS,CAAC;IACvC,MAAM,CAAC,UAAsB;QAC3B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,CAAC,QAAwD,IAAS,CAAC;CAC3E;AAgBD;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACb,KAAK,CAAW;IACR,MAAM,CAAY;IAClB,IAAI,CAAqB;IAE1C,YAAY,MAAiB,EAAE,IAAwB;QACrD,IAAI,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;kFAC8E;IAC9E,KAAK,CAAC,OAAO,CAAC,KAA2B;QACvC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QACxB,gGAAgG;QAChG,+FAA+F;QAC/F,gGAAgG;QAChG,qDAAqD;QACrD,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,+FAA+F;QAC/F,+FAA+F;QAC/F,uFAAuF;QACvF,qDAAqD;QACrD,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAClG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;YACnC,GAAG;YACH,GAAG,KAAK;YACR,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa;YACtC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IAChE,CAAC;IAED;kDAC8C;IAC9C,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,UAAU,CACd,OAAoC,EACpC,OAA0E,EAAE;QAE5E,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CACvF,CAAC;QACF,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1B,CAAC;CACF;AAED,2FAA2F;AAC3F,MAAM,UAAU,iBAAiB,CAC/B,MAAiB,EACjB,IAAwB;IAExB,OAAO,IAAI,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rindle/client",
3
- "version": "0.3.1",
3
+ "version": "0.4.2",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // comparator, Backend seam, and the ArrayView contract. (The ArrayView folding impl + the
3
3
  // Store/backend wiring land in later slices.)
4
4
 
5
- export * from "./schema.ts"; // table/columns/primaryKey, createSchema/extendSchema, string/number/boolean/json, Col, RowOf, Row, TableDef, tableSpec, SCHEMA
5
+ export * from "./schema.ts"; // table/columns/primaryKey, createSchema/extendSchema, refineTable/refineSchema, string/number/boolean/json, Col, RowOf, Row, TableDef, tableSpec, SCHEMA
6
6
  export * from "./operators.ts"; // eq/ne/gt/ge/lt/le/like/notLike/ilike/notIlike/inList/notInList/is/isNot, and/or, Pred, Cond, Arg
7
7
  export * from "./query.ts"; // Query, QueryRoot, queries/newQueryBuilder, defineQuery/NamedQuery, exists, notExists, defineFragment/Fragment/FragmentRef/isFragment
8
8
  export * from "./view.ts"; // ArrayView, SingularArrayView, FlatArrayView, SingularView, ViewTypes
@@ -12,6 +12,29 @@ export * from "./ssr.ts"; // createServerStore, ServerStore, OneShotBackend, One
12
12
  export { resolveChange, subRow } from "./resolve.ts"; // positional FlatChange → named rows (inverse of the wire encoder)
13
13
  export type { NamedRow, ResolvedChange } from "./resolve.ts";
14
14
 
15
+ export type { KeyedRow, MutationOp, ServerWriteTx } from "./mutation-ops.ts"; // isomorphic mutation op vocabulary
16
+ // shared (generator) mutator seam — one body, sync on the client + async on the server
17
+ export {
18
+ driveMutationAsync,
19
+ driveMutationSync,
20
+ isGeneratorMutator,
21
+ isoTx,
22
+ shared,
23
+ } from "./mutation-ops.ts";
24
+ export type {
25
+ ArgSchema,
26
+ AsyncEffectExec,
27
+ BatchEffect,
28
+ IsoTx,
29
+ MutationGen,
30
+ MutatorCtx,
31
+ ReadEffect,
32
+ SharedMutator,
33
+ SharedMutatorWithArgs,
34
+ SyncEffectExec,
35
+ YieldEffect,
36
+ } from "./mutation-ops.ts";
37
+
15
38
  export { stableKey } from "./key.ts"; // canonical viewKey for an AST (shared with @rindle/react)
16
39
 
17
40
  export { COMPARATOR_VERSION, compareNumber, compareRows, compareString, compareValue } from "./compare.ts";
@@ -0,0 +1,190 @@
1
+ // Isomorphic mutation ops — the backend-agnostic "rindle mutation" vocabulary
2
+ // (MUTATORS-ISOMORPHIC plan). A mutator names WHAT it writes (insert/update/upsert/
3
+ // insertIgnore/delete over a keyed row), not HOW — the server renders dialect SQL and the
4
+ // client applies to its local engine. This lives in `@rindle/client` (the leaf both tiers
5
+ // import); the SQL renderer that consumes it is server-only (`@rindle/api-server`).
6
+
7
+ import type { WireValue } from "./types.ts";
8
+
9
+ /** A keyed row: column name → cell. The ergonomic write shape (validated against the schema at
10
+ * runtime). JSON columns carry their raw JSON string (a {@link WireValue}), never a parsed object. */
11
+ export type KeyedRow = Record<string, WireValue>;
12
+
13
+ /** One structured write intent — the discriminated union that mirrors the write half of the client
14
+ * `MutationTx` 1:1. Keyed (column-name addressed), so it is independent of column order. */
15
+ export type MutationOp =
16
+ | { kind: "insert"; table: string; row: KeyedRow } // a FULL row (every column present)
17
+ | { kind: "upsert"; table: string; row: KeyedRow } // full row; replace non-pk cols on pk conflict
18
+ | { kind: "insertIgnore"; table: string; row: KeyedRow } // full row; do nothing on pk conflict
19
+ | { kind: "update"; table: string; row: KeyedRow } // pk cols + the non-pk cols to change
20
+ | { kind: "delete"; table: string; pk: KeyedRow }; // pk cols only
21
+
22
+ /** The ASYNC server-side write surface a mutator runs against. Semantically the twin of the client's
23
+ * synchronous `MutationTx` write methods — same names, same keyed-row arguments — but every op is a
24
+ * Promise so a Postgres backend can execute it live against an open transaction (read-your-writes).
25
+ * A rindle/daemon backend resolves writes immediately (accumulate) and serves `row` from committed
26
+ * state. `insert` requires every column; `update`/`delete` require the pk columns. */
27
+ export interface ServerWriteTx {
28
+ /** Insert a FULL row (every column named; missing or unknown columns throw). */
29
+ insert(table: string, row: KeyedRow): Promise<void>;
30
+ /** Update the row identified by the pk columns; only the named non-pk columns change. A missing
31
+ * row is a NO-OP. */
32
+ update(table: string, row: KeyedRow): Promise<void>;
33
+ /** Insert, or fully replace when the pk already exists (a FULL row, like `insert`). */
34
+ upsert(table: string, row: KeyedRow): Promise<void>;
35
+ /** Insert a FULL row, or do nothing if the pk already exists (the SQL twin of the client
36
+ * `if (!tx.row(pk)) tx.insert(row)` pattern). */
37
+ insertIgnore(table: string, row: KeyedRow): Promise<void>;
38
+ /** Delete the row identified by the pk columns. A missing row is a NO-OP. */
39
+ delete(table: string, pk: KeyedRow): Promise<void>;
40
+ /** Read one row by primary key, through the OPEN transaction on both backends
41
+ * (read-your-writes: live on Postgres; via an interactive mutation session on the daemon,
42
+ * DAEMON-INTERACTIVE-TXN-DESIGN.md §5.3). */
43
+ row(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
44
+ }
45
+
46
+ // --------------------------------------------------------------------------- shared (generator) mutators
47
+ //
48
+ // The isomorphic mutator seam (MUTATORS-ISOMORPHIC plan §"one body, two tiers"): a mutator is a
49
+ // GENERATOR that `yield`s effects instead of an async/sync function. A generator is neither sync nor
50
+ // async — the tier's DRIVER decides — so ONE body runs synchronously against the browser wasm engine
51
+ // AND against a live async Postgres transaction on the server. Writes are `yield`ed and pipelined by
52
+ // the driver (never individually awaited by the body); a `yield tx.row(...)` is the one thing that
53
+ // suspends, evaluating to the read row. `yield tx.all([...])` fans reads out (Promise.all on the
54
+ // server, in-order on the client) — order-preserving on both tiers, so the body stays deterministic.
55
+
56
+ /** A point read a generator mutator yields; the driver resolves it and feeds the row back through
57
+ * `gen.next(row)`. Read-your-writes on every tier: the client reads its local engine, and both
58
+ * server backends read the open transaction (the daemon via an interactive mutation session). */
59
+ export type ReadEffect = { kind: "row"; table: string; pk: KeyedRow };
60
+
61
+ /** A fan-out a generator mutator yields: run several effects "together". The server driver resolves
62
+ * them with `Promise.all`; the client driver runs them in array order (already synchronous). Results
63
+ * return in the SAME order on both tiers, so the mutator stays deterministic. The `yield` evaluates
64
+ * to `(KeyedRow | undefined)[]` at runtime (cast it — the generator's single next-type is a row). */
65
+ export type BatchEffect = { kind: "all"; effects: readonly YieldEffect[] };
66
+
67
+ /** Everything a generator mutator may `yield`: a write {@link MutationOp}, a {@link ReadEffect}, or a
68
+ * {@link BatchEffect}. */
69
+ export type YieldEffect = MutationOp | ReadEffect | BatchEffect;
70
+
71
+ /** The tier-AGNOSTIC effect factory a generator mutator writes against. Every method just BUILDS an
72
+ * effect to `yield` — it performs no I/O and holds no state, so the single {@link isoTx} instance is
73
+ * shared by every mutator on both tiers; only the driver differs. `insert`/`upsert`/`insertIgnore`
74
+ * take a FULL row; `update`/`delete` take the pk columns (`update` adds the columns to change). */
75
+ export interface IsoTx {
76
+ insert(table: string, row: KeyedRow): MutationOp;
77
+ update(table: string, row: KeyedRow): MutationOp;
78
+ upsert(table: string, row: KeyedRow): MutationOp;
79
+ insertIgnore(table: string, row: KeyedRow): MutationOp;
80
+ delete(table: string, pk: KeyedRow): MutationOp;
81
+ row(table: string, pk: KeyedRow): ReadEffect;
82
+ all(effects: readonly YieldEffect[]): BatchEffect;
83
+ }
84
+
85
+ /** The one shared effect factory (stateless — see {@link IsoTx}). */
86
+ export const isoTx: IsoTx = {
87
+ insert: (table, row) => ({ kind: "insert", table, row }),
88
+ update: (table, row) => ({ kind: "update", table, row }),
89
+ upsert: (table, row) => ({ kind: "upsert", table, row }),
90
+ insertIgnore: (table, row) => ({ kind: "insertIgnore", table, row }),
91
+ delete: (table, pk) => ({ kind: "delete", table, pk }),
92
+ row: (table, pk) => ({ kind: "row", table, pk }),
93
+ all: (effects) => ({ kind: "all", effects }),
94
+ };
95
+
96
+ /** The minimal per-invocation context a shared mutator sees on BOTH tiers: the acting principal. The
97
+ * server injects its AUTHENTICATED user; the client injects its local user. (The server's own
98
+ * `MutationContext` is a superset of this.) */
99
+ export interface MutatorCtx {
100
+ user: string;
101
+ }
102
+
103
+ /** What a generator mutator IS: `yield tx.<op>()` on every side effect; a `yield tx.row()` expression
104
+ * evaluates to the row. Neither sync nor async — the tier's driver decides, which is what lets one
105
+ * body run synchronously on the client and against a live async transaction on the server. */
106
+ export type MutationGen = Generator<YieldEffect, void, KeyedRow | undefined>;
107
+
108
+ /** A generator (isomorphic) mutator, shared verbatim by both tiers: the client trusts typed `args`,
109
+ * the server parses untrusted args into `Args` before invoking. */
110
+ export type SharedMutator<Args, Ctx extends MutatorCtx = MutatorCtx> = (
111
+ tx: IsoTx,
112
+ args: Args,
113
+ ctx: Ctx,
114
+ ) => MutationGen;
115
+
116
+ /** The minimal arg validator a shared mutator can carry so the SERVER can parse UNTRUSTED wire args
117
+ * before driving it (the client trusts its typed callsites and never calls this). Structural on
118
+ * purpose — a zod schema satisfies it as-is — so `@rindle/client` stays validator-library-agnostic. */
119
+ export interface ArgSchema<Args> {
120
+ parse(raw: unknown): Args;
121
+ }
122
+
123
+ /** A shared mutator that CARRIES its own arg validator, co-located at the def site (`shared(schema,
124
+ * gen)`). The client registers it exactly like a bare generator mutator — the `.args` validator is
125
+ * inert there (typed callsites skip the parse); the server ({@link runSharedMutation}, via the
126
+ * api-server's `sharedApiMutators`) reads `.args` to parse untrusted wire args before driving the
127
+ * SAME body. */
128
+ export type SharedMutatorWithArgs<Args, Ctx extends MutatorCtx = MutatorCtx> = SharedMutator<Args, Ctx> & {
129
+ args: ArgSchema<Args>;
130
+ };
131
+
132
+ /** Co-locate a shared (generator) mutator with the validator for its args — pairing the arg SHAPE
133
+ * with the body that consumes it at ONE site, so neither tier restates it (the client derives its
134
+ * callsite type from `Args`; the server parses untrusted args through `.args`). Returns the SAME
135
+ * generator function with an `args` property attached (`Object.assign` mutates + returns it), so the
136
+ * registered value is byte-for-byte what the client drove before: {@link isGeneratorMutator} still
137
+ * detects it and it still `satisfies ClientRegistry`. */
138
+ export function shared<Args, Ctx extends MutatorCtx = MutatorCtx>(
139
+ args: ArgSchema<Args>,
140
+ run: SharedMutator<Args, Ctx>,
141
+ ): SharedMutatorWithArgs<Args, Ctx> {
142
+ return Object.assign(run, { args });
143
+ }
144
+
145
+ /** True iff `fn` is a generator function (a shared/isomorphic mutator) rather than a plain function —
146
+ * the driver accepts both forms. Detected structurally (native `GeneratorFunction`). */
147
+ export function isGeneratorMutator(fn: unknown): fn is (...args: never[]) => MutationGen {
148
+ return (
149
+ typeof fn === "function" &&
150
+ (fn as { constructor?: { name?: string } }).constructor?.name === "GeneratorFunction"
151
+ );
152
+ }
153
+
154
+ /** A tier's SYNCHRONOUS effect executor (the browser wasm engine): apply a write, resolve a read —
155
+ * both immediate. */
156
+ export interface SyncEffectExec {
157
+ apply(op: MutationOp): void;
158
+ read(table: string, pk: KeyedRow): KeyedRow | undefined;
159
+ }
160
+
161
+ /** Drive a generator mutator SYNCHRONOUSLY (the client, inside the wasm write transaction). Each
162
+ * yielded write applies immediately; each read is resolved and fed back; a batch runs in order. */
163
+ export function driveMutationSync(gen: MutationGen, exec: SyncEffectExec): void {
164
+ const run = (eff: YieldEffect): KeyedRow | undefined => {
165
+ if (eff.kind === "row") return exec.read(eff.table, eff.pk);
166
+ if (eff.kind === "all") return eff.effects.map(run) as unknown as KeyedRow | undefined;
167
+ exec.apply(eff);
168
+ return undefined;
169
+ };
170
+ for (let step = gen.next(); !step.done; step = gen.next(run(step.value)));
171
+ }
172
+
173
+ /** A tier's ASYNCHRONOUS effect executor (the server transaction): every op is a Promise. */
174
+ export interface AsyncEffectExec {
175
+ apply(op: MutationOp): Promise<void>;
176
+ read(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
177
+ }
178
+
179
+ /** Drive a generator mutator ASYNCHRONOUSLY (the server, against the open transaction). Writes are
180
+ * awaited (harmless: a single interactive Postgres connection serializes statements anyway, and the
181
+ * daemon backend resolves them instantly), reads suspend, and a batch fans out with `Promise.all`. */
182
+ export async function driveMutationAsync(gen: MutationGen, exec: AsyncEffectExec): Promise<void> {
183
+ const run = async (eff: YieldEffect): Promise<KeyedRow | undefined> => {
184
+ if (eff.kind === "row") return exec.read(eff.table, eff.pk);
185
+ if (eff.kind === "all") return (await Promise.all(eff.effects.map(run))) as unknown as KeyedRow | undefined;
186
+ await exec.apply(eff);
187
+ return undefined;
188
+ };
189
+ for (let step = gen.next(); !step.done; step = gen.next(await run(step.value)));
190
+ }
package/src/schema.ts CHANGED
@@ -10,7 +10,9 @@ import type { Arg, Cond } from "./operators.ts";
10
10
  import { fieldCondition } from "./operators.ts";
11
11
  import type { ColType } from "./types.ts";
12
12
 
13
- /** A column descriptor. `type` drives the comparator + JSON parsing; `__t` is a phantom. */
13
+ /** A column descriptor. `type` drives the comparator + JSON parsing; `__t` is a phantom. A
14
+ * nullable column is a `Col<T | null>` — that is the ONLY thing nullability changes at the type
15
+ * level, so `RowOf` (and the field-condition factory) widen automatically. */
14
16
  export interface Col<T> {
15
17
  readonly type: ColType;
16
18
  readonly __t?: T;
@@ -19,10 +21,21 @@ export type ColT<X> = X extends Col<infer T> ? T : never;
19
21
  export type AnyCols = Record<string, Col<unknown>>;
20
22
  export type RowOf<C extends AnyCols> = { [K in keyof C]: ColT<C[K]> };
21
23
 
22
- export const string = (): Col<string> => ({ type: "string" });
23
- export const number = (): Col<number> => ({ type: "number" });
24
- export const boolean = (): Col<boolean> => ({ type: "boolean" });
25
- export const json = <T = unknown>(): Col<T> => ({ type: "json" });
24
+ /** The chainable form returned by the column factories: a {@link Col} plus `.nullable()`.
25
+ *
26
+ * `.nullable()` widens the column's value type to `T | null` — its `Row<…>` field becomes
27
+ * `T | null`, and (design 206 Phase 2) it may be omitted from an insert. `rindle schema gen` emits
28
+ * `.nullable()` for every nullable (non-`NOT NULL`) SQL column; you can also call it by hand on a
29
+ * local-only table's columns. It is idempotent and stays chainable. */
30
+ export interface ColBuilder<T> extends Col<T> {
31
+ nullable(): ColBuilder<T | null>;
32
+ }
33
+ const makeCol = <T>(type: ColType): ColBuilder<T> => ({ type, nullable: () => makeCol<T | null>(type) });
34
+
35
+ export const string = <T extends string = string>(): ColBuilder<T> => makeCol<T>("string");
36
+ export const number = <T extends number = number>(): ColBuilder<T> => makeCol<T>("number");
37
+ export const boolean = (): ColBuilder<boolean> => makeCol<boolean>("boolean");
38
+ export const json = <T = unknown>(): ColBuilder<T> => makeCol<T>("json");
26
39
 
27
40
  /** Metadata key on a {@link TableDef} (a `unique symbol`, so no column name collides). */
28
41
  export const SCHEMA: unique symbol = Symbol("rindle.schema");
@@ -175,6 +188,118 @@ function addTableMeta(tables: Record<string, TableMeta>, m: TableMeta, caller: "
175
188
  tables[m.name] = m;
176
189
  }
177
190
 
191
+ // ----------------------------- refinement (narrowing generated column types) ---------------------
192
+ //
193
+ // SQL carries a column's KIND (string/number/boolean/json) but not a refinement WITHIN a kind — the
194
+ // element type of `json<T>()`, or a string/number literal union. `refineTable` re-types those
195
+ // columns on a generated `TableDef` (so its field conditions, `rel(...)`s, and `Row<typeof t>` all
196
+ // narrow), and `refineSchema` swaps the refined defs into the generated schema (so query roots and
197
+ // result rows narrow too). Both are runtime-validated IDENTITIES: a refinement narrows the phantom
198
+ // TS type but can never change a column's runtime kind, so the def still matches the daemon's wire
199
+ // schema exactly. Keep refinements in a hand-written module (beside `extendSchema`'s local tables);
200
+ // `schema.gen.ts` stays a pure, wholesale-regenerated artifact.
201
+
202
+ /** Per-column narrowings for {@link refineTable}: each entry must keep the column's kind and narrow
203
+ * its TS type (`Col<T2>` with `T2 extends T`) — `json<Meta>()` on a json column, a literal-union
204
+ * `string<"a" | "b">()` on a string column. Kind changes are rejected (cross-kind at compile time,
205
+ * same-phantom kind flips like `string()` on a json column at runtime). */
206
+ export type ColRefinements<C extends AnyCols> = {
207
+ readonly [K in keyof C]?: Col<ColT<C[K]>>;
208
+ };
209
+
210
+ /** `C` with the columns named in `R` re-typed to their refined `Col`s. */
211
+ export type RefinedCols<C extends AnyCols, R extends ColRefinements<C>> = {
212
+ [K in keyof C]: K extends keyof R ? (R[K] extends Col<unknown> ? R[K] : C[K]) : C[K];
213
+ };
214
+
215
+ /** Narrow a generated table's column TYPES without touching its runtime shape.
216
+ *
217
+ * Returns the SAME def, re-typed (identity, after validating that every refined column exists and
218
+ * keeps its kind) — so conditions built from it, `rel(...)`s anchored on it, and `Row<typeof t>`
219
+ * all see the narrowed types. Pass the result to {@link refineSchema} so query roots narrow too. */
220
+ export function refineTable<N extends string, C extends AnyCols, R extends ColRefinements<C>>(
221
+ base: TableDef<N, C>,
222
+ cols: R,
223
+ ): TableDef<N, RefinedCols<C, R>> {
224
+ const meta = base[SCHEMA];
225
+ for (const [name, col] of Object.entries(cols) as [string, Col<unknown> | undefined][]) {
226
+ if (col === undefined) continue;
227
+ const baseCol = meta.columns[name];
228
+ if (baseCol === undefined) {
229
+ throw new Error(`refineTable: table "${meta.name}" has no column "${name}".`);
230
+ }
231
+ if (col.type !== baseCol.type) {
232
+ throw new Error(
233
+ `refineTable: column "${meta.name}.${name}" is ${baseCol.type}, not ${col.type} — a refinement ` +
234
+ `may only narrow the TS type WITHIN a column's kind, never change the kind itself.`,
235
+ );
236
+ }
237
+ }
238
+ return base as unknown as TableDef<N, RefinedCols<C, R>>;
239
+ }
240
+
241
+ /** A table def acceptable to {@link refineSchema} over `Schema<S>`: its NAME must be one of `S`'s
242
+ * tables (a def for an unknown table is a compile error at the call site). */
243
+ export type RefinableTable<S extends ColsMap> = {
244
+ readonly [SCHEMA]: TableMeta<Extract<keyof S, string>, AnyCols>;
245
+ };
246
+
247
+ /** `S` with each table named in `T` re-typed to that def's (refined) columns. (The inner
248
+ * `extends AnyCols` guard is how the checker proves the remapped `SchemaOf<T>[K]` is a column
249
+ * map while `T` is still generic; it always holds for a concrete `T`.) */
250
+ export type RefinedColsMap<S extends ColsMap, T extends readonly AnyTable[]> = {
251
+ [K in keyof S]: K extends keyof SchemaOf<T> ? (SchemaOf<T>[K] extends AnyCols ? SchemaOf<T>[K] : S[K]) : S[K];
252
+ };
253
+
254
+ /** Swap {@link refineTable}-narrowed table defs into a generated schema, re-typing those tables for
255
+ * everything downstream of the schema (`newQueryBuilder`/`queries` roots, store row types).
256
+ *
257
+ * Runtime-validated identity: each def must name a table already in the schema and match its
258
+ * runtime shape exactly (same columns, kinds, primary key, and locality) — refinement narrows TS
259
+ * types, never what's on the wire. Composes with {@link extendSchema} in either order. */
260
+ export function refineSchema<S extends ColsMap, const T extends readonly RefinableTable<S>[]>(
261
+ base: Schema<S>,
262
+ opts: { tables: T },
263
+ ): Schema<RefinedColsMap<S, T>> {
264
+ const seen = new Set<string>();
265
+ for (const t of opts.tables) {
266
+ const m = t[SCHEMA];
267
+ const baseMeta = base.tables[m.name];
268
+ if (baseMeta === undefined) {
269
+ throw new Error(
270
+ `refineSchema: schema has no table "${m.name}" — refinement re-types existing tables only ` +
271
+ `(add local-only tables with extendSchema).`,
272
+ );
273
+ }
274
+ if (seen.has(m.name)) {
275
+ throw new Error(`refineSchema: table "${m.name}" is refined twice.`);
276
+ }
277
+ seen.add(m.name);
278
+ assertRefinementMatches(m, baseMeta);
279
+ }
280
+ return base as unknown as Schema<RefinedColsMap<S, T>>;
281
+ }
282
+
283
+ /** The refined def must be the same table the schema already carries — identical column set, kinds,
284
+ * primary key, and locality — so swapping its TYPE in cannot change any runtime behavior. */
285
+ function assertRefinementMatches(m: TableMeta, baseMeta: TableMeta): void {
286
+ const cols = Object.keys(m.columns);
287
+ const baseCols = Object.keys(baseMeta.columns);
288
+ const matches =
289
+ cols.length === baseCols.length &&
290
+ cols.every((c) => baseMeta.columns[c] !== undefined && m.columns[c].type === baseMeta.columns[c].type) &&
291
+ m.primaryKey.length === baseMeta.primaryKey.length &&
292
+ m.primaryKey.every((k, i) => baseMeta.primaryKey[i] === k) &&
293
+ (m.local === true) === (baseMeta.local === true);
294
+ if (!matches) {
295
+ throw new Error(
296
+ `refineSchema: table "${m.name}" does not match the schema's table of that name — pass the ` +
297
+ `output of refineTable over the SAME generated def (identical columns, kinds, primary key, ` +
298
+ `and locality).`,
299
+ );
300
+ }
301
+ }
302
+
178
303
  /** Whether `table` is a {@link TableMeta.local local-only} table in `schema` (an unknown table
179
304
  * reads as non-local). The single locality predicate the backends key off. */
180
305
  export function isLocalTable<S extends ColsMap>(schema: Schema<S>, table: string): boolean {
package/src/ssr.ts CHANGED
@@ -111,6 +111,27 @@ export class ServerStore<S extends ColsMap> {
111
111
  dehydrate(): DehydratedState {
112
112
  return this.store.dehydrate();
113
113
  }
114
+
115
+ /**
116
+ * Loader-phase convenience over {@link preload} + {@link dehydrate}: preload EVERY query (reads run
117
+ * concurrently) and return the dehydrated first-paint cache. Composition keeps this to one read per
118
+ * composed root query — no request waterfall (SSR-DESIGN.md §6.2).
119
+ *
120
+ * A failed read degrades to NO seed for that one query — `onError` fires and the browser's live
121
+ * engine fills it in right after hydration — rather than rejecting the whole batch (which would trip
122
+ * the route's error boundary). The seed is a first-paint optimization; the live `subscribe` is the
123
+ * source of truth, so a missing seed never affects correctness. Without `onError` a failed read is
124
+ * swallowed silently — pass one to log.
125
+ */
126
+ async preloadAll(
127
+ queries: Array<Query<any, any, any>>,
128
+ opts: { onError?: (query: Query<any, any, any>, err: unknown) => void } = {},
129
+ ): Promise<DehydratedState> {
130
+ await Promise.all(
131
+ queries.map((query) => this.preload(query).catch((err) => opts.onError?.(query, err))),
132
+ );
133
+ return this.dehydrate();
134
+ }
114
135
  }
115
136
 
116
137
  /** Construct a {@link ServerStore} — the one-shot REST Store for server-side rendering. */