github.com has no runner for hanzo-build-linux-amd64, so a caller under
.github/workflows is a gate that can never be scheduled. This is the ~7-line
caller on the plane that can (git.hanzo.ai git-runner fleet), pinned @v1.
Additional defense-in-depth in case consumers pass untrusted input into
Flight Client.
Flight Client generally assumes trusted input.
We'll reserve these kind of fixes for Flight Client in case the
untrusted input leads to catastrophic vulnerabilities e.g. prototype
pollutions that can be used for remote code executions.
## Summary
`FragmentInstance.blur()` only matched the active element against the
first level of host children. If the focused element was nested inside
one of those children, `focus()` could reach it but `blur()` would leave
it focused.
This treats an active element contained by a Fragment host child as part
of the Fragment and blurs the active element itself. It also adds
regression coverage for a nested input.
Fixes#37124.
## How did you test this change?
- `yarn test ReactDOMFragmentRefs-test --runInBand` (65 tests passed)
- `yarn test --prod ReactDOMFragmentRefs-test --runInBand` (65 tests
passed)
- `yarn prettier`
- `yarn linc`
- `yarn flow dom-node`
Closes https://github.com/vercel/next.js/issues/95848.
Fixes a hang: if an update changes what's inside a server-rendered
Suspense or Activity boundary before that boundary has hydrated, and the
affected content is hidden, React stops committing. The new content
renders once its data arrives, but the render is discarded every time,
nothing is scheduled, and nothing ever pings — the update never lands
and the app appears frozen.
"Hidden" means either of two things, and there's a test for each:
- the update itself hides a dehydrated `<Activity>` (while mounting new
sibling content that suspends), or
- the dehydrated boundary is inside the primary tree of a parent
boundary that just suspended and is showing its fallback.
This is how we found it in practice.
With https://github.com/vercel/next.js/pull/95682, pressing Back before
hydration finishes made the router replay the missed navigation from its
first effect. This worked fine outside of Cache Components, but in Cache
Components mode (which turns on Activity), the old page's Activity
(still dehydrated) gets hidden, the new page's content suspends inside
the layout's Suspense boundary, and after the data arrives the page
stays blank forever.
As a result, https://github.com/vercel/next.js/pull/95682 got reverted.
If we fix this, we can unrevert it.
## Why it happens
When an update changes a dehydrated boundary, we schedule a render at a
higher priority to hydrate it before the update applies. If we already
tried that, we give up and client render, but mark the render as
suspended so it doesn't commit while the hydration attempt might still
finish first.
Both steps assume the attempt can actually run. Inside a hidden tree it
can't, because updates in hidden trees are deferred until the tree is
revealed. The scheduled attempt never runs but still consumes the retry
lane, which sends every later render into the give-up path — and the
give-up path keeps discarding finished renders, waiting for a hydration
attempt that isn't in flight. Once the last piece of data resolves
there's nothing left to ping us awake. The root ends up with
`pendingLanes === suspendedLanes`, `pingedLanes` empty, and no callback
scheduled.
The update doesn't need to be sync or discrete: a plain setState from an
effect is enough. Wrapping the same update in `startTransition` avoids
it, which is probably why this went unnoticed.
## The fix
If the boundary is inside a hidden tree (`isCurrentTreeHidden()`), skip
the hydration attempt and client render right away. There's nothing
visible to protect: replacing hidden server HTML doesn't show, and the
replacement children render when the tree is revealed.
One behavior note: this discards the hidden server HTML instead of
preserving it for later hydration on reveal, same as the existing
give-up path. Keeping it dehydrated and hydrating at reveal would be a
nicer follow-up, but needs commit-phase support that doesn't exist
today.
## How did you test this change?
The first commit adds failing tests for both boundary types; the fix
makes them pass. `startTransition` variants of the same scenarios are
included as passing controls. Ran the Activity, partial/selective
hydration, Fizz, Suspense, and Offscreen suites in both release
channels.
## Summary
Migrate internal `Libraries/` import subpaths from `react-native` to the
incoming dedicated entry points in React Native 0.87.
**Motivation**
See [**RFC0894: Removing deep imports from
react-native**](https://github.com/react-native-community/discussions-and-proposals/pull/894)
**Changes**
The `react-private-interface` entry point has been created explicitly
for the private contract between React ↔ React Native, and is 1:1 with
the previous `ReactNativePrivateInterface` module.
Both of the below `Libraries/` paths still exist, but are deprecated —
this is a lagging migration that will enable cleanup in a future RN
version.
- `react-native/Libraries/Core/InitializeCore` →
`react-native/setup-env`
[react/react-native#57475](https://github.com/react/react-native/pull/57475)
- `react-native/Libraries/ReactPrivate/ReactNativePrivateInterface` →
`react-native/react-private-interface`
[react/react-native#57495](https://github.com/react/react-native/pull/57495)
## Test Plan
Flow
[Secure Ecmascript](https://github.com/tc39/proposal-ses) would freeze
the prototype of intrinsics. Since `ReactPromise` inherits the prototype
from `Promise`, it also copies over the writable definition.
Using `defineProperty` on an inherited property is compatible with SES
though. That's also closer to how classes are specced in JS.
---------
Co-authored-by: Hanzo Dev <[email protected]>
Stacked on #37112
In dev effects are validated using a double-invoke technique. To do this
the effects are destroyed and recreated during a validation traversal.
However this same disappear and reappear path is used when Offscreen
Fibers go hidden and HostSingletons have unique behavior when going
hidden. So now that the double invoke effects process happens during
hydration it is more common to have your Singletons be released and
acquired during this validation phase which is observable most notably
by having extra attributes removed from them. The prior commits in this
stack deal with preserving non-react owned attributes on release however
it is semantically incorrect to release and acquire the singleton during
this validation because it isn't really an effect, it simply lives in
this traversal to avoid having to do another traversal during the
commit.
This change adds a bit of info to the release and acquire path to only
conditionally perform the necessary reacquire flow if we are not in the
validation phase.
Removing attributes does not actually reset all property state on the
singleton instance. It also has the side effect of wiping any 3rd party
set attributes and properties that 3rd party scripts and extensions.
Reimplements the singleton release to clear properties on the instance
based on the last committed props for the singleton fiber.
Notably there is an unfixed path with preamble contribution markers that
still just clears based on attributes which covers SSR'd attributes that
need to be wiped before client recovery on hydration can continue. There
are gated failing tests for this as a TODO.
Buffers Bridge messages during extension port reconnects and adds a
readiness handshake for ordered queue flushing. Includes regression
coverage for reconnect delivery and listener cleanup.
Potential scenario could be a long user session, where Chrome kills one
of the extension ports to save resources and then user re-connects by
navigating back to the DevTools UI.
Builds on #37049 by validating Store operation invariants before
mutation. Missing nodes, invalid element types, inconsistent
parent-child relationships, and invalid reorder operations now emit and
throw explicit errors instead of silently continuing with corrupted
state.
Adds a canonical-render regression test for invalid child removal.
Builds on #37048 by replacing `any`-based Bridge and Wall boundaries
with typed `mixed` values and explicit runtime validation. Invalid
messages and post-shutdown operations now throw, while shutdown reliably
flushes queued messages even if cleanup fails.
Strengthens the DevTools Bridge and Wall contracts:
- Models event dictionaries as event-to-payload maps, using `void` for
events without payloads.
- Types `send(event, payload?)` directly, eliminating runtime
payload-arity handling.
- Replaces broad `any` transport types with `mixed` and boundary
validation.
- Throws on invalid lifecycle usage instead of warning or silently
returning.
- Ensures shutdown flushes queued messages even when Wall cleanup fails.
- Updates Wall implementations and adds Bridge lifecycle coverage.
Ensures the standalone DevTools Bridge fully shuts down when its
WebSocket closes, with re-entrancy protection. Adds tests confirming
event-only shutdown leaves the Bridge active while socket closure shuts
it down.
Tightens `EventEmitter` listener types and fixes error handling so the
first thrown value is preserved while subsequent errors are reported
instead of swallowed. Adds regression coverage for listener failures.
Turbopack has introduced a new format for chunks (experimental):
```typescript
type ChunkUrlOrMerged = ChunkUrl | [ChunkUrl, ChunkPath[], number[]]
```
This will allow us to dynamically choose to merge / unmerge chunks based
on already loaded chunks. See
https://github.com/vercel/next.js/pull/95261 for more context behind
these feature.
However, we noticed that this breaks `prepareDestinationWithChunks()` on
server-side renders. The prepared chunk script tags were a stringified
version of this array:
<img width="1758" height="474" alt="Screenshot 2026-07-22 at 9 37 06 am"
src="https://github.com/user-attachments/assets/fa273b44-5942-4037-b955-4338472242fe"
/>
Instead, we should prepare the merged chunk as these SSR-d pages would
not have any loaded chunks tracked in memory (that happens on the
client).
This PR brings back this optimisation and also would remove the
console-logged errors associated with them.
---------
Co-authored-by: Sebastian "Sebbie" Silbermann <[email protected]>
Fixes#27670.
When an Activity subtree is hidden, its passive effects are
disconnected, which unsubscribes from the store. On reveal, the effects
are reconnected by replaying the fiber's effect list without a render,
but if `updateStoreInstance` was not in the effect list then the
component would be left stale. This happened both when a layout effect
mutated the store during the reveal commit (after the subtree rendered
but before it resubscribed) and when the store changed while hidden and
the component bailed out of rendering during the reveal.
We now push the updateStoreInstance effect unconditionally but tag it
with HookHasEffect only under the same conditions as before, so regular
commits skip it when nothing changed but reconnection always triggers it
so it can trigger a rerender if appropriate.
e.g. `render(<Fragment ref={...}>{children}</Fragment>)` where
`children` contain no Host Components.
For `Document` we just noop because some part of the document is always
in view.
For `ShadowRoot` we use its `host`.
Any other `DocumentFragment` without a `host` issues a warning because
there's nothing to scroll. This matches the existing warning when you
call `scrollIntoView(false)` on an empty Fragment without siblings.
Notably this also applies to `<html><body /></html>` in `children`
because we skip `HostSingleton` at the moment.
---------
Co-authored-by: Hanzo Dev <[email protected]>
Adds SSR support for nested parentEnter/parentExit View Transitions.
Fizz now emits vt-parent-enter/vt-parent-exit annotations during
streaming, and the client picks them up on hydration, so nested
enter/exit animations work for Suspense reveals.
When a Fragment has no children, React would consider scrolling to
siblings first and then to parents. However, React only considered
`HostComponent` for the siblings.
Since we already have a heuristic for scrolling to `HostText`, we can
reuse that same heuristic.
---------
Co-authored-by: Hanzo Dev <[email protected]>
Ran into this test failure as part of
https://github.com/react/react/pull/36917 - it seems that the added code
was just enough to increase stack size and fail the deep tree recovery
test in CI. Looking into that, there appears to be a gap here with
retries, including a TODO test case for the scenario.
Fizz recovers from stack overflows in extremely deep trees by catching
the first overflow in the `renderNode` trampoline and spawning a
continuation task. That continuation is retried via `retryRenderTask →
retryNode`, which has no trampoline above it. So if the remaining tree
still doesn't fit in one fresh stack, the overflow was treated as a
fatal error instead of recovering again.
This fix re-schedules the task when a retried render overflows but
`task.node` advanced (proving forward progress was made). If this is a
real in-component overflow, `task.node` doesn't advance and we still
fail.
The existing test used `n={1000}`, which only required one recovery
round and didn't catch this gap in source mode. It's updated to
`n={1200}`, which reliably requires multiple recovery rounds.
We should provide a way for consumers to handle the installation on
their own, so the index entrypoint will just export the APIs and won't
have any side-effects.
The `/register` entrypoint (via `import
react-devtools-cdt-mcp/register`), will subscribe an event listener and
install the tools.
Adds support for the scenario, when the package is imported multiple
times for the same target.
This will return the same (referentially) facade object with tools, and
will only clean up if all invocations were cleaned up.
This add an additional validation that the package is imported in a
browser-like environment. If not, we should be explicit with the user
and throw an error.
## Summary
Microtasks are enabled by default in RN already, so there's no need to
gate their use in Fabric atm.
## How did you test this change?
Existing tests. Tested e2e at Meta.
We used to avoid updating .defaultValue until blur on number inputs
because of a Chrome bug, but the relevant Chromium behavior was fixed in
2020 in M85 https://issues.chromium.org/issues/40124871 and likewise in
WebKit https://bugs.webkit.org/show_bug.cgi?id=217156 so I think we can
probably just remove this special-cased logic.
We do still need some special logic for number inputs because it's the
only type where we let users specify a non-string value that has
multiple string representations and need to be careful not to clobber
the .value if it has a slightly different string than what you'd get
from casting a number to string.
Fixes#29862.
Tested with the number inputs fixture in current Chrome, Safari, and
Firefox.
Two bugs:
- Type changes to a lazy's inner component (e.g., adding a `memo`
comparison function) were missed by the refresh scan.
- If a SimpleMemoComponent needs to be remounted, previously
resolveRemountTypeForHotReloading would return the inner function
and we would incorrectly recreate the fiber as a FunctionComponent.
Instead, we can just use elementType consistently now that the
beginWork functions consistently call resolveFamily on the wrapped
components.
Editing the second argument of memo() previously never took effect
until something else remounted the tree, for two separate reasons:
- When adding a comparison function, we need to switch from
SimpleMemoComponent to MemoComponent so canPreserveStateBetween
should return false.
- MemoComponent was missing from the hot reload type resolution in
createWorkInProgress, so existing fibers kept reading .compare from
the old memo object forever.
New behavior:
- Adding or removing the comparison function remounts; we need to do
this when adding (SimpleMemoComponent doesn't support a comparison
function) so let's also do it when removing.
- Editing the comparison function implementation applies in place
with state preserved
The TODO in isCompatibleFamilyForHotReloading is removed as that
wasn't the right place to do this check.
If an edit changed the kind of the type (e.g. a plain function got
wrapped in memo()), the fiber could get the old kind's tag with the
new kind's type, usually throwing an error.
Previously resolveForwardRefForHotReloading was treated as a special
case. It's simpler to move this logic to updateForwardRef which
mirrors how updateMemoComponent and mountLazyComponent already are,
and it allows us to unify to a single resolveTypeForHotReloading which
we'll use in the next commit.
Since .type is no longer changing we need to set didReceiveUpdate
manually.
This behavior is already tested in "can update forwardRef render
function in isolation".
Same as #35227, but for `set`.
V8's `stack` setter silently noops when `this` is a Proxy
(nodejs/node#60862), so prodstack-stripping assignments like
`error.stack = 'Error: ' + message` in `resolveErrorProd` were dropped
and reads returned the real stack.
This caused `yarn test --prod` failures on Node 21+.
## Test Plan
`yarn test --prod` passes on both old and new Nodes.
Co-authored-by: Hanzo Dev <[email protected]>
Claude found this while running the Flight/Fizz bench.
---
While measuring memory in this fixture, the Flight server appeared to
leak ~140KB per render: `heapUsed` grew steadily across hundreds of
renders even with forced GC, and roughly 3x worse when a Flight client
consumed the stream in the same process. Fizz-only runs were flat, so it
looked like a real Flight leak.
On a closer look, the benchmark loop seems to be lying. React schedules
one `setImmediate` per request, but every render in this fixture
completes entirely in promises and `nextTick` callbacks, so a tight
benchmark loop never gives the event loop a chance to run immediates at
all. They quietly pile up, and each pending callback keeps its
already-finished request alive.
Heap snapshots show that every "leaked" request was held by a pending
immediate, and nothing else. Adding a single `await setImmediate` per
iteration makes memory completely flat over 300 renders, with or without
a same-process Flight client. A real server yields to the event loop on
every request, so this can't happen outside a synthetic loop.
The fix is to yield to the event loop between benchmark iterations,
outside the timed window, so latency numbers are unaffected. Each
variant now also reports how much heap it retains after the run settles.
That number was meaningless before — the pile-up made variants with
bigger per-request graphs look like they used more memory when they
didn't.
After the fix, a full `yarn bench:bare` run retains +0.3–0.6MB total per
variant after 1000 iterations (JIT/warmup noise), instead of drifting by
hundreds of MB.
---------
Co-authored-by: Hanzo Dev <[email protected]>
## Summary
Newer V8 versions (Node.js ≥ ~22) name the stack frames of static
methods on the `Promise` constructor `Promise.all`, `Promise.race`,
etc., where older versions named them `Function.all`, `Function.race`,
etc. The `isPromiseCreationInternal` and `isPromiseAwaitInternal`
allowlists in `ReactFlightServer` only matched the old spellings.
## How did you test this change?
- `yarn test --no-watchman ReactFlightAsyncDebugInfo` — 18/18 pass on
both Node 20.19.0 and Node 24.16.0 (previously, "can track async
information when awaited" failed on Node 24)
- `yarn test --silent --no-watchman packages/react-server/src/__tests__
packages/react-server-dom-webpack` — 243/243 pass on both Node versions,
default and `-r=experimental` channels
- `yarn test-www --silent --no-watchman
packages/react-server/src/__tests__` with `__VARIANT__` true and false —
pass
- `yarn flow dom-node`, `yarn prettier`, `yarn linc` — pass
Co-authored-by: Hanzo Dev <[email protected]>
## Summary
The compiler silently rewrote functions containing `using` and `await
using` declarations (explicit resource management). BuildHIR's
`VariableDeclaration` case only special-cases `var`; every other kind
falls through to `InstructionKind.Const`. A `using` declaration
therefore compiled as a plain memoized `const`, and the implicit dispose
call at scope exit was dropped from the compiled output. No diagnostic
was emitted. The Rust port mirrored the same bug in `build_hir.rs`.
The Rust port had a second, harder failure: its
`VariableDeclarationKind` enum had no `await using` variant, so a Babel
AST containing one failed serde deserialization at the NAPI boundary
("unknown variant `await using`") and threw for the whole file.
The fix records the same per-function Todo bail as the existing `var`
case, in both implementations, then continues lowering the declaration
as `const` so references do not break while the error unwinds (mirroring
how `var` continues as `let`). This matches the compiler's established
Todo-bail precedent for unsupported syntax: the function is skipped with
a logged `CompileError` diagnostic, `panicThreshold` is respected, and
sibling functions in the same file still compile. This deliberately
contrasts with oxc-project/oxc#24217, which handles the same syntax with
a silent per-function skip; nothing in this compiler is skipped without
a surfaced diagnostic.
Changes:
- TS: `BuildHIR.ts` records a Todo `CompilerErrorDetail` for `using` and
`await using` kinds, in the style of the existing `var` case.
- Rust: same Todo `record_error` in `build_hir.rs`. New `AwaitUsing`
variant (serde name `"await using"`) in `react_compiler_ast`, so `await
using` survives the NAPI boundary, covered by a serde round-trip unit
test.
- Test harness: enables the `explicitResourceManagement` parser plugin
in snap and in the e2e script's Babel baseline so fixtures can use the
syntax.
Fixtures (identical snapshots for the TS and Rust pipelines):
- `error.todo-using-declaration` and
`error.todo-await-using-declaration` show the Todo diagnostic with code
frames.
- `using-declaration-bailout-sibling-compiles` shows the bailing
function left untouched (disposal preserved) while a sibling component
in the same file is memoized.
The first commit snapshots the previous broken behavior (`using`
memoized as `const` with disposal dropped); the second commit lands the
fix and updates the snapshots.
## How did you test this change?
All results below are from the branch rebased onto current main.
TS implementation:
- `yarn snap`: 1809 tests, 1809 passed, 0 failed.
- `yarn workspace babel-plugin-react-compiler lint`: clean. `yarn
prettier-check` at the repo root: clean.
- Manually verified pre-fix output: a component with `using resource =
getResource(props.id)` compiled to a memoized `const` with no disposal.
Rust implementation:
- `cargo test --workspace`: 42 passed, 0 failed (includes the new serde
round-trip unit test for both `using` kinds).
- `bash scripts/test-babel-ast.sh`: passed.
- `bash scripts/test-rust-port.sh`: 1808 passed, 0 failed.
- `yarn snap --rust`: 1809 tests, 1809 passed, 0 failed, against the
same `.expect.md` snapshots as the TS run.
- `scripts/test-e2e.ts` (babel variant): the three using fixtures pass
code and events parity.
- Manually verified `await using` now deserializes across the NAPI
boundary and records the Todo instead of throwing.
<div><a
href="https://cursor.com/agents/bc-5fe4350d-5d42-4c0e-9001-429ed0b5e5b7"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a
href="https://cursor.com/background-agent?bcId=bc-5fe4350d-5d42-4c0e-9001-429ed0b5e5b7"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
---------
Co-authored-by: Hanzo Dev <[email protected]>
Fixes#30659. I'm not confident in this yet but here's what Fable said
based on
https://github.com/react/react/pull/32214#issuecomment-2734436781:
Editing a component from a plain function to a memo or forwardRef
wrapper (or between wrapper kinds) crashed with "Component is not a
function", because canPreserveStateBetween only compared hook
signatures. The edit was classified as state-preserving, so
createWorkInProgress swapped the wrapper object in as the type of a
FunctionComponent fiber and renderWithHooks tried to call it.
Two fixes, both DEV-only:
- canPreserveStateBetween returns false when typeof or $$typeof differ.
A fiber's tag is derived from the kind of its type, so state can never
be preserved across a kind change; these edits must go to staleFamilies
(remount). Nested wrappers need no special handling because register()
creates a family per nesting level ($type/$render ids), and each level's
kind is compared against its own family.
- The reconciler now finds and remounts wrapper fibers whose outer kind
changed. scheduleFibersWithFamiliesRecursively previously resolved
families only through the inner implementation (type/type.render), but a
kind-changing edit is recorded only on the outer type's family, so edits
like memo -> function never reached the scanner and were silently
dropped. The scanner now also checks the elementType's family, and the
_debugNeedsRemount branch in beginWork rebuilds the fiber from
family.current (the latest registered type for the fiber's identity)
rather than the fiber's possibly-stale inner type. This also fixes stale
simple memo remounts recreating as plain functions, dropping the memo
wrapper.
Unlike the reverted #30660 (see #32214), this does not fabricate
families or guess type shapes: families are still created in exactly one
place, and the same ID always resolves to the same family.
Restores the tests from #30660 and adds coverage for changing the inner
type of a memo between function and forwardRef.
Co-authored-by: BIKI DAS <[email protected]>
Co-authored-by: dan <[email protected]>
## Summary
Fixes#36601
### Problem
In `lowerJsxElementName` (BuildHIR.ts), the condition `if
(tag.match(/^[A-Z]/))` only treats JSX tags starting with an **uppercase
letter** as component references. Tags starting with `_`, `$`, or any
other non-letter character fell through to the `else` branch and were
incorrectly classified as `BuiltinTag` (host/intrinsic elements).
This meant that `<_Bar />` or `<$Foo />` was treated like `<div />`,
causing the compiler to skip memoization of the component and
potentially producing incorrect output.
### Root Cause
JSX semantics (as implemented by Babel's JSX transform) are:
- Tag starts with **lowercase** letter → host/intrinsic element (string
tag)
- **Everything else** → component reference (in-scope identifier)
The original code only handled the first half of that rule ("starts with
uppercase → component") while ignoring identifiers like `_Bar` and
`$Foo`.
### Fix
Change:
```ts
if (tag.match(/^[A-Z]/)) {
```
To:
```ts
if (!tag.match(/^[a-z]/)) {
```
This correctly classifies any JSX identifier that does NOT start with a
lowercase letter as a component reference, matching JSX spec semantics.
### Test
Added fixture `jsx-underscore-prefix-component` that renders `<_Bar />`
and verifies the compiler correctly memoizes it as a component
reference.
## How did you test this change?
- Added new compiler fixture test: `jsx-underscore-prefix-component`
- Ran `yarn workspace babel-plugin-react-compiler lint` ✅
- Ran snap tests with the new fixture to generate expected output ✅
## Summary
#34493 added `@tanstack/react-virtual`'s `useVirtualizer` to the known
incompatible libraries, but not `useWindowVirtualizer`.
Both hooks are thin wrappers around the same internal
`useVirtualizerBase`, so they return the same referentially-stable
virtualizer instance. Its methods (e.g. `getVirtualItems()`,
`getTotalSize()`) return internally-mutated values rather than new ones,
which is exactly the "interior mutability" pattern that breaks
memoization described in the module type provider.
Because only `useVirtualizer` was registered, code using
`useWindowVirtualizer` is silently compiled with the same unsafe
memoization. In our app this froze `getVirtualItems()` to its
first-render value (an empty `[]` before measurement), producing
permanently-empty virtualized lists — the same class of bug #34493
fixed, just via the window variant.
This registers `useWindowVirtualizer` alongside `useVirtualizer` with
the identical shape, in **both** implementations:
-
`compiler/packages/babel-plugin-react-compiler/src/HIR/DefaultModuleTypeProvider.ts`
-
`compiler/crates/react_compiler_hir/src/default_module_type_provider.rs`
(Rust port)
See also the community report referenced in #34493:
https://github.com/TanStack/virtual/issues/736#issuecomment-3065658277
## How did you test this change?
Each addition mirrors the existing `useVirtualizer` entry exactly (same
kind, params, return type, and `knownIncompatible` message shape); both
are data-only additions to the module type providers, following the
precedent set by #34493 which changed only the TS file. The Rust change
keeps the two providers in sync.