import type { Context } from '../context.ts' import { getCookie } from '../middleware/cookie/index.ts' import type { Env, ValidationTargets, MiddlewareHandler } from '../types.ts' type ValidationTargetKeysWithBody = 'form' | 'json' type ValidationTargetByMethod = M extends 'get' | 'head' // GET and HEAD request must not have a body content. ? Exclude : keyof ValidationTargets export type ValidationFunction< InputType, OutputType, E extends Env = {}, P extends string = string > = ( value: InputType, c: Context ) => OutputType | Response | Promise | Promise export const validator = < InputType, P extends string, M extends string, U extends ValidationTargetByMethod, OutputType = ValidationTargets[U], P2 extends string = P, V extends { in: { [K in U]: unknown extends InputType ? OutputType : InputType } out: { [K in U]: OutputType } } = { in: { [K in U]: unknown extends InputType ? OutputType : InputType } out: { [K in U]: OutputType } }, // eslint-disable-next-line @typescript-eslint/no-explicit-any E extends Env = any >( target: U, validationFunc: ValidationFunction< unknown extends InputType ? ValidationTargets[U] : InputType, OutputType, E, P2 > ): MiddlewareHandler => { return async (c, next) => { let value = {} switch (target) { case 'json': try { value = await c.req.json() } catch { console.error('Error: Malformed JSON in request body') return c.json( { success: false, message: 'Malformed JSON in request body', }, 400 ) } break case 'form': value = await c.req.parseBody() break case 'query': value = Object.fromEntries( Object.entries(c.req.queries()).map(([k, v]) => { return v.length === 1 ? [k, v[0]] : [k, v] }) ) break case 'queries': value = c.req.queries() console.log('Warnings: Validate type `queries` is deprecated. Use `query` instead.') break case 'param': value = c.req.param() as Record break case 'header': value = c.req.header() break case 'cookie': value = getCookie(c) break } const res = await validationFunc(value as never, c as never) if (res instanceof Response) { return res } c.req.addValidatedData(target, res as never) await next() } }