Skip to content

feat(v26): Zod plugin: Patch Zod's CJS#3087

Merged
RobinTail merged 17 commits intomake-v26from
exp-plugin-cjs-too
Nov 25, 2025
Merged

feat(v26): Zod plugin: Patch Zod's CJS#3087
RobinTail merged 17 commits intomake-v26from
exp-plugin-cjs-too

Conversation

@RobinTail
Copy link
Copy Markdown
Owner

@RobinTail RobinTail commented Nov 24, 2025

Fixes #2981

After fixing globalRegistry by #3086 , it could be now considered to address the dual package hazard completely.
For that I'm trying to make Zod plugin patch both CJS and ESM exports of Zod, so that it would no matter which environment user runs the framework.

Summary by CodeRabbit

  • Bug Fixes

    • Resolved TypeError in CommonJS environments by extending Zod's CJS exports.
  • Documentation

    • Removed ESM-only requirements and related error notes from documentation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 24, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR extends the Zod plugin to patch both ESM and CJS exports of Zod, fixing the TypeError in CommonJS environments where .example() was unavailable. It introduces new meta helpers and per-package augmentation logic, adds CJS testing, and updates documentation.

Changes

Cohort / File(s) Summary
Documentation updates
CHANGELOG.md, README.md, zod-plugin/CHANGELOG.md
Updated v26 changelog and v3.0.0 zod-plugin changelog to document that the plugin now patches both ESM and CJS exports. Removed ESM execution guidance and TypeError caveat from README.
New meta helpers
zod-plugin/src/meta.ts
Added three meta setter functions: exampleSetter() to register examples, deprecationSetter() to mark types as deprecated, and labelSetter() to assign default labels.
Package and class utilities
zod-plugin/src/packages.ts
Introduced getPackages() to retrieve both ESM and CJS Zod exports and getClasses() to extract Zod class constructors from a package.
Runtime augmentation refactor
zod-plugin/src/runtime.ts
Refactored prototype augmentation to use per-package/per-class approach via getPackages() and getClasses(), delegating meta logic to imported helpers and applying patches to both ESM and CJS exports.
Test suite enhancements
cjs-test/zod-plugin.spec.ts, zod-plugin/tests/runtime.spec.ts
Added new CJS-focused test verifying .example() works in CommonJS. Enhanced existing test suite to support both ESM and CJS variants via parameterized testing.

Sequence Diagram

sequenceDiagram
    participant Plugin as Zod Plugin<br/>(Runtime Init)
    participant GetPkg as getPackages()
    participant ESM as ESM Zod<br/>Export
    participant CJS as CJS Zod<br/>Export (via require)
    participant GetCls as getClasses()
    participant Patch as Per-Class<br/>Prototype Patch

    Plugin->>GetPkg: Initialize
    GetPkg->>ESM: Load ESM export
    GetPkg->>CJS: Load CJS export<br/>(if different)
    GetPkg-->>Plugin: [zESM, zCJS]

    loop For each Zod package
        Plugin->>GetCls: Extract classes
        GetCls-->>Plugin: [ZodType, ZodDefault, ...]
        
        loop For each class
            Plugin->>Patch: Apply meta helpers to<br/>class.prototype
            Note over Patch: example()<br/>deprecated()<br/>label()
        end
    end

    Note over Plugin,Patch: Both ESM & CJS now<br/>have augmented prototypes
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • zod-plugin/src/meta.ts — New file with meta helpers; requires understanding of Zod's global registry interaction and meta propagation semantics
  • zod-plugin/src/packages.ts — New utilities handling both ESM and CJS module resolution; verify the require() fallback logic and class filtering correctness
  • zod-plugin/src/runtime.ts — Significant refactor from direct augmentation to per-package approach; ensure per-class prototype assignments don't have unintended scoping issues
  • Test files — Verify CJS test actually exercises CommonJS module resolution; check parameterized test coverage includes both variants

Possibly related PRs

  • #2981: Addresses the reported issue "v25: example is not a function" in CommonJS environments, which this PR directly resolves
  • #2955: Implements similar Zod example runtime augmentation and globalRegistry handling patterns
  • #2842: Changes how Zod example metadata is stored and retrieved via the global registry

Suggested labels

patched

Poem

🐰 A fix for both paths, ESM and CJS,
No more lost examples in the darkness, I insist!
Per-package patches, meta helpers so neat,
CommonJS now thrives—the circle's complete!

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(v26): Zod plugin: Patch Zod's CJS' clearly and concisely describes the main change—extending the Zod plugin to patch CommonJS exports in addition to ESM.
Linked Issues check ✅ Passed The PR directly addresses issue #2981 by enabling the Zod plugin to patch both CJS and ESM exports, fixing the TypeError from .example() in CJS environments and restoring .meta() functionality across both module systems.
Out of Scope Changes check ✅ Passed All changes are in-scope: new meta helper exports, package discovery utilities, runtime augmentation refactoring for dual-package support, test coverage for CJS, and documentation updates explaining the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coveralls-official
Copy link
Copy Markdown

coveralls-official Bot commented Nov 24, 2025

Coverage Status

coverage: 100.0%. remained the same
when pulling 23925d2 on exp-plugin-cjs-too
into 5391071 on make-v26.

@RobinTail RobinTail added the refactoring The better way to achieve the same result label Nov 24, 2025
@RobinTail RobinTail changed the title exp(v26): Zod plugin to patch CJS bundle of Zod feat(v26): Zod plugin: Patch CJS exports of Zod too Nov 24, 2025
@RobinTail
Copy link
Copy Markdown
Owner Author

I consider the experiment a success

@RobinTail RobinTail added the enhancement New feature or request label Nov 24, 2025
@RobinTail RobinTail added this to the v26 milestone Nov 24, 2025
@RobinTail RobinTail mentioned this pull request Nov 24, 2025
@RobinTail RobinTail changed the title feat(v26): Zod plugin: Patch CJS exports of Zod too feat(v26): Zod plugin: Patch Zod's CJS Nov 24, 2025
@RobinTail
Copy link
Copy Markdown
Owner Author

@RobinTail
Copy link
Copy Markdown
Owner Author

@coderabbitat review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 25, 2025

@RobinTail: I'll review the changes in this PR. Since you've successfully QAed the dual package hazard fix that patches both CJS and ESM exports of Zod, I'll take a look at the implementation.


🧠 Learnings used
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2697
File: CHANGELOG.md:5-5
Timestamp: 2025-06-02T21:11:20.768Z
Learning: In the express-zod-api repository, RobinTail follows a release workflow where package.json version is only updated on the master branch after merging all planned release changes. Changelog entries may show future version numbers while package.json remains at the previous version during feature development, and this is intentional workflow, not a version inconsistency that needs to be flagged.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-10-02T17:42:48.840Z
Learning: In express-zod-api v25 (ESM-only), the `.example()` method error occurs when user code runs as CommonJS. Express Zod API patches Zod's ESM bundle with `.example()`, but CommonJS code requires a separate CJS bundle instance that lacks this patch. Users must run their code as ESM by: (1) setting `"type": "module"` in package.json, (2) using `.mts` or `.mjs` file extensions, or (3) using tools like `tsx` or `vite-node` that provide their own ESM-compatible compilation.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/tests/form-schema.spec.ts:31-31
Timestamp: 2025-05-27T19:27:13.492Z
Learning: Zod version 3.25.0 and later expose the Zod v4 API through the special import paths "zod/v4" and "zod/v4/core", allowing v4 features like .loose() to be used even when the package.json dependency shows a 3.x version.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-01T09:48:13.742Z
Learning: In express-zod-api, when migrating from Zod v3 to v4, the correct approach for internal type imports is to change from `import type { $ZodType } from "zod/v4/core"` to `import { z } from "zod"` and then use `z.core.$ZodType`. The zod/v4/core module is reexported as z.core by the main zod package, making this a valid and working approach.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-08T16:45:20.527Z
Learning: express-zod-api/zod-plugin: The ZodType.prototype.brand getter returns setBrand.bind(this) each time (no memoization). setBrand must always call pack() and include { brand: undefined } when unbranding so that pack → .check() runs and prior brand is overwritten; do not suggest removing the explicit undefined entry.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2736
File: express-zod-api/tsup.config.ts:12-26
Timestamp: 2025-06-14T16:42:52.972Z
Learning: In express-zod-api tsup configurations, the direct mutation of `options.supported` in the `esbuildOptions` callback is intentional behavior and should not be flagged as a side effect issue.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2878
File: zod-plugin/runtime.ts:39-42
Timestamp: 2025-08-08T11:59:04.814Z
Learning: express-zod-api/zod-plugin/runtime.ts (TypeScript): Do not memoize the ZodType.prototype.brand getter. The current design returning setBrand.bind(this) on each access is intentional/preferred; avoid redefining "brand" as a data property per instance.

Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2869
File: zod-plugin/package.json:42-44
Timestamp: 2025-08-05T14:43:24.702Z
Learning: Zod version 4 has been officially released and is stable. As of August 2025, the latest published version is 4.0.14 (published July 30, 2025). Zod v4 is available on npm and can be used in peer dependencies with version constraints like "^4.0.0".
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
zod-plugin/src/packages.ts (1)

1-17: Package/class discovery correctly supports dual zod bundles

Using getPackages() to collect the ESM z plus a distinct CJS z (when present) and getClasses() to enumerate Zod* constructors gives runtime.ts exactly what it needs to patch both module formats without double‑augmenting shared instances.

If you ever want stricter typing here, you could add explicit return types, e.g. getPackages(): typeof z[] and a constructor type alias for getClasses, but it’s not required for correctness.

zod-plugin/src/meta.ts (1)

1-18: Meta helpers correctly encapsulate example/deprecated/label behavior

The three helpers use globalRegistry and this.meta(...) in the expected way, and defining them as regular functions makes them safe to attach as prototype methods from runtime.ts. This nicely centralizes behavior that was previously inlined in the runtime.

If you ever need stricter types, you could make exampleSetter generic on this (e.g. <T extends z.ZodTypeAny>(this: T, value: z.output<T>)) but the current signature is adequate for runtime correctness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5391071 and b77213f.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • README.md (0 hunks)
  • cjs-test/zod-plugin.spec.ts (1 hunks)
  • zod-plugin/CHANGELOG.md (1 hunks)
  • zod-plugin/src/meta.ts (1 hunks)
  • zod-plugin/src/packages.ts (1 hunks)
  • zod-plugin/src/runtime.ts (1 hunks)
  • zod-plugin/tests/runtime.spec.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • README.md
🧰 Additional context used
🧠 Learnings (22)
📓 Common learnings
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-10-02T17:42:48.840Z
Learning: In express-zod-api v25 (ESM-only), the `.example()` method error occurs when user code runs as CommonJS. Express Zod API patches Zod's ESM bundle with `.example()`, but CommonJS code requires a separate CJS bundle instance that lacks this patch. Users must run their code as ESM by: (1) setting `"type": "module"` in package.json, (2) using `.mts` or `.mjs` file extensions, or (3) using tools like `tsx` or `vite-node` that provide their own ESM-compatible compilation.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2697
File: CHANGELOG.md:5-5
Timestamp: 2025-06-02T21:11:20.768Z
Learning: In the express-zod-api repository, RobinTail follows a release workflow where package.json version is only updated on the master branch after merging all planned release changes. Changelog entries may show future version numbers while package.json remains at the previous version during feature development, and this is intentional workflow, not a version inconsistency that needs to be flagged.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2736
File: express-zod-api/tsup.config.ts:12-26
Timestamp: 2025-06-14T16:42:52.972Z
Learning: In express-zod-api tsup configurations, the direct mutation of `options.supported` in the `esbuildOptions` callback is intentional behavior and should not be flagged as a side effect issue.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2697
File: CHANGELOG.md:5-5
Timestamp: 2025-06-02T21:08:56.475Z
Learning: The `cjs-test` directory in the express-zod-api repository is a test workspace and should be excluded when checking for main project version consistency with changelog entries.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/tests/form-schema.spec.ts:31-31
Timestamp: 2025-05-27T19:27:13.492Z
Learning: Zod version 3.25.0 and later expose the Zod v4 API through the special import paths "zod/v4" and "zod/v4/core", allowing v4 features like .loose() to be used even when the package.json dependency shows a 3.x version.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-01T09:48:13.742Z
Learning: In express-zod-api, when migrating from Zod v3 to v4, the correct approach for internal type imports is to change from `import type { $ZodType } from "zod/v4/core"` to `import { z } from "zod"` and then use `z.core.$ZodType`. The zod/v4/core module is reexported as z.core by the main zod package, making this a valid and working approach.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/tests/buffer-schema.spec.ts:32-37
Timestamp: 2025-05-27T19:35:57.357Z
Learning: In the express-zod-api project, tests are run from the `express-zod-api` workspace directory, and the project uses an ESM-first environment without `__dirname`. Relative paths like `../logo.svg` in test files correctly resolve to the repository root due to this test execution context.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2869
File: zod-plugin/package.json:42-44
Timestamp: 2025-08-05T14:43:24.702Z
Learning: Zod version 4 has been officially released and is stable. As of August 2025, the latest published version is 4.0.14 (published July 30, 2025). Zod v4 is available on npm and can be used in peer dependencies with version constraints like "^4.0.0".
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/src/json-schema-helpers.ts:1-3
Timestamp: 2025-05-27T20:27:17.015Z
Learning: Ramda is correctly listed as a dependency in express-zod-api/package.json, so imports of ramda utilities are properly supported.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/src/json-schema-helpers.ts:1-3
Timestamp: 2025-05-27T20:27:17.015Z
Learning: Ramda is correctly listed as a dependency in express-zod-api/package.json, so imports of ramda utilities are properly supported.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-09-29T03:35:55.561Z
Learning: In the express-zod-api repository, packages are built using tsdown before publishing. Source code with .ts extensions is not published - only the built JavaScript bundles and declaration files in the dist/ directory are published to npm. This means .ts extensions in source imports don't affect consumers.
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-08T16:45:20.527Z
Learning: express-zod-api/zod-plugin: The ZodType.prototype.brand getter returns setBrand.bind(this) each time (no memoization). setBrand must always call pack() and include { brand: undefined } when unbranding so that pack → .check() runs and prior brand is overwritten; do not suggest removing the explicit undefined entry.
📚 Learning: 2025-10-02T17:42:48.840Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-10-02T17:42:48.840Z
Learning: In express-zod-api v25 (ESM-only), the `.example()` method error occurs when user code runs as CommonJS. Express Zod API patches Zod's ESM bundle with `.example()`, but CommonJS code requires a separate CJS bundle instance that lacks this patch. Users must run their code as ESM by: (1) setting `"type": "module"` in package.json, (2) using `.mts` or `.mjs` file extensions, or (3) using tools like `tsx` or `vite-node` that provide their own ESM-compatible compilation.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-08-01T09:48:13.742Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-01T09:48:13.742Z
Learning: In express-zod-api, when migrating from Zod v3 to v4, the correct approach for internal type imports is to change from `import type { $ZodType } from "zod/v4/core"` to `import { z } from "zod"` and then use `z.core.$ZodType`. The zod/v4/core module is reexported as z.core by the main zod package, making this a valid and working approach.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-06-02T21:08:56.475Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2697
File: CHANGELOG.md:5-5
Timestamp: 2025-06-02T21:08:56.475Z
Learning: The `cjs-test` directory in the express-zod-api repository is a test workspace and should be excluded when checking for main project version consistency with changelog entries.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • CHANGELOG.md
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-05-27T19:35:57.357Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/tests/buffer-schema.spec.ts:32-37
Timestamp: 2025-05-27T19:35:57.357Z
Learning: In the express-zod-api project, tests are run from the `express-zod-api` workspace directory, and the project uses an ESM-first environment without `__dirname`. Relative paths like `../logo.svg` in test files correctly resolve to the repository root due to this test execution context.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-05-28T18:58:10.064Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2428
File: express-zod-api/src/index.ts:44-44
Timestamp: 2025-05-28T18:58:10.064Z
Learning: The type-only import `import type {} from "qs";` in express-zod-api/src/index.ts is necessary to avoid TS2742 errors for exported functions like attachRouting, makeRequestMock, testEndpoint, and testMiddleware that have types depending on types/qs. This import provides the reference TypeScript needs to infer portable type names.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-05-27T19:27:13.492Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/tests/form-schema.spec.ts:31-31
Timestamp: 2025-05-27T19:27:13.492Z
Learning: Zod version 3.25.0 and later expose the Zod v4 API through the special import paths "zod/v4" and "zod/v4/core", allowing v4 features like .loose() to be used even when the package.json dependency shows a 3.x version.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-06-14T16:42:52.972Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2736
File: express-zod-api/tsup.config.ts:12-26
Timestamp: 2025-06-14T16:42:52.972Z
Learning: In express-zod-api tsup configurations, the direct mutation of `options.supported` in the `esbuildOptions` callback is intentional behavior and should not be flagged as a side effect issue.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • CHANGELOG.md
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-08-08T11:59:04.814Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2878
File: zod-plugin/runtime.ts:39-42
Timestamp: 2025-08-08T11:59:04.814Z
Learning: express-zod-api/zod-plugin/runtime.ts (TypeScript): Do not memoize the ZodType.prototype.brand getter. The current design returning setBrand.bind(this) on each access is intentional/preferred; avoid redefining "brand" as a data property per instance.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-09-29T03:35:55.561Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-09-29T03:35:55.561Z
Learning: In the express-zod-api repository, packages are built using tsdown before publishing. Source code with .ts extensions is not published - only the built JavaScript bundles and declaration files in the dist/ directory are published to npm. This means .ts extensions in source imports don't affect consumers.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
  • zod-plugin/src/runtime.ts
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-08-08T16:45:20.527Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-08T16:45:20.527Z
Learning: express-zod-api/zod-plugin: The ZodType.prototype.brand getter returns setBrand.bind(this) each time (no memoization). setBrand must always call pack() and include { brand: undefined } when unbranding so that pack → .check() runs and prior brand is overwritten; do not suggest removing the explicit undefined entry.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
📚 Learning: 2025-08-08T16:45:20.527Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-08T16:45:20.527Z
Learning: express-zod-api/zod-plugin: The brand setter must be immutable and always invoke pack→.check() via the getter-bound setBrand. Including brandProperty with an explicit undefined enables unbranding and should not be optimized away.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
📚 Learning: 2025-08-08T16:45:20.527Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-08T16:45:20.527Z
Learning: In express-zod-api zod-plugin, the brand setter must be immutable and always call `.check()` (packer’s onattach). Therefore, writing an explicit undefined entry in the bag for brand is intentional and should not be suggested for removal.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
📚 Learning: 2025-05-27T20:27:17.015Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/src/json-schema-helpers.ts:1-3
Timestamp: 2025-05-27T20:27:17.015Z
Learning: The JSONSchema type is not exported from the main "zod" module and must be imported from "zod/v4/core" when using Zod v4. This is acceptable for type-only imports as they don't create runtime dependencies.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/packages.ts
  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-08-08T16:45:20.527Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 0
File: :0-0
Timestamp: 2025-08-08T16:45:20.527Z
Learning: express-zod-api zod-plugin: The brand setter must stay immutable and always invoke pack→.check() (onattach). Including brandProperty with an explicit undefined enables “unbranding” (overwriting a previous brand) and should not be optimized away.

Applied to files:

  • zod-plugin/tests/runtime.spec.ts
  • zod-plugin/src/meta.ts
  • zod-plugin/src/runtime.ts
📚 Learning: 2025-05-27T20:27:17.015Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/src/json-schema-helpers.ts:1-3
Timestamp: 2025-05-27T20:27:17.015Z
Learning: Ramda is correctly listed as a dependency in express-zod-api/package.json, so imports of ramda utilities are properly supported.

Applied to files:

  • zod-plugin/src/packages.ts
  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
  • cjs-test/zod-plugin.spec.ts
📚 Learning: 2025-09-29T06:00:16.488Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2974
File: express-zod-api/src/documentation-helpers.ts:48-48
Timestamp: 2025-09-29T06:00:16.488Z
Learning: In the express-zod-api repository, the project uses pnpm workspaces with autoInstallPeers: false, meaning peerDependencies are not installed. The actual TypeScript version used for development comes from devDependencies which references "catalog:dev" in pnpm-workspace.yaml where it's set to a recent version (^5.9.2), not the peerDependency version which is just the minimum consumer requirement.

Applied to files:

  • zod-plugin/src/packages.ts
📚 Learning: 2025-08-05T14:43:24.702Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2869
File: zod-plugin/package.json:42-44
Timestamp: 2025-08-05T14:43:24.702Z
Learning: Zod version 4 has been officially released and is stable. As of August 2025, the latest published version is 4.0.14 (published July 30, 2025). Zod v4 is available on npm and can be used in peer dependencies with version constraints like "^4.0.0".

Applied to files:

  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
📚 Learning: 2025-06-02T21:11:20.768Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2697
File: CHANGELOG.md:5-5
Timestamp: 2025-06-02T21:11:20.768Z
Learning: In the express-zod-api repository, RobinTail follows a release workflow where package.json version is only updated on the master branch after merging all planned release changes. Changelog entries may show future version numbers while package.json remains at the previous version during feature development, and this is intentional workflow, not a version inconsistency that needs to be flagged.

Applied to files:

  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
📚 Learning: 2025-05-27T20:08:50.699Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/src/metadata.ts:5-13
Timestamp: 2025-05-27T20:08:50.699Z
Learning: The `_zod` property in Zod v4 schemas is officially documented and recommended for library authors to differentiate between Zod 3 and Zod 4 schemas at runtime, despite the underscore prefix. This is explicitly mentioned in Zod's library authors documentation and is not a private internal property.

Applied to files:

  • zod-plugin/CHANGELOG.md
  • CHANGELOG.md
📚 Learning: 2025-05-28T07:58:09.853Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: express-zod-api/src/documentation-helpers.ts:508-512
Timestamp: 2025-05-28T07:58:09.853Z
Learning: In express-zod-api, when working with Zod's JSON schema override callbacks, using `delete` to mutate `ctx.jsonSchema` is the recommended approach per Zod's official documentation, even if it triggers performance linting warnings. This is preferable to creating copies with `undefined` values, especially for snapshot testing.

Applied to files:

  • zod-plugin/src/meta.ts
📚 Learning: 2025-05-27T20:03:34.213Z
Learnt from: RobinTail
Repo: RobinTail/express-zod-api PR: 2546
File: example/factories.ts:35-42
Timestamp: 2025-05-27T20:03:34.213Z
Learning: The `./example` directory in the express-zod-api repository contains demonstration code for educational purposes only, not intended for production use. Example code can make simplified assumptions for brevity and clarity, and should not be flagged for missing production-level error handling, security measures, or edge case handling.

Applied to files:

  • cjs-test/zod-plugin.spec.ts
🧬 Code graph analysis (1)
zod-plugin/src/runtime.ts (4)
zod-plugin/src/packages.ts (2)
  • getPackages (4-9)
  • getClasses (11-17)
zod-plugin/src/meta.ts (3)
  • exampleSetter (3-10)
  • deprecationSetter (12-14)
  • labelSetter (16-18)
zod-plugin/src/brand.ts (1)
  • setBrand (8-10)
zod-plugin/src/remap.ts (1)
  • remap (39-50)
🔇 Additional comments (5)
zod-plugin/CHANGELOG.md (1)

7-8: Changelog correctly reflects dual ESM+CJS patching

The note that the plugin now patches both ESM and CJS exports of zod matches the new runtime/packages implementation and supported version range; no further changes needed.

CHANGELOG.md (1)

10-13: v26 changelog entry aligns with the implemented CJS fix

The new bullets about globalRegistry and extending CJS exports, including attribution and reference to v25.5.3, accurately describe the behavior introduced by the updated Zod plugin and tests.

cjs-test/zod-plugin.spec.ts (1)

1-12: CJS workspace test accurately covers the dual‑package hazard

Loading express-zod-api for side effects and then requiring zod is a solid way to assert that the CJS zod instance is patched; the expectations on .example() and meta().examples directly exercise the regression from issue #2981.

zod-plugin/tests/runtime.spec.ts (1)

1-11: Variant‑parametrized tests give good ESM+CJS coverage

Running the existing runtime test suite via describe.each for { zESM, zCJS } is a clean way to validate that all augmented methods behave identically across ESM and CJS; the createRequire‑based CJS import is appropriate in this test context.

zod-plugin/src/runtime.ts (1)

1-42: Per‑package augmentation cleanly extends support to both ESM and CJS zod exports

The guarded loop over getPackages()/getClasses(pkg) correctly applies example, deprecated, and brand to all relevant Zod prototypes, and then adds label on ZodDefault and remap on ZodObject per package. The pluginFlag gate prevents double‑installation across builds, and the brand getter continues to use setBrand.bind(this) as intended. This matches the documented behavior and dual‑package mitigation goals.

Comment thread zod-plugin/CHANGELOG.md Outdated
@RobinTail RobinTail added the documentation Improvements or additions to documentation label Nov 25, 2025
@RobinTail
Copy link
Copy Markdown
Owner Author

ready

@RobinTail RobinTail merged commit f549767 into make-v26 Nov 25, 2025
13 checks passed
@RobinTail RobinTail deleted the exp-plugin-cjs-too branch November 25, 2025 05:26
@RobinTail RobinTail linked an issue Nov 25, 2025 that may be closed by this pull request
RobinTail added a commit that referenced this pull request Dec 1, 2025
<img width="283" height="353" alt="image"
src="https://github.com/user-attachments/assets/5355c68c-e807-4b61-83a8-17e66a117f7d"
/>

[Lia Smith](https://en.wikipedia.org/wiki/Suicide_of_Lia_Smith) was a 21
years young transgender woman.

She attended Middlebury College in Middlebury, Vermont, majoring in
computer science and statistics. She was a member of the college's chess
club, bridge club, LGBTQ+ club, and Japanese club. She was a diver on
the Middlebury Panthers women's swimming and diving team.

In February 2025 Middlebury College had removed Lia Smith diving profile
from their website, following President Donald Trump's executive order,
which attempts to ban transgender women and girls from competing in
women's sports.

On October 18, 2025 Lia Smith committed suicide.

The tragedy of Lia Smith highlights that the push for exclusion of
transgender women from public women's spaces and sports is not an issue
of theoretical fairness, but one with devastating consequences for the
dignity, safety, and lives of trans individuals. Smith, who was an
openly transgender student athlete, described the immense difficulty and
feeling of being unwelcome in locker rooms, emphasizing the lack of a
clear, safe space for her. Her plea that transgender people are "not
trying to get into women's spaces to be perverts... we're just being
ourselves" underscores that inclusion is a matter of allowing trans
women to exist and access necessary public facilities with basic
dignity, rather than an invasion of privacy or a threat. Denying access
to these spaces does not eliminate their need for them, but instead
subjects them to alienation and heightened psychological distress.

Furthermore, Lia's experience as a diver, where she was reportedly
targeted by anti-trans websites and ultimately left her team citing
difficulties, demonstrates the severe emotional and social toll of
exclusion in sports. Critics of anti-trans policies explicitly linked
her death by suicide to the hostile climate and constant public scrutiny
and harassment directed at trans athletes. Allowing trans women to
participate in women's sports and access women's facilities is therefore
a vital component of protecting the mental health and physical safety of
transgender people. When a society creates systems that reject a
person’s identity and deny them opportunities for community, activity,
and belonging—all essential parts of a functioning life—it creates an
environment of intense hostility and vulnerability. Inclusion, in
contrast, affirms the full humanity of trans women and is a necessary
measure against the systemic discrimination that contributed to her
anguish.

--------------------------------

- #2938 
- #3049
- #2934 
- #3086 
- #3087 
- #3094
- ❔  #3102 

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

* **New Features**
* Config-driven routing behavior; Integration instances accept
per-instance config.
  * Zod plugin adds CJS compatibility and new schema metadata helpers.
  * Release bumped to v26 (beta).

* **Bug Fixes**
  * Restored Zod examples/metadata in CommonJS environments.

* **Breaking Changes**
  * Handler/middleware parameter renamed: options → ctx.
  * Removed DependsOnMethod; use inline per-route objects.
  * EndpointsFactory.addOptions() renamed to addContext().

* **Docs**
  * README and CHANGELOG updated for v26 migration and examples.

* **Tests**
  * Added CJS runtime tests and updated tests to use ctx.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request prevention refactoring The better way to achieve the same result

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v25: example is not a function

1 participant