import { Connection } from './transport.js'; /** * The largest single message admitted to the stream, matching the server's * frame/body cap. Enforced at the API surface (a command's promise rejects) * rather than by queueing: an oversized message in the retransmit buffer would * be a permanent reconnect-replay loop, so it must never be admitted. */ export declare const MAX_MESSAGE_BYTES: number; /** A client's deliberate goodbye (client → server). */ export declare const CLOSE_GOODBYE = 0; /** Authentication failed (server → client, terminal). */ export declare const CLOSE_UNAUTHORIZED = 1; /** The presented token is unknown, evicted, or unredeemable (terminal). */ export declare const CLOSE_INVALID_TOKEN = 2; /** A protocol violation — malformed, oversize, or a sequence gap (terminal). */ export declare const CLOSE_PROTOCOL_VIOLATION = 3; /** The server is going away — a drain; transient, resume elsewhere. */ export declare const CLOSE_GOING_AWAY = 4; /** The close slot ending a connection: `[code, detail?]`. */ export type WireClose = readonly [code: number, detail?: string]; /** * An envelope's wire tuple: `[seq, ackSeq, messages, token?, close?]`. The * trailing slots are positional and omitted when unused — a 3-element array is * the steady state, a 4-element one also carries a token, and a 5-element one * ends the connection, with a `null` filler in the `token` slot when no token * accompanies the close. */ export type WireEnvelope = readonly [ seq: number, ackSeq: number, messages: readonly unknown[], token?: string | null, close?: WireClose ]; /** * Validate a parsed value as a {@link WireEnvelope}, or `null` when it is not a * well-formed 3-to-5-element envelope. A malformed envelope is never fatal — the * caller recovers by resuming (see {@link Host.receive}). The result is the same * array, narrowed: read it by labelled slot. Exported so the golden vectors * drive the exact validator the runtime runs. */ export declare function decodeEnvelope(raw: unknown): WireEnvelope | null; /** The outcome of applying one envelope's messages (see {@link applyReceiver}). */ export interface Applied { /** The messages actually applied, in order (duplicates skipped). */ delivered: unknown[]; /** The peer-message count after applying — the new `ack_seq` to send back. */ applied: number; /** True when a sequence gap was found: a protocol violation, terminal. */ violation: boolean; } /** * The receiver rule: apply one envelope's `messages` (numbered from `seq`) * against the count of peer messages `applied` so far. A message at or below * `applied` is a replay's duplicate — skipped; the rest apply in order, * advancing the count; a gap (the first message numbered beyond the next * expected) stops the pass and marks a violation. Because duplicates are * skipped by number, every envelope is idempotent to reapply — which is what * makes retry, resume, and steady state one code path. */ export declare function applyReceiver(applied: number, seq: number, messages: ReadonlyArray): Applied; /** * The outbound retransmit buffer: every message put on the wire but not yet * acked by the server, with the seq the buffer starts after. The client drops * everything the server acks and replays the remainder on a resume. */ export interface Outbound { /** Buffer a message until the server acks it. */ push(message: unknown): void; /** Drop everything the server has now acked; never goes backwards on a stale ack. */ trim(ackSeq: number): void; /** The count of our messages the server has acked. */ ackedSeq(): number; /** The seq the next pushed message will carry (one past the last buffered). */ nextSeq(): number; /** The messages the server has not acked, for a replay. */ replay(): unknown[]; /** How many messages are currently unacked. */ size(): number; } /** Build an empty {@link Outbound}. */ export declare function createOutbound(): Outbound; /** * What {@link Host.receive} tells the carriage to do next: * - `live` — the frame applied; stay in the steady state; * - `recover` — drop this physical link and re-establish it (a malformed frame, * or a drain `[4]`): the WebSocket reconnects and resumes, http1 retries — a * resume replays past the peer's ack, so nothing is lost; * - `terminal` — the connection has ended (a close `[1]`/`[2]`/`[3]`, or a * locally-detected sequence gap): the core has already resolved `closed`; the * carriage tears its link down and does not reconnect. */ export type Ingest = 'live' | 'recover' | 'terminal'; /** * The protocol operations a {@link Carriage} drives. The logical connection owns * every byte of state behind these; the carriage supplies only physical * transmission and timing. */ export interface Host { /** Ingest one raw inbound frame and report what the carriage should do next. */ receive(frame: string): Ingest; /** * Encode the whole unacked run (plus anything newly queued), carrying the * token when one is held — a resume, an http1 request, or the first open. * Always returns a frame, even with no messages (a token-only resume). */ fullFrame(): string; /** * Encode only the newly-queued messages, with no token — the WebSocket steady * state. `null` when nothing new is queued (the client sends no bare acks). */ steadyFrame(): string | null; /** Encode the goodbye: the whole unacked run, the token, and close `[0]`. */ goodbyeFrame(): string; /** True when there is unsent or unacked outbound data. */ hasPending(): boolean; /** True once a resume token is held (so a fresh link resumes rather than opens). */ hasToken(): boolean; /** Terminate the connection with `reason` — a terminal transport failure the * carriage detects out of band (an http1 `401`, a construction failure) — * carrying the close code it stands in for where the carriage knows one * (http1 maps its terminal statuses onto the registry; a construction * failure has no code). */ fail(reason: string, code?: number): void; } /** * A physical transport strategy behind {@link logicalConnection}. The core calls * these; the carriage owns the sockets/requests and their timers, feeding frames * back through the {@link Host}. */ export interface Carriage { /** Wire the carriage to its host and begin establishing the first link. */ connect(host: Host): void; /** New outbound data is queued (or a resume is possible) — send if the link * allows; otherwise a no-op until the link is ready. */ wake(): void; /** Best-effort transmit of the final goodbye frame before a deliberate close. */ sendGoodbye(frame: string): void; /** Release every physical resource — the connection has ended, for any reason. * Idempotent. */ stopped(): void; } /** * Build the logical {@link Connection} over a physical {@link Carriage}. State * lives in the closure; the returned object is the {@link Connection} the * {@link Channel} consumes. The core encodes and decodes envelopes, counts seqs * in each direction, trims the retransmit buffer against inbound acks, keeps the * newest resume token, applies the receiver rule, and buffers inbound messages * until a listener takes them. A transient drop is invisible (the carriage * reconnects and the resume replays); only a terminal close or a caller * `close()` resolves `closed`. */ export declare function logicalConnection(carriage: Carriage): Connection;