Repository navigation
Conversation
…lish, in 3.1 to 3.0
Effect's `S.NullishOr(X)` and `S.optional(S.NullOr(X))` come out of
@orpc/effect as `anyOf: [X, { type: "null" }, { type: "null" }]`. The
nullable merge only looked at two-branch anyOfs, so 3.0 kept the anyOf,
which NSwag reads as an empty class and Kiota as a string. It now merges
any anyOf whose branches are all only-null but one, and when it becomes
a oneOf it keeps that branch and one null branch.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxDhzz5VuQrnkwczU91Bv6
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run reviewed the full net diff against main — the extension of the 3.1→3.0 nullish-anyOf merge to unions with more than two branches.
- Collect every multi-branch
anyOf—finishSchemanow queuesout.anyOfwhenlength > 1(renamedtwoBranchAnyOfs→nullableAnyOfs), so Effect'sanyOf: [X, {type:"null"}, {type:"null"}]reachesmergeNullable. - Merge only a single non-null branch —
mergeNullablefilters outisOnlyNullSchemabranches and proceeds only when exactly one non-null branch remains; the non-null count>= 2case returns early, preserving genuine unions like[string, number, null]. - Dedup null on the
oneOfpath — when the pair becomes aoneOf, it keepsotherplus a single null branch, ordered bybranches[0] === other, so null isn't duplicated (which would make theoneOfreject null). - Tests and README — the #87 assertion that pinned
[string, null, null]as a keptanyOfmoved to the merge test, with new per-orderoneOfand[string, number, null]retention cases; one README table row updated.
I verified the diff against the surrounding converter: the 3-branch outputs match the equivalent 2-branch pattern for each path (merge→nullable, oneOf ordering, kept anyOf), the strict isOnlyNullSchema shape (exactly three keys, type: "string", nullable: true, enum: [null]) prevents any non-null branch from being misclassified as null, and the whole suite passes (vitest run — 5 files, 262 tests).
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Requested by dinwwwh · project thread
Problem
Effect's
S.NullishOr(X)andS.optional(S.NullOr(X))come out of@orpc/effectasanyOf: [X, { type: "null" }, { type: "null" }]. The nullable merge from #87/#88 only looks at two-branch anyOfs, so 3.0 keeps the anyOf.NSwag 14.7 on an oRPC Effect model (
addr: S.NullishOr(S.Struct({ city })),tags: S.optional(S.NullOr(S.Array(S.String))),count: S.NullishOr(S.Int),name: S.optional(S.NullOr(S.String))): on main it makes empty wrapper classes, and the C# client fails on both a filled payload ("Cannot deserialize the current JSON array into type 'Tags'") and an all-null one ("Required property 'addr' expects a value but got null"). With this PR it makes a nullable class,ICollection<string>,long?andstring, and both payloads deserialize. Kiota reads the kept anyOf as a string.Change
finishSchemaqueues every anyOf with more than one branch (the list is renamedtwoBranchAnyOfstonullableAnyOfs).mergeNullablemerges when all branches but one are only-null.oneOf, it keeps the other branch and one null branch, in their order, since aoneOfrejects null when it matches two branches. It picks by position, not identity, so a doc that uses one null object twice still gets null once.[string, null, null]as a kept anyOf moves to the merge test, plus aoneOfcase for each order and[string, number, null]staying an anyOf. One README row.Checks
pnpm run checkclean.Note: #99 also edits
downgradeV30, so whichever merges second needs a small rebase for the rename.Generated by Claude Code