vite-plugin-react-server 1.2.5 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +8 -4
- package/dist/plugin/orchestrator/createPluginOrchestrator.client.d.ts.map +1 -1
- package/dist/plugin/orchestrator/createPluginOrchestrator.client.js +3 -0
- package/dist/plugin/orchestrator/createPluginOrchestrator.server.d.ts.map +1 -1
- package/dist/plugin/orchestrator/createPluginOrchestrator.server.js +3 -0
- package/dist/plugin/vendor/register-vendor.d.ts +2 -0
- package/dist/plugin/vendor/register-vendor.d.ts.map +1 -0
- package/dist/plugin/vendor/register-vendor.js +50 -0
- package/dist/plugin/vendor/vendor-alias.d.ts +12 -0
- package/dist/plugin/vendor/vendor-alias.d.ts.map +1 -0
- package/dist/plugin/vendor/vendor-alias.js +97 -0
- package/dist/plugin/vendor/vendor.client.d.ts +1 -1
- package/dist/plugin/vendor/vendor.client.d.ts.map +1 -1
- package/dist/plugin/vendor/vendor.client.js +19 -6
- package/dist/plugin/vendor/vendor.server.d.ts.map +1 -1
- package/dist/plugin/vendor/vendor.server.js +18 -5
- package/dist/plugin/vendor/vendor.static.d.ts.map +1 -1
- package/dist/plugin/vendor/vendor.static.js +18 -5
- package/dist/plugin/worker/createWorker.d.ts.map +1 -1
- package/dist/plugin/worker/createWorker.js +5 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/oss-experimental/react-server-dom-esm/LICENSE +21 -0
- package/oss-experimental/react-server-dom-esm/README.md +5 -0
- package/oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +2996 -0
- package/oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.js +1612 -0
- package/oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +3124 -0
- package/oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.js +1752 -0
- package/oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +4213 -0
- package/oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js +2857 -0
- package/oss-experimental/react-server-dom-esm/client.browser.js +7 -0
- package/oss-experimental/react-server-dom-esm/client.js +3 -0
- package/oss-experimental/react-server-dom-esm/client.node.js +7 -0
- package/oss-experimental/react-server-dom-esm/esm/package.json +3 -0
- package/oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +3969 -0
- package/oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js +2347 -0
- package/oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.js +515 -0
- package/oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-server.js +13 -0
- package/oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-server.node.js +13 -0
- package/oss-experimental/react-server-dom-esm/index.js +12 -0
- package/oss-experimental/react-server-dom-esm/package.json +63 -0
- package/oss-experimental/react-server-dom-esm/server.js +6 -0
- package/oss-experimental/react-server-dom-esm/server.node.js +17 -0
- package/oss-experimental/react-server-dom-esm/static.js +6 -0
- package/oss-experimental/react-server-dom-esm/static.node.js +12 -0
- package/package.json +8 -4
- package/plugin/orchestrator/createPluginOrchestrator.client.ts +4 -0
- package/plugin/orchestrator/createPluginOrchestrator.server.ts +4 -0
- package/plugin/vendor/register-vendor.ts +51 -0
- package/plugin/vendor/vendor-alias.ts +112 -0
- package/plugin/vendor/vendor.client.ts +24 -9
- package/plugin/vendor/vendor.server.ts +23 -7
- package/plugin/vendor/vendor.static.ts +21 -7
- package/plugin/worker/createWorker.ts +4 -0
- package/scripts/build-oss-experimental.sh +161 -0
package/oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.js
ADDED
|
@@ -0,0 +1,2347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license React
|
|
3
|
+
* react-server-dom-esm-client.browser.production.js
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as ReactDOM from 'react-dom';
|
|
12
|
+
|
|
13
|
+
function createStringDecoder() {
|
|
14
|
+
return new TextDecoder();
|
|
15
|
+
}
|
|
16
|
+
const decoderOptions = {
|
|
17
|
+
stream: true
|
|
18
|
+
};
|
|
19
|
+
function readPartialStringChunk(decoder, buffer) {
|
|
20
|
+
return decoder.decode(buffer, decoderOptions);
|
|
21
|
+
}
|
|
22
|
+
function readFinalStringChunk(decoder, buffer) {
|
|
23
|
+
return decoder.decode(buffer);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Module root path
|
|
27
|
+
|
|
28
|
+
function resolveClientReference(bundlerConfig, metadata) {
|
|
29
|
+
const baseURL = bundlerConfig;
|
|
30
|
+
return {
|
|
31
|
+
specifier: baseURL + metadata[0],
|
|
32
|
+
name: metadata[1]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function resolveServerReference(config, id) {
|
|
36
|
+
const baseURL = config;
|
|
37
|
+
const idx = id.lastIndexOf('#');
|
|
38
|
+
const exportName = id.slice(idx + 1);
|
|
39
|
+
const fullURL = id.slice(0, idx);
|
|
40
|
+
if (!fullURL.startsWith(baseURL)) {
|
|
41
|
+
throw new Error('Attempted to load a Server Reference outside the hosted root.');
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
specifier: fullURL,
|
|
45
|
+
name: exportName
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const asyncModuleCache = new Map();
|
|
49
|
+
function preloadModule(metadata) {
|
|
50
|
+
const existingPromise = asyncModuleCache.get(metadata.specifier);
|
|
51
|
+
if (existingPromise) {
|
|
52
|
+
if (existingPromise.status === 'fulfilled') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return existingPromise;
|
|
56
|
+
} else {
|
|
57
|
+
// $FlowFixMe[unsupported-syntax]
|
|
58
|
+
const modulePromise = import(metadata.specifier);
|
|
59
|
+
modulePromise.then(value => {
|
|
60
|
+
const fulfilledThenable = modulePromise;
|
|
61
|
+
fulfilledThenable.status = 'fulfilled';
|
|
62
|
+
fulfilledThenable.value = value;
|
|
63
|
+
}, reason => {
|
|
64
|
+
const rejectedThenable = modulePromise;
|
|
65
|
+
rejectedThenable.status = 'rejected';
|
|
66
|
+
rejectedThenable.reason = reason;
|
|
67
|
+
});
|
|
68
|
+
asyncModuleCache.set(metadata.specifier, modulePromise);
|
|
69
|
+
return modulePromise;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function requireModule(metadata) {
|
|
73
|
+
let moduleExports;
|
|
74
|
+
// We assume that preloadModule has been called before, which
|
|
75
|
+
// should have added something to the module cache.
|
|
76
|
+
const promise = asyncModuleCache.get(metadata.specifier);
|
|
77
|
+
if (promise.status === 'fulfilled') {
|
|
78
|
+
moduleExports = promise.value;
|
|
79
|
+
} else {
|
|
80
|
+
throw promise.reason;
|
|
81
|
+
}
|
|
82
|
+
return moduleExports[metadata.name];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const ReactDOMSharedInternals = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
86
|
+
|
|
87
|
+
// This client file is in the shared folder because it applies to both SSR and browser contexts.
|
|
88
|
+
// It is the configuration of the FlightClient behavior which can run in either environment.
|
|
89
|
+
|
|
90
|
+
function dispatchHint(code, model) {
|
|
91
|
+
const dispatcher = ReactDOMSharedInternals.d; /* ReactDOMCurrentDispatcher */
|
|
92
|
+
switch (code) {
|
|
93
|
+
case 'D':
|
|
94
|
+
{
|
|
95
|
+
const refined = refineModel(code, model);
|
|
96
|
+
const href = refined;
|
|
97
|
+
dispatcher.D(/* prefetchDNS */href);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
case 'C':
|
|
101
|
+
{
|
|
102
|
+
const refined = refineModel(code, model);
|
|
103
|
+
if (typeof refined === 'string') {
|
|
104
|
+
const href = refined;
|
|
105
|
+
dispatcher.C(/* preconnect */href);
|
|
106
|
+
} else {
|
|
107
|
+
const href = refined[0];
|
|
108
|
+
const crossOrigin = refined[1];
|
|
109
|
+
dispatcher.C(/* preconnect */href, crossOrigin);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
case 'L':
|
|
114
|
+
{
|
|
115
|
+
const refined = refineModel(code, model);
|
|
116
|
+
const href = refined[0];
|
|
117
|
+
const as = refined[1];
|
|
118
|
+
if (refined.length === 3) {
|
|
119
|
+
const options = refined[2];
|
|
120
|
+
dispatcher.L(/* preload */href, as, options);
|
|
121
|
+
} else {
|
|
122
|
+
dispatcher.L(/* preload */href, as);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
case 'm':
|
|
127
|
+
{
|
|
128
|
+
const refined = refineModel(code, model);
|
|
129
|
+
if (typeof refined === 'string') {
|
|
130
|
+
const href = refined;
|
|
131
|
+
dispatcher.m(/* preloadModule */href);
|
|
132
|
+
} else {
|
|
133
|
+
const href = refined[0];
|
|
134
|
+
const options = refined[1];
|
|
135
|
+
dispatcher.m(/* preloadModule */href, options);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
case 'X':
|
|
140
|
+
{
|
|
141
|
+
const refined = refineModel(code, model);
|
|
142
|
+
if (typeof refined === 'string') {
|
|
143
|
+
const href = refined;
|
|
144
|
+
dispatcher.X(/* preinitScript */href);
|
|
145
|
+
} else {
|
|
146
|
+
const href = refined[0];
|
|
147
|
+
const options = refined[1];
|
|
148
|
+
dispatcher.X(/* preinitScript */href, options);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
case 'S':
|
|
153
|
+
{
|
|
154
|
+
const refined = refineModel(code, model);
|
|
155
|
+
if (typeof refined === 'string') {
|
|
156
|
+
const href = refined;
|
|
157
|
+
dispatcher.S(/* preinitStyle */href);
|
|
158
|
+
} else {
|
|
159
|
+
const href = refined[0];
|
|
160
|
+
const precedence = refined[1] === 0 ? undefined : refined[1];
|
|
161
|
+
const options = refined.length === 3 ? refined[2] : undefined;
|
|
162
|
+
dispatcher.S(/* preinitStyle */href, precedence, options);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
case 'M':
|
|
167
|
+
{
|
|
168
|
+
const refined = refineModel(code, model);
|
|
169
|
+
if (typeof refined === 'string') {
|
|
170
|
+
const href = refined;
|
|
171
|
+
dispatcher.M(/* preinitModuleScript */href);
|
|
172
|
+
} else {
|
|
173
|
+
const href = refined[0];
|
|
174
|
+
const options = refined[1];
|
|
175
|
+
dispatcher.M(/* preinitModuleScript */href, options);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Flow is having trouble refining the HintModels so we help it a bit.
|
|
183
|
+
// This should be compiled out in the production build.
|
|
184
|
+
function refineModel(code, model) {
|
|
185
|
+
return model;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const REACT_ELEMENT_TYPE = Symbol.for('react.transitional.element') ;
|
|
189
|
+
const REACT_LAZY_TYPE = Symbol.for('react.lazy');
|
|
190
|
+
const REACT_POSTPONE_TYPE = Symbol.for('react.postpone');
|
|
191
|
+
const MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
|
192
|
+
const FAUX_ITERATOR_SYMBOL = '@@iterator';
|
|
193
|
+
function getIteratorFn(maybeIterable) {
|
|
194
|
+
if (maybeIterable === null || typeof maybeIterable !== 'object') {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
|
|
198
|
+
if (typeof maybeIterator === 'function') {
|
|
199
|
+
return maybeIterator;
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const ASYNC_ITERATOR = Symbol.asyncIterator;
|
|
204
|
+
|
|
205
|
+
const isArrayImpl = Array.isArray;
|
|
206
|
+
function isArray(a) {
|
|
207
|
+
return isArrayImpl(a);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
211
|
+
|
|
212
|
+
function createTemporaryReferenceSet() {
|
|
213
|
+
return new Map();
|
|
214
|
+
}
|
|
215
|
+
function writeTemporaryReference(set, reference, object) {
|
|
216
|
+
set.set(reference, object);
|
|
217
|
+
}
|
|
218
|
+
function readTemporaryReference(set, reference) {
|
|
219
|
+
return set.get(reference);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const ObjectPrototype = Object.prototype;
|
|
223
|
+
const knownServerReferences = new WeakMap();
|
|
224
|
+
|
|
225
|
+
// Serializable values
|
|
226
|
+
|
|
227
|
+
// Thenable<ReactServerValue>
|
|
228
|
+
|
|
229
|
+
function serializeByValueID(id) {
|
|
230
|
+
return '$' + id.toString(16);
|
|
231
|
+
}
|
|
232
|
+
function serializePromiseID(id) {
|
|
233
|
+
return '$@' + id.toString(16);
|
|
234
|
+
}
|
|
235
|
+
function serializeServerReferenceID(id) {
|
|
236
|
+
return '$F' + id.toString(16);
|
|
237
|
+
}
|
|
238
|
+
function serializeTemporaryReferenceMarker() {
|
|
239
|
+
return '$T';
|
|
240
|
+
}
|
|
241
|
+
function serializeFormDataReference(id) {
|
|
242
|
+
// Why K? F is "Function". D is "Date". What else?
|
|
243
|
+
return '$K' + id.toString(16);
|
|
244
|
+
}
|
|
245
|
+
function serializeNumber(number) {
|
|
246
|
+
if (Number.isFinite(number)) {
|
|
247
|
+
if (number === 0 && 1 / number === -Infinity) {
|
|
248
|
+
return '$-0';
|
|
249
|
+
} else {
|
|
250
|
+
return number;
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
if (number === Infinity) {
|
|
254
|
+
return '$Infinity';
|
|
255
|
+
} else if (number === -Infinity) {
|
|
256
|
+
return '$-Infinity';
|
|
257
|
+
} else {
|
|
258
|
+
return '$NaN';
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function serializeUndefined() {
|
|
263
|
+
return '$undefined';
|
|
264
|
+
}
|
|
265
|
+
function serializeDateFromDateJSON(dateJSON) {
|
|
266
|
+
// JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString.
|
|
267
|
+
// We need only tack on a $D prefix.
|
|
268
|
+
return '$D' + dateJSON;
|
|
269
|
+
}
|
|
270
|
+
function serializeBigInt(n) {
|
|
271
|
+
return '$n' + n.toString(10);
|
|
272
|
+
}
|
|
273
|
+
function serializeMapID(id) {
|
|
274
|
+
return '$Q' + id.toString(16);
|
|
275
|
+
}
|
|
276
|
+
function serializeSetID(id) {
|
|
277
|
+
return '$W' + id.toString(16);
|
|
278
|
+
}
|
|
279
|
+
function serializeBlobID(id) {
|
|
280
|
+
return '$B' + id.toString(16);
|
|
281
|
+
}
|
|
282
|
+
function serializeIteratorID(id) {
|
|
283
|
+
return '$i' + id.toString(16);
|
|
284
|
+
}
|
|
285
|
+
function escapeStringValue(value) {
|
|
286
|
+
if (value[0] === '$') {
|
|
287
|
+
// We need to escape $ prefixed strings since we use those to encode
|
|
288
|
+
// references to IDs and as special symbol values.
|
|
289
|
+
return '$' + value;
|
|
290
|
+
} else {
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function processReply(root, formFieldPrefix, temporaryReferences, resolve, reject) {
|
|
295
|
+
let nextPartId = 1;
|
|
296
|
+
let pendingParts = 0;
|
|
297
|
+
let formData = null;
|
|
298
|
+
const writtenObjects = new WeakMap();
|
|
299
|
+
let modelRoot = root;
|
|
300
|
+
function serializeTypedArray(tag, typedArray) {
|
|
301
|
+
const blob = new Blob([
|
|
302
|
+
// We should be able to pass the buffer straight through but Node < 18 treat
|
|
303
|
+
// multi-byte array blobs differently so we first convert it to single-byte.
|
|
304
|
+
new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength)]);
|
|
305
|
+
const blobId = nextPartId++;
|
|
306
|
+
if (formData === null) {
|
|
307
|
+
formData = new FormData();
|
|
308
|
+
}
|
|
309
|
+
formData.append(formFieldPrefix + blobId, blob);
|
|
310
|
+
return '$' + tag + blobId.toString(16);
|
|
311
|
+
}
|
|
312
|
+
function serializeBinaryReader(reader) {
|
|
313
|
+
if (formData === null) {
|
|
314
|
+
// Upgrade to use FormData to allow us to stream this value.
|
|
315
|
+
formData = new FormData();
|
|
316
|
+
}
|
|
317
|
+
const data = formData;
|
|
318
|
+
pendingParts++;
|
|
319
|
+
const streamId = nextPartId++;
|
|
320
|
+
const buffer = [];
|
|
321
|
+
function progress(entry) {
|
|
322
|
+
if (entry.done) {
|
|
323
|
+
const blobId = nextPartId++;
|
|
324
|
+
data.append(formFieldPrefix + blobId, new Blob(buffer));
|
|
325
|
+
data.append(formFieldPrefix + streamId, '"$o' + blobId.toString(16) + '"');
|
|
326
|
+
data.append(formFieldPrefix + streamId, 'C'); // Close signal
|
|
327
|
+
pendingParts--;
|
|
328
|
+
if (pendingParts === 0) {
|
|
329
|
+
resolve(data);
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
buffer.push(entry.value);
|
|
333
|
+
reader.read(new Uint8Array(1024)).then(progress, reject);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
reader.read(new Uint8Array(1024)).then(progress, reject);
|
|
337
|
+
return '$r' + streamId.toString(16);
|
|
338
|
+
}
|
|
339
|
+
function serializeReader(reader) {
|
|
340
|
+
if (formData === null) {
|
|
341
|
+
// Upgrade to use FormData to allow us to stream this value.
|
|
342
|
+
formData = new FormData();
|
|
343
|
+
}
|
|
344
|
+
const data = formData;
|
|
345
|
+
pendingParts++;
|
|
346
|
+
const streamId = nextPartId++;
|
|
347
|
+
function progress(entry) {
|
|
348
|
+
if (entry.done) {
|
|
349
|
+
data.append(formFieldPrefix + streamId, 'C'); // Close signal
|
|
350
|
+
pendingParts--;
|
|
351
|
+
if (pendingParts === 0) {
|
|
352
|
+
resolve(data);
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
try {
|
|
356
|
+
// $FlowFixMe[incompatible-type]: While plain JSON can return undefined we never do here.
|
|
357
|
+
const partJSON = JSON.stringify(entry.value, resolveToJSON);
|
|
358
|
+
data.append(formFieldPrefix + streamId, partJSON);
|
|
359
|
+
reader.read().then(progress, reject);
|
|
360
|
+
} catch (x) {
|
|
361
|
+
reject(x);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
reader.read().then(progress, reject);
|
|
366
|
+
return '$R' + streamId.toString(16);
|
|
367
|
+
}
|
|
368
|
+
function serializeReadableStream(stream) {
|
|
369
|
+
// Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the
|
|
370
|
+
// receiving side. For binary streams, we serialize them as plain Blobs.
|
|
371
|
+
let binaryReader;
|
|
372
|
+
try {
|
|
373
|
+
// $FlowFixMe[extra-arg]: This argument is accepted.
|
|
374
|
+
binaryReader = stream.getReader({
|
|
375
|
+
mode: 'byob'
|
|
376
|
+
});
|
|
377
|
+
} catch (x) {
|
|
378
|
+
return serializeReader(stream.getReader());
|
|
379
|
+
}
|
|
380
|
+
return serializeBinaryReader(binaryReader);
|
|
381
|
+
}
|
|
382
|
+
function serializeAsyncIterable(iterable, iterator) {
|
|
383
|
+
if (formData === null) {
|
|
384
|
+
// Upgrade to use FormData to allow us to stream this value.
|
|
385
|
+
formData = new FormData();
|
|
386
|
+
}
|
|
387
|
+
const data = formData;
|
|
388
|
+
pendingParts++;
|
|
389
|
+
const streamId = nextPartId++;
|
|
390
|
+
|
|
391
|
+
// Generators/Iterators are Iterables but they're also their own iterator
|
|
392
|
+
// functions. If that's the case, we treat them as single-shot. Otherwise,
|
|
393
|
+
// we assume that this iterable might be a multi-shot and allow it to be
|
|
394
|
+
// iterated more than once on the receiving server.
|
|
395
|
+
const isIterator = iterable === iterator;
|
|
396
|
+
|
|
397
|
+
// There's a race condition between when the stream is aborted and when the promise
|
|
398
|
+
// resolves so we track whether we already aborted it to avoid writing twice.
|
|
399
|
+
function progress(entry) {
|
|
400
|
+
if (entry.done) {
|
|
401
|
+
if (entry.value === undefined) {
|
|
402
|
+
data.append(formFieldPrefix + streamId, 'C'); // Close signal
|
|
403
|
+
} else {
|
|
404
|
+
// Unlike streams, the last value may not be undefined. If it's not
|
|
405
|
+
// we outline it and encode a reference to it in the closing instruction.
|
|
406
|
+
try {
|
|
407
|
+
// $FlowFixMe[incompatible-type]: While plain JSON can return undefined we never do here.
|
|
408
|
+
const partJSON = JSON.stringify(entry.value, resolveToJSON);
|
|
409
|
+
data.append(formFieldPrefix + streamId, 'C' + partJSON); // Close signal
|
|
410
|
+
} catch (x) {
|
|
411
|
+
reject(x);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
pendingParts--;
|
|
416
|
+
if (pendingParts === 0) {
|
|
417
|
+
resolve(data);
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
try {
|
|
421
|
+
// $FlowFixMe[incompatible-type]: While plain JSON can return undefined we never do here.
|
|
422
|
+
const partJSON = JSON.stringify(entry.value, resolveToJSON);
|
|
423
|
+
data.append(formFieldPrefix + streamId, partJSON);
|
|
424
|
+
iterator.next().then(progress, reject);
|
|
425
|
+
} catch (x) {
|
|
426
|
+
reject(x);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
iterator.next().then(progress, reject);
|
|
432
|
+
return '$' + (isIterator ? 'x' : 'X') + streamId.toString(16);
|
|
433
|
+
}
|
|
434
|
+
function resolveToJSON(key, value) {
|
|
435
|
+
const parent = this;
|
|
436
|
+
if (value === null) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
if (typeof value === 'object') {
|
|
440
|
+
switch (value.$$typeof) {
|
|
441
|
+
case REACT_ELEMENT_TYPE:
|
|
442
|
+
{
|
|
443
|
+
if (temporaryReferences !== undefined && key.indexOf(':') === -1) {
|
|
444
|
+
// TODO: If the property name contains a colon, we don't dedupe. Escape instead.
|
|
445
|
+
const parentReference = writtenObjects.get(parent);
|
|
446
|
+
if (parentReference !== undefined) {
|
|
447
|
+
// If the parent has a reference, we can refer to this object indirectly
|
|
448
|
+
// through the property name inside that parent.
|
|
449
|
+
const reference = parentReference + ':' + key;
|
|
450
|
+
// Store this object so that the server can refer to it later in responses.
|
|
451
|
+
writeTemporaryReference(temporaryReferences, reference, value);
|
|
452
|
+
return serializeTemporaryReferenceMarker();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
throw new Error('React Element cannot be passed to Server Functions from the Client without a ' + 'temporary reference set. Pass a TemporaryReferenceSet to the options.' + (''));
|
|
456
|
+
}
|
|
457
|
+
case REACT_LAZY_TYPE:
|
|
458
|
+
{
|
|
459
|
+
// Resolve lazy as if it wasn't here. In the future this will be encoded as a Promise.
|
|
460
|
+
const lazy = value;
|
|
461
|
+
const payload = lazy._payload;
|
|
462
|
+
const init = lazy._init;
|
|
463
|
+
if (formData === null) {
|
|
464
|
+
// Upgrade to use FormData to allow us to stream this value.
|
|
465
|
+
formData = new FormData();
|
|
466
|
+
}
|
|
467
|
+
pendingParts++;
|
|
468
|
+
try {
|
|
469
|
+
const resolvedModel = init(payload);
|
|
470
|
+
// We always outline this as a separate part even though we could inline it
|
|
471
|
+
// because it ensures a more deterministic encoding.
|
|
472
|
+
const lazyId = nextPartId++;
|
|
473
|
+
const partJSON = serializeModel(resolvedModel, lazyId);
|
|
474
|
+
// $FlowFixMe[incompatible-type] We know it's not null because we assigned it above.
|
|
475
|
+
const data = formData;
|
|
476
|
+
data.append(formFieldPrefix + lazyId, partJSON);
|
|
477
|
+
return serializeByValueID(lazyId);
|
|
478
|
+
} catch (x) {
|
|
479
|
+
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
|
|
480
|
+
// Suspended
|
|
481
|
+
pendingParts++;
|
|
482
|
+
const lazyId = nextPartId++;
|
|
483
|
+
const thenable = x;
|
|
484
|
+
const retry = function () {
|
|
485
|
+
// While the first promise resolved, its value isn't necessarily what we'll
|
|
486
|
+
// resolve into because we might suspend again.
|
|
487
|
+
try {
|
|
488
|
+
const partJSON = serializeModel(value, lazyId);
|
|
489
|
+
// $FlowFixMe[incompatible-type] We know it's not null because we assigned it above.
|
|
490
|
+
const data = formData;
|
|
491
|
+
data.append(formFieldPrefix + lazyId, partJSON);
|
|
492
|
+
pendingParts--;
|
|
493
|
+
if (pendingParts === 0) {
|
|
494
|
+
resolve(data);
|
|
495
|
+
}
|
|
496
|
+
} catch (reason) {
|
|
497
|
+
reject(reason);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
thenable.then(retry, retry);
|
|
501
|
+
return serializeByValueID(lazyId);
|
|
502
|
+
} else {
|
|
503
|
+
// In the future we could consider serializing this as an error
|
|
504
|
+
// that throws on the server instead.
|
|
505
|
+
reject(x);
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
} finally {
|
|
509
|
+
pendingParts--;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// $FlowFixMe[method-unbinding]
|
|
515
|
+
if (typeof value.then === 'function') {
|
|
516
|
+
// We assume that any object with a .then property is a "Thenable" type,
|
|
517
|
+
// or a Promise type. Either of which can be represented by a Promise.
|
|
518
|
+
if (formData === null) {
|
|
519
|
+
// Upgrade to use FormData to allow us to stream this value.
|
|
520
|
+
formData = new FormData();
|
|
521
|
+
}
|
|
522
|
+
pendingParts++;
|
|
523
|
+
const promiseId = nextPartId++;
|
|
524
|
+
const thenable = value;
|
|
525
|
+
thenable.then(partValue => {
|
|
526
|
+
try {
|
|
527
|
+
const partJSON = serializeModel(partValue, promiseId);
|
|
528
|
+
// $FlowFixMe[incompatible-type] We know it's not null because we assigned it above.
|
|
529
|
+
const data = formData;
|
|
530
|
+
data.append(formFieldPrefix + promiseId, partJSON);
|
|
531
|
+
pendingParts--;
|
|
532
|
+
if (pendingParts === 0) {
|
|
533
|
+
resolve(data);
|
|
534
|
+
}
|
|
535
|
+
} catch (reason) {
|
|
536
|
+
reject(reason);
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
// In the future we could consider serializing this as an error
|
|
540
|
+
// that throws on the server instead.
|
|
541
|
+
reject);
|
|
542
|
+
return serializePromiseID(promiseId);
|
|
543
|
+
}
|
|
544
|
+
const existingReference = writtenObjects.get(value);
|
|
545
|
+
if (existingReference !== undefined) {
|
|
546
|
+
if (modelRoot === value) {
|
|
547
|
+
// This is the ID we're currently emitting so we need to write it
|
|
548
|
+
// once but if we discover it again, we refer to it by id.
|
|
549
|
+
modelRoot = null;
|
|
550
|
+
} else {
|
|
551
|
+
// We've already emitted this as an outlined object, so we can
|
|
552
|
+
// just refer to that by its existing ID.
|
|
553
|
+
return existingReference;
|
|
554
|
+
}
|
|
555
|
+
} else if (key.indexOf(':') === -1) {
|
|
556
|
+
// TODO: If the property name contains a colon, we don't dedupe. Escape instead.
|
|
557
|
+
const parentReference = writtenObjects.get(parent);
|
|
558
|
+
if (parentReference !== undefined) {
|
|
559
|
+
// If the parent has a reference, we can refer to this object indirectly
|
|
560
|
+
// through the property name inside that parent.
|
|
561
|
+
const reference = parentReference + ':' + key;
|
|
562
|
+
writtenObjects.set(value, reference);
|
|
563
|
+
if (temporaryReferences !== undefined) {
|
|
564
|
+
// Store this object so that the server can refer to it later in responses.
|
|
565
|
+
writeTemporaryReference(temporaryReferences, reference, value);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (isArray(value)) {
|
|
570
|
+
// $FlowFixMe[incompatible-return]
|
|
571
|
+
return value;
|
|
572
|
+
}
|
|
573
|
+
// TODO: Should we the Object.prototype.toString.call() to test for cross-realm objects?
|
|
574
|
+
if (value instanceof FormData) {
|
|
575
|
+
if (formData === null) {
|
|
576
|
+
// Upgrade to use FormData to allow us to use rich objects as its values.
|
|
577
|
+
formData = new FormData();
|
|
578
|
+
}
|
|
579
|
+
const data = formData;
|
|
580
|
+
const refId = nextPartId++;
|
|
581
|
+
// Copy all the form fields with a prefix for this reference.
|
|
582
|
+
// These must come first in the form order because we assume that all the
|
|
583
|
+
// fields are available before this is referenced.
|
|
584
|
+
const prefix = formFieldPrefix + refId + '_';
|
|
585
|
+
// $FlowFixMe[prop-missing]: FormData has forEach.
|
|
586
|
+
value.forEach((originalValue, originalKey) => {
|
|
587
|
+
// $FlowFixMe[incompatible-call]
|
|
588
|
+
data.append(prefix + originalKey, originalValue);
|
|
589
|
+
});
|
|
590
|
+
return serializeFormDataReference(refId);
|
|
591
|
+
}
|
|
592
|
+
if (value instanceof Map) {
|
|
593
|
+
const mapId = nextPartId++;
|
|
594
|
+
const partJSON = serializeModel(Array.from(value), mapId);
|
|
595
|
+
if (formData === null) {
|
|
596
|
+
formData = new FormData();
|
|
597
|
+
}
|
|
598
|
+
formData.append(formFieldPrefix + mapId, partJSON);
|
|
599
|
+
return serializeMapID(mapId);
|
|
600
|
+
}
|
|
601
|
+
if (value instanceof Set) {
|
|
602
|
+
const setId = nextPartId++;
|
|
603
|
+
const partJSON = serializeModel(Array.from(value), setId);
|
|
604
|
+
if (formData === null) {
|
|
605
|
+
formData = new FormData();
|
|
606
|
+
}
|
|
607
|
+
formData.append(formFieldPrefix + setId, partJSON);
|
|
608
|
+
return serializeSetID(setId);
|
|
609
|
+
}
|
|
610
|
+
if (value instanceof ArrayBuffer) {
|
|
611
|
+
const blob = new Blob([value]);
|
|
612
|
+
const blobId = nextPartId++;
|
|
613
|
+
if (formData === null) {
|
|
614
|
+
formData = new FormData();
|
|
615
|
+
}
|
|
616
|
+
formData.append(formFieldPrefix + blobId, blob);
|
|
617
|
+
return '$' + 'A' + blobId.toString(16);
|
|
618
|
+
}
|
|
619
|
+
if (value instanceof Int8Array) {
|
|
620
|
+
// char
|
|
621
|
+
return serializeTypedArray('O', value);
|
|
622
|
+
}
|
|
623
|
+
if (value instanceof Uint8Array) {
|
|
624
|
+
// unsigned char
|
|
625
|
+
return serializeTypedArray('o', value);
|
|
626
|
+
}
|
|
627
|
+
if (value instanceof Uint8ClampedArray) {
|
|
628
|
+
// unsigned clamped char
|
|
629
|
+
return serializeTypedArray('U', value);
|
|
630
|
+
}
|
|
631
|
+
if (value instanceof Int16Array) {
|
|
632
|
+
// sort
|
|
633
|
+
return serializeTypedArray('S', value);
|
|
634
|
+
}
|
|
635
|
+
if (value instanceof Uint16Array) {
|
|
636
|
+
// unsigned short
|
|
637
|
+
return serializeTypedArray('s', value);
|
|
638
|
+
}
|
|
639
|
+
if (value instanceof Int32Array) {
|
|
640
|
+
// long
|
|
641
|
+
return serializeTypedArray('L', value);
|
|
642
|
+
}
|
|
643
|
+
if (value instanceof Uint32Array) {
|
|
644
|
+
// unsigned long
|
|
645
|
+
return serializeTypedArray('l', value);
|
|
646
|
+
}
|
|
647
|
+
if (value instanceof Float32Array) {
|
|
648
|
+
// float
|
|
649
|
+
return serializeTypedArray('G', value);
|
|
650
|
+
}
|
|
651
|
+
if (value instanceof Float64Array) {
|
|
652
|
+
// double
|
|
653
|
+
return serializeTypedArray('g', value);
|
|
654
|
+
}
|
|
655
|
+
if (value instanceof BigInt64Array) {
|
|
656
|
+
// number
|
|
657
|
+
return serializeTypedArray('M', value);
|
|
658
|
+
}
|
|
659
|
+
if (value instanceof BigUint64Array) {
|
|
660
|
+
// unsigned number
|
|
661
|
+
// We use "m" instead of "n" since JSON can start with "null"
|
|
662
|
+
return serializeTypedArray('m', value);
|
|
663
|
+
}
|
|
664
|
+
if (value instanceof DataView) {
|
|
665
|
+
return serializeTypedArray('V', value);
|
|
666
|
+
}
|
|
667
|
+
// TODO: Blob is not available in old Node/browsers. Remove the typeof check later.
|
|
668
|
+
if (typeof Blob === 'function' && value instanceof Blob) {
|
|
669
|
+
if (formData === null) {
|
|
670
|
+
formData = new FormData();
|
|
671
|
+
}
|
|
672
|
+
const blobId = nextPartId++;
|
|
673
|
+
formData.append(formFieldPrefix + blobId, value);
|
|
674
|
+
return serializeBlobID(blobId);
|
|
675
|
+
}
|
|
676
|
+
const iteratorFn = getIteratorFn(value);
|
|
677
|
+
if (iteratorFn) {
|
|
678
|
+
const iterator = iteratorFn.call(value);
|
|
679
|
+
if (iterator === value) {
|
|
680
|
+
// Iterator, not Iterable
|
|
681
|
+
const iteratorId = nextPartId++;
|
|
682
|
+
const partJSON = serializeModel(Array.from(iterator), iteratorId);
|
|
683
|
+
if (formData === null) {
|
|
684
|
+
formData = new FormData();
|
|
685
|
+
}
|
|
686
|
+
formData.append(formFieldPrefix + iteratorId, partJSON);
|
|
687
|
+
return serializeIteratorID(iteratorId);
|
|
688
|
+
}
|
|
689
|
+
return Array.from(iterator);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// TODO: ReadableStream is not available in old Node. Remove the typeof check later.
|
|
693
|
+
if (typeof ReadableStream === 'function' && value instanceof ReadableStream) {
|
|
694
|
+
return serializeReadableStream(value);
|
|
695
|
+
}
|
|
696
|
+
const getAsyncIterator = value[ASYNC_ITERATOR];
|
|
697
|
+
if (typeof getAsyncIterator === 'function') {
|
|
698
|
+
// We treat AsyncIterables as a Fragment and as such we might need to key them.
|
|
699
|
+
return serializeAsyncIterable(value, getAsyncIterator.call(value));
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Verify that this is a simple plain object.
|
|
703
|
+
const proto = getPrototypeOf(value);
|
|
704
|
+
if (proto !== ObjectPrototype && (proto === null || getPrototypeOf(proto) !== null)) {
|
|
705
|
+
if (temporaryReferences === undefined) {
|
|
706
|
+
throw new Error('Only plain objects, and a few built-ins, can be passed to Server Functions. ' + 'Classes or null prototypes are not supported.' + (''));
|
|
707
|
+
}
|
|
708
|
+
// We will have written this object to the temporary reference set above
|
|
709
|
+
// so we can replace it with a marker to refer to this slot later.
|
|
710
|
+
return serializeTemporaryReferenceMarker();
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// $FlowFixMe[incompatible-return]
|
|
714
|
+
return value;
|
|
715
|
+
}
|
|
716
|
+
if (typeof value === 'string') {
|
|
717
|
+
// TODO: Maybe too clever. If we support URL there's no similar trick.
|
|
718
|
+
if (value[value.length - 1] === 'Z') {
|
|
719
|
+
// Possibly a Date, whose toJSON automatically calls toISOString
|
|
720
|
+
// $FlowFixMe[incompatible-use]
|
|
721
|
+
const originalValue = parent[key];
|
|
722
|
+
if (originalValue instanceof Date) {
|
|
723
|
+
return serializeDateFromDateJSON(value);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return escapeStringValue(value);
|
|
727
|
+
}
|
|
728
|
+
if (typeof value === 'boolean') {
|
|
729
|
+
return value;
|
|
730
|
+
}
|
|
731
|
+
if (typeof value === 'number') {
|
|
732
|
+
return serializeNumber(value);
|
|
733
|
+
}
|
|
734
|
+
if (typeof value === 'undefined') {
|
|
735
|
+
return serializeUndefined();
|
|
736
|
+
}
|
|
737
|
+
if (typeof value === 'function') {
|
|
738
|
+
const referenceClosure = knownServerReferences.get(value);
|
|
739
|
+
if (referenceClosure !== undefined) {
|
|
740
|
+
const id = referenceClosure.id,
|
|
741
|
+
bound = referenceClosure.bound;
|
|
742
|
+
const referenceClosureJSON = JSON.stringify({
|
|
743
|
+
id,
|
|
744
|
+
bound
|
|
745
|
+
}, resolveToJSON);
|
|
746
|
+
if (formData === null) {
|
|
747
|
+
// Upgrade to use FormData to allow us to stream this value.
|
|
748
|
+
formData = new FormData();
|
|
749
|
+
}
|
|
750
|
+
// The reference to this function came from the same client so we can pass it back.
|
|
751
|
+
const refId = nextPartId++;
|
|
752
|
+
formData.set(formFieldPrefix + refId, referenceClosureJSON);
|
|
753
|
+
return serializeServerReferenceID(refId);
|
|
754
|
+
}
|
|
755
|
+
if (temporaryReferences !== undefined && key.indexOf(':') === -1) {
|
|
756
|
+
// TODO: If the property name contains a colon, we don't dedupe. Escape instead.
|
|
757
|
+
const parentReference = writtenObjects.get(parent);
|
|
758
|
+
if (parentReference !== undefined) {
|
|
759
|
+
// If the parent has a reference, we can refer to this object indirectly
|
|
760
|
+
// through the property name inside that parent.
|
|
761
|
+
const reference = parentReference + ':' + key;
|
|
762
|
+
// Store this object so that the server can refer to it later in responses.
|
|
763
|
+
writeTemporaryReference(temporaryReferences, reference, value);
|
|
764
|
+
return serializeTemporaryReferenceMarker();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
throw new Error('Client Functions cannot be passed directly to Server Functions. ' + 'Only Functions passed from the Server can be passed back again.');
|
|
768
|
+
}
|
|
769
|
+
if (typeof value === 'symbol') {
|
|
770
|
+
if (temporaryReferences !== undefined && key.indexOf(':') === -1) {
|
|
771
|
+
// TODO: If the property name contains a colon, we don't dedupe. Escape instead.
|
|
772
|
+
const parentReference = writtenObjects.get(parent);
|
|
773
|
+
if (parentReference !== undefined) {
|
|
774
|
+
// If the parent has a reference, we can refer to this object indirectly
|
|
775
|
+
// through the property name inside that parent.
|
|
776
|
+
const reference = parentReference + ':' + key;
|
|
777
|
+
// Store this object so that the server can refer to it later in responses.
|
|
778
|
+
writeTemporaryReference(temporaryReferences, reference, value);
|
|
779
|
+
return serializeTemporaryReferenceMarker();
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
throw new Error('Symbols cannot be passed to a Server Function without a ' + 'temporary reference set. Pass a TemporaryReferenceSet to the options.' + (''));
|
|
783
|
+
}
|
|
784
|
+
if (typeof value === 'bigint') {
|
|
785
|
+
return serializeBigInt(value);
|
|
786
|
+
}
|
|
787
|
+
throw new Error("Type " + typeof value + " is not supported as an argument to a Server Function.");
|
|
788
|
+
}
|
|
789
|
+
function serializeModel(model, id) {
|
|
790
|
+
if (typeof model === 'object' && model !== null) {
|
|
791
|
+
const reference = serializeByValueID(id);
|
|
792
|
+
writtenObjects.set(model, reference);
|
|
793
|
+
if (temporaryReferences !== undefined) {
|
|
794
|
+
// Store this object so that the server can refer to it later in responses.
|
|
795
|
+
writeTemporaryReference(temporaryReferences, reference, model);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
modelRoot = model;
|
|
799
|
+
// $FlowFixMe[incompatible-return] it's not going to be undefined because we'll encode it.
|
|
800
|
+
return JSON.stringify(model, resolveToJSON);
|
|
801
|
+
}
|
|
802
|
+
function abort(reason) {
|
|
803
|
+
if (pendingParts > 0) {
|
|
804
|
+
pendingParts = 0; // Don't resolve again later.
|
|
805
|
+
// Resolve with what we have so far, which may have holes at this point.
|
|
806
|
+
// They'll error when the stream completes on the server.
|
|
807
|
+
if (formData === null) {
|
|
808
|
+
resolve(json);
|
|
809
|
+
} else {
|
|
810
|
+
resolve(formData);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const json = serializeModel(root, 0);
|
|
815
|
+
if (formData === null) {
|
|
816
|
+
// If it's a simple data structure, we just use plain JSON.
|
|
817
|
+
resolve(json);
|
|
818
|
+
} else {
|
|
819
|
+
// Otherwise, we use FormData to let us stream in the result.
|
|
820
|
+
formData.set(formFieldPrefix + '0', json);
|
|
821
|
+
if (pendingParts === 0) {
|
|
822
|
+
// $FlowFixMe[incompatible-call] this has already been refined.
|
|
823
|
+
resolve(formData);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return abort;
|
|
827
|
+
}
|
|
828
|
+
function registerBoundServerReference(reference, id, bound, encodeFormAction) {
|
|
829
|
+
if (knownServerReferences.has(reference)) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
knownServerReferences.set(reference, {
|
|
833
|
+
id,
|
|
834
|
+
originalBind: reference.bind,
|
|
835
|
+
bound
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
function registerServerReference(reference, id, encodeFormAction) {
|
|
839
|
+
registerBoundServerReference(reference, id, null);
|
|
840
|
+
return reference;
|
|
841
|
+
}
|
|
842
|
+
function createBoundServerReference(metaData, callServer, encodeFormAction, findSourceMapURL // DEV-only
|
|
843
|
+
) {
|
|
844
|
+
const id = metaData.id;
|
|
845
|
+
const bound = metaData.bound;
|
|
846
|
+
const action = function () {
|
|
847
|
+
// $FlowFixMe[method-unbinding]
|
|
848
|
+
const args = Array.prototype.slice.call(arguments);
|
|
849
|
+
const p = bound;
|
|
850
|
+
if (!p) {
|
|
851
|
+
return callServer(id, args);
|
|
852
|
+
}
|
|
853
|
+
if (p.status === 'fulfilled') {
|
|
854
|
+
const boundArgs = p.value;
|
|
855
|
+
return callServer(id, boundArgs.concat(args));
|
|
856
|
+
}
|
|
857
|
+
// Since this is a fake Promise whose .then doesn't chain, we have to wrap it.
|
|
858
|
+
// TODO: Remove the wrapper once that's fixed.
|
|
859
|
+
return Promise.resolve(p).then(function (boundArgs) {
|
|
860
|
+
return callServer(id, boundArgs.concat(args));
|
|
861
|
+
});
|
|
862
|
+
};
|
|
863
|
+
registerBoundServerReference(action, id, bound);
|
|
864
|
+
return action;
|
|
865
|
+
}
|
|
866
|
+
function createServerReference(id, callServer, encodeFormAction, findSourceMapURL,
|
|
867
|
+
// DEV-only
|
|
868
|
+
functionName) {
|
|
869
|
+
const action = function () {
|
|
870
|
+
// $FlowFixMe[method-unbinding]
|
|
871
|
+
const args = Array.prototype.slice.call(arguments);
|
|
872
|
+
return callServer(id, args);
|
|
873
|
+
};
|
|
874
|
+
registerBoundServerReference(action, id, null);
|
|
875
|
+
return action;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const ROW_ID = 0;
|
|
879
|
+
const ROW_TAG = 1;
|
|
880
|
+
const ROW_LENGTH = 2;
|
|
881
|
+
const ROW_CHUNK_BY_NEWLINE = 3;
|
|
882
|
+
const ROW_CHUNK_BY_LENGTH = 4;
|
|
883
|
+
const PENDING = 'pending';
|
|
884
|
+
const BLOCKED = 'blocked';
|
|
885
|
+
const RESOLVED_MODEL = 'resolved_model';
|
|
886
|
+
const RESOLVED_MODULE = 'resolved_module';
|
|
887
|
+
const INITIALIZED = 'fulfilled';
|
|
888
|
+
const ERRORED = 'rejected';
|
|
889
|
+
|
|
890
|
+
// $FlowFixMe[missing-this-annot]
|
|
891
|
+
function ReactPromise(status, value, reason, response) {
|
|
892
|
+
this.status = status;
|
|
893
|
+
this.value = value;
|
|
894
|
+
this.reason = reason;
|
|
895
|
+
this._response = response;
|
|
896
|
+
}
|
|
897
|
+
// We subclass Promise.prototype so that we get other methods like .catch
|
|
898
|
+
ReactPromise.prototype = Object.create(Promise.prototype);
|
|
899
|
+
// TODO: This doesn't return a new Promise chain unlike the real .then
|
|
900
|
+
ReactPromise.prototype.then = function (resolve, reject) {
|
|
901
|
+
const chunk = this;
|
|
902
|
+
// If we have resolved content, we try to initialize it first which
|
|
903
|
+
// might put us back into one of the other states.
|
|
904
|
+
switch (chunk.status) {
|
|
905
|
+
case RESOLVED_MODEL:
|
|
906
|
+
initializeModelChunk(chunk);
|
|
907
|
+
break;
|
|
908
|
+
case RESOLVED_MODULE:
|
|
909
|
+
initializeModuleChunk(chunk);
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
// The status might have changed after initialization.
|
|
913
|
+
switch (chunk.status) {
|
|
914
|
+
case INITIALIZED:
|
|
915
|
+
resolve(chunk.value);
|
|
916
|
+
break;
|
|
917
|
+
case PENDING:
|
|
918
|
+
case BLOCKED:
|
|
919
|
+
if (resolve) {
|
|
920
|
+
if (chunk.value === null) {
|
|
921
|
+
chunk.value = [];
|
|
922
|
+
}
|
|
923
|
+
chunk.value.push(resolve);
|
|
924
|
+
}
|
|
925
|
+
if (reject) {
|
|
926
|
+
if (chunk.reason === null) {
|
|
927
|
+
chunk.reason = [];
|
|
928
|
+
}
|
|
929
|
+
chunk.reason.push(reject);
|
|
930
|
+
}
|
|
931
|
+
break;
|
|
932
|
+
default:
|
|
933
|
+
if (reject) {
|
|
934
|
+
reject(chunk.reason);
|
|
935
|
+
}
|
|
936
|
+
break;
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
function readChunk(chunk) {
|
|
940
|
+
// If we have resolved content, we try to initialize it first which
|
|
941
|
+
// might put us back into one of the other states.
|
|
942
|
+
switch (chunk.status) {
|
|
943
|
+
case RESOLVED_MODEL:
|
|
944
|
+
initializeModelChunk(chunk);
|
|
945
|
+
break;
|
|
946
|
+
case RESOLVED_MODULE:
|
|
947
|
+
initializeModuleChunk(chunk);
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
// The status might have changed after initialization.
|
|
951
|
+
switch (chunk.status) {
|
|
952
|
+
case INITIALIZED:
|
|
953
|
+
return chunk.value;
|
|
954
|
+
case PENDING:
|
|
955
|
+
case BLOCKED:
|
|
956
|
+
|
|
957
|
+
throw chunk;
|
|
958
|
+
default:
|
|
959
|
+
throw chunk.reason;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function getRoot(response) {
|
|
963
|
+
const chunk = getChunk(response, 0);
|
|
964
|
+
return chunk;
|
|
965
|
+
}
|
|
966
|
+
function createPendingChunk(response) {
|
|
967
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
968
|
+
return new ReactPromise(PENDING, null, null, response);
|
|
969
|
+
}
|
|
970
|
+
function createBlockedChunk(response) {
|
|
971
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
972
|
+
return new ReactPromise(BLOCKED, null, null, response);
|
|
973
|
+
}
|
|
974
|
+
function createErrorChunk(response, error) {
|
|
975
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
976
|
+
return new ReactPromise(ERRORED, null, error, response);
|
|
977
|
+
}
|
|
978
|
+
function wakeChunk(listeners, value) {
|
|
979
|
+
for (let i = 0; i < listeners.length; i++) {
|
|
980
|
+
const listener = listeners[i];
|
|
981
|
+
listener(value);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners) {
|
|
985
|
+
switch (chunk.status) {
|
|
986
|
+
case INITIALIZED:
|
|
987
|
+
wakeChunk(resolveListeners, chunk.value);
|
|
988
|
+
break;
|
|
989
|
+
case PENDING:
|
|
990
|
+
case BLOCKED:
|
|
991
|
+
if (chunk.value) {
|
|
992
|
+
for (let i = 0; i < resolveListeners.length; i++) {
|
|
993
|
+
chunk.value.push(resolveListeners[i]);
|
|
994
|
+
}
|
|
995
|
+
} else {
|
|
996
|
+
chunk.value = resolveListeners;
|
|
997
|
+
}
|
|
998
|
+
if (chunk.reason) {
|
|
999
|
+
if (rejectListeners) {
|
|
1000
|
+
for (let i = 0; i < rejectListeners.length; i++) {
|
|
1001
|
+
chunk.reason.push(rejectListeners[i]);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
} else {
|
|
1005
|
+
chunk.reason = rejectListeners;
|
|
1006
|
+
}
|
|
1007
|
+
break;
|
|
1008
|
+
case ERRORED:
|
|
1009
|
+
if (rejectListeners) {
|
|
1010
|
+
wakeChunk(rejectListeners, chunk.reason);
|
|
1011
|
+
}
|
|
1012
|
+
break;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function triggerErrorOnChunk(chunk, error) {
|
|
1016
|
+
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
|
|
1017
|
+
// If we get more data to an already resolved ID, we assume that it's
|
|
1018
|
+
// a stream chunk since any other row shouldn't have more than one entry.
|
|
1019
|
+
const streamChunk = chunk;
|
|
1020
|
+
const controller = streamChunk.reason;
|
|
1021
|
+
// $FlowFixMe[incompatible-call]: The error method should accept mixed.
|
|
1022
|
+
controller.error(error);
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
const listeners = chunk.reason;
|
|
1026
|
+
const erroredChunk = chunk;
|
|
1027
|
+
erroredChunk.status = ERRORED;
|
|
1028
|
+
erroredChunk.reason = error;
|
|
1029
|
+
if (listeners !== null) {
|
|
1030
|
+
wakeChunk(listeners, error);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function createResolvedModelChunk(response, value) {
|
|
1034
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1035
|
+
return new ReactPromise(RESOLVED_MODEL, value, null, response);
|
|
1036
|
+
}
|
|
1037
|
+
function createResolvedModuleChunk(response, value) {
|
|
1038
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1039
|
+
return new ReactPromise(RESOLVED_MODULE, value, null, response);
|
|
1040
|
+
}
|
|
1041
|
+
function createInitializedTextChunk(response, value) {
|
|
1042
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1043
|
+
return new ReactPromise(INITIALIZED, value, null, response);
|
|
1044
|
+
}
|
|
1045
|
+
function createInitializedBufferChunk(response, value) {
|
|
1046
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1047
|
+
return new ReactPromise(INITIALIZED, value, null, response);
|
|
1048
|
+
}
|
|
1049
|
+
function createInitializedIteratorResultChunk(response, value, done) {
|
|
1050
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1051
|
+
return new ReactPromise(INITIALIZED, {
|
|
1052
|
+
done: done,
|
|
1053
|
+
value: value
|
|
1054
|
+
}, null, response);
|
|
1055
|
+
}
|
|
1056
|
+
function createInitializedStreamChunk(response, value, controller) {
|
|
1057
|
+
// We use the reason field to stash the controller since we already have that
|
|
1058
|
+
// field. It's a bit of a hack but efficient.
|
|
1059
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1060
|
+
return new ReactPromise(INITIALIZED, value, controller, response);
|
|
1061
|
+
}
|
|
1062
|
+
function createResolvedIteratorResultChunk(response, value, done) {
|
|
1063
|
+
// To reuse code as much code as possible we add the wrapper element as part of the JSON.
|
|
1064
|
+
const iteratorResultJSON = (done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';
|
|
1065
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1066
|
+
return new ReactPromise(RESOLVED_MODEL, iteratorResultJSON, null, response);
|
|
1067
|
+
}
|
|
1068
|
+
function resolveIteratorResultChunk(chunk, value, done) {
|
|
1069
|
+
// To reuse code as much code as possible we add the wrapper element as part of the JSON.
|
|
1070
|
+
const iteratorResultJSON = (done ? '{"done":true,"value":' : '{"done":false,"value":') + value + '}';
|
|
1071
|
+
resolveModelChunk(chunk, iteratorResultJSON);
|
|
1072
|
+
}
|
|
1073
|
+
function resolveModelChunk(chunk, value) {
|
|
1074
|
+
if (chunk.status !== PENDING) {
|
|
1075
|
+
// If we get more data to an already resolved ID, we assume that it's
|
|
1076
|
+
// a stream chunk since any other row shouldn't have more than one entry.
|
|
1077
|
+
const streamChunk = chunk;
|
|
1078
|
+
const controller = streamChunk.reason;
|
|
1079
|
+
controller.enqueueModel(value);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const resolveListeners = chunk.value;
|
|
1083
|
+
const rejectListeners = chunk.reason;
|
|
1084
|
+
const resolvedChunk = chunk;
|
|
1085
|
+
resolvedChunk.status = RESOLVED_MODEL;
|
|
1086
|
+
resolvedChunk.value = value;
|
|
1087
|
+
if (resolveListeners !== null) {
|
|
1088
|
+
// This is unfortunate that we're reading this eagerly if
|
|
1089
|
+
// we already have listeners attached since they might no
|
|
1090
|
+
// longer be rendered or might not be the highest pri.
|
|
1091
|
+
initializeModelChunk(resolvedChunk);
|
|
1092
|
+
// The status might have changed after initialization.
|
|
1093
|
+
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
function resolveModuleChunk(chunk, value) {
|
|
1097
|
+
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
|
|
1098
|
+
// We already resolved. We didn't expect to see this.
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const resolveListeners = chunk.value;
|
|
1102
|
+
const rejectListeners = chunk.reason;
|
|
1103
|
+
const resolvedChunk = chunk;
|
|
1104
|
+
resolvedChunk.status = RESOLVED_MODULE;
|
|
1105
|
+
resolvedChunk.value = value;
|
|
1106
|
+
if (resolveListeners !== null) {
|
|
1107
|
+
initializeModuleChunk(resolvedChunk);
|
|
1108
|
+
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
let initializingHandler = null;
|
|
1112
|
+
function initializeModelChunk(chunk) {
|
|
1113
|
+
const prevHandler = initializingHandler;
|
|
1114
|
+
initializingHandler = null;
|
|
1115
|
+
const resolvedModel = chunk.value;
|
|
1116
|
+
|
|
1117
|
+
// We go to the BLOCKED state until we've fully resolved this.
|
|
1118
|
+
// We do this before parsing in case we try to initialize the same chunk
|
|
1119
|
+
// while parsing the model. Such as in a cyclic reference.
|
|
1120
|
+
const cyclicChunk = chunk;
|
|
1121
|
+
cyclicChunk.status = BLOCKED;
|
|
1122
|
+
cyclicChunk.value = null;
|
|
1123
|
+
cyclicChunk.reason = null;
|
|
1124
|
+
try {
|
|
1125
|
+
const value = parseModel(chunk._response, resolvedModel);
|
|
1126
|
+
// Invoke any listeners added while resolving this model. I.e. cyclic
|
|
1127
|
+
// references. This may or may not fully resolve the model depending on
|
|
1128
|
+
// if they were blocked.
|
|
1129
|
+
const resolveListeners = cyclicChunk.value;
|
|
1130
|
+
if (resolveListeners !== null) {
|
|
1131
|
+
cyclicChunk.value = null;
|
|
1132
|
+
cyclicChunk.reason = null;
|
|
1133
|
+
wakeChunk(resolveListeners, value);
|
|
1134
|
+
}
|
|
1135
|
+
if (initializingHandler !== null) {
|
|
1136
|
+
if (initializingHandler.errored) {
|
|
1137
|
+
throw initializingHandler.value;
|
|
1138
|
+
}
|
|
1139
|
+
if (initializingHandler.deps > 0) {
|
|
1140
|
+
// We discovered new dependencies on modules that are not yet resolved.
|
|
1141
|
+
// We have to keep the BLOCKED state until they're resolved.
|
|
1142
|
+
initializingHandler.value = value;
|
|
1143
|
+
initializingHandler.chunk = cyclicChunk;
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const initializedChunk = chunk;
|
|
1148
|
+
initializedChunk.status = INITIALIZED;
|
|
1149
|
+
initializedChunk.value = value;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
const erroredChunk = chunk;
|
|
1152
|
+
erroredChunk.status = ERRORED;
|
|
1153
|
+
erroredChunk.reason = error;
|
|
1154
|
+
} finally {
|
|
1155
|
+
initializingHandler = prevHandler;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function initializeModuleChunk(chunk) {
|
|
1159
|
+
try {
|
|
1160
|
+
const value = requireModule(chunk.value);
|
|
1161
|
+
const initializedChunk = chunk;
|
|
1162
|
+
initializedChunk.status = INITIALIZED;
|
|
1163
|
+
initializedChunk.value = value;
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
const erroredChunk = chunk;
|
|
1166
|
+
erroredChunk.status = ERRORED;
|
|
1167
|
+
erroredChunk.reason = error;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// Report that any missing chunks in the model is now going to throw this
|
|
1172
|
+
// error upon read. Also notify any pending promises.
|
|
1173
|
+
function reportGlobalError(response, error) {
|
|
1174
|
+
response._closed = true;
|
|
1175
|
+
response._closedReason = error;
|
|
1176
|
+
response._chunks.forEach(chunk => {
|
|
1177
|
+
// If this chunk was already resolved or errored, it won't
|
|
1178
|
+
// trigger an error but if it wasn't then we need to
|
|
1179
|
+
// because we won't be getting any new data to resolve it.
|
|
1180
|
+
if (chunk.status === PENDING) {
|
|
1181
|
+
triggerErrorOnChunk(chunk, error);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
function createElement(response, type, key, props, owner,
|
|
1186
|
+
// DEV-only
|
|
1187
|
+
stack,
|
|
1188
|
+
// DEV-only
|
|
1189
|
+
validated // DEV-only
|
|
1190
|
+
) {
|
|
1191
|
+
let element;
|
|
1192
|
+
{
|
|
1193
|
+
element = {
|
|
1194
|
+
// This tag allows us to uniquely identify this as a React Element
|
|
1195
|
+
$$typeof: REACT_ELEMENT_TYPE,
|
|
1196
|
+
type,
|
|
1197
|
+
key,
|
|
1198
|
+
ref: null,
|
|
1199
|
+
props
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
if (initializingHandler !== null) {
|
|
1203
|
+
const handler = initializingHandler;
|
|
1204
|
+
// We pop the stack to the previous outer handler before leaving the Element.
|
|
1205
|
+
// This is effectively the complete phase.
|
|
1206
|
+
initializingHandler = handler.parent;
|
|
1207
|
+
if (handler.errored) {
|
|
1208
|
+
// Something errored inside this Element's props. We can turn this Element
|
|
1209
|
+
// into a Lazy so that we can still render up until that Lazy is rendered.
|
|
1210
|
+
const erroredChunk = createErrorChunk(response, handler.value);
|
|
1211
|
+
return createLazyChunkWrapper(erroredChunk);
|
|
1212
|
+
}
|
|
1213
|
+
if (handler.deps > 0) {
|
|
1214
|
+
// We have blocked references inside this Element but we can turn this into
|
|
1215
|
+
// a Lazy node referencing this Element to let everything around it proceed.
|
|
1216
|
+
const blockedChunk = createBlockedChunk(response);
|
|
1217
|
+
handler.value = element;
|
|
1218
|
+
handler.chunk = blockedChunk;
|
|
1219
|
+
return createLazyChunkWrapper(blockedChunk);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return element;
|
|
1223
|
+
}
|
|
1224
|
+
function createLazyChunkWrapper(chunk) {
|
|
1225
|
+
const lazyType = {
|
|
1226
|
+
$$typeof: REACT_LAZY_TYPE,
|
|
1227
|
+
_payload: chunk,
|
|
1228
|
+
_init: readChunk
|
|
1229
|
+
};
|
|
1230
|
+
return lazyType;
|
|
1231
|
+
}
|
|
1232
|
+
function getChunk(response, id) {
|
|
1233
|
+
const chunks = response._chunks;
|
|
1234
|
+
let chunk = chunks.get(id);
|
|
1235
|
+
if (!chunk) {
|
|
1236
|
+
if (response._closed) {
|
|
1237
|
+
// We have already errored the response and we're not going to get
|
|
1238
|
+
// anything more streaming in so this will immediately error.
|
|
1239
|
+
chunk = createErrorChunk(response, response._closedReason);
|
|
1240
|
+
} else {
|
|
1241
|
+
chunk = createPendingChunk(response);
|
|
1242
|
+
}
|
|
1243
|
+
chunks.set(id, chunk);
|
|
1244
|
+
}
|
|
1245
|
+
return chunk;
|
|
1246
|
+
}
|
|
1247
|
+
function waitForReference(referencedChunk, parentObject, key, response, map, path) {
|
|
1248
|
+
let handler;
|
|
1249
|
+
if (initializingHandler) {
|
|
1250
|
+
handler = initializingHandler;
|
|
1251
|
+
handler.deps++;
|
|
1252
|
+
} else {
|
|
1253
|
+
handler = initializingHandler = {
|
|
1254
|
+
parent: null,
|
|
1255
|
+
chunk: null,
|
|
1256
|
+
value: null,
|
|
1257
|
+
deps: 1,
|
|
1258
|
+
errored: false
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function fulfill(value) {
|
|
1262
|
+
for (let i = 1; i < path.length; i++) {
|
|
1263
|
+
while (value.$$typeof === REACT_LAZY_TYPE) {
|
|
1264
|
+
// We never expect to see a Lazy node on this path because we encode those as
|
|
1265
|
+
// separate models. This must mean that we have inserted an extra lazy node
|
|
1266
|
+
// e.g. to replace a blocked element. We must instead look for it inside.
|
|
1267
|
+
const chunk = value._payload;
|
|
1268
|
+
if (chunk === handler.chunk) {
|
|
1269
|
+
// This is a reference to the thing we're currently blocking. We can peak
|
|
1270
|
+
// inside of it to get the value.
|
|
1271
|
+
value = handler.value;
|
|
1272
|
+
continue;
|
|
1273
|
+
} else if (chunk.status === INITIALIZED) {
|
|
1274
|
+
value = chunk.value;
|
|
1275
|
+
continue;
|
|
1276
|
+
} else {
|
|
1277
|
+
// If we're not yet initialized we need to skip what we've already drilled
|
|
1278
|
+
// through and then wait for the next value to become available.
|
|
1279
|
+
path.splice(0, i - 1);
|
|
1280
|
+
chunk.then(fulfill, reject);
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
value = value[path[i]];
|
|
1285
|
+
}
|
|
1286
|
+
const mappedValue = map(response, value, parentObject, key);
|
|
1287
|
+
parentObject[key] = mappedValue;
|
|
1288
|
+
|
|
1289
|
+
// If this is the root object for a model reference, where `handler.value`
|
|
1290
|
+
// is a stale `null`, the resolved value can be used directly.
|
|
1291
|
+
if (key === '' && handler.value === null) {
|
|
1292
|
+
handler.value = mappedValue;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// If the parent object is an unparsed React element tuple, we also need to
|
|
1296
|
+
// update the props and owner of the parsed element object (i.e.
|
|
1297
|
+
// handler.value).
|
|
1298
|
+
if (parentObject[0] === REACT_ELEMENT_TYPE && typeof handler.value === 'object' && handler.value !== null && handler.value.$$typeof === REACT_ELEMENT_TYPE) {
|
|
1299
|
+
const element = handler.value;
|
|
1300
|
+
switch (key) {
|
|
1301
|
+
case '3':
|
|
1302
|
+
element.props = mappedValue;
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
handler.deps--;
|
|
1307
|
+
if (handler.deps === 0) {
|
|
1308
|
+
const chunk = handler.chunk;
|
|
1309
|
+
if (chunk === null || chunk.status !== BLOCKED) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
const resolveListeners = chunk.value;
|
|
1313
|
+
const initializedChunk = chunk;
|
|
1314
|
+
initializedChunk.status = INITIALIZED;
|
|
1315
|
+
initializedChunk.value = handler.value;
|
|
1316
|
+
if (resolveListeners !== null) {
|
|
1317
|
+
wakeChunk(resolveListeners, handler.value);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
function reject(error) {
|
|
1322
|
+
if (handler.errored) {
|
|
1323
|
+
// We've already errored. We could instead build up an AggregateError
|
|
1324
|
+
// but if there are multiple errors we just take the first one like
|
|
1325
|
+
// Promise.all.
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
handler.errored = true;
|
|
1329
|
+
handler.value = error;
|
|
1330
|
+
const chunk = handler.chunk;
|
|
1331
|
+
if (chunk === null || chunk.status !== BLOCKED) {
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
triggerErrorOnChunk(chunk, error);
|
|
1335
|
+
}
|
|
1336
|
+
referencedChunk.then(fulfill, reject);
|
|
1337
|
+
|
|
1338
|
+
// Return a place holder value for now.
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
function loadServerReference(response, metaData, parentObject, key) {
|
|
1342
|
+
if (!response._serverReferenceConfig) {
|
|
1343
|
+
// In the normal case, we can't load this Server Reference in the current environment and
|
|
1344
|
+
// we just return a proxy to it.
|
|
1345
|
+
return createBoundServerReference(metaData, response._callServer);
|
|
1346
|
+
}
|
|
1347
|
+
// If we have a module mapping we can load the real version of this Server Reference.
|
|
1348
|
+
const serverReference = resolveServerReference(response._serverReferenceConfig, metaData.id);
|
|
1349
|
+
let promise = preloadModule(serverReference);
|
|
1350
|
+
if (!promise) {
|
|
1351
|
+
if (!metaData.bound) {
|
|
1352
|
+
const resolvedValue = requireModule(serverReference);
|
|
1353
|
+
registerBoundServerReference(resolvedValue, metaData.id, metaData.bound);
|
|
1354
|
+
return resolvedValue;
|
|
1355
|
+
} else {
|
|
1356
|
+
promise = Promise.resolve(metaData.bound);
|
|
1357
|
+
}
|
|
1358
|
+
} else if (metaData.bound) {
|
|
1359
|
+
promise = Promise.all([promise, metaData.bound]);
|
|
1360
|
+
}
|
|
1361
|
+
let handler;
|
|
1362
|
+
if (initializingHandler) {
|
|
1363
|
+
handler = initializingHandler;
|
|
1364
|
+
handler.deps++;
|
|
1365
|
+
} else {
|
|
1366
|
+
handler = initializingHandler = {
|
|
1367
|
+
parent: null,
|
|
1368
|
+
chunk: null,
|
|
1369
|
+
value: null,
|
|
1370
|
+
deps: 1,
|
|
1371
|
+
errored: false
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
function fulfill() {
|
|
1375
|
+
let resolvedValue = requireModule(serverReference);
|
|
1376
|
+
if (metaData.bound) {
|
|
1377
|
+
// This promise is coming from us and should have initilialized by now.
|
|
1378
|
+
const boundArgs = metaData.bound.value.slice(0);
|
|
1379
|
+
boundArgs.unshift(null); // this
|
|
1380
|
+
resolvedValue = resolvedValue.bind.apply(resolvedValue, boundArgs);
|
|
1381
|
+
}
|
|
1382
|
+
registerBoundServerReference(resolvedValue, metaData.id, metaData.bound);
|
|
1383
|
+
parentObject[key] = resolvedValue;
|
|
1384
|
+
|
|
1385
|
+
// If this is the root object for a model reference, where `handler.value`
|
|
1386
|
+
// is a stale `null`, the resolved value can be used directly.
|
|
1387
|
+
if (key === '' && handler.value === null) {
|
|
1388
|
+
handler.value = resolvedValue;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// If the parent object is an unparsed React element tuple, we also need to
|
|
1392
|
+
// update the props and owner of the parsed element object (i.e.
|
|
1393
|
+
// handler.value).
|
|
1394
|
+
if (parentObject[0] === REACT_ELEMENT_TYPE && typeof handler.value === 'object' && handler.value !== null && handler.value.$$typeof === REACT_ELEMENT_TYPE) {
|
|
1395
|
+
const element = handler.value;
|
|
1396
|
+
switch (key) {
|
|
1397
|
+
case '3':
|
|
1398
|
+
element.props = resolvedValue;
|
|
1399
|
+
break;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
handler.deps--;
|
|
1403
|
+
if (handler.deps === 0) {
|
|
1404
|
+
const chunk = handler.chunk;
|
|
1405
|
+
if (chunk === null || chunk.status !== BLOCKED) {
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const resolveListeners = chunk.value;
|
|
1409
|
+
const initializedChunk = chunk;
|
|
1410
|
+
initializedChunk.status = INITIALIZED;
|
|
1411
|
+
initializedChunk.value = handler.value;
|
|
1412
|
+
if (resolveListeners !== null) {
|
|
1413
|
+
wakeChunk(resolveListeners, handler.value);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
function reject(error) {
|
|
1418
|
+
if (handler.errored) {
|
|
1419
|
+
// We've already errored. We could instead build up an AggregateError
|
|
1420
|
+
// but if there are multiple errors we just take the first one like
|
|
1421
|
+
// Promise.all.
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
handler.errored = true;
|
|
1425
|
+
handler.value = error;
|
|
1426
|
+
const chunk = handler.chunk;
|
|
1427
|
+
if (chunk === null || chunk.status !== BLOCKED) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
triggerErrorOnChunk(chunk, error);
|
|
1431
|
+
}
|
|
1432
|
+
promise.then(fulfill, reject);
|
|
1433
|
+
|
|
1434
|
+
// Return a place holder value for now.
|
|
1435
|
+
return null;
|
|
1436
|
+
}
|
|
1437
|
+
function getOutlinedModel(response, reference, parentObject, key, map) {
|
|
1438
|
+
const path = reference.split(':');
|
|
1439
|
+
const id = parseInt(path[0], 16);
|
|
1440
|
+
const chunk = getChunk(response, id);
|
|
1441
|
+
switch (chunk.status) {
|
|
1442
|
+
case RESOLVED_MODEL:
|
|
1443
|
+
initializeModelChunk(chunk);
|
|
1444
|
+
break;
|
|
1445
|
+
case RESOLVED_MODULE:
|
|
1446
|
+
initializeModuleChunk(chunk);
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
// The status might have changed after initialization.
|
|
1450
|
+
switch (chunk.status) {
|
|
1451
|
+
case INITIALIZED:
|
|
1452
|
+
let value = chunk.value;
|
|
1453
|
+
for (let i = 1; i < path.length; i++) {
|
|
1454
|
+
while (value.$$typeof === REACT_LAZY_TYPE) {
|
|
1455
|
+
const referencedChunk = value._payload;
|
|
1456
|
+
if (referencedChunk.status === INITIALIZED) {
|
|
1457
|
+
value = referencedChunk.value;
|
|
1458
|
+
} else {
|
|
1459
|
+
return waitForReference(referencedChunk, parentObject, key, response, map, path.slice(i - 1));
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
value = value[path[i]];
|
|
1463
|
+
}
|
|
1464
|
+
const chunkValue = map(response, value, parentObject, key);
|
|
1465
|
+
return chunkValue;
|
|
1466
|
+
case PENDING:
|
|
1467
|
+
case BLOCKED:
|
|
1468
|
+
return waitForReference(chunk, parentObject, key, response, map, path);
|
|
1469
|
+
default:
|
|
1470
|
+
// This is an error. Instead of erroring directly, we're going to encode this on
|
|
1471
|
+
// an initialization handler so that we can catch it at the nearest Element.
|
|
1472
|
+
if (initializingHandler) {
|
|
1473
|
+
initializingHandler.errored = true;
|
|
1474
|
+
initializingHandler.value = chunk.reason;
|
|
1475
|
+
} else {
|
|
1476
|
+
initializingHandler = {
|
|
1477
|
+
parent: null,
|
|
1478
|
+
chunk: null,
|
|
1479
|
+
value: chunk.reason,
|
|
1480
|
+
deps: 0,
|
|
1481
|
+
errored: true
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
// Placeholder
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
function createMap(response, model) {
|
|
1489
|
+
return new Map(model);
|
|
1490
|
+
}
|
|
1491
|
+
function createSet(response, model) {
|
|
1492
|
+
return new Set(model);
|
|
1493
|
+
}
|
|
1494
|
+
function createBlob(response, model) {
|
|
1495
|
+
return new Blob(model.slice(1), {
|
|
1496
|
+
type: model[0]
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
function createFormData(response, model) {
|
|
1500
|
+
const formData = new FormData();
|
|
1501
|
+
for (let i = 0; i < model.length; i++) {
|
|
1502
|
+
formData.append(model[i][0], model[i][1]);
|
|
1503
|
+
}
|
|
1504
|
+
return formData;
|
|
1505
|
+
}
|
|
1506
|
+
function extractIterator(response, model) {
|
|
1507
|
+
// $FlowFixMe[incompatible-use]: This uses raw Symbols because we're extracting from a native array.
|
|
1508
|
+
return model[Symbol.iterator]();
|
|
1509
|
+
}
|
|
1510
|
+
function createModel(response, model) {
|
|
1511
|
+
return model;
|
|
1512
|
+
}
|
|
1513
|
+
function parseModelString(response, parentObject, key, value) {
|
|
1514
|
+
if (value[0] === '$') {
|
|
1515
|
+
if (value === '$') {
|
|
1516
|
+
// A very common symbol.
|
|
1517
|
+
if (initializingHandler !== null && key === '0') {
|
|
1518
|
+
// We we already have an initializing handler and we're abound to enter
|
|
1519
|
+
// a new element, we need to shadow it because we're now in a new scope.
|
|
1520
|
+
// This is effectively the "begin" or "push" phase of Element parsing.
|
|
1521
|
+
// We'll pop later when we parse the array itself.
|
|
1522
|
+
initializingHandler = {
|
|
1523
|
+
parent: initializingHandler,
|
|
1524
|
+
chunk: null,
|
|
1525
|
+
value: null,
|
|
1526
|
+
deps: 0,
|
|
1527
|
+
errored: false
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
return REACT_ELEMENT_TYPE;
|
|
1531
|
+
}
|
|
1532
|
+
switch (value[1]) {
|
|
1533
|
+
case '$':
|
|
1534
|
+
{
|
|
1535
|
+
// This was an escaped string value.
|
|
1536
|
+
return value.slice(1);
|
|
1537
|
+
}
|
|
1538
|
+
case 'L':
|
|
1539
|
+
{
|
|
1540
|
+
// Lazy node
|
|
1541
|
+
const id = parseInt(value.slice(2), 16);
|
|
1542
|
+
const chunk = getChunk(response, id);
|
|
1543
|
+
// We create a React.lazy wrapper around any lazy values.
|
|
1544
|
+
// When passed into React, we'll know how to suspend on this.
|
|
1545
|
+
return createLazyChunkWrapper(chunk);
|
|
1546
|
+
}
|
|
1547
|
+
case '@':
|
|
1548
|
+
{
|
|
1549
|
+
// Promise
|
|
1550
|
+
if (value.length === 2) {
|
|
1551
|
+
// Infinite promise that never resolves.
|
|
1552
|
+
return new Promise(() => {});
|
|
1553
|
+
}
|
|
1554
|
+
const id = parseInt(value.slice(2), 16);
|
|
1555
|
+
const chunk = getChunk(response, id);
|
|
1556
|
+
return chunk;
|
|
1557
|
+
}
|
|
1558
|
+
case 'S':
|
|
1559
|
+
{
|
|
1560
|
+
// Symbol
|
|
1561
|
+
return Symbol.for(value.slice(2));
|
|
1562
|
+
}
|
|
1563
|
+
case 'F':
|
|
1564
|
+
{
|
|
1565
|
+
// Server Reference
|
|
1566
|
+
const ref = value.slice(2);
|
|
1567
|
+
return getOutlinedModel(response, ref, parentObject, key, loadServerReference);
|
|
1568
|
+
}
|
|
1569
|
+
case 'T':
|
|
1570
|
+
{
|
|
1571
|
+
// Temporary Reference
|
|
1572
|
+
const reference = '$' + value.slice(2);
|
|
1573
|
+
const temporaryReferences = response._tempRefs;
|
|
1574
|
+
if (temporaryReferences == null) {
|
|
1575
|
+
throw new Error('Missing a temporary reference set but the RSC response returned a temporary reference. ' + 'Pass a temporaryReference option with the set that was used with the reply.');
|
|
1576
|
+
}
|
|
1577
|
+
return readTemporaryReference(temporaryReferences, reference);
|
|
1578
|
+
}
|
|
1579
|
+
case 'Q':
|
|
1580
|
+
{
|
|
1581
|
+
// Map
|
|
1582
|
+
const ref = value.slice(2);
|
|
1583
|
+
return getOutlinedModel(response, ref, parentObject, key, createMap);
|
|
1584
|
+
}
|
|
1585
|
+
case 'W':
|
|
1586
|
+
{
|
|
1587
|
+
// Set
|
|
1588
|
+
const ref = value.slice(2);
|
|
1589
|
+
return getOutlinedModel(response, ref, parentObject, key, createSet);
|
|
1590
|
+
}
|
|
1591
|
+
case 'B':
|
|
1592
|
+
{
|
|
1593
|
+
// Blob
|
|
1594
|
+
const ref = value.slice(2);
|
|
1595
|
+
return getOutlinedModel(response, ref, parentObject, key, createBlob);
|
|
1596
|
+
}
|
|
1597
|
+
case 'K':
|
|
1598
|
+
{
|
|
1599
|
+
// FormData
|
|
1600
|
+
const ref = value.slice(2);
|
|
1601
|
+
return getOutlinedModel(response, ref, parentObject, key, createFormData);
|
|
1602
|
+
}
|
|
1603
|
+
case 'Z':
|
|
1604
|
+
{
|
|
1605
|
+
// Error
|
|
1606
|
+
{
|
|
1607
|
+
return resolveErrorProd();
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
case 'i':
|
|
1611
|
+
{
|
|
1612
|
+
// Iterator
|
|
1613
|
+
const ref = value.slice(2);
|
|
1614
|
+
return getOutlinedModel(response, ref, parentObject, key, extractIterator);
|
|
1615
|
+
}
|
|
1616
|
+
case 'I':
|
|
1617
|
+
{
|
|
1618
|
+
// $Infinity
|
|
1619
|
+
return Infinity;
|
|
1620
|
+
}
|
|
1621
|
+
case '-':
|
|
1622
|
+
{
|
|
1623
|
+
// $-0 or $-Infinity
|
|
1624
|
+
if (value === '$-0') {
|
|
1625
|
+
return -0;
|
|
1626
|
+
} else {
|
|
1627
|
+
return -Infinity;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
case 'N':
|
|
1631
|
+
{
|
|
1632
|
+
// $NaN
|
|
1633
|
+
return NaN;
|
|
1634
|
+
}
|
|
1635
|
+
case 'u':
|
|
1636
|
+
{
|
|
1637
|
+
// matches "$undefined"
|
|
1638
|
+
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
|
|
1639
|
+
return undefined;
|
|
1640
|
+
}
|
|
1641
|
+
case 'D':
|
|
1642
|
+
{
|
|
1643
|
+
// Date
|
|
1644
|
+
return new Date(Date.parse(value.slice(2)));
|
|
1645
|
+
}
|
|
1646
|
+
case 'n':
|
|
1647
|
+
{
|
|
1648
|
+
// BigInt
|
|
1649
|
+
return BigInt(value.slice(2));
|
|
1650
|
+
}
|
|
1651
|
+
case 'E':
|
|
1652
|
+
case 'Y':
|
|
1653
|
+
default:
|
|
1654
|
+
{
|
|
1655
|
+
// We assume that anything else is a reference ID.
|
|
1656
|
+
const ref = value.slice(1);
|
|
1657
|
+
return getOutlinedModel(response, ref, parentObject, key, createModel);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
return value;
|
|
1662
|
+
}
|
|
1663
|
+
function parseModelTuple(response, value) {
|
|
1664
|
+
const tuple = value;
|
|
1665
|
+
if (tuple[0] === REACT_ELEMENT_TYPE) {
|
|
1666
|
+
// TODO: Consider having React just directly accept these arrays as elements.
|
|
1667
|
+
// Or even change the ReactElement type to be an array.
|
|
1668
|
+
return createElement(response, tuple[1], tuple[2], tuple[3]);
|
|
1669
|
+
}
|
|
1670
|
+
return value;
|
|
1671
|
+
}
|
|
1672
|
+
function missingCall() {
|
|
1673
|
+
throw new Error('Trying to call a function from "use server" but the callServer option ' + 'was not implemented in your router runtime.');
|
|
1674
|
+
}
|
|
1675
|
+
function ResponseInstance(bundlerConfig, serverReferenceConfig, moduleLoading, callServer, encodeFormAction, nonce, temporaryReferences, findSourceMapURL, replayConsole, environmentName) {
|
|
1676
|
+
const chunks = new Map();
|
|
1677
|
+
this._bundlerConfig = bundlerConfig;
|
|
1678
|
+
this._serverReferenceConfig = serverReferenceConfig;
|
|
1679
|
+
this._moduleLoading = moduleLoading;
|
|
1680
|
+
this._callServer = callServer !== undefined ? callServer : missingCall;
|
|
1681
|
+
this._encodeFormAction = encodeFormAction;
|
|
1682
|
+
this._nonce = nonce;
|
|
1683
|
+
this._chunks = chunks;
|
|
1684
|
+
this._stringDecoder = createStringDecoder();
|
|
1685
|
+
this._fromJSON = null;
|
|
1686
|
+
this._rowState = 0;
|
|
1687
|
+
this._rowID = 0;
|
|
1688
|
+
this._rowTag = 0;
|
|
1689
|
+
this._rowLength = 0;
|
|
1690
|
+
this._buffer = [];
|
|
1691
|
+
this._closed = false;
|
|
1692
|
+
this._closedReason = null;
|
|
1693
|
+
this._tempRefs = temporaryReferences;
|
|
1694
|
+
// Don't inline this call because it causes closure to outline the call above.
|
|
1695
|
+
this._fromJSON = createFromJSONCallback(this);
|
|
1696
|
+
}
|
|
1697
|
+
function createResponse(bundlerConfig, serverReferenceConfig, moduleLoading, callServer, encodeFormAction, nonce, temporaryReferences, findSourceMapURL, replayConsole, environmentName) {
|
|
1698
|
+
// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors
|
|
1699
|
+
return new ResponseInstance(bundlerConfig, serverReferenceConfig, moduleLoading, callServer, encodeFormAction, nonce, temporaryReferences);
|
|
1700
|
+
}
|
|
1701
|
+
function resolveModel(response, id, model) {
|
|
1702
|
+
const chunks = response._chunks;
|
|
1703
|
+
const chunk = chunks.get(id);
|
|
1704
|
+
if (!chunk) {
|
|
1705
|
+
chunks.set(id, createResolvedModelChunk(response, model));
|
|
1706
|
+
} else {
|
|
1707
|
+
resolveModelChunk(chunk, model);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
function resolveText(response, id, text) {
|
|
1711
|
+
const chunks = response._chunks;
|
|
1712
|
+
const chunk = chunks.get(id);
|
|
1713
|
+
if (chunk && chunk.status !== PENDING) {
|
|
1714
|
+
// If we get more data to an already resolved ID, we assume that it's
|
|
1715
|
+
// a stream chunk since any other row shouldn't have more than one entry.
|
|
1716
|
+
const streamChunk = chunk;
|
|
1717
|
+
const controller = streamChunk.reason;
|
|
1718
|
+
controller.enqueueValue(text);
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
chunks.set(id, createInitializedTextChunk(response, text));
|
|
1722
|
+
}
|
|
1723
|
+
function resolveBuffer(response, id, buffer) {
|
|
1724
|
+
const chunks = response._chunks;
|
|
1725
|
+
const chunk = chunks.get(id);
|
|
1726
|
+
if (chunk && chunk.status !== PENDING) {
|
|
1727
|
+
// If we get more data to an already resolved ID, we assume that it's
|
|
1728
|
+
// a stream chunk since any other row shouldn't have more than one entry.
|
|
1729
|
+
const streamChunk = chunk;
|
|
1730
|
+
const controller = streamChunk.reason;
|
|
1731
|
+
controller.enqueueValue(buffer);
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
chunks.set(id, createInitializedBufferChunk(response, buffer));
|
|
1735
|
+
}
|
|
1736
|
+
function resolveModule(response, id, model) {
|
|
1737
|
+
const chunks = response._chunks;
|
|
1738
|
+
const chunk = chunks.get(id);
|
|
1739
|
+
const clientReferenceMetadata = parseModel(response, model);
|
|
1740
|
+
const clientReference = resolveClientReference(response._bundlerConfig, clientReferenceMetadata);
|
|
1741
|
+
|
|
1742
|
+
// TODO: Add an option to encode modules that are lazy loaded.
|
|
1743
|
+
// For now we preload all modules as early as possible since it's likely
|
|
1744
|
+
// that we'll need them.
|
|
1745
|
+
const promise = preloadModule(clientReference);
|
|
1746
|
+
if (promise) {
|
|
1747
|
+
let blockedChunk;
|
|
1748
|
+
if (!chunk) {
|
|
1749
|
+
// Technically, we should just treat promise as the chunk in this
|
|
1750
|
+
// case. Because it'll just behave as any other promise.
|
|
1751
|
+
blockedChunk = createBlockedChunk(response);
|
|
1752
|
+
chunks.set(id, blockedChunk);
|
|
1753
|
+
} else {
|
|
1754
|
+
// This can't actually happen because we don't have any forward
|
|
1755
|
+
// references to modules.
|
|
1756
|
+
blockedChunk = chunk;
|
|
1757
|
+
blockedChunk.status = BLOCKED;
|
|
1758
|
+
}
|
|
1759
|
+
promise.then(() => resolveModuleChunk(blockedChunk, clientReference), error => triggerErrorOnChunk(blockedChunk, error));
|
|
1760
|
+
} else {
|
|
1761
|
+
if (!chunk) {
|
|
1762
|
+
chunks.set(id, createResolvedModuleChunk(response, clientReference));
|
|
1763
|
+
} else {
|
|
1764
|
+
// This can't actually happen because we don't have any forward
|
|
1765
|
+
// references to modules.
|
|
1766
|
+
resolveModuleChunk(chunk, clientReference);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
function resolveStream(response, id, stream, controller) {
|
|
1771
|
+
const chunks = response._chunks;
|
|
1772
|
+
const chunk = chunks.get(id);
|
|
1773
|
+
if (!chunk) {
|
|
1774
|
+
chunks.set(id, createInitializedStreamChunk(response, stream, controller));
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
if (chunk.status !== PENDING) {
|
|
1778
|
+
// We already resolved. We didn't expect to see this.
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
const resolveListeners = chunk.value;
|
|
1782
|
+
const resolvedChunk = chunk;
|
|
1783
|
+
resolvedChunk.status = INITIALIZED;
|
|
1784
|
+
resolvedChunk.value = stream;
|
|
1785
|
+
resolvedChunk.reason = controller;
|
|
1786
|
+
if (resolveListeners !== null) {
|
|
1787
|
+
wakeChunk(resolveListeners, chunk.value);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
function startReadableStream(response, id, type) {
|
|
1791
|
+
let controller = null;
|
|
1792
|
+
const stream = new ReadableStream({
|
|
1793
|
+
type: type,
|
|
1794
|
+
start(c) {
|
|
1795
|
+
controller = c;
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
let previousBlockedChunk = null;
|
|
1799
|
+
const flightController = {
|
|
1800
|
+
enqueueValue(value) {
|
|
1801
|
+
if (previousBlockedChunk === null) {
|
|
1802
|
+
controller.enqueue(value);
|
|
1803
|
+
} else {
|
|
1804
|
+
// We're still waiting on a previous chunk so we can't enqueue quite yet.
|
|
1805
|
+
previousBlockedChunk.then(function () {
|
|
1806
|
+
controller.enqueue(value);
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
},
|
|
1810
|
+
enqueueModel(json) {
|
|
1811
|
+
if (previousBlockedChunk === null) {
|
|
1812
|
+
// If we're not blocked on any other chunks, we can try to eagerly initialize
|
|
1813
|
+
// this as a fast-path to avoid awaiting them.
|
|
1814
|
+
const chunk = createResolvedModelChunk(response, json);
|
|
1815
|
+
initializeModelChunk(chunk);
|
|
1816
|
+
const initializedChunk = chunk;
|
|
1817
|
+
if (initializedChunk.status === INITIALIZED) {
|
|
1818
|
+
controller.enqueue(initializedChunk.value);
|
|
1819
|
+
} else {
|
|
1820
|
+
chunk.then(v => controller.enqueue(v), e => controller.error(e));
|
|
1821
|
+
previousBlockedChunk = chunk;
|
|
1822
|
+
}
|
|
1823
|
+
} else {
|
|
1824
|
+
// We're still waiting on a previous chunk so we can't enqueue quite yet.
|
|
1825
|
+
const blockedChunk = previousBlockedChunk;
|
|
1826
|
+
const chunk = createPendingChunk(response);
|
|
1827
|
+
chunk.then(v => controller.enqueue(v), e => controller.error(e));
|
|
1828
|
+
previousBlockedChunk = chunk;
|
|
1829
|
+
blockedChunk.then(function () {
|
|
1830
|
+
if (previousBlockedChunk === chunk) {
|
|
1831
|
+
// We were still the last chunk so we can now clear the queue and return
|
|
1832
|
+
// to synchronous emitting.
|
|
1833
|
+
previousBlockedChunk = null;
|
|
1834
|
+
}
|
|
1835
|
+
resolveModelChunk(chunk, json);
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
},
|
|
1839
|
+
close(json) {
|
|
1840
|
+
if (previousBlockedChunk === null) {
|
|
1841
|
+
controller.close();
|
|
1842
|
+
} else {
|
|
1843
|
+
const blockedChunk = previousBlockedChunk;
|
|
1844
|
+
// We shouldn't get any more enqueues after this so we can set it back to null.
|
|
1845
|
+
previousBlockedChunk = null;
|
|
1846
|
+
blockedChunk.then(() => controller.close());
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
error(error) {
|
|
1850
|
+
if (previousBlockedChunk === null) {
|
|
1851
|
+
// $FlowFixMe[incompatible-call]
|
|
1852
|
+
controller.error(error);
|
|
1853
|
+
} else {
|
|
1854
|
+
const blockedChunk = previousBlockedChunk;
|
|
1855
|
+
// We shouldn't get any more enqueues after this so we can set it back to null.
|
|
1856
|
+
previousBlockedChunk = null;
|
|
1857
|
+
blockedChunk.then(() => controller.error(error));
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1861
|
+
resolveStream(response, id, stream, flightController);
|
|
1862
|
+
}
|
|
1863
|
+
function asyncIterator() {
|
|
1864
|
+
// Self referencing iterator.
|
|
1865
|
+
return this;
|
|
1866
|
+
}
|
|
1867
|
+
function createIterator(next) {
|
|
1868
|
+
const iterator = {
|
|
1869
|
+
next: next
|
|
1870
|
+
// TODO: Add return/throw as options for aborting.
|
|
1871
|
+
};
|
|
1872
|
+
// TODO: The iterator could inherit the AsyncIterator prototype which is not exposed as
|
|
1873
|
+
// a global but exists as a prototype of an AsyncGenerator. However, it's not needed
|
|
1874
|
+
// to satisfy the iterable protocol.
|
|
1875
|
+
iterator[ASYNC_ITERATOR] = asyncIterator;
|
|
1876
|
+
return iterator;
|
|
1877
|
+
}
|
|
1878
|
+
function startAsyncIterable(response, id, iterator) {
|
|
1879
|
+
const buffer = [];
|
|
1880
|
+
let closed = false;
|
|
1881
|
+
let nextWriteIndex = 0;
|
|
1882
|
+
const flightController = {
|
|
1883
|
+
enqueueValue(value) {
|
|
1884
|
+
if (nextWriteIndex === buffer.length) {
|
|
1885
|
+
buffer[nextWriteIndex] = createInitializedIteratorResultChunk(response, value, false);
|
|
1886
|
+
} else {
|
|
1887
|
+
const chunk = buffer[nextWriteIndex];
|
|
1888
|
+
const resolveListeners = chunk.value;
|
|
1889
|
+
const rejectListeners = chunk.reason;
|
|
1890
|
+
const initializedChunk = chunk;
|
|
1891
|
+
initializedChunk.status = INITIALIZED;
|
|
1892
|
+
initializedChunk.value = {
|
|
1893
|
+
done: false,
|
|
1894
|
+
value: value
|
|
1895
|
+
};
|
|
1896
|
+
if (resolveListeners !== null) {
|
|
1897
|
+
wakeChunkIfInitialized(chunk, resolveListeners, rejectListeners);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
nextWriteIndex++;
|
|
1901
|
+
},
|
|
1902
|
+
enqueueModel(value) {
|
|
1903
|
+
if (nextWriteIndex === buffer.length) {
|
|
1904
|
+
buffer[nextWriteIndex] = createResolvedIteratorResultChunk(response, value, false);
|
|
1905
|
+
} else {
|
|
1906
|
+
resolveIteratorResultChunk(buffer[nextWriteIndex], value, false);
|
|
1907
|
+
}
|
|
1908
|
+
nextWriteIndex++;
|
|
1909
|
+
},
|
|
1910
|
+
close(value) {
|
|
1911
|
+
closed = true;
|
|
1912
|
+
if (nextWriteIndex === buffer.length) {
|
|
1913
|
+
buffer[nextWriteIndex] = createResolvedIteratorResultChunk(response, value, true);
|
|
1914
|
+
} else {
|
|
1915
|
+
resolveIteratorResultChunk(buffer[nextWriteIndex], value, true);
|
|
1916
|
+
}
|
|
1917
|
+
nextWriteIndex++;
|
|
1918
|
+
while (nextWriteIndex < buffer.length) {
|
|
1919
|
+
// In generators, any extra reads from the iterator have the value undefined.
|
|
1920
|
+
resolveIteratorResultChunk(buffer[nextWriteIndex++], '"$undefined"', true);
|
|
1921
|
+
}
|
|
1922
|
+
},
|
|
1923
|
+
error(error) {
|
|
1924
|
+
closed = true;
|
|
1925
|
+
if (nextWriteIndex === buffer.length) {
|
|
1926
|
+
buffer[nextWriteIndex] = createPendingChunk(response);
|
|
1927
|
+
}
|
|
1928
|
+
while (nextWriteIndex < buffer.length) {
|
|
1929
|
+
triggerErrorOnChunk(buffer[nextWriteIndex++], error);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
const iterable = {
|
|
1934
|
+
[ASYNC_ITERATOR]() {
|
|
1935
|
+
let nextReadIndex = 0;
|
|
1936
|
+
return createIterator(arg => {
|
|
1937
|
+
if (arg !== undefined) {
|
|
1938
|
+
throw new Error('Values cannot be passed to next() of AsyncIterables passed to Client Components.');
|
|
1939
|
+
}
|
|
1940
|
+
if (nextReadIndex === buffer.length) {
|
|
1941
|
+
if (closed) {
|
|
1942
|
+
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
1943
|
+
return new ReactPromise(INITIALIZED, {
|
|
1944
|
+
done: true,
|
|
1945
|
+
value: undefined
|
|
1946
|
+
}, null, response);
|
|
1947
|
+
}
|
|
1948
|
+
buffer[nextReadIndex] = createPendingChunk(response);
|
|
1949
|
+
}
|
|
1950
|
+
return buffer[nextReadIndex++];
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
// TODO: If it's a single shot iterator we can optimize memory by cleaning up the buffer after
|
|
1955
|
+
// reading through the end, but currently we favor code size over this optimization.
|
|
1956
|
+
resolveStream(response, id, iterator ? iterable[ASYNC_ITERATOR]() : iterable, flightController);
|
|
1957
|
+
}
|
|
1958
|
+
function stopStream(response, id, row) {
|
|
1959
|
+
const chunks = response._chunks;
|
|
1960
|
+
const chunk = chunks.get(id);
|
|
1961
|
+
if (!chunk || chunk.status !== INITIALIZED) {
|
|
1962
|
+
// We didn't expect not to have an existing stream;
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
const streamChunk = chunk;
|
|
1966
|
+
const controller = streamChunk.reason;
|
|
1967
|
+
controller.close(row === '' ? '"$undefined"' : row);
|
|
1968
|
+
}
|
|
1969
|
+
function resolveErrorProd(response) {
|
|
1970
|
+
const error = new Error('An error occurred in the Server Components render. The specific message is omitted in production' + ' builds to avoid leaking sensitive details. A digest property is included on this error instance which' + ' may provide additional details about the nature of the error.');
|
|
1971
|
+
error.stack = 'Error: ' + error.message;
|
|
1972
|
+
return error;
|
|
1973
|
+
}
|
|
1974
|
+
function resolvePostponeProd(response, id) {
|
|
1975
|
+
const error = new Error('A Server Component was postponed. The reason is omitted in production' + ' builds to avoid leaking sensitive details.');
|
|
1976
|
+
const postponeInstance = error;
|
|
1977
|
+
postponeInstance.$$typeof = REACT_POSTPONE_TYPE;
|
|
1978
|
+
postponeInstance.stack = 'Error: ' + error.message;
|
|
1979
|
+
const chunks = response._chunks;
|
|
1980
|
+
const chunk = chunks.get(id);
|
|
1981
|
+
if (!chunk) {
|
|
1982
|
+
chunks.set(id, createErrorChunk(response, postponeInstance));
|
|
1983
|
+
} else {
|
|
1984
|
+
triggerErrorOnChunk(chunk, postponeInstance);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
function resolveHint(response, code, model) {
|
|
1988
|
+
const hintModel = parseModel(response, model);
|
|
1989
|
+
dispatchHint(code, hintModel);
|
|
1990
|
+
}
|
|
1991
|
+
function mergeBuffer(buffer, lastChunk) {
|
|
1992
|
+
const l = buffer.length;
|
|
1993
|
+
// Count the bytes we'll need
|
|
1994
|
+
let byteLength = lastChunk.length;
|
|
1995
|
+
for (let i = 0; i < l; i++) {
|
|
1996
|
+
byteLength += buffer[i].byteLength;
|
|
1997
|
+
}
|
|
1998
|
+
// Allocate enough contiguous space
|
|
1999
|
+
const result = new Uint8Array(byteLength);
|
|
2000
|
+
let offset = 0;
|
|
2001
|
+
// Copy all the buffers into it.
|
|
2002
|
+
for (let i = 0; i < l; i++) {
|
|
2003
|
+
const chunk = buffer[i];
|
|
2004
|
+
result.set(chunk, offset);
|
|
2005
|
+
offset += chunk.byteLength;
|
|
2006
|
+
}
|
|
2007
|
+
result.set(lastChunk, offset);
|
|
2008
|
+
return result;
|
|
2009
|
+
}
|
|
2010
|
+
function resolveTypedArray(response, id, buffer, lastChunk, constructor, bytesPerElement) {
|
|
2011
|
+
// If the view fits into one original buffer, we just reuse that buffer instead of
|
|
2012
|
+
// copying it out to a separate copy. This means that it's not always possible to
|
|
2013
|
+
// transfer these values to other threads without copying first since they may
|
|
2014
|
+
// share array buffer. For this to work, it must also have bytes aligned to a
|
|
2015
|
+
// multiple of a size of the type.
|
|
2016
|
+
const chunk = buffer.length === 0 && lastChunk.byteOffset % bytesPerElement === 0 ? lastChunk : mergeBuffer(buffer, lastChunk);
|
|
2017
|
+
// TODO: The transfer protocol of RSC is little-endian. If the client isn't little-endian
|
|
2018
|
+
// we should convert it instead. In practice big endian isn't really Web compatible so it's
|
|
2019
|
+
// somewhat safe to assume that browsers aren't going to run it, but maybe there's some SSR
|
|
2020
|
+
// server that's affected.
|
|
2021
|
+
const view = new constructor(chunk.buffer, chunk.byteOffset, chunk.byteLength / bytesPerElement);
|
|
2022
|
+
resolveBuffer(response, id, view);
|
|
2023
|
+
}
|
|
2024
|
+
function processFullBinaryRow(response, id, tag, buffer, chunk) {
|
|
2025
|
+
switch (tag) {
|
|
2026
|
+
case 65 /* "A" */:
|
|
2027
|
+
// We must always clone to extract it into a separate buffer instead of just a view.
|
|
2028
|
+
resolveBuffer(response, id, mergeBuffer(buffer, chunk).buffer);
|
|
2029
|
+
return;
|
|
2030
|
+
case 79 /* "O" */:
|
|
2031
|
+
resolveTypedArray(response, id, buffer, chunk, Int8Array, 1);
|
|
2032
|
+
return;
|
|
2033
|
+
case 111 /* "o" */:
|
|
2034
|
+
resolveBuffer(response, id, buffer.length === 0 ? chunk : mergeBuffer(buffer, chunk));
|
|
2035
|
+
return;
|
|
2036
|
+
case 85 /* "U" */:
|
|
2037
|
+
resolveTypedArray(response, id, buffer, chunk, Uint8ClampedArray, 1);
|
|
2038
|
+
return;
|
|
2039
|
+
case 83 /* "S" */:
|
|
2040
|
+
resolveTypedArray(response, id, buffer, chunk, Int16Array, 2);
|
|
2041
|
+
return;
|
|
2042
|
+
case 115 /* "s" */:
|
|
2043
|
+
resolveTypedArray(response, id, buffer, chunk, Uint16Array, 2);
|
|
2044
|
+
return;
|
|
2045
|
+
case 76 /* "L" */:
|
|
2046
|
+
resolveTypedArray(response, id, buffer, chunk, Int32Array, 4);
|
|
2047
|
+
return;
|
|
2048
|
+
case 108 /* "l" */:
|
|
2049
|
+
resolveTypedArray(response, id, buffer, chunk, Uint32Array, 4);
|
|
2050
|
+
return;
|
|
2051
|
+
case 71 /* "G" */:
|
|
2052
|
+
resolveTypedArray(response, id, buffer, chunk, Float32Array, 4);
|
|
2053
|
+
return;
|
|
2054
|
+
case 103 /* "g" */:
|
|
2055
|
+
resolveTypedArray(response, id, buffer, chunk, Float64Array, 8);
|
|
2056
|
+
return;
|
|
2057
|
+
case 77 /* "M" */:
|
|
2058
|
+
resolveTypedArray(response, id, buffer, chunk, BigInt64Array, 8);
|
|
2059
|
+
return;
|
|
2060
|
+
case 109 /* "m" */:
|
|
2061
|
+
resolveTypedArray(response, id, buffer, chunk, BigUint64Array, 8);
|
|
2062
|
+
return;
|
|
2063
|
+
case 86 /* "V" */:
|
|
2064
|
+
resolveTypedArray(response, id, buffer, chunk, DataView, 1);
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const stringDecoder = response._stringDecoder;
|
|
2068
|
+
let row = '';
|
|
2069
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
2070
|
+
row += readPartialStringChunk(stringDecoder, buffer[i]);
|
|
2071
|
+
}
|
|
2072
|
+
row += readFinalStringChunk(stringDecoder, chunk);
|
|
2073
|
+
processFullStringRow(response, id, tag, row);
|
|
2074
|
+
}
|
|
2075
|
+
function processFullStringRow(response, id, tag, row) {
|
|
2076
|
+
switch (tag) {
|
|
2077
|
+
case 73 /* "I" */:
|
|
2078
|
+
{
|
|
2079
|
+
resolveModule(response, id, row);
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
case 72 /* "H" */:
|
|
2083
|
+
{
|
|
2084
|
+
const code = row[0];
|
|
2085
|
+
resolveHint(response, code, row.slice(1));
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
case 69 /* "E" */:
|
|
2089
|
+
{
|
|
2090
|
+
const errorInfo = JSON.parse(row);
|
|
2091
|
+
let error;
|
|
2092
|
+
{
|
|
2093
|
+
error = resolveErrorProd();
|
|
2094
|
+
}
|
|
2095
|
+
error.digest = errorInfo.digest;
|
|
2096
|
+
const errorWithDigest = error;
|
|
2097
|
+
const chunks = response._chunks;
|
|
2098
|
+
const chunk = chunks.get(id);
|
|
2099
|
+
if (!chunk) {
|
|
2100
|
+
chunks.set(id, createErrorChunk(response, errorWithDigest));
|
|
2101
|
+
} else {
|
|
2102
|
+
triggerErrorOnChunk(chunk, errorWithDigest);
|
|
2103
|
+
}
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
case 84 /* "T" */:
|
|
2107
|
+
{
|
|
2108
|
+
resolveText(response, id, row);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
case 78 /* "N" */:
|
|
2112
|
+
case 68 /* "D" */:
|
|
2113
|
+
case 87 /* "W" */:
|
|
2114
|
+
{
|
|
2115
|
+
throw new Error('Failed to read a RSC payload created by a development version of React ' + 'on the server while using a production version on the client. Always use ' + 'matching versions on the server and the client.');
|
|
2116
|
+
}
|
|
2117
|
+
case 82 /* "R" */:
|
|
2118
|
+
{
|
|
2119
|
+
startReadableStream(response, id, undefined);
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
// Fallthrough
|
|
2123
|
+
case 114 /* "r" */:
|
|
2124
|
+
{
|
|
2125
|
+
startReadableStream(response, id, 'bytes');
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
// Fallthrough
|
|
2129
|
+
case 88 /* "X" */:
|
|
2130
|
+
{
|
|
2131
|
+
startAsyncIterable(response, id, false);
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
// Fallthrough
|
|
2135
|
+
case 120 /* "x" */:
|
|
2136
|
+
{
|
|
2137
|
+
startAsyncIterable(response, id, true);
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
// Fallthrough
|
|
2141
|
+
case 67 /* "C" */:
|
|
2142
|
+
{
|
|
2143
|
+
stopStream(response, id, row);
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
// Fallthrough
|
|
2147
|
+
case 80 /* "P" */:
|
|
2148
|
+
{
|
|
2149
|
+
{
|
|
2150
|
+
{
|
|
2151
|
+
resolvePostponeProd(response, id);
|
|
2152
|
+
}
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
// Fallthrough
|
|
2157
|
+
default:
|
|
2158
|
+
/* """ "{" "[" "t" "f" "n" "0" - "9" */{
|
|
2159
|
+
// We assume anything else is JSON.
|
|
2160
|
+
resolveModel(response, id, row);
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
function processBinaryChunk(response, chunk) {
|
|
2166
|
+
let i = 0;
|
|
2167
|
+
let rowState = response._rowState;
|
|
2168
|
+
let rowID = response._rowID;
|
|
2169
|
+
let rowTag = response._rowTag;
|
|
2170
|
+
let rowLength = response._rowLength;
|
|
2171
|
+
const buffer = response._buffer;
|
|
2172
|
+
const chunkLength = chunk.length;
|
|
2173
|
+
while (i < chunkLength) {
|
|
2174
|
+
let lastIdx = -1;
|
|
2175
|
+
switch (rowState) {
|
|
2176
|
+
case ROW_ID:
|
|
2177
|
+
{
|
|
2178
|
+
const byte = chunk[i++];
|
|
2179
|
+
if (byte === 58 /* ":" */) {
|
|
2180
|
+
// Finished the rowID, next we'll parse the tag.
|
|
2181
|
+
rowState = ROW_TAG;
|
|
2182
|
+
} else {
|
|
2183
|
+
rowID = rowID << 4 | (byte > 96 ? byte - 87 : byte - 48);
|
|
2184
|
+
}
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
case ROW_TAG:
|
|
2188
|
+
{
|
|
2189
|
+
const resolvedRowTag = chunk[i];
|
|
2190
|
+
if (resolvedRowTag === 84 /* "T" */ || resolvedRowTag === 65 /* "A" */ || resolvedRowTag === 79 /* "O" */ || resolvedRowTag === 111 /* "o" */ || resolvedRowTag === 85 /* "U" */ || resolvedRowTag === 83 /* "S" */ || resolvedRowTag === 115 /* "s" */ || resolvedRowTag === 76 /* "L" */ || resolvedRowTag === 108 /* "l" */ || resolvedRowTag === 71 /* "G" */ || resolvedRowTag === 103 /* "g" */ || resolvedRowTag === 77 /* "M" */ || resolvedRowTag === 109 /* "m" */ || resolvedRowTag === 86 /* "V" */) {
|
|
2191
|
+
rowTag = resolvedRowTag;
|
|
2192
|
+
rowState = ROW_LENGTH;
|
|
2193
|
+
i++;
|
|
2194
|
+
} else if (resolvedRowTag > 64 && resolvedRowTag < 91 /* "A"-"Z" */ || resolvedRowTag === 35 /* "#" */ || resolvedRowTag === 114 /* "r" */ || resolvedRowTag === 120 /* "x" */) {
|
|
2195
|
+
rowTag = resolvedRowTag;
|
|
2196
|
+
rowState = ROW_CHUNK_BY_NEWLINE;
|
|
2197
|
+
i++;
|
|
2198
|
+
} else {
|
|
2199
|
+
rowTag = 0;
|
|
2200
|
+
rowState = ROW_CHUNK_BY_NEWLINE;
|
|
2201
|
+
// This was an unknown tag so it was probably part of the data.
|
|
2202
|
+
}
|
|
2203
|
+
continue;
|
|
2204
|
+
}
|
|
2205
|
+
case ROW_LENGTH:
|
|
2206
|
+
{
|
|
2207
|
+
const byte = chunk[i++];
|
|
2208
|
+
if (byte === 44 /* "," */) {
|
|
2209
|
+
// Finished the rowLength, next we'll buffer up to that length.
|
|
2210
|
+
rowState = ROW_CHUNK_BY_LENGTH;
|
|
2211
|
+
} else {
|
|
2212
|
+
rowLength = rowLength << 4 | (byte > 96 ? byte - 87 : byte - 48);
|
|
2213
|
+
}
|
|
2214
|
+
continue;
|
|
2215
|
+
}
|
|
2216
|
+
case ROW_CHUNK_BY_NEWLINE:
|
|
2217
|
+
{
|
|
2218
|
+
// We're looking for a newline
|
|
2219
|
+
lastIdx = chunk.indexOf(10 /* "\n" */, i);
|
|
2220
|
+
break;
|
|
2221
|
+
}
|
|
2222
|
+
case ROW_CHUNK_BY_LENGTH:
|
|
2223
|
+
{
|
|
2224
|
+
// We're looking for the remaining byte length
|
|
2225
|
+
lastIdx = i + rowLength;
|
|
2226
|
+
if (lastIdx > chunk.length) {
|
|
2227
|
+
lastIdx = -1;
|
|
2228
|
+
}
|
|
2229
|
+
break;
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
const offset = chunk.byteOffset + i;
|
|
2233
|
+
if (lastIdx > -1) {
|
|
2234
|
+
// We found the last chunk of the row
|
|
2235
|
+
const length = lastIdx - i;
|
|
2236
|
+
const lastChunk = new Uint8Array(chunk.buffer, offset, length);
|
|
2237
|
+
processFullBinaryRow(response, rowID, rowTag, buffer, lastChunk);
|
|
2238
|
+
// Reset state machine for a new row
|
|
2239
|
+
i = lastIdx;
|
|
2240
|
+
if (rowState === ROW_CHUNK_BY_NEWLINE) {
|
|
2241
|
+
// If we're trailing by a newline we need to skip it.
|
|
2242
|
+
i++;
|
|
2243
|
+
}
|
|
2244
|
+
rowState = ROW_ID;
|
|
2245
|
+
rowTag = 0;
|
|
2246
|
+
rowID = 0;
|
|
2247
|
+
rowLength = 0;
|
|
2248
|
+
buffer.length = 0;
|
|
2249
|
+
} else {
|
|
2250
|
+
// The rest of this row is in a future chunk. We stash the rest of the
|
|
2251
|
+
// current chunk until we can process the full row.
|
|
2252
|
+
const length = chunk.byteLength - i;
|
|
2253
|
+
const remainingSlice = new Uint8Array(chunk.buffer, offset, length);
|
|
2254
|
+
buffer.push(remainingSlice);
|
|
2255
|
+
// Update how many bytes we're still waiting for. If we're looking for
|
|
2256
|
+
// a newline, this doesn't hurt since we'll just ignore it.
|
|
2257
|
+
rowLength -= remainingSlice.byteLength;
|
|
2258
|
+
break;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
response._rowState = rowState;
|
|
2262
|
+
response._rowID = rowID;
|
|
2263
|
+
response._rowTag = rowTag;
|
|
2264
|
+
response._rowLength = rowLength;
|
|
2265
|
+
}
|
|
2266
|
+
function parseModel(response, json) {
|
|
2267
|
+
return JSON.parse(json, response._fromJSON);
|
|
2268
|
+
}
|
|
2269
|
+
function createFromJSONCallback(response) {
|
|
2270
|
+
// $FlowFixMe[missing-this-annot]
|
|
2271
|
+
return function (key, value) {
|
|
2272
|
+
if (typeof value === 'string') {
|
|
2273
|
+
// We can't use .bind here because we need the "this" value.
|
|
2274
|
+
return parseModelString(response, this, key, value);
|
|
2275
|
+
}
|
|
2276
|
+
if (typeof value === 'object' && value !== null) {
|
|
2277
|
+
return parseModelTuple(response, value);
|
|
2278
|
+
}
|
|
2279
|
+
return value;
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
function close(response) {
|
|
2283
|
+
// In case there are any remaining unresolved chunks, they won't
|
|
2284
|
+
// be resolved now. So we need to issue an error to those.
|
|
2285
|
+
// Ideally we should be able to early bail out if we kept a
|
|
2286
|
+
// ref count of pending chunks.
|
|
2287
|
+
reportGlobalError(response, new Error('Connection closed.'));
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
function createResponseFromOptions(options) {
|
|
2291
|
+
return createResponse(options && options.moduleBaseURL ? options.moduleBaseURL : '', null, null, options && options.callServer ? options.callServer : undefined, undefined,
|
|
2292
|
+
// encodeFormAction
|
|
2293
|
+
undefined,
|
|
2294
|
+
// nonce
|
|
2295
|
+
options && options.temporaryReferences ? options.temporaryReferences : undefined);
|
|
2296
|
+
}
|
|
2297
|
+
function startReadingFromStream(response, stream) {
|
|
2298
|
+
const reader = stream.getReader();
|
|
2299
|
+
function progress(_ref) {
|
|
2300
|
+
const done = _ref.done,
|
|
2301
|
+
value = _ref.value;
|
|
2302
|
+
if (done) {
|
|
2303
|
+
close(response);
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
const buffer = value;
|
|
2307
|
+
processBinaryChunk(response, buffer);
|
|
2308
|
+
return reader.read().then(progress).catch(error);
|
|
2309
|
+
}
|
|
2310
|
+
function error(e) {
|
|
2311
|
+
reportGlobalError(response, e);
|
|
2312
|
+
}
|
|
2313
|
+
reader.read().then(progress).catch(error);
|
|
2314
|
+
}
|
|
2315
|
+
function createFromReadableStream(stream, options) {
|
|
2316
|
+
const response = createResponseFromOptions(options);
|
|
2317
|
+
startReadingFromStream(response, stream);
|
|
2318
|
+
return getRoot(response);
|
|
2319
|
+
}
|
|
2320
|
+
function createFromFetch(promiseForResponse, options) {
|
|
2321
|
+
const response = createResponseFromOptions(options);
|
|
2322
|
+
promiseForResponse.then(function (r) {
|
|
2323
|
+
startReadingFromStream(response, r.body);
|
|
2324
|
+
}, function (e) {
|
|
2325
|
+
reportGlobalError(response, e);
|
|
2326
|
+
});
|
|
2327
|
+
return getRoot(response);
|
|
2328
|
+
}
|
|
2329
|
+
function encodeReply(value, options) /* We don't use URLSearchParams yet but maybe */{
|
|
2330
|
+
return new Promise((resolve, reject) => {
|
|
2331
|
+
const abort = processReply(value, '', options && options.temporaryReferences ? options.temporaryReferences : undefined, resolve, reject);
|
|
2332
|
+
if (options && options.signal) {
|
|
2333
|
+
const signal = options.signal;
|
|
2334
|
+
if (signal.aborted) {
|
|
2335
|
+
abort(signal.reason);
|
|
2336
|
+
} else {
|
|
2337
|
+
const listener = () => {
|
|
2338
|
+
abort(signal.reason);
|
|
2339
|
+
signal.removeEventListener('abort', listener);
|
|
2340
|
+
};
|
|
2341
|
+
signal.addEventListener('abort', listener);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
export { createFromFetch, createFromReadableStream, createServerReference, createTemporaryReferenceSet, encodeReply, registerServerReference };
|