relay-runtime 12.0.0 → 13.0.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 (216) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +67 -0
  3. package/handlers/RelayDefaultHandlerProvider.js.flow +3 -3
  4. package/handlers/connection/ConnectionHandler.js.flow +9 -18
  5. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  6. package/handlers/connection/MutationHandlers.js.flow +8 -12
  7. package/index.js +2 -2
  8. package/index.js.flow +43 -34
  9. package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
  10. package/lib/handlers/connection/ConnectionHandler.js +13 -19
  11. package/lib/handlers/connection/ConnectionInterface.js +1 -1
  12. package/lib/handlers/connection/MutationHandlers.js +4 -7
  13. package/lib/index.js +49 -46
  14. package/lib/multi-actor-environment/ActorIdentifier.js +1 -1
  15. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +9 -5
  16. package/lib/multi-actor-environment/ActorUtils.js +1 -1
  17. package/lib/multi-actor-environment/MultiActorEnvironment.js +36 -23
  18. package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +1 -1
  19. package/lib/multi-actor-environment/index.js +3 -3
  20. package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -2
  21. package/lib/mutations/RelayRecordProxy.js +4 -3
  22. package/lib/mutations/RelayRecordSourceMutator.js +4 -3
  23. package/lib/mutations/RelayRecordSourceProxy.js +13 -5
  24. package/lib/mutations/RelayRecordSourceSelectorProxy.js +13 -5
  25. package/lib/mutations/applyOptimisticMutation.js +7 -7
  26. package/lib/mutations/commitLocalUpdate.js +1 -1
  27. package/lib/mutations/commitMutation.js +16 -15
  28. package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +242 -0
  29. package/lib/mutations/validateMutation.js +7 -7
  30. package/lib/network/ConvertToExecuteFunction.js +3 -2
  31. package/lib/network/RelayNetwork.js +4 -3
  32. package/lib/network/RelayNetworkTypes.js +1 -1
  33. package/lib/network/RelayObservable.js +2 -4
  34. package/lib/network/RelayQueryResponseCache.js +3 -3
  35. package/lib/network/wrapNetworkWithLogObserver.js +3 -2
  36. package/lib/query/GraphQLTag.js +3 -2
  37. package/lib/query/PreloadableQueryRegistry.js +1 -1
  38. package/lib/query/fetchQuery.js +7 -6
  39. package/lib/query/fetchQueryInternal.js +1 -1
  40. package/lib/query/fetchQuery_DEPRECATED.js +3 -2
  41. package/lib/store/ClientID.js +8 -2
  42. package/lib/store/DataChecker.js +17 -18
  43. package/lib/store/OperationExecutor.js +14 -14
  44. package/lib/store/RelayConcreteVariables.js +7 -10
  45. package/lib/store/RelayExperimentalGraphResponseHandler.js +153 -0
  46. package/lib/store/RelayExperimentalGraphResponseTransform.js +391 -0
  47. package/lib/store/RelayModernEnvironment.js +67 -43
  48. package/lib/store/RelayModernFragmentSpecResolver.js +9 -9
  49. package/lib/store/RelayModernOperationDescriptor.js +3 -2
  50. package/lib/store/RelayModernRecord.js +13 -12
  51. package/lib/store/RelayModernSelector.js +15 -9
  52. package/lib/store/RelayModernStore.js +15 -16
  53. package/lib/store/RelayOperationTracker.js +1 -1
  54. package/lib/store/RelayOptimisticRecordSource.js +1 -1
  55. package/lib/store/RelayPublishQueue.js +12 -6
  56. package/lib/store/RelayReader.js +131 -40
  57. package/lib/store/RelayRecordSource.js +1 -1
  58. package/lib/store/RelayRecordState.js +1 -1
  59. package/lib/store/RelayReferenceMarker.js +11 -12
  60. package/lib/store/RelayResponseNormalizer.js +26 -23
  61. package/lib/store/RelayStoreReactFlightUtils.js +4 -4
  62. package/lib/store/RelayStoreSubscriptions.js +7 -5
  63. package/lib/store/RelayStoreTypes.js +1 -1
  64. package/lib/store/RelayStoreUtils.js +6 -6
  65. package/lib/store/ResolverCache.js +7 -7
  66. package/lib/store/ResolverFragments.js +12 -8
  67. package/lib/store/StoreInspector.js +1 -1
  68. package/lib/store/TypeID.js +1 -1
  69. package/lib/store/ViewerPattern.js +1 -1
  70. package/lib/store/cloneRelayHandleSourceField.js +6 -5
  71. package/lib/store/cloneRelayScalarHandleSourceField.js +6 -5
  72. package/lib/store/createFragmentSpecResolver.js +1 -1
  73. package/lib/store/createRelayContext.js +4 -2
  74. package/lib/store/defaultGetDataID.js +1 -1
  75. package/lib/store/defaultRequiredFieldLogger.js +1 -1
  76. package/lib/store/hasOverlappingIDs.js +1 -1
  77. package/lib/store/isRelayModernEnvironment.js +1 -1
  78. package/lib/store/normalizeRelayPayload.js +1 -1
  79. package/lib/store/readInlineData.js +7 -3
  80. package/lib/subscription/requestSubscription.js +4 -6
  81. package/lib/util/JSResourceTypes.flow.js +1 -1
  82. package/lib/util/NormalizationNode.js +1 -1
  83. package/lib/util/ReaderNode.js +1 -1
  84. package/lib/util/RelayConcreteNode.js +2 -1
  85. package/lib/util/RelayDefaultHandleKey.js +1 -1
  86. package/lib/util/RelayError.js +1 -1
  87. package/lib/util/RelayFeatureFlags.js +8 -3
  88. package/lib/util/RelayProfiler.js +1 -1
  89. package/lib/util/RelayReplaySubject.js +1 -1
  90. package/lib/util/RelayRuntimeTypes.js +1 -7
  91. package/lib/util/StringInterner.js +71 -0
  92. package/lib/util/createPayloadFor3DField.js +1 -1
  93. package/lib/util/deepFreeze.js +1 -1
  94. package/lib/util/generateID.js +1 -1
  95. package/lib/util/getAllRootVariables.js +29 -0
  96. package/lib/util/getFragmentIdentifier.js +16 -8
  97. package/lib/util/getOperation.js +3 -2
  98. package/lib/util/getPaginationMetadata.js +1 -1
  99. package/lib/util/getPaginationVariables.js +3 -4
  100. package/lib/util/getPendingOperationsForFragment.js +1 -1
  101. package/lib/util/getRefetchMetadata.js +1 -1
  102. package/lib/util/getRelayHandleKey.js +3 -3
  103. package/lib/util/getRequestIdentifier.js +3 -3
  104. package/lib/util/getValueAtPath.js +1 -1
  105. package/lib/util/isEmptyObject.js +1 -1
  106. package/lib/util/isPromise.js +1 -1
  107. package/lib/util/isScalarAndEqual.js +1 -1
  108. package/lib/util/recycleNodesInto.js +1 -1
  109. package/lib/util/registerEnvironmentWithDevTools.js +1 -1
  110. package/lib/util/reportMissingRequiredFields.js +1 -1
  111. package/lib/util/resolveImmediate.js +1 -1
  112. package/lib/util/stableCopy.js +1 -1
  113. package/lib/util/withDuration.js +1 -1
  114. package/multi-actor-environment/ActorIdentifier.js.flow +1 -1
  115. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +28 -20
  116. package/multi-actor-environment/ActorUtils.js.flow +3 -3
  117. package/multi-actor-environment/MultiActorEnvironment.js.flow +46 -25
  118. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +28 -12
  119. package/multi-actor-environment/index.js.flow +2 -3
  120. package/mutations/RelayDeclarativeMutationConfig.js.flow +33 -28
  121. package/mutations/RelayRecordProxy.js.flow +5 -6
  122. package/mutations/RelayRecordSourceMutator.js.flow +5 -7
  123. package/mutations/RelayRecordSourceProxy.js.flow +20 -11
  124. package/mutations/RelayRecordSourceSelectorProxy.js.flow +16 -6
  125. package/mutations/applyOptimisticMutation.js.flow +14 -15
  126. package/mutations/commitLocalUpdate.js.flow +2 -2
  127. package/mutations/commitMutation.js.flow +36 -49
  128. package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +318 -0
  129. package/mutations/validateMutation.js.flow +20 -18
  130. package/network/ConvertToExecuteFunction.js.flow +3 -3
  131. package/network/RelayNetwork.js.flow +5 -6
  132. package/network/RelayNetworkTypes.js.flow +1 -1
  133. package/network/RelayObservable.js.flow +2 -4
  134. package/network/RelayQueryResponseCache.js.flow +4 -4
  135. package/network/wrapNetworkWithLogObserver.js.flow +9 -9
  136. package/package.json +2 -2
  137. package/query/GraphQLTag.js.flow +11 -11
  138. package/query/PreloadableQueryRegistry.js.flow +5 -3
  139. package/query/fetchQuery.js.flow +19 -19
  140. package/query/fetchQueryInternal.js.flow +7 -10
  141. package/query/fetchQuery_DEPRECATED.js.flow +7 -7
  142. package/relay-runtime.js +2 -2
  143. package/relay-runtime.min.js +3 -3
  144. package/store/ClientID.js.flow +10 -3
  145. package/store/DataChecker.js.flow +21 -30
  146. package/store/OperationExecutor.js.flow +55 -63
  147. package/store/RelayConcreteVariables.js.flow +5 -11
  148. package/store/RelayExperimentalGraphResponseHandler.js.flow +121 -0
  149. package/store/RelayExperimentalGraphResponseTransform.js.flow +470 -0
  150. package/store/RelayModernEnvironment.js.flow +57 -28
  151. package/store/RelayModernFragmentSpecResolver.js.flow +18 -20
  152. package/store/RelayModernOperationDescriptor.js.flow +11 -12
  153. package/store/RelayModernRecord.js.flow +20 -13
  154. package/store/RelayModernSelector.js.flow +25 -15
  155. package/store/RelayModernStore.js.flow +22 -26
  156. package/store/RelayOperationTracker.js.flow +12 -18
  157. package/store/RelayOptimisticRecordSource.js.flow +3 -3
  158. package/store/RelayPublishQueue.js.flow +43 -24
  159. package/store/RelayReader.js.flow +181 -68
  160. package/store/RelayRecordSource.js.flow +3 -3
  161. package/store/RelayRecordState.js.flow +1 -1
  162. package/store/RelayReferenceMarker.js.flow +13 -16
  163. package/store/RelayResponseNormalizer.js.flow +44 -42
  164. package/store/RelayStoreReactFlightUtils.js.flow +4 -5
  165. package/store/RelayStoreSubscriptions.js.flow +10 -9
  166. package/store/RelayStoreTypes.js.flow +73 -30
  167. package/store/RelayStoreUtils.js.flow +9 -10
  168. package/store/ResolverCache.js.flow +17 -15
  169. package/store/ResolverFragments.js.flow +18 -25
  170. package/store/StoreInspector.js.flow +3 -3
  171. package/store/TypeID.js.flow +2 -2
  172. package/store/ViewerPattern.js.flow +3 -3
  173. package/store/cloneRelayHandleSourceField.js.flow +6 -7
  174. package/store/cloneRelayScalarHandleSourceField.js.flow +6 -7
  175. package/store/createFragmentSpecResolver.js.flow +4 -5
  176. package/store/createRelayContext.js.flow +3 -3
  177. package/store/defaultGetDataID.js.flow +1 -1
  178. package/store/defaultRequiredFieldLogger.js.flow +1 -1
  179. package/store/hasOverlappingIDs.js.flow +1 -1
  180. package/store/isRelayModernEnvironment.js.flow +1 -1
  181. package/store/normalizeRelayPayload.js.flow +7 -8
  182. package/store/readInlineData.js.flow +8 -9
  183. package/subscription/requestSubscription.js.flow +16 -25
  184. package/util/JSResourceTypes.flow.js.flow +1 -1
  185. package/util/NormalizationNode.js.flow +1 -1
  186. package/util/ReaderNode.js.flow +10 -1
  187. package/util/RelayConcreteNode.js.flow +4 -1
  188. package/util/RelayDefaultHandleKey.js.flow +1 -1
  189. package/util/RelayError.js.flow +1 -1
  190. package/util/RelayFeatureFlags.js.flow +15 -5
  191. package/util/RelayProfiler.js.flow +1 -1
  192. package/util/RelayReplaySubject.js.flow +3 -4
  193. package/util/RelayRuntimeTypes.js.flow +70 -3
  194. package/util/StringInterner.js.flow +69 -0
  195. package/util/createPayloadFor3DField.js.flow +4 -4
  196. package/util/deepFreeze.js.flow +1 -1
  197. package/util/generateID.js.flow +1 -1
  198. package/util/getAllRootVariables.js.flow +36 -0
  199. package/util/getFragmentIdentifier.js.flow +28 -16
  200. package/util/getOperation.js.flow +3 -3
  201. package/util/getPaginationMetadata.js.flow +6 -11
  202. package/util/getPaginationVariables.js.flow +6 -10
  203. package/util/getPendingOperationsForFragment.js.flow +3 -3
  204. package/util/getRefetchMetadata.js.flow +8 -12
  205. package/util/getRelayHandleKey.js.flow +2 -3
  206. package/util/getRequestIdentifier.js.flow +4 -4
  207. package/util/getValueAtPath.js.flow +1 -1
  208. package/util/isEmptyObject.js.flow +1 -1
  209. package/util/isPromise.js.flow +1 -1
  210. package/util/isScalarAndEqual.js.flow +1 -1
  211. package/util/recycleNodesInto.js.flow +1 -1
  212. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  213. package/util/reportMissingRequiredFields.js.flow +1 -1
  214. package/util/resolveImmediate.js.flow +2 -2
  215. package/util/stableCopy.js.flow +1 -1
  216. package/util/withDuration.js.flow +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -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
 
@@ -259,13 +293,6 @@ class RelayReader {
259
293
  const selection = selections[i];
260
294
  switch (selection.kind) {
261
295
  case REQUIRED_FIELD:
262
- invariant(
263
- RelayFeatureFlags.ENABLE_REQUIRED_DIRECTIVES,
264
- 'RelayReader(): Encountered a `@required` directive at path "%s" in `%s` without the `ENABLE_REQUIRED_DIRECTIVES` feature flag enabled.',
265
- selection.path,
266
- this._selector.node.name,
267
- );
268
-
269
296
  const fieldValue = this._readRequiredField(selection, record, data);
270
297
  if (fieldValue == null) {
271
298
  const {action} = selection;
@@ -325,8 +352,8 @@ class RelayReader {
325
352
 
326
353
  // store flags to reset after reading
327
354
  const parentIsMissingData = this._isMissingData;
328
- const parentIsWithinUnmatchedTypeRefinement = this
329
- ._isWithinUnmatchedTypeRefinement;
355
+ const parentIsWithinUnmatchedTypeRefinement =
356
+ this._isWithinUnmatchedTypeRefinement;
330
357
 
331
358
  const typeName = RelayModernRecord.getType(record);
332
359
  const typeID = generateTypeID(typeName);
@@ -339,14 +366,15 @@ class RelayReader {
339
366
  parentIsWithinUnmatchedTypeRefinement ||
340
367
  implementsInterface === false;
341
368
  this._traverseSelections(selection.selections, record, data);
342
- this._isWithinUnmatchedTypeRefinement = parentIsWithinUnmatchedTypeRefinement;
369
+ this._isWithinUnmatchedTypeRefinement =
370
+ parentIsWithinUnmatchedTypeRefinement;
343
371
 
344
372
  if (implementsInterface === false) {
345
373
  // Type known to not implement the interface, no data expected
346
374
  this._isMissingData = parentIsMissingData;
347
375
  } else if (implementsInterface == null) {
348
376
  // Don't know if the type implements the interface or not
349
- this._isMissingData = true;
377
+ this._markDataAsMissing();
350
378
  }
351
379
  }
352
380
  break;
@@ -374,12 +402,24 @@ class RelayReader {
374
402
  case DEFER:
375
403
  case CLIENT_EXTENSION: {
376
404
  const isMissingData = this._isMissingData;
405
+ const alreadyMissingClientEdges = this._missingClientEdges.length;
406
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
407
+ this._clientEdgeTraversalPath.push(null);
408
+ }
377
409
  const hasExpectedData = this._traverseSelections(
378
410
  selection.selections,
379
411
  record,
380
412
  data,
381
413
  );
382
- this._isMissingData = isMissingData;
414
+ // The only case where we want to suspend due to missing data off of
415
+ // a client extension is if we reached a client edge that we might be
416
+ // able to fetch:
417
+ this._isMissingData =
418
+ isMissingData ||
419
+ this._missingClientEdges.length > alreadyMissingClientEdges;
420
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
421
+ this._clientEdgeTraversalPath.pop();
422
+ }
383
423
  if (!hasExpectedData) {
384
424
  return false;
385
425
  }
@@ -406,6 +446,13 @@ class RelayReader {
406
446
  case ACTOR_CHANGE:
407
447
  this._readActorChange(selection, record, data);
408
448
  break;
449
+ case CLIENT_EDGE:
450
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
451
+ this._readClientEdge(selection, record, data);
452
+ } else {
453
+ throw new Error('Client edges are not yet supported.');
454
+ }
455
+ break;
409
456
  default:
410
457
  (selection: empty);
411
458
  invariant(
@@ -452,7 +499,7 @@ class RelayReader {
452
499
  field: ReaderRelayResolver,
453
500
  record: Record,
454
501
  data: SelectorData,
455
- ): ?mixed {
502
+ ): void {
456
503
  const {resolverModule, fragment} = field;
457
504
  const storageKey = getStorageKey(field, this._variables);
458
505
  const resolverID = ClientID.generateClientID(
@@ -525,8 +572,66 @@ class RelayReader {
525
572
  if (seenRecord != null) {
526
573
  this._seenRecords.add(seenRecord);
527
574
  }
528
- data[storageKey] = result;
529
- return result;
575
+
576
+ const applicationName = field.alias ?? field.name;
577
+ data[applicationName] = result;
578
+ }
579
+
580
+ _readClientEdge(
581
+ field: ReaderClientEdge,
582
+ record: Record,
583
+ data: SelectorData,
584
+ ): void {
585
+ const backingField = field.backingField;
586
+
587
+ // Because ReaderClientExtension doesn't have `alias` or `name` and so I don't know
588
+ // how to get its applicationName or storageKey yet:
589
+ invariant(
590
+ backingField.kind !== 'ClientExtension',
591
+ 'Client extension client edges are not yet implemented.',
592
+ );
593
+
594
+ const applicationName = backingField.alias ?? backingField.name;
595
+
596
+ const backingFieldData = {};
597
+ this._traverseSelections([backingField], record, backingFieldData);
598
+ const destinationDataID = backingFieldData[applicationName];
599
+
600
+ if (destinationDataID == null) {
601
+ data[applicationName] = destinationDataID;
602
+ return;
603
+ }
604
+
605
+ invariant(
606
+ typeof destinationDataID === 'string',
607
+ 'Plural client edges not are yet implemented',
608
+ ); // FIXME support plural
609
+
610
+ // Not wrapping the push/pop in a try/finally because if we throw, the
611
+ // Reader object is not usable after that anyway.
612
+ this._clientEdgeTraversalPath.push({
613
+ readerClientEdge: field,
614
+ clientEdgeDestinationID: destinationDataID,
615
+ });
616
+
617
+ const prevData = data[applicationName];
618
+ invariant(
619
+ prevData == null || typeof prevData === 'object',
620
+ 'RelayReader(): Expected data for field `%s` on record `%s` ' +
621
+ 'to be an object, got `%s`.',
622
+ applicationName,
623
+ RelayModernRecord.getDataID(record),
624
+ prevData,
625
+ );
626
+ const value = this._traverse(
627
+ field.linkedField,
628
+ destinationDataID,
629
+ // $FlowFixMe[incompatible-variance]
630
+ prevData,
631
+ );
632
+ data[applicationName] = value;
633
+
634
+ this._clientEdgeTraversalPath.pop();
530
635
  }
531
636
 
532
637
  _readFlightField(
@@ -536,14 +641,12 @@ class RelayReader {
536
641
  ): ?mixed {
537
642
  const applicationName = field.alias ?? field.name;
538
643
  const storageKey = getStorageKey(field, this._variables);
539
- const reactFlightClientResponseRecordID = RelayModernRecord.getLinkedRecordID(
540
- record,
541
- storageKey,
542
- );
644
+ const reactFlightClientResponseRecordID =
645
+ RelayModernRecord.getLinkedRecordID(record, storageKey);
543
646
  if (reactFlightClientResponseRecordID == null) {
544
647
  data[applicationName] = reactFlightClientResponseRecordID;
545
648
  if (reactFlightClientResponseRecordID === undefined) {
546
- this._isMissingData = true;
649
+ this._markDataAsMissing();
547
650
  }
548
651
  return reactFlightClientResponseRecordID;
549
652
  }
@@ -554,7 +657,7 @@ class RelayReader {
554
657
  if (reactFlightClientResponseRecord == null) {
555
658
  data[applicationName] = reactFlightClientResponseRecord;
556
659
  if (reactFlightClientResponseRecord === undefined) {
557
- this._isMissingData = true;
660
+ this._markDataAsMissing();
558
661
  }
559
662
  return reactFlightClientResponseRecord;
560
663
  }
@@ -574,7 +677,7 @@ class RelayReader {
574
677
  const storageKey = getStorageKey(field, this._variables);
575
678
  const value = RelayModernRecord.getValue(record, storageKey);
576
679
  if (value === undefined) {
577
- this._isMissingData = true;
680
+ this._markDataAsMissing();
578
681
  }
579
682
  data[applicationName] = value;
580
683
  return value;
@@ -591,7 +694,7 @@ class RelayReader {
591
694
  if (linkedID == null) {
592
695
  data[applicationName] = linkedID;
593
696
  if (linkedID === undefined) {
594
- this._isMissingData = true;
697
+ this._markDataAsMissing();
595
698
  }
596
699
  return linkedID;
597
700
  }
@@ -626,7 +729,7 @@ class RelayReader {
626
729
  if (externalRef == null) {
627
730
  data[applicationName] = externalRef;
628
731
  if (externalRef === undefined) {
629
- this._isMissingData = true;
732
+ this._markDataAsMissing();
630
733
  }
631
734
  return data[applicationName];
632
735
  }
@@ -659,7 +762,7 @@ class RelayReader {
659
762
  if (linkedIDs == null) {
660
763
  data[applicationName] = linkedIDs;
661
764
  if (linkedIDs === undefined) {
662
- this._isMissingData = true;
765
+ this._markDataAsMissing();
663
766
  }
664
767
  return linkedIDs;
665
768
  }
@@ -677,7 +780,7 @@ class RelayReader {
677
780
  linkedIDs.forEach((linkedID, nextIndex) => {
678
781
  if (linkedID == null) {
679
782
  if (linkedID === undefined) {
680
- this._isMissingData = true;
783
+ this._markDataAsMissing();
681
784
  }
682
785
  // $FlowFixMe[cannot-write]
683
786
  linkedArray[nextIndex] = linkedID;
@@ -715,7 +818,7 @@ class RelayReader {
715
818
  const component = RelayModernRecord.getValue(record, componentKey);
716
819
  if (component == null) {
717
820
  if (component === undefined) {
718
- this._isMissingData = true;
821
+ this._markDataAsMissing();
719
822
  }
720
823
  return;
721
824
  }
@@ -760,9 +863,19 @@ class RelayReader {
760
863
  ? getArgumentValues(fragmentSpread.args, this._variables)
761
864
  : {};
762
865
  data[FRAGMENT_OWNER_KEY] = this._owner;
763
- data[
764
- IS_WITHIN_UNMATCHED_TYPE_REFINEMENT
765
- ] = this._isWithinUnmatchedTypeRefinement;
866
+ data[IS_WITHIN_UNMATCHED_TYPE_REFINEMENT] =
867
+ this._isWithinUnmatchedTypeRefinement;
868
+
869
+ if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
870
+ if (
871
+ this._clientEdgeTraversalPath.length > 0 &&
872
+ this._clientEdgeTraversalPath[
873
+ this._clientEdgeTraversalPath.length - 1
874
+ ] !== null
875
+ ) {
876
+ data[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath];
877
+ }
878
+ }
766
879
  }
767
880
 
768
881
  _createInlineDataOrResolverFragmentPointer(
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -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
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -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({