Skip to content

fix: complete zod/v4 migration (supersedes #158)#161

Merged
mcampa merged 4 commits into
masterfrom
fix/zod-v4-migration-complete
May 22, 2026
Merged

fix: complete zod/v4 migration (supersedes #158)#161
mcampa merged 4 commits into
masterfrom
fix/zod-v4-migration-complete

Conversation

@mcampa
Copy link
Copy Markdown
Owner

@mcampa mcampa commented May 22, 2026

Supersedes #158 by rebasing onto current master and fixing the CI failure.

Summary

#158 widens the zod peer dependency to `^3.25.0 || ^4.0.0` and switches `src/` imports from `'zod'` to `'zod/v4'`, per the zod docs for package authors. That PR fails CI with a `tsc` OOM, because all `test/` files still `import { z } from 'zod'`. TypeScript treats `ZodObject` from `'zod'` and `'zod/v4'` as nominally distinct types — even though they resolve to the same runtime class in `zod@4.0.14` — and the mismatch explodes type instantiation through tRPC's generic `Procedure`/`Router` machinery via `OpenApiMeta`, exhausting Node's heap.

See investigation: #158 (comment).

Changes

  1. Follow zod guidance on supporting v4 - for users incrementally migrating from v3 #158 commits, rebased onto current master (preserves the `OpenApiContactObject`/`OpenApiLicenseObject` code from feat: add contact and license to GenerateOpenApiDocumentOptions #157)
  2. Test files updated to `'zod/v4'` — 8 files, mechanical `sed` replacement, resolves the OOM
  3. Cast on one test's examples meta — `test/generator.test.ts` uses the v3-style `{ Lily: ..., John: ... }` named-object form for `.meta({ examples })`. zod-openapi v5 types this as `unknown[]`, but the runtime still accepts the record form. Cast preserves existing behavior.

Test plan

  • `pnpm exec tsc --noEmit` passes in ~2s (no OOM)
  • `pnpm run build` succeeds (full build pipeline: tsc + jest + cjs + esm)
  • All 129 tests passing across 8 suites
  • CI green

🤖 Generated with Claude Code

Sam152 and others added 3 commits May 22, 2026 08:39
Test files were importing z from 'zod' while src/ now imports from
'zod/v4'. TypeScript treats those as nominally distinct types, and the
mismatch causes type instantiation to explode through tRPC's generic
machinery, OOMing tsc.

Also cast the named-object examples in the multipleExamples test to
preserve existing runtime behavior — zod-openapi v5 types meta examples
as unknown[], but the runtime still accepts the v3-style record form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the Zod v4 “permalink import” migration by switching internal and test-suite Zod imports to zod/v4 and widening the package’s zod peer dependency to support both zod@^3.25 and zod@^4.

Changes:

  • Widen zod peer dependency to ^3.25.0 || ^4.0.0.
  • Migrate src/ and test/ Zod imports from zod to zod/v4 to prevent nominal type mismatches (and the resulting tsc OOM).
  • Adjust one test’s .meta({ examples }) typing to compile under the updated zod-openapi typings.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
package.json Widens zod peer dependency range to support ^3.25 and ^4.
src/utils/zod.ts Switches Zod imports to zod/v4 for consistent typing/runtime.
src/utils/procedure.ts Switches Zod imports to zod/v4 for consistent typing/runtime.
src/types.ts Updates ZodObject type import to come from zod/v4.
src/generator/schema.ts Switches Zod imports to zod/v4 for consistent schema generation typing.
src/generator/index.ts Switches ZodSchema import to zod/v4 for defs typing.
src/adapters/node-http/core.ts Switches Zod imports to zod/v4 to keep runtime checks/types consistent.
test/generator.test.ts Updates import to zod/v4 and adds a type assertion for .meta({ examples }).
test/adapters/standalone.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.
test/adapters/nuxt.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.
test/adapters/next.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.
test/adapters/koa.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.
test/adapters/fetch.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.
test/adapters/fastify.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.
test/adapters/express.test.ts Updates Zod import to zod/v4 to avoid nominal mismatches in tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/generator.test.ts
Comment thread src/generator/index.ts
Comment thread src/generator/schema.ts
Comment thread src/utils/procedure.ts
Comment thread src/utils/zod.ts Outdated
Comment thread src/adapters/node-http/core.ts
Addresses Copilot review on #161:

- ZodSchema, ZodObject, ZodAny, ZodRawShape, ZodType, ZodError are only
  used in type positions — move them to `import type` so stricter
  TypeScript settings (verbatimModuleSyntax, preserveValueImports)
  don't emit unused value imports.
- ZodTypeAny was imported in node-http/core.ts but never referenced —
  remove it.
- Replace the broad `as never` cast on the multipleExamples test with
  `as unknown as unknown[]` plus a comment explaining why the runtime
  still accepts the v3-style named-record form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mcampa
Copy link
Copy Markdown
Owner Author

mcampa commented May 22, 2026

Addressed all 6 Copilot review comments in 4ab0e19:

  • Type-only imports — `ZodSchema`, `ZodObject`, `ZodAny`, `ZodRawShape`, `ZodType`, `ZodError` are all used only in type positions. Split them into `import type` declarations across the 5 source files. Keeps `z` (and `ZodArray` for the `instanceof` checks) as value imports.
  • Unused `ZodTypeAny` in `src/adapters/node-http/core.ts` — removed.
  • `as never` cast in `test/generator.test.ts` — replaced with `as unknown as unknown[]` and added a comment explaining why the runtime still accepts the v3-style named-record form even though zod-openapi v5 types `meta.examples` as `unknown[]`.

`pnpm run build` (tsc + jest + cjs + esm) green: 129/129 tests passing, full build succeeds.

@mcampa mcampa merged commit a30cf8b into master May 22, 2026
3 checks passed
@mcampa mcampa deleted the fix/zod-v4-migration-complete branch May 22, 2026 16:03
@mcampa mcampa mentioned this pull request May 22, 2026
4 tasks
mcampa added a commit that referenced this pull request May 22, 2026
- feat: contact/license fields on GenerateOpenApiDocumentOptions (#157)
- feat: support zod v3 (>=3.25) alongside v4 via zod/v4 permalink (#161)
- fix: bump zod-openapi to 5.4.6 and raise peer dep to ^5.4.4 to prevent
  the discriminated-union crash on zod >=4.1.13 (#162)
- fix: add x-${string} index signature to contact/license types (#160)
- chore: bump h3 to ^1.15.5 (#159)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants