1. 3-Tier Package Topology (v4/core, classic, mini)
Zod v4.6.7 organizes its packages/zod/src/ directory into a strict Directed Acyclic Graph (DAG) across three primary architectural tiers (v4/core, v4/classic, v4/mini), supplemented by 64 localization modules (v4/locales) and thin package subpath entrypoints. Every dependency arrow points strictly downward: v4/core has zero imports from v4/classic or v4/mini, and v4/classic and v4/mini never import each other.
Source Code
from drawlib.canvas import save, setup
from drawlib.icons import phosphor
from drawlib.lines import line
from drawlib.smartarts import CardList, Panel
from drawlib.styles import Styles
from drawlib.text import text
setup(width=156, height=110)
text(
(78, 105.5),
"Zod v4.6.7 Module Dependency DAG (packages/zod/src/)",
style=Styles.DarkBold.patch(text_size=13.5),
)
text(
(78, 101.5),
"Strict Downward-Only Imports from Subpath Entrypoints -> Classic/Mini Facades -> Core & Locales",
style=Styles.Muted.patch(text_size=10.0),
)
# Tier 0: Top-level Entrypoints
p_top = Panel(
"Subpath Entrypoints (packages/zod/src/*.ts)",
style=Styles.Neutral,
header_style=Styles.Secondary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=5.8,
)
p_top.draw(xy=(6, 80), width=144, height=17.5)
top_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="horizontal",
spacing=2.0,
)
top_cards.add("src/index.ts", description="export { z, z as default }", style=Styles.PrimaryNeutral)
top_cards.add("src/locales/index.ts", description="49+ locale barrel")
top_cards.add("src/v4/core/index.ts", description="core barrel export")
top_cards.add("src/compile.ts", description="AOT postProcessor")
top_cards.add("src/mini/index.ts", description="re-exports v4/mini", style=Styles.PrimaryNeutral)
top_cards.draw(xy=(8.5, 81.5), width=139, height=8.5)
# Tier 1 Left: v4/classic (12 modules)
p_classic = Panel(
"v4/classic/ (12 Modules)",
style=Styles.PrimaryNeutral,
header_style=Styles.Primary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.stack,
icon_style=Styles.White,
header_height=5.8,
)
p_classic.draw(xy=(6, 45), width=52, height=28)
classic_list = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
classic_list.add("external.ts & schemas.ts", description="ZodType + 35+ chain methods")
classic_list.add("from-json-schema.ts", description="JSON Schema parser + compat.ts")
classic_list.draw(xy=(8.5, 47.0), width=47, height=18.0)
# Tier 1 Center: v4/locales (64 files) — adjacent to v4/classic
p_loc = Panel(
"v4/locales/ (64 Files)",
style=Styles.Neutral,
header_style=Styles.Secondary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.globe,
icon_style=Styles.White,
header_height=5.8,
)
p_loc.draw(xy=(66, 45), width=34, height=28)
loc_list = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
loc_list.add("en.ts (Default)", description="Auto-loaded by classic")
loc_list.add("ja.ts, de.ts, fr.ts...", description="Opt-in via z.config()")
loc_list.draw(xy=(68.0, 47.0), width=30, height=18.0)
# Tier 1 Right: v4/mini (9 modules)
p_mini = Panel(
"v4/mini/ (9 Modules)",
style=Styles.PrimaryNeutral,
header_style=Styles.Primary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.feather,
icon_style=Styles.White,
header_height=5.8,
)
p_mini.draw(xy=(104, 45), width=46, height=28)
mini_list = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
mini_list.add("external.ts & schemas.ts", description="ZodMiniType (no method chains)")
mini_list.add("checks.ts, parse.ts, iso.ts", description="Tree-shakable check functions")
mini_list.draw(xy=(106.5, 47.0), width=41, height=18.0)
# Tier 2 Bottom: v4/core (21 modules)
p_core = Panel(
"v4/core/ — Shared Validation, Trait & Compiler Engine (21 Modules — Zero Upward Dependencies)",
style=Styles.Neutral,
header_style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.cpu,
icon_style=Styles.White,
header_height=5.8,
)
p_core.draw(xy=(6, 5), width=144, height=33)
core_r1 = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="horizontal",
spacing=2.5,
)
core_r1.add(
"core.ts & util.ts",
description="$constructor, globalConfig, clone()",
style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=9.5),
description_style=Styles.White.patch(text_size=9.5),
)
core_r1.add(
"schemas.ts & api.ts",
description="$ZodType & shared _string factories",
style=Styles.PrimaryNeutral,
)
core_r1.add(
"checks.ts, regexes.ts & parse.ts",
description="$ZodCheck, RegExps, _parse, _encode",
)
core_r1.draw(xy=(8.5, 20.5), width=139, height=9.5)
core_r2 = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="horizontal",
spacing=2.5,
)
core_r2.add(
"doc.ts & compile.ts",
description="Doc closure builder & JIT compiler",
style=Styles.AccentFlat,
text_style=Styles.WhiteBold.patch(text_size=9.5),
description_style=Styles.White.patch(text_size=9.5),
)
core_r2.add(
"registries.ts & memoizer.ts",
description="WeakMap registry & cycle memoizer",
)
core_r2.add(
"to-json-schema.ts & standard-schema",
description="JSON Schema Drafts & ~standard v1",
)
core_r2.draw(xy=(8.5, 8.0), width=139, height=9.5)
# Downward arrows (gap = 7.0 units)
line((32, 80), (32, 73), arrow_head="->", style=Styles.PrimaryBold)
line((83, 80), (83, 73), arrow_head="->", style=Styles.SecondaryBold)
line((127, 80), (127, 73), arrow_head="->", style=Styles.PrimaryBold)
# Classic imports en.ts from adjacent locales (gap = 8.0 units)
line((58, 59), (66, 59), arrow_head="->", style=Styles.SecondaryBold)
text((62, 62.0), "en()", style=Styles.Secondary.patch(text_size=9.5))
# Classic, Locales, Mini -> Core (gap = 7.0 units)
line((32, 45), (32, 38), arrow_head="->", style=Styles.PrimaryBold)
text((44, 41.5), "import * as core", style=Styles.Primary.patch(text_size=9.5))
line((83, 45), (83, 38), arrow_head="->", style=Styles.SecondaryBold)
line((127, 45), (127, 38), arrow_head="->", style=Styles.PrimaryBold)
text((139, 41.5), "import * as core", style=Styles.Primary.patch(text_size=9.5))
save()
1. Complete Module Inventory Across All Four Directories
1.1 packages/zod/src/v4/core/ (21 Modules — The Engine)
v4/core contains every runtime algorithm, type-level inference primitive, check implementation, JIT compiler pass, and JSON Schema processor. None of its 21 modules attach convenience wrapper methods (such as .optional() or .min()) onto schema prototypes.
Module (v4/core/) |
Size / Scale | Core Responsibility & Exported Symbols |
|---|---|---|
core.ts |
~9.7 KB | Defines $constructor(), NEVER, newError(), $brand, input<T>, output<T>, infer<T>, and globalConfig (globalThis.__zod_globalConfig). |
util.ts |
~48.1 KB | Foundational runtime and type utilities: Cached / cached(), defineLazy(), defineLazyInternal(), installLazyProp(), members(), derived(), clone(), mergeDefs(), aborted(), finalizeIssue(), toZod<T>(), and allowsEval. |
schemas.ts |
~177.1 KB (5,227 LOC) | Defines $ZodType and 45+ core schema constructors ($ZodString, $ZodNumber, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodPipe, $ZodCodec, etc.) plus ParsePayload and ParseContextInternal. |
api.ts |
~58.2 KB | Shared factory helper functions (_string, _number, _email, _uuid, _isoDateTime, etc.) parameterized by a Class: util.SchemaClass<...> constructor so v4/classic and v4/mini share 100% of parameter normalization logic. |
checks.ts |
~39.0 KB | Defines $ZodCheck and 25+ refinement/mutation check traits ($ZodCheckLessThan, $ZodCheckGreaterThan, $ZodCheckMinLength, $ZodCheckRegex, $ZodCheckStringFormat, $ZodCheckOverwrite, etc.). |
parse.ts |
~12.6 KB | Higher-order execution runners: _parse, _parseAsync, _safeParse, _safeParseAsync, validate, validateAsync, _encode, _decode, _safeEncode, and _safeDecode. |
errors.ts |
~18.9 KB | Discriminated issue unions ($ZodIssue, $ZodRawIssue), lazy-message $ZodError / $ZodRealError, and error formatting helpers (treeifyError, prettifyError, formatError, flattenError). |
regexes.ts |
~12.7 KB | Precompiled regular expressions (email, uuid, ipv4, ipv6, base64, cuid2, ulid, datetime, duration, etc.) and builders. |
doc.ts |
~1.5 KB | Doc closure code-generation buffer that formats indented JS source lines and compiles them via new Function(...Object.keys(closed), body). |
compile.ts |
~97.9 KB | Full schema compiler (compile(), withParser()) generating specialized synchronous validators and fast-path parsers with fallback semantics. |
registries.ts |
~3.4 KB | $ZodRegistry (WeakMap schema metadata + Map ID index), $output / $input placeholder symbols, $replace<Meta, S>, and globalRegistry. |
memoizer.ts |
~12.1 KB | $ZodMemoizer implementation (memoizer()) enabling cyclic object/array/map/set input graphs to be parsed without infinite recursion. |
to-json-schema.ts |
~35.0 KB | Functional JSON Schema pipeline (initializeContext, processSchema, extractDefs, finalize) supporting Draft-04, Draft-07, Draft-2020-12, and OpenAPI 3.0. |
json-schema-processors.ts |
~38.4 KB | Per-type JSON Schema processor callbacks (stringProcessor, numberProcessor, objectProcessor, aggregateChecks, toJSONSchema). |
json-schema-generator.ts |
~4.6 KB | Backward-compatible JSONSchemaGenerator class wrapper around to-json-schema.ts. |
json-schema.ts |
~3.9 KB | TypeScript type definitions for JSON Schema Draft payloads (BaseSchema, ObjectSchema, etc.). |
standard-schema.ts |
~6.3 KB | Standard Schema v1 (~standard) and Standard JSON Schema type contracts (StandardSchemaV1, StandardSchemaWithJSONProps). |
visit.ts & zsf.ts |
~13.6 KB | Schema graph traversal (visit()) and internal schema serialization format (zsf.ts). |
versions.ts & index.ts |
~0.8 KB | Version constant (4.6.7) and barrel export for zod/v4/core. |
1.2 packages/zod/src/v4/classic/ vs. packages/zod/src/v4/mini/
Both v4/classic and v4/mini are thin facades that wrap v4/core constructors using $constructor trait composition and delegate construction to core._string(Class, params) in v4/core/api.ts:
// In packages/zod/src/v4/classic/schemas.ts:
export function string(params?: string | core.$ZodStringParams): ZodString {
return core._string(ZodString, params);
}
// In packages/zod/src/v4/mini/schemas.ts:
export function string(params?: string | core.$ZodStringParams): ZodMiniString<string> {
return core._string(ZodMiniString, params) as any;
}
Why does zod/mini produce ~85% smaller bundles for small client applications? There are three concrete reasons in the source code:
- Zero Wrapper/Refinement Methods on
ZodMiniType.prototype: Inv4/classic/schemas.ts(L184-357),ZodTypeinstalls over 35 methods onto its prototype (.optional(),.nullable(),.nullish(),.array(),.or(),.and(),.transform(),.default(),.catch(),.pipe(),.readonly(),.describe(),.meta(),.toJSONSchema()). BecauseZodType.prototypereferencesoptional(),nullable(),array(),union(),intersection(),pipe(),transform(), andcreateToJSONSchemaMethod(), Rollup/esbuild/Webpack cannot tree-shake those schema constructors or the JSON Schema generator out of azodbundle even if the application only callsz.string().parse("hi"). By contrast,ZodMiniType(v4/mini/schemas.ts:L55-103) installs onlyparse,parseAsync,safeParse,safeParseAsync,check(with),clone,brand,register, andapply. - Zero Format Methods on
ZodMiniString.prototype: Inv4/classic/schemas.ts(L535-614),ZodStringattaches.email(),.url(),.jwt(),.uuid(),.cuid2(),.ulid(),.ipv4(),.cidrv6(),.e164(),.datetime(), etc. Every one of those methods referencesZodEmail,ZodUUID,ZodJWT, and their regular expressions inv4/core/regexes.ts. Inv4/mini/schemas.ts(L116-122),ZodMiniStringhas no prototype methods at all; users compose checks functionally viaz.string().check(z.minLength(5), z.email()), allowing bundlers to drop every unused check and regex. - No Automatic Default Locale Import in
v4/mini: Look atv4/classic/schemas.ts:L14-16:function _ensureDefaultLocale(): void { if (!core.globalConfig.localeError) core.config(en()); }ZodTypecalls_ensureDefaultLocale()on first schema construction, which bundlesv4/locales/en.ts(~5.1 KB of English error message templates) automatically.ZodMiniTypeomits_ensureDefaultLocale(), falling back to"Invalid input"infinalizeIssue()(v4/core/util.ts:L978) unless the developer explicitly opts intoz.config(z.locales.en())orz.config(z.locales.ja()).
2. Dual ESM/CJS Build Pipeline (zshy & @zod/source)
Zod v4 uses zshy (packages/zod/package.json:L38-126) to compile TypeScript sources under src/ into dual ESM (.js + .d.ts) and CommonJS (.cjs + .d.cts) entrypoints while supporting zero-build monorepo development via the custom @zod/source export condition.
Source Code
from drawlib.canvas import save, setup
from drawlib.lines import line
from drawlib.smartarts import CardList, ChevronProcess, Panel
from drawlib.styles import Styles
from drawlib.text import text
setup(width=156, height=90)
text(
(78, 85.5),
"Package Export Resolution & zshy Dual ESM/CJS Build Pipeline",
style=Styles.DarkBold.patch(text_size=13.5),
)
# Top pipeline chevrons
pipe = ChevronProcess(
style=Styles.PrimaryNeutral,
text_style=Styles.DarkBold.patch(text_size=10.0),
description_style=Styles.Dark.patch(text_size=9.5),
corner_angle=60.0,
spacing=1.5,
flat_left_end=True,
)
pipe.add(
"1. TypeScript Source",
description="packages/zod/src/**/*.ts",
style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=10.0),
description_style=Styles.White.patch(text_size=9.5),
)
pipe.add("2. zshy Build Compiler", description="tsconfig.build.json")
pipe.add("3. Postbuild & Stubs", description="write-stub-package-jsons.ts")
pipe.add(
"4. Conditional Exports",
description="ESM / CJS / @zod/source",
style=Styles.AccentFlat,
text_style=Styles.WhiteBold.patch(text_size=10.0),
description_style=Styles.White.patch(text_size=9.5),
)
pipe.draw(xy=(6, 67), width=144, height=13)
# Left Panel: 11 Subpath Exports in package.json
p_exp = Panel(
"11 Subpaths in package.json",
style=Styles.Neutral,
header_style=Styles.Primary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=6.0,
)
p_exp.draw(xy=(6, 6), width=46, height=54)
exp_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.3,
)
exp_cards.add(". & ./v4", description="./src/index.ts (Classic API)", style=Styles.PrimaryNeutral)
exp_cards.add("./mini & ./v4/mini", description="./src/mini/index.ts (Mini API)", style=Styles.PrimaryNeutral)
exp_cards.add("./v4/core", description="./src/v4/core/index.ts (Engine)")
exp_cards.add("./compile", description="./src/compile.ts (Side-effect AOT)")
exp_cards.add("./locales & ./v4/locales/*", description="./src/v4/locales/* (49+ Locales)")
exp_cards.draw(xy=(8.5, 8.0), width=41, height=44.0)
# Center Panel: 4 Export Conditions per Subpath
p_cond = Panel(
"Per-Subpath Conditions",
style=Styles.Neutral,
header_style=Styles.Secondary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=6.0,
)
p_cond.draw(xy=(55, 6), width=46, height=54)
cond_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
cond_cards.add(
'"@zod/source": "./src/*.ts"',
description="Direct TS in Vitest / monorepo",
style=Styles.PrimaryNeutral,
)
cond_cards.add('"types": "./*.d.cts"', description="TypeScript declaration files")
cond_cards.add('"import": "./*.js"', description="Native ES Module (.js + .d.ts)")
cond_cards.add('"require": "./*.cjs"', description="Sealed CommonJS (sealCjsExports)")
cond_cards.draw(xy=(57.5, 8.0), width=41, height=44.0)
# Right Panel: Bundler Tree-Shaking Optimizations
p_shake = Panel(
"Tree-Shaking & Purity Guarantees",
style=Styles.Neutral,
header_style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=6.0,
)
p_shake.draw(xy=(104, 6), width=46, height=54)
shake_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
shake_cards.add(
'sideEffects: ["./compile.*"]',
description="All other modules side-effect free",
style=Styles.AccentFlat,
text_style=Styles.WhiteBold.patch(text_size=9.5),
description_style=Styles.White.patch(text_size=9.5),
)
shake_cards.add("/*@__PURE__*/ Annotations", description="Marks factory calls side-effect free")
shake_cards.add("export { z, z as default }", description="Fixes Rollup/Webpack import (#6050)")
shake_cards.add("IIFE NUMBER_FORMAT_RANGES", description="Avoids Number.* dead-code retention")
shake_cards.draw(xy=(106.5, 8.0), width=41, height=44.0)
line((29, 67), (29, 60), arrow_head="->", style=Styles.PrimaryBold)
line((78, 67), (78, 60), arrow_head="->", style=Styles.SecondaryBold)
line((127, 67), (127, 60), arrow_head="->", style=Styles.PrimaryBold)
save()
2.1 Why import * as core and export { z, z as default } Matter
A subtle bundler pitfall in JavaScript module graphs is how Rollup, Webpack, and esbuild handle namespace re-exports (import * as z vs export default z). Look at the comment in packages/zod/src/index.ts:
// packages/zod/src/index.ts
import * as z from "./v4/classic/external.js";
export * from "./v4/classic/external.js";
// Aliasing the binding, instead of `export default z`, keeps the default export a
// re-export of `z` rather than a fresh namespace value. Rollup and Webpack can then
// shake `import z from "zod"` as they already shake `import { z } from "zod"` — without
// this they pull in every locale. See #6050.
export { z, z as default };
When a file writes export default z, Rollup and Webpack materialize the module namespace object as a first-class runtime value, forcing every property of external.js—including export * as locales from "../locales/index.js" (all 49+ locales!)—into the bundle. By writing export { z, z as default }, default remains a live ES module binding alias, allowing bundlers to statically resolve z.string() even when imported via import z from "zod".
2.2 Explicit sideEffects Whitelisting for zod/compile
In packages/zod/package.json:L13-17, only the AOT compilation entrypoint is listed in sideEffects:
"sideEffects": [
"./compile.js",
"./compile.cjs",
"./src/compile.ts"
],
Every other module in Zod v4 is treated as pure (sideEffects: false) by bundlers:
- Every
$constructor(...)call is prefixed with/*@__PURE__*/, and$constructoritself is annotated with/*@__NO_SIDE_EFFECTS__*/(packages/zod/src/v4/core/core.ts:L51). - Top-level constants that read properties off built-in globals (such as
Number.MIN_SAFE_INTEGERinNUMBER_FORMAT_RANGES,packages/zod/src/v4/core/util.ts:L741) are wrapped in/*@__PURE__*/ (() => ({ ... }))()IIFEs because esbuild refuses to tree-shake bare top-level object literals containingNumber.*member accesses. - Conversely,
import "zod/compile"(packages/zod/src/compile.ts) mutatescore.globalConfig.postProcessorat import time to wrap every subsequently constructed schema in a lazy JIT/AOT compilation shim, so listing it insideEffectsprevents bundlers from dropping bareimport "zod/compile"statements.