diff --git a/.changeset/hot-symbols-thank.md b/.changeset/hot-symbols-thank.md new file mode 100644 index 00000000..65fa222b --- /dev/null +++ b/.changeset/hot-symbols-thank.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-validator': minor +--- + +Added optional opt field for passthrough and might further editions, passthrough and tests to it diff --git a/packages/zod-validator/src/index.test.ts b/packages/zod-validator/src/index.test.ts index 23e21bd8..f5074e1c 100644 --- a/packages/zod-validator/src/index.test.ts +++ b/packages/zod-validator/src/index.test.ts @@ -49,10 +49,10 @@ describe('Basic', () => { } } & { query?: - | { - name?: string | undefined - } - | undefined + | { + name?: string | undefined + } + | undefined } output: { success: boolean @@ -378,3 +378,87 @@ describe('Case-Insensitive Headers', () => { type verify = Expect> }) }) + +describe('With options + passthrough', () => { + const app = new Hono() + const jsonSchema = z.object({ + name: z.string(), + age: z.number() + }); + + const route = app.post( + '/', + zValidator('json', jsonSchema), + (c) => { + const data = c.req.valid('json') + + return c.json({ + success: true, + data + }) + } + ).post('/extended', + zValidator('json', jsonSchema, undefined, { + passthroughObject: true + }), + (c) => { + const data = c.req.valid('json') + + return c.json({ + success: true, + data + }) + } + ) + + it('Should be ok due to passthrough schema', async () => { + const req = new Request('http://localhost/extended', { + body: JSON.stringify({ + name: 'Superman', + age: 20, + length: 170, + weight: 55 + }), + 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.json()).toEqual({ + success: true, + data: { + name: "Superman", + age: 20, + length: 170, + weight: 55 + } + }) + }) + it('Should be ok due to required schema', async () => { + const req = new Request('http://localhost', { + body: JSON.stringify({ + name: 'Superman', + age: 20, + }), + 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.json()).toEqual({ + success: true, + data: { + name: "Superman", + age: 20 + } + }) + }) + +}) \ No newline at end of file diff --git a/packages/zod-validator/src/index.ts b/packages/zod-validator/src/index.ts index 8a96e9d3..cb8f8b1d 100644 --- a/packages/zod-validator/src/index.ts +++ b/packages/zod-validator/src/index.ts @@ -1,7 +1,7 @@ import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono' import { validator } from 'hono/validator' import { ZodObject } from 'zod' -import type { ZodError, ZodSchema, z } from 'zod' +import { ZodError, ZodSchema, z } from 'zod' export type Hook< T, @@ -27,23 +27,24 @@ export const zValidator = < Out = z.output, I extends Input = { in: HasUndefined extends true - ? { - [K in Target]?: In extends ValidationTargets[K] - ? In - : { [K2 in keyof In]?: ValidationTargets[K][K2] } - } - : { - [K in Target]: In extends ValidationTargets[K] - ? In - : { [K2 in keyof In]: ValidationTargets[K][K2] } - } + ? { + [K in Target]?: In extends ValidationTargets[K] + ? In + : { [K2 in keyof In]?: ValidationTargets[K][K2] } + } + : { + [K in Target]: In extends ValidationTargets[K] + ? In + : { [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> + hook?: Hook, E, P, Target>, + opt?: { passthroughObject?: boolean } ): MiddlewareHandler => // @ts-expect-error not typed well validator(target, async (value, c) => { @@ -63,7 +64,12 @@ export const zValidator = < ) } - const result = await schema.safeParseAsync(validatorValue) + let result: z.infer; + if (opt && "passthroughObject" in opt) { + result = await schema.passthrough().safeParseAsync(validatorValue) + } else { + result = await schema.safeParseAsync(validatorValue) + } if (hook) { const hookResult = await hook({ data: validatorValue, ...result, target }, c)