fix(bundler): inline a $ref to a sequence or scalar as that kind of node - #637
Merged
Merged
Conversation
When composed bundling cannot lift a $ref into components (for example
`tags: {$ref: tags.yaml#/tags}`), the inline fallback replaced only the
$ref node's Content with the target's Content. The $ref node is a
mapping, so a sequence target produced a mapping node holding the
sequence items. walkAndRewriteRefs walks mappings in key/value pairs, so
an odd number of items read past the end of the slice and panicked with
"index out of range [N] with length N"; an even number silently left a
corrupted mapping behind.
All four Content-only replacement sites now go through inlineRefNode,
which swaps content for mapping targets exactly as before and, for any
other target, also adopts its kind, tag, style, value and alias.
Fixes #607
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #637 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 297 297
Lines 37662 37671 +9
=========================================
+ Hits 37662 37671 +9
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
daveshanley
added a commit
that referenced
this pull request
Sep 25, 2026
…writeRefs The bounds check added to walkAndRewriteRefs for #607 covered a mapping node holding sequence content. That node came from the composer's inline fallback copying only a sequence target's Content onto a $ref mapping node, which is now fixed at the source (inlineRefNode, #637). The walker can no longer be handed an odd-length mapping, so the guard never runs and was the only uncovered block in the bundler package. The regression test from #608 (63, 64 and 65 $ref'd tags through BundleBytesComposed) stays and passes without the guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #607. Supersedes #608.
Problem
BundleBytesComposedpanics withindex out of range [N] with length Nwhen a$refresolves to an odd-length array, e.g.tags: {$ref: "tags.yaml#/tags"}.walkAndRewriteRefsalready handles sequences correctly. The corrupt node comes from the composer. When a$refcan't be lifted intocomponents, the inline fallback (inlineProcessRef,inlineMatchingRefs,rewriteInlinedAbsoluteRefs) replaces only the$refnode's.Contentwith the target's.Content. The$refnode is a mapping, so a sequence target becomes a mapping node holding N sequence items:Content[i+1]past the end → panic;Fix
One helper,
inlineRefNode(refNode, target), used at all four Content-only sites:Contentonly, exactly as today, so the ref node keeps its own style and comments. Output for the common case is unchanged.Kind,Tag,Style,ValueandAlias.replaceRefNodeWithContent(extension refs) wasn't reused because it always copies style, anchor and comments. That would change the rendered output of ordinary mapping refs.Why not #608
#608 adds a bounds check inside
walkAndRewriteRefs. That stops the panic but leaves the corrupted mapping node in place, and once the node is built correctly the guard can never trigger.Note:
$ref'dtagsare still not in the outputAfter this fix neither composed nor inline (
BundleBytes) bundling crashes, but neither emitstagsfortags: {$ref: ...}. This was already true for inline bundling onmain. The bundlers render from the model, andextractTags(datamodel/low/v3/create_document.go) only accepts a sequence node. OpenAPI 3.x does not allow a Reference Object fortags, so the model ignores it. Supporting it would mean resolving the ref inextractTags, while keeping the original key/value nodes sotagsstill renders in its original position. That's a separate decision, so it's not in this PR.Tests
bundler/issue607_test.go:TestBundleBytesComposed_Issue607_SequenceRefDoesNotPanic: 1, 2, 3, 63, 64 and 65 tags. The odd counts panic onmain.TestBundleDocumentComposed_Issue607_InlinedSequenceRefBecomesSequence: after composing, the roottagsnode is a real!!seqwith the three tags. An operation-leveltags: {$ref: ...}flow list renders as[a, b, c].TestInlineRefNode_Issue607: sequence, scalar, alias and mapping targets. The mapping case keeps its kind, tag, flow style and head comment.bundlercoverage: 100%../testsand the root package pass.🤖 Generated with Claude Code