Skip to content

Update all non-major dependencies - #4128

Open
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/all-minor-patch
Open

Update all non-major dependencies#4128
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/all-minor-patch

Conversation

@renovate

@renovate renovate Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
babel-jest (source) 30.4.130.5.0 age confidence
jest (source) 30.4.230.5.0 age confidence
wrangler (source) 4.127.04.127.1 age confidence
zod (source) 4.4.34.5.2 age confidence

Release Notes

jestjs/jest (babel-jest)

v30.5.0

Compare Source

Features
  • [@jest/expect-utils, jest-mock] Add mockFn.whenCalledWith(...args) for configuring return values per argument list, with first-class asymmetric-matcher support (#​16053)
  • [@jest/expect-utils] Export AsymmetricMatcher and FunctionParameters types (previously private to expect) (#​16053)
  • [jest-circus, jest-core, jest-jasmine2, jest-test-result, jest-types] --collectTests now expands test.each/describe.each cases and reports per-status counts (skipped/todo via the new wouldRun flag for selected tests) plus a summary line that match a real run, including under --testNamePattern and .only/fdescribe focus on both the circus and jasmine2 runners (#​16259)
  • [jest-circus, jest-environment, jest-runtime, jest-types] Add describe-level retries via jest.retryTimes(..., {entireDescribe: true}) (#​16322)
  • [jest-circus, jest-message-util, jest-reporters, jest-types] Add retryMessages to AssertionResult and export formatErrorStack, so the retry log renders nested cause and AggregateError sections with code frames instead of serialized [cause]:/[errors]: markers (#​16316)
  • [jest-circus, jest-types] Add unhandledErrorsDetailed to Circus.RunResult, so an unhandled rejection reports its cause chain and AggregateError entries with code frames instead of a pre-serialized stack (#​16316)
  • [jest-haste-map] Replace NodeWatcher and FSEventsWatcher with @parcel/watcher for the non-watchman watch path (#​16188)
  • [jest-resolve] Bump unrs-resolver to 1.12.1, remove jest-pnp-resolver and unnecessary checks (#​15721)
  • [jest-resolve] Honor Node's --preserve-symlinks / NODE_PRESERVE_SYMLINKS in the default resolver by passing symlinks: false to unrs-resolver (#​16260)
  • [jest-runtime] Apply automocking and manual __mocks__ files to synchronously evaluable ESM graphs on Node 24.9+ - static imports, dynamic import() and require() of an ESM file now generate an automock from the real module's namespace instead of failing with "Attempting to import a mock without a factory". Graphs that need async evaluation (top-level await) or an async-only resolver or transformer still throw (#​16391)
  • [jest-runtime] Route process.getBuiltinModule through the sandbox, so it returns the sandbox process and the hooked node:module instead of the host's (#​16391)
  • [jest-runtime] Throw an actionable error from module.register() and module.registerHooks() inside a test - the hooks attached to the loader running Jest itself, never saw the sandboxed requires they were meant for, and stayed registered for every later test file in the worker (#​16391)
  • [jest-runtime] Surface resolution and import-attribute errors in an ESM graph before executing any of its CJS dependencies on Node 24.9+, matching Node's run-nothing-on-a-broken-graph behavior; the legacy loader on older versions keeps its linking-time execution order (#​16391)
  • [jest-runtime] Throw ERR_SOURCE_PHASE_NOT_DEFINED with an actionable message for import source and import.source(), instead of failing at instantiation with V8's bare "Source phase import object is not defined" (#​16391)
  • [jest-runtime] Emit the JSON-without-import-attribute deprecation warning once per test file instead of once per worker, so it is no longer silently swallowed for every file after the first (#​16391)
  • [jest-runtime] Set import.meta.main to true in the test file and false in every module it loads, matching Node 24+ (#​16367)
  • [jest-runtime] Resolve the module-sync export condition, so a package that exposes its ESM entry point for require() loads the same file Node would (#​16336)
  • [jest-snapshot] Add external snapshot paths to custom reporter failure details (#​16374)
Fixes
  • [jest-console, jest-reporters] CustomConsole now buffers console output so TestResult.console is populated for reporters when verbose is enabled, while GitHubActionsReporter avoids replaying buffered output in verbose mode (#​16155)
  • [expect, jest-message-util, jest-pattern, jest-regex-util, jest-util] Revert node: protocol imports to restore webpack/browser-bundle compatibility (#​16167)
  • [expect] Widen toMatchObject and objectContaining parameter type from Record<string, unknown> to object so class instances are accepted (#​16196)
  • [jest-circus] Call a generator test body with the shared test context, so this matches what a regular test function receives (#​16347)
  • [jest-circus] Capture the error listeners of the parent process instead of the in-sandbox process, so listeners registered before the test file survive teardown and sandbox listeners no longer leak onto the parent (#​16347)
  • [jest-circus] Clear currentlyRunningTest after skipped and todo tests (#​16342)
  • [jest-circus] Prevent late done() callbacks from affecting later test or hook invocations (#​16343)
  • [jest-circus, jest-jasmine2] Honor --expand when formatting node:assert failures, instead of always collapsing the diff (#​16347)
  • [jest-circus, jest-jasmine2, jest-message-util] Serialize the inner errors of an AggregateError into failureMessages, retryReasons and unhandledErrors, so --json output and reporter annotations include them (#​16316)
  • [jest-circus, jest-snapshot] Keep snapshot state and counts correct when a test retries (#​16344)
  • [@jest/create-cache-key-function] Include the caller support flags in the generated key, so a transformer that emits ESM or CJS based on them no longer shares one cache entry between the two (#​16331)
  • [@jest/create-cache-key-function] Include the stringified project config in the generated key, so editing a transformer's own settings invalidates what it cached (#​16331)
  • [@jest/transform] Include the caller support flags in a transform's cache key, so a file transformed both as ESM and as CJS no longer serves one shape's output for the other (#​16331)
  • [jest-config] Add missing findRelatedTests, outputFile, and replname entries to ValidConfig so they no longer trigger spurious "Unknown option" warnings (#​16224)
  • [jest-config] Use --config for the global config when multiple --projects are specified (#​16273)
  • [jest-core] Serialize bigint values in --json and --outputFile output as their literal form (4n), instead of failing the run with TypeError: Do not know how to serialize a BigInt (#​16338)
  • [jest-core] Do not report a CustomGC async resource (used by N-API addons such as napi-rs for per-isolate GC bookkeeping) as an open handle, since it is napi_unref'd by the addon and can never keep the event loop alive (#​16379)
  • [jest-each] Keep a $&, $`, $' or $$ inside a %p param value out of the replacement, so the title shows the value instead of the text around it (#​16338)
  • [jest-each] Interpolate a bigint into a %j title as its literal form ("4n") at any depth, instead of throwing TypeError: Do not know how to serialize a BigInt while collecting the tests (#​16338)
  • [jest-environment, jest-runtime] Bind sandboxInjectedGlobals to the right values when injectGlobals is false, instead of shifting every one of them by a position (#​16377)
  • [jest-environment-node, jest-util] Only warn about a conflicting globalsCleanup mode when one was explicitly configured, and follow the mode that is actually in effect (#​16323)
  • [jest-environment-node, jest-util] Stop resolving lazy globals when setting up an environment, so Node 26's builtin module globals are no longer loaded (and no longer emit their deprecation warnings) for every test file (#​16324)
  • [jest-haste-map] Keep watch mode alive when an outside process briefly makes a file unreadable on Windows, instead of tearing the watcher down on EPERM (#​16295)
  • [jest-haste-map] Keep indexing when an outside process holds a file open on Windows, instead of failing the whole crawl on EPERM (#​16358)
  • [jest-haste-map] Keep a duplicated manual mock resolving when the file it pointed at is deleted in watch mode (#​16360)
  • [jest-haste-map] Shut the worker farm down when a duplicate manual mock aborts the build under throwOnModuleCollision (#​16354)
  • [jest-haste-map] Attach the watchman client's error listener before the first command, so a watchman failure falls back to the node crawler instead of crashing on an unhandled error event, and always end the client (#​16355)
  • [jest-haste-map] Stop delivering watch events after WatchmanWatcher is closed, and route its warnings through the configured console (#​16355)
  • [jest-haste-map] Restore the nested duplicates index correctly in ModuleMap.fromJSON, so a haste collision reported inside a test worker raises DuplicateHasteCandidatesError instead of a TypeError (#​16353)
  • [jest-haste-map] Match watched files on a full extension, so moduleFileExtensions: ['js'] no longer accepts foo.mjs (#​16352)
  • [jest-haste-map] Delimit the fields that make up the haste map cache key, so two different option sets cannot hash to the same cache file (#​16352)
  • [jest-message-util] Print the inner errors of an AggregateError thrown inside a test (#​16316)
  • [jest-message-util] Indent nested cause and AggregateError sections of a test failure by one level per depth, so the nesting is legible instead of rendering flat (#​16316)
  • [jest-message-util] Color stack traces line by line so blank lines stay blank (#​16316)
  • [jest-message-util] Detect Jest's own frames without assuming the checkout directory's name, and cover @jest/* packages, so stack traces and code frames point at user code (#​16326)
  • [jest-mock] mockResolvedValue / mockRejectedValue now see all overload return types, so a Promise-returning overload survives even when a later overload returns a non-Promise (e.g. pg.Client['end']) (#​16237)
  • [@jest-environment/jsdom-abstract] Make @types/jsdom a peer dependency (#​16166)
  • [jest-mock] Remove the leftover own accessor descriptor when restoring a spyOn of an inherited getter or setter, so the instance keeps reflecting the prototype (#​16226)
  • [jest-resolve] Include extensionsToTreatAsEsm in the shouldLoadAsEsm cache key, so projects with different extension lists don't read each other's answers (#​16369)
  • [jest-resolve] Make getModuleIDAsync build and cache data: URI module IDs the same way as getModuleID (#​16370)
  • [jest-resolve] Keep the node: prefix when resolving a core module asynchronously, so a builtin that only exists prefixed (node:sea, node:sqlite, node:test, node:test/reporters) resolves instead of failing as a missing bare package (#​16388)
  • [jest-resolve] Look up manual mocks for node: protocol specifiers under the unprefixed name they are stored as (#​16388)
  • [jest-resolve] Apply moduleNameMapper consistently to both spellings of core module specifiers (fs vs node:fs) (#​16390)
  • [jest-resolve] Keep virtual and ordinary mock module IDs isolated across test files (#​16296)
  • [jest-resolve] Guard missing require.resolve.paths (#​16052)
  • [jest-resolve, jest-config, jest-runner] Support a user resolver written as an ES module (#​16332)
  • [jest-resolve, jest-runtime] Throw the CJS parse error for ESM syntax in a "type": "commonjs" package or a .cjs file instead of loading it as ESM, matching Node (#​16368)
  • [@jest/source-map] Keep source map sources that name a scheme, such as webpack:///, instead of resolving them into a path that does not exist (#​16327)
  • [@jest/source-map] Look up --testLocationInResults positions at the right column, and keep a mapping to the first column instead of discarding it (#​16327)
  • [@jest/source-map] Warn when a source map cannot be parsed, instead of silently leaving its frames untranslated (#​16327)
  • [jest-runner, @jest/source-map] Keep a source-mapped stack for an error thrown after the test environment was torn down (#​16327)
  • [jest-runtime, @jest/source-map] Keep source maps past teardown and past the next test file's install, so a stack from a file no earlier stack mentioned still points at the original source (#​16330)
  • [jest-runtime] Report that no coverage was collected when getAllV8CoverageInfoCopy is called after teardown, instead of returning an empty result (#​16385)
  • [jest-runtime] Cache a CJS module's parsed exports before walking its re-exports, so two modules that re-export each other no longer overflow the stack when imported from ESM (#​16363)
  • [jest-runtime] Keep a re-exported ES module's parse failure from marking the re-exporting CommonJS file as ESM, so module.exports = require('./dep.mjs') loads instead of failing with module is not defined (#​16363)
  • [jest-runtime] Scope module mocks instantiated inside jest.isolateModules/isolateModulesAsync to that block, so a mock first imported there no longer outlives it - matching how CommonJS mocks already behave (#​16365)
  • [jest-runtime] Suspend module isolation while generating an automock, so loading the real module to read its shape no longer populates the isolated registry (#​16365)
  • [jest-runtime] Check a cached ES module's status before require() returns it, so a module whose evaluation threw rethrows that error and one left linked by a failed sibling is evaluated instead of returning uninitialized bindings (#​16364)
  • [jest-runtime] Report the original ERR_REQUIRE_ASYNC_MODULE when a require() of a top-level-await graph is retried, instead of a spurious "concurrent import()" error (#​16364)
  • [jest-runtime] Throw the evaluation error when another caller's import() of the same module failed while we awaited it, instead of resolving with the errored module (#​16364)
  • [jest-runtime] Mark the result of require()ing an ES module that has a default export with __esModule: true through a live-binding facade, and serve the same object from require.cache, matching Node (#​16367)
  • [jest-runtime] Provide a CommonJS module's exports under the 'module.exports' named export when imported from ESM, matching Node 23+ (#​16367)
  • [jest-runtime] Give the test file itself a non-null require.main (#​16367)
  • [jest-runtime] Populate module.children with the modules a file loads, matching Node (#​16368)
  • [jest-runtime] Provide import.meta.resolve and import.meta.jest in data: URI modules, accept any-case mediatype parameters, and use Node's error codes for invalid data: URIs (#​16368)
  • [jest-runtime] Key ES modules by full URL, so query and fragment suffixes create the same module instances as Node and show up in import.meta.url (#​16375)
  • [jest-runtime] Share modules between overlapping graphs when a CommonJS module require()s an ES module mid-load, instead of evaluating shared dependencies twice (#​16375)
  • [jest-runtime] Throw ERR_REQUIRE_CYCLE_MODULE like Node when a CommonJS module require()s an ES module that is still being loaded, instead of evaluating the module a second time (#​16366)
  • [jest-runtime] Key builtin modules in the ESM registry by one canonical specifier (#​16341)
  • [jest-runtime] import.meta.resolve() for a builtin uses its node: specifier (#​16341)
  • [jest-runtime] Fall back to native ESM when a .js file contains ESM syntax but has no "type":"module" marker (#​16152)
  • [jest-runtime] Allow require() of ESM-marked files on Node < 24.9 via transform fallback (#​16244)
  • [jest-runtime, @jest/transform] Surface actionable ERR_REQUIRE_ESM error for files with untransformed ESM syntax instead of the generic "unexpected token" message (#​16244)
  • [jest-runtime] Support older test environments whose moduleMocker does not implement clearMocksOnScope (#​16169)
  • [jest-runtime] Apply jest.unstable_mockModule when the mocked file itself is require()d, not only when it is imported as a dependency (#​16389)
  • [jest-runtime] Apply jest.unstable_mockModule to statically imported data: URIs on Node 24.9+, matching dynamic import() (#​16389)
  • [jest-runtime] Run an async jest.unstable_mockModule factory once per module instead of twice, and fail the import instead of crashing the worker when the factory rejects (#​16389)
  • [jest-runtime] Hide a require(esm) module that failed to evaluate from require.cache, as Node does, instead of exposing a namespace with uninitialized bindings (#​16389)
  • [jest-runtime] Strip the byte-order mark when importing a JSON module, matching require() and Node (#​16389)
  • [jest-runtime] Throw ERR_REQUIRE_ASYNC_MODULE when require(esm) runs under an async-only custom resolver, instead of silently resolving with the default resolver (#​16389)
  • [jest-runtime] Parse imported JSON modules with the test realm's JSON, so their objects pass instanceof Object inside the test like require()d JSON does (#​16389)
  • [jest-runtime] Accept every file: URL string in the sandboxed module.createRequire, including one with a localhost authority, as Node does (#​16389)
  • [jest-runtime] Point at {virtual: true} when jest.mock or jest.unstable_mockModule is given a module that cannot be resolved (#​16389)
  • [jest-reporters] Fix coverage report table formatting in CI/GitHub Actions environments where process.stdout.columns is undefined by falling back to the COLUMNS env var or 80 columns in CI, preserving existing behaviour in other non-TTY environments (#​16227)
  • [jest-runtime] Support CJS-in-ESM exports via "module.exports" named exports (#​16277)
  • [jest-snapshot] Keep a skipped or failed test's hinted snapshots, instead of reporting them obsolete (#​16348)
  • [jest-util] Stop globsToMatcher reusing a cached matcher compiled with different picomatch options, and keep its dot: true default when dot is passed as undefined (#​16381)
  • [pretty-format] Move the react-is aliases into the @jest scope, so they cannot be shadowed by unrelated packages published under the alias names (#​16333)
Chore & Maintenance
  • [docs] Document the intentional divergences from Node's module system in the ECMAScript Modules page (#​16368)
  • [docs] Note deprecation of react-test-renderer in React Native tutorial and pretty-format README (#​16294)
  • [docs] Use @testing-library/react-native in the React Native tutorial instead of the deprecated react-test-renderer (#​16318)
  • [babel-jest, @jest/transform] Update babel-plugin-istanbul to v8 (#​16049)
  • [jest-config, @jest/reporters, jest-runtime] Update glob to v13 (#​16397)
  • [jest-haste-map] Refactor massive class into multiple files (#​16180)
  • [jest-haste-map] Drop walker dependency; replace hand-rolled directory recursion in the JS crawler and watcher startup with fdir (#​16187)
  • [jest-haste-map] Reuse cached metadata for files whose haste name is a known duplicate, instead of re-reading and re-parsing them on every startup (#​16351)
  • [jest-haste-map] Cache the watchman socket path and replace the watchman --version probe with get-sockname, so warm runs spawn no watchman processes (#​16386)
  • [jest-resolve] Store the per-directory package-type lookup in the cache it reads, so it actually memoizes (#​16369)
  • [jest-resolve, jest-runtime] Cut repeated work on the resolution hot path: hoist the platform-extension list to construction, memoize isCoreModule and the options cache-key serialization, skip mapper preparation when no moduleNameMapper is configured, run each mapper regex once, and stop re-parsing NODE_OPTIONS on every default-resolver call (#​16371)
  • [jest-resolve] Cut warm resolution cost to about a third: reuse one unrs-resolver factory per options shape instead of cloning per resolution, compose the factory cache key from per-array cached strings instead of serializing options, and stop constructing an Error for misses that findNodeModule swallows; add a __benchmarks__ suite for the default resolver (#​16373)
  • [jest-runner, @jest/source-map] Replace source-map-support with an implementation in @jest/source-map (#​16327)
  • [jest-snapshot] Load babel, semver and synckit lazily, so requiring the package (which every test process does through @jest/expect) no longer loads ~200 modules that only writing inline snapshots needs (#​16387)
  • [jest-runtime] Reduce per-require overhead: skip module ID resolution when no mock can apply, answer core modules before probing for a manual mock, share one require.cache proxy across modules, and cache empty files (#​16376)
  • [@jest/source-map] Deprecate getCallsite in favour of SourceMapSupport#getCallsite (#​16327)
  • [jest-runtime] Avoid magical null value in ESM loader (#​16160)
cloudflare/workers-sdk (wrangler)

v4.127.1

Compare Source

Patch Changes
colinhacks/zod (zod)

v4.5.2

Compare Source

Commits:

  • a354314 fix(docs): keep blog posts out of the docs collection (#​6484)
  • d378c42 ci: drop canary publishing from the release workflow (#​6487)
  • 212b941 fix(v4): let a prototype method getter answer a bare call so vi.spyOn works (#​6488)
  • e7576f5 docs(blog): let the page show through the navbar in dark mode (#​6489)
  • fedb06f fix(docs): match the blog TOC hover bar to the 2px active indicator
  • 6c932fc chore: bump devcontainer image to Node 24 (#​6470)
  • 6635d9d docs(blog): soften the "method memoization" attribution
  • 019ae29 fix(docs): drop ISR on the docs route so the home page hydrates
  • 652bb43 chore(docs): drop the scroll log from the route-change scroller
  • 571c8e8 fix(docs): render blog tabs with the stock fumadocs tab card
  • 9a193aa 4.5.2

v4.5.1

Compare Source

Commits:

  • 2e862db ci: gate the GitHub release and JSR publish on the version being live on npm
  • 8e03380 4.5.1

v4.5.0

Compare Source

Zod 4.5 is now available.

npm install zod@latest

At a glance:

z.compile()

You can now pre-compile any Zod schema using z.compile(schema). This dramatically speeds up parsing performance.

import * as z from "zod";

const Player = z.object({
  username: z.string(),
  bio: z.string(),
  xp: z.number()
});

const CompiledPlayer = z.compile(Player);

A compiled schema can be used exactly like an uncompiled one. There are no special rules around compiled schemas. They're just faster.

Player.parse({ ... });
CompiledPlayer.parse({ ... }); // ~2x faster

On objects, arrays, and unions, this speeds up parsing by a factor of ~3–7. More complex schemas stand to benefit more than simpler ones.

Time per parse on a shared nanosecond axis, standard parser as a gray bar with the compiled time as a blue bar inside it: an array of 10 objects 377 ns to 68 ns (5.5x), a 20-key object 301 ns to 38 ns (7.8x), an array of 10 strings 241 ns to 33 ns (7.3x), a union of 3 objects 190 ns to 36 ns (5.3x), a 3-element tuple 119 ns to 33 ns (3.6x), a 5-key strict object 117 ns to 32 ns (3.7x), a discriminated union 92 ns to 27 ns (3.4x), a 5-key object 76 ns to 28 ns (2.8x); up to 7.8x faster when compiled

Time per parse by schema type, standard parser vs compiled — lower is better (benchmark)

Below are the Moltar benchmark results comparing Zod (compiled and uncompiled) against the Moltar ParseSafe bench.

Bar chart of operations per second on the moltar benchmark fixture, parseSafe category: Zod 4 compiled 47.5M, typia 45.3M, Zod 4 11.6M, valibot 1.8M, effect 1.7M, Zod 3 1.2M, arktype 152k, yup 121k

Throughput on the moltar benchmark fixture (parseSafe: returns a new object with unknown keys stripped) — higher is better (benchmark)

And the equivalent results for the Moltar AssertLoose bench. Tested against the new z.validate(schema, input) function (detailed later in the post).

Bar chart of operations per second on the moltar benchmark fixture, assertLoose category: typia 74.9M, arktype 66.2M, Zod 4 compiled 60.6M, Zod 4 6.5M, valibot 1.9M, effect 1.7M, Zod 3 1.2M, yup 124k

Throughput on the moltar benchmark fixture (assertLoose: returns a boolean, unknown keys allowed) — higher is better (benchmark)

Zod's entire test suite runs twice—once normally and again with auto-compilation enabled globally—to ensure perfect fidelity.

How it works

Under the hood, z.compile() walks the entire schema once and produces a hyperoptimized snippet of flat, loop-free JavaScript that can validate inputs far faster than a standard runtime validator. This snippet can be executed via new Function() (effectively a more powerful eval) to serve as a fast-path validator. Schemas use this to "fast check" validity, falling back to the regular runtime logic on validation failure to provide granular error information.

Take this simple Point schema:

const Point = z.object({
  x: z.number(),
  y: z.number()
});

Here is the generated snippet for it:

const isPoint = new Function("input", `
  if (typeof input !== "object" || input === null) return false;
  if (typeof input.x !== "number") return false;
  if (typeof input.y !== "number") return false;
  return true;
`);

isPoint({ x: 1, y: 2 }); // true
isPoint({ x: "1" });     // false

For the large majority of inputs, the generated function validates the data with the fastest logic JavaScript can express: straight-line typeof checks and property reads, with no interpreter in between. When it can't handle an input, Zod falls back to the standard parser.

This is the function Zod generates for the Player schema above:

if (typeof input !== "object" || input === null || Array.isArray(input)) return INVALID;
const v0 = input["username"];
if (typeof v0 !== "string") return INVALID;
const v1 = input["bio"];
if (typeof v1 !== "string") return INVALID;
const v2 = input["xp"];
if (typeof v2 !== "number" || !Number.isFinite(v2)) return INVALID;
const v3 = { "username": v0, "bio": v1, "xp": v2 };
return v3;

Armed with the power of new Function(), this happens in-process at runtime. There is no need to integrate with your build system.

The compiled schema is purely additive on top of the existing schema. It tacks on the pre-compiled fast path for checking valid inputs. When invalid data is detected, it returns the INVALID symbol to signal that parsing should fall back to the uncompiled parser. This structurally prevents subtle deviations in error reporting between compiled and uncompiled variants.

import "zod/compile"

To compile every schema in an application, import zod/compile once at the top of your entry point. Every schema constructed after that import is automatically compiled the first time it's used to parse data.

import "zod/compile"; // must come before modules that define schemas
import * as z from "zod";

const schema = z.object({ name: z.string() });
schema.parse({ name: "ok" }); // compiled on first parse

It also works as a Node.js CLI flag, which guarantees it runs before any module defines a schema:

node --import zod/compile app.js

Or set preload in bunfig.toml or nub.jsonc.

{
  "preload": ["zod/compile"]
}

All schemas benefit to varying degrees, though complex object/tuple/array schemas benefit more than simple scalar validators.

Read the docs, or the full technical writeup: Introducing z.compile()

z.creditCard()

A new string format: 12–19 digits, optionally separated by single spaces or hyphens, with a valid Luhn checksum. (#​5931)

z.creditCard().parse("4111 1111 1111 1111"); // ✅
z.creditCard().parse("4111 1111 1111 1112"); // ❌ bad checksum

z.properties()

The multi-property counterpart to z.property(). (#​5912)

const httpsUrl = z.instanceof(URL).check(
  ...z.properties({
    protocol: z.literal("https:" as string),
    hostname: z.string().regex(z.regexes.domain),
  })
);

httpsUrl.parse(new URL("https://example.com")); // ✅
httpsUrl.parse(new URL("http://localhost")); // ❌ protocol

z.deepPartial()

Back in functional form after being removed as a method in Zod 4. (#​5928)

const Post = z.object({
  title: z.string(),
  author: z.object({ name: z.string(), email: z.string() }),
});

const PartialPost = z.deepPartial(Post);
type PartialPost = z.output<typeof PartialPost>;
// => { title?: string; author?: { name?: string; email?: string } }

PartialPost.parse({ author: {} }); // ✅

The result is still a ZodObject, so .shape and .extend() keep working.

.exactPartial()

Like .partial(), but wraps each field in z.exactOptional() instead of z.optional(): keys may be omitted, but an explicit undefined is rejected. This matches TypeScript's Partial<> under exactOptionalPropertyTypes. (#​6065)

const Recipe = z.object({ title: z.string(), servings: z.number() });

const PartialRecipe = Recipe.exactPartial();
PartialRecipe.parse({});                    // ✅
PartialRecipe.parse({ title: undefined });  // ❌

In Zod Mini it's a top-level function: z.exactPartial(Recipe).

z.validate()

Standalone boolean validation, in Zod, Zod Mini, and Zod Core. It answers "is this input valid?" without constructing a ZodError, which makes rejection cheap: on invalid input it is up to 16x faster than .safeParse().success. The return type is a guard on the schema's input type, and z.validateAsync() covers schemas with async refinements. (#​6471)

z.validate(z.string(), "hi"); // true
z.validate(z.string(), 42);   // false

z.input() / z.output()

Project a schema onto its input or output side. Useful for validating the two halves of a codec independently. (#​5928)

const isoDate = z.codec(z.iso.datetime(), z.date(), {
  decode: (s) => new Date(s),
  encode: (d) => d.toISOString(),
});

const Event = z.object({ name: z.string(), at: isoDate });

z.input(Event).parse({ name: "launch", at: "2024-01-01T00:00:00Z" }); // ✅
z.output(Event).parse({ name: "launch", at: new Date() });            // ✅

This is a no-op on schemas not containing codecs/pipes.

z.toZod<T>()

A utility to define a Zod schema that agrees exactly with a static type, often one that is handwritten or externally defined. (#​5913)

type Player = { username: string; xp: number };

const Player = z.toZod<Player>()(
  z.object({
    username: z.string(),
    xp: z.number(),
  })
);

Player.shape.username; // ZodString — the schema is returned unchanged

z.getDiscriminatedOption()

Extract a discriminated union member by discriminator value. (#​5947)

const Fruit = z.object({ type: z.literal("fruit"), seeds: z.boolean() });
const Veg = z.object({ type: z.literal("vegetable"), leafy: z.boolean() });
const Produce = z.discriminatedUnion("type", [Fruit, Veg]);

z.getDiscriminatedOption(Produce, "fruit"); // typeof Fruit
z.getDiscriminatedOption(Produce, "meat");  // ❌ TypeScript error

Cyclical inputs

Zod recursive schemas now support cyclical data. For bundle size reasons, Zod Mini requires you to register a memoizer explicitly. (#​6387, #​6482)

Zod

const Category = z.object({
  name: z.string(),
  get subcategories() {
    return z.array(Category);
  },
});

const input: any = { name: "root", subcategories: [] };
input.subcategories.push(input);

const result = Category.parse(input);
result.subcategories[0] === result; // true

Zod Mini

// register a memoizer before defining any schemas
z.config({ memoizer: z.memoizer() });

const result = Category.parse(input);
result.subcategories[0] === result; // true

9x reduction in schema memory footprint

In Zod 4.4 a bare z.string() retained 7.5kb of heap. In Zod 4.5 it retains 784 bytes.

Bar chart of heap retained by one schema instance, zod 4.4.3 versus 4.5: a 10-key object 82.0kb to 11.0kb, a union 17.5kb to 2.13kb, z.string().min(1) 16.7kb to 3.37kb, a record 16.4kb to 2.64kb, z.string().optional() 12.6kb to 1.50kb, an array of strings 11.2kb to 1.93kb, z.string() 7.53kb to 784b, z.number() 4.44kb to 706b. Up to 9.8x smaller than 4.4.3.

Retained heap per schema instance, Zod 4.4.3 vs 4.5 (benchmark)

In Zod 4.4 and earlier, all schema methods were automatically bound to the instance itself. This allowed users to pluck methods from schemas without causing issues due to this-binding.

const { parse } = z.string();

parse("some data");

A consequence of this is that each bound method allocates space on the heap; method implementations are not shared across all instances via prototype, as you'd expect. Zod 4.5 implements a method memoization pattern that avoids allocating bound methods until they are actually accessed.

Read the deep dive: Reducing Zod's memory footprint by an order of magnitude

Faster failures

Zod .parse()/.safeParse() instantiates a JavaScript Error, which captures a stack trace. In the case of validation failures, this is often much slower than the parsing logic itself. When using .safeParse(), Zod no longer captures this stack trace, speeding up failure-path parses by a factor of ~7.5x. (#​6316, #​6450)

const result = Player.safeParse({ username: 42, bio: "hello", xp: 12 });
result.success; // false — ~7.5x faster than Zod 4.4
Bar chart of time per failing safeParse: zod 4.4 at 6.3 microseconds, zod 4.5 at 840 nanoseconds — 7.6x faster

Player schema (benchmark)

Symbol keys in z.object()

A shape can now declare a symbol key. TypeScript tracks it: a const symbol infers as unique symbol, so z.infer makes the key required and checks its value type. Undeclared symbol keys are still ignored. (#​6448)

const TAG = Symbol("tag");
const schema = z.object({ name: z.string(), [TAG]: z.number() });

schema.parse({ name: "alice", [TAG]: 42 }); // ✅ { name: "alice", [TAG]: 42 }
schema.safeParse({ name: "alice" });        // ❌ the symbol key is required

Bug fixes

All of these fix soundness issues, so a schema that relied on the old behavior may now reject input it used to accept.

⚠️ z.iso.datetime() requires seconds

RFC 3339 mandates seconds. z.iso.datetime() and z.iso.datetime({ offset: true }) no longer accept minute-precision input like 2020-01-01T06:15Z. local: true still admits 2020-01-01T06:15, since an unqualified datetime is outside RFC 3339 either way. (#​6457)

z.iso.datetime().parse("2020-01-01T06:15:00Z"); // ✅
z.iso.datetime().parse("2020-01-01T06:15Z");    // ❌ was accepted in 4.4

To accept both forms, union the two precisions:

z.union([z.iso.datetime(), z.iso.datetime({ precision: -1 })]);
⚠️ String length counts code points

.min(), .max(), and .length() counted UTF-16 code units, so z.string().max(5) rejected five emoji. They now count Unicode code points, which is what every non-JS consumer of a length bound does (Postgres, MySQL, Go, Python, and the maxLength that z.toJSONSchema() emits). .max() only loosens; .min() and .length() tighten for astral input. Graphemes are unchanged — a ZWJ sequence is still several code points. (#​6441)

z.string().max(5).parse("😀😀😀😀😀"); // was too_big, now passes
z.string().min(5).parse("😀😀😀");     // was fine, now too_small

Closes #​3355.

⚠️ Record keys and intersections match TypeScript

A record's key schema now governs only the keys that match it, the way TypeScript treats an index signature. Intersecting an object with a pattern-keyed record no longer rejects the object's own keys. (#​6412)

z.object({ name: z.string() })
  .and(z.record(z.string().regex(/^S_/), z.string()))
  .parse({ name: "a", S_a: "s" });
// 4.4: throws invalid_key on "name"
// 4.5: { name: "a", S_a: "s" }

Separately, an unrecognized_keys issue no longer aborts the schema it came from, so a strict object with an extra key and a bad value now reports both issues instead of just the first. Closes #​2200, #​2573, #​4017, #​5663.

⚠️ __proto__ is always stripped

Object and record parsers now drop a __proto__ key whether it comes from the input, is declared by the schema, or is produced by a record key transform. A key that a record's key schema normalizes to __proto__ is dropped too. .strict() reports an own __proto__ input key as unrecognized_keys instead of silently swallowing it. Error formatters and both JSON Schema converters use own-property writes so a toString or constructor path segment can't walk onto Object.prototype (#​6213, #​6367, #​6346). (#​6386, #​6354, #​6355, #​6221)

⚠️ Stricter string formats
  • z.ipv6() validated by handing the string to new URL(), which let ::@1\ and ::1\n through. It now checks the address alphabet directly (#​6442).
  • z.ulid() restricts the first character to 07; anything higher overflows the 48-bit timestamp. A fixture that doesn't start with a real timestamp, such as one with a leading letter, is now rejected (#​6095).
  • z.httpUrl() enforces the RFC 1035 length limits on the host, matching z.hostname() (#​6035).
  • z.emoji() no longer backtracks exponentially on a failed match (#​6347).
  • z.string().includes(sub, { position: N }) emits a JSON Schema pattern that allows at least N leading characters, matching String.prototype.includes (#​6024).

Commits

Zod 4.5 rolls up 155 commits. Thanks to everyone who contributed: @​dokson, @​deepshekhardas, @​zirkelc, @​francisjohnjohnston-web, @​MerlijnW70, @​codinsonn, @​oimo23, @​JSap0914, @​zelinewang, @​abhishek-chaudhary2003, @​spokodev, @​Mohammad-Faiz-Cloud-Engineer, @​hamed-bavar, @​MGPOCKY, [@​C

Note

PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@github-actions

Copy link
Copy Markdown
Contributor

💻 Website Preview

The latest changes are available as preview in: https://pr-4128.fets-3ku.pages.dev

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

✅ Benchmark Results

     ✓ no_errors{server:node-http,mode:no-schema}
     ✓ expected_result{server:node-http,mode:no-schema}
     ✓ no_errors{server:node-http,mode:json-schema}
     ✓ expected_result{server:node-http,mode:json-schema}
     ✓ no_errors{server:uWebSockets,mode:no-schema}
     ✓ expected_result{server:uWebSockets,mode:no-schema}
     ✓ no_errors{server:uWebSockets,mode:json-schema}
     ✓ expected_result{server:uWebSockets,mode:json-schema}

     checks......................................: 100.00% ✓ 879662      ✗ 0     
     data_received...............................: 80 MB   666 kB/s
     data_sent...................................: 56 MB   465 kB/s
     http_req_blocked............................: avg=1.45µs   min=671ns    med=1.28µs   max=318.99µs p(90)=1.77µs   p(95)=2.31µs  
     http_req_connecting.........................: avg=1ns      min=0s       med=0s       max=146.6µs  p(90)=0s       p(95)=0s      
     http_req_duration...........................: avg=194.01µs min=126.03µs med=184.35µs max=11.51ms  p(90)=211.96µs p(95)=220.78µs
       { expected_response:true }................: avg=194.01µs min=126.03µs med=184.35µs max=11.51ms  p(90)=211.96µs p(95)=220.78µs
     ✓ { server:node-http,mode:json-schema }.....: avg=202.95µs min=141.93µs med=192.36µs max=8.44ms   p(90)=219.87µs p(95)=228.1µs 
     ✓ { server:node-http,mode:no-schema }.......: avg=195.5µs  min=130.76µs med=182.98µs max=9.71ms   p(90)=211.99µs p(95)=222.2µs 
     ✓ { server:uWebSockets,mode:json-schema }...: avg=192.32µs min=130.19µs med=184.69µs max=11.51ms  p(90)=209.38µs p(95)=216.46µs
     ✓ { server:uWebSockets,mode:no-schema }.....: avg=185.88µs min=126.03µs med=177.67µs max=8.81ms   p(90)=203.12µs p(95)=210.95µs
     http_req_failed.............................: 0.00%   ✓ 0           ✗ 439831
     http_req_receiving..........................: avg=24.82µs  min=9.54µs   med=23.84µs  max=3.15ms   p(90)=31.31µs  p(95)=33.42µs 
     http_req_sending............................: avg=8.32µs   min=4.3µs    med=7.68µs   max=1.27ms   p(90)=11.48µs  p(95)=12.9µs  
     http_req_tls_handshaking....................: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting............................: avg=160.86µs min=98.38µs  med=150.98µs max=11.44ms  p(90)=176.68µs p(95)=184.51µs
     http_reqs...................................: 439831  3665.219112/s
     iteration_duration..........................: avg=267.97µs min=193.04µs med=257.21µs max=11.7ms   p(90)=290.23µs p(95)=301.39µs
     iterations..................................: 439831  3665.219112/s
     vus.........................................: 1       min=1         max=1   
     vus_max.....................................: 2       min=2         max=2   

@renovate
renovate Bot force-pushed the renovate/all-minor-patch branch from ac7cbfa to d9f913b Compare August 28, 2026 19:15
@renovate renovate Bot changed the title Update all non-major dependencies to v30.5.0 Update all non-major dependencies Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants