UNPKG

8.73 kBJavaScriptView Raw
1/** @license React v16.12.0
2 * react-dom-unstable-flight-client.development.js
3 *
4 * Copyright (c) Facebook, Inc. and its affiliates.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE file in the root directory of this source tree.
8 */
9
10'use strict';
11
12
13
14if (process.env.NODE_ENV !== "production") {
15 (function() {
16'use strict';
17
18var supportsBinaryStreams = true;
19function createStringDecoder() {
20 return new TextDecoder();
21}
22var decoderOptions = {
23 stream: true
24};
25function readPartialStringChunk(decoder, buffer) {
26 return decoder.decode(buffer, decoderOptions);
27}
28function readFinalStringChunk(decoder, buffer) {
29 return decoder.decode(buffer);
30}
31
32var PENDING = 0;
33var RESOLVED = 1;
34var ERRORED = 2;
35function createResponse(source) {
36 var modelRoot = {};
37 var rootChunk = createPendingChunk();
38 definePendingProperty(modelRoot, 'model', rootChunk);
39 var chunks = new Map();
40 chunks.set(0, rootChunk);
41 var response = {
42 source: source,
43 partialRow: '',
44 modelRoot: modelRoot,
45 chunks: chunks,
46 fromJSON: function (key, value) {
47 return parseFromJSON(response, this, key, value);
48 }
49 };
50
51 if (supportsBinaryStreams) {
52 response.stringDecoder = createStringDecoder();
53 }
54
55 return response;
56}
57
58function createPendingChunk() {
59 var resolve = null;
60 var promise = new Promise(function (r) {
61 return resolve = r;
62 });
63 return {
64 status: PENDING,
65 value: promise,
66 resolve: resolve
67 };
68}
69
70function createErrorChunk(error) {
71 return {
72 status: ERRORED,
73 value: error,
74 resolve: null
75 };
76}
77
78function triggerErrorOnChunk(chunk, error) {
79 if (chunk.status !== PENDING) {
80 // We already resolved. We didn't expect to see this.
81 return;
82 }
83
84 var resolve = chunk.resolve;
85 var erroredChunk = chunk;
86 erroredChunk.status = ERRORED;
87 erroredChunk.value = error;
88 erroredChunk.resolve = null;
89 resolve();
90}
91
92function createResolvedChunk(value) {
93 return {
94 status: RESOLVED,
95 value: value,
96 resolve: null
97 };
98}
99
100function resolveChunk(chunk, value) {
101 if (chunk.status !== PENDING) {
102 // We already resolved. We didn't expect to see this.
103 return;
104 }
105
106 var resolve = chunk.resolve;
107 var resolvedChunk = chunk;
108 resolvedChunk.status = RESOLVED;
109 resolvedChunk.value = value;
110 resolvedChunk.resolve = null;
111 resolve();
112} // Report that any missing chunks in the model is now going to throw this
113// error upon read. Also notify any pending promises.
114
115
116function reportGlobalError(response, error) {
117 response.chunks.forEach(function (chunk) {
118 // If this chunk was already resolved or errored, it won't
119 // trigger an error but if it wasn't then we need to
120 // because we won't be getting any new data to resolve it.
121 triggerErrorOnChunk(chunk, error);
122 });
123}
124
125function definePendingProperty(object, key, chunk) {
126 Object.defineProperty(object, key, {
127 configurable: false,
128 enumerable: true,
129 get: function () {
130 if (chunk.status === RESOLVED) {
131 return chunk.value;
132 } else {
133 throw chunk.value;
134 }
135 }
136 });
137}
138
139function parseFromJSON(response, targetObj, key, value) {
140 if (typeof value === 'string' && value[0] === '$') {
141 if (value[1] === '$') {
142 // This was an escaped string value.
143 return value.substring(1);
144 } else {
145 var id = parseInt(value.substring(1), 16);
146 var chunks = response.chunks;
147 var chunk = chunks.get(id);
148
149 if (!chunk) {
150 chunk = createPendingChunk();
151 chunks.set(id, chunk);
152 } else if (chunk.status === RESOLVED) {
153 return chunk.value;
154 }
155
156 definePendingProperty(targetObj, key, chunk);
157 return undefined;
158 }
159 }
160
161 return value;
162}
163
164function resolveJSONRow(response, id, json) {
165 var model = JSON.parse(json, response.fromJSON);
166 var chunks = response.chunks;
167 var chunk = chunks.get(id);
168
169 if (!chunk) {
170 chunks.set(id, createResolvedChunk(model));
171 } else {
172 resolveChunk(chunk, model);
173 }
174}
175
176function processFullRow(response, row) {
177 if (row === '') {
178 return;
179 }
180
181 var tag = row[0];
182
183 switch (tag) {
184 case 'J':
185 {
186 var colon = row.indexOf(':', 1);
187 var id = parseInt(row.substring(1, colon), 16);
188 var json = row.substring(colon + 1);
189 resolveJSONRow(response, id, json);
190 return;
191 }
192
193 case 'E':
194 {
195 var _colon = row.indexOf(':', 1);
196
197 var _id = parseInt(row.substring(1, _colon), 16);
198
199 var _json = row.substring(_colon + 1);
200
201 var errorInfo = JSON.parse(_json);
202 var error = new Error(errorInfo.message);
203 error.stack = errorInfo.stack;
204 var chunks = response.chunks;
205 var chunk = chunks.get(_id);
206
207 if (!chunk) {
208 chunks.set(_id, createErrorChunk(error));
209 } else {
210 triggerErrorOnChunk(chunk, error);
211 }
212
213 return;
214 }
215
216 default:
217 {
218 // Assume this is the root model.
219 resolveJSONRow(response, 0, row);
220 return;
221 }
222 }
223}
224
225function processStringChunk(response, chunk, offset) {
226 var linebreak = chunk.indexOf('\n', offset);
227
228 while (linebreak > -1) {
229 var fullrow = response.partialRow + chunk.substring(offset, linebreak);
230 processFullRow(response, fullrow);
231 response.partialRow = '';
232 offset = linebreak + 1;
233 linebreak = chunk.indexOf('\n', offset);
234 }
235
236 response.partialRow += chunk.substring(offset);
237}
238function processBinaryChunk(response, chunk) {
239 if (!supportsBinaryStreams) {
240 throw new Error("This environment don't support binary chunks.");
241 }
242
243 var stringDecoder = response.stringDecoder;
244 var linebreak = chunk.indexOf(10); // newline
245
246 while (linebreak > -1) {
247 var fullrow = response.partialRow + readFinalStringChunk(stringDecoder, chunk.subarray(0, linebreak));
248 processFullRow(response, fullrow);
249 response.partialRow = '';
250 chunk = chunk.subarray(linebreak + 1);
251 linebreak = chunk.indexOf(10); // newline
252 }
253
254 response.partialRow += readPartialStringChunk(stringDecoder, chunk);
255}
256function complete(response) {
257 // In case there are any remaining unresolved chunks, they won't
258 // be resolved now. So we need to issue an error to those.
259 // Ideally we should be able to early bail out if we kept a
260 // ref count of pending chunks.
261 reportGlobalError(response, new Error('Connection closed.'));
262}
263function getModelRoot(response) {
264 return response.modelRoot;
265}
266
267// This file intentionally does *not* have the Flow annotation.
268// Don't add it. See `./inline-typed.js` for an explanation.
269
270function startReadingFromStream(response, stream) {
271 var reader = stream.getReader();
272
273 function progress(_ref) {
274 var done = _ref.done,
275 value = _ref.value;
276
277 if (done) {
278 complete(response);
279 return;
280 }
281
282 var buffer = value;
283 processBinaryChunk(response, buffer);
284 return reader.read().then(progress, error);
285 }
286
287 function error(e) {
288 reportGlobalError(response, e);
289 }
290
291 reader.read().then(progress, error);
292}
293
294function readFromReadableStream(stream) {
295 var response = createResponse(stream);
296 startReadingFromStream(response, stream);
297 return getModelRoot(response);
298}
299
300function readFromFetch(promiseForResponse) {
301 var response = createResponse(promiseForResponse);
302 promiseForResponse.then(function (r) {
303 startReadingFromStream(response, r.body);
304 }, function (e) {
305 reportGlobalError(response, e);
306 });
307 return getModelRoot(response);
308}
309
310function readFromXHR(request) {
311 var response = createResponse(request);
312 var processedLength = 0;
313
314 function progress(e) {
315 var chunk = request.responseText;
316 processStringChunk(response, chunk, processedLength);
317 processedLength = chunk.length;
318 }
319
320 function load(e) {
321 progress(e);
322 complete(response);
323 }
324
325 function error(e) {
326 reportGlobalError(response, new TypeError('Network error'));
327 }
328
329 request.addEventListener('progress', progress);
330 request.addEventListener('load', load);
331 request.addEventListener('error', error);
332 request.addEventListener('abort', error);
333 request.addEventListener('timeout', error);
334 return getModelRoot(response);
335}
336
337var ReactFlightDOMClient = {
338 readFromXHR: readFromXHR,
339 readFromFetch: readFromFetch,
340 readFromReadableStream: readFromReadableStream
341};
342
343var ReactFlightDOMClient$1 = Object.freeze({
344 default: ReactFlightDOMClient
345});
346
347var ReactFlightDOMClient$2 = ( ReactFlightDOMClient$1 && ReactFlightDOMClient ) || ReactFlightDOMClient$1;
348
349// TODO: decide on the top-level export form.
350// This is hacky but makes it work with both Rollup and Jest
351
352
353var unstableFlightClient = ReactFlightDOMClient$2.default || ReactFlightDOMClient$2;
354
355module.exports = unstableFlightClient;
356 })();
357}