feat!: Support Zod v3/v4 and add strict parsing mode as the default - #3
Merged
Merged
Conversation
Read schema internals from _zod.def (Zod v4) or _def (Zod v3) and normalize Zod v4 type names to Zod v3 style names. Remove the runtime dependency on ZodFirstPartyTypeKind, which Zod v4 no longer exports. Unwrap effects (Zod v3 refine/transform/preprocess) and pipelines (Zod v4 transform) to the schema describing the parser input, so refined and transformed schemas are now parseable instead of throwing UnparseableSchemaError. Applying the effect is left to the schema. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jWzL9LC7Q6bTGaXDoKq4z
razor-x
marked this pull request as ready for review
August 5, 2026 21:13
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.
Summary
Prerequisite for migrating nextlove off its hand-rolled query param parser (companion PR: seamapi/nextlove#182). Two changes:
zod@^3 || ^4, but this package read Zod v3 internals (_def.typeName), so every schema built with Zod v4 threwUnparseableSchemaError.strictoption (defaulttrue) — strict mode only parses the serializer's expected output, making the parser a perfect inverse of @seamapi/url-search-params-serializer. Generous parsing (the previous behavior) moves behindstrict: false, isolating its extra input formats and their limitations.Strict mode (
strict: true, the default)Perfectly aligned with the serializer — only its output format is parsed:
foo=a&foo=b(plusfoo=for the empty array).foo=a,b&foo=cparses as['a,b', 'c']. The serializer can emit such values, so strict mode round-trips them; the "no commas in array values" limitation now only applies to generous mode.foo[]=afor an array namedfoo, a param namedfoo[]is unrelated tofoo: it parses as a param literally namedfoo[](which the serializer can produce for a key of that name, so it round-trips). The same applies to record keys ending in[]— literal keys in strict mode.true/false.null/ the empty array.New bijection tests cover the previously non-invertible cases: array values containing commas, and params literally named with a bracket suffix.
Generous mode (
strict: false)Preserves the previous behavior: comma (
foo=a,b) and bracket (foo[]=a) array formats, generous booleans (yes,1,NO, …), and whitespace trimming/null handling — at the cost of array values not containing commas. Unchanged in both modes: unparseable values pass through as strings for the schema to reject, emptyz.string()isnull, unknown params are ignored.Zod v3/v4 compatibility
_zod.def(v4) with fallback to_def(v3); v4 type strings are normalized to v3-style type names.peerDependencieswidened tozod@^3.0.0 || ^4.0.0.ZodFirstPartyTypeKind, which Zod v4 no longer exports (it would fail at module load)..refine()/.transform()/z.preprocess()/.pipe()) are unwrapped to the schema describing the parser input; applying the effect is left to the schema. Previously these threwUnparseableSchemaError.nativeEnumintoenum): all-string enums parse as strings, all-numeric as numbers. Zod v4 multi-value literals (z.literal(['a','b'])) are supported.~5.3.3→~5.8.3(Zod v4 types need TS ≥ 5.4).Testing
166 tests pass: the existing Zod v3 suite (updated to pass
strict: falsewhere it exercises generous parsing), a newtest/strict-parsing.test.ts(including literalfoo[]params), and a newtest/zod-v4.test.tsrunning core behavior against Zod v4 via azod-v4npm alias.Breaking changes
strict: falseto keep the previous behavior.Semantic-release: publishes as
0.1.0(0.x minor bump for the breakingfeat!).