Skip to content

fix(renderer): preserve hydration range topology - #161

Merged
smiggleworth merged 9 commits into
mainfrom
fix/hydration-range-integration
Jul 27, 2026
Merged

fix(renderer): preserve hydration range topology#161
smiggleworth merged 9 commits into
mainfrom
fix/hydration-range-integration

Conversation

@smiggleworth

@smiggleworth smiggleworth commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • adopt keyed component rows in mixed-parent hydration without claiming a preceding static sibling
  • keep component-returned arrays and context-scope children structurally transparent across SPA, SSR, SSG, hydration, updates, rollback, and cleanup
  • preserve automatic DefaultPortal host parity across SSG and hydration, including closed nested Sheet content
  • advance hydration correctly across empty and populated Show boundary sequences so following components are adopted once
  • give each context scope a distinct renderer identity while retaining its symbol for context lookup
  • prepare @askrjs/askr@0.0.78 and align the rendering/versioned recipe docs

Red to green

  • keyed For after a static Link: hydration previously grew 8 anchors to 15; now stays at 8 while the direct-For control stays at 7, with server identity preserved through reorder/removal
  • component arrays/context scopes: hydration previously introduced a visible keyed wrapper; regressions now preserve direct-child topology, focus, selection, identity, portal updates, rollback, and cleanup
  • nested closed Sheet: hydration previously replaced the static label and created client-only automatic portal hosts; SSG now emits matching host topology and preserves identity through open, close, reopen, rollback, and cleanup
  • consecutive empty Show boundaries: hydration previously duplicated the following component and lost its server identity; the cursor now advances once per empty boundary and preserves the tail through open/close/reopen
  • mixed false/true Show boundaries: the populated boundary previously materialized before the live cursor and duplicated the following component; hydration now adopts a shape-compatible server element without reusing state across branch replacements
  • sibling scope providers: private symbol stringification previously collapsed their renderer identity; scopes now receive distinct stable numeric identities and update independently

Validation

  • combined focused regressions: 13 files, 161 tests
  • npm run check: 249 jsdom files / 1,275 tests; 13 browser files / 40 tests; 20 unit files / 151 tests; all formatting, lint, type, build, publint, checks, installed-package, and packed-artifact gates
  • npm run bench: tiers 1-4, no budget failures
  • exact @askrjs/askr@0.0.78 tarball SHA-256 76aac85462a387885a8504d513fd93305f3928162af39e4363dfb37c340a81b5
  • exact website consumer proof: package install/version, docs/client/SSG focused 5/5, and strict hydration verification 338/338
  • mobile docs Sheet at 390x844: unique trigger and dialog, aria-expanded false → true → false, complete Sheet teardown, restored sibling topology, no renderer wrappers, and zero console errors

Fixes #159
Fixes #160
Fixes #162
Fixes #163
Fixes #164

Copilot AI review requested due to automatic review settings July 27, 2026 19:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes hydration topology/identity issues in the renderer by (1) correctly adopting keyed For rows even when preceded by static siblings, and (2) keeping component-returned arrays (notably context scopes) structurally transparent across SSR/SSG + hydration and subsequent updates/rollback/cleanup. It also updates the docs and bumps the package version to prepare @askrjs/askr@0.0.78.

Changes:

  • Add jsdom regressions covering keyed For adoption after static siblings during hydration, and transparent hydration for context-scope/component-returned arrays (including portal host behavior, focus/selection preservation, and rollback).
  • Update renderer hydration/commit logic to treat arrays and marked “transparent component results” as range-based (no visible wrapper host) and to adopt keyed elements produced by component vnodes during mixed-parent hydration.
  • Bump package version to 0.0.78 and align rendering/recipe docs with the new hydration/topology guarantees.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/jsdom/ssr/for-static-sibling-hydration.test.tsx Regression ensuring keyed For adopts only its server range after a preceding static sibling and preserves identity through updates.
tests/jsdom/ssr/component-array-hydration.test.tsx Regression ensuring context-scope/component-array hydration stays wrapper-free and preserves identity/focus across portal updates.
tests/jsdom/dom/component-array-structure.test.tsx SPA-side structure, focus/selection preservation, and rollback coverage for component-returned arrays.
src/runtime/context.ts Marks defineScope’s internal provider component as “transparent result” to enable wrapper-free hydration/updates.
src/common/control.ts Introduces a durable marker API for “transparent component results” (markTransparentComponentResult / hasTransparentComponentResult).
src/renderer/child-shape.ts Centralizes “transparent component result” and “transparent range result” detection (arrays/fragments/control boundaries/marked components).
src/renderer/component-host-results.ts Avoids materializing wrapper hosts when component results are transparent (including arrays). Adds nested-result resolution for host-based reuse.
src/renderer/component-range-commit.ts Enhances range replacement to resolve and sync nested transparent component results during commit/hydration.
src/renderer/component-fragment-range.ts Extends fragment-range syncing to array results and preserves automatic portal hosts during staging moves.
src/renderer/component-host-instances.ts Improves stable instance resolution for unkeyed multi-node ranges moving across transparent wrapper owners.
src/renderer/boundary-range-adoption.ts Adds canAdoptHydratedElement to allow keyed hydration adoption for component vnodes.
src/renderer/boundary-range-sync.ts Uses canAdoptHydratedElement when adopting keyed rows in mixed-parent hydration.
docs/core/rendering.md Updates documentation to describe “transparent component ranges” for fragments and arrays; documents keyed For adoption behavior.
docs/guides/platform-recipes.md Updates documented version contract to @askrjs/askr@0.0.78.
package.json Bumps package version to 0.0.78.
package-lock.json Aligns lockfile version metadata to 0.0.78.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated 1 comment.

Comment thread src/runtime/context.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated 1 comment.

Comment thread src/renderer/component-host-results.ts
@smiggleworth

Copy link
Copy Markdown
Contributor Author

Resolved follow-up for open review thread: commit f1bf8f9 now includes the hydrated instance in in (it now uses ). This should address unresolved Copilot thread on src/renderer/component-host-results.ts. PR still has other open review items in the current history.

@smiggleworth

Copy link
Copy Markdown
Contributor Author

Resolved the open review item on component-host-results.ts. Commit f1bf8f9 updates adoptEmptySSRPortalHydrationHost so __ASKR_INSTANCES now always includes the hydrated owner instance (set of [instance, ...retainedInstances]). This should address the unresolved Copilot thread.

@smiggleworth
smiggleworth merged commit 80ab65d into main Jul 27, 2026
3 checks passed
@smiggleworth
smiggleworth deleted the fix/hydration-range-integration branch July 27, 2026 20:53
@smiggleworth
smiggleworth requested a review from Copilot July 27, 2026 21:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 28 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment