7
* @flow
8
*/
9
10
-import type {Source, StringDecoder} from './ReactFlightClientHostConfig';
11
-
12
-import {
13
- supportsBinaryStreams,
14
- createStringDecoder,
15
- readPartialStringChunk,
16
- readFinalStringChunk,
17
-} from './ReactFlightClientHostConfig';
18
-
10
export type ReactModelRoot<T> = {|
11
model: T,
12
|};
13
23
-type JSONValue =
14
+export type JSONValue =
15
| number
16
| null
17
| boolean
18
| string
28
- | {[key: string]: JSONValue, ...};
19
+ | {[key: string]: JSONValue}
20
+ | Array<JSONValue>;
21
22
const PENDING = 0;
23
const RESOLVED = 1;
40
|};
41
type Chunk = PendingChunk | ResolvedChunk | ErroredChunk;
42
51
-type OpaqueResponseWithoutDecoder = {
52
- source: Source,
43
+export type Response = {
44
partialRow: string,
45
modelRoot: ReactModelRoot<any>,
46
chunks: Map<number, Chunk>,
56
- fromJSON: (key: string, value: JSONValue) => any,
57
- ...
47
};
48
60
-type OpaqueResponse = OpaqueResponseWithoutDecoder & {
61
- stringDecoder: StringDecoder,
62
- ...
63
-};
64
-
65
-export function createResponse(source: Source): OpaqueResponse {
49
+export function createResponse(): Response {
50
let modelRoot: ReactModelRoot<any> = ({}: any);
51
let rootChunk: Chunk = createPendingChunk();
52
definePendingProperty(modelRoot, 'model', rootChunk);
53
let chunks: Map<number, Chunk> = new Map();
54
chunks.set(0, rootChunk);
71
-
72
- let response: OpaqueResponse = (({
73
- source,
55
+ let response = {
56
partialRow: '',
57
modelRoot,
58
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
- }
59
+ };
60
return response;
61
}
62
114
115
// Report that any missing chunks in the model is now going to throw this
116
// error upon read. Also notify any pending promises.
141
-export function reportGlobalError(
142
- response: OpaqueResponse,
143
- error: Error,
144
-): void {
117
+export function reportGlobalError(response: Response, error: Error): void {
118
response.chunks.forEach(chunk => {
119
// If this chunk was already resolved or errored, it won't
120
// trigger an error but if it wasn't then we need to
141
});
142
}
143
171
-function parseFromJSON(
172
- response: OpaqueResponse,
144
+export function parseModelFromJSON(
145
+ response: Response,
146
targetObj: Object,
147
key: string,
148
value: JSONValue,
168
return value;
169
}
170
198
-function resolveJSONRow(
199
- response: OpaqueResponse,
171
+export function resolveModelChunk<T>(
172
+ response: Response,
173
id: number,
201
- json: string,
174
+ model: T,
175
): void {
203
- let model = JSON.parse(json, response.fromJSON);
176
let chunks = response.chunks;
177
let chunk = chunks.get(id);
178
if (!chunk) {
182
}
183
}
184
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,
185
+export function resolveErrorChunk(
186
+ response: Response,
187
+ id: number,
188
+ message: string,
189
+ stack: string,
190
): 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
191
+ let error = new Error(message);
192
+ error.stack = stack;
193
+ let chunks = response.chunks;
194
+ let chunk = chunks.get(id);
195
+ if (!chunk) {
196
+ chunks.set(id, createErrorChunk(error));
197
+ } else {
198
+ triggerErrorOnChunk(chunk, error);
199
}
284
- response.partialRow += readPartialStringChunk(stringDecoder, chunk);
200
}
201
287
-export function complete(response: OpaqueResponse): void {
202
+export function close(response: Response): void {
203
// In case there are any remaining unresolved chunks, they won't
204
// be resolved now. So we need to issue an error to those.
205
// Ideally we should be able to early bail out if we kept a
207
reportGlobalError(response, new Error('Connection closed.'));
208
}
209
295
-export function getModelRoot<T>(response: OpaqueResponse): ReactModelRoot<T> {
210
+export function getModelRoot<T>(response: Response): ReactModelRoot<T> {
211
return response.modelRoot;
212
}