Files
dev/third_party/pi/packages/chord/src/wire.ts
alexion 6081e3e325 chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
2026-10-15 17:58:48 -04:00

182 lines
8.1 KiB
TypeScript

import { type Static, type TSchema, Type } from "typebox";
import { Value } from "typebox/value";
import { DescriptionSchema } from "./contract.ts";
import { CHORD_ERROR_CODES, ChordError } from "./errors.ts";
// JSON frames between two hosts (or a host and a client). Ids are chosen by the side that creates the hold, call or
// subscription; every frame type has one direction, so ids never collide:
// consumer → provider: hold, release, call, cancel, sub, unsub, resync
// provider → consumer: shape, gone, result, state
// State is addressed by (hold id, member): a hold implies the state of its variant, sent as units (a shape followed by
// a snapshot of each member the consumer lacks; see Connection).
// both: hello, ping, pong, bye, root_end, ack; parent → child: invalidate, prepare, commit, abort, retire;
// child → parent: vote
//
// Each frame is declared once, as the schema every received frame is checked against (`isFrame`); the types are derived
// from it. Payloads (hold inputs, call args, results, snapshots) stay `unknown`: they are validated where they are
// used, against the contract. State ops are checked op by op by the delta decoder.
/**
* The link protocol version, announced in `hello`. Peers of different versions do not talk: the side that notices
* closes the link with reason `protocol-version <its version>`, so the other side learns it too
* (`LinkOptions.onProtocolMismatch`).
*/
export const PROTOCOL_VERSION = 4;
const Id = Type.Integer({ minimum: 1 });
const Name = Type.String({ minLength: 1 });
const Reload = Type.Integer({ minimum: 1 });
/** Arrays a sender may pass as readonly. */
const List = <T extends TSchema>(item: T) => Type.Immutable(Type.Array(item));
const RootRefSchema = Type.Object({ rootId: Name, codeVersion: Type.Integer({ minimum: 1 }) });
export type RootRef = Static<typeof RootRefSchema>;
const WireErrorSchema = Type.Object({
code: Type.Enum(CHORD_ERROR_CODES),
message: Type.String(),
reason: Type.Optional(Type.String()),
});
export type WireError = Static<typeof WireErrorSchema>;
const WireReloadOptionsSchema = Type.Object({
remove: Type.Optional(List(Type.String())),
gateDeadline: Type.Optional(Type.Number()),
retireDeadline: Type.Optional(Type.Number()),
});
export type WireReloadOptions = Static<typeof WireReloadOptionsSchema>;
/** A child's prepared reload, sent with its vote (same fields as `ReloadReport`, without children). */
const WireReloadReportSchema = Type.Object({
codeVersion: Type.Integer(),
previous: Type.Integer(),
changed: List(Type.String()),
removed: List(Type.String()),
prepared: List(Type.String()),
gated: List(Type.String()),
});
export type WireReloadReport = Static<typeof WireReloadReportSchema>;
const FrameSchema = Type.Union([
Type.Object({
t: Type.Literal("hello"),
/** The link protocol version (`PROTOCOL_VERSION`; absent: 1). Peers of another version close the link. */
protocol: Type.Optional(Type.Integer({ minimum: 1 })),
name: Name,
kind: Type.Union([Type.Literal("host"), Type.Literal("client")]),
codeVersion: Type.Integer({ minimum: 0 }),
/** The sender acknowledges what it receives (`ack`). Flow control applies only when both hellos say so. */
acks: Type.Optional(Type.Boolean()),
}),
/** Every frame received so far except `ack` frames, counted from the link's start, once processed. */
Type.Object({ t: Type.Literal("ack"), n: Type.Integer({ minimum: 1 }) }),
Type.Object({
t: Type.Literal("hold"),
id: Id,
/**
* Target host name, resolved by the consumer from the token's `host` and `input`. Relays route on it after
* checking it against both; the target refuses an input its own contract places elsewhere.
*/
at: Name,
/** The consumer's contract token; the provider checks that it covers it. Remote tokens carry `host`. */
token: DescriptionSchema,
/** Normalized with the consumer's contract. */
input: Type.Unknown(),
/** A forwarded principal. Honored only on frames from the parent link. */
principal: Type.Optional(Type.Unknown()),
/**
* The client connection the hold comes from: an opaque id the server assigned to it, forwarded by relays. Honored
* only on frames from the parent link; present means the hold is a client's (not a reload participant).
*/
origin: Type.Optional(Name),
/** False: no state for this hold until `sub` (a method-only binding). */
state: Type.Optional(Type.Literal(false)),
/** The consumer already has values (a re-hold after a link loss): its unit is not urgent. */
again: Type.Optional(Type.Literal(true)),
}),
Type.Object({ t: Type.Literal("release"), id: Id }),
Type.Object({
t: Type.Literal("shape"),
id: Id,
variant: Type.Optional(Type.String()),
members: List(Type.String()),
contract: Type.String(),
/** The state members whose snapshots follow this frame; the consumer applies the shape once they are here. */
state: List(Name),
}),
Type.Object({ t: Type.Literal("gone"), id: Id, error: WireErrorSchema }),
Type.Object({
t: Type.Literal("call"),
id: Id,
hold: Id,
member: Name,
args: Type.Optional(Type.Unknown()),
/** Absent: the receiving host starts a root (browsers never carry roots). */
root: Type.Optional(RootRefSchema),
}),
Type.Object({
t: Type.Literal("result"),
id: Id,
value: Type.Optional(Type.Unknown()),
error: Type.Optional(WireErrorSchema),
}),
Type.Object({ t: Type.Literal("cancel"), id: Id }),
/** Starts the state of a hold opened with `state: false` (answered with a unit). */
Type.Object({ t: Type.Literal("sub"), hold: Id }),
/** Stops the state of a hold. */
Type.Object({ t: Type.Literal("unsub"), hold: Id }),
/** Sends member `m` again as a reset (a relay that had to drop what waited for it). */
Type.Object({ t: Type.Literal("resync"), hold: Id, m: Name }),
Type.Object({
t: Type.Literal("state"),
hold: Id,
m: Name,
/** `snap`/`reset` without `value`: the value is `undefined`. */
as: Type.Union([Type.Literal("snap"), Type.Literal("reset"), Type.Literal("ops"), Type.Literal("error")]),
value: Type.Optional(Type.Unknown()),
/** Encoded with the stream's delta encoder; the decoder validates each op. */
ops: Type.Optional(List(List(Type.Unknown()))),
error: Type.Optional(WireErrorSchema),
}),
Type.Object({ t: Type.Literal("ping"), n: Type.Integer() }),
Type.Object({ t: Type.Literal("pong"), n: Type.Integer() }),
/** Ends the link (consumed by the link itself). `reason: "protocol-version <n>"`: the sender speaks version n. */
Type.Object({ t: Type.Literal("bye"), reason: Type.Optional(Type.String()) }),
/** The root ended at its origin: its leases on objects end (sent on every link the root's calls used). */
Type.Object({ t: Type.Literal("root_end"), rootId: Name }),
Type.Object({ t: Type.Literal("invalidate"), pattern: Type.Record(Type.String(), Type.Unknown()) }),
Type.Object({
t: Type.Literal("prepare"),
reload: Reload,
codeVersion: Type.Integer({ minimum: 1 }),
plugins: List(Type.String()),
options: WireReloadOptionsSchema,
}),
Type.Object({
t: Type.Literal("vote"),
reload: Reload,
error: Type.Optional(WireErrorSchema),
report: Type.Optional(WireReloadReportSchema),
}),
Type.Object({ t: Type.Literal("commit"), reload: Reload }),
/** The parent dropped `codeVersion`: roots of it may still come from there no longer. */
Type.Object({ t: Type.Literal("retire"), codeVersion: Type.Integer({ minimum: 1 }) }),
Type.Object({ t: Type.Literal("abort"), reload: Reload }),
]);
export type Frame = Static<typeof FrameSchema>;
/** A frame that passes validation; anything else is a protocol violation. */
export const isFrame = (value: unknown): value is Frame => Value.Check(FrameSchema, value);
export function toWireError(error: unknown): WireError {
if (error instanceof ChordError) {
return error.reason === undefined
? { code: error.code, message: error.message }
: { code: error.code, message: error.message, reason: error.reason };
}
return { code: "unavailable", message: error instanceof Error ? error.message : String(error) };
}
export const fromWireError = (error: WireError): ChordError =>
new ChordError(error.code, error.message.replace(/^[a-z_]+(\/[a-z_]+)?: /, ""), { reason: error.reason });