/**
* 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>)
}