Rindle

API index and search · Build metadata

Supporting declarations

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

Exact source

PkCols

/** Per-table primary-key column indices (into a positional row) — how `(table, pk)` is keyed. */
export type PkCols = Record<string, number[]>;

ColCounts

/** Per-table FULL column count — the width of a base/union row. Needed to scatter a projected
 *  (narrower) wire row into the shared positional layout (PROJECTION-SUPPORT-DESIGN.md §5.3). */
export type ColCounts = Record<string, number>;

UnionCell

/** A union-row cell: a present wire value, or `ABSENT` (`undefined`) when the column is not
 *  present on the shared row. */
type UnionCell = WireValue | undefined;

NormalizedSync

export declare class NormalizedSync {
    private readonly pkCols;
    /** Per-table full width, for scattering projected rows. Empty ⇒ projection unsupported
     *  (every query must be `'*'`), which is the pre-projection behavior. */
    private readonly colCounts;
    /** `(table, pk)` key → the one shared base entry. */
    private readonly base;
    /** queryId → the set of `(table, pk)` keys that query currently footprints. */
    private readonly qfoot;
    /** queryId → (table → the base ColIds it contributes for that table, in the order its wire
     *  rows are positional against — its `required_cols` for the table, §4.1). A query spans
     *  multiple base tables (root + related/EXISTS children), each projected independently; a
     *  table absent from the inner map ⇒ that query syncs it `'*'` (all columns, full width). */
    private readonly qcols;
    constructor(pkCols: PkCols, colCounts?: ColCounts);
    /**
     * Register a query's projection for one base table (PROJECTION-SUPPORT-DESIGN.md §4.1): the
     * base ColIds it contributes for `table`, in the order that table's wire rows are positional
     * against. Omit a table (or never call) for a `'*'` table — it then contributes every column
     * and its rows are full width. Must be called before the query's first batch.
     *
     * A `cols[i] < 0` entry is a DROP sentinel: wire column `i` is an EXPANDED server column the
     * client doesn't have (the server side of an `expand-then-contract` migration), so its cell is
     * discarded rather than scattered. The query still contributes only its real (`>= 0`) ColIds.
     */
    registerProjection(queryId: QueryId, table: string, cols: number[]): void;
    /**
     * Drop a query's projection for one table, reverting it to `'*'` (full presence, rows scattered
     * verbatim). The inverse of {@link registerProjection}; a no-op if none was registered. Needed
     * because `qcols` persists across re-hydrate epochs: if a live subscription's hello narrows from
     * an EXPANDED layout (a `-1`-bearing map) back to an exact full-width one, the stale map must be
     * cleared or it would mis-scatter the now-exact rows. A no-op for an unprojected (`'*'`) table.
     */
    unregisterProjection(queryId: QueryId, table: string): void;
    /**
     * Register a table's primary-key columns after construction — for a **synthetic aggregate
     * table** (`AGGREGATE-SYNC-DESIGN.md` §3.3): a relationship `count` is synced as a
     * server-authoritative `__agg_*` base table that is not in the client's typed schema, so
     * the backend registers it here (and on the local engine) as queries that use it arrive.
     * Idempotent — re-registering the same table (a second query over the same aggregate)
     * is a no-op. Once registered, its rows refcount/GC exactly like any base table.
     */
    registerTable(table: string, primaryKey: number[]): void;
    /**
     * The inverse of {@link registerTable} for a synthetic aggregate table whose last
     * referencing query is gone (`AGGREGATE-SYNC-DESIGN.md` §4): drop its primary-key
     * registration so the table is unknown again. A balanced stream has already GC'd its rows
     * at the `1→0` transition (via {@link dropQuery}); defensively this also sweeps any residual
     * base rows + per-query footprint entries for the table, so a later re-registration of the
     * same name starts clean. A no-op for an unregistered table.
     */
    unregisterTable(table: string): void;
    /**
     * Apply one query's normalized batch (its hydrate snapshot or one transaction's ops) and
     * return the NET base-table mutations to commit to the wasm `Db` in a single transaction.
     * Cross-query refcount + per-query dedup + column union (§4, §5):
     * - `add`: counted into this query's footprint once; on the base `0→1` transition the row
     *   enters at this query's projection; on a `1→N` transition the query's columns are merged
     *   into the shared union (widen) and an `edit` is forwarded if the union changed.
     * - `remove`: on the base `N→0` transition the row leaves; on `N→M>0` the union is recomputed
     *   from the remaining queries and an `edit` narrows the shared row.
     * - `edit`: column-merge this query's cells into the shared union; forward once (idempotent
     *   across queries — same source row, same values).
     */
    applyBatch(queryId: QueryId, ops: NormalizedOp[]): Mutation[];
    /**
     * Re-hydrate one query under a new epoch (§5.3): the server re-sent `queryId`'s whole
     * footprint as seq-0 `add`s. Diff the new footprint against the query's current one —
     * rows only in the old set leave (refcount out, GC/narrow), rows only in the new set enter
     * (refcount in / widen), and an intersecting row whose value changed during the gap is an
     * edit. Only `queryId`'s references move; other queries' counts are untouched. Returns the
     * net mutations to commit.
     */
    rehydrate(queryId: QueryId, snapshot: NormalizedOp[]): Mutation[];
    /**
     * Drop a query (§5.1): decrement its footprint's refcounts, GC each row at the last
     * reference (or narrow the union if others remain), and forget the query. `O(footprint)`,
     * no per-row scan. Returns the net mutations. (The caller also tells the server to
     * deregister the stream.)
     */
    dropQuery(queryId: QueryId): Mutation[];
    /** The number of distinct base rows currently synced into the local store. */
    baseSize(): number;
    /** How many queries currently reference `(table, row)` (0 if absent). The lookup keys by PK,
     *  so a projected `row` need only carry its PK columns at the base positions. */
    refCount(table: string, row: UnionCell[]): number;
    /**
     * The synced (server-authoritative) row currently held for `(table, pkCells)`, or
     * `undefined` if no query references it. `pkCells` are the primary-key cells in
     * `primaryKey` order (NOT a full row) — the same key the cross-query refcount uses.
     *
     * The optimistic aggregate overlay (`AGGREGATE-SYNC-DESIGN.md` §4) reads the server's
     * `__agg` count cell through this so it can compute `displayed = server_base ⊕ delta`
     * from the authoritative base rather than re-deriving it from the local engine's head
     * (which already carries the optimistic layer — a torn read).
     */
    baseRow(table: string, pkCells: WireValue[]): (WireValue | undefined)[] | undefined;
    private footOf;
    /** Scatter a query's (possibly narrower) wire row for `table` into the shared full-width
     *  positional layout. No registered projection for `(queryId, table)` ⇒ a `'*'` table whose
     *  row is already full width (returned as-is). Otherwise allocate a full-width row of
     *  `ABSENT` and place `row[i]` at `cols[i]`. */
    private scatter;
    /** Column-merge `incoming`'s present cells over `e.row` (a widen / pure-value change) and
     *  forward a single `edit` if the union changed. Both rows are full width. */
    private mergeInto;
    private add;
    private remove;
    private removeKey;
    private edit;
    /** The columns present on a `table` row footprinted by `queries`: the union of their
     *  projections for that table. `null` ⇒ all columns present (some referencing query syncs
     *  the table `'*'`), so the row is full. */
    private presentCols;
    private key;
}