feat(cli): add preserve-one-of-in-all-of OpenAPI setting - #17289
Conversation
There was a problem hiding this comment.
AI Review Summary
Adds an opt-in preserve-one-of-in-all-of setting that rewrites allOf: [oneOf: [A,B], S] into oneOf: [allOf: [A,S], allOf: [B,S]] before the existing allOf-merge path, plus plumbing through generators.yml/fern.yml schemas and fixtures. The plumbing looks consistent and the default keeps existing behavior. Two correctness gaps in the rewrite: sibling oneOf/anyOf and a bare required on the parent are silently discarded.
- 🟡 2 warning(s)
- 🔵 1 suggestion(s)
Docs Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on
Docs generation runs |
SDK Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on Full benchmark table (click to expand)
main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via |
Description
Linear ticket: N/A
A
oneOfused as a member of anallOfis a union of object variants:The V3 importer flattens it instead — every variant's properties are merged into one object and marked optional — so the union never reaches the IR. The same
oneOfin field position converts to anundiscriminatedUnionand renders a variant selector in the API reference, which is why the two positions behave differently today. Flattening also widens validation:{ leafId, branchId }becomes representable even though the spec forbids it.This adds an opt-in OpenAPI spec setting that distributes the
allOfover the union instead:Because the IR is shared by docs and every SDK generator, this changes generated request shapes for affected schemas, so it is off by default rather than a global flip.
Before / after for the fixture added here (
ExportRequest: allOf: [oneOf: [ExportLeaf, ExportBranch, ExportTree], {timeout, tag}]):Changes Made
SchemaConverter.tryDistributeAllOfOverOneOfrewritesallOf: [oneOf: [A, B], S]asoneOf: [allOf: [A, S], allOf: [B, S]]before the existing allOf-merging path, so each variant keeps its own properties plus the shared ones. Union members with adiscriminatorare left alone, since a discriminated union only keeps its discriminator intact.preserve-one-of-in-all-ofthrough the existing OpenAPI settings path (generators.yml schema + serialization, configuration loader,APIDefinitionSettings,ParseOpenAPIOptions) and thefern.yml(CLI v2) equivalents, mirroringinline-all-of-schemas. Defaults tofalse.generators-yml.schema.jsonandfern-yml.schema.jsonviapnpm jsonschema.allof-oneof-memberfixture (setting on) andallof-oneof-member-defaultfixture (setting off) plus assertions, and the IR snapshots the V3 fixture suites generate for them.Testing
Unit tests added/updated
Manual testing completed
pnpm vitest run src/__test__/allof-oneof-member.test.tsinv3-importer-tests— 3 passed (union with the flag, flattened object without it, field-position union unchanged).pnpm turbo run testforv3-importer-tests,configuration,configuration-loader,cli-v2,openapi-ir-parser,api-workspace-commons— all passed exceptv3-sdks > url-reference, which fails withTypeError: fetch failedbecause the sandbox has no network access to the remote spec it downloads.pnpm fern:build,pnpm jsonschema,pnpm format:fix,biome checkon the touched files — clean.