import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse, Input } from 'hono' import { validator } from 'hono/validator' import type { z, ZodSchema, ZodError } from 'zod' export type Hook< T, E extends Env, P extends string, Target extends keyof ValidationTargets = keyof ValidationTargets, O = {} > = ( result: ({ success: true; data: T } | { success: false; error: ZodError; data: T }) & { target: Target }, c: Context ) => Response | void | TypedResponse | Promise> type HasUndefined = undefined extends T ? true : false export const zValidator = < T extends ZodSchema, Target extends keyof ValidationTargets, E extends Env, P extends string, In = z.input, Out = z.output, I extends Input = { in: HasUndefined extends true ? { [K in Target]?: K extends 'json' ? In : HasUndefined extends true ? { [K2 in keyof In]?: ValidationTargets[K][K2] } : { [K2 in keyof In]: ValidationTargets[K][K2] } } : { [K in Target]: K extends 'json' ? In : HasUndefined extends true ? { [K2 in keyof In]?: ValidationTargets[K][K2] } : { [K2 in keyof In]: ValidationTargets[K][K2] } } out: { [K in Target]: Out } }, V extends I = I >( target: Target, schema: T, hook?: Hook, E, P, Target> ): MiddlewareHandler => // @ts-expect-error not typed well validator(target, async (value, c) => { const result = await schema.safeParseAsync(value) if (hook) { const hookResult = await hook({ data: value, ...result, target }, c) if (hookResult) { if (hookResult instanceof Response) { return hookResult } if ('response' in hookResult) { return hookResult.response } } } if (!result.success) { return c.json(result, 400) } return result.data as z.infer })