fix: externalise zod / zod-form-data to fix cross-realm refinement loss (#222) - #223
Conversation
|
@daichiyasunami-vottia is attempting to deploy a commit to the Markus Blomqvist's projects Team on Vercel. A member of the Team first needs to authorize it. |
6a63ebd to
02b9b11
Compare
) `zod` and `zod-form-data` were bundled by tsup, so the schema introspection inside this package (Zod 4's `toJSONSchema`) ran in the bundled Zod's realm while user schemas were built by the host's installed Zod. When the host ran a different minor version, `_zod.bag` was unreadable across realms and refinements (`.min()`, `.max()`, `.email()`, `.regex()`, `.uuid()`, ...) silently disappeared from the generated OpenAPI schema. This commit: - Moves `zod` (required) and `zod-form-data` (optional) from devDependencies into peerDependencies so the host install is used at runtime. - Marks `zod` and `zod-form-data` external in `tsup.config.ts` so they are no longer inlined into the bundle. - Adds a regression test that asserts the full set of common Zod refinements (`.min`, `.max`, `.regex`, `.email`, `.url`, `.uuid`, `.iso.datetime`, `.int`, `.multipleOf`, `.enum`, `.literal`, `.default`, `.optional`) are preserved in the generated OpenAPI schema. - Adds a CI matrix over multiple Zod 4 minor versions (4.1.13 / 4.2.0 / 4.4.3 / latest) so future divergence between host and library realms is caught early. BREAKING CHANGE: consumers must install `zod` themselves. `zod-form-data` remains optional and only needs to be installed when using the form-data features.
02b9b11 to
746dbf3
Compare
|
Updated to fix the
Added Locally:
The Vercel deploy failures are unrelated — they're "Authorization required to deploy" for outside-contributor PRs. |
|
Quick clarification on the "checks failed" state shown on this PR page: The two failing checks are both Vercel — they fail with The actual GitHub Actions CI does not appear on this PR page because the upstream's
Full run: https://github.com/daichiyasunami-vottia/next-rest-framework/actions/runs/26892303285 If you'd like CI to show up directly on outside-contributor PRs going forward, the smallest change would be to add |
Summary
Fixes #222.
zod(andzod-form-data) were bundled intodist/by tsup, so all Zod 4 introspection inside this package (e.g.toJSONSchemaingetJsonSchema) ran in the bundled Zod's realm while user-provided schemas were built by the host's installed Zod. When the host ran a different minor version, the bundled Zod'sJSONSchemaGeneratorcould not read_zod.bag/_zod.def.checksfrom the host's schemas, and refinements (.min(),.max(),.email(),.url(),.uuid(),.iso.datetime(),.regex(),.multipleOf(), …) silently disappeared from the generated OpenAPI schema.Concretely: with
next-rest-framework@6.1.1bundlingzod@4.1.13, a host usingzod@4.4.3and writingz.string().min(1)produced{ "type": "string" }(nominLength) in the generated OpenAPI.Changes
packages/next-rest-framework/package.json— moveszod(required) andzod-form-data(optional) intopeerDependencieswithpeerDependenciesMetamarkingzod-form-dataas optional. Both remain indevDependenciesfor local development and tests.packages/next-rest-framework/tsup.config.ts— markszodandzod-form-dataexternal so they are no longer inlined intodist/. Bundle size dropped roughly 40 % (≈ 19 000 → 7 900 lines fordist/index.js).packages/next-rest-framework/tests/app-router/route.test.ts— adds a regression test (preserves Zod refinements in generated OpenAPI schema) asserting the full set of common refinements survivegetPathsFromRoute:z.string().min(1).max(100)minLength: 1,maxLength: 100z.string().regex(/^[a-z-]+$/)pattern: '^[a-z-]+$'z.email()/z.url()/z.uuid()/z.iso.datetime()format: 'email' / 'uri' / 'uuid' / 'date-time'z.number().min(0).max(150)minimum: 0,maximum: 150z.number().int()type: 'integer'z.number().multipleOf(5)multipleOf: 5z.array(...).min(1).max(5)minItems: 1,maxItems: 5z.enum([...])/z.literal(...)enum: [...]z.boolean().default(false)default: false, not inrequiredz.string().optional()required.github/workflows/ci.yml— adds a newzod-matrixjob that runs the package's build + test against Zod4.1.13 / 4.2.0 / 4.4.3 / latest, so future divergence between host and library realms is detected on every push. The existingRun CI pipelinejob is untouched.Test plan
pnpm run build— succeeds; verifieddist/index.jsno longer contains a copy of Zod (var version = { major: 4, minor: 1, patch: 13 }is gone) and now containsrequire("zod")/import { z } from "zod".pnpm test— 100 tests pass (was 99; new test added).pnpm testagainstzod@4.1.13— passes.pnpm testagainstzod@4.4.3— passes (this is the version that previously failed cross-realm).packages/next-rest-frameworkpnpm run lint(tsc) — clean.prettier --check '**/*.{ts,json}'(the project's lint glob) — clean.Breaking change
Consumers now need to install
zodthemselves (andzod-form-dataif they use the form-data helpers). For pnpm / npm 7+, this happens automatically via peer dependency resolution; for older clients an explicit install is required.The smallest migration is:
A major version bump (
6.x->7.0) is appropriate. Happy to draft a migration note for the README/CHANGELOG if you'd like.Notes
.min()/.max()etc. from generated OpenAPI #222 (with a script that prints the emptybagfrom the bundled realm)._zod.bagaccess entirely and route everything through the Standard Schema interface (~standard.vendor), so the library stays compatible across major Zod versions. That is a bigger refactor — happy to follow up in a separate PR if you'd like.