mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
d8089f2cf2
Builds on top of https://github.com/facebook/react/pull/26661 This lets you pass FormData objects through the Flight Reply serialization. It does that by prefixing each entry with the ID of the reference and then the decoding side creates a new FormData object containing only those fields (without the prefix). Ideally this should be more generic. E.g. you should be able to pass Blobs, Streams and Typed Arrays by reference inside plain objects too. You should also be able to send Blobs and FormData in the regular Flight serialization too so that they can go both directions. They should be symmetrical. We'll get around to adding more of those features in the Flight protocol as we go. --------- Co-authored-by: Sophie Alpert <git@sophiebits.com>
138 lines
3.3 KiB
JavaScript
138 lines
3.3 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 {Thenable} from 'shared/ReactTypes.js';
|
|
|
|
import type {Response as FlightResponse} from 'react-client/src/ReactFlightClientStream';
|
|
|
|
import type {ReactServerValue} from 'react-client/src/ReactFlightReplyClient';
|
|
|
|
import {
|
|
createResponse,
|
|
getRoot,
|
|
reportGlobalError,
|
|
processStringChunk,
|
|
processBinaryChunk,
|
|
close,
|
|
} from 'react-client/src/ReactFlightClientStream';
|
|
|
|
import {
|
|
processReply,
|
|
createServerReference,
|
|
} from 'react-client/src/ReactFlightReplyClient';
|
|
|
|
type CallServerCallback = <A, T>(string, args: A) => Promise<T>;
|
|
|
|
export type Options = {
|
|
callServer?: CallServerCallback,
|
|
};
|
|
|
|
function createResponseFromOptions(options: void | Options) {
|
|
return createResponse(
|
|
null,
|
|
options && options.callServer ? options.callServer : undefined,
|
|
);
|
|
}
|
|
|
|
function startReadingFromStream(
|
|
response: FlightResponse,
|
|
stream: ReadableStream,
|
|
): void {
|
|
const reader = stream.getReader();
|
|
function progress({
|
|
done,
|
|
value,
|
|
}: {
|
|
done: boolean,
|
|
value: ?any,
|
|
...
|
|
}): void | Promise<void> {
|
|
if (done) {
|
|
close(response);
|
|
return;
|
|
}
|
|
const buffer: Uint8Array = (value: any);
|
|
processBinaryChunk(response, buffer);
|
|
return reader.read().then(progress).catch(error);
|
|
}
|
|
function error(e: any) {
|
|
reportGlobalError(response, e);
|
|
}
|
|
reader.read().then(progress).catch(error);
|
|
}
|
|
|
|
function createFromReadableStream<T>(
|
|
stream: ReadableStream,
|
|
options?: Options,
|
|
): Thenable<T> {
|
|
const response: FlightResponse = createResponseFromOptions(options);
|
|
startReadingFromStream(response, stream);
|
|
return getRoot(response);
|
|
}
|
|
|
|
function createFromFetch<T>(
|
|
promiseForResponse: Promise<Response>,
|
|
options?: Options,
|
|
): Thenable<T> {
|
|
const response: FlightResponse = createResponseFromOptions(options);
|
|
promiseForResponse.then(
|
|
function (r) {
|
|
startReadingFromStream(response, (r.body: any));
|
|
},
|
|
function (e) {
|
|
reportGlobalError(response, e);
|
|
},
|
|
);
|
|
return getRoot(response);
|
|
}
|
|
|
|
function createFromXHR<T>(
|
|
request: XMLHttpRequest,
|
|
options?: Options,
|
|
): Thenable<T> {
|
|
const response: FlightResponse = createResponseFromOptions(options);
|
|
let processedLength = 0;
|
|
function progress(e: ProgressEvent): void {
|
|
const chunk = request.responseText;
|
|
processStringChunk(response, chunk, processedLength);
|
|
processedLength = chunk.length;
|
|
}
|
|
function load(e: ProgressEvent): void {
|
|
progress(e);
|
|
close(response);
|
|
}
|
|
function error(e: ProgressEvent): void {
|
|
reportGlobalError(response, new TypeError('Network error'));
|
|
}
|
|
request.addEventListener('progress', progress);
|
|
request.addEventListener('load', load);
|
|
request.addEventListener('error', error);
|
|
request.addEventListener('abort', error);
|
|
request.addEventListener('timeout', error);
|
|
return getRoot(response);
|
|
}
|
|
|
|
function encodeReply(
|
|
value: ReactServerValue,
|
|
): Promise<
|
|
string | URLSearchParams | FormData,
|
|
> /* We don't use URLSearchParams yet but maybe */ {
|
|
return new Promise((resolve, reject) => {
|
|
processReply(value, '', resolve, reject);
|
|
});
|
|
}
|
|
|
|
export {
|
|
createFromXHR,
|
|
createFromFetch,
|
|
createFromReadableStream,
|
|
encodeReply,
|
|
createServerReference,
|
|
};
|