Files
react/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js
T
Sebastian Markbåge 629541bcc0 [Flight] Transfer Debug Info in Server-to-Server Flight Requests (#28275)
A Flight Server can be a consumer of a stream from another Server. In
this case the meta data is attached to debugInfo properties on lazy,
Promises, Arrays or Elements that might in turn get forwarded to the
next stream. In this case we want to forward this debug information to
the client in the stream.

I also added a DEV only `environmentName` option to the Flight Server.
This lets you name the server that is producing the debug info so that
you can trace the origin of where that component is executing. This
defaults to `"server"`. DevTools could use this for badges or different
colors.
2024-02-12 13:38:14 -05:00

109 lines
2.7 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {ReactClientValue} from 'react-server/src/ReactFlightServer';
import type {Thenable} from 'shared/ReactTypes';
import type {ClientManifest} from './ReactFlightServerConfigWebpackBundler';
import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig';
import {
createRequest,
startWork,
startFlowing,
stopFlowing,
abort,
} from 'react-server/src/ReactFlightServer';
import {
createResponse,
close,
getRoot,
} from 'react-server/src/ReactFlightReplyServer';
import {
decodeAction,
decodeFormState,
} from 'react-server/src/ReactFlightActionServer';
export {
registerServerReference,
registerClientReference,
createClientModuleProxy,
} from './ReactFlightWebpackReferences';
type Options = {
environmentName?: string,
identifierPrefix?: string,
signal?: AbortSignal,
onError?: (error: mixed) => void,
onPostpone?: (reason: string) => void,
};
function renderToReadableStream(
model: ReactClientValue,
webpackMap: ClientManifest,
options?: Options,
): ReadableStream {
const request = createRequest(
model,
webpackMap,
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.onPostpone : undefined,
options ? options.environmentName : undefined,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort(request, (signal: any).reason);
} else {
const listener = () => {
abort(request, (signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
const stream = new ReadableStream(
{
type: 'bytes',
start: (controller): ?Promise<void> => {
startWork(request);
},
pull: (controller): ?Promise<void> => {
startFlowing(request, controller);
},
cancel: (reason): ?Promise<void> => {
stopFlowing(request);
abort(request, reason);
},
},
// $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
{highWaterMark: 0},
);
return stream;
}
function decodeReply<T>(
body: string | FormData,
webpackMap: ServerManifest,
): Thenable<T> {
if (typeof body === 'string') {
const form = new FormData();
form.append('0', body);
body = form;
}
const response = createResponse(webpackMap, '', body);
const root = getRoot<T>(response);
close(response);
return root;
}
export {renderToReadableStream, decodeReply, decodeAction, decodeFormState};