/** * The Standard Schema v1 interface (https://standardschema.dev), vendored as * types + a tiny runtime helper so any compliant validator - zod, valibot, * arktype, … - validates requests without coupling the framework to one lib. * The spec is MIT-licensed and explicitly designed to be copied. */ export interface StandardSchemaV1 { readonly "~standard": StandardSchemaProps } export interface StandardSchemaProps { readonly version: 1 readonly vendor: string readonly validate: (value: unknown) => StandardResult | Promise> readonly types?: StandardTypes | undefined } export type StandardResult = StandardSuccess | StandardFailure export interface StandardSuccess { readonly value: Output readonly issues?: undefined } export interface StandardFailure { readonly issues: ReadonlyArray } export interface StandardIssue { readonly message: string readonly path?: ReadonlyArray | undefined } export interface StandardPathSegment { readonly key: PropertyKey } export interface StandardTypes { readonly input: Input readonly output: Output } export type InferOutput = NonNullable< Schema["~standard"]["types"] >["output"] export type InferInput = NonNullable< Schema["~standard"]["types"] >["input"] export type ValidationOutcome = | { readonly ok: true; readonly value: Output } | { readonly ok: false; readonly issues: ReadonlyArray } /** Format Standard Schema issues consistently across server and client contract diagnostics. */ export function formatStandardIssues(issues: ReadonlyArray): string { return issues .map((issue) => { const path = Array.isArray(issue.path) ? issue.path .map((segment) => String(typeof segment === "object" && segment !== null ? segment.key : segment), ) .join(".") : "" return path === "" ? issue.message : `${path}: ${issue.message}` }) .join("; ") } function normalizeStandardResult( result: StandardResult, ): ValidationOutcome { if (result.issues !== undefined) { return { ok: false, issues: result.issues } } return { ok: true, value: result.value } } /** Run a Standard Schema and normalize the result. Sync validators stay sync; async validators are awaited. */ export function validateStandard( schema: Schema, value: unknown, ): ValidationOutcome> | Promise>> { const result = schema["~standard"].validate(value) return result instanceof Promise ? result.then((settled) => normalizeStandardResult(settled as StandardResult>), ) : normalizeStandardResult(result as StandardResult>) }