API index and search · Build metadata
@rindle/wasm
0.0.0 · Public export map; development manifest version (0.0.0).
Aggregate
TypeAliasDeclaration · Source: packages/client/src/ast.ts:67 · Supporting declarations
An aggregate over a (correlated) subquery's rows (REDUCE-DESIGN.md). v1: count(*).
Set on a related subquery (Ast.aggregate), it marks that relationship a count
aggregate the builder lowers to a scalar-projected singular relationship (§9).
export type Aggregate = "count";and
FunctionDeclaration · Source: packages/client/src/operators.ts:66 · Supporting declarations
All children must hold.
export declare function and<R>(...conds: Cond<R>[]): Cond<R>;AnyCols
TypeAliasDeclaration · Source: packages/client/src/schema.ts:27 · Supporting declarations
export type AnyCols = Record<string, Col<unknown>>;AnyQuery
TypeAliasDeclaration · Source: packages/client/src/query.ts:308 · Supporting declarations
export type AnyQuery = Query<any, any, any, any, any, any>;AnyRelationship
TypeAliasDeclaration · Source: packages/client/src/schema.ts:469 · Supporting declarations
Any relationship, for positions that only read its correlation / child table.
export type AnyRelationship = Relationship<AnyCols, AnyCols>;AnyTable
TypeAliasDeclaration · Source: packages/client/src/schema.ts:102 · Supporting declarations
export type AnyTable = TableLike<AnyCols>;Arg
TypeAliasDeclaration · Source: packages/client/src/operators.ts:47 · Supporting declarations
A field argument: its typed predicate, or a bare value (bare = eq sugar).
export type Arg<V> = Pred<V> | V;ArgSchema
InterfaceDeclaration · Source: packages/client/src/mutation-ops.ts:161 · Supporting declarations
The minimal arg validator a shared mutator can carry so the SERVER can parse UNTRUSTED wire args
before driving it (the client trusts its typed callsites and never calls this). Structural on
purpose — a zod schema satisfies it as-is — so @rindle/client stays validator-library-agnostic.
export interface ArgSchema<Args> {
parse(raw: unknown): Args;
}ArrayView
InterfaceDeclaration · Source: packages/client/src/view.ts:33 · Supporting declarations
The public ArrayView contract materialize() returns.
export interface ArrayView<R> {
/** The current materialized result (reference-stable where data is unchanged). */
readonly data: readonly R[];
/** The engine query id the Store assigned this view (1:1 with the view). The same `qid` the raw
* change stream ({@link Store.subscribeChanges}) tags each frame with, so a consumer can
* correlate this query with its `ChangeEvent`s (e.g. to bind a narrator) straight off
* `materialize(query).qid` — no separate handle needed. */
readonly qid: QueryId;
/** The query's view `WireSchema` (the position→name source for {@link resolveChange}), captured
* from its `hello` frame. `null` while PENDING — a remote backend's `hello` arrives async; an
* in-process backend (wasm/replica) populates it synchronously during `materialize`. */
readonly schema: WireSchema | null;
/** The query's SERVER-CHANNEL state (`unknown` while loading, `complete` once server-authoritative;
* the `error` variant is reserved and currently unproduced). A pending optimistic mutation no
* longer moves this — it is a separate axis (FOLDED-MUTATIONS-DESIGN §7). `complete` for backends
* with no server lifecycle. Changes notify subscribers. */
readonly resultType: ResultType;
/** Subscribe; fires immediately with the current data, then after each applied batch (and after
* a {@link resultType} change — re-read `resultType` in the listener). */
subscribe(listener: (data: readonly R[]) => void): () => void;
/** Subscribe to this view's folded CHANGE stream — the `FlatChange[]` it applies, NET of no-op
* cycles (a rebase's balanced `remove`+`add` / edit round-trip cancels, so a correctly predicted
* optimistic write delivers nothing here). Carries the diff {@link subscribe} throws away; the
* per-view seam a narrator rides. Does NOT replay on subscribe — attach via
* `store.materialize(query, { onChanges })` to catch a synchronous backend's first snapshot.
* A view with any change listener also enriches its own `remove` ops with the evicted subtree
* (per-view, no global opt-in). Returns a detach function. */
onChanges(listener: ViewChangeListener): () => void;
/** Tear down + stop receiving updates. */
destroy(): void;
}AssembledNode
InterfaceDeclaration · Source: packages/client/src/store.ts:33 · Supporting declarations
A single assembled (nested-by-name) row from POST /query (SSR-DESIGN.md §3.3): the cells
under cols, each in-view relationship inlined by its alias (a nested array / object, or a
scalar for a countAs aggregate). {@link Store.assembleSnapshot} converts these to the
view's projected result shape.
export interface AssembledNode {
cols: Record<string, WireValue>;
[rel: string]: unknown;
}assembleDurableText
FunctionDeclaration · Source: packages/client/src/stream.ts:87 · Supporting declarations
The durable half of the splice for the mapped-table layout: the compacted body followed by
whatever chunk rows have not been folded into it yet.
Chunks are ALWAYS the suffix after body — the closing checkpoint rewrites body and drops the
chunks it absorbed in ONE transaction — so a reader never observes a torn state where a chunk both
is and is not in the body.
export declare function assembleDurableText(message: {
body?: string | null;
} | null | undefined, chunks?: ReadonlyArray<{
seq: number;
text: string;
}>): string;assertNoLocalTables
FunctionDeclaration · Source: packages/client/src/query.ts:845 · Supporting declarations
Throw if ast references a local-only table ANYWHERE — the complete form of the {@link queries}
root guard (Q1 / E3 client half). The root proxy get only sees the table accessed off the
builder root; a local table reached through a relationship / sub / countAs / exists CHILD is
passed as a value and never touches the proxy, so it is caught here instead: at .ast() for a
server-scope builder, and again as an SSR backstop in ServerStore.preload. A server/named query
may never name a local table (201-LOCAL-ONLY-TABLES-DESIGN.md §5).
export declare function assertNoLocalTables<S extends ColsMap>(ast: Ast, schema: Schema<S>): void;Ast
InterfaceDeclaration · Source: packages/client/src/ast.ts:71 · Supporting declarations
The query AST. table is the only required field; the builder omits empty/false fields
(matching the Rust skip_serializing_if), which the deserializer treats as absent.
export interface Ast {
table: string;
alias?: string;
/** Projection (PROJECTION-SUPPORT-DESIGN.md §6). Absent ⇒ select all columns; present ⇒
* project to just these (drives what syncs + what the view reports). Serializes only when
* set, matching the Rust `Ast.select`'s `skip_serializing_if`. */
select?: string[];
where?: Condition;
related?: CorrelatedSubquery[];
start?: Bound;
limit?: number;
one?: boolean;
/** Aggregate this (sub)query's rows instead of materializing them (`REDUCE-DESIGN.md`
* §9). `count` on a `related` subquery surfaces a scalar `commentCount` field; the
* builder lowers it to a scalar-projected singular relationship. Absent ⇒ ordinary rows. */
aggregate?: Aggregate;
/** The {@link Ast.aggregate} value is **precomputed** — supplied as rows of a (synthetic)
* source table rather than reduced from child rows. Set by the normalized client's AST
* rewrite (`AGGREGATE-SYNC-DESIGN.md` §3.3) so the local engine reads the server's count
* with a plain singular join + the same projection instead of a `reduce`. Only meaningful
* with `aggregate`; absent ⇒ `false`. */
aggregatePrecomputed?: boolean;
/** Top-level `GROUP BY` columns (names), meaningful only alongside a root {@link Ast.aggregate}
* (`REDUCE-DESIGN.md` §8). Empty + `aggregate` set ⇒ a **global** aggregate (one `[count]` row);
* non-empty ⇒ one `[group…, count]` row per distinct value-tuple. Distinct from a relationship
* aggregate's implicit grouping (the correlation child key). Absent on the wire ⇒ empty. */
groupBy?: string[];
/** `HAVING` — a filter over the **post-aggregation** rows of a root {@link Ast.aggregate}
* (`REDUCE-DESIGN.md` §4: a filter directly above the `reduce`). Its condition addresses the
* aggregate's *output* columns — the {@link Ast.groupBy} columns and the synthetic `count`
* column — not base-table columns (those go in {@link Ast.where}, which filters rows *below* the
* reduce). Absent ⇒ no post-aggregation filter. */
having?: Condition;
orderBy?: OrderPart[];
}AsyncEffectExec
InterfaceDeclaration · Source: packages/client/src/mutation-ops.ts:247 · Supporting declarations
A tier's ASYNCHRONOUS effect executor (the server transaction): every op is a Promise.
export interface AsyncEffectExec {
apply(op: MutationOp): Promise<void>;
read(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
query(q: QueryArg): Promise<QueryResultRow[]>;
}Backend
InterfaceDeclaration · Source: packages/client/src/types.ts:171 · Supporting declarations
The seam the core talks to. Backend-agnostic: the same interface for the in-process WASM backend and a remote (network) backend. The core never knows which. A backend pushes a per-query {@link ChangeEvent} stream via {@link Backend.onEvent}; the remote backend additionally owns the epoch/seq/gap protocol and emits only clean, in-order events (so the ArrayView never sees the wire protocol).
export interface Backend {
registerQuery(queryId: QueryId, ast: unknown, remote?: RemoteQuery): void;
unregisterQuery(queryId: QueryId): void;
/** Optional split path for local-first backends: retain/release a named remote footprint
* without creating another local materialized view. `localQueryId`, when provided, names
* the local AST view this remote footprint feeds. `ast`, when provided, gives the backend
* the local schema context for sync-only retains that still need aggregate/table setup. */
retainRemoteQuery?(queryId: QueryId, remote: RemoteQuery, localQueryId?: QueryId, ast?: Ast): void;
releaseRemoteQuery?(queryId: QueryId): void;
/** Local: applies now (changes flow back on the stream). Remote: sends to the server. */
mutate(mutations: Mutation[]): Promise<void>;
/** Optional DIRECT-COMMIT path for LOCAL-only tables (`201-LOCAL-ONLY-TABLES-DESIGN.md` §6):
* applies the writes straight to the engine, OUTSIDE the optimistic pending stack (a local
* table is untracked, so it never rebases). The backend rejects any synced/tracked table (M2).
* Backends with no local-table support (a plain remote sync backend) omit it.
*
* `onCommitted` (207 §5.1) runs after the engine commit is applied but BEFORE subscriber
* delivery: a subscriber throwing during delivery re-raises out of this call, and a caller
* that must stay coherent with the engine (the persistence tap) cannot tell that throw from
* a pre-commit rejection — the callback can, because it fires exactly when the commit is in. */
writeLocal?(mutations: Mutation[], onCommitted?: () => void): void;
onEvent(handler: (queryId: QueryId, event: ChangeEvent) => void): void;
/** Optional: the backend pushes per-query {@link ResultType} changes here and the core routes
* each to the matching view (so `view.resultType` tracks it). Backends with no lifecycle (the
* in-process engine) omit it and every view stays `complete`. */
onResultType?(handler: (queryId: QueryId, resultType: ResultType) => void): void;
/** Optional: brackets one commit's coherent multi-query delivery so the Store can fold every
* affected view BEFORE notifying any subscriber — cross-view-atomic notification. The
* in-process engine derives the whole commit's per-query deltas against one consistent post-
* commit snapshot, then dispatches them one query at a time; without a barrier a subscriber on
* the first-dispatched view, re-reading a sibling view in its callback, would observe that
* sibling's PRE-commit state (it has not folded yet). The backend calls the handler with
* `"begin"` before delivering a commit's per-query `batch` events (via {@link onEvent}) and
* `"end"` after the last one; between them the Store folds each view but DEFERS its
* notification, firing every affected view's subscribers together at `"end"`. Backends with no
* synchronous multi-query commit boundary (a plain remote backend, where each query's frame
* arrives in its own message) omit it — the Store then notifies per event, exactly as before. */
onCommitBoundary?(handler: (phase: "begin" | "end") => void): void;
/** Optional dev-only authoritative server stream tap. It is additive, like
* `Store.subscribeChanges`, and must not displace the backend's normal event handler. */
__attachDevtoolsServerDeltas?(observer: BackendDevObserver): () => void;
}BackendDevObserver
InterfaceDeclaration · Source: packages/client/src/types.ts:140 · Supporting declarations
Dev-only passive tap over a backend's authoritative server stream. Optional because in-process backends have no server stream, and older/custom backends may only expose the Store tap.
export interface BackendDevObserver {
onServerDelta?(qid: QueryId, ev: BackendServerDelta): void;
}BackendServerDelta
TypeAliasDeclaration · Source: packages/client/src/types.ts:134 · Supporting declarations
A dev-only authoritative server delta exposed by backends that can distinguish the upstream server stream from their local view/IVM stream. Flat remote backends surface clean {@link ChangeEvent}s; local-first normalized/optimistic backends surface the path-free {@link NormalizedEvent}s before they are folded into the local engine.
export type BackendServerDelta = {
format: "flat";
event: ChangeEvent;
} | {
format: "normalized";
event: NormalizedEvent;
};BatchEffect
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:72 · Supporting declarations
A fan-out a generator mutator yields: run several effects "together". The server driver resolves
them with Promise.all; the client driver runs them in array order (already synchronous). Results
return in the same order on both tiers. Each result has its effect's shape: a row, query rows,
undefined for a write, or an array for a nested batch. Cast the yielded result to that shape;
the generator's declared next-type covers only point reads.
export type BatchEffect = {
kind: "all";
effects: readonly YieldEffect[];
};boolean
VariableDeclaration · Source: packages/client/src/schema.ts:48 · Supporting declarations
export declare const boolean: () => ColBuilder<boolean>;Bound
InterfaceDeclaration · Source: packages/client/src/ast.ts:53 · Supporting declarations
A paging lower bound (Bound, src/ast.rs). row is a partial wire row — the bound
columns by name (only the sort columns are read by the engine's Skip comparator).
exclusive ⇒ start after the bound row (Basis::After); else at it (Basis::At).
export interface Bound {
row: Record<string, LitValue>;
exclusive: boolean;
}CachedQueryView
InterfaceDeclaration · Source: packages/client/src/store.ts:50 · Supporting declarations
export interface CachedQueryView<Q extends Query<any, any, any>> {
readonly view: ReturnType<Q["materialize"]>;
/** Retain this query's named remote footprint and release it later. Ad-hoc local queries
* return a no-op release function. */
retain(query: Q): () => void;
destroy(): void;
}ChangeEvent
TypeAliasDeclaration · Source: packages/client/src/types.ts:79 · Supporting declarations
What a {@link Backend} pushes per query: the handshake (hello), the (possibly
chunked) hydrate snapshot, then incremental batches. The core builds an ArrayView
on hello, hydrates on snapshot, folds on batch — identically for any backend.
export type ChangeEvent = {
type: "hello";
schema: WireSchema;
comparatorVersion: number;
} | {
type: "snapshot";
adds: FlatChange[];
last: boolean;
} | {
type: "batch";
events: FlatChange[];
catchUp?: boolean;
};ChangePhase
TypeAliasDeclaration · Source: packages/client/src/view.ts:23 · Supporting declarations
The phase of an {@link ArrayView.onChanges} delivery: the initial hydrate snapshot vs a later
incremental batch — the same distinction a narrator draws (ChangeEvent snapshot/batch).
export type ChangePhase = "snapshot" | "batch";CLIENT_MUTATIONS_SCHEMA
VariableDeclaration · Source: packages/client/src/types.ts:291 · Supporting declarations
The system table's wire schema — appended to the client's expected schemas so the
lmid query's hello passes CRIT#4 validation.
export declare const CLIENT_MUTATIONS_SCHEMA: NormalizedTableSchema;CLIENT_MUTATIONS_TABLE
VariableDeclaration · Source: packages/client/src/types.ts:282 · Supporting declarations
The replicated bookkeeping table carrying each client's high-water mutation id
(lmid). Engine-hosted and served like any base table; reserved (never part of a
user schema). Columns: [client_id, last_mutation_id], PK client_id.
export declare const CLIENT_MUTATIONS_TABLE = "_rindle_client_mutations";Col
InterfaceDeclaration · Source: packages/client/src/schema.ts:21 · Supporting declarations
A column descriptor. type drives the comparator + JSON parsing; __t is a phantom. A
nullable column is a Col<T | null> — that is the ONLY thing nullability changes at the type
level, so RowOf (and the field-condition factory) widen automatically.
optional is the runtime companion of that phantom: set by {@link ColBuilder.nullable}, it is
what the write funnels read to make a nullable column omittable from an insert (filled with
null, design 206 §6.2). It mirrors the engine's ColumnDef.optional
(pragma_table_info.notnull == 0); absent ⇒ NOT NULL / required.
export interface Col<T> {
readonly type: ColType;
readonly optional?: boolean;
readonly __t?: T;
}ColBuilder
InterfaceDeclaration · Source: packages/client/src/schema.ts:37 · Supporting declarations
The chainable form returned by the column factories: a {@link Col} plus .nullable().
.nullable() widens the column's value type to T | null — its Row<…> field becomes
T | null — and sets the runtime {@link Col.optional} marker so it may be omitted from an insert
(design 206 §6.2). rindle schema gen emits .nullable() for every nullable (non-NOT NULL) SQL
column; you can also call it by hand on a local-only table's columns. It is idempotent and stays
chainable.
export interface ColBuilder<T> extends Col<T> {
nullable(): ColBuilder<T | null>;
}ColRefinements
TypeAliasDeclaration · Source: packages/client/src/schema.ts:303 · Supporting declarations
Per-column narrowings for {@link refineTable}: each entry must keep the column's kind and narrow
its TS type (Col<T2> with T2 extends T) — json<Meta>() on a json column, a literal-union
string<"a" | "b">() on a string column. Kind changes are rejected (cross-kind at compile time,
same-phantom kind flips like string() on a json column at runtime).
export type ColRefinements<C extends AnyCols> = {
readonly [K in keyof C]?: Col<ColT<C[K]>>;
};ColsMap
TypeAliasDeclaration · Source: packages/client/src/schema.ts:199 · Supporting declarations
name → columns, the resolved schema map carried in the {@link Schema} type.
export type ColsMap = Record<string, AnyCols>;ColT
TypeAliasDeclaration · Source: packages/client/src/schema.ts:26 · Supporting declarations
export type ColT<X> = X extends Col<infer T> ? T : never;ColType
TypeAliasDeclaration · Source: packages/client/src/types.ts:16 · Supporting declarations
Declared column type (from the typed schema) — drives the comparator + JSON parsing.
"int64" is the exact-i64 column plane (design 226): the vocabulary exists from
Stage C4 (codegen can name it), but no exact integer cell crosses this wire — and
WireValue gains no bigint — until Stage E lands the browser bigint plane; until
then the daemon refuses IVM queries whose footprint touches such a column.
export type ColType = "string" | "number" | "boolean" | "json" | "int64";COMPARATOR_VERSION
VariableDeclaration · Source: packages/client/src/compare.ts:28 · Supporting declarations
The compare_values / compare_rows algorithm-contract version (=== the engine's
wire_schema::COMPARATOR_VERSION). A remote subscriber hard-rejects a hello whose
comparatorVersion differs — the total order is a code contract, not data, so a schema
fingerprint can't cover it. Bump in lockstep with the Rust constant if the order changes.
v2 (design 226 Stage B): the engine's mixed Int/Float comparison became exact instead
of f64-widening. No TS behavior change — every value a client can hold today is an f64
number, on which v1 and v2 order identically (the §8 gate keeps int64 cells out of
the browser until Stage E) — but the contract the version names is the engine's.
export declare const COMPARATOR_VERSION = 2;compareNumber
FunctionDeclaration · Source: packages/client/src/compare.ts:44 · Supporting declarations
number compare with f64::total_cmp semantics (NaN deterministic & last; -0 < +0).
export declare function compareNumber(a: number, b: number): -1 | 0 | 1;compareRows
FunctionDeclaration · Source: packages/client/src/compare.ts:84 · Supporting declarations
Compare two rows by a resolved sort ([columnIndex, ascending] pairs). First non-equal
column wins; a descending column negates.
export declare function compareRows(a: WireValue[], b: WireValue[], sort: [
number,
boolean
][]): -1 | 0 | 1;compareString
FunctionDeclaration · Source: packages/client/src/compare.ts:52 · Supporting declarations
UTF-8 bytewise string compare (=== SQLite BINARY / Rust &str Ord). Correct for
supplementary-plane code points, where JS < / localeCompare (UTF-16) would disagree.
export declare function compareString(a: string, b: string): -1 | 0 | 1;compareValue
FunctionDeclaration · Source: packages/client/src/compare.ts:63 · Supporting declarations
Compare two bare cells, dispatching on the runtime type (null sorts first).
export declare function compareValue(a: WireValue, b: WireValue): -1 | 0 | 1;Cond
TypeAliasDeclaration · Source: packages/client/src/operators.ts:51 · Supporting declarations
A condition over row R. The runtime value is the wire {@link Condition}; R is a
phantom brand so a condition for one table can't be .where()d onto another.
export type Cond<R> = Condition & {
readonly __row?: R;
};Condition
TypeAliasDeclaration · Source: packages/client/src/ast.ts:39 · Supporting declarations
The filter tree — type-tagged; recursive. There is no generic NOT node (negation is
via the negated operators and NOT EXISTS, matching src/ast.rs Condition).
export type Condition = {
type: "simple";
op: SimpleOp;
left: ValuePosition;
right: ValuePosition;
} | {
type: "and";
conditions: Condition[];
} | {
type: "or";
conditions: Condition[];
} | {
type: "correlatedSubquery";
related: CorrelatedSubquery;
op: ExistsOp;
flip?: boolean;
scalar?: boolean;
};Correlation
InterfaceDeclaration · Source: packages/client/src/ast.ts:45 · Supporting declarations
export interface Correlation {
parentField: string[];
childField: string[];
}createLocalFragmentRef
FunctionDeclaration · Source: packages/client/src/query.ts:610 · Supporting declarations
export declare function createLocalFragmentRef<F extends Fragment<any, any, any>>(fragment: F, pk: Record<string, LitValue>, coverage: FragmentCoverage): LocalFragmentRef<F>;createLocalFragmentRefForTable
FunctionDeclaration · Source: packages/client/src/query.ts:618 · Supporting declarations
export declare function createLocalFragmentRefForTable<F extends Fragment<any, any, any>>(table: string, pk: Record<string, LitValue>, coverage: FragmentCoverage): LocalFragmentRef<F>;createRootFragmentRef
FunctionDeclaration · Source: packages/client/src/query.ts:632 · Supporting declarations
export declare function createRootFragmentRef<F extends Fragment<any, any, any>, Q extends AnyQuery>(fragment: F, query: Q, coverageKey?: string): LocalFragmentRef<F>;createSchema
FunctionDeclaration · Source: packages/client/src/schema.ts:227 · Supporting declarations
export declare function createSchema<const T extends readonly AnyTable[]>(opts: {
tables: T;
}): Schema<SchemaOf<T>, PkMapOf<T>>;createServerStore
FunctionDeclaration · Source: packages/client/src/ssr.ts:140 · Supporting declarations
Construct a {@link ServerStore} — the one-shot REST Store for server-side rendering.
export declare function createServerStore<S extends ColsMap>(schema: Schema<S>, opts: ServerStoreOptions): ServerStore<S>;createWasmStore
FunctionDeclaration · Source: packages/wasm/src/index.ts:276 · Supporting declarations
Convenience: init the wasm + return a ready local {@link Store}.
export declare function createWasmStore<S extends ColsMap>(schema: Schema<S>): Promise<Store<S>>;defineFragment
FunctionDeclaration · Source: packages/client/src/query.ts:577 · Supporting declarations
Define a co-located, composable {@link Fragment}: a named selection over table. The returned
value is callable (the build transform), so it threads through the existing sub/Rels
spine — no GraphQL, no codegen, no schema change (design §3).
const UserAvatar = defineFragment(schema.user, (f) => f.select("id", "name", "avatarUrl"));
const CommentRow = defineFragment(schema.comment, (f) =>
f.select("id", "body", "authorId")
.sub("author", schema.user, { parent: ["authorId"], child: ["id"] }, UserAvatar));
export declare function defineFragment<C extends AnyCols, Rels = {}, Sel extends string = never, LocalRels = Rels>(table: TableLike<C>, build: (q: Query<C>) => Query<C, Rels, boolean, Sel, LocalRels>): Fragment<C, Rels, Sel, LocalRels>;defineMutators
FunctionDeclaration · Source: packages/client/src/mutation-ops.ts:202 · Supporting declarations
export declare function defineMutators<S extends ColsMap, P extends Record<string, string>>(_schema: Schema<S, P>): {
shared<Args, Ctx extends MutatorCtx = MutatorCtx>(args: ArgSchema<Args>, run: (tx: IsoTx<S, P>, args: Args, ctx: Ctx) => MutationGen): SharedMutatorWithArgs<Args, Ctx>;
};defineQuery
FunctionDeclaration · Source: packages/client/src/query.ts:472 · Supporting declarations
Define ONE co-located, named query. Keep it next to the component that reads it (a *.queries.ts
file), so a query lives where its fragments and its component live.
The returned value is callable on the client (it stamps its result with name + args, so the
subscription SYNCS) and registerable on the server ({@link registerQueries }). The optional
validate step turns untrusted wire args into the canonical args type — and that type flows to
both the call signature and build, so the shape is written exactly once. validate runs on
BOTH tiers (it builds the byte-identical authoritative AST on the server, and guards + guarantees
the same AST on the client). If the server must DIVERGE from the client, define a second
defineQuery with the same name for the server and register that one instead.
build may take a second CONTEXT parameter (see {@link QueryBuilder}). Context is off-wire: the
client passes its session ctx at the callsite, the server injects its authoritative ctx — so a
per-user query stays symmetric without ever trusting (or transmitting) a client-supplied identity.
// recentComments({ limit }) — validated, no ctx, byte-identical on both tiers
export const recentCommentsQuery = defineQuery(
"recentComments",
(raw): { limit: number } => ({ limit: validateFeedLimit(raw) }),
({ limit }) => q.comment.orderBy("createdAt", "desc").limit(limit).include(FeedItemFragment),
);
// myIssues({ limit }, ctx) — ctx-scoped; the wire still carries only { limit }
export const myIssuesQuery = defineQuery(
"myIssues",
(raw): { limit: number } => ({ limit: validateLimit(raw) }),
({ limit }, ctx: { user: string }) => q.issue.where.ownerId(ctx.user).limit(limit),
);
// client: myIssuesQuery({ limit: 20 }, { user: currentUser() })
export declare function defineQuery<Q extends AnyQuery>(name: string, build: () => Q, options?: DefineQueryOptions<void>): NamedQuery<void, [
], Q>;
export declare function defineQuery<Args, Ctx extends readonly unknown[], Q extends AnyQuery>(name: string, build: (args: Args, ...ctx: Ctx) => Q, options?: DefineQueryOptions<Args>): NamedQuery<Args, Ctx, Q>;
export declare function defineQuery<Args, Ctx extends readonly unknown[], Q extends AnyQuery>(name: string, validate: QueryValidator<Args>, build: (args: Args, ...ctx: Ctx) => Q, options?: DefineQueryOptions<Args>): NamedQuery<Args, Ctx, Q>;DefineQueryOptions
InterfaceDeclaration · Source: packages/client/src/query.ts:383 · Supporting declarations
Options for {@link defineQuery}.
export interface DefineQueryOptions<Args = any> {
/** Declare this query realtime-eligible — see {@link RealtimeQueryLabel}. */
realtime?: RealtimeQueryLabel<Args>;
}defineRelationships
FunctionDeclaration · Source: packages/client/src/schema.ts:488 · Supporting declarations
A typed registry of named {@link Relationship}s — defineRelationships({ issueOwner: rel(...) }).
A thin identity helper that names the bag and constrains its values; the keys are yours to choose.
export declare function defineRelationships<R extends Record<string, AnyRelationship>>(rels: R): R;DehydratedQuery
InterfaceDeclaration · Source: packages/client/src/store.ts:20 · Supporting declarations
One query's SSR snapshot, keyed by its viewKey ({@link stableKey} of the AST): the
pre-projected first-paint rows plus the cvMin watermark they reflect (SSR-DESIGN.md §6.2).
Serializable as-is into the HTML — rows are already JSON values (json columns parsed).
export interface DehydratedQuery {
rows: unknown[];
cvMin: number;
}DehydratedState
TypeAliasDeclaration · Source: packages/client/src/store.ts:27 · Supporting declarations
The whole dehydrated cache: every preloaded query's snapshot, keyed by viewKey. The server
builds it with {@link Store.dehydrate}; the browser seeds it with {@link Store.hydrate}.
export type DehydratedState = Record<string, DehydratedQuery>;Dir
TypeAliasDeclaration · Source: packages/client/src/ast.ts:5 · Supporting declarations
export type Dir = "asc" | "desc";driveMutationAsync
FunctionDeclaration · Source: packages/client/src/mutation-ops.ts:257 · Supporting declarations
Drive a generator mutator ASYNCHRONOUSLY (the server, against the open transaction). Writes are
awaited (harmless: a single interactive Postgres connection serializes statements anyway, and the
daemon backend resolves them instantly), reads (point or query) suspend, and a batch fans out with
Promise.all.
export declare function driveMutationAsync(gen: MutationGen, exec: AsyncEffectExec): Promise<void>;driveMutationSync
FunctionDeclaration · Source: packages/client/src/mutation-ops.ts:235 · Supporting declarations
Drive a generator mutator SYNCHRONOUSLY (the client, inside the wasm write transaction). Each yielded write applies immediately; each read (point or query) is resolved and fed back; a batch runs in order.
export declare function driveMutationSync(gen: MutationGen, exec: SyncEffectExec): void;EnsureQueryOptions
InterfaceDeclaration · Source: packages/client/src/ensure.ts:22 · Supporting declarations
export interface EnsureQueryOptions {
/** Readiness policy. Defaults to `complete`. */
until?: EnsureQueryUntil;
/** Cancel this caller's wait. The shared query may stay retained for another waiter/prefetch. */
signal?: AbortSignal;
}EnsureQueryUntil
TypeAliasDeclaration · Source: packages/client/src/ensure.ts:20 · Supporting declarations
When an {@link QueryEnsureCache.ensure} call may resolve.
completewaits for the server-authoritative result (the default).presentresolves as soon as the local view contains a result, while the remote retain keeps revalidating in the background. An authoritative empty result also resolves it, so a real not-found query never waits forever.
present deliberately does not add a partial {@link ResultType}: a locally useful answer and
server authority are independent facts. While it resolves early, the view's result type remains
unknown until the server says otherwise.
export type EnsureQueryUntil = "complete" | "present";eq
VariableDeclaration · Source: packages/client/src/operators.ts:26 · Supporting declarations
export declare const eq: <V>(v: V) => Pred<V>;exists
FunctionDeclaration · Source: packages/client/src/query.ts:1210 · Supporting declarations
EXISTS by a named {@link Relationship} — correlation from the rel; the parent row type is the
relationship's parent, so the condition lands on the matching .where().
EXISTS (<correlated subquery>) — a condition (use inside where/or/and).
export declare function exists<PC extends AnyCols, CC extends AnyCols>(relationship: Relationship<PC, CC>, build?: (q: Query<CC>) => Query<CC, unknown>, opts?: ExistsOpts): Cond<RowOf<PC>>;
/** `EXISTS (<correlated subquery>)` — a condition (use inside `where`/`or`/`and`). */
export declare function exists<CC extends AnyCols, R = unknown>(child: TableLike<CC>, corr: {
parent: Array<keyof R & string>;
child: Array<keyof CC & string>;
}, build?: (q: Query<CC>) => Query<CC, unknown>, opts?: ExistsOpts): Cond<R>;existsNoSync
FunctionDeclaration · Source: packages/client/src/query.ts:1255 · Supporting declarations
EXISTS (<correlated subquery>) as a server-only, non-syncing gate (exists_noSync).
Identical to {@link exists} for filtering parent visibility, but stamps the subquery
system: "permissions", so the engine's normalized serializer prunes its witnesses from the
footprint — the permission table's rows are never synced to the client and the client never
re-evaluates the gate. Use this when building the server's query (the user's API server);
the client holds its own un-gated query.
export declare function existsNoSync<PC extends AnyCols, CC extends AnyCols>(relationship: Relationship<PC, CC>, build?: (q: Query<CC>) => Query<CC, unknown>): Cond<RowOf<PC>>;
export declare function existsNoSync<CC extends AnyCols, R = unknown>(child: TableLike<CC>, corr: {
parent: Array<keyof R & string>;
child: Array<keyof CC & string>;
}, build?: (q: Query<CC>) => Query<CC, unknown>): Cond<R>;ExistsOp
TypeAliasDeclaration · Source: packages/client/src/ast.ts:35 · Supporting declarations
export type ExistsOp = "EXISTS" | "NOT EXISTS";ExistsOpts
InterfaceDeclaration · Source: packages/client/src/query.ts:1173 · Supporting declarations
Options for exists / notExists.
export interface ExistsOpts {
/**
* Fold this `EXISTS` as a build-time **scalar** subquery: when the child binds a
* statically-unique key, the engine reads it once, inlines the correlation value as a
* literal, and deletes the join. **Snapshot semantics** — the inlined value does not
* react to later child changes. Defaults to `false` (a live `EXISTS` join).
*/
scalar?: boolean;
}extendSchema
FunctionDeclaration · Source: packages/client/src/schema.ts:245 · Supporting declarations
Extend a generated/synced schema with client-authoritative local-only tables.
This is the ergonomic path for SQL-first apps: keep schema.gen.ts fully generated, define
private UI tables in a tiny hand-written file, then hand the combined schema to the browser
client. The added tables MUST be table(name, { local: true }): extendSchema deliberately
refuses to append ordinary synced tables, because those need to come from daemon introspection
(rindle schema gen) so the server and client cannot drift.
export declare function extendSchema<S extends ColsMap, P extends Record<string, string>, const T extends readonly AnyTable[]>(base: Schema<S, P>, opts: {
tables: T;
}): Schema<S & SchemaOf<T>, P & PkMapOf<T>>;fieldCondition
FunctionDeclaration · Source: packages/client/src/operators.ts:54 · Supporting declarations
Build a simple condition from a field name + (predicate | bare value).
export declare function fieldCondition(field: string, arg: unknown): Condition;FlatArrayView
ClassDeclaration · Source: packages/client/src/view.ts:348 · Supporting declarations
export declare class FlatArrayView<R = unknown> implements ArrayView<R> {
private readonly _qid;
private _schema;
private types?;
private seeded;
private top;
private dirty;
private cached;
private rt;
private readonly listeners;
private readonly changeListeners;
private pendingSegments;
constructor(schema?: WireSchema, types?: ViewTypes, qid?: QueryId);
get qid(): QueryId;
get schema(): WireSchema | null;
get resultType(): ResultType;
/** Set the query's lifecycle state (the Store routes the backend's per-query signal here).
* Notifies subscribers on a change so a status-bound listener (React `useQueryStatus`) re-reads,
* WITHOUT re-projecting data (it is unchanged). */
setResultType(rt: ResultType): void;
/** (Re)bind to a schema and clear the tree IN PLACE. The first `hello` (pending → ready)
* and a re-hydrate (gap → new epoch — FLAT-CHANGES-DESIGN.md §2.3) both go through here, so
* the caller's view reference and its subscribers survive a re-subscribe. Does NOT notify —
* the snapshot that follows (`applyChanges`) does, avoiding an empty-then-filled flicker.
* KEEPS any SSR `seeded` rows: they are retired only when the first live snapshot lands
* ({@link retireSeed}, driven by the Store), so `data` shows the seed — not an empty tree —
* across the whole `hello`→first-`snapshot` gap. */
reset(schema: WireSchema, types?: ViewTypes): void;
/** Install a pre-projected SSR first-paint snapshot (SSR-DESIGN.md §6). The rows are already
* in result shape (json columns parsed, relationships nested), so a view with no live backend
* (the server one-shot Store) reads them directly, and a browser view shows them until its
* first live snapshot lands ({@link retireSeed}). Does NOT notify — it is set at materialize
* time, before any subscriber, and the live snapshot that follows notifies. */
seed(rows: readonly R[]): void;
/** Retire the SSR first-paint seed — the Store calls this as it folds the maintained tree's first
* live snapshot, so `data` switches from the seed to the live tree with no empty gap between them
* (the seed deliberately survived the earlier `reset`/`hello`). Idempotent. Does NOT notify — the
* snapshot fold it accompanies does; BUT when that fold is empty (a 0-row result, or rows already
* in `top`) it notifies nothing, so the Store forces a {@link notify} on the strength of the `true`
* return here — else the view reads the live tree yet never re-renders (a frozen seed). Returns
* whether a live seed was actually cleared (so the Store knows a forced notify is owed). */
retireSeed(): boolean;
/** Apply a batch (the hydrate snapshot or one transaction's events) in order, then
* notify subscribers once. Order is significant (FLAT-CHANGES-DESIGN.md §5.4). A no-op
* while pending (changes never precede the `hello` that resets the schema).
*
* `enrichRemoves` ⇒ before a removed node is dropped, reconstruct its full subtree and attach it
* to the `remove` op's `node` (in place, so the same event object the Store fans out to its
* change subscribers carries it). Off by default — paid only when a consumer asked for it, and
* only on a real eviction (an rc-decrement that keeps the row leaves `node` absent). A view with
* an attached {@link onChanges} listener ALSO enriches (per-view, no global opt-in), so a
* narrator can resolve a removed row's subs whether or not the store-global counter is set.
*
* `deferNotify` ⇒ fold but do NOT notify; the caller is responsible for calling {@link flush}
* later. The Store uses this to fold every view in one commit before notifying any subscriber
* (cross-view-atomic notification — `Store.onCommitBoundary`); standalone use leaves it off, so
* a bare view still fires its subscribers after each applied batch.
*
* `phase` tags the {@link onChanges} delivery (`snapshot` for the hydrate, `batch` otherwise); it
* does not affect the fold. Returns whether the batch changed the view (so a deferring caller
* knows it must be flushed). */
applyChanges(events: FlatChange[], enrichRemoves?: boolean, deferNotify?: boolean, phase?: ChangePhase): boolean;
/** Notify subscribers with the current data. The deferred half of {@link applyChanges} (when
* `deferNotify` was set): the Store calls this at the commit-notify barrier — after every view
* touched by the same commit has folded — so a subscriber that re-reads a sibling view inside
* its callback observes post-commit data, never a torn mid-commit state. */
flush(): void;
get data(): readonly R[];
subscribe(listener: (data: readonly R[]) => void): () => void;
onChanges(listener: ViewChangeListener): () => void;
/** Net the folded batch and hand the survivors to the change listeners, AFTER the data `notify`
* (the order `Store.subscribeChanges` consumers already observe). A batch that nets to nothing —
* a correctly predicted rebase — makes no call, so a narrator sees only real change. Each listener
* is isolated: a throwing one reports to `note` (the caller re-raises the first) and the rest still
* run, so one bad narration template never starves a sibling listener nor corrupts the view. */
private deliverChanges;
destroy(): void;
private applyAt;
private applyOp;
private applyAdd;
/** Drop one path to `row`. Returns the evicted {@link Node} (its full subtree intact) when the
* last path went away — `null` when another path still holds it (rc decremented, nothing left
* the result). */
private applyRemove;
private applyEdit;
private project;
/** Notify data subscribers with the current {@link data}. Normally driven by a fold ({@link
* applyChanges}/{@link flush}); the Store also calls it directly to land a seed retirement whose
* accompanying fold was empty (see {@link retireSeed}). */
notify(): void;
}FlatChange
InterfaceDeclaration · Source: packages/client/src/types.ts:69 · Supporting declarations
export interface FlatChange {
path: PathSeg[];
op: FlatOp;
}FlatOp
TypeAliasDeclaration · Source: packages/client/src/types.ts:64 · Supporting declarations
A positional change at one level of the view tree. A remove ships only the leaving row
(the receiver already holds the subtree and locates it by key) — node is NEVER on the wire;
it is an OPTIONAL client-side enrichment carrying the full removed subtree, attached by the
ArrayView when a change consumer opts in (Store.subscribeChanges(_, { removedSubtree: true })),
so a narrator can resolve a removed row's nested subs just as it can on an add.
export type FlatOp = {
tag: "add";
node: WireNode;
} | {
tag: "remove";
row: WireValue[];
node?: WireNode;
} | {
tag: "edit";
old: WireValue[];
new: WireValue[];
};Fragment
InterfaceDeclaration · Source: packages/client/src/query.ts:533 · Supporting declarations
A reusable, typed selection over a table — Relay's "fragment", promoted to a first-class value. Two verbs compose a fragment, on one axis — does its data belong to THIS row or to a related one:
- SAME node —
q.include(Frag): merge its selection into this node. This is also how you root a fragment onto a base query —queries.t.where.id(x).include(Frag). - CHILD node —
q.sub(alias, child, corr, Frag): nest it under a relationship (sub's 4th arg).
A Fragment is therefore also a build transform ((q: Query<C>) => Query<C, Rels>); that
call signature is the mechanism by which sub accepts a fragment as its build. Prefer
include to root a fragment (canonical + merged) over calling it directly. It additionally
carries its table (for {@link FragmentRef}/useFragment typing and masking). Composing
fragments assembles ONE {@link Ast} → one materialization → one /query — the whole point
(no request waterfall; design §1).
export interface Fragment<C extends AnyCols, Rels = {}, Sel extends string = never, LocalRels = Rels> {
(q: Query<C>): Query<C, Rels, false, Sel, LocalRels>;
readonly table: TableLike<C>;
readonly [FRAGMENT_BRAND]: true;
}fragmentAst
FunctionDeclaration · Source: packages/client/src/query.ts:651 · Supporting declarations
export declare function fragmentAst<F extends Fragment<any, any, any>>(fragment: F): Ast;FragmentCoverage
InterfaceDeclaration · Source: packages/client/src/query.ts:548 · Supporting declarations
export interface FragmentCoverage<Q extends AnyQuery = AnyQuery> {
readonly key: string;
readonly query: Q;
}FragmentData
TypeAliasDeclaration · Source: packages/client/src/query.ts:544 · Supporting declarations
The data returned by useFragment(fragment, ref): the fragment's own selected columns plus
immediate relationship values. Relationships whose builder is another fragment surface as
opaque {@link FragmentRef}s, so child-owned payload is read only by the child fragment reader.
export type FragmentData<F> = F extends Fragment<infer C, unknown, infer Sel, infer LocalRels> ? Projected<C, Sel> & LocalRels : never;fragmentKey
FunctionDeclaration · Source: packages/client/src/query.ts:605 · Supporting declarations
export declare function fragmentKey(ref: FragmentRef<any>): string;FragmentRef
TypeAliasDeclaration · Source: packages/client/src/query.ts:563 · Supporting declarations
The opaque token a parent passes to a component that reads {@link FragmentData} for F.
export type FragmentRef<F> = F extends Fragment<any, any, any, any> ? LocalFragmentRef<F> : never;frameResumePoint
FunctionDeclaration · Source: packages/client/src/stream.ts:51 · Supporting declarations
The resume point a frame implies — what rides an SSE id: line so a reconnecting EventSource
hands it straight back as Last-Event-ID. undefined for frames that are not a position.
export declare function frameResumePoint(frame: StreamFrame): number | undefined;ge
VariableDeclaration · Source: packages/client/src/operators.ts:29 · Supporting declarations
export declare const ge: <V extends number | string>(v: V) => Pred<V>;gt
VariableDeclaration · Source: packages/client/src/operators.ts:28 · Supporting declarations
export declare const gt: <V extends number | string>(v: V) => Pred<V>;ilike
VariableDeclaration · Source: packages/client/src/operators.ts:34 · Supporting declarations
export declare const ilike: (pattern: string) => Pred<string>;initWasm
FunctionDeclaration · Source: packages/wasm/src/index.ts:67 · Supporting declarations
Initialize the wasm module (idempotent). Call once at startup before new WasmBackend
/ createWasmStore. Browser/bundler: no args (the wasm is fetched). Node: the bytes are
read from the package. Pass moduleOrPath to override (a WebAssembly.Module, URL, or bytes).
export declare function initWasm(moduleOrPath?: unknown): Promise<void>;inList
VariableDeclaration · Source: packages/client/src/operators.ts:36 · Supporting declarations
export declare const inList: <V>(values: V[]) => Pred<V>;Insert
TypeAliasDeclaration · Source: packages/client/src/schema.ts:136 · Supporting declarations
The insert type of a table def: Insert<typeof issue> → { id: string; … assignee?: string | null }.
The whole-table ergonomic form of {@link InsertOf} (the insert-side twin of {@link Row}).
export type Insert<T extends AnyTable> = InsertOf<T[typeof SCHEMA]["columns"]>;insertCell
FunctionDeclaration · Source: packages/client/src/schema.ts:528 · Supporting declarations
The cell a full insert writes for column c: the given value, or null when a nullable column
is omitted (design 206 §6.2). The caller's completeness check ({@link InsertPlan.required})
guarantees a non-nullable column is present, so its omission never reaches here.
export declare function insertCell(row: Record<string, WireValue>, c: string): WireValue;InsertOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:128 · Supporting declarations
The INSERT shape of a column map: {@link RowOf} with every NULLABLE column made OPTIONAL (?) —
it may be omitted and is filled with null by both write funnels (design 206 §6.2/§7). NOT NULL
columns stay required. The insert-side twin of {@link RowOf}, mirroring Drizzle's $inferInsert
vs $inferSelect split.
export type InsertOf<C extends AnyCols> = Simplify<{
[K in keyof C as InsertOptional<C[K]> extends true ? never : K]: ColT<C[K]>;
} & {
[K in keyof C as InsertOptional<C[K]> extends true ? K : never]?: ColT<C[K]>;
}>;insertPlan
FunctionDeclaration · Source: packages/client/src/schema.ts:519 · Supporting declarations
Derive a table's {@link InsertPlan} from its column markers.
export declare function insertPlan(meta: TableMeta): InsertPlan;InsertPlan
InterfaceDeclaration · Source: packages/client/src/schema.ts:509 · Supporting declarations
A table's insert-completeness plan, derived once from its Col markers and shared by BOTH write
funnels — the client trackingTx and the server renderOp — so their required-sets can't drift
(design 206 §6.1/§6.2). A NOT NULL column is required; a nullable column (.nullable() set
Col.optional) may be omitted and is filled with null (see {@link insertCell}). PK columns are
never nullable (introspection forces them non-null), so they are always required.
export interface InsertPlan {
/** Every column, in wire order. */
readonly columns: string[];
/** Columns that MUST be present on a full insert — the non-nullable ones. */
readonly required: string[];
/** The nullable (omittable-to-null) columns, for a fast membership test in the fill. */
readonly nullable: ReadonlySet<string>;
}int64
VariableDeclaration · Source: packages/client/src/schema.ts:54 · Supporting declarations
The exact-i64 column plane (design 226, BIGINT/INT8 decltype): typed bigint in
application code. The vocabulary exists from Stage C4 so generated schemas can name it;
no exact integer cell enters the browser IVM until Stage E — until then the daemon
refuses IVM queries whose footprint touches the column, and the SQL plane carries it.
export declare const int64: <T extends bigint = bigint>() => ColBuilder<T>;is
VariableDeclaration · Source: packages/client/src/operators.ts:38 · Supporting declarations
export declare const is: <V>(v: V) => Pred<V>;isFragment
FunctionDeclaration · Source: packages/client/src/query.ts:586 · Supporting declarations
Runtime guard: is v a {@link Fragment} (a defineFragment value, not a plain build fn)?
export declare function isFragment(v: unknown): v is Fragment<AnyCols, unknown, never, unknown>;isFragmentRelationship
FunctionDeclaration · Source: packages/client/src/query.ts:601 · Supporting declarations
export declare function isFragmentRelationship(rel: CorrelatedSubquery): boolean;isGeneratorMutator
FunctionDeclaration · Source: packages/client/src/mutation-ops.ts:217 · Supporting declarations
True iff fn is a generator function (a shared/isomorphic mutator) rather than a plain function —
the driver accepts both forms. Detected structurally (native GeneratorFunction).
export declare function isGeneratorMutator(fn: unknown): fn is (...args: never[]) => MutationGen;isLocalTable
FunctionDeclaration · Source: packages/client/src/schema.ts:406 · Supporting declarations
Whether table is a {@link TableMeta.locallocal-only} table in schema (an unknown table
reads as non-local). The single locality predicate the backends key off. BOTH variants —
true and "session" — are local here; the persisted/ephemeral split matters only to the
persistence plane ({@link persistedLocalTableNames}).
export declare function isLocalTable<S extends ColsMap>(schema: Schema<S>, table: string): boolean;isNot
VariableDeclaration · Source: packages/client/src/operators.ts:39 · Supporting declarations
export declare const isNot: <V>(v: V) => Pred<V>;isNotNull
VariableDeclaration · Source: packages/client/src/operators.ts:44 · Supporting declarations
IS NOT NULL — the negation of {@link isNull}; V is inferred from the field, like {@link isNull}.
export declare const isNotNull: <V = never>() => Pred<V>;isNull
VariableDeclaration · Source: packages/client/src/operators.ts:42 · Supporting declarations
IS NULL — a field-agnostic null check. V is inferred from the field's expected Arg<V>
(e.g. where.signedAt(isNull())), so it fits a column of any type without a cast.
export declare const isNull: <V = never>() => Pred<V>;isoTx
VariableDeclaration · Source: packages/client/src/mutation-ops.ts:127 · Supporting declarations
The one shared effect factory (stateless — see {@link IsoTx}). Its methods just BUILD a
{@link MutationOp}, so the single instance serves every schema; the generic {@link IsoTx} view is
applied at the authoring site (a json<T> cell is a parsed object here and is stringified by the
funnels, {@link toCell }), hence the cast — the runtime shape is schema-agnostic.
export declare const isoTx: IsoTx;IsoTx
InterfaceDeclaration · Source: packages/client/src/mutation-ops.ts:102 · Supporting declarations
The tier-AGNOSTIC effect factory a generator mutator writes against. Every method just BUILDS an
effect to yield — it performs no I/O and holds no state, so the single {@link isoTx} instance is
shared by every mutator on both tiers; only the driver differs. insert/upsert/insertIgnore
require non-nullable columns and permit nullable omissions, which become null.
update/delete require the PK columns; update also names the columns to change.
export interface IsoTx<S extends ColsMap = ColsMap, P extends Record<string, string> = PkMap<S>> {
/** Insert a row. Omitted nullable columns become `null`; database defaults are not applied. */
insert<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): MutationOp;
/** Update the row identified by its pk columns (REQUIRED); only the named non-pk columns change. */
update<N extends keyof S & string>(table: N, row: UpdateOf<S[N], PkColsOf<S, P, N>>): MutationOp;
/** Insert, or replace non-PK columns on PK conflict, with {@link IsoTx.insert}'s omission rules. */
upsert<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): MutationOp;
/** Insert with {@link IsoTx.insert}'s omission rules, or do nothing on PK conflict. */
insertIgnore<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): MutationOp;
/** Delete the row identified by its pk columns. */
delete<N extends keyof S & string>(table: N, pk: PkOf<S[N], PkColsOf<S, P, N>>): MutationOp;
/** Read one row by primary key (read-your-writes). */
row<N extends keyof S & string>(table: N, pk: PkOf<S[N], PkColsOf<S, P, N>>): ReadEffect;
/** Run a full query (`where`/`orderBy`/`limit`/join) over the state this mutator is mutating —
* read-your-writes, like {@link row} but for arbitrary shapes. Pass a query from the tier-agnostic
* builder, e.g. `tx.query(q.issue.where("ownerId", "=", ctx.user))` where `q = newQueryBuilder(schema)`.
* The `yield` evaluates to {@link QueryResultRow}`[]` (cast it — the generator's single next-type is a row). */
query(query: QueryArg): QueryEffect;
all(effects: readonly YieldEffect[]): BatchEffect;
}IsoTxOf
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:200 · Supporting declarations
The typed {@link IsoTx} for a given schema — IsoTxOf<typeof schema>. Use it to annotate a helper
that a mutator body passes its tx to (e.g. const ensureUser = (tx: IsoTxOf<typeof schema>, …)),
so the helper gets the same table/column/pk typing the defineMutators shared callback does.
/** The typed {@link IsoTx} for a given schema — `IsoTxOf<typeof schema>`. Use it to annotate a helper
* that a mutator body passes its `tx` to (e.g. `const ensureUser = (tx: IsoTxOf<typeof schema>, …)`),
* so the helper gets the same table/column/pk typing the `defineMutators` `shared` callback does. */
export type IsoTxOf<Sch extends Schema> = Sch extends Schema<infer S, infer P> ? IsoTx<S, P> : never;isPred
FunctionDeclaration · Source: packages/client/src/operators.ts:22 · Supporting declarations
export declare function isPred(x: unknown): x is Pred<unknown>;isRelationship
FunctionDeclaration · Source: packages/client/src/schema.ts:493 · Supporting declarations
Runtime guard: is v a {@link Relationship} value (not a table or a plain object)?
export declare function isRelationship(v: unknown): v is AnyRelationship;isReservedTableName
FunctionDeclaration · Source: packages/client/src/schema.ts:223 · Supporting declarations
Whether name collides with an engine-reserved synthetic prefix ({@link RESERVED_TABLE_PREFIXES}).
export declare function isReservedTableName(name: string): boolean;json
VariableDeclaration · Source: packages/client/src/schema.ts:49 · Supporting declarations
export declare const json: <T = unknown>() => ColBuilder<T>;KeyedRow
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:13 · Supporting declarations
A keyed row: column name → cell. The ergonomic write shape (validated against the schema at runtime). JSON columns carry their raw JSON string (a {@link WireValue}), never a parsed object.
export type KeyedRow = Record<string, WireValue>;le
VariableDeclaration · Source: packages/client/src/operators.ts:31 · Supporting declarations
export declare const le: <V extends number | string>(v: V) => Pred<V>;like
VariableDeclaration · Source: packages/client/src/operators.ts:32 · Supporting declarations
export declare const like: (pattern: string) => Pred<string>;LitValue
TypeAliasDeclaration · Source: packages/client/src/ast.ts:11 · Supporting declarations
An untagged literal (null | bool | number | string | array). Numbers are one f64.
export type LitValue = null | boolean | number | string | LitValue[];LMID_QUERY_NAME
VariableDeclaration · Source: packages/client/src/types.ts:287 · Supporting declarations
The reserved server-query name every optimistic client subscribes at connect: the
one-row system query CLIENT_MUTATIONS_TABLE WHERE client_id = <me>. The server
derives the identity from the connection (args are ignored).
export declare const LMID_QUERY_NAME = "_rindle/clientLmid";localFragmentReadAst
FunctionDeclaration · Source: packages/client/src/query.ts:655 · Supporting declarations
export declare function localFragmentReadAst<F extends Fragment<any, any, any>>(fragment: F, ref: LocalFragmentRef<F>, primaryKeyFor: (table: string) => readonly string[]): Ast;LocalFragmentRef
InterfaceDeclaration · Source: packages/client/src/query.ts:553 · Supporting declarations
export interface LocalFragmentRef<F extends Fragment<any, any, any, any> = Fragment<any, any, any, any>> {
readonly [LOCAL_FRAGMENT_REF_BRAND]: true;
readonly source: "local";
readonly table: string;
readonly pk: Readonly<Record<string, LitValue>>;
readonly coverage: FragmentCoverage;
readonly __fragment?: F;
}localQueryReadAst
FunctionDeclaration · Source: packages/client/src/query.ts:687 · Supporting declarations
export declare function localQueryReadAst(query: AnyQuery, primaryKeyFor: (table: string) => readonly string[]): Ast;localRootFragmentRefsAst
FunctionDeclaration · Source: packages/client/src/query.ts:670 · Supporting declarations
export declare function localRootFragmentRefsAst<F extends Fragment<any, any, any>>(fragment: F, query: AnyQuery, primaryKeyFor: (table: string) => readonly string[]): Ast;localSchemaHash
FunctionDeclaration · Source: packages/client/src/schema.ts:430 · Supporting declarations
A stable fingerprint of the schema's PERSISTED local tables only — the persistence gate's
schemaHash (207-LOCAL-TABLE-PERSISTENCE-DESIGN.md §3.3 / P7). Per local: true table:
(name, ordered column names + types + optionality, pk columns). Column order is kept (rows are
positional); tables are sorted by name so registration order can't skew it; synced-table AND
local: "session" changes never move it (reshaping an ephemeral table must not wipe durable
data). The value is the canonical descriptor itself, not a digest — local-table sets are tiny,
and exactness (no collision can ever skip a P7 clear) beats compactness here.
export declare function localSchemaHash<S extends ColsMap>(schema: Schema<S>): string;localTableNames
FunctionDeclaration · Source: packages/client/src/schema.ts:412 · Supporting declarations
The set of local-only table names in schema (201-LOCAL-ONLY-TABLES-DESIGN.md §4) — BOTH
variants (true and "session"); every locality rule except persistence keys off this set.
export declare function localTableNames<S extends ColsMap>(schema: Schema<S>): Set<string>;lt
VariableDeclaration · Source: packages/client/src/operators.ts:30 · Supporting declarations
export declare const lt: <V extends number | string>(v: V) => Pred<V>;Mutation
TypeAliasDeclaration · Source: packages/client/src/types.ts:89 · Supporting declarations
export type Mutation = {
op: "add";
table: string;
row: WireValue[];
} | {
op: "remove";
table: string;
row: WireValue[];
} | {
op: "edit";
table: string;
old: WireValue[];
new: WireValue[];
};MutationEnvelope
InterfaceDeclaration · Source: packages/client/src/types.ts:236 · Supporting declarations
The upstream named-mutator envelope (OPTIMISTIC-WRITES-DESIGN.md §8.1): the wire
carries the name + JSON args, never code; mid totally orders a client's mutations.
export interface MutationEnvelope {
clientID: string;
mid: number;
name: string;
args: unknown;
}MutationGen
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:148 · Supporting declarations
What a generator mutator IS: yield tx.<op>() on every side effect; a yield tx.row() expression
evaluates to the row. Neither sync nor async — the tier's driver decides, which is what lets one
body run synchronously on the client and against a live async transaction on the server.
export type MutationGen = Generator<YieldEffect, void, KeyedRow | undefined>;MutationOp
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:17 · Supporting declarations
One structured write intent — the discriminated union that mirrors the write half of the client
MutationTx 1:1. Keyed (column-name addressed), so it is independent of column order.
export type MutationOp = {
kind: "insert";
table: string;
row: KeyedRow;
} | {
kind: "upsert";
table: string;
row: KeyedRow;
} | {
kind: "insertIgnore";
table: string;
row: KeyedRow;
} | {
kind: "update";
table: string;
row: KeyedRow;
} | {
kind: "delete";
table: string;
pk: KeyedRow;
};MutationOutcomeFrame
InterfaceDeclaration · Source: packages/client/src/types.ts:261 · Supporting declarations
A room authority's verdict for a NON-applied mutation (RINDLE-REALTIME-QUERY-ENABLEMENT
§3.3, the deopt handshake; Slice H-iv-b server half / H-v client half). Sent on the author's
own socket for every mutation the room did NOT apply, always BEFORE the lmid ack that burns
the mid (same-socket ordering only — the ack may reach the client through another path
first, e.g. a replayed lmid snapshot). Applied mutations send NOTHING: silence + lmid
coverage ⇒ applied.
kind: "deopt"— the room's §3.3 commit gate refused the routed mutation (or its environment fell short, e.g.tx.query); the mid is burnt in the ROOM ledger with zero effects and the client re-enqueues the same logical mutation onto the daemon stream.name/argsare echoed so the frame is SELF-CONTAINED: a client that already retired the entry (the burnt-mid confirm won the race, or the frame is a replay re-answer) re-invokes from the frame alone.kind: "rejected"— FINAL (authz/validation): the mid is burnt the same way and the prediction snaps back on the ordinary lmid release; no re-route.
reason may be absent on a re-answered frame whose record was seeded from journal replay
(the verdict is journaled; the reason is not).
export interface MutationOutcomeFrame {
mid: number;
kind: "deopt" | "rejected";
reason?: string;
/** Echoed on DEOPT frames only (self-contained re-invoke — see above). */
name?: string;
args?: unknown;
}MutatorCtx
InterfaceDeclaration · Source: packages/client/src/mutation-ops.ts:141 · Supporting declarations
The minimal per-invocation context a shared mutator sees on BOTH tiers: the acting principal. The
server injects its AUTHENTICATED user; the client injects its local user. (The server's own
MutationContext is a superset of this.)
export interface MutatorCtx {
user: string;
}NamedQuery
InterfaceDeclaration · Source: packages/client/src/query.ts:425 · Supporting declarations
A single, co-located NAMED query (see {@link defineQuery}). It is:
- callable on the client —
q(args, ctx?)validates + builds + stamps the result with its remote subscription identity (name+ args — NOT ctx), so a component that imports it always SYNCS. There is no unstamped builder to import by accident. - registerable on the server — it carries its
queryNameand aresolvethat re-runs the SAME validator on untrusted wire args and builds the AUTHORITATIVEQueryfrom the server's own ctx ({@link registerQueriesregisterQueries} in@rindle/api-server).
Ctx is the ctx parameter list mirrored from build — [], [ctx: C], or [ctx?: C]. It is
never part of the wire identity.
export interface NamedQuery<Args, Ctx extends readonly unknown[], Q extends AnyQuery> {
(args: Args, ...ctx: Ctx): Q;
/** The wire identity. The daemon leases by this name; client and server MUST agree on it — which
* they do, because both sides import the SAME `defineQuery` value. */
readonly queryName: string;
/** The §2.1 realtime label, when this query was defined with one ({@link DefineQueryOptions}).
* Absent on unlabeled queries. Carried onto the stamped built Query too, and preserved through
* `registerQueries` on the server. */
readonly realtime?: RealtimeQueryLabel<Args>;
/** Server-side: validate raw wire args, then build the authoritative `Query` from the server's
* authoritative ctx (forwarded by `registerQueries`). */
resolve(rawArgs: unknown, ...ctx: Ctx): Q;
}NamedRow
TypeAliasDeclaration · Source: packages/client/src/resolve.ts:26 · Supporting declarations
A row named against its level's wire columns.
export type NamedRow = Record<string, WireValue>;ne
VariableDeclaration · Source: packages/client/src/operators.ts:27 · Supporting declarations
export declare const ne: <V>(v: V) => Pred<V>;newQueryBuilder
FunctionDeclaration · Source: packages/client/src/query.ts:1333 · Supporting declarations
A schema-bound query-builder factory for portable client/server query definitions. SERVER scope: local-only tables are excluded (a named/remote query may never reference one).
export declare function newQueryBuilder<S extends ColsMap>(schema: Schema<S>): QueryRoot<S>;NormalizedEvent
TypeAliasDeclaration · Source: packages/client/src/types.ts:123 · Supporting declarations
A per-query NORMALIZED stream event — the path-free twin of {@link ChangeEvent}. The
hello carries the flat per-table schemas (no nested view schema, §3); snapshot/batch
carry table-tagged {@link NormalizedOp}s. The NormalizedSync layer folds these into the
local engine's base tables.
cv (the global commit version the frame's data reflects — OPTIMISTIC-WRITES-DESIGN.md
§8.3/§8.6) is present on sources that speak the optimistic protocol; the plain normalized
path may omit it.
export type NormalizedEvent = {
type: "hello";
tables: NormalizedTableSchema[];
comparatorVersion: number;
normalizedFp: string;
} | {
type: "snapshot";
ops: NormalizedOp[];
cv?: number;
} | {
type: "batch";
ops: NormalizedOp[];
cv?: number;
};NormalizedOp
TypeAliasDeclaration · Source: packages/client/src/types.ts:100 · Supporting declarations
A table-tagged, path-free row delta — the normalized wire payload (the path-free twin
of {@link FlatChange}; NORMALIZED-CHANGES-DESIGN.md §3). Rows are positional (bare cells; a
json column is its raw JSON string). op is the discriminant. Lives here (not in
@rindle/normalized) so both the protocol (@rindle/remote) and the sync layer share one type.
export type NormalizedOp = {
table: string;
op: "add";
row: WireValue[];
} | {
table: string;
op: "remove";
row: WireValue[];
} | {
table: string;
op: "edit";
old: WireValue[];
new: WireValue[];
};NormalizedSource
InterfaceDeclaration · Source: packages/client/src/types.ts:221 · Supporting declarations
The server side of the normalized local-first path (NORMALIZED-CHANGES-DESIGN.md §5/§7):
registers queries and pushes each one's normalized footprint stream ({@link NormalizedEvent}s).
Both the in-process native (@rindle/replica) source and the ws (@rindle/remote
RemoteNormalizedSource) implement it; @rindle/normalized's NormalizedBackend consumes one,
never knowing which — a sibling seam of {@link Backend} for the normalized composition.
export interface NormalizedSource {
registerQuery(queryId: QueryId, remote: RemoteQuery): void;
unregisterQuery(queryId: QueryId): void;
/** Send base-table writes to the server; the authoritative stream flows back. */
mutate(mutations: Mutation[]): Promise<void>;
onNormalized(handler: (queryId: QueryId, event: NormalizedEvent) => void): void;
/** Optional: the backend hands its OWN typed per-table schemas so the source can validate
* each server `hello` against them (column order / PK by name) and reject a schema skew
* rather than silently transposing positional cells (CRIT#4 / §3 "drift ⇒ re-subscribe").
* Sources that can't skew (the in-process native source) may omit it. */
expectClientSchema?(tables: NormalizedTableSchema[]): void;
}NormalizedTableSchema
InterfaceDeclaration · Source: packages/client/src/types.ts:107 · Supporting declarations
One base table's flat schema on the normalized hello (§3): column names (in order) +
primary-key column indices. Wire rows are positional against columns.
export interface NormalizedTableSchema {
name: string;
columns: string[];
primaryKey: number[];
}normalizedTableSchemas
FunctionDeclaration · Source: packages/client/src/schema.ts:550 · Supporting declarations
The client's per-table flat schema (name + column order + PK indices), the shape a
normalized hello advertises (NORMALIZED-CHANGES-DESIGN.md §3). Used to validate a
server hello against the CLIENT's own typed schema so a column-order / PK skew is caught
instead of silently transposing positional cells (CRIT#4). Sorted by name for stable
ordering.
Local-only tables are omitted (201-LOCAL-ONLY-TABLES-DESIGN.md E1): the client never
claims them to the server, the server never expects/ships them, and they stay out of the schema
fingerprint (normalizedFp) so a local table can't skew it vs. the server's.
export declare function normalizedTableSchemas<S extends ColsMap>(schema: Schema<S>): {
name: string;
columns: string[];
primaryKey: number[];
}[];notExists
FunctionDeclaration · Source: packages/client/src/query.ts:1229 · Supporting declarations
NOT EXISTS by a named {@link Relationship}.
NOT EXISTS (<correlated subquery>).
export declare function notExists<PC extends AnyCols, CC extends AnyCols>(relationship: Relationship<PC, CC>, build?: (q: Query<CC>) => Query<CC, unknown>, opts?: ExistsOpts): Cond<RowOf<PC>>;
/** `NOT EXISTS (<correlated subquery>)`. */
export declare function notExists<CC extends AnyCols, R = unknown>(child: TableLike<CC>, corr: {
parent: Array<keyof R & string>;
child: Array<keyof CC & string>;
}, build?: (q: Query<CC>) => Query<CC, unknown>, opts?: ExistsOpts): Cond<R>;notExistsNoSync
FunctionDeclaration · Source: packages/client/src/query.ts:1270 · Supporting declarations
NOT EXISTS (<correlated subquery>) as a server-only, non-syncing gate — the NOT EXISTS form of {@link existsNoSync} (a deny-style permission rule).
export declare function notExistsNoSync<PC extends AnyCols, CC extends AnyCols>(relationship: Relationship<PC, CC>, build?: (q: Query<CC>) => Query<CC, unknown>): Cond<RowOf<PC>>;
export declare function notExistsNoSync<CC extends AnyCols, R = unknown>(child: TableLike<CC>, corr: {
parent: Array<keyof R & string>;
child: Array<keyof CC & string>;
}, build?: (q: Query<CC>) => Query<CC, unknown>): Cond<R>;notIlike
VariableDeclaration · Source: packages/client/src/operators.ts:35 · Supporting declarations
export declare const notIlike: (pattern: string) => Pred<string>;notInList
VariableDeclaration · Source: packages/client/src/operators.ts:37 · Supporting declarations
export declare const notInList: <V>(values: V[]) => Pred<V>;notLike
VariableDeclaration · Source: packages/client/src/operators.ts:33 · Supporting declarations
export declare const notLike: (pattern: string) => Pred<string>;number
VariableDeclaration · Source: packages/client/src/schema.ts:47 · Supporting declarations
export declare const number: <T extends number = number>() => ColBuilder<T>;OneShotBackend
ClassDeclaration · Source: packages/client/src/ssr.ts:48 · Supporting declarations
A no-op live backend for SSR (SSR-DESIGN.md §6.1): it opens no transport and never streams.
registerQuery is inert, mutate rejects, and no ChangeEvent is pushed. Every view stays
PENDING — reading its SSR {@link Store.seedAssembledseed} — and, lacking onResultType,
reports complete (a backend with no server lifecycle leaves every view authoritative).
export declare class OneShotBackend implements Backend {
registerQuery(_qid: QueryId, _ast: unknown, _remote?: RemoteQuery): void;
unregisterQuery(_qid: QueryId): void;
mutate(_mutations: Mutation[]): Promise<void>;
onEvent(_handler: (queryId: QueryId, event: ChangeEvent) => void): void;
}OneShotQueryFn
TypeAliasDeclaration · Source: packages/client/src/ssr.ts:38 · Supporting declarations
The one-shot read the server Store calls to preload a query. Two topologies inject different fns (SSR-DESIGN.md §6):
- Direct to the daemon (the trusted tier holds the daemon token): use
ast—(i) => daemon.query(i). - Through the application's API tier (the authority resolves names → ASTs, the loader never
sends a raw AST): use
name/args—(i) => fetch('/api/rindle/read', {name: i.name, args: i.args}).
ast is always present (the Store seeds the local view by its viewKey); name/args are
present when the preloaded query came from defineQuery. This Store does not authenticate
users or filter rows. The injected function must enforce access: either call the authenticated
API route, or authorize and construct the AST before a privileged direct daemon read.
export type OneShotQueryFn = (input: {
ast: unknown;
name?: string;
args?: unknown;
visibilityKey?: string;
ttlMs?: number;
}) => Promise<OneShotResult>;OneShotResult
InterfaceDeclaration · Source: packages/client/src/ssr.ts:21 · Supporting declarations
An authorized one-shot read result, as far as the server Store needs it: the
assembled rows plus the cvMin baseline they reflect. (Matches @rindle/daemon-client's
QueryOnceOutput, but @rindle/client stays dependency-free — inject the call.)
export interface OneShotResult {
rows: AssembledNode[];
cvMin?: number;
}OptimisticSource
InterfaceDeclaration · Source: packages/client/src/types.ts:303 · Supporting declarations
The server side of the OPTIMISTIC path (OPTIMISTIC-WRITES-DESIGN.md §8): the
{@link NormalizedSource} stream with cv-tagged data frames, plus the connection-level
{@link ProgressFrame} channel and the named-mutator upstream. The client buffers data
frames by cv and applies all cv ≤ cvMin as one coherent release (§8.5).
export interface OptimisticSource {
registerQuery(queryId: QueryId, remote: RemoteQuery): void;
unregisterQuery(queryId: QueryId): void;
/** Ship one named-mutator invocation upstream (§8.1). Confirmation rides the progress frames. */
pushMutation(envelope: MutationEnvelope): Promise<void>;
onNormalized(handler: (queryId: QueryId, event: NormalizedEvent) => void): void;
onProgress(handler: (frame: ProgressFrame) => void): void;
/** Optional: the backend hands its OWN typed per-table schemas so the source validates each
* server `hello` against them and rejects a schema skew (CRIT#4); see {@link NormalizedSource}. */
expectClientSchema?(tables: NormalizedTableSchema[]): void;
/** Optional: fired when the server restarts (a transport that can detect it, e.g. via a daemon
* boot id). The backend resets its `cv` watermark so the server's reset `cv` sequence is
* accepted rather than dropped as stale. In-process sources never restart and omit it. */
onRestart?(handler: () => void): void;
/** Optional (Slice H-v): the channel's {@link MutationOutcomeFrame} stream — the room deopt
* handshake's client half. **OUT-OF-BAND BY DESIGN**: the frame carries no `cv` and the source
* MUST dispatch it immediately on arrival, never behind the cv buffer — a deopt has to migrate
* its pending entry BEFORE the buffered lmid release that would otherwise retire it as a
* success (and the §7.3 hold-back trigger, keyed on the entry's confirming domain, would then
* park its staged writes the wrong way). Sources whose authority never deopts (the in-process
* native source, a plain daemon) omit it. */
onMutationOutcome?(handler: (frame: MutationOutcomeFrame) => void): void;
/** Optional (Slice H-v, the §7.5 rule-3 crash-window closer): fired when the transport
* RE-establishes its session (reconnect → re-`init`), BEFORE any post-reconnect frame is
* processed — the ordering is load-bearing: the re-subscribed lmid stream's fresh snapshot may
* cover a mid whose `mutationOutcome` frame died with the old socket, and once that release
* retires the entry as an apparent success there is nothing left to re-send. The backend
* re-sends this domain's unconfirmed pending envelopes with their ORIGINAL mids, in mid order;
* the source may DEFER their delivery until the session is re-authorized (a room's
* `pushMutation` requires the lease-token subscribe's subject). Idempotent under the domain's
* own ledger — a processed mid dedups silently (silence + lmid coverage ⇒ applied), a
* non-applied mid is re-answered from the authority's recorded-outcome map (resolving even an
* already-retired entry through the handshake's not-found arm). Distinct from
* {@link onRestart} (a NEW server incarnation): a same-incarnation socket drop re-syncs
* without restarting, and envelopes sent into the dead socket are exactly what this recovers.
* In-process sources never drop a session and omit it. */
onResync?(handler: () => void): void;
}or
FunctionDeclaration · Source: packages/client/src/operators.ts:71 · Supporting declarations
At least one child must hold.
export declare function or<R>(...conds: Cond<R>[]): Cond<R>;OrderPart
TypeAliasDeclaration · Source: packages/client/src/ast.ts:8 · Supporting declarations
One (field, direction) ordering — serializes as the 2-tuple ["id", "asc"].
export type OrderPart = [
field: string,
dir: Dir
];PathSeg
InterfaceDeclaration · Source: packages/client/src/types.ts:54 · Supporting declarations
export interface PathSeg {
rel: number;
parentRow: WireValue[];
}persistedLocalTableNames
FunctionDeclaration · Source: packages/client/src/schema.ts:419 · Supporting declarations
The subset of {@link localTableNames} eligible for the persistence plane — local: true only.
A local: "session" table stays outside it: never persisted, never replicated across tabs
(207-LOCAL-TABLE-PERSISTENCE-DESIGN.md §5.4).
export declare function persistedLocalTableNames<S extends ColsMap>(schema: Schema<S>): Set<string>;PkColsOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:152 · Supporting declarations
The primary-key column union for table N of a schema, read from its __pk map P and narrowed
to N's actual columns (falling back to all columns when P doesn't name N — e.g. the loose
default schema). Feeds {@link PkOf}/{@link UpdateOf} in the typed mutator tx.
export type PkColsOf<S extends ColsMap, P extends Record<string, string>, N extends keyof S> = (N extends keyof P ? P[N] : keyof S[N] & string) & keyof S[N] & string;PkMap
TypeAliasDeclaration · Source: packages/client/src/schema.ts:203 · Supporting declarations
name → (some subset of its column names): the loose shape a {@link Schema}'s pk-map satisfies. The fallback when a schema wasn't built through {@link createSchema} — every column could be the pk.
export type PkMap<S extends ColsMap> = {
[N in keyof S]: keyof S[N] & string;
};PkMapOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:194 · Supporting declarations
name → its primary-key column union, derived from the tables array (the PK captured by the
primaryKey(...) builder). Powers the mutator tx's typed update/delete/row pk args.
export type PkMapOf<T extends readonly AnyTable[]> = {
[E in T[number] as E[typeof SCHEMA]["name"]]: E[typeof SCHEMA]["primaryKey"][number];
};PkOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:140 · Supporting declarations
The exact PRIMARY-KEY columns of a table (each typed), required — the identity a delete/row,
and the WHERE half of an update, take. PK is the table's pk-column union (the schema's __pk).
export type PkOf<C extends AnyCols, PK extends string> = {
[K in PK & keyof C]: ColT<C[K & keyof C]>;
};Pred
InterfaceDeclaration · Source: packages/client/src/operators.ts:11 · Supporting declarations
A value predicate (an operator applied to a value). V is a phantom for type-checking.
export interface Pred<V> {
readonly [PRED]: true;
readonly op: SimpleOp;
readonly value: LitValue;
readonly __v?: V;
}ProgressFrame
InterfaceDeclaration · Source: packages/client/src/types.ts:275 · Supporting declarations
The connection-level progress frame (§8.6): advances the coherent-apply release point
(cvMin). A pure release signal — mutation confirmation does NOT ride it: lmid is a
row in {@link CLIENT_MUTATIONS_TABLE}, delivered through the client's own per-client
system query ({@link LMID_QUERY_NAME}) like any data, so it is released by the same
cvMin as the commit's effects (transactionally coherent by construction).
export interface ProgressFrame {
cvMin: number;
}queries
FunctionDeclaration · Source: packages/client/src/query.ts:1297 · Supporting declarations
A typed query entry over a schema: queries(schema).issue.where.closed(false)….
export declare function queries<S extends ColsMap>(schema: Schema<S>, onMaterialize?: (query: AnyQuery) => unknown, opts?: QueriesOptions): QueryRoot<S>;QueriesOptions
InterfaceDeclaration · Source: packages/client/src/query.ts:1289 · Supporting declarations
Scoping for {@link queries} (201-LOCAL-ONLY-TABLES-DESIGN.md §5).
export interface QueriesOptions {
/** Include {@link TableMeta.local local-only} tables in the builder's root scope. The **local**
* builder (`store.query`) sets this; the **server** builder ({@link newQueryBuilder}) does NOT,
* so naming a local table in a remote/named query is a build error (Q1 / E3 client half). */
includeLocal?: boolean;
}Query
TypeAliasDeclaration · Source: packages/client/src/query.ts:296 · Supporting declarations
export type Query<C extends AnyCols, Rels = {}, One extends boolean = false, Sel extends string = never, LocalRels = Rels, Agg = false> = Omit<QueryBase<C, Rels, One, Sel, LocalRels, Agg>, "where"> & {
where: QueryBase<C, Rels, One, Sel, LocalRels, Agg>["where"] & WhereProxy<C, Rels, One, Sel, LocalRels>;
} & WhereSugar<C, Rels, One, Sel, LocalRels>;QueryArg
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:84 · Supporting declarations
What {@link IsoTx.query} accepts: a query handle whose .ast() lowers to the wire {@link Ast} —
exactly what the typed query builder produces (newQueryBuilder(schema).<table>…, the tier-agnostic
server-scope builder both tiers can construct because it performs no I/O). Structural so the seam
need not carry the builder's heavy generics.
export type QueryArg = {
ast(): Ast;
};QueryBuilder
TypeAliasDeclaration · Source: packages/client/src/query.ts:358 · Supporting declarations
Build a Query (an Ast constructor) from already-validated args, plus an optional CONTEXT — the
authenticated principal the query is scoped to. Ctx is off-wire: the client passes its own
session ctx at the callsite, the server injects the AUTHORITATIVE ctx ({@link NamedQuery.resolve}
via registerQueries), and the wire still carries only name + args. Both tiers run the same
build, so whenever their ctx agrees the AST is byte-identical — and a client can never spoof it,
since the server re-derives ctx from its trusted session and ignores anything the client claimed.
The ctx is a tuple so a query opts into it by how it types build's second parameter — and that
shape becomes the call signature verbatim:
(args) => Q— no ctx (q(args)).(args, ctx: C) => Q— ctx REQUIRED (q(args, ctx)); for always-authenticated queries.(args, ctx?: C) => Q— ctx OPTIONAL (q(args)orq(args, ctx)); sound only when "no ctx" is a real, SYMMETRIC state — i.e. the build yields the SAME broad AST whether ctx is absent or present-with-no-user (anonymous / SSR), since the server always forwards a ctx (possibly anonymous). Otherwise type ctx required so the type catches an omitted ctx.
export type QueryBuilder<Args, Q extends AnyQuery, Ctx extends readonly unknown[] = [
]> = (args: Args, ...ctx: Ctx) => Q;QueryEffect
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:91 · Supporting declarations
A full-shape read a generator mutator yields — a where/orderBy/limit/join query over the
state this mutator is mutating (read-your-writes, like {@link ReadEffect} but an arbitrary shape).
Evaluates to {@link QueryResultRow}[] — ALWAYS an array of the matching rows, in the query's
order, on BOTH tiers (a root .one() is not unwrapped here: take [0]). The single next-type is
a row, so cast the yield ((yield tx.query(q)) as unknown as QueryResultRow[]), same as all.
export type QueryEffect = {
kind: "query";
query: QueryArg;
};QueryEnsureCache
ClassDeclaration · Source: packages/client/src/ensure.ts:76 · Supporting declarations
Deduplicates route/intent preloads and holds their live view through the navigation handoff.
The cache materializes the real named query rather than retaining sync coverage alone: that is
what makes until: "present" observable for overlapping queries already satisfied by local
normalized rows. Once the server marks the query complete, the entry is kept briefly for a
destination component to take its own retain, then released automatically.
export declare class QueryEnsureCache<S extends ColsMap> implements QueryEnsurer {
private readonly store;
private readonly releaseDelayMs;
private readonly maxEntries;
private readonly entries;
private closed;
constructor(store: Store<S>, options?: QueryEnsureCacheOptions);
/**
* Ensure a named query is retained, resolving according to `options.until`.
*
* Concurrent calls for the same `(name, args, AST)` share one materialized view and one remote
* subscription. A `present` call can resolve from local rows while a concurrent `complete` call
* continues waiting for server authority.
*/
ensure<Q extends AnyQuery>(query: Q, options?: EnsureQueryOptions): Promise<void>;
/** Release every retained preload and reject outstanding waits. Idempotent. */
close(): void;
/** Number of retained query entries (primarily useful for tests/devtools). */
size(): number;
private createEntry;
private inspect;
private isReady;
private touch;
private scheduleRelease;
private trim;
private dispose;
private queryError;
private detachAbort;
}QueryEnsureCacheOptions
InterfaceDeclaration · Source: packages/client/src/ensure.ts:29 · Supporting declarations
export interface QueryEnsureCacheOptions {
/** Keep a completed preload alive for this long so the destination can adopt its rows. */
releaseDelayMs?: number;
/** Bound retained preloads. In-flight waits are never evicted. */
maxEntries?: number;
}QueryEnsurer
InterfaceDeclaration · Source: packages/client/src/ensure.ts:37 · Supporting declarations
The structural surface consumed by framework adapters such as @rindle/tanstack.
export interface QueryEnsurer {
ensure(query: AnyQuery, options?: EnsureQueryOptions): Promise<void>;
}queryFromAst
FunctionDeclaration · Source: packages/client/src/query.ts:694 · Supporting declarations
export declare function queryFromAst(ast: Ast): AnyQuery;QueryId
TypeAliasDeclaration · Source: packages/client/src/types.ts:128 · Supporting declarations
export type QueryId = number;QueryInspect
InterfaceDeclaration · Source: packages/client/src/store.ts:74 · Supporting declarations
One live materialized view's read-only summary for a devtools pane (DEBUG-TOOLS-BROWSER-DESIGN
§4.2 — "surface, not instrument"). All fields are already held by the {@link Store}; this is the
single read-only accessor over the otherwise-private views/asts maps.
export interface QueryInspect {
/** The Store-assigned query id (also the backend's qid — the Store passes it straight through). */
qid: QueryId;
/** The query's AST (`Store.asts`), for the inspector's pretty-print / table-derivation. */
ast: Ast;
/** The view's SERVER-CHANNEL state (`unknown` while loading, `complete` once authoritative). */
resultType: ResultType;
/** Current materialized row count (`view.data.length`). */
rowCount: number;
/** A capped peek at the projected rows (reference-stable objects off the live view). */
sample: readonly unknown[];
}QueryLocalData
TypeAliasDeclaration · Source: packages/client/src/query.ts:310 · Supporting declarations
export type QueryLocalData<Q extends AnyQuery> = Q extends Query<infer C, any, infer One, infer Sel, infer LocalRels, infer Agg> ? [
Agg
] extends [
false
] ? One extends true ? (Projected<C, Sel> & LocalRels) | null : readonly (Projected<C, Sel> & LocalRels)[] : readonly Agg[] : never;QueryResultRow
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:78 · Supporting declarations
A row returned by a {@link QueryEffect}: column name → cell, plus each materialized relationship
name → its nested row(s) — an array (a plural relationship) or a single row / null (a .one()
relationship). Recursive. Presented identically on both tiers (a view.data row of the same
query).
export type QueryResultRow = {
[key: string]: WireValue | QueryResultRow | QueryResultRow[];
};QueryRoot
TypeAliasDeclaration · Source: packages/client/src/query.ts:1286 · Supporting declarations
export type QueryRoot<S extends ColsMap> = {
[N in keyof S]: Query<S[N]>;
};QueryValidator
TypeAliasDeclaration · Source: packages/client/src/query.ts:340 · Supporting declarations
Turn raw, UNTRUSTED wire args into the canonical, typed args a query is built from. Its return
type IS the query's args type — written once, it flows to both the client call signature and the
build step, so there's no second place to restate the shape.
export type QueryValidator<Args> = (rawArgs: unknown) => Args;ReadEffect
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:65 · Supporting declarations
A point read a generator mutator yields; the driver resolves it and feeds the row back through
gen.next(row). Read-your-writes on every tier: the client reads its local engine, and both
server backends read the open transaction (the daemon via an interactive mutation session).
export type ReadEffect = {
kind: "row";
table: string;
pk: KeyedRow;
};RealtimeQueryLabel
InterfaceDeclaration · Source: packages/client/src/query.ts:372 · Supporting declarations
The realtime LABEL a named query may declare (RINDLE-REALTIME-QUERY-ENABLEMENT-DESIGN.md §2.1):
which api-server room PROFILE the query wants to be served from, and how the query's args map
to that profile's key args. This is the DECLARATION only — the serve decision (covering proof,
lease routing) is the server's, and the final room key is minted server-side by the profile's
own key under authoritative ctx, so a label can never place a query in a room the server
didn't derive itself. Both tiers import the SAME defineQuery value, so they always agree
which profile a query belongs to.
export interface RealtimeQueryLabel<Args = any> {
/** The room profile name — must match a `realtime.rooms` key on the api-server (validated
* loudly at `createRindleApiServer` construction). May not contain `/` (the wire room-key
* delimiter). */
readonly room: string;
/** Map the query's VALIDATED args to the profile's key args (what the server feeds the
* profile's `key(args)`). Identity when omitted. Must be pure — both tiers may run it. */
readonly args?: (queryArgs: Args) => unknown;
}RefinableTable
TypeAliasDeclaration · Source: packages/client/src/schema.ts:340 · Supporting declarations
A table def acceptable to {@link refineSchema} over Schema<S>: its NAME must be one of S's
tables (a def for an unknown table is a compile error at the call site).
export type RefinableTable<S extends ColsMap> = {
readonly [SCHEMA]: TableMeta<Extract<keyof S, string>, AnyCols>;
};RefinedCols
TypeAliasDeclaration · Source: packages/client/src/schema.ts:308 · Supporting declarations
C with the columns named in R re-typed to their refined Cols.
export type RefinedCols<C extends AnyCols, R extends ColRefinements<C>> = {
[K in keyof C]: K extends keyof R ? (R[K] extends Col<unknown> ? R[K] : C[K]) : C[K];
};RefinedColsMap
TypeAliasDeclaration · Source: packages/client/src/schema.ts:347 · Supporting declarations
S with each table named in T re-typed to that def's (refined) columns. (The inner
extends AnyCols guard is how the checker proves the remapped SchemaOf<T>[K] is a column
map while T is still generic; it always holds for a concrete T.)
export type RefinedColsMap<S extends ColsMap, T extends readonly AnyTable[]> = {
[K in keyof S]: K extends keyof SchemaOf<T> ? (SchemaOf<T>[K] extends AnyCols ? SchemaOf<T>[K] : S[K]) : S[K];
};refineSchema
FunctionDeclaration · Source: packages/client/src/schema.ts:357 · Supporting declarations
Swap {@link refineTable}-narrowed table defs into a generated schema, re-typing those tables for
everything downstream of the schema (newQueryBuilder/queries roots, store row types).
Runtime-validated identity: each def must name a table already in the schema and match its runtime shape exactly (same columns, kinds, primary key, and locality) — refinement narrows TS types, never what's on the wire. Composes with {@link extendSchema} in either order.
export declare function refineSchema<S extends ColsMap, P extends Record<string, string>, const T extends readonly RefinableTable<S>[]>(base: Schema<S, P>, opts: {
tables: T;
}): Schema<RefinedColsMap<S, T>, P>;refineTable
FunctionDeclaration · Source: packages/client/src/schema.ts:317 · Supporting declarations
Narrow a generated table's column TYPES without touching its runtime shape.
Returns the SAME def, re-typed (identity, after validating that every refined column exists and
keeps its kind) — so conditions built from it, rel(...)s anchored on it, and Row<typeof t>
all see the narrowed types. Pass the result to {@link refineSchema} so query roots narrow too.
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>>;rel
FunctionDeclaration · Source: packages/client/src/schema.ts:476 · Supporting declarations
Declare a relationship once: rel(issue, user, { ownerId: "id" }) means issue.ownerId → user.id.
mapping is { [parentColumn]: childColumn } (a composite join is multiple entries). The parent
table is used only to type-check the keys; pass the result to sub/countAs/exists.
export declare function rel<PC extends AnyCols, CC extends AnyCols>(_parent: TableLike<PC>, child: TableLike<CC>, mapping: Partial<Record<keyof PC & string, keyof CC & string>>): Relationship<PC, CC>;Relationship
InterfaceDeclaration · Source: packages/client/src/schema.ts:458 · Supporting declarations
A reusable, typed JOIN between two tables — the correlation declared once (design §4). Built with
{@link rel}; parameterized by the parent columns PC (so a sub checks the relationship belongs to
the query's table) and the child columns CC (which flow into the nested result type). Pass it to
sub/countAs/exists in place of an explicit child + { parent, child } correlation.
export interface Relationship<PC extends AnyCols, CC extends AnyCols> {
/** The child table the relationship points at. */
readonly child: TableLike<CC>;
/** Correlation keys: `parent[i]` (a parent column) joins to `child[i]` (a child column). */
readonly correlation: {
readonly parent: readonly string[];
readonly child: readonly string[];
};
/** Phantom binding the parent columns so `Query<C>.sub(alias, rel)` rejects a rel for another table. */
readonly __parent?: PC;
readonly [RELATIONSHIP_BRAND]: true;
}RemoteQuery
InterfaceDeclaration · Source: packages/client/src/types.ts:159 · Supporting declarations
The network identity for a named query subscription. The local AST remains local; remote
normalized/optimistic sources use only this (name,args) pair upstream.
export interface RemoteQuery {
name: string;
args: unknown;
}RESERVED_TABLE_PREFIXES
VariableDeclaration · Source: packages/client/src/schema.ts:220 · Supporting declarations
Table-name prefixes reserved by the engine for SYNTHETIC tables — __agg_<fnv> aggregate
bases (AGGREGATE-SYNC-DESIGN.md §3.3) and _rindle_* system tables (e.g. the lmid table).
A user table (synced OR local) under one of these would shadow a synthetic source — and since
registerTable is idempotent-on-name and base tables register before synthetics, the later
synthetic registration would silently no-op and the agg join would read the user table as the
count (silent corruption, 201-LOCAL-ONLY-TABLES-DESIGN.md N1). The ban makes that
structurally impossible — checked once, statically, at {@link createSchema}.
export declare const RESERVED_TABLE_PREFIXES: readonly [
"__agg_",
"_rindle_"
];resolveChange
FunctionDeclaration · Source: packages/client/src/resolve.ts:84 · Supporting declarations
Lift one FlatChange into a {@link ResolvedChange} against the query's WireSchema, or null
if its path doesn't resolve to a materialized level.
export declare function resolveChange(schema: WireSchema, change: FlatChange): ResolvedChange | null;ResolvedChange
InterfaceDeclaration · Source: packages/client/src/resolve.ts:30 · Supporting declarations
One resolved change: a FlatChange lifted out of positional/indexed wire form into names,
using the query's WireSchema (from hello) as the sole position→name source.
export interface ResolvedChange {
/** Relationship-alias chain from the query root to the changed level (`[]` ⇒ the root rows). */
aliasChain: string[];
/** The alias of the changed level (`""` ⇒ root), i.e. the last of `aliasChain`. */
alias: string;
op: "add" | "remove" | "edit";
/** The affected row, named. For `edit` this is the NEW row; see `old` for the prior one. */
row: NamedRow;
/** The prior row, named (present only for `edit`). */
old?: NamedRow;
/** The PARENT row (named), for a nested/aggregate change — e.g. the `ticket_type` whose `sold`
* count moved. Taken from the path's last `parentRow`; absent for a root-level change. */
parent?: NamedRow;
/** Set when the changed level is a `countAs`/aggregate slot. The value is EXACT — read from the
* slot's projected count column (`WireRel.project.col`). */
aggregate?: {
alias: string;
value: WireValue;
previous?: WireValue;
};
/** The raw node whose children a consumer can dig a named sub-row out of (via {@link subRow}). On
* an `add` the engine always ships it; on a `remove` it is present only when the consumer opted
* into the removed subtree (see the `op` mapping below). */
node?: WireNode;
/** The changed level's `WireSchema` — used by {@link subRow} to resolve a named sub of `node`. */
levelSchema: WireSchema;
}ResultType
TypeAliasDeclaration · Source: packages/client/src/types.ts:155 · Supporting declarations
A query's SERVER-CHANNEL state, surfaced on its {@link ArrayView } (FOLDED-MUTATIONS-DESIGN §7 — formerly conflated with pending-ness, OPTIMISTIC-WRITES-DESIGN.md §6):
unknown— not hydrated: the server has not produced a first result for this query yet;complete— the server has answered. STAYScompletewhile a local mutation is pending (the prediction is the client's best current answer); reversion on rejection is an event (onRejected), not a downgrade of completeness;error— RESERVED for a future server-side, query-level error signal (seedesigns/101-QUERY-ERRORS-DESIGN.md); no longer produced by a pending mutation. "Is a prediction pending here?" is now a separate reactive axis (the backend'spending(qid)/onPending), not folded into this type. A backend with no server lifecycle (the in-process engine) leaves every viewcomplete.
export type ResultType = "unknown" | "complete" | "error";Row
TypeAliasDeclaration · Source: packages/client/src/schema.ts:107 · Supporting declarations
The row type of a table definition: Row<typeof issue> → { id: string; … }. The
whole-table ergonomic form of {@link RowOf}, so app code derives its row interfaces from
the schema instead of hand-maintaining a parallel twin.
export type Row<T extends AnyTable> = RowOf<T[typeof SCHEMA]["columns"]>;RowOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:28 · Supporting declarations
export type RowOf<C extends AnyCols> = {
[K in keyof C]: ColT<C[K]>;
};rowsEqual
FunctionDeclaration · Source: packages/client/src/view.ts:196 · Supporting declarations
Elementwise equality over positional bare-cell rows — THE row comparator for every JS-side
diff (views, aggregate heads, the persistence mirror), exported so no caller grows a drifted
private copy. Per cell: === keeps -0 === 0 (the engine's key semantics treat them equal);
the Object.is arm makes a NaN cell equal itself (under !== alone, a NaN-bearing row never
matches any copy of itself, so every diff re-emits it as a spurious edit forever).
export declare function rowsEqual(a: WireValue[], b: WireValue[]): boolean;Schema
InterfaceDeclaration · Source: packages/client/src/schema.ts:205 · Supporting declarations
export interface Schema<S extends ColsMap = ColsMap, P extends Record<string, string> = PkMap<S>> {
readonly tables: Readonly<Record<string, TableMeta>>;
/** Phantom carrying name→columns for query-root inference (never read at runtime). */
readonly __cols: S;
/** Phantom carrying name→pk-column-union for the typed mutator tx (never read at runtime). */
readonly __pk: P;
}SCHEMA
VariableDeclaration · Source: packages/client/src/schema.ts:57 · Supporting declarations
Metadata key on a {@link TableDef} (a unique symbol, so no column name collides).
export declare const SCHEMA: unique symbol;SchemaOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:188 · Supporting declarations
name → columns, derived from the tables array (for typing store.query.<table>).
export type SchemaOf<T extends readonly AnyTable[]> = {
[E in T[number] as E[typeof SCHEMA]["name"]]: E[typeof SCHEMA]["columns"];
};ServerDeltaOp
TypeAliasDeclaration · Source: packages/wasm/src/index.ts:57 · Supporting declarations
One base-table row op of a coherent server delta (the §1.3 D), bare cells.
export type ServerDeltaOp = {
table: string;
type: "add";
row: unknown[];
} | {
table: string;
type: "remove";
row: unknown[];
} | {
table: string;
type: "edit";
row: unknown[];
old: unknown[];
};ServerStore
ClassDeclaration · Source: packages/client/src/ssr.ts:78 · Supporting declarations
The server-side Store wrapper (SSR-DESIGN.md §6.2). Wraps a {@link Store} over a
{@link OneShotBackend} and adds the loader-phase preload plus dehydrate. Pass .store to
the React <Rindle> provider for the synchronous render; return .dehydrate() from the loader.
Create one instance per request, with an authorized read function for that request's principal.
Its snapshots seed views; they do not persist synced rows in the browser database.
export declare class ServerStore<S extends ColsMap> {
readonly store: Store<S>;
private readonly schema;
private readonly opts;
constructor(schema: Schema<S>, opts: ServerStoreOptions);
/** Run the one-shot read for `query` and seed its first-paint snapshot (SSR-DESIGN.md §6.2).
* Call once per query in the route loader, before the synchronous render. */
preload(query: Query<any, any, any>): Promise<void>;
/** The dehydrated first-paint cache for every preloaded query — embed in the HTML, then
* `store.hydrate(...)` it in the browser. */
dehydrate(): DehydratedState;
/**
* Loader-phase convenience over {@link preload} + {@link dehydrate}: preload EVERY query (reads run
* concurrently) and return the dehydrated first-paint cache. Composition keeps this to one read per
* composed root query — no request waterfall (SSR-DESIGN.md §6.2).
*
* A failed read produces no seed for that query and calls `onError`, if provided. Other reads
* can still supply seeds. The browser must establish its own successful live subscription to
* obtain the missing data; hydration alone does not retry this read. Without `onError`, the
* preload failure is silent. An `onError` callback that throws rejects the batch.
*/
preloadAll(queries: Array<Query<any, any, any>>, opts?: {
onError?: (query: Query<any, any, any>, err: unknown) => void;
}): Promise<DehydratedState>;
}ServerStoreOptions
InterfaceDeclaration · Source: packages/client/src/ssr.ts:57 · Supporting declarations
export interface ServerStoreOptions {
/** Performs an authorized one-shot read. See {@link OneShotQueryFn} for access ownership. */
query: OneShotQueryFn;
/** Optional namespace for query deduplication, forwarded to every preload. Different keys
* prevent pipeline sharing for the same AST. This key does not authorize access or filter rows;
* the query must already contain the required visibility predicates. */
visibilityKey?: string;
/** Optional idle TTL (ms) the warm pipeline is left at, forwarded to every preload (SSR-DESIGN.md
* §3.4). The TTL is NOT part of the dedup key, so a shared materialization keeps the LONGEST TTL
* any caller requested (max-wins) — `ttlMs` can extend a query's warm-handoff window, never
* shrink it; absent ⇒ the daemon's default idle TTL. */
ttlMs?: number;
}ServerWriteTx
InterfaceDeclaration · Source: packages/client/src/mutation-ops.ts:29 · Supporting declarations
The ASYNC server-side write surface a mutator runs against. Semantically the twin of the client's
synchronous MutationTx write methods — same names, same keyed-row arguments — but every op is a
Promise. Reads see this transaction's own writes on both Postgres and the daemon; the daemon
opens an interactive transaction when a read is needed. Inserts require every non-nullable
column and fill omitted nullable columns with null. Updates and deletes require the PK.
export interface ServerWriteTx {
/** Insert a row. Missing required columns and unknown columns throw; omitted nullable
* columns become `null`, rather than using database defaults. */
insert(table: string, row: KeyedRow): Promise<void>;
/** Update the row identified by the pk columns; only the named non-pk columns change. A missing
* row is a NO-OP. */
update(table: string, row: KeyedRow): Promise<void>;
/** Insert, or replace non-PK columns on PK conflict, using the same insert shape. */
upsert(table: string, row: KeyedRow): Promise<void>;
/** Insert with the same nullable-column rules as `insert`, or do nothing on PK conflict. */
insertIgnore(table: string, row: KeyedRow): Promise<void>;
/** Delete the row identified by the pk columns. A missing row is a NO-OP. */
delete(table: string, pk: KeyedRow): Promise<void>;
/** Read one row by primary key, through the OPEN transaction on both backends
* (read-your-writes: live on Postgres; via an interactive mutation session on the daemon,
* DAEMON-INTERACTIVE-TXN-DESIGN.md §5.3). */
row(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
}SimpleOp
TypeAliasDeclaration · Source: packages/client/src/ast.ts:19 · Supporting declarations
The wire comparison operators (exact strings, src/ast.rs Op).
export type SimpleOp = "=" | "!=" | "<" | "<=" | ">" | ">=" | "IS" | "IS NOT" | "LIKE" | "NOT LIKE" | "ILIKE" | "NOT ILIKE" | "IN" | "NOT IN";SingularArrayView
InterfaceDeclaration · Source: packages/client/src/view.ts:68 · Supporting declarations
What a top-level .one() query materializes to: the single row (or null), not an array.
A thin adapter over a {@link FlatArrayView} — all the folding is shared; only the result
boundary unwraps (data[0] ?? null). Reference identity of the row is preserved.
export interface SingularArrayView<R> {
/** The single current row, or `null` when the query matches nothing. */
readonly data: R | null;
/** The engine query id — see {@link ArrayView.qid}. */
readonly qid: QueryId;
/** The query's view `WireSchema` — see {@link ArrayView.schema}. */
readonly schema: WireSchema | null;
/** The query's lifecycle state — see {@link ArrayView.resultType}. */
readonly resultType: ResultType;
/** Subscribe; fires immediately with the current row, then after each applied batch (and after
* a {@link resultType} change). */
subscribe(listener: (data: R | null) => void): () => void;
/** Subscribe to the view's folded CHANGE stream — see {@link ArrayView.onChanges}. The changes
* are the same positional `FlatChange`s as the plural view (a `.one()` is just the list capped to
* one), so a narrator resolves them identically. */
onChanges(listener: ViewChangeListener): () => void;
/** Tear down + stop receiving updates. */
destroy(): void;
}SingularView
ClassDeclaration · Source: packages/client/src/view.ts:90 · Supporting declarations
Wrap a plural {@link FlatArrayView} as a {@link SingularArrayView} for a .one() query
(the engine caps it to limit = 1, so the top list holds at most one node).
export declare class SingularView<R> implements SingularArrayView<R> {
private readonly inner;
constructor(inner: ArrayView<R>);
get data(): R | null;
get qid(): QueryId;
get schema(): WireSchema | null;
get resultType(): ResultType;
subscribe(listener: (data: R | null) => void): () => void;
onChanges(listener: ViewChangeListener): () => void;
destroy(): void;
}spliceStreamText
FunctionDeclaration · Source: packages/client/src/stream.ts:75 · Supporting declarations
Merge the durable plane with the live tail.
durable is what the IVM view shows; produced is what a subscription has accumulated (the prefix
it joined at, plus every chunk). Both are prefixes of the same response, so the merge is "take
the longer" — no diffing, no overlap handling, no ranges.
The length comparison is the whole algorithm, which is why a caller MUST seed its accumulator with
the text it joined at: a tail carrying only the chunks it received would read as shorter than the
durable text and be discarded. useStreamedText does that for you.
export declare function spliceStreamText(durable: string, produced: string): string;stableKey
FunctionDeclaration · Source: packages/client/src/key.ts:6 · Supporting declarations
export declare function stableKey(value: unknown, seen?: WeakSet<object>): string;Store
ClassDeclaration · Source: packages/client/src/store.ts:92 · Supporting declarations
export declare class Store<S extends ColsMap> {
/** Type-safe query entry: `store.query.issue.where.closed(false).materialize()`. */
readonly query: QueryRoot<S>;
private readonly schema;
private readonly backend;
private nextId;
private readonly views;
private readonly asts;
private readonly syncLeases;
private readonly seeds;
private changeListeners?;
private removedSubtreeWanted;
private resultTypeListeners?;
private readonly hasResultTypeLifecycle;
private commitDepth;
private readonly pendingFlush;
private readonly pendingChanges;
constructor(schema: Schema<S>, backend: Backend);
/** Materialize any fluent query object. Named queries subscribe remotely by `(name,args)`;
* ad-hoc builder queries are local-only for local-first backends.
*
* `opts.onChanges` binds a narrator to this view's DIFF stream ({@link ArrayView.onChanges}) — the
* per-view seam that replaces filtering the store-global {@link subscribeChanges} by `qid`. It is
* wired BEFORE the backend registers the query, so a synchronous backend's first `snapshot` (fired
* inside `registerQuery`, before this returns) is delivered too. */
materialize<Q extends Query<any, any, any>>(query: Q, opts?: {
onChanges?: ViewChangeListener;
}): ReturnType<Q["materialize"]>;
/** One-shot AUTHORITATIVE read: materialize `query`, wait until its result is server-authoritative
* ({@link ResultType} `"complete"`), read the data once, then destroy the view — resolving with the
* plain result rather than a live subscription. Rejects if the query enters the `"error"` state. Use
* it for exports, imports, undo snapshots — anywhere that wants the current answer as a value.
*
* A synchronous local-first backend (wasm/replica) has already delivered the first snapshot inside
* {@link materialize}, so the view is `"complete"` on entry and this settles on the next microtask
* without ever attaching a listener; a remote backend settles when the first live snapshot lands.
* The query is NEVER left subscribed — the view is destroyed before the promise settles either way.
* (A remote query that never completes leaves the promise pending, exactly as a `resultType` poll
* would; race a timeout at the call site if you need one.) */
readOnce<Q extends Query<any, any, any>>(query: Q): Promise<ReturnType<Q["materialize"]>["data"]>;
/** True when the backend can retain a remote named query independently from the local
* materialized AST view. React uses this to keep one local view per AST while still sending
* every mounted `(name,args)` lease through the backend. */
canRetainRemoteQueries(): boolean;
/** Build one local AST view, with remote syncing retained separately through the returned
* handle. This is a lower-level API for UI bindings; ordinary app code should keep using
* `materialize(query)`. */
createCachedQueryView<Q extends Query<any, any, any>>(query: Q): CachedQueryView<Q>;
/** Retain a named remote query purely for normalized/local-first coverage. This does not
* register or materialize the query AST locally, so React can keep server sync coverage alive
* without subscribing to the broad coverage result tree. */
retainSyncQuery<Q extends Query<any, any, any>>(query: Q): SyncQueryLease;
/** Apply a batch of mutations (object rows → positional). Resolves when the backend has
* accepted them (local: applied; remote: sent). The resulting view updates flow back via
* the backend's event stream. */
write(fn: (tx: WriteTx<S>) => void): Promise<void>;
/** Direct-commit write to LOCAL-only tables (`201-LOCAL-ONLY-TABLES-DESIGN.md` §6): the
* client-authoritative path for selection state, draft text, view prefs, scratch rows. It
* bypasses the optimistic pending stack entirely — a local table is untracked, so it never
* rebases, reverts, or waits on a server confirmation (it "moves on its own").
*
* Rejects a synced/tracked table (M2): a direct write to one would be un-applied on the very
* next server rewind. Local writes also must NOT live inside a replayable mutator (M1) — the
* server runs the mutator from `args` alone and cannot see local tables; use this instead.
* Same keyed `WriteTx` shape as {@link write}. `async` so the no-seam / M2 guards surface as a
* REJECTED promise (the `Promise<void>` contract) rather than a synchronous throw that escapes a
* caller's `.catch` and crashes the event handler / render frame. */
writeLocal(fn: (tx: WriteTx<S>) => void): Promise<void>;
/** Drain a keyed `WriteTx` callback into positional {@link Mutation}s (shared by
* {@link write} / {@link writeLocal}; json columns stringified, rows in column order). */
private collectMutations;
/** Seed a query's first-paint snapshot from a `POST /query` response (server side): convert the
* assembled rows to the view's projected shape and stash them by `viewKey`. A view materialized
* for this AST (during the synchronous render) reads the seed; {@link dehydrate} serializes it. */
seedAssembled(ast: Ast, rows: AssembledNode[], cvMin: number): void;
/** The dehydrated first-paint cache for every preloaded query — embed it in the HTML and pass it
* to {@link hydrate} in the browser (SSR-DESIGN.md §6.2). */
dehydrate(): DehydratedState;
/** Seed the browser store from the server's {@link dehydrate} output (SSR-DESIGN.md §6.2): each
* view materialized for a hydrated AST shows these rows until its first live `hello` reconciles. */
hydrate(state: DehydratedState): void;
/** A query's hydrated first-paint snapshot, by `viewKey` — what React's `getServerSnapshot`
* reads so an SSR render (and the matching client hydration pass) sees the seeded rows without
* opening a subscription. */
seedSnapshot(viewKey: string): DehydratedQuery | undefined;
primaryKeyFor(table: string): readonly string[];
/** Convert assembled (nested-by-name) rows (SSR-DESIGN.md §3.3) into the view's projected result
* shape: spread `cols` (parsing json columns), recurse into each relationship by its alias
* (plural → array, `.one()` → object/null, `countAs` → bare scalar). */
assembleSnapshot(ast: Ast, rows: AssembledNode[]): unknown[];
private assembleNode;
private registerMaterialized;
private retainRemote;
private onEvent;
/** Retire a view's SSR seed — from the view (so `data` switches from the seed to the maintained
* tree) AND from the seeds map (so no later mount re-seeds a now-live query) — but ONLY once the
* query is AUTHORITATIVE (`resultType === "complete"`). Called BEFORE the fold it accompanies, so
* that fold's notify already reflects the live tree with no empty gap. Idempotent.
*
* The gate is the fix for the synchronous optimistic/wasm backend: it fires a query's FIRST snapshot
* from LOCAL, not-yet-synced state while the query is still `unknown` (`registerMaterialized` marks a
* lifecycle-backed remote view `unknown` up front for exactly this), then delivers the authoritative
* rows one event later as a `catchUp` batch — having already flipped the query to `complete`. So the
* seed survives the pre-sync snapshot (`unknown` ⇒ skip) and retires on the catch-up (`complete` ⇒
* retire). A lifecycle-LESS backend (pure wasm, the SSR one-shot, tests) is `complete` from creation,
* so its first snapshot retires the seed exactly as before this gate existed. */
private retireSeedIfLive;
/** Retire the SSR seed (if authoritative) and fold the accompanying hydration delta — BEFORE the
* fold so its notify already reflects the live tree with no empty gap. The subtlety: a hydration
* can fold NOTHING — a 0-row authoritative result, or one whose rows are already present in `top`
* (a query whose result is fully covered by an already-hydrated sibling: the shared rows dedup to
* zero net base mutations). Then {@link FlatArrayView.applyChanges} notifies nothing, so the
* seed→tree switch would never reach subscribers and the view freezes on the stale seed. Guard
* against that: if the seed retired but the fold was a no-op, force the handoff notify (inline, or
* via the commit-boundary flush). Flash-safe — the forced notify only fires when there was nothing
* to fold, so `data` is already the correct live tree by then. */
private foldHydration;
/** Fold a batch into its view, then notify now or — inside a commit bracket — defer the view's
* notification to the commit boundary, so all sibling views fold first (cross-view-atomic
* notification; see `commitDepth`). */
private applyAndTrack;
/** Deliver everything deferred during the just-ended commit, after every affected view has folded
* (cross-view-atomic notification): view subscribers first, then the raw change stream
* (narrators/devtools), each frame in arrival order. A throwing listener does not stop the others
* — the first error is re-raised only once the whole flush completes (mirroring the backend's
* per-query isolation). View subscribers run before change listeners, preserving the per-event
* order that held before coalescing (a view's subscribers fired before its change frame). */
private flushCommit;
/** Subscribe to the raw per-query {@link ChangeEvent} stream (hello / snapshot / batch) the Store
* routes to its views, tagged by `qid`. Fired AFTER the event is folded — and, for a commit that
* fans out to several queries (the in-process engine's `onCommitBoundary`), after EVERY view in
* that commit has folded — so a listener that re-reads ANY view (its own or a sibling) sees
* post-commit state, never a torn mid-commit one. Frames keep their arrival (engine-dispatch)
* order, one per affected query. This is the supported way to drive change-derived layers
* (e.g. {@link resolveChange} → @rindle/narrator, or a devtools pane) off a live store — attach
* BEFORE `materialize` to catch a synchronous backend's first `hello`+`snapshot`.
* The per-query view `WireSchema` rides the `hello` frame (also readable via `view.schema`).
*
* The third listener arg is the post-fold {@link ArrayView} for this `qid` (so a template wanting
* list context — current `data`, `schema`, `resultType` — needn't look it up). It is ALWAYS the
* plural view, even for a top-level `.one()` query: the Store retains the list-shaped view, not the
* SingularView wrapper handed back from `materialize`. `undefined` only if the view is mid-teardown.
*
* `opts.removedSubtree` enriches every `remove` op on this stream with the full removed subtree
* ({@link FlatOp.node}), so a consumer can resolve a removed row's nested subs exactly as on an
* `add` (a bare remove carries only the leaving row). It is reconstructed client-side from the
* view — no wire/engine cost — and paid only on real evictions while at least one subscriber asks.
*
* Returns a detach function; multiple listeners may attach. */
subscribeChanges(listener: (qid: QueryId, ev: ChangeEvent, view?: ArrayView<unknown>) => void, opts?: {
removedSubtree?: boolean;
}): () => void;
/** Subscribe to per-query {@link ResultType} transitions (the server-channel lifecycle the backend
* pushes — `unknown` → `complete`, etc.), tagged by `qid`. Fired only on a CHANGE (never replayed
* on attach; read `view.resultType` for the current value). The supported seam for a status-driven
* layer (a devtools pane). Returns a detach function; multiple listeners may attach. */
subscribeResultType(listener: (qid: QueryId, rt: ResultType) => void): () => void;
/** A read-only snapshot of every live materialized view (DEBUG-TOOLS-BROWSER-DESIGN §4.2): its
* qid, AST, {@link ResultType}, row count, and a capped row sample. Built fresh on each call from
* the live `views`/`asts` maps — never cached, never mutating. `sampleRows` caps the per-query
* peek (default 50) so a large view doesn't bloat the snapshot. */
__inspect(sampleRows?: number): StoreInspect;
private columns;
/** An object row → a positional cell array in the table's column order (json → string). */
private positionalize;
/** The per-level column types parallel to the WireSchema, so the view parses json columns. */
private viewTypes;
}StoreInspect
InterfaceDeclaration · Source: packages/client/src/store.ts:88 · Supporting declarations
A frozen snapshot of the Store's live query state for a devtools pane ({@link Store.__inspect}).
export interface StoreInspect {
queries: QueryInspect[];
}STREAM_STATUS_STREAMING
VariableDeclaration · Source: packages/client/src/stream.ts:22 · Supporting declarations
The value in the mapped status column while a stream is live.
export declare const STREAM_STATUS_STREAMING = "streaming";StreamFrame
TypeAliasDeclaration · Source: packages/client/src/stream.ts:32 · Supporting declarations
One frame of a subscription. A subscription always begins with open and always ends with exactly
one terminal frame — end, stale, or absent — after which the iterator completes.
stale and absent are the two "you are on the durable plane now" answers, and both are SAFE:
the store holds everything below floorSeq and everything through durableSeq, so the reader's
IVM view converges without the stream. Neither is an error.
export type StreamFrame =
/** Join accepted. `from` is the (clamped) offset the replay starts at. */
{
type: "open";
streamId: string;
from: number;
seq: number;
durableSeq: number;
ended: boolean;
}
/** PRODUCED text — not a durability claim. `text.length === seq - from`, always. */
| {
type: "chunk";
from: number;
seq: number;
text: string;
}
/** The store now holds the prefix through `seq`. */
| {
type: "durable";
seq: number;
}
/** Sealed. No further frames. */
| {
type: "end";
seq: number;
status: StreamStatus;
error?: string;
}
/** `from` is below the producer's retained buffer floor (or the subscriber fell too far behind):
* read the store. (A raw `EventSource` rejoins automatically on its reconnect; `useStreamedText`
* deliberately stays on the durable plane instead — correct, at checkpoint granularity.) */
| {
type: "stale";
floorSeq: number;
durableSeq: number;
}
/** The process serving this subscribe is not hosting the stream (wrong instance, already evicted,
* or it never existed): the store is the whole truth. */
| {
type: "absent";
};StreamStatus
TypeAliasDeclaration · Source: packages/client/src/stream.ts:19 · Supporting declarations
How a stream ended.
complete— the model finished.cancelled— the reader asked it to stop and the producer honoured it.error— the generation threw.interrupted— the host went away mid-generation. The one status that implies the store may be short of what was produced.
export type StreamStatus = "complete" | "cancelled" | "error" | "interrupted";string
VariableDeclaration · Source: packages/client/src/schema.ts:46 · Supporting declarations
export declare const string: <T extends string = string>() => ColBuilder<T>;subRow
FunctionDeclaration · Source: packages/client/src/resolve.ts:128 · Supporting declarations
Read a named sub-row off a change's node by relationship alias (e.g. the guest under an
rsvp) — the add node, or a remove's subtree when the consumer opted into it. The alias →
wire slot mapping comes from the changed level's WireSchema. null when no node rode along.
export declare function subRow(rc: ResolvedChange, alias: string): NamedRow | null;SyncEffectExec
InterfaceDeclaration · Source: packages/client/src/mutation-ops.ts:226 · Supporting declarations
A tier's SYNCHRONOUS effect executor (the browser wasm engine): apply a write, resolve a read — both immediate.
export interface SyncEffectExec {
apply(op: MutationOp): void;
read(table: string, pk: KeyedRow): KeyedRow | undefined;
query(q: QueryArg): QueryResultRow[];
}SyncQueryLease
InterfaceDeclaration · Source: packages/client/src/store.ts:58 · Supporting declarations
export interface SyncQueryLease {
readonly resultType: ResultType;
subscribe(listener: () => void): () => void;
release(): void;
}table
FunctionDeclaration · Source: packages/client/src/schema.ts:160 · Supporting declarations
table("issue").columns({ id: string(), … }).primaryKey("id"). Pass { local: true } for a
{@link TableMeta.locallocal-only} table (201-LOCAL-ONLY-TABLES-DESIGN.md).
export declare function table<N extends string>(name: N, opts?: TableOptions): {
columns<C extends AnyCols>(cols: C): {
primaryKey<K extends keyof C & string>(...keys: K[]): TableDef<N, C, K>;
};
};TableDef
TypeAliasDeclaration · Source: packages/client/src/schema.ts:92 · Supporting declarations
export type TableDef<N extends string, C extends AnyCols, PK extends string = string> = {
readonly [SCHEMA]: TableMeta<N, C, PK>;
} & {
readonly [K in keyof C]: (arg: Arg<ColT<C[K]>>) => Cond<RowOf<C>>;
};TableLike
TypeAliasDeclaration · Source: packages/client/src/schema.ts:101 · Supporting declarations
Any table, for positions that only read its metadata. The field-factory part of a
TableDef is invariant in C, so callers constrain on the [SCHEMA] meta only — to
which every concrete TableDef<N, C> is assignable.
export type TableLike<C extends AnyCols> = {
readonly [SCHEMA]: TableMeta<string, C>;
};tableMeta
FunctionDeclaration · Source: packages/client/src/schema.ts:183 · Supporting declarations
Read a table's metadata (columns / PK / name).
export declare function tableMeta(t: AnyTable): TableMeta;TableMeta
InterfaceDeclaration · Source: packages/client/src/schema.ts:59 · Supporting declarations
export interface TableMeta<N extends string = string, C extends AnyCols = AnyCols, PK extends string = string> {
readonly name: N;
readonly columns: C;
readonly primaryKey: readonly PK[];
/** A **local-only** table (`201-LOCAL-ONLY-TABLES-DESIGN.md`): client-authoritative,
* never synced/tracked/rebased. The single marker the whole design keys off (§4) — it is
* immutable for the table's lifetime (N2) and never crosses the wire (C2). Absent ⇒ an
* ordinary synced table.
*
* `true` ⇒ eligible for the local-persistence plane (durable + cross-tab when the client
* enables `persistLocal`, `207-LOCAL-TABLE-PERSISTENCE-DESIGN.md`). `"session"` ⇒ local but
* EPHEMERAL: outside the plane entirely — never persisted, never replicated across tabs,
* per-client-instance state that empties on reload (201's original behavior) even when
* `persistLocal` is on. Every OTHER locality rule (untracked source, M1/M2 guards, E3/Q1
* wire exclusion) treats both variants identically. */
readonly local?: boolean | "session";
}TableOptions
InterfaceDeclaration · Source: packages/client/src/schema.ts:83 · Supporting declarations
Options for {@link table}.
export interface TableOptions {
/** Declare a {@link TableMeta.local local-only} table (selection state, draft text, view
* prefs, scratch rows): client-authoritative, never synced or rebased. See
* `201-LOCAL-ONLY-TABLES-DESIGN.md`. Pass `"session"` for a local table that must stay
* EPHEMERAL and per-tab even when the client enables `persistLocal` — e.g. selection state
* that should not follow the user across tabs or reloads (207 §5.4). */
local?: boolean | "session";
}tableSpec
FunctionDeclaration · Source: packages/client/src/schema.ts:498 · Supporting declarations
The SchemaSpec (columns + primaryKey indices) the wasm Db.registerTable wants.
export declare function tableSpec(meta: TableMeta): {
columns: string[];
primaryKey: number[];
};toCell
FunctionDeclaration · Source: packages/client/src/schema.ts:536 · Supporting declarations
Encode a keyed-row cell to its wire {@link WireValue} for a column KIND — the one place both write
funnels stringify a json<T> object (the typed mutator surface / design 206 §7). An
already-stringified json value (a string) or any non-json cell passes through unchanged, so a
mutator may pass EITHER a parsed object OR a JSON string. Mirrors store.positionalize.
export declare function toCell(v: WireValue | object, type: ColType): WireValue;UpdateOf
TypeAliasDeclaration · Source: packages/client/src/schema.ts:145 · Supporting declarations
The UPDATE shape of a table: its {@link PkOfprimary-key columns} REQUIRED (they identify the row)
plus every non-pk column OPTIONAL (?) and typed — a nullable one stays T | null (settable to
null), a NOT NULL one is T. Omitted non-pk columns are left unchanged.
export type UpdateOf<C extends AnyCols, PK extends string> = Simplify<PkOf<C, PK> & {
[K in Exclude<keyof C, PK>]?: ColT<C[K]>;
}>;ValuePosition
TypeAliasDeclaration · Source: packages/client/src/ast.ts:14 · Supporting declarations
A column reference or a literal — type-tagged.
export type ValuePosition = {
type: "column";
name: string;
} | {
type: "literal";
value: LitValue;
};ViewChangeListener
TypeAliasDeclaration · Source: packages/client/src/view.ts:30 · Supporting declarations
A per-view change listener ({@link ArrayView.onChanges}): the net FlatChange[] this view folded,
the {@link ChangePhase} it arrived on, and the view's WireSchema (the position→name source for
resolveChange). This is the DIFF the data channel ({@link ArrayView.subscribe}) discards — the
seam a narrator drives off. The schema is passed (not closed over) because the first snapshot
fires synchronously inside materialize, before the caller holds the view handle.
export type ViewChangeListener = (changes: FlatChange[], phase: ChangePhase, schema: WireSchema) => void;ViewTypes
InterfaceDeclaration · Source: packages/client/src/view.ts:16 · Supporting declarations
Per-level column types (parallel to the WireSchema), used to JSON.parse json columns on projection. Built by the Store from the typed schema; absent ⇒ no parsing (bare values).
export interface ViewTypes {
columnTypes: ColType[];
rels: Record<number, ViewTypes>;
}WasmBackend
ClassDeclaration · Source: packages/wasm/src/index.ts:88 · Supporting declarations
export declare class WasmBackend<S extends ColsMap> implements Backend {
private readonly db;
private handler;
private boundaryHandler;
/** Local-only table names (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4): registered UNTRACKED (so the
* optimistic rewind never reverts them) and the only tables {@link writeLocal} accepts. */
private readonly localTables;
private readonly dispatchQueue;
private draining;
constructor(schema: Schema<S>);
/** Register an additional base table after construction — for a SYNTHETIC aggregate table
* (`AGGREGATE-SYNC-DESIGN.md` §3.3) that is not in the typed schema. The
* `NormalizedBackend` registers each such `__agg_*` table once, before a query that reads
* it, so the local engine can join to it (the relationship `count` it backs is shipped by
* the server, never recomputed). Always synced (tracked) — synthetic tables are never local. */
registerTable(name: string, spec: {
columns: string[];
primaryKey: number[];
}): void;
/** Direct-commit a batch of LOCAL-only writes (`201-LOCAL-ONLY-TABLES-DESIGN.md` §6): push them
* straight through the engine on the ordinary delivery path, OUTSIDE any optimistic cycle (a
* local table is untracked, so it never rebases). Rejects a synced/tracked table (M2).
* `onCommitted` fires between the engine commit and subscriber delivery ({@link writeWith}) —
* the post-commit anchor the persistence tap needs (207 §5.1). */
writeLocal(mutations: Mutation[], onCommitted?: () => void): void;
/** Remove a synthetic aggregate table registered by {@link registerTable}, once the last
* query reading it has been unregistered (`AGGREGATE-SYNC-DESIGN.md` §4): frees the engine
* source + its optimistic baseline so aggregate state is reclaimed, not permanent. Throws
* if a query still reads it (the backend refcounts readers, so it calls this only at 0). */
unregisterTable(name: string): void;
registerQuery(qid: QueryId, ast: Ast): void;
unregisterQuery(qid: QueryId): void;
mutate(mutations: Mutation[]): Promise<void>;
onEvent(handler: (qid: QueryId, ev: ChangeEvent) => void): void;
/** Register the Store's commit-boundary handler ({@link Backend.onCommitBoundary}): `dispatch`
* calls it around each commit's per-query batch delivery so the Store can fold every affected
* view before notifying any subscriber. */
onCommitBoundary(handler: (phase: "begin" | "end") => void): void;
/** Deliver one commit's per-query batches. Non-reentrant (#15): if a delivery is already in
* progress (a subscriber re-entered via write()), enqueue and let the active drain pick it
* up FIFO, so each query folds commits in order. Per-query isolated (#11): a view's fold or
* subscriber throwing does NOT drop sibling queries' batches — the first error is re-raised
* only after every query has been delivered.
*
* Cross-view-atomic notification: each commit is bracketed with `boundaryHandler("begin"/"end")`
* so the Store folds ALL of this commit's views before notifying ANY subscriber (a subscriber
* re-reading a sibling view then sees post-commit data). `begin`/`end` stay balanced even if a
* fold throws (the `finally`), and a re-entrant write enqueued during the `end` flush drains as
* its own bracketed commit in the next loop turn — preserving per-query commit order (#15). */
private dispatch;
/** Run `f` against a raw staged write txn (with the §4.1 `get` read path), commit, and
* dispatch the resulting batches on the ordinary event stream. Inside an open server
* batch the engine buffers the events into the cycle instead (commit returns `[]`),
* so re-invocations dispatch nothing here — delivery is `serverBatchEnd`'s.
*
* `onCommitted` runs after `tx.commit()` returns but before `dispatch` delivers to
* subscribers: a throw from `f` (pre-commit — engine untouched) skips it; a subscriber
* throw re-raised by `dispatch` happens after it. It is the only point where "the commit
* is applied" is knowable to a caller that must not confuse the two failure modes. */
writeWith(f: (tx: WasmWriteTxn) => void, onCommitted?: () => void): void;
/** Open a §1.3 reconcile cycle against the coherent server delta: the engine rewinds every
* tracked table (optimistic layer un-applied, delta folded in, each table's baseline re-forked)
* and starts buffering. Re-invoke the still-pending mutators via {@link writeWith}, then call
* {@link serverBatchEnd}. On error the rebase state is poisoned — discard the backend and
* re-hydrate. */
serverBatchBegin(deltas: ServerDeltaOp[]): void;
/** Close the cycle: the whole buffered stream (rewind + re-invocations) coalesces to
* the minimal net per query (§3 — a confirmed-correct prediction delivers nothing)
* and dispatches as ONE batch per affected query on the ordinary event stream. */
serverBatchEnd(): void;
}WasmWriteTxn
InterfaceDeclaration · Source: packages/wasm/src/index.ts:41 · Supporting declarations
A raw staged write transaction over the wasm engine — the surface an optimistic client
mutator runs against (get reads the live state under this txn's staged overlay, so a
read-dependent mutator sees its own writes; OPTIMISTIC-WRITES-DESIGN.md §4.1).
export interface WasmWriteTxn {
add(table: string, row: unknown[]): void;
remove(table: string, row: unknown[]): void;
edit(table: string, oldRow: unknown[], newRow: unknown[]): void;
get(table: string, pk: unknown[]): unknown[] | undefined;
/** Run a one-shot query over the state this txn is mutating — the live base plus this
* txn's own staged writes-so-far (read-your-writes; 203-MUTATOR-READS-DESIGN.md §5.2).
* Synchronous (over a lazy read-cache fork of the staged buffer). Returns the query's rows
* as keyed objects with their materialized `related` children nested by name (identical in
* shape to a `view.data` row), in the query's order. */
query(ast: Ast): unknown[];
commit(): Array<{
queryId: number;
events: unknown[];
}>;
rollback(): void;
}WireNode
InterfaceDeclaration · Source: packages/client/src/types.ts:49 · Supporting declarations
export interface WireNode {
row: WireValue[];
rels: {
rel: number;
children: WireNode[];
}[];
}WireProjection
InterfaceDeclaration · Source: packages/client/src/types.ts:22 · Supporting declarations
A scalar-projection annotation on a relationship slot (REDUCE-DESIGN.md §9): a
relationship aggregate (issue { commentCount: count(comments) }) lives as a singular
one-row relationship whose child is the aggregate row; this tells the receiver to unwrap
it into a scalar field — commentCount: 5 instead of commentCount: [{ count: 5 }].
export interface WireProjection {
/** Which CHILD column to surface as the scalar value (the aggregate column). */
col: number;
/** The value to emit when the relationship is empty (a childless parent) — the
* aggregate identity (`0` for count, `null` for sum/avg). */
identity: WireValue;
}WireRel
InterfaceDeclaration · Source: packages/client/src/types.ts:30 · Supporting declarations
export interface WireRel {
name: string;
slot: number;
/** `null` ⇒ an out-of-view / gating slot (the in-view gate drops `Child`s addressed here). */
child: WireSchema | null;
/** Non-null ⇒ a scalar-projected relationship aggregate the receiver unwraps to a scalar
* (REDUCE-DESIGN.md §9). Absent/`null` for an ordinary (plural or `.one()`) relationship. */
project?: WireProjection | null;
}WireSchema
InterfaceDeclaration · Source: packages/client/src/types.ts:40 · Supporting declarations
export interface WireSchema {
columns: string[];
primaryKey: number[];
/** Resolved, PK-completed sort: `[columnIndex, ascending]` pairs (the comparator input). */
sort: [
number,
boolean
][];
singular: boolean;
relationships: WireRel[];
}WireValue
TypeAliasDeclaration · Source: packages/client/src/types.ts:9 · Supporting declarations
A bare wire cell. (JSON columns arrive as their raw JSON string.)
export type WireValue = number | string | boolean | null;WriteTx
InterfaceDeclaration · Source: packages/client/src/store.ts:44 · Supporting declarations
The write transaction handed to store.write(tx => …). Rows are objects keyed by column;
the Store positionalizes them (and stringifies json columns) before the backend sees them.
add takes an {@link InsertOf} row — a nullable column may be omitted (it is filled with null,
design 206 §7). remove/edit take a full {@link RowOf} row: they identify an EXISTING row, so
every column (nullable ones as their actual T | null value) must be present.
export interface WriteTx<S extends ColsMap> {
add<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): void;
remove<N extends keyof S & string>(table: N, row: RowOf<S[N]>): void;
edit<N extends keyof S & string>(table: N, oldRow: RowOf<S[N]>, newRow: RowOf<S[N]>): void;
}YieldEffect
TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:95 · Supporting declarations
Everything a generator mutator may yield: a write {@link MutationOp}, a point {@link ReadEffect},
a full-query {@link QueryEffect}, or a {@link BatchEffect} fan-out.
export type YieldEffect = MutationOp | ReadEffect | BatchEffect | QueryEffect;