Repository: colinhacks/zod
Pull request #4435 (2025-05-20 03:48:31)

fix: Small typo in V4 docs
Thanks for Zod!

<!--

Development of the next major version of Zod (`v4`) is currently underway. During this phase, only bugfixes and documentation improvements are being accepted into the `main` branch. All other PRs should target the `v4` branch. Thanks for contribting to OSS!

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Documentation**
  - Fixed a typo in the "Recursive objects" section for improved clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

coderabbitai[bot]: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->

> [!NOTE]
> Currently processing new changes in this PR. This may take a few minutes, please wait...
> 
> <details>
> <summary>📥 Commits</summary>
> 
> Reviewing files that changed from the base of the PR and between d0318272b752bd336585a07b3f1273420f3576b1 and d32a064d22752b7e11661db61f542e819bc8b587.
> 
> </details>
> 
> <details>
> <summary>📒 Files selected for processing (1)</summary>
> 
> * `packages/docs/content/v4/index.mdx` (1 hunks)
> 
> </details>
> 
> ```ascii
>  ________________________________________________________________________
> < Obi-Wan Kenobi has taught me well. I sense a disturbance in your code. >
>  ------------------------------------------------------------------------
>   \
>    \   \
>         \ /\
>         ( )
>       .( o ).
> ```

<!-- end of auto-generated comment: review in progress by coderabbit.ai -->
<!-- usage_tips_start -->

> [!TIP]
> <details>
> <summary>CodeRabbit can use your project's `biome` configuration to improve the quality of JS/TS/CSS/JSON code reviews.</summary>
> 
> Add a [configuration file](https://biomejs.dev/reference/configuration/) to your project to customize how CodeRabbit runs `biome`.
> 
> </details>

<!-- usage_tips_end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

<details open="true">
<summary>🧪 Generate Unit Tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with Unit Tests
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit Unit Tests in branch `patch-1`
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post Copyable Unit Tests in Comment

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=colinhacks/zod&utm_content=4435):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai auto-generate unit tests` to generate unit tests for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Patch:
diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml
index 339084f7..16e94a99 100644
--- a/.github/workflows/release-canary.yml
+++ b/.github/workflows/release-canary.yml
@@ -6,8 +6,9 @@ on:
     branches:
       - main
     paths:
-      - "src/**"
+      - "packages/zod/**"
       - ".github/workflows/release-canary.yml"
+
 jobs:
   build_and_publish:
     runs-on: ubuntu-latest
@@ -20,30 +21,37 @@ jobs:
         with:
           fetch-depth: 0
 
-      # - name: Set up Node
-      #   uses: actions/setup-node@v4
-      #   with:
-      #     node-version: 20
+      - name: Set up Node
+        uses: actions/setup-node@v4
+        with:
+          node-version: 20
+      
+      - name: Install pnpm
+        uses: pnpm/action-setup@v4
 
-      # - name: Install dependencies
-      #   run: |
-      #     yarn install
+      - name: Install dependencies
+        run: |
+          pnpm install
 
-      # - name: Build
-      #   run: |
-      #     yarn clean
-      #     yarn build
+      - name: Build and test
+        working-directory: ./packages/zod
+        run: |
+          pnpm run test
+          pnpm build
 
-      # - name: Set version
-      #   run: |
-      #     npm --no-git-tag-version version minor
-      #     npm --no-git-tag-version version $(npm pkg get version | sed 's/"//g')-canary.$(date +'%Y%m%dT%H%M%S')
+      - name: Set version
+        working-directory: ./packages/zod
+        run: |
+          npm --no-git-tag-version version minor
+          npm --no-git-tag-version version $(npm pkg get version | sed 's/"//g')-canary.$(date +'%Y%m%dT%H%M%S')
 
-      # - id: publish
-      #   name: Publish to NPM
-      #   uses: JS-DevTools/npm-publish@v3
-      #   with:
-      #     token: ${{ secrets.NPM_TOKEN }}
-      #     dry-run: false
-      #     tag: canary
-      #     provenance: true
+      - id: publish
+        name: Publish to NPM
+        uses: JS-DevTools/npm-publish@v3
+        with:
+          token: ${{ secrets.NPM_TOKEN }}
+          dry-run: false
+          provenance: true
+          tag: canary
+          package: ./packages/zod
+          
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 95beb4a4..8af73fc9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,7 +6,8 @@ on:
     branches:
       - main
     paths:
-      - packages/zod/package.json
+      - "packages/zod/package.json"
+      - ".github/workflows/release.yml"
 
 jobs:
   build_and_publish:
@@ -24,18 +25,20 @@ jobs:
         uses: actions/setup-node@v4
         with:
           node-version: 20
+      
       - name: Install pnpm
         uses: pnpm/action-setup@v4
+        
       - name: Install dependencies
         run: |
           pnpm install
 
-      - name: Install dependencies
+      - name: Build and test
         working-directory: ./packages/zod
         run: |
           pnpm run test
           pnpm build
-
+          
       - id: publish
         name: Publish to NPM
         uses: JS-DevTools/npm-publish@v3
@@ -46,47 +49,47 @@ jobs:
           package: ./packages/zod
 
       - name: Post-publish
-        if: steps.publish.outputs.type != 'none'
+        if: ${{ steps.publish.outputs.type }}
         run: |
           echo "Published ${{ steps.publish.outputs.type }} version: ${{ steps.publish.outputs.version }}"
 
       - name: Publish skipped
-        if: steps.publish.outputs.type == 'none'
+        if: ${{ !steps.publish.outputs.type }}
         run: |
           echo "Version in package.json has not changed. Skipping."
           exit 0
      
-      # - name: Configure changelog
-      #   if: steps.publish.outputs.type != 'none'
-      #   run: |
-      #     echo '{"categories": [], "template": "## Commits:\n\n${{ '${{UNCATEGORIZED}}' }}", "pr_template": ${{ '"- ${{MERGE_SHA}} ${{TITLE}}"' }} }' > changelog_config.json
-      #     cat changelog_config.json
-      #     echo "last_tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
-      #     echo "curr_commit=$(git rev-parse HEAD)" >> $GITHUB_ENV
+      - name: Configure changelog
+        if: ${{ steps.publish.outputs.type }}
+        run: |
+          echo '{"categories": [], "template": "## Commits:\n\n${{ '${{UNCATEGORIZED}}' }}", "pr_template": ${{ '"- ${{MERGE_SHA}} ${{TITLE}}"' }} }' > changelog_config.json
+          cat changelog_config.json
+          echo "last_tag=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
+          echo "curr_commit=$(git rev-parse HEAD)" >> $GITHUB_ENV
 
-      # - name: Generate changelog
-      #   if: steps.publish.outputs.type != 'none'
-      #   id: github_release
-      #   uses: mikepenz/release-changelog-builder-action@v2.9.0
-      #   env:
-      #     GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      #   with:
-      #     fromTag: "${{ env.last_tag }}"
-      #     toTag: ${{ github.ref }}
-      #     commitMode: true
-      #     configuration: changelog_config.json
+      - name: Generate changelog
+        if: ${{ steps.publish.outputs.type }}
+        id: github_release
+        uses: mikepenz/release-changelog-builder-action@v2.9.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          fromTag: "${{ env.last_tag }}"
+          toTag: ${{ github.ref }}
+          commitMode: true
+          configuration: changelog_config.json
 
-      # - name: Create release
-      #   if: steps.publish.outputs.type != 'none'
-      #   id: create_release
-      #   uses: actions/create-release@v1
-      #   env:
-      #     GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      #   with:
-      #     tag_name: v${{ steps.publish.outputs.version }}
-      #     release_name: v${{ steps.publish.outputs.version }}
-      #     commitish: ${{ github.ref }}
-      #     body: ${{steps.github_release.outputs.changelog}}
-      #     draft: false
-      #     prerelease: false
+      - name: Create release
+        if: ${{ steps.publish.outputs.type }}
+        id: create_release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          tag_name: v${{ steps.publish.outputs.version }}
+          release_name: v${{ steps.publish.outputs.version }}
+          commitish: ${{ github.ref }}
+          body: ${{steps.github_release.outputs.changelog}}
+          draft: false
+          prerelease: false
 
diff --git a/.husky/pre-commit b/.husky/pre-commit
index ba85fbee..ccd710b5 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,4 +1,7 @@
 #!/bin/sh
 . "$(dirname "$0")/_/husky.sh"
 
+pnpm semver-check
 npx lint-staged --verbose
+
+
diff --git a/.husky/pre-push b/.husky/pre-push
index 8ef6b5f1..3b20f4e1 100755
--- a/.husky/pre-push
+++ b/.husky/pre-push
@@ -2,3 +2,5 @@
 . "$(dirname "$0")/_/husky.sh"
 
 pnpm test
+
+pnpm semver-check
diff --git a/README.md b/README.md
index 8c66df94..2cbc27fa 100644
--- a/README.md
+++ b/README.md
@@ -164,7 +164,7 @@ try {
 To avoid a `try/catch` block, you can use the `.safeParse()` method to get back a plain result object containing either the successfully parsed data or a `ZodError`. The result type is a [discriminated union](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions), so you can handle both cases conveniently.
 
 ```ts
-const result = Player.parse({ username: 42, xp: "100" });
+const result = Player.safeParse({ username: 42, xp: "100" });
 if (!result.success) {
   result.error;   // ZodError instance
 } else {
diff --git a/package.json b/package.json
index 557f6349..0f9179c6 100644
--- a/package.json
+++ b/package.json
@@ -26,11 +26,13 @@
     "chalk": "^5.4.1",
     "console-table-printer": "^2.12.1",
     "execa": "^9.5.2",
+    "globby": "^14.1.0",
     "husky": "^7.0.4",
     "jsr": "^0.12.4",
     "lint-staged": "^12.5.0",
     "mitata": "^0.1.14",
     "prettier": "^3.5.3",
+    "semver": "^7.7.2",
     "supershy": "^1.0.0",
     "tinybench": "^2.9.0",
     "ts-morph": "^14.0.0",
@@ -63,7 +65,7 @@
     "build": "pnpm run -r --filter '!@zod/docs' build",
     "test:watch": "vitest",
     "test": "vitest run",
-    "prepublishOnly": "pnpm run test && pnpm run build && tsx scripts/prepublish.ts",
+    "prepublishOnly": "pnpm run test && pnpm run build",
     "dev": "tsx --conditions @zod/source",
     "dev:watch": "tsx --conditions @zod/source --watch",
     "dev:play": "pnpm dev play.ts",
@@ -74,6 +76,7 @@
     "publish:jsr": "jsr publish --dry-run",
     "bundle:rollup": "rollup -c",
     "bundle:esbuild": "esbuild --bundle ./scratch/input.ts --outfile=./scratch/out_esbuild.js --bundle --format=esm",
-    "pub:beta": "pnpm run -r pub:beta"
+    "pub:beta": "pnpm run -r pub:beta",
+    "semver-check": "tsx scripts/semver-check.ts"
   }
 }
diff --git a/packages/docs/content/api.mdx b/packages/docs/content/api.mdx
index 6679dd0f..3a88d5c2 100644
--- a/packages/docs/content/api.mdx
+++ b/packages/docs/content/api.mdx
@@ -174,8 +174,8 @@ z.string().lowercase();
 </Tab>
 <Tab value="Zod Mini">
 ```ts zod/v4-mini
-z.string().check(z.max(5));
-z.string().check(z.min(5));
+z.string().check(z.maximum(5)); // or z.lte()
+z.string().check(z.minimum(5)); // or z.gte()
 z.string().check(z.length(5));
 z.string().check(z.regex(/^[a-z]+$/));
 z.string().check(z.startsWith("aaa"));
diff --git a/packages/docs/content/basics.mdx b/packages/docs/content/basics.mdx
index bdaae173..fd6d5b3f 100644
--- a/packages/docs/content/basics.mdx
+++ b/packages/docs/content/basics.mdx
@@ -133,7 +133,7 @@ try {
 To avoid a `try/catch` block, you can use the `.safeParse()` method to get back a plain result object containing either the successfully parsed data or a `ZodError`. The result type is a [discriminated union](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions), so you can handle both cases conveniently.
 
 ```ts
-const result = Player.parse({ username: 42, xp: "100" });
+const result = Player.safeParse({ username: 42, xp: "100" });
 if (!result.success) {
   result.error;   // ZodError instance
 } else {
diff --git a/packages/docs/content/error-customization.mdx b/packages/docs/content/error-customization.mdx
index 3ac421bc..3c3ecf23 100644
--- a/packages/docs/content/error-customization.mdx
+++ b/packages/docs/content/error-customization.mdx
@@ -56,7 +56,7 @@ Every issue contains a `message` property with a human-readable error message. E
 
 ## The `error` param
 
-Virtually every Zod API accepts an optional error message parameter.
+Virtually every Zod API accepts an optional error message.
 
 ```ts
 z.string("Not a string!");
@@ -87,7 +87,7 @@ z.string("Bad!");
 z.string().min(5, "Too short!");
 z.uuid("Bad UUID!");
 z.iso.date("Bad date!");
-z.array(z.string(), "Bad array!");
+z.array(z.string(), "Not an array!");
 z.array(z.string()).min(5, "Too few items!");
 z.set(z.string(), "Bad set!");
 ```
@@ -133,7 +133,8 @@ z.set(z.string(), { error: "Bad set!" });
 </Tab>
 </Tabs>
 
-The `error` param optionally accepts a function. This function will be called at parse time if a valiation error occurs.
+
+The `error` param optionally accepts a function. An error customization function is known as an **error map** in Zod terminology. The error map will can at parse time if a valiation error occurs.
 
 ```ts
 z.string({ error: ()=>`[${Date.now()}]: Validation failure.` });
@@ -143,47 +144,19 @@ z.string({ error: ()=>`[${Date.now()}]: Validation failure.` });
 **Note** — In Zod v3, there were separate params for `message` (a string) and `errorMap` (a function). These have been unified in Zod 4 as `error`.
 </Callout>
 
+The error map receives a context object you can use to customize the error message based on the validation issue.
 
-The `error` function received a context object you can use to customize the error message based on the `input` or other validation information.
-
-<Tabs groupId="lib" items={["Zod", "Zod Mini"]}>
-<Tab value="Zod">
 ```ts
 z.string({
-  error: (iss) => iss.input===undefined ? "Field is required." : "Invalid input."
+  error: (iss) => iss.input === undefined ? "Field is required." : "Invalid input."
 });
 ```
-</Tab>
-<Tab value="Zod Mini">
-```ts
-z.string({
-  error: (iss) => iss.input===undefined ? "Field is required." : "Invalid input."
-});
-```
-</Tab>
-</Tabs>
 
 For advanced cases, the `iss` object provides additional information you can use to customize the error.
 
-
-<Tabs groupId="lib" items={["Zod", "Zod Mini"]}>
-<Tab value="Zod">
-```ts
-z.string({
-  error: (iss) => {
-    iss.code; // the issue code
-    iss.input; // the input data
-    iss.inst; // the schema/check that originated this issue
-    iss.path; // the path of the error
-  },
-});
-```
-</Tab>
-<Tab value="Zod Mini">
 ```ts
 z.string({
   error: (iss) => {
-    iss.inst;
     iss.code; // the issue code
     iss.input; // the input data
     iss.inst; // the schema/check that originated this issue
@@ -191,8 +164,6 @@ z.string({
   },
 });
 ```
-</Tab>
-</Tabs>
 
 Depending on the API you are using, there may be additional properties available. Use TypeScript's autocomplete to explore the available properties.
 
@@ -207,6 +178,22 @@ z.string().min(5, {
 });
 ```
 
+Return `undefined` to avoid setting an error message. This is useful for customizing certain error messages but not others. Zod will yield control to the next error map in the [precedence chain](#error-precedence).
+
+```ts
+z.int64({
+  error: (issue) => {
+    // override too_big error message
+    if (issue.code === "too_big") {
+      return { message: `Value must be <${issue.maximum}` };
+    }
+
+    //  defer to default
+    return undefined;
+  },
+});
+```
+
 ## Per-parse error customization
 
 To customize errors on a *per-parse* basis, pass an error map into the parse method:
@@ -291,7 +278,7 @@ To support internationalization of error message, Zod provides several built-in
 <Tab value="Zod">
 ```ts
 import { z } from "zod/v4";
-import en from "zod/v4/locales/en"
+import en from "zod/v4/locales/en.js"
 
 z.config(en());
 ```
@@ -299,7 +286,7 @@ z.config(en());
 <Tab value="Zod Mini">
 ```ts
 import { z } from "zod/v4-mini"
-import en from "zod/v4/locales/en"
+import en from "zod/v4/locales/en.js"
 
 z.config(en());
 ```
@@ -312,7 +299,7 @@ To lazily load a locale, consider dynamic imports:
 import { z } from "zod/v4";
 
 async function loadLocale(locale: string) {
-  const { default: locale } = await import(`zod/v4/locales/${locale}`);
+  const { default: locale } = await import(`zod/v4/locales/${locale}.js`);
   z.config(locale());
 };
 
diff --git a/packages/docs/content/json-schema.mdx b/packages/docs/content/json-schema.mdx
index 94298d72..7039b2d5 100644
--- a/packages/docs/content/json-schema.mdx
+++ b/packages/docs/content/json-schema.mdx
@@ -142,7 +142,7 @@ Below is a quick reference for each supported parameter. Each one is explained i
 interface ToJSONSchemaParams {
   /** The JSON Schema version to target.
    * - `"draft-2020-12"` — Default. JSON Schema Draft 2020-12
-   * - `"draft-7"` — Default. JSON Schema Draft 7 */
+   * - `"draft-7"` — JSON Schema Draft 7 */
   target?: "draft-7" | "draft-2020-12";
 
   /** A registry used to look up metadata for each schema. 
@@ -398,7 +398,7 @@ z.toJSONSchema(User, { reused: "ref" });
 
 ### `override`
 
-To define some custom override logic, use `override`. The provided callback has access to the original Zod schema and the default JSON Schema. *This function should dircectly modify `ctx.jsonSchema`.*
+To define some custom override logic, use `override`. The provided callback has access to the original Zod schema and the default JSON Schema. *This function should directly modify `ctx.jsonSchema`.*
 
 
 ```ts
@@ -414,6 +414,21 @@ z.toJSONSchema(mySchema, {
 });
 ```
 
+Note that unrepresentable types will throw an `Error` before this functions is called. If you are trying to define custom behavior for an unrepresentable type, you'll need to use set the `unrepresentable: "any"` alongside `override`.
+
+```ts
+// support z.date() as ISO datetime strings
+const result = z.toJSONSchema(z.date(), {
+  unrepresentable: "any",
+  override: (ctx) => {
+    const def = ctx.zodSchema._zod.def;
+    if(def.type ==="date"){
+      ctx.jsonSchema.type = "string";
+      ctx.jsonSchema.format = "date-time";
+    }
+  },
+});
+```
 
 ### `io`
 
diff --git a/packages/docs/content/packages/core.mdx b/packages/docs/content/packages/core.mdx
index 7164f7a1..65b9ed10 100644
--- a/packages/docs/content/packages/core.mdx
+++ b/packages/docs/content/packages/core.mdx
@@ -88,9 +88,10 @@ export type $ZodTypes =
   | $ZodFile;
 ```
 
-All `zod/v4/core` subclasses only contain a single property: `_zod`. This property is an object containing the schemas *internals*. The goal is to make `zod/v4/core` as extensible and unopinionated as possible. Other libraries can "build their own Zod" on top of these classes without `zod/v4/core` cluttering up the interface. 
 
-Refer to the implementations of `zod/v4` and `zod/v4-mini` for examples of how to extend these classes. 
+## Internals
+
+All `zod/v4/core` subclasses only contain a single property: `_zod`. This property is an object containing the schemas *internals*. The goal is to make `zod/v4/core` as extensible and unopinionated as possible. Other libraries can "build their own Zod" on top of these classes without `zod/v4/core` cluttering up the interface. Refer to the implementations of `zod/v4` and `zod/v4-mini` for examples of how to extend these classes. 
 
 The `_zod` internals property contains some notable properties:
 
@@ -122,7 +123,6 @@ export function walk(_schema: z.$ZodType) {
 
 There are a number of subclasses of `$ZodString` that implement various *string formats*. These are exported as `z.$ZodStringFormatTypes`.
 
-
 ```ts
 export type $ZodStringFormatTypes =
   | $ZodGUID
@@ -151,6 +151,21 @@ export type $ZodStringFormatTypes =
 ```
 
 
+## Parsing 
+
+As the Zod Core schema classes have no methods, there are top-level functions for parsing data.
+
+```ts
+import * as z from "zod/v4/core";
+
+const schema = new z.$ZodString({ type: "string" });
+z.parse(schema, "hello");
+z.safeParse(schema, "hello");
+await z.parseAsync(schema, "hello");
+await z.safeParseAsync(schema, "hello");
+```
+
+
 ## Checks
 
 Every Zod schema contains an array of *checks*. These perform post-parsing refinements (and occasionally mutations) that *do not affect* the inferred type.
diff --git a/packages/docs/content/v4/changelog.mdx b/packages/docs/content/v4/changelog.mdx
index 1c6be066..0cef0445 100644
--- a/packages/docs/content/v4/changelog.mdx
+++ b/packages/docs/content/v4/changelog.mdx
@@ -363,7 +363,7 @@ type schemaInput = z.input<typeof schema>;
 
 ## `.default()` updates
 
-The application of `.default()` has changed in a subtle way. If the input is `undefined`, `ZodDefault` short-circuits the parsing process and returns the default value. The the default value must be assignable to the *output type*.
+The application of `.default()` has changed in a subtle way. If the input is `undefined`, `ZodDefault` short-circuits the parsing process and returns the default value. The default value must be assignable to the *output type*.
 
 ```ts
 const schema = z.string()
diff --git a/packages/docs/content/v4/index.mdx b/packages/docs/content/v4/index.mdx
index 10978acb..65d91819 100644
--- a/packages/docs/content/v4/index.mdx
+++ b/packages/docs/content/v4/index.mdx
@@ -28,7 +28,7 @@ Then import Zod 4 from the `"/v4"` subpath:
 import { z } from "zod/v4";
 ```
 
-Down the road, when there's broad support for Zod 4, we'll publish `zod@4.0.0` on npm. At this point, Zod 4 will be exported from the package root (`"zod"`). The `"zod/v4"` subpath will remain available. For a detailed writeup on the reasons for this versioning scheme, refer to [this writeup](https://github.com/colinhacks/zod/issues/4371). 
+Down the road, when there's broad support for Zod 4, we'll publish `zod@4.0.0` on npm. At this point, Zod 4 will be exported from the package root (`"zod"`). The `"zod/v4"` subpath will remain available. For a detailed writeup on the reasons for this versioning scheme, refer to [this issue](https://github.com/colinhacks/zod/issues/4371). 
 
 For a complete list of breaking changes, refer to the [Migration guide](/v4/changelog). This post focuses on new features & enhancements.
 
@@ -79,7 +79,7 @@ summary for z.string().parse
    14.71x faster than zod3
 ```
 
-### 3x faster array parsing
+### 7x faster array parsing
 
 ```sh
 $ pnpm bench array
@@ -396,7 +396,7 @@ z.toUpperCase();
 ```
 
 
-This more functional API makes it easier for bundlers to tree-shaking the APIs you don't use. While `zod/v4` is still recommended for the majority of use cases, any projects with uncommonly strict bundle size constraints should consider `zod/v4-mini`.  
+This more functional API makes it easier for bundlers to tree-shake the APIs you don't use. While `zod/v4` is still recommended for the majority of use cases, any projects with uncommonly strict bundle size constraints should consider `zod/v4-mini`.  
 
 ### 6.6x reduction in core bundle size
 
@@ -535,7 +535,7 @@ Refer to the [JSON Schema docs](/json-schema) for information on customizing the
 
 ## Recursive objects
 
-This was an unexpected on. After years of trying to crack this problem, I finally [found a way](https://x.com/colinhacks/status/1919286275133378670) to properly infer recursive object types in Zod. To define a recursive type:
+This was an unexpected one. After years of trying to crack this problem, I finally [found a way](https://x.com/colinhacks/status/1919286275133378670) to properly infer recursive object types in Zod. To define a recursive type:
 
 ```ts
 const Category = z.object({
diff --git a/packages/zod/build.mts b/packages/zod/build.mts
new file mode 100644
index 00000000..06ee6643
--- /dev/null
+++ b/packages/zod/build.mts
@@ -0,0 +1,123 @@
+// import * as fs from "node:fs";
+// import * as path from "node:path";
+import * as fs from "node:fs";
+import * as path from "node:path";
+import { execaSync } from "execa";
+// import { globbySync } from "globby";
+
+const $ = execaSync({ stdio: "inherit" });
+
+$`rm -rf ./dist`;
+
+function writePackageJson(dir: string, fields: Record<string, any>) {
+  const packageJsonPath = path.join(path.resolve(dir), "package.json");
+  fs.mkdirSync(dir, {
+    recursive: true,
+  });
+  console.log(`writing package.json to ${packageJsonPath}...`);
+  fs.writeFileSync(packageJsonPath, JSON.stringify(fields, null, 2));
+  return packageJsonPath;
+}
+
+console.log("building ESM...");
+const esmPkg = writePackageJson("./src", { type: "module" });
+$`pnpm tsc -p tsconfig.esm.json`;
+fs.rmSync(esmPkg, { force: true });
+
+// const esmDistDir = path.resolve("./dist/esm");
+// const esmPackageJsonPath = path.join(esmDistDir, "package.json");
+// fs.mkdirSync(esmDistDir, { recursive: true });
+// fs.writeFileSync(esmPackageJsonPath, JSON.stringify({ type: "module" }, null, 2));
+writePackageJson("./dist/esm", { type: "module" });
+
+console.log("building CJS...");
+const cjsPkg = writePackageJson("./src", { type: "commonjs" });
+$`pnpm tsc -p tsconfig.cjs.json`;
+fs.rmSync(cjsPkg, { force: true });
+// const cjsPath = "./dist/cjs";
+// const cjsDistDir = path.resolve(cjsPath);
+// const cjsPackageJsonPath = path.join(path.resolve(cjsPath), "package.json");
+// fs.mkdirSync(cjsDistDir, { recursive: true });
+// fs.writeFileSync(cjsPackageJsonPath, JSON.stringify({ type: "commonjs" }, null, 2));
+writePackageJson("./dist/cjs", { type: "commonjs" });
+
+console.log("building types...");
+$`pnpm tsc -p tsconfig.types.json`;
+// const typesPath = "./dist/types";
+// const typesDistDir = path.resolve(typesPath);
+// const typesPackageJsonPath = path.join(path.resolve(typesPath), "package.json");
+// fs.mkdirSync(typesDistDir, { recursive: true });
+// fs.writeFileSync(typesPackageJsonPath, JSON.stringify({ type: "module" }, null, 2));
+writePackageJson("./dist/types", { type: "commonjs" });
+
+console.log("DONE.");
+
+// /**
+//  * Synchronously builds TypeScript projects (ESM and CommonJS)
+//  * and recursively renames all .js files in the dist directory to .mjs.
+//  * Uses execa's $.sync for shell commands and globbySync for file finding.
+//  * Pipes subprocess logs (stdout/stderr) to the main process.
+//  */
+// // Resolve the absolute path to the 'dist' directory
+// const distDir = path.resolve("./dist");
+
+// // Check if the 'dist' directory exists and remove it if it does
+// // The 'recursive' and 'force' options ensure it works for non-empty directories
+// // and doesn't throw an error if the directory doesn't exist.
+// console.log("Cleaning up old dist directory..."); // Added log for clarity
+// if (fs.existsSync(distDir)) {
+//   // Use rmdirSync instead of rmSync for compatibility
+//   fs.rmSync(distDir, { recursive: true });
+//   console.log("Dist directory cleaned."); // Added log for clarity
+// } else {
+//   console.log("Dist directory does not exist, skipping clean-up."); // Added log for clarity
+// }
+
+// console.log("Compiling ES modules..."); // Added log for clarity
+// // Compile ES modules using pnpm tsc with the specified tsconfig
+// // $.sync executes the command synchronously and waits for it to complete.
+// // stdio: 'inherit' pipes the subprocess's stdout and stderr to the main process's console.
+// $`pnpm tsc -p tsconfig.esm.json`;
+// console.log("ES modules compiled."); // Added log for clarity
+
+// {
+//   console.log("Renaming .js files to .mjs in dist directory..."); // Added log for clarity
+//   const tsFiles = globbySync(`${distDir}/**/*.js`, {
+//     onlyFiles: true,
+//   });
+
+//   // Iterate over each found .js file and rename it to .mjs
+//   for (const file of tsFiles) {
+//     if (file.endsWith(".mjs")) continue; // skip if already .mjs
+//     // Create the new path by replacing the .js extension with .mjs
+//     const newPath = file.replace(/\.js$/, ".mjs");
+//     // Execute the 'mv' command synchronously to rename the file
+//     // Using $.sync for this shell command as well, with stdio inherited.
+//     fs.renameSync(file, newPath);
+//   }
+//   console.log("Files renamed to .mjs."); // Added log for clarity
+// }
+// console.log("Compiling CommonJS modules..."); // Added log for clarity
+// // Compile CommonJS modules using pnpm tsc with the specified tsconfig
+// // stdio: 'inherit' pipes the subprocess's stdout and stderr to the main process's console.
+// $`pnpm tsc -p tsconfig.cjs.json`;
+// console.log("CommonJS modules compiled."); // Added log for clarity
+
+// console.log("Build process completed successfully!"); // Added log for clarity
+// {
+//   console.log("Renaming .js files to .mjs in dist directory..."); // Added log for clarity
+//   const tsFiles = globbySync(`${distDir}/**/*.js`, {
+//     onlyFiles: true,
+//   });
+
+//   // Iterate over each found .js file and rename it to .mjs
+//   for (const file of tsFiles) {
+//     if (file.endsWith(".cjs")) continue; // skip if already .mjs
+//     // Create the new path by replacing the .js extension with .mjs
+//     const newPath = file.replace(/\.js$/, ".cjs");
+//     // Execute the 'mv' command synchronously to rename the file
+//     // Using $.sync for this shell command as well, with stdio inherited.
+//     fs.renameSync(file, newPath);
+//   }
+//   console.log("Files renamed to .cjs."); // Added log for clarity
+// }
diff --git a/packages/zod/package.json b/packages/zod/package.json
index 5478ddb7..be0db740 100644
--- a/packages/zod/package.json
+++ b/packages/zod/package.json
@@ -1,103 +1,79 @@
 {
   "name": "zod",
-  "version": "3.25.7",
+  "version": "3.25.28",
   "type": "module",
   "author": "Colin McDonnell <zod@colinhacks.com>",
   "description": "TypeScript-first schema declaration and validation library with static type inference",
-  "files": ["src", "dist"],
+  "files": ["dist", "v3", "v4", "v4-mini"],
   "funding": "https://github.com/sponsors/colinhacks",
   "homepage": "https://zod.dev",
   "keywords": ["typescript", "schema", "validation", "type", "inference"],
   "license": "MIT",
   "sideEffects": false,
-  "main": "./dist/commonjs/index.js",
-  "types": "./dist/esm/index.d.ts",
+  "main": "./dist/cjs/index.js",
+  "types": "./dist/types/index.d.ts",
   "module": "./dist/esm/index.js",
   "exports": {
     "./package.json": "./package.json",
     ".": {
       "import": {
         "@zod/source": "./src/index.ts",
-        "types": "./dist/esm/index.d.ts",
+        "types": "./dist/types/index.d.ts",
         "default": "./dist/esm/index.js"
       },
       "require": {
         "@zod/source": "./src/index.ts",
-        "types": "./dist/esm/index.d.ts",
-        "default": "./dist/commonjs/index.js"
+        "types": "./dist/types/index.d.ts",
+        "default": "./dist/cjs/index.js"
       }
     },
     "./v3": {
       "import": {
-        "@zod/source": "./src/v3/index.ts",
-        "types": "./dist/esm/v3/index.d.ts",
-        "default": "./dist/esm/v3/index.js"
+        "@zod/source": "./src/index.ts",
+        "types": "./dist/types/index.d.ts",
+        "default": "./dist/esm/index.js"
       },
       "require": {
-        "@zod/source": "./src/v3/index.ts",
-        "types": "./dist/esm/v3/index.d.ts",
-        "default": "./dist/commonjs/v3/index.js"
+        "@zod/source": "./src/index.ts",
+        "types": "./dist/types/index.d.ts",
+        "default": "./dist/cjs/index.js"
       }
     },
     "./v4": {
       "import": {
         "@zod/source": "./src/v4/index.ts",
-        "types": "./dist/esm/v4/index.d.ts",
+        "types": "./dist/types/v4/index.d.ts",
         "default": "./dist/esm/v4/index.js"
       },
       "require": {
         "@zod/source": "./src/v4/index.ts",
-        "types": "./dist/esm/v4/index.d.ts",
-        "default": "./dist/commonjs/v4/index.js"
+        "types": "./dist/types/v4/index.d.ts",
+        "default": "./dist/cjs/v4/index.js"
       }
     },
     "./v4-mini": {
-      "import": {
-        "@zod/source": "./src/v4/mini/index.ts",
-        "types": "./dist/esm/v4/mini/index.d.ts",
-        "default": "./dist/esm/v4/mini/index.js"
-      },
-      "require": {
-        "@zod/source": "./src/v4/mini/index.ts",
-        "types": "./dist/esm/v4/mini/index.d.ts",
-        "default": "./dist/commonjs/v4/mini/index.js"
-      }
+      "@zod/source": "./src/v4/mini/index.ts",
+      "types": "./dist/types/v4/mini/index.d.ts",
+      "import": "./dist/esm/v4/mini/index.js",
+      "require": "./dist/cjs/v4/mini/index.js"
     },
     "./v4/core": {
-      "import": {
-        "@zod/source": "./src/v4/core/index.ts",
-        "types": "./dist/esm/v4/core/index.d.ts",
-        "default": "./dist/esm/v4/core/index.js"
-      },
-      "require": {
-        "@zod/source": "./src/v4/core/index.ts",
-        "types": "./dist/esm/v4/core/index.d.ts",
-        "default": "./dist/commonjs/v4/core/index.js"
-      }
+      "@zod/source": "./src/v4/core/index.ts",
+      "types": "./dist/types/v4/core/index.d.ts",
+      "import": "./dist/esm/v4/core/index.js",
+      "require": "./dist/cjs/v4/core/index.js"
     },
     "./v4/locales": {
-      "import": {
-        "@zod/source": "./src/v4/locales/index.ts",
-        "types": "./dist/esm/v4/locales/index.d.ts",
-        "default": "./dist/esm/v4/locales/index.js"
-      },
-      "require": {
-        "@zod/source": "./src/v4/locales/index.ts",
-        "types": "./dist/esm/v4/locales/index.d.ts",
-        "default": "./dist/commonjs/v4/locales/index.js"
-      }
+      "@zod/source": "./src/v4/locales/index.ts",
+      "types": "./dist/types/v4/locales/index.d.ts",
+      "import": "./dist/esm/v4/locales/index.js",
+      "require": "./dist/cjs/v4/locales/index.js"
     },
     "./v4/locales/*": {
-      "import": {
-        "@zod/source": "./src/v4/locales/*",
-        "types": "./dist/esm/v4/locales/*",
-        "default": "./dist/esm/v4/locales/*"
-      },
-      "require": {
-        "@zod/source": "./src/v4/locales/*",
-        "types": "./dist/esm/v4/locales/*",
-        "default": "./dist/commonjs/v4/locales/*"
-      }
+      "@zod/source": "./src/v4/locales/*",
+      "types": "./dist/types/v4/locales/*",
+      "import": "./dist/esm/v4/locales/*",
+      "require": "./dist/cjs/v4/locales/*"
     }
   },
   "tshy": {
@@ -128,8 +104,8 @@
   },
   "scripts": {
     "clean": "rm -rf dist",
-    "build": "supershy",
-    "postbuild": "pnpm biome check --write . && tsx postbuild.ts",
+    "build": "tsx ./build.mts",
+    "postbuild": "pnpm biome check --write .",
     "test:watch": "pnpm vitest",
     "test": "pnpm vitest run",
     "bump:beta": "pnpm version \"v$(pnpm pkg get version | jq -r)-beta.$(date +%Y%m%dT%H%M%S)\"",
diff --git a/packages/zod/src/v4/classic/schemas.ts b/packages/zod/src/v4/classic/schemas.ts
index b8ca8970..8cc254cc 100644
--- a/packages/zod/src/v4/classic/schemas.ts
+++ b/packages/zod/src/v4/classic/schemas.ts
@@ -35,8 +35,8 @@ export interface ZodType<out Output = unknown, out Input = unknown> extends core
     registry: R,
     ...meta: this extends R["_schema"]
       ? undefined extends R["_meta"]
-        ? [core.$ZodRegistry<R["_meta"], this>["_meta"]?]
-        : [core.$ZodRegistry<R["_meta"], this>["_meta"]]
+        ? [core.$replace<R["_meta"], this>?]
+        : [core.$replace<R["_meta"], this>]
       : ["Incompatible schema"]
   ): this;
 
@@ -116,7 +116,7 @@ export interface ZodType<out Output = unknown, out Input = unknown> extends core
 export const ZodType: core.$constructor<ZodType> = /*@__PURE__*/ core.$constructor("ZodType", (inst, def) => {
   core.$ZodType.init(inst, def);
   inst.def = def;
-  inst._def = def;
+  Object.defineProperty(inst, "_def", { value: def });
 
   // base methods
   inst.check = (...checks) => {
@@ -1064,7 +1064,7 @@ export function keyof<T extends ZodObject>(schema: T): ZodLiteral<keyof T["_zod"
 // ZodObject
 
 export interface ZodObject<
-  // @ts-ignore cast variance
+  /** @ts-ignore Cast variance */
   out Shape extends core.$ZodShape = core.$ZodLooseShape,
   out Config extends core.$ZodObjectConfig = core.$ZodObjectConfig,
 > extends ZodType {
@@ -1540,11 +1540,21 @@ export function nativeEnum<T extends util.EnumLike>(entries: T, params?: string
 export interface ZodLiteral<T extends util.Primitive = util.Primitive> extends ZodType {
   _zod: core.$ZodLiteralInternals<T>;
   values: Set<T>;
+  /** @legacy Use `.values` instead. Accessing this property will throw an error if the literal accepts multiple values. */
+  value: T;
 }
 export const ZodLiteral: core.$constructor<ZodLiteral> = /*@__PURE__*/ core.$constructor("ZodLiteral", (inst, def) => {
   core.$ZodLiteral.init(inst, def);
   ZodType.init(inst, def);
   inst.values = new Set(def.values);
+  Object.defineProperty(inst, "value", {
+    get() {
+      if (def.values.length > 1) {
+        throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");
+      }
+      return def.values[0];
+    },
+  });
 });
 
 export function literal<const T extends Array<util.Literal>>(
@@ -1962,10 +1972,10 @@ export function check<O = unknown>(fn: core.CheckFn<O>, params?: string | core.$
   return ch;
 }
 
-export function custom<O = unknown, I = O>(
-  fn?: (data: O) => unknown,
+export function custom<O>(
+  fn?: (data: unknown) => unknown,
   _params?: string | core.$ZodCustomParams | undefined
-): ZodCustom<O, I> {
+): ZodCustom<O, O> {
   return core._custom(ZodCustom, fn ?? (() => true), _params) as any;
 }
 
diff --git a/packages/zod/src/v4/core/checks.ts b/packages/zod/src/v4/core/checks.ts
index 5202d58e..a19026e5 100644
--- a/packages/zod/src/v4/core/checks.ts
+++ b/packages/zod/src/v4/core/checks.ts
@@ -313,7 +313,7 @@ export const $ZodCheckNumberFormat: core.$constructor<$ZodCheckNumberFormat> = /
               input,
               code: "too_big",
               maximum: Number.MAX_SAFE_INTEGER,
-              note: "Integers must be within the the safe integer range.",
+              note: "Integers must be within the safe integer range.",
               inst,
               origin,
               continue: !def.abort,
diff --git a/packages/zod/src/v4/core/core.ts b/packages/zod/src/v4/core/core.ts
index 707c5a05..fc7f86c6 100644
--- a/packages/zod/src/v4/core/core.ts
+++ b/packages/zod/src/v4/core/core.ts
@@ -14,42 +14,46 @@ export /*@__NO_SIDE_EFFECTS__*/ function $constructor<T extends ZodTrait, D = T[
   initializer: (inst: T, def: D) => void,
   params?: { Parent?: typeof Class }
 ): $constructor<T, D> {
-  const Parent = params?.Parent ?? Object;
+  function init(inst: T, def: D) {
+    Object.defineProperty(inst, "_zod", {
+      value: inst._zod ?? {},
+      enumerable: false,
+    });
+
+    inst._zod.traits ??= new Set();
 
-  class _ extends Parent {
-    constructor(def: D) {
-      super();
-      const th = this as any;
-      _.init(th, def);
-      th._zod.deferred ??= [];
-      for (const fn of th._zod.deferred) {
-        fn();
-      }
+    inst._zod.traits.add(name);
+    initializer(inst, def);
+    // support prototype modifications
+    for (const k in _.prototype) {
+      Object.defineProperty(inst, k, { value: (_.prototype as any)[k].bind(inst) });
     }
-    static init(inst: T, def: D) {
-      Object.defineProperty(inst, "_zod", {
-        value: inst._zod ?? {},
-        enumerable: false,
-      });
-      // inst._zod ??= {} as any;
-      inst._zod.traits ??= new Set();
-      // const seen = inst._zod.traits.has(name);
-
-      inst._zod.traits.add(name);
-      initializer(inst, def);
-      // support prototype modifications
-      for (const k in _.prototype) {
-        Object.defineProperty(inst, k, { value: (_.prototype as any)[k].bind(inst) });
-      }
-      inst._zod.constr = _;
-      inst._zod.def = def;
+    inst._zod.constr = _;
+    inst._zod.def = def;
+  }
+
+  // doesn't work if Parent has a constructor with arguments
+  const Parent = params?.Parent ?? Object;
+  class Definition extends Parent {}
+  Object.defineProperty(Definition, "name", { value: name });
+
+  function _(this: any, def: D) {
+    const inst = params?.Parent ? new Definition() : this;
+    init(inst, def);
+    inst._zod.deferred ??= [];
+    for (const fn of inst._zod.deferred) {
+      fn();
     }
+    return inst;
+  }
 
-    static override [Symbol.hasInstance](inst: any) {
+  Object.defineProperty(_, "init", { value: init });
+  Object.defineProperty(_, Symbol.hasInstance, {
+    value: (inst: any) => {
       if (params?.Parent && inst instanceof params.Parent) return true;
       return inst?._zod?.traits?.has(name);
-    }
-  }
+    },
+  });
   Object.defineProperty(_, "name", { value: name });
   return _ as any;
 }
diff --git a/packages/zod/src/v4/core/errors.ts b/packages/zod/src/v4/core/errors.ts
index 59fb738b..fff075e5 100644
--- a/packages/zod/src/v4/core/errors.ts
+++ b/packages/zod/src/v4/core/errors.ts
@@ -86,7 +86,7 @@ export interface $ZodIssueInvalidValue<Input = unknown> extends $ZodIssueBase {
 }
 
 export interface $ZodIssueCustom extends $ZodIssueBase {
-  readonly code?: "custom";
+  readonly code: "custom";
   readonly params?: Record<string, any> | undefined;
   readonly input: unknown;
 }
@@ -149,6 +149,8 @@ export type $ZodIssue =
   | $ZodIssueInvalidValue
   | $ZodIssueCustom;
 
+export type $ZodIssueCode = $ZodIssue["code"];
+
 export type $ZodRawIssue<T extends $ZodIssueBase = $ZodIssue> = T extends any ? RawIssue<T> : never;
 type RawIssue<T extends $ZodIssueBase> = util.Flatten<
   util.MakePartial<T, "message" | "path"> & {
@@ -190,7 +192,6 @@ const initializer = (inst: $ZodError, def: $ZodIssue[]): void => {
     value: def,
     enumerable: false,
   });
-  // inst.message = JSON.stringify(def, util.jsonStringifyReplacer, 2);
   Object.defineProperty(inst, "message", {
     get() {
       return JSON.stringify(def, util.jsonStringifyReplacer, 2);
@@ -198,22 +199,6 @@ const initializer = (inst: $ZodError, def: $ZodIssue[]): void => {
     enumerable: true,
     // configurable: false,
   });
-  // inst.toString = () => inst.message;
-
-  // inst.message = `Invalid input`;
-  // Object.defineProperty(inst, "message", {
-  //   get() {
-  //     return (
-  //       "\n" +
-  //       inst.issues
-  //         .map((iss) => {
-  //           return `✖ ${iss.message}${iss.path.length ? ` [${iss.path.join(".")}]` : ""}`;
-  //         })
-  //         .join("\n")
-  //     );
-  //   },
-  //   enumerable: false,
-  // });
 };
 
 export const $ZodError: $constructor<$ZodError> = $constructor("$ZodError", initializer);
diff --git a/packages/zod/src/v4/core/function.ts b/packages/zod/src/v4/core/function.ts
index a8057cb5..a3bae95e 100644
--- a/packages/zod/src/v4/core/function.ts
+++ b/packages/zod/src/v4/core/function.ts
@@ -12,40 +12,55 @@ import type * as util from "./util.js";
 //////////                      //////////
 //////////////////////////////////////////
 //////////////////////////////////////////
-export interface $ZodFunctionDef /* extends schemas.$ZodTypeDef */ {
+export interface $ZodFunctionDef<
+  In extends $ZodFunctionIn = $ZodFunctionIn,
+  Out extends $ZodFunctionOut = $ZodFunctionOut,
+> {
   type: "function";
-  input: $ZodFunctionArgs | null;
-  output: schemas.$ZodType | null;
+  input: In;
+  output: Out;
 }
 
 export type $ZodFunctionArgs = schemas.$ZodType<unknown[], unknown[]>;
+export type $ZodFunctionIn = $ZodFunctionArgs | null;
+export type $ZodFunctionOut = schemas.$ZodType | null;
 
-export type $InferInnerFunctionType<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
-  ...args: Args["_zod"]["output"]
-) => Returns["_zod"]["input"];
+export type $InferInnerFunctionType<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
+  ...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["output"]
+) => null extends Returns ? unknown : NonNullable<Returns>["_zod"]["input"];
 
-export type $InferInnerFunctionTypeAsync<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
-  ...args: Args["_zod"]["output"]
-) => util.MaybeAsync<Returns["_zod"]["input"]>;
+export type $InferInnerFunctionTypeAsync<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
+  ...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["output"]
+) => null extends Returns ? unknown : util.MaybeAsync<NonNullable<Returns>["_zod"]["input"]>;
 
-export type $InferOuterFunctionType<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
-  ...args: Args["_zod"]["input"]
-) => Returns["_zod"]["output"];
+export type $InferOuterFunctionType<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
+  ...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["input"]
+) => null extends Returns ? unknown : NonNullable<Returns>["_zod"]["output"];
 
-export type $InferOuterFunctionTypeAsync<Args extends $ZodFunctionArgs, Returns extends schemas.$ZodType> = (
-  ...args: Args["_zod"]["input"]
-) => util.MaybeAsync<Returns["_zod"]["output"]>;
+export type $InferOuterFunctionTypeAsync<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
+  ...args: null extends Args ? never[] : NonNullable<Args>["_zod"]["input"]
+) => null extends Returns ? unknown : util.MaybeAsync<NonNullable<Returns>["_zod"]["output"]>;
 
 export class $ZodFunction<
-  Args extends $ZodFunctionArgs = $ZodFunctionArgs,
-  Returns extends schemas.$ZodType = schemas.$ZodType,
+  Args extends $ZodFunctionIn = $ZodFunctionIn,
+  Returns extends $ZodFunctionOut = $ZodFunctionOut,
 > {
-  _def: $ZodFunctionDef;
+  // _zod!: {
+  //   def: $ZodFunctionDef<Args, Returns>;
+  //   input: $InferInnerFunctionType<Args, Returns>;
+  //   output: $InferOuterFunctionType<Args, Returns>;
+  // };
+
+  def: $ZodFunctionDef<Args, Returns>;
+
+  /** @deprecated */
+  _def!: $ZodFunctionDef<Args, Returns>;
   _input!: $InferInnerFunctionType<Args, Returns>;
   _output!: $InferOuterFunctionType<Args, Returns>;
 
-  constructor(def: $ZodFunctionDef) {
+  constructor(def: $ZodFunctionDef<Args, Returns>) {
     this._def = def;
+    this.def = def;
   }
 
   implement<F extends $InferInnerFunctionType<Args, Returns>>(
@@ -59,7 +74,7 @@ export class $ZodFunction<
       if (!Array.isArray(parsedArgs)) {
         throw new Error("Invalid arguments schema: not an array or tuple schema.");
       }
-      const output = func(...parsedArgs);
+      const output = func(...(parsedArgs as any));
       return this._def.output ? parse(this._def.output, output, undefined, { callee: impl }) : output;
     }) as any;
     return impl;
@@ -77,17 +92,17 @@ export class $ZodFunction<
       if (!Array.isArray(parsedArgs)) {
         throw new Error("Invalid arguments schema: not an array or tuple schema.");
       }
-      const output = await func(...parsedArgs);
+      const output = await func(...(parsedArgs as any));
       return this._def.output ? parseAsync(this._def.output, output, undefined, { callee: impl }) : output;
     }) as any;
     return impl;
   }
 
-  input<const Items extends util.TupleItems, const Rest extends schemas.$ZodType | null = null>(
+  input<const Items extends util.TupleItems, const Rest extends $ZodFunctionOut = null>(
     args: Items,
     rest?: Rest
   ): $ZodFunction<schemas.$ZodTuple<Items, Rest>, Returns>;
-  input<NewArgs extends $ZodFunctionArgs>(args: NewArgs): $ZodFunction<NewArgs, Returns>;
+  input<NewArgs extends $ZodFunctionIn>(args: NewArgs): $ZodFunction<NewArgs, Returns>;
   input(...args: any[]): $ZodFunction<any, Returns> {
     if (Array.isArray(args[0])) {
       return new $ZodFunction({
@@ -116,19 +131,28 @@ export class $ZodFunction<
   }
 }
 
-export interface $ZodFunctionParams<I extends $ZodFunctionArgs, O extends schemas.$ZodType> {
+export interface $ZodFunctionParams<I extends $ZodFunctionIn, O extends schemas.$ZodType> {
   input?: I;
   output?: O;
 }
 
 function _function(): $ZodFunction;
+function _function<const In extends Array<schemas.$ZodType> = Array<schemas.$ZodType>>(params: {
+  input: In;
+}): $ZodFunction<$ZodTuple<In, null>, null>;
+function _function<const In extends $ZodFunctionIn = $ZodFunctionIn>(params: {
+  input: In;
+}): $ZodFunction<In, null>;
+function _function<const Out extends $ZodFunctionOut = $ZodFunctionOut>(params: {
+  output: Out;
+}): $ZodFunction<null, Out>;
 function _function<
-  const In extends Array<schemas.$ZodType> = Array<schemas.$ZodType>,
+  In extends $ZodFunctionIn = $ZodFunctionIn,
   Out extends schemas.$ZodType = schemas.$ZodType,
->(params?: { input?: In; output?: Out }): $ZodFunction<$ZodTuple<In, null>, Out>;
-function _function<In extends $ZodFunctionArgs = $ZodFunctionArgs, Out extends schemas.$ZodType = schemas.$ZodType>(
-  params?: $ZodFunctionParams<In, Out>
-): $ZodFunction<In, Out>;
+>(params?: {
+  input: In;
+  output: Out;
+}): $ZodFunction<In, Out>;
 function _function(params?: {
   output?: schemas.$ZodType;
   input?: $ZodFunctionArgs | Array<schemas.$ZodType>;
diff --git a/packages/zod/src/v4/core/schemas.ts b/packages/zod/src/v4/core/schemas.ts
index d1e976eb..03dd354e 100644
--- a/packages/zod/src/v4/core/schemas.ts
+++ b/packages/zod/src/v4/core/schemas.ts
@@ -81,7 +81,6 @@ export interface $ZodTypeDef {
   checks?: checks.$ZodCheck<never>[];
 }
 
-// @ts-ignore
 /** @internal */
 export interface $ZodTypeInternals<out O = unknown, out I = unknown> {
   /** The `@zod/core` version of this schema */
@@ -1582,7 +1581,7 @@ export interface $ZodObjectDef<Shape extends $ZodShape = $ZodShape> extends $Zod
 }
 
 export interface $ZodObjectInternals<
-  // @ts-ignore Cast variance
+  /** @ts-ignore Cast variance */
   out Shape extends Readonly<$ZodShape> = Readonly<$ZodShape>,
   out Config extends $ZodObjectConfig = $ZodObjectConfig,
 > extends $ZodTypeInternals<any, any> {
@@ -1676,7 +1675,7 @@ export type $catchall<T extends $ZodType> = {
   in: { [k: string]: core.input<T> };
 };
 export interface $ZodObject<
-  // @ts-ignore Cast variance
+  /** @ts-ignore Cast variance */
   out Shape extends Readonly<$ZodShape> = Readonly<$ZodShape>,
   out Params extends $ZodObjectConfig = $ZodObjectConfig,
 > extends $ZodType {
@@ -2714,7 +2713,7 @@ export interface $ZodEnumDef<T extends util.EnumLike = util.EnumLike> extends $Z
 }
 
 export interface $ZodEnumInternals<
-  // @ts-ignore Cast variance
+  /** @ts-ignore Cast variance */
   out T extends util.EnumLike = util.EnumLike,
 > extends $ZodTypeInternals<$InferEnumOutput<T>, $InferEnumInput<T>> {
   // enum: T;
@@ -2778,7 +2777,7 @@ export interface $ZodLiteralDef extends $ZodTypeDef {
 
 export interface $ZodLiteralInternals<T extends util.Primitive = util.Primitive> extends $ZodTypeInternals<T, T> {
   def: $ZodLiteralDef;
-  values: util.PrimitiveSet;
+  values: Set<T>;
   pattern: RegExp;
   isst: errors.$ZodIssueInvalidValue;
 }
@@ -2866,7 +2865,10 @@ export const $ZodLiteral: core.$constructor<$ZodLiteral> = /*@__PURE__*/ core.$c
 //////////////////////////////////////////
 
 // provide a fallback in case the File interface isn't provided in the environment
-interface File {}
+declare global {
+  interface File {}
+}
+
 export interface $ZodFileDef extends $ZodTypeDef {
   type: "file";
 }
diff --git a/packages/zod/src/v4/core/to-json-schema.ts b/packages/zod/src/v4/core/to-json-schema.ts
index 7d8a0aab..060298ac 100644
--- a/packages/zod/src/v4/core/to-json-schema.ts
+++ b/packages/zod/src/v4/core/to-json-schema.ts
@@ -16,7 +16,7 @@ interface JSONSchemaGeneratorParams {
    * - `"any"` — Unrepresentable types become `{}` */
   unrepresentable?: "throw" | "any";
   /** Arbitrary custom logic that can be used to modify the generated JSON Schema. */
-  override?: (ctx: { zodSchema: schemas.$ZodType; jsonSchema: JSONSchema.BaseSchema }) => void;
+  override?: (ctx: { zodSchema: schemas.$ZodTypes; jsonSchema: JSONSchema.BaseSchema }) => void;
   /** Whether to extract the `"input"` or `"output"` type. Relevant to transforms, Error converting schema to JSONz, defaults, coerced primitives, etc.
    * - `"output" — Default. Convert the output schema.
    * - `"input"` — Convert the input schema. */
@@ -73,7 +73,7 @@ export class JSONSchemaGenerator {
   metadataRegistry: $ZodRegistry<Record<string, any>>;
   target: "draft-7" | "draft-2020-12";
   unrepresentable: "throw" | "any";
-  override: (ctx: { zodSchema: schemas.$ZodType; jsonSchema: JSONSchema.BaseSchema }) => void;
+  override: (ctx: { zodSchema: schemas.$ZodTypes; jsonSchema: JSONSchema.BaseSchema }) => void;
   io: "input" | "output";
 
   counter = 0;
@@ -285,7 +285,11 @@ export class JSONSchemaGenerator {
 
           // catchall
           if (def.catchall?._zod.def.type === "never") {
+            // strict
             json.additionalProperties = false;
+          } else if (!def.catchall) {
+            // regular
+            if (this.io === "output") json.additionalProperties = false;
           } else if (def.catchall) {
             json.additionalProperties = this.process(def.catchall, {
               ...params,
@@ -604,7 +608,6 @@ export class JSONSchemaGenerator {
       // wipe away all properties except $ref
       const schema = seen.schema;
       for (const key in schema) {
-        // @ts-ignore
         delete schema[key];
         schema.$ref = ref;
       }
@@ -691,7 +694,7 @@ export class JSONSchemaGenerator {
 
       if (!seen.isParent)
         this.override({
-          zodSchema,
+          zodSchema: zodSchema as schemas.$ZodTypes,
           jsonSchema: schema,
         });
     };
@@ -720,9 +723,9 @@ export class JSONSchemaGenerator {
     }
 
     if (this.target === "draft-2020-12") {
-      result.$schema = "https://json-schema.org/draft-2020-12/schema";
+      result.$schema = "https://json-schema.org/draft/2020-12/schema";
     } else if (this.target === "draft-7") {
-      result.$schema = "https://json-schema.org/draft-07/schema";
+      result.$schema = "http://json-schema.org/draft-07/schema#";
     } else {
       console.warn(`Invalid target: ${this.target}`);
     }
diff --git a/packages/zod/src/v4/mini/schemas.ts b/packages/zod/src/v4/mini/schemas.ts
index d4b9b9a4..6b4f54aa 100644
--- a/packages/zod/src/v4/mini/schemas.ts
+++ b/packages/zod/src/v4/mini/schemas.ts
@@ -14,8 +14,8 @@ export interface ZodMiniType<out Output = unknown, out Input = unknown> extends
     registry: R,
     ...meta: this extends R["_schema"]
       ? undefined extends R["_meta"]
-        ? [core.$ZodRegistry<R["_meta"], this>["_meta"]?]
-        : [core.$ZodRegistry<R["_meta"], this>["_meta"]]
+        ? [core.$replace<R["_meta"], this>?]
+        : [core.$replace<R["_meta"], this>]
       : ["Incompatible schema"]
   ): this;
   brand<T extends PropertyKey = PropertyKey>(
@@ -695,7 +695,7 @@ export function keyof<T extends ZodMiniObject>(schema: T): ZodMiniLiteral<keyof
 
 // ZodMiniObject
 export interface ZodMiniObject<
-  // @ts-ignore Cast variance
+  /** @ts-ignore Cast variance */
   out Shape extends core.$ZodShape = core.$ZodShape,
   out Config extends core.$ZodObjectConfig = core.$ZodObjectConfig,
 > extends ZodMiniType {
diff --git a/packages/zod/tsconfig.cjs.json b/packages/zod/tsconfig.cjs.json
new file mode 100644
index 00000000..87658db7
--- /dev/null
+++ b/packages/zod/tsconfig.cjs.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../.configs/tsconfig.base.json",
+  "compilerOptions": {
+    "rootDir": "./src",
+    "module": "Node16",
+    "moduleResolution": "node16",
+    "customConditions": ["@zod/source"],
+    "outDir": "./dist/cjs"
+  },
+  "include": ["src"],
+  "exclude": ["**/tests/**"]
+}
diff --git a/packages/zod/tsconfig.esm.json b/packages/zod/tsconfig.esm.json
new file mode 100644
index 00000000..dc020bde
--- /dev/null
+++ b/packages/zod/tsconfig.esm.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../.configs/tsconfig.base.json",
+  "compilerOptions": {
+    "rootDir": "./src",
+    "module": "esnext",
+    "moduleResolution": "bundler",
+    "customConditions": ["@zod/source"],
+    "outDir": "./dist/esm"
+  },
+  "include": ["src"],
+  "exclude": ["**/tests/**"]
+}
diff --git a/packages/zod/tsconfig.json b/packages/zod/tsconfig.json
index d71e7512..36fb9c8d 100644
--- a/packages/zod/tsconfig.json
+++ b/packages/zod/tsconfig.json
@@ -1,9 +1,6 @@
 {
   "extends": "../../.configs/tsconfig.base.json",
   "compilerOptions": {
-    "isolatedDeclarations": false,
-    "composite": true,
-    "exactOptionalPropertyTypes": true,
     "module": "NodeNext",
     "moduleResolution": "nodenext",
     "customConditions": ["@zod/source"]
diff --git a/packages/zod/tsconfig.types.json b/packages/zod/tsconfig.types.json
new file mode 100644
index 00000000..cd30daa5
--- /dev/null
+++ b/packages/zod/tsconfig.types.json
@@ -0,0 +1,15 @@
+{
+  "extends": "../../.configs/tsconfig.base.json",
+  "compilerOptions": {
+    "rootDir": "./src",
+    "module": "Node16",
+    "moduleResolution": "node16",
+    "customConditions": ["@zod/source"],
+    "declaration": true,
+    "emitDeclarationOnly": true,
+    "outDir": "./dist/types",
+    "removeComments": false
+  },
+  "include": ["src"],
+  "exclude": ["**/tests/**"]
+}
diff --git a/packages/zod/v3/index.d.ts b/packages/zod/v3/index.d.ts
new file mode 100644
index 00000000..5e93f214
--- /dev/null
+++ b/packages/zod/v3/index.d.ts
@@ -0,0 +1,1 @@
+export * from "../dist/types/v3/index.d.ts";
diff --git a/packages/zod/v3/index.js b/packages/zod/v3/index.js
new file mode 100644
index 00000000..ee04209d
--- /dev/null
+++ b/packages/zod/v3/index.js
@@ -0,0 +1,1 @@
+export * from "../dist/esm/v3/index.js";
diff --git a/packages/zod/v4-mini/index.d.ts b/packages/zod/v4-mini/index.d.ts
new file mode 100644
index 00000000..63288d8c
--- /dev/null
+++ b/packages/zod/v4-mini/index.d.ts
@@ -0,0 +1,1 @@
+export * from "../dist/types/v4/mini/index.d.ts";
diff --git a/packages/zod/v4-mini/index.js b/packages/zod/v4-mini/index.js
new file mode 100644
index 00000000..509f8a8d
--- /dev/null
+++ b/packages/zod/v4-mini/index.js
@@ -0,0 +1,1 @@
+export * from "../dist/esm/v4/mini/index.js";
diff --git a/packages/zod/v4/core/index.d.ts b/packages/zod/v4/core/index.d.ts
new file mode 100644
index 00000000..dd890b7f
--- /dev/null
+++ b/packages/zod/v4/core/index.d.ts
@@ -0,0 +1,1 @@
+export * from "../../dist/types/v4/core/index.d.ts";
diff --git a/packages/zod/v4/core/index.js b/packages/zod/v4/core/index.js
new file mode 100644
index 00000000..6e6c051c
--- /dev/null
+++ b/packages/zod/v4/core/index.js
@@ -0,0 +1,1 @@
+export * from "../../dist/esm/v4/core/index.js";
diff --git a/packages/zod/v4/index.d.ts b/packages/zod/v4/index.d.ts
new file mode 100644
index 00000000..aa34be52
--- /dev/null
+++ b/packages/zod/v4/index.d.ts
@@ -0,0 +1,1 @@
+export * from "../dist/types/v4/index.d.ts";
diff --git a/packages/zod/v4/index.js b/packages/zod/v4/index.js
new file mode 100644
index 00000000..ad08461b
--- /dev/null
+++ b/packages/zod/v4/index.js
@@ -0,0 +1,1 @@
+export * from "../dist/esm/v4/index.js";
diff --git a/packages/zod/v4/locales/en.d.ts b/packages/zod/v4/locales/en.d.ts
new file mode 100644
index 00000000..41378370
--- /dev/null
+++ b/packages/zod/v4/locales/en.d.ts
@@ -0,0 +1,2 @@
+export * from "../../dist/types/v4/locales/en.d.ts";
+export { default } from "../../dist/types/v4/locales/en.d.ts";
diff --git a/packages/zod/v4/locales/en.js b/packages/zod/v4/locales/en.js
new file mode 100644
index 00000000..9d4b4794
--- /dev/null
+++ b/packages/zod/v4/locales/en.js
@@ -0,0 +1,2 @@
+export * from "../../dist/esm/v4/locales/en.js";
+export { default } from "../../dist/esm/v4/locales/en.js";
diff --git a/packages/zod/v4/locales/index.d.ts b/packages/zod/v4/locales/index.d.ts
new file mode 100644
index 00000000..2f123947
--- /dev/null
+++ b/packages/zod/v4/locales/index.d.ts
@@ -0,0 +1,1 @@
+export * from "../../dist/types/v4/locales/index.d.ts";
diff --git a/packages/zod/v4/locales/index.js b/packages/zod/v4/locales/index.js
new file mode 100644
index 00000000..5e244534
--- /dev/null
+++ b/packages/zod/v4/locales/index.js
@@ -0,0 +1,1 @@
+export * from "../../dist/esm/v4/locales/index.js";
diff --git a/play.ts b/play.ts
index 734571f9..fcc1a10b 100644
--- a/play.ts
+++ b/play.ts
@@ -1,9 +1,13 @@
-import type { LanguageModelV1 } from "@ai-sdk/provider";
-import { generateObject } from "ai";
-import type { z } from "zod";
+import * as z from "zod/v4";
 
-export function myFn<T>(model: LanguageModelV1, schema: z.Schema<T>) {
-  return generateObject({ model, schema });
-}
+export const a = z.object({
+  asdf: z.iso.datetime(),
+});
 
-myFn;
+const fn = z.function({
+  // input: z.tuple([z.string()], z.number()),
+  output: z.string(),
+});
+
+fn.def.output;
+fn.def.input;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4ab2934e..a8632891 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,6 +41,9 @@ importers:
       execa:
         specifier: ^9.5.2
         version: 9.5.2
+      globby:
+        specifier: ^14.1.0
+        version: 14.1.0
       husky:
         specifier: ^7.0.4
         version: 7.0.4
@@ -56,6 +59,9 @@ importers:
       prettier:
         specifier: ^3.5.3
         version: 3.5.3
+      semver:
+        specifier: ^7.7.2
+        version: 7.7.2
       supershy:
         specifier: ^1.0.0
         version: 1.0.0
@@ -109,7 +115,7 @@ importers:
     dependencies:
       '@inkeep/cxkit-react':
         specifier: ^0.5.52
-        version: 0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@packages+zod)
+        version: 0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.3)
       '@radix-ui/react-accordion':
         specifier: ^1.2.4
         version: 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -2284,6 +2290,10 @@ packages:
     resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
     engines: {node: '>=10'}
 
+  '@sindresorhus/merge-streams@2.3.0':
+    resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
+    engines: {node: '>=18'}
+
   '@sindresorhus/merge-streams@4.0.0':
     resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
     engines: {node: '>=18'}
@@ -3427,6 +3437,10 @@ packages:
     engines: {node: '>=12'}
     deprecated: Glob versions prior to v9 are no longer supported
 
+  globby@14.1.0:
+    resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==}
+    engines: {node: '>=18'}
+
   gopd@1.2.0:
     resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
     engines: {node: '>= 0.4'}
@@ -3560,6 +3574,10 @@ packages:
     resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
 
+  ignore@7.0.4:
+    resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==}
+    engines: {node: '>= 4'}
+
   image-size@2.0.1:
     resolution: {integrity: sha512-NI6NK/2zchlZopsQrcVIS7jxA0/rtIy74B+/rx5s7rKQyFebmQjZVhzxXgRZJROk+WhhOq+S6sUaODxp0L5hfg==}
     engines: {node: '>=16.x'}
@@ -4457,6 +4475,10 @@ packages:
     resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==}
     engines: {node: 20 || >=22}
 
+  path-type@6.0.0:
+    resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==}
+    engines: {node: '>=18'}
+
   pathe@1.1.2:
     resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
 
@@ -4824,16 +4846,6 @@ packages:
     resolution: {integrity: sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==}
     engines: {node: '>=6'}
 
-  semver@7.6.3:
-    resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
-    engines: {node: '>=10'}
-    hasBin: true
-
-  semver@7.7.1:
-    resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
-    engines: {node: '>=10'}
-    hasBin: true
-
   semver@7.7.2:
     resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
     engines: {node: '>=10'}
@@ -4889,6 +4901,10 @@ packages:
     resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==}
     engines: {node: '>=8'}
 
+  slash@5.1.0:
+    resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
+    engines: {node: '>=14.16'}
+
   slice-ansi@3.0.0:
     resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
     engines: {node: '>=8'}
@@ -5588,7 +5604,7 @@ snapshots:
       commander: 10.0.1
       marked: 9.1.6
       marked-terminal: 7.3.0(marked@9.1.6)
-      semver: 7.7.1
+      semver: 7.7.2
 
   '@arethetypeswrong/core@0.17.4':
     dependencies:
@@ -5597,7 +5613,7 @@ snapshots:
       cjs-module-lexer: 1.4.3
       fflate: 0.8.2
       lru-cache: 10.4.3
-      semver: 7.7.1
+      semver: 7.7.2
       typescript: 5.6.1-rc
       validate-npm-package-name: 5.0.1
 
@@ -6012,7 +6028,7 @@ snapshots:
 
   '@inkeep/cxkit-color-mode@0.5.52': {}
 
-  '@inkeep/cxkit-primitives@0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@packages+zod)':
+  '@inkeep/cxkit-primitives@0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.3)':
     dependencies:
       '@inkeep/cxkit-color-mode': 0.5.52
       '@inkeep/cxkit-theme': 0.5.52
@@ -6044,7 +6060,7 @@ snapshots:
       dequal: 2.0.3
       humps: 2.0.1
       merge-anything: 5.1.7
-      openai: 4.78.1(encoding@0.1.13)(zod@packages+zod)
+      openai: 4.78.1(encoding@0.1.13)(zod@3.24.3)
       prism-react-renderer: 2.4.1(react@19.0.0)
       react: 19.0.0
       react-dom: 19.0.0(react@19.0.0)
@@ -6065,9 +6081,9 @@ snapshots:
       - supports-color
       - zod
 
-  '@inkeep/cxkit-react@0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@packages+zod)':
+  '@inkeep/cxkit-react@0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.3)':
     dependencies:
-      '@inkeep/cxkit-styled': 0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@packages+zod)
+      '@inkeep/cxkit-styled': 0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.3)
       '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.11)(react@19.0.0)
     transitivePeerDependencies:
       - '@types/react'
@@ -6078,9 +6094,9 @@ snapshots:
       - supports-color
       - zod
 
-  '@inkeep/cxkit-styled@0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@packages+zod)':
+  '@inkeep/cxkit-styled@0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.3)':
     dependencies:
-      '@inkeep/cxkit-primitives': 0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@packages+zod)
+      '@inkeep/cxkit-primitives': 0.5.52(@types/react-dom@19.0.4(@types/react@19.0.11))(@types/react@19.0.11)(encoding@0.1.13)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(zod@3.24.3)
       class-variance-authority: 0.7.1
       clsx: 2.1.1
       merge-anything: 5.1.7
@@ -6213,11 +6229,11 @@ snapshots:
   '@npmcli/fs@2.1.2':
     dependencies:
       '@gar/promisify': 1.1.3
-      semver: 7.7.1
+      semver: 7.7.2
 
   '@npmcli/fs@3.1.1':
     dependencies:
-      semver: 7.7.1
+      semver: 7.7.2
 
   '@npmcli/git@4.1.0':
     dependencies:
@@ -6227,7 +6243,7 @@ snapshots:
       proc-log: 3.0.0
       promise-inflight: 1.0.1
       promise-retry: 2.0.1
-      semver: 7.7.1
+      semver: 7.7.2
       which: 3.0.1
     transitivePeerDependencies:
       - bluebird
@@ -7377,6 +7393,8 @@ snapshots:
 
   '@sindresorhus/is@4.6.0': {}
 
+  '@sindresorhus/merge-streams@2.3.0': {}
+
   '@sindresorhus/merge-streams@4.0.0': {}
 
   '@standard-schema/spec@1.0.0': {}
@@ -8620,6 +8638,15 @@ snapshots:
       minimatch: 5.1.6
       once: 1.4.0
 
+  globby@14.1.0:
+    dependencies:
+      '@sindresorhus/merge-streams': 2.3.0
+      fast-glob: 3.3.3
+      ignore: 7.0.4
+      path-type: 6.0.0
+      slash: 5.1.0
+      unicorn-magic: 0.3.0
+
   gopd@1.2.0: {}
 
   graceful-fs@4.2.11: {}
@@ -8825,6 +8852,8 @@ snapshots:
     dependencies:
       minimatch: 9.0.5
 
+  ignore@7.0.4: {}
+
   image-size@2.0.1: {}
 
   imurmurhash@0.1.4: {}
@@ -9805,7 +9834,7 @@ snapshots:
       nopt: 6.0.0
       npmlog: 6.0.2
       rimraf: 3.0.2
-      semver: 7.7.1
+      semver: 7.7.2
       tar: 6.2.1
       which: 2.0.2
     transitivePeerDependencies:
@@ -9822,7 +9851,7 @@ snapshots:
     dependencies:
       hosted-git-info: 6.1.3
       is-core-module: 2.16.1
-      semver: 7.7.1
+      semver: 7.7.2
       validate-npm-package-license: 3.0.4
 
   normalize-path@3.0.0: {}
@@ -9833,7 +9862,7 @@ snapshots:
 
   npm-install-checks@6.3.0:
     dependencies:
-      semver: 7.7.1
+      semver: 7.7.2
 
   npm-normalize-package-bin@3.0.1: {}
 
@@ -9841,7 +9870,7 @@ snapshots:
     dependencies:
       hosted-git-info: 6.1.3
       proc-log: 3.0.0
-      semver: 7.7.1
+      semver: 7.7.2
       validate-npm-package-name: 5.0.1
 
   npm-packlist@7.0.4:
@@ -9853,7 +9882,7 @@ snapshots:
       npm-install-checks: 6.3.0
       npm-normalize-package-bin: 3.0.1
       npm-package-arg: 10.1.0
-      semver: 7.7.1
+      semver: 7.7.2
 
   npm-registry-fetch@14.0.5:
     dependencies:
@@ -9919,7 +9948,7 @@ snapshots:
       regex: 6.0.1
       regex-recursion: 6.0.2
 
-  openai@4.78.1(encoding@0.1.13)(zod@packages+zod):
+  openai@4.78.1(encoding@0.1.13)(zod@3.24.3):
     dependencies:
       '@types/node': 18.19.86
       '@types/node-fetch': 2.6.12
@@ -9929,7 +9958,7 @@ snapshots:
       formdata-node: 4.4.1
       node-fetch: 2.7.0(encoding@0.1.13)
     optionalDependencies:
-      zod: link:packages/zod
+      zod: 3.24.3
     transitivePeerDependencies:
       - encoding
 
@@ -10018,6 +10047,8 @@ snapshots:
       lru-cache: 11.1.0
       minipass: 7.1.2
 
+  path-type@6.0.0: {}
+
   pathe@1.1.2: {}
 
   pathe@2.0.3: {}
@@ -10503,11 +10534,6 @@ snapshots:
 
   semiver@1.1.0: {}
 
-  semver@7.6.3:
-    optional: true
-
-  semver@7.7.1: {}
-
   semver@7.7.2: {}
 
   serialize-javascript@6.0.2:
@@ -10529,7 +10555,7 @@ snapshots:
     dependencies:
       color: 4.2.3
       detect-libc: 2.0.3
-      semver: 7.6.3
+      semver: 7.7.2
     optionalDependencies:
       '@img/sharp-darwin-arm64': 0.33.5
       '@img/sharp-darwin-x64': 0.33.5
@@ -10596,6 +10622,8 @@ snapshots:
     dependencies:
       unicode-emoji-modifier-base: 1.0.0
 
+  slash@5.1.0: {}
+
   slice-ansi@3.0.0:
     dependencies:
       ansi-styles: 4.3.0
diff --git a/scripts/prepublish.ts b/scripts/prepublish.ts
deleted file mode 100644
index a3e6c82f..00000000
--- a/scripts/prepublish.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { readFileSync } from "node:fs";
-
-// read package.json for zod and @zod/mini
-const zodPackageJson = JSON.parse(readFileSync("packages/zod/package.json", "utf-8"));
-const zodMiniPackageJson = JSON.parse(readFileSync("packages/mini/package.json", "utf-8"));
-
-// check that versions are the same
-if (zodPackageJson.version !== zodMiniPackageJson.version) {
-  throw new Error(
-    `zod and @zod/mini versions do not match: ${zodPackageJson.version} !== ${zodMiniPackageJson.version}`
-  );
-}
diff --git a/scripts/semver-check.ts b/scripts/semver-check.ts
new file mode 100644
index 00000000..bb3398b8
--- /dev/null
+++ b/scripts/semver-check.ts
@@ -0,0 +1,31 @@
+import { readFileSync } from "node:fs";
+import { join } from "node:path";
+import { fileURLToPath } from "node:url";
+import semver from "semver";
+
+const __dirname = fileURLToPath(new URL(".", import.meta.url));
+const packageJsonPath = join(__dirname, "../packages/zod/package.json");
+
+try {
+  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
+  const version = packageJson.version;
+
+  if (!version) {
+    throw new Error("Version field is missing in package.json");
+  }
+
+  if (!semver.valid(version)) {
+    throw new Error(`Invalid semver version: ${version}`);
+  }
+
+  // check x.y.z format with regex
+  const semverRegex = /^\d+\.\d+\.\d+$/;
+  if (!semverRegex.test(version)) {
+    throw new Error(`Version ${version} does not match x.y.z format`);
+  }
+
+  console.log(`Valid semver version: ${version}`);
+} catch (error) {
+  console.error(`Error: ${error.message}`);
+  process.exit(1);
+}
diff --git a/tsconfig.json b/tsconfig.json
index 8f00c107..67e572c6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,7 @@
   "extends": "./.configs/tsconfig.base.json",
   "compilerOptions": {
     "rootDir": ".",
-    "noEmit": true
+    "declaration": true
   },
   "exclude": ["packages/docs"]
 }


Test patch:
diff --git a/packages/zod/src/v4/classic/tests/error-utils.test.ts b/packages/zod/src/v4/classic/tests/error-utils.test.ts
index d45e748b..c1e79a16 100644
--- a/packages/zod/src/v4/classic/tests/error-utils.test.ts
+++ b/packages/zod/src/v4/classic/tests/error-utils.test.ts
@@ -1,6 +1,8 @@
 import { expect, expectTypeOf, test } from "vitest";
 import * as z from "zod/v4";
 
+declare const iss: z.core.$ZodIssueCode;
+
 const Test = z.object({
   f1: z.number(),
   f2: z.string().optional(),
diff --git a/packages/zod/src/v4/classic/tests/error.test.ts b/packages/zod/src/v4/classic/tests/error.test.ts
index aa14b194..19e25abf 100644
--- a/packages/zod/src/v4/classic/tests/error.test.ts
+++ b/packages/zod/src/v4/classic/tests/error.test.ts
@@ -1,3 +1,4 @@
+import { inspect } from "node:util";
 import { expect, test } from "vitest";
 import * as z from "zod/v4";
 
@@ -683,7 +684,6 @@ test("error serialization", () => {
   try {
     z.string().parse(123);
   } catch (e) {
-    console.dir(e, { depth: null });
     expect(e).toMatchInlineSnapshot(`
       [ZodError: [
         {
@@ -694,5 +694,15 @@ test("error serialization", () => {
         }
       ]]
     `);
+    expect(inspect(e).split("\n").slice(0, 8).join("\n")).toMatchInlineSnapshot(`
+      "ZodError: [
+        {
+          "expected": "string",
+          "code": "invalid_type",
+          "path": [],
+          "message": "Invalid input: expected string, received number"
+        }
+      ]"
+    `);
   }
 });
diff --git a/packages/zod/src/v4/classic/tests/file.test.ts b/packages/zod/src/v4/classic/tests/file.test.ts
index 2fc48009..10424b2d 100644
--- a/packages/zod/src/v4/classic/tests/file.test.ts
+++ b/packages/zod/src/v4/classic/tests/file.test.ts
@@ -1,3 +1,4 @@
+// @ts-ignore
 import { File as WebFile } from "@web-std/file";
 
 import { afterEach, beforeEach, expect, test } from "vitest";
diff --git a/packages/zod/src/v4/classic/tests/function.test.ts b/packages/zod/src/v4/classic/tests/function.test.ts
index 93056da0..3fb9a7b4 100644
--- a/packages/zod/src/v4/classic/tests/function.test.ts
+++ b/packages/zod/src/v4/classic/tests/function.test.ts
@@ -68,7 +68,8 @@ test("function inference 1", () => {
 test("args method", () => {
   const t1 = z.function();
   type t1 = (typeof t1)["_input"];
-  expectTypeOf<t1>().toEqualTypeOf<(...args_1: unknown[]) => unknown>();
+  expectTypeOf<t1>().toEqualTypeOf<(...args_1: never[]) => unknown>();
+  t1._input;
 
   const t2args = z.tuple([z.string()], z.unknown());
 
@@ -81,6 +82,14 @@ test("args method", () => {
   expectTypeOf<t3>().toEqualTypeOf<(arg: string, ...args_1: unknown[]) => boolean>();
 });
 
+test("contravariance", () => {
+  const fn = z.function().implement((_a: string, _b: number) => {
+    return new Date();
+  });
+
+  expectTypeOf(fn).toEqualTypeOf<(a: string, b: number) => Date>();
+});
+
 const args2 = z.tuple([
   z.object({
     f1: z.number(),
diff --git a/packages/zod/src/v4/classic/tests/index.test.ts b/packages/zod/src/v4/classic/tests/index.test.ts
index ad0132cc..559a2d2b 100644
--- a/packages/zod/src/v4/classic/tests/index.test.ts
+++ b/packages/zod/src/v4/classic/tests/index.test.ts
@@ -706,17 +706,17 @@ test("z.templateLiteral", () => {
 });
 
 // this returns both a schema and a check
-test("z.custom", () => {
+test("z.custom schema", () => {
   const a = z.custom((val) => {
     return typeof val === "string";
   });
   expect(z.parse(a, "hello")).toEqual("hello");
   expect(() => z.parse(a, 123)).toThrow();
+});
 
-  const b = z.string().check(z.custom((val) => val.length > 3));
-
-  expect(z.parse(b, "hello")).toEqual("hello");
-  expect(() => z.parse(b, "hi")).toThrow();
+test("z.custom check", () => {
+  // @ts-expect-error Inference not possible, use z.refine()
+  z.date().check(z.custom((val) => val.getTime() > 0));
 });
 
 test("z.check", () => {
diff --git a/packages/zod/src/v4/classic/tests/json-schema.test.ts b/packages/zod/src/v4/classic/tests/json-schema.test.ts
index 80b6a0e3..a6efe894 100644
--- a/packages/zod/src/v4/classic/tests/json-schema.test.ts
+++ b/packages/zod/src/v4/classic/tests/json-schema.test.ts
@@ -1,226 +1,226 @@
 import { describe, expect, test } from "vitest";
 import * as z from "zod/v4";
-import { toJSONSchema } from "zod/v4/core";
+// import * as zCore from "zod/v4/core";
 
 describe("toJSONSchema", () => {
   test("primitive types", () => {
-    expect(toJSONSchema(z.string())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.string())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.number())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.boolean())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.boolean())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "boolean",
       }
     `);
-    expect(toJSONSchema(z.null())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.null())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "null",
       }
     `);
-    expect(toJSONSchema(z.undefined())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.undefined())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "null",
       }
     `);
-    expect(toJSONSchema(z.any())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.any())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
       }
     `);
-    expect(toJSONSchema(z.unknown())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.unknown())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
       }
     `);
-    expect(toJSONSchema(z.never())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.never())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "not": {},
       }
     `);
-    expect(toJSONSchema(z.email())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.email())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "email",
         "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.datetime())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.datetime())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "date-time",
         "pattern": "^((\\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])))T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?(Z)$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.date())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.date())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "date",
         "pattern": "^((\\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])))$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.time())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.time())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "time",
         "pattern": "^([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.duration())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.duration())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "duration",
         "pattern": "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.ipv4())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.ipv4())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "ipv4",
         "pattern": "^(?:(?: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])$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.ipv6())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.ipv6())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "ipv6",
         "pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.uuid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.uuid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "uuid",
         "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.guid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.guid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "uuid",
         "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.url())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.url())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "uri",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.base64())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.base64())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "contentEncoding": "base64",
         "format": "base64",
         "pattern": "^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.cuid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.cuid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "cuid",
         "pattern": "^[cC][^\\s-]{8,}$",
         "type": "string",
       }
     `);
-    // expect(toJSONSchema(z.regex(/asdf/))).toMatchInlineSnapshot();
-    expect(toJSONSchema(z.emoji())).toMatchInlineSnapshot(`
+    // expect(z.toJSONSchema(z.regex(/asdf/))).toMatchInlineSnapshot();
+    expect(z.toJSONSchema(z.emoji())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "emoji",
         "pattern": "^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.nanoid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.nanoid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "nanoid",
         "pattern": "^[a-zA-Z0-9_-]{21}$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.cuid2())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.cuid2())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "cuid2",
         "pattern": "^[0-9a-z]+$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.ulid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.ulid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "ulid",
         "pattern": "^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$",
         "type": "string",
       }
     `);
-    // expect(toJSONSchema(z.cidr())).toMatchInlineSnapshot();
-    expect(toJSONSchema(z.number())).toMatchInlineSnapshot(`
+    // expect(z.toJSONSchema(z.cidr())).toMatchInlineSnapshot();
+    expect(z.toJSONSchema(z.number())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.int())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.int())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 9007199254740991,
         "minimum": -9007199254740991,
         "type": "integer",
       }
     `);
-    expect(toJSONSchema(z.int32())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.int32())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 2147483647,
         "minimum": -2147483648,
         "type": "integer",
       }
     `);
-    expect(toJSONSchema(z.float32())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.float32())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 3.4028234663852886e+38,
         "minimum": -3.4028234663852886e+38,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.float64())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.float64())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 1.7976931348623157e+308,
         "minimum": -1.7976931348623157e+308,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.jwt())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.jwt())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "jwt",
         "type": "string",
       }
@@ -228,28 +228,28 @@ describe("toJSONSchema", () => {
   });
 
   test("unsupported schema types", () => {
-    expect(() => toJSONSchema(z.bigint())).toThrow("BigInt cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.int64())).toThrow("BigInt cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.symbol())).toThrow("Symbols cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.void())).toThrow("Void cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.date())).toThrow("Date cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.map(z.string(), z.number()))).toThrow("Map cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.set(z.string()))).toThrow("Set cannot be represented in JSON Schema");
-    expect(() => toJSONSchema(z.custom(() => true))).toThrow("Custom types cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.bigint())).toThrow("BigInt cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.int64())).toThrow("BigInt cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.symbol())).toThrow("Symbols cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.void())).toThrow("Void cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.date())).toThrow("Date cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.map(z.string(), z.number()))).toThrow("Map cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.set(z.string()))).toThrow("Set cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(z.custom(() => true))).toThrow("Custom types cannot be represented in JSON Schema");
 
     // File type
     const fileSchema = z.file();
-    expect(() => toJSONSchema(fileSchema)).toThrow("File cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(fileSchema)).toThrow("File cannot be represented in JSON Schema");
 
     // Transform
     const transformSchema = z.string().transform((val) => Number.parseInt(val));
-    expect(() => toJSONSchema(transformSchema)).toThrow("Transforms cannot be represented in JSON Schema");
+    expect(() => z.toJSONSchema(transformSchema)).toThrow("Transforms cannot be represented in JSON Schema");
 
     // Static catch values
     const staticCatchSchema = z.string().catch(() => "sup");
-    expect(toJSONSchema(staticCatchSchema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(staticCatchSchema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "default": "sup",
         "type": "string",
       }
@@ -257,110 +257,110 @@ describe("toJSONSchema", () => {
 
     // Dynamic catch values
     const dynamicCatchSchema = z.string().catch((ctx) => `${ctx.issues.length}`);
-    expect(() => toJSONSchema(dynamicCatchSchema)).toThrow("Dynamic catch values are not supported in JSON Schema");
+    expect(() => z.toJSONSchema(dynamicCatchSchema)).toThrow("Dynamic catch values are not supported in JSON Schema");
   });
 
   test("string formats", () => {
-    expect(toJSONSchema(z.string().email())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.string().email())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "email",
         "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.string().uuid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.string().uuid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "uuid",
         "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.datetime())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.datetime())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "date-time",
         "pattern": "^((\\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])))T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?(Z)$",
         "type": "string",
       }
     `);
 
-    expect(toJSONSchema(z.iso.date())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.date())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "date",
         "pattern": "^((\\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])))$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.time())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.time())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "time",
         "pattern": "^([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.iso.duration())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.iso.duration())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "duration",
         "pattern": "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
         "type": "string",
       }
     `);
-    // expect(toJSONSchema(z.string().ip())).toMatchInlineSnapshot(`
+    // expect(z.toJSONSchema(z.string().ip())).toMatchInlineSnapshot(`
     //   {
     //     "pattern": /\\(\\^\\(\\?:\\(\\?: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\\]\\)\\$\\)\\|\\(\\^\\(\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{7\\}\\|::\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,6\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{1\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,5\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{2\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,4\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{3\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,3\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{4\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,2\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{5\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,1\\}\\)\\(\\[a-fA-F0-9\\]\\{1,4\\}\\|\\(\\(\\(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\\}\\)\\)\\)\\$\\)/,
     //     "type": "string",
     //   }
     // `);
-    expect(toJSONSchema(z.ipv4())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.ipv4())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "ipv4",
         "pattern": "^(?:(?: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])$",
         "type": "string",
       }
     `);
 
-    expect(toJSONSchema(z.ipv6())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.ipv6())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "ipv6",
         "pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$",
         "type": "string",
       }
     `);
 
-    expect(toJSONSchema(z.base64())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.base64())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "contentEncoding": "base64",
         "format": "base64",
         "pattern": "^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.url())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.url())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "uri",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.guid())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.guid())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "uuid",
         "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$",
         "type": "string",
       }
     `);
-    expect(toJSONSchema(z.string().regex(/asdf/))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.string().regex(/asdf/))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "format": "regex",
         "pattern": "asdf",
         "type": "string",
@@ -369,10 +369,10 @@ describe("toJSONSchema", () => {
   });
 
   test("number constraints", () => {
-    expect(toJSONSchema(z.number().min(5).max(10))).toMatchInlineSnapshot(
+    expect(z.toJSONSchema(z.number().min(5).max(10))).toMatchInlineSnapshot(
       `
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 10,
         "minimum": 5,
         "type": "number",
@@ -380,86 +380,86 @@ describe("toJSONSchema", () => {
     `
     );
 
-    expect(toJSONSchema(z.number().gt(5).gt(10))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().gt(5).gt(10))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "exclusiveMinimum": 10,
         "type": "number",
       }
     `);
 
-    expect(toJSONSchema(z.number().gt(5).gte(10))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().gt(5).gte(10))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "minimum": 10,
         "type": "number",
       }
     `);
 
-    expect(toJSONSchema(z.number().lt(5).lt(3))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().lt(5).lt(3))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "exclusiveMaximum": 3,
         "type": "number",
       }
     `);
 
-    expect(toJSONSchema(z.number().lt(5).lt(3).lte(2))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().lt(5).lt(3).lte(2))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 2,
         "type": "number",
       }
     `);
 
-    expect(toJSONSchema(z.number().lt(5).lte(3))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().lt(5).lte(3))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 3,
         "type": "number",
       }
     `);
 
-    expect(toJSONSchema(z.number().gt(5).lt(10))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().gt(5).lt(10))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "exclusiveMaximum": 10,
         "exclusiveMinimum": 5,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.number().gte(5).lte(10))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().gte(5).lte(10))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 10,
         "minimum": 5,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.number().positive())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().positive())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "exclusiveMinimum": 0,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.number().negative())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().negative())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "exclusiveMaximum": 0,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.number().nonpositive())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().nonpositive())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "maximum": 0,
         "type": "number",
       }
     `);
-    expect(toJSONSchema(z.number().nonnegative())).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.number().nonnegative())).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "minimum": 0,
         "type": "number",
       }
@@ -467,9 +467,9 @@ describe("toJSONSchema", () => {
   });
 
   test("arrays", () => {
-    expect(toJSONSchema(z.array(z.string()))).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(z.array(z.string()))).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "items": {
           "type": "string",
         },
@@ -480,9 +480,9 @@ describe("toJSONSchema", () => {
 
   test("unions", () => {
     const schema = z.union([z.string(), z.number()]);
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "anyOf": [
           {
             "type": "string",
@@ -498,11 +498,12 @@ describe("toJSONSchema", () => {
   test("intersections", () => {
     const schema = z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
 
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "allOf": [
           {
+            "additionalProperties": false,
             "properties": {
               "name": {
                 "type": "string",
@@ -514,6 +515,7 @@ describe("toJSONSchema", () => {
             "type": "object",
           },
           {
+            "additionalProperties": false,
             "properties": {
               "age": {
                 "type": "number",
@@ -531,9 +533,9 @@ describe("toJSONSchema", () => {
 
   test("record", () => {
     const schema = z.record(z.string(), z.boolean());
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": {
           "type": "boolean",
         },
@@ -547,9 +549,9 @@ describe("toJSONSchema", () => {
 
   test("tuple", () => {
     const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "items": {
           "type": "boolean",
         },
@@ -568,9 +570,9 @@ describe("toJSONSchema", () => {
 
   test("promise", () => {
     const schema = z.promise(z.string());
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "string",
       }
     `);
@@ -578,9 +580,9 @@ describe("toJSONSchema", () => {
 
   test("lazy", () => {
     const schema = z.lazy(() => z.string());
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "string",
       }
     `);
@@ -589,9 +591,9 @@ describe("toJSONSchema", () => {
   // enum
   test("enum", () => {
     const schema = z.enum(["a", "b", "c"]);
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "enum": [
           "a",
           "b",
@@ -604,20 +606,20 @@ describe("toJSONSchema", () => {
   // literal
   test("literal", () => {
     const a = z.literal("hello");
-    expect(toJSONSchema(a)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "const": "hello",
       }
     `);
 
     const b = z.literal(["hello", undefined, null, 5, BigInt(1324)]);
-    expect(() => toJSONSchema(b)).toThrow();
+    expect(() => z.toJSONSchema(b)).toThrow();
 
     const c = z.literal(["hello", null, 5]);
-    expect(toJSONSchema(c)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "enum": [
           "hello",
           null,
@@ -633,9 +635,9 @@ describe("toJSONSchema", () => {
       .string()
       .transform((val) => Number.parseInt(val))
       .pipe(z.number());
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "type": "number",
       }
     `);
@@ -647,10 +649,11 @@ describe("toJSONSchema", () => {
       age: z.number(),
     });
 
-    expect(toJSONSchema(schema)).toMatchInlineSnapshot(
+    expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(
       `
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "additionalProperties": false,
         "properties": {
           "age": {
             "type": "number",
@@ -669,15 +672,75 @@ describe("toJSONSchema", () => {
     );
   });
 
+  test("additionalproperties in z.object", () => {
+    const a = z.object({
+      name: z.string(),
+    });
+    expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
+      {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "additionalProperties": false,
+        "properties": {
+          "name": {
+            "type": "string",
+          },
+        },
+        "required": [
+          "name",
+        ],
+        "type": "object",
+      }
+    `);
+    expect(z.toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
+      {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "properties": {
+          "name": {
+            "type": "string",
+          },
+        },
+        "required": [
+          "name",
+        ],
+        "type": "object",
+      }
+    `);
+    expect(
+      z.toJSONSchema(a, {
+        io: "input",
+        override(ctx) {
+          const def = (ctx.zodSchema as z.core.$ZodTypes)._zod.def;
+          if (def.type === "object" && !def.catchall) {
+            (ctx.jsonSchema as z.core.JSONSchema.ObjectSchema).additionalProperties = false;
+          }
+        },
+      })
+    ).toMatchInlineSnapshot(`
+      {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "additionalProperties": false,
+        "properties": {
+          "name": {
+            "type": "string",
+          },
+        },
+        "required": [
+          "name",
+        ],
+        "type": "object",
+      }
+    `);
+  });
+
   test("catchall objects", () => {
     const a = z.strictObject({
       name: z.string(),
       age: z.number(),
     });
 
-    expect(toJSONSchema(a)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": false,
         "properties": {
           "age": {
@@ -701,9 +764,9 @@ describe("toJSONSchema", () => {
       })
       .catchall(z.string());
 
-    expect(toJSONSchema(b)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": {
           "type": "string",
         },
@@ -723,9 +786,9 @@ describe("toJSONSchema", () => {
       name: z.string(),
     });
 
-    expect(toJSONSchema(c)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": {},
         "properties": {
           "name": {
@@ -747,11 +810,12 @@ describe("toJSONSchema", () => {
       nonoptional: z.string().optional().nonoptional(),
     });
 
-    const result = toJSONSchema(schema);
+    const result = z.toJSONSchema(schema);
 
     expect(result).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "additionalProperties": false,
         "properties": {
           "nonoptional": {
             "type": "string",
@@ -783,10 +847,11 @@ describe("toJSONSchema", () => {
       subcategories: z.array(z.lazy(() => categorySchema)),
     });
 
-    const result = toJSONSchema(categorySchema);
+    const result = z.toJSONSchema(categorySchema);
     expect(result).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "additionalProperties": false,
         "properties": {
           "name": {
             "type": "string",
@@ -813,10 +878,11 @@ describe("toJSONSchema", () => {
       age: z.number().optional(),
     });
 
-    const result = toJSONSchema(userSchema);
+    const result = z.toJSONSchema(userSchema);
     expect(result).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "additionalProperties": false,
         "properties": {
           "age": {
             "type": "number",
@@ -839,9 +905,9 @@ describe("toJSONSchema", () => {
       age: z.number(),
     });
 
-    expect(toJSONSchema(a)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": false,
         "properties": {
           "age": {
@@ -865,9 +931,9 @@ describe("toJSONSchema", () => {
       })
       .catchall(z.string());
 
-    expect(toJSONSchema(b)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": {
           "type": "string",
         },
@@ -887,9 +953,9 @@ describe("toJSONSchema", () => {
       name: z.string(),
     });
 
-    expect(toJSONSchema(c)).toMatchInlineSnapshot(`
+    expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
       {
-        "$schema": "https://json-schema.org/draft-2020-12/schema",
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
         "additionalProperties": {},
         "properties": {
           "name": {
@@ -912,7 +978,7 @@ describe("toJSONSchema", () => {
       },
     });
 
-    const result = toJSONSchema(TreeNodeSchema);
+    const result = z.toJSONSchema(TreeNodeSchema);
 
     // Should have definitions for recursive schema
     expect(JSON.stringify(result, null, 2)).toMatchInlineSnapshot(
@@ -931,7 +997,8 @@ describe("toJSONSchema", () => {
           "id",
           "children"
         ],
-        "$schema": "https://json-schema.org/draft-2020-12/schema"
+        "additionalProperties": false,
+        "$schema": "https://json-schema.org/draft/2020-12/schema"
       }"
     `
     );
@@ -952,7 +1019,7 @@ describe("toJSONSchema", () => {
       },
     });
 
-    const result = toJSONSchema(FolderSchema);
+    const result = z.toJSONSchema(FolderSchema);
 
     // Should have definitions for both schemas
     expect(JSON.stringify(result, null, 2)).toMatchInlineSnapshot(
@@ -978,7 +1045,8 @@ describe("toJSONSchema", () => {
               "required": [
                 "name",
                 "parent"
-              ]
+              ],
+              "additionalProperties": false
             }
           }
         },
@@ -986,7 +1054,8 @@ describe("toJSONSchema", () => {
           "name",
           "files"
         ],
-        "$schema": "https://json-schema.org/draft-2020-12/schema"
+        "additionalProperties": false,
+        "$schema": "https://json-schema.org/draft/2020-12/schema"
       }"
     `
     );
@@ -994,7 +1063,7 @@ describe("toJSONSchema", () => {
 });
 
 test("override", () => {
-  const schema = z.toJSONSchema(z.string(), {
+  const schema = z.z.toJSONSchema(z.string(), {
     override: (ctx) => {
       ctx.zodSchema;
       ctx.jsonSchema;
@@ -1003,7 +1072,7 @@ test("override", () => {
   });
   expect(schema).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "string",
       "whatever": "sup",
     }
@@ -1020,7 +1089,7 @@ test("override: do not run on references", () => {
     .pipe(z.date())
     .meta({ c: true })
     .brand("dateIn");
-  z.toJSONSchema(schema, {
+  z.z.toJSONSchema(schema, {
     unrepresentable: "any",
     io: "input",
     override(_) {
@@ -1033,7 +1102,7 @@ test("override: do not run on references", () => {
 
 test("override with refs", () => {
   const a = z.string().optional();
-  const result = z.toJSONSchema(a, {
+  const result = z.z.toJSONSchema(a, {
     override(ctx) {
       if (ctx.zodSchema._zod.def.type === "string") {
         ctx.jsonSchema.type = "STRING";
@@ -1043,7 +1112,7 @@ test("override with refs", () => {
 
   expect(result).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "STRING",
     }
   `);
@@ -1052,7 +1121,7 @@ test("override with refs", () => {
 test("override execution order", () => {
   const schema = z.union([z.string(), z.number()]);
   let unionSchema!: any;
-  z.toJSONSchema(schema, {
+  z.z.toJSONSchema(schema, {
     override(ctx) {
       if (ctx.zodSchema._zod.def.type === "union") {
         unionSchema = ctx.jsonSchema;
@@ -1081,19 +1150,19 @@ test("pipe", () => {
     .pipe(z.number());
   // ZodPipe
 
-  const a = z.toJSONSchema(mySchema);
+  const a = z.z.toJSONSchema(mySchema);
   expect(a).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "number",
     }
   `);
   // => { type: "number" }
 
-  const b = z.toJSONSchema(mySchema, { io: "input" });
+  const b = z.z.toJSONSchema(mySchema, { io: "input" });
   expect(b).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "string",
     }
   `);
@@ -1115,13 +1184,14 @@ test("passthrough schemas", () => {
     e: z.pipe(Internal, Internal),
   });
 
-  const result = z.toJSONSchema(External, {
+  const result = z.z.toJSONSchema(External, {
     reused: "ref",
   });
   expect(result).toMatchInlineSnapshot(`
     {
       "$defs": {
         "__schema0": {
+          "additionalProperties": false,
           "properties": {
             "num": {
               "type": "number",
@@ -1137,7 +1207,8 @@ test("passthrough schemas", () => {
           "type": "object",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "a": {
           "$ref": "#/$defs/__schema0",
@@ -1168,7 +1239,7 @@ test("passthrough schemas", () => {
 
 test("extract schemas with id", () => {
   const name = z.string().meta({ id: "name" });
-  const result = z.toJSONSchema(
+  const result = z.z.toJSONSchema(
     z.object({
       first_name: name,
       last_name: name.nullable(),
@@ -1188,7 +1259,8 @@ test("extract schemas with id", () => {
           "type": "string",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "age": {
           "$ref": "#/$defs/age",
@@ -1221,10 +1293,10 @@ test("extract schemas with id", () => {
 });
 
 test("unrepresentable literal values are ignored", () => {
-  const a = z.toJSONSchema(z.literal(["hello", null, 5, BigInt(1324), undefined]), { unrepresentable: "any" });
+  const a = z.z.toJSONSchema(z.literal(["hello", null, 5, BigInt(1324), undefined]), { unrepresentable: "any" });
   expect(a).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "enum": [
         "hello",
         null,
@@ -1234,10 +1306,10 @@ test("unrepresentable literal values are ignored", () => {
     }
   `);
 
-  const b = z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), { unrepresentable: "any" });
+  const b = z.z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), { unrepresentable: "any" });
   expect(b).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "enum": [
         null,
         5,
@@ -1246,10 +1318,10 @@ test("unrepresentable literal values are ignored", () => {
     }
   `);
 
-  const c = z.toJSONSchema(z.literal([undefined]), { unrepresentable: "any" });
+  const c = z.z.toJSONSchema(z.literal([undefined]), { unrepresentable: "any" });
   expect(c).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
     }
   `);
 });
@@ -1257,7 +1329,7 @@ test("unrepresentable literal values are ignored", () => {
 test("describe with id", () => {
   const jobId = z.string().meta({ id: "jobId" });
 
-  const a = z.toJSONSchema(
+  const a = z.z.toJSONSchema(
     z.object({
       current: jobId.describe("Current job"),
       previous: jobId.describe("Previous job"),
@@ -1271,7 +1343,8 @@ test("describe with id", () => {
           "type": "string",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "current": {
           "$ref": "#/$defs/jobId",
@@ -1294,7 +1367,7 @@ test("describe with id", () => {
 test("overwrite id", () => {
   const jobId = z.string().meta({ id: "aaa" });
 
-  const a = z.toJSONSchema(
+  const a = z.z.toJSONSchema(
     z.object({
       current: jobId,
       previous: jobId.meta({ id: "bbb" }),
@@ -1312,7 +1385,8 @@ test("overwrite id", () => {
           "id": "bbb",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "current": {
           "$ref": "#/$defs/aaa",
@@ -1329,7 +1403,7 @@ test("overwrite id", () => {
     }
   `);
 
-  const b = z.toJSONSchema(
+  const b = z.z.toJSONSchema(
     z.object({
       current: jobId,
       previous: jobId.meta({ id: "ccc" }),
@@ -1350,7 +1424,8 @@ test("overwrite id", () => {
           "id": "ccc",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "current": {
           "$ref": "#/$defs/aaa",
@@ -1371,7 +1446,7 @@ test("overwrite id", () => {
 test("overwrite descriptions", () => {
   const field = z.string().describe("a").describe("b").describe("c");
 
-  const a = z.toJSONSchema(
+  const a = z.z.toJSONSchema(
     z.object({
       d: field.describe("d"),
       e: field.describe("e"),
@@ -1379,7 +1454,8 @@ test("overwrite descriptions", () => {
   );
   expect(a).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "d": {
           "description": "d",
@@ -1398,7 +1474,7 @@ test("overwrite descriptions", () => {
     }
   `);
 
-  const b = z.toJSONSchema(
+  const b = z.z.toJSONSchema(
     z.object({
       d: field.describe("d"),
       e: field.describe("e"),
@@ -1415,7 +1491,8 @@ test("overwrite descriptions", () => {
           "type": "string",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "d": {
           "$ref": "#/$defs/__schema0",
@@ -1456,11 +1533,12 @@ test("top-level readonly", () => {
     .readonly()
     .meta({ id: "B" });
 
-  const result = z.toJSONSchema(A);
+  const result = z.z.toJSONSchema(A);
   expect(result).toMatchInlineSnapshot(`
     {
       "$defs": {
         "B": {
+          "additionalProperties": false,
           "id": "B",
           "properties": {
             "a": {
@@ -1478,7 +1556,8 @@ test("top-level readonly", () => {
           "type": "object",
         },
       },
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "id": "A",
       "properties": {
         "b": {
@@ -1518,12 +1597,13 @@ test("basic registry", () => {
   myRegistry.add(User, { id: "User" });
   myRegistry.add(Post, { id: "Post" });
 
-  const result = z.toJSONSchema(myRegistry);
+  const result = z.z.toJSONSchema(myRegistry);
   expect(result).toMatchInlineSnapshot(`
     {
       "schemas": {
         "Post": {
-          "$schema": "https://json-schema.org/draft-2020-12/schema",
+          "$schema": "https://json-schema.org/draft/2020-12/schema",
+          "additionalProperties": false,
           "properties": {
             "author": {
               "$ref": "User",
@@ -1543,7 +1623,8 @@ test("basic registry", () => {
           "type": "object",
         },
         "User": {
-          "$schema": "https://json-schema.org/draft-2020-12/schema",
+          "$schema": "https://json-schema.org/draft/2020-12/schema",
+          "additionalProperties": false,
           "properties": {
             "name": {
               "type": "string",
@@ -1568,28 +1649,28 @@ test("basic registry", () => {
 
 test("_ref", () => {
   // const a = z.promise(z.string().describe("a"));
-  const a = z.toJSONSchema(z.promise(z.string().describe("a")));
+  const a = z.z.toJSONSchema(z.promise(z.string().describe("a")));
   expect(a).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "description": "a",
       "type": "string",
     }
   `);
 
-  const b = z.toJSONSchema(z.lazy(() => z.string().describe("a")));
+  const b = z.z.toJSONSchema(z.lazy(() => z.string().describe("a")));
   expect(b).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "description": "a",
       "type": "string",
     }
   `);
 
-  const c = z.toJSONSchema(z.optional(z.string().describe("a")));
+  const c = z.z.toJSONSchema(z.optional(z.string().describe("a")));
   expect(c).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "description": "a",
       "type": "string",
     }
@@ -1605,44 +1686,44 @@ test("defaults/prefaults", () => {
   const c = a.default(1234);
 
   // a
-  expect(toJSONSchema(a)).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "number",
     }
   `);
-  expect(toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "string",
     }
   `);
 
   // b
-  expect(toJSONSchema(b)).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "type": "number",
     }
   `);
-  expect(toJSONSchema(b, { io: "input" })).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(b, { io: "input" })).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "default": "hello",
       "type": "string",
     }
   `);
   // c
-  expect(toJSONSchema(c)).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "default": 1234,
       "type": "number",
     }
   `);
-  expect(toJSONSchema(c, { io: "input" })).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(c, { io: "input" })).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "default": 1234,
       "type": "string",
     }
@@ -1657,9 +1738,9 @@ test("input type", () => {
     d: z.string().nullable(),
     e: z.string().prefault("hello"),
   });
-  expect(toJSONSchema(schema, { io: "input" })).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(schema, { io: "input" })).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "properties": {
         "a": {
           "type": "string",
@@ -1693,9 +1774,10 @@ test("input type", () => {
       "type": "object",
     }
   `);
-  expect(toJSONSchema(schema, { io: "output" })).toMatchInlineSnapshot(`
+  expect(z.toJSONSchema(schema, { io: "output" })).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
+      "additionalProperties": false,
       "properties": {
         "a": {
           "type": "string",
@@ -1740,20 +1822,20 @@ test("examples on pipe", () => {
     // .pipe(z.transform(Number).meta({ examples: [4] }))
     .meta({ examples: [4] });
 
-  const i = z.toJSONSchema(schema, { io: "input", unrepresentable: "any" });
+  const i = z.z.toJSONSchema(schema, { io: "input", unrepresentable: "any" });
   expect(i).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "examples": [
         "test",
       ],
       "type": "string",
     }
   `);
-  const o = z.toJSONSchema(schema, { io: "output", unrepresentable: "any" });
+  const o = z.z.toJSONSchema(schema, { io: "output", unrepresentable: "any" });
   expect(o).toMatchInlineSnapshot(`
     {
-      "$schema": "https://json-schema.org/draft-2020-12/schema",
+      "$schema": "https://json-schema.org/draft/2020-12/schema",
       "examples": [
         4,
       ],
@@ -1762,21 +1844,21 @@ test("examples on pipe", () => {
 });
 
 // test("number checks", () => {
-//   expect(z.toJSONSchema(z.number().int())).toMatchInlineSnapshot(`
+//   expect(z.z.toJSONSchema(z.number().int())).toMatchInlineSnapshot(`
 //     {
 //       "maximum": 9007199254740991,
 //       "minimum": -9007199254740991,
 //       "type": "integer",
 //     }
 //   `);
-//   expect(z.toJSONSchema(z.int())).toMatchInlineSnapshot(`
+//   expect(z.z.toJSONSchema(z.int())).toMatchInlineSnapshot(`
 //     {
 //       "maximum": 9007199254740991,
 //       "minimum": -9007199254740991,
 //       "type": "integer",
 //     }
 //   `);
-//   expect(z.toJSONSchema(z.int().positive())).toMatchInlineSnapshot(`
+//   expect(z.z.toJSONSchema(z.int().positive())).toMatchInlineSnapshot(`
 //     {
 //       "exclusiveMinimum": 0,
 //       "maximum": 9007199254740991,
@@ -1784,14 +1866,14 @@ test("examples on pipe", () => {
 //       "type": "integer",
 //     }
 //   `);
-//   expect(z.toJSONSchema(z.int().nonnegative())).toMatchInlineSnapshot(`
+//   expect(z.z.toJSONSchema(z.int().nonnegative())).toMatchInlineSnapshot(`
 //     {
 //       "maximum": 9007199254740991,
 //       "minimum": 0,
 //       "type": "integer",
 //     }
 //   `);
-//   expect(z.toJSONSchema(z.int().gt(0))).toMatchInlineSnapshot(`
+//   expect(z.z.toJSONSchema(z.int().gt(0))).toMatchInlineSnapshot(`
 //     {
 //       "exclusiveMinimum": 0,
 //       "maximum": 9007199254740991,
@@ -1799,7 +1881,7 @@ test("examples on pipe", () => {
 //       "type": "integer",
 //     }
 //   `);
-//   expect(z.toJSONSchema(z.int().gte(0))).toMatchInlineSnapshot(`
+//   expect(z.z.toJSONSchema(z.int().gte(0))).toMatchInlineSnapshot(`
 //     {
 //       "maximum": 9007199254740991,
 //       "minimum": 0,
diff --git a/packages/zod/src/v4/classic/tests/literal.test.ts b/packages/zod/src/v4/classic/tests/literal.test.ts
index 904fb514..e769dcd8 100644
--- a/packages/zod/src/v4/classic/tests/literal.test.ts
+++ b/packages/zod/src/v4/classic/tests/literal.test.ts
@@ -80,3 +80,8 @@ test("literal bigint default error message", () => {
   expect(result.error!.issues.length).toEqual(1);
   expect(result.error!.issues[0].message).toEqual(`Invalid input: expected 12n`);
 });
+
+test(".value getter", () => {
+  expect(z.literal("tuna").value).toEqual("tuna");
+  expect(() => z.literal([1, 2, 3]).value).toThrow();
+});
diff --git a/packages/zod/src/v4/classic/tests/registries.test.ts b/packages/zod/src/v4/classic/tests/registries.test.ts
index 2b93f0ae..cf210c9f 100644
--- a/packages/zod/src/v4/classic/tests/registries.test.ts
+++ b/packages/zod/src/v4/classic/tests/registries.test.ts
@@ -157,3 +157,13 @@ test("inherit across clone", () => {
   const C = B.describe("hello");
   expect(C.meta()).toEqual({ a: true, b: true, description: "hello" });
 });
+
+test("register with replace", () => {
+  z.string().register(z.globalRegistry, {
+    examples: ["example"],
+  });
+  z.string().register(z.globalRegistry, {
+    // @ts-expect-error
+    examples: [1234],
+  });
+});