alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
69 lines (63 loc) • 2.08 kB
text/typescript
import { TypeBoxError } from "../errors/TypeBoxError.ts";
import type { Static, TSchema } from "./TypeProvider.ts";
export interface ValidateOptions {
trim?: boolean;
nullToUndefined?: boolean;
deleteUndefined?: boolean;
}
/**
* Validates + coerces a value against a zod schema.
*
* Trimming / lowercasing / defaults / unknown-key stripping all live in the
* schema itself now (zod), so this is a thin wrapper over `schema.parse`.
* No typebox `Compile`, no `eval` — safe inside Cloudflare Workers.
*/
export class SchemaValidator {
public validate<T extends TSchema>(
schema: T,
value: unknown,
_options: ValidateOptions = {},
): Static<T> {
const result = schema.safeParse(value);
if (!result.success) {
// Map the first zod issue onto the framework's validation-error contract
// (`TypeBoxError`), which the HTTP layer, CLI, and ORM all catch.
const issue = result.error.issues[0];
const path = issue?.path?.length ? issue.path.join("/") : "";
let message = issue?.message ?? "Validation failed";
// zod reports a missing required field as "expected X, received
// undefined" — phrase it as a "required" rejection.
if (
issue?.code === "invalid_type" &&
/received undefined/i.test(message) &&
path
) {
message = `'${path}' is required`;
}
throw new TypeBoxError({
message,
instancePath: path ? `/${path}` : "",
params: issue,
});
}
return result.data as Static<T>;
}
public safeValidate<T extends TSchema>(schema: T, value: unknown) {
return schema.safeParse(value);
}
/** Zod schemas are immutable — clone is a pass-through. */
public clone<T extends TSchema>(schema: T): T {
return schema;
}
/**
* Legacy pre-processing entry point. Coercion now happens inside `parse`,
* so this is a no-op kept for call-site compatibility.
*/
public beforeParse(
_schema: unknown,
value: unknown,
_options?: ValidateOptions,
): unknown {
return value;
}
}