Rindle

API index and search · Build metadata

Supporting declarations

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

Exact source

Fnv

export declare class Fnv {
    h: bigint;
    private byte;
    u8(v: number): void;
    /** A `u32` little-endian (matching Rust `v.to_le_bytes()`). */
    u32(v: number): void;
    /** A length-prefixed string: `u32(byteLen)` then the UTF-8 bytes (disambiguates joins). */
    s(str: string): void;
}

schemaFp

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

Hello

/** The subscription handshake, sent once before any {@link Batch}. */
export interface Hello {
    epoch: number;
    comparatorVersion: number;
    schema: WireSchema;
    schemaFp: string;
}

Batch

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

SubscribeClientMsg

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

ClientMsg

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

ServerMsg

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

Publisher

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

ProtocolErrorKind

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

ProtocolError

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

Subscriber

/** 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`
 *  `ChangeEvent`s. 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";
}