import { toExactUint8Array } from "./bytes.js";import { errorSidecarResponsePayload, fromGeneratedSidecarRequestPayload, isMatchingSidecarResponsePayload, type LiveSidecarRequestPayload, type LiveSidecarResponsePayload, toGeneratedSidecarResponsePayload,} from "./callbacks.js";import { fromGeneratedEventPayload, type LiveSidecarEventPayload,} from "./event-buffer.js";import { decodeJsonFramePayload, encodeJsonFramePayload, type TransportPayloadCodec,} from "./frame-payload-codec.js";import type { ClassifiedFrame } from "./frame-rpc.js";import * as protocol from "./generated-protocol.js";import { bigIntToSafeNumber } from "./numbers.js";import { fromGeneratedOwnershipScope, type LiveOwnershipScope, toGeneratedOwnershipScope,} from "./ownership.js";import { SIDECAR_PROTOCOL_SCHEMA, validateSidecarProtocolSchema,} from "./protocol-schema.js";import { type LiveRequestPayload, toGeneratedRequestPayload,} from "./request-payloads.js";import { fromGeneratedResponsePayload, type LiveResponsePayload,} from "./response-payloads.js"; export interface LiveRequestFrame { frame_type: "request"; schema: typeof SIDECAR_PROTOCOL_SCHEMA; request_id: number; ownership: LiveOwnershipScope; payload: LiveRequestPayload;} export interface LiveEventFrame { frame_type: "event"; schema: typeof SIDECAR_PROTOCOL_SCHEMA; ownership: LiveOwnershipScope; payload: LiveSidecarEventPayload;} export interface LiveSidecarRequestFrame { frame_type: "sidecar_request"; schema: typeof SIDECAR_PROTOCOL_SCHEMA; request_id: number; ownership: LiveOwnershipScope; payload: LiveSidecarRequestPayload;} export interface LiveResponseFrame { frame_type: "response"; schema: typeof SIDECAR_PROTOCOL_SCHEMA; request_id: number; ownership: LiveOwnershipScope; payload: LiveResponsePayload;} export interface LiveSidecarResponseFrame { frame_type: "sidecar_response"; schema: typeof SIDECAR_PROTOCOL_SCHEMA; request_id: number; ownership: LiveOwnershipScope; payload: LiveSidecarResponsePayload;} export type LiveControlPayload = { type: "shutdown"; reason: string;}; export interface LiveControlFrame { frame_type: "control"; schema: typeof SIDECAR_PROTOCOL_SCHEMA; payload: LiveControlPayload;} export type LiveProtocolFrame = | LiveRequestFrame | LiveResponseFrame | LiveEventFrame | LiveSidecarRequestFrame | LiveSidecarResponseFrame | LiveControlFrame; export type LiveSidecarWrittenProtocolFrame = | LiveResponseFrame | LiveEventFrame | LiveSidecarRequestFrame; export type ProtocolFramePayloadCodec = TransportPayloadCodec; export type ClassifiedSidecarWrittenProtocolFrame = ClassifiedFrame< LiveResponseFrame, LiveEventFrame, LiveSidecarRequestFrame>; export type LiveSidecarRequestHandler = ( request: LiveSidecarRequestFrame,) => Promise<LiveSidecarResponsePayload> | LiveSidecarResponsePayload; export class HostProtocolFrameFactory { private nextRequestId = 1; createRequestFrame(input: { ownership: LiveOwnershipScope; payload: LiveRequestPayload; }): LiveRequestFrame { return { frame_type: "request", schema: SIDECAR_PROTOCOL_SCHEMA, request_id: this.nextRequestId++, ownership: input.ownership, payload: input.payload, }; } createSidecarResponseFrame(input: { request: LiveSidecarRequestFrame; payload: LiveSidecarResponsePayload; }): LiveSidecarResponseFrame { return { frame_type: "sidecar_response", schema: SIDECAR_PROTOCOL_SCHEMA, request_id: input.request.request_id, ownership: input.request.ownership, payload: input.payload, }; } createControlFrame(payload: LiveControlPayload): LiveControlFrame { return { frame_type: "control", schema: SIDECAR_PROTOCOL_SCHEMA, payload, }; }} export async function resolveSidecarRequestFramePayload( request: LiveSidecarRequestFrame, handler: LiveSidecarRequestHandler | null,): Promise<LiveSidecarResponsePayload> { try { if (!handler) { throw new Error( `no sidecar request handler registered for ${request.payload.type}`, ); } const payload = await handler(request); if (!isMatchingSidecarResponsePayload(request.payload, payload)) { throw new Error( `sidecar handler returned ${payload.type} for ${request.payload.type}`, ); } return payload; } catch (error) { return errorSidecarResponsePayload(request.payload, error); }} export function toGeneratedProtocolFrame( frame: LiveProtocolFrame,): protocol.ProtocolFrame { switch (frame.frame_type) { case "request": return { tag: "RequestFrame", val: { schema: frame.schema, requestId: BigInt(frame.request_id), ownership: toGeneratedOwnershipScope(frame.ownership), payload: toGeneratedRequestPayload(frame.payload), }, }; case "sidecar_response": return { tag: "SidecarResponseFrame", val: { schema: frame.schema, requestId: BigInt(frame.request_id), ownership: toGeneratedOwnershipScope(frame.ownership), payload: toGeneratedSidecarResponsePayload(frame.payload), }, }; case "control": return { tag: "ControlFrame", val: { schema: frame.schema, payload: toGeneratedControlPayload(frame.payload), }, }; case "response": case "event": case "sidecar_request": throw new Error( `BARE encoding is only implemented for host-written frames, received ${frame.frame_type}`, ); }} export function encodeBareProtocolFrame(frame: LiveProtocolFrame): Uint8Array { return protocol.encodeProtocolFrame(toGeneratedProtocolFrame(frame));} export function decodeBareProtocolFrame( payload: Uint8Array,): LiveSidecarWrittenProtocolFrame { return fromGeneratedSidecarWrittenProtocolFrame( protocol.decodeProtocolFrame(toExactUint8Array(payload)), );} export function encodeProtocolFramePayload( frame: LiveProtocolFrame, codec: ProtocolFramePayloadCodec,): Uint8Array { if (codec === "json") { return encodeJsonFramePayload(frame); } return encodeBareProtocolFrame(frame);} export function decodeProtocolFramePayload( payload: Uint8Array, codec: ProtocolFramePayloadCodec,): LiveSidecarWrittenProtocolFrame { if (codec === "json") { return decodeJsonFramePayload<LiveSidecarWrittenProtocolFrame>(payload); } return decodeBareProtocolFrame(payload);} export function classifySidecarWrittenProtocolFrame( frame: LiveSidecarWrittenProtocolFrame,): ClassifiedSidecarWrittenProtocolFrame { switch (frame.frame_type) { case "response": return { kind: "response", requestId: frame.request_id, frame, }; case "event": return { kind: "event", frame }; case "sidecar_request": return { kind: "sidecarRequest", frame }; }} export function fromGeneratedSidecarWrittenProtocolFrame( frame: protocol.ProtocolFrame,): LiveSidecarWrittenProtocolFrame { switch (frame.tag) { case "ResponseFrame": return { frame_type: "response", schema: toLiveProtocolSchema(frame.val.schema), request_id: bigIntToSafeNumber( frame.val.requestId, "response request id", ), ownership: fromGeneratedOwnershipScope(frame.val.ownership), payload: fromGeneratedResponsePayload(frame.val.payload), }; case "EventFrame": return { frame_type: "event", schema: toLiveProtocolSchema(frame.val.schema), ownership: fromGeneratedOwnershipScope(frame.val.ownership), payload: fromGeneratedEventPayload(frame.val.payload), }; case "SidecarRequestFrame": return { frame_type: "sidecar_request", schema: toLiveProtocolSchema(frame.val.schema), request_id: bigIntToSafeNumber( frame.val.requestId, "sidecar request id", ), ownership: fromGeneratedOwnershipScope(frame.val.ownership), payload: fromGeneratedSidecarRequestPayload(frame.val.payload), }; case "RequestFrame": case "SidecarResponseFrame": case "ControlFrame": throw new Error(`unsupported BARE protocol frame tag: ${frame.tag}`); }} export function toGeneratedControlPayload( payload: LiveControlPayload,): protocol.ControlPayload { switch (payload.type) { case "shutdown": return { tag: "ShutdownControl", val: { reason: payload.reason }, }; }} export function toLiveProtocolSchema( schema: protocol.ProtocolSchema,): typeof SIDECAR_PROTOCOL_SCHEMA { return validateSidecarProtocolSchema(schema);}
import { toExactUint8Array } from "./bytes.js";
import {
errorSidecarResponsePayload,
fromGeneratedSidecarRequestPayload,
isMatchingSidecarResponsePayload,
type LiveSidecarRequestPayload,
type LiveSidecarResponsePayload,
toGeneratedSidecarResponsePayload,
} from "./callbacks.js";
import {
fromGeneratedEventPayload,
type LiveSidecarEventPayload,
} from "./event-buffer.js";
import {
decodeJsonFramePayload,
encodeJsonFramePayload,
type TransportPayloadCodec,
} from "./frame-payload-codec.js";
import type { ClassifiedFrame } from "./frame-rpc.js";
import * as protocol from "./generated-protocol.js";
import { bigIntToSafeNumber } from "./numbers.js";
import {
fromGeneratedOwnershipScope,
type LiveOwnershipScope,
toGeneratedOwnershipScope,
} from "./ownership.js";
import {
SIDECAR_PROTOCOL_SCHEMA,
validateSidecarProtocolSchema,
} from "./protocol-schema.js";
import {
type LiveRequestPayload,
toGeneratedRequestPayload,
} from "./request-payloads.js";
import {
fromGeneratedResponsePayload,
type LiveResponsePayload,
} from "./response-payloads.js";
export interface LiveRequestFrame {
frame_type: "request";
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
request_id: number;
ownership: LiveOwnershipScope;
payload: LiveRequestPayload;
}
export interface LiveEventFrame {
frame_type: "event";
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
ownership: LiveOwnershipScope;
payload: LiveSidecarEventPayload;
}
export interface LiveSidecarRequestFrame {
frame_type: "sidecar_request";
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
request_id: number;
ownership: LiveOwnershipScope;
payload: LiveSidecarRequestPayload;
}
export interface LiveResponseFrame {
frame_type: "response";
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
request_id: number;
ownership: LiveOwnershipScope;
payload: LiveResponsePayload;
}
export interface LiveSidecarResponseFrame {
frame_type: "sidecar_response";
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
request_id: number;
ownership: LiveOwnershipScope;
payload: LiveSidecarResponsePayload;
}
export type LiveControlPayload = {
type: "shutdown";
reason: string;
};
export interface LiveControlFrame {
frame_type: "control";
schema: typeof SIDECAR_PROTOCOL_SCHEMA;
payload: LiveControlPayload;
}
export type LiveProtocolFrame =
| LiveRequestFrame
| LiveResponseFrame
| LiveEventFrame
| LiveSidecarRequestFrame
| LiveSidecarResponseFrame
| LiveControlFrame;
export type LiveSidecarWrittenProtocolFrame =
| LiveResponseFrame
| LiveEventFrame
| LiveSidecarRequestFrame;
export type ProtocolFramePayloadCodec = TransportPayloadCodec;
export type ClassifiedSidecarWrittenProtocolFrame = ClassifiedFrame<
LiveResponseFrame,
LiveEventFrame,
LiveSidecarRequestFrame
>;
export type LiveSidecarRequestHandler = (
request: LiveSidecarRequestFrame,
) => Promise<LiveSidecarResponsePayload> | LiveSidecarResponsePayload;
export class HostProtocolFrameFactory {
private nextRequestId = 1;
createRequestFrame(input: {
ownership: LiveOwnershipScope;
payload: LiveRequestPayload;
}): LiveRequestFrame {
return {
frame_type: "request",
schema: SIDECAR_PROTOCOL_SCHEMA,
request_id: this.nextRequestId++,
ownership: input.ownership,
payload: input.payload,
};
}
createSidecarResponseFrame(input: {
request: LiveSidecarRequestFrame;
payload: LiveSidecarResponsePayload;
}): LiveSidecarResponseFrame {
return {
frame_type: "sidecar_response",
schema: SIDECAR_PROTOCOL_SCHEMA,
request_id: input.request.request_id,
ownership: input.request.ownership,
payload: input.payload,
};
}
createControlFrame(payload: LiveControlPayload): LiveControlFrame {
return {
frame_type: "control",
schema: SIDECAR_PROTOCOL_SCHEMA,
payload,
};
}
}
export async function resolveSidecarRequestFramePayload(
request: LiveSidecarRequestFrame,
handler: LiveSidecarRequestHandler | null,
): Promise<LiveSidecarResponsePayload> {
try {
if (!handler) {
throw new Error(
`no sidecar request handler registered for ${request.payload.type}`,
);
}
const payload = await handler(request);
if (!isMatchingSidecarResponsePayload(request.payload, payload)) {
throw new Error(
`sidecar handler returned ${payload.type} for ${request.payload.type}`,
);
}
return payload;
} catch (error) {
return errorSidecarResponsePayload(request.payload, error);
}
}
export function toGeneratedProtocolFrame(
frame: LiveProtocolFrame,
): protocol.ProtocolFrame {
switch (frame.frame_type) {
case "request":
return {
tag: "RequestFrame",
val: {
schema: frame.schema,
requestId: BigInt(frame.request_id),
ownership: toGeneratedOwnershipScope(frame.ownership),
payload: toGeneratedRequestPayload(frame.payload),
},
};
case "sidecar_response":
return {
tag: "SidecarResponseFrame",
val: {
schema: frame.schema,
requestId: BigInt(frame.request_id),
ownership: toGeneratedOwnershipScope(frame.ownership),
payload: toGeneratedSidecarResponsePayload(frame.payload),
},
};
case "control":
return {
tag: "ControlFrame",
val: {
schema: frame.schema,
payload: toGeneratedControlPayload(frame.payload),
},
};
case "response":
case "event":
case "sidecar_request":
throw new Error(
`BARE encoding is only implemented for host-written frames, received ${frame.frame_type}`,
);
}
}
export function encodeBareProtocolFrame(frame: LiveProtocolFrame): Uint8Array {
return protocol.encodeProtocolFrame(toGeneratedProtocolFrame(frame));
}
export function decodeBareProtocolFrame(
payload: Uint8Array,
): LiveSidecarWrittenProtocolFrame {
return fromGeneratedSidecarWrittenProtocolFrame(
protocol.decodeProtocolFrame(toExactUint8Array(payload)),
);
}
export function encodeProtocolFramePayload(
frame: LiveProtocolFrame,
codec: ProtocolFramePayloadCodec,
): Uint8Array {
if (codec === "json") {
return encodeJsonFramePayload(frame);
}
return encodeBareProtocolFrame(frame);
}
export function decodeProtocolFramePayload(
payload: Uint8Array,
codec: ProtocolFramePayloadCodec,
): LiveSidecarWrittenProtocolFrame {
if (codec === "json") {
return decodeJsonFramePayload<LiveSidecarWrittenProtocolFrame>(payload);
}
return decodeBareProtocolFrame(payload);
}
export function classifySidecarWrittenProtocolFrame(
frame: LiveSidecarWrittenProtocolFrame,
): ClassifiedSidecarWrittenProtocolFrame {
switch (frame.frame_type) {
case "response":
return {
kind: "response",
requestId: frame.request_id,
frame,
};
case "event":
return { kind: "event", frame };
case "sidecar_request":
return { kind: "sidecarRequest", frame };
}
}
export function fromGeneratedSidecarWrittenProtocolFrame(
frame: protocol.ProtocolFrame,
): LiveSidecarWrittenProtocolFrame {
switch (frame.tag) {
case "ResponseFrame":
return {
frame_type: "response",
schema: toLiveProtocolSchema(frame.val.schema),
request_id: bigIntToSafeNumber(
frame.val.requestId,
"response request id",
),
ownership: fromGeneratedOwnershipScope(frame.val.ownership),
payload: fromGeneratedResponsePayload(frame.val.payload),
};
case "EventFrame":
return {
frame_type: "event",
schema: toLiveProtocolSchema(frame.val.schema),
ownership: fromGeneratedOwnershipScope(frame.val.ownership),
payload: fromGeneratedEventPayload(frame.val.payload),
};
case "SidecarRequestFrame":
return {
frame_type: "sidecar_request",
schema: toLiveProtocolSchema(frame.val.schema),
request_id: bigIntToSafeNumber(
frame.val.requestId,
"sidecar request id",
),
ownership: fromGeneratedOwnershipScope(frame.val.ownership),
payload: fromGeneratedSidecarRequestPayload(frame.val.payload),
};
case "RequestFrame":
case "SidecarResponseFrame":
case "ControlFrame":
throw new Error(`unsupported BARE protocol frame tag: ${frame.tag}`);
}
}
export function toGeneratedControlPayload(
payload: LiveControlPayload,
): protocol.ControlPayload {
switch (payload.type) {
case "shutdown":
return {
tag: "ShutdownControl",
val: { reason: payload.reason },
};
}
}
export function toLiveProtocolSchema(
schema: protocol.ProtocolSchema,
): typeof SIDECAR_PROTOCOL_SCHEMA {
return validateSidecarProtocolSchema(schema);
}