Skip to content

Commit 081ca0c

Browse files
committed
Add support for Zod 4
1 parent 1465159 commit 081ca0c

File tree

12 files changed

+176
-31
lines changed

12 files changed

+176
-31
lines changed

apis/hono-zod-validator/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[< Back](./../../README.md)
22

3-
# @jderjs/hono-zod-validator v0.1.0
3+
# @jderjs/hono-zod-validator v0.2.0
44

55
## Functions
66

apis/hono-zod-validator/functions/zValidator.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
```ts
66
function zValidator<T, Target>(target, schema): MiddlewareHandler<Env, string, {
7-
in: HasUndefined<input<T>> extends true ? { [K in keyof ValidationTargets]?: input<T> extends ValidationTargets[K] ? input<input<T>> : { [K2 in string | number | symbol]?: ValidationTargets[K][K2] } } : { [K in keyof ValidationTargets]: input<T> extends ValidationTargets[K] ? input<input<T>> : { [K2 in string | number | symbol]: ValidationTargets[K][K2] } };
8-
out: { [K in keyof ValidationTargets]: output<T> };
7+
in: HasUndefined<zInput<T>> extends true ? { [K in keyof ValidationTargets]?: zInput<T> extends ValidationTargets[K] ? ValidationTargets[K] & zInput<T> : { [K2 in string | number | symbol]?: ValidationTargets[K][K2] } } : { [K in keyof ValidationTargets]: zInput<T> extends ValidationTargets[K] ? ValidationTargets[K] & zInput<T> : { [K2 in string | number | symbol]: ValidationTargets[K][K2] } };
8+
out: { [K in keyof ValidationTargets]: zOutput<T> };
99
}>;
1010
```
1111

12-
Defined in: [packages/hono-zod-validator/src/index.ts:66](https://github.com/jder-std/hono/blob/2842c6d10ee2eb6a69808b60fa37fe11e9b4b2af/packages/hono-zod-validator/src/index.ts#L66)
12+
Defined in: [packages/hono-zod-validator/src/index.ts:71](https://github.com/jder-std/hono/blob/1465159f14b3ecf8a286fdcee5879be8360c4c0d/packages/hono-zod-validator/src/index.ts#L71)
1313

1414
Validate the request with Zod.
1515

@@ -72,7 +72,10 @@ app.post(
7272

7373
### T
7474

75-
`T` *extends* `ZodType`\<`any`, `ZodTypeDef`, `any`\>
75+
`T` *extends*
76+
\| `ZodType`\<`any`, `ZodTypeDef`, `any`\>
77+
\| `ZodType`\<`any`, `ZodTypeDef`, `any`\>
78+
\| `$ZodType`\<`unknown`, `unknown`\>
7679

7780
### Target
7881

@@ -91,6 +94,6 @@ app.post(
9194
## Returns
9295

9396
`MiddlewareHandler`\<`Env`, `string`, \{
94-
`in`: `HasUndefined`\<`input`\<`T`\>\> *extends* `true` ? \{ \[K in keyof ValidationTargets\]?: input\<T\> extends ValidationTargets\[K\] ? input\<input\<T\>\> : \{ \[K2 in string \| number \| symbol\]?: ValidationTargets\[K\]\[K2\] \} \} : \{ \[K in keyof ValidationTargets\]: input\<T\> extends ValidationTargets\[K\] ? input\<input\<T\>\> : \{ \[K2 in string \| number \| symbol\]: ValidationTargets\[K\]\[K2\] \} \};
95-
`out`: `{ [K in keyof ValidationTargets]: output<T> }`;
97+
`in`: `HasUndefined`\<`zInput`\<`T`\>\> *extends* `true` ? \{ \[K in keyof ValidationTargets\]?: zInput\<T\> extends ValidationTargets\[K\] ? ValidationTargets\[K\] & zInput\<T\> : \{ \[K2 in string \| number \| symbol\]?: ValidationTargets\[K\]\[K2\] \} \} : \{ \[K in keyof ValidationTargets\]: zInput\<T\> extends ValidationTargets\[K\] ? ValidationTargets\[K\] & zInput\<T\> : \{ \[K2 in string \| number \| symbol\]: ValidationTargets\[K\]\[K2\] \} \};
98+
`out`: `{ [K in keyof ValidationTargets]: zOutput<T> }`;
9699
\}\>

examples/hono-zod-validator/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@jderjs/hono": "workspace:~",
1414
"@jderjs/hono-zod-validator": "workspace:~",
1515
"hono": "4.5.0",
16-
"zod": "3.19.1"
16+
"zod": "3.25.6"
1717
},
1818
"devDependencies": {
1919
"@hono/vite-build": "^1.6.1",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 0.2.0 (2025-05-28)
2+
3+
### What's New
4+
5+
- Add support for Zod 4
6+
7+
### What's Changed
8+
9+
- Require `zod@^3.25.6`
10+
111
## 0.1.0 (2025-05-27)
212

313
First release

packages/hono-zod-validator/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jderjs/hono-zod-validator",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "A Zod validator for Hono",
55
"keywords": [
66
"jder",
@@ -34,15 +34,15 @@
3434
"types": "./dist/index.d.ts",
3535
"files": ["dist"],
3636
"dependencies": {
37-
"@hono/zod-validator": "~0.5.0",
37+
"@hono/zod-validator": "~0.6.0",
3838
"@jderjs/hono": "workspace:~"
3939
},
4040
"devDependencies": {
4141
"hono": "4.5.0",
42-
"zod": "3.19.1"
42+
"zod": "3.25.6"
4343
},
4444
"peerDependencies": {
4545
"hono": "^4.5.0",
46-
"zod": "^3.19.1"
46+
"zod": "^3.25.6"
4747
}
4848
}

packages/hono-zod-validator/src/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import type { ValidationTargets } from "hono";
2-
import type { ZodIssue, ZodSchema } from "zod";
2+
import type * as v3 from "zod";
3+
import type * as v4 from "zod/v4/core";
34

45
import { zValidator as zv } from "@hono/zod-validator";
56
import { createJsonResponse } from "@jderjs/hono/response";
67
import { HTTPException } from "hono/http-exception";
78

9+
type ZodSchema = any extends v4.$ZodType
10+
? v3.ZodType
11+
: v3.ZodType | v4.$ZodType;
12+
813
/**
914
* Validate the request with Zod.
1015
*
@@ -72,7 +77,8 @@ const zValidator = <
7277
) => {
7378
return zv(target, schema, (result, c) => {
7479
if (!result.success) {
75-
const err: ZodIssue | undefined = result.error.issues[0];
80+
const err: v4.$ZodIssue | v3.ZodIssue | undefined =
81+
result.error.issues[0];
7682

7783
throw new HTTPException(400, {
7884
res: createJsonResponse(c, {

pnpm-lock.yaml

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/hono-zod-validator/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
"@jderjs/hono": "workspace:~",
1111
"@jderjs/hono-zod-validator": "workspace:~",
1212
"hono": "4.5.0",
13-
"zod": "3.19.1"
13+
"zod": "3.25.6"
1414
}
1515
}

tests/hono-zod-validator/src/index.test.ts renamed to tests/hono-zod-validator/src/v3.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createJsonResponse } from "@jderjs/hono/response";
55
import { Hono } from "hono";
66
import { testClient } from "hono/testing";
77
import { describe, expect, it } from "vitest";
8-
import { z } from "zod";
8+
import { z } from "zod/v3";
99

1010
const json = z.object({
1111
name: z.string(),
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import type { JsonResponse } from "@jderjs/hono/response";
2+
3+
import { zValidator } from "@jderjs/hono-zod-validator";
4+
import { createJsonResponse } from "@jderjs/hono/response";
5+
import { Hono } from "hono";
6+
import { testClient } from "hono/testing";
7+
import { describe, expect, it } from "vitest";
8+
import { z } from "zod/v4";
9+
10+
const json = z.object({
11+
name: z.string(),
12+
age: z.number(),
13+
});
14+
15+
type Json = z.infer<typeof json>;
16+
17+
const app = new Hono().post("/", zValidator("json", json), (c): Response => {
18+
return createJsonResponse(c, {
19+
data: {
20+
name: c.req.valid("json").name,
21+
age: c.req.valid("json").age,
22+
},
23+
});
24+
});
25+
26+
const client = testClient(app);
27+
28+
describe("Zod validator test", (): void => {
29+
it("should work", async (): Promise<void> => {
30+
const res = await client.index.$post({
31+
json: {
32+
name: "Alpheus",
33+
age: 99,
34+
},
35+
});
36+
37+
expect(res.status).toBe(200);
38+
39+
const result: JsonResponse<Json> =
40+
(await res.json()) as JsonResponse<Json>;
41+
42+
expect(result.success).toBe(true);
43+
44+
expect(result.data?.name).toBe("Alpheus");
45+
46+
expect(result.data?.age).toBe(99);
47+
});
48+
49+
it("should not work with empty age", async (): Promise<void> => {
50+
const res = await client.index.$post({
51+
// @ts-expect-error
52+
json: {
53+
name: "Alpheus",
54+
},
55+
});
56+
57+
expect(res.status).toBe(400);
58+
59+
const result: JsonResponse = (await res.json()) as JsonResponse;
60+
61+
expect(result.success).toBe(false);
62+
63+
expect(result.error?.code).toBe("invalid");
64+
65+
expect(result.error?.field).toBe("age");
66+
});
67+
68+
it("should not work with empty name", async (): Promise<void> => {
69+
const res = await client.index.$post({
70+
// @ts-expect-error
71+
json: {
72+
age: 99,
73+
},
74+
});
75+
76+
expect(res.status).toBe(400);
77+
78+
const result: JsonResponse = (await res.json()) as JsonResponse;
79+
80+
expect(result.success).toBe(false);
81+
82+
expect(result.error?.code).toBe("invalid");
83+
84+
expect(result.error?.field).toBe("name");
85+
});
86+
87+
it("should not work with name in number", async (): Promise<void> => {
88+
const res = await client.index.$post({
89+
json: {
90+
// @ts-expect-error
91+
name: 99,
92+
age: 99,
93+
},
94+
});
95+
96+
expect(res.status).toBe(400);
97+
98+
const result: JsonResponse = (await res.json()) as JsonResponse;
99+
100+
expect(result.success).toBe(false);
101+
102+
expect(result.error?.code).toBe("invalid");
103+
104+
expect(result.error?.field).toBe("name");
105+
});
106+
});

0 commit comments

Comments
 (0)