[Flight] Allow overriding request.timeOrigin via options.startTime (#35598)

Currently, IO that finished before the request started is not considered
IO:

https://github.com/facebook/react/blob/da33fa5028edd00cf9697b153efe2b3f5e3f643d/packages/react-server/src/ReactFlightServer.js#L5338-L5343
This leads to loss of debug info when a flight stream is deserialized
and serialized again.
We can solve this by allowing "when the the request started" to be set
to a point in the past, when the original stream started by doing

```js
const startTime = performance.now() + performance.timeOrigin
// ... stuff happens and time passes...
ReactServer.renderToReadableStream(..., { startTime })
```
This commit is contained in:
Janka Uryga
2026-02-03 15:29:51 +01:00
committed by GitHub
parent 2647358088
commit 6fdd8666c4
15 changed files with 356 additions and 2 deletions
+1
View File
@@ -184,6 +184,7 @@ export function experimental_renderToHTML(
handleFlightError,
options ? options.identifierPrefix : undefined,
undefined,
undefined,
'Markup',
undefined,
false,
@@ -73,6 +73,7 @@ type Options = {
signal?: AbortSignal,
debugChannel?: {onMessage?: (message: string) => void},
onError?: (error: mixed) => void,
startTime?: number,
};
function render(model: ReactClientValue, options?: Options): Destination {
@@ -84,6 +85,7 @@ function render(model: ReactClientValue, options?: Options): Destination {
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
__DEV__ && options && options.debugChannel !== undefined,
@@ -146,6 +146,7 @@ type Options = {
onError?: (error: mixed) => void,
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
startTime?: number,
};
type PipeableStream = {
@@ -183,6 +184,7 @@ function renderToPipeableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannel !== undefined,
@@ -272,6 +274,7 @@ type PrerenderOptions = {
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
signal?: AbortSignal,
startTime?: number,
};
type StaticResult = {
@@ -303,6 +306,7 @@ function prerenderToNodeStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -12,6 +12,7 @@ import type {
ReactClientValue,
} from 'react-server/src/ReactFlightServer';
import type {ReactFormState, Thenable} from 'shared/ReactTypes';
import {
preloadModule,
requireModule,
@@ -67,6 +68,7 @@ type Options = {
signal?: AbortSignal,
temporaryReferences?: TemporaryReferenceSet,
onError?: (error: mixed) => void,
startTime?: number,
};
function startReadingFromDebugChannelReadableStream(
@@ -128,6 +130,7 @@ export function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -215,6 +218,7 @@ export function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -12,6 +12,7 @@ import type {
ReactClientValue,
} from 'react-server/src/ReactFlightServer';
import type {ReactFormState, Thenable} from 'shared/ReactTypes';
import {
preloadModule,
requireModule,
@@ -72,6 +73,7 @@ type Options = {
signal?: AbortSignal,
temporaryReferences?: TemporaryReferenceSet,
onError?: (error: mixed) => void,
startTime?: number,
};
function startReadingFromDebugChannelReadableStream(
@@ -133,6 +135,7 @@ export function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -220,6 +223,7 @@ export function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -159,6 +159,7 @@ type Options = {
onError?: (error: mixed) => void,
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
startTime?: number,
};
type PipeableStream = {
@@ -195,6 +196,7 @@ export function renderToPipeableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannel !== undefined,
@@ -352,6 +354,7 @@ export function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -434,6 +437,7 @@ type PrerenderOptions = {
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
signal?: AbortSignal,
startTime?: number,
};
type StaticResult = {
@@ -464,6 +468,7 @@ export function prerenderToNodeStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -526,6 +531,7 @@ export function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -64,6 +64,7 @@ type Options = {
signal?: AbortSignal,
temporaryReferences?: TemporaryReferenceSet,
onError?: (error: mixed) => void,
startTime?: number,
};
function startReadingFromDebugChannelReadableStream(
@@ -126,6 +127,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -214,6 +216,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -69,6 +69,7 @@ type Options = {
signal?: AbortSignal,
temporaryReferences?: TemporaryReferenceSet,
onError?: (error: mixed) => void,
startTime?: number,
};
function startReadingFromDebugChannelReadableStream(
@@ -131,6 +132,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -219,6 +221,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -152,6 +152,7 @@ type Options = {
onError?: (error: mixed) => void,
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
startTime?: number,
};
type PipeableStream = {
@@ -189,6 +190,7 @@ function renderToPipeableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannel !== undefined,
@@ -347,6 +349,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -429,6 +432,7 @@ type PrerenderOptions = {
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
signal?: AbortSignal,
startTime?: number,
};
type StaticResult = {
@@ -460,6 +464,7 @@ function prerenderToNodeStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -523,6 +528,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -152,6 +152,7 @@ type Options = {
onError?: (error: mixed) => void,
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
startTime?: number,
};
type PipeableStream = {
@@ -189,6 +190,7 @@ function renderToPipeableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -347,6 +349,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -429,6 +432,7 @@ type PrerenderOptions = {
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
signal?: AbortSignal,
startTime?: number,
};
type StaticResult = {
@@ -460,6 +464,7 @@ function prerenderToNodeStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -523,6 +528,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -101,7 +101,7 @@ describe('ReactFlightDOMNode', () => {
return (
' in ' +
name +
(/\d/.test(m)
(/:\d+:\d+/.test(m)
? preserveLocation
? ' ' + location.replace(__filename, relativeFilename)
: ' (at **)'
@@ -1589,6 +1589,296 @@ describe('ReactFlightDOMNode', () => {
expect(ownerStack).toBeNull();
}
});
function createReadableWithLateRelease(initialChunks, lateChunks, signal) {
// Create a new Readable and push all initial chunks immediately.
const readable = new Stream.Readable({...streamOptions, read() {}});
for (let i = 0; i < initialChunks.length; i++) {
readable.push(initialChunks[i]);
}
// When prerendering is aborted, push all dynamic chunks. They won't be
// considered for rendering, but they include debug info we want to use.
signal.addEventListener(
'abort',
() => {
for (let i = 0; i < lateChunks.length; i++) {
readable.push(lateChunks[i]);
}
setImmediate(() => {
readable.push(null);
});
},
{once: true},
);
return readable;
}
async function reencodeFlightStream(
staticChunks,
dynamicChunks,
startTime,
serverConsumerManifest,
) {
let staticEndTime = -1;
const chunks = {
static: [],
dynamic: [],
};
await new Promise(async resolve => {
const renderStageController = new AbortController();
const serverStream = createReadableWithLateRelease(
staticChunks,
dynamicChunks,
renderStageController.signal,
);
const decoded = await ReactServerDOMClient.createFromNodeStream(
serverStream,
serverConsumerManifest,
{
// We're re-encoding the whole stream, so we don't want to filter out any debug info.
endTime: undefined,
},
);
setTimeout(async () => {
const stream = ReactServerDOMServer.renderToPipeableStream(
decoded,
webpackMap,
{
filterStackFrame,
// Pass in the original render's startTime to avoid omitting its IO info.
startTime,
},
);
const passThrough = new Stream.PassThrough(streamOptions);
passThrough.on('data', chunk => {
if (!renderStageController.signal.aborted) {
chunks.static.push(chunk);
} else {
chunks.dynamic.push(chunk);
}
});
passThrough.on('end', resolve);
stream.pipe(passThrough);
});
setTimeout(() => {
staticEndTime = performance.now() + performance.timeOrigin;
renderStageController.abort();
});
});
return {chunks, staticEndTime};
}
// @gate __DEV__
it('can preserve old IO info when decoding and re-encoding a stream with options.startTime', async () => {
let resolveDynamicData;
function getDynamicData() {
return new Promise(resolve => {
resolveDynamicData = resolve;
});
}
async function Dynamic() {
const data = await getDynamicData();
return ReactServer.createElement('p', null, data);
}
function App() {
return ReactServer.createElement(
'html',
null,
ReactServer.createElement(
'body',
null,
ReactServer.createElement(
ReactServer.Suspense,
{fallback: 'Loading...'},
// TODO: having a wrapper <section> here seems load-bearing.
// ReactServer.createElement(ReactServer.createElement(Dynamic)),
ReactServer.createElement(
'section',
null,
ReactServer.createElement(Dynamic),
),
),
),
);
}
const resolveDynamic = () => {
resolveDynamicData('Hi Janka');
};
// 1. Render <App />, dividing the output into static and dynamic content.
let startTime = -1;
let isStatic = true;
const chunks1 = {
static: [],
dynamic: [],
};
await new Promise(resolve => {
setTimeout(async () => {
startTime = performance.now() + performance.timeOrigin;
const stream = ReactServerDOMServer.renderToPipeableStream(
ReactServer.createElement(App),
webpackMap,
{
filterStackFrame,
startTime,
environmentName() {
return isStatic ? 'Prerender' : 'Server';
},
},
);
const passThrough = new Stream.PassThrough(streamOptions);
passThrough.on('data', chunk => {
if (isStatic) {
chunks1.static.push(chunk);
} else {
chunks1.dynamic.push(chunk);
}
});
passThrough.on('end', resolve);
stream.pipe(passThrough);
});
setTimeout(() => {
isStatic = false;
resolveDynamic();
});
});
//===============================================
// 2. Decode the stream from the previous step and render it again.
// This should preserve existing debug info.
const serverConsumerManifest = {
moduleMap: null,
moduleLoading: null,
};
const {chunks: chunks2, staticEndTime: reencodeStaticEndTime} =
await reencodeFlightStream(
chunks1.static,
chunks1.dynamic,
// This is load-bearing. If we don't pass a startTime, IO info
// from the initial render will be skipped (because it finished in the past)
// and we won't get the precise location of the blocking await in the owner stack.
startTime,
serverConsumerManifest,
);
//===============================================
// 3. SSR the stream from the previous step and abort it after the static stage
// (which should trigger `onError` for each "hole" that hasn't resolved yet)
function ClientRoot({response}) {
return use(response);
}
let ssrStream;
let ownerStack;
let componentStack;
await new Promise(async (resolve, reject) => {
const renderController = new AbortController();
const serverStream = createReadableWithLateRelease(
chunks2.static,
chunks2.dynamic,
renderController.signal,
);
const decodedPromise = ReactServerDOMClient.createFromNodeStream(
serverStream,
serverConsumerManifest,
{
endTime: reencodeStaticEndTime,
},
);
setTimeout(() => {
ssrStream = ReactDOMServer.renderToPipeableStream(
React.createElement(ClientRoot, {
response: decodedPromise,
}),
{
onError(err, errorInfo) {
componentStack = errorInfo.componentStack;
ownerStack = React.captureOwnerStack
? React.captureOwnerStack()
: null;
return null;
},
},
);
renderController.signal.addEventListener(
'abort',
() => {
const {reason} = renderController.signal;
ssrStream.abort(reason);
},
{
once: true,
},
);
});
setTimeout(() => {
renderController.abort(new Error('ssr-abort'));
resolve();
});
});
const result = await readResult(ssrStream);
expect(normalizeCodeLocInfo(componentStack)).toBe(
'\n' +
// TODO:
// when we reencode a stream, the component stack doesn't have server frames for the dynamic content
// (which is what causes the dynamic hole here)
// because Flight delays forwarding debug info for lazies until they resolve.
// (the owner stack is filled in `pushHaltedAwaitOnComponentStack`, so it works fine)
//
// ' in Dynamic (at **)\n'
' in section\n' +
' in Suspense\n' +
' in body\n' +
' in html\n' +
' in App (at **)\n' +
' in ClientRoot (at **)',
);
expect(normalizeCodeLocInfo(ownerStack)).toBe(
'\n' +
gate(flags =>
flags.enableAsyncDebugInfo
? ' in Dynamic (at **)\n'
: ' in section\n',
) +
' in App (at **)',
);
expect(result).toContain(
'Switched to client rendering because the server rendering aborted due to:\n\n' +
'ssr-abort',
);
});
});
it('warns with a tailored message if eval is not available in dev', async () => {
@@ -64,6 +64,7 @@ type Options = {
signal?: AbortSignal,
temporaryReferences?: TemporaryReferenceSet,
onError?: (error: mixed) => void,
startTime?: number,
};
function startReadingFromDebugChannelReadableStream(
@@ -126,6 +127,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -214,6 +216,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -69,6 +69,7 @@ type Options = {
signal?: AbortSignal,
temporaryReferences?: TemporaryReferenceSet,
onError?: (error: mixed) => void,
startTime?: number,
};
function startReadingFromDebugChannelReadableStream(
@@ -131,6 +132,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -219,6 +221,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -152,6 +152,7 @@ type Options = {
onError?: (error: mixed) => void,
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
startTime?: number,
};
type PipeableStream = {
@@ -189,6 +190,7 @@ function renderToPipeableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -347,6 +349,7 @@ function renderToReadableStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
@@ -429,6 +432,7 @@ type PrerenderOptions = {
identifierPrefix?: string,
temporaryReferences?: TemporaryReferenceSet,
signal?: AbortSignal,
startTime?: number,
};
type StaticResult = {
@@ -460,6 +464,7 @@ function prerenderToNodeStream(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
@@ -523,6 +528,7 @@ function prerender(
options ? options.onError : undefined,
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
false,
+14 -1
View File
@@ -660,6 +660,7 @@ function RequestInstance(
onFatalError: (error: mixed) => void,
identifierPrefix?: string,
temporaryReferences: void | TemporaryReferenceSet,
debugStartTime: void | number, // Profiling-only
environmentName: void | string | (() => string), // DEV-only
filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only
keepDebugAlive: boolean, // DEV-only
@@ -751,7 +752,15 @@ function RequestInstance(
// This avoids leaking unnecessary information like how long the server has
// been running and allows for more compact representation of each timestamp.
// The time origin is stored as an offset in the time space of this environment.
timeOrigin = this.timeOrigin = performance.now();
if (typeof debugStartTime === 'number') {
// We expect `startTime` to be an absolute timestamp, so relativize it to match the other case.
timeOrigin = this.timeOrigin =
debugStartTime -
// $FlowFixMe[prop-missing]
performance.timeOrigin;
} else {
timeOrigin = this.timeOrigin = performance.now();
}
emitTimeOriginChunk(
this,
timeOrigin +
@@ -784,6 +793,7 @@ export function createRequest(
onError: void | ((error: mixed) => ?string),
identifierPrefix: void | string,
temporaryReferences: void | TemporaryReferenceSet,
debugStartTime: void | number, // Profiling-only
environmentName: void | string | (() => string), // DEV-only
filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only
keepDebugAlive: boolean, // DEV-only
@@ -802,6 +812,7 @@ export function createRequest(
noop,
identifierPrefix,
temporaryReferences,
debugStartTime,
environmentName,
filterStackFrame,
keepDebugAlive,
@@ -816,6 +827,7 @@ export function createPrerenderRequest(
onError: void | ((error: mixed) => ?string),
identifierPrefix: void | string,
temporaryReferences: void | TemporaryReferenceSet,
debugStartTime: void | number, // Profiling-only
environmentName: void | string | (() => string), // DEV-only
filterStackFrame: void | ((url: string, functionName: string) => boolean), // DEV-only
keepDebugAlive: boolean, // DEV-only
@@ -834,6 +846,7 @@ export function createPrerenderRequest(
onFatalError,
identifierPrefix,
temporaryReferences,
debugStartTime,
environmentName,
filterStackFrame,
keepDebugAlive,