Rindle

API index and search · Build metadata

Supporting declarations

packages/api-server/src/rooms.ts. These declarations explain referenced types. Only package-page symbols are package exports.

Exact source

RoomProfile

/**
 * One NAMED room profile (§2.1). Rooms are document-scoped, not query-scoped: a profile is what
 * stands up and feeds a room — the flat `resolveFootprint` promoted to a named, reusable unit.
 * Labeled queries opt into a profile; the wire room key for a named profile is minted SERVER-side
 * as `"<profile>/<key>"`, and `/room-boot` splits it back to resolve the footprint.
 */
export interface RoomProfile<User = unknown> {
    /** Derive the profile-local room key from the (label-mapped) query args. Runs server-side under
     *  authoritative inputs — the client learns the key from the lease, it never computes one. */
    key: (args: any) => string;
    /** Build the room's canonical footprint — the replica boundary the room loads and follows —
     *  from the profile-local doc key (this profile's `key` output; what `/room-boot` receives
     *  after the prefix split). MUST be unwindowed (§2.3): no `limit`/`start`/`one` anywhere in the
     *  AST — enforced loudly at construction when statically resolvable, and again at every boot.
     *  Should be a TOTAL builder over any key (a shape constructor, not an authorizer). */
    footprint: (docKey: string, ctx: ApiContext<User>) => MaybePromise<ApiQueryResult>;
    /** Loaded-but-never-room-written tables (§2.2 — the "followed" set: real external writers, the
     *  daemon stays write-authoritative for them). Must be a subset of the footprint's tables; the
     *  room's writable scope is footprint minus context. Defaults to `[]`. */
    context?: readonly string[];
}

queryResultToAst

export declare function queryResultToAst(result: ApiQueryResult): Ast;

ROOM_DOC_SEPARATOR

/** The wire room-key delimiter: a named profile's doc is `"<profile>/<key>"`; a doc with no known
 *  profile prefix belongs to the legacy anonymous profile (`resolveFootprint`, bare-key form). */
export declare const ROOM_DOC_SEPARATOR = "/";

mintRoomDoc

/** Mint the wire room doc for a named profile (the lease path — G-iv-b — mints these). */
export declare function mintRoomDoc(profile: string, key: string): string;

splitRoomDoc

/** Split a wire room doc at the FIRST separator. `undefined` for a bare doc (no separator, or an
 *  empty would-be profile name). The CALLER decides whether the prefix actually names a profile —
 *  a legacy doc may itself contain `/`, and then falls through to the legacy resolver whole. */
export declare function splitRoomDoc(doc: string): {
    profile: string;
    key: string;
} | undefined;

assertUnwindowedFootprint

/**
 * §2.3 "profile footprints are unwindowed", enforced: throw loudly (with the offending AST path)
 * if `limit` / `start` (cursor paging — the skip lowering) / `one` appears ANYWHERE in the
 * footprint tree — the root, a related subquery, or an EXISTS child. A window computed over an
 * unwindowed superset equals the window over the full store, which is what makes room-serving of
 * windowed *queries* sound; a windowed *footprint* would silently break that, so it is rejected
 * by construction. Windows belong on the labeled queries a room serves, never on the footprint.
 */
export declare function assertUnwindowedFootprint(ast: Ast, profile: string): void;

FootprintScan

export interface FootprintScan {
    tables: Set<string>;
    joinKeys: Map<string, Set<string>>;
}

scanFootprint

/** Walk the footprint: every base table it draws rows from, and — per table — the correlation
 *  (join-key) columns every related/EXISTS edge binds on it. These are §3.2's routing metadata
 *  and slice H's "room mutators never write join keys" enforcement input; G-iv-b ships them to
 *  the room engine as part of the RoomTableSpec compilation ({@link compileRoomTableSpecs}). */
export declare function scanFootprint(ast: Ast): FootprintScan;

RoomTableSpec

/**
 * One footprint table's spec on the lease wire (`QueryLeaseResponse.realtime.tables`): the §2.2
 * owned/followed split plus §3.2's per-table routing metadata, compiled from the RESOLVED
 * footprint AST at lease time (so key-dependent and non-static profiles compile too).
 *
 * `writable`:
 * - `none` — a context table (§2.2 "followed"): loaded by the room, never room-written; the
 *   daemon stays write-authoritative.
 * - `predicate` — a writable table. `where` is the ROW-LOCAL part of the footprint's predicate
 *   for this table (simple column-vs-literal conditions composed with and/or); an ABSENT `where`
 *   means no row-local constraint — every row the room holds for this table is in the writable
 *   scope. `joinKeyCols` are the correlation columns the footprint binds on this table (slice
 *   H's "room mutators never write join keys" enforcement input).
 *
 * Dropping the non-row-local parts (correlated/EXISTS conditions, column-vs-column comparisons)
 * only ever WIDENS the predicate, which is sound here: the room only relays rows the footprint
 * materialized, so the held-row set — not this predicate — is the real outer bound; `where` is a
 * row-local refinement of it, and a wider refinement can never mark a held footprint row
 * non-writable that the full predicate would have allowed. (An OR with any non-row-local
 * disjunct is dropped WHOLE — keeping only some disjuncts would narrow, which is not sound.)
 *
 * `footprintWhere` (H-iii — the lease-wire flip): the EXACT footprint-membership predicate,
 * identical to {@link RoomScopeSpec.footprintWhere} (ONE compiler feeds both wires — the lease's
 * table specs and the boot wire's scopes are now the SAME objects). The client's §3 router
 * consumes it for the pk-membership read proof; it is advisory routing metadata, never a
 * credential (the room gate re-proves engine-side, H-iv).
 */
export type RoomTableSpec = {
    table: string;
    footprintWhere?: Condition;
    writable: {
        kind: "none";
    } | {
        kind: "predicate";
        where?: Condition;
        joinKeyCols: string[];
    };
};

RoomScopeSpec

/**
 * One footprint table's FULL scope spec (slice H-iv-b): byte-compatible with
 * `rindle-room-core`'s `TableScopeSpec` (scope.rs), the wasm room's `enableWritesV2` input. It
 * rides the boot wire (`RoomBootResponse.scopes`); since slice H-iii the LEASE wire's
 * {@link RoomTableSpec} carries `footprintWhere` too (the client-side routing proof), so the two
 * shapes are now structurally IDENTICAL — kept as two names because they are two wires with two
 * consumers (the room gate vs the client router).
 *
 * `footprintWhere` is computed for EVERY footprint table — context (`kind: "none"`) ones
 * included, because the gate proves ABSENT READS on any readable table with it (§3.1: an absent
 * read is covered only when the footprint predicate is decidable from the pk columns alone and
 * evaluates true on the read key). ABSENT (no exact membership predicate exists) ⇒ no absent
 * read on that table is provable — the room gate fails closed to a deopt.
 *
 * **`footprintWhere` is EXACT-only — the opposite discipline from the writable `where`.** The
 * writable side ships the WIDENING extraction ({@link tablePredicate}) and that is sound (the
 * held-row set is the real outer bound). The absent-read proof points the other way: it uses
 * `footprintWhere` to conclude "a row with this pk would have been IN the footprint, so the
 * room's absence is truth's absence" — a WIDENED predicate over-claims exactly there (a root
 * `where` mixing pk-column conjuncts with a dropped EXISTS, or a correlated child whose own
 * `where` reads only its pk, would prove an absence the dropped part can contradict). So
 * `footprintWhere` is emitted ONLY when the membership predicate is exact
 * ({@link exactMembershipPredicate}): every node reading the table is the footprint ROOT (a
 * child node's implicit correlation to its parent is itself a dropped, non-row-local constraint)
 * and the root's `where` extraction dropped nothing. An unconstrained exact root (the whole
 * table rides the footprint) emits the vacuous-true `{type:"and",conditions:[]}` — the gate's
 * combinator pins empty-AND = true — so whole-table footprints keep provable absent reads.
 * Child/correlated tables get NO `footprintWhere` and their absent reads deopt (fail closed);
 * propagating a parent constraint through a pk-covering correlation is a future refinement.
 */
export type RoomScopeSpec = {
    table: string;
    footprintWhere?: Condition;
    writable: RoomTableSpec["writable"];
};

compileRoomScopeSpecs

/** Compile the per-table {@link RoomScopeSpec}s from a RESOLVED footprint AST + the profile's
 *  context set — the ONE compiler both wires derive from (the boot wire ships it whole; the
 *  lease wire serializes the {@link RoomTableSpec} projection via {@link compileRoomTableSpecs}).
 *  Deterministic output order (tables sorted by name). */
export declare function compileRoomScopeSpecs(footprint: Ast, context: ReadonlySet<string>): RoomScopeSpec[];

compileRoomTableSpecs

/** Compile the per-table {@link RoomTableSpec}s from a RESOLVED footprint AST + the profile's
 *  context set — since H-iii the lease wire carries `footprintWhere` exactly where the boot wire
 *  does, so this IS {@link compileRoomScopeSpecs} (one compiler, two wires; the identity is
 *  pinned by serve-proof.test.ts). Kept as a named entry point for the lease call site. */
export declare function compileRoomTableSpecs(footprint: Ast, context: ReadonlySet<string>): RoomTableSpec[];

PROBE_DOC_KEY

/** The symbolic doc key construction-time validation resolves each footprint with. A profile
 *  footprint should be a TOTAL builder over any key; one that throws on this key (or is async /
 *  ctx-dependent) simply defers its unwindowed check to boot, with a loud warning. */
export declare const PROBE_DOC_KEY = "__rindle-room-profile-probe__";

CompileRoomProfilesInput

export interface CompileRoomProfilesInput<User> {
    rooms: Record<string, RoomProfile<User>> | undefined;
    /** The typed schema, when configured — enables the context-table existence check. */
    schema: Schema | undefined;
    /** Loud-diagnostics sink (defaults to `console.warn`); injectable for tests. */
    warn?: (message: string) => void;
}

compileRoomProfiles

/**
 * Compile + validate the named room profiles — §2.3 "loud at registration". Rules enforced here:
 * the profile name is wire-key-safe (no `/`); (b) the footprint is UNWINDOWED (throw, with the
 * offending AST path); (c) context tables exist in the schema and are a subset of the footprint's
 * tables (throw); (d) per-table join-key columns are derived and RECORDED on the compiled profile
 * (a loud WARNING when the footprint isn't statically resolvable and they can't be).
 */
export declare function compileRoomProfiles<User>(input: CompileRoomProfilesInput<User>): Map<string, CompiledRoomProfile<User>>;

queryRealtimeLabel

/** The realtime label a registered query carries (stamped by `registerQueries` from its
 *  `defineQuery` options), or `undefined` for an unlabeled query. The lease path uses this to
 *  derive `(room profile, args mapping)` for a named query. */
export declare function queryRealtimeLabel(query: ApiQuery<any, any> | undefined): RealtimeQueryLabel | undefined;

assertLabeledProfilesExist

/** Rule (a) of the §2.3 "loud at registration" checks: every labeled registered query must name
 *  an existing room profile — a label pointing at nothing is config drift that would otherwise
 *  surface as a query that silently never room-serves. */
export declare function assertLabeledProfilesExist<User>(queries: Record<string, ApiQuery<User, any>> | undefined, profiles: ReadonlyMap<string, CompiledRoomProfile<User>>): void;