Skip to content

fix: zod enum properties respect the default field#3727

Merged
mrlubos merged 4 commits intomainfrom
copilot/fix-default-field-in-zod-enum
Apr 8, 2026
Merged

fix: zod enum properties respect the default field#3727
mrlubos merged 4 commits intomainfrom
copilot/fix-default-field-in-zod-enum

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 8, 2026

Enum schemas with a default value were silently dropping it, so the generated Zod schema never included .default(...). This affected all three OpenAPI spec versions (2.0.x, 3.0.x, 3.1.x).

Root cause

parseEnum in each shared parser called initIrSchema() but not parseSchemaMeta(). Every other schema type (parseString, parseNumber, etc.) calls both. parseSchemaMeta is what copies default, readOnly, format, and similar fields onto the IR schema — so all of them were silently dropped for enums.

Changes

  • packages/shared/src/openApi/{2.0.x,3.0.x,3.1.x}/parser/schema.ts — add parseSchemaMeta({ irSchema, schema }) call immediately after initIrSchema inside each parseEnum function
  • Snapshots updated to reflect newly correct output

Before / After

# OpenAPI schema
foo:
  type: string
  enum: [foo, bar, baz]
  default: foo
// Before
foo: z.enum(['foo', 'bar', 'baz']).optional()

// After
foo: z.enum(['foo', 'bar', 'baz']).optional().default('foo')

Beyond default, this also fixes enum schemas with readOnly: true — they now correctly produce .readonly() in Zod and readonly on the TypeScript type, matching behavior of all other schema types.

@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@pullfrog
Copy link
Copy Markdown

pullfrog bot commented Apr 8, 2026

Reviewed this WIP draft PR — it has no code changes yet (empty "Initial plan" commit). Submitted a review with root cause analysis and implementation guidance: the bug is in the parser layer where parseEnum doesn't call parseSchemaMeta, so default values are lost before reaching the zod plugin.

Task list (3/3 completed)
  • Read the PR diff to identify changes
  • Analyze changes and draft review
  • Submit review

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 8, 2026

🦋 Changeset detected

Latest commit: 73846df

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hey-api/shared Patch
@hey-api/openapi-ts Patch
@hey-api/openapi-python Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hey-api-docs Ready Ready Preview, Comment Apr 8, 2026 2:31am

Request Review

@pullfrog
Copy link
Copy Markdown

pullfrog bot commented Apr 8, 2026

TL;DR — The parseEnum functions across all three OpenAPI spec version parsers were missing a call to parseSchemaMeta, causing enum schemas to silently drop metadata like default, readOnly, and validation constraints. This fix adds the missing call so that enum properties with a default field now correctly emit .default() in Zod/Valibot output and readonly in type generation.

Key changes

  • Add parseSchemaMeta call to parseEnum — The 2.0.x, 3.0.x, and 3.1.x schema parsers now process enum metadata (defaults, read/write-only, constraints) that was previously ignored.
  • Fix test spec default values to use valid enum members — The parameterEnum defaults in all three spec versions changed from 0 (not a valid enum member) to 'Success', ensuring the test fixtures accurately reflect real-world usage.
  • Convert arrow functions to function declarations — All const fn = () => {} patterns in the three parser files are replaced with function fn() {} declarations, a stylistic cleanup applied uniformly.
  • Update 166 test snapshots — Zod, Valibot, oRPC, and type generation snapshots reflect the newly emitted .default('Success') calls and readonly modifiers on enum properties.

Summary | 172 files | 4 commits | base: maincopilot/fix-default-field-in-zod-enum


Enum metadata now flows through the IR

Before: parseEnum called initIrSchema (which only processes JSDoc fields like title, description, deprecated) but skipped parseSchemaMeta — so default, readOnly, writeOnly, and validation constraints on enum schemas were silently discarded.
After: parseEnum calls parseSchemaMeta({ irSchema, schema }) immediately after initIrSchema, matching the pattern used by parseType, parseAllOf, parseAnyOf, and parseOneOf.

This is a one-line fix repeated in each of the three spec-version parsers. The downstream effect is that Zod output now emits .default(value) on enum schemas, Valibot emits the default as a second argument to v.optional(), and TypeScript types gain readonly where the spec declares readOnly: true.

Why were the test spec defaults also changed?

The original specs used default: 0 for enum parameters whose allowed values were ['Success', 'Warning', 'Error'] — a numeric default that is not a valid member. With parseSchemaMeta now wired up, this invalid default would surface in generated code as .default(0) instead of a meaningful value. The fix changes the default to 'Success', an actual enum member, making the test fixtures consistent with the spec contract.

packages/shared/src/openApi/2.0.x/parser/schema.ts · packages/shared/src/openApi/3.0.x/parser/schema.ts · packages/shared/src/openApi/3.1.x/parser/schema.ts · specs/3.1.x/full.yaml

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

Copy link
Copy Markdown

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

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

Important

This PR has no code changes yet — just an empty "Initial plan" commit. Leaving guidance on the fix below.

Root Cause

The bug is in the parser layer, not the zod plugin. The parseEnum function in all three parser versions (2.0.x, 3.0.x, 3.1.x) calls initIrSchema({ schema }) but never calls parseSchemaMeta({ irSchema, schema }). This means default (and other metadata like format, accessScope, minimum/maximum, pattern) is silently dropped for enum schemas.

The zod plugin already handles default correctly — defaultMeta(schema) reads schema.default and applyModifiers() chains .default(...) when it's defined. The value just never makes it into the IR.

Fix

Add parseSchemaMeta({ irSchema, schema }) after initIrSchema in each parser's parseEnum:

  • packages/shared/src/openApi/3.1.x/parser/schema.tsparseEnum, after line 947
  • packages/shared/src/openApi/3.0.x/parser/schema.tsparseEnum, after line 863
  • packages/shared/src/openApi/2.0.x/parser/schema.tsparseEnum, after line 455
const parseEnum = ({ context, schema, state }) => {
  let irSchema = initIrSchema({ schema });
  parseSchemaMeta({ irSchema, schema }); // <-- add this
  irSchema.type = 'enum';
  // ...
};

Compare with parseType in the same files — it already calls parseSchemaMeta and serves as a reference for the correct pattern.

Snapshot Updates

Existing specs already exercise this path. After the fix, expect snapshot changes where enum schemas gain .default(...) calls — e.g. zEnumWithNumbers should gain .default(200), and parameterEnum entries in CallWithDefaultParameters should gain their defaults.

Scope Consideration

parseSchemaMeta also propagates format, accessScope, range constraints, etc. Verify whether any of these are problematic on enum IR schemas, or whether the downstream consumers (zod, TypeScript plugin, etc.) gracefully ignore irrelevant fields. If not, a narrower fix that only copies default may be safer.

Pullfrog  | Fix it ➔View workflow run𝕏

Copilot AI changed the title [WIP] Fix default field in zod enum properties fix: zod enum properties respect the default field Apr 8, 2026
Copilot AI requested a review from mrlubos April 8, 2026 01:14
@mrlubos mrlubos marked this pull request as ready for review April 8, 2026 02:20
@pullfrog
Copy link
Copy Markdown

pullfrog bot commented Apr 8, 2026

Reviewed PR #3727. The core fix (adding parseSchemaMeta to parseEnum in all three OpenAPI parsers) is correct and follows established patterns. Flagged that the const => to function refactor should ideally be a separate commit/PR to keep the behavioral change easy to audit. Review posted.

Task list (4/4 completed)
  • Read the diff: changeset + source changes (parsers)
  • Read the diff: snapshot changes (sampling)
  • Verify the fix pattern against existing code
  • Self-critique and submit review

Pullfrog  | View workflow run | Triggered by Pullfrog𝕏

@dosubot dosubot bot added lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files. bug 🔥 Broken or incorrect behavior. labels Apr 8, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 21.12676% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 40.04%. Comparing base (0983189) to head (73846df).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
packages/shared/src/openApi/3.1.x/parser/schema.ts 3.84% 25 Missing ⚠️
packages/shared/src/openApi/3.0.x/parser/schema.ts 4.00% 24 Missing ⚠️
packages/shared/src/openApi/2.0.x/parser/schema.ts 65.00% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3727      +/-   ##
==========================================
- Coverage   40.24%   40.04%   -0.21%     
==========================================
  Files         520      520              
  Lines       19302    19243      -59     
  Branches     5726     5720       -6     
==========================================
- Hits         7769     7705      -64     
- Misses       9337     9342       +5     
  Partials     2196     2196              
Flag Coverage Δ
unittests 40.04% <21.12%> (-0.21%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

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

Important

The actual fix (3 added parseSchemaMeta calls) is correct and well-motivated, but it's buried in a large const => function refactor across all three parser files. This makes the diff ~900 lines when the functional change is ~6 lines. These should be separate commits or separate PRs — mixing a syntactic refactor with a behavioral fix makes the change hard to audit and bisect.

The root cause analysis is accurate: parseEnum called initIrSchema (JSDoc + extensions) but never parseSchemaMeta (which copies default, readOnly, format, etc.), so all that metadata was silently dropped. The fix correctly follows the pattern established by parseType, parseOneType, and the 3.1.x composition functions.

Snapshot changes are consistent — enums now emit .default(...), .readonly(), and readonly on TypeScript types where the spec declares them.

Tangential observation (not blocking): in the 3.0.x parser, parseAllOf, parseAnyOf, and parseOneOf still don't call parseSchemaMeta after initIrSchema, unlike their 3.1.x counterparts. That's a pre-existing gap, not something to fix here.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run𝕏

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 8, 2026

Open in StackBlitz

@hey-api/codegen-core

npm i https://pkg.pr.new/@hey-api/codegen-core@3727

@hey-api/json-schema-ref-parser

npm i https://pkg.pr.new/@hey-api/json-schema-ref-parser@3727

@hey-api/nuxt

npm i https://pkg.pr.new/@hey-api/nuxt@3727

@hey-api/openapi-ts

npm i https://pkg.pr.new/@hey-api/openapi-ts@3727

@hey-api/shared

npm i https://pkg.pr.new/@hey-api/shared@3727

@hey-api/spec-types

npm i https://pkg.pr.new/@hey-api/spec-types@3727

@hey-api/types

npm i https://pkg.pr.new/@hey-api/types@3727

@hey-api/vite-plugin

npm i https://pkg.pr.new/@hey-api/vite-plugin@3727

commit: 73846df

@mrlubos mrlubos merged commit c1ab143 into main Apr 8, 2026
10 of 12 checks passed
@mrlubos mrlubos deleted the copilot/fix-default-field-in-zod-enum branch April 8, 2026 02:35
@hey-api hey-api bot mentioned this pull request Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug 🔥 Broken or incorrect behavior. lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zod: enum properties don't respect the "default" field

2 participants