relay-runtime 10.0.1 → 10.1.3
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/handlers/RelayDefaultHandlerProvider.js.flow +6 -0
- package/handlers/connection/MutationHandlers.js.flow +152 -20
- package/index.js +1 -1
- package/index.js.flow +17 -1
- package/lib/handlers/RelayDefaultHandlerProvider.js +9 -0
- package/lib/handlers/connection/MutationHandlers.js +185 -21
- package/lib/index.js +7 -0
- package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -7
- package/lib/mutations/commitMutation.js +1 -4
- package/lib/mutations/validateMutation.js +28 -12
- package/lib/network/RelayQueryResponseCache.js +3 -7
- package/lib/query/GraphQLTag.js +2 -1
- package/lib/query/fetchQuery.js +2 -3
- package/lib/query/fetchQueryInternal.js +2 -3
- package/lib/store/DataChecker.js +85 -10
- package/lib/store/RelayConcreteVariables.js +2 -6
- package/lib/store/RelayModernEnvironment.js +81 -72
- package/lib/store/RelayModernFragmentSpecResolver.js +14 -7
- package/lib/store/RelayModernOperationDescriptor.js +6 -5
- package/lib/store/RelayModernQueryExecutor.js +46 -33
- package/lib/store/RelayModernRecord.js +3 -7
- package/lib/store/RelayModernStore.js +39 -137
- package/lib/store/RelayOperationTracker.js +7 -9
- package/lib/store/RelayOptimisticRecordSource.js +2 -6
- package/lib/store/RelayPublishQueue.js +1 -1
- package/lib/store/RelayReader.js +196 -33
- package/lib/store/RelayRecordSourceMapImpl.js +3 -5
- package/lib/store/RelayReferenceMarker.js +87 -5
- package/lib/store/RelayResponseNormalizer.js +115 -19
- package/lib/store/RelayStoreReactFlightUtils.js +47 -0
- package/lib/store/RelayStoreSubscriptions.js +162 -0
- package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +258 -0
- package/lib/store/StoreInspector.js +2 -6
- package/lib/store/createRelayContext.js +5 -0
- package/lib/store/defaultRequiredFieldLogger.js +18 -0
- package/lib/store/normalizeRelayPayload.js +2 -6
- package/lib/subscription/requestSubscription.js +2 -3
- package/lib/util/NormalizationNode.js +1 -5
- package/lib/util/RelayConcreteNode.js +2 -0
- package/lib/util/RelayFeatureFlags.js +7 -2
- package/lib/util/createPayloadFor3DField.js +2 -7
- package/lib/util/getFragmentIdentifier.js +12 -3
- package/lib/util/getOperation.js +33 -0
- package/lib/util/isEmptyObject.js +25 -0
- package/lib/util/recycleNodesInto.js +4 -1
- package/lib/util/reportMissingRequiredFields.js +48 -0
- package/mutations/commitMutation.js.flow +1 -2
- package/mutations/validateMutation.js.flow +34 -5
- package/network/RelayNetworkTypes.js.flow +22 -0
- package/package.json +2 -2
- package/query/GraphQLTag.js.flow +3 -1
- package/query/fetchQuery.js.flow +2 -2
- package/query/fetchQueryInternal.js.flow +0 -5
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/DataChecker.js.flow +68 -2
- package/store/RelayModernEnvironment.js.flow +107 -87
- package/store/RelayModernFragmentSpecResolver.js.flow +13 -1
- package/store/RelayModernOperationDescriptor.js.flow +5 -1
- package/store/RelayModernQueryExecutor.js.flow +47 -23
- package/store/RelayModernStore.js.flow +33 -107
- package/store/RelayPublishQueue.js.flow +1 -1
- package/store/RelayReader.js.flow +180 -15
- package/store/RelayReferenceMarker.js.flow +72 -5
- package/store/RelayResponseNormalizer.js.flow +130 -19
- package/store/RelayStoreReactFlightUtils.js.flow +64 -0
- package/store/RelayStoreSubscriptions.js.flow +168 -0
- package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +259 -0
- package/store/RelayStoreTypes.js.flow +130 -37
- package/store/createRelayContext.js.flow +3 -0
- package/store/defaultRequiredFieldLogger.js.flow +23 -0
- package/subscription/requestSubscription.js.flow +5 -2
- package/util/NormalizationNode.js.flow +17 -2
- package/util/ReaderNode.js.flow +20 -1
- package/util/RelayConcreteNode.js.flow +6 -0
- package/util/RelayFeatureFlags.js.flow +12 -1
- package/util/getFragmentIdentifier.js.flow +33 -9
- package/util/getOperation.js.flow +40 -0
- package/util/getRequestIdentifier.js.flow +1 -1
- package/util/isEmptyObject.js.flow +25 -0
- package/util/recycleNodesInto.js.flow +11 -0
- package/util/reportMissingRequiredFields.js.flow +51 -0
|
@@ -21,14 +21,17 @@ const {
|
|
|
21
21
|
CLIENT_EXTENSION,
|
|
22
22
|
CONDITION,
|
|
23
23
|
DEFER,
|
|
24
|
+
FLIGHT_FIELD,
|
|
24
25
|
FRAGMENT_SPREAD,
|
|
25
26
|
INLINE_DATA_FRAGMENT_SPREAD,
|
|
26
27
|
INLINE_FRAGMENT,
|
|
27
28
|
LINKED_FIELD,
|
|
28
29
|
MODULE_IMPORT,
|
|
30
|
+
REQUIRED_FIELD,
|
|
29
31
|
SCALAR_FIELD,
|
|
30
32
|
STREAM,
|
|
31
33
|
} = require('../util/RelayConcreteNode');
|
|
34
|
+
const {getReactFlightClientResponse} = require('./RelayStoreReactFlightUtils');
|
|
32
35
|
const {
|
|
33
36
|
FRAGMENTS_KEY,
|
|
34
37
|
FRAGMENT_OWNER_KEY,
|
|
@@ -44,11 +47,13 @@ const {
|
|
|
44
47
|
const {generateTypeID} = require('./TypeID');
|
|
45
48
|
|
|
46
49
|
import type {
|
|
50
|
+
ReaderFlightField,
|
|
47
51
|
ReaderFragmentSpread,
|
|
48
52
|
ReaderInlineDataFragmentSpread,
|
|
49
53
|
ReaderLinkedField,
|
|
50
54
|
ReaderModuleImport,
|
|
51
55
|
ReaderNode,
|
|
56
|
+
ReaderRequiredField,
|
|
52
57
|
ReaderScalarField,
|
|
53
58
|
ReaderSelection,
|
|
54
59
|
} from '../util/ReaderNode';
|
|
@@ -60,6 +65,7 @@ import type {
|
|
|
60
65
|
SelectorData,
|
|
61
66
|
SingularReaderSelector,
|
|
62
67
|
Snapshot,
|
|
68
|
+
MissingRequiredFields,
|
|
63
69
|
} from './RelayStoreTypes';
|
|
64
70
|
|
|
65
71
|
function read(
|
|
@@ -76,6 +82,7 @@ function read(
|
|
|
76
82
|
class RelayReader {
|
|
77
83
|
_isMissingData: boolean;
|
|
78
84
|
_isWithinUnmatchedTypeRefinement: boolean;
|
|
85
|
+
_missingRequiredFields: ?MissingRequiredFields;
|
|
79
86
|
_owner: RequestDescriptor;
|
|
80
87
|
_recordSource: RecordSource;
|
|
81
88
|
_seenRecords: {[dataID: DataID]: ?Record, ...};
|
|
@@ -85,6 +92,7 @@ class RelayReader {
|
|
|
85
92
|
constructor(recordSource: RecordSource, selector: SingularReaderSelector) {
|
|
86
93
|
this._isMissingData = false;
|
|
87
94
|
this._isWithinUnmatchedTypeRefinement = false;
|
|
95
|
+
this._missingRequiredFields = null;
|
|
88
96
|
this._owner = selector.owner;
|
|
89
97
|
this._recordSource = recordSource;
|
|
90
98
|
this._seenRecords = {};
|
|
@@ -151,6 +159,7 @@ class RelayReader {
|
|
|
151
159
|
isMissingData: this._isMissingData && isDataExpectedToBePresent,
|
|
152
160
|
seenRecords: this._seenRecords,
|
|
153
161
|
selector: this._selector,
|
|
162
|
+
missingRequiredFields: this._missingRequiredFields,
|
|
154
163
|
};
|
|
155
164
|
}
|
|
156
165
|
|
|
@@ -168,8 +177,12 @@ class RelayReader {
|
|
|
168
177
|
return record;
|
|
169
178
|
}
|
|
170
179
|
const data = prevData || {};
|
|
171
|
-
this._traverseSelections(
|
|
172
|
-
|
|
180
|
+
const hadRequiredData = this._traverseSelections(
|
|
181
|
+
node.selections,
|
|
182
|
+
record,
|
|
183
|
+
data,
|
|
184
|
+
);
|
|
185
|
+
return hadRequiredData ? data : null;
|
|
173
186
|
}
|
|
174
187
|
|
|
175
188
|
_getVariableValue(name: string): mixed {
|
|
@@ -181,14 +194,62 @@ class RelayReader {
|
|
|
181
194
|
return this._variables[name];
|
|
182
195
|
}
|
|
183
196
|
|
|
197
|
+
_maybeReportUnexpectedNull(
|
|
198
|
+
fieldPath: string,
|
|
199
|
+
action: 'LOG' | 'THROW',
|
|
200
|
+
record: Record,
|
|
201
|
+
) {
|
|
202
|
+
if (this._missingRequiredFields?.action === 'THROW') {
|
|
203
|
+
// Chained @required directives may cause a parent `@required(action:
|
|
204
|
+
// THROW)` field to become null, so the first missing field we
|
|
205
|
+
// encounter is likely to be the root cause of the error.
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const owner = this._selector.node.name;
|
|
209
|
+
|
|
210
|
+
switch (action) {
|
|
211
|
+
case 'THROW':
|
|
212
|
+
this._missingRequiredFields = {action, field: {path: fieldPath, owner}};
|
|
213
|
+
return;
|
|
214
|
+
case 'LOG':
|
|
215
|
+
if (this._missingRequiredFields == null) {
|
|
216
|
+
this._missingRequiredFields = {action, fields: []};
|
|
217
|
+
}
|
|
218
|
+
this._missingRequiredFields.fields.push({path: fieldPath, owner});
|
|
219
|
+
return;
|
|
220
|
+
default:
|
|
221
|
+
(action: empty);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
184
225
|
_traverseSelections(
|
|
185
226
|
selections: $ReadOnlyArray<ReaderSelection>,
|
|
186
227
|
record: Record,
|
|
187
228
|
data: SelectorData,
|
|
188
|
-
):
|
|
229
|
+
): boolean /* had all expected data */ {
|
|
189
230
|
for (let i = 0; i < selections.length; i++) {
|
|
190
231
|
const selection = selections[i];
|
|
191
232
|
switch (selection.kind) {
|
|
233
|
+
case REQUIRED_FIELD:
|
|
234
|
+
invariant(
|
|
235
|
+
RelayFeatureFlags.ENABLE_REQUIRED_DIRECTIVES,
|
|
236
|
+
'RelayReader(): Encountered a `@required` directive at path "%s" in `%s` without the `ENABLE_REQUIRED_DIRECTIVES` feature flag enabled.',
|
|
237
|
+
selection.path,
|
|
238
|
+
this._selector.node.name,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const fieldValue = this._readRequiredField(selection, record, data);
|
|
242
|
+
if (fieldValue == null) {
|
|
243
|
+
const {action} = selection;
|
|
244
|
+
if (action !== 'NONE') {
|
|
245
|
+
this._maybeReportUnexpectedNull(selection.path, action, record);
|
|
246
|
+
}
|
|
247
|
+
// We are going to throw, or our parent is going to get nulled out.
|
|
248
|
+
// Either way, sibling values are going to be ignored, so we can
|
|
249
|
+
// bail early here as an optimization.
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
192
253
|
case SCALAR_FIELD:
|
|
193
254
|
this._readScalar(selection, record, data);
|
|
194
255
|
break;
|
|
@@ -202,7 +263,14 @@ class RelayReader {
|
|
|
202
263
|
case CONDITION:
|
|
203
264
|
const conditionValue = this._getVariableValue(selection.condition);
|
|
204
265
|
if (conditionValue === selection.passingValue) {
|
|
205
|
-
this._traverseSelections(
|
|
266
|
+
const hasExpectedData = this._traverseSelections(
|
|
267
|
+
selection.selections,
|
|
268
|
+
record,
|
|
269
|
+
data,
|
|
270
|
+
);
|
|
271
|
+
if (!hasExpectedData) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
206
274
|
}
|
|
207
275
|
break;
|
|
208
276
|
case INLINE_FRAGMENT: {
|
|
@@ -211,7 +279,14 @@ class RelayReader {
|
|
|
211
279
|
// concrete type refinement: only read data if the type exactly matches
|
|
212
280
|
const typeName = RelayModernRecord.getType(record);
|
|
213
281
|
if (typeName != null && typeName === selection.type) {
|
|
214
|
-
this._traverseSelections(
|
|
282
|
+
const hasExpectedData = this._traverseSelections(
|
|
283
|
+
selection.selections,
|
|
284
|
+
record,
|
|
285
|
+
data,
|
|
286
|
+
);
|
|
287
|
+
if (!hasExpectedData) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
215
290
|
}
|
|
216
291
|
} else if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
|
|
217
292
|
// Similar to the logic in read(): data is only expected to be present
|
|
@@ -260,13 +335,36 @@ class RelayReader {
|
|
|
260
335
|
this._createInlineDataFragmentPointer(selection, record, data);
|
|
261
336
|
break;
|
|
262
337
|
case DEFER:
|
|
263
|
-
case CLIENT_EXTENSION:
|
|
338
|
+
case CLIENT_EXTENSION: {
|
|
264
339
|
const isMissingData = this._isMissingData;
|
|
265
|
-
this._traverseSelections(
|
|
340
|
+
const hasExpectedData = this._traverseSelections(
|
|
341
|
+
selection.selections,
|
|
342
|
+
record,
|
|
343
|
+
data,
|
|
344
|
+
);
|
|
266
345
|
this._isMissingData = isMissingData;
|
|
346
|
+
if (!hasExpectedData) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
267
349
|
break;
|
|
268
|
-
|
|
269
|
-
|
|
350
|
+
}
|
|
351
|
+
case STREAM: {
|
|
352
|
+
const hasExpectedData = this._traverseSelections(
|
|
353
|
+
selection.selections,
|
|
354
|
+
record,
|
|
355
|
+
data,
|
|
356
|
+
);
|
|
357
|
+
if (!hasExpectedData) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
case FLIGHT_FIELD:
|
|
363
|
+
if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
|
|
364
|
+
this._readFlightField(selection, record, data);
|
|
365
|
+
} else {
|
|
366
|
+
throw new Error('Flight fields are not yet supported.');
|
|
367
|
+
}
|
|
270
368
|
break;
|
|
271
369
|
default:
|
|
272
370
|
(selection: empty);
|
|
@@ -277,13 +375,76 @@ class RelayReader {
|
|
|
277
375
|
);
|
|
278
376
|
}
|
|
279
377
|
}
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
_readRequiredField(
|
|
382
|
+
selection: ReaderRequiredField,
|
|
383
|
+
record: Record,
|
|
384
|
+
data: SelectorData,
|
|
385
|
+
): ?mixed {
|
|
386
|
+
switch (selection.field.kind) {
|
|
387
|
+
case SCALAR_FIELD:
|
|
388
|
+
return this._readScalar(selection.field, record, data);
|
|
389
|
+
case LINKED_FIELD:
|
|
390
|
+
if (selection.field.plural) {
|
|
391
|
+
return this._readPluralLink(selection.field, record, data);
|
|
392
|
+
} else {
|
|
393
|
+
return this._readLink(selection.field, record, data);
|
|
394
|
+
}
|
|
395
|
+
default:
|
|
396
|
+
(selection.field.kind: empty);
|
|
397
|
+
invariant(
|
|
398
|
+
false,
|
|
399
|
+
'RelayReader(): Unexpected ast kind `%s`.',
|
|
400
|
+
selection.kind,
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
_readFlightField(
|
|
406
|
+
field: ReaderFlightField,
|
|
407
|
+
record: Record,
|
|
408
|
+
data: SelectorData,
|
|
409
|
+
): ?mixed {
|
|
410
|
+
const applicationName = field.alias ?? field.name;
|
|
411
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
412
|
+
const reactFlightClientResponseRecordID = RelayModernRecord.getLinkedRecordID(
|
|
413
|
+
record,
|
|
414
|
+
storageKey,
|
|
415
|
+
);
|
|
416
|
+
if (reactFlightClientResponseRecordID == null) {
|
|
417
|
+
data[applicationName] = reactFlightClientResponseRecordID;
|
|
418
|
+
if (reactFlightClientResponseRecordID === undefined) {
|
|
419
|
+
this._isMissingData = true;
|
|
420
|
+
}
|
|
421
|
+
return reactFlightClientResponseRecordID;
|
|
422
|
+
}
|
|
423
|
+
const reactFlightClientResponseRecord = this._recordSource.get(
|
|
424
|
+
reactFlightClientResponseRecordID,
|
|
425
|
+
);
|
|
426
|
+
this._seenRecords[
|
|
427
|
+
reactFlightClientResponseRecordID
|
|
428
|
+
] = reactFlightClientResponseRecord;
|
|
429
|
+
if (reactFlightClientResponseRecord == null) {
|
|
430
|
+
data[applicationName] = reactFlightClientResponseRecord;
|
|
431
|
+
if (reactFlightClientResponseRecord === undefined) {
|
|
432
|
+
this._isMissingData = true;
|
|
433
|
+
}
|
|
434
|
+
return reactFlightClientResponseRecord;
|
|
435
|
+
}
|
|
436
|
+
const clientResponse = getReactFlightClientResponse(
|
|
437
|
+
reactFlightClientResponseRecord,
|
|
438
|
+
);
|
|
439
|
+
data[applicationName] = clientResponse;
|
|
440
|
+
return clientResponse;
|
|
280
441
|
}
|
|
281
442
|
|
|
282
443
|
_readScalar(
|
|
283
444
|
field: ReaderScalarField,
|
|
284
445
|
record: Record,
|
|
285
446
|
data: SelectorData,
|
|
286
|
-
):
|
|
447
|
+
): ?mixed {
|
|
287
448
|
const applicationName = field.alias ?? field.name;
|
|
288
449
|
const storageKey = getStorageKey(field, this._variables);
|
|
289
450
|
const value = RelayModernRecord.getValue(record, storageKey);
|
|
@@ -291,13 +452,14 @@ class RelayReader {
|
|
|
291
452
|
this._isMissingData = true;
|
|
292
453
|
}
|
|
293
454
|
data[applicationName] = value;
|
|
455
|
+
return value;
|
|
294
456
|
}
|
|
295
457
|
|
|
296
458
|
_readLink(
|
|
297
459
|
field: ReaderLinkedField,
|
|
298
460
|
record: Record,
|
|
299
461
|
data: SelectorData,
|
|
300
|
-
):
|
|
462
|
+
): ?mixed {
|
|
301
463
|
const applicationName = field.alias ?? field.name;
|
|
302
464
|
const storageKey = getStorageKey(field, this._variables);
|
|
303
465
|
const linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
|
|
@@ -306,7 +468,7 @@ class RelayReader {
|
|
|
306
468
|
if (linkedID === undefined) {
|
|
307
469
|
this._isMissingData = true;
|
|
308
470
|
}
|
|
309
|
-
return;
|
|
471
|
+
return linkedID;
|
|
310
472
|
}
|
|
311
473
|
|
|
312
474
|
const prevData = data[applicationName];
|
|
@@ -319,14 +481,16 @@ class RelayReader {
|
|
|
319
481
|
prevData,
|
|
320
482
|
);
|
|
321
483
|
// $FlowFixMe[incompatible-variance]
|
|
322
|
-
|
|
484
|
+
const value = this._traverse(field, linkedID, prevData);
|
|
485
|
+
data[applicationName] = value;
|
|
486
|
+
return value;
|
|
323
487
|
}
|
|
324
488
|
|
|
325
489
|
_readPluralLink(
|
|
326
490
|
field: ReaderLinkedField,
|
|
327
491
|
record: Record,
|
|
328
492
|
data: SelectorData,
|
|
329
|
-
):
|
|
493
|
+
): ?mixed {
|
|
330
494
|
const applicationName = field.alias ?? field.name;
|
|
331
495
|
const storageKey = getStorageKey(field, this._variables);
|
|
332
496
|
const linkedIDs = RelayModernRecord.getLinkedRecordIDs(record, storageKey);
|
|
@@ -336,7 +500,7 @@ class RelayReader {
|
|
|
336
500
|
if (linkedIDs === undefined) {
|
|
337
501
|
this._isMissingData = true;
|
|
338
502
|
}
|
|
339
|
-
return;
|
|
503
|
+
return linkedIDs;
|
|
340
504
|
}
|
|
341
505
|
|
|
342
506
|
const prevData = data[applicationName];
|
|
@@ -372,6 +536,7 @@ class RelayReader {
|
|
|
372
536
|
linkedArray[nextIndex] = this._traverse(field, linkedID, prevItem);
|
|
373
537
|
});
|
|
374
538
|
data[applicationName] = linkedArray;
|
|
539
|
+
return linkedArray;
|
|
375
540
|
}
|
|
376
541
|
|
|
377
542
|
/**
|
|
@@ -15,14 +15,18 @@
|
|
|
15
15
|
const RelayConcreteNode = require('../util/RelayConcreteNode');
|
|
16
16
|
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
17
17
|
const RelayModernRecord = require('./RelayModernRecord');
|
|
18
|
+
const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
|
|
18
19
|
const RelayStoreUtils = require('./RelayStoreUtils');
|
|
19
20
|
|
|
20
21
|
const cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
|
|
22
|
+
const getOperation = require('../util/getOperation');
|
|
21
23
|
const invariant = require('invariant');
|
|
22
24
|
|
|
23
25
|
const {generateTypeID} = require('./TypeID');
|
|
24
26
|
|
|
27
|
+
import type {ReactFlightPayloadQuery} from '../network/RelayNetworkTypes';
|
|
25
28
|
import type {
|
|
29
|
+
NormalizationFlightField,
|
|
26
30
|
NormalizationLinkedField,
|
|
27
31
|
NormalizationModuleImport,
|
|
28
32
|
NormalizationNode,
|
|
@@ -40,6 +44,7 @@ const {
|
|
|
40
44
|
CONDITION,
|
|
41
45
|
CLIENT_EXTENSION,
|
|
42
46
|
DEFER,
|
|
47
|
+
FLIGHT_FIELD,
|
|
43
48
|
FRAGMENT_SPREAD,
|
|
44
49
|
INLINE_FRAGMENT,
|
|
45
50
|
LINKED_FIELD,
|
|
@@ -50,7 +55,7 @@ const {
|
|
|
50
55
|
STREAM,
|
|
51
56
|
TYPE_DISCRIMINATOR,
|
|
52
57
|
} = RelayConcreteNode;
|
|
53
|
-
const {getStorageKey, getModuleOperationKey} = RelayStoreUtils;
|
|
58
|
+
const {ROOT_ID, getStorageKey, getModuleOperationKey} = RelayStoreUtils;
|
|
54
59
|
|
|
55
60
|
function mark(
|
|
56
61
|
recordSource: RecordSource,
|
|
@@ -73,6 +78,7 @@ function mark(
|
|
|
73
78
|
*/
|
|
74
79
|
class RelayReferenceMarker {
|
|
75
80
|
_operationLoader: OperationLoader | null;
|
|
81
|
+
_operationName: ?string;
|
|
76
82
|
_recordSource: RecordSource;
|
|
77
83
|
_references: Set<DataID>;
|
|
78
84
|
_variables: Variables;
|
|
@@ -84,12 +90,16 @@ class RelayReferenceMarker {
|
|
|
84
90
|
operationLoader: ?OperationLoader,
|
|
85
91
|
) {
|
|
86
92
|
this._operationLoader = operationLoader ?? null;
|
|
93
|
+
this._operationName = null;
|
|
87
94
|
this._recordSource = recordSource;
|
|
88
95
|
this._references = references;
|
|
89
96
|
this._variables = variables;
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
mark(node: NormalizationNode, dataID: DataID): void {
|
|
100
|
+
if (node.kind === 'Operation' || node.kind === 'SplitOperation') {
|
|
101
|
+
this._operationName = node.name;
|
|
102
|
+
}
|
|
93
103
|
this._traverse(node, dataID);
|
|
94
104
|
}
|
|
95
105
|
|
|
@@ -146,6 +156,7 @@ class RelayReferenceMarker {
|
|
|
146
156
|
this._traverseSelections(selection.selections, record);
|
|
147
157
|
}
|
|
148
158
|
break;
|
|
159
|
+
// $FlowFixMe[incompatible-type]
|
|
149
160
|
case FRAGMENT_SPREAD:
|
|
150
161
|
invariant(
|
|
151
162
|
false,
|
|
@@ -195,6 +206,13 @@ class RelayReferenceMarker {
|
|
|
195
206
|
case CLIENT_EXTENSION:
|
|
196
207
|
this._traverseSelections(selection.selections, record);
|
|
197
208
|
break;
|
|
209
|
+
case FLIGHT_FIELD:
|
|
210
|
+
if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
|
|
211
|
+
this._traverseFlightField(selection, record);
|
|
212
|
+
} else {
|
|
213
|
+
throw new Error('Flight fields are not yet supported.');
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
198
216
|
default:
|
|
199
217
|
(selection: empty);
|
|
200
218
|
invariant(
|
|
@@ -213,16 +231,20 @@ class RelayReferenceMarker {
|
|
|
213
231
|
const operationLoader = this._operationLoader;
|
|
214
232
|
invariant(
|
|
215
233
|
operationLoader !== null,
|
|
216
|
-
'RelayReferenceMarker: Expected an operationLoader to be configured when using `@module`.'
|
|
234
|
+
'RelayReferenceMarker: Expected an operationLoader to be configured when using `@module`. ' +
|
|
235
|
+
'Could not load fragment `%s` in operation `%s`.',
|
|
236
|
+
moduleImport.fragmentName,
|
|
237
|
+
this._operationName ?? '(unknown)',
|
|
217
238
|
);
|
|
218
239
|
const operationKey = getModuleOperationKey(moduleImport.documentName);
|
|
219
240
|
const operationReference = RelayModernRecord.getValue(record, operationKey);
|
|
220
241
|
if (operationReference == null) {
|
|
221
242
|
return;
|
|
222
243
|
}
|
|
223
|
-
const
|
|
224
|
-
if (
|
|
225
|
-
|
|
244
|
+
const normalizationRootNode = operationLoader.get(operationReference);
|
|
245
|
+
if (normalizationRootNode != null) {
|
|
246
|
+
const selections = getOperation(normalizationRootNode).selections;
|
|
247
|
+
this._traverseSelections(selections, record);
|
|
226
248
|
}
|
|
227
249
|
// Otherwise, if the operation is not available, we assume that the data
|
|
228
250
|
// cannot have been processed yet and therefore isn't in the store to
|
|
@@ -252,6 +274,51 @@ class RelayReferenceMarker {
|
|
|
252
274
|
}
|
|
253
275
|
});
|
|
254
276
|
}
|
|
277
|
+
|
|
278
|
+
_traverseFlightField(field: NormalizationFlightField, record: Record): void {
|
|
279
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
280
|
+
const linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
|
|
281
|
+
if (linkedID == null) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this._references.add(linkedID);
|
|
285
|
+
|
|
286
|
+
const reactFlightClientResponseRecord = this._recordSource.get(linkedID);
|
|
287
|
+
|
|
288
|
+
if (reactFlightClientResponseRecord == null) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const reachableQueries = RelayModernRecord.getValue(
|
|
293
|
+
reactFlightClientResponseRecord,
|
|
294
|
+
RelayStoreReactFlightUtils.REACT_FLIGHT_QUERIES_STORAGE_KEY,
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
if (!Array.isArray(reachableQueries)) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const operationLoader = this._operationLoader;
|
|
302
|
+
invariant(
|
|
303
|
+
operationLoader !== null,
|
|
304
|
+
'DataChecker: Expected an operationLoader to be configured when using ' +
|
|
305
|
+
'React Flight',
|
|
306
|
+
);
|
|
307
|
+
// In Flight, the variables that are in scope for reachable queries aren't
|
|
308
|
+
// the same as what's in scope for the outer query.
|
|
309
|
+
const prevVariables = this._variables;
|
|
310
|
+
// $FlowFixMe[incompatible-cast]
|
|
311
|
+
for (const query of (reachableQueries: Array<ReactFlightPayloadQuery>)) {
|
|
312
|
+
this._variables = query.variables;
|
|
313
|
+
const operationReference = query.module;
|
|
314
|
+
const normalizationRootNode = operationLoader.get(operationReference);
|
|
315
|
+
if (normalizationRootNode != null) {
|
|
316
|
+
const operation = getOperation(normalizationRootNode);
|
|
317
|
+
this._traverse(operation, ROOT_ID);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
this._variables = prevVariables;
|
|
321
|
+
}
|
|
255
322
|
}
|
|
256
323
|
|
|
257
324
|
module.exports = {mark};
|