/** Closed reply envelopes shared by isolated regex evaluators; decoded in the host. */ import { Type, type Static, type TSchema } from "typebox"; import { Value } from "typebox/value"; import { HashlineError, type HashlineErrorArguments } from "../core/errors.ts"; import { ERROR_CODES, ERROR_DEFINITIONS, CAUSE_SCHEMA, type ErrorCode } from "./report-schema.ts"; import { restoreCause, type WorkerError } from "./report-cause.ts"; const ERROR_SCHEMAS = ERROR_CODES.map((code) => { const facts = ERROR_DEFINITIONS[code].facts; return Type.Object( { code: Type.Literal(code), message: Type.String(), facts: (facts.required?.length ?? 0) > 0 ? facts : Type.Optional(facts), cause: Type.Optional(CAUSE_SCHEMA), }, { additionalProperties: false }, ); }); export type RegexWorkerReply = | { readonly result: Result } | { readonly error: WorkerError }; export function invalidWorkerResult(): HashlineError<"REGEX_WORKER_FAILED"> { return new HashlineError("REGEX_WORKER_FAILED", "Regex worker returned an invalid result."); } export function workerReplyDecoder(result: T) { const schema = Type.Union([ Type.Object({ result }, { additionalProperties: false }), Type.Object({ error: Type.Union(ERROR_SCHEMAS) }, { additionalProperties: false }), ]); // The dynamic error union is validated against the same code-specific schemas. return (value: unknown): RegexWorkerReply> | undefined => Value.Check(schema, value) ? (value as RegexWorkerReply>) : undefined; } /** Restore an error workerReplyDecoder checked as the same HashlineError the worker threw. */ export function restoreWorkerError(error: WorkerError): HashlineError { // The parameter ties facts to code; TypeScript cannot carry that link into the generic constructor. const options = [ { facts: error.facts, cause: restoreCause(error.cause) }, ] as HashlineErrorArguments; return new HashlineError(error.code, error.message, ...options); }