relay-runtime 11.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 (169) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +2 -2
  2. package/handlers/connection/ConnectionHandler.js.flow +8 -10
  3. package/handlers/connection/MutationHandlers.js.flow +31 -7
  4. package/index.js +1 -1
  5. package/index.js.flow +60 -36
  6. package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
  7. package/lib/handlers/connection/ConnectionHandler.js +8 -8
  8. package/lib/handlers/connection/MutationHandlers.js +61 -5
  9. package/lib/index.js +58 -43
  10. package/lib/multi-actor-environment/ActorIdentifier.js +33 -0
  11. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +152 -0
  12. package/lib/multi-actor-environment/ActorUtils.js +27 -0
  13. package/lib/multi-actor-environment/MultiActorEnvironment.js +419 -0
  14. package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
  15. package/lib/multi-actor-environment/index.js +21 -0
  16. package/lib/mutations/RelayDeclarativeMutationConfig.js +4 -1
  17. package/lib/mutations/RelayRecordProxy.js +3 -2
  18. package/lib/mutations/RelayRecordSourceMutator.js +3 -2
  19. package/lib/mutations/RelayRecordSourceProxy.js +12 -4
  20. package/lib/mutations/RelayRecordSourceSelectorProxy.js +18 -5
  21. package/lib/mutations/applyOptimisticMutation.js +6 -6
  22. package/lib/mutations/commitMutation.js +14 -10
  23. package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +238 -0
  24. package/lib/mutations/validateMutation.js +12 -5
  25. package/lib/network/ConvertToExecuteFunction.js +2 -1
  26. package/lib/network/RelayNetwork.js +3 -2
  27. package/lib/network/RelayQueryResponseCache.js +21 -4
  28. package/lib/network/wrapNetworkWithLogObserver.js +79 -0
  29. package/lib/query/GraphQLTag.js +3 -2
  30. package/lib/query/fetchQuery.js +6 -5
  31. package/lib/query/fetchQueryInternal.js +1 -1
  32. package/lib/query/fetchQuery_DEPRECATED.js +2 -1
  33. package/lib/store/ClientID.js +7 -1
  34. package/lib/store/DataChecker.js +141 -60
  35. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +532 -195
  36. package/lib/store/RelayConcreteVariables.js +24 -4
  37. package/lib/store/RelayModernEnvironment.js +175 -234
  38. package/lib/store/RelayModernFragmentSpecResolver.js +52 -26
  39. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  40. package/lib/store/RelayModernRecord.js +47 -12
  41. package/lib/store/RelayModernSelector.js +14 -8
  42. package/lib/store/RelayModernStore.js +58 -29
  43. package/lib/store/RelayOperationTracker.js +34 -24
  44. package/lib/store/RelayPublishQueue.js +41 -13
  45. package/lib/store/RelayReader.js +287 -46
  46. package/lib/store/RelayRecordSource.js +87 -3
  47. package/lib/store/RelayReferenceMarker.js +55 -31
  48. package/lib/store/RelayResponseNormalizer.js +250 -108
  49. package/lib/store/RelayStoreReactFlightUtils.js +8 -12
  50. package/lib/store/RelayStoreSubscriptions.js +14 -9
  51. package/lib/store/RelayStoreUtils.js +11 -5
  52. package/lib/store/ResolverCache.js +213 -0
  53. package/lib/store/ResolverFragments.js +61 -0
  54. package/lib/store/cloneRelayHandleSourceField.js +5 -4
  55. package/lib/store/cloneRelayScalarHandleSourceField.js +5 -4
  56. package/lib/store/createRelayContext.js +4 -2
  57. package/lib/store/defaultGetDataID.js +3 -1
  58. package/lib/store/readInlineData.js +6 -2
  59. package/lib/subscription/requestSubscription.js +35 -9
  60. package/lib/util/RelayConcreteNode.js +4 -0
  61. package/lib/util/RelayFeatureFlags.js +11 -4
  62. package/lib/util/RelayProfiler.js +17 -187
  63. package/lib/util/RelayReplaySubject.js +22 -7
  64. package/lib/util/RelayRuntimeTypes.js +0 -6
  65. package/lib/util/StringInterner.js +71 -0
  66. package/lib/util/deepFreeze.js +1 -0
  67. package/lib/util/getFragmentIdentifier.js +15 -7
  68. package/lib/util/getOperation.js +2 -1
  69. package/lib/util/getPaginationMetadata.js +41 -0
  70. package/lib/util/getPaginationVariables.js +66 -0
  71. package/lib/util/getPendingOperationsForFragment.js +55 -0
  72. package/lib/util/getRefetchMetadata.js +36 -0
  73. package/lib/util/getRelayHandleKey.js +2 -2
  74. package/lib/util/getRequestIdentifier.js +2 -2
  75. package/lib/util/getValueAtPath.js +51 -0
  76. package/lib/util/isEmptyObject.js +1 -1
  77. package/lib/util/registerEnvironmentWithDevTools.js +26 -0
  78. package/lib/util/withDuration.js +31 -0
  79. package/multi-actor-environment/ActorIdentifier.js.flow +43 -0
  80. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +225 -0
  81. package/multi-actor-environment/ActorUtils.js.flow +33 -0
  82. package/multi-actor-environment/MultiActorEnvironment.js.flow +506 -0
  83. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +261 -0
  84. package/multi-actor-environment/index.js.flow +26 -0
  85. package/mutations/RelayDeclarativeMutationConfig.js.flow +32 -26
  86. package/mutations/RelayRecordProxy.js.flow +4 -5
  87. package/mutations/RelayRecordSourceMutator.js.flow +4 -6
  88. package/mutations/RelayRecordSourceProxy.js.flow +19 -10
  89. package/mutations/RelayRecordSourceSelectorProxy.js.flow +22 -7
  90. package/mutations/applyOptimisticMutation.js.flow +13 -14
  91. package/mutations/commitLocalUpdate.js.flow +1 -1
  92. package/mutations/commitMutation.js.flow +35 -46
  93. package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +309 -0
  94. package/mutations/validateMutation.js.flow +28 -16
  95. package/network/ConvertToExecuteFunction.js.flow +2 -2
  96. package/network/RelayNetwork.js.flow +4 -5
  97. package/network/RelayNetworkTypes.js.flow +17 -8
  98. package/network/RelayObservable.js.flow +1 -1
  99. package/network/RelayQueryResponseCache.js.flow +34 -20
  100. package/network/wrapNetworkWithLogObserver.js.flow +100 -0
  101. package/package.json +3 -2
  102. package/query/GraphQLTag.js.flow +9 -9
  103. package/query/PreloadableQueryRegistry.js.flow +2 -1
  104. package/query/fetchQuery.js.flow +11 -13
  105. package/query/fetchQueryInternal.js.flow +6 -9
  106. package/query/fetchQuery_DEPRECATED.js.flow +6 -6
  107. package/relay-runtime.js +2 -2
  108. package/relay-runtime.min.js +2 -2
  109. package/store/ClientID.js.flow +14 -3
  110. package/store/DataChecker.js.flow +162 -67
  111. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +616 -283
  112. package/store/RelayConcreteVariables.js.flow +27 -5
  113. package/store/RelayModernEnvironment.js.flow +176 -235
  114. package/store/RelayModernFragmentSpecResolver.js.flow +55 -31
  115. package/store/RelayModernOperationDescriptor.js.flow +12 -7
  116. package/store/RelayModernRecord.js.flow +67 -11
  117. package/store/RelayModernSelector.js.flow +24 -14
  118. package/store/RelayModernStore.js.flow +72 -36
  119. package/store/RelayOperationTracker.js.flow +59 -43
  120. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  121. package/store/RelayPublishQueue.js.flow +79 -34
  122. package/store/RelayReader.js.flow +351 -72
  123. package/store/RelayRecordSource.js.flow +72 -6
  124. package/store/RelayReferenceMarker.js.flow +60 -33
  125. package/store/RelayResponseNormalizer.js.flow +288 -102
  126. package/store/RelayStoreReactFlightUtils.js.flow +9 -13
  127. package/store/RelayStoreSubscriptions.js.flow +19 -11
  128. package/store/RelayStoreTypes.js.flow +210 -44
  129. package/store/RelayStoreUtils.js.flow +25 -11
  130. package/store/ResolverCache.js.flow +249 -0
  131. package/store/ResolverFragments.js.flow +121 -0
  132. package/store/StoreInspector.js.flow +2 -2
  133. package/store/TypeID.js.flow +1 -1
  134. package/store/ViewerPattern.js.flow +2 -2
  135. package/store/cloneRelayHandleSourceField.js.flow +5 -6
  136. package/store/cloneRelayScalarHandleSourceField.js.flow +5 -6
  137. package/store/createFragmentSpecResolver.js.flow +3 -4
  138. package/store/createRelayContext.js.flow +3 -3
  139. package/store/defaultGetDataID.js.flow +3 -1
  140. package/store/normalizeRelayPayload.js.flow +6 -7
  141. package/store/readInlineData.js.flow +7 -8
  142. package/subscription/requestSubscription.js.flow +54 -27
  143. package/util/NormalizationNode.js.flow +16 -3
  144. package/util/ReaderNode.js.flow +38 -2
  145. package/util/RelayConcreteNode.js.flow +4 -0
  146. package/util/RelayFeatureFlags.js.flow +24 -8
  147. package/util/RelayProfiler.js.flow +22 -194
  148. package/util/RelayReplaySubject.js.flow +9 -9
  149. package/util/RelayRuntimeTypes.js.flow +73 -4
  150. package/util/StringInterner.js.flow +69 -0
  151. package/util/createPayloadFor3DField.js.flow +3 -3
  152. package/util/deepFreeze.js.flow +2 -1
  153. package/util/getFragmentIdentifier.js.flow +27 -15
  154. package/util/getOperation.js.flow +2 -2
  155. package/util/getPaginationMetadata.js.flow +72 -0
  156. package/util/getPaginationVariables.js.flow +108 -0
  157. package/util/getPendingOperationsForFragment.js.flow +62 -0
  158. package/util/getRefetchMetadata.js.flow +79 -0
  159. package/util/getRelayHandleKey.js.flow +1 -2
  160. package/util/getRequestIdentifier.js.flow +3 -3
  161. package/util/getValueAtPath.js.flow +46 -0
  162. package/util/isEmptyObject.js.flow +2 -1
  163. package/util/registerEnvironmentWithDevTools.js.flow +33 -0
  164. package/util/resolveImmediate.js.flow +1 -1
  165. package/util/withDuration.js.flow +32 -0
  166. package/lib/store/RelayRecordSourceMapImpl.js +0 -107
  167. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
  168. package/store/RelayRecordSourceMapImpl.js.flow +0 -91
  169. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
@@ -12,17 +12,41 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
- const RelayModernRecord = require('./RelayModernRecord');
17
- const RelayProfiler = require('../util/RelayProfiler');
18
-
19
- const areEqual = require('areEqual');
20
- const invariant = require('invariant');
21
- 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';
22
40
 
23
41
  const {
24
- CONDITION,
42
+ ACTOR_IDENTIFIER_FIELD_NAME,
43
+ getActorIdentifierFromPayload,
44
+ } = require('../multi-actor-environment/ActorUtils');
45
+ const {
46
+ ACTOR_CHANGE,
47
+ CLIENT_COMPONENT,
25
48
  CLIENT_EXTENSION,
49
+ CONDITION,
26
50
  DEFER,
27
51
  FLIGHT_FIELD,
28
52
  FRAGMENT_SPREAD,
@@ -35,52 +59,34 @@ const {
35
59
  STREAM,
36
60
  TYPE_DISCRIMINATOR,
37
61
  } = require('../util/RelayConcreteNode');
62
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
38
63
  const {generateClientID, isClientID} = require('./ClientID');
64
+ const {getLocalVariables} = require('./RelayConcreteVariables');
65
+ const RelayModernRecord = require('./RelayModernRecord');
39
66
  const {createNormalizationSelector} = require('./RelayModernSelector');
40
67
  const {
41
- refineToReactFlightPayloadData,
42
- REACT_FLIGHT_QUERIES_STORAGE_KEY,
68
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
43
69
  REACT_FLIGHT_TREE_STORAGE_KEY,
44
70
  REACT_FLIGHT_TYPE_NAME,
71
+ refineToReactFlightPayloadData,
45
72
  } = require('./RelayStoreReactFlightUtils');
46
73
  const {
74
+ ROOT_ID,
75
+ ROOT_TYPE,
76
+ TYPENAME_KEY,
47
77
  getArgumentValues,
48
78
  getHandleStorageKey,
49
79
  getModuleComponentKey,
50
80
  getModuleOperationKey,
51
81
  getStorageKey,
52
- TYPENAME_KEY,
53
- ROOT_ID,
54
- ROOT_TYPE,
55
82
  } = require('./RelayStoreUtils');
56
- const {generateTypeID, TYPE_SCHEMA_TYPE} = require('./TypeID');
57
-
58
- import type {PayloadData} from '../network/RelayNetworkTypes';
59
- import type {
60
- NormalizationDefer,
61
- NormalizationFlightField,
62
- NormalizationLinkedField,
63
- NormalizationModuleImport,
64
- NormalizationNode,
65
- NormalizationScalarField,
66
- NormalizationStream,
67
- } from '../util/NormalizationNode';
68
- import type {DataID, Variables} from '../util/RelayRuntimeTypes';
69
- import type {
70
- HandleFieldPayload,
71
- IncrementalDataPlaceholder,
72
- ModuleImportPayload,
73
- MutableRecordSource,
74
- NormalizationSelector,
75
- ReactFlightReachableQuery,
76
- ReactFlightPayloadDeserializer,
77
- ReactFlightServerErrorHandler,
78
- Record,
79
- RelayResponsePayload,
80
- } 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');
81
87
 
82
88
  export type GetDataID = (
83
- fieldValue: {[string]: mixed, ...},
89
+ fieldValue: interface {[string]: mixed},
84
90
  typeName: string,
85
91
  ) => mixed;
86
92
 
@@ -90,6 +96,8 @@ export type NormalizationOptions = {|
90
96
  +path?: $ReadOnlyArray<string>,
91
97
  +reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
92
98
  +reactFlightServerErrorHandler?: ?ReactFlightServerErrorHandler,
99
+ +shouldProcessClientComponents?: ?boolean,
100
+ +actorIdentifier?: ?ActorIdentifier,
93
101
  |};
94
102
 
95
103
  /**
@@ -117,37 +125,41 @@ function normalize(
117
125
  * Helper for handling payloads.
118
126
  */
119
127
  class RelayResponseNormalizer {
128
+ _actorIdentifier: ?ActorIdentifier;
120
129
  _getDataId: GetDataID;
121
130
  _handleFieldPayloads: Array<HandleFieldPayload>;
122
131
  _treatMissingFieldsAsNull: boolean;
123
132
  _incrementalPlaceholders: Array<IncrementalDataPlaceholder>;
124
133
  _isClientExtension: boolean;
125
134
  _isUnmatchedAbstractType: boolean;
126
- _moduleImportPayloads: Array<ModuleImportPayload>;
135
+ _followupPayloads: Array<FollowupPayload>;
127
136
  _path: Array<string>;
128
137
  _recordSource: MutableRecordSource;
129
138
  _variables: Variables;
130
139
  _reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
131
140
  _reactFlightServerErrorHandler: ?ReactFlightServerErrorHandler;
141
+ _shouldProcessClientComponents: ?boolean;
132
142
 
133
143
  constructor(
134
144
  recordSource: MutableRecordSource,
135
145
  variables: Variables,
136
146
  options: NormalizationOptions,
137
147
  ) {
148
+ this._actorIdentifier = options.actorIdentifier;
138
149
  this._getDataId = options.getDataID;
139
150
  this._handleFieldPayloads = [];
140
151
  this._treatMissingFieldsAsNull = options.treatMissingFieldsAsNull;
141
152
  this._incrementalPlaceholders = [];
142
153
  this._isClientExtension = false;
143
154
  this._isUnmatchedAbstractType = false;
144
- this._moduleImportPayloads = [];
155
+ this._followupPayloads = [];
145
156
  this._path = options.path ? [...options.path] : [];
146
157
  this._recordSource = recordSource;
147
158
  this._variables = variables;
148
159
  this._reactFlightPayloadDeserializer =
149
160
  options.reactFlightPayloadDeserializer;
150
161
  this._reactFlightServerErrorHandler = options.reactFlightServerErrorHandler;
162
+ this._shouldProcessClientComponents = options.shouldProcessClientComponents;
151
163
  }
152
164
 
153
165
  normalizeResponse(
@@ -166,7 +178,7 @@ class RelayResponseNormalizer {
166
178
  errors: null,
167
179
  fieldPayloads: this._handleFieldPayloads,
168
180
  incrementalPlaceholders: this._incrementalPlaceholders,
169
- moduleImportPayloads: this._moduleImportPayloads,
181
+ followupPayloads: this._followupPayloads,
170
182
  source: this._recordSource,
171
183
  isFinal: false,
172
184
  };
@@ -204,13 +216,22 @@ class RelayResponseNormalizer {
204
216
  this._normalizeField(node, selection, record, data);
205
217
  break;
206
218
  case CONDITION:
207
- const conditionValue = this._getVariableValue(selection.condition);
219
+ const conditionValue = Boolean(
220
+ this._getVariableValue(selection.condition),
221
+ );
208
222
  if (conditionValue === selection.passingValue) {
209
223
  this._traverseSelections(selection, record, data);
210
224
  }
211
225
  break;
212
226
  case FRAGMENT_SPREAD: {
227
+ const prevVariables = this._variables;
228
+ this._variables = getLocalVariables(
229
+ this._variables,
230
+ selection.fragment.argumentDefinitions,
231
+ selection.args,
232
+ );
213
233
  this._traverseSelections(selection.fragment, record, data);
234
+ this._variables = prevVariables;
214
235
  break;
215
236
  }
216
237
  case INLINE_FRAGMENT: {
@@ -220,7 +241,7 @@ class RelayResponseNormalizer {
220
241
  if (typeName === selection.type) {
221
242
  this._traverseSelections(selection, record, data);
222
243
  }
223
- } else if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
244
+ } else {
224
245
  const implementsInterface = data.hasOwnProperty(abstractKey);
225
246
  const typeName = RelayModernRecord.getType(record);
226
247
  const typeID = generateTypeID(typeName);
@@ -237,36 +258,24 @@ class RelayResponseNormalizer {
237
258
  if (implementsInterface) {
238
259
  this._traverseSelections(selection, record, data);
239
260
  }
240
- } else {
241
- // legacy behavior for abstract refinements: always normalize even
242
- // if the type doesn't conform, but track if the type matches or not
243
- // for determining whether response fields are expected to be present
244
- const implementsInterface = data.hasOwnProperty(abstractKey);
245
- const parentIsUnmatchedAbstractType = this._isUnmatchedAbstractType;
246
- this._isUnmatchedAbstractType =
247
- this._isUnmatchedAbstractType || !implementsInterface;
248
- this._traverseSelections(selection, record, data);
249
- this._isUnmatchedAbstractType = parentIsUnmatchedAbstractType;
250
261
  }
251
262
  break;
252
263
  }
253
264
  case TYPE_DISCRIMINATOR: {
254
- if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
255
- const {abstractKey} = selection;
256
- const implementsInterface = data.hasOwnProperty(abstractKey);
257
- const typeName = RelayModernRecord.getType(record);
258
- const typeID = generateTypeID(typeName);
259
- let typeRecord = this._recordSource.get(typeID);
260
- if (typeRecord == null) {
261
- typeRecord = RelayModernRecord.create(typeID, TYPE_SCHEMA_TYPE);
262
- this._recordSource.set(typeID, typeRecord);
263
- }
264
- RelayModernRecord.setValue(
265
- typeRecord,
266
- abstractKey,
267
- implementsInterface,
268
- );
265
+ const {abstractKey} = selection;
266
+ const implementsInterface = data.hasOwnProperty(abstractKey);
267
+ const typeName = RelayModernRecord.getType(record);
268
+ const typeID = generateTypeID(typeName);
269
+ let typeRecord = this._recordSource.get(typeID);
270
+ if (typeRecord == null) {
271
+ typeRecord = RelayModernRecord.create(typeID, TYPE_SCHEMA_TYPE);
272
+ this._recordSource.set(typeID, typeRecord);
269
273
  }
274
+ RelayModernRecord.setValue(
275
+ typeRecord,
276
+ abstractKey,
277
+ implementsInterface,
278
+ );
270
279
  break;
271
280
  }
272
281
  case LINKED_HANDLE:
@@ -277,13 +286,17 @@ class RelayResponseNormalizer {
277
286
  const fieldKey = getStorageKey(selection, this._variables);
278
287
  const handleKey = getHandleStorageKey(selection, this._variables);
279
288
  this._handleFieldPayloads.push({
289
+ /* $FlowFixMe[class-object-subtyping] added when improving typing
290
+ * for this parameters */
280
291
  args,
281
292
  dataID: RelayModernRecord.getDataID(record),
282
293
  fieldKey,
283
294
  handle: selection.handle,
284
295
  handleKey,
285
296
  handleArgs: selection.handleArgs
286
- ? getArgumentValues(selection.handleArgs, this._variables)
297
+ ? /* $FlowFixMe[class-object-subtyping] added when improving typing
298
+ * for this parameters */
299
+ getArgumentValues(selection.handleArgs, this._variables)
287
300
  : {},
288
301
  });
289
302
  break;
@@ -302,6 +315,12 @@ class RelayResponseNormalizer {
302
315
  this._traverseSelections(selection, record, data);
303
316
  this._isClientExtension = isClientExtension;
304
317
  break;
318
+ case CLIENT_COMPONENT:
319
+ if (this._shouldProcessClientComponents === false) {
320
+ break;
321
+ }
322
+ this._traverseSelections(selection.fragment, record, data);
323
+ break;
305
324
  case FLIGHT_FIELD:
306
325
  if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
307
326
  this._normalizeFlightField(node, selection, record, data);
@@ -309,6 +328,9 @@ class RelayResponseNormalizer {
309
328
  throw new Error('Flight fields are not yet supported.');
310
329
  }
311
330
  break;
331
+ case ACTOR_CHANGE:
332
+ this._normalizeActorChange(node, selection, record, data);
333
+ break;
312
334
  default:
313
335
  (selection: empty);
314
336
  invariant(
@@ -352,6 +374,7 @@ class RelayResponseNormalizer {
352
374
  this._variables,
353
375
  ),
354
376
  typeName: RelayModernRecord.getType(record),
377
+ actorIdentifier: this._actorIdentifier,
355
378
  });
356
379
  }
357
380
  }
@@ -384,6 +407,7 @@ class RelayResponseNormalizer {
384
407
  parentID: RelayModernRecord.getDataID(record),
385
408
  node: stream,
386
409
  variables: this._variables,
410
+ actorIdentifier: this._actorIdentifier,
387
411
  });
388
412
  }
389
413
  }
@@ -414,13 +438,16 @@ class RelayResponseNormalizer {
414
438
  operationReference ?? null,
415
439
  );
416
440
  if (operationReference != null) {
417
- this._moduleImportPayloads.push({
441
+ this._followupPayloads.push({
442
+ kind: 'ModuleImportPayload',
443
+ args: moduleImport.args,
418
444
  data,
419
445
  dataID: RelayModernRecord.getDataID(record),
420
446
  operationReference,
421
447
  path: [...this._path],
422
448
  typeName,
423
449
  variables: this._variables,
450
+ actorIdentifier: this._actorIdentifier,
424
451
  });
425
452
  }
426
453
  }
@@ -478,7 +505,11 @@ class RelayResponseNormalizer {
478
505
  this._validateConflictingFieldsWithIdenticalId(
479
506
  record,
480
507
  storageKey,
481
- 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,
482
513
  );
483
514
  }
484
515
  }
@@ -513,6 +544,99 @@ class RelayResponseNormalizer {
513
544
  }
514
545
  }
515
546
 
547
+ _normalizeActorChange(
548
+ parent: NormalizationNode,
549
+ selection: NormalizationActorChange,
550
+ record: Record,
551
+ data: PayloadData,
552
+ ) {
553
+ const field = selection.linkedField;
554
+ invariant(
555
+ typeof data === 'object' && data,
556
+ '_normalizeActorChange(): Expected data for field `%s` to be an object.',
557
+ field.name,
558
+ );
559
+ const responseKey = field.alias || field.name;
560
+ const storageKey = getStorageKey(field, this._variables);
561
+ const fieldValue = data[responseKey];
562
+
563
+ if (fieldValue == null) {
564
+ if (fieldValue === undefined) {
565
+ const isOptionalField =
566
+ this._isClientExtension || this._isUnmatchedAbstractType;
567
+
568
+ if (isOptionalField) {
569
+ return;
570
+ } else if (!this._treatMissingFieldsAsNull) {
571
+ if (__DEV__) {
572
+ warning(
573
+ false,
574
+ 'RelayResponseNormalizer: Payload did not contain a value ' +
575
+ 'for field `%s: %s`. Check that you are parsing with the same ' +
576
+ 'query that was used to fetch the payload.',
577
+ responseKey,
578
+ storageKey,
579
+ );
580
+ }
581
+ return;
582
+ }
583
+ }
584
+ RelayModernRecord.setValue(record, storageKey, null);
585
+ return;
586
+ }
587
+
588
+ const actorIdentifier = getActorIdentifierFromPayload(fieldValue);
589
+ if (actorIdentifier == null) {
590
+ if (__DEV__) {
591
+ warning(
592
+ false,
593
+ 'RelayResponseNormalizer: Payload did not contain a value ' +
594
+ 'for field `%s`. Check that you are parsing with the same ' +
595
+ 'query that was used to fetch the payload. Payload is `%s`.',
596
+ ACTOR_IDENTIFIER_FIELD_NAME,
597
+ JSON.stringify(fieldValue, null, 2),
598
+ );
599
+ }
600
+ RelayModernRecord.setValue(record, storageKey, null);
601
+ return;
602
+ }
603
+
604
+ // $FlowFixMe[incompatible-call]
605
+ const typeName = field.concreteType ?? this._getRecordType(fieldValue);
606
+ const nextID =
607
+ this._getDataId(
608
+ // $FlowFixMe[incompatible-call]
609
+ fieldValue,
610
+ typeName,
611
+ ) ||
612
+ RelayModernRecord.getLinkedRecordID(record, storageKey) ||
613
+ generateClientID(RelayModernRecord.getDataID(record), storageKey);
614
+
615
+ invariant(
616
+ typeof nextID === 'string',
617
+ 'RelayResponseNormalizer: Expected id on field `%s` to be a string.',
618
+ storageKey,
619
+ );
620
+
621
+ RelayModernRecord.setActorLinkedRecordID(
622
+ record,
623
+ storageKey,
624
+ actorIdentifier,
625
+ nextID,
626
+ );
627
+
628
+ this._followupPayloads.push({
629
+ kind: 'ActorPayload',
630
+ data: (fieldValue: $FlowFixMe),
631
+ dataID: nextID,
632
+ path: [...this._path, responseKey],
633
+ typeName,
634
+ variables: this._variables,
635
+ node: field,
636
+ actorIdentifier,
637
+ });
638
+ }
639
+
516
640
  _normalizeFlightField(
517
641
  parent: NormalizationNode,
518
642
  selection: NormalizationFlightField,
@@ -524,11 +648,37 @@ class RelayResponseNormalizer {
524
648
  const fieldValue = data[responseKey];
525
649
 
526
650
  if (fieldValue == null) {
651
+ if (fieldValue === undefined) {
652
+ // Flight field may be missing in the response if:
653
+ // - It is inside an abstract type refinement where the concrete type does
654
+ // not conform to the interface/union.
655
+ // However an otherwise-required field may also be missing if the server
656
+ // is configured to skip fields with `null` values, in which case the
657
+ // client is assumed to be correctly configured with
658
+ // treatMissingFieldsAsNull=true.
659
+ if (this._isUnmatchedAbstractType) {
660
+ // Field not expected to exist regardless of whether the server is pruning null
661
+ // fields or not.
662
+ return;
663
+ } else {
664
+ // Not optional and the server is not pruning null fields: field is expected
665
+ // to be present
666
+ invariant(
667
+ this._treatMissingFieldsAsNull,
668
+ 'RelayResponseNormalizer: Payload did not contain a value for ' +
669
+ 'field `%s: %s`. Check that you are parsing with the same ' +
670
+ 'query that was used to fetch the payload.',
671
+ responseKey,
672
+ storageKey,
673
+ );
674
+ }
675
+ }
527
676
  RelayModernRecord.setValue(record, storageKey, null);
528
677
  return;
529
678
  }
530
679
 
531
680
  const reactFlightPayload = refineToReactFlightPayloadData(fieldValue);
681
+ const reactFlightPayloadDeserializer = this._reactFlightPayloadDeserializer;
532
682
 
533
683
  invariant(
534
684
  reactFlightPayload != null,
@@ -538,10 +688,10 @@ class RelayResponseNormalizer {
538
688
  fieldValue,
539
689
  );
540
690
  invariant(
541
- typeof this._reactFlightPayloadDeserializer === 'function',
691
+ typeof reactFlightPayloadDeserializer === 'function',
542
692
  'RelayResponseNormalizer: Expected reactFlightPayloadDeserializer to ' +
543
693
  'be a function, got `%s`.',
544
- this._reactFlightPayloadDeserializer,
694
+ reactFlightPayloadDeserializer,
545
695
  );
546
696
 
547
697
  if (reactFlightPayload.errors.length > 0) {
@@ -562,65 +712,104 @@ class RelayResponseNormalizer {
562
712
  }
563
713
  }
564
714
 
565
- // This typically indicates that a fatal server error prevented rows from
566
- // being written. When this occurs, we should not continue normalization of
567
- // the Flight field because the row response is malformed.
568
- //
569
- // Receiving empty rows is OK because it can indicate the start of a stream.
715
+ const reactFlightID = generateClientID(
716
+ RelayModernRecord.getDataID(record),
717
+ getStorageKey(selection, this._variables),
718
+ );
719
+ let reactFlightClientResponseRecord = this._recordSource.get(reactFlightID);
720
+ if (reactFlightClientResponseRecord == null) {
721
+ reactFlightClientResponseRecord = RelayModernRecord.create(
722
+ reactFlightID,
723
+ REACT_FLIGHT_TYPE_NAME,
724
+ );
725
+ this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
726
+ }
727
+
570
728
  if (reactFlightPayload.tree == null) {
729
+ // This typically indicates that a fatal server error prevented rows from
730
+ // being written. When this occurs, we should not continue normalization of
731
+ // the Flight field because the row response is malformed.
732
+ //
733
+ // Receiving empty rows is OK because it can indicate the start of a stream.
571
734
  warning(
572
735
  false,
573
736
  'RelayResponseNormalizer: Expected `tree` not to be null. This ' +
574
737
  'typically indicates that a fatal server error prevented any Server ' +
575
738
  'Component rows from being written.',
576
739
  );
740
+ // We create the flight record with a null value for the tree
741
+ // and empty reachable definitions
742
+ RelayModernRecord.setValue(
743
+ reactFlightClientResponseRecord,
744
+ REACT_FLIGHT_TREE_STORAGE_KEY,
745
+ null,
746
+ );
747
+ RelayModernRecord.setValue(
748
+ reactFlightClientResponseRecord,
749
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
750
+ [],
751
+ );
752
+ RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
577
753
  return;
578
754
  }
579
755
 
580
756
  // We store the deserialized reactFlightClientResponse in a separate
581
757
  // record and link it to the parent record. This is so we can GC the Flight
582
758
  // tree later even if the parent record is still reachable.
583
- const reactFlightClientResponse = this._reactFlightPayloadDeserializer(
759
+ const reactFlightClientResponse = reactFlightPayloadDeserializer(
584
760
  reactFlightPayload.tree,
585
761
  );
586
- const reactFlightID = generateClientID(
587
- RelayModernRecord.getDataID(record),
588
- getStorageKey(selection, this._variables),
589
- );
590
- let reactFlightClientResponseRecord = this._recordSource.get(reactFlightID);
591
- if (reactFlightClientResponseRecord == null) {
592
- reactFlightClientResponseRecord = RelayModernRecord.create(
593
- reactFlightID,
594
- REACT_FLIGHT_TYPE_NAME,
595
- );
596
- this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
597
- }
762
+
598
763
  RelayModernRecord.setValue(
599
764
  reactFlightClientResponseRecord,
600
765
  REACT_FLIGHT_TREE_STORAGE_KEY,
601
766
  reactFlightClientResponse,
602
767
  );
603
- const reachableQueries: Array<ReactFlightReachableQuery> = [];
768
+
769
+ const reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions> =
770
+ [];
604
771
  for (const query of reactFlightPayload.queries) {
605
772
  if (query.response.data != null) {
606
- this._moduleImportPayloads.push({
773
+ this._followupPayloads.push({
774
+ kind: 'ModuleImportPayload',
775
+ args: null,
607
776
  data: query.response.data,
608
777
  dataID: ROOT_ID,
609
778
  operationReference: query.module,
610
779
  path: [],
611
780
  typeName: ROOT_TYPE,
612
781
  variables: query.variables,
782
+ actorIdentifier: this._actorIdentifier,
613
783
  });
614
784
  }
615
- reachableQueries.push({
785
+ reachableExecutableDefinitions.push({
616
786
  module: query.module,
617
787
  variables: query.variables,
618
788
  });
619
789
  }
790
+ for (const fragment of reactFlightPayload.fragments) {
791
+ if (fragment.response.data != null) {
792
+ this._followupPayloads.push({
793
+ kind: 'ModuleImportPayload',
794
+ args: null,
795
+ data: fragment.response.data,
796
+ dataID: fragment.__id,
797
+ operationReference: fragment.module,
798
+ path: [],
799
+ typeName: fragment.__typename,
800
+ variables: fragment.variables,
801
+ actorIdentifier: this._actorIdentifier,
802
+ });
803
+ }
804
+ reachableExecutableDefinitions.push({
805
+ module: fragment.module,
806
+ variables: fragment.variables,
807
+ });
808
+ }
620
809
  RelayModernRecord.setValue(
621
810
  reactFlightClientResponseRecord,
622
- REACT_FLIGHT_QUERIES_STORAGE_KEY,
623
- reachableQueries,
811
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
812
+ reachableExecutableDefinitions,
624
813
  );
625
814
  RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
626
815
  }
@@ -826,9 +1015,6 @@ class RelayResponseNormalizer {
826
1015
  }
827
1016
  }
828
1017
 
829
- const instrumentedNormalize: typeof normalize = RelayProfiler.instrument(
830
- 'RelayResponseNormalizer.normalize',
1018
+ module.exports = {
831
1019
  normalize,
832
- );
833
-
834
- module.exports = {normalize: instrumentedNormalize};
1020
+ };
@@ -12,14 +12,15 @@
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
 
22
- const REACT_FLIGHT_QUERIES_STORAGE_KEY = 'queries';
18
+ const {getType} = require('./RelayModernRecord');
19
+ const invariant = require('invariant');
20
+
21
+ // Reachable (client) executable definitions encountered while server component
22
+ // rendering
23
+ const REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY = 'executableDefinitions';
23
24
  const REACT_FLIGHT_TREE_STORAGE_KEY = 'tree';
24
25
  const REACT_FLIGHT_TYPE_NAME = 'ReactFlightComponent';
25
26
 
@@ -32,6 +33,7 @@ function refineToReactFlightPayloadData(
32
33
  typeof payload.status !== 'string' ||
33
34
  (!Array.isArray(payload.tree) && payload.tree !== null) ||
34
35
  !Array.isArray(payload.queries) ||
36
+ !Array.isArray(payload.fragments) ||
35
37
  !Array.isArray(payload.errors)
36
38
  ) {
37
39
  return null;
@@ -48,17 +50,11 @@ function getReactFlightClientResponse(
48
50
  'got %s.',
49
51
  record,
50
52
  );
51
- const response: ?ReactFlightClientResponse = (record[
52
- REACT_FLIGHT_TREE_STORAGE_KEY
53
- ]: $FlowFixMe);
54
- if (response != null) {
55
- return response;
56
- }
57
- return null;
53
+ return (record[REACT_FLIGHT_TREE_STORAGE_KEY]: $FlowFixMe);
58
54
  }
59
55
 
60
56
  module.exports = {
61
- REACT_FLIGHT_QUERIES_STORAGE_KEY,
57
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
62
58
  REACT_FLIGHT_TREE_STORAGE_KEY,
63
59
  REACT_FLIGHT_TYPE_NAME,
64
60
  getReactFlightClientResponse,