Skip to content

Commit 9686ab4

Browse files
committed
Split FlightClient into a basic part and a stream part
Same split as the server.
1 parent 2162d25 commit 9686ab4

File tree

6 files changed

+214
-117
lines changed

6 files changed

+214
-117
lines changed

packages/react-client/flight.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
* @flow
88
*/
99

10-
export * from './src/ReactFlightClient';
10+
export * from './src/ReactFlightClientStream';

packages/react-client/src/ReactFlightClient.js

Lines changed: 29 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,19 @@
77
* @flow
88
*/
99

10-
import type {Source, StringDecoder} from './ReactFlightClientHostConfig';
11-
12-
import {
13-
supportsBinaryStreams,
14-
createStringDecoder,
15-
readPartialStringChunk,
16-
readFinalStringChunk,
17-
} from './ReactFlightClientHostConfig';
10+
import type {Source} from './ReactFlightClientHostConfig';
1811

1912
export type ReactModelRoot<T> = {|
2013
model: T,
2114
|};
2215

23-
type JSONValue =
16+
export type JSONValue =
2417
| number
2518
| null
2619
| boolean
2720
| string
28-
| {[key: string]: JSONValue, ...};
21+
| {[key: string]: JSONValue}
22+
| Array<JSONValue>;
2923

3024
const PENDING = 0;
3125
const RESOLVED = 1;
@@ -48,39 +42,25 @@ type ErroredChunk = {|
4842
|};
4943
type Chunk = PendingChunk | ResolvedChunk | ErroredChunk;
5044

51-
type OpaqueResponseWithoutDecoder = {
45+
export type Response = {
5246
source: Source,
5347
partialRow: string,
5448
modelRoot: ReactModelRoot<any>,
5549
chunks: Map<number, Chunk>,
56-
fromJSON: (key: string, value: JSONValue) => any,
57-
...
58-
};
59-
60-
type OpaqueResponse = OpaqueResponseWithoutDecoder & {
61-
stringDecoder: StringDecoder,
62-
...
6350
};
6451

65-
export function createResponse(source: Source): OpaqueResponse {
52+
export function createResponse(source: Source): Response {
6653
let modelRoot: ReactModelRoot<any> = ({}: any);
6754
let rootChunk: Chunk = createPendingChunk();
6855
definePendingProperty(modelRoot, 'model', rootChunk);
6956
let chunks: Map<number, Chunk> = new Map();
7057
chunks.set(0, rootChunk);
71-
72-
let response: OpaqueResponse = (({
58+
let response = {
7359
source,
7460
partialRow: '',
7561
modelRoot,
7662
chunks: chunks,
77-
fromJSON: function(key, value) {
78-
return parseFromJSON(response, this, key, value);
79-
},
80-
}: OpaqueResponseWithoutDecoder): any);
81-
if (supportsBinaryStreams) {
82-
response.stringDecoder = createStringDecoder();
83-
}
63+
};
8464
return response;
8565
}
8666

@@ -138,10 +118,7 @@ function resolveChunk(chunk: Chunk, value: mixed): void {
138118

139119
// Report that any missing chunks in the model is now going to throw this
140120
// error upon read. Also notify any pending promises.
141-
export function reportGlobalError(
142-
response: OpaqueResponse,
143-
error: Error,
144-
): void {
121+
export function reportGlobalError(response: Response, error: Error): void {
145122
response.chunks.forEach(chunk => {
146123
// If this chunk was already resolved or errored, it won't
147124
// trigger an error but if it wasn't then we need to
@@ -168,8 +145,8 @@ function definePendingProperty(
168145
});
169146
}
170147

171-
function parseFromJSON(
172-
response: OpaqueResponse,
148+
export function parseModelFromJSON(
149+
response: Response,
173150
targetObj: Object,
174151
key: string,
175152
value: JSONValue,
@@ -195,12 +172,11 @@ function parseFromJSON(
195172
return value;
196173
}
197174

198-
function resolveJSONRow(
199-
response: OpaqueResponse,
175+
export function resolveModelChunk<T>(
176+
response: Response,
200177
id: number,
201-
json: string,
178+
model: T,
202179
): void {
203-
let model = JSON.parse(json, response.fromJSON);
204180
let chunks = response.chunks;
205181
let chunk = chunks.get(id);
206182
if (!chunk) {
@@ -210,88 +186,31 @@ function resolveJSONRow(
210186
}
211187
}
212188

213-
function processFullRow(response: OpaqueResponse, row: string): void {
214-
if (row === '') {
215-
return;
216-
}
217-
let tag = row[0];
218-
switch (tag) {
219-
case 'J': {
220-
let colon = row.indexOf(':', 1);
221-
let id = parseInt(row.substring(1, colon), 16);
222-
let json = row.substring(colon + 1);
223-
resolveJSONRow(response, id, json);
224-
return;
225-
}
226-
case 'E': {
227-
let colon = row.indexOf(':', 1);
228-
let id = parseInt(row.substring(1, colon), 16);
229-
let json = row.substring(colon + 1);
230-
let errorInfo = JSON.parse(json);
231-
let error = new Error(errorInfo.message);
232-
error.stack = errorInfo.stack;
233-
let chunks = response.chunks;
234-
let chunk = chunks.get(id);
235-
if (!chunk) {
236-
chunks.set(id, createErrorChunk(error));
237-
} else {
238-
triggerErrorOnChunk(chunk, error);
239-
}
240-
return;
241-
}
242-
default: {
243-
// Assume this is the root model.
244-
resolveJSONRow(response, 0, row);
245-
return;
246-
}
247-
}
248-
}
249-
250-
export function processStringChunk(
251-
response: OpaqueResponse,
252-
chunk: string,
253-
offset: number,
254-
): void {
255-
let linebreak = chunk.indexOf('\n', offset);
256-
while (linebreak > -1) {
257-
let fullrow = response.partialRow + chunk.substring(offset, linebreak);
258-
processFullRow(response, fullrow);
259-
response.partialRow = '';
260-
offset = linebreak + 1;
261-
linebreak = chunk.indexOf('\n', offset);
262-
}
263-
response.partialRow += chunk.substring(offset);
264-
}
265-
266-
export function processBinaryChunk(
267-
response: OpaqueResponse,
268-
chunk: Uint8Array,
189+
export function resolveErrorChunk(
190+
response: Response,
191+
id: number,
192+
message: string,
193+
stack: string,
269194
): void {
270-
if (!supportsBinaryStreams) {
271-
throw new Error("This environment don't support binary chunks.");
272-
}
273-
let stringDecoder = response.stringDecoder;
274-
let linebreak = chunk.indexOf(10); // newline
275-
while (linebreak > -1) {
276-
let fullrow =
277-
response.partialRow +
278-
readFinalStringChunk(stringDecoder, chunk.subarray(0, linebreak));
279-
processFullRow(response, fullrow);
280-
response.partialRow = '';
281-
chunk = chunk.subarray(linebreak + 1);
282-
linebreak = chunk.indexOf(10); // newline
195+
let error = new Error(message);
196+
error.stack = stack;
197+
let chunks = response.chunks;
198+
let chunk = chunks.get(id);
199+
if (!chunk) {
200+
chunks.set(id, createErrorChunk(error));
201+
} else {
202+
triggerErrorOnChunk(chunk, error);
283203
}
284-
response.partialRow += readPartialStringChunk(stringDecoder, chunk);
285204
}
286205

287-
export function complete(response: OpaqueResponse): void {
206+
export function complete(response: Response): void {
288207
// In case there are any remaining unresolved chunks, they won't
289208
// be resolved now. So we need to issue an error to those.
290209
// Ideally we should be able to early bail out if we kept a
291210
// ref count of pending chunks.
292211
reportGlobalError(response, new Error('Connection closed.'));
293212
}
294213

295-
export function getModelRoot<T>(response: OpaqueResponse): ReactModelRoot<T> {
214+
export function getModelRoot<T>(response: Response): ReactModelRoot<T> {
296215
return response.modelRoot;
297216
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import type {Response as ResponseBase, JSONValue} from './ReactFlightClient';
11+
12+
import type {Source, StringDecoder} from './ReactFlightClientHostConfig';
13+
14+
import {
15+
createResponse as createResponseImpl,
16+
resolveModelChunk,
17+
resolveErrorChunk,
18+
parseModelFromJSON,
19+
} from './ReactFlightClient';
20+
21+
import {
22+
supportsBinaryStreams,
23+
createStringDecoder,
24+
readPartialStringChunk,
25+
readFinalStringChunk,
26+
} from './ReactFlightClientHostConfig';
27+
28+
export type ReactModelRoot<T> = {|
29+
model: T,
30+
|};
31+
32+
type Response = ResponseBase & {
33+
fromJSON: (key: string, value: JSONValue) => any,
34+
stringDecoder: StringDecoder,
35+
};
36+
37+
export function createResponse(source: Source): Response {
38+
let response: Response = (createResponseImpl(source): any);
39+
response.fromJSON = function(key: string, value: JSONValue) {
40+
return parseModelFromJSON(response, this, key, value);
41+
};
42+
if (supportsBinaryStreams) {
43+
response.stringDecoder = createStringDecoder();
44+
}
45+
return response;
46+
}
47+
48+
function processFullRow(response: Response, row: string): void {
49+
if (row === '') {
50+
return;
51+
}
52+
let tag = row[0];
53+
switch (tag) {
54+
case 'J': {
55+
let colon = row.indexOf(':', 1);
56+
let id = parseInt(row.substring(1, colon), 16);
57+
let json = row.substring(colon + 1);
58+
let model = JSON.parse(json, response.fromJSON);
59+
resolveModelChunk(response, id, model);
60+
return;
61+
}
62+
case 'E': {
63+
let colon = row.indexOf(':', 1);
64+
let id = parseInt(row.substring(1, colon), 16);
65+
let json = row.substring(colon + 1);
66+
let errorInfo = JSON.parse(json);
67+
resolveErrorChunk(response, id, errorInfo.message, errorInfo.stack);
68+
return;
69+
}
70+
default: {
71+
// Assume this is the root model.
72+
let model = JSON.parse(row, response.fromJSON);
73+
resolveModelChunk(response, 0, model);
74+
return;
75+
}
76+
}
77+
}
78+
79+
export function processStringChunk(
80+
response: Response,
81+
chunk: string,
82+
offset: number,
83+
): void {
84+
let linebreak = chunk.indexOf('\n', offset);
85+
while (linebreak > -1) {
86+
let fullrow = response.partialRow + chunk.substring(offset, linebreak);
87+
processFullRow(response, fullrow);
88+
response.partialRow = '';
89+
offset = linebreak + 1;
90+
linebreak = chunk.indexOf('\n', offset);
91+
}
92+
response.partialRow += chunk.substring(offset);
93+
}
94+
95+
export function processBinaryChunk(
96+
response: Response,
97+
chunk: Uint8Array,
98+
): void {
99+
if (!supportsBinaryStreams) {
100+
throw new Error("This environment don't support binary chunks.");
101+
}
102+
let stringDecoder = response.stringDecoder;
103+
let linebreak = chunk.indexOf(10); // newline
104+
while (linebreak > -1) {
105+
let fullrow =
106+
response.partialRow +
107+
readFinalStringChunk(stringDecoder, chunk.subarray(0, linebreak));
108+
processFullRow(response, fullrow);
109+
response.partialRow = '';
110+
chunk = chunk.subarray(linebreak + 1);
111+
linebreak = chunk.indexOf(10); // newline
112+
}
113+
response.partialRow += readPartialStringChunk(stringDecoder, chunk);
114+
}
115+
116+
export {reportGlobalError, complete, getModelRoot} from './ReactFlightClient';

0 commit comments

Comments
 (0)