Commit Graph

229 Commits

Author SHA1 Message Date
Sebastian Markbåge
acee65d6d0
[Flight] Track Awaits on I/O as Debug Info (#33388)
This lets us track what data each Server Component depended on. This
will be used by Performance Track and React DevTools.

We use Node.js `async_hooks`. This has a number of downside. It is
Node.js specific so this feature is not available in other runtimes
until something equivalent becomes available. It's [discouraged by
Node.js docs](https://nodejs.org/api/async_hooks.html#async-hooks). It's
also slow which makes this approach only really viable in development
mode. At least with stack traces. However, it's really the only solution
that gives us the data that we need.

The [Diagnostic
Channel](https://nodejs.org/api/diagnostics_channel.html) API is not
sufficient. Not only is many Node.js built-in APIs missing but all
libraries like databases are also missing. Were as `async_hooks` covers
pretty much anything async in the Node.js ecosystem.

However, even if coverage was wider it's not actually showing the
information we want. It's not enough to show the low level I/O that is
happening because that doesn't provide the context. We need the stack
trace in user space code where it was initiated and where it was
awaited. It's also not each low level socket operation that we want to
surface but some higher level concept which can span a sequence of I/O
operations but as far as user space is concerned.

Therefore this solution is anchored on stack traces and ignore listing
to determine what the interesting span is. It is somewhat
Promise-centric (and in particular async/await) because it allows us to
model an abstract span instead of just random I/O. Async/await points
are also especially useful because this allows Async Stacks to show the
full sequence which is not supported by random callbacks. However, if no
Promises are involved we still to our best to show the stack causing
plain I/O callbacks.

Additionally, we don't want to track all possible I/O. For example,
side-effects like logging that doesn't affect the rendering performance
doesn't need to be included. We only want to include things that
actually block the rendering output. We also need to track which data
blocks each component so that we can track which data caused a
particular subtree to suspend.

We can do this using `async_hooks` because we can track the graph of
what resolved what and then spawned what.

To track what suspended what, something has to resolve. Therefore it
needs to run to completion before we can show what it was suspended on.
So something that never resolves, won't be tracked for example.

We use the `async_hooks` in `ReactFlightServerConfigDebugNode` to build
up an `ReactFlightAsyncSequence` graph that collects the stack traces
for basically all I/O and Promises allocated in the whole app. This is
pretty heavy, especially the stack traces, but it's because we don't
know which ones we'll need until they resolve. We don't materialize the
stacks until we need them though.

Once they end up pinging the Flight runtime, we collect which current
executing task that pinged the runtime and then log the sequence that
led up until that runtime into the RSC protocol. Currently we only
include things that weren't already resolved before we started rendering
this task/component, so that we don't log the entire history each time.

Each operation is split into two parts. First a `ReactIOInfo` which
represents an I/O operation and its start/end time. Basically the start
point where it was start. This is basically represents where you called
`new Promise()` or when entering an `async function` which has an
implied Promise. It can be started in a different component than where
it's awaited and it can be awaited in multiple places. Therefore this is
global information and not associated with a specific Component.

The second part is `ReactAsyncInfo`. This represents where this I/O was
`await`:ed or `.then()` called. This is associated with a point in the
tree (usually the Promise that's a direct child of a Component). Since
you can have multiple different I/O awaited in a sequence technically it
forms a dependency graph but to simplify the model these awaits as
flattened into the `ReactDebugInfo` list. Basically it contains each
await in a sequence that affected this part from unblocking.

This means that the same `ReactAsyncInfo` can appear in mutliple
components if they all await the same `ReactIOInfo` but the same Promise
only appears once.

Promises that are only resolved by other Promises or immediately are not
considered here. Only if they're resolved by an I/O operation. We pick
the Promise basically on the border between user space code and ignored
listed code (`node_modules`) to pick the most specific span but abstract
enough to not give too much detail irrelevant to the current audience.
Similarly, the deepest `await` in user space is marked as the relevant
`await` point.

This feature is only available in the `node` builds of React. Not if you
use the `edge` builds inside of Node.js.

---------

Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-06-03 14:14:40 -04:00
Sebastian Markbåge
21fdf308a1
Use a shared noop function from shared/noop (#33154)
Stacked on #33150.

We use `noop` functions in a lot of places as place holders. I don't
think there's any real optimizations we get from having separate
instances. This moves them to use a common instance in `shared/noop`.
2025-05-08 21:33:18 -04:00
Sebastian Markbåge
a437c99ff7
[Flight] Clarify that location field is a FunctionLocation not a CallSite (#33141)
Follow up to #33136.

This clarifies in the types where the conversion happens from a CallSite
which we use to simulate getting the enclosing line/col to a
FunctionLocation which doesn't represent a CallSite but actually just
the function which only has an enclosing line/col.
2025-05-07 13:02:41 -04:00
Sebastian Markbåge
0ff1d13b80
[Flight] Parse Stack Trace from Structured CallSite if available (#33135)
This is first step to include more enclosing line/column in the parsed
data.

We install our own `prepareStackTrace` to collect structured callsite
data and only fall back to parsing the string if it was already
evaluated or if `prepareStackTrace` doesn't work in this environment.

We still mirror the default V8 format for encoding the function name
part. A lot of this is covered by tests already.
2025-05-07 11:43:37 -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
52ea641449
[Flight] Don't increase serializedSize for every recursive pass (#33123)
I noticed that we increase this in the recursive part of the algorithm.
This would mean that we'd count a key more than once if it has Server
Components inside it recursively resolving. This moves it out to where
we enter from toJSON. Which is called once per JSON entry (and therefore
once per key).
2025-05-05 11:37:39 -04: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
Hendrik Liebau
914319ae59
[Flight] Don't hang forever when prerendering a rejected promise (#32953) 2025-04-23 11:02:43 +02: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
Ricky
e0fe347967
[flags] remove enableOwnerStacks (#32426)
Bassed off: https://github.com/facebook/react/pull/32425

Wait to land internally.

[Commit to
review.](66aa6a4dbb)

This has landed everywhere
2025-03-04 12:34:34 -05:00
Sebastian "Sebbie" Silbermann
5f05181a8b
Include error name in error chunks (#32157) 2025-01-22 16:39:00 +01:00
Hendrik Liebau
829401dc17
[Flight] Transport custom error names in dev mode (#32116)
Typed errors is not a feature that Flight currently supports. However,
for presentation purposes, serializing a custom error name is something
we could support today.

With this PR, we're now transporting custom error names through the
server-client boundary, so that they are available in the client e.g.
for console replaying. One example where this can be useful is when you
want to print debug information while leveraging the fact that
`console.warn` displays the error stack, including handling of hiding
and source mapping stack frames. In this case you may want to show
`Warning: ...` or `Debug: ...` instead of `Error: ...`.

In prod mode, we still transport an obfuscated error that uses the
default `Error` name, to not leak any sensitive information from the
server to the client. This also means that you must not rely on the
error name to discriminate errors, e.g. when handling them in an error
boundary.
2025-01-17 23:48:57 +01:00
Ricky
f42f8c0635
[flags] Remove enableServerComponentLogs (#31772)
This has landed everywhere.
2025-01-03 12:53:19 -05:00
Hendrik Liebau
7eb8234f7c
[Flight] Fix double-incremented pending chunks counter (#31833)
Before calling `emitTimingChunk` inside of `forwardDebugInfo`, we must
not increment `request.pendingChunks`, as this is already done inside of
the `emitTimingChunk` function.

I don't have a unit test for this, but manually verified that this fixes
the hanging responses in https://github.com/vercel/next.js/pull/73804.
2024-12-18 16:35:02 +01:00
Ricky
152080276c
Remove enableFlightReadableStream (#31766)
Base: https://github.com/facebook/react/pull/31765

Landed everywhere
2024-12-13 16:39:13 -05:00
Ricky
08dfd0b805
Remove enableBinaryflight (#31759)
Based off https://github.com/facebook/react/pull/31757

This has landed everywhere.
2024-12-13 14:50:13 -05:00
Sebastian Markbåge
79ddf5b574
[Flight] Track Timing Information (#31716)
Stacked on #31715.

This adds profiling data for Server Components to the RSC stream (but
doesn't yet use it for anything). This is on behind
`enableProfilerTimer` which is on for Dev and Profiling builds. However,
for now there's no Profiling build of Flight so in practice only in DEV.
It's gated on `enableComponentPerformanceTrack` which is experimental
only for now.

We first emit a timeOrigin in the beginning of the stream. This provides
us a relative time to emit timestamps against for cross environment
transfer so that we can log it in terms of absolute times. Using this as
a separate field allows the actual relative timestamps to be a bit more
compact representation and preserves floating point precision.

We emit a timestamp before emitting a Server Component which represents
the start time of the Server Component. The end time is either when the
next Server Component starts or when we finish the task.

We omit the end time for simple tasks that are outlined without Server
Components.

By encoding this as part of the debugInfo stream, this information can
be forwarded between Server to Server RSC.
2024-12-10 20:46:19 -05:00
Sebastian Markbåge
4a8fc0f92e
[Flight] Don't call onError/onPostpone when halting and unify error branches (#31715)
We shouldn't call onError/onPostpone when we halt a stream because that
node didn't error yet. Its digest would also get lost.

We also have a lot of error branches now for thenables and streams. This
unifies them under erroredTask. I'm not yet unifying the cases that
don't allocate a task for the error when those are outlined.
2024-12-10 11:59:50 -05:00
Sebastian Markbåge
372ec00c03
Update ReactDebugInfo types to declare timing info separately (#31714)
This clarifies a few things by ensuring that there is always at least
one required field. This can be used to refine the object to one of the
specific types. However, it's probably just a matter of time until we
make this tagged unions instead. E.g. it would be nice to rename the
`name` field `ReactComponentInfo` to `type` and tag it with the React
Element symbol because then it's just the same as a React Element.

I also extract a time field. The idea is that this will advance (or
rewind) the time to the new timestamp and then anything below would be
defined as happening within that time stamp. E.g. to model the start and
end for a server component you'd do something like:

```
[
  {time: 123},
  {name: 'Component', ... },
  {time: 124},
]
```

The reason this needs to be in the `ReactDebugInfo` is so that timing
information from one environment gets transferred into the next
environment. It lets you take a Promise from one world and transfer it
into another world and its timing information is preserved without
everything else being preserved.

I've gone back and forth on if this should be part of each other Info
object like `ReactComponentInfo` but since those can be deduped and can
change formats (e.g. this should really just be a React Element) it's
better to store this separately.

The time format is relative to a `timeOrigin` which is the current
environment's `timeOrigin`. When it's serialized between environments
this needs to be considered.

Emitting these timings is not yet implemented in this PR.

---------

Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>
2024-12-09 19:47:43 -05:00
Sebastian Markbåge
3d2ab01a55
[Flight] Extract special cases for Server Component return value position (#31713)
This is just moving some code into a helper.

We have a bunch of special cases for the return value slot of a Server
Component that's different from just rendering that inside an object.
This was getting a little tricky to reason about inline with the rest of
rendering.
2024-12-09 17:20:46 -05:00
Sebastian Markbåge
1c9b138714
Don't serialize chunk ids for Hint and Console rows (#31671)
Hints and Console logs are side-effects and don't belong to any
particular value. They're `void`. Therefore they don't need a row ID.

In the current parsing scheme it's ok to omit the id. It just becomes
`0` which is the initial value which is then unused for these row types.

So it looks like:

```
:HP[...]
:W[...]
0:{...}
```

We could patch the parsing to encode the tag in the ID so it's more like
the ID is the target of the side-effect.

```
H:P[...]
W:[...]
0:{...}
```

Or move the tagging to the beginning like it used to be.

But this seems simple enough for now.
2024-12-04 19:53:20 -05:00
Jan Kassens
07aa494432
Remove enableRefAsProp feature flag (#30346)
The flag is fully rolled out.
2024-11-04 14:30:58 -05:00
Sebastian Markbåge
b7e2157922
[Flight] Handle errors during JSON stringify of console values (#31391)
Some checks failed
(Runtime) Build and Test / yarn test-build (1/3, -r=stable --env=development) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (1/3, -r=stable --env=production) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (2/3, --project=devtools -r=experimental) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (2/3, -r=experimental --env=development) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (2/3, -r=experimental --env=production) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (2/3, -r=stable --env=development) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (2/3, -r=stable --env=production) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (3/3, --project=devtools -r=experimental) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (3/3, -r=experimental --env=development) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (3/3, -r=experimental --env=production) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (3/3, -r=stable --env=development) (push) Has been cancelled
(Runtime) Build and Test / yarn test-build (3/3, -r=stable --env=production) (push) Has been cancelled
(Runtime) Build and Test / Process artifacts combined (push) Has been cancelled
(Runtime) Build and Test / Search build artifacts for unminified errors (push) Has been cancelled
(Runtime) Build and Test / Check release dependencies (push) Has been cancelled
(Runtime) Build and Test / Check fixtures DOM (stable) (push) Has been cancelled
(Runtime) Build and Test / Run fixtures Flight tests (push) Has been cancelled
(Runtime) Build and Test / Build DevTools and process artifacts (push) Has been cancelled
(Runtime) Build and Test / Run DevTools e2e tests (push) Has been cancelled
(Runtime) Build and Test / Download base build for sizebot (push) Has been cancelled
(Runtime) Build and Test / Run sizebot (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
(Runtime) Publish Prereleases Nightly / Publish to Canary channel (push) Has been cancelled
(Runtime) Publish Prereleases Nightly / Publish to Experimental channel (push) Has been cancelled
(Compiler) Publish Prereleases Nightly / Publish to Experimental channel (push) Has been cancelled
(Compiler) Publish Prereleases Weekly / Publish to beta channel (push) Has been cancelled
When we serialize debug info we should never error even though we don't
currently support everything being serialized. Since it's non-essential
dev information.

We already handle errors in the replacer but not when errors happen in
the JSON algorithm itself - such as cyclic errors.

We should ideally support cyclic objects but regardless we should
gracefully handle the errors.
2024-10-31 16:47:51 -04:00
Sam Zhou
45804af18d
[flow] Eliminate usage of more than 1-arg React.AbstractComponent in React codebase (#31314)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

In order to adopt react 19's ref-as-prop model, Flow needs to eliminate
all the places where they are treated differently.
`React.AbstractComponent` is the worst example of this, and we need to
eliminate it.

This PR eliminates them from the react repo, and only keeps the one that
has 1 argument of props.

## How did you test this change?

yarn flow
2024-10-21 16:17:41 -07:00
Sebastian Markbåge
cd22717c27
[Flight] Also don't cut off type and key (#31209) 2024-10-13 18:57:50 +02:00
Sebastian Markbåge
70fb136391
Fix limit without owner stacks (#31179)
Move out of gate.
2024-10-10 14:52:33 -04:00
Josh Story
38af456a49
[Flight] don't emit chunks for rejected thenables after abort (#31169)
When aborting we emit chunks for each pending task. However there was a
bug where a thenable could also reject before we could flush and we end
up with an extra chunk throwing off the pendingChunks bookeeping. When a
task is retried we skip it if is is not in PENDING status because we
understand it was completed some other way. We need to replciate this
for the reject pathway on serialized thenables since aborting if
effectively completing all pending tasks and not something we need to
continue to do once the thenable rejects later.
2024-10-10 06:47:32 -07:00
Sebastian Markbåge
566b0b0f14
[Flight] Don't limit objects that are children of special types (#31160)
We can't make a special getter to mark the boundary of deep
serialization (which can be used for lazy loading in the future) when
the parent object is a special object that we parse with
getOutlinedModel. Such as Map/Set and JSX.

This marks the objects that are direct children of those as not possible
to limit.

I don't love this solution since ideally it would maybe be more local to
the serialization of a specific object.

It also means that very deep trees of only Map/Set never get cut off.
Maybe we should instead override the `get()` and enumeration methods on
these instead somehow.

It's important to have it be a getter though because that's the
mechanism that lets us lazy-load more depth in the future.
2024-10-10 07:36:37 -04:00
Josh Story
f5b8d9378b
[Flight] Serialize top-level Date (#31163)
renderModelDesctructive can sometimes be called direclty on Date values.
When this happens we don't first call toJSON on the Date value so we
need to explicitly handle the case where where the rendered value is a
Date instance as well. This change updates renderModelDesctructive to
account for sometimes receiving Date instances directly.
2024-10-09 20:29:48 -07:00
Sebastian Markbåge
654e387d7e
[Flight] Serialize Server Components Props in DEV (#31105)
This allows us to show props in React DevTools when inspecting a Server
Component.

I currently drastically limit the object depth that's serialized since
this is very implicit and you can have heavy objects on the server.

We previously was using the general outlineModel to outline
ReactComponentInfo but we weren't consistently using it everywhere which
could cause some bugs with the parsing when it got deduped on the
client. It also lead to the weird feature detect of `isReactComponent`.
It also meant that this serialization was using the plain serialization
instead of `renderConsoleValue` which means we couldn't safely serialize
arbitrary debug info that isn't serializable there.

So the main change here is to call `outlineComponentInfo` and have that
always write every "Server Component" instance as outlined and in a way
that lets its props be serialized using `renderConsoleValue`.

<img width="1150" alt="Screenshot 2024-10-01 at 1 25 05 AM"
src="https://github.com/user-attachments/assets/f6e7811d-51a3-46b9-bbe0-1b8276849ed4">
2024-10-01 01:39:20 -04:00
Sebastian Markbåge
326832a56d
[Flight] Serialize Error Values (#31104)
The idea is that the RSC protocol is a superset of Structured Clone.
#25687 One exception that we left out was serializing Error objects as
values. We serialize "throws" or "rejections" as Error (regardless of
their type) but not Error values.

This fixes that by serializing `Error` objects. We don't include digest
in this case since we don't call `onError` and it's not really expected
that you'd log it on the server with some way to look it up.

In general this is not super useful outside throws. Especially since we
hide their values in prod. However, there is one case where it is quite
useful. When you replay console logs in DEV you might often log an Error
object within the scope of a Server Component. E.g. the default RSC
error handling just console.error and error object.

Before this would just be an empty object due to our lax console log
serialization:
<img width="1355" alt="Screenshot 2024-09-30 at 2 24 03 PM"
src="https://github.com/user-attachments/assets/694b3fd3-f95f-4863-9321-bcea3f5c5db4">
After:
<img width="1348" alt="Screenshot 2024-09-30 at 2 36 48 PM"
src="https://github.com/user-attachments/assets/834b129d-220d-43a2-a2f4-2eb06921747d">

TODO for a follow up: Flight Reply direction. This direction doesn't
actually serialize thrown errors because they always reject the
serialization.
2024-09-30 15:45:13 -04:00
Hendrik Liebau
04bd67a490
Resolve references to deduped owner objects (#30549)
This is a follow-up from #30528 to not only handle props (the critical
change), but also the owner ~and stack~ of a referenced element.

~Handling stacks here is rather academic because the Flight Server
currently does not deduplicate owner stacks. And if they are really
identical, we should probably just dedupe the whole element.~ EDIT:
Removed from the PR.

Handling owner objects on the other hand is an actual requirement as
reported in https://github.com/vercel/next.js/issues/69545. This problem
only affects the stable release channel, as the absence of owner stacks
allows for the specific kind of shared owner deduping as demonstrated in
the unit test.
2024-09-24 02:34:53 -04:00
Josh Story
fc5ef50da8
[Flight] Start initial work immediately (#30961)
In a past update we made render and prerender have different work
scheduling behavior because these methods are meant to be used in
differeent environments with different performance tradeoffs in mind.
For instance to prioritize streaming we want to allow as much IO to
complete before triggering a round of work because we want to flush as
few intermediate UI states. With Prerendering there will never be any
intermediate UI states so we can more aggressively render tasks as they
complete.

One thing we've found is that even during render we should ideally kick
off work immediately. This update normalizes the intitial work for
render and prerender to start in a microtask. Choosing microtask over
sync is somewhat arbitrary but there really isn't a reason to make them
different between render/prerender so for now we'll unify them and keep
it as a microtask for now.

This change also updates pinging behavior. If the request is still in
the initial task that spawned it then pings will schedule on the
microtask queue. This allows immediately available async APIs to resolve
right away. The concern with doing this for normal pings is that it
might crowd out IO events but since this is the initial task there would
be IO to already be scheduled.
2024-09-14 09:26:01 -07:00
Josh Story
6774caa379
[Flight] properly track pendingChunks when changing environment names (#30958)
When the environment name changes for a chunk we issue a new debug chunk
which updates the environment name. This chunk was not beign included in
the pendingChunks count so the count was off when flushing
2024-09-13 15:55:42 -07:00
Sebastian Markbåge
dff50825c6
[Flight] Track owner/stack where the Flight Client reads as the root (#30933)
This means that the owner of a Component rendered on the remote server
becomes the Component on this server.

Ideally we'd support this for the Client side too. In particular Fiber
but currently ReactComponentInfo's owner is typed as only supporting
other ReactComponentInfo and it's a bigger lift to support that.
2024-09-12 17:19:34 -04:00
Josh Story
dc32c7f35e
[Flight] use microtask for scheduling during prerenders (#30768)
In https://github.com/facebook/react/pull/29491 I updated the work
scheduler for Flight to use microtasks to perform work when something
pings. This is useful but it does have some downsides in terms of our
ability to do task prioritization. Additionally the initial work is not
instantiated using a microtask which is inconsistent with how pings
work.

In this change I update the scheduling logic to use microtasks
consistently for prerenders and use regular tasks for renders both for
the initial work and pings.
2024-08-20 21:43:21 -07:00
Josh Story
92d26c8e93
[Flight] When halting omit any reference rather than refer to a shared missing chunk (#30750)
When aborting a prerender we should leave references unfulfilled, not
share a common unfullfilled reference. functionally today this doesn't
matter because we don't have resuming but the semantic is that the row
was not available when the abort happened and in a resume the row should
fill in. But by pointing each task to a common unfulfilled chunk we lose
the ability for these references to resolves to distinct values on
resume.
2024-08-20 10:22:39 -07:00
Josh Story
a960b92cb9
[Flight] model halting as never delivered chunks (#30740)
stacked on: #30731

We've refined the model of halting a prerender. Now when you abort
during a prerender we simply omit the rows that would complete the
flight render. This is analagous to prerendering in Fizz where you must
resume the prerender to actually result in errors propagating in the
postponed holes. We don't have a resume yet for flight and it's not
entirely clear how that will work however the key insight here is that
deciding whether the never resolving rows are an error or not should
really be done on the consuming side rather than in the producer.

This PR also reintroduces the logs for the abort error/postpone when
prerendering which will give you some indication that something wasn't
finished when the prerender was aborted.
2024-08-19 19:34:20 -07:00
Sebastian Markbåge
0fa9476b9b
[Flight] Revert Emit Infinite Promise as a Halted Row (#30746) (#30748)
This reverts commit 52c9c43735.

Just kidding. We realized we probably don't want to do the halted row
thing after all.
2024-08-19 16:34:38 -04:00
Sebastian Markbåge
52c9c43735
[Flight] Emit Infinite Promise as a Halted Row (#30746)
Stacked on #30731.

When logging a Promise we emit it as an infinite promise instead of
blocking the replay on it.

This models that as a halted row instead. No need for this special case.

I unflag the receiving side since now it's used to replace a feature
that's already unflagged so it's used.
2024-08-19 15:02:41 -04:00
Josh Story
9d082b5500
[Flight] model halted references explicitly (#30731)
using infinitely suspending promises isn't right because this will parse
as a promise which is only appropriate if the value we're halting at is
a promise. Instead we need to have a special marker type that says this
reference will never resolve. Additionally flight client needs to not
error any halted references when the stream closes because they will
otherwise appear as an error

addresses:
https://github.com/facebook/react/pull/30705#discussion_r1720479974
2024-08-19 11:24:41 -07:00
Sebastian Markbåge
6ebfd5b082
[Flight] Source Map Server Actions to their Server Location (#30741)
This uses a similar technique to what we use to generate fake stack
frames for server components. This generates an eval:ed wrapper function
around the Server Reference proxy we create on the client. This wrapper
function gets the original `name` of the action on the server and I also
add a source map if `findSourceMapURL` is defined that points back to
the source of the server function.

For `"use server"` on the server, there's no new API. It just uses the
callsite of `registerServerReference()` on the Server. We can infer the
function name from the actual function on the server and we already have
the `findSourceMapURL` on the client receiving it.

For `"use server"` imported from the client, there's two new options
added to `createServerReference()` (in addition to the optional
[`encodeFormAction`](#27563)). These are only used in DEV mode. The
[`findSourceMapURL`](#29708) option is the same one added in #29708. We
need to pass this these references aren't created in the context of any
specific request but globally. The other weird thing about this case is
that this is actually a case where the compiled environment is the
client so any source maps are the same as for the client layer, so the
environment name here is just `"Client"`.

```diff
  createServerReference(
    id: string,
    callServer: CallServerCallback,
    encodeFormAction?: EncodeFormActionCallback,
+   findSourceMapURL?: FindSourceMapURLCallback, // DEV-only
+   functionName?: string, // DEV-only
  )
```

The key is that we use the location of the
`registerServerReference()`/`createServerReference()` call as the
location of the function. A compiler can either emit those at the same
locations as the original functions or use source maps to have those
segments refer to the original location of the function (or in the case
of a re-export the original location of the re-export is also a fine
approximate). The compiled output must call these directly without a
wrapper function because the wrapper adds a stack frame. I decided
against complicated and fragile dev-only options to skip n number of
frames that would just end up in prod code. The implementation just
skips one frame - our own. Otherwise it'll just point all source mapping
to the wrapper.

We don't have a `"use server"` imported from the client implementation
in the reference implementation/fixture so it's a bit tricky to test
that. In the case of CJS on the server, we just use a runtime instead of
compiler so it's tricky to source map those appropriately. We can
implement it for ESM on the server which is the main thing we're testing
in the fixture. It's easier in a real implementation where all the
compilation is just one pass. It's a little tricky since we have to
parse and append to other source maps but I'd like to do that as a
follow up. Or maybe that's just an exercise for the reader.

You can right click an action and click "Go to Definition".

<img width="1323" alt="Screenshot 2024-08-17 at 6 04 27 PM"
src="https://github.com/user-attachments/assets/94d379b3-8871-4671-a20d-cbf9cfbc2c6e">

For now they simply don't point to the right place but you can still
jump to the right file in the fixture:

<img width="1512" alt="Screenshot 2024-08-17 at 5 58 40 PM"
src="https://github.com/user-attachments/assets/1ea5d665-e25a-44ca-9515-481dd3c5c2fe">

In Firefox/Safari given that the location doesn't exist in the source
map yet, the browser refuses to open the file. Where as Chrome does
nearest (last) line.
2024-08-18 12:31:45 -04:00
Josh Story
7b41cdc093
[Flight][Static] Implement halting a prerender behind enableHalt (#30705)
enableHalt turns on a mode for flight prerenders where aborts are
treated like infinitely stalled outcomes while still completing the
prerender. For regular tasks we simply serialize the slot as a promise
that never settles. For ReadableStream, Blob, and Async Iterators we
just never advance the serialization so they remain unfinished when
consumed on the client.

When enableHalt is turned on aborts of prerenders will halt rather than
error. The abort reason is forwarded to the upstream produces of the
aforementioned async iterators, blobs, and ReadableStreams. In the
future if we expose a signal that you can consume from within a render
to cancel additional work the abort reason will also be forwarded there
2024-08-16 14:21:57 -07:00
Josh Story
fa6eab5854
[Flight] Implement prerender (#30686)
Prerendering in flight is similar to prerendering in Fizz. Instead of
receiving a result (the stream) immediately a promise is returned which
resolves to the stream when the prerender is complete. The promise will
reject if the flight render fatally errors otherwise it will resolve
when the render is completed or is aborted.
2024-08-15 14:28:28 -07:00
Sebastian Markbåge
19bd26beb6
[Flight/DevTools] Pass the Server Component's "key" as Part of the ReactComponentInfo (#30703)
Supports showing the key in DevTools on the Server Component that the
key was applied to. We can also use this to reconcile to preserve
instance equality when they're reordered.

One thing that's a bit weird about this is that if you provide an
explicit key on a Server Component that alone doesn't have any
semantics. It's because we pass the key down and let the nearest child
inherit the key or get prefixed by the key.

So you might see the same key as a prefix on the child of the Server
Component too which might be a bit confusing. We could remove the prefix
from children but that might also be a bit confusing if they collide.

The div in this case doesn't have a key explicitly specified. It gets it
from the Server Component parent.

<img width="1107" alt="Screenshot 2024-08-14 at 10 06 36 PM"
src="https://github.com/user-attachments/assets/cfc517cc-e737-44c3-a1be-050049267ee2">

Overall keys get a bit confusing when you apply filter. Especially since
it's so common to actually apply the key on a Host Instance. So you
often don't see the key.
2024-08-15 11:04:53 -04:00
Josh Story
2a540194ad
[Flight] do not emit error after abort (#30683)
When synchronously aborting in a non-async Function Component if you
throw after aborting the task would error rather than abort because
React never observed the AbortSignal.

Using a sigil to throw after aborting during render isn't effective b/c
the user code itself could throw so insteead we just read the request
status. This is ok b/c we don't expect any tasks to still be pending
after the currently running task finishes.

However I found one instance where that wasn't true related to
serializing thenables which I've fixed so we may find other cases. If we
do, though it's almost certainly a bug in our task bookkeeping so we
should just fix it if it comes up.

I also updated `abort` to not set the status to ABORTING unless the
status was OPEN. we don't want to ever leave CLOSED or CLOSING status
2024-08-13 20:59:45 -07:00
Josh Story
f6d1df6648
[Flight] erroring after abort should not result in unhandled rejection (#30675)
When I implemented the ability to abort synchronoulsy in flight I made
it possible for erroring async server components to cause an unhandled
rejection error. In the current implementation if you abort during the
synchronous phase of a Function Component and then throw an error in the
synchronous phase React will not attach any promise handlers because it
short circuits the thenable treatment and throws an AbortSigil instead.
This change updates the rendering logic to ignore the rejecting
component.
2024-08-13 13:42:10 -07:00
Sebastian Markbåge
ba6a9e94ed
[Flight] Warn for keyless fragments in an array (#30588)
Conceptually this is the same as rendering this as if it was a built-in
Server Component.
2024-08-02 20:08:08 -04:00
Sebastian Markbåge
12e9579099
[Flight] Enable owner stacks on the client when replaying logs (#30473)
There's a special case that happens when we replay logs on the client
because this doesn't happen within the context of any particular
rendered component. So we need to reimplement things that would normally
be handled by a full client like Fiber.

The implementation of `getOwnerStackByComponentInfoInDev` is the
simplest version since it doesn't have any client components in it so I
move it to `shared/`. It's only used by Flight but both `react-server/`
and `react-client/` packages. The ReactComponentInfo type is also more
generic than just Flight anyway.

In a follow up I still need to implement this in React DevTools when
native tasks are not available so that it appends it to the console.
2024-07-31 07:56:15 -04:00
Sebastian Markbåge
e5d22459ff
[Flight] Include environment name both in the virtual URL and findSourceMapURL (#30452)
This way you can use the environment to know where to look for the
source map in case you have multiple server environments.

This becomes part of the public protocol since it's part of what you'll
parse out of the `rsc://React/` prefixed URLs inside of
`captureOwnerStack`.
2024-07-25 11:14:24 -04:00