relay-runtime 11.0.1 → 13.0.0-rc.1

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 -17
  3. package/handlers/connection/MutationHandlers.js.flow +7 -11
  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 +13 -19
  8. package/lib/handlers/connection/MutationHandlers.js +4 -7
  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 +10 -5
  25. package/lib/network/ConvertToExecuteFunction.js +2 -1
  26. package/lib/network/RelayNetwork.js +3 -2
  27. package/lib/network/RelayQueryResponseCache.js +21 -5
  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 +123 -54
  35. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +518 -200
  36. package/lib/store/RelayConcreteVariables.js +26 -8
  37. package/lib/store/RelayExperimentalGraphResponseHandler.js +153 -0
  38. package/lib/store/RelayExperimentalGraphResponseTransform.js +391 -0
  39. package/lib/store/RelayModernEnvironment.js +175 -240
  40. package/lib/store/RelayModernFragmentSpecResolver.js +52 -26
  41. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  42. package/lib/store/RelayModernRecord.js +47 -12
  43. package/lib/store/RelayModernSelector.js +14 -8
  44. package/lib/store/RelayModernStore.js +56 -28
  45. package/lib/store/RelayOperationTracker.js +34 -24
  46. package/lib/store/RelayPublishQueue.js +41 -13
  47. package/lib/store/RelayReader.js +288 -48
  48. package/lib/store/RelayRecordSource.js +87 -3
  49. package/lib/store/RelayReferenceMarker.js +34 -22
  50. package/lib/store/RelayResponseNormalizer.js +211 -110
  51. package/lib/store/RelayStoreReactFlightUtils.js +4 -10
  52. package/lib/store/RelayStoreSubscriptions.js +14 -9
  53. package/lib/store/RelayStoreUtils.js +12 -7
  54. package/lib/store/ResolverCache.js +213 -0
  55. package/lib/store/ResolverFragments.js +61 -0
  56. package/lib/store/cloneRelayHandleSourceField.js +5 -4
  57. package/lib/store/cloneRelayScalarHandleSourceField.js +5 -4
  58. package/lib/store/createRelayContext.js +4 -2
  59. package/lib/store/readInlineData.js +6 -2
  60. package/lib/subscription/requestSubscription.js +34 -25
  61. package/lib/util/RelayConcreteNode.js +3 -0
  62. package/lib/util/RelayFeatureFlags.js +10 -4
  63. package/lib/util/RelayProfiler.js +17 -187
  64. package/lib/util/RelayReplaySubject.js +22 -7
  65. package/lib/util/RelayRuntimeTypes.js +0 -6
  66. package/lib/util/StringInterner.js +71 -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 +26 -16
  95. package/network/ConvertToExecuteFunction.js.flow +2 -2
  96. package/network/RelayNetwork.js.flow +4 -5
  97. package/network/RelayNetworkTypes.js.flow +5 -4
  98. package/network/RelayObservable.js.flow +1 -1
  99. package/network/RelayQueryResponseCache.js.flow +34 -21
  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 +141 -59
  111. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +605 -303
  112. package/store/RelayConcreteVariables.js.flow +27 -8
  113. package/store/RelayExperimentalGraphResponseHandler.js.flow +124 -0
  114. package/store/RelayExperimentalGraphResponseTransform.js.flow +475 -0
  115. package/store/RelayModernEnvironment.js.flow +173 -240
  116. package/store/RelayModernFragmentSpecResolver.js.flow +55 -31
  117. package/store/RelayModernOperationDescriptor.js.flow +12 -7
  118. package/store/RelayModernRecord.js.flow +67 -11
  119. package/store/RelayModernSelector.js.flow +24 -14
  120. package/store/RelayModernStore.js.flow +66 -36
  121. package/store/RelayOperationTracker.js.flow +59 -43
  122. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  123. package/store/RelayPublishQueue.js.flow +79 -34
  124. package/store/RelayReader.js.flow +351 -73
  125. package/store/RelayRecordSource.js.flow +72 -6
  126. package/store/RelayReferenceMarker.js.flow +40 -26
  127. package/store/RelayResponseNormalizer.js.flow +258 -99
  128. package/store/RelayStoreReactFlightUtils.js.flow +4 -11
  129. package/store/RelayStoreSubscriptions.js.flow +19 -11
  130. package/store/RelayStoreTypes.js.flow +209 -43
  131. package/store/RelayStoreUtils.js.flow +24 -11
  132. package/store/ResolverCache.js.flow +249 -0
  133. package/store/ResolverFragments.js.flow +121 -0
  134. package/store/StoreInspector.js.flow +2 -2
  135. package/store/TypeID.js.flow +1 -1
  136. package/store/ViewerPattern.js.flow +2 -2
  137. package/store/cloneRelayHandleSourceField.js.flow +5 -6
  138. package/store/cloneRelayScalarHandleSourceField.js.flow +5 -6
  139. package/store/createFragmentSpecResolver.js.flow +3 -4
  140. package/store/createRelayContext.js.flow +3 -3
  141. package/store/normalizeRelayPayload.js.flow +6 -7
  142. package/store/readInlineData.js.flow +7 -8
  143. package/subscription/requestSubscription.js.flow +53 -41
  144. package/util/NormalizationNode.js.flow +10 -3
  145. package/util/ReaderNode.js.flow +38 -2
  146. package/util/RelayConcreteNode.js.flow +5 -0
  147. package/util/RelayFeatureFlags.js.flow +24 -10
  148. package/util/RelayProfiler.js.flow +22 -194
  149. package/util/RelayReplaySubject.js.flow +9 -9
  150. package/util/RelayRuntimeTypes.js.flow +72 -3
  151. package/util/StringInterner.js.flow +69 -0
  152. package/util/createPayloadFor3DField.js.flow +3 -3
  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 +1 -0
  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,18 +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,
25
47
  CLIENT_COMPONENT,
26
48
  CLIENT_EXTENSION,
49
+ CONDITION,
27
50
  DEFER,
28
51
  FLIGHT_FIELD,
29
52
  FRAGMENT_SPREAD,
@@ -36,49 +59,31 @@ const {
36
59
  STREAM,
37
60
  TYPE_DISCRIMINATOR,
38
61
  } = require('../util/RelayConcreteNode');
62
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
39
63
  const {generateClientID, isClientID} = require('./ClientID');
64
+ const {getLocalVariables} = require('./RelayConcreteVariables');
65
+ const RelayModernRecord = require('./RelayModernRecord');
40
66
  const {createNormalizationSelector} = require('./RelayModernSelector');
41
67
  const {
42
- refineToReactFlightPayloadData,
43
68
  REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
44
69
  REACT_FLIGHT_TREE_STORAGE_KEY,
45
70
  REACT_FLIGHT_TYPE_NAME,
71
+ refineToReactFlightPayloadData,
46
72
  } = require('./RelayStoreReactFlightUtils');
47
73
  const {
74
+ ROOT_ID,
75
+ ROOT_TYPE,
76
+ TYPENAME_KEY,
48
77
  getArgumentValues,
49
78
  getHandleStorageKey,
50
79
  getModuleComponentKey,
51
80
  getModuleOperationKey,
52
81
  getStorageKey,
53
- TYPENAME_KEY,
54
- ROOT_ID,
55
- ROOT_TYPE,
56
82
  } = require('./RelayStoreUtils');
57
- const {generateTypeID, TYPE_SCHEMA_TYPE} = require('./TypeID');
58
-
59
- import type {PayloadData} from '../network/RelayNetworkTypes';
60
- import type {
61
- NormalizationDefer,
62
- NormalizationFlightField,
63
- NormalizationLinkedField,
64
- NormalizationModuleImport,
65
- NormalizationNode,
66
- NormalizationScalarField,
67
- NormalizationStream,
68
- } from '../util/NormalizationNode';
69
- import type {DataID, Variables} from '../util/RelayRuntimeTypes';
70
- import type {
71
- HandleFieldPayload,
72
- IncrementalDataPlaceholder,
73
- ModuleImportPayload,
74
- MutableRecordSource,
75
- NormalizationSelector,
76
- ReactFlightReachableExecutableDefinitions,
77
- ReactFlightPayloadDeserializer,
78
- ReactFlightServerErrorHandler,
79
- Record,
80
- RelayResponsePayload,
81
- } 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');
82
87
 
83
88
  export type GetDataID = (
84
89
  fieldValue: interface {[string]: mixed},
@@ -92,6 +97,7 @@ export type NormalizationOptions = {|
92
97
  +reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
93
98
  +reactFlightServerErrorHandler?: ?ReactFlightServerErrorHandler,
94
99
  +shouldProcessClientComponents?: ?boolean,
100
+ +actorIdentifier?: ?ActorIdentifier,
95
101
  |};
96
102
 
97
103
  /**
@@ -119,13 +125,14 @@ function normalize(
119
125
  * Helper for handling payloads.
120
126
  */
121
127
  class RelayResponseNormalizer {
128
+ _actorIdentifier: ?ActorIdentifier;
122
129
  _getDataId: GetDataID;
123
130
  _handleFieldPayloads: Array<HandleFieldPayload>;
124
131
  _treatMissingFieldsAsNull: boolean;
125
132
  _incrementalPlaceholders: Array<IncrementalDataPlaceholder>;
126
133
  _isClientExtension: boolean;
127
134
  _isUnmatchedAbstractType: boolean;
128
- _moduleImportPayloads: Array<ModuleImportPayload>;
135
+ _followupPayloads: Array<FollowupPayload>;
129
136
  _path: Array<string>;
130
137
  _recordSource: MutableRecordSource;
131
138
  _variables: Variables;
@@ -138,13 +145,14 @@ class RelayResponseNormalizer {
138
145
  variables: Variables,
139
146
  options: NormalizationOptions,
140
147
  ) {
148
+ this._actorIdentifier = options.actorIdentifier;
141
149
  this._getDataId = options.getDataID;
142
150
  this._handleFieldPayloads = [];
143
151
  this._treatMissingFieldsAsNull = options.treatMissingFieldsAsNull;
144
152
  this._incrementalPlaceholders = [];
145
153
  this._isClientExtension = false;
146
154
  this._isUnmatchedAbstractType = false;
147
- this._moduleImportPayloads = [];
155
+ this._followupPayloads = [];
148
156
  this._path = options.path ? [...options.path] : [];
149
157
  this._recordSource = recordSource;
150
158
  this._variables = variables;
@@ -170,7 +178,7 @@ class RelayResponseNormalizer {
170
178
  errors: null,
171
179
  fieldPayloads: this._handleFieldPayloads,
172
180
  incrementalPlaceholders: this._incrementalPlaceholders,
173
- moduleImportPayloads: this._moduleImportPayloads,
181
+ followupPayloads: this._followupPayloads,
174
182
  source: this._recordSource,
175
183
  isFinal: false,
176
184
  };
@@ -182,7 +190,6 @@ class RelayResponseNormalizer {
182
190
  'RelayResponseNormalizer(): Undefined variable `%s`.',
183
191
  name,
184
192
  );
185
- // $FlowFixMe[cannot-write]
186
193
  return this._variables[name];
187
194
  }
188
195
 
@@ -209,13 +216,22 @@ class RelayResponseNormalizer {
209
216
  this._normalizeField(node, selection, record, data);
210
217
  break;
211
218
  case CONDITION:
212
- const conditionValue = this._getVariableValue(selection.condition);
219
+ const conditionValue = Boolean(
220
+ this._getVariableValue(selection.condition),
221
+ );
213
222
  if (conditionValue === selection.passingValue) {
214
223
  this._traverseSelections(selection, record, data);
215
224
  }
216
225
  break;
217
226
  case FRAGMENT_SPREAD: {
227
+ const prevVariables = this._variables;
228
+ this._variables = getLocalVariables(
229
+ this._variables,
230
+ selection.fragment.argumentDefinitions,
231
+ selection.args,
232
+ );
218
233
  this._traverseSelections(selection.fragment, record, data);
234
+ this._variables = prevVariables;
219
235
  break;
220
236
  }
221
237
  case INLINE_FRAGMENT: {
@@ -225,7 +241,7 @@ class RelayResponseNormalizer {
225
241
  if (typeName === selection.type) {
226
242
  this._traverseSelections(selection, record, data);
227
243
  }
228
- } else if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
244
+ } else {
229
245
  const implementsInterface = data.hasOwnProperty(abstractKey);
230
246
  const typeName = RelayModernRecord.getType(record);
231
247
  const typeID = generateTypeID(typeName);
@@ -242,36 +258,24 @@ class RelayResponseNormalizer {
242
258
  if (implementsInterface) {
243
259
  this._traverseSelections(selection, record, data);
244
260
  }
245
- } else {
246
- // legacy behavior for abstract refinements: always normalize even
247
- // if the type doesn't conform, but track if the type matches or not
248
- // for determining whether response fields are expected to be present
249
- const implementsInterface = data.hasOwnProperty(abstractKey);
250
- const parentIsUnmatchedAbstractType = this._isUnmatchedAbstractType;
251
- this._isUnmatchedAbstractType =
252
- this._isUnmatchedAbstractType || !implementsInterface;
253
- this._traverseSelections(selection, record, data);
254
- this._isUnmatchedAbstractType = parentIsUnmatchedAbstractType;
255
261
  }
256
262
  break;
257
263
  }
258
264
  case TYPE_DISCRIMINATOR: {
259
- if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
260
- const {abstractKey} = selection;
261
- const implementsInterface = data.hasOwnProperty(abstractKey);
262
- const typeName = RelayModernRecord.getType(record);
263
- const typeID = generateTypeID(typeName);
264
- let typeRecord = this._recordSource.get(typeID);
265
- if (typeRecord == null) {
266
- typeRecord = RelayModernRecord.create(typeID, TYPE_SCHEMA_TYPE);
267
- this._recordSource.set(typeID, typeRecord);
268
- }
269
- RelayModernRecord.setValue(
270
- typeRecord,
271
- abstractKey,
272
- implementsInterface,
273
- );
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);
274
273
  }
274
+ RelayModernRecord.setValue(
275
+ typeRecord,
276
+ abstractKey,
277
+ implementsInterface,
278
+ );
275
279
  break;
276
280
  }
277
281
  case LINKED_HANDLE:
@@ -282,13 +286,17 @@ class RelayResponseNormalizer {
282
286
  const fieldKey = getStorageKey(selection, this._variables);
283
287
  const handleKey = getHandleStorageKey(selection, this._variables);
284
288
  this._handleFieldPayloads.push({
289
+ /* $FlowFixMe[class-object-subtyping] added when improving typing
290
+ * for this parameters */
285
291
  args,
286
292
  dataID: RelayModernRecord.getDataID(record),
287
293
  fieldKey,
288
294
  handle: selection.handle,
289
295
  handleKey,
290
296
  handleArgs: selection.handleArgs
291
- ? getArgumentValues(selection.handleArgs, this._variables)
297
+ ? /* $FlowFixMe[class-object-subtyping] added when improving typing
298
+ * for this parameters */
299
+ getArgumentValues(selection.handleArgs, this._variables)
292
300
  : {},
293
301
  });
294
302
  break;
@@ -320,6 +328,9 @@ class RelayResponseNormalizer {
320
328
  throw new Error('Flight fields are not yet supported.');
321
329
  }
322
330
  break;
331
+ case ACTOR_CHANGE:
332
+ this._normalizeActorChange(node, selection, record, data);
333
+ break;
323
334
  default:
324
335
  (selection: empty);
325
336
  invariant(
@@ -363,6 +374,7 @@ class RelayResponseNormalizer {
363
374
  this._variables,
364
375
  ),
365
376
  typeName: RelayModernRecord.getType(record),
377
+ actorIdentifier: this._actorIdentifier,
366
378
  });
367
379
  }
368
380
  }
@@ -395,6 +407,7 @@ class RelayResponseNormalizer {
395
407
  parentID: RelayModernRecord.getDataID(record),
396
408
  node: stream,
397
409
  variables: this._variables,
410
+ actorIdentifier: this._actorIdentifier,
398
411
  });
399
412
  }
400
413
  }
@@ -425,13 +438,16 @@ class RelayResponseNormalizer {
425
438
  operationReference ?? null,
426
439
  );
427
440
  if (operationReference != null) {
428
- this._moduleImportPayloads.push({
441
+ this._followupPayloads.push({
442
+ kind: 'ModuleImportPayload',
443
+ args: moduleImport.args,
429
444
  data,
430
445
  dataID: RelayModernRecord.getDataID(record),
431
446
  operationReference,
432
447
  path: [...this._path],
433
448
  typeName,
434
449
  variables: this._variables,
450
+ actorIdentifier: this._actorIdentifier,
435
451
  });
436
452
  }
437
453
  }
@@ -489,7 +505,11 @@ class RelayResponseNormalizer {
489
505
  this._validateConflictingFieldsWithIdenticalId(
490
506
  record,
491
507
  storageKey,
492
- 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,
493
513
  );
494
514
  }
495
515
  }
@@ -524,6 +544,99 @@ class RelayResponseNormalizer {
524
544
  }
525
545
  }
526
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
+
527
640
  _normalizeFlightField(
528
641
  parent: NormalizationNode,
529
642
  selection: NormalizationFlightField,
@@ -535,11 +648,37 @@ class RelayResponseNormalizer {
535
648
  const fieldValue = data[responseKey];
536
649
 
537
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
+ }
538
676
  RelayModernRecord.setValue(record, storageKey, null);
539
677
  return;
540
678
  }
541
679
 
542
680
  const reactFlightPayload = refineToReactFlightPayloadData(fieldValue);
681
+ const reactFlightPayloadDeserializer = this._reactFlightPayloadDeserializer;
543
682
 
544
683
  invariant(
545
684
  reactFlightPayload != null,
@@ -549,10 +688,10 @@ class RelayResponseNormalizer {
549
688
  fieldValue,
550
689
  );
551
690
  invariant(
552
- typeof this._reactFlightPayloadDeserializer === 'function',
691
+ typeof reactFlightPayloadDeserializer === 'function',
553
692
  'RelayResponseNormalizer: Expected reactFlightPayloadDeserializer to ' +
554
693
  'be a function, got `%s`.',
555
- this._reactFlightPayloadDeserializer,
694
+ reactFlightPayloadDeserializer,
556
695
  );
557
696
 
558
697
  if (reactFlightPayload.errors.length > 0) {
@@ -573,54 +712,74 @@ class RelayResponseNormalizer {
573
712
  }
574
713
  }
575
714
 
576
- // This typically indicates that a fatal server error prevented rows from
577
- // being written. When this occurs, we should not continue normalization of
578
- // the Flight field because the row response is malformed.
579
- //
580
- // 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
+
581
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.
582
734
  warning(
583
735
  false,
584
736
  'RelayResponseNormalizer: Expected `tree` not to be null. This ' +
585
737
  'typically indicates that a fatal server error prevented any Server ' +
586
738
  'Component rows from being written.',
587
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);
588
753
  return;
589
754
  }
590
755
 
591
756
  // We store the deserialized reactFlightClientResponse in a separate
592
757
  // record and link it to the parent record. This is so we can GC the Flight
593
758
  // tree later even if the parent record is still reachable.
594
- const reactFlightClientResponse = this._reactFlightPayloadDeserializer(
759
+ const reactFlightClientResponse = reactFlightPayloadDeserializer(
595
760
  reactFlightPayload.tree,
596
761
  );
597
- const reactFlightID = generateClientID(
598
- RelayModernRecord.getDataID(record),
599
- getStorageKey(selection, this._variables),
600
- );
601
- let reactFlightClientResponseRecord = this._recordSource.get(reactFlightID);
602
- if (reactFlightClientResponseRecord == null) {
603
- reactFlightClientResponseRecord = RelayModernRecord.create(
604
- reactFlightID,
605
- REACT_FLIGHT_TYPE_NAME,
606
- );
607
- this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
608
- }
762
+
609
763
  RelayModernRecord.setValue(
610
764
  reactFlightClientResponseRecord,
611
765
  REACT_FLIGHT_TREE_STORAGE_KEY,
612
766
  reactFlightClientResponse,
613
767
  );
614
- const reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions> = [];
768
+
769
+ const reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions> =
770
+ [];
615
771
  for (const query of reactFlightPayload.queries) {
616
772
  if (query.response.data != null) {
617
- this._moduleImportPayloads.push({
773
+ this._followupPayloads.push({
774
+ kind: 'ModuleImportPayload',
775
+ args: null,
618
776
  data: query.response.data,
619
777
  dataID: ROOT_ID,
620
778
  operationReference: query.module,
621
779
  path: [],
622
780
  typeName: ROOT_TYPE,
623
781
  variables: query.variables,
782
+ actorIdentifier: this._actorIdentifier,
624
783
  });
625
784
  }
626
785
  reachableExecutableDefinitions.push({
@@ -630,13 +789,16 @@ class RelayResponseNormalizer {
630
789
  }
631
790
  for (const fragment of reactFlightPayload.fragments) {
632
791
  if (fragment.response.data != null) {
633
- this._moduleImportPayloads.push({
792
+ this._followupPayloads.push({
793
+ kind: 'ModuleImportPayload',
794
+ args: null,
634
795
  data: fragment.response.data,
635
796
  dataID: fragment.__id,
636
797
  operationReference: fragment.module,
637
798
  path: [],
638
799
  typeName: fragment.__typename,
639
800
  variables: fragment.variables,
801
+ actorIdentifier: this._actorIdentifier,
640
802
  });
641
803
  }
642
804
  reachableExecutableDefinitions.push({
@@ -853,9 +1015,6 @@ class RelayResponseNormalizer {
853
1015
  }
854
1016
  }
855
1017
 
856
- const instrumentedNormalize: typeof normalize = RelayProfiler.instrument(
857
- 'RelayResponseNormalizer.normalize',
1018
+ module.exports = {
858
1019
  normalize,
859
- );
860
-
861
- module.exports = {normalize: instrumentedNormalize};
1020
+ };
@@ -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';
@@ -51,13 +50,7 @@ function getReactFlightClientResponse(
51
50
  'got %s.',
52
51
  record,
53
52
  );
54
- const response: ?ReactFlightClientResponse = (record[
55
- REACT_FLIGHT_TREE_STORAGE_KEY
56
- ]: $FlowFixMe);
57
- if (response != null) {
58
- return response;
59
- }
60
- return null;
53
+ return (record[REACT_FLIGHT_TREE_STORAGE_KEY]: $FlowFixMe);
61
54
  }
62
55
 
63
56
  module.exports = {