fix(valibot): emit acyclic schema refs before dependents#4077
Conversation
|
|
|
|
@yslpn is attempting to deploy a commit to the Hey API Team on Vercel. A member of the Team first needs to authorize it. |
|
@yslpn did you consider any other approaches? |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4077 +/- ##
==========================================
- Coverage 38.78% 38.74% -0.05%
==========================================
Files 627 627
Lines 21897 21920 +23
Branches 6408 6414 +6
==========================================
Hits 8492 8492
- Misses 10921 10940 +19
- Partials 2484 2488 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — emits acyclic Valibot schema refs before their dependents, eliminating unnecessary v.lazy() wrappers and v.GenericSchema type annotations when a referenced schema appears later in the document.
- Add
isCyclicReferencecycle detection in the processor usingtransitiveDependenciesfrom the spec graph, plus a direct self-reference check - Add
ensureReferenceRegisteredthat recursively emits acyclic target schemas before the referencing schema is fully walked - Thread
ensureReferenceRegisteredinto the visitor viaVisitorConfig, called byreference()before theisRegistered/v.lazy()fallback - Normalize
$refvianormalizeJsonPointerin both the visitor query and processor lookup, fixing a latent mismatch where un-normalized refs (with escaped chars) could bypass symbol registration checks - Add test fixture (
validators-acyclic-ref-order.yaml) withParentdeclared beforeChildand a snapshot confirmingvChildemits first andvParentreferences it directly
ℹ️ Nitpicks
-
Zod has the same forward-reference issue and
resourceIdmismatch.packages/openapi-ts/src/plugins/zod/v4/visitor.ts:244(and v3/mini) uses raw$refwithoutnormalizeJsonPointerand lacks any acyclic-forward-emission path — the Zod plugin could benefit from the same treatment in a follow-up. -
isCyclicReferencefalls back tofalsewhentransitiveDependenciesis absent.processor.ts:28chains through?.and?? false, so a missing graph silently treats all non-self refs as acyclic. In practice the graph is always built for OAS specs, but if it were absent, mutual refs would hit thethrowon line 66 instead of falling through tov.lazy(). A comment or guarding this behindgraphpresence could help future readers, though it's not a risk today.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
@hey-api/codegen-core
@hey-api/json-schema-ref-parser
@hey-api/nuxt
@hey-api/openapi-ts
@hey-api/shared
@hey-api/spec-types
@hey-api/types
@hey-api/vite-plugin
commit: |
@mrlubos Yes, I initially thought about properly typing all the lazy schemes. It turned out to be quite complicated. I had to change a lot of files, and it's not ready yet, so I'm not sure it can be merged. The current fix removes lazy for cases where it's not needed. In my example, there's no cyclic dependency or anything similar. So this can be considered a point fix for my case. But the lazy typing issue can't be resolved yet #4027. I've been developing a new recursive scheme for valibot for some time open-circle/valibot#1504 Perhaps after its implementation, the fix can be simplified. |
Indeed!
My main concern with this is that it introduces a bespoke pattern into a single plugin. The problem seems like something that should be recurring though? Even Pullfrog pointed out Zod which would be my first question too. But the same issue should exist with Faker and many other plugins. Do you agree? If so, I'd be looking to introduce this as a pattern or API other plugins could implement. It would make refactoring and understanding code much easier |
Yes, I agree. This should be a shared pattern/API rather than a Valibot-only solution. I kept this scoped because I was focused on the concrete issue I hit, but I’m not familiar enough with the broader plugin architecture to do that refactor properly. |

Fix Valibot acyclic reference ordering
Summary
v.lazy()for forward references that can be safely emitted first.This prevents generated validators from becoming
v.GenericSchemasolely because a referenced schema appears later in the OpenAPI document.Related issue
Related to #4027.
Testing
pnpm exec vitest run packages/openapi-ts-tests/valibot/v1/test/3.1.x.test.tspnpm exec tsc --noEmit --pretty false --project packages/openapi-ts/tsconfig.jsonpnpm exec tsc --noEmit --pretty false --project packages/openapi-ts-tests/valibot/v1/tsconfig.json