fix(downgrader): stop nested multi-type arrays from doubling 3.1 to 3.0 output - #21
Conversation
….0 output
A schema like `{ type: ['array', 'object'], items: ... }` kept `items` at the
top level and cloned it into the synthesized array variant, so every nesting
level doubled the output. `items` now moves into the array variant instead.
# Conflicts: # packages/downgrader/README.md
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.
ℹ️ Minor suggestion inline — the fix itself looks correct.
Reviewed changes
applyTypesmoves siblingitems— inpackages/downgrader/src/v3.1-to-v3.0.ts, the synthesizedarrayvariant now reusesout.itemsand deletes the top-level copy instead of deep-cloning it while leaving one in place, so nested multi-type arrays grow linearly instead of doubling per level.- Malformed-
allOfguard — an early return preserves top-levelitemswhen an existinganyOfplus a non-arrayallOfmakes the type union get dropped. - Tests and docs — items-placement cases (moved / retained when no
arrayvariant / retained when the union is dropped), a 10-level regression test asserting exact output, and the README mapping row.
I re-ran pnpm test (355 passing across 5 files) and traced the move against every applyTypes branch: removing top-level items is safe because it only constrains arrays, and every array instance must still satisfy the array variant (directly as anyOf, or nested under allOf when anyOf already exists).
DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Regression test for the cyclic multi-type array variant —
8c7c7b1adds a case converting a self-referential{ type: ['array', 'object'], items: <self> }schema, asserting the top-levelitemsis gone, thearrayvariant'sitemspoints at the converted schema, and the input is left unmutated. I reverted only theitemsmove locally and confirmed the new test fails on the pre-fix code (the result keeps anitemsself-reference), then re-ran the file against the restored source (141 passing).
This directly addresses the prior pullfrog inline suggestion; that thread has been resolved. No further concerns.
DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

The 3.1 → 3.0 downgrader no longer doubles its output at each nesting level of a multi-type array schema such as
{ type: ['array', 'object'], items: ... }. A 20-level chain took about 5.6 s and produced 85M characters of JSON. It now takes 0.3 ms and produces 1,117 characters.Fixes
itemsnext to a multi-typetypenow moves into the generatedarrayvariant. Before, it stayed at the top level and a copy went into the variant. Validation is unchanged: top-levelitemsonly applies to arrays, and only thearrayvariant accepts arrays.arrayvariant point back at the converted schema. Before, it pointed at a partial copy that had noanyOf.Performance
Pointing both places at the same converted object would fix conversion time but not size.
JSON.stringifywrites shared objects out in full, so the serialized document stayed at 85M characters.Testing
itemsstays at the top level when the union has noarrayvariant, and when an invalidallOfmakes the converter drop the union.pnpm vitest run(355 tests),pnpm lintandpnpm type:checkpass. The e2e and corpus tests validate the output against the 3.0 schema.