relay-runtime 18.2.0 → 20.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 (83) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +8 -6
  3. package/index.js +1 -1
  4. package/index.js.flow +3 -0
  5. package/lib/experimental.js +5 -2
  6. package/lib/index.js +3 -0
  7. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +1 -1
  8. package/lib/mutations/RelayRecordSourceProxy.js +2 -1
  9. package/lib/mutations/createUpdatableProxy.js +1 -1
  10. package/lib/mutations/validateMutation.js +2 -2
  11. package/lib/network/RelayObservable.js +1 -3
  12. package/lib/network/wrapNetworkWithLogObserver.js +2 -2
  13. package/lib/query/fetchQuery.js +1 -1
  14. package/lib/store/DataChecker.js +12 -8
  15. package/lib/store/OperationExecutor.js +93 -43
  16. package/lib/store/RelayModernEnvironment.js +13 -4
  17. package/lib/store/RelayModernFragmentSpecResolver.js +4 -4
  18. package/lib/store/RelayModernStore.js +49 -24
  19. package/lib/store/RelayPublishQueue.js +11 -15
  20. package/lib/store/RelayReader.js +134 -151
  21. package/lib/store/RelayReferenceMarker.js +14 -7
  22. package/lib/store/RelayResponseNormalizer.js +57 -31
  23. package/lib/store/RelayStoreSubscriptions.js +2 -2
  24. package/lib/store/RelayStoreUtils.js +8 -0
  25. package/lib/store/ResolverFragments.js +2 -2
  26. package/lib/store/createRelayLoggingContext.js +17 -0
  27. package/lib/store/generateTypenamePrefixedDataID.js +9 -0
  28. package/lib/store/live-resolvers/LiveResolverCache.js +4 -2
  29. package/lib/store/live-resolvers/resolverDataInjector.js +4 -4
  30. package/lib/store/normalizeResponse.js +2 -2
  31. package/lib/store/observeFragmentExperimental.js +60 -13
  32. package/lib/store/observeQueryExperimental.js +21 -0
  33. package/lib/util/RelayFeatureFlags.js +7 -1
  34. package/lib/util/handlePotentialSnapshotErrors.js +11 -8
  35. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +1 -0
  36. package/mutations/RelayRecordSourceProxy.js.flow +4 -0
  37. package/mutations/createUpdatableProxy.js.flow +1 -1
  38. package/mutations/validateMutation.js.flow +3 -3
  39. package/network/RelayNetworkTypes.js.flow +3 -0
  40. package/network/RelayObservable.js.flow +1 -5
  41. package/network/wrapNetworkWithLogObserver.js.flow +19 -1
  42. package/package.json +1 -1
  43. package/query/fetchQuery.js.flow +1 -1
  44. package/store/DataChecker.js.flow +16 -4
  45. package/store/OperationExecutor.js.flow +101 -15
  46. package/store/RelayExperimentalGraphResponseTransform.js.flow +4 -4
  47. package/store/RelayModernEnvironment.js.flow +22 -6
  48. package/store/RelayModernFragmentSpecResolver.js.flow +6 -6
  49. package/store/RelayModernSelector.js.flow +2 -0
  50. package/store/RelayModernStore.js.flow +86 -27
  51. package/store/RelayPublishQueue.js.flow +32 -21
  52. package/store/RelayReader.js.flow +168 -97
  53. package/store/RelayReferenceMarker.js.flow +15 -5
  54. package/store/RelayResponseNormalizer.js.flow +104 -69
  55. package/store/RelayStoreSubscriptions.js.flow +2 -2
  56. package/store/RelayStoreTypes.js.flow +34 -4
  57. package/store/RelayStoreUtils.js.flow +29 -0
  58. package/store/ResolverCache.js.flow +2 -2
  59. package/store/ResolverFragments.js.flow +5 -3
  60. package/store/StoreInspector.js.flow +5 -0
  61. package/store/createRelayContext.js.flow +3 -2
  62. package/store/createRelayLoggingContext.js.flow +46 -0
  63. package/store/generateTypenamePrefixedDataID.js.flow +25 -0
  64. package/store/live-resolvers/LiveResolverCache.js.flow +7 -2
  65. package/store/live-resolvers/resolverDataInjector.js.flow +10 -6
  66. package/store/normalizeResponse.js.flow +2 -0
  67. package/store/observeFragmentExperimental.js.flow +82 -28
  68. package/store/observeQueryExperimental.js.flow +61 -0
  69. package/store/waitForFragmentExperimental.js.flow +4 -3
  70. package/util/NormalizationNode.js.flow +2 -1
  71. package/util/RelayConcreteNode.js.flow +2 -0
  72. package/util/RelayError.js.flow +1 -0
  73. package/util/RelayFeatureFlags.js.flow +28 -0
  74. package/util/RelayRuntimeTypes.js.flow +6 -3
  75. package/util/getPaginationVariables.js.flow +2 -0
  76. package/util/handlePotentialSnapshotErrors.js.flow +23 -11
  77. package/util/registerEnvironmentWithDevTools.js.flow +4 -2
  78. package/util/withProvidedVariables.js.flow +1 -0
  79. package/util/withStartAndDuration.js.flow +3 -0
  80. package/relay-runtime-experimental.js +0 -4
  81. package/relay-runtime-experimental.min.js +0 -9
  82. package/relay-runtime.js +0 -4
  83. package/relay-runtime.min.js +0 -9
@@ -145,13 +145,13 @@ if (__DEV__) {
145
145
  return validateModuleImport(context);
146
146
  case 'TypeDiscriminator':
147
147
  return validateAbstractKey(context, selection.abstractKey);
148
- case 'RelayResolver':
149
- case 'RelayLiveResolver':
150
148
  case 'ClientEdgeToClientObject':
151
149
  case 'LinkedHandle':
152
150
  case 'ScalarHandle':
153
151
  case 'Defer':
154
- case 'Stream': {
152
+ case 'Stream':
153
+ case 'RelayResolver':
154
+ case 'RelayLiveResolver': {
155
155
  // TODO(T35864292) - Add missing validations for these types
156
156
  return;
157
157
  }
@@ -11,6 +11,7 @@
11
11
 
12
12
  'use strict';
13
13
 
14
+ import type {OperationAvailability} from '../store/RelayStoreTypes';
14
15
  import type {RequestParameters} from '../util/RelayConcreteNode';
15
16
  import type {CacheConfig, Variables} from '../util/RelayRuntimeTypes';
16
17
  import type RelayObservable, {ObservableFromValue} from './RelayObservable';
@@ -104,6 +105,8 @@ export type ExecuteFunction = (
104
105
  logRequestInfo?: ?LogRequestInfoFunction,
105
106
  encryptedVariables?: ?string,
106
107
  preprocessResponse?: ?preprocessResponseFunction,
108
+ // Run datachecker on the current operation and returns the OperationAvailability
109
+ checkOperation?: () => OperationAvailability,
107
110
  ) => RelayObservable<GraphQLResponse>;
108
111
 
109
112
  /**
@@ -614,11 +614,7 @@ if (__DEV__) {
614
614
  // Default implementation of HostReportErrors() in development builds.
615
615
  // Can be replaced by the host application environment.
616
616
  RelayObservable.onUnhandledError((error, isUncaughtThrownError) => {
617
- declare function fail(string): void;
618
- if (typeof fail === 'function') {
619
- // In test environments (Jest), fail() immediately fails the current test.
620
- fail(String(error));
621
- } else if (isUncaughtThrownError) {
617
+ if (isUncaughtThrownError) {
622
618
  // Rethrow uncaught thrown errors on the next frame to avoid breaking
623
619
  // current logic.
624
620
  setTimeout(() => {
@@ -12,12 +12,17 @@
12
12
  'use strict';
13
13
  import type ActorSpecificEnvironment from '../multi-actor-environment/ActorSpecificEnvironment';
14
14
  import type RelayModernEnvironment from '../store/RelayModernEnvironment';
15
+ import type {
16
+ LogRequestInfoFunction,
17
+ OperationAvailability,
18
+ } from '../store/RelayStoreTypes';
15
19
  import type {RequestParameters} from '../util/RelayConcreteNode';
16
20
  import type {CacheConfig, Variables} from '../util/RelayRuntimeTypes';
17
21
  import type {
18
22
  GraphQLResponse,
19
23
  INetwork,
20
24
  UploadableMap,
25
+ preprocessResponseFunction,
21
26
  } from './RelayNetworkTypes';
22
27
  import type RelayObservable from './RelayObservable';
23
28
  import type {Subscription} from './RelayObservable';
@@ -42,6 +47,10 @@ function wrapNetworkWithLogObserver(
42
47
  variables: Variables,
43
48
  cacheConfig: CacheConfig,
44
49
  uploadables?: ?UploadableMap,
50
+ _?: ?LogRequestInfoFunction,
51
+ encryptedVariables?: ?string,
52
+ preprocessResponse?: ?preprocessResponseFunction,
53
+ checkOperation?: () => OperationAvailability,
45
54
  ): RelayObservable<GraphQLResponse> {
46
55
  const networkRequestId = generateID();
47
56
  const logObserver = {
@@ -89,7 +98,16 @@ function wrapNetworkWithLogObserver(
89
98
  });
90
99
  };
91
100
  return network
92
- .execute(params, variables, cacheConfig, uploadables, logRequestInfo)
101
+ .execute(
102
+ params,
103
+ variables,
104
+ cacheConfig,
105
+ uploadables,
106
+ logRequestInfo,
107
+ encryptedVariables,
108
+ preprocessResponse,
109
+ checkOperation,
110
+ )
93
111
  .do(logObserver);
94
112
  },
95
113
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "relay-runtime",
3
3
  "description": "A core runtime for building GraphQL-driven applications.",
4
- "version": "18.2.0",
4
+ "version": "20.0.0",
5
5
  "keywords": [
6
6
  "graphql",
7
7
  "relay"
@@ -138,7 +138,7 @@ function fetchQuery<TVariables: Variables, TData, TRawResponse>(
138
138
  const fetchPolicy = options?.fetchPolicy ?? 'network-only';
139
139
 
140
140
  function readData(snapshot: Snapshot): TData {
141
- handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
141
+ handlePotentialSnapshotErrors(environment, snapshot.fieldErrors);
142
142
  /* $FlowFixMe[incompatible-return] we assume readData returns the right
143
143
  * data just having written it from network or checked availability. */
144
144
  return snapshot.data;
@@ -73,6 +73,7 @@ function check(
73
73
  getDataID: GetDataID,
74
74
  shouldProcessClientComponents: ?boolean,
75
75
  log: ?LogFunction,
76
+ useExecTimeResolvers: ?boolean,
76
77
  ): Availability {
77
78
  if (log != null) {
78
79
  log({
@@ -90,6 +91,8 @@ function check(
90
91
  operationLoader,
91
92
  getDataID,
92
93
  shouldProcessClientComponents,
94
+ log,
95
+ useExecTimeResolvers,
93
96
  );
94
97
  const result = checker.check(node, dataID);
95
98
  if (log != null) {
@@ -112,6 +115,7 @@ class DataChecker {
112
115
  _recordSourceProxy: RelayRecordSourceProxy;
113
116
  _recordWasMissing: boolean;
114
117
  _source: RecordSource;
118
+ _useExecTimeResolvers: boolean;
115
119
  _variables: Variables;
116
120
  _shouldProcessClientComponents: ?boolean;
117
121
  +_getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource;
@@ -123,6 +127,7 @@ class DataChecker {
123
127
  ActorIdentifier,
124
128
  [RelayRecordSourceMutator, RelayRecordSourceProxy],
125
129
  >;
130
+ _log: ?LogFunction;
126
131
 
127
132
  constructor(
128
133
  getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource,
@@ -135,6 +140,8 @@ class DataChecker {
135
140
  operationLoader: ?OperationLoader,
136
141
  getDataID: GetDataID,
137
142
  shouldProcessClientComponents: ?boolean,
143
+ log: ?LogFunction,
144
+ useExecTimeResolvers: ?boolean,
138
145
  ) {
139
146
  this._getSourceForActor = getSourceForActor;
140
147
  this._getTargetForActor = getTargetForActor;
@@ -144,6 +151,7 @@ class DataChecker {
144
151
  const [mutator, recordSourceProxy] = this._getMutatorAndRecordProxyForActor(
145
152
  defaultActorIdentifier,
146
153
  );
154
+ this._useExecTimeResolvers = useExecTimeResolvers ?? false;
147
155
  this._mostRecentlyInvalidatedAt = null;
148
156
  this._handlers = handlers;
149
157
  this._mutator = mutator;
@@ -152,6 +160,7 @@ class DataChecker {
152
160
  this._recordWasMissing = false;
153
161
  this._variables = variables;
154
162
  this._shouldProcessClientComponents = shouldProcessClientComponents;
163
+ this._log = log;
155
164
  }
156
165
 
157
166
  _getMutatorAndRecordProxyForActor(
@@ -170,6 +179,7 @@ class DataChecker {
170
179
  this._getDataID,
171
180
  undefined,
172
181
  this._handlers,
182
+ this._log,
173
183
  );
174
184
  tuple = [mutator, recordSourceProxy];
175
185
  this._mutatorRecordSourceProxyCache.set(actorIdentifier, tuple);
@@ -449,13 +459,15 @@ class DataChecker {
449
459
  this._traverseSelections(selection.fragment.selections, dataID);
450
460
  break;
451
461
  case 'RelayResolver':
452
- this._checkResolver(selection, dataID);
453
- break;
454
462
  case 'RelayLiveResolver':
455
- this._checkResolver(selection, dataID);
463
+ if (!this._useExecTimeResolvers) {
464
+ this._checkResolver(selection, dataID);
465
+ }
456
466
  break;
457
467
  case 'ClientEdgeToClientObject':
458
- this._checkResolver(selection.backingField, dataID);
468
+ if (!this._useExecTimeResolvers) {
469
+ this._checkResolver(selection.backingField, dataID);
470
+ }
459
471
  break;
460
472
  default:
461
473
  (selection: empty);
@@ -138,6 +138,7 @@ class Executor<TMutation: MutationParameters> {
138
138
  _operationTracker: OperationTracker;
139
139
  _operationUpdateEpochs: Map<string, number>;
140
140
  _optimisticUpdates: null | Array<OptimisticUpdate<TMutation>>;
141
+ _useExecTimeResolvers: boolean;
141
142
  _pendingModulePayloadsCount: number;
142
143
  +_getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue;
143
144
  _shouldProcessClientComponents: ?boolean;
@@ -158,6 +159,7 @@ class Executor<TMutation: MutationParameters> {
158
159
  +_isSubscriptionOperation: boolean;
159
160
  +_seenActors: Set<ActorIdentifier>;
160
161
  _normalizeResponse: NormalizeResponseFunction;
162
+ _execTimeResolverResponseComplete: boolean;
161
163
 
162
164
  constructor({
163
165
  actorIdentifier,
@@ -193,6 +195,12 @@ class Executor<TMutation: MutationParameters> {
193
195
  this._operationTracker = operationTracker;
194
196
  this._operationUpdateEpochs = new Map();
195
197
  this._optimisticUpdates = null;
198
+ this._useExecTimeResolvers =
199
+ this._operation.request.node.operation.use_exec_time_resolvers ??
200
+ this._operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() ===
201
+ true ??
202
+ false;
203
+ this._execTimeResolverResponseComplete = false;
196
204
  this._pendingModulePayloadsCount = 0;
197
205
  this._getPublishQueue = getPublishQueue;
198
206
  this._scheduler = scheduler;
@@ -245,6 +253,12 @@ class Executor<TMutation: MutationParameters> {
245
253
  cacheConfig: this._operation.request.cacheConfig ?? {},
246
254
  });
247
255
  },
256
+ unsubscribe: () => {
257
+ this._log({
258
+ name: 'execute.unsubscribe',
259
+ executeId: this._executeId,
260
+ });
261
+ },
248
262
  });
249
263
 
250
264
  if (
@@ -293,7 +307,7 @@ class Executor<TMutation: MutationParameters> {
293
307
  }
294
308
 
295
309
  _updateActiveState(): void {
296
- let activeState;
310
+ let activeState: ActiveState;
297
311
  switch (this._state) {
298
312
  case 'started': {
299
313
  activeState = 'active';
@@ -309,7 +323,11 @@ class Executor<TMutation: MutationParameters> {
309
323
  }
310
324
  case 'loading_final': {
311
325
  activeState =
312
- this._pendingModulePayloadsCount > 0 ? 'active' : 'inactive';
326
+ this._pendingModulePayloadsCount > 0 ||
327
+ (this._useExecTimeResolvers &&
328
+ !this._execTimeResolverResponseComplete)
329
+ ? 'active'
330
+ : 'inactive';
313
331
  break;
314
332
  }
315
333
  default:
@@ -510,9 +528,10 @@ class Executor<TMutation: MutationParameters> {
510
528
  return;
511
529
  }
512
530
 
513
- const [nonIncrementalResponses, incrementalResponses] =
531
+ const [nonIncrementalResponses, incrementalResponses, normalizedResponses] =
514
532
  partitionGraphQLResponses(responsesWithData);
515
533
  const hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
534
+ const hasNormalizedResponses = normalizedResponses.length > 0;
516
535
 
517
536
  // In theory this doesn't preserve the ordering of the batch.
518
537
  // The idea is that a batch is always:
@@ -547,6 +566,37 @@ class Executor<TMutation: MutationParameters> {
547
566
  this._processPayloadFollowups(payloadFollowups);
548
567
  }
549
568
 
569
+ if (hasNormalizedResponses) {
570
+ const payloadFollowups = [];
571
+ for (let i = 0; i < normalizedResponses.length; i++) {
572
+ const response = normalizedResponses[i];
573
+ const source = new RelayRecordSource(
574
+ response.data as $FlowExpectedError,
575
+ );
576
+ const isFinal = response.extensions?.is_final === true;
577
+ const payload: RelayResponsePayload = {
578
+ errors: [],
579
+ fieldPayloads: [],
580
+ followupPayloads: [],
581
+ incrementalPlaceholders: [],
582
+ isFinal,
583
+ source,
584
+ };
585
+ this._getPublishQueueAndSaveActor().commitPayload(
586
+ this._operation,
587
+ payload,
588
+ this._updater,
589
+ );
590
+ payloadFollowups.push(payload);
591
+ this._execTimeResolverResponseComplete = isFinal;
592
+ if (isFinal) {
593
+ // Need to update the active state to mark the query as inactive,
594
+ // incase server payloads have completed
595
+ this._updateActiveState();
596
+ }
597
+ }
598
+ }
599
+
550
600
  if (incrementalResponses.length > 0) {
551
601
  const payloadFollowups =
552
602
  this._processIncrementalResponses(incrementalResponses);
@@ -572,7 +622,9 @@ class Executor<TMutation: MutationParameters> {
572
622
  // the publish queue here, which will later be passed to the store (via
573
623
  // notify) to indicate that this operation caused the store to update
574
624
  const updatedOwners = this._runPublishQueue(
575
- hasNonIncrementalResponses ? this._operation : undefined,
625
+ hasNonIncrementalResponses || hasNormalizedResponses
626
+ ? this._operation
627
+ : undefined,
576
628
  );
577
629
 
578
630
  if (hasNonIncrementalResponses) {
@@ -606,10 +658,12 @@ class Executor<TMutation: MutationParameters> {
606
658
  {
607
659
  actorIdentifier: this._actorIdentifier,
608
660
  getDataID: this._getDataID,
661
+ log: this._log,
609
662
  path: [],
610
663
  shouldProcessClientComponents: this._shouldProcessClientComponents,
611
664
  treatMissingFieldsAsNull,
612
665
  },
666
+ this._useExecTimeResolvers,
613
667
  );
614
668
  validateOptimisticResponsePayload(payload);
615
669
  optimisticUpdates.push({
@@ -708,17 +762,26 @@ class Executor<TMutation: MutationParameters> {
708
762
  followupPayload.dataID,
709
763
  variables,
710
764
  );
765
+ const nextResponse: GraphQLResponseWithData = {
766
+ data: followupPayload.data,
767
+ // `is_final` flag needs to be set for processing nested defer and 3D
768
+ // when the server doesn't support streaming
769
+ extensions:
770
+ this._state === 'loading_final' ? {is_final: true} : undefined,
771
+ };
711
772
  return this._normalizeResponse(
712
- {data: followupPayload.data},
773
+ nextResponse,
713
774
  selector,
714
775
  followupPayload.typeName,
715
776
  {
716
777
  actorIdentifier: this._actorIdentifier,
717
778
  getDataID: this._getDataID,
779
+ log: this._log,
718
780
  path: followupPayload.path,
719
781
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
720
782
  shouldProcessClientComponents: this._shouldProcessClientComponents,
721
783
  },
784
+ this._useExecTimeResolvers,
722
785
  );
723
786
  }
724
787
 
@@ -797,10 +860,12 @@ class Executor<TMutation: MutationParameters> {
797
860
  {
798
861
  actorIdentifier: this._actorIdentifier,
799
862
  getDataID: this._getDataID,
863
+ log: this._log,
800
864
  path: [],
801
865
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
802
866
  shouldProcessClientComponents: this._shouldProcessClientComponents,
803
867
  },
868
+ this._useExecTimeResolvers,
804
869
  );
805
870
  this._getPublishQueueAndSaveActor().commitPayload(
806
871
  this._operation,
@@ -875,7 +940,8 @@ class Executor<TMutation: MutationParameters> {
875
940
  placeholder.label,
876
941
  placeholder.path,
877
942
  placeholder,
878
- {data: placeholder.data},
943
+ // `is_final` flag needs to be set for processing nested defer payloads
944
+ {data: placeholder.data, extensions: {is_final: true}},
879
945
  ),
880
946
  );
881
947
  }
@@ -889,7 +955,14 @@ class Executor<TMutation: MutationParameters> {
889
955
  }
890
956
 
891
957
  _maybeCompleteSubscriptionOperationTracking() {
892
- if (!this._isSubscriptionOperation) {
958
+ if (
959
+ !this._isSubscriptionOperation &&
960
+ !(
961
+ this._useExecTimeResolvers &&
962
+ this._execTimeResolverResponseComplete &&
963
+ this._state === 'loading_final'
964
+ )
965
+ ) {
893
966
  return;
894
967
  }
895
968
  if (
@@ -1253,10 +1326,12 @@ class Executor<TMutation: MutationParameters> {
1253
1326
  {
1254
1327
  actorIdentifier: this._actorIdentifier,
1255
1328
  getDataID: this._getDataID,
1329
+ log: this._log,
1256
1330
  path: placeholder.path,
1257
1331
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
1258
1332
  shouldProcessClientComponents: this._shouldProcessClientComponents,
1259
1333
  },
1334
+ this._useExecTimeResolvers,
1260
1335
  );
1261
1336
  this._getPublishQueueAndSaveActor().commitPayload(
1262
1337
  this._operation,
@@ -1477,13 +1552,20 @@ class Executor<TMutation: MutationParameters> {
1477
1552
  record: nextParentRecord,
1478
1553
  fieldPayloads,
1479
1554
  });
1480
- const relayPayload = this._normalizeResponse(response, selector, typeName, {
1481
- actorIdentifier: this._actorIdentifier,
1482
- getDataID: this._getDataID,
1483
- path: [...normalizationPath, responseKey, String(itemIndex)],
1484
- treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
1485
- shouldProcessClientComponents: this._shouldProcessClientComponents,
1486
- });
1555
+ const relayPayload = this._normalizeResponse(
1556
+ response,
1557
+ selector,
1558
+ typeName,
1559
+ {
1560
+ actorIdentifier: this._actorIdentifier,
1561
+ getDataID: this._getDataID,
1562
+ log: this._log,
1563
+ path: [...normalizationPath, responseKey, String(itemIndex)],
1564
+ treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
1565
+ shouldProcessClientComponents: this._shouldProcessClientComponents,
1566
+ },
1567
+ this._useExecTimeResolvers,
1568
+ );
1487
1569
  return {
1488
1570
  fieldPayloads,
1489
1571
  itemID,
@@ -1586,9 +1668,11 @@ function partitionGraphQLResponses(
1586
1668
  ): [
1587
1669
  $ReadOnlyArray<GraphQLResponseWithData>,
1588
1670
  $ReadOnlyArray<IncrementalGraphQLResponse>,
1671
+ $ReadOnlyArray<GraphQLResponseWithData>,
1589
1672
  ] {
1590
1673
  const nonIncrementalResponses: Array<GraphQLResponseWithData> = [];
1591
1674
  const incrementalResponses: Array<IncrementalGraphQLResponse> = [];
1675
+ const normalizedResponses: Array<GraphQLResponseWithData> = [];
1592
1676
  responses.forEach(response => {
1593
1677
  if (response.path != null || response.label != null) {
1594
1678
  const {label, path} = response;
@@ -1606,11 +1690,13 @@ function partitionGraphQLResponses(
1606
1690
  path,
1607
1691
  response,
1608
1692
  });
1693
+ } else if (response.extensions?.is_normalized === true) {
1694
+ normalizedResponses.push(response);
1609
1695
  } else {
1610
1696
  nonIncrementalResponses.push(response);
1611
1697
  }
1612
1698
  });
1613
- return [nonIncrementalResponses, incrementalResponses];
1699
+ return [nonIncrementalResponses, incrementalResponses, normalizedResponses];
1614
1700
  }
1615
1701
 
1616
1702
  function stableStringify(value: mixed): string {
@@ -222,10 +222,10 @@ export class GraphModeNormalizer {
222
222
  $streamID,
223
223
  __id: dataID,
224
224
  __typename: ROOT_TYPE,
225
- };
225
+ } as RecordChunk;
226
226
  yield {
227
227
  $kind: 'Complete',
228
- };
228
+ } as CompleteChunk;
229
229
  }
230
230
 
231
231
  *_flushFields(
@@ -247,9 +247,9 @@ export class GraphModeNormalizer {
247
247
  __typename: typename,
248
248
  __id: cacheKey,
249
249
  $streamID,
250
- };
250
+ } as RecordChunk;
251
251
  } else if (Object.keys(fields).length > 0) {
252
- yield {...fields, $kind: 'Extend', $streamID};
252
+ yield {...fields, $kind: 'Extend', $streamID} as ExtendChunk;
253
253
  }
254
254
  return $streamID;
255
255
  }
@@ -58,6 +58,7 @@ const defaultGetDataID = require('./defaultGetDataID');
58
58
  const defaultRelayFieldLogger = require('./defaultRelayFieldLogger');
59
59
  const normalizeResponse = require('./normalizeResponse');
60
60
  const OperationExecutor = require('./OperationExecutor');
61
+ const RelayModernStore = require('./RelayModernStore');
61
62
  const RelayPublishQueue = require('./RelayPublishQueue');
62
63
  const RelayRecordSource = require('./RelayRecordSource');
63
64
  const invariant = require('invariant');
@@ -71,7 +72,7 @@ export type EnvironmentConfig = {
71
72
  +network: INetwork,
72
73
  +normalizeResponse?: ?NormalizeResponseFunction,
73
74
  +scheduler?: ?TaskScheduler,
74
- +store: Store,
75
+ +store?: Store,
75
76
  +missingFieldHandlers?: ?$ReadOnlyArray<MissingFieldHandler>,
76
77
  +operationTracker?: ?OperationTracker,
77
78
  +getDataID?: ?GetDataID,
@@ -118,6 +119,15 @@ class RelayModernEnvironment implements IEnvironment {
118
119
  );
119
120
  }
120
121
  }
122
+ const store =
123
+ config.store ??
124
+ new RelayModernStore(new RelayRecordSource(), {
125
+ log: config.log,
126
+ operationLoader: config.operationLoader,
127
+ getDataID: config.getDataID,
128
+ shouldProcessClientComponents: config.shouldProcessClientComponents,
129
+ });
130
+
121
131
  this.__log = config.log ?? emptyFunction;
122
132
  this.relayFieldLogger = config.relayFieldLogger ?? defaultRelayFieldLogger;
123
133
  this._defaultRenderPolicy =
@@ -128,13 +138,14 @@ class RelayModernEnvironment implements IEnvironment {
128
138
  this._getDataID = config.getDataID ?? defaultGetDataID;
129
139
  this._missingFieldHandlers = config.missingFieldHandlers ?? [];
130
140
  this._publishQueue = new RelayPublishQueue(
131
- config.store,
141
+ store,
132
142
  config.handlerProvider ?? RelayDefaultHandlerProvider,
133
143
  this._getDataID,
134
144
  this._missingFieldHandlers,
145
+ this.__log,
135
146
  );
136
147
  this._scheduler = config.scheduler ?? null;
137
- this._store = config.store;
148
+ this._store = store;
138
149
  this.options = config.options;
139
150
  this._isServer = config.isServer ?? false;
140
151
  this._normalizeResponse = config.normalizeResponse ?? normalizeResponse;
@@ -327,13 +338,18 @@ class RelayModernEnvironment implements IEnvironment {
327
338
  operation: OperationDescriptor,
328
339
  }): RelayObservable<GraphQLResponse> {
329
340
  return this._execute({
330
- createSource: () =>
331
- this.getNetwork().execute(
341
+ createSource: () => {
342
+ return this.getNetwork().execute(
332
343
  operation.request.node.params,
333
344
  operation.request.variables,
334
345
  operation.request.cacheConfig || {},
335
346
  null,
336
- ),
347
+ undefined,
348
+ undefined,
349
+ undefined,
350
+ () => this.check(operation),
351
+ );
352
+ },
337
353
  isClientPayload: false,
338
354
  operation,
339
355
  optimisticConfig: null,
@@ -14,7 +14,7 @@
14
14
  import type {ConcreteRequest} from '../util/RelayConcreteNode';
15
15
  import type {Disposable, Variables} from '../util/RelayRuntimeTypes';
16
16
  import type {
17
- ErrorResponseFields,
17
+ FieldErrors,
18
18
  FragmentMap,
19
19
  FragmentSpecResolver,
20
20
  FragmentSpecResults,
@@ -228,7 +228,7 @@ class SelectorResolver {
228
228
  _data: ?SelectorData;
229
229
  _environment: IEnvironment;
230
230
  _isMissingData: boolean;
231
- _errorResponseFields: ?ErrorResponseFields;
231
+ _fieldErrors: ?FieldErrors;
232
232
  _rootIsQueryRenderer: boolean;
233
233
  _selector: SingularReaderSelector;
234
234
  _subscription: ?Disposable;
@@ -244,7 +244,7 @@ class SelectorResolver {
244
244
  this._callback = callback;
245
245
  this._data = snapshot.data;
246
246
  this._isMissingData = snapshot.isMissingData;
247
- this._errorResponseFields = snapshot.errorResponseFields;
247
+ this._fieldErrors = snapshot.fieldErrors;
248
248
  this._environment = environment;
249
249
  this._rootIsQueryRenderer = rootIsQueryRenderer;
250
250
  this._selector = selector;
@@ -325,7 +325,7 @@ class SelectorResolver {
325
325
  }
326
326
  }
327
327
  }
328
- handlePotentialSnapshotErrors(this._environment, this._errorResponseFields);
328
+ handlePotentialSnapshotErrors(this._environment, this._fieldErrors);
329
329
  return this._data;
330
330
  }
331
331
 
@@ -340,7 +340,7 @@ class SelectorResolver {
340
340
  const snapshot = this._environment.lookup(selector);
341
341
  this._data = recycleNodesInto(this._data, snapshot.data);
342
342
  this._isMissingData = snapshot.isMissingData;
343
- this._errorResponseFields = snapshot.errorResponseFields;
343
+ this._fieldErrors = snapshot.fieldErrors;
344
344
  this._selector = selector;
345
345
  this._subscription = this._environment.subscribe(snapshot, this._onChange);
346
346
  }
@@ -376,7 +376,7 @@ class SelectorResolver {
376
376
  _onChange = (snapshot: Snapshot): void => {
377
377
  this._data = snapshot.data;
378
378
  this._isMissingData = snapshot.isMissingData;
379
- this._errorResponseFields = snapshot.errorResponseFields;
379
+ this._fieldErrors = snapshot.fieldErrors;
380
380
  this._callback();
381
381
  };
382
382
  }
@@ -340,6 +340,7 @@ function getVariablesFromObject(
340
340
  const fragment = fragments[key];
341
341
  const item = object[key];
342
342
  const itemVariables = getVariablesFromFragment(fragment, item);
343
+ // $FlowFixMe[unsafe-object-assign]
343
344
  Object.assign(variables, itemVariables);
344
345
  }
345
346
  }
@@ -397,6 +398,7 @@ function getVariablesFromPluralFragment(
397
398
  if (value != null) {
398
399
  const itemVariables = getVariablesFromSingularFragment(fragment, value);
399
400
  if (itemVariables != null) {
401
+ // $FlowFixMe[unsafe-object-assign]
400
402
  Object.assign(variables, itemVariables);
401
403
  }
402
404
  }