Node.js streams: First pass (#90500)
### What?
Note: This is only the first step. There's more follow-up PRs to
implement the transforms as Node.js streams.
Introduces a compile-time switchable Node.js streams layer for the app
rendering pipeline, gated behind `__NEXT_USE_NODE_STREAMS`. When
enabled, React's `renderToPipeableStream` / `resumeToPipeableStream`
APIs produce native Node.js `Readable` streams instead of web
`ReadableStream`s, eliminating repeated conversions between the two
stream types during rendering.
### Why?
The current rendering pipeline uses web `ReadableStream` everywhere to
be consistent between Node.js/Edge, but the Node.js server ultimately
pipes responses to Node `http.ServerResponse`. We found ReadableStream
to be much slower than using Node.js streams directly, especially under
load. By using Node.js streams natively throughout the pipeline, we can
avoid this cost.
### How?
- **`AnyStream` type** (`ReadableStream<Uint8Array> | Readable`):
Introduced as the universal stream type across the rendering pipeline,
replacing hardcoded `ReadableStream<Uint8Array>` in all stream-carrying
signatures (`app-render.tsx`, `render-result.ts`,
`flight-render-result.ts`, `hot-reloader-types.ts`, etc.).
- **`stream-ops.ts` compile-time switcher**: Changed from a simple
re-export of `stream-ops.web.ts` to a conditional `require()` that loads
either `stream-ops.node.ts` or `stream-ops.web.ts` based on
`process.env.__NEXT_USE_NODE_STREAMS`. Both modules export the same type
surface so no casts are needed.
- **`stream-ops.node.ts` (new)**: Node.js stream implementation that
uses `renderToPipeableStream`, `resumeToPipeableStream`, and
`PassThrough` for native pipeable rendering. Continue functions bridge
to existing web transforms via `Readable.fromWeb()` / `Readable.toWeb()`
at the boundary.
- **`stream-ops.web.ts`**: Updated to wrap continue/utility functions
instead of re-exporting them directly, so both modules have the same
shaped exports. Added `teeStream`, `streamToUint8Array`,
`processPrelude` wrappers.
- **`debug-channel-server.ts` switcher**: Same compile-time pattern —
loads `debug-channel-server.node.ts` (PassThrough-based) or
`debug-channel-server.web.ts` (WritableStream-based).
- **`pipe-readable.ts`**: Added `pipeNodeReadableToNodeResponse()` that
pipes a Node `Readable` directly to `ServerResponse` with backpressure
handling, without converting to web streams first.
- **`render-result.ts`**: Accepts `Readable` as a response type. When a
Node `Readable` is the response, `pipeToNodeResponse()` uses the new
native pipe path. `readable` and `pipeable` getters convert via
`Readable.toWeb()` only when needed.
- **`app-render.tsx`**: Replaced `ReadableStream<Uint8Array>` with
`AnyStream` throughout. Replaced `.tee()` calls with `teeStream()`.
Updated `accumulateStreamChunks` to handle both stream types with a
shared `accumulateChunk` helper. Removed `nodeStreamFromReadableStream`
utility (no longer needed).
- **`instant-validation.tsx`**: Changed from directly importing
`renderToReadableStream` from `react-server-dom-webpack/server` to using
the `renderToFlightStream` function from `stream-ops`, so validation
uses the same stream backend as the main render.
- **`use-flight-response.tsx`**: Updated debug stream handling to accept
both `Readable` and `ReadableStream`, converting as needed.