Repository navigation
fix(downgrader): count every 3.1 annotation beside record key enums and nullable targets - #205
Conversation
…nd nullable targets
A record keyed by an enum with an `example`, `examples`, `default`,
`deprecated`, `$comment` or `x-` key beside the names lost its names in
3.0, so it allowed any key (Meilisearch's utoipa `example`, ElevenLabs'
Pydantic `default`, oRPC `.meta({ examples })`). A nullable `$ref` to a
nullable `anyOf` with `examples` or `$comment` beside it kept the `anyOf`.
One helper, `isInputAnnotation`, now checks 3.1 input keys for
annotations in `enumNames`, `admitsNull` and `isAnnotatedRef`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VVaw7SWqGqen9de9vgJcko
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
isInputAnnotationhelper — new helper (examples/$comment/isAnnotation) centralizes the 3.1 annotation set;isAnnotatedRefnow delegates to it as a pure equivalent refactor.enumNameswidening — both the$refsibling check and the resolved-target keyword allow-list now accept any input annotation instead of onlytitle/description, so records keyed by an enum carryingexample/examples/default/deprecated/$comment/x-convert to fixedproperties+additionalProperties: false.admitsNullwidening — acceptsexamples/$commentbeside ananyOfwith a null branch, or alone; its only caller (nullable$refto a null-admitting target) now drops theanyOf.- Docs & tests — README row updated; new assertions cover the enum-key,
$ref-to-enum, and nullable-anyOfcases.
The added keywords are all non-restricting by definition, so the widening carries no value-loss risk. Verified pnpm run check clean and pnpm test 364 passing on 4e65f5a.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Requested by dinwwwh · project thread
Three spots in the 3.1 to 3.0 converter check 3.1 input keys for annotations, and two of them used a shorter list than "every annotation":
enumNames, from feat(downgrader): turn a propertyNames enum into one property per name in 3.1 to 3.0 #100) accepted onlyenum,const,title,description, andtype: "string"in the key schema. Any other annotation, such asexample,examples,default,deprecated,$commentor anx-key, made it bail, so the 3.0 record allowed any key.BatchStats.statusandBatchStats.typesare keyed by an enum withexample: "processing". openapi-typescript gave[key: string]: number. With this change it givesenqueued?: number; processing?: number; ....ConversationInitiationSourcehasdefault: "unknown"beside its 27-value enum. The records keyed by it now get 27 properties, and the discriminatedoneOfresponses that hold them keep theironeOfinstead of becominganyOf.z.record(E, V)whereEhas.meta({ id, examples })or.meta({ id, deprecated: true })now gets one property per name, as a describedEalready did.$refto a nullableanyOf(admitsNull, from fix(downgrader): keep only the $ref of a nullable reference to a nullable anyOf #203) allowed only 3.0 annotations beside the target'sanyOf, so theexamplesthat Zod's.metawrites there kept theanyOf. NSwag reads that as an empty class.isAnnotatedRef(#204) already had the longer list. A newisInputAnnotationhelper (examples,$comment, or a 3.0 annotation) now serves all three.The README row for enum-keyed records now says that annotations beside the names are dropped. The nullable-reference row already said "only annotations", so it is unchanged.
Evidence from the issue hunt: on a 19,775-doc corpus the
enumNameschange touches 7 docs at 64 sites, with no throws and no values lost per the value oracle. TheadmitsNullchange touches none of those docs, but it fixes the oRPC repro.Tests
pnpm run checkandpnpm testpass (364 tests). Downgrader coverage stays at 100%.$ref-to-enum test, and the nullableanyOftarget test.Generated by Claude Code