Skip to content

Commit 913a5c9

Browse files
committed
feat: zodmint/hono and zodmint/trpc adapters (v2.7.0)
- zodmint/hono: mockHonoHandler() — Hono Handler returning c.json(mock(schema)) mockHonoApp() — full stub Hono app from route spec array Peer dep: hono >= 3.0.0 - zodmint/trpc: mockTrpcCaller() — Proxy-based caller, no @trpc/server required mockProcedureOutput() — synchronous one-off output generation Per-procedure { schema, options } form supported - README, CHANGELOG, ROADMAP, playground updated - 568 tests passing
1 parent 75decd6 commit 913a5c9

11 files changed

Lines changed: 731 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to zodmint are documented here. This project follows [Keep a
44

55
---
66

7+
## [2.7.0] - 2026-06-03
8+
9+
### Added
10+
- **`zodmint/hono`** — new sub-entry for testing Hono routes with schema-valid mock data. `mockHonoHandler(schema, options?)` returns a Hono `Handler` that responds with `c.json(mock(schema))`. `mockHonoApp(specs)` builds a complete stub Hono app from a route spec array (`"METHOD /path"`). Supports `status`, `headers`, all `MockOptions`. Invalid method or malformed route throws `ZodForgeError`. Peer dep: `hono >= 3.0.0`.
11+
- **`zodmint/trpc`** — new sub-entry for mocking tRPC callers. `mockTrpcCaller(procedureMap)` returns a Proxy-based caller where any procedure chain resolves to `Promise<z.infer<S>>`. No `@trpc/server` peer dependency required. Procedure map values can be bare schemas or `{ schema, options }` for per-procedure `MockOptions`. Unknown procedures return `Promise<undefined>`. `mockProcedureOutput(schema, options?)` is a synchronous named wrapper around `mock()` for one-off output generation.
12+
13+
---
14+
715
## [2.6.0] - 2026-06-03
816

917
### Added

README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,94 @@ const feed = client.getQueryData(["feed"]);
13541354

13551355
---
13561356

1357+
### Hono — `zodmint/hono`
1358+
1359+
zodmint ships a `zodmint/hono` sub-entry for testing Hono routes with schema-valid mock data. Zero overhead: responds with `c.json()` directly, no HTTP roundtrip.
1360+
1361+
```bash
1362+
npm install hono
1363+
```
1364+
1365+
```typescript
1366+
import { mockHonoHandler, mockHonoApp } from "zodmint/hono";
1367+
```
1368+
1369+
**`mockHonoHandler(schema, options?)`** — a Hono `Handler` that responds with valid mock JSON. Drop it into any route:
1370+
1371+
```typescript
1372+
import { Hono } from "hono";
1373+
import { mockHonoHandler } from "zodmint/hono";
1374+
1375+
const app = new Hono();
1376+
app.get("/users/:id", mockHonoHandler(UserSchema));
1377+
app.post("/users", mockHonoHandler(UserSchema, { status: 201, seed: 42 }));
1378+
1379+
const res = await app.request("/users/1");
1380+
const user = await res.json();
1381+
// user passes UserSchema.safeParse ✓
1382+
```
1383+
1384+
Accepts all `MockOptions` plus `status` (default 200) and `headers`.
1385+
1386+
**`mockHonoApp(specs)`** — builds a complete mock Hono app in one call. Useful when you want a full stub API for integration tests:
1387+
1388+
```typescript
1389+
const mockApi = mockHonoApp([
1390+
{ route: "GET /users/:id", schema: UserSchema },
1391+
{ route: "POST /users", schema: UserSchema, status: 201 },
1392+
{ route: "GET /posts", schema: z.array(PostSchema), seed: 2 },
1393+
]);
1394+
1395+
const res = await mockApi.request("/posts");
1396+
const posts = await res.json();
1397+
// posts is a valid Post[] ✓
1398+
```
1399+
1400+
Route format is `"METHOD /path"` — same convention as `zodmint/msw`. Invalid method or missing space throws `ZodForgeError`.
1401+
1402+
---
1403+
1404+
### tRPC — `zodmint/trpc`
1405+
1406+
zodmint ships a `zodmint/trpc` sub-entry for mocking tRPC callers in unit tests. No `@trpc/server` required — the mock caller is a Proxy that intercepts any procedure chain and returns schema-valid data.
1407+
1408+
```typescript
1409+
import { mockTrpcCaller, mockProcedureOutput } from "zodmint/trpc";
1410+
```
1411+
1412+
**`mockTrpcCaller(procedureMap, defaultOptions?)`** — creates a mock caller. Keys are dot-separated procedure paths; values are Zod output schemas (or `{ schema, options }` for per-procedure control):
1413+
1414+
```typescript
1415+
const caller = mockTrpcCaller({
1416+
"users.getById": UserSchema,
1417+
"users.list": z.array(UserSchema),
1418+
"posts.create": { schema: PostSchema, options: { seed: 1 } },
1419+
});
1420+
1421+
const user = await caller.users.getById({ id: "1" }); // valid User
1422+
const users = await caller.users.list(); // valid User[]
1423+
const post = await caller.posts.create({ title: "hi" }); // valid Post
1424+
```
1425+
1426+
Every procedure call returns `Promise<z.infer<S>>`. Input arguments are accepted but ignored. Procedures not in the map return `Promise<undefined>` — useful for partially mocking a router.
1427+
1428+
Cast to your router's caller type for full IDE completion:
1429+
1430+
```typescript
1431+
import type { createCallerFactory } from "@trpc/server";
1432+
type Caller = ReturnType<ReturnType<typeof createCallerFactory<typeof appRouter>>>;
1433+
1434+
const caller = mockTrpcCaller({ ... }) as Caller;
1435+
```
1436+
1437+
**`mockProcedureOutput(schema, options?)`** — synchronous one-off output generation. Named wrapper around `mock()` for clarity in tRPC test contexts:
1438+
1439+
```typescript
1440+
const output = mockProcedureOutput(getUserOutputSchema, { seed: 42 });
1441+
```
1442+
1443+
---
1444+
13571445
## Prior art
13581446

13591447
zodmint was built with an eye on the existing ecosystem. Three libraries were studied for orientation:

examples/18-hono.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// 18-hono.ts — zodmint/hono: mock handlers and apps for Hono route testing
2+
import { z } from "zod";
3+
import { Hono } from "hono";
4+
import { mockHonoHandler, mockHonoApp } from "../src/hono.js";
5+
6+
const UserSchema = z.object({
7+
id: z.string().uuid(),
8+
name: z.string(),
9+
email: z.string().email(),
10+
});
11+
12+
const PostSchema = z.object({
13+
id: z.string().uuid(),
14+
title: z.string(),
15+
userId: z.string().uuid(),
16+
});
17+
18+
// --- mockHonoHandler: attach to any Hono app ---
19+
const app = new Hono();
20+
app.get("/users/:id", mockHonoHandler(UserSchema, { seed: 1 }));
21+
app.post("/users", mockHonoHandler(UserSchema, { status: 201 }));
22+
23+
const res = await app.request("/users/abc");
24+
const user = await res.json();
25+
console.log("GET /users/:id →", user);
26+
27+
// --- mockHonoApp: build a complete mock API in one call ---
28+
const mockApi = mockHonoApp([
29+
{ route: "GET /users/:id", schema: UserSchema },
30+
{ route: "GET /posts", schema: z.array(PostSchema), seed: 2 },
31+
{ route: "POST /users", schema: UserSchema, status: 201 },
32+
]);
33+
34+
const postRes = await mockApi.request("/posts");
35+
const posts = await postRes.json();
36+
console.log("GET /posts →", posts);

examples/19-trpc.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// 19-trpc.ts — zodmint/trpc: mock callers for tRPC procedure testing
2+
import { z } from "zod";
3+
import { mockTrpcCaller, mockProcedureOutput } from "zodmint/trpc";
4+
5+
const UserSchema = z.object({
6+
id: z.string().uuid(),
7+
name: z.string(),
8+
email: z.string().email(),
9+
role: z.enum(["admin", "user"]),
10+
});
11+
12+
const PostSchema = z.object({
13+
id: z.string().uuid(),
14+
title: z.string(),
15+
userId: z.string().uuid(),
16+
});
17+
18+
// --- mockTrpcCaller: mock a full router's procedures ---
19+
const caller = mockTrpcCaller({
20+
"users.getById": UserSchema,
21+
"users.list": z.array(UserSchema),
22+
"posts.create": { schema: PostSchema, options: { seed: 1 } },
23+
});
24+
25+
const user = await (caller as any).users.getById({ id: "1" });
26+
const users = await (caller as any).users.list();
27+
const post = await (caller as any).posts.create({ title: "Hello" });
28+
29+
console.log("user:", user);
30+
console.log("users:", users);
31+
console.log("post:", post);
32+
33+
// --- mockProcedureOutput: one-off synchronous output generation ---
34+
const output = mockProcedureOutput(UserSchema, { seed: 42 });
35+
console.log("procedure output:", output);

package-lock.json

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

package.json

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zodmint",
3-
"version": "2.6.0",
3+
"version": "2.7.0",
44
"description": "TypeScript-first schema-valid test fixture generator for Zod schemas",
55
"type": "module",
66
"main": "./dist/index.cjs",
@@ -41,6 +41,16 @@
4141
"types": "./dist/tanstack-query.d.ts",
4242
"import": "./dist/tanstack-query.js",
4343
"require": "./dist/tanstack-query.cjs"
44+
},
45+
"./trpc": {
46+
"types": "./dist/trpc.d.ts",
47+
"import": "./dist/trpc.js",
48+
"require": "./dist/trpc.cjs"
49+
},
50+
"./hono": {
51+
"types": "./dist/hono.d.ts",
52+
"import": "./dist/hono.js",
53+
"require": "./dist/hono.cjs"
4454
}
4555
},
4656
"bin": {
@@ -69,11 +79,12 @@
6979
},
7080
"peerDependencies": {
7181
"@prisma/client": ">=5.0.0",
82+
"@tanstack/query-core": ">=5.0.0",
7283
"drizzle-orm": ">=0.29.0",
7384
"fast-check": ">=3.0.0",
7485
"msw": ">=2.0.0",
7586
"zod": ">=3.23.0",
76-
"@tanstack/query-core": ">=5.0.0"
87+
"hono": ">=3.0.0"
7788
},
7889
"peerDependenciesMeta": {
7990
"@prisma/client": {
@@ -90,48 +101,54 @@
90101
},
91102
"@tanstack/query-core": {
92103
"optional": true
104+
},
105+
"hono": {
106+
"optional": true
93107
}
94108
},
95109
"devDependencies": {
96110
"@tanstack/query-core": "^5.101.0",
97111
"@types/node": "^20.0.0",
98112
"fast-check": "^3.22.0",
113+
"hono": "^4.12.23",
99114
"msw": "^2.14.6",
100115
"tsup": "^8.0.0",
101116
"typescript": "^5.4.0",
102117
"vitest": "^1.6.0",
103118
"zod": "^4.4.3"
104119
},
105120
"keywords": [
106-
"zod",
107-
"mock",
108-
"fixture",
109-
"factory",
110-
"testing",
121+
"boundary-testing",
111122
"typescript",
112-
"schema",
113-
"generator",
114-
"test-data",
123+
"fake-data",
115124
"mock-data",
125+
"factory",
126+
"coverage",
127+
"db-seed",
116128
"fixtures",
117-
"zod-mock",
118-
"zod-faker",
119-
"test-fixtures",
120-
"fake-data",
121-
"data-generator",
122-
"msw",
123-
"mock-service-worker",
129+
"mock",
130+
"fixture",
124131
"snapshot",
125-
"pin",
126-
"seed",
127-
"cli",
128132
"prisma",
129133
"drizzle",
134+
"cli",
135+
"zod",
136+
"hono",
137+
"trpc",
138+
"schema",
130139
"database-seeding",
131-
"db-seed",
140+
"msw",
141+
"zod-faker",
142+
"test-fixtures",
143+
"generator",
144+
"pin",
145+
"test-data",
146+
"data-generator",
147+
"testing",
148+
"seed",
149+
"mock-service-worker",
132150
"storybook",
133-
"coverage",
134-
"boundary-testing"
151+
"zod-mock"
135152
],
136153
"license": "MIT",
137154
"author": {
@@ -146,4 +163,4 @@
146163
"bugs": {
147164
"url": "https://github.com/gonll/zodmint/issues"
148165
}
149-
}
166+
}

0 commit comments

Comments
 (0)