relay-runtime 12.0.0 → 13.0.0-rc.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.
Files changed (138) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +2 -2
  2. package/handlers/connection/ConnectionHandler.js.flow +8 -17
  3. package/handlers/connection/MutationHandlers.js.flow +7 -11
  4. package/index.js +1 -1
  5. package/index.js.flow +40 -33
  6. package/lib/handlers/connection/ConnectionHandler.js +12 -18
  7. package/lib/handlers/connection/MutationHandlers.js +3 -6
  8. package/lib/index.js +45 -45
  9. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +8 -4
  10. package/lib/multi-actor-environment/MultiActorEnvironment.js +35 -22
  11. package/lib/multi-actor-environment/index.js +2 -2
  12. package/lib/mutations/RelayDeclarativeMutationConfig.js +4 -1
  13. package/lib/mutations/RelayRecordProxy.js +3 -2
  14. package/lib/mutations/RelayRecordSourceMutator.js +3 -2
  15. package/lib/mutations/RelayRecordSourceProxy.js +12 -4
  16. package/lib/mutations/RelayRecordSourceSelectorProxy.js +12 -4
  17. package/lib/mutations/applyOptimisticMutation.js +6 -6
  18. package/lib/mutations/commitMutation.js +15 -14
  19. package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +238 -0
  20. package/lib/mutations/validateMutation.js +6 -6
  21. package/lib/network/ConvertToExecuteFunction.js +2 -1
  22. package/lib/network/RelayNetwork.js +3 -2
  23. package/lib/network/RelayObservable.js +1 -3
  24. package/lib/network/RelayQueryResponseCache.js +2 -2
  25. package/lib/network/wrapNetworkWithLogObserver.js +2 -1
  26. package/lib/query/GraphQLTag.js +2 -1
  27. package/lib/query/fetchQuery.js +6 -5
  28. package/lib/query/fetchQuery_DEPRECATED.js +2 -1
  29. package/lib/store/ClientID.js +7 -1
  30. package/lib/store/DataChecker.js +16 -17
  31. package/lib/store/OperationExecutor.js +13 -13
  32. package/lib/store/RelayConcreteVariables.js +6 -9
  33. package/lib/store/RelayModernEnvironment.js +66 -42
  34. package/lib/store/RelayModernFragmentSpecResolver.js +8 -8
  35. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  36. package/lib/store/RelayModernRecord.js +12 -11
  37. package/lib/store/RelayModernSelector.js +14 -8
  38. package/lib/store/RelayModernStore.js +14 -15
  39. package/lib/store/RelayPublishQueue.js +11 -5
  40. package/lib/store/RelayReader.js +130 -37
  41. package/lib/store/RelayReferenceMarker.js +10 -11
  42. package/lib/store/RelayResponseNormalizer.js +25 -22
  43. package/lib/store/RelayStoreReactFlightUtils.js +3 -3
  44. package/lib/store/RelayStoreSubscriptions.js +6 -4
  45. package/lib/store/RelayStoreUtils.js +5 -5
  46. package/lib/store/ResolverCache.js +6 -6
  47. package/lib/store/ResolverFragments.js +9 -5
  48. package/lib/store/cloneRelayHandleSourceField.js +5 -4
  49. package/lib/store/cloneRelayScalarHandleSourceField.js +5 -4
  50. package/lib/store/createRelayContext.js +3 -1
  51. package/lib/store/readInlineData.js +6 -2
  52. package/lib/subscription/requestSubscription.js +5 -5
  53. package/lib/util/RelayConcreteNode.js +1 -0
  54. package/lib/util/RelayFeatureFlags.js +7 -1
  55. package/lib/util/RelayRuntimeTypes.js +0 -6
  56. package/lib/util/StringInterner.js +71 -0
  57. package/lib/util/getFragmentIdentifier.js +15 -7
  58. package/lib/util/getOperation.js +2 -1
  59. package/lib/util/getPaginationVariables.js +2 -3
  60. package/lib/util/getRelayHandleKey.js +2 -2
  61. package/lib/util/getRequestIdentifier.js +2 -2
  62. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +27 -19
  63. package/multi-actor-environment/ActorUtils.js.flow +2 -2
  64. package/multi-actor-environment/MultiActorEnvironment.js.flow +45 -24
  65. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +27 -11
  66. package/multi-actor-environment/index.js.flow +1 -2
  67. package/mutations/RelayDeclarativeMutationConfig.js.flow +32 -26
  68. package/mutations/RelayRecordProxy.js.flow +4 -5
  69. package/mutations/RelayRecordSourceMutator.js.flow +4 -6
  70. package/mutations/RelayRecordSourceProxy.js.flow +19 -10
  71. package/mutations/RelayRecordSourceSelectorProxy.js.flow +15 -5
  72. package/mutations/applyOptimisticMutation.js.flow +13 -14
  73. package/mutations/commitLocalUpdate.js.flow +1 -1
  74. package/mutations/commitMutation.js.flow +35 -48
  75. package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +309 -0
  76. package/mutations/validateMutation.js.flow +19 -17
  77. package/network/ConvertToExecuteFunction.js.flow +2 -2
  78. package/network/RelayNetwork.js.flow +4 -5
  79. package/network/RelayObservable.js.flow +1 -3
  80. package/network/RelayQueryResponseCache.js.flow +3 -3
  81. package/network/wrapNetworkWithLogObserver.js.flow +8 -7
  82. package/package.json +1 -1
  83. package/query/GraphQLTag.js.flow +9 -9
  84. package/query/PreloadableQueryRegistry.js.flow +2 -1
  85. package/query/fetchQuery.js.flow +11 -13
  86. package/query/fetchQueryInternal.js.flow +6 -9
  87. package/query/fetchQuery_DEPRECATED.js.flow +6 -6
  88. package/relay-runtime.js +2 -2
  89. package/relay-runtime.min.js +2 -2
  90. package/store/ClientID.js.flow +9 -2
  91. package/store/DataChecker.js.flow +20 -29
  92. package/store/OperationExecutor.js.flow +54 -62
  93. package/store/RelayConcreteVariables.js.flow +4 -10
  94. package/store/RelayModernEnvironment.js.flow +56 -27
  95. package/store/RelayModernFragmentSpecResolver.js.flow +17 -19
  96. package/store/RelayModernOperationDescriptor.js.flow +10 -11
  97. package/store/RelayModernRecord.js.flow +19 -12
  98. package/store/RelayModernSelector.js.flow +24 -14
  99. package/store/RelayModernStore.js.flow +21 -24
  100. package/store/RelayOperationTracker.js.flow +11 -17
  101. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  102. package/store/RelayPublishQueue.js.flow +42 -23
  103. package/store/RelayReader.js.flow +180 -60
  104. package/store/RelayRecordSource.js.flow +2 -2
  105. package/store/RelayReferenceMarker.js.flow +12 -15
  106. package/store/RelayResponseNormalizer.js.flow +43 -41
  107. package/store/RelayStoreReactFlightUtils.js.flow +3 -4
  108. package/store/RelayStoreSubscriptions.js.flow +9 -8
  109. package/store/RelayStoreTypes.js.flow +72 -29
  110. package/store/RelayStoreUtils.js.flow +8 -9
  111. package/store/ResolverCache.js.flow +16 -14
  112. package/store/ResolverFragments.js.flow +15 -22
  113. package/store/StoreInspector.js.flow +2 -2
  114. package/store/TypeID.js.flow +1 -1
  115. package/store/ViewerPattern.js.flow +2 -2
  116. package/store/cloneRelayHandleSourceField.js.flow +5 -6
  117. package/store/cloneRelayScalarHandleSourceField.js.flow +5 -6
  118. package/store/createFragmentSpecResolver.js.flow +3 -4
  119. package/store/createRelayContext.js.flow +2 -2
  120. package/store/normalizeRelayPayload.js.flow +6 -7
  121. package/store/readInlineData.js.flow +7 -8
  122. package/subscription/requestSubscription.js.flow +16 -24
  123. package/util/ReaderNode.js.flow +9 -0
  124. package/util/RelayConcreteNode.js.flow +1 -0
  125. package/util/RelayFeatureFlags.js.flow +14 -2
  126. package/util/RelayReplaySubject.js.flow +2 -3
  127. package/util/RelayRuntimeTypes.js.flow +69 -2
  128. package/util/StringInterner.js.flow +69 -0
  129. package/util/createPayloadFor3DField.js.flow +3 -3
  130. package/util/getFragmentIdentifier.js.flow +27 -15
  131. package/util/getOperation.js.flow +2 -2
  132. package/util/getPaginationMetadata.js.flow +5 -7
  133. package/util/getPaginationVariables.js.flow +5 -9
  134. package/util/getPendingOperationsForFragment.js.flow +2 -2
  135. package/util/getRefetchMetadata.js.flow +6 -7
  136. package/util/getRelayHandleKey.js.flow +1 -2
  137. package/util/getRequestIdentifier.js.flow +3 -3
  138. package/util/resolveImmediate.js.flow +1 -1
@@ -12,14 +12,39 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const ClientID = require('./ClientID');
16
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
17
- const RelayModernRecord = require('./RelayModernRecord');
18
-
19
- const invariant = require('invariant');
15
+ import type {
16
+ ReaderActorChange,
17
+ ReaderClientEdge,
18
+ ReaderFlightField,
19
+ ReaderFragment,
20
+ ReaderFragmentSpread,
21
+ ReaderInlineDataFragmentSpread,
22
+ ReaderLinkedField,
23
+ ReaderModuleImport,
24
+ ReaderNode,
25
+ ReaderRelayResolver,
26
+ ReaderRequiredField,
27
+ ReaderScalarField,
28
+ ReaderSelection,
29
+ } from '../util/ReaderNode';
30
+ import type {DataID, Variables} from '../util/RelayRuntimeTypes';
31
+ import type {
32
+ ClientEdgeTraversalInfo,
33
+ DataIDSet,
34
+ MissingClientEdgeRequestInfo,
35
+ MissingRequiredFields,
36
+ Record,
37
+ RecordSource,
38
+ RequestDescriptor,
39
+ SelectorData,
40
+ SingularReaderSelector,
41
+ Snapshot,
42
+ } from './RelayStoreTypes';
43
+ import type {ResolverCache} from './ResolverCache';
20
44
 
21
45
  const {
22
46
  ACTOR_CHANGE,
47
+ CLIENT_EDGE,
23
48
  CLIENT_EXTENSION,
24
49
  CONDITION,
25
50
  DEFER,
@@ -29,54 +54,32 @@ const {
29
54
  INLINE_FRAGMENT,
30
55
  LINKED_FIELD,
31
56
  MODULE_IMPORT,
32
- REQUIRED_FIELD,
33
57
  RELAY_RESOLVER,
58
+ REQUIRED_FIELD,
34
59
  SCALAR_FIELD,
35
60
  STREAM,
36
61
  } = require('../util/RelayConcreteNode');
62
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
63
+ const ClientID = require('./ClientID');
64
+ const RelayModernRecord = require('./RelayModernRecord');
37
65
  const {getReactFlightClientResponse} = require('./RelayStoreReactFlightUtils');
38
66
  const {
39
- FRAGMENTS_KEY,
67
+ CLIENT_EDGE_TRAVERSAL_PATH,
40
68
  FRAGMENT_OWNER_KEY,
41
69
  FRAGMENT_PROP_NAME_KEY,
70
+ FRAGMENTS_KEY,
42
71
  ID_KEY,
43
72
  IS_WITHIN_UNMATCHED_TYPE_REFINEMENT,
44
73
  MODULE_COMPONENT_KEY,
45
74
  ROOT_ID,
46
75
  getArgumentValues,
47
- getStorageKey,
48
76
  getModuleComponentKey,
77
+ getStorageKey,
49
78
  } = require('./RelayStoreUtils');
50
79
  const {NoopResolverCache} = require('./ResolverCache');
51
80
  const {withResolverContext} = require('./ResolverFragments');
52
81
  const {generateTypeID} = require('./TypeID');
53
-
54
- import type {
55
- ReaderFlightField,
56
- ReaderFragment,
57
- ReaderFragmentSpread,
58
- ReaderInlineDataFragmentSpread,
59
- ReaderLinkedField,
60
- ReaderActorChange,
61
- ReaderModuleImport,
62
- ReaderNode,
63
- ReaderRelayResolver,
64
- ReaderRequiredField,
65
- ReaderScalarField,
66
- ReaderSelection,
67
- } from '../util/ReaderNode';
68
- import type {DataID, Variables} from '../util/RelayRuntimeTypes';
69
- import type {
70
- Record,
71
- RecordSource,
72
- RequestDescriptor,
73
- SelectorData,
74
- SingularReaderSelector,
75
- Snapshot,
76
- MissingRequiredFields,
77
- DataIDSet,
78
- } from './RelayStoreTypes';
79
- import type {ResolverCache} from './ResolverCache';
82
+ const invariant = require('invariant');
80
83
 
81
84
  function read(
82
85
  recordSource: RecordSource,
@@ -95,7 +98,9 @@ function read(
95
98
  * @private
96
99
  */
97
100
  class RelayReader {
101
+ _clientEdgeTraversalPath: Array<ClientEdgeTraversalInfo | null>;
98
102
  _isMissingData: boolean;
103
+ _missingClientEdges: Array<MissingClientEdgeRequestInfo>;
99
104
  _isWithinUnmatchedTypeRefinement: boolean;
100
105
  _missingRequiredFields: ?MissingRequiredFields;
101
106
  _owner: RequestDescriptor;
@@ -110,6 +115,12 @@ class RelayReader {
110
115
  selector: SingularReaderSelector,
111
116
  resolverCache: ResolverCache,
112
117
  ) {
118
+ this._clientEdgeTraversalPath =
119
+ RelayFeatureFlags.ENABLE_CLIENT_EDGES &&
120
+ selector.clientEdgeTraversalPath?.length
121
+ ? [...selector.clientEdgeTraversalPath]
122
+ : [];
123
+ this._missingClientEdges = [];
113
124
  this._isMissingData = false;
114
125
  this._isWithinUnmatchedTypeRefinement = false;
115
126
  this._missingRequiredFields = null;
@@ -184,12 +195,36 @@ class RelayReader {
184
195
  return {
185
196
  data,
186
197
  isMissingData: this._isMissingData && isDataExpectedToBePresent,
198
+ missingClientEdges:
199
+ RelayFeatureFlags.ENABLE_CLIENT_EDGES && this._missingClientEdges.length
200
+ ? this._missingClientEdges
201
+ : null,
187
202
  seenRecords: this._seenRecords,
188
203
  selector: this._selector,
189
204
  missingRequiredFields: this._missingRequiredFields,
190
205
  };
191
206
  }
192
207
 
208
+ _markDataAsMissing(): void {
209
+ this._isMissingData = true;
210
+ if (
211
+ RelayFeatureFlags.ENABLE_CLIENT_EDGES &&
212
+ this._clientEdgeTraversalPath.length
213
+ ) {
214
+ const top =
215
+ this._clientEdgeTraversalPath[this._clientEdgeTraversalPath.length - 1];
216
+ // Top can be null if we've traversed past a client edge into an ordinary
217
+ // client extension field; we never want to fetch in response to missing
218
+ // data off of a client extension field.
219
+ if (top !== null) {
220
+ this._missingClientEdges.push({
221
+ request: top.readerClientEdge.operation,
222
+ clientEdgeDestinationID: top.clientEdgeDestinationID,
223
+ });
224
+ }
225
+ }
226
+ }
227
+
193
228
  _traverse(
194
229
  node: ReaderNode,
195
230
  dataID: DataID,
@@ -199,7 +234,7 @@ class RelayReader {
199
234
  this._seenRecords.add(dataID);
200
235
  if (record == null) {
201
236
  if (record === undefined) {
202
- this._isMissingData = true;
237
+ this._markDataAsMissing();
203
238
  }
204
239
  return record;
205
240
  }
@@ -218,7 +253,6 @@ class RelayReader {
218
253
  'RelayReader(): Undefined variable `%s`.',
219
254
  name,
220
255
  );
221
- // $FlowFixMe[cannot-write]
222
256
  return this._variables[name];
223
257
  }
224
258
 
@@ -325,8 +359,8 @@ class RelayReader {
325
359
 
326
360
  // store flags to reset after reading
327
361
  const parentIsMissingData = this._isMissingData;
328
- const parentIsWithinUnmatchedTypeRefinement = this
329
- ._isWithinUnmatchedTypeRefinement;
362
+ const parentIsWithinUnmatchedTypeRefinement =
363
+ this._isWithinUnmatchedTypeRefinement;
330
364
 
331
365
  const typeName = RelayModernRecord.getType(record);
332
366
  const typeID = generateTypeID(typeName);
@@ -339,14 +373,15 @@ class RelayReader {
339
373
  parentIsWithinUnmatchedTypeRefinement ||
340
374
  implementsInterface === false;
341
375
  this._traverseSelections(selection.selections, record, data);
342
- this._isWithinUnmatchedTypeRefinement = parentIsWithinUnmatchedTypeRefinement;
376
+ this._isWithinUnmatchedTypeRefinement =
377
+ parentIsWithinUnmatchedTypeRefinement;
343
378
 
344
379
  if (implementsInterface === false) {
345
380
  // Type known to not implement the interface, no data expected
346
381
  this._isMissingData = parentIsMissingData;
347
382
  } else if (implementsInterface == null) {
348
383
  // Don't know if the type implements the interface or not
349
- this._isMissingData = true;
384
+ this._markDataAsMissing();
350
385
  }
351
386
  }
352
387
  break;
@@ -374,12 +409,24 @@ class RelayReader {
374
409
  case DEFER:
375
410
  case CLIENT_EXTENSION: {
376
411
  const isMissingData = this._isMissingData;
412
+ const alreadyMissingClientEdges = this._missingClientEdges.length;
413
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
414
+ this._clientEdgeTraversalPath.push(null);
415
+ }
377
416
  const hasExpectedData = this._traverseSelections(
378
417
  selection.selections,
379
418
  record,
380
419
  data,
381
420
  );
382
- this._isMissingData = isMissingData;
421
+ // The only case where we want to suspend due to missing data off of
422
+ // a client extension is if we reached a client edge that we might be
423
+ // able to fetch:
424
+ this._isMissingData =
425
+ isMissingData ||
426
+ this._missingClientEdges.length > alreadyMissingClientEdges;
427
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
428
+ this._clientEdgeTraversalPath.pop();
429
+ }
383
430
  if (!hasExpectedData) {
384
431
  return false;
385
432
  }
@@ -406,6 +453,13 @@ class RelayReader {
406
453
  case ACTOR_CHANGE:
407
454
  this._readActorChange(selection, record, data);
408
455
  break;
456
+ case CLIENT_EDGE:
457
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
458
+ this._readClientEdge(selection, record, data);
459
+ } else {
460
+ throw new Error('Client edges are not yet supported.');
461
+ }
462
+ break;
409
463
  default:
410
464
  (selection: empty);
411
465
  invariant(
@@ -452,7 +506,7 @@ class RelayReader {
452
506
  field: ReaderRelayResolver,
453
507
  record: Record,
454
508
  data: SelectorData,
455
- ): ?mixed {
509
+ ): void {
456
510
  const {resolverModule, fragment} = field;
457
511
  const storageKey = getStorageKey(field, this._variables);
458
512
  const resolverID = ClientID.generateClientID(
@@ -525,8 +579,66 @@ class RelayReader {
525
579
  if (seenRecord != null) {
526
580
  this._seenRecords.add(seenRecord);
527
581
  }
528
- data[storageKey] = result;
529
- return result;
582
+
583
+ const applicationName = field.alias ?? field.name;
584
+ data[applicationName] = result;
585
+ }
586
+
587
+ _readClientEdge(
588
+ field: ReaderClientEdge,
589
+ record: Record,
590
+ data: SelectorData,
591
+ ): void {
592
+ const backingField = field.backingField;
593
+
594
+ // Because ReaderClientExtension doesn't have `alias` or `name` and so I don't know
595
+ // how to get its applicationName or storageKey yet:
596
+ invariant(
597
+ backingField.kind !== 'ClientExtension',
598
+ 'Client extension client edges are not yet implemented.',
599
+ );
600
+
601
+ const applicationName = backingField.alias ?? backingField.name;
602
+
603
+ const backingFieldData = {};
604
+ this._traverseSelections([backingField], record, backingFieldData);
605
+ const destinationDataID = backingFieldData[applicationName];
606
+
607
+ if (destinationDataID == null) {
608
+ data[applicationName] = destinationDataID;
609
+ return;
610
+ }
611
+
612
+ invariant(
613
+ typeof destinationDataID === 'string',
614
+ 'Plural client edges not are yet implemented',
615
+ ); // FIXME support plural
616
+
617
+ // Not wrapping the push/pop in a try/finally because if we throw, the
618
+ // Reader object is not usable after that anyway.
619
+ this._clientEdgeTraversalPath.push({
620
+ readerClientEdge: field,
621
+ clientEdgeDestinationID: destinationDataID,
622
+ });
623
+
624
+ const prevData = data[applicationName];
625
+ invariant(
626
+ prevData == null || typeof prevData === 'object',
627
+ 'RelayReader(): Expected data for field `%s` on record `%s` ' +
628
+ 'to be an object, got `%s`.',
629
+ applicationName,
630
+ RelayModernRecord.getDataID(record),
631
+ prevData,
632
+ );
633
+ const value = this._traverse(
634
+ field.linkedField,
635
+ destinationDataID,
636
+ // $FlowFixMe[incompatible-variance]
637
+ prevData,
638
+ );
639
+ data[applicationName] = value;
640
+
641
+ this._clientEdgeTraversalPath.pop();
530
642
  }
531
643
 
532
644
  _readFlightField(
@@ -536,14 +648,12 @@ class RelayReader {
536
648
  ): ?mixed {
537
649
  const applicationName = field.alias ?? field.name;
538
650
  const storageKey = getStorageKey(field, this._variables);
539
- const reactFlightClientResponseRecordID = RelayModernRecord.getLinkedRecordID(
540
- record,
541
- storageKey,
542
- );
651
+ const reactFlightClientResponseRecordID =
652
+ RelayModernRecord.getLinkedRecordID(record, storageKey);
543
653
  if (reactFlightClientResponseRecordID == null) {
544
654
  data[applicationName] = reactFlightClientResponseRecordID;
545
655
  if (reactFlightClientResponseRecordID === undefined) {
546
- this._isMissingData = true;
656
+ this._markDataAsMissing();
547
657
  }
548
658
  return reactFlightClientResponseRecordID;
549
659
  }
@@ -554,7 +664,7 @@ class RelayReader {
554
664
  if (reactFlightClientResponseRecord == null) {
555
665
  data[applicationName] = reactFlightClientResponseRecord;
556
666
  if (reactFlightClientResponseRecord === undefined) {
557
- this._isMissingData = true;
667
+ this._markDataAsMissing();
558
668
  }
559
669
  return reactFlightClientResponseRecord;
560
670
  }
@@ -574,7 +684,7 @@ class RelayReader {
574
684
  const storageKey = getStorageKey(field, this._variables);
575
685
  const value = RelayModernRecord.getValue(record, storageKey);
576
686
  if (value === undefined) {
577
- this._isMissingData = true;
687
+ this._markDataAsMissing();
578
688
  }
579
689
  data[applicationName] = value;
580
690
  return value;
@@ -591,7 +701,7 @@ class RelayReader {
591
701
  if (linkedID == null) {
592
702
  data[applicationName] = linkedID;
593
703
  if (linkedID === undefined) {
594
- this._isMissingData = true;
704
+ this._markDataAsMissing();
595
705
  }
596
706
  return linkedID;
597
707
  }
@@ -626,7 +736,7 @@ class RelayReader {
626
736
  if (externalRef == null) {
627
737
  data[applicationName] = externalRef;
628
738
  if (externalRef === undefined) {
629
- this._isMissingData = true;
739
+ this._markDataAsMissing();
630
740
  }
631
741
  return data[applicationName];
632
742
  }
@@ -659,7 +769,7 @@ class RelayReader {
659
769
  if (linkedIDs == null) {
660
770
  data[applicationName] = linkedIDs;
661
771
  if (linkedIDs === undefined) {
662
- this._isMissingData = true;
772
+ this._markDataAsMissing();
663
773
  }
664
774
  return linkedIDs;
665
775
  }
@@ -677,7 +787,7 @@ class RelayReader {
677
787
  linkedIDs.forEach((linkedID, nextIndex) => {
678
788
  if (linkedID == null) {
679
789
  if (linkedID === undefined) {
680
- this._isMissingData = true;
790
+ this._markDataAsMissing();
681
791
  }
682
792
  // $FlowFixMe[cannot-write]
683
793
  linkedArray[nextIndex] = linkedID;
@@ -715,7 +825,7 @@ class RelayReader {
715
825
  const component = RelayModernRecord.getValue(record, componentKey);
716
826
  if (component == null) {
717
827
  if (component === undefined) {
718
- this._isMissingData = true;
828
+ this._markDataAsMissing();
719
829
  }
720
830
  return;
721
831
  }
@@ -760,9 +870,19 @@ class RelayReader {
760
870
  ? getArgumentValues(fragmentSpread.args, this._variables)
761
871
  : {};
762
872
  data[FRAGMENT_OWNER_KEY] = this._owner;
763
- data[
764
- IS_WITHIN_UNMATCHED_TYPE_REFINEMENT
765
- ] = this._isWithinUnmatchedTypeRefinement;
873
+ data[IS_WITHIN_UNMATCHED_TYPE_REFINEMENT] =
874
+ this._isWithinUnmatchedTypeRefinement;
875
+
876
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
877
+ if (
878
+ this._clientEdgeTraversalPath.length > 0 &&
879
+ this._clientEdgeTraversalPath[
880
+ this._clientEdgeTraversalPath.length - 1
881
+ ] !== null
882
+ ) {
883
+ data[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath];
884
+ }
885
+ }
766
886
  }
767
887
 
768
888
  _createInlineDataOrResolverFragmentPointer(
@@ -12,8 +12,6 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const RelayRecordState = require('./RelayRecordState');
16
-
17
15
  import type {DataID} from '../util/RelayRuntimeTypes';
18
16
  import type {RecordState} from './RelayRecordState';
19
17
  import type {
@@ -22,6 +20,8 @@ import type {
22
20
  RecordObjectMap,
23
21
  } from './RelayStoreTypes';
24
22
 
23
+ const RelayRecordState = require('./RelayRecordState');
24
+
25
25
  const {EXISTENT, NONEXISTENT, UNKNOWN} = RelayRecordState;
26
26
 
27
27
  /**
@@ -12,19 +12,6 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const RelayConcreteNode = require('../util/RelayConcreteNode');
16
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
17
- const RelayModernRecord = require('./RelayModernRecord');
18
- const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
19
- const RelayStoreUtils = require('./RelayStoreUtils');
20
-
21
- const cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
22
- const getOperation = require('../util/getOperation');
23
- const invariant = require('invariant');
24
-
25
- const {getLocalVariables} = require('./RelayConcreteVariables');
26
- const {generateTypeID} = require('./TypeID');
27
-
28
15
  import type {
29
16
  NormalizationFlightField,
30
17
  NormalizationLinkedField,
@@ -37,11 +24,22 @@ import type {
37
24
  DataIDSet,
38
25
  NormalizationSelector,
39
26
  OperationLoader,
27
+ ReactFlightReachableExecutableDefinitions,
40
28
  Record,
41
29
  RecordSource,
42
- ReactFlightReachableExecutableDefinitions,
43
30
  } from './RelayStoreTypes';
44
31
 
32
+ const getOperation = require('../util/getOperation');
33
+ const RelayConcreteNode = require('../util/RelayConcreteNode');
34
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
35
+ const cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
36
+ const {getLocalVariables} = require('./RelayConcreteVariables');
37
+ const RelayModernRecord = require('./RelayModernRecord');
38
+ const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
39
+ const RelayStoreUtils = require('./RelayStoreUtils');
40
+ const {generateTypeID} = require('./TypeID');
41
+ const invariant = require('invariant');
42
+
45
43
  const {
46
44
  ACTOR_CHANGE,
47
45
  CONDITION,
@@ -127,7 +125,6 @@ class RelayReferenceMarker {
127
125
  'RelayReferenceMarker(): Undefined variable `%s`.',
128
126
  name,
129
127
  );
130
- // $FlowFixMe[cannot-write]
131
128
  return this._variables[name];
132
129
  }
133
130
 
@@ -12,12 +12,31 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
- const RelayModernRecord = require('./RelayModernRecord');
17
-
18
- const areEqual = require('areEqual');
19
- const invariant = require('invariant');
20
- const warning = require('warning');
15
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
16
+ import type {PayloadData} from '../network/RelayNetworkTypes';
17
+ import type {
18
+ NormalizationActorChange,
19
+ NormalizationDefer,
20
+ NormalizationFlightField,
21
+ NormalizationLinkedField,
22
+ NormalizationModuleImport,
23
+ NormalizationNode,
24
+ NormalizationScalarField,
25
+ NormalizationStream,
26
+ } from '../util/NormalizationNode';
27
+ import type {DataID, Variables} from '../util/RelayRuntimeTypes';
28
+ import type {
29
+ FollowupPayload,
30
+ HandleFieldPayload,
31
+ IncrementalDataPlaceholder,
32
+ MutableRecordSource,
33
+ NormalizationSelector,
34
+ ReactFlightPayloadDeserializer,
35
+ ReactFlightReachableExecutableDefinitions,
36
+ ReactFlightServerErrorHandler,
37
+ Record,
38
+ RelayResponsePayload,
39
+ } from './RelayStoreTypes';
21
40
 
22
41
  const {
23
42
  ACTOR_IDENTIFIER_FIELD_NAME,
@@ -25,9 +44,9 @@ const {
25
44
  } = require('../multi-actor-environment/ActorUtils');
26
45
  const {
27
46
  ACTOR_CHANGE,
28
- CONDITION,
29
47
  CLIENT_COMPONENT,
30
48
  CLIENT_EXTENSION,
49
+ CONDITION,
31
50
  DEFER,
32
51
  FLIGHT_FIELD,
33
52
  FRAGMENT_SPREAD,
@@ -40,52 +59,31 @@ const {
40
59
  STREAM,
41
60
  TYPE_DISCRIMINATOR,
42
61
  } = require('../util/RelayConcreteNode');
62
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
43
63
  const {generateClientID, isClientID} = require('./ClientID');
44
64
  const {getLocalVariables} = require('./RelayConcreteVariables');
65
+ const RelayModernRecord = require('./RelayModernRecord');
45
66
  const {createNormalizationSelector} = require('./RelayModernSelector');
46
67
  const {
47
- refineToReactFlightPayloadData,
48
68
  REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
49
69
  REACT_FLIGHT_TREE_STORAGE_KEY,
50
70
  REACT_FLIGHT_TYPE_NAME,
71
+ refineToReactFlightPayloadData,
51
72
  } = require('./RelayStoreReactFlightUtils');
52
73
  const {
74
+ ROOT_ID,
75
+ ROOT_TYPE,
76
+ TYPENAME_KEY,
53
77
  getArgumentValues,
54
78
  getHandleStorageKey,
55
79
  getModuleComponentKey,
56
80
  getModuleOperationKey,
57
81
  getStorageKey,
58
- TYPENAME_KEY,
59
- ROOT_ID,
60
- ROOT_TYPE,
61
82
  } = require('./RelayStoreUtils');
62
- const {generateTypeID, TYPE_SCHEMA_TYPE} = require('./TypeID');
63
-
64
- import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
65
- import type {PayloadData} from '../network/RelayNetworkTypes';
66
- import type {
67
- NormalizationActorChange,
68
- NormalizationDefer,
69
- NormalizationFlightField,
70
- NormalizationLinkedField,
71
- NormalizationModuleImport,
72
- NormalizationNode,
73
- NormalizationScalarField,
74
- NormalizationStream,
75
- } from '../util/NormalizationNode';
76
- import type {DataID, Variables} from '../util/RelayRuntimeTypes';
77
- import type {
78
- FollowupPayload,
79
- HandleFieldPayload,
80
- IncrementalDataPlaceholder,
81
- MutableRecordSource,
82
- NormalizationSelector,
83
- ReactFlightPayloadDeserializer,
84
- ReactFlightReachableExecutableDefinitions,
85
- ReactFlightServerErrorHandler,
86
- Record,
87
- RelayResponsePayload,
88
- } from './RelayStoreTypes';
83
+ const {TYPE_SCHEMA_TYPE, generateTypeID} = require('./TypeID');
84
+ const areEqual = require('areEqual');
85
+ const invariant = require('invariant');
86
+ const warning = require('warning');
89
87
 
90
88
  export type GetDataID = (
91
89
  fieldValue: interface {[string]: mixed},
@@ -192,7 +190,6 @@ class RelayResponseNormalizer {
192
190
  'RelayResponseNormalizer(): Undefined variable `%s`.',
193
191
  name,
194
192
  );
195
- // $FlowFixMe[cannot-write]
196
193
  return this._variables[name];
197
194
  }
198
195
 
@@ -508,7 +505,11 @@ class RelayResponseNormalizer {
508
505
  this._validateConflictingFieldsWithIdenticalId(
509
506
  record,
510
507
  storageKey,
511
- fieldValue,
508
+ // When using `treatMissingFieldsAsNull` the conflicting validation raises a false positive
509
+ // because the value is set using `null` but validated using `fieldValue` which at this point
510
+ // will be `undefined`.
511
+ // Setting this to `null` matches the value that we actually set to the `fieldValue`.
512
+ null,
512
513
  );
513
514
  }
514
515
  }
@@ -765,7 +766,8 @@ class RelayResponseNormalizer {
765
766
  reactFlightClientResponse,
766
767
  );
767
768
 
768
- const reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions> = [];
769
+ const reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions> =
770
+ [];
769
771
  for (const query of reactFlightPayload.queries) {
770
772
  if (query.response.data != null) {
771
773
  this._followupPayloads.push({
@@ -12,13 +12,12 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const invariant = require('invariant');
16
-
17
- const {getType} = require('./RelayModernRecord');
18
-
19
15
  import type {ReactFlightPayloadData} from '../network/RelayNetworkTypes';
20
16
  import type {ReactFlightClientResponse, Record} from './RelayStoreTypes';
21
17
 
18
+ const {getType} = require('./RelayModernRecord');
19
+ const invariant = require('invariant');
20
+
22
21
  // Reachable (client) executable definitions encountered while server component
23
22
  // rendering
24
23
  const REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY = 'executableDefinitions';