├── .changeset ├── README.md └── config.json ├── .configs ├── rollup.config.js └── tsconfig.base.json ├── .devcontainer └── devcontainer.json ├── .editorconfig ├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .husky ├── .gitignore ├── pre-commit └── pre-push ├── .nojekyll ├── .npmrc ├── .nvmrc ├── .vscode ├── launch.json └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── biome.jsonc ├── logo.svg ├── logo ├── Logo Black.ai ├── Logo Black.jpg ├── Logo Black.pdf ├── Logo Black.png ├── Logo Outer Glow.ai ├── Logo Outer Glow.jpg ├── Logo Outer Glow.pdf ├── Logo Outer Glow.png ├── Logo White.ai ├── Logo White.jpg ├── Logo White.pdf ├── Logo White.png ├── Logo.ai ├── Logo.jpg ├── Logo.pdf └── Logo.png ├── package.json ├── packages ├── bench │ ├── _temp.ts │ ├── array.ts │ ├── benchUtil.ts │ ├── boolean.ts │ ├── datetime-regex.ts │ ├── datetime.ts │ ├── discriminated-union.ts │ ├── error-handling.ts │ ├── index.ts │ ├── instanceof.ts │ ├── ipv4-regex.ts │ ├── jit-union.ts │ ├── key-iteration.ts │ ├── lazy-box.ts │ ├── libs.ts │ ├── metabench.ts │ ├── number.ts │ ├── object-async.ts │ ├── object-creation.ts │ ├── object-fail.ts │ ├── object-moltar.ts │ ├── object-safe.ts │ ├── object-safeasync.ts │ ├── object-setup.ts │ ├── object.ts │ ├── package.json │ ├── property-access.ts │ ├── safe.ts │ ├── safeparse.ts │ ├── string.ts │ ├── tsconfig.bench.json │ └── union.ts ├── docs │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── app │ │ ├── (doc) │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── _home │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── apple-icon.png │ │ ├── global.css │ │ ├── icon.png │ │ ├── layout.config.tsx │ │ ├── layout.tsx │ │ ├── logo.ico │ │ ├── og.png │ │ │ └── route.tsx │ │ └── theme.css │ ├── biome.jsonc │ ├── components │ │ ├── accordion.tsx │ │ ├── bronze.tsx │ │ ├── ecosystem-v3.tsx │ │ ├── ecosystem.tsx │ │ ├── featured.tsx │ │ ├── gold.tsx │ │ ├── heading.tsx │ │ ├── if.tsx │ │ ├── inkeep-bubble.tsx │ │ ├── inkeep-search.tsx │ │ ├── platinum.tsx │ │ ├── scroller.tsx │ │ ├── sidebar-item.tsx │ │ ├── silver.tsx │ │ └── tabs.tsx │ ├── content │ │ ├── api.mdx │ │ ├── basics.mdx │ │ ├── ecosystem.mdx │ │ ├── error-customization.mdx │ │ ├── error-formatting.mdx │ │ ├── generic-functions.mdx │ │ ├── index.mdx │ │ ├── json-schema.mdx │ │ ├── library-authors.mdx │ │ ├── meta.json │ │ ├── metadata.mdx │ │ ├── object-vs-interface.mdx │ │ ├── packages │ │ │ ├── core.mdx │ │ │ ├── mini.mdx │ │ │ ├── v3.mdx │ │ │ └── zod.mdx │ │ ├── parsing.mdx │ │ └── v4 │ │ │ ├── changelog.mdx │ │ │ └── index.mdx │ ├── loaders │ │ ├── source.ts │ │ └── stars.ts │ ├── next.config.mjs │ ├── package.json │ ├── pages │ │ └── api │ │ │ └── _og.tsx │ ├── pnpm-lock.yaml │ ├── postcss.config.mjs │ ├── public │ │ ├── github-white.png │ │ ├── logo │ │ │ ├── logo-black.ai │ │ │ ├── logo-black.jpg │ │ │ ├── logo-black.pdf │ │ │ ├── logo-black.png │ │ │ ├── logo-glow.ai │ │ │ ├── logo-glow.jpg │ │ │ ├── logo-glow.pdf │ │ │ ├── logo-glow.png │ │ │ ├── logo-white.ai │ │ │ ├── logo-white.jpg │ │ │ ├── logo-white.pdf │ │ │ ├── logo-white.png │ │ │ ├── logo.ai │ │ │ ├── logo.jpg │ │ │ ├── logo.pdf │ │ │ ├── logo.png │ │ │ ├── logo.svg │ │ │ ├── logo_square.png │ │ │ ├── profile_circle.png │ │ │ └── profile_square.png │ │ └── robots.txt │ ├── source.config.ts │ └── tsconfig.json ├── treeshake │ ├── .gitignore │ ├── example-mini.ts │ ├── example.ts │ ├── package.json │ ├── rollup.config.js │ ├── valibot-boolean.ts │ ├── valibot-object.ts │ ├── valibot-string.ts │ ├── zod-boolean.ts │ ├── zod-full.ts │ ├── zod-locales.ts │ ├── zod-mini-boolean.ts │ ├── zod-mini-full.ts │ ├── zod-mini-object.ts │ ├── zod-mini-string.ts │ ├── zod-object.ts │ ├── zod-string.ts │ ├── zod3-boolean.ts │ ├── zod3-full.ts │ ├── zod3-object.ts │ └── zod3-string.ts ├── tsc │ ├── .gitignore │ ├── README.md │ ├── bench │ │ ├── index.ts │ │ ├── lots-of-objects.ts │ │ ├── object-with-extend.ts │ │ ├── omit-extend.ts │ │ ├── string.ts │ │ └── strongly-connected.ts │ ├── bisect.ts │ ├── extend.ts │ ├── generate.ts │ ├── package.json │ ├── tsconfig.bench.json │ └── tsconfig.json └── zod │ ├── LICENSE │ ├── README.md │ ├── build.mts │ ├── jsr.json │ ├── package.json │ ├── postbuild.ts │ ├── src │ ├── index.ts │ ├── v3 │ │ ├── ZodError.ts │ │ ├── benchmarks │ │ │ ├── datetime.ts │ │ │ ├── discriminatedUnion.ts │ │ │ ├── index.ts │ │ │ ├── ipv4.ts │ │ │ ├── object.ts │ │ │ ├── primitives.ts │ │ │ ├── realworld.ts │ │ │ ├── string.ts │ │ │ └── union.ts │ │ ├── errors.ts │ │ ├── external.ts │ │ ├── helpers │ │ │ ├── enumUtil.ts │ │ │ ├── errorUtil.ts │ │ │ ├── parseUtil.ts │ │ │ ├── partialUtil.ts │ │ │ ├── typeAliases.ts │ │ │ └── util.ts │ │ ├── index.ts │ │ ├── locales │ │ │ └── en.ts │ │ ├── standard-schema.ts │ │ ├── tests │ │ │ ├── Mocker.ts │ │ │ ├── all-errors.test.ts │ │ │ ├── anyunknown.test.ts │ │ │ ├── array.test.ts │ │ │ ├── async-parsing.test.ts │ │ │ ├── async-refinements.test.ts │ │ │ ├── base.test.ts │ │ │ ├── bigint.test.ts │ │ │ ├── branded.test.ts │ │ │ ├── catch.test.ts │ │ │ ├── coerce.test.ts │ │ │ ├── complex.test.ts │ │ │ ├── custom.test.ts │ │ │ ├── date.test.ts │ │ │ ├── deepmasking.test.ts │ │ │ ├── default.test.ts │ │ │ ├── description.test.ts │ │ │ ├── discriminated-unions.test.ts │ │ │ ├── enum.test.ts │ │ │ ├── error.test.ts │ │ │ ├── firstparty.test.ts │ │ │ ├── firstpartyschematypes.test.ts │ │ │ ├── function.test.ts │ │ │ ├── generics.test.ts │ │ │ ├── instanceof.test.ts │ │ │ ├── intersection.test.ts │ │ │ ├── language-server.source.ts │ │ │ ├── language-server.test.ts │ │ │ ├── literal.test.ts │ │ │ ├── map.test.ts │ │ │ ├── masking.test.ts │ │ │ ├── mocker.test.ts │ │ │ ├── nan.test.ts │ │ │ ├── nativeEnum.test.ts │ │ │ ├── nullable.test.ts │ │ │ ├── number.test.ts │ │ │ ├── object-augmentation.test.ts │ │ │ ├── object-in-es5-env.test.ts │ │ │ ├── object.test.ts │ │ │ ├── optional.test.ts │ │ │ ├── parseUtil.test.ts │ │ │ ├── parser.test.ts │ │ │ ├── partials.test.ts │ │ │ ├── pickomit.test.ts │ │ │ ├── pipeline.test.ts │ │ │ ├── preprocess.test.ts │ │ │ ├── primitive.test.ts │ │ │ ├── promise.test.ts │ │ │ ├── readonly.test.ts │ │ │ ├── record.test.ts │ │ │ ├── recursive.test.ts │ │ │ ├── refine.test.ts │ │ │ ├── safeparse.test.ts │ │ │ ├── set.test.ts │ │ │ ├── standard-schema.test.ts │ │ │ ├── string.test.ts │ │ │ ├── transformer.test.ts │ │ │ ├── tuple.test.ts │ │ │ ├── unions.test.ts │ │ │ ├── validations.test.ts │ │ │ └── void.test.ts │ │ └── types.ts │ └── v4 │ │ ├── classic │ │ ├── checks.ts │ │ ├── coerce.ts │ │ ├── compat.ts │ │ ├── errors.ts │ │ ├── external.ts │ │ ├── index.ts │ │ ├── iso.ts │ │ ├── parse.ts │ │ ├── schemas.ts │ │ └── tests │ │ │ ├── anyunknown.test.ts │ │ │ ├── array.test.ts │ │ │ ├── assignability.test.ts │ │ │ ├── async-parsing.test.ts │ │ │ ├── async-refinements.test.ts │ │ │ ├── base.test.ts │ │ │ ├── bigint.test.ts │ │ │ ├── brand.test.ts │ │ │ ├── catch.test.ts │ │ │ ├── coalesce.test.ts │ │ │ ├── coerce.test.ts │ │ │ ├── custom.test.ts │ │ │ ├── date.test.ts │ │ │ ├── default.test.ts │ │ │ ├── description.test.ts │ │ │ ├── discriminated-unions.test.ts │ │ │ ├── enum.test.ts │ │ │ ├── error-utils.test.ts │ │ │ ├── error.test.ts │ │ │ ├── file.test.ts │ │ │ ├── firstparty.test.ts │ │ │ ├── function.test.ts │ │ │ ├── generics.test.ts │ │ │ ├── index.test.ts │ │ │ ├── instanceof.test.ts │ │ │ ├── intersection.test.ts │ │ │ ├── json.test.ts │ │ │ ├── lazy.test.ts │ │ │ ├── literal.test.ts │ │ │ ├── map.test.ts │ │ │ ├── nan.test.ts │ │ │ ├── nested-refine.test.ts │ │ │ ├── nonoptional.test.ts │ │ │ ├── nullable.test.ts │ │ │ ├── number.test.ts │ │ │ ├── object.test.ts │ │ │ ├── optional.test.ts │ │ │ ├── partial.test.ts │ │ │ ├── pickomit.test.ts │ │ │ ├── pipe.test.ts │ │ │ ├── prefault.test.ts │ │ │ ├── preprocess.test.ts │ │ │ ├── primitive.test.ts │ │ │ ├── promise.test.ts │ │ │ ├── readonly.test.ts │ │ │ ├── record.test.ts │ │ │ ├── recursive-types.test.ts │ │ │ ├── refine.test.ts │ │ │ ├── registries.test.ts │ │ │ ├── set.test.ts │ │ │ ├── standard-schema.test.ts │ │ │ ├── string-formats.test.ts │ │ │ ├── string.test.ts │ │ │ ├── stringbool.test.ts │ │ │ ├── template-literal.test.ts │ │ │ ├── to-json-schema.test.ts │ │ │ ├── transform.test.ts │ │ │ ├── tuple.test.ts │ │ │ ├── union.test.ts │ │ │ ├── validations.test.ts │ │ │ └── void.test.ts │ │ ├── core │ │ ├── api.ts │ │ ├── checks.ts │ │ ├── config.ts │ │ ├── core.ts │ │ ├── doc.ts │ │ ├── errors.ts │ │ ├── function.ts │ │ ├── index.ts │ │ ├── json-schema.ts │ │ ├── parse.ts │ │ ├── regexes.ts │ │ ├── registries.ts │ │ ├── schemas.ts │ │ ├── standard-schema.ts │ │ ├── tests │ │ │ ├── index.test.ts │ │ │ └── locales │ │ │ │ ├── be.test.ts │ │ │ │ ├── en.test.ts │ │ │ │ ├── ru.test.ts │ │ │ │ └── tr.test.ts │ │ ├── to-json-schema.ts │ │ ├── util.ts │ │ ├── versions.ts │ │ └── zsf.ts │ │ ├── index.ts │ │ ├── locales │ │ ├── ar.ts │ │ ├── az.ts │ │ ├── be.ts │ │ ├── ca.ts │ │ ├── cs.ts │ │ ├── de.ts │ │ ├── en.ts │ │ ├── es.ts │ │ ├── fa.ts │ │ ├── fi.ts │ │ ├── fr-CA.ts │ │ ├── fr.ts │ │ ├── he.ts │ │ ├── hu.ts │ │ ├── id.ts │ │ ├── index.ts │ │ ├── it.ts │ │ ├── ja.ts │ │ ├── kh.ts │ │ ├── ko.ts │ │ ├── mk.ts │ │ ├── ms.ts │ │ ├── nl.ts │ │ ├── no.ts │ │ ├── ota.ts │ │ ├── pl.ts │ │ ├── pt.ts │ │ ├── ru.ts │ │ ├── sl.ts │ │ ├── sv.ts │ │ ├── ta.ts │ │ ├── th.ts │ │ ├── tr.ts │ │ ├── ua.ts │ │ ├── ur.ts │ │ ├── vi.ts │ │ ├── zh-CN.ts │ │ └── zh-TW.ts │ │ └── mini │ │ ├── checks.ts │ │ ├── coerce.ts │ │ ├── external.ts │ │ ├── index.ts │ │ ├── iso.ts │ │ ├── parse.ts │ │ ├── schemas.ts │ │ └── tests │ │ ├── assignability.test.ts │ │ ├── brand.test.ts │ │ ├── checks.test.ts │ │ ├── computed.test.ts │ │ ├── error.test.ts │ │ ├── functions.test.ts │ │ ├── index.test.ts │ │ ├── number.test.ts │ │ ├── object.test.ts │ │ ├── prototypes.test.ts │ │ ├── recursive-types.test.ts │ │ └── string.test.ts │ ├── tsconfig.cjs.json │ ├── tsconfig.esm.json │ ├── tsconfig.json │ ├── tsconfig.types.json │ ├── v3 │ ├── index.d.ts │ └── index.js │ ├── v4-mini │ ├── index.d.ts │ └── index.js │ ├── v4 │ ├── core │ │ ├── index.d.ts │ │ └── index.js │ ├── index.d.ts │ ├── index.js │ └── locales │ │ ├── en.d.ts │ │ ├── en.js │ │ ├── index.d.ts │ │ └── index.js │ └── vitest.config.ts ├── play.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── rfcs └── index.md ├── scripts └── semver-check.ts ├── tea.yaml ├── tsconfig.json ├── vitest.root.mts └── vitest.workspace.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.configs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from "@rollup/plugin-commonjs"; 2 | import resolve from "@rollup/plugin-node-resolve"; 3 | import typescript from "@rollup/plugin-typescript"; 4 | import filesize from "rollup-plugin-filesize"; 5 | 6 | /** @type {import('rollup').RollupOptions} */ 7 | export default { 8 | input: "./scratch/input.ts", // Your TypeScript entry file 9 | output: { 10 | file: "./scratch/out_rollup.js", // Output file 11 | format: "esm", // ES module format to enable tree-shaking 12 | // sourcemap: true, // Generate sourcemaps for easier debugging 13 | }, 14 | plugins: [ 15 | resolve(), // Resolve node_modules 16 | commonjs(), // Convert CommonJS modules to ES6 17 | typescript(), // Compile TypeScript 18 | filesize(), // Display bundle size 19 | ], 20 | treeshake: { 21 | preset: "smallest", 22 | // preset: "recommended", 23 | annotations: true, 24 | }, // Enable tree-shaking 25 | }; 26 | -------------------------------------------------------------------------------- /.configs/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["es6", "dom"], 5 | "module": "NodeNext", 6 | "moduleResolution": "NodeNext", 7 | "jsx": "react", 8 | 9 | "strict": true, 10 | "alwaysStrict": true, 11 | 12 | "exactOptionalPropertyTypes": true, 13 | "skipLibCheck": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": true, 17 | "noImplicitAny": true, 18 | "noImplicitReturns": true, 19 | "noImplicitThis": true, 20 | "noImplicitOverride": true, 21 | 22 | "noFallthroughCasesInSwitch": true, 23 | "resolveJsonModule": true, 24 | 25 | "removeComments": false, 26 | "esModuleInterop": true, 27 | "emitDecoratorMetadata": true, 28 | "experimentalDecorators": true, 29 | "downlevelIteration": true, 30 | "isolatedModules": true, 31 | 32 | "pretty": true, 33 | "customConditions": ["@zod/source"] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node 3 | { 4 | "name": "Node.js & TypeScript", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye", 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | // "features": {}, 10 | 11 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 12 | // "forwardPorts": [], 13 | 14 | // Use 'postCreateCommand' to run commands after the container is created. 15 | "postCreateCommand": "pnpm i" 16 | 17 | // Configure tool-specific properties. 18 | // "customizations": {}, 19 | 20 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 21 | // "remoteUser": "root" 22 | } 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | charset = utf-8 13 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | branches: 9 | - main 10 | - v4 11 | 12 | jobs: 13 | test-node: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | node: ["latest"] 18 | typescript: ["5.5", "latest"] 19 | name: Test with TypeScript ${{ matrix.typescript }} on Node ${{ matrix.node }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node }} 25 | - uses: pnpm/action-setup@v4 26 | - run: pnpm install 27 | - run: pnpm add typescript@${{ matrix.typescript }} -w 28 | - run: pnpm build 29 | - run: pnpm test 30 | 31 | lint: 32 | runs-on: ubuntu-latest 33 | strategy: 34 | matrix: 35 | node: ["latest"] 36 | name: Lint on Node ${{ matrix.node }} 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: actions/setup-node@v4 40 | with: 41 | node-version: ${{ matrix.node }} 42 | - uses: pnpm/action-setup@v4 43 | - run: pnpm install 44 | - run: pnpm format:check 45 | - run: pnpm lint:check 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | node_modules 3 | /lib 4 | /__buildtest__ 5 | coverage 6 | .idea 7 | *.log 8 | playground.ts 9 | scratch 10 | workspace.code-workspace 11 | .netlify 12 | bun.lockb 13 | out.png 14 | logos 15 | .zed 16 | dist 17 | .tshy 18 | .tshy-build 19 | packages/tsc-perf 20 | .pnpm-store 21 | 22 | tsconfig.vitest-temp.json 23 | *.tgz 24 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm semver-check 5 | npx lint-staged --verbose 6 | 7 | 8 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm test 5 | 6 | pnpm semver-check 7 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/.nojekyll -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | link-workspace-packages=true 2 | # prefer-workspace-packages=true 3 | hoist-workspace-packages=false 4 | save-workspace-protocol=false 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "runtimeExecutable": "node", 10 | "request": "launch", 11 | "name": "node", 12 | "args": [], 13 | "runtimeArgs": ["--experimental-strip-types"], 14 | "skipFiles": ["/**"], 15 | // "program": "${workspaceFolder}/play.ts", 16 | "program": "${file}", 17 | "console": "integratedTerminal", 18 | "internalConsoleOptions": "neverOpen" 19 | 20 | // "outFiles": ["${workspaceFolder}/**/*.js"] 21 | }, 22 | { 23 | "name": "tsx", 24 | "type": "node", 25 | "request": "launch", 26 | "runtimeArgs": ["--conditions=@zod/source"], 27 | 28 | // Debug current file in VSCode 29 | "program": "${file}", 30 | 31 | /* 32 | * Path to tsx binary 33 | * Assuming locally installed 34 | */ 35 | "runtimeExecutable": "tsx", 36 | 37 | /* 38 | * Open terminal when debugging starts (Optional) 39 | * Useful to see console.logs 40 | */ 41 | "console": "integratedTerminal", 42 | "internalConsoleOptions": "neverOpen", 43 | 44 | // Files to exclude from debugger (e.g. call stack) 45 | "skipFiles": [ 46 | // Node.js internal core modules 47 | "/**", 48 | 49 | // Ignore all dependencies (optional) 50 | "${workspaceFolder}/node_modules/**" 51 | ] 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib", 3 | "typescript.validate.enable": true, 4 | "typescript.tsserver.experimental.enableProjectDiagnostics": true, 5 | "editor.defaultFormatter": "biomejs.biome", 6 | "editor.formatOnSave": true, 7 | "editor.codeActionsOnSave": { 8 | "source.organizeImports.biome": "explicit", 9 | "source.fixAll": "always", 10 | "quickfix.biome": "explicit" 11 | }, 12 | "files.exclude": { 13 | "./scratch": false, 14 | "**/.tshy-build": true, 15 | "**/dist": true, 16 | "**/lib": true 17 | }, 18 | "search.useIgnoreFiles": false, 19 | "editor.guides.indentation": false 20 | } 21 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: colinhacks 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Colin McDonnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | packages/zod/README.md -------------------------------------------------------------------------------- /biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | 4 | "formatter": { 5 | "enabled": true, 6 | "indentStyle": "space", 7 | "lineWidth": 120 8 | }, 9 | "javascript": { 10 | "formatter": { 11 | "trailingCommas": "es5" 12 | } 13 | }, 14 | "json": { 15 | "formatter": { 16 | "trailingCommas": "none" 17 | } 18 | }, 19 | "linter": { 20 | "enabled": true, 21 | "rules": { 22 | "recommended": true, 23 | "suspicious": { 24 | "noExplicitAny": "off", // `any` is amazing 25 | "noUnsafeDeclarationMerging": "off", 26 | "noMisleadingInstantiator": "off", 27 | "noEmptyInterface": "off", 28 | "noConfusingVoidType": "off", 29 | "noThenProperty": "off" 30 | }, 31 | "style": { 32 | "noUnusedTemplateLiteral": "off", // why is this even a best practice? 33 | "noParameterAssign": "off", // required for performant coercion in _parse 34 | "noNonNullAssertion": "off", 35 | "useTemplate": "off", 36 | "noUselessElse": "off" 37 | }, 38 | "correctness": { 39 | "noUnusedImports": { 40 | "level": "error", 41 | "fix": "none" 42 | }, 43 | "noUnusedVariables": { 44 | "level": "warn", 45 | "fix": "none" 46 | } 47 | }, 48 | "complexity": { 49 | "noUselessConstructor": "off", 50 | "noBannedTypes": "off", 51 | "useArrowFunction": "off", 52 | "useLiteralKeys": { 53 | "fix": "unsafe", 54 | "level": "error" 55 | } 56 | }, 57 | "performance": { 58 | "noDelete": "off" 59 | } 60 | } 61 | }, 62 | "files": { 63 | "ignore": [ 64 | "lib", 65 | "coverage", 66 | "dist", 67 | ".tshy", 68 | ".tshy-build", 69 | "experiments", 70 | "node_modules", 71 | ".next", 72 | "packages/bench", 73 | "packages/treeshake", 74 | ".source" 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /logo/Logo Black.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Black.ai -------------------------------------------------------------------------------- /logo/Logo Black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Black.jpg -------------------------------------------------------------------------------- /logo/Logo Black.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Black.pdf -------------------------------------------------------------------------------- /logo/Logo Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Black.png -------------------------------------------------------------------------------- /logo/Logo Outer Glow.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Outer Glow.ai -------------------------------------------------------------------------------- /logo/Logo Outer Glow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Outer Glow.jpg -------------------------------------------------------------------------------- /logo/Logo Outer Glow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Outer Glow.pdf -------------------------------------------------------------------------------- /logo/Logo Outer Glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo Outer Glow.png -------------------------------------------------------------------------------- /logo/Logo White.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo White.ai -------------------------------------------------------------------------------- /logo/Logo White.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo White.jpg -------------------------------------------------------------------------------- /logo/Logo White.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo White.pdf -------------------------------------------------------------------------------- /logo/Logo White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo White.png -------------------------------------------------------------------------------- /logo/Logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo.ai -------------------------------------------------------------------------------- /logo/Logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo.jpg -------------------------------------------------------------------------------- /logo/Logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo.pdf -------------------------------------------------------------------------------- /logo/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/logo/Logo.png -------------------------------------------------------------------------------- /packages/bench/_temp.ts: -------------------------------------------------------------------------------- 1 | import { string } from "zod/v4"; 2 | 3 | const schema = string(); 4 | const result = schema.parse("hello"); 5 | -------------------------------------------------------------------------------- /packages/bench/array.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema, randomString } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | const { zod3, zod4 } = makeSchema((z) => z.array(z.string())); 5 | const DATA = makeData(1000, () => Array.from({ length: 3 }, () => randomString(10))); 6 | 7 | // make sure they're working 8 | console.log(zod3.parse(DATA[0])); 9 | console.log(zod4.parse(DATA[0])); 10 | 11 | const bench = metabench("z.array() parsing", { 12 | zod3() { 13 | for (const _ of DATA) zod3.parse(_); 14 | }, 15 | zod4() { 16 | for (const _ of DATA) zod4.parse(_); 17 | }, 18 | }); 19 | 20 | await bench.run(); 21 | -------------------------------------------------------------------------------- /packages/bench/benchUtil.ts: -------------------------------------------------------------------------------- 1 | import * as zNew from "zod/v4"; 2 | import * as zOld from "zod3"; 3 | 4 | export function makeSchema(factory: (z: typeof zNew) => T) { 5 | return { 6 | zod3: factory(zOld as any) as T, 7 | zod4: factory(zNew as any) as T, 8 | // zod4Ts: factory(zodNewTs as any), 9 | }; 10 | } 11 | 12 | export function randomString(length: number): string { 13 | const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 14 | let result = ""; 15 | for (let i = 0; i < length; i++) { 16 | result += characters.charAt(Math.floor(Math.random() * characters.length)); 17 | } 18 | return result; 19 | } 20 | 21 | export function randomPick(options: any[]): any { 22 | return options[Math.floor(Math.random() * options.length)]; 23 | } 24 | 25 | export function makeData(count: number, factory: object | (() => any)): any[] { 26 | return Array.from({ length: count }, () => { 27 | // clone non primitive data 28 | if (typeof factory === "object") return { ...factory }; 29 | if (typeof factory === "function") return factory(); 30 | throw new Error("Invalid factory"); 31 | }); 32 | } 33 | 34 | export function formatNumber(val: number): string { 35 | if (val >= 1e12) { 36 | return `${toFixed(val / 1e12)}T`; 37 | } 38 | if (val >= 1e9) { 39 | return `${toFixed(val / 1e9)}B`; 40 | } 41 | if (val >= 1e6) { 42 | return `${toFixed(val / 1e6)}M`; 43 | } 44 | if (val >= 1e3) { 45 | return `${toFixed(val / 1e3)}k`; 46 | } 47 | if (val >= 1) { 48 | return val.toString(); 49 | } 50 | if (val >= 1e-3) { 51 | return `${toFixed(val * 1e3)}m`; 52 | } 53 | if (val >= 1e-6) { 54 | return `${toFixed(val * 1e6)}µ`; 55 | } 56 | if (val >= 1e-9) { 57 | return `${toFixed(val * 1e9)}n`; 58 | } 59 | if (val >= 1e-12) { 60 | return `${toFixed(val * 1e12)}p`; 61 | } 62 | return val.toString(); 63 | } 64 | 65 | function toFixed(val: number) { 66 | return val.toPrecision(3); 67 | } 68 | -------------------------------------------------------------------------------- /packages/bench/boolean.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | const { zod3, zod4 } = makeSchema((z) => z.boolean()); 5 | const DATA = makeData(10000, () => Math.random() > 0.5); 6 | 7 | const bench = metabench("z.boolean().parse", { 8 | zod3() { 9 | for (const _ of DATA) zod3.parse(_); 10 | }, 11 | zod4() { 12 | for (const _ of DATA) zod4.parse(_); 13 | }, 14 | }); 15 | 16 | await bench.run(); 17 | -------------------------------------------------------------------------------- /packages/bench/datetime-regex.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | 3 | const bench = metabench("datetime regex"); 4 | 5 | const DATA = "2021-01-01"; 6 | const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]); 7 | const MONTHS_30 = new Set([4, 6, 9, 11]); 8 | 9 | const simpleDatetimeRegex = /^(\d{4})-(\d{2})-(\d{2})$/; 10 | const datetimeRegexNoLeapYearValidation = 11 | /^\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\d|2\d))$/; 12 | const datetimeRegexWithLeapYearValidation = 13 | /^((\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\d|3[01])|(0[469]|11)-(0[1-9]|[12]\d|30)|(02)-(0[1-9]|1\d|2[0-8])))$/; 14 | 15 | bench 16 | .add("new Date()", () => { 17 | return !Number.isNaN(new Date(DATA).getTime()); 18 | }) 19 | .add("regex (no validation)", () => { 20 | return simpleDatetimeRegex.test(DATA); 21 | }) 22 | .add("regex (no leap year)", () => { 23 | return datetimeRegexNoLeapYearValidation.test(DATA); 24 | }) 25 | .add("regex (w/ leap year)", () => { 26 | return datetimeRegexWithLeapYearValidation.test(DATA); 27 | }) 28 | .add("capture groups + code", () => { 29 | const match = DATA.match(simpleDatetimeRegex); 30 | if (!match) return false; 31 | 32 | // Extract year, month, and day from the capture groups 33 | const year = Number.parseInt(match[1], 10); 34 | const month = Number.parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1 35 | const day = Number.parseInt(match[3], 10); 36 | 37 | if (month === 2) { 38 | if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) { 39 | return day <= 29; 40 | } 41 | return day <= 28; 42 | } 43 | if (MONTHS_30.has(month)) { 44 | return day <= 30; 45 | } 46 | if (MONTHS_31.has(month)) { 47 | return day <= 31; 48 | } 49 | return false; 50 | }); 51 | 52 | await bench.run(); 53 | -------------------------------------------------------------------------------- /packages/bench/datetime.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | const { zod3, zod4 } = makeSchema((z) => z.string().datetime()); 5 | const DATA = makeData(10000, () => new Date().toISOString()); 6 | 7 | const bench = metabench("z.string().datetime().parse", { 8 | zod3() { 9 | for (const _ of DATA) zod3.parse(_); 10 | }, 11 | zod4() { 12 | for (const _ of DATA) zod4.parse(_); 13 | }, 14 | }); 15 | 16 | await bench.run(); 17 | -------------------------------------------------------------------------------- /packages/bench/error-handling.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | import * as z4 from "zod/v4"; 5 | import * as z from "zod"; 6 | 7 | const a = z4.object({ a: z4.string() }); 8 | const b = z.object({ a: z.string() }); 9 | 10 | const DATA = makeData(10000, () => ({ b: `${Math.random()}` })); 11 | 12 | const bench = metabench("safeparse error", { 13 | zod4() { 14 | for (const _ of DATA) { 15 | try{a.safeParse(_);} catch(e){e;} 16 | } 17 | }, 18 | zod4new() { 19 | for (const _ of DATA) { 20 | try{b.safeParse(_);} catch(e){e;} 21 | } 22 | } 23 | }); 24 | 25 | await bench.run(); 26 | -------------------------------------------------------------------------------- /packages/bench/index.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | 5 | async function run() { 6 | const files = process.argv[2].split(",").map((file) => import.meta.resolve(`./${file}`).replace("file://", "")); 7 | 8 | for (const file of files) { 9 | await $`pnpm tsx ${file}`; 10 | } 11 | } 12 | 13 | run(); 14 | 15 | // exit on Ctrl-C 16 | process.on("SIGINT", () => { 17 | console.log("Exiting..."); 18 | process.exit(); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/bench/ipv4-regex.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | 3 | const DATA = "127.0.0.1"; 4 | const ipv4RegexA = 5 | /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/; 6 | const ipv4RegexB = 7 | /^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/; 8 | const ipv4RegexC = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/; 9 | const ipv4RegexD = /^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/; 10 | const ipv4RegexE = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/; 11 | const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; 12 | const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/; 13 | const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/; 14 | const ipv4RegexI = 15 | /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; 16 | 17 | const bench = metabench("ipv4 regex") 18 | .add("A", () => { 19 | return ipv4RegexA.test(DATA); 20 | }) 21 | .add("B", () => { 22 | return ipv4RegexB.test(DATA); 23 | }) 24 | .add("C", () => { 25 | return ipv4RegexC.test(DATA); 26 | }) 27 | .add("D", () => { 28 | return ipv4RegexD.test(DATA); 29 | }) 30 | .add("E", () => { 31 | return ipv4RegexE.test(DATA); 32 | }) 33 | .add("F", () => { 34 | return ipv4RegexF.test(DATA); 35 | }) 36 | .add("G", () => { 37 | return ipv4RegexG.test(DATA); 38 | }) 39 | .add("H", () => { 40 | return ipv4RegexH.test(DATA); 41 | }) 42 | .add("I", () => { 43 | return ipv4RegexI.test(DATA); 44 | }); 45 | 46 | await bench.run(); 47 | -------------------------------------------------------------------------------- /packages/bench/jit-union.ts: -------------------------------------------------------------------------------- 1 | import { makeData, randomPick, randomString } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | import * as z4 from "zod/v4-mini"; 5 | 6 | const z4fields = { 7 | data1: z4.string(), 8 | data2: z4.string(), 9 | data3: z4.string(), 10 | data4: z4.string(), 11 | data5: z4.string(), 12 | data6: z4.string(), 13 | data7: z4.string(), 14 | data8: z4.string(), 15 | data9: z4.string(), 16 | data10: z4.string(), 17 | } 18 | const z4Union = z4.union([ 19 | z4.object({ 20 | type: z4.literal("a"), 21 | ...z4fields 22 | }), 23 | z4.object({ 24 | type: z4.literal("b"), 25 | ...z4fields 26 | }), 27 | z4.object({ 28 | type: z4.literal("c"), 29 | ...z4fields 30 | }), 31 | z4.object({ 32 | type: z4.literal("d"), 33 | ...z4fields 34 | }), 35 | z4.object({ 36 | type: z4.literal("e"), 37 | ...z4fields 38 | }), 39 | z4.object({ 40 | type: z4.literal("f"), 41 | ...z4fields 42 | }), 43 | z4.object({ 44 | type: z4.literal("g"), 45 | ...z4fields 46 | }), 47 | ]); 48 | 49 | 50 | 51 | const DATA = makeData(100, () => ({ 52 | type: randomPick([ 53 | "a", 54 | "b", 55 | "c", 56 | "d", 57 | "e", 58 | "f", 59 | "g" 60 | ]), 61 | data1: randomString(10), 62 | data2: randomString(10), 63 | data3: randomString(10), 64 | data4: randomString(10), 65 | data5: randomString(10), 66 | data6: randomString(10), 67 | data7: randomString(10), 68 | data8: randomString(10), 69 | data9: randomString(10), 70 | data10: randomString(10), 71 | })); 72 | 73 | 74 | const args= {jitless: true} 75 | console.dir(z4Union.parse(DATA[0]), {depth: null}); 76 | console.dir(z4Union.parse(DATA[0], args), {depth: null}); 77 | const bench = metabench("z.discriminatedUnion().parse", { 78 | 79 | v4_jit() { 80 | for (const item of DATA) { 81 | z4Union.parse(item); 82 | } 83 | }, 84 | v4_jitless() { 85 | for (const item of DATA) { 86 | z4Union.parse(item,args); 87 | } 88 | }, 89 | 90 | }) 91 | 92 | await bench.run(); 93 | -------------------------------------------------------------------------------- /packages/bench/key-iteration.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | 3 | const DATA_WITH_SYMBOLS = Array.from({ length: 1000 }, () => { 4 | return { 5 | [Symbol.for("comet")]: 0, 6 | str: 0, 7 | 773: 0, 8 | 0: 0, 9 | "-1": 0, 10 | 8: 0, 11 | "second str": 0, 12 | }; 13 | }); 14 | 15 | const DATA_WITHOUT_SYMBOLS = Array.from({ length: 1000 }, () => { 16 | return { 17 | str: 0, 18 | 773: 0, 19 | 0: 0, 20 | "-1": 0, 21 | 8: 0, 22 | "second str": 0, 23 | }; 24 | }); 25 | 26 | let temp: any; 27 | const bench = metabench("key iteration", { 28 | for_in_no_symbols() { 29 | for (const d of DATA_WITHOUT_SYMBOLS) { 30 | for (const k in d) temp = k; 31 | } 32 | }, 33 | reflect_no_symbols() { 34 | for (const d of DATA_WITHOUT_SYMBOLS) { 35 | for (const k of Reflect.ownKeys(d)) temp = k; 36 | } 37 | }, 38 | for_in_with_symbols() { 39 | for (const d of DATA_WITH_SYMBOLS) { 40 | for (const k in d) temp = k; 41 | } 42 | }, 43 | reflect_with_symbols() { 44 | for (const d of DATA_WITH_SYMBOLS) { 45 | for (const k of Reflect.ownKeys(d)) temp = k; 46 | } 47 | }, 48 | }); 49 | 50 | await bench.run(); 51 | -------------------------------------------------------------------------------- /packages/bench/lazy-box.ts: -------------------------------------------------------------------------------- 1 | import { randomString } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | export function lazyWithInternalProp(getter: () => T) { 5 | return { 6 | __value: undefined as T, 7 | get value() { 8 | if (this.__value) return this.__value; 9 | const value = getter(); 10 | this.__value = value; 11 | return value; 12 | }, 13 | }; 14 | } 15 | 16 | export function lazyWithScopeProp(getter: () => T): { 17 | value: T; 18 | __value?: T; 19 | } { 20 | let __value: T; 21 | return { 22 | get value() { 23 | if (__value) return __value; 24 | const value = getter(); 25 | __value = value; 26 | return value; 27 | }, 28 | }; 29 | } 30 | 31 | export function lazyWithGetterOverride(getter: () => T): { 32 | value: T; 33 | } { 34 | return { 35 | get value() { 36 | const value = getter(); 37 | Object.defineProperty(this, "value", { value }); 38 | return value; 39 | }, 40 | }; 41 | } 42 | 43 | const a = lazyWithInternalProp(() => randomString(1000)); 44 | const b = lazyWithScopeProp(() => randomString(1000)); 45 | const c = lazyWithGetterOverride(() => randomString(1000)); 46 | 47 | const bench = metabench("lazy box", { 48 | internal_prop() { 49 | a.value; 50 | }, 51 | scope_prop() { 52 | b.value; 53 | }, 54 | getter_override() { 55 | c.value; 56 | }, 57 | }); 58 | 59 | await bench.run(); 60 | -------------------------------------------------------------------------------- /packages/bench/libs.ts: -------------------------------------------------------------------------------- 1 | import { makeData, randomString } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | import { type } from "arktype"; 5 | import * as v from "valibot"; 6 | import * as z from "zod/v4"; 7 | 8 | const schema = z.object({ 9 | a: z.string(), 10 | b: z.string(), 11 | c: z.string(), 12 | }); 13 | 14 | const atschema = type({ 15 | a: "string", 16 | b: "string", 17 | c: "string", 18 | }); 19 | 20 | const vschema = v.object({ 21 | a: v.string(), 22 | b: v.string(), 23 | c: v.string(), 24 | }); 25 | 26 | const DATA = makeData(1000, () => ({ 27 | a: randomString(10), 28 | b: randomString(10), 29 | c: randomString(10), 30 | })); 31 | 32 | console.log(z.parse(schema, DATA[0])); 33 | console.log(atschema(DATA[0])); 34 | console.log(v.parse(vschema, DATA[0])); 35 | 36 | const bench = metabench("zod vs arktype", { 37 | zod4() { 38 | for (const _ of DATA) z.parse(schema, _); 39 | }, 40 | arktype() { 41 | for (const _ of DATA) atschema(_); 42 | }, 43 | valibot() { 44 | for (const _ of DATA) v.parse(vschema, _); 45 | }, 46 | // baseline() { 47 | // for (const _ of DATA) { 48 | // typeof _ === "object" && 49 | // _ !== null && 50 | // typeof _.a === "string" && 51 | // typeof _.b === "string" && 52 | // typeof _.c === "string"; 53 | // } 54 | // }, 55 | }); 56 | 57 | await bench.run(); 58 | -------------------------------------------------------------------------------- /packages/bench/number.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | const { zod3, zod4 } = makeSchema((z) => z.number()); 5 | 6 | const DATA = makeData(10000, () => Math.random()); 7 | const bench = metabench("z.number().parse", { 8 | zod3() { 9 | for (const _ of DATA) zod3.parse(_); 10 | }, 11 | zod4() { 12 | for (const _ of DATA) zod4.parse(_); 13 | }, 14 | }); 15 | 16 | await bench.run(); 17 | -------------------------------------------------------------------------------- /packages/bench/object-async.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | import { DATA, zod3, zod4 } from "./object-setup.js"; 3 | 4 | const bench = metabench("small: z.object().parseAsync", { 5 | async zod3() { 6 | for (const _ of DATA) await zod3.parseAsync(_); 7 | }, 8 | async zod4() { 9 | for (const _ of DATA) await zod4.parseAsync(_); 10 | }, 11 | }); 12 | 13 | await bench.run(); 14 | -------------------------------------------------------------------------------- /packages/bench/object-creation.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | 3 | class ZodFail { 4 | status = "fail"; 5 | constructor(public value: string) {} 6 | } 7 | 8 | const bench = metabench("object creation") 9 | .add("raw object", () => { 10 | const obj = { status: "fail", value: "this is a test" }; 11 | obj.value; 12 | }) 13 | .add("constructor", () => { 14 | const obj = new ZodFail("this is a test"); 15 | obj.value; 16 | }); 17 | 18 | await bench.run(); 19 | -------------------------------------------------------------------------------- /packages/bench/object-fail.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | import { DATA, zod3, zod4 } from "./object-setup.js"; 3 | 4 | const bench = metabench("small: z.object().safeParseAsync", { 5 | zod3() { 6 | for (const _ of DATA) zod3.parse(_); 7 | }, 8 | zod4() { 9 | for (const _ of DATA) zod4.parse(_); 10 | }, 11 | }); 12 | 13 | await bench.run(); 14 | -------------------------------------------------------------------------------- /packages/bench/object-safe.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | import { DATA, zod3, zod4 } from "./object-setup.js"; 3 | 4 | const bench = metabench("small: z.object().safeParse", { 5 | zod3() { 6 | for (const _ of DATA) zod3.safeParse(_); 7 | }, 8 | zod4() { 9 | for (const _ of DATA) zod4.safeParse(_); 10 | }, 11 | }); 12 | 13 | await bench.run(); 14 | -------------------------------------------------------------------------------- /packages/bench/object-safeasync.ts: -------------------------------------------------------------------------------- 1 | import { metabench } from "./metabench.js"; 2 | import { DATA, zod3, zod4 } from "./object-setup.js"; 3 | 4 | const bench = metabench("small: z.object().safeParseAsync", { 5 | async zod3() { 6 | for (const _ of DATA) await zod3.spa(_); 7 | }, 8 | async zod4() { 9 | for (const _ of DATA) await zod4.spa(_); 10 | }, 11 | }); 12 | 13 | await bench.run(); 14 | -------------------------------------------------------------------------------- /packages/bench/object-setup.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema } from "./benchUtil.js"; 2 | // import { metabench } from "./metabench.js"; 3 | 4 | export const { zod3, zod4 } = makeSchema((z) => 5 | z.object({ 6 | string: z.string(), 7 | boolean: z.boolean(), 8 | number: z.number(), 9 | }) 10 | ); 11 | 12 | // biome-ignore lint/style/noVar: 13 | // biome-ignore lint/correctness/noInnerDeclarations: 14 | export var DATA: any[] = makeData(1000, () => { 15 | return Object.freeze({ 16 | number: Math.random(), 17 | string: `${Math.random()}`, 18 | boolean: Math.random() > 0.5, 19 | }); 20 | }); 21 | 22 | // const bench = metabench("small: z.object().parse", { 23 | // zod3() { 24 | // for (const d of DATA) { 25 | // zod3.parse(d); 26 | // } 27 | // }, 28 | // zod4() { 29 | // for (const d of DATA) { 30 | // zod4.parse(d); 31 | // } 32 | // }, 33 | // }); 34 | 35 | // await bench.run(); 36 | -------------------------------------------------------------------------------- /packages/bench/object.ts: -------------------------------------------------------------------------------- 1 | import * as z4 from "zod/v4"; 2 | import * as z3 from "zod3"; 3 | import { metabench } from "./metabench.js"; 4 | import * as v from "valibot"; 5 | import { type } from 'arktype'; 6 | 7 | const z3Schema = z3.object({ 8 | string: z3.string(), 9 | boolean: z3.boolean(), 10 | number: z3.number(), 11 | }); 12 | 13 | const z4Schema = z4.object({ 14 | string: z4.string(), 15 | boolean: z4.boolean(), 16 | number: z4.number(), 17 | }); 18 | 19 | const valibotSchema = v.object({ 20 | string: v.string(), 21 | boolean: v.boolean(), 22 | number: v.number(), 23 | }); 24 | 25 | const DATA = Array.from({ length: 1000 }, () => 26 | Object.freeze({ 27 | number: Math.random(), 28 | string: `${Math.random()}`, 29 | boolean: Math.random() > 0.5, 30 | }) 31 | ); 32 | 33 | console.log(z3Schema.parse(DATA[0])); 34 | console.log(z4Schema.parse(DATA[0])); 35 | console.log(v.parse(valibotSchema, DATA[0])); 36 | 37 | 38 | const bench = metabench("z.object().parse", { 39 | zod3() { 40 | for (const d of DATA) z3Schema.parse(d); 41 | }, 42 | zod4() { 43 | for (const d of DATA) z4Schema.parse(d); 44 | }, 45 | valibot() { 46 | for (const d of DATA) v.parse(valibotSchema, d); 47 | }, 48 | }); 49 | 50 | await bench.run(); 51 | -------------------------------------------------------------------------------- /packages/bench/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zod/benchmarks", 3 | "private": true, 4 | "type": "module", 5 | "devDependencies": { 6 | "arktype": "^2.1.19", 7 | "valibot": "^1.0.0", 8 | "zod": "workspace:*", 9 | "zodnext": "npm:zod@^3.25.0", 10 | "zod3": "npm:zod@^3.23.7" 11 | }, 12 | "scripts": { 13 | "bench": "tsx --conditions @zod/source index.ts" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/bench/property-access.ts: -------------------------------------------------------------------------------- 1 | import { randomString } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | export const objA = { 5 | value: randomString(1000), 6 | }; 7 | 8 | // with Proxy, no get trap 9 | export const objB = new Proxy( 10 | { 11 | value: randomString(1000), 12 | }, 13 | {} 14 | ); 15 | 16 | // with Proxy, get/set trap 17 | export const objC = new Proxy( 18 | { 19 | value: randomString(1000), 20 | }, 21 | { 22 | get(target, prop) { 23 | return (target as any)[prop]; 24 | }, 25 | set(target, prop, value) { 26 | (target as any)[prop] = value; 27 | return true; 28 | }, 29 | } 30 | ); 31 | 32 | // with Proxy, get/set trap, use Reflect 33 | export const objD = new Proxy( 34 | { 35 | value: randomString(1000), 36 | }, 37 | { 38 | get(target, prop) { 39 | return Reflect.get(target, prop); 40 | }, 41 | set(target, prop, value) { 42 | return Reflect.set(target, prop, value); 43 | }, 44 | } 45 | ); 46 | 47 | // getter 48 | const r = randomString(1000); 49 | export const objE = { 50 | get value() { 51 | return r; 52 | }, 53 | }; 54 | 55 | const bench = metabench("property access", { 56 | // internal_prop() { 57 | // a.value; 58 | // }, 59 | // scope_prop() { 60 | // b.value; 61 | // }, 62 | // getter_override() { 63 | // c.value; 64 | // }, 65 | 66 | objB() { 67 | objB.value; 68 | }, 69 | objC() { 70 | objC.value; 71 | }, 72 | objD() { 73 | objD.value; 74 | }, 75 | objE() { 76 | objE.value; 77 | }, 78 | objA() { 79 | objA.value; 80 | }, 81 | objNone() {}, 82 | }); 83 | 84 | await bench.run(); 85 | -------------------------------------------------------------------------------- /packages/bench/safe.ts: -------------------------------------------------------------------------------- 1 | import { randomString } from "./benchUtil.js"; 2 | import { benchWithData } from "./metabench.js"; 3 | 4 | class ZodFail { 5 | status = "fail"; 6 | constructor(public value: string) { 7 | // super(); 8 | } 9 | } 10 | 11 | function makeSuccess(value: unknown) { 12 | return { status: "success", value }; 13 | } 14 | function makeFail(value: unknown) { 15 | return { status: "success", value }; 16 | } 17 | 18 | const bench = benchWithData({ 19 | name: "safe return", 20 | data() { 21 | return Math.random() > 0.5 ? randomString(15) : Math.random(); 22 | }, 23 | batch: 1000, 24 | benchmarks: { 25 | baseline(d) { 26 | return typeof d !== "string"; 27 | }, 28 | 29 | discUnion(d) { 30 | if (typeof d !== "string") { 31 | return makeFail(d); 32 | // biome-ignore lint: bug in biome 33 | } else { 34 | return makeSuccess(d); 35 | } 36 | }, 37 | union(d) { 38 | if (typeof d !== "string") { 39 | return new ZodFail("too big"); 40 | // biome-ignore lint: bug in biome 41 | } else { 42 | return d; 43 | } 44 | }, 45 | }, 46 | }); 47 | // .add("union", function () { 48 | // // const value = Math.random(); 49 | 50 | // if (typeof d !== "string") { 51 | // return new ZodFail("too big"); 52 | // // biome-ignore lint: bug in biome 53 | // } else { 54 | // } 55 | // }) 56 | // .add("disc union", (d) => { 57 | // if (typeof d !== "string") { 58 | // return makeFail(d); 59 | // // biome-ignore lint: bug in biome 60 | // } else { 61 | // return makeSuccess(d); 62 | // } 63 | // }) 64 | // .add("baseline", (d) => { 65 | // return typeof d !== "string"; 66 | // }); 67 | 68 | await bench.run(); 69 | -------------------------------------------------------------------------------- /packages/bench/safeparse.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | 5 | 6 | const schema = z.object({ 7 | string: z.string(), 8 | boolean: z.boolean(), 9 | number: z.number(), 10 | }); 11 | 12 | 13 | const DATA = Array.from({ length: 1000 }, () => 14 | Object.freeze({ 15 | // number: Math.random(), 16 | // string: `${Math.random()}`, 17 | // boolean: Math.random() > 0.5, 18 | }) 19 | ); 20 | 21 | // console.log(z3Schema.parse(DATA[0])); 22 | console.log(schema.safeParse(DATA[0])); 23 | // console.log(v.parse(valibotSchema, DATA[0])); 24 | 25 | const bench = metabench("safeparse vs parse — fail", { 26 | parse() { 27 | for (const d of DATA) { 28 | try{ 29 | const result = schema.parse(d); 30 | }catch(e){ 31 | e; 32 | } 33 | } 34 | }, 35 | safeparse() { 36 | for (const d of DATA) { 37 | const result = schema.safeParse(d); 38 | result.error; 39 | } 40 | } 41 | 42 | }); 43 | 44 | await bench.run(); 45 | -------------------------------------------------------------------------------- /packages/bench/string.ts: -------------------------------------------------------------------------------- 1 | import { makeData, makeSchema } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | const { zod3, zod4 } = makeSchema((z) => z.string()); 5 | 6 | const DATA = makeData(10000, () => `${Math.random()}`); 7 | const bench = metabench("z.string().parse", { 8 | zod3() { 9 | for (const _ of DATA) zod3.parse(_); 10 | }, 11 | zod4() { 12 | for (const _ of DATA) zod4.parse(_); 13 | }, 14 | }); 15 | 16 | await bench.run(); 17 | -------------------------------------------------------------------------------- /packages/bench/tsconfig.bench.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.configs/tsconfig.base.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "extendedDiagnostics": true, 6 | "traceResolution": true, 7 | "customConditions": [], 8 | "skipDefaultLibCheck": true, 9 | "skipLibCheck": true, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/bench/union.ts: -------------------------------------------------------------------------------- 1 | import { makeSchema } from "./benchUtil.js"; 2 | import { metabench } from "./metabench.js"; 3 | 4 | const { zod3, zod4 } = makeSchema((z) => { 5 | const aSchema = z.object({ 6 | type: z.literal("a"), 7 | }); 8 | 9 | const bSchema = z.object({ 10 | type: z.literal("b"), 11 | }); 12 | 13 | const cSchema = z.object({ 14 | type: z.literal("c"), 15 | }); 16 | 17 | return z.union([aSchema, bSchema, cSchema]); 18 | }); 19 | 20 | const DATA = { type: "c" }; 21 | const bench = metabench("z.union().parse") 22 | .add("zod3", () => { 23 | zod3.parse(DATA); 24 | }) 25 | .add("zod4", () => { 26 | zod4.parse(DATA); 27 | }); 28 | 29 | await bench.run(); 30 | -------------------------------------------------------------------------------- /packages/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts 29 | 30 | .env 31 | -------------------------------------------------------------------------------- /packages/docs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "biomejs.biome", 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.organizeImports.biome": "explicit", 6 | "source.fixAll": "always", 7 | "quickfix.biome": "explicit" 8 | }, 9 | "editor.wordWrap": "wordWrapColumn", 10 | "editor.wordWrapColumn": 120, 11 | "[mdx]": { 12 | "editor.defaultFormatter": "unifiedjs.vscode-mdx", 13 | "editor.formatOnSave": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/docs/README.md: -------------------------------------------------------------------------------- 1 | # onepager-fuma 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/docs/app/_home/layout.tsx: -------------------------------------------------------------------------------- 1 | import { baseOptions } from "@/app/layout.config"; 2 | import { HomeLayout } from "fumadocs-ui/layouts/home"; 3 | import type { ReactNode } from "react"; 4 | 5 | export default function Layout({ children }: { children: ReactNode }) { 6 | return {children}; 7 | } 8 | -------------------------------------------------------------------------------- /packages/docs/app/_home/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | export default function HomePage() { 4 | return ( 5 |
6 |

Hello World

7 |

8 | You can open{" "} 9 | 10 | /docs 11 | {" "} 12 | and see the documentation. 13 |

14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/docs/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { source } from "@/loaders/source"; 2 | import { createFromSource } from "fumadocs-core/search/server"; 3 | 4 | export const { GET } = createFromSource(source); 5 | -------------------------------------------------------------------------------- /packages/docs/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/app/apple-icon.png -------------------------------------------------------------------------------- /packages/docs/app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/app/icon.png -------------------------------------------------------------------------------- /packages/docs/app/layout.config.tsx: -------------------------------------------------------------------------------- 1 | import Logo from "@/public/logo/logo.png"; 2 | // import LogoWhite from "@/public/logo/logo-white.png"; 3 | import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; 4 | import Image from "next/image"; 5 | export const logo = ( 6 |
7 | Zod logo 8 | Zod logo 9 |
10 | ); 11 | 12 | /** 13 | * Shared layout configurations 14 | * 15 | * you can customise layouts individually from: 16 | * Home Layout: app/(home)/layout.tsx 17 | * Docs Layout: app/docs/layout.tsx 18 | */ 19 | export const baseOptions: BaseLayoutProps = { 20 | nav: { 21 | title: <>{logo}, 22 | }, 23 | // links: [ 24 | // { 25 | // text: 'Documentation', 26 | // url: '/docs', 27 | // active: 'nested-url', 28 | // }, 29 | // ], 30 | }; 31 | -------------------------------------------------------------------------------- /packages/docs/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { InkeepBubble } from "@/components/inkeep-bubble"; 2 | import InkeepSearchBox from "@/components/inkeep-search"; 3 | 4 | import "./global.css"; 5 | import Scroller from "@/components/scroller"; 6 | import { Analytics } from "@vercel/analytics/react"; 7 | import { Banner } from "fumadocs-ui/components/banner"; 8 | import { RootProvider } from "fumadocs-ui/provider"; 9 | import { Inter } from "next/font/google"; 10 | import { type ReactNode, Suspense } from "react"; 11 | 12 | const inter = Inter({ 13 | subsets: ["latin"], 14 | }); 15 | 16 | export default function Layout({ children }: { children: ReactNode }) { 17 | return ( 18 | 19 | 20 | 21 | 💎 Zod 4 is now stable!   22 | 23 | Read the announcement. 24 | 25 | 26 | 27 | 28 | 35 | {children} 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /packages/docs/app/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/app/logo.ico -------------------------------------------------------------------------------- /packages/docs/biome.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "formatter": { 4 | "enabled": true, 5 | "indentStyle": "space", 6 | "lineWidth": 120 7 | }, 8 | "javascript": { 9 | "formatter": { 10 | "trailingCommas": "es5" 11 | } 12 | }, 13 | "json": { 14 | "formatter": { 15 | "trailingCommas": "none" 16 | } 17 | }, 18 | "linter": { 19 | "enabled": true, 20 | "rules": { 21 | "recommended": true, 22 | "suspicious": { 23 | "noExplicitAny": "off", // `any` is amazing 24 | "noUnsafeDeclarationMerging": "off", 25 | "noMisleadingInstantiator": "off", 26 | "noEmptyInterface": "off", 27 | "noConfusingVoidType": "off", 28 | "noThenProperty": "off" 29 | }, 30 | "style": { 31 | "noUnusedTemplateLiteral": "off", // why is this even a best practice? 32 | "noParameterAssign": "off", // required for performant coercion in _parse 33 | "noNonNullAssertion": "off", 34 | "useTemplate": "off", 35 | "noUselessElse": "off" 36 | }, 37 | "correctness": { 38 | "noUnusedImports": { 39 | "level": "error", 40 | "fix": "none" 41 | }, 42 | "noUnusedVariables": { 43 | "level": "warn", 44 | "fix": "none" 45 | } 46 | }, 47 | "complexity": { 48 | "noUselessConstructor": "off", 49 | "noBannedTypes": "off", 50 | "useArrowFunction": "off", 51 | "useLiteralKeys": { 52 | "fix": "unsafe", 53 | "level": "error" 54 | } 55 | }, 56 | "performance": { 57 | "noDelete": "off" 58 | }, 59 | "security": { 60 | "noDangerouslySetInnerHtml": "off", 61 | "noDangerouslySetInnerHtmlWithChildren": "off" 62 | } 63 | } 64 | }, 65 | "files": { 66 | "ignore": ["lib", "coverage", "dist", ".tshy", ".tshy-build", "experiments", ".source"] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/docs/components/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as AccordionPrimitive from "@radix-ui/react-accordion"; 4 | import { ChevronRight } from "lucide-react"; 5 | import { type ComponentPropsWithoutRef, forwardRef } from "react"; 6 | 7 | export const Accordion = forwardRef< 8 | HTMLDivElement, 9 | Omit, "value"> & { 10 | title: string; 11 | } 12 | >(({ title, className, children, ...props }, ref) => { 13 | return ( 14 | 20 | 21 | 22 | 23 | {title} 24 | 25 | 26 | 30 |
{children}
31 |
32 |
33 | ); 34 | }); 35 | 36 | Accordion.displayName = "Accordion"; 37 | -------------------------------------------------------------------------------- /packages/docs/components/featured.tsx: -------------------------------------------------------------------------------- 1 | type FeatureData = { 2 | name: string; 3 | link: string; 4 | lightImage: string; 5 | darkImage: string; 6 | }; 7 | 8 | export function Featured(props: { data: FeatureData }) { 9 | const { data: feature } = props; 10 | return ( 11 | <> 12 |

17 | Featured sponsor: {feature.name} 18 |

19 |
20 | 21 |
22 | 23 | 24 | {`${feature.name} 25 | 26 |
27 |
28 |

29 | Interested in featuring?{" "} 30 | 31 | Get in touch. 32 | 33 |

34 |
35 | 36 | ); 37 | } 38 | 39 | //

Featured sponsor: Fern

40 | 41 | //
42 | // 43 | // 44 | // 45 | // fern logo 46 | // 47 | // 48 | //
49 | //

Learn more about featured sponsorships

50 | //
51 | -------------------------------------------------------------------------------- /packages/docs/components/heading.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Link as LinkIcon } from "lucide-react"; 4 | import Link from "next/link"; 5 | import type { ComponentPropsWithoutRef } from "react"; 6 | 7 | type Types = "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; 8 | type HeadingProps = Omit, "as"> & { 9 | as?: T; 10 | }; 11 | 12 | export function Heading({ as, className, ...props }: HeadingProps): React.ReactElement { 13 | const As = as ?? "h1"; 14 | 15 | if (!props.id) return ; 16 | 17 | return ( 18 | 19 | { 27 | // // function __handleScroll(){ 28 | // // if id query parameter is present, scroll to the element with that id 29 | // const params = new URLSearchParams(window.location.search); 30 | // console.dir(params, { depth: null }); 31 | // const id = params.get("id"); 32 | // console.dir(params, { depth: null }); 33 | // if (id) { 34 | // console.dir(document.getElementById(id), { depth: null }); 35 | // document.getElementById(id)?.scrollIntoView(); 36 | // } 37 | // // } 38 | // }} 39 | > 40 | {props.children} 41 | 42 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /packages/docs/components/if.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export function If(props: { groupId: string; values: string[]; children: any }) { 4 | if (typeof window === "undefined") return null; 5 | // read key from localstorage 6 | const value = window.localStorage.getItem(props.groupId); 7 | // if key is in values, return children 8 | if (value && props.values.includes(value)) { 9 | return props.children; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/components/inkeep-bubble.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { InkeepChatButton, type InkeepChatButtonProps } from "@inkeep/cxkit-react"; 4 | import { useEffect, useState } from "react"; 5 | 6 | export function InkeepBubble() { 7 | // color mode sync target 8 | const [syncTarget, setSyncTarget] = useState(null); 9 | 10 | // document is not available in the server 11 | useEffect(() => { 12 | setSyncTarget(document.documentElement); 13 | }, []); 14 | 15 | const config = { 16 | baseSettings: { 17 | apiKey: process.env.NEXT_PUBLIC_INKEEP_KEY!, // required 18 | primaryBrandColor: "#EE63C0", // your brand color, widget color scheme is derived from this 19 | organizationDisplayName: "Zod", 20 | // ...optional settings 21 | colorMode: { 22 | sync: { 23 | target: syncTarget!, 24 | attributes: ["class"], 25 | isDarkMode: (attributes: any) => !!attributes.class?.includes("dark"), 26 | }, 27 | }, 28 | theme: { 29 | styles: [ 30 | { 31 | key: "custom-theme", 32 | type: "style", 33 | 34 | value: ` 35 | .ikp-chat-button__container { 36 | z-index: var(--ikp-z-index-overlay); 37 | }`, 38 | }, 39 | ], 40 | }, 41 | }, 42 | searchSettings: { 43 | // optional settings 44 | }, 45 | aiChatSettings: { 46 | // optional settings 47 | 48 | aiAssistantAvatar: "https://zod.dev/logo/logo.png", // use your own ai assistant avatar 49 | exampleQuestions: [ 50 | "How do I convert a Zod schema to a JSON Schema?", 51 | "How do I define a cyclical object type?", 52 | "How do I globally customize errors?", 53 | ], 54 | }, 55 | } satisfies InkeepChatButtonProps; 56 | 57 | return ; 58 | } 59 | -------------------------------------------------------------------------------- /packages/docs/components/inkeep-search.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { InkeepModalSearchAndChat, type InkeepModalSearchAndChatProps } from "@inkeep/cxkit-react"; 4 | import type { SharedProps } from "fumadocs-ui/components/dialog/search"; 5 | import { useEffect, useState } from "react"; 6 | 7 | export default function InkeepSearchBox(props: SharedProps) { 8 | const [syncTarget, setSyncTarget] = useState(null); 9 | const { open, onOpenChange } = props; 10 | // We do this because document is not available in the server 11 | useEffect(() => { 12 | setSyncTarget(document.documentElement); 13 | }, []); 14 | 15 | // if(!syncTarget) return null; 16 | 17 | const config: InkeepModalSearchAndChatProps = { 18 | baseSettings: { 19 | apiKey: process.env.NEXT_PUBLIC_INKEEP_KEY, // required 20 | primaryBrandColor: "#EE63C0", // your brand color, widget color scheme is derived from this 21 | organizationDisplayName: "Zod", 22 | // ...optional settings 23 | colorMode: { 24 | sync: { 25 | target: syncTarget!, 26 | attributes: ["class"], 27 | isDarkMode: (attributes) => !!attributes.class?.includes("dark"), 28 | }, 29 | }, 30 | }, 31 | modalSettings: { 32 | isOpen: open, 33 | onOpenChange, 34 | // optional settings 35 | }, 36 | // searchSettings: { 37 | // // optional settings 38 | // }, 39 | // aiChatSettings: { 40 | // // optional settings 41 | // aiAssistantAvatar: "https://mydomain.com/mylogo", // use your own ai assistant avatar 42 | // exampleQuestions: ["Example question 1?", "Example question 2?", "Example question 3?"], 43 | // }, 44 | }; 45 | return ; 46 | } 47 | -------------------------------------------------------------------------------- /packages/docs/components/platinum.tsx: -------------------------------------------------------------------------------- 1 | export const Platinum = () => { 2 | return ( 3 |
4 |
5 |
6 | 7 |
8 | CodeRabbit logo 13 | CodeRabbit logo 18 |
19 |
20 |

21 | Cut code review time & bugs in half 22 |

23 |

24 | 25 | coderabbit.ai 26 | 27 |

28 |
29 |
30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/docs/components/scroller.tsx: -------------------------------------------------------------------------------- 1 | // app/RouteChangeListener.tsx 2 | "use client"; 3 | 4 | import { usePathname, useSearchParams } from "next/navigation"; 5 | import { useEffect } from "react"; 6 | 7 | export default function Scroller() { 8 | const pathname = usePathname(); 9 | const searchParams = useSearchParams(); 10 | // const firstRender = useRef(true); 11 | 12 | // run after every route or query-string change 13 | // biome-ignore lint: 14 | useEffect(() => { 15 | // skip the very first render if you only care about later navigations 16 | handleScroll(); 17 | // firstRender.current = false; 18 | }, [pathname, searchParams]); 19 | 20 | // also run once on the initial load (optional) 21 | useEffect(handleScroll, []); 22 | 23 | function handleScroll() { 24 | console.dir("handling scroll...", { depth: null }); 25 | // accept either ?id=foo or #foo 26 | const id = searchParams?.get("id") ?? window.location.hash.slice(1); 27 | const el = id ? document.getElementById(id) : null; 28 | el?.scrollIntoView(); 29 | } 30 | 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /packages/docs/components/sidebar-item.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import type { PageTree } from "fumadocs-core/server"; 4 | import { 5 | SidebarItem as InternalSidebarItem, 6 | SidebarSeparator as InternalSidebarSeparator, 7 | } from "fumadocs-ui/layouts/docs/sidebar"; 8 | 9 | const Tags: Record = { 10 | "/packages/core": "New", 11 | "/packages/mini": "New", 12 | "/json-schema": "New", 13 | "/metadata": "New", 14 | "/packages/v3": "Legacy", 15 | }; 16 | export const SidebarItem = ({ 17 | item, 18 | }: { 19 | item: PageTree.Item; 20 | }) => { 21 | const name = `${item.name}`; 22 | // const isCode = name.startsWith("`") && name.endsWith("`"); 23 | // const tagMatch = name.match(/#(\w+)$/); 24 | // const tag = tagMatch ? tagMatch[1] : null; 25 | // const cleanName = tagMatch ? name.replace(/#\w+$/, "").trim() : name; 26 | 27 | const tag = Tags[item.url]; //?.toUpperCase(); 28 | return ( 29 | 30 |
31 |

{name}

32 |

33 | {tag && ( 34 | 35 | {tag} 36 | 37 | )} 38 |

39 |
40 |
41 | ); 42 | }; 43 | 44 | export const SidebarSeparator = ({ 45 | item, 46 | }: { 47 | item: PageTree.Separator; 48 | }) => { 49 | return ( 50 | 54 | {item.icon} 55 | {item.name} 56 | 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /packages/docs/components/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Primitive, Tab, type TabsProps } from "fumadocs-ui/components/tabs"; 4 | import React from "react"; 5 | 6 | interface ChildProps { 7 | title: string; 8 | children: React.ReactNode; 9 | } 10 | 11 | const Tabs = ({ children, ...rest }: TabsProps) => { 12 | const validChildren = React.Children.toArray(children) 13 | .filter(React.isValidElement) 14 | .filter((child: any) => child.props.title); 15 | 16 | if (validChildren.length === 0) { 17 | console.warn("Tabs expects at least one valid Tab child, but none were found."); 18 | return null; 19 | } 20 | 21 | const tabs = validChildren.map((child) => { 22 | const { title } = child.props as ChildProps; 23 | return title; 24 | }); 25 | 26 | return ( 27 | 28 | 29 | {validChildren.map((child) => { 30 | const { title } = child.props as ChildProps; 31 | return ( 32 | 37 | {title} 38 | 39 | ); 40 | })} 41 | 42 | {validChildren.map((child) => { 43 | const { title, children: childContent, ...props } = child.props as ChildProps; 44 | 45 | return ( 46 | 53 | {childContent} 54 | 55 | ); 56 | })} 57 | 58 | ); 59 | }; 60 | 61 | export { Tabs, Tab }; 62 | -------------------------------------------------------------------------------- /packages/docs/content/ecosystem.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ecosystem 3 | --- 4 | 5 | import { 6 | ApiLibraries, 7 | FormIntegrations, 8 | ZodToX, 9 | XToZod, 10 | MockingLibraries, 11 | PoweredByZod, 12 | ZodUtilities, 13 | } from "../components/ecosystem"; 14 | 15 | > **Note** — To avoid bloat and confusion, the Ecosystem section has been wiped clean with the release of Zod 4. If you've updated your library to work with Zod 4, please submit a PR to add it back in. 16 | 17 | There are a growing number of tools that are built atop or support Zod natively! If you've built a tool or library on top of Zod, let me know [on Twitter](https://x.com/colinhacks) or [start a Discussion](https://github.com/colinhacks/zod/discussions). I'll add it below and tweet it out. 18 | 19 | ## Resources 20 | 21 | - [Total TypeScript Zod Tutorial](https://www.totaltypescript.com/tutorials/zod) by [@mattpocockuk](https://x.com/mattpocockuk) 22 | - [Fixing TypeScript's Blindspot: Runtime Typechecking](https://www.youtube.com/watch?v=rY_XqfSHock) by [@jherr](https://x.com/jherr) 23 | 24 | 25 | ## API Libraries 26 | 27 | 28 | 29 | 30 | ## Form Integrations 31 | 32 | 33 | 34 | 35 | ## Zod to X 36 | 37 | 38 | 39 | 40 | ## X to Zod 41 | 42 | 43 | 44 | 45 | ## Mocking Libraries 46 | 47 | 48 | 49 | 50 | ## Powered by Zod 51 | 52 | 53 | 54 | 55 | ## Zod Utilities 56 | 57 | 58 | -------------------------------------------------------------------------------- /packages/docs/content/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "pages": [ 4 | "---Zod 4---", 5 | "v4/index", 6 | "v4/changelog", 7 | "---Documentation---", 8 | "index", 9 | "basics", 10 | "api", 11 | "error-customization", 12 | "error-formatting", 13 | "metadata", 14 | "json-schema", 15 | "ecosystem", 16 | "library-authors", 17 | "---Packages---", 18 | "packages/zod", 19 | "packages/mini", 20 | "packages/core" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/docs/content/object-vs-interface.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Object vs. interface" 3 | --- 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/docs/content/packages/zod.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Zod 3 | --- 4 | 5 | The `zod/v4` package is the "flagship" library of the Zod ecosystem. It strikes a balance between developer experience and bundle size that's ideal for the vast majority of applications. 6 | 7 | > If you have uncommonly strict constraints around bundle size, consider [Zod Mini](/packages/mini). 8 | 9 | Zod aims to provide a schema API that maps one-to-one to TypeScript's type system. 10 | 11 | ```ts 12 | import { z } from "zod/v4"; 13 | 14 | const schema = z.object({ 15 | name: z.string(), 16 | age: z.number().int().positive(), 17 | email: z.string().email(), 18 | }); 19 | ``` 20 | 21 | The API relies on methods to provide a concise, chainable, autocomplete-friendly way to define complex types. 22 | 23 | ```ts 24 | z.string() 25 | .min(5) 26 | .max(10) 27 | .toLowerCase(); 28 | ``` 29 | 30 | All schemas extend the `z.ZodType` base class, which in turn extends `z.$ZodType` from [`zod/v4/core`](/packages/core). All instance of `ZodType` implement the following methods: 31 | 32 | ```ts 33 | import { z } from "zod/v4"; 34 | 35 | const mySchema = z.string(); 36 | 37 | // parsing 38 | mySchema.parse(data); 39 | mySchema.safeParse(data); 40 | mySchema.parseAsync(data); 41 | mySchema.safeParseAsync(data); 42 | 43 | 44 | // refinements 45 | mySchema.refine(refinementFunc); 46 | mySchema.superRefine(refinementFunc); // deprecated, use `.check()` 47 | mySchema.overwrite(overwriteFunc); 48 | 49 | // wrappers 50 | mySchema.optional(); 51 | mySchema.nonoptional(); 52 | mySchema.nullable(); 53 | mySchema.nullish(); 54 | mySchema.default(defaultValue); 55 | mySchema.array(); 56 | mySchema.or(otherSchema); 57 | mySchema.transform(transformFunc); 58 | mySchema.catch(catchValue); 59 | mySchema.pipe(otherSchema); 60 | mySchema.readonly(); 61 | 62 | // metadata and registries 63 | mySchema.register(registry, metadata); 64 | mySchema.describe(description); 65 | mySchema.meta(metadata); 66 | 67 | // utilities 68 | mySchema.check(checkOrFunction); 69 | mySchema.clone(def); 70 | mySchema.brand(); 71 | mySchema.isOptional(); // boolean 72 | mySchema.isNullable(); // boolean 73 | ``` 74 | -------------------------------------------------------------------------------- /packages/docs/loaders/source.ts: -------------------------------------------------------------------------------- 1 | import { docs } from "@/.source"; 2 | import { loader } from "fumadocs-core/source"; 3 | import { icons } from "lucide-react"; 4 | import { createElement } from "react"; 5 | 6 | // `loader()` also assign a URL to your pages 7 | // See https://fumadocs.vercel.app/docs/headless/source-api for more info 8 | export const source = loader({ 9 | baseUrl: "/", 10 | source: docs.toFumadocsSource(), 11 | icon(icon) { 12 | if (!icon) { 13 | // You may set a default icon 14 | return; 15 | } 16 | 17 | if (icon in icons) return createElement(icons[icon as keyof typeof icons]); 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /packages/docs/loaders/stars.ts: -------------------------------------------------------------------------------- 1 | const GITHUB_TOKEN = process.env.GITHUB_TOKEN!; 2 | const API_URL = "https://api.github.com/graphql"; 3 | 4 | export async function fetchStars(resources: { slug: string; stars?: number }[]) { 5 | try { 6 | if (resources.length === 0) return; 7 | const uniqueSlugs = Array.from( 8 | new Set( 9 | resources 10 | .filter((r) => r.stars === undefined) 11 | .map((r, id) => ({ 12 | id, 13 | slug: r.slug, 14 | })) 15 | ) 16 | ); 17 | 18 | if (uniqueSlugs.length === 0) return; 19 | 20 | const queryParts = uniqueSlugs.map(({ id, slug }) => { 21 | const [owner, name] = slug.split("/"); 22 | return ` 23 | repo${id}: repository(owner: "${owner}", name: "${name}") { 24 | stargazerCount 25 | } 26 | `; 27 | }); 28 | 29 | const query = `{ ${queryParts.join("\n")} }`; 30 | const res = await fetch(API_URL, { 31 | method: "POST", 32 | headers: { 33 | Authorization: `Bearer ${GITHUB_TOKEN}`, 34 | "Content-Type": "application/json", 35 | }, 36 | body: JSON.stringify({ query }), 37 | }); 38 | 39 | const json = await res.json(); 40 | 41 | if (json.errors) { 42 | console.error("GraphQL errors:", json.errors); 43 | throw new Error("Failed to fetch GitHub stars"); 44 | } 45 | 46 | // Create a map of slug → star count 47 | const starsMap = new Map(); 48 | for (const slug of uniqueSlugs) { 49 | const count = json.data[`repo${slug.id}`]?.stargazerCount; 50 | if (typeof count === "number") { 51 | starsMap.set(slug.slug, count); 52 | } 53 | } 54 | 55 | // Mutate in-place 56 | for (const r of resources) { 57 | r.stars = starsMap.get(r.slug); 58 | } 59 | 60 | // sort by star coun (descending) in place 61 | resources.sort((a, b) => (b.stars || 0) - (a.stars || 0)); 62 | } catch (_) { 63 | throw new Error("Failed to fetch GitHub stars"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zod/docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "dev": "next dev", 8 | "start": "next start", 9 | "postinstall": "fumadocs-mdx" 10 | }, 11 | "dependencies": { 12 | "@inkeep/cxkit-react": "^0.5.80", 13 | "@radix-ui/react-accordion": "^1.2.4", 14 | "@vercel/analytics": "^1.5.0", 15 | "@vercel/og": "^0.6.8", 16 | "fumadocs-core": "15.1.0", 17 | "fumadocs-mdx": "11.5.6", 18 | "fumadocs-ui": "15.1.0", 19 | "lucide-react": "^0.483.0", 20 | "next": "15.2.2", 21 | "octokit": "^4.1.2", 22 | "react": "^19.0.0", 23 | "react-dom": "^19.0.0" 24 | }, 25 | "devDependencies": { 26 | "@tailwindcss/postcss": "^4.0.14", 27 | "@types/mdx": "^2.0.13", 28 | "@types/node": "22.13.10", 29 | "@types/react": "^19.0.10", 30 | "@types/react-dom": "^19.0.4", 31 | "postcss": "^8.5.3", 32 | "tailwindcss": "^4.0.14", 33 | "typescript": "^5.8.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/docs/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/docs/public/github-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/github-white.png -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-black.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-black.ai -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-black.jpg -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-black.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-black.pdf -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-black.png -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-glow.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-glow.ai -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-glow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-glow.jpg -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-glow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-glow.pdf -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-glow.png -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-white.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-white.ai -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-white.jpg -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-white.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-white.pdf -------------------------------------------------------------------------------- /packages/docs/public/logo/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo-white.png -------------------------------------------------------------------------------- /packages/docs/public/logo/logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo.ai -------------------------------------------------------------------------------- /packages/docs/public/logo/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo.jpg -------------------------------------------------------------------------------- /packages/docs/public/logo/logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo.pdf -------------------------------------------------------------------------------- /packages/docs/public/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo.png -------------------------------------------------------------------------------- /packages/docs/public/logo/logo_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/logo_square.png -------------------------------------------------------------------------------- /packages/docs/public/logo/profile_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/profile_circle.png -------------------------------------------------------------------------------- /packages/docs/public/logo/profile_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colinhacks/zod/343bd590278c68f86208395ab5da6c213ccc88f3/packages/docs/public/logo/profile_square.png -------------------------------------------------------------------------------- /packages/docs/public/robots.txt: -------------------------------------------------------------------------------- 1 | Allow: /api/og/* 2 | -------------------------------------------------------------------------------- /packages/docs/source.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, defineDocs } from "fumadocs-mdx/config"; 2 | 3 | export const docs = defineDocs({ 4 | dir: "content", 5 | }); 6 | 7 | export default defineConfig({ 8 | mdxOptions: { 9 | // MDX options 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "bundler", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "paths": { 19 | "@/.source": ["./.source/index.ts"], 20 | "@/*": ["./*"] 21 | }, 22 | "plugins": [ 23 | { 24 | "name": "next" 25 | } 26 | ] 27 | }, 28 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "components/if.tsx"], 29 | "exclude": ["node_modules"] 30 | } 31 | -------------------------------------------------------------------------------- /packages/treeshake/.gitignore: -------------------------------------------------------------------------------- 1 | out.js 2 | -------------------------------------------------------------------------------- /packages/treeshake/example-mini.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4-mini" 2 | 3 | z.string().check( 4 | z.minLength(5), 5 | z.maxLength(10), 6 | z.refine(val => val.includes("@")), 7 | z.trim() 8 | ).parse("asd@adsf"); 9 | -------------------------------------------------------------------------------- /packages/treeshake/example.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4" 2 | 3 | // z.string().check( 4 | // z.minLength(5), 5 | // z.maxLength(10), 6 | // z.refine(val => val.includes("@")), 7 | // z.trim() 8 | // ); 9 | 10 | z.string().min(5).max(10).refine(val => val.includes("@")).trim().parse("asd@adsf");; 11 | -------------------------------------------------------------------------------- /packages/treeshake/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zod/treeshaking", 3 | "private": true, 4 | "type": "module", 5 | "license": "MIT", 6 | "scripts": { 7 | "bundle": "rollup -c rollup.config.js --input", 8 | "bundle:rollup": "rollup --input", 9 | "bundle:esbuild": "esbuild --bundle ./in.ts --outfile=./out.js --bundle --format=esm" 10 | }, 11 | "devDependencies": { 12 | "@rollup/plugin-commonjs": "^28.0.3", 13 | "@rollup/plugin-node-resolve": "^16.0.1", 14 | "@rollup/plugin-terser": "^0.4.4", 15 | "@rollup/plugin-typescript": "^12.1.2", 16 | "@types/node": "^22.13.13", 17 | "zod": "workspace:*", 18 | "rollup": "^4.37.0", 19 | "rollup-plugin-filesize": "^10.0.0", 20 | "rollup-plugin-bundle-size": "^1.0.3", 21 | "valibot": "^1.0.0", 22 | "zod3": "npm:zod@^3.24.0", 23 | "zod4": "npm:zod@^3.25.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/treeshake/rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from "@rollup/plugin-commonjs"; 2 | import resolve from "@rollup/plugin-node-resolve"; 3 | // import terser from "@rollup/plugin-terser"; 4 | import typescript from "@rollup/plugin-typescript"; 5 | import bundleSize from "rollup-plugin-bundle-size"; 6 | import filesize from "rollup-plugin-filesize"; 7 | 8 | /** @type {import('rollup').RollupOptions} */ 9 | export default { 10 | // input: "./in.ts", // Your TypeScript entry file 11 | output: { 12 | file: "./out.js", // Output file 13 | format: "esm", // ES module format to enable tree-shaking 14 | // sourcemap: true, // Generate sourcemaps for easier debugging 15 | }, 16 | plugins: [ 17 | resolve({ 18 | 19 | }), // Resolve node_modules 20 | commonjs(), // Convert CommonJS modules to ES6 21 | typescript(), // Compile TypeScript 22 | // bundleSize(), 23 | filesize(), // Display bundle size 24 | // terser(), 25 | ], 26 | treeshake: { 27 | preset: "smallest", 28 | // preset: "recommended", 29 | annotations: true, 30 | }, // Enable tree-shaking 31 | }; 32 | -------------------------------------------------------------------------------- /packages/treeshake/valibot-boolean.ts: -------------------------------------------------------------------------------- 1 | import * as z from "valibot"; 2 | 3 | const schema = z.boolean() 4 | console.log(z.parse(schema, "hi")); 5 | -------------------------------------------------------------------------------- /packages/treeshake/valibot-object.ts: -------------------------------------------------------------------------------- 1 | import * as z from "valibot"; 2 | 3 | const schema = z.object({ a: z.string(), b: z.number(), c: z.boolean() }); 4 | console.log( 5 | z.parse(schema, { 6 | a: "asdf", 7 | b: 123, 8 | c: true, 9 | }) 10 | ); 11 | -------------------------------------------------------------------------------- /packages/treeshake/valibot-string.ts: -------------------------------------------------------------------------------- 1 | import * as z from "valibot"; 2 | 3 | const schema = z.pipe(z.string(), z.minLength(1)); 4 | console.log(z.parse(schema, "hi")); 5 | -------------------------------------------------------------------------------- /packages/treeshake/zod-boolean.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4"; 2 | 3 | const schema = z.boolean(); 4 | console.log(schema.parse(true)); 5 | -------------------------------------------------------------------------------- /packages/treeshake/zod-full.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4"; 2 | 3 | export const schema = z.object({ 4 | name: z.string(), 5 | age: z.number(), 6 | email: z.boolean(), 7 | }); 8 | -------------------------------------------------------------------------------- /packages/treeshake/zod-locales.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod/v4-mini" 2 | 3 | z.config(z.locales.en()); 4 | 5 | z.string().parse(12); 6 | -------------------------------------------------------------------------------- /packages/treeshake/zod-mini-boolean.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4-mini" 2 | 3 | const schema = z.boolean(); 4 | console.log(schema.parse(true)); 5 | -------------------------------------------------------------------------------- /packages/treeshake/zod-mini-full.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4-mini" 2 | 3 | export const schema = z.object({ 4 | name: z.string(), 5 | age: z.number(), 6 | email: z.boolean(), 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /packages/treeshake/zod-mini-object.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4-mini" 2 | 3 | const schema = z.object({ a: z.string(), b: z.number(), c: z.boolean() }); 4 | 5 | schema.parse({ 6 | a: "asdf", 7 | b: 123, 8 | c: true, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/treeshake/zod-mini-string.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod/v4-mini" 2 | 3 | const schema = z.string().check(z.minLength(5)); 4 | 5 | console.log(schema.parse("hi")); 6 | -------------------------------------------------------------------------------- /packages/treeshake/zod-object.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4"; 2 | 3 | const schema = z.object({ a: z.string(), b: z.number(), c: z.boolean() }); 4 | 5 | schema.parse({ 6 | a: "asdf", 7 | b: 123, 8 | c: true, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/treeshake/zod-string.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v4"; 2 | 3 | const schema = z.string().min(5); 4 | console.log(schema.parse("hi")); 5 | -------------------------------------------------------------------------------- /packages/treeshake/zod3-boolean.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod3"; 2 | 3 | const schema = z.boolean(); 4 | console.log(schema.parse(true)); 5 | -------------------------------------------------------------------------------- /packages/treeshake/zod3-full.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod3"; 2 | 3 | export const schema = z.object({ 4 | name: z.string(), 5 | age: z.number(), 6 | email: z.boolean(), 7 | }); 8 | -------------------------------------------------------------------------------- /packages/treeshake/zod3-object.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod3"; 2 | 3 | const schema = z.object({ a: z.string(), b: z.number(), c: z.boolean() }); 4 | 5 | schema.parse({ 6 | a: "asdf", 7 | b: 123, 8 | c: true, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/treeshake/zod3-string.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod3"; 2 | 3 | const schema = z.string().min(5); 4 | console.log(schema.parse("hi")); 5 | -------------------------------------------------------------------------------- /packages/tsc/.gitignore: -------------------------------------------------------------------------------- 1 | src/** 2 | -------------------------------------------------------------------------------- /packages/tsc/README.md: -------------------------------------------------------------------------------- 1 | # Zod perftesting 2 | 3 | `node generateRandomSchemas.js` to generate some random Zod schemas (pregenerated ones already exist in `src/index.ts`) 4 | 5 | `npm run build-bench` to run `tsc` with `extendedDiagnostics` 6 | 7 | Either modify zod's typings in `node_modules/zod` or `npm link` a local copy and do modifications there. Remember to build `zod` in between! 8 | -------------------------------------------------------------------------------- /packages/tsc/bench/index.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | 5 | async function run() { 6 | const fileNames = process.argv[2]; 7 | 8 | if (!fileNames) { 9 | console.error("Specify a benchmark name(s), e.g. `pnpm bench object-with-extend`"); 10 | process.exit(1); 11 | } 12 | const files = fileNames.split(",").map((file) => import.meta.resolve(`./${file}`).replace("file://", "")); 13 | 14 | for (const file of files) { 15 | await $`pnpm tsx --conditions @zod/source ${file}`; 16 | } 17 | } 18 | 19 | run(); 20 | 21 | // exit on Ctrl-C 22 | process.on("SIGINT", () => { 23 | console.log("Exiting..."); 24 | process.exit(); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/tsc/bench/lots-of-objects.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | import * as gen from "../generate.js"; 5 | 6 | const SHARED = { 7 | numSchemas: 500, 8 | numKeys: 5, 9 | numExtends: 1, 10 | }; 11 | console.log("╔════════════════╗"); 12 | console.log("║ Zod v3 ║"); 13 | console.log("╚════════════════╝"); 14 | await gen.generate({ 15 | ...gen.ZOD3, 16 | schemaType: "z.object", 17 | ...SHARED, 18 | }); 19 | 20 | await $`pnpm run build:bench`; 21 | 22 | console.log("╔════════════════╗"); 23 | console.log("║ Zod v4 ║"); 24 | console.log("╚════════════════╝"); 25 | await gen.generate({ 26 | ...gen.ZOD, 27 | schemaType: "z.object", 28 | ...SHARED, 29 | }); 30 | 31 | await $`pnpm run build:bench`; 32 | 33 | // console.log("╔═════════════════╗"); 34 | // console.log("║ ArkType ║"); 35 | // console.log("╚═════════════════╝"); 36 | // await gen.generate({ 37 | // gen. ...ARKTYPE, 38 | // ...SHARED 39 | // }); 40 | 41 | // await $`pnpm run build:bench`; 42 | -------------------------------------------------------------------------------- /packages/tsc/bench/object-with-extend.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | import * as gen from "../generate.js"; 5 | 6 | console.log("╔════════════════╗"); 7 | console.log("║ Zod v3 ║"); 8 | console.log("╚════════════════╝"); 9 | await gen.generate({ 10 | ...gen.ZOD3, 11 | schemaType: "z.object", 12 | numSchemas: 1, 13 | numExtends: 1, 14 | methods: [""], 15 | numKeys: 5, 16 | numRefs: 0, 17 | }); 18 | 19 | await $`pnpm run build:bench`; 20 | 21 | console.log("╔════════════════╗"); 22 | console.log("║ Zod v4 ║"); 23 | console.log("╚════════════════╝"); 24 | await gen.generate({ 25 | ...gen.ZOD, 26 | schemaType: "z.object", 27 | numSchemas: 1, 28 | numExtends: 1, 29 | methods: [""], 30 | numKeys: 5, 31 | numRefs: 0, 32 | }); 33 | 34 | await $`pnpm run build:bench`; 35 | -------------------------------------------------------------------------------- /packages/tsc/bench/omit-extend.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | import * as gen from "../generate.js"; 5 | 6 | console.log("╔═════════════════════╗"); 7 | console.log("║ Zod v3.24.2 ║"); 8 | console.log("╚═════════════════════╝"); 9 | await gen.generateExtendChain({ 10 | ...gen.ZOD, 11 | numSchemas: 14, 12 | numKeys: 6, 13 | imports: ["import * as z from 'zod3-24-2'"], 14 | }); 15 | 16 | await $`pnpm run build:bench`; 17 | 18 | console.log("╔═════════════════════╗"); 19 | console.log("║ Zod v3.24.3 ║"); 20 | console.log("╚═════════════════════╝"); 21 | await gen.generateExtendChain({ 22 | ...gen.ZOD, 23 | numSchemas: 14, 24 | numKeys: 6, 25 | imports: ["import * as z from 'zod3-24-3'"], 26 | }); 27 | 28 | await $`pnpm run build:bench`; 29 | 30 | console.log("╔══════════════════════╗"); 31 | console.log("║ Zod v4 (pre) ║"); 32 | console.log("╚══════════════════════╝"); 33 | await gen.generateExtendChain({ 34 | ...gen.ZOD, 35 | numSchemas: 14, 36 | numKeys: 6, 37 | imports: ["import * as z from 'zod4'"], 38 | }); 39 | 40 | await $`pnpm run build:bench`; 41 | 42 | console.log("╔═══════════════════════╗"); 43 | console.log("║ Zod v4 (curr) ║"); 44 | console.log("╚═══════════════════════╝"); 45 | await gen.generateExtendChain({ 46 | ...gen.ZOD, 47 | numSchemas: 14, 48 | numKeys: 6, 49 | imports: ["import * as z from 'zod'"], 50 | }); 51 | 52 | await $`pnpm run build:bench`; 53 | -------------------------------------------------------------------------------- /packages/tsc/bench/string.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | import * as gen from "../generate.js"; 5 | 6 | console.log("╔════════════════╗"); 7 | console.log("║ Zod v3 ║"); 8 | console.log("╚════════════════╝"); 9 | await gen.generate({ 10 | ...gen.ZOD3, 11 | numSchemas: 0, 12 | numKeys: 0, 13 | custom: ` 14 | export const schema = z.string(); 15 | export type schema = z.infer; 16 | `, 17 | }); 18 | 19 | await $`pnpm run build:bench`; 20 | 21 | console.log("╔════════════════╗"); 22 | console.log("║ Zod v4 ║"); 23 | console.log("╚════════════════╝"); 24 | await gen.generate({ 25 | ...gen.ZOD, 26 | numSchemas: 0, 27 | numKeys: 0, 28 | custom: ` 29 | export const schema = z.string(); 30 | export type schema = z.infer; 31 | `, 32 | }); 33 | 34 | await $`pnpm run build:bench`; 35 | -------------------------------------------------------------------------------- /packages/tsc/bench/strongly-connected.ts: -------------------------------------------------------------------------------- 1 | import { execa } from "execa"; 2 | 3 | const $ = execa({ stdout: "inherit", stderr: "inherit" }); 4 | import * as gen from "../generate.js"; 5 | 6 | console.log("╔════════════════╗"); 7 | console.log("║ Zod v4 ║"); 8 | console.log("╚════════════════╝"); 9 | await gen.generate({ 10 | ...gen.ZOD, 11 | schemaType: "z.object", 12 | numSchemas: 500, 13 | numKeys: 3, 14 | numRefs: 1, 15 | // methods: [""], 16 | }); 17 | 18 | await $`pnpm run build:bench`; 19 | -------------------------------------------------------------------------------- /packages/tsc/extend.ts: -------------------------------------------------------------------------------- 1 | // import { z } from "zod/v4"; 2 | 3 | // const Type1 = z.object({}); 4 | // let test1: z.infer; 5 | 6 | // const Type2 = Type1.extend({}); 7 | // let test2: z.infer; 8 | 9 | // const Type3 = Type2.extend({}); 10 | // let test3: z.infer; 11 | 12 | // const Type4 = Type3.extend({}); 13 | // let test4: z.infer; 14 | 15 | // const Type5 = Type4.extend({}); 16 | // let test5: z.infer; 17 | 18 | // const Type6 = Type5.extend({}); 19 | // let test6: z.infer; 20 | 21 | // const Type7 = Type6.extend({}); 22 | // let test7: z.infer; 23 | 24 | // const Type8 = Type7.extend({}); 25 | // let test8: z.infer; 26 | 27 | // const Type9 = Type8.extend({}); 28 | // let test9: z.infer; 29 | 30 | // const Type10 = Type9.extend({}); 31 | // let test10: z.infer; 32 | 33 | // const Type11 = Type10.extend({}); 34 | // let test11: z.infer; 35 | 36 | // const Type12 = Type11.extend({}); 37 | // let test12: z.infer; 38 | 39 | // const Type13 = Type12.extend({}); 40 | // let test13: z.infer; 41 | 42 | // const Type14 = Type13.extend({}); 43 | // let test14: z.infer; 44 | 45 | // const Type15 = Type14.extend({}); 46 | // let test15: z.infer; 47 | -------------------------------------------------------------------------------- /packages/tsc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zod/tsc-perftest", 3 | "type": "module", 4 | "private": true, 5 | "version": "1.0.0", 6 | "main": "./lib/index.js", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "@ai-sdk/provider": "^1.1.3", 10 | "@types/node": "^22.10.5", 11 | "@typescript/analyze-trace": "^0.10.1", 12 | "ai": "^4.3.16", 13 | "arktype": "^2.0.0", 14 | "execa": "^9.5.2", 15 | "valibot": "^1.0.0", 16 | "zod": "workspace:*", 17 | "zod3": "npm:zod@^3.0.0", 18 | "zod3-24-2": "npm:zod@3.24.2", 19 | "zod3-24-3": "npm:zod@3.24.3", 20 | "zod4": "npm:zod@4.0.0-beta.20250420T053007" 21 | }, 22 | "files": ["./bin/*", "./lib/*"], 23 | "typings": "./lib/index.d.ts", 24 | "scripts": { 25 | "generate": "tsx generate.ts", 26 | "build": "tsc", 27 | "build:bench": "tsc -p tsconfig.bench.json", 28 | "lint": "tslint -c tslint.json src/**/*.ts", 29 | "prepublish": "npm run build", 30 | "bench": "tsx ./bench/index.ts", 31 | "bisect": "tsx bisect.ts" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/tsc/tsconfig.bench.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "noEmit": false, 6 | "extendedDiagnostics": true, 7 | "skipLibCheck": true, 8 | "customConditions": [] 9 | }, 10 | "files": ["src/index.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/tsc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.configs/tsconfig.base.json", 3 | 4 | "compilerOptions": { 5 | "target": "es2022", 6 | "module": "NodeNext", 7 | "customConditions": [], 8 | "incremental": false, 9 | "noEmit": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/zod/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Colin McDonnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/zod/jsr.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zod/zod", 3 | "version": "4.0.0-alpha.0", 4 | "exports": { 5 | ".": "./src/index.ts", 6 | "./package.json": "./package.json" 7 | }, 8 | "publish": { 9 | "include": ["src", "LICENSE", "package.json", "README.md"], 10 | "exclude": ["tests", "dist"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/zod/postbuild.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "node:fs"; 2 | 3 | const packageJsonPath = "./package.json"; 4 | 5 | try { 6 | // Read the package.json file 7 | const packageJson = readFileSync(packageJsonPath, "utf-8"); 8 | 9 | // Replace occurrences of "types": "./dist/commonjs/ with "types": "./dist/esm/ 10 | const updatedPackageJson = packageJson.replace(/"types": "\.\/dist\/commonjs\//g, '"types": "./dist/esm/'); 11 | 12 | // Write the updated content back to package.json 13 | writeFileSync(packageJsonPath, updatedPackageJson, "utf-8"); 14 | 15 | console.log('Successfully updated "types" paths in package.json'); 16 | } catch (error) { 17 | console.error("Error updating package.json:", error); 18 | } 19 | -------------------------------------------------------------------------------- /packages/zod/src/index.ts: -------------------------------------------------------------------------------- 1 | import z3 from "./v3/index.js"; 2 | export * from "./v3/index.js"; 3 | 4 | export default z3; 5 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/datetime.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | const datetimeValidationSuite = new Benchmark.Suite("datetime"); 4 | 5 | const DATA = "2021-01-01"; 6 | const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]); 7 | const MONTHS_30 = new Set([4, 6, 9, 11]); 8 | 9 | const simpleDatetimeRegex = /^(\d{4})-(\d{2})-(\d{2})$/; 10 | const datetimeRegexNoLeapYearValidation = 11 | /^\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\d|2\d))$/; 12 | const datetimeRegexWithLeapYearValidation = 13 | /^((\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\d|3[01])|(0[469]|11)-(0[1-9]|[12]\d|30)|(02)-(0[1-9]|1\d|2[0-8])))$/; 14 | 15 | datetimeValidationSuite 16 | .add("new Date()", () => { 17 | return !Number.isNaN(new Date(DATA).getTime()); 18 | }) 19 | .add("regex (no validation)", () => { 20 | return simpleDatetimeRegex.test(DATA); 21 | }) 22 | .add("regex (no leap year)", () => { 23 | return datetimeRegexNoLeapYearValidation.test(DATA); 24 | }) 25 | .add("regex (w/ leap year)", () => { 26 | return datetimeRegexWithLeapYearValidation.test(DATA); 27 | }) 28 | .add("capture groups + code", () => { 29 | const match = DATA.match(simpleDatetimeRegex); 30 | if (!match) return false; 31 | 32 | // Extract year, month, and day from the capture groups 33 | const year = Number.parseInt(match[1], 10); 34 | const month = Number.parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1 35 | const day = Number.parseInt(match[3], 10); 36 | 37 | if (month === 2) { 38 | if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) { 39 | return day <= 29; 40 | } 41 | return day <= 28; 42 | } 43 | if (MONTHS_30.has(month)) { 44 | return day <= 30; 45 | } 46 | if (MONTHS_31.has(month)) { 47 | return day <= 31; 48 | } 49 | return false; 50 | }) 51 | 52 | .on("cycle", (e: Benchmark.Event) => { 53 | console.log(`${datetimeValidationSuite.name!}: ${e.target}`); 54 | }); 55 | 56 | export default { 57 | suites: [datetimeValidationSuite], 58 | }; 59 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/discriminatedUnion.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | import { z } from "zod/v3"; 4 | 5 | const doubleSuite = new Benchmark.Suite("z.discriminatedUnion: double"); 6 | const manySuite = new Benchmark.Suite("z.discriminatedUnion: many"); 7 | 8 | const aSchema = z.object({ 9 | type: z.literal("a"), 10 | }); 11 | const objA = { 12 | type: "a", 13 | }; 14 | 15 | const bSchema = z.object({ 16 | type: z.literal("b"), 17 | }); 18 | const objB = { 19 | type: "b", 20 | }; 21 | 22 | const cSchema = z.object({ 23 | type: z.literal("c"), 24 | }); 25 | const objC = { 26 | type: "c", 27 | }; 28 | 29 | const dSchema = z.object({ 30 | type: z.literal("d"), 31 | }); 32 | 33 | const double = z.discriminatedUnion("type", [aSchema, bSchema]); 34 | const many = z.discriminatedUnion("type", [aSchema, bSchema, cSchema, dSchema]); 35 | 36 | doubleSuite 37 | .add("valid: a", () => { 38 | double.parse(objA); 39 | }) 40 | .add("valid: b", () => { 41 | double.parse(objB); 42 | }) 43 | .add("invalid: null", () => { 44 | try { 45 | double.parse(null); 46 | } catch (_err) {} 47 | }) 48 | .add("invalid: wrong shape", () => { 49 | try { 50 | double.parse(objC); 51 | } catch (_err) {} 52 | }) 53 | .on("cycle", (e: Benchmark.Event) => { 54 | console.log(`${(doubleSuite as any).name}: ${e.target}`); 55 | }); 56 | 57 | manySuite 58 | .add("valid: a", () => { 59 | many.parse(objA); 60 | }) 61 | .add("valid: c", () => { 62 | many.parse(objC); 63 | }) 64 | .add("invalid: null", () => { 65 | try { 66 | many.parse(null); 67 | } catch (_err) {} 68 | }) 69 | .add("invalid: wrong shape", () => { 70 | try { 71 | many.parse({ type: "unknown" }); 72 | } catch (_err) {} 73 | }) 74 | .on("cycle", (e: Benchmark.Event) => { 75 | console.log(`${(manySuite as any).name}: ${e.target}`); 76 | }); 77 | 78 | export default { 79 | suites: [doubleSuite, manySuite], 80 | }; 81 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/index.ts: -------------------------------------------------------------------------------- 1 | import type Benchmark from "benchmark"; 2 | 3 | import datetimeBenchmarks from "./datetime.js"; 4 | import discriminatedUnionBenchmarks from "./discriminatedUnion.js"; 5 | import ipv4Benchmarks from "./ipv4.js"; 6 | import objectBenchmarks from "./object.js"; 7 | import primitiveBenchmarks from "./primitives.js"; 8 | import realworld from "./realworld.js"; 9 | import stringBenchmarks from "./string.js"; 10 | import unionBenchmarks from "./union.js"; 11 | 12 | const argv = process.argv.slice(2); 13 | let suites: Benchmark.Suite[] = []; 14 | 15 | if (!argv.length) { 16 | suites = [ 17 | ...realworld.suites, 18 | ...primitiveBenchmarks.suites, 19 | ...stringBenchmarks.suites, 20 | ...objectBenchmarks.suites, 21 | ...unionBenchmarks.suites, 22 | ...discriminatedUnionBenchmarks.suites, 23 | ]; 24 | } else { 25 | if (argv.includes("--realworld")) { 26 | suites.push(...realworld.suites); 27 | } 28 | if (argv.includes("--primitives")) { 29 | suites.push(...primitiveBenchmarks.suites); 30 | } 31 | if (argv.includes("--string")) { 32 | suites.push(...stringBenchmarks.suites); 33 | } 34 | if (argv.includes("--object")) { 35 | suites.push(...objectBenchmarks.suites); 36 | } 37 | if (argv.includes("--union")) { 38 | suites.push(...unionBenchmarks.suites); 39 | } 40 | if (argv.includes("--discriminatedUnion")) { 41 | suites.push(...datetimeBenchmarks.suites); 42 | } 43 | if (argv.includes("--datetime")) { 44 | suites.push(...datetimeBenchmarks.suites); 45 | } 46 | if (argv.includes("--ipv4")) { 47 | suites.push(...ipv4Benchmarks.suites); 48 | } 49 | } 50 | 51 | for (const suite of suites) { 52 | suite.run({}); 53 | } 54 | 55 | // exit on Ctrl-C 56 | process.on("SIGINT", function () { 57 | console.log("Exiting..."); 58 | process.exit(); 59 | }); 60 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/ipv4.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | const suite = new Benchmark.Suite("ipv4"); 4 | 5 | const DATA = "127.0.0.1"; 6 | const ipv4RegexA = 7 | /^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/; 8 | const ipv4RegexB = 9 | /^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/; 10 | const ipv4RegexC = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/; 11 | const ipv4RegexD = /^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/; 12 | const ipv4RegexE = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/; 13 | const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/; 14 | const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/; 15 | const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/; 16 | const ipv4RegexI = 17 | /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; 18 | 19 | suite 20 | .add("A", () => { 21 | return ipv4RegexA.test(DATA); 22 | }) 23 | .add("B", () => { 24 | return ipv4RegexB.test(DATA); 25 | }) 26 | .add("C", () => { 27 | return ipv4RegexC.test(DATA); 28 | }) 29 | .add("D", () => { 30 | return ipv4RegexD.test(DATA); 31 | }) 32 | .add("E", () => { 33 | return ipv4RegexE.test(DATA); 34 | }) 35 | .add("F", () => { 36 | return ipv4RegexF.test(DATA); 37 | }) 38 | .add("G", () => { 39 | return ipv4RegexG.test(DATA); 40 | }) 41 | .add("H", () => { 42 | return ipv4RegexH.test(DATA); 43 | }) 44 | .add("I", () => { 45 | return ipv4RegexI.test(DATA); 46 | }) 47 | .on("cycle", (e: Benchmark.Event) => { 48 | console.log(`${suite.name!}: ${e.target}`); 49 | }); 50 | 51 | export default { 52 | suites: [suite], 53 | }; 54 | 55 | if (require.main === module) { 56 | suite.run(); 57 | } 58 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/object.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | import { z } from "zod/v3"; 4 | 5 | const emptySuite = new Benchmark.Suite("z.object: empty"); 6 | const shortSuite = new Benchmark.Suite("z.object: short"); 7 | const longSuite = new Benchmark.Suite("z.object: long"); 8 | 9 | const empty = z.object({}); 10 | const short = z.object({ 11 | string: z.string(), 12 | }); 13 | const long = z.object({ 14 | string: z.string(), 15 | number: z.number(), 16 | boolean: z.boolean(), 17 | }); 18 | 19 | emptySuite 20 | .add("valid", () => { 21 | empty.parse({}); 22 | }) 23 | .add("valid: extra keys", () => { 24 | empty.parse({ string: "string" }); 25 | }) 26 | .add("invalid: null", () => { 27 | try { 28 | empty.parse(null); 29 | } catch (_err) {} 30 | }) 31 | .on("cycle", (e: Benchmark.Event) => { 32 | console.log(`${(emptySuite as any).name}: ${e.target}`); 33 | }); 34 | 35 | shortSuite 36 | .add("valid", () => { 37 | short.parse({ string: "string" }); 38 | }) 39 | .add("valid: extra keys", () => { 40 | short.parse({ string: "string", number: 42 }); 41 | }) 42 | .add("invalid: null", () => { 43 | try { 44 | short.parse(null); 45 | } catch (_err) {} 46 | }) 47 | .on("cycle", (e: Benchmark.Event) => { 48 | console.log(`${(shortSuite as any).name}: ${e.target}`); 49 | }); 50 | 51 | longSuite 52 | .add("valid", () => { 53 | long.parse({ string: "string", number: 42, boolean: true }); 54 | }) 55 | .add("valid: extra keys", () => { 56 | long.parse({ string: "string", number: 42, boolean: true, list: [] }); 57 | }) 58 | .add("invalid: null", () => { 59 | try { 60 | long.parse(null); 61 | } catch (_err) {} 62 | }) 63 | .on("cycle", (e: Benchmark.Event) => { 64 | console.log(`${(longSuite as any).name}: ${e.target}`); 65 | }); 66 | 67 | export default { 68 | suites: [emptySuite, shortSuite, longSuite], 69 | }; 70 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/realworld.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | import { z } from "zod/v3"; 4 | 5 | const shortSuite = new Benchmark.Suite("realworld"); 6 | 7 | const People = z.array( 8 | z.object({ 9 | type: z.literal("person"), 10 | hair: z.enum(["blue", "brown"]), 11 | active: z.boolean(), 12 | name: z.string(), 13 | age: z.number().int(), 14 | hobbies: z.array(z.string()), 15 | address: z.object({ 16 | street: z.string(), 17 | zip: z.string(), 18 | country: z.string(), 19 | }), 20 | }) 21 | ); 22 | 23 | let i = 0; 24 | 25 | function num() { 26 | return ++i; 27 | } 28 | 29 | function str() { 30 | return (++i % 100).toString(16); 31 | } 32 | 33 | function array(fn: () => T): T[] { 34 | return Array.from({ length: ++i % 10 }, () => fn()); 35 | } 36 | 37 | const people = Array.from({ length: 100 }, () => { 38 | return { 39 | type: "person", 40 | hair: i % 2 ? "blue" : "brown", 41 | active: !!(i % 2), 42 | name: str(), 43 | age: num(), 44 | hobbies: array(str), 45 | address: { 46 | street: str(), 47 | zip: str(), 48 | country: str(), 49 | }, 50 | }; 51 | }); 52 | 53 | shortSuite 54 | .add("valid", () => { 55 | People.parse(people); 56 | }) 57 | .on("cycle", (e: Benchmark.Event) => { 58 | console.log(`${(shortSuite as any).name}: ${e.target}`); 59 | }); 60 | 61 | export default { 62 | suites: [shortSuite], 63 | }; 64 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/string.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | import { z } from "zod/v3"; 4 | 5 | const SUITE_NAME = "z.string"; 6 | const suite = new Benchmark.Suite(SUITE_NAME); 7 | 8 | const empty = ""; 9 | const short = "short"; 10 | const long = "long".repeat(256); 11 | const manual = (str: unknown) => { 12 | if (typeof str !== "string") { 13 | throw new Error("Not a string"); 14 | } 15 | 16 | return str; 17 | }; 18 | const stringSchema = z.string(); 19 | const optionalStringSchema = z.string().optional(); 20 | const optionalNullableStringSchema = z.string().optional().nullable(); 21 | 22 | suite 23 | .add("empty string", () => { 24 | stringSchema.parse(empty); 25 | }) 26 | .add("short string", () => { 27 | stringSchema.parse(short); 28 | }) 29 | .add("long string", () => { 30 | stringSchema.parse(long); 31 | }) 32 | .add("optional string", () => { 33 | optionalStringSchema.parse(long); 34 | }) 35 | .add("nullable string", () => { 36 | optionalNullableStringSchema.parse(long); 37 | }) 38 | .add("nullable (null) string", () => { 39 | optionalNullableStringSchema.parse(null); 40 | }) 41 | .add("invalid: null", () => { 42 | try { 43 | stringSchema.parse(null); 44 | } catch (_err) {} 45 | }) 46 | .add("manual parser: long", () => { 47 | manual(long); 48 | }) 49 | .on("cycle", (e: Benchmark.Event) => { 50 | console.log(`${SUITE_NAME}: ${e.target}`); 51 | }); 52 | 53 | export default { 54 | suites: [suite], 55 | }; 56 | -------------------------------------------------------------------------------- /packages/zod/src/v3/benchmarks/union.ts: -------------------------------------------------------------------------------- 1 | import Benchmark from "benchmark"; 2 | 3 | import { z } from "zod/v3"; 4 | 5 | const doubleSuite = new Benchmark.Suite("z.union: double"); 6 | const manySuite = new Benchmark.Suite("z.union: many"); 7 | 8 | const aSchema = z.object({ 9 | type: z.literal("a"), 10 | }); 11 | const objA = { 12 | type: "a", 13 | }; 14 | 15 | const bSchema = z.object({ 16 | type: z.literal("b"), 17 | }); 18 | const objB = { 19 | type: "b", 20 | }; 21 | 22 | const cSchema = z.object({ 23 | type: z.literal("c"), 24 | }); 25 | const objC = { 26 | type: "c", 27 | }; 28 | 29 | const dSchema = z.object({ 30 | type: z.literal("d"), 31 | }); 32 | 33 | const double = z.union([aSchema, bSchema]); 34 | const many = z.union([aSchema, bSchema, cSchema, dSchema]); 35 | 36 | doubleSuite 37 | .add("valid: a", () => { 38 | double.parse(objA); 39 | }) 40 | .add("valid: b", () => { 41 | double.parse(objB); 42 | }) 43 | .add("invalid: null", () => { 44 | try { 45 | double.parse(null); 46 | } catch (_err) {} 47 | }) 48 | .add("invalid: wrong shape", () => { 49 | try { 50 | double.parse(objC); 51 | } catch (_err) {} 52 | }) 53 | .on("cycle", (e: Benchmark.Event) => { 54 | console.log(`${(doubleSuite as any).name}: ${e.target}`); 55 | }); 56 | 57 | manySuite 58 | .add("valid: a", () => { 59 | many.parse(objA); 60 | }) 61 | .add("valid: c", () => { 62 | many.parse(objC); 63 | }) 64 | .add("invalid: null", () => { 65 | try { 66 | many.parse(null); 67 | } catch (_err) {} 68 | }) 69 | .add("invalid: wrong shape", () => { 70 | try { 71 | many.parse({ type: "unknown" }); 72 | } catch (_err) {} 73 | }) 74 | .on("cycle", (e: Benchmark.Event) => { 75 | console.log(`${(manySuite as any).name}: ${e.target}`); 76 | }); 77 | 78 | export default { 79 | suites: [doubleSuite, manySuite], 80 | }; 81 | -------------------------------------------------------------------------------- /packages/zod/src/v3/errors.ts: -------------------------------------------------------------------------------- 1 | import type { ZodErrorMap } from "./ZodError.js"; 2 | import defaultErrorMap from "./locales/en.js"; 3 | 4 | let overrideErrorMap = defaultErrorMap; 5 | export { defaultErrorMap }; 6 | 7 | export function setErrorMap(map: ZodErrorMap) { 8 | overrideErrorMap = map; 9 | } 10 | 11 | export function getErrorMap() { 12 | return overrideErrorMap; 13 | } 14 | -------------------------------------------------------------------------------- /packages/zod/src/v3/external.ts: -------------------------------------------------------------------------------- 1 | export * from "./errors.js"; 2 | export * from "./helpers/parseUtil.js"; 3 | export * from "./helpers/typeAliases.js"; 4 | export * from "./helpers/util.js"; 5 | export * from "./types.js"; 6 | export * from "./ZodError.js"; 7 | -------------------------------------------------------------------------------- /packages/zod/src/v3/helpers/enumUtil.ts: -------------------------------------------------------------------------------- 1 | export namespace enumUtil { 2 | type UnionToIntersectionFn = (T extends unknown ? (k: () => T) => void : never) extends ( 3 | k: infer Intersection 4 | ) => void 5 | ? Intersection 6 | : never; 7 | 8 | type GetUnionLast = UnionToIntersectionFn extends () => infer Last ? Last : never; 9 | 10 | type UnionToTuple = [T] extends [never] 11 | ? Tuple 12 | : UnionToTuple>, [GetUnionLast, ...Tuple]>; 13 | 14 | type CastToStringTuple = T extends [string, ...string[]] ? T : never; 15 | 16 | export type UnionToTupleString = CastToStringTuple>; 17 | } 18 | -------------------------------------------------------------------------------- /packages/zod/src/v3/helpers/errorUtil.ts: -------------------------------------------------------------------------------- 1 | export namespace errorUtil { 2 | export type ErrMessage = string | { message?: string | undefined }; 3 | export const errToObj = (message?: ErrMessage): { message?: string | undefined } => 4 | typeof message === "string" ? { message } : message || {}; 5 | // biome-ignore lint: 6 | export const toString = (message?: ErrMessage): string | undefined => 7 | typeof message === "string" ? message : message?.message; 8 | } 9 | -------------------------------------------------------------------------------- /packages/zod/src/v3/helpers/partialUtil.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ZodArray, 3 | ZodNullable, 4 | ZodObject, 5 | ZodOptional, 6 | ZodRawShape, 7 | ZodTuple, 8 | ZodTupleItems, 9 | ZodTypeAny, 10 | } from "../types.js"; 11 | 12 | export namespace partialUtil { 13 | export type DeepPartial = T extends ZodObject 14 | ? ZodObject< 15 | { [k in keyof T["shape"]]: ZodOptional> }, 16 | T["_def"]["unknownKeys"], 17 | T["_def"]["catchall"] 18 | > 19 | : T extends ZodArray 20 | ? ZodArray, Card> 21 | : T extends ZodOptional 22 | ? ZodOptional> 23 | : T extends ZodNullable 24 | ? ZodNullable> 25 | : T extends ZodTuple 26 | ? { 27 | [k in keyof Items]: Items[k] extends ZodTypeAny ? DeepPartial : never; 28 | } extends infer PI 29 | ? PI extends ZodTupleItems 30 | ? ZodTuple 31 | : never 32 | : never 33 | : T; 34 | } 35 | -------------------------------------------------------------------------------- /packages/zod/src/v3/helpers/typeAliases.ts: -------------------------------------------------------------------------------- 1 | export type Primitive = string | number | symbol | bigint | boolean | null | undefined; 2 | export type Scalars = Primitive | Primitive[]; 3 | -------------------------------------------------------------------------------- /packages/zod/src/v3/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from "./external.js"; 2 | export * from "./external.js"; 3 | export { z }; 4 | export default z; 5 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/Mocker.ts: -------------------------------------------------------------------------------- 1 | function getRandomInt(max: number) { 2 | return Math.floor(Math.random() * Math.floor(max)); 3 | } 4 | 5 | const testSymbol = Symbol("test"); 6 | 7 | export class Mocker { 8 | pick = (...args: any[]): any => { 9 | return args[getRandomInt(args.length)]; 10 | }; 11 | 12 | get string(): string { 13 | return Math.random().toString(36).substring(7); 14 | } 15 | get number(): number { 16 | return Math.random() * 100; 17 | } 18 | get bigint(): bigint { 19 | return BigInt(Math.floor(Math.random() * 10000)); 20 | } 21 | get boolean(): boolean { 22 | return Math.random() < 0.5; 23 | } 24 | get date(): Date { 25 | return new Date(Math.floor(Date.now() * Math.random())); 26 | } 27 | get symbol(): symbol { 28 | return testSymbol; 29 | } 30 | get null(): null { 31 | return null; 32 | } 33 | get undefined(): undefined { 34 | return undefined; 35 | } 36 | get stringOptional(): string | undefined { 37 | return this.pick(this.string, this.undefined); 38 | } 39 | get stringNullable(): string | null { 40 | return this.pick(this.string, this.null); 41 | } 42 | get numberOptional(): number | undefined { 43 | return this.pick(this.number, this.undefined); 44 | } 45 | get numberNullable(): number | null { 46 | return this.pick(this.number, this.null); 47 | } 48 | get booleanOptional(): boolean | undefined { 49 | return this.pick(this.boolean, this.undefined); 50 | } 51 | get booleanNullable(): boolean | null { 52 | return this.pick(this.boolean, this.null); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/anyunknown.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | 7 | test("check any inference", () => { 8 | const t1 = z.any(); 9 | t1.optional(); 10 | t1.nullable(); 11 | type t1 = z.infer; 12 | util.assertEqual(true); 13 | }); 14 | 15 | test("check unknown inference", () => { 16 | const t1 = z.unknown(); 17 | t1.optional(); 18 | t1.nullable(); 19 | type t1 = z.infer; 20 | util.assertEqual(true); 21 | }); 22 | 23 | test("check never inference", () => { 24 | const t1 = z.never(); 25 | expect(() => t1.parse(undefined)).toThrow(); 26 | expect(() => t1.parse("asdf")).toThrow(); 27 | expect(() => t1.parse(null)).toThrow(); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/async-refinements.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | test("parse async test", async () => { 7 | const schema1 = z.string().refine(async (_val) => false); 8 | expect(() => schema1.parse("asdf")).toThrow(); 9 | 10 | const schema2 = z.string().refine((_val) => Promise.resolve(true)); 11 | return await expect(() => schema2.parse("asdf")).toThrow(); 12 | }); 13 | 14 | test("parseAsync async test", async () => { 15 | const schema1 = z.string().refine(async (_val) => true); 16 | await schema1.parseAsync("asdf"); 17 | 18 | const schema2 = z.string().refine(async (_val) => false); 19 | return await expect(schema2.parseAsync("asdf")).rejects.toBeDefined(); 20 | // expect(async () => await schema2.parseAsync('asdf')).toThrow(); 21 | }); 22 | 23 | test("parseAsync async test", async () => { 24 | // expect.assertions(2); 25 | 26 | const schema1 = z.string().refine((_val) => Promise.resolve(true)); 27 | const v1 = await schema1.parseAsync("asdf"); 28 | expect(v1).toEqual("asdf"); 29 | 30 | const schema2 = z.string().refine((_val) => Promise.resolve(false)); 31 | await expect(schema2.parseAsync("asdf")).rejects.toBeDefined(); 32 | 33 | const schema3 = z.string().refine((_val) => Promise.resolve(true)); 34 | await expect(schema3.parseAsync("asdf")).resolves.toEqual("asdf"); 35 | return await expect(schema3.parseAsync("qwer")).resolves.toEqual("qwer"); 36 | }); 37 | 38 | test("parseAsync async with value", async () => { 39 | const schema1 = z.string().refine(async (val) => { 40 | return val.length > 5; 41 | }); 42 | await expect(schema1.parseAsync("asdf")).rejects.toBeDefined(); 43 | 44 | const v = await schema1.parseAsync("asdf123"); 45 | return await expect(v).toEqual("asdf123"); 46 | }); 47 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/base.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | 7 | test("type guard", () => { 8 | const stringToNumber = z.string().transform((arg) => arg.length); 9 | 10 | const s1 = z.object({ 11 | stringToNumber, 12 | }); 13 | type t1 = z.input; 14 | 15 | const data = { stringToNumber: "asdf" }; 16 | const parsed = s1.safeParse(data); 17 | if (parsed.success) { 18 | util.assertEqual(true); 19 | } 20 | }); 21 | 22 | test("test this binding", () => { 23 | const callback = (predicate: (val: string) => boolean) => { 24 | return predicate("hello"); 25 | }; 26 | 27 | expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true 28 | expect(callback((value) => z.string().safeParse(value).success)).toBe(true); // true 29 | }); 30 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/bigint.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | const gtFive = z.bigint().gt(BigInt(5)); 7 | const gteFive = z.bigint().gte(BigInt(5)); 8 | const ltFive = z.bigint().lt(BigInt(5)); 9 | const lteFive = z.bigint().lte(BigInt(5)); 10 | const positive = z.bigint().positive(); 11 | const negative = z.bigint().negative(); 12 | const nonnegative = z.bigint().nonnegative(); 13 | const nonpositive = z.bigint().nonpositive(); 14 | const multipleOfFive = z.bigint().multipleOf(BigInt(5)); 15 | 16 | test("passing validations", () => { 17 | z.bigint().parse(BigInt(1)); 18 | z.bigint().parse(BigInt(0)); 19 | z.bigint().parse(BigInt(-1)); 20 | gtFive.parse(BigInt(6)); 21 | gteFive.parse(BigInt(5)); 22 | gteFive.parse(BigInt(6)); 23 | ltFive.parse(BigInt(4)); 24 | lteFive.parse(BigInt(5)); 25 | lteFive.parse(BigInt(4)); 26 | positive.parse(BigInt(3)); 27 | negative.parse(BigInt(-2)); 28 | nonnegative.parse(BigInt(0)); 29 | nonnegative.parse(BigInt(7)); 30 | nonpositive.parse(BigInt(0)); 31 | nonpositive.parse(BigInt(-12)); 32 | multipleOfFive.parse(BigInt(15)); 33 | }); 34 | 35 | test("failing validations", () => { 36 | expect(() => gtFive.parse(BigInt(5))).toThrow(); 37 | expect(() => gteFive.parse(BigInt(4))).toThrow(); 38 | expect(() => ltFive.parse(BigInt(5))).toThrow(); 39 | expect(() => lteFive.parse(BigInt(6))).toThrow(); 40 | expect(() => positive.parse(BigInt(0))).toThrow(); 41 | expect(() => positive.parse(BigInt(-2))).toThrow(); 42 | expect(() => negative.parse(BigInt(0))).toThrow(); 43 | expect(() => negative.parse(BigInt(3))).toThrow(); 44 | expect(() => nonnegative.parse(BigInt(-1))).toThrow(); 45 | expect(() => nonpositive.parse(BigInt(1))).toThrow(); 46 | expect(() => multipleOfFive.parse(BigInt(13))).toThrow(); 47 | }); 48 | 49 | test("min max getters", () => { 50 | expect(z.bigint().min(BigInt(5)).minValue).toEqual(BigInt(5)); 51 | expect(z.bigint().min(BigInt(5)).min(BigInt(10)).minValue).toEqual(BigInt(10)); 52 | 53 | expect(z.bigint().max(BigInt(5)).maxValue).toEqual(BigInt(5)); 54 | expect(z.bigint().max(BigInt(5)).max(BigInt(1)).maxValue).toEqual(BigInt(1)); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/branded.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | 7 | test("branded types", () => { 8 | const mySchema = z 9 | .object({ 10 | name: z.string(), 11 | }) 12 | .brand<"superschema">(); 13 | 14 | // simple branding 15 | type MySchema = z.infer; 16 | util.assertEqual(true); 17 | 18 | const doStuff = (arg: MySchema) => arg; 19 | doStuff(mySchema.parse({ name: "hello there" })); 20 | 21 | // inheritance 22 | const extendedSchema = mySchema.brand<"subschema">(); 23 | type ExtendedSchema = z.infer; 24 | util.assertEqual & z.BRAND<"subschema">>(true); 25 | 26 | doStuff(extendedSchema.parse({ name: "hello again" })); 27 | 28 | // number branding 29 | const numberSchema = z.number().brand<42>(); 30 | type NumberSchema = z.infer; 31 | util.assertEqual(true); 32 | 33 | // symbol branding 34 | const MyBrand: unique symbol = Symbol("hello"); 35 | type MyBrand = typeof MyBrand; 36 | const symbolBrand = z.number().brand<"sup">().brand(); 37 | type SymbolBrand = z.infer; 38 | // number & { [z.BRAND]: { sup: true, [MyBrand]: true } } 39 | util.assertEqual & z.BRAND>(true); 40 | 41 | // keeping brands out of input types 42 | const age = z.number().brand<"age">(); 43 | 44 | type Age = z.infer; 45 | type AgeInput = z.input; 46 | 47 | util.assertEqual(false); 48 | util.assertEqual(true); 49 | util.assertEqual, Age>(true); 50 | 51 | // @ts-expect-error 52 | doStuff({ name: "hello there!" }); 53 | }); 54 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/complex.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from "vitest"; 2 | import * as z from "zod/v3"; 3 | 4 | const crazySchema = z.object({ 5 | tuple: z.tuple([ 6 | z.string().nullable().optional(), 7 | z.number().nullable().optional(), 8 | z.boolean().nullable().optional(), 9 | z.null().nullable().optional(), 10 | z.undefined().nullable().optional(), 11 | z.literal("1234").nullable().optional(), 12 | ]), 13 | merged: z 14 | .object({ 15 | k1: z.string().optional(), 16 | }) 17 | .merge(z.object({ k1: z.string().nullable(), k2: z.number() })), 18 | union: z.array(z.union([z.literal("asdf"), z.literal(12)])).nonempty(), 19 | array: z.array(z.number()), 20 | // sumTransformer: z.transformer(z.array(z.number()), z.number(), (arg) => { 21 | // return arg.reduce((a, b) => a + b, 0); 22 | // }), 23 | sumMinLength: z.array(z.number()).refine((arg) => arg.length > 5), 24 | intersection: z.intersection(z.object({ p1: z.string().optional() }), z.object({ p1: z.number().optional() })), 25 | enum: z.intersection(z.enum(["zero", "one"]), z.enum(["one", "two"])), 26 | nonstrict: z.object({ points: z.number() }).nonstrict(), 27 | numProm: z.promise(z.number()), 28 | lenfun: z.function(z.tuple([z.string()]), z.boolean()), 29 | }); 30 | 31 | // const asyncCrazySchema = crazySchema.extend({ 32 | // // async_transform: z.transformer( 33 | // // z.array(z.number()), 34 | // // z.number(), 35 | // // async (arg) => { 36 | // // return arg.reduce((a, b) => a + b, 0); 37 | // // } 38 | // // ), 39 | // async_refine: z.array(z.number()).refine(async (arg) => arg.length > 5), 40 | // }); 41 | 42 | test("parse", () => { 43 | crazySchema.parse({ 44 | tuple: ["asdf", 1234, true, null, undefined, "1234"], 45 | merged: { k1: "asdf", k2: 12 }, 46 | union: ["asdf", 12, "asdf", 12, "asdf", 12], 47 | array: [12, 15, 16], 48 | // sumTransformer: [12, 15, 16], 49 | sumMinLength: [12, 15, 16, 98, 24, 63], 50 | intersection: {}, 51 | enum: "one", 52 | nonstrict: { points: 1234 }, 53 | numProm: Promise.resolve(12), 54 | lenfun: (x: string) => x.length, 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/custom.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | test("passing validations", () => { 7 | const example1 = z.custom((x) => typeof x === "number"); 8 | example1.parse(1234); 9 | expect(() => example1.parse({})).toThrow(); 10 | }); 11 | 12 | test("string params", () => { 13 | const example1 = z.custom((x) => typeof x !== "number", "customerr"); 14 | const result = example1.safeParse(1234); 15 | expect(result.success).toEqual(false); 16 | // @ts-ignore 17 | expect(JSON.stringify(result.error).includes("customerr")).toEqual(true); 18 | }); 19 | 20 | test("async validations", async () => { 21 | const example1 = z.custom(async (x) => { 22 | return typeof x === "number"; 23 | }); 24 | const r1 = await example1.safeParseAsync(1234); 25 | expect(r1.success).toEqual(true); 26 | expect(r1.data).toEqual(1234); 27 | 28 | const r2 = await example1.safeParseAsync("asdf"); 29 | expect(r2.success).toEqual(false); 30 | expect(r2.error!.issues.length).toEqual(1); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/date.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | const beforeBenchmarkDate = new Date(2022, 10, 4); 7 | const benchmarkDate = new Date(2022, 10, 5); 8 | const afterBenchmarkDate = new Date(2022, 10, 6); 9 | 10 | const minCheck = z.date().min(benchmarkDate); 11 | const maxCheck = z.date().max(benchmarkDate); 12 | 13 | test("passing validations", () => { 14 | minCheck.parse(benchmarkDate); 15 | minCheck.parse(afterBenchmarkDate); 16 | 17 | maxCheck.parse(benchmarkDate); 18 | maxCheck.parse(beforeBenchmarkDate); 19 | }); 20 | 21 | test("failing validations", () => { 22 | expect(() => minCheck.parse(beforeBenchmarkDate)).toThrow(); 23 | expect(() => maxCheck.parse(afterBenchmarkDate)).toThrow(); 24 | }); 25 | 26 | test("min max getters", () => { 27 | expect(minCheck.minDate).toEqual(benchmarkDate); 28 | expect(minCheck.min(afterBenchmarkDate).minDate).toEqual(afterBenchmarkDate); 29 | 30 | expect(maxCheck.maxDate).toEqual(benchmarkDate); 31 | expect(maxCheck.max(beforeBenchmarkDate).maxDate).toEqual(beforeBenchmarkDate); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/description.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | const description = "a description"; 7 | 8 | test("passing `description` to schema should add a description", () => { 9 | expect(z.string({ description }).description).toEqual(description); 10 | expect(z.number({ description }).description).toEqual(description); 11 | expect(z.boolean({ description }).description).toEqual(description); 12 | }); 13 | 14 | test("`.describe` should add a description", () => { 15 | expect(z.string().describe(description).description).toEqual(description); 16 | expect(z.number().describe(description).description).toEqual(description); 17 | expect(z.boolean().describe(description).description).toEqual(description); 18 | }); 19 | 20 | test("description should carry over to chained schemas", () => { 21 | const schema = z.string({ description }); 22 | expect(schema.description).toEqual(description); 23 | expect(schema.optional().description).toEqual(description); 24 | expect(schema.optional().nullable().default("default").description).toEqual(description); 25 | }); 26 | 27 | test("description should not carry over to chained array schema", () => { 28 | const schema = z.string().describe(description); 29 | 30 | expect(schema.description).toEqual(description); 31 | expect(schema.array().description).toEqual(undefined); 32 | expect(z.array(schema).description).toEqual(undefined); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/firstpartyschematypes.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { test } from "vitest"; 3 | 4 | import type { ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind } from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | 7 | test("Identify missing [ZodFirstPartySchemaTypes]", () => { 8 | type ZodFirstPartySchemaForType = ZodFirstPartySchemaTypes extends infer Schema 9 | ? Schema extends { _def: { typeName: T } } 10 | ? Schema 11 | : never 12 | : never; 13 | type ZodMappedTypes = { 14 | [key in ZodFirstPartyTypeKind]: ZodFirstPartySchemaForType; 15 | }; 16 | type ZodFirstPartySchemaTypesMissingFromUnion = keyof { 17 | [key in keyof ZodMappedTypes as ZodMappedTypes[key] extends { _def: never } ? key : never]: unknown; 18 | }; 19 | 20 | util.assertEqual(true); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/generics.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | 7 | test("generics", () => { 8 | async function stripOuter(schema: TData, data: unknown) { 9 | return z 10 | .object({ 11 | nested: schema, // as z.ZodTypeAny, 12 | }) 13 | .transform((data) => { 14 | return data.nested!; 15 | }) 16 | .parse({ nested: data }); 17 | } 18 | 19 | const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" }); 20 | util.assertEqual>(true); 21 | }); 22 | 23 | // test("assignability", () => { 24 | // const createSchemaAndParse = ( 25 | // key: K, 26 | // valueSchema: VS, 27 | // data: unknown 28 | // ) => { 29 | // const schema = z.object({ 30 | // [key]: valueSchema, 31 | // } as { [k in K]: VS }); 32 | // return { [key]: valueSchema }; 33 | // const parsed = schema.parse(data); 34 | // return parsed; 35 | // // const inferred: z.infer> = parsed; 36 | // // return inferred; 37 | // }; 38 | // const parsed = createSchemaAndParse("foo", z.string(), { foo: "" }); 39 | // util.assertEqual(true); 40 | // }); 41 | 42 | test("nested no undefined", () => { 43 | const inner = z.string().or(z.array(z.string())); 44 | const outer = z.object({ inner }); 45 | type outerSchema = z.infer; 46 | z.util.assertEqual(true); 47 | expect(outer.safeParse({ inner: undefined }).success).toEqual(false); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/instanceof.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | 7 | test("instanceof", async () => { 8 | class Test {} 9 | class Subtest extends Test {} 10 | abstract class AbstractBar { 11 | constructor(public val: string) {} 12 | } 13 | class Bar extends AbstractBar {} 14 | 15 | const TestSchema = z.instanceof(Test); 16 | const SubtestSchema = z.instanceof(Subtest); 17 | const AbstractSchema = z.instanceof(AbstractBar); 18 | const BarSchema = z.instanceof(Bar); 19 | 20 | TestSchema.parse(new Test()); 21 | TestSchema.parse(new Subtest()); 22 | SubtestSchema.parse(new Subtest()); 23 | AbstractSchema.parse(new Bar("asdf")); 24 | const bar = BarSchema.parse(new Bar("asdf")); 25 | expect(bar.val).toEqual("asdf"); 26 | 27 | await expect(() => SubtestSchema.parse(new Test())).toThrow(/Input not instance of Subtest/); 28 | await expect(() => TestSchema.parse(12)).toThrow(/Input not instance of Test/); 29 | 30 | util.assertEqual>(true); 31 | }); 32 | 33 | test("instanceof fatal", () => { 34 | const schema = z.instanceof(Date).refine((d) => d.toString()); 35 | const res = schema.safeParse(null); 36 | expect(res.success).toBe(false); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/language-server.source.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod/v3"; 2 | 3 | export const filePath = __filename; 4 | 5 | // z.object() 6 | 7 | export const Test = z.object({ 8 | f1: z.number(), 9 | }); 10 | 11 | export type Test = z.infer; 12 | 13 | export const instanceOfTest: Test = { 14 | f1: 1, 15 | }; 16 | 17 | // z.object().merge() 18 | 19 | export const TestMerge = z 20 | .object({ 21 | f2: z.string().optional(), 22 | }) 23 | .merge(Test); 24 | 25 | export type TestMerge = z.infer; 26 | 27 | export const instanceOfTestMerge: TestMerge = { 28 | f1: 1, 29 | f2: "string", 30 | }; 31 | 32 | // z.union() 33 | 34 | export const TestUnion = z.union([ 35 | z.object({ 36 | f2: z.string().optional(), 37 | }), 38 | Test, 39 | ]); 40 | 41 | export type TestUnion = z.infer; 42 | 43 | export const instanceOfTestUnion: TestUnion = { 44 | f1: 1, 45 | f2: "string", 46 | }; 47 | 48 | // z.object().partial() 49 | 50 | export const TestPartial = Test.partial(); 51 | 52 | export type TestPartial = z.infer; 53 | 54 | export const instanceOfTestPartial: TestPartial = { 55 | f1: 1, 56 | }; 57 | 58 | // z.object().pick() 59 | 60 | export const TestPick = TestMerge.pick({ f1: true }); 61 | 62 | export type TestPick = z.infer; 63 | 64 | export const instanceOfTestPick: TestPick = { 65 | f1: 1, 66 | }; 67 | 68 | // z.object().omit() 69 | 70 | export const TestOmit = TestMerge.omit({ f2: true }); 71 | 72 | export type TestOmit = z.infer; 73 | 74 | export const instanceOfTestOmit: TestOmit = { 75 | f1: 1, 76 | }; 77 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/literal.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | const literalTuna = z.literal("tuna"); 7 | const literalFortyTwo = z.literal(42); 8 | const literalTrue = z.literal(true); 9 | 10 | const terrificSymbol = Symbol("terrific"); 11 | const literalTerrificSymbol = z.literal(terrificSymbol); 12 | 13 | test("passing validations", () => { 14 | literalTuna.parse("tuna"); 15 | literalFortyTwo.parse(42); 16 | literalTrue.parse(true); 17 | literalTerrificSymbol.parse(terrificSymbol); 18 | }); 19 | 20 | test("failing validations", () => { 21 | expect(() => literalTuna.parse("shark")).toThrow(); 22 | expect(() => literalFortyTwo.parse(43)).toThrow(); 23 | expect(() => literalTrue.parse(false)).toThrow(); 24 | expect(() => literalTerrificSymbol.parse(Symbol("terrific"))).toThrow(); 25 | }); 26 | 27 | test("invalid_literal should have `received` field with data", () => { 28 | const data = "shark"; 29 | const result = literalTuna.safeParse(data); 30 | if (!result.success) { 31 | const issue = result.error.issues[0]; 32 | if (issue.code === "invalid_literal") { 33 | expect(issue.received).toBe(data); 34 | } 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/masking.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { test } from "vitest"; 3 | 4 | test("masking test", () => {}); 5 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/mocker.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { test } from "vitest"; 3 | 4 | import { Mocker } from "./Mocker.js"; 5 | 6 | test("mocker", () => { 7 | const mocker = new Mocker(); 8 | mocker.string; 9 | mocker.number; 10 | mocker.boolean; 11 | mocker.null; 12 | mocker.undefined; 13 | mocker.stringOptional; 14 | mocker.stringNullable; 15 | mocker.numberOptional; 16 | mocker.numberNullable; 17 | mocker.booleanOptional; 18 | mocker.booleanNullable; 19 | }); 20 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/nan.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | const schema = z.nan(); 7 | 8 | test("passing validations", () => { 9 | schema.parse(Number.NaN); 10 | schema.parse(Number("Not a number")); 11 | }); 12 | 13 | test("failing validations", () => { 14 | expect(() => schema.parse(5)).toThrow(); 15 | expect(() => schema.parse("John")).toThrow(); 16 | expect(() => schema.parse(true)).toThrow(); 17 | expect(() => schema.parse(null)).toThrow(); 18 | expect(() => schema.parse(undefined)).toThrow(); 19 | expect(() => schema.parse({})).toThrow(); 20 | expect(() => schema.parse([])).toThrow(); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/nullable.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | function checkErrors(a: z.ZodTypeAny, bad: any) { 7 | let expected: any; 8 | try { 9 | a.parse(bad); 10 | } catch (error) { 11 | expected = (error as z.ZodError).formErrors; 12 | } 13 | try { 14 | a.nullable().parse(bad); 15 | } catch (error) { 16 | expect((error as z.ZodError).formErrors).toEqual(expected); 17 | } 18 | } 19 | 20 | test("Should have error messages appropriate for the underlying type", () => { 21 | checkErrors(z.string().min(2), 1); 22 | z.string().min(2).nullable().parse(null); 23 | checkErrors(z.number().gte(2), 1); 24 | z.number().gte(2).nullable().parse(null); 25 | checkErrors(z.boolean(), ""); 26 | z.boolean().nullable().parse(null); 27 | checkErrors(z.null(), null); 28 | z.null().nullable().parse(null); 29 | checkErrors(z.null(), {}); 30 | z.null().nullable().parse(null); 31 | checkErrors(z.object({}), 1); 32 | z.object({}).nullable().parse(null); 33 | checkErrors(z.tuple([]), 1); 34 | z.tuple([]).nullable().parse(null); 35 | checkErrors(z.unknown(), 1); 36 | z.unknown().nullable().parse(null); 37 | }); 38 | 39 | test("unwrap", () => { 40 | const unwrapped = z.string().nullable().unwrap(); 41 | expect(unwrapped).toBeInstanceOf(z.ZodString); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/object-augmentation.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | test("object augmentation", () => { 7 | const Animal = z 8 | .object({ 9 | species: z.string(), 10 | }) 11 | .augment({ 12 | population: z.number(), 13 | }); 14 | // overwrites `species` 15 | const ModifiedAnimal = Animal.augment({ 16 | species: z.array(z.string()), 17 | }); 18 | ModifiedAnimal.parse({ 19 | species: ["asd"], 20 | population: 1324, 21 | }); 22 | 23 | const bad = () => 24 | ModifiedAnimal.parse({ 25 | species: "asdf", 26 | population: 1324, 27 | } as any); 28 | expect(bad).toThrow(); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/object-in-es5-env.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | const RealSet = Set; 7 | const RealMap = Map; 8 | const RealDate = Date; 9 | 10 | test("doesn’t throw when Date is undefined", () => { 11 | delete (globalThis as any).Date; 12 | const result = z.object({}).safeParse({}); 13 | expect(result.success).toEqual(true); 14 | globalThis.Date = RealDate; 15 | }); 16 | 17 | test("doesn’t throw when Set is undefined", () => { 18 | delete (globalThis as any).Set; 19 | const result = z.object({}).safeParse({}); 20 | expect(result.success).toEqual(true); 21 | globalThis.Set = RealSet; 22 | }); 23 | 24 | test("doesn’t throw when Map is undefined", () => { 25 | delete (globalThis as any).Map; 26 | const result = z.object({}).safeParse({}); 27 | expect(result.success).toEqual(true); 28 | globalThis.Map = RealMap; 29 | }); 30 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/optional.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | function checkErrors(a: z.ZodTypeAny, bad: any) { 7 | let expected: any; 8 | try { 9 | a.parse(bad); 10 | } catch (error) { 11 | expected = (error as z.ZodError).formErrors; 12 | } 13 | try { 14 | a.optional().parse(bad); 15 | } catch (error) { 16 | expect((error as z.ZodError).formErrors).toEqual(expected); 17 | } 18 | } 19 | 20 | test("Should have error messages appropriate for the underlying type", () => { 21 | checkErrors(z.string().min(2), 1); 22 | z.string().min(2).optional().parse(undefined); 23 | checkErrors(z.number().gte(2), 1); 24 | z.number().gte(2).optional().parse(undefined); 25 | checkErrors(z.boolean(), ""); 26 | z.boolean().optional().parse(undefined); 27 | checkErrors(z.undefined(), null); 28 | z.undefined().optional().parse(undefined); 29 | checkErrors(z.null(), {}); 30 | z.null().optional().parse(undefined); 31 | checkErrors(z.object({}), 1); 32 | z.object({}).optional().parse(undefined); 33 | checkErrors(z.tuple([]), 1); 34 | z.tuple([]).optional().parse(undefined); 35 | checkErrors(z.unknown(), 1); 36 | z.unknown().optional().parse(undefined); 37 | }); 38 | 39 | test("unwrap", () => { 40 | const unwrapped = z.string().optional().unwrap(); 41 | expect(unwrapped).toBeInstanceOf(z.ZodString); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/parseUtil.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import { type SyncParseReturnType, isAborted, isDirty, isValid } from "../helpers/parseUtil.js"; 5 | 6 | test("parseUtil isInvalid should use structural typing", () => { 7 | // Test for issue #556: https://github.com/colinhacks/zod/issues/556 8 | const aborted: SyncParseReturnType = { status: "aborted" }; 9 | const dirty: SyncParseReturnType = { status: "dirty", value: "whatever" }; 10 | const valid: SyncParseReturnType = { status: "valid", value: "whatever" }; 11 | 12 | expect(isAborted(aborted)).toBe(true); 13 | expect(isAborted(dirty)).toBe(false); 14 | expect(isAborted(valid)).toBe(false); 15 | 16 | expect(isDirty(aborted)).toBe(false); 17 | expect(isDirty(dirty)).toBe(true); 18 | expect(isDirty(valid)).toBe(false); 19 | 20 | expect(isValid(aborted)).toBe(false); 21 | expect(isValid(dirty)).toBe(false); 22 | expect(isValid(valid)).toBe(true); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/parser.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | test("parse strict object with unknown keys", () => { 7 | expect(() => 8 | z 9 | .object({ name: z.string() }) 10 | .strict() 11 | .parse({ name: "bill", unknownKey: 12 } as any) 12 | ).toThrow(); 13 | }); 14 | 15 | test("parse nonstrict object with unknown keys", () => { 16 | z.object({ name: z.string() }).nonstrict().parse({ name: "bill", unknownKey: 12 }); 17 | }); 18 | 19 | test("invalid left side of intersection", () => { 20 | expect(() => z.intersection(z.string(), z.number()).parse(12 as any)).toThrow(); 21 | }); 22 | 23 | test("invalid right side of intersection", () => { 24 | expect(() => z.intersection(z.string(), z.number()).parse("12" as any)).toThrow(); 25 | }); 26 | 27 | test("parsing non-array in tuple schema", () => { 28 | expect(() => z.tuple([]).parse("12" as any)).toThrow(); 29 | }); 30 | 31 | test("incorrect num elements in tuple", () => { 32 | expect(() => z.tuple([]).parse(["asdf"] as any)).toThrow(); 33 | }); 34 | 35 | test("invalid enum value", () => { 36 | expect(() => z.enum(["Blue"]).parse("Red" as any)).toThrow(); 37 | }); 38 | 39 | test("parsing unknown", () => { 40 | z.string().parse("Red" as unknown); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/pipeline.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | test("string to number pipeline", () => { 7 | const schema = z.string().transform(Number).pipe(z.number()); 8 | expect(schema.parse("1234")).toEqual(1234); 9 | }); 10 | 11 | test("string to number pipeline async", async () => { 12 | const schema = z 13 | .string() 14 | .transform(async (val) => Number(val)) 15 | .pipe(z.number()); 16 | expect(await schema.parseAsync("1234")).toEqual(1234); 17 | }); 18 | 19 | test("break if dirty", () => { 20 | const schema = z 21 | .string() 22 | .refine((c) => c === "1234") 23 | .transform(async (val) => Number(val)) 24 | .pipe(z.number().refine((v) => v < 100)); 25 | const r1: any = schema.safeParse("12345"); 26 | expect(r1.error.issues.length).toBe(1); 27 | const r2: any = schema.safeParse("3"); 28 | expect(r2.error.issues.length).toBe(1); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/safeparse.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | const stringSchema = z.string(); 6 | 7 | test("safeparse fail", () => { 8 | const safe = stringSchema.safeParse(12); 9 | expect(safe.success).toEqual(false); 10 | expect(safe.error).toBeInstanceOf(z.ZodError); 11 | }); 12 | 13 | test("safeparse pass", () => { 14 | const safe = stringSchema.safeParse("12"); 15 | expect(safe.success).toEqual(true); 16 | expect(safe.data).toEqual("12"); 17 | }); 18 | 19 | test("safeparse unexpected error", () => { 20 | expect(() => 21 | stringSchema 22 | .refine((data) => { 23 | throw new Error(data); 24 | }) 25 | .safeParse("12") 26 | ).toThrow(); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/unions.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | 6 | test("function parsing", () => { 7 | const schema = z.union([z.string().refine(() => false), z.number().refine(() => false)]); 8 | const result = schema.safeParse("asdf"); 9 | expect(result.success).toEqual(false); 10 | }); 11 | 12 | test("union 2", () => { 13 | const result = z.union([z.number(), z.string().refine(() => false)]).safeParse("a"); 14 | expect(result.success).toEqual(false); 15 | }); 16 | 17 | test("return valid over invalid", () => { 18 | const schema = z.union([ 19 | z.object({ 20 | email: z.string().email(), 21 | }), 22 | z.string(), 23 | ]); 24 | expect(schema.parse("asdf")).toEqual("asdf"); 25 | expect(schema.parse({ email: "asdlkjf@lkajsdf.com" })).toEqual({ 26 | email: "asdlkjf@lkajsdf.com", 27 | }); 28 | }); 29 | 30 | test("return dirty result over aborted", () => { 31 | const result = z.union([z.number(), z.string().refine(() => false)]).safeParse("a"); 32 | expect(result.success).toEqual(false); 33 | if (!result.success) { 34 | expect(result.error.issues).toEqual([ 35 | { 36 | code: "custom", 37 | message: "Invalid input", 38 | path: [], 39 | }, 40 | ]); 41 | } 42 | }); 43 | 44 | test("options getter", async () => { 45 | const union = z.union([z.string(), z.number()]); 46 | union.options[0].parse("asdf"); 47 | union.options[1].parse(1234); 48 | await union.options[0].parseAsync("asdf"); 49 | await union.options[1].parseAsync(1234); 50 | }); 51 | 52 | test("readonly union", async () => { 53 | const options = [z.string(), z.number()] as const; 54 | const union = z.union(options); 55 | union.parse("asdf"); 56 | union.parse(12); 57 | }); 58 | -------------------------------------------------------------------------------- /packages/zod/src/v3/tests/void.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore TS6133 2 | import { expect, test } from "vitest"; 3 | 4 | import * as z from "zod/v3"; 5 | import { util } from "../helpers/util.js"; 6 | test("void", () => { 7 | const v = z.void(); 8 | v.parse(undefined); 9 | 10 | expect(() => v.parse(null)).toThrow(); 11 | expect(() => v.parse("")).toThrow(); 12 | 13 | type v = z.infer; 14 | util.assertEqual(true); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/checks.ts: -------------------------------------------------------------------------------- 1 | export { 2 | _lt as lt, 3 | _lte as lte, 4 | _gt as gt, 5 | _gte as gte, 6 | _positive as positive, 7 | _negative as negative, 8 | _nonpositive as nonpositive, 9 | _nonnegative as nonnegative, 10 | _multipleOf as multipleOf, 11 | _maxSize as maxSize, 12 | _minSize as minSize, 13 | _size as size, 14 | _maxLength as maxLength, 15 | _minLength as minLength, 16 | _length as length, 17 | _regex as regex, 18 | _lowercase as lowercase, 19 | _uppercase as uppercase, 20 | _includes as includes, 21 | _startsWith as startsWith, 22 | _endsWith as endsWith, 23 | _property as property, 24 | _mime as mime, 25 | _overwrite as overwrite, 26 | _normalize as normalize, 27 | _trim as trim, 28 | _toLowerCase as toLowerCase, 29 | _toUpperCase as toUpperCase, 30 | } from "zod/v4/core"; 31 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/coerce.ts: -------------------------------------------------------------------------------- 1 | import * as core from "zod/v4/core"; 2 | import * as schemas from "./schemas.js"; 3 | 4 | export interface ZodCoercedString extends schemas._ZodString {} 5 | export function string(params?: string | core.$ZodStringParams): ZodCoercedString { 6 | return core._coercedString(schemas.ZodString, params) as any; 7 | } 8 | 9 | export interface ZodCoercedNumber extends schemas._ZodNumber {} 10 | export function number(params?: string | core.$ZodNumberParams): ZodCoercedNumber { 11 | return core._coercedNumber(schemas.ZodNumber, params) as ZodCoercedNumber; 12 | } 13 | 14 | export interface ZodCoercedBoolean extends schemas._ZodBoolean {} 15 | export function boolean(params?: string | core.$ZodBooleanParams): ZodCoercedBoolean { 16 | return core._coercedBoolean(schemas.ZodBoolean, params) as ZodCoercedBoolean; 17 | } 18 | 19 | export interface ZodCoercedBigInt extends schemas._ZodBigInt {} 20 | export function bigint(params?: string | core.$ZodBigIntParams): ZodCoercedBigInt { 21 | return core._coercedBigint(schemas.ZodBigInt, params) as ZodCoercedBigInt; 22 | } 23 | 24 | export interface ZodCoercedDate extends schemas._ZodDate {} 25 | export function date(params?: string | core.$ZodDateParams): ZodCoercedDate { 26 | return core._coercedDate(schemas.ZodDate, params) as ZodCoercedDate; 27 | } 28 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/external.ts: -------------------------------------------------------------------------------- 1 | export * as core from "zod/v4/core"; 2 | export * from "./schemas.js"; 3 | export * from "./checks.js"; 4 | export * from "./errors.js"; 5 | export * from "./parse.js"; 6 | export * from "./compat.js"; 7 | 8 | // zod-specified 9 | import { config } from "zod/v4/core"; 10 | import en from "zod/v4/locales/en.js"; 11 | config(en()); 12 | 13 | export type { infer, output, input } from "zod/v4/core"; 14 | export { 15 | globalRegistry, 16 | type GlobalMeta, 17 | registry, 18 | config, 19 | function, 20 | $output, 21 | $input, 22 | $brand, 23 | clone, 24 | regexes, 25 | treeifyError, 26 | prettifyError, 27 | formatError, 28 | flattenError, 29 | toJSONSchema, 30 | } from "zod/v4/core"; 31 | 32 | export * as locales from "../locales/index.js"; 33 | 34 | // iso 35 | // must be exported from top-level 36 | // https://github.com/colinhacks/zod/issues/4491 37 | export { ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration } from "./iso.js"; 38 | export * as iso from "./iso.js"; 39 | 40 | // coerce 41 | export type { 42 | ZodCoercedString, 43 | ZodCoercedNumber, 44 | ZodCoercedBigInt, 45 | ZodCoercedBoolean, 46 | ZodCoercedDate, 47 | } from "./coerce.js"; 48 | export * as coerce from "./coerce.js"; 49 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from "./external.js"; 2 | 3 | export { z }; 4 | export * from "./external.js"; 5 | export default z; 6 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/parse.ts: -------------------------------------------------------------------------------- 1 | import * as core from "zod/v4/core"; 2 | import { type ZodError, ZodRealError } from "./errors.js"; 3 | 4 | export type ZodSafeParseResult = ZodSafeParseSuccess | ZodSafeParseError; 5 | export type ZodSafeParseSuccess = { success: true; data: T; error?: never }; 6 | export type ZodSafeParseError = { success: false; data?: never; error: ZodError }; 7 | 8 | export const parse: ( 9 | schema: T, 10 | value: unknown, 11 | _ctx?: core.ParseContext, 12 | _params?: { callee?: core.util.AnyFunc; Err?: core.$ZodErrorClass } 13 | ) => core.output = /* @__PURE__ */ core._parse(ZodRealError) as any; 14 | 15 | export const parseAsync: ( 16 | schema: T, 17 | value: unknown, 18 | _ctx?: core.ParseContext, 19 | _params?: { callee?: core.util.AnyFunc; Err?: core.$ZodErrorClass } 20 | ) => Promise> = /* @__PURE__ */ core._parseAsync(ZodRealError) as any; 21 | 22 | export const safeParse: ( 23 | schema: T, 24 | value: unknown, 25 | _ctx?: core.ParseContext 26 | // _params?: { callee?: core.util.AnyFunc; Err?: core.$ZodErrorClass } 27 | ) => ZodSafeParseResult> = /* @__PURE__ */ core._safeParse(ZodRealError) as any; 28 | 29 | export const safeParseAsync: ( 30 | schema: T, 31 | value: unknown, 32 | _ctx?: core.ParseContext 33 | ) => Promise>> = /* @__PURE__ */ core._safeParseAsync(ZodRealError) as any; 34 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/anyunknown.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | test("check any inference", () => { 6 | const t1 = z.any(); 7 | t1.optional(); 8 | t1.nullable(); 9 | type t1 = z.infer; 10 | expectTypeOf().toEqualTypeOf(); 11 | }); 12 | 13 | test("check unknown inference", () => { 14 | const t1 = z.unknown(); 15 | t1.optional(); 16 | t1.nullable(); 17 | type t1 = z.infer; 18 | expectTypeOf().toEqualTypeOf(); 19 | }); 20 | 21 | test("check never inference", () => { 22 | const t1 = z.never(); 23 | expect(() => t1.parse(undefined)).toThrow(); 24 | expect(() => t1.parse("asdf")).toThrow(); 25 | expect(() => t1.parse(null)).toThrow(); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/async-refinements.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | test("async refine .parse()", async () => { 6 | // throws ZodAsyncError 7 | const s1 = z.string().refine(async (_val) => true); 8 | expect(() => s1.safeParse("asdf")).toThrow(); 9 | }); 10 | 11 | test("async refine", async () => { 12 | const s1 = z.string().refine(async (_val) => true); 13 | const r1 = await s1.parseAsync("asdf"); 14 | expect(r1).toEqual("asdf"); 15 | 16 | const s2 = z.string().refine(async (_val) => false); 17 | const r2 = await s2.safeParseAsync("asdf"); 18 | expect(r2.success).toBe(false); 19 | expect(r2).toMatchInlineSnapshot(` 20 | { 21 | "error": [ZodError: [ 22 | { 23 | "code": "custom", 24 | "path": [], 25 | "message": "Invalid input" 26 | } 27 | ]], 28 | "success": false, 29 | } 30 | `); 31 | }); 32 | 33 | test("async refine with Promises", async () => { 34 | // expect.assertions(2); 35 | 36 | const schema1 = z.string().refine((_val) => Promise.resolve(true)); 37 | const v1 = await schema1.parseAsync("asdf"); 38 | expect(v1).toEqual("asdf"); 39 | 40 | const schema2 = z.string().refine((_val) => Promise.resolve(false)); 41 | await expect(schema2.parseAsync("asdf")).rejects.toBeDefined(); 42 | 43 | const schema3 = z.string().refine((_val) => Promise.resolve(true)); 44 | await expect(schema3.parseAsync("asdf")).resolves.toEqual("asdf"); 45 | return await expect(schema3.parseAsync("qwer")).resolves.toEqual("qwer"); 46 | }); 47 | 48 | test("async refine that uses value", async () => { 49 | const schema1 = z.string().refine(async (val) => { 50 | return val.length > 5; 51 | }); 52 | 53 | const r1 = await schema1.safeParseAsync("asdf"); 54 | expect(r1.success).toBe(false); 55 | expect(r1.error).toMatchInlineSnapshot(` 56 | [ZodError: [ 57 | { 58 | "code": "custom", 59 | "path": [], 60 | "message": "Invalid input" 61 | } 62 | ]] 63 | `); 64 | 65 | const r2 = await schema1.safeParseAsync("asdf123"); 66 | expect(r2.success).toBe(true); 67 | expect(r2.data).toEqual("asdf123"); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/base.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import * as z from "zod/v4"; 3 | 4 | test("test this binding", () => { 5 | const parse = z.string().parse; 6 | expect(parse("asdf")).toBe("asdf"); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/bigint.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | const gtFive = z.bigint().gt(BigInt(5)); 6 | const gteFive = z.bigint().gte(BigInt(5)); 7 | const ltFive = z.bigint().lt(BigInt(5)); 8 | const lteFive = z.bigint().lte(BigInt(5)); 9 | const positive = z.bigint().positive(); 10 | const negative = z.bigint().negative(); 11 | const nonnegative = z.bigint().nonnegative(); 12 | const nonpositive = z.bigint().nonpositive(); 13 | const multipleOfFive = z.bigint().multipleOf(BigInt(5)); 14 | 15 | test("passing validations", () => { 16 | z.bigint().parse(BigInt(1)); 17 | z.bigint().parse(BigInt(0)); 18 | z.bigint().parse(BigInt(-1)); 19 | gtFive.parse(BigInt(6)); 20 | gteFive.parse(BigInt(5)); 21 | gteFive.parse(BigInt(6)); 22 | ltFive.parse(BigInt(4)); 23 | lteFive.parse(BigInt(5)); 24 | lteFive.parse(BigInt(4)); 25 | positive.parse(BigInt(3)); 26 | negative.parse(BigInt(-2)); 27 | nonnegative.parse(BigInt(0)); 28 | nonnegative.parse(BigInt(7)); 29 | nonpositive.parse(BigInt(0)); 30 | nonpositive.parse(BigInt(-12)); 31 | multipleOfFive.parse(BigInt(15)); 32 | }); 33 | 34 | test("failing validations", () => { 35 | expect(() => gtFive.parse(BigInt(5))).toThrow(); 36 | expect(() => gteFive.parse(BigInt(4))).toThrow(); 37 | expect(() => ltFive.parse(BigInt(5))).toThrow(); 38 | expect(() => lteFive.parse(BigInt(6))).toThrow(); 39 | expect(() => positive.parse(BigInt(0))).toThrow(); 40 | expect(() => positive.parse(BigInt(-2))).toThrow(); 41 | expect(() => negative.parse(BigInt(0))).toThrow(); 42 | expect(() => negative.parse(BigInt(3))).toThrow(); 43 | expect(() => nonnegative.parse(BigInt(-1))).toThrow(); 44 | expect(() => nonpositive.parse(BigInt(1))).toThrow(); 45 | expect(() => multipleOfFive.parse(BigInt(13))).toThrow(); 46 | }); 47 | 48 | test("min max getters", () => { 49 | expect(z.bigint().min(BigInt(5)).minValue).toEqual(BigInt(5)); 50 | expect(z.bigint().min(BigInt(5)).min(BigInt(10)).minValue).toEqual(BigInt(10)); 51 | 52 | expect(z.bigint().max(BigInt(5)).maxValue).toEqual(BigInt(5)); 53 | expect(z.bigint().max(BigInt(5)).max(BigInt(1)).maxValue).toEqual(BigInt(1)); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/coalesce.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | test("coalesce", () => { 4 | expect(true).toBe(true); 5 | }); 6 | 7 | // test("nonoptional with default", () => { 8 | // const schema = z.string().optional().coalesce("hi"); 9 | // expectTypeOf().toEqualTypeOf(); 10 | // expectTypeOf().toEqualTypeOf(); 11 | // expect(schema.parse(undefined)).toBe("hi"); 12 | // }); 13 | 14 | // test("nonoptional in object", () => { 15 | // const schema = z.object({ hi: z.string().optional().nonoptional("hi") }); 16 | 17 | // expectTypeOf().toEqualTypeOf<{ hi: string | undefined }>(); 18 | // expectTypeOf().toEqualTypeOf<{ hi: string }>(); 19 | // expect(schema.parse(undefined)).toBe("hi"); 20 | // }); 21 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/custom.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | test("passing validations", () => { 6 | const example1 = z.custom((x) => typeof x === "number"); 7 | example1.parse(1234); 8 | expect(() => example1.parse({})).toThrow(); 9 | }); 10 | 11 | test("string params", () => { 12 | const example1 = z.custom((x) => typeof x !== "number", "customerr"); 13 | const result = example1.safeParse(1234); 14 | expect(result.success).toEqual(false); 15 | expect(JSON.stringify(result.error).includes("customerr")).toEqual(true); 16 | }); 17 | 18 | test("instanceof", () => { 19 | const fn = (value: string) => Uint8Array.from(Buffer.from(value, "base64")); 20 | 21 | // Argument of type 'ZodCustom, unknown>' is not assignable to parameter of type '$ZodType>'. 22 | z.string().transform(fn).pipe(z.instanceof(Uint8Array)); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/date.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | const beforeBenchmarkDate = new Date(2022, 10, 4); 6 | const benchmarkDate = new Date(2022, 10, 5); 7 | const afterBenchmarkDate = new Date(2022, 10, 6); 8 | 9 | const minCheck = z.date().min(benchmarkDate); 10 | const maxCheck = z.date().max(benchmarkDate); 11 | 12 | test("passing validations", () => { 13 | minCheck.parse(benchmarkDate); 14 | minCheck.parse(afterBenchmarkDate); 15 | 16 | maxCheck.parse(benchmarkDate); 17 | maxCheck.parse(beforeBenchmarkDate); 18 | }); 19 | 20 | test("failing validations", () => { 21 | expect(() => minCheck.parse(beforeBenchmarkDate)).toThrow(); 22 | expect(() => maxCheck.parse(afterBenchmarkDate)).toThrow(); 23 | }); 24 | 25 | test("min max getters", () => { 26 | expect(minCheck.minDate).toEqual(benchmarkDate); 27 | expect(minCheck.min(afterBenchmarkDate).minDate).toEqual(afterBenchmarkDate); 28 | 29 | expect(maxCheck.maxDate).toEqual(benchmarkDate); 30 | expect(maxCheck.max(beforeBenchmarkDate).maxDate).toEqual(beforeBenchmarkDate); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/description.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | const description = "a description"; 6 | 7 | // test("passing `description` to schema should add a description", () => { 8 | // expect(z.string({ description }).description).toEqual(description); 9 | // expect(z.number({ description }).description).toEqual(description); 10 | // expect(z.boolean({ description }).description).toEqual(description); 11 | // }); 12 | 13 | test(".describe", () => { 14 | expect(z.string().describe(description).description).toEqual(description); 15 | expect(z.number().describe(description).description).toEqual(description); 16 | expect(z.boolean().describe(description).description).toEqual(description); 17 | }); 18 | 19 | test("adding description with z.globalRegistry", () => { 20 | const schema = z.string(); 21 | z.core.globalRegistry.add(schema, { description }); 22 | z.core.globalRegistry.get(schema); 23 | expect(schema.description).toEqual(description); 24 | }); 25 | 26 | // in Zod 4 descriptions are not inherited 27 | // test("description should carry over to chained schemas", () => { 28 | // const schema = z.string().describe(description); 29 | // expect(schema.description).toEqual(description); 30 | // expect(schema.optional().description).toEqual(description); 31 | // expect(schema.optional().nullable().default("default").description).toEqual(description); 32 | // }); 33 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/file.test.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { File as WebFile } from "@web-std/file"; 3 | 4 | import { afterEach, beforeEach, expect, test } from "vitest"; 5 | 6 | import * as z from "zod/v4"; 7 | 8 | const minCheck = z.file().min(5); 9 | const maxCheck = z.file().max(8); 10 | const mimeCheck = z.file().mime(["text/plain", "application/json"]); 11 | 12 | const originalFile = global.File; 13 | beforeEach(async () => { 14 | if (!globalThis.File) globalThis.File = WebFile; 15 | }); 16 | afterEach(() => { 17 | if (globalThis.File !== originalFile) { 18 | globalThis.File = originalFile; 19 | } 20 | }); 21 | 22 | test("passing validations", () => { 23 | minCheck.parse(new File(["12345"], "test.txt")); 24 | maxCheck.parse(new File(["12345678"], "test.txt")); 25 | mimeCheck.parse(new File([""], "test.csv", { type: "text/plain" })); 26 | expect(() => mimeCheck.parse(new File([""], "test.txt"))).toThrow(); 27 | expect(() => mimeCheck.parse(new File([""], "test.txt", { type: "text/csv" }))).toThrow(); 28 | }); 29 | 30 | test("failing validations", () => { 31 | expect(() => minCheck.parse(new File(["1234"], "test.txt"))).toThrow(); 32 | expect(() => maxCheck.parse(new File(["123456789"], "test.txt"))).toThrow(); 33 | expect(() => mimeCheck.parse(new File([""], "test.csv"))).toThrow(); 34 | expect(() => mimeCheck.parse(new File([""], "test.csv", { type: "text/csv" }))).toThrow(); 35 | }); 36 | 37 | // test("min max getters", () => { 38 | // expect(minCheck.minSize).toEqual(5); 39 | // expect(minCheck.min(10).minSize).toEqual(10); 40 | 41 | // expect(maxCheck.maxSize).toEqual(8); 42 | // expect(maxCheck.max(6).maxSize).toEqual(6); 43 | // }); 44 | 45 | // test("accept getter", () => { 46 | // expect(mimeCheck.acceptedTypes).toEqual(["text/plain", "application/json"]); 47 | // expect(mimeCheck.type(["text/plain", "application/xml"]).acceptedTypes).toEqual(["text/plain"]); 48 | // }); 49 | 50 | // test("invalid mime types", () => { 51 | // expect(() => z.file().type([".txt"])).toThrow(); 52 | // }); 53 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/generics.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | import * as z from "zod/v4"; 3 | 4 | function nest(schema: TData) { 5 | return z.object({ 6 | nested: schema, 7 | }); 8 | } 9 | 10 | test("generics", () => { 11 | const a = nest(z.object({ a: z.string() })); 12 | type a = z.infer; 13 | expectTypeOf().toEqualTypeOf<{ nested: { a: string } }>(); 14 | 15 | const b = nest(z.object({ a: z.string().optional() })); 16 | type b = z.infer; 17 | expectTypeOf().toEqualTypeOf<{ nested: { a?: string | undefined } }>(); 18 | }); 19 | 20 | test("generics with optional", () => { 21 | async function stripOuter(schema: TData, data: unknown) { 22 | return z 23 | .object({ 24 | nested: schema.optional(), 25 | }) 26 | .transform((data) => { 27 | return data.nested; 28 | }) 29 | .parse({ nested: data }); 30 | } 31 | 32 | const result = stripOuter(z.object({ a: z.string() }), { a: "asdf" }); 33 | expectTypeOf().toEqualTypeOf>(); 34 | }); 35 | 36 | // test("assignability", () => { 37 | // const createSchemaAndParse = (key: K, valueSchema: VS, data: unknown) => { 38 | // const schema = z.object({ 39 | // [key]: valueSchema, 40 | // }); 41 | // // return { [key]: valueSchema }; 42 | // const parsed = schema.parse(data); 43 | // return parsed; 44 | // // const inferred: z.infer> = parsed; 45 | // // return inferred; 46 | // }; 47 | // const parsed = createSchemaAndParse("foo", z.string(), { foo: "" }); 48 | // expectTypeOf().toEqualTypeOf<{ foo: string }>(); 49 | // }); 50 | 51 | test("nested no undefined", () => { 52 | const inner = z.string().or(z.array(z.string())); 53 | const outer = z.object({ inner }); 54 | type outerSchema = z.infer; 55 | expectTypeOf().toEqualTypeOf<{ inner: string | string[] }>(); 56 | 57 | expect(outer.safeParse({ inner: undefined }).success).toEqual(false); 58 | }); 59 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/instanceof.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | import * as z from "zod/v4"; 3 | 4 | test("instanceof", async () => { 5 | class Test {} 6 | class Subtest extends Test {} 7 | abstract class AbstractBar { 8 | constructor(public val: string) {} 9 | } 10 | class Bar extends AbstractBar {} 11 | 12 | const TestSchema = z.instanceof(Test); 13 | const SubtestSchema = z.instanceof(Subtest); 14 | const AbstractSchema = z.instanceof(AbstractBar); 15 | const BarSchema = z.instanceof(Bar); 16 | 17 | TestSchema.parse(new Test()); 18 | TestSchema.parse(new Subtest()); 19 | SubtestSchema.parse(new Subtest()); 20 | AbstractSchema.parse(new Bar("asdf")); 21 | const bar = BarSchema.parse(new Bar("asdf")); 22 | expect(bar.val).toEqual("asdf"); 23 | 24 | await expect(() => SubtestSchema.parse(new Test())).toThrow(); 25 | await expect(() => TestSchema.parse(12)).toThrow(); 26 | 27 | expectTypeOf().toEqualTypeOf>(); 28 | }); 29 | 30 | test("instanceof fatal", () => { 31 | const schema = z.instanceof(Date).refine((d) => d.toString()); 32 | const res = schema.safeParse(null); 33 | expect(res.success).toBe(false); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/nan.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | const schema = z.nan(); 6 | 7 | test("passing validations", () => { 8 | schema.parse(Number.NaN); 9 | schema.parse(Number("Not a number")); 10 | expectTypeOf().toEqualTypeOf(); 11 | }); 12 | 13 | test("failing validations", () => { 14 | expect(() => schema.parse(5)).toThrow(); 15 | expect(() => schema.parse("John")).toThrow(); 16 | expect(() => schema.parse(true)).toThrow(); 17 | expect(() => schema.parse(null)).toThrow(); 18 | expect(() => schema.parse(undefined)).toThrow(); 19 | expect(() => schema.parse({})).toThrow(); 20 | expect(() => schema.parse([])).toThrow(); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/nullable.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | test(".nullable()", () => { 6 | const nullable = z.string().nullable(); 7 | expect(nullable.parse(null)).toBe(null); 8 | expect(nullable.parse("asdf")).toBe("asdf"); 9 | expect(() => nullable.parse(123)).toThrow(); 10 | }); 11 | 12 | test(".nullable unwrap", () => { 13 | const schema = z.string().nullable(); 14 | expect(schema).toBeInstanceOf(z.ZodNullable); 15 | expect(schema.unwrap()).toBeInstanceOf(z.ZodString); 16 | }); 17 | 18 | test("z.null", () => { 19 | const n = z.null(); 20 | expect(n.parse(null)).toBe(null); 21 | expect(() => n.parse("asdf")).toThrow(); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/prefault.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | import { z } from "zod/v4"; 3 | 4 | test("basic prefault", () => { 5 | const a = z.prefault(z.string().trim(), " default "); 6 | expect(a).toBeInstanceOf(z.ZodPrefault); 7 | expect(a.parse(" asdf ")).toEqual("asdf"); 8 | expect(a.parse(undefined)).toEqual("default"); 9 | 10 | type inp = z.input; 11 | expectTypeOf().toEqualTypeOf(); 12 | type out = z.output; 13 | expectTypeOf().toEqualTypeOf(); 14 | }); 15 | 16 | test("prefault inside object", () => { 17 | // test optinality 18 | const a = z.object({ 19 | name: z.string().optional(), 20 | age: z.number().default(1234), 21 | email: z.string().prefault("1234"), 22 | }); 23 | 24 | type inp = z.input; 25 | expectTypeOf().toEqualTypeOf<{ 26 | name?: string | undefined; 27 | age?: number | undefined; 28 | email?: string | undefined; 29 | }>(); 30 | 31 | type out = z.output; 32 | expectTypeOf().toEqualTypeOf<{ 33 | name?: string | undefined; 34 | age: number; 35 | email: string; 36 | }>(); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/standard-schema.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | test("length checks", async () => { 6 | const schema = z.string(); 7 | const result = await schema["~standard"].validate(12); 8 | expect(result).toMatchInlineSnapshot(` 9 | { 10 | "issues": [ 11 | { 12 | "code": "invalid_type", 13 | "expected": "string", 14 | "message": "Invalid input: expected string, received number", 15 | "path": [], 16 | }, 17 | ], 18 | } 19 | `); 20 | }); 21 | 22 | test("length checks", async () => { 23 | const schema = z.string(); 24 | const result = await schema["~standard"].validate("asdf"); 25 | expect(result).toMatchInlineSnapshot(` 26 | { 27 | "value": "asdf", 28 | } 29 | `); 30 | }); 31 | 32 | test("length checks", async () => { 33 | const schema = z.string().refine(async (val) => val.length > 5); 34 | const result = await schema["~standard"].validate(12); 35 | expect(result).toMatchInlineSnapshot(` 36 | { 37 | "issues": [ 38 | { 39 | "code": "invalid_type", 40 | "expected": "string", 41 | "message": "Invalid input: expected string, received number", 42 | "path": [], 43 | }, 44 | ], 45 | } 46 | `); 47 | }); 48 | 49 | test("length checks", async () => { 50 | const schema = z.string().refine(async (val) => val.length > 5); 51 | const result = await schema["~standard"].validate("234134134"); 52 | expect(result).toMatchInlineSnapshot(` 53 | { 54 | "value": "234134134", 55 | } 56 | `); 57 | }); 58 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/string-formats.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | 3 | import * as z from "zod/v4"; 4 | 5 | test("string format methods", () => { 6 | const a = z.email().min(10); 7 | const b = z.email().max(10); 8 | const c = z.email().length(10); 9 | const d = z.email().uppercase(); 10 | const e = z.email().lowercase(); 11 | 12 | // Positive and negative cases for `a` 13 | expect(a.safeParse("longemail@example.com").success).toBe(true); // Positive 14 | expect(a.safeParse("ort@e.co").success).toBe(false); // Negative 15 | 16 | // Positive and negative cases for `b` 17 | expect(b.safeParse("sho@e.co").success).toBe(true); // Positive 18 | expect(b.safeParse("longemail@example.com").success).toBe(false); // Negative 19 | 20 | // Positive and negative cases for `c` 21 | expect(c.safeParse("56780@e.co").success).toBe(true); // Positive 22 | expect(c.safeParse("shoasdfasdfrt@e.co").success).toBe(false); // Negative 23 | 24 | // Positive and negative cases for `d` 25 | expect(d.safeParse("EMAIL@EXAMPLE.COM").success).toBe(true); // Positive 26 | expect(d.safeParse("email@example.com").success).toBe(false); // Negative 27 | 28 | // Positive and negative cases for `e` 29 | expect(e.safeParse("email@example.com").success).toBe(true); // Positive 30 | expect(e.safeParse("EMAIL@EXAMPLE.COM").success).toBe(false); // Negative 31 | }); 32 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/stringbool.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | import * as z from "zod/v4"; 3 | 4 | test("z.stringbool", () => { 5 | const a = z.stringbool(); 6 | type a = z.infer; 7 | expectTypeOf().toEqualTypeOf(); 8 | 9 | expect(z.parse(a, "true")).toEqual(true); 10 | expect(z.parse(a, "yes")).toEqual(true); 11 | expect(z.parse(a, "1")).toEqual(true); 12 | expect(z.parse(a, "on")).toEqual(true); 13 | expect(z.parse(a, "y")).toEqual(true); 14 | expect(z.parse(a, "enabled")).toEqual(true); 15 | expect(z.parse(a, "TRUE")).toEqual(true); 16 | 17 | expect(z.parse(a, "false")).toEqual(false); 18 | expect(z.parse(a, "no")).toEqual(false); 19 | expect(z.parse(a, "0")).toEqual(false); 20 | expect(z.parse(a, "off")).toEqual(false); 21 | expect(z.parse(a, "n")).toEqual(false); 22 | expect(z.parse(a, "disabled")).toEqual(false); 23 | expect(z.parse(a, "FALSE")).toEqual(false); 24 | 25 | expect(z.safeParse(a, "other")).toMatchObject({ success: false }); 26 | expect(z.safeParse(a, "")).toMatchObject({ success: false }); 27 | expect(z.safeParse(a, undefined)).toMatchObject({ success: false }); 28 | expect(z.safeParse(a, {})).toMatchObject({ success: false }); 29 | expect(z.safeParse(a, true)).toMatchObject({ success: false }); 30 | expect(z.safeParse(a, false)).toMatchObject({ success: false }); 31 | 32 | const b = z.stringbool({ 33 | truthy: ["y"], 34 | falsy: ["n"], 35 | }); 36 | expect(z.parse(b, "y")).toEqual(true); 37 | expect(z.parse(b, "n")).toEqual(false); 38 | expect(z.safeParse(b, "true")).toMatchObject({ success: false }); 39 | expect(z.safeParse(b, "false")).toMatchObject({ success: false }); 40 | 41 | const c = z.stringbool({ 42 | case: "sensitive", 43 | }); 44 | expect(z.parse(c, "true")).toEqual(true); 45 | expect(z.safeParse(c, "TRUE")).toMatchObject({ success: false }); 46 | }); 47 | 48 | // test custom error messages 49 | test("z.stringbool with custom error messages", () => { 50 | const a = z.stringbool("wrong!"); 51 | 52 | expect(() => a.parse("")).toThrowError("wrong!"); 53 | }); 54 | -------------------------------------------------------------------------------- /packages/zod/src/v4/classic/tests/void.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | import * as z from "zod/v4"; 3 | test("void", () => { 4 | const v = z.void(); 5 | v.parse(undefined); 6 | 7 | expect(() => v.parse(null)).toThrow(); 8 | expect(() => v.parse("")).toThrow(); 9 | 10 | type v = z.infer; 11 | expectTypeOf().toEqualTypeOf(); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/zod/src/v4/core/config.ts: -------------------------------------------------------------------------------- 1 | import type * as errors from "./errors.js"; 2 | 3 | export interface $ZodConfig { 4 | /** Custom error map. Overrides `config().localeError`. */ 5 | customError?: errors.$ZodErrorMap | undefined; 6 | /** Localized error map. Lowest priority. */ 7 | localeError?: errors.$ZodErrorMap | undefined; 8 | } 9 | 10 | export const globalConfig: $ZodConfig = {}; 11 | 12 | export function config(config?: Partial<$ZodConfig>): $ZodConfig { 13 | if (config) Object.assign(globalConfig, config); 14 | return globalConfig; 15 | } 16 | -------------------------------------------------------------------------------- /packages/zod/src/v4/core/doc.ts: -------------------------------------------------------------------------------- 1 | type ModeWriter = (doc: Doc, modes: { execution: "sync" | "async" }) => void; 2 | 3 | export class Doc { 4 | args!: string[]; 5 | content: string[] = []; 6 | indent = 0; 7 | 8 | constructor(args: string[] = []) { 9 | if (this) this.args = args; 10 | } 11 | 12 | indented(fn: (doc: Doc) => void) { 13 | this.indent += 1; 14 | fn(this); 15 | this.indent -= 1; 16 | } 17 | 18 | write(fn: ModeWriter): void; 19 | write(line: string): void; 20 | write(arg: any) { 21 | if (typeof arg === "function") { 22 | (arg as ModeWriter)(this, { execution: "sync" }); 23 | (arg as ModeWriter)(this, { execution: "async" }); 24 | return; 25 | } 26 | 27 | const content = arg as string; 28 | const lines = content.split("\n").filter((x) => x); 29 | const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length)); 30 | const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x); 31 | for (const line of dedented) { 32 | this.content.push(line); 33 | } 34 | } 35 | 36 | compile() { 37 | const F = Function; 38 | const args = this?.args; 39 | const content = this?.content ?? [``]; 40 | const lines = [...content.map((x) => ` ${x}`)]; 41 | // console.log(lines.join("\n")); 42 | // console.dir("COMPILE", {depth: null}); 43 | return new F(...args, lines.join("\n")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/zod/src/v4/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./core.js"; 2 | export * from "./parse.js"; 3 | export * from "./errors.js"; 4 | export * from "./schemas.js"; 5 | export * from "./checks.js"; 6 | export * from "./versions.js"; 7 | export * as util from "./util.js"; 8 | export * as regexes from "./regexes.js"; 9 | export * as locales from "../locales/index.js"; 10 | export * from "./registries.js"; 11 | export * from "./doc.js"; 12 | export * from "./function.js"; 13 | export * from "./api.js"; 14 | export * from "./to-json-schema.js"; 15 | export * as JSONSchema from "./json-schema.js"; 16 | -------------------------------------------------------------------------------- /packages/zod/src/v4/core/tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, expectTypeOf, test } from "vitest"; 2 | import * as z from "zod/v3"; 3 | 4 | test("test", () => { 5 | expect(true).toBe(true); 6 | }); 7 | 8 | test("test2", () => { 9 | expect(() => z.string().parse(234)).toThrowErrorMatchingInlineSnapshot(` 10 | [ZodError: [ 11 | { 12 | "code": "invalid_type", 13 | "expected": "string", 14 | "received": "number", 15 | "path": [], 16 | "message": "Expected string, received number" 17 | } 18 | ]] 19 | `); 20 | }); 21 | 22 | test("async validation", async () => { 23 | const testTuple = z 24 | .tuple([z.string().refine(async () => true), z.number().refine(async () => true)]) 25 | .refine(async () => true); 26 | expectTypeOf().toEqualTypeOf<[string, number]>(); 27 | 28 | const val = await testTuple.parseAsync(["asdf", 1234]); 29 | expect(val).toEqual(val); 30 | 31 | const r1 = await testTuple.safeParseAsync(["asdf", "asdf"]); 32 | expect(r1.success).toEqual(false); 33 | expect(r1.error!).toMatchInlineSnapshot(` 34 | [ZodError: [ 35 | { 36 | "code": "invalid_type", 37 | "expected": "number", 38 | "received": "string", 39 | "path": [ 40 | 1 41 | ], 42 | "message": "Expected number, received string" 43 | } 44 | ]] 45 | `); 46 | }); 47 | -------------------------------------------------------------------------------- /packages/zod/src/v4/core/tests/locales/en.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { parsedType } from "../../../locales/en.js"; 3 | 4 | test("parsedType", () => { 5 | expect(parsedType("string")).toBe("string"); 6 | expect(parsedType(1)).toBe("number"); 7 | expect(parsedType(true)).toBe("boolean"); 8 | expect(parsedType(null)).toBe("null"); 9 | expect(parsedType(undefined)).toBe("undefined"); 10 | expect(parsedType([])).toBe("array"); 11 | expect(parsedType({})).toBe("object"); 12 | expect(parsedType(new Date())).toBe("Date"); 13 | expect(parsedType(new Map())).toBe("Map"); 14 | expect(parsedType(new Set())).toBe("Set"); 15 | expect(parsedType(new Error())).toBe("Error"); 16 | 17 | const nullPrototype = Object.create(null); 18 | expect(parsedType(nullPrototype)).toBe("object"); 19 | 20 | const doubleNullPrototype = Object.create(Object.create(null)); 21 | expect(parsedType(doubleNullPrototype)).toBe("object"); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/zod/src/v4/core/versions.ts: -------------------------------------------------------------------------------- 1 | export const version = { 2 | major: 4, 3 | minor: 0, 4 | patch: 0 as number, 5 | } as const; 6 | -------------------------------------------------------------------------------- /packages/zod/src/v4/index.ts: -------------------------------------------------------------------------------- 1 | import z4 from "./classic/index.js"; 2 | export * from "./classic/index.js"; 3 | 4 | export default z4; 5 | -------------------------------------------------------------------------------- /packages/zod/src/v4/locales/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ar } from "./ar.js"; 2 | export { default as az } from "./az.js"; 3 | export { default as be } from "./be.js"; 4 | export { default as ca } from "./ca.js"; 5 | export { default as cs } from "./cs.js"; 6 | export { default as de } from "./de.js"; 7 | export { default as en } from "./en.js"; 8 | export { default as es } from "./es.js"; 9 | export { default as fa } from "./fa.js"; 10 | export { default as fi } from "./fi.js"; 11 | export { default as fr } from "./fr.js"; 12 | export { default as frCA } from "./fr-CA.js"; 13 | export { default as he } from "./he.js"; 14 | export { default as hu } from "./hu.js"; 15 | export { default as id } from "./id.js"; 16 | export { default as it } from "./it.js"; 17 | export { default as ja } from "./ja.js"; 18 | export { default as kh } from "./kh.js"; 19 | export { default as ko } from "./ko.js"; 20 | export { default as mk } from "./mk.js"; 21 | export { default as ms } from "./ms.js"; 22 | export { default as nl } from "./nl.js"; 23 | export { default as no } from "./no.js"; 24 | export { default as ota } from "./ota.js"; 25 | export { default as pl } from "./pl.js"; 26 | export { default as pt } from "./pt.js"; 27 | export { default as ru } from "./ru.js"; 28 | export { default as sl } from "./sl.js"; 29 | export { default as sv } from "./sv.js"; 30 | export { default as ta } from "./ta.js"; 31 | export { default as th } from "./th.js"; 32 | export { default as tr } from "./tr.js"; 33 | export { default as ua } from "./ua.js"; 34 | export { default as ur } from "./ur.js"; 35 | export { default as vi } from "./vi.js"; 36 | export { default as zhCN } from "./zh-CN.js"; 37 | export { default as zhTW } from "./zh-TW.js"; 38 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/checks.ts: -------------------------------------------------------------------------------- 1 | export { 2 | _lt as lt, 3 | _lte as lte, 4 | _lte as maximum, 5 | _gt as gt, 6 | _gte as gte, 7 | _gte as minimum, 8 | _positive as positive, 9 | _negative as negative, 10 | _nonpositive as nonpositive, 11 | _nonnegative as nonnegative, 12 | _multipleOf as multipleOf, 13 | _maxSize as maxSize, 14 | _minSize as minSize, 15 | _size as size, 16 | _maxLength as maxLength, 17 | _minLength as minLength, 18 | _length as length, 19 | _regex as regex, 20 | _lowercase as lowercase, 21 | _uppercase as uppercase, 22 | _includes as includes, 23 | _startsWith as startsWith, 24 | _endsWith as endsWith, 25 | _property as property, 26 | _mime as mime, 27 | _overwrite as overwrite, 28 | _normalize as normalize, 29 | _trim as trim, 30 | _toLowerCase as toLowerCase, 31 | _toUpperCase as toUpperCase, 32 | } from "zod/v4/core"; 33 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/coerce.ts: -------------------------------------------------------------------------------- 1 | import * as core from "zod/v4/core"; 2 | import * as schemas from "./schemas.js"; 3 | 4 | export function string(params?: string | core.$ZodStringParams): schemas.ZodMiniString { 5 | return core._coercedString(schemas.ZodMiniString, params) as schemas.ZodMiniString; 6 | } 7 | 8 | export function number(params?: string | core.$ZodNumberParams): schemas.ZodMiniNumber { 9 | return core._coercedNumber(schemas.ZodMiniNumber, params) as schemas.ZodMiniNumber; 10 | } 11 | 12 | export function boolean(params?: string | core.$ZodBooleanParams): schemas.ZodMiniBoolean { 13 | return core._coercedBoolean(schemas.ZodMiniBoolean, params) as schemas.ZodMiniBoolean; 14 | } 15 | 16 | export function bigint(params?: string | core.$ZodBigIntParams): schemas.ZodMiniBigInt { 17 | return core._coercedBigint(schemas.ZodMiniBigInt, params) as schemas.ZodMiniBigInt; 18 | } 19 | 20 | export function date(params?: string | core.$ZodDateParams): schemas.ZodMiniDate { 21 | return core._coercedDate(schemas.ZodMiniDate, params) as schemas.ZodMiniDate; 22 | } 23 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/external.ts: -------------------------------------------------------------------------------- 1 | export * as core from "zod/v4/core"; 2 | export * from "./parse.js"; 3 | export * from "./schemas.js"; 4 | export * from "./checks.js"; 5 | 6 | export type { infer, output, input } from "zod/v4/core"; 7 | export { 8 | globalRegistry, 9 | registry, 10 | config, 11 | $output, 12 | $input, 13 | $brand, 14 | function, 15 | clone, 16 | regexes, 17 | treeifyError, 18 | prettifyError, 19 | formatError, 20 | flattenError, 21 | toJSONSchema, 22 | } from "zod/v4/core"; 23 | 24 | export * as locales from "../locales/index.js"; 25 | /** A special constant with type `never` */ 26 | // export const NEVER = {} as never; 27 | 28 | // iso 29 | export * as iso from "./iso.js"; 30 | export { 31 | ZodMiniISODateTime, 32 | ZodMiniISODate, 33 | ZodMiniISOTime, 34 | ZodMiniISODuration, 35 | } from "./iso.js"; 36 | 37 | // coerce 38 | export * as coerce from "./coerce.js"; 39 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/index.ts: -------------------------------------------------------------------------------- 1 | import * as z from "./external.js"; 2 | export * from "./external.js"; 3 | export { z }; 4 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/parse.ts: -------------------------------------------------------------------------------- 1 | export { parse, safeParse, parseAsync, safeParseAsync } from "zod/v4/core"; 2 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/tests/brand.test.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf, test } from "vitest"; 2 | import * as z from "../index.js"; 3 | 4 | test("branded types", () => { 5 | const mySchema = z 6 | .object({ 7 | name: z.string(), 8 | }) 9 | .brand<"superschema">(); 10 | 11 | // simple branding 12 | type MySchema = z.infer; 13 | // Using true for type equality assertion 14 | expectTypeOf().toEqualTypeOf<{ name: string } & z.$brand<"superschema">>(); 15 | 16 | const doStuff = (arg: MySchema) => arg; 17 | doStuff(z.parse(mySchema, { name: "hello there" })); 18 | 19 | // inheritance 20 | const extendedSchema = mySchema.brand<"subschema">(); 21 | type ExtendedSchema = z.infer; 22 | expectTypeOf().toEqualTypeOf<{ name: string } & z.$brand<"superschema"> & z.$brand<"subschema">>(); 23 | 24 | doStuff(z.parse(extendedSchema, { name: "hello again" })); 25 | 26 | // number branding 27 | const numberSchema = z.number().brand<42>(); 28 | type NumberSchema = z.infer; 29 | expectTypeOf().toEqualTypeOf(); 30 | 31 | // symbol branding 32 | const MyBrand: unique symbol = Symbol("hello"); 33 | type MyBrand = typeof MyBrand; 34 | const symbolBrand = z.number().brand<"sup">().brand(); 35 | type SymbolBrand = z.infer; 36 | // number & { [z.$brand]: { sup: true, [MyBrand]: true } } 37 | expectTypeOf().toEqualTypeOf & z.$brand>(); 38 | 39 | // keeping brands out of input types 40 | const age = z.number().brand<"age">(); 41 | type Age1 = z.infer; 42 | type AgeInput1 = z.input; 43 | 44 | // Using not for type inequality assertion 45 | expectTypeOf().not.toEqualTypeOf(); 46 | expectTypeOf().toEqualTypeOf(); 47 | expectTypeOf>().toEqualTypeOf(); 48 | 49 | // @ts-expect-error 50 | doStuff({ name: "hello there!" }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/tests/computed.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import * as z from "zod/v4-mini"; 3 | import { util as zc } from "zod/v4/core"; 4 | 5 | test("min/max", () => { 6 | const a = z.number().check(z.minimum(5), z.minimum(6), z.minimum(7), z.maximum(10), z.maximum(11), z.maximum(12)); 7 | 8 | expect(a._zod.bag.minimum).toEqual(7); 9 | expect(a._zod.bag.maximum).toEqual(10); 10 | }); 11 | 12 | test("multipleOf", () => { 13 | const b = z.number().check(z.multipleOf(5)); 14 | expect(b._zod.bag.multipleOf).toEqual(5); 15 | }); 16 | 17 | test("int64 format", () => { 18 | const c = z.int64(); 19 | expect(c._zod.bag.format).toEqual("int64"); 20 | expect(c._zod.bag.minimum).toEqual(zc.BIGINT_FORMAT_RANGES.int64[0]); 21 | expect(c._zod.bag.maximum).toEqual(zc.BIGINT_FORMAT_RANGES.int64[1]); 22 | }); 23 | 24 | test("int32 format", () => { 25 | const d = z.int32(); 26 | expect(d._zod.bag.format).toEqual("int32"); 27 | expect(d._zod.bag.minimum).toEqual(zc.NUMBER_FORMAT_RANGES.int32[0]); 28 | expect(d._zod.bag.maximum).toEqual(zc.NUMBER_FORMAT_RANGES.int32[1]); 29 | }); 30 | 31 | test("array size", () => { 32 | const e = z.array(z.string()).check(z.length(5)); 33 | expect(e._zod.bag.length).toEqual(5); 34 | expect(e._zod.bag.minimum).toEqual(5); 35 | expect(e._zod.bag.maximum).toEqual(5); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/tests/error.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import * as z from "zod/v4-mini"; 3 | 4 | test("no locale by default", () => { 5 | const result = z.safeParse(z.string(), 12); 6 | expect(result.success).toEqual(false); 7 | expect(result.error!.issues.length).toEqual(1); 8 | expect(result.error!.issues[0].message).toEqual("Invalid input"); 9 | }); 10 | 11 | test("error inheritance", () => { 12 | const e1 = z.string().safeParse(123).error!; 13 | expect(e1).toBeInstanceOf(z.core.$ZodError); 14 | // expect(e1).not.toBeInstanceOf(Error); 15 | 16 | try { 17 | z.string().parse(123); 18 | } catch (e2) { 19 | expect(e2).toBeInstanceOf(z.core.$ZodRealError); 20 | expect(e2).toBeInstanceOf(Error); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/tests/functions.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | // import * as z from "zod/v4/core"; 3 | 4 | test("z.function", () => { 5 | expect(true).toEqual(true); 6 | }); 7 | 8 | // test("z.function", () => { 9 | // const a = z.function({ 10 | // args: z.tuple([z.string()]), 11 | // returns: z.string(), 12 | // }); 13 | 14 | // const myFunc = a.implement((name: string | number) => `Hello, ${name}!`); 15 | 16 | // expect(myFunc("world")).toEqual("Hello, world!"); 17 | // expect(() => myFunc(123 as any)).toThrow(); 18 | 19 | // // this won't run 20 | // () => { 21 | // // @ts-expect-error 22 | // const r = myFunc(123); 23 | // expectTypeOf(r).toEqualTypeOf(); 24 | // }; 25 | // }); 26 | 27 | // test("z.function async", async () => { 28 | // const b = z.function({ 29 | // args: z.tuple([z.string()]).$check(async (_) => {}), 30 | // returns: z.string().$check(async (_) => {}), 31 | // }); 32 | // const myFuncAsync = b.implementAsync(async (name) => `Hello, ${name}!`); 33 | 34 | // expect(await myFuncAsync("world")).toEqual("Hello, world!"); 35 | // expect(myFuncAsync(123 as any)).rejects.toThrow(); 36 | 37 | // // this won't run 38 | // () => { 39 | // // @ts-expect-error 40 | // const r = myFuncAsync(123); 41 | // expectTypeOf(r).toEqualTypeOf>(); 42 | // }; 43 | // }); 44 | -------------------------------------------------------------------------------- /packages/zod/src/v4/mini/tests/prototypes.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import * as z from "zod/v4-mini"; 3 | 4 | // declare module "@zod/core" { 5 | // interface $ZodType { 6 | // /** @deprecated */ 7 | // fun(): string; 8 | // } 9 | // } 10 | 11 | test("prototype extension", () => { 12 | z.ZodMiniType.prototype.fun = function () { 13 | return "fun"; 14 | }; 15 | 16 | // should pass 17 | const result = (z.string() as any).fun(); 18 | expect(result).toBe("fun"); 19 | // expectTypeOf().toEqualTypeOf(); 20 | 21 | // clean up 22 | z.ZodMiniType.prototype.fun = undefined; 23 | }); 24 | -------------------------------------------------------------------------------- /packages/zod/tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.configs/tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "module": "Node16", 6 | "moduleResolution": "node16", 7 | "customConditions": ["@zod/source"], 8 | "outDir": "./dist/cjs" 9 | }, 10 | "include": ["src"], 11 | "exclude": ["**/tests/**"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/zod/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.configs/tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "module": "esnext", 6 | "moduleResolution": "bundler", 7 | "customConditions": ["@zod/source"], 8 | "outDir": "./dist/esm" 9 | }, 10 | "include": ["src"], 11 | "exclude": ["**/tests/**"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/zod/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.configs/tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "moduleResolution": "nodenext", 6 | "customConditions": ["@zod/source"] 7 | }, 8 | "exclude": ["vitest.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/zod/tsconfig.types.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.configs/tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "module": "Node16", 6 | "moduleResolution": "node16", 7 | "customConditions": ["@zod/source"], 8 | "declaration": true, 9 | "emitDeclarationOnly": true, 10 | "outDir": "./dist/types", 11 | "removeComments": false 12 | }, 13 | "include": ["src"], 14 | "exclude": ["**/tests/**"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/zod/v3/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "../dist/types/v3/index.js"; 2 | import z from "../dist/types/v3/index.js"; 3 | export default z; 4 | -------------------------------------------------------------------------------- /packages/zod/v3/index.js: -------------------------------------------------------------------------------- 1 | export * from "../dist/esm/v3/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4-mini/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "../dist/types/v4/mini/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4-mini/index.js: -------------------------------------------------------------------------------- 1 | export * from "../dist/esm/v4/mini/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4/core/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "../../dist/types/v4/core/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4/core/index.js: -------------------------------------------------------------------------------- 1 | export * from "../../dist/esm/v4/core/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4/index.d.ts: -------------------------------------------------------------------------------- 1 | import z from "../dist/types/v4/index.js"; 2 | export * from "../dist/types/v4/index.js"; 3 | export default z; 4 | -------------------------------------------------------------------------------- /packages/zod/v4/index.js: -------------------------------------------------------------------------------- 1 | export * from "../dist/esm/v4/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4/locales/en.d.ts: -------------------------------------------------------------------------------- 1 | export * from "../../dist/types/v4/locales/en.d.ts"; 2 | export { default } from "../../dist/types/v4/locales/en.d.ts"; 3 | -------------------------------------------------------------------------------- /packages/zod/v4/locales/en.js: -------------------------------------------------------------------------------- 1 | export * from "../../dist/esm/v4/locales/en.js"; 2 | export { default } from "../../dist/esm/v4/locales/en.js"; 3 | -------------------------------------------------------------------------------- /packages/zod/v4/locales/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from "../../dist/types/v4/locales/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/v4/locales/index.js: -------------------------------------------------------------------------------- 1 | export * from "../../dist/esm/v4/locales/index.js"; 2 | -------------------------------------------------------------------------------- /packages/zod/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineProject, mergeConfig } from "vitest/config"; 2 | import rootConfig from "../../vitest.root.mjs"; 3 | 4 | export default mergeConfig(rootConfig, defineProject({})) as object; 5 | -------------------------------------------------------------------------------- /play.ts: -------------------------------------------------------------------------------- 1 | // import { z } from "zod/v4"; 2 | 3 | import type { z } from "zod/v4"; 4 | 5 | export type RefinedSchema = 6 | T extends z.core.$ZodUnion 7 | ? RefinedUnionSchema // <-- Type instantiation is excessively deep and possibly infinite. 8 | : never; 9 | 10 | export type RefinedTypeSchema = T; 11 | export type RefinedUnionSchema = T; 12 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | -------------------------------------------------------------------------------- /rfcs/index.md: -------------------------------------------------------------------------------- 1 | # RFCs 2 | -------------------------------------------------------------------------------- /scripts/semver-check.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "node:fs"; 2 | import { join } from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | import semver from "semver"; 5 | 6 | const __dirname = fileURLToPath(new URL(".", import.meta.url)); 7 | const packageJsonPath = join(__dirname, "../packages/zod/package.json"); 8 | 9 | try { 10 | const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); 11 | const version = packageJson.version; 12 | 13 | if (!version) { 14 | throw new Error("Version field is missing in package.json"); 15 | } 16 | 17 | if (!semver.valid(version)) { 18 | throw new Error(`Invalid semver version: ${version}`); 19 | } 20 | 21 | // check x.y.z format with regex 22 | const semverRegex = /^\d+\.\d+\.\d+$/; 23 | if (!semverRegex.test(version)) { 24 | throw new Error(`Version ${version} does not match x.y.z format`); 25 | } 26 | 27 | console.log(`Valid semver version: ${version}`); 28 | } catch (error) { 29 | console.error(`Error: ${error.message}`); 30 | process.exit(1); 31 | } 32 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0xF233A42130Bcdd8b22FFB5D9593199f31C3Eeb87' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.configs/tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "declaration": true 6 | }, 7 | "exclude": ["packages/docs"] 8 | } 9 | -------------------------------------------------------------------------------- /vitest.root.mts: -------------------------------------------------------------------------------- 1 | import { type UserConfig, defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | resolve: { 5 | conditions: ["@zod/source"], 6 | }, 7 | test: { 8 | watch: false, 9 | isolate: true, 10 | typecheck: { 11 | include: ["**/*.test.ts"], 12 | enabled: true, 13 | ignoreSourceErrors: false, 14 | checker: "tsc", 15 | tsconfig: "./tsconfig.json", 16 | }, 17 | 18 | silent: true, 19 | }, 20 | }) as UserConfig; 21 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | export default ["packages/*"] as string[]; 2 | --------------------------------------------------------------------------------