feat(zod-validator): pass target from zod-validator to a hook (#695)
* feat(zod-validator): passing target to hook * feat(zod-validator): trigger githubaction * feat(zod-validator): add changeset * feat: pass narrower type to zod validation hookpull/749/head
parent
45f5d45bfa
commit
eda3584791
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@hono/zod-validator': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(zod-validator): pass target to a hook
|
|
@ -2,9 +2,9 @@ import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse,
|
||||||
import { validator } from 'hono/validator'
|
import { validator } from 'hono/validator'
|
||||||
import type { z, ZodSchema, ZodError } from 'zod'
|
import type { z, ZodSchema, ZodError } from 'zod'
|
||||||
|
|
||||||
export type Hook<T, E extends Env, P extends string, O = {}> = (
|
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 },
|
result: ({ success: true; data: T} | { success: false; error: ZodError; data: T }) & {target: Target },
|
||||||
c: Context<E, P>
|
c: Context<E, P>,
|
||||||
) => Response | void | TypedResponse<O> | Promise<Response | void | TypedResponse<O>>
|
) => Response | void | TypedResponse<O> | Promise<Response | void | TypedResponse<O>>
|
||||||
|
|
||||||
type HasUndefined<T> = undefined extends T ? true : false
|
type HasUndefined<T> = undefined extends T ? true : false
|
||||||
|
@ -38,14 +38,14 @@ export const zValidator = <
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: T,
|
schema: T,
|
||||||
hook?: Hook<z.infer<T>, E, P>
|
hook?: Hook<z.infer<T>, E, P, Target>
|
||||||
): 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)
|
const result = await schema.safeParseAsync(value)
|
||||||
|
|
||||||
if (hook) {
|
if (hook) {
|
||||||
const hookResult = await hook({ data: value, ...result }, c)
|
const hookResult = await hook({ data: value, ...result, target, }, c)
|
||||||
if (hookResult) {
|
if (hookResult) {
|
||||||
if (hookResult instanceof Response) {
|
if (hookResult instanceof Response) {
|
||||||
return hookResult
|
return hookResult
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { Hono } from 'hono'
|
||||||
import type { Equal, Expect } from 'hono/utils/types'
|
import type { Equal, Expect } from 'hono/utils/types'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { zValidator } from '../src'
|
import { zValidator } from '../src'
|
||||||
|
import {vi} from 'vitest'
|
||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
type ExtractSchema<T> = T extends Hono<infer _, infer S> ? S : never
|
type ExtractSchema<T> = T extends Hono<infer _, infer S> ? S : never
|
||||||
|
@ -256,3 +258,46 @@ describe('With Async Hook', () => {
|
||||||
expect(await res.text()).toBe('123 is invalid!')
|
expect(await res.text()).toBe('123 is invalid!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe('With target', () => {
|
||||||
|
it('should call hook for correctly validated target', async () => {
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const jsonHook = vi.fn()
|
||||||
|
const paramHook = vi.fn()
|
||||||
|
const queryHook = vi.fn()
|
||||||
|
app.post(
|
||||||
|
'/:id/post',
|
||||||
|
zValidator('json', schema, jsonHook),
|
||||||
|
zValidator('param', schema, paramHook),
|
||||||
|
zValidator('query', schema, queryHook),
|
||||||
|
(c) => {
|
||||||
|
return c.text('ok')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const req = new Request('http://localhost/1/post?id=2', {
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: '3',
|
||||||
|
}),
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await app.request(req)
|
||||||
|
expect(res).not.toBeNull()
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.text()).toBe('ok')
|
||||||
|
expect(paramHook).toHaveBeenCalledWith({data: {id: '1'}, success: true, target:
|
||||||
|
'param'}, expect.anything())
|
||||||
|
expect(queryHook).toHaveBeenCalledWith({data: {id: '2'}, success: true, target: 'query'}, expect.anything())
|
||||||
|
expect(jsonHook).toHaveBeenCalledWith({data: {id: '3'}, success: true, target: 'json'}, expect.anything())
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue