relay-runtime 10.0.1 → 10.1.0
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 +114 -3
- package/index.js +1 -1
- package/index.js.flow +16 -1
- package/lib/handlers/RelayDefaultHandlerProvider.js +9 -0
- package/lib/handlers/connection/MutationHandlers.js +138 -12
- package/lib/index.js +7 -0
- package/lib/mutations/RelayDeclarativeMutationConfig.js +2 -2
- package/lib/mutations/commitMutation.js +1 -4
- package/lib/mutations/validateMutation.js +27 -7
- package/lib/network/RelayQueryResponseCache.js +2 -2
- 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 +82 -5
- package/lib/store/RelayModernEnvironment.js +18 -6
- package/lib/store/RelayModernFragmentSpecResolver.js +10 -1
- package/lib/store/RelayModernOperationDescriptor.js +6 -5
- package/lib/store/RelayModernQueryExecutor.js +44 -23
- package/lib/store/RelayModernStore.js +25 -14
- package/lib/store/RelayOperationTracker.js +2 -2
- package/lib/store/RelayPublishQueue.js +1 -1
- package/lib/store/RelayReader.js +196 -33
- package/lib/store/RelayRecordSourceMapImpl.js +2 -2
- package/lib/store/RelayReferenceMarker.js +89 -5
- package/lib/store/RelayResponseNormalizer.js +119 -19
- package/lib/store/RelayStoreReactFlightUtils.js +47 -0
- package/lib/store/defaultRequiredFieldLogger.js +18 -0
- package/lib/store/normalizeRelayPayload.js +1 -1
- 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 +5 -2
- 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 +29 -9
- 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 +31 -15
- 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/RelayStoreTypes.js.flow +90 -31
- 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 +8 -1
- package/util/getFragmentIdentifier.js.flow +33 -9
- package/util/getOperation.js.flow +40 -0
- package/util/isEmptyObject.js.flow +25 -0
- package/util/recycleNodesInto.js.flow +11 -0
- package/util/reportMissingRequiredFields.js.flow +51 -0
|
@@ -18,10 +18,12 @@ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
|
18
18
|
const RelayModernRecord = require('./RelayModernRecord');
|
|
19
19
|
const RelayRecordSourceMutator = require('../mutations/RelayRecordSourceMutator');
|
|
20
20
|
const RelayRecordSourceProxy = require('../mutations/RelayRecordSourceProxy');
|
|
21
|
+
const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
|
|
21
22
|
const RelayStoreUtils = require('./RelayStoreUtils');
|
|
22
23
|
|
|
23
24
|
const cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
|
|
24
25
|
const cloneRelayScalarHandleSourceField = require('./cloneRelayScalarHandleSourceField');
|
|
26
|
+
const getOperation = require('../util/getOperation');
|
|
25
27
|
const invariant = require('invariant');
|
|
26
28
|
|
|
27
29
|
const {isClientID} = require('./ClientID');
|
|
@@ -30,6 +32,7 @@ const {generateTypeID} = require('./TypeID');
|
|
|
30
32
|
|
|
31
33
|
import type {
|
|
32
34
|
NormalizationField,
|
|
35
|
+
NormalizationFlightField,
|
|
33
36
|
NormalizationLinkedField,
|
|
34
37
|
NormalizationModuleImport,
|
|
35
38
|
NormalizationNode,
|
|
@@ -43,6 +46,7 @@ import type {
|
|
|
43
46
|
MutableRecordSource,
|
|
44
47
|
NormalizationSelector,
|
|
45
48
|
OperationLoader,
|
|
49
|
+
ReactFlightReachableQuery,
|
|
46
50
|
Record,
|
|
47
51
|
RecordSource,
|
|
48
52
|
} from './RelayStoreTypes';
|
|
@@ -56,6 +60,7 @@ const {
|
|
|
56
60
|
CONDITION,
|
|
57
61
|
CLIENT_EXTENSION,
|
|
58
62
|
DEFER,
|
|
63
|
+
FLIGHT_FIELD,
|
|
59
64
|
FRAGMENT_SPREAD,
|
|
60
65
|
INLINE_FRAGMENT,
|
|
61
66
|
LINKED_FIELD,
|
|
@@ -67,6 +72,7 @@ const {
|
|
|
67
72
|
TYPE_DISCRIMINATOR,
|
|
68
73
|
} = RelayConcreteNode;
|
|
69
74
|
const {
|
|
75
|
+
ROOT_ID,
|
|
70
76
|
getModuleOperationKey,
|
|
71
77
|
getStorageKey,
|
|
72
78
|
getArgumentValues,
|
|
@@ -375,6 +381,7 @@ class DataChecker {
|
|
|
375
381
|
case STREAM:
|
|
376
382
|
this._traverseSelections(selection.selections, dataID);
|
|
377
383
|
break;
|
|
384
|
+
// $FlowFixMe[incompatible-type]
|
|
378
385
|
case FRAGMENT_SPREAD:
|
|
379
386
|
invariant(
|
|
380
387
|
false,
|
|
@@ -409,6 +416,13 @@ class DataChecker {
|
|
|
409
416
|
} // else: if it does or doesn't implement, we don't need to check or skip anything else
|
|
410
417
|
}
|
|
411
418
|
break;
|
|
419
|
+
case FLIGHT_FIELD:
|
|
420
|
+
if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
|
|
421
|
+
this._checkFlightField(selection, dataID);
|
|
422
|
+
} else {
|
|
423
|
+
throw new Error('Flight fields are not yet supported.');
|
|
424
|
+
}
|
|
425
|
+
break;
|
|
412
426
|
default:
|
|
413
427
|
(selection: empty);
|
|
414
428
|
invariant(
|
|
@@ -437,8 +451,9 @@ class DataChecker {
|
|
|
437
451
|
}
|
|
438
452
|
return;
|
|
439
453
|
}
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
454
|
+
const normalizationRootNode = operationLoader.get(operationReference);
|
|
455
|
+
if (normalizationRootNode != null) {
|
|
456
|
+
const operation = getOperation(normalizationRootNode);
|
|
442
457
|
this._traverse(operation, dataID);
|
|
443
458
|
} else {
|
|
444
459
|
// If the fragment is not available, we assume that the data cannot have been
|
|
@@ -495,6 +510,57 @@ class DataChecker {
|
|
|
495
510
|
});
|
|
496
511
|
}
|
|
497
512
|
}
|
|
513
|
+
|
|
514
|
+
_checkFlightField(field: NormalizationFlightField, dataID: DataID): void {
|
|
515
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
516
|
+
const linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
|
|
517
|
+
|
|
518
|
+
if (linkedID == null) {
|
|
519
|
+
if (linkedID === undefined) {
|
|
520
|
+
this._handleMissing();
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const tree = this._mutator.getValue(
|
|
527
|
+
linkedID,
|
|
528
|
+
RelayStoreReactFlightUtils.REACT_FLIGHT_TREE_STORAGE_KEY,
|
|
529
|
+
);
|
|
530
|
+
const reachableQueries = this._mutator.getValue(
|
|
531
|
+
linkedID,
|
|
532
|
+
RelayStoreReactFlightUtils.REACT_FLIGHT_QUERIES_STORAGE_KEY,
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
if (tree == null || !Array.isArray(reachableQueries)) {
|
|
536
|
+
this._handleMissing();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const operationLoader = this._operationLoader;
|
|
541
|
+
invariant(
|
|
542
|
+
operationLoader !== null,
|
|
543
|
+
'DataChecker: Expected an operationLoader to be configured when using ' +
|
|
544
|
+
'React Flight.',
|
|
545
|
+
);
|
|
546
|
+
// In Flight, the variables that are in scope for reachable queries aren't
|
|
547
|
+
// the same as what's in scope for the outer query.
|
|
548
|
+
const prevVariables = this._variables;
|
|
549
|
+
// $FlowFixMe[incompatible-cast]
|
|
550
|
+
for (const query of (reachableQueries: Array<ReactFlightReachableQuery>)) {
|
|
551
|
+
this._variables = query.variables;
|
|
552
|
+
const normalizationRootNode = operationLoader.get(query.module);
|
|
553
|
+
if (normalizationRootNode != null) {
|
|
554
|
+
const operation = getOperation(normalizationRootNode);
|
|
555
|
+
this._traverseSelections(operation.selections, ROOT_ID);
|
|
556
|
+
} else {
|
|
557
|
+
// If the fragment is not available, we assume that the data cannot have
|
|
558
|
+
// been processed yet and must therefore be missing.
|
|
559
|
+
this._handleMissing();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
this._variables = prevVariables;
|
|
563
|
+
}
|
|
498
564
|
}
|
|
499
565
|
|
|
500
566
|
module.exports = {
|
|
@@ -22,6 +22,7 @@ const RelayPublishQueue = require('./RelayPublishQueue');
|
|
|
22
22
|
const RelayRecordSource = require('./RelayRecordSource');
|
|
23
23
|
|
|
24
24
|
const defaultGetDataID = require('./defaultGetDataID');
|
|
25
|
+
const defaultRequiredFieldLogger = require('./defaultRequiredFieldLogger');
|
|
25
26
|
const generateID = require('../util/generateID');
|
|
26
27
|
const invariant = require('invariant');
|
|
27
28
|
|
|
@@ -48,6 +49,7 @@ import type {
|
|
|
48
49
|
IEnvironment,
|
|
49
50
|
LogFunction,
|
|
50
51
|
MissingFieldHandler,
|
|
52
|
+
RequiredFieldLogger,
|
|
51
53
|
OperationAvailability,
|
|
52
54
|
OperationDescriptor,
|
|
53
55
|
OperationLoader,
|
|
@@ -55,6 +57,7 @@ import type {
|
|
|
55
57
|
OptimisticResponseConfig,
|
|
56
58
|
OptimisticUpdateFunction,
|
|
57
59
|
PublishQueue,
|
|
60
|
+
ReactFlightPayloadDeserializer,
|
|
58
61
|
SelectorStoreUpdater,
|
|
59
62
|
SingularReaderSelector,
|
|
60
63
|
Snapshot,
|
|
@@ -68,6 +71,7 @@ export type EnvironmentConfig = {|
|
|
|
68
71
|
+treatMissingFieldsAsNull?: boolean,
|
|
69
72
|
+log?: ?LogFunction,
|
|
70
73
|
+operationLoader?: ?OperationLoader,
|
|
74
|
+
+reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
|
|
71
75
|
+network: INetwork,
|
|
72
76
|
+scheduler?: ?TaskScheduler,
|
|
73
77
|
+store: Store,
|
|
@@ -82,12 +86,14 @@ export type EnvironmentConfig = {|
|
|
|
82
86
|
+UNSTABLE_defaultRenderPolicy?: ?RenderPolicy,
|
|
83
87
|
+options?: mixed,
|
|
84
88
|
+isServer?: boolean,
|
|
89
|
+
+requiredFieldLogger?: ?RequiredFieldLogger,
|
|
85
90
|
|};
|
|
86
91
|
|
|
87
92
|
class RelayModernEnvironment implements IEnvironment {
|
|
88
93
|
__log: LogFunction;
|
|
89
94
|
+_defaultRenderPolicy: RenderPolicy;
|
|
90
95
|
_operationLoader: ?OperationLoader;
|
|
96
|
+
_reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
|
|
91
97
|
_network: INetwork;
|
|
92
98
|
_publishQueue: PublishQueue;
|
|
93
99
|
_scheduler: ?TaskScheduler;
|
|
@@ -100,6 +106,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
100
106
|
_operationExecutions: Map<string, ActiveState>;
|
|
101
107
|
+options: mixed;
|
|
102
108
|
+_isServer: boolean;
|
|
109
|
+
requiredFieldLogger: RequiredFieldLogger;
|
|
103
110
|
|
|
104
111
|
constructor(config: EnvironmentConfig) {
|
|
105
112
|
this.configName = config.configName;
|
|
@@ -108,6 +115,8 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
108
115
|
: RelayDefaultHandlerProvider;
|
|
109
116
|
this._treatMissingFieldsAsNull = config.treatMissingFieldsAsNull === true;
|
|
110
117
|
const operationLoader = config.operationLoader;
|
|
118
|
+
const reactFlightPayloadDeserializer =
|
|
119
|
+
config.reactFlightPayloadDeserializer;
|
|
111
120
|
if (__DEV__) {
|
|
112
121
|
if (operationLoader != null) {
|
|
113
122
|
invariant(
|
|
@@ -119,8 +128,18 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
119
128
|
operationLoader,
|
|
120
129
|
);
|
|
121
130
|
}
|
|
131
|
+
if (reactFlightPayloadDeserializer != null) {
|
|
132
|
+
invariant(
|
|
133
|
+
typeof reactFlightPayloadDeserializer === 'function',
|
|
134
|
+
'RelayModernEnvironment: Expected `reactFlightPayloadDeserializer` ' +
|
|
135
|
+
' to be a function, got `%s`.',
|
|
136
|
+
reactFlightPayloadDeserializer,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
122
139
|
}
|
|
123
140
|
this.__log = config.log ?? emptyFunction;
|
|
141
|
+
this.requiredFieldLogger =
|
|
142
|
+
config.requiredFieldLogger ?? defaultRequiredFieldLogger;
|
|
124
143
|
this._defaultRenderPolicy =
|
|
125
144
|
config.UNSTABLE_defaultRenderPolicy ??
|
|
126
145
|
RelayFeatureFlags.ENABLE_PARTIAL_RENDERING_DEFAULT === true
|
|
@@ -162,6 +181,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
162
181
|
this._missingFieldHandlers = config.missingFieldHandlers;
|
|
163
182
|
this._operationTracker =
|
|
164
183
|
config.operationTracker ?? new RelayOperationTracker();
|
|
184
|
+
this._reactFlightPayloadDeserializer = reactFlightPayloadDeserializer;
|
|
165
185
|
}
|
|
166
186
|
|
|
167
187
|
getStore(): Store {
|
|
@@ -226,6 +246,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
226
246
|
operationLoader: this._operationLoader,
|
|
227
247
|
optimisticConfig,
|
|
228
248
|
publishQueue: this._publishQueue,
|
|
249
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
229
250
|
scheduler: this._scheduler,
|
|
230
251
|
sink,
|
|
231
252
|
source,
|
|
@@ -263,6 +284,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
263
284
|
operationLoader: this._operationLoader,
|
|
264
285
|
optimisticConfig: null,
|
|
265
286
|
publishQueue: this._publishQueue,
|
|
287
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
266
288
|
scheduler: this._scheduler,
|
|
267
289
|
sink,
|
|
268
290
|
source: RelayObservable.from({
|
|
@@ -339,14 +361,11 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
339
361
|
*/
|
|
340
362
|
execute({
|
|
341
363
|
operation,
|
|
342
|
-
cacheConfig,
|
|
343
364
|
updater,
|
|
344
|
-
}: {
|
|
365
|
+
}: {|
|
|
345
366
|
operation: OperationDescriptor,
|
|
346
|
-
cacheConfig?: ?CacheConfig,
|
|
347
367
|
updater?: ?SelectorStoreUpdater,
|
|
348
|
-
|
|
349
|
-
}): RelayObservable<GraphQLResponse> {
|
|
368
|
+
|}): RelayObservable<GraphQLResponse> {
|
|
350
369
|
const [logObserver, logRequestInfo] = this.__createLogObserver(
|
|
351
370
|
operation.request.node.params,
|
|
352
371
|
operation.request.variables,
|
|
@@ -356,7 +375,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
356
375
|
.execute(
|
|
357
376
|
operation.request.node.params,
|
|
358
377
|
operation.request.variables,
|
|
359
|
-
cacheConfig || {},
|
|
378
|
+
operation.request.cacheConfig || {},
|
|
360
379
|
null,
|
|
361
380
|
logRequestInfo,
|
|
362
381
|
)
|
|
@@ -367,6 +386,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
367
386
|
operationLoader: this._operationLoader,
|
|
368
387
|
optimisticConfig: null,
|
|
369
388
|
publishQueue: this._publishQueue,
|
|
389
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
370
390
|
scheduler: this._scheduler,
|
|
371
391
|
sink,
|
|
372
392
|
source,
|
|
@@ -391,14 +411,12 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
391
411
|
* environment.executeMutation({...}).subscribe({...}).
|
|
392
412
|
*/
|
|
393
413
|
executeMutation({
|
|
394
|
-
cacheConfig,
|
|
395
414
|
operation,
|
|
396
415
|
optimisticResponse,
|
|
397
416
|
optimisticUpdater,
|
|
398
417
|
updater,
|
|
399
418
|
uploadables,
|
|
400
419
|
}: {|
|
|
401
|
-
cacheConfig?: ?CacheConfig,
|
|
402
420
|
operation: OperationDescriptor,
|
|
403
421
|
optimisticUpdater?: ?SelectorStoreUpdater,
|
|
404
422
|
optimisticResponse?: ?Object,
|
|
@@ -423,7 +441,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
423
441
|
operation.request.node.params,
|
|
424
442
|
operation.request.variables,
|
|
425
443
|
{
|
|
426
|
-
...cacheConfig,
|
|
444
|
+
...operation.request.cacheConfig,
|
|
427
445
|
force: true,
|
|
428
446
|
},
|
|
429
447
|
uploadables,
|
|
@@ -436,6 +454,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
436
454
|
operationLoader: this._operationLoader,
|
|
437
455
|
optimisticConfig,
|
|
438
456
|
publishQueue: this._publishQueue,
|
|
457
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
439
458
|
scheduler: this._scheduler,
|
|
440
459
|
sink,
|
|
441
460
|
source,
|
|
@@ -473,6 +492,7 @@ class RelayModernEnvironment implements IEnvironment {
|
|
|
473
492
|
operationTracker: this._operationTracker,
|
|
474
493
|
optimisticConfig: null,
|
|
475
494
|
publishQueue: this._publishQueue,
|
|
495
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
476
496
|
scheduler: this._scheduler,
|
|
477
497
|
sink,
|
|
478
498
|
source,
|
|
@@ -17,6 +17,7 @@ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
|
17
17
|
const areEqual = require('areEqual');
|
|
18
18
|
const invariant = require('invariant');
|
|
19
19
|
const isScalarAndEqual = require('../util/isScalarAndEqual');
|
|
20
|
+
const reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
|
|
20
21
|
const warning = require('warning');
|
|
21
22
|
|
|
22
23
|
const {getPromiseForActiveRequest} = require('../query/fetchQueryInternal');
|
|
@@ -34,6 +35,7 @@ import type {
|
|
|
34
35
|
FragmentMap,
|
|
35
36
|
FragmentSpecResolver,
|
|
36
37
|
FragmentSpecResults,
|
|
38
|
+
MissingRequiredFields,
|
|
37
39
|
PluralReaderSelector,
|
|
38
40
|
RelayContext,
|
|
39
41
|
SelectorData,
|
|
@@ -216,6 +218,7 @@ class SelectorResolver {
|
|
|
216
218
|
_data: ?SelectorData;
|
|
217
219
|
_environment: IEnvironment;
|
|
218
220
|
_isMissingData: boolean;
|
|
221
|
+
_missingRequiredFields: ?MissingRequiredFields;
|
|
219
222
|
_selector: SingularReaderSelector;
|
|
220
223
|
_subscription: ?Disposable;
|
|
221
224
|
|
|
@@ -228,6 +231,7 @@ class SelectorResolver {
|
|
|
228
231
|
this._callback = callback;
|
|
229
232
|
this._data = snapshot.data;
|
|
230
233
|
this._isMissingData = snapshot.isMissingData;
|
|
234
|
+
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
231
235
|
this._environment = environment;
|
|
232
236
|
this._selector = selector;
|
|
233
237
|
this._subscription = environment.subscribe(snapshot, this._onChange);
|
|
@@ -282,6 +286,12 @@ class SelectorResolver {
|
|
|
282
286
|
throw promise;
|
|
283
287
|
}
|
|
284
288
|
}
|
|
289
|
+
if (this._missingRequiredFields != null) {
|
|
290
|
+
reportMissingRequiredFields(
|
|
291
|
+
this._environment,
|
|
292
|
+
this._missingRequiredFields,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
285
295
|
return this._data;
|
|
286
296
|
}
|
|
287
297
|
|
|
@@ -296,6 +306,7 @@ class SelectorResolver {
|
|
|
296
306
|
const snapshot = this._environment.lookup(selector);
|
|
297
307
|
this._data = snapshot.data;
|
|
298
308
|
this._isMissingData = snapshot.isMissingData;
|
|
309
|
+
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
299
310
|
this._selector = selector;
|
|
300
311
|
this._subscription = this._environment.subscribe(snapshot, this._onChange);
|
|
301
312
|
}
|
|
@@ -314,7 +325,7 @@ class SelectorResolver {
|
|
|
314
325
|
// NOTE: We manually create the request descriptor here instead of
|
|
315
326
|
// calling createOperationDescriptor() because we want to set a
|
|
316
327
|
// descriptor with *unaltered* variables as the fragment owner.
|
|
317
|
-
// This is a hack that allows us to preserve
|
|
328
|
+
// This is a hack that allows us to preserve existing (broken)
|
|
318
329
|
// behavior of RelayModern containers while using fragment ownership
|
|
319
330
|
// to propagate variables instead of Context.
|
|
320
331
|
// For more details, see the summary of D13999308
|
|
@@ -331,6 +342,7 @@ class SelectorResolver {
|
|
|
331
342
|
_onChange = (snapshot: Snapshot): void => {
|
|
332
343
|
this._data = snapshot.data;
|
|
333
344
|
this._isMissingData = snapshot.isMissingData;
|
|
345
|
+
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
334
346
|
this._callback();
|
|
335
347
|
};
|
|
336
348
|
}
|
|
@@ -23,7 +23,7 @@ const {
|
|
|
23
23
|
const {ROOT_ID} = require('./RelayStoreUtils');
|
|
24
24
|
|
|
25
25
|
import type {ConcreteRequest} from '../util/RelayConcreteNode';
|
|
26
|
-
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
26
|
+
import type {CacheConfig, DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
27
27
|
import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -35,6 +35,7 @@ import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
|
|
|
35
35
|
function createOperationDescriptor(
|
|
36
36
|
request: ConcreteRequest,
|
|
37
37
|
variables: Variables,
|
|
38
|
+
cacheConfig?: ?CacheConfig,
|
|
38
39
|
dataID?: DataID = ROOT_ID,
|
|
39
40
|
): OperationDescriptor {
|
|
40
41
|
const operation = request.operation;
|
|
@@ -42,6 +43,7 @@ function createOperationDescriptor(
|
|
|
42
43
|
const requestDescriptor = createRequestDescriptor(
|
|
43
44
|
request,
|
|
44
45
|
operationVariables,
|
|
46
|
+
cacheConfig,
|
|
45
47
|
);
|
|
46
48
|
const operationDescriptor = {
|
|
47
49
|
fragment: createReaderSelector(
|
|
@@ -68,11 +70,13 @@ function createOperationDescriptor(
|
|
|
68
70
|
function createRequestDescriptor(
|
|
69
71
|
request: ConcreteRequest,
|
|
70
72
|
variables: Variables,
|
|
73
|
+
cacheConfig?: ?CacheConfig,
|
|
71
74
|
): RequestDescriptor {
|
|
72
75
|
const requestDescriptor = {
|
|
73
76
|
identifier: getRequestIdentifier(request.params, variables),
|
|
74
77
|
node: request,
|
|
75
78
|
variables: variables,
|
|
79
|
+
cacheConfig: cacheConfig,
|
|
76
80
|
};
|
|
77
81
|
if (__DEV__) {
|
|
78
82
|
deepFreeze(variables);
|
|
@@ -19,6 +19,7 @@ const RelayObservable = require('../network/RelayObservable');
|
|
|
19
19
|
const RelayRecordSource = require('./RelayRecordSource');
|
|
20
20
|
const RelayResponseNormalizer = require('./RelayResponseNormalizer');
|
|
21
21
|
|
|
22
|
+
const getOperation = require('../util/getOperation');
|
|
22
23
|
const invariant = require('invariant');
|
|
23
24
|
const stableCopy = require('../util/stableCopy');
|
|
24
25
|
const warning = require('warning');
|
|
@@ -46,6 +47,7 @@ import type {
|
|
|
46
47
|
OptimisticResponseConfig,
|
|
47
48
|
OptimisticUpdate,
|
|
48
49
|
PublishQueue,
|
|
50
|
+
ReactFlightPayloadDeserializer,
|
|
49
51
|
Record,
|
|
50
52
|
RelayResponsePayload,
|
|
51
53
|
SelectorStoreUpdater,
|
|
@@ -54,10 +56,12 @@ import type {
|
|
|
54
56
|
} from '../store/RelayStoreTypes';
|
|
55
57
|
import type {
|
|
56
58
|
NormalizationLinkedField,
|
|
57
|
-
|
|
59
|
+
NormalizationOperation,
|
|
60
|
+
NormalizationRootNode,
|
|
58
61
|
NormalizationSelectableNode,
|
|
62
|
+
NormalizationSplitOperation,
|
|
59
63
|
} from '../util/NormalizationNode';
|
|
60
|
-
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
64
|
+
import type {DataID, Variables, Disposable} from '../util/RelayRuntimeTypes';
|
|
61
65
|
import type {GetDataID} from './RelayResponseNormalizer';
|
|
62
66
|
import type {NormalizationOptions} from './RelayResponseNormalizer';
|
|
63
67
|
|
|
@@ -70,6 +74,7 @@ export type ExecuteConfig = {|
|
|
|
70
74
|
+operationTracker?: ?OperationTracker,
|
|
71
75
|
+optimisticConfig: ?OptimisticResponseConfig,
|
|
72
76
|
+publishQueue: PublishQueue,
|
|
77
|
+
+reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
|
|
73
78
|
+scheduler?: ?TaskScheduler,
|
|
74
79
|
+sink: Sink<GraphQLResponse>,
|
|
75
80
|
+source: RelayObservable<GraphQLResponse>,
|
|
@@ -126,6 +131,7 @@ class Executor {
|
|
|
126
131
|
_optimisticUpdates: null | Array<OptimisticUpdate>;
|
|
127
132
|
_pendingModulePayloadsCount: number;
|
|
128
133
|
_publishQueue: PublishQueue;
|
|
134
|
+
_reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
|
|
129
135
|
_scheduler: ?TaskScheduler;
|
|
130
136
|
_sink: Sink<GraphQLResponse>;
|
|
131
137
|
_source: Map<
|
|
@@ -136,6 +142,7 @@ class Executor {
|
|
|
136
142
|
_store: Store;
|
|
137
143
|
_subscriptions: Map<number, Subscription>;
|
|
138
144
|
_updater: ?SelectorStoreUpdater;
|
|
145
|
+
_retainDisposable: ?Disposable;
|
|
139
146
|
+_isClientPayload: boolean;
|
|
140
147
|
|
|
141
148
|
constructor({
|
|
@@ -153,6 +160,7 @@ class Executor {
|
|
|
153
160
|
treatMissingFieldsAsNull,
|
|
154
161
|
getDataID,
|
|
155
162
|
isClientPayload,
|
|
163
|
+
reactFlightPayloadDeserializer,
|
|
156
164
|
}: ExecuteConfig): void {
|
|
157
165
|
this._getDataID = getDataID;
|
|
158
166
|
this._treatMissingFieldsAsNull = treatMissingFieldsAsNull;
|
|
@@ -175,6 +183,7 @@ class Executor {
|
|
|
175
183
|
this._subscriptions = new Map();
|
|
176
184
|
this._updater = updater;
|
|
177
185
|
this._isClientPayload = isClientPayload === true;
|
|
186
|
+
this._reactFlightPayloadDeserializer = reactFlightPayloadDeserializer;
|
|
178
187
|
|
|
179
188
|
const id = this._nextSubscriptionId++;
|
|
180
189
|
source.subscribe({
|
|
@@ -223,6 +232,10 @@ class Executor {
|
|
|
223
232
|
}
|
|
224
233
|
this._incrementalResults.clear();
|
|
225
234
|
this._completeOperationTracker();
|
|
235
|
+
if (this._retainDisposable) {
|
|
236
|
+
this._retainDisposable.dispose();
|
|
237
|
+
this._retainDisposable = null;
|
|
238
|
+
}
|
|
226
239
|
}
|
|
227
240
|
|
|
228
241
|
_updateActiveState(): void {
|
|
@@ -423,19 +436,22 @@ class Executor {
|
|
|
423
436
|
|
|
424
437
|
// In theory this doesn't preserve the ordering of the batch.
|
|
425
438
|
// The idea is that a batch is always:
|
|
426
|
-
// * at
|
|
427
|
-
// * followed zero or more incremental payloads
|
|
439
|
+
// * at most one non-incremental payload
|
|
440
|
+
// * followed by zero or more incremental payloads
|
|
428
441
|
// The non-incremental payload can appear if the server sends a batch
|
|
429
|
-
//
|
|
442
|
+
// with the initial payload followed by some early-to-resolve incremental
|
|
430
443
|
// payloads (although, can that even happen?)
|
|
431
444
|
if (nonIncrementalResponses.length > 0) {
|
|
432
445
|
const payloadFollowups = this._processResponses(nonIncrementalResponses);
|
|
433
|
-
// Please note
|
|
446
|
+
// Please note that we're passing `this._operation` to the publish
|
|
434
447
|
// queue here, which will later passed to the store (via notify)
|
|
435
|
-
// to indicate that this is an operation that
|
|
448
|
+
// to indicate that this is an operation that caused the store to update
|
|
436
449
|
const updatedOwners = this._publishQueue.run(this._operation);
|
|
437
450
|
this._updateOperationTracker(updatedOwners);
|
|
438
451
|
this._processPayloadFollowups(payloadFollowups);
|
|
452
|
+
if (this._incrementalPayloadsPending && !this._retainDisposable) {
|
|
453
|
+
this._retainDisposable = this._store.retain(this._operation);
|
|
454
|
+
}
|
|
439
455
|
}
|
|
440
456
|
|
|
441
457
|
if (incrementalResponses.length > 0) {
|
|
@@ -474,6 +490,7 @@ class Executor {
|
|
|
474
490
|
{
|
|
475
491
|
getDataID: this._getDataID,
|
|
476
492
|
path: [],
|
|
493
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
477
494
|
treatMissingFieldsAsNull,
|
|
478
495
|
},
|
|
479
496
|
);
|
|
@@ -525,11 +542,11 @@ class Executor {
|
|
|
525
542
|
moduleImportPayload,
|
|
526
543
|
);
|
|
527
544
|
} else {
|
|
528
|
-
const
|
|
545
|
+
const moduleImportOptimisticUpdates = this._processOptimisticModuleImport(
|
|
529
546
|
operation,
|
|
530
547
|
moduleImportPayload,
|
|
531
548
|
);
|
|
532
|
-
optimisticUpdates.push(...
|
|
549
|
+
optimisticUpdates.push(...moduleImportOptimisticUpdates);
|
|
533
550
|
}
|
|
534
551
|
}
|
|
535
552
|
}
|
|
@@ -551,15 +568,17 @@ class Executor {
|
|
|
551
568
|
{
|
|
552
569
|
getDataID: this._getDataID,
|
|
553
570
|
path: moduleImportPayload.path,
|
|
571
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
554
572
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
555
573
|
},
|
|
556
574
|
);
|
|
557
575
|
}
|
|
558
576
|
|
|
559
577
|
_processOptimisticModuleImport(
|
|
560
|
-
|
|
578
|
+
normalizationRootNode: NormalizationRootNode,
|
|
561
579
|
moduleImportPayload: ModuleImportPayload,
|
|
562
580
|
): $ReadOnlyArray<OptimisticUpdate> {
|
|
581
|
+
const operation = getOperation(normalizationRootNode);
|
|
563
582
|
const optimisticUpdates = [];
|
|
564
583
|
const modulePayload = this._normalizeModuleImport(
|
|
565
584
|
moduleImportPayload,
|
|
@@ -585,22 +604,22 @@ class Executor {
|
|
|
585
604
|
if (operation == null || this._state !== 'started') {
|
|
586
605
|
return;
|
|
587
606
|
}
|
|
588
|
-
const
|
|
607
|
+
const moduleImportOptimisticUpdates = this._processOptimisticModuleImport(
|
|
589
608
|
operation,
|
|
590
609
|
moduleImportPayload,
|
|
591
610
|
);
|
|
592
|
-
|
|
611
|
+
moduleImportOptimisticUpdates.forEach(update =>
|
|
593
612
|
this._publishQueue.applyUpdate(update),
|
|
594
613
|
);
|
|
595
614
|
if (this._optimisticUpdates == null) {
|
|
596
615
|
warning(
|
|
597
616
|
false,
|
|
598
|
-
'RelayModernQueryExecutor: Unexpected ModuleImport
|
|
617
|
+
'RelayModernQueryExecutor: Unexpected ModuleImport optimistic ' +
|
|
599
618
|
'update in operation %s.' +
|
|
600
619
|
this._operation.request.node.params.name,
|
|
601
620
|
);
|
|
602
621
|
} else {
|
|
603
|
-
this._optimisticUpdates.push(...
|
|
622
|
+
this._optimisticUpdates.push(...moduleImportOptimisticUpdates);
|
|
604
623
|
this._publishQueue.run();
|
|
605
624
|
}
|
|
606
625
|
});
|
|
@@ -624,8 +643,9 @@ class Executor {
|
|
|
624
643
|
ROOT_TYPE,
|
|
625
644
|
{
|
|
626
645
|
getDataID: this._getDataID,
|
|
627
|
-
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
628
646
|
path: [],
|
|
647
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
648
|
+
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
629
649
|
},
|
|
630
650
|
);
|
|
631
651
|
this._publishQueue.commitPayload(
|
|
@@ -737,13 +757,12 @@ class Executor {
|
|
|
737
757
|
moduleImportPayload: ModuleImportPayload,
|
|
738
758
|
operationLoader: OperationLoader,
|
|
739
759
|
): void {
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (syncOperation != null) {
|
|
760
|
+
const node = operationLoader.get(moduleImportPayload.operationReference);
|
|
761
|
+
if (node != null) {
|
|
762
|
+
const operation = getOperation(node);
|
|
744
763
|
// If the operation module is available synchronously, normalize the
|
|
745
764
|
// data synchronously.
|
|
746
|
-
this._handleModuleImportPayload(moduleImportPayload,
|
|
765
|
+
this._handleModuleImportPayload(moduleImportPayload, operation);
|
|
747
766
|
this._maybeCompleteSubscriptionOperationTracking();
|
|
748
767
|
} else {
|
|
749
768
|
// Otherwise load the operation module and schedule a task to normalize
|
|
@@ -766,10 +785,13 @@ class Executor {
|
|
|
766
785
|
.then(resolve, reject);
|
|
767
786
|
}),
|
|
768
787
|
)
|
|
769
|
-
.map((operation: ?
|
|
788
|
+
.map((operation: ?NormalizationRootNode) => {
|
|
770
789
|
if (operation != null) {
|
|
771
790
|
this._schedule(() => {
|
|
772
|
-
this._handleModuleImportPayload(
|
|
791
|
+
this._handleModuleImportPayload(
|
|
792
|
+
moduleImportPayload,
|
|
793
|
+
getOperation(operation),
|
|
794
|
+
);
|
|
773
795
|
});
|
|
774
796
|
}
|
|
775
797
|
})
|
|
@@ -789,7 +811,7 @@ class Executor {
|
|
|
789
811
|
|
|
790
812
|
_handleModuleImportPayload(
|
|
791
813
|
moduleImportPayload: ModuleImportPayload,
|
|
792
|
-
operation: NormalizationSplitOperation,
|
|
814
|
+
operation: NormalizationSplitOperation | NormalizationOperation,
|
|
793
815
|
): void {
|
|
794
816
|
const relayPayload = this._normalizeModuleImport(
|
|
795
817
|
moduleImportPayload,
|
|
@@ -997,6 +1019,7 @@ class Executor {
|
|
|
997
1019
|
{
|
|
998
1020
|
getDataID: this._getDataID,
|
|
999
1021
|
path: placeholder.path,
|
|
1022
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
1000
1023
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1001
1024
|
},
|
|
1002
1025
|
);
|
|
@@ -1211,6 +1234,7 @@ class Executor {
|
|
|
1211
1234
|
const relayPayload = normalizeResponse(response, selector, typeName, {
|
|
1212
1235
|
getDataID: this._getDataID,
|
|
1213
1236
|
path: [...normalizationPath, responseKey, String(itemIndex)],
|
|
1237
|
+
reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
|
|
1214
1238
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1215
1239
|
});
|
|
1216
1240
|
return {
|