Files
quixos-protocol/src/execution-world/ports.ts
T

79 lines
3.8 KiB
TypeScript
Raw Normal View History

/** Process/module ABI shared by generated adapters. Values are decoded against the checked
* operation/port schema by the coordinator, not trusted because this header parsed. */
export const transactionPortAbi = "quixos-transaction-ports-v1";
export type PortAction =
| { kind: "state"; primitive: "read" | "write" | "edit" }
| { kind: "edge"; primitive: "resolve" | "connect" | "disconnect" }
| { kind: "interface"; operationId: string; key?: string }
| { kind: "query" }
| { kind: "creation"; key: string };
export interface PortInvocation {
abi: typeof transactionPortAbi;
/** Coordinator-issued frame and invocation handles, not application object IDs. */
frame: string;
invocation: string;
sequence: number;
portId: string;
action: PortAction;
input: unknown;
}
export interface PackageInvocation {
abi: typeof transactionPortAbi;
frame: string;
invocation: string;
exportId: string;
effect: "snapshot" | "transaction" | "remote";
/** Capability arguments/receiver use the versioned managed-reference codec. */
input: unknown;
receiver: unknown | null;
}
export type InvocationReply =
| { abi: typeof transactionPortAbi; invocation: string; status: "returned"; output: unknown }
| { abi: typeof transactionPortAbi; invocation: string; status: "failed"; error: { code: string; message: string } };
export type ServiceControl =
| { abi: typeof transactionPortAbi; kind: "ready" }
| { abi: typeof transactionPortAbi; kind: "cancel"; invocation: string }
| { abi: typeof transactionPortAbi; kind: "shutdown" };
/** ABI-only header check. LF-08 resolves handles and enforces the frame/port authority. */
export function checkPortInvocation(value: unknown): PortInvocation {
if (!value || typeof value !== "object" || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype)
throw new Error("Invalid port invocation");
const fields = value as Record<string, unknown>;
const keys = ["abi", "frame", "invocation", "sequence", "portId", "action", "input"];
if (
Object.keys(fields).length !== keys.length ||
keys.some((key) => !Object.hasOwn(fields, key)) ||
fields.abi !== transactionPortAbi
)
throw new Error("Invalid port invocation fields or ABI");
for (const key of ["frame", "invocation", "portId"])
if (typeof fields[key] !== "string" || !fields[key] || fields[key].length > 4096)
throw new Error(`Invalid port invocation ${key}`);
if (!Number.isSafeInteger(fields.sequence) || (fields.sequence as number) < 1)
throw new Error("Invalid port invocation sequence");
const action = fields.action as Record<string, unknown>;
if (
!action ||
typeof action !== "object" ||
Array.isArray(action) ||
Object.getPrototypeOf(action) !== Object.prototype
)
throw new Error("Invalid port action");
const exact = (names: string[]) =>
Object.keys(action).length === names.length && names.every((name) => Object.hasOwn(action, name));
const identifier = (value: unknown) => typeof value === "string" && value.length > 0 && value.length <= 1024;
const valid =
action.kind === "state"
? exact(["kind", "primitive"]) && ["read", "write", "edit"].includes(action.primitive as string)
: action.kind === "edge"
? exact(["kind", "primitive"]) && ["resolve", "connect", "disconnect"].includes(action.primitive as string)
: action.kind === "interface"
? (exact(["kind", "operationId"]) || (exact(["kind", "operationId", "key"]) && identifier(action.key))) &&
identifier(action.operationId)
: action.kind === "creation"
? exact(["kind", "key"]) && identifier(action.key)
: action.kind === "query" && exact(["kind"]);
if (!valid) throw new Error("Invalid port action");
return { ...fields, action: { ...action } } as unknown as PortInvocation;
}