Repository navigation
fix(downgrader): count examples and $comment as annotations beside a tagged $ref - #204
Conversation
…tagged $ref A `$ref` to a tagged union with only annotations beside it, and such a branch of a tagged union, stays the bare `$ref` in 3.0. The check read the 3.1 input but counted only 3.0 annotation names, so the `examples` the Anthropic API writes beside `BetaWebhookEvent.data` kept the `allOf` wrapper, and openapi-typescript then left out the `type` tag. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VVaw7Pe42johGviNe9drKR
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
isAnnotatedRefacceptsexamplesand$comment—packages/downgrader/src/v3.1-to-v3.0.ts:358now treats the two 3.1 annotation keys as annotation-only siblings, so a$refbeside them stays bare in a tagged union or when pointing at a schema whosediscriminatorsurvives.- Tests extended — both tagged-ref tests in
packages/downgrader/tests/v3.1-to-v3.0.test.tsnow includeexamples/$commenton the ref, and the branch/property cases assert the bare$ref. - README
$refrow — names the Anthropicexampleswritten beside the property holding a tagged union.
Both call sites are tagged-only (convertTaggedBranch, and convertSchema's isAnnotatedRef && isTaggedRef guard), and examples/$comment are validation-neutral, so the change cannot widen acceptance — it only drops the siblings as documented. Verified locally: vitest run packages/downgrader → 287 passed.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
…nd nullable targets (#205) <!-- claude-projects-attribution: {"github_login":"dinwwwh"} --> _Requested by **dinwwwh** · [project thread](https://claude.ai/code/project/chan_01UcKMz91tMarPV1kWLVgLYE?thread=cmsg_01UcKMz91tMarPV1kWLVgLYE7TKScycrTLZfzZMNY1F8sa)_ 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": 1. **Records keyed by an enum** (`enumNames`, from #100) accepted only `enum`, `const`, `title`, `description`, and `type: "string"` in the key schema. Any other annotation, such as `example`, `examples`, `default`, `deprecated`, `$comment` or an `x-` key, made it bail, so the 3.0 record allowed any key. - Meilisearch 1.15.2 (utoipa): `BatchStats.status` and `BatchStats.types` are keyed by an enum with `example: "processing"`. openapi-typescript gave `[key: string]: number`. With this change it gives `enqueued?: number; processing?: number; ...`. - ElevenLabs (Pydantic): `ConversationInitiationSource` has `default: "unknown"` beside its 27-value enum. The records keyed by it now get 27 properties, and the discriminated `oneOf` responses that hold them keep their `oneOf` instead of becoming `anyOf`. - oRPC with Zod: `z.record(E, V)` where `E` has `.meta({ id, examples })` or `.meta({ id, deprecated: true })` now gets one property per name, as a described `E` already did. 2. **A nullable `$ref` to a nullable `anyOf`** (`admitsNull`, from #203) allowed only 3.0 annotations beside the target's `anyOf`, so the `examples` that Zod's `.meta` writes there kept the `anyOf`. NSwag reads that as an empty class. `isAnnotatedRef` (#204) already had the longer list. A new `isInputAnnotation` helper (`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 `enumNames` change touches 7 docs at 64 sites, with no throws and no values lost per the value oracle. The `admitsNull` change touches none of those docs, but it fixes the oRPC repro. ### Tests - `pnpm run check` and `pnpm test` pass (364 tests). Downgrader coverage stays at 100%. - The new assertions fail on main: the enum-key test, the `$ref`-to-enum test, and the nullable `anyOf` target test. --- _Generated by [Claude Code](https://claude.ai/code/session_01VVaw7SWqGqen9de9vgJcko)_ Co-authored-by: Claude <[email protected]>

Requested by dinwwwh · project thread
What
#150 and #174 keep a
$refwith only annotations beside it bare in 3.0 when it is a branch of a tagged union, or points to a schema whosediscriminatorstays. The check,isAnnotatedRef, reads the 3.1 input but only counted the 3.0 annotation names (example,description, ...) andx-keys. So the 3.1 annotationsexamplesand$commentmade it fail, and{ $ref: Tagged, examples: [...] }became{ allOf: [{ $ref: Tagged }], example: ... }.Real case: the Anthropic API's
BetaWebhookEvent.datais{ $ref: BetaWebhookEventData, examples: [...] }, whereBetaWebhookEventDatais a 22-wayoneOfwith adiscriminatorandmapping. On main, openapi-typescript then writesdata: Omit<components["schemas"]["BetaWebhookEventData"], "type">, dropping the tag so code can't narrow ontype. With this change it writesdata: components["schemas"]["BetaWebhookEventData"].Change
isAnnotatedRefalso acceptsexamplesand$comment. As withexample, they are dropped beside the bare$ref.$refto a tagged target) now includeexamplesand$comment; both fail on main.$refrow names theexamplesthe Anthropic API writes there.Checks
pnpm run check,pnpm test(364 tests), coverage 100%.Generated by Claude Code