API index and search · Build metadata
Supporting declarations
packages/client/src/schema.ts. These declarations explain referenced types. Only package-page symbols are package exports.
Col
/** 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;
}ColT
export type ColT<X> = X extends Col<infer T> ? T : never;AnyCols
export type AnyCols = Record<string, Col<unknown>>;RowOf
export type RowOf<C extends AnyCols> = {
[K in keyof C]: ColT<C[K]>;
};ColBuilder
/** 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>;
}string
export declare const string: <T extends string = string>() => ColBuilder<T>;number
export declare const number: <T extends number = number>() => ColBuilder<T>;boolean
export declare const boolean: () => ColBuilder<boolean>;json
export declare const json: <T = unknown>() => ColBuilder<T>;int64
/** 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>;SCHEMA
/** Metadata key on a {@link TableDef} (a `unique symbol`, so no column name collides). */
export declare const SCHEMA: unique symbol;TableMeta
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
/** 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";
}TableDef
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
/** 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>;
};AnyTable
export type AnyTable = TableLike<AnyCols>;Row
/** 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"]>;Simplify
/** Flatten an intersection of mapped types into a single object type (preserving `?`/`readonly`) so
* {@link InsertOf} reads as one clean shape in editor hovers, not `A & B`. */
type Simplify<T> = {
[K in keyof T]: T[K];
};IsTop
/** True for the top types `unknown`/`any` (`unknown extends any` too), where `[null] extends [T]` is
* vacuously true and so can't tell a `.nullable()` apart. */
type IsTop<T> = unknown extends T ? true : false;InsertOptional
/** Whether column `X` may be OMITTED from an insert: it admits `null` at the type level — EXCLUDING
* the top types. A bare `json()` is `json<unknown>()`, and `unknown | null` collapses back to
* `unknown`, erasing whether `.nullable()` was applied; treating it as required is the safe
* direction (a `NOT NULL` json column is never wrongly made optional). Declare `json<T>()` to make a
* nullable json column omittable. The runtime companion is `Col.optional` (design 206 §6.2/§7). */
type InsertOptional<X> = IsTop<ColT<X>> extends true ? false : [
null
] extends [
ColT<X>
] ? true : false;InsertOf
/** 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]>;
}>;Insert
/** 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"]>;PkOf
/** 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]>;
};UpdateOf
/** The UPDATE shape of a table: its {@link PkOf primary-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]>;
}>;PkColsOf
/** 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;table
/** `table("issue").columns({ id: string(), … }).primaryKey("id")`. Pass `{ local: true }` for a
* {@link TableMeta.local local-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>;
};
};tableMeta
/** Read a table's metadata (columns / PK / name). */
export declare function tableMeta(t: AnyTable): TableMeta;SchemaOf
/** 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"];
};PkMapOf
/** 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];
};ColsMap
/** name → columns, the resolved schema map carried in the {@link Schema} type. */
export type ColsMap = Record<string, AnyCols>;PkMap
/** 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;
};Schema
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;
}RESERVED_TABLE_PREFIXES
/** 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_"
];isReservedTableName
/** Whether `name` collides with an engine-reserved synthetic prefix ({@link RESERVED_TABLE_PREFIXES}). */
export declare function isReservedTableName(name: string): boolean;createSchema
export declare function createSchema<const T extends readonly AnyTable[]>(opts: {
tables: T;
}): Schema<SchemaOf<T>, PkMapOf<T>>;extendSchema
/** 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>>;ColRefinements
/** 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]>>;
};RefinedCols
/** `C` with the columns named in `R` re-typed to their refined `Col`s. */
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];
};refineTable
/** 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>>;RefinableTable
/** 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>;
};RefinedColsMap
/** `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
/** 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>;isLocalTable
/** Whether `table` is a {@link TableMeta.local local-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;localTableNames
/** 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>;persistedLocalTableNames
/** 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>;localSchemaHash
/** 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;RELATIONSHIP_BRAND
/** Brand on a {@link Relationship} value (a `unique symbol`, distinct from a {@link TableDef}). */
declare const RELATIONSHIP_BRAND: unique symbol;Relationship
/**
* 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;
}AnyRelationship
/** Any relationship, for positions that only read its correlation / child table. */
export type AnyRelationship = Relationship<AnyCols, AnyCols>;rel
/**
* 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>;defineRelationships
/** 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;isRelationship
/** Runtime guard: is `v` a {@link Relationship} value (not a table or a plain object)? */
export declare function isRelationship(v: unknown): v is AnyRelationship;tableSpec
/** The `SchemaSpec` (`columns` + `primaryKey` indices) the wasm `Db.registerTable` wants. */
export declare function tableSpec(meta: TableMeta): {
columns: string[];
primaryKey: number[];
};InsertPlan
/** 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>;
}insertPlan
/** Derive a table's {@link InsertPlan} from its column markers. */
export declare function insertPlan(meta: TableMeta): InsertPlan;insertCell
/** 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;toCell
/** 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;normalizedTableSchemas
/** 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[];
}[];