fix(zod-validator): support for case-insensitive `headers` target validation (#860)
parent
27ff98f26e
commit
803f011e41
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@hono/zod-validator': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Case-insensitive Zod schemas for headers
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse, Input } from 'hono'
|
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
|
||||||
import { validator } from 'hono/validator'
|
import { validator } from 'hono/validator'
|
||||||
import type { z, ZodSchema, ZodError } from 'zod'
|
import { ZodObject, type ZodError, type ZodSchema, type z } from 'zod'
|
||||||
|
|
||||||
export type Hook<
|
export type Hook<
|
||||||
T,
|
T,
|
||||||
|
@ -46,10 +46,26 @@ export const zValidator = <
|
||||||
): MiddlewareHandler<E, P, V> =>
|
): MiddlewareHandler<E, P, V> =>
|
||||||
// @ts-expect-error not typed well
|
// @ts-expect-error not typed well
|
||||||
validator(target, async (value, c) => {
|
validator(target, async (value, c) => {
|
||||||
const result = await schema.safeParseAsync(value)
|
let validatorValue = value
|
||||||
|
|
||||||
|
// in case where our `target` === `header`, Hono parses all of the headers into lowercase.
|
||||||
|
// this might not match the Zod schema, so we want to make sure that we account for that when parsing the schema.
|
||||||
|
if (target === 'header' && schema instanceof ZodObject) {
|
||||||
|
// create an object that maps lowercase schema keys to lowercase
|
||||||
|
const schemaKeys = Object.keys(schema.shape)
|
||||||
|
const caseInsensitiveKeymap = Object.fromEntries(
|
||||||
|
schemaKeys.map((key) => [key.toLowerCase(), key])
|
||||||
|
)
|
||||||
|
|
||||||
|
validatorValue = Object.fromEntries(
|
||||||
|
Object.entries(value).map(([key, value]) => [caseInsensitiveKeymap[key] || key, value])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await schema.safeParseAsync(validatorValue)
|
||||||
|
|
||||||
if (hook) {
|
if (hook) {
|
||||||
const hookResult = await hook({ data: value, ...result, target }, c)
|
const hookResult = await hook({ data: validatorValue, ...result, target }, c)
|
||||||
if (hookResult) {
|
if (hookResult) {
|
||||||
if (hookResult instanceof Response) {
|
if (hookResult instanceof Response) {
|
||||||
return hookResult
|
return hookResult
|
||||||
|
|
|
@ -339,3 +339,33 @@ describe('Only Types', () => {
|
||||||
type verify = Expect<Equal<Expected, Actual>>
|
type verify = Expect<Equal<Expected, Actual>>
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Case-Insensitive Headers', () => {
|
||||||
|
it('Should ignore the case for headers in the Zod schema and return 200', () => {
|
||||||
|
const app = new Hono()
|
||||||
|
const headerSchema = z.object({
|
||||||
|
'Content-Type': z.string(),
|
||||||
|
ApiKey: z.string(),
|
||||||
|
onlylowercase: z.string(),
|
||||||
|
ONLYUPPERCASE: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = app.get('/', zValidator('header', headerSchema), (c) => {
|
||||||
|
const headers = c.req.valid('header')
|
||||||
|
return c.json(headers)
|
||||||
|
})
|
||||||
|
|
||||||
|
type Actual = ExtractSchema<typeof route>
|
||||||
|
type Expected = {
|
||||||
|
'/': {
|
||||||
|
$get: {
|
||||||
|
input: {
|
||||||
|
header: z.infer<typeof headerSchema>
|
||||||
|
}
|
||||||
|
output: z.infer<typeof headerSchema>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type verify = Expect<Equal<Expected, Actual>>
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue