mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-05 16:04:43 +00:00
* Split the inbound WebSocket validator per message type zod-aot compiled WSOutboundMessageSchema into one 11 MB function. Every inbound frame, heartbeats included, ran through it, so V8 hosts (daemon clients, plugin hosts, the CLI, Electron) eventually Maglev-compiled the whole function: about 800 MB of compiler memory and multi-second stalls, again after each deoptimization. Generate a tag-only envelope validator plus one validator per session message, and dispatch on the message type. Issue paths, defaults and passthrough behave as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use forward slashes in generated validator imports path.relative() returns backslash paths on Windows, which break the import specifiers written into the generated files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Declare the session export helper as a function Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.1 KiB
TypeScript
55 lines
2.1 KiB
TypeScript
import { ZodRealError } from "zod";
|
|
import type { z } from "zod";
|
|
import {
|
|
SessionOutboundMessageValidators,
|
|
WSOutboundEnvelope,
|
|
} from "../generated/validation/ws-outbound.aot.js";
|
|
import type { SessionOutboundMessage, WSOutboundMessage } from "../messages.js";
|
|
|
|
type ValidationResult<T> = { success: true; data: T } | { success: false; error: z.ZodError };
|
|
|
|
interface GeneratedValidator<T> {
|
|
safeParse(input: unknown): ValidationResult<T>;
|
|
}
|
|
|
|
// zod-aot emits runtime JavaScript from the envelope and message schemas but not its TypeScript surface.
|
|
// Protocol regression tests cover the patched compiler cases behind this boundary.
|
|
const envelopeValidator = WSOutboundEnvelope as GeneratedValidator<WSOutboundMessage>;
|
|
const sessionMessageValidators = SessionOutboundMessageValidators as Record<
|
|
SessionOutboundMessage["type"],
|
|
GeneratedValidator<SessionOutboundMessage>
|
|
>;
|
|
|
|
// The message validator reports paths relative to the message, nested issues included.
|
|
function underMessage(issue: z.core.$ZodIssue): z.core.$ZodIssue {
|
|
const path = ["message", ...issue.path];
|
|
switch (issue.code) {
|
|
case "invalid_union":
|
|
return issue.inclusive === false
|
|
? { ...issue, path }
|
|
: { ...issue, path, errors: issue.errors.map((branch) => branch.map(underMessage)) };
|
|
case "invalid_key":
|
|
case "invalid_element":
|
|
return { ...issue, path, issues: issue.issues.map(underMessage) };
|
|
default:
|
|
return { ...issue, path };
|
|
}
|
|
}
|
|
|
|
export function validateWSOutboundMessage(input: unknown): ValidationResult<WSOutboundMessage> {
|
|
const envelope = envelopeValidator.safeParse(input);
|
|
if (!envelope.success || envelope.data.type !== "session") {
|
|
return envelope;
|
|
}
|
|
const message = envelope.data.message;
|
|
const result = sessionMessageValidators[message.type].safeParse(message);
|
|
if (!result.success) {
|
|
return { success: false, error: new ZodRealError(result.error.issues.map(underMessage)) };
|
|
}
|
|
// Message validators return a new object when they apply defaults.
|
|
if (result.data === message) {
|
|
return envelope;
|
|
}
|
|
return { success: true, data: { ...envelope.data, message: result.data } };
|
|
}
|