Rindle

API index and search · Build metadata

Supporting declarations

packages/client/src/view.ts. These declarations explain referenced types. Only package-page symbols are package exports.

Exact source

ViewTypes

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

ChangePhase

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

ViewChangeListener

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

ArrayView

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

SingularArrayView

/** 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

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

rowsEqual

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

FlatArrayView

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