Cancel a superseded Server Components HMR refresh's server-side work (#95486)
When rapid edits overlap Server Components HMR refreshes in `next dev`,
only the newest refresh can commit. The client already aborts a
superseded refresh's fetch, which closes its response. We use that
response-close to stop the server work the superseded refresh started,
so the dev server no longer runs a render (and, under Cache Components,
a validation) whose result is discarded.
When the `serverComponentsHmrCancellation` flag is enabled and the
request is an HMR refresh over a Node response, we derive an abort
signal from `signalFromNodeResponse(ctx.res.originalResponse)` and
thread it into the Flight render. This covers both dev RSC render paths:
the Cache Components staged render in
`generateDynamicFlightRenderResultWithStagesInDev` and the
non-Cache-Components render in `generateDynamicFlightRenderResult`.
The render is aborted through `renderToNodeFlightStream`, which calls
`abort()` on the pipeable that `renderToPipeableStream` returns when the
signal fires. React's Node Flight API has no `signal` option, unlike the
Web `renderToReadableStream`, aborting the returned pipeable is the
intended way to stop the render. This also removes the incorrect
`signal` field from the `renderToPipeableStream` type declaration and
derives `FlightRenderOptions` from `renderToReadableStream`'s options,
so the render wrappers are no longer typed as `any`.
Under Cache Components the superseded refresh additionally skips its
detached validation, which only runs on the staged render, so it never
validates a discarded tree, and any error thrown while its background
renders tear down is swallowed. The aborted Flight renders surface as
abort errors that `createReactServerErrorHandler` already ignores, so
nothing reaches the error overlay or the CLI.
The `hmr-rsc-cancellation` suite exercises both render paths, with Cache
Components controlled by the `__NEXT_CACHE_COMPONENTS` test shard rather
than the fixture config. A child component logs when React renders it,
and the test asserts the superseded refresh never logs, since its render
is aborted before reaching the child, while the committed refresh does.
The detached-validation assertions run only when Cache Components is
enabled.