Skip to content

fix: never lose nodes the parser relocates behind the walk frontier - #22

Merged
aralroca merged 3 commits into
mainfrom
fix/stream-walker-robustness
Jul 24, 2026
Merged

fix: never lose nodes the parser relocates behind the walk frontier#22
aralroca merged 3 commits into
mainfrom
fix/stream-walker-robustness

Conversation

@aralroca

Copy link
Copy Markdown
Collaborator

Failing tests first (previous commit), then the fix. Three related walker bugs:

  1. shouldIgnoreNode descended into an ignored first child. The skip loop hopped by the walk field (firstChild), so an ignored node's content was walked as if it were the parent's child list — losing every following sibling. It now hops by nextSibling. (Likely related to Web Component not loading when navigating with SPA brisa#739-class reports.)

  2. Chunk-boundary loss via parser reordering (the "hard to reproduce in a test" case). The HTML parser can relocate a late chunk's nodes before positions the walker already passed — e.g. table foster parenting: …<tr><td>y</td></tr> in chunk 1, stray text in chunk 2 gets fostered before the <table>. The streamed walk can't revisit, so the node was silently dropped. diff() now runs one reconciliation pass over the settled document when the stream completes: no waits, onNextNode not replayed, transitions not re-fired — and on converged trees it's a pure no-op (zero mutations).

  3. setAttributes stole Attr nodes from the streamed tree (setNamedItemNS moves the attribute), so any later comparison against the new tree saw those attributes as missing and removed them from the live DOM — the settled pass surfaced this immediately. New attributes are now cloned.

Verification beyond the two new suite tests:

  • Byte-level split sweep: a realistic page pair diffed at all 760 two-chunk boundaries plus 36 chunk-size/phase/delay combos over real 18–45KB pages — zero divergences from the single-chunk result.
  • In-situ: Janux docs navigating via SPA through a 256B/10ms chunking proxy with the fetch buffer disabled, 8/8 clean navigations against this branch.
  • Bundle: 1264B gzip (bundlewatch max 1.5KB).

Also: the "Chrome View Transitions API" describe now launches Chrome in beforeAll — bun ≥1.3 fails tests whose hooks throw (1.2.9 tolerated the lazily-assigned browser), so the suite couldn't run locally on modern bun at all.

Known pre-existing (untouched): tsc --noEmit error in the test harness (eval(onNextNode) with possibly-undefined arg).

🤖 Generated with Claude Code

Aral Roca added 3 commits July 24, 2026 16:49
…nted node across chunks

Also: launch Chrome in beforeAll for the View Transitions describe (bun >=1.3
fails tests whose hooks throw; 1.2.9 swallowed the undefined browser).
…skip ignored nodes by sibling

Three related walker fixes:

- shouldIgnoreNode hopped by the walk field, so an ignored FIRST child made
  the walker descend into the ignored node's subtree instead of skipping to
  its sibling.
- The HTML parser can move a late chunk's nodes BEFORE positions the walker
  already passed (table foster parenting): diff() now runs one settled
  reconciliation pass over the fully-parsed document (no waits, callbacks
  not replayed) so those nodes always land. Converged trees make it a no-op.
- setAttributes stole Attr nodes from the streamed tree (setNamedItemNS
  moves them), which made the settled pass see them as removed; new
  attributes are cloned instead.
An unconditional second pass fought DOM mutations made between insertion and
stream end by the page's own code (custom element connectedCallback, scripts).
The stream walker now tracks visited nodes (clone-inserted subtrees count as
walked) and the reconciliation only runs when the settled document contains
nodes the walk never saw — fully-walked pages skip it entirely.
@aralroca
aralroca merged commit 3e2d319 into main Jul 24, 2026
1 check passed
@aralroca
aralroca deleted the fix/stream-walker-robustness branch July 24, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant