Rindle

API index and search · Build metadata

@rindle/remote

0.0.0 · Public export map; development manifest version (0.0.0).

Source revision 05d0bf2c2e56 · build details
Source revision: 05d0bf2c2e56.
TypeScript input SHA-256: aabe6cfcc4172b870d5e272142958e9ea8d8784c2aa23133156e5a7ee633318e
Generated 2026-09-04T23:58:25.590Z with TypeScript 6.0.3. Public TypeScript checks and declaration emit passed. Package runtime tests are separate.

Entry point source

AffinityTicketStore

InterfaceDeclaration · Source: packages/remote/src/affinity.ts:23 · Supporting declarations

A mutable holder for the current opaque affinity ticket, shared by the transport, the source, and the lease POST (see the module header).

export interface AffinityTicketStore {
    /** The ticket to offer on the next ws handshake, or undefined — ticketless, so the edge
     *  anycasts + mints a fresh one. A persisted/previous-connection ticket may be returned here
     *  even while it is NOT yet safe for a lease; see {@link leaseTicket}. */
    get(): string | undefined;
    /** Record a freshly minted/refreshed ticket (from a connection's `{t:"affinity"}` frame). */
    set(ticket: string): void;
    /** Drop the ticket so the next connect goes ticketless (the pinned follower is gone, §8). */
    clear(): void;
    /** Mark the held ticket handshake-only until this connection emits a fresh mint frame. Called
     *  before the first connection and every reconnect, after the held ticket was already selected
     *  for the ws subprotocol offer. This prevents a restored/rotated/expired persisted ticket from
     *  racing an HTTP lease onto an independently-anycast follower. */
    connectionPending(): void;
    /** Resolve with a ticket minted/refreshed on the CURRENT connection, or the next one
     *  {@link set}. A persisted ticket deliberately does not resolve this wait. */
    waitForTicket(): Promise<string>;
    /** Obtain the current connection's ticket for an HTTP lease. The first missing-ticket wait is
     *  bounded; its timeout removes the waiter and latches ticketless fallback, so later leases
     *  return immediately until a mint frame arrives. Concurrent callers share the one timer.
     *  `timedOut` is true only for that transition, allowing one warning per fallback episode. */
    leaseTicket(timeoutMs: number): Promise<{
        ticket?: string;
        timedOut: boolean;
    }>;
}

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;
};

Batch

InterfaceDeclaration · Source: packages/remote/src/protocol.ts:102 · Supporting declarations

One transaction's flat changes (or the seq-0 hydrate snapshot). events apply in order.

export interface Batch {
    epoch: number;
    seq: number;
    schemaFp: string;
    events: FlatChange[];
}

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";

ClientMsg

TypeAliasDeclaration · Source: packages/remote/src/protocol.ts:123 · Supporting declarations

export type ClientMsg = {
    t: "init";
    clientID: string;
} | SubscribeClientMsg | {
    t: "unsubscribe";
    queryId: number;
} | {
    t: "mutate";
    mutations: Mutation[];
} | {
    t: "pushMutation";
    envelope: MutationEnvelope;
};

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;
};

CorrelatedSubquery

InterfaceDeclaration · Source: packages/client/src/ast.ts:58 · Supporting declarations

export interface CorrelatedSubquery {
    correlation: Correlation;
    subquery: Ast;
    system?: "client" | "permissions" | "test";
}

Correlation

InterfaceDeclaration · Source: packages/client/src/ast.ts:45 · Supporting declarations

export interface Correlation {
    parentField: string[];
    childField: string[];
}

createAffinityTicketStore

FunctionDeclaration · Source: packages/remote/src/affinity.ts:57 · Supporting declarations

Build an {@link AffinityTicketStore}, optionally persisted. Pure/in-memory when persist is omitted.

export declare function createAffinityTicketStore(persist?: TicketPersistence): AffinityTicketStore;

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>;

createQueuedMutationSender

FunctionDeclaration · Source: packages/remote/src/mutation-queue.ts:66 · Supporting declarations

A MutationEnvelopeSender (the pushMutation option of RemoteOptimisticSource) that queues envelopes and delivers them as confirmed, in-order batches.

export declare function createQueuedMutationSender(opts: QueuedMutationSenderOptions): MutationEnvelopeSender;

createRemoteNormalizedSource

FunctionDeclaration · Source: packages/remote/src/remote-source.ts:208 · Supporting declarations

Convenience: a RemoteNormalizedSource over a ws URL or a custom transport.

export declare function createRemoteNormalizedSource(urlOrTransport: string | Transport, opts?: RemoteNormalizedSourceOptions): RemoteNormalizedSource;

createRemoteOptimisticSource

FunctionDeclaration · Source: packages/remote/src/optimistic-source.ts:554 · Supporting declarations

Convenience: a RemoteOptimisticSource over a ws URL or a custom transport. A URL becomes a replaceable connection seeded at that endpoint (so a routed lease can still migrate it); a pre-built transport stays fixed.

export declare function createRemoteOptimisticSource(urlOrTransport: string | Transport, clientID: string, opts?: RemoteOptimisticSourceOptions): RemoteOptimisticSource;

createRemoteStore

FunctionDeclaration · Source: packages/remote/src/backend.ts:214 · Supporting declarations

Convenience: a Store backed by a remote server, over a ws URL or a custom transport.

export declare function createRemoteStore<S extends ColsMap>(schema: Schema<S>, urlOrTransport: string | Transport, opts?: RemoteBackendOptions): Store<S>;

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>;

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.

  • complete waits for the server-authoritative result (the default).
  • present resolves 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>;

Hello

InterfaceDeclaration · Source: packages/remote/src/protocol.ts:94 · Supporting declarations

The subscription handshake, sent once before any {@link Batch}.

export interface Hello {
    epoch: number;
    comparatorVersion: number;
    schema: WireSchema;
    schemaFp: string;
}

ilike

VariableDeclaration · Source: packages/client/src/operators.ts:34 · Supporting declarations

export declare const ilike: (pattern: string) => Pred<string>;

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;
}

MutationEnvelopeSender

TypeAliasDeclaration · Source: packages/remote/src/subscribe.ts:24 · Supporting declarations

export type MutationEnvelopeSender = (envelope: MutationEnvelope) => void | PromiseLike<void>;

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/args are 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 clientq(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 queryName and a resolve that re-runs the SAME validator on untrusted wire args and builds the AUTHORITATIVE Query from the server's own ctx ({@link registerQueries registerQueries} 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>;

NormalizedBatch

InterfaceDeclaration · Source: packages/remote/src/normalized.ts:102 · Supporting declarations

One committed transaction's normalized ops (or the seq-0 hydrate snapshot). cv (the global commit version the frame reflects) is stamped by optimistic-protocol servers and rides through to the NormalizedEvent (OPTIMISTIC-WRITES-DESIGN.md §8.3).

export interface NormalizedBatch {
    epoch: number;
    seq: number;
    normalizedFp: string;
    ops: NormalizedOp[];
    cv?: number;
}

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;
};

normalizedFp

FunctionDeclaration · Source: packages/remote/src/normalized.ts:22 · Supporting declarations

A tables set's content fingerprint — FNV-1a 64 over the canonical, length-prefixed byte stream of rindle-replica::normalize_protocol::normalized_fp (PK resolved to column NAMES), as 16-char lowercase hex (=== the Rust hex). tables MUST be sorted by name (the server's NormalizedPublisher guarantees it) so the fingerprint is order-stable.

export declare function normalizedFp(tables: NormalizedTableSchema[]): string;

NormalizedHello

InterfaceDeclaration · Source: packages/remote/src/normalized.ts:92 · Supporting declarations

The slim normalized handshake (§3), sent once before any {@link NormalizedBatch}.

export interface NormalizedHello {
    epoch: number;
    comparatorVersion: number;
    tables: NormalizedTableSchema[];
    normalizedFp: string;
}

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;
}

NormalizedSubscriber

ClassDeclaration · Source: packages/remote/src/normalized.ts:115 · Supporting declarations

Receiver side: validates a normalized frame stream (comparator at hello; per batch — epoch match, fingerprint match, strict in-order seq) and emits clean NormalizedEvents. It does NOT fold (the NormalizedSync layer does). Mirrors the flat {@link Subscriber }.

export declare class NormalizedSubscriber {
    readonly epoch: number;
    readonly normalizedFp: string;
    private readonly emit;
    private phase;
    private lastSeq;
    /**
     * @param hello         the server's normalized handshake.
     * @param emit          clean-event sink (the `NormalizedSync` fold).
     * @param clientTables  the CLIENT's own typed per-table schemas (all tables). When given,
     *   each table the hello advertises is validated by NAME against it (column order + PK
     *   indices). The hello's `tables` is a per-query SUBSET (the query's table tree), so this
     *   checks each advertised table rather than one global fingerprint. A mismatch (routine
     *   deployment / schema skew) is rejected here — without it, positional rows aligned to the
     *   SERVER's column order are stored verbatim under the CLIENT's order, silently swapping
     *   cells and mis-keying the refcount/GC (CRIT#4 / §3 "drift ⇒ re-subscribe").
     */
    constructor(hello: NormalizedHello, emit: (ev: NormalizedEvent) => void, clientTables?: NormalizedTableSchema[]);
    /** Apply one normalized batch (or the seq-0 snapshot). Returns `"duplicate"` for an
     *  already-applied seq; throws {@link ProtocolError} on a gap / epoch / fp mismatch (the
     *  caller re-hydrates under a new epoch). */
    apply(batch: NormalizedBatch): "applied" | "duplicate";
}

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>;

offerSubprotocols

FunctionDeclaration · Source: packages/remote/src/affinity.ts:134 · Supporting declarations

The subprotocol list to offer for one connection: the base protocol always, plus the held ticket (already an aff.<…> token) when one exists.

export declare function offerSubprotocols(store: AffinityTicketStore): string[];

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;
}

ProtocolError

ClassDeclaration · Source: packages/remote/src/protocol.ts:204 · Supporting declarations

A protocol violation. All but a duplicate (handled silently) are unrecoverable for the current subscription — the RemoteBackend re-hydrates under a new epoch.

export declare class ProtocolError extends Error {
    readonly kind: ProtocolErrorKind;
    constructor(kind: ProtocolErrorKind, message: string);
}

ProtocolErrorKind

TypeAliasDeclaration · Source: packages/remote/src/protocol.ts:200 · Supporting declarations

export type ProtocolErrorKind = "comparator-mismatch" | "epoch-mismatch" | "schema-mismatch" | "gap";

Publisher

ClassDeclaration · Source: packages/remote/src/protocol.ts:166 · Supporting declarations

Sender side: stamps batches with the subscription epoch + schemaFp and drives the gap-free seq. The hydrate snapshot is seq 0; increments are seq 1, 2, …. An empty transaction emits no batch and consumes no seq (so a gap always means a lost batch).

export declare class Publisher {
    readonly epoch: number;
    readonly schema: WireSchema;
    readonly schemaFp: string;
    private nextSeq;
    constructor(epoch: number, schema: WireSchema);
    hello(): Hello;
    /** The hydrate snapshot (seq 0). Always emitted, even when empty, so the receiver learns it. */
    snapshot(events: FlatChange[]): Batch;
    /** One transaction's events — `null` for an empty transaction (no batch, no seq consumed). */
    commit(events: FlatChange[]): Batch | null;
    private emit;
}

PushOutcome

InterfaceDeclaration · Source: packages/remote/src/mutation-queue.ts:19 · Supporting declarations

One envelope's outcome from the API server's mutate endpoint.

export interface PushOutcome {
    accepted: boolean;
    reason?: string;
}

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) or q(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;

QueuedMutationSenderOptions

InterfaceDeclaration · Source: packages/remote/src/mutation-queue.ts:24 · Supporting declarations

export interface QueuedMutationSenderOptions {
    /** Deliver one in-order batch; resolve once the daemon durably processed it (i.e. the
     *  API server awaited its daemon call before responding). Throw to have the SAME batch
     *  retried. Return per-envelope outcomes, or void when everything was accepted. */
    send: (envelopes: MutationEnvelope[]) => Promise<PushOutcome[] | void>;
    /** A policy rejection for one envelope (never retried; the prediction snaps back via the
     *  lmid release — this callback is where the reason reaches the app). */
    onRejected?: (envelope: MutationEnvelope, reason: string) => void;
    /** A failed flush attempt, before its retry. The ONLY surface for a transport/authority
     *  failure: `send` throwing is otherwise absorbed by the retry loop, so a queue built without
     *  this hook fails perfectly silently while head-of-line blocking every later mutation. */
    onError?: (err: unknown, attempt: number) => void;
    /** Backoff before retry `attempt` (1-based). Default: 200ms · 2^(attempt-1), capped at 5s. */
    retryDelayMs?: (attempt: number) => number;
    /** Max envelopes per flush. Default 32. */
    maxBatch?: number;
}

RawMutationSender

TypeAliasDeclaration · Source: packages/remote/src/subscribe.ts:22 · Supporting declarations

export type RawMutationSender = (mutations: Mutation[]) => void | PromiseLike<void>;

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;
}

RemoteBackend

ClassDeclaration · Source: packages/remote/src/backend.ts:48 · Supporting declarations

export declare class RemoteBackend implements Backend {
    private readonly transport;
    private readonly resolveSubscribe;
    private readonly sendMutation?;
    private handler;
    private readonly devObservers;
    private readonly subs;
    constructor(transport: Transport, opts?: RemoteBackendOptions);
    registerQuery(qid: QueryId, _ast: unknown, remote?: RemoteQuery): void;
    unregisterQuery(qid: QueryId): void;
    /** Eventually-consistent: the mutation is sent; the resulting batches arrive async on the
     *  stream. The promise resolves once sent (the §2.3 write asymmetry, named not leaked). */
    mutate(mutations: Mutation[]): Promise<void>;
    onEvent(handler: (qid: QueryId, ev: ChangeEvent) => void): void;
    __attachDevtoolsServerDeltas(observer: BackendDevObserver): () => void;
    private subscribe;
    private onServerMsg;
    /** Route a `queryError` by its 101 §5 classification: retryable ⇒ keep the QState (and the
     *  rows already folded downstream — 101 §6) and re-subscribe after a jittered backoff
     *  honoring `retryAfterMs`; terminal (or pre-classification servers) ⇒ drop the
     *  subscription, exactly as before (FOLLOWER-LAG-SHED §6.3). */
    private onQueryError;
    private openSubscriber;
    private applyBatch;
    private emitServerEvent;
}

RemoteBackendOptions

InterfaceDeclaration · Source: packages/remote/src/backend.ts:41 · Supporting declarations

export interface RemoteBackendOptions {
    /** Resolve the upstream subscribe target. Defaults to embedded-server `{name,args}`. */
    resolveSubscribe?: SubscribeResolver;
    /** Override raw authoritative writes, e.g. POST them to an app API server. */
    sendMutation?: RawMutationSender;
}

RemoteNormalizedSource

ClassDeclaration · Source: packages/remote/src/remote-source.ts:58 · Supporting declarations

export declare class RemoteNormalizedSource implements NormalizedSource {
    private readonly transport;
    private readonly resolveSubscribe;
    private readonly sendMutation?;
    private handler;
    private readonly subs;
    /** The client's own typed per-table schemas, for hello validation (CRIT#4); set by the backend. */
    private clientTables;
    constructor(transport: Transport, opts?: RemoteNormalizedSourceOptions);
    expectClientSchema(tables: NormalizedTableSchema[]): void;
    registerQuery(qid: QueryId, remote: RemoteQuery): void;
    unregisterQuery(qid: QueryId): void;
    mutate(mutations: Mutation[]): Promise<void>;
    onNormalized(handler: (qid: QueryId, ev: NormalizedEvent) => void): void;
    private subscribe;
    private onServerMsg;
    /** Route a `queryError` by its 101 §5 classification: retryable ⇒ keep the QState (and the
     *  rows already folded downstream — 101 §6) and re-subscribe after a jittered backoff
     *  honoring `retryAfterMs`; terminal (or pre-classification servers) ⇒ drop the
     *  subscription, exactly as before (FOLLOWER-LAG-SHED §6.3). */
    private onQueryError;
    private openSubscriber;
    private applyBatch;
}

RemoteNormalizedSourceOptions

InterfaceDeclaration · Source: packages/remote/src/remote-source.ts:51 · Supporting declarations

export interface RemoteNormalizedSourceOptions {
    /** Resolve the upstream subscribe target. Defaults to embedded-server `{name,args}`. */
    resolveSubscribe?: SubscribeResolver;
    /** Override raw authoritative writes, e.g. POST them to an app API server. */
    sendMutation?: RawMutationSender;
}

RemoteOptimisticConnection

TypeAliasDeclaration · Source: packages/remote/src/optimistic-source.ts:77 · Supporting declarations

How the source obtains its transport:

  • a pre-built {@link Transport} (or { transport }) — FIXED: no endpoint migration, wsEndpoint on leases is ignored (in-process / tests / a single static daemon);
  • { factory, endpoint? } — REPLACEABLE: transports are built on demand. An initial endpoint (a static wsUrl or an SSR-injected bootstrap) opens eagerly; otherwise the first lease's wsEndpoint opens it lazily, and a later lease naming a DIFFERENT endpoint migrates the whole session there.
export type RemoteOptimisticConnection = Transport | {
    transport: Transport;
} | {
    factory: TransportFactory;
    endpoint?: string;
};

RemoteOptimisticSource

ClassDeclaration · Source: packages/remote/src/optimistic-source.ts:94 · Supporting declarations

export declare class RemoteOptimisticSource implements OptimisticSource {
    /** The CURRENT transport (undefined in pure-lazy mode until the first lease opens one). */
    private transport;
    /** The ws endpoint the current transport points at (undefined for a fixed transport). */
    private currentEndpoint;
    /** Builds a transport for an endpoint; undefined ⇒ fixed transport (no migration). */
    private readonly transportFactory;
    private readonly clientID;
    private readonly resolveSubscribe;
    private readonly pushMutationSender?;
    /** Resolve the current affinity ticket store. A thunk lets the one-call client turn affinity on
     *  after its first pure-lazy lease returns a placement ticket, before it opens the socket. */
    private readonly affinityStore;
    private handler;
    private progressHandler;
    private restartHandler;
    private outcomeHandler;
    private resyncHandler;
    /** Set by {@link resync} when this is a LEASE-AUTH session (some sub presented a `leaseToken`):
     *  transport pushes queue in {@link pendingPushes} until the first authenticated re-subscribe's
     *  hello re-establishes the socket's subject, then flush (see QState.authed). Never set on a
     *  token-less (embedded/rindled) session — its pushes need no subject and go straight out. */
    private awaitingAuthedHello;
    /** Envelopes held while {@link awaitingAuthedHello} (H-v §7.5 rule 3). Every entry corresponds
     *  to a still-pending backend mutation (the re-send reconstructs from pending entries; app
     *  invokes in the window are pending by definition), so a superseding resync may CLEAR this —
     *  its own re-send regenerates whatever still matters. */
    private pendingPushes;
    private readonly subs;
    /** Queries whose subscribe is waiting for a transport to exist — endpoint-less subscribes issued
     *  in pure-lazy mode before any lease opens a transport (the lmid system query is registered by
     *  the backend at construction). Flushed when a transport comes up. */
    private readonly deferred;
    /** One warning per source when an APP lease resolves without a `wsEndpoint` in pure-lazy mode —
     *  nothing will ever open the transport, which is otherwise silent (views just stay empty). */
    private warnedEndpointlessLease;
    /** The daemon's boot id (from each `nhello`); a change means it restarted. */
    private lastBootId;
    /** The client's own typed per-table schemas, for hello validation (CRIT#4); set by the backend. */
    private clientTables;
    /** Once true (set by {@link close}), in-flight lease resolutions are inert — they must not open a
     *  new transport or send after teardown. */
    private closed;
    /** True once any lease has carried a routed `wsEndpoint`. Gates onDown re-leasing so a single
     *  UNROUTED daemon keeps its pre-router behavior (recover via reconnect→resync only), not an extra
     *  lease POST during an outage. */
    private sawRoutedEndpoint;
    /** Bumped at the start of every re-subscribe-all pass. A migrate triggered mid-pass starts a new
     *  pass (higher generation); the outer pass then aborts instead of re-subscribing queries twice. */
    private resubscribeGen;
    constructor(connection: RemoteOptimisticConnection, clientID: string, opts?: RemoteOptimisticSourceOptions);
    /** Wire a transport's handlers (no `init`). */
    private attach;
    /** Make `transport` the current one, announce identity, and (re)subscribe anything deferred. */
    private bringUp;
    /** Build + bring up a fresh transport to `endpoint` (replaceable mode only). */
    private openEndpoint;
    /** Migrate the whole session to a new follower (§2.3): build the new transport, tear the old one
     *  down, and re-subscribe EVERY active query there (re-leasing — the old tokens are
     *  follower-local and invalid on the new node). */
    private migrate;
    /** Re-subscribe every live query on the current transport (each re-resolves its lease). A
     *  re-subscribe can synchronously trigger a `migrate` (lease names a new endpoint), whose own
     *  re-subscribe pass supersedes this one — the generation check then aborts this pass so a query
     *  is never re-subscribed (and re-leased) twice. */
    private resubscribeAll;
    /** Flush subscribes deferred until a transport existed (e.g. the lmid query in pure-lazy mode). */
    private flushDeferred;
    /** The current follower's ws is sustainedly down — re-lease every query. The router returns a
     *  (possibly new) `wsEndpoint`: a changed one migrates the session; an unchanged one re-subscribes
     *  over the reconnecting transport (READ-ROUTER-DESIGN.md §3). No-op for an UNROUTED daemon (no
     *  lease ever carried a `wsEndpoint`) — there is nowhere to move, so we keep the pre-router
     *  behavior and let the transport's own reconnect→resync recover. */
    private onDown;
    /** Tear down the current transport and make any in-flight lease resolution inert (a late lease
     *  must NOT open a new transport after the consumer closed the client). */
    close(): void;
    /** Register a handler fired when the DAEMON restarts (a new boot id) — the backend resets its
     *  `cv` watermark so the new daemon's reset `cv` sequence is accepted instead of dropped. */
    onRestart(handler: () => void): void;
    expectClientSchema(tables: NormalizedTableSchema[]): void;
    registerQuery(qid: QueryId, remote: RemoteQuery): void;
    unregisterQuery(qid: QueryId): void;
    pushMutation(envelope: MutationEnvelope): Promise<void>;
    onNormalized(handler: (qid: QueryId, ev: NormalizedEvent) => void): void;
    onProgress(handler: (frame: ProgressFrame) => void): void;
    /** The room deopt handshake's verdict stream (H-v). Dispatched OUT-OF-BAND on arrival — see
     *  {@link onServerMsg}'s `mutationOutcome` arm for why it must never wait behind the cv buffer. */
    onMutationOutcome(handler: (frame: MutationOutcomeFrame) => void): void;
    /** Fired once per re-established session, SYNCHRONOUSLY inside {@link resync} — before any
     *  post-reconnect frame can release (the §7.5 rule-3 window: a replayed lmid snapshot must not
     *  retire an entry whose outcome frame died with the old socket before the re-send captured
     *  it). The backend re-sends the domain's unconfirmed pending envelopes with their original
     *  mids; on a lease-auth session their DELIVERY is deferred until the first token hello
     *  re-authenticates the socket ({@link pendingPushes}). */
    onResync(handler: () => void): void;
    private subscribe;
    private onServerMsg;
    /** On reconnect: re-announce identity, fire the `onResync` re-send, and re-subscribe every live
     *  query (each re-resolves its lease, so a restarted daemon re-materializes + re-leases on the
     *  transiently). The re-send fires HERE — synchronously, before any post-reconnect frame can be
     *  processed — because the §7.5 rule-3 window closes fast: the re-subscribed lmid stream's
     *  fresh snapshot may cover a mid whose outcome frame died with the OLD socket, and once the
     *  release retires that entry as an apparent success there is nothing left to re-send (the
     *  lost-deopt write would silently vanish). Firing now captures the in-flight set intact; on a
     *  lease-auth session the envelopes themselves are HELD ({@link pendingPushes}) until the first
     *  token re-subscribe's hello re-authenticates the socket, then flush in order — so the shell's
     *  subject gate never refuses them, and its re-answer (a recorded outcome for any non-applied
     *  mid) resolves even an already-retired entry via the handshake's not-found arm. */
    private resync;
    /** Track the daemon's boot id; a change (after the first) means it restarted — fire onRestart. */
    private observeBootId;
    /** Route a `queryError` by its 101 §5 classification: retryable ⇒ keep the QState (and the
     *  rows already folded downstream — 101 §6) and re-subscribe after a jittered backoff
     *  honoring `retryAfterMs`; terminal (or pre-classification servers) ⇒ drop the
     *  subscription, exactly as before. Fixes the stranded-client gap: a worker fault's or a
     *  shedding follower's error now heals end-to-end (FOLLOWER-LAG-SHED §6.3). */
    private onQueryError;
    private openSubscriber;
    private applyBatch;
}

RemoteOptimisticSourceOptions

InterfaceDeclaration · Source: packages/remote/src/optimistic-source.ts:82 · Supporting declarations

export interface RemoteOptimisticSourceOptions {
    /** Resolve the upstream subscribe target. Defaults to embedded-server `{name,args}`. */
    resolveSubscribe?: SubscribeResolver;
    /** Override named-mutator delivery, e.g. POST envelopes to the app API server. */
    pushMutation?: MutationEnvelopeSender;
    /** Follower-affinity mode (FOLLOWER-AFFINITY-DESIGN.md §3): the shared ticket store. When set, the
     *  source records the follower's minted ticket from the `{t:"affinity"}` frame and CLEARS it on a
     *  sustained outage so the next (ticketless) reconnect anycasts to a live follower and re-pins
     *  (§8). Absent ⇒ affinity off (today's behavior). */
    affinity?: AffinityTicketStore | (() => AffinityTicketStore | undefined);
}

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. STAYS complete while 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 (see designs/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's pending(qid) / onPending), not folded into this type. A backend with no server lifecycle (the in-process engine) leaves every view complete.
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;

schemaFp

FunctionDeclaration · Source: packages/remote/src/protocol.ts:85 · Supporting declarations

A WireSchema's content fingerprint — FNV-1a 64 over the canonical, length-prefixed byte stream of src/wire_schema.rs, rendered as 16-char lowercase hex (=== Rust SchemaFp Display). A string (not a JS number) so the full 64 bits survive JSON without precision loss.

export declare function schemaFp(ws: WireSchema): string;

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"];
};

ServerMsg

TypeAliasDeclaration · Source: packages/remote/src/protocol.ts:130 · Supporting declarations

export type ServerMsg = {
    t: "hello";
    queryId: number;
    hello: Hello;
} | {
    t: "batch";
    queryId: number;
    batch: Batch;
} | {
    t: "nhello";
    queryId: number;
    hello: NormalizedHello;
    bootId?: string;
} | {
    t: "nbatch";
    queryId: number;
    batch: NormalizedBatch;
} | {
    t: "queryError";
    queryId: number;
    message: string;
    code?: string;
    retryable?: boolean;
    retryAfterMs?: number;
} | {
    t: "progress";
    frame: ProgressFrame;
} | {
    t: "affinity";
    ticket: string;
} | {
    t: "mutationOutcome";
    mid: number;
    kind: "deopt" | "rejected";
    reason?: string;
    name?: string;
    args?: unknown;
} | {
    t: "error";
    queryId?: number;
    message: string;
};

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>;
}

shared

FunctionDeclaration · Source: packages/client/src/mutation-ops.ts:180 · Supporting declarations

Co-locate a shared (generator) mutator with the validator for its args — pairing the arg SHAPE with the body that consumes it at ONE site, so neither tier restates it (the client derives its callsite type from Args; the server parses untrusted args through .args). Returns the SAME generator function with an args property attached (Object.assign mutates + returns it), so the registered value is byte-for-byte what the client drove before: {@link isGeneratorMutator} still detects it and it still satisfies ClientRegistry.

export declare function shared<Args, Ctx extends MutatorCtx = MutatorCtx>(args: ArgSchema<Args>, run: SharedMutator<Args, Ctx>): SharedMutatorWithArgs<Args, Ctx>;

SharedMutator

TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:152 · Supporting declarations

A generator (isomorphic) mutator, shared verbatim by both tiers: the client trusts typed args, the server parses untrusted args into Args before invoking.

export type SharedMutator<Args, Ctx extends MutatorCtx = MutatorCtx> = (tx: IsoTx, args: Args, ctx: Ctx) => MutationGen;

SharedMutatorWithArgs

TypeAliasDeclaration · Source: packages/client/src/mutation-ops.ts:170 · Supporting declarations

A shared mutator that CARRIES its own arg validator, co-located at the def site (shared(schema, gen)). The client registers it exactly like a bare generator mutator — the .args validator is inert there (typed callsites skip the parse); the server ({@link runSharedMutation }, via the api-server's sharedApiMutators) reads .args to parse untrusted wire args before driving the SAME body.

export type SharedMutatorWithArgs<Args, Ctx extends MutatorCtx = MutatorCtx> = SharedMutator<Args, Ctx> & {
    args: ArgSchema<Args>;
};

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;

SubscribeClientMsg

TypeAliasDeclaration · Source: packages/remote/src/protocol.ts:119 · Supporting declarations

The multiplexed wire messages (many queries over one connection, tagged by queryId). subscribe.mode selects the serializer (default flat); a normalized subscription gets nhello/nbatch back instead of hello/batch (NORMALIZED-CHANGES-DESIGN.md §6). Embedded servers receive {name,args}. Daemon/serverless deployments can instead receive an opaque leaseToken that the app API server minted after auth + named-query resolution.

The OPTIMISTIC path (OPTIMISTIC-WRITES-DESIGN.md §8) adds init (the connection identifies its stable clientID, so progress frames can carry that client's lmid) and pushMutation (one named-mutator envelope up); the server answers with the normalized frames (cv-stamped) plus connection-level progress frames.

export type SubscribeClientMsg = {
    t: "subscribe";
    queryId: number;
    name: string;
    args: unknown;
    mode?: "flat" | "normalized";
} | {
    t: "subscribe";
    queryId: number;
    leaseToken: string;
    mode?: "flat" | "normalized";
};

SubscribeMode

TypeAliasDeclaration · Source: packages/remote/src/subscribe.ts:5 · Supporting declarations

export type SubscribeMode = "flat" | "normalized";

Subscriber

ClassDeclaration · Source: packages/remote/src/protocol.ts:216 · Supporting declarations

Receiver side: validates a frame stream (comparator at hello; per batch — epoch match, schema-fp match, strict in-order seq) and emits the clean hello/snapshot/batch ChangeEvents. It does NOT fold (the core ArrayView does) — the §2.2 split.

export declare class Subscriber {
    readonly epoch: number;
    readonly schemaFp: string;
    private readonly emit;
    private phase;
    private lastSeq;
    constructor(hello: Hello, emit: (ev: ChangeEvent) => void);
    /** Apply one incremental batch (or the seq-0 snapshot). Returns `"duplicate"` for an
     *  already-applied seq (discarded — rc ops are not idempotent); throws {@link ProtocolError}
     *  on a gap / epoch / schema mismatch (the caller re-hydrates). */
    apply(batch: Batch): "applied" | "duplicate";
}

SubscribeRequest

InterfaceDeclaration · Source: packages/remote/src/subscribe.ts:7 · Supporting declarations

export interface SubscribeRequest {
    queryId: QueryId;
    remote: RemoteQuery;
    mode: SubscribeMode;
}

SubscribeResolver

TypeAliasDeclaration · Source: packages/remote/src/subscribe.ts:20 · Supporting declarations

export type SubscribeResolver = (request: SubscribeRequest) => SubscribeTarget | PromiseLike<SubscribeTarget>;

SubscribeTarget

TypeAliasDeclaration · Source: packages/remote/src/subscribe.ts:13 · Supporting declarations

export type SubscribeTarget = {
    name: string;
    args: unknown;
    leaseToken?: never;
    wsEndpoint?: never;
} | {
    leaseToken: string;
    wsEndpoint?: string;
    name?: never;
    args?: never;
};

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[];
};

TicketPersistence

InterfaceDeclaration · Source: packages/remote/src/affinity.ts:49 · Supporting declarations

Where a store persists the ticket across reloads. The browser backs this with sessionStorage (per-TAB, so two tabs can pin two regions — design §13); tests pass none (pure in-memory).

export interface TicketPersistence {
    load(): string | undefined;
    save(ticket: string): void;
    clear(): void;
}

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;

Transport

InterfaceDeclaration · Source: packages/remote/src/transport.ts:7 · Supporting declarations

export interface Transport {
    /** Send a message up to the server. */
    send(msg: ClientMsg): void;
    /** Register the single handler for incoming server messages. */
    onMessage(handler: (msg: ServerMsg) => void): void;
    /** Register a handler fired after the connection is RE-established (not the first open) — the
     *  source uses it to re-`init` + re-subscribe so a dropped/restarted daemon heals. Optional:
     *  transports without reconnect (mocks, in-process) may omit it. */
    onReconnect?(handler: () => void): void;
    /** Register a handler fired when the connection is SUSTAINEDLY down — repeated reconnects to the
     *  same endpoint have failed (a dead/removed follower, READ-ROUTER-DESIGN.md §3). The source uses
     *  it to re-lease: the router returns a (possibly new) `wsEndpoint`, and a changed one migrates the
     *  whole session off the dead node. Optional: transports without failover (mocks, in-process,
     *  fixed endpoints) may omit it. */
    onDown?(handler: () => void): void;
    /** Tear down the connection. */
    close(): void;
}

TransportFactory

TypeAliasDeclaration · Source: packages/remote/src/optimistic-source.ts:68 · Supporting declarations

Build a transport to a follower's public ws endpoint (READ-ROUTER-DESIGN.md §2.3). Default (endpoint) => new WsTransport(endpoint).

export type TransportFactory = (endpoint: string) => Transport;

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>;
}

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;
}

WS_SUBPROTOCOL

VariableDeclaration · Source: packages/remote/src/affinity.ts:19 · Supporting declarations

The base ws subprotocol the browser offers alongside its aff.* ticket; the fleet follower echoes it on the 101 (a strict browser closes a socket whose selected subprotocol it never offered, so the base is always offered in affinity mode). Inlined — NOT imported from @rindle/affinity's WS_SUBPROTOCOL — to keep that crate's node:crypto out of the browser bundle (the same duplicate-to-stay-bundle-clean discipline the lease wire types use). Keep the two spellings in lock-step.

export declare const WS_SUBPROTOCOL = "rindle.v1";

WsTransport

ClassDeclaration · Source: packages/remote/src/transport.ts:30 · Supporting declarations

A Transport over a WebSocket (text JSON frames). Messages sent before the socket opens are buffered and flushed on open (so registerQuery/mutate can be called eagerly). Reconnects with capped exponential backoff: if the socket drops (e.g. the daemon restarted) it reopens and fires onReconnect so the source rebuilds its subscriptions.

export declare class WsTransport implements Transport {
    private readonly url;
    /** Reads the subprotocols to offer at each (re)connect — in affinity mode, `["rindle.v1", "aff.…"]`
     *  with the CURRENT ticket (FOLLOWER-AFFINITY-DESIGN.md §5). Undefined ⇒ offer none (today's
     *  single-daemon behavior, byte-identical). Evaluated per connect so a reconnect presents the
     *  freshest (or freshly cleared) ticket. */
    private readonly subprotocols?;
    private ws;
    private handler;
    private reconnectHandler;
    private downHandler;
    /** Buffered as PRE-SERIALIZED frames: serialization happens at `send` time so an
     *  unserializable message (a `bigint` query arg — `JSON.stringify` throws on bigint)
     *  throws typed INTO ITS CALLER instead of detonating later inside the socket's
     *  `open` listener, where it would strand every frame queued behind it. */
    private readonly pending;
    private open;
    private everOpened;
    private closedByUser;
    private attempt;
    private reconnectTimer;
    /** Failed reconnect attempts after which the connection is declared "down" (fires `onDown`). */
    private readonly downThreshold;
    /** True once `onDown` has fired for the CURRENT down episode; reset on the next successful open
     *  so a later outage fires again (but a single episode fires `onDown` exactly once — no re-lease
     *  storm while a follower is gone). */
    private downFired;
    constructor(url: string, opts?: {
        downThreshold?: number;
        subprotocols?: () => string[];
    });
    private connect;
    private scheduleReconnect;
    send(msg: ClientMsg): void;
    onMessage(handler: (msg: ServerMsg) => void): void;
    onReconnect(handler: () => void): void;
    onDown(handler: () => void): void;
    close(): 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;