chore: move the contracts to zod 4 - #283
Merged
Merged
Conversation
The surface was smaller than the issue feared: two `z.record` calls needing an
explicit key schema, and five `.default({})` on objects whose fields all have
defaults, which v4 types against the output rather than the input — `.prefault`
is the replacement.
The wire format is what mattered, since apps/api returns zod's own message text
in its 400 bodies. It is unchanged in shape and better in content:
before {"path":"resource.commitSha","message":"Invalid"}
after {"path":"resource.commitSha",
"message":"Invalid string: must match pattern /^[0-9a-f]{7,40}$/i"}
zod-to-json-schema is dropped entirely — v4 generates JSON Schema itself, so
the OpenAPI document and the API reference now come from the same library that
validates the requests.
That swap brought a defect worth naming. zod emits `$defs` beside a recursive
schema while anchoring the `$ref` at the document root, so every reference from
the attributes type dangled. Hoisting them exposed a second one: a recursive
definition refers to itself, and copying it verbatim left that reference
pointing at the old name. The new guard walks every `$ref` in the document and
resolves it, which is what caught the second bug after I had written the fix
for the first.
Verified against a live API: 18 operations, no dangling reference, a valid
batch still accepted with 202. Full suite under `turbo run test --force`, which
runs the packages concurrently.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #269.
Smaller than the issue feared
Two
z.record(schema)calls needing an explicit key schema, and five.default({})on objects whose fields all carry defaults — v4 types.defaultagainst the output, and.prefaultis the replacement for the input-side behaviour. That was the whole breaking surface.The wire format was the thing to watch
apps/apireturns zod's own message text in its400bodies, so a zod major moves what API clients see. Verified against a live instance — the shape is unchanged and the content is better:A valid batch still returns
202.zod-to-json-schemais gonev4 generates JSON Schema itself, so the OpenAPI document and the API reference now come from the same library that validates the requests — one fewer place for the description of a request to disagree with its enforcement.
The swap brought a defect, and then a second one
zod emits
$defsbeside a recursive schema while anchoring the$refat the document root, where nothing lives. Every reference from the recursive attributes type dangled, and a generated client stops there.Hoisting the definitions to the root exposed a second: a recursive definition refers to itself, so copying it verbatim left that reference pointing at the pre-hoist name. The fix rewrites references inside the definitions as well as around them.
The new guard walks every
$refin the document and resolves it against the document. It is what caught the second bug — after I had already written what I thought was the fix for the first. It also asserts the document still contains references, so it cannot pass by having nothing to check.Verified
62/62 turbo tasks under
--force, which runs the packages concurrently rather than from cache — the same check that caught the Prisma 7 regression in #268.