v4.4.0
Breaking Changes📦 zodView on GitHub →
⚠ 6 breaking✨ 5 features🐛 18 fixes⚡ 1 deprecations🔧 17 symbols
Summary
This minor release focuses heavily on correctness and soundness fixes, particularly around tuple parsing defaults, object property requirements when using z.undefined(), and stricter string validation rules. Several changes are potentially breaking for code relying on previously ambiguous input handling.
⚠️ Breaking Changes
- Object properties defined as `z.undefined()` are now treated as required keys (the key must be present, but its value can be `undefined`). If the key should be allowed to be absent, use `.optional()` on `z.undefined()`.
- `.merge()` now throws if the receiving schema has refinements, due to ambiguous behavior. Prefer `.extend()` or `.safeExtend()` for object composition.
- JSON Schema conversion (`z.toJSONSchema()`) no longer includes redundant `id` fields in `$defs` entries. Consumers reading these internal fields directly may break.
- Base64 validation now rejects whitespace (e.g., "Zm 9v" is invalid).
- CUID validation through `z.cuid()` has been tightened, and CUID v1 is deprecated.
- HTTP URL validation (`z.httpUrl()`) now rejects malformed URLs like "https:/example.com" which were previously accepted after normalization by the underlying `URL` constructor.
Migration Steps
- If you relied on object properties defined as `z.undefined()` being optional (i.e., the key could be missing), change the schema to `z.undefined().optional()`.
- If you were using `.merge()` where the receiver schema had refinements, switch to `.extend()` or `.safeExtend()`.
- Review code that parses base64 strings if it relied on whitespace being ignored.
- Review code that parses HTTP URLs if it relied on malformed inputs like "https:/example.com" being accepted.
- If you snapshot ZodError outputs, be aware that error paths for unions and discriminated unions may have changed slightly for correctness.
✨ New Features
- Tuple parsing now correctly materializes default values in the output array.
- Added support for `ctx.addIssue()` within transform callbacks.
- Added the `when` option for `.superRefine()`.
- Codec inversion was added via `.invert()`.
- Record schemas now run transforms on record keys.
🐛 Bug Fixes
- Tuple parsing now correctly handles defaults, optional tails, explicit `undefined`, and under-filled inputs, ensuring defaults appear in output.
- Trailing optional elements that are absent remain absent (not filled with `undefined`).
- Explicit `undefined` values supplied by the caller in tuples are preserved.
- Parsed tuples are now dense when optional elements appear before later defaults.
- Tuple length errors are more consistent, potentially affecting function input error reporting.
- Refinements from the second schema are preserved when `.merge()` is called on a schema with refinements (though merging is discouraged).
- JSON Schema conversion strips redundant `id` fields from `$defs` entries.
- JSON Schema fixes for Draft-04/OpenAPI 3.0 min/max intersections.
- JSON Schema fixes for recursive lazy schemas with `.describe()`.
- Falsy prefault values are no longer emitted as defaults.
- Nested union paths are preserved correctly in `z.treeifyError()` and `z.formatError()` output.
- Invalid discriminated union errors now include discriminator options and improved messages.
- Record key refinement failures now surface as structured `invalid_key` issues.
- Non-enumerable properties are skipped more consistently in record processing.
- The v3-style single-argument `z.record(valueType)` form works again.
- Schema generation from JSON Schema now applies metadata more consistently across `enum`, `const`, `not`, `anyOf`, and multi-type schemas.
- Schema generation from JSON Schema now rejects or normalizes more non-JSON-like inputs, including cyclic objects and `BigInt`.
- Encoding through `z.discriminatedUnion().encode()` now works when the discriminator uses a codec.
Affected Symbols
⚡ Deprecations
- CUID v1 validation through `z.cuid()` is deprecated.