81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import { zValidator } from "@hono/zod-validator";
|
|
import type { MiddlewareHandler } from "@hono/hono";
|
|
import { z } from "zod";
|
|
import type { HonoEnv } from "./types.ts";
|
|
import { ZodSchema } from "zod/v4";
|
|
import { HTTPStatus } from "@oneday/http-status";
|
|
import Logging from "@proxnet/undead-logging";
|
|
|
|
const log = new Logging("Validation");
|
|
|
|
// thanks claude, this hurt my brain!
|
|
|
|
export const typedZValidator = <
|
|
T extends 'query' | 'json' | 'form' | 'header' | 'param' | 'cookie',
|
|
S extends ZodSchema
|
|
>(
|
|
target: T,
|
|
schema: S,
|
|
httpOk?: boolean,
|
|
recNetError?: string
|
|
) => {
|
|
return zValidator(target, schema, (result, c) => {
|
|
|
|
if (!result.success) {
|
|
log.w(`Validation failed: ${JSON.stringify(result.error)}`);
|
|
|
|
if (recNetError) return c.json({ success: false, error: recNetError }, httpOk == true ? HTTPStatus.OK : HTTPStatus.BadRequest);
|
|
else return c.json({ success: false, error: "Request validation failed" }, httpOk == true ? HTTPStatus.OK : HTTPStatus.BadRequest);
|
|
}
|
|
}) as MiddlewareHandler<
|
|
HonoEnv,
|
|
string,
|
|
{
|
|
in: { [K in T]: z.input<S> };
|
|
out: { [K in T]: z.output<S> };
|
|
}
|
|
>;
|
|
};
|
|
|
|
/**
|
|
* @deprecated Use transformCheckEnum after #.coerce.number() (optionally)
|
|
*/
|
|
export const transformStringToEnum = <T>(anEnum: { [s: string]: string | number }, str?: boolean) => {
|
|
return (arg: string, ctx: z.RefinementCtx<string | number>) => {
|
|
if (!str) {
|
|
const int = parseInt(arg);
|
|
if (isNaN(int) || !Number.isSafeInteger(int)) ctx.addIssue("number was not valid");
|
|
else if (anEnum[int]) return int as T;
|
|
else ctx.addIssue("number was not a valid enum member");
|
|
} else {
|
|
const vals = Object.values(anEnum);
|
|
if (vals.includes(arg)) return arg as T;
|
|
else ctx.addIssue("string was not a valid enum member");
|
|
}
|
|
}
|
|
}
|
|
|
|
export const transformCheckEnum = <T>(anEnum: { [s: string]: string | number }) => {
|
|
return (arg: number | string, ctx: z.RefinementCtx<number | string>) => {
|
|
function invalid() {
|
|
ctx.addIssue("Invalid enum member");
|
|
}
|
|
|
|
if (typeof arg == 'string') {
|
|
if (typeof anEnum[arg] == 'number') return anEnum[arg] as T;
|
|
else {
|
|
invalid();
|
|
return null;
|
|
}
|
|
} else if (typeof arg == 'number') {
|
|
if (typeof anEnum[arg] == 'string') return arg as T;
|
|
else {
|
|
invalid();
|
|
return null;
|
|
}
|
|
} else {
|
|
invalid();
|
|
return null;
|
|
}
|
|
}
|
|
} |