From 73c961310a90b192844434c69e19ed67e0458768 Mon Sep 17 00:00:00 2001 From: migawka Date: Fri, 18 Apr 2025 23:34:53 +0200 Subject: [PATCH 1/2] add optional passthrough to options --- packages/zod-validator/src/index.ts | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/zod-validator/src/index.ts b/packages/zod-validator/src/index.ts index 8a96e9d3c..a630869e1 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 && "passthrough" 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) From c8b2f10eee334bd1b3e57f9a7917f61da858ea93 Mon Sep 17 00:00:00 2001 From: migawka Date: Sat, 19 Apr 2025 00:22:33 +0200 Subject: [PATCH 2/2] runable tests and yarn changeset --- .changeset/hot-symbols-thank.md | 5 ++ packages/zod-validator/src/index.test.ts | 92 ++++++++++++++++++++++-- packages/zod-validator/src/index.ts | 2 +- 3 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 .changeset/hot-symbols-thank.md diff --git a/.changeset/hot-symbols-thank.md b/.changeset/hot-symbols-thank.md new file mode 100644 index 000000000..65fa222b2 --- /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 23e21bd8a..f5074e1cc 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 a630869e1..cb8f8b1dc 100644 --- a/packages/zod-validator/src/index.ts +++ b/packages/zod-validator/src/index.ts @@ -65,7 +65,7 @@ export const zValidator = < } let result: z.infer; - if (opt && "passthrough" in opt) { + if (opt && "passthroughObject" in opt) { result = await schema.passthrough().safeParseAsync(validatorValue) } else { result = await schema.safeParseAsync(validatorValue)