diff --git a/.changeset/eleven-birds-beg.md b/.changeset/eleven-birds-beg.md new file mode 100644 index 00000000..f75e4180 --- /dev/null +++ b/.changeset/eleven-birds-beg.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-validator': minor +--- + +feat: support coerce diff --git a/packages/zod-validator/package.json b/packages/zod-validator/package.json index 094e0b22..750ea90a 100644 --- a/packages/zod-validator/package.json +++ b/packages/zod-validator/package.json @@ -31,10 +31,10 @@ "zod": "^3.19.1" }, "devDependencies": { - "hono": "^3.11.7", + "hono": "^4.0.10", "jest": "^29.7.0", "rimraf": "^5.0.5", "typescript": "^5.3.3", - "zod": "3.19.1" + "zod": "^3.22.4" } } diff --git a/packages/zod-validator/src/index.ts b/packages/zod-validator/src/index.ts index 32eab9b9..ec355f41 100644 --- a/packages/zod-validator/src/index.ts +++ b/packages/zod-validator/src/index.ts @@ -1,4 +1,4 @@ -import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse } from 'hono' +import type { Context, MiddlewareHandler, Env, ValidationTargets, TypedResponse, Input } from 'hono' import { validator } from 'hono/validator' import type { z, ZodSchema, ZodError } from 'zod' @@ -14,20 +14,33 @@ export const zValidator = < Target extends keyof ValidationTargets, E extends Env, P extends string, - I = z.input, - O = z.output, - V extends { - in: HasUndefined extends true ? { [K in Target]?: I } : { [K in Target]: I } - out: { [K in Target]: O } - } = { - in: HasUndefined extends true ? { [K in Target]?: I } : { [K in Target]: I } - out: { [K in Target]: O } - } + In = z.input, + Out = z.output, + I extends Input = { + in: HasUndefined extends true + ? { + [K in Target]?: K extends 'json' + ? In + : HasUndefined extends true + ? { [K2 in keyof In]?: ValidationTargets[K][K2] } + : { [K2 in keyof In]: ValidationTargets[K][K2] } + } + : { + [K in Target]: K extends 'json' + ? In + : HasUndefined extends true + ? { [K2 in keyof In]?: ValidationTargets[K][K2] } + : { [K2 in keyof In]: ValidationTargets[K][K2] } + } + out: { [K in Target]: Out } + }, + V extends I = I >( target: Target, schema: T, hook?: Hook, E, P> ): MiddlewareHandler => + // @ts-expect-error not typed well validator(target, async (value, c) => { const result = await schema.safeParseAsync(value) diff --git a/packages/zod-validator/test/index.test.ts b/packages/zod-validator/test/index.test.ts index 230b1ae9..a48a49c8 100644 --- a/packages/zod-validator/test/index.test.ts +++ b/packages/zod-validator/test/index.test.ts @@ -28,7 +28,7 @@ describe('Basic', () => { const data = c.req.valid('json') const query = c.req.valid('query') - return c.jsonT({ + return c.json({ success: true, message: `${data.name} is ${data.age}`, queryName: query?.name, @@ -48,7 +48,7 @@ describe('Basic', () => { } & { query?: | { - name?: string | undefined + name?: string | string[] | undefined } | undefined } @@ -92,6 +92,9 @@ describe('Basic', () => { age: '20', }), method: 'POST', + headers: { + 'content-type': 'application/json', + }, }) const res = await app.request(req) expect(res).not.toBeNull() @@ -101,6 +104,47 @@ describe('Basic', () => { }) }) +describe('coerce', () => { + const app = new Hono() + + const querySchema = z.object({ + page: z.coerce.number(), + }) + + const route = app.get('/page', zValidator('query', querySchema), (c) => { + const { page } = c.req.valid('query') + return c.json({ page }) + }) + + type Actual = ExtractSchema + type Expected = { + '/page': { + $get: { + input: { + query: { + page: string | string[] + } + } + output: { + page: number + } + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type verify = Expect> + + it('Should return 200 response', async () => { + const res = await app.request('/page?page=123') + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ + page: 123, + }) + }) +}) + describe('With Hook', () => { const app = new Hono() diff --git a/yarn.lock b/yarn.lock index 61dff423..0b47b174 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1748,11 +1748,11 @@ __metadata: version: 0.0.0-use.local resolution: "@hono/zod-validator@workspace:packages/zod-validator" dependencies: - hono: "npm:^3.11.7" + hono: "npm:^4.0.10" jest: "npm:^29.7.0" rimraf: "npm:^5.0.5" typescript: "npm:^5.3.3" - zod: "npm:3.19.1" + zod: "npm:^3.22.4" peerDependencies: hono: ">=3.9.0" zod: ^3.19.1 @@ -8917,6 +8917,13 @@ __metadata: languageName: node linkType: hard +"hono@npm:^4.0.10": + version: 4.0.10 + resolution: "hono@npm:4.0.10" + checksum: a68deed2a216dd956e6012a834312a09ffcf18a8e61b851ec6b168ad5cf13d9696f7fa3dce25286b4b2e92f6ea7102ece8f097f423ff385236f7b83c3a68032c + languageName: node + linkType: hard + "hono@npm:^4.0.2": version: 4.0.2 resolution: "hono@npm:4.0.2" @@ -18216,13 +18223,6 @@ __metadata: languageName: node linkType: hard -"zod@npm:3.19.1": - version: 3.19.1 - resolution: "zod@npm:3.19.1" - checksum: e08197793f26916f8abea40687fc968b2de0471049b29b7ff25825a9f28ba24205d1c5b8ad26df17538b051928192f9ef8f9ef3132aece9cb56f0830a7450c26 - languageName: node - linkType: hard - "zod@npm:^3.20.2, zod@npm:^3.20.6, zod@npm:^3.22.1, zod@npm:^3.22.4": version: 3.22.4 resolution: "zod@npm:3.22.4"