Commit Graph

313 Commits

Author SHA1 Message Date
Hendrik Liebau
0d721b60c2
[Flight] Don't hang after resolving cyclic references (#34988) 2025-10-27 22:06:28 +01:00
Sebastian Markbåge
2cfb221937
[Flight] Allow passing DEV only startTime as an option (#34912)
When you use the `createFromFetch` API we assume that the start time of
the request is the same time as when you call `createFromFetch` but in
principle you could use it with a Promise that starts earlier and just
happens to resolve to a `Response`.

When you use `createFromReadableStream` that is almost definitely the
case. E.g. you might have started it way earlier and you don't call
`createFromReadableStream` until you get the headers back (the fetch
promise resolves).

This adds an option to pass in the start time for debug purposes if you
started the request before starting to parse it.
2025-10-19 16:38:33 -04:00
Hendrik Liebau
dc485c7303
[Flight] Fix detached ArrayBuffer error when streaming typed arrays (#34849)
Using `renderToReadableStream` in Node.js with binary data from
`fs.readFileSync` (or `Buffer.allocUnsafe`) could cause downstream
consumers (like compression middleware) to fail with "Cannot perform
Construct on a detached ArrayBuffer".

The issue occurs because Node.js uses an 8192-byte Buffer pool for small
allocations (< 4KB). When React's `VIEW_SIZE` was 2KB, files between
~2KB and 4KB would be passed through as views of pooled buffers rather
than copied into `currentView`. ByteStreams (`type: 'bytes'`) detach
ArrayBuffers during transfer, which corrupts the shared Buffer pool and
causes subsequent Buffer operations to fail.

Increasing `VIEW_SIZE` from 2KB to 4KB ensures all chunks smaller than
4KB are copied into `currentView` (which uses a dedicated 4KB buffer
outside the pool), while chunks 4KB or larger don't use the pool anyway.
Thus no pooled buffers are ever exposed to ByteStream detachment.

This adds 2KB memory per active stream, copies chunks in the 2-4KB range
instead of passing them as views (small CPU cost), and buffers up to 2KB
more data before flushing. However, it avoids duplicating large binary
data (which copying everything would require, like the Edge entry point
currently does in `typedArrayToBinaryChunk`).

Related issues:

- https://github.com/vercel/next.js/issues/84753
- https://github.com/vercel/next.js/issues/84858
2025-10-17 22:13:52 +02:00
Sebastian Markbåge
56e846921d
[Flight] Exclude RSC Stream if the stream resolves in a task (#34838) 2025-10-14 14:28:47 +02:00
Hendrik Liebau
ead92181bd
[Flight] Avoid unnecessary indirection when serializing debug info (#34797)
Some checks failed
(Compiler) Playground / Test playground (push) Has been cancelled
(Compiler) TypeScript / Discover yarn workspaces (push) Has been cancelled
(Compiler) TypeScript / Lint babel-plugin-react-compiler (push) Has been cancelled
(Compiler) TypeScript / Jest babel-plugin-react-compiler (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (6) (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (7) (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (8) (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (9) (push) Has been cancelled
(Runtime) Fuzz tests / test_fuzz (push) Has been cancelled
(Shared) Lint / Run prettier (push) Has been cancelled
(Shared) Lint / Run eslint (push) Has been cancelled
(Shared) Lint / Check license (push) Has been cancelled
(Shared) Lint / Test print warnings (push) Has been cancelled
(Compiler) TypeScript / Test ${{ matrix.workspace_name }} (push) Has been cancelled
(Runtime) Publish Prereleases Nightly / Publish to Canary channel (push) Has been cancelled
(Compiler) Publish Prereleases Nightly / Publish to Experimental channel (push) Has been cancelled
(Runtime) Publish Prereleases Nightly / Publish to Experimental channel (push) Has been cancelled
When a debug channel is hooked up, and we're serializing debug models,
if the result is an already outlined reference, we can emit it directly,
without also outlining the reference. This would create an unnecessary
indirection.

Before:

```
:N1760023808330.2688
0:D"$2"
0:D"$3"
0:D"$4"
0:"hi"

1:{"name":"Component","key":null,"env":"Server","stack":[],"props":{}}
2:{"time":3.0989999999999327}
3:"$1"
4:{"time":3.261792000000014}
```

After:

```
:N1760023786873.8916
0:D"$2"
0:D"$1"
0:D"$3"
0:"hi"

1:{"name":"Component","key":null,"env":"Server","stack":[],"props":{}}
2:{"time":2.4145829999999933}
3:{"time":2.5488749999999527}
```

Notice how the second debug info chunk is now directly referencing chunk
`1` in the debug channel, without outlining and referencing `"$1"` as
its own debug chunk `3`.

This not only simplifies the RSC payload, and reduces overhead. But more
importantly it helps the client resolve cyclic references when a model
has debug info that has a reference back to the model. The client is
currently not able to resolve such a cycle when those chunk indirections
are involved. Ideally, it would also be able to resolve them regardless,
but that requires more work. In the meantime, this fixes an immediate
issue.
2025-10-10 21:44:28 +02:00
Hendrik Liebau
d44659744f
[Flight] Fix preload as attribute for stylesheets (#34760)
Follow-up to #34604. For a stylesheet, we need to render `<link
rel="preload" as="style" ...>`, and not `<link rel="preload"
as="stylesheet" ...>`.
([ref](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload#what_types_of_content_can_be_preloaded))

fixes vercel/next.js#84569
2025-10-10 21:40:56 +02:00
Sebastian "Sebbie" Silbermann
79ca5ae855
Bump next prerelease version numbers (#34674) 2025-10-02 00:31:55 +02:00
Sebastian "Sebbie" Silbermann
1bd1f01f2a
Ship partial-prerendering APIs to Canary (#34633) 2025-10-01 18:22:30 +02:00
Sebastian "Sebbie" Silbermann
5667a41fe4
Bump next prerelease version numbers (#34639) 2025-10-01 15:15:24 +02:00
Ruslan Lesiutin
c552618a82
flags: make enableAsyncDebugInfo dynamic for www (#34430)
As titled. This adds dev-only debugging information to Fizz / Flight
that could be used for tracking Promise's stack traces in "suspended by"
section of DevTools.
2025-09-26 11:43:03 -07:00
Sebastian Markbåge
047715c4ba
[Flight] Preload <img> and <link> using hints before they're rendered (#34604)
In Fizz and Fiber we emit hints for suspensey images and CSS as soon as
we discover them during render. At the beginning of the stream. This
adds a similar capability when a Host Component is known to be a Host
Component during the Flight render.

The client doesn't know that these resources are in the payload until it
parses that particular component which is lazy. So they need to be
hoisted with hints. We detect when these are rendered during Flight and
add them as hints. That allows you to consume a Flight payload to
preload prefetched content without having to render it.

`<link rel="preload">` can be hoisted more or less as is.

`<link rel="stylesheet">` we preload but we don't actually insert them
anywhere until they're rendered. We do these even for non-suspensey
stylesheets since we know that when they're rendered they're going to
start loading even if they're not immediately used. They're never lazy.

`<img src>` we only preload if they follow the suspensey image pattern
since otherwise they may be more lazy e.g. by if they're in the
viewport. We also skip if they're known to be inside `<picture>`. Same
as Fizz. Ideally this would preload the other `<source>` but it's
tricky.

The downside of this is that you might conditionally render something in
only one branch given a client component. However, in that case you're
already eagerly fetching the server component's data in that branch so
it's not too much of a stretch that you want to eagerly fetch the
corresponding resources as well. If you wanted it to be lazy, you
should've done a lazy fetch of the RSC.

We don't collect hints when any of these are wrapped in a Client
Component. In those cases you might want to add your own preload to a
wrapper Shared Component.

Everything is skipped if it's known to be inside `<noscript>`.

Note that the format context is approximate (see #34601) so it's
possible for these hints to overfetch or underfetch if you try to trick
it. E.g. by rendering Server Components inside a Client Component that
renders `<noscript>`.

---------

Co-authored-by: Josh Story <josh.c.story@gmail.com>
2025-09-25 23:44:14 -04:00
Hendrik Liebau
ac2c1a5a58
[Flight] Ensure blocked debug info is handled properly (#34524)
This PR ensures that server components are reliably included in the
DevTools component tree, even if debug info is received delayed, e.g.
when using a debug channel. The fix consists of three parts:

- We must not unset the debug chunk before all debug info entries are
resolved.
- We must ensure that the "RSC Stream" IO debug info entry is pushed
last, after all other entries were resolved.
- We need to transfer the debug info from blocked element chunks onto
the lazy node and the element.

Ideally, we wouldn't even create a lazy node for blocked elements that
are at the root of the JSON payload, because that would basically wrap a
lazy in a lazy. This optimization that ensures that everything around
the blocked element can proceed is only needed for nested elements.
However, we also need it for resolving deduped references in blocked
root elements, unless we adapt that logic, which would be a bigger lift.

When reloading the Flight fixture, the component tree is now displayed
deterministically. Previously, it would sometimes omit synchronous
server components.

<img width="306" height="565" alt="complete"
src="https://github.com/user-attachments/assets/db61aa10-1816-43e6-9903-0e585190cdf1"
/>

---------

Co-authored-by: Sebastian Markbage <sebastian@calyptus.eu>
2025-09-25 15:13:15 +02:00
Sebastian Markbåge
969a9790ad
[Flight] Track I/O Entry for the RSC Stream itself (#34425)
One thing that can suspend is the downloading of the RSC stream itself.
This tracks an I/O entry for each Promise (`SomeChunk<T>`) that
represents the request to the RSC stream. As the value we use the
`Response` for `createFromFetch` (or the `ReadableStream` for
`createFromReadableStream`). The start time is when you called those.

Since we're not awaiting the whole stream, each I/O entry represents the
part of the stream up until it got unblocked. However, in a production
environment with TLS packets and buffering in practice the chunks
received by the client isn't exactly at the boundary of each row. It's a
bit longer into larger chunks. From testing, it seems like multiples of
16kb or 64kb uncompressed are common. To simulate a production
environment we group into roughly 64kb chunks if they happen in rapid
sequence. Note that this might be too small to give a good idea because
of the throttle many boundaries might be skipped anyway so this might
show too many.

The React DevTools will see each I/O entry as separate but dedupe if an
outer boundary already depends on the same chunk. This deduping makes it
so that small boundaries that are blocked on the same chunk, don't get
treated as having unique suspenders. If you have a boundary with large
content, then that content will likely be in a separate chunk which is
not in the parent and then it gets marked as.

This is all just an approximation. The goal of this is just to highlight
that very large boundaries will very likely suspend even if they don't
suspend on any I/O on the server. In practice, these boundaries can
float around a lot and it's really any Suspense boundary that might
suspend but some are more likely than others which this is meant to
highlight.

It also just lets you inspect how many bytes needs to be transferred
before you can show a particular part of the content, to give you an
idea that it's not just I/O on the server that might suspend.

If you don't use the debug channel it can be misleading since the data
in development mode stream will have a lot more data in it which leads
to more chunking.

Similarly to "client references" these I/O infos don't have an "env"
since it's the client that has the I/O and so those are excluded from
flushing in the Server performance tracks.

Note that currently the same Response can appear many times in the same
Instance of SuspenseNode in DevTools when there are multiple chunks. In
a follow up I'll show only the last one per Response at any given level.

Note that when a separate debugChannel is used it has its own I/O entry
that's on the `_debugInfo` for the debug chunks in that channel.
However, if everything works correctly these should never leak into the
DevTools UI since they should never be propagated from a debug chunk to
the values waited by the runtime. This is easy to break though.
2025-09-09 16:46:11 -04:00
Hendrik Liebau
bb6f0c8d2f
[Flight] Fix wrong missing key warning when static child is blocked (#34350) 2025-09-01 11:03:57 +02:00
Hendrik Liebau
9c2e2b8475
[Flight] Don't drop debug info if there's only a readable debug channel (#34304)
When the Flight Client is waiting for pending debug chunks, it drops the
debug info if there is no writable side of the debug channel defined.
However, it should instead check if there's no readable side defined.

Fixing this is not only important for browser clients that don't want or
need a return channel, but it's also crucial for server-side rendering,
because the Node and Edge clients only accept a readable side of the
debug channel. So they can't even define a noop writable side as a
workaround.
2025-08-27 13:50:19 +02:00
Hendrik Liebau
cacc20e37c
[Flight] Wait for both streams to end before closing the response (#34301)
When a debug channel is defined, we must ensure that we don't close the
Flight Client's response when the debug channel's readable is done, but
the RSC stream is still flowing. Now, we wait for both streams to end
before closing the response.
2025-08-26 17:15:25 +02:00
Jan Kassens
df10309e2b
Update Flow to 0.279 (#34277)
Multiple of these version upgrades required minor additional
annotations.
2025-08-25 11:02:56 -04:00
Jan Kassens
d73b6f1110
Update Flow to 0.261 (#34255)
- 0.261 required to pull out a constant to preserve refinement
- 0.259 needed some updated suppressions for hacky stuff
2025-08-21 15:02:49 -04:00
Hendrik Liebau
0bc71e67ab
[Flight] Add debugChannel option to Edge and Node clients (#34236)
When a debug channel is used between the Flight server and a browser
Flight client, we want to allow the same RSC stream to be used for
server-side rendering. To support this, the Edge and Node Flight clients
also need to accept a `debugChannel` option. Without it, debug
information would be missing (e.g. for SSR error stacks), and in some
cases this could result in `Connection closed` errors.

This PR adds support for the `debugChannel` option in the Edge and Node
clients for ESM, Parcel, Turbopack, and Webpack. Unlike the browser
clients, these clients only support a one-way channel, since the Flight
server’s return protocol is not designed for multiple clients.

The implementation follows the approach used in the browser clients, but
excludes the writable parts.
2025-08-20 16:46:34 +02:00
Sebastian Markbåge
0c89b160f6
[Flight] Add DebugInfo for Bundler Chunks (#34226)
This adds a "suspended by" row for each chunk that is referenced from a
client reference. So when you select a client component, you can see
what bundles will block that client component when loading on the
client.

This is only done in the browser build since if we added it on the
server, it would show up as a blocking resource and while it's possible
we expect that a typical server request won't block on loading JS.

<img width="664" height="486" alt="Screenshot 2025-08-17 at 3 45 14 PM"
src="https://github.com/user-attachments/assets/b1f83445-2a4e-4470-9a20-7cd215ab0482"
/>

<img width="745" height="678" alt="Screenshot 2025-08-17 at 3 46 58 PM"
src="https://github.com/user-attachments/assets/3558eae1-cf34-4e11-9d0e-02ec076356a4"
/>

Currently this is only included if it ends up wrapped in a lazy like in
the typical type position of a Client Component, but there's a general
issue that maybe hard references need to transfer their debug info to
the parent which can transfer it to the Fiber.
2025-08-18 11:34:00 -04:00
Sebastian Markbåge
c499adf8c8
[Flight] Allow Temporary References to be awaited (#34084)
Fixes #33534.

`.then` method can be tested when you await a value that's not a
Promise. For regular Client References we have a way to mark those as
"async" and yield a reference to the unwrapped value in case it's a
Promise on the Client.

However, the realization is that we never serialize Promises as opaque
when passed from the client to the server. If a Promise is passed, then
it would've been deserialized as a Promise (while still registered as a
temporary reference) and not one of these Proxy objects.

Technically it could be a non-function value on the client which would
be wrong but you're not supposed to dot into it in the first place.

So we can just assume it's `undefined`.
2025-08-02 18:44:20 -04:00
Sebastian Markbåge
538ac7ae4b
[Flight] Fix debug info leaking to outer handler (#34081)
The `waitForReference` call for debug info can trigger inside a
different object's initializingHandler. In that case, we can get
confused by which one is the root object.

We have this special case to detect if the initializing handler's object
is `null` and we have an empty string key, then we should replace the
root object's value with the resolved value.


52612a7cbd/packages/react-client/src/ReactFlightClient.js (L1374)

However, if the initializing handler actually should have the value
`null` then we might get confused by this and replace it with the
resolved value from a debug object. This fixes it by just using a
non-empty string as the key for the waitForReference on debug value
since we're not going to use it anyway.

It used to be impossible to get into this state since a `null` value at
the root couldn't have any reference inside itself but now the debug
info for a `null` value can have outstanding references.

However, a better fix might be using a placeholder marker object instead
of null or better yet ensuring that we know which root we're
initializing in the debug model.
2025-08-01 15:44:48 -04:00
Josh Story
5a04619f60
[Flight] Properly close stream when no chunks need to be written after prerender (#33982)
There is an edge case when prerendering where if you have nothing to
write you can end up in a state where the prerender is in status closed
before you can provide a destination. In this case the destination is
never closed becuase it assumes it already would have been.

This condition can happen now because of the introduction of the deubg
stream. Before this a request would never entere closed status if there
was no active destination. When a destination was added it would perform
a flush and possibly close the stream. Now, it is possible to flush
without a destination because you might have debug chunks to stream and
you can end up closing the stream independent of an active destination.

There are a number of ways we can solve this but the one that seems to
adhere best to the original design is to only set the status to CLOSED
when a destination is active. This means that if you don't have an
active destination when the pendingChunks count hits zero it will not
enter CLOSED status until you startFlowing.
2025-07-24 19:38:31 -07:00
Sebastian Markbåge
28d4bc496b
[Flight] Make debug info and console log resolve in predictable order (#33665)
This resolves an outstanding issue where it was possible for debug info
and console logs to become out of order if they up blocked. E.g. by a
future reference or a client reference that hasn't loaded yet. Such as
if you console.log a client reference followed by one that doesn't. This
encodes the order similar to how the stream chunks work.

This also blocks the main chunk from resolving until the last debug info
has fully loaded, including future references and client references.
This also ensures that we could send some of that data in a different
stream, since then it can come out of order.
2025-07-19 20:13:26 -04:00
Sebastian Markbåge
eb7f8b42c9
[Flight] Add Separate Outgoing Debug Channel (#33754)
This lets us pass a writable on the server side and readable on the
client side to send debug info through a separate channel so that it
doesn't interfere with the main payload as much. The main payload refers
to chunks defined in the debug info which means it's still blocked on it
though. This ensures that the debug data has loaded by the time the
value is rendered so that the next step can forward the data.

This will be a bit fragile to race conditions until #33665 lands.
Another follow up needed is the ability to skip the debug channel on the
receiving side. Right now it'll block forever if you don't provide one
since we're blocking on the debug data.
2025-07-10 16:22:44 -04:00
Sebastian Markbåge
453a19a107
[Flight] Collect Debug Info from Rejections in Aborted Render (#33708)
This delays the abort by splitting the abort into a first step that just
flags a task as abort and tracks the time that we aborted. This first
step also invokes the `cacheSignal()` abort handler.

Then in a macrotask do we finish flushing the abort (or halt). This
ensures that any microtasks after the abort signal can finish flushing
which may emit rejections or fulfill (e.g. if you try/catch the abort or
if it was allSettled). These rejections are themselves signals for which
promise was blocked on what promise which forms a graph that we can use
for debug info. Notably this doesn't include any additional data in the
output since we don't include any data produced after the abort. It just
uses the additional execution to collect more debug info.

The abort itself might not have been spawned from I/O but it's still
interesting to mark Promises that aborted as interesting since they may
have been blocked on I/O. So we take the inner most Promise that
resolved after the end time (presumably due to the abort signal but also
could've just finished after but that's still after the abort).

Since the microtasks can spawn new Promises after the ones that reject
we ignore any of those that started after the abort.
2025-07-05 17:01:41 -04:00
Sebastian Markbåge
3cfcdfb307
[Flight] Resolve Deep Cycles (#33664)
Stacked on #33666.

If we ever get a future reference to a cycle and that reference gets
eagerly parsed before the target has loaded then we can end up with a
cycle that never gets resolved. That's because our cycle resolution only
works if the cyclic future reference is created synchronously within the
parsing path of the child.

I haven't been able to construct a normal scenario where this would
break. So this doesn't fail any tests. However, I can construct it with
debug info since those are eagerly evaluated. It's also a prerequisite
if the debug data can come out of order, like if it's on a different
stream.

The fix here is to make all the internal dependencies in the "listener"
list into introspectable objects instead of closures. That way we can
traverse the list of dependencies of a blocked reference to see if it
ends up in a cycle and therefore skip the reference.

It would be nice to address this once and for all to be more resilient
to server changes, but I'm not sure if it's worth this complexity and
the extra CPU cost of tracing the dependencies. Especially if it's just
for debug data.

closes #32316
fixes vercel/next.js#72104

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
2025-06-29 10:56:16 -04:00
Sebastian Markbåge
9c2a8dd5f8
[Flight] Ensure we dedupe references if we later discover that it's the model root (#33666)
I noticed we weren't deduping these cases.
2025-06-29 10:47:33 -04:00
Sebastian Markbåge
bfc8801e0f
[Flight] Write Debug Info to Separate Priority Queue (#33654)
This writes all debug info to a separate priority queue. In the future
I'll put this on a different channel.

Ideally I think we'd put it in the bottom of the stream but because it
actually blocks the elements from resolving anyway it ends up being
better to put them ahead. At least for now.

When we have two separate channels it's not possible to rely on the
order for consistency Even then we might write to that queue first for
this reason. We can't rely on it though. Which will show up like things
turning into Lazy instead of Element similar to how outlining can.
2025-06-27 09:45:11 -04:00
Hendrik Liebau
9b2a545b32
[Flight] Add tests for component and owner stacks of halted components (#33644)
This PR adds tests for the Node.js and Edge builds to verify that
component stacks and owner stacks of halted components appear as
expected, now that recent enhancements for those have been implemented
(the latest one being #33634).

---------

Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-06-25 22:34:35 +02:00
Sebastian Markbåge
bbc13fa17b
[Flight] Add Debug Channel option for stateful connection to the backend in DEV (#33627)
This adds plumbing for opening a stream from the Flight Client to the
Flight Server so it can ask for more data on-demand. In this mode, the
Flight Server keeps the connection open as long as the client is still
alive and there's more objects to load. It retains any depth limited
objects so that they can be asked for later. In this first PR it just
releases the object when it's discovered on the server and doesn't
actually lazy load it yet. That's coming in a follow up.

This strategy is built on the model that each request has its own
channel for this. Instead of some global registry. That ensures that
referential identity is preserved within a Request and the Request can
refer to previously written objects by reference.

The fixture implements a WebSocket per request but it doesn't have to be
done that way. It can be multiplexed through an existing WebSocket for
example. The current protocol is just a Readable(Stream) on the server
and WritableStream on the client. It could even be sent through a HTTP
request body if browsers implemented full duplex (which they don't).

This PR only implements the direction of messages from Client to Server.
However, I also plan on adding Debug Channel in the other direction to
allow debug info (optionally) be sent from Server to Client through this
channel instead of through the main RSC request. So the `debugChannel`
option will be able to take writable or readable or both.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
2025-06-24 11:16:09 -04:00
Sebastian Markbåge
ff93c4448c
[Flight] Track Debug Info from Synchronously Unwrapped Promises (#33485)
Stacked on #33482.

There's a flaw with getting information from the execution context of
the ping. For the soft-deprecated "throw a promise" technique, this is a
bit unreliable because you could in theory throw the same one multiple
times. Similarly, a more fundamental flaw with that API is that it
doesn't allow for tracking the information of Promises that are already
synchronously able to resolve.

This stops tracking the async debug info in the case of throwing a
Promise and only when you render a Promise. That means some loss of data
but we should just warn for throwing a Promise anyway.

Instead, this also adds support for tracking `use()`d thenables and
forwarding `_debugInfo` from then. This is done by extracting the info
from the Promise after the fact instead of in the resolve so that it
only happens once at the end after the pings are done.

This also supports passing the same Promise in multiple places and
tracking the debug info at each location, even if it was already
instrumented with a synchronous value by the time of the second use.
2025-06-11 12:07:10 -04:00
Hendrik Liebau
e4b88ae4c6
[Flight] Add Web Streams APIs to unbundled Node entries for Webpack (#33480) 2025-06-07 23:39:25 +02:00
Sebastian Markbåge
b367b60927
[Flight] Add "use ..." boundary after the change instead of before it (#33478)
I noticed that the ThirdPartyComponent in the fixture was showing the
wrong stack and the `"use third-party"` is in the wrong location.

<img width="628" alt="Screenshot 2025-06-06 at 11 22 11 PM"
src="https://github.com/user-attachments/assets/f0013380-d79e-4765-b371-87fd61b3056b"
/>

When creating the initial JSX inside the third party server, we should
make sure that it has no owner. In a real cross-server environment you
get this by default by just executing in different context. But since
the fixture example is inside the same AsyncLocalStorage as the parent
it already has an owner which gets transferred. So we should make sure
that were we create the JSX has no owner to simulate this.

When we then parse a null owner on the receiving side, we replace its
owner/stack with the owner/stack of the call to `createFrom...` to
connect them. This worked fine with only two environments. The bug was
that when we did this and then transferred the result to a third
environment we took the original parsed stack trace. We should instead
parse a new one from the replaced stack in the current environment.

The second bug was that the `"use third-party"` badge ends up in the
wrong place when we do this kind of thing. Because the stack of the
thing entering the new environment is the call to `createFrom...` which
is in the old environment even though the component itself executes in
the new environment. So to see if there's a change we should be
comparing the current environment of the task to the owner's environment
instead of the next environment after the task.

After:

<img width="494" alt="Screenshot 2025-06-07 at 1 13 28 AM"
src="https://github.com/user-attachments/assets/e2e870ba-f125-4526-a853-bd29f164cf09"
/>
2025-06-07 11:28:57 -04:00
Sebastian Markbåge
9666605abf
[Flight] Add Web Stream support to the Flight Server in Node (#33474)
This needs some tweaks to the implementation and a conversion but simple
enough.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
2025-06-07 10:40:09 -04:00
Sebastian Markbåge
280ff6fed2
[Flight] Add Web Stream support to the Flight Client in Node (#33473)
This effectively lets us consume Web Streams in a Node build. In fact
the Node entry point is now just adding Node stream APIs.

For the client, this is simple because the configs are not actually
stream type specific. The server is a little trickier.
2025-06-06 17:14:15 -04:00
Sebastian Markbåge
82f3684c63
Revert Node Web Streams (#33472)
Reverts #33457, #33456 and #33442.

There are too many issues with wrappers, lazy init, stateful modules,
duplicate instantiation of async_hooks and duplication of code.

Instead, we'll just do a wrapper polyfill that uses Node Streams
internally.

I kept the client indirection files that I added for consistency with
the server though.
2025-06-06 16:26:36 -04:00
Sebastian Markbåge
ab859e31be
[Flight] Build Node.js Web Streams builds for Turbopack and Parcel (#33457)
Same as #33456 and #33442 but for Turbopack and Parcel.
2025-06-06 11:07:40 -04:00
Sebastian Markbåge
e8d15fa19e
[Flight] Build node-webstreams version of bundled webpack server (#33456)
Follow up to #33442. This is the bundled version.

To keep type check passes from exploding and the maintainance of the
annoying `paths: []` list small, this doesn't add this to flow type
checks. We might miss some config but every combination should already
be covered by other one passes.

I also don't add any jest tests because to test these double export
entry points we need conditional importing to cover builds and
non-builds which turns out to be difficult for the Flight builds so
these aren't covered by any basic build tests.

This approach is what I'm going for, for the other bundlers too.
2025-06-06 11:07:15 -04:00
Sebastian Markbåge
a5110b22f0
[Flight] Add a Node.js Web Streams bundle for unbundled client/server for Webpack (#33442)
Like #33441 but for Flight.

This is just one of the many combinations needed. I'm just starting with
one.
2025-06-05 14:29:02 -04:00
Sebastian Markbåge
1ae0a845bd
Use underscore instead of « » for useId algorithm (#33422)
Alternative to #33421. The difference is that this also adds an
underscore between the "R" and the ID.

The reason we wanted to use special characters is because we use the
full spectrum of A-Z 0-9 in our ID generation so we can basically
collide with any common word (or anyone using a similar algorithm,
base64 or even base16). It's a little less likely that someone would put
`_R_` specifically unless you generate like two IDs separated by
underscore.


![9w2ogt](https://github.com/user-attachments/assets/21b2d2ac-1a3a-4657-ba0b-1616e49dfdee)
2025-06-03 11:30:17 -04:00
Sebastian Markbåge
1c43d0aed7
Unify serverAct helpers (#33327)
This uses the richer `serverAct` helper that we already use in other
tests.

This avoids using the `Scheduler`. We don't use that package on the
server so it doesn't make sense to simulate going through it.
Additionally, we really should be getting rid of it on the client too to
favor `postTask` polyfills.
2025-05-21 16:13:54 -04:00
Sebastian Markbåge
b94603b955
[Fizz] Gate rel="expect" behind enableFizzBlockingRender (#33183)
Enabled in experimental channel.

We know this is critical semantics to enforce at the HTML level since if
you don't then you can't add explicit boundaries after the fact.
However, this might have to go in a major release to allow for
upgrading.
2025-05-13 10:17:53 -04:00
Jenny Steele
2bcf06b692
[ReactFlightWebpackPlugin] Add support for .mjs file extension (#33028)
## Summary
Our builds generate files with a `.mjs` file extension. These are
currently filtered out by `ReactFlightWebpackPlugin` so I am updating it
to support this file extension.

This fixes https://github.com/facebook/react/issues/33155

## How did you test this change?
I built the plugin with this change and used `yalc` to test it in my
project. I confirmed the expected files now show up in
`react-client-manifest.json`
2025-05-12 21:16:15 -04:00
Sebastian "Sebbie" Silbermann
b9cfa0d308
[Flight] Prevent serialized size leaking across requests (#33121) 2025-05-05 18:30:33 +02:00
Sebastian Markbåge
49ea8bf569
[Flight] Defer Elements if the parent chunk is too large (#33030)
Same principle as #33029 but for Flight.

We pretty aggressively create separate rows for things in Flight (every
Server Component that's an async function create a microtask). However,
sync Server Components and just plain Host Components are not. Plus we
should ideally ideally inline more of the async ones in the same way
Fizz does.

This means that we can create rows that end up very large. Especially if
all the data is already available. We can't show the parent content
until the whole thing loads on the client.

We don't really know where Suspense boundaries are for Flight but any
Element is potentially a point that can be split.

This heuristic counts roughly how much we've serialized to block the
current chunk and once a limit is exceeded, we start deferring all
Elements. That way they get outlined into future chunks that are later
in the stream. Since they get replaced by Lazy references the parent can
potentially get unblocked.

This can help if you're trying to stream a very large document with a
client nav for example.
2025-04-30 14:21:28 -04:00
Sebastian Markbåge
143d3e1b89
[Fizz] Emit link rel="expect" to block render before the shell has fully loaded (#33016)
The semantics of React is that anything outside of Suspense boundaries
in a transition doesn't display until it has fully unsuspended. With SSR
streaming the intention is to preserve that.

We explicitly don't want to support the mode of document streaming
normally supported by the browser where it can paint content as tags
stream in since that leads to content popping in and thrashing in
unpredictable ways. This should instead be modeled explictly by nested
Suspense boundaries or something like SuspenseList.

After the first shell any nested Suspense boundaries are only revealed,
by script, once they're fully streamed in to the next boundary. So this
is already the case there. However, for the initial shell we have been
at the mercy of browser heuristics for how long it decides to stream
before the first paint.

Chromium now has [an API explicitly for this use
case](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API/Using#stabilizing_page_state_to_make_cross-document_transitions_consistent)
that lets us model the semantics that we want. This is always important
but especially so with MPA View Transitions.

After this a simple document looks like this:

```html
<!DOCTYPE html>
<html>
  <head>
     <link rel="expect" href="#«R»" blocking="render"/>
  </head>
  <body>
    <p>hello world</p>
    <script src="bootstrap.js" id="«R»" async=""></script>
    ...
  </body>
</html>
```

The `rel="expect"` tag indicates that we want to wait to paint until we
have streamed far enough to be able to paint the id `"«R»"` which
indicates the shell.

Ideally this `id` would be assigned to the root most HTML element in the
body. However, this is tricky in our implementation because there can be
multiple and we can render them out of order.

So instead, we assign the id to the first bootstrap script if there is
one since these are always added to the end of the shell. If there isn't
a bootstrap script then we emit an empty `<template
id="«R»"></template>` instead as a marker.

Since we currently put as much as possible in the shell if it's loaded
by the time we render, this can have some negative effects for very
large documents. We should instead apply the heuristic where very large
Suspense boundaries get outlined outside the shell even if they're
immediately available. This means that even prerenders can end up with
script tags.

We only emit the `rel="expect"` if you're rendering a whole document.
I.e. if you rendered either a `<html>` or `<head>` tag. If you're
rendering a partial document, then we don't really know where the
streaming parts are anyway and can't provide such guarantees. This does
apply whether you're streaming or not because we still want to block
rendering until the end, but in practice any serialized state that needs
hydrate should still be embedded after the completion id.
2025-04-25 11:52:28 -04:00
Hendrik Liebau
914319ae59
[Flight] Don't hang forever when prerendering a rejected promise (#32953) 2025-04-23 11:02:43 +02:00
lauren
50c5cdb653
Bump next prerelease version numbers (#32782)
Updates the version numbers in the prerelease channels.
2025-03-28 16:20:04 -04:00
Sebastian "Sebbie" Silbermann
4a9df08157
Stop creating Owner Stacks if many have been created recently (#32529)
Co-authored-by: Jack Pope <jackpope1@gmail.com>
2025-03-23 15:47:03 -07:00