Skip to content

feat(v4): add explicit z.precompile(...) for startup-restricted runtimes#5728

Open
whoiskatrin wants to merge 1 commit intocolinhacks:mainfrom
whoiskatrin:workers-jit
Open

feat(v4): add explicit z.precompile(...) for startup-restricted runtimes#5728
whoiskatrin wants to merge 1 commit intocolinhacks:mainfrom
whoiskatrin:workers-jit

Conversation

@whoiskatrin
Copy link
Copy Markdown

Summary

This PR adds an explicit z.precompile(...) API to precompile object fast-paths in environments where new Function may only be available during startup (for example Cloudflare Workers).

Instead of relying on implicit runtime heuristics/timing, this makes the behavior deterministic and opt-in.

Prior context

This builds on earlier exploration in #5462 and #5565. The approach here favors an explicit precompile API over implicit runtime heuristics/timing so behavior is deterministic and easier to reason about in startup-restricted runtimes.

What changed

  • Added precompile(schema | schema[]) in core:
    • packages/zod/src/v4/core/precompile.ts
  • Exported publicly:
    • packages/zod/src/v4/core/index.ts
    • packages/zod/src/v4/classic/external.ts (z.precompile(...))
  • Updated object JIT internals:
    • packages/zod/src/v4/core/schemas.ts
    • object schemas now expose an internal compile hook and gracefully fall back to the non-JIT path if compile fails.
  • Updated eval capability check:
    • packages/zod/src/v4/core/util.ts
    • switched to feature detection (new Function) without hardcoded Cloudflare user-agent disabling.
  • Added docs section:
    • packages/docs/content/v4/index.mdx
    • includes startup/module-scope precompile guidance.

API

import * as z from "zod";

const User = z.object({
  id: z.uuidv4(),
  email: z.email(),
});

z.precompile(User);

z.precompile(...) is best-effort:

  • compiles reachable object fast-paths where possible,
  • no throw required for unavailable compilation,
  • parsing falls back safely.

Test approach

New focused tests

Added packages/zod/src/v4/classic/tests/workers-jit.test.ts with coverage for:

  1. Direct object precompile
    • verifies object schemas are compiled.
  2. Nested object traversal
    • verifies nested object graph compilation.
  3. Wrapped + recursive traversal
    • verifies traversal through wrappers/compositions (union, record, optional, readonly, catch, lazy).
  4. Graceful fallback
    • stubs Function to throw and verifies object parse still succeeds via non-JIT path.

Regression checks run locally

  • pnpm test packages/zod/src/v4/classic/tests/workers-jit.test.ts
  • pnpm test packages/zod/src/v4/classic/tests/object.test.ts packages/zod/src/v4/classic/tests/lazy.test.ts packages/zod/src/v4/classic/tests/record.test.ts
  • pre-push hook also ran full suite (pnpm test) successfully.

Notes

  • This keeps default behavior safe and adds an explicit path for users who can/want to precompile during startup.
  • This does not force eager compilation globally and avoids per-parse environment probing.

@dosubot
Copy link
Copy Markdown

dosubot bot commented Feb 21, 2026

Related Documentation

Checked 0 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

@whoiskatrin
Copy link
Copy Markdown
Author

Just to be specific this is not “automatic JIT for all Workers code” and not guaranteed speedup for dynamically created per-request schemas. Best value comes when schemas are module-scope and reused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant