relay-runtime 18.1.0 → 19.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 (92) hide show
  1. package/experimental.js +1 -1
  2. package/experimental.js.flow +22 -9
  3. package/handlers/connection/ConnectionHandler.js.flow +6 -1
  4. package/index.js +1 -1
  5. package/index.js.flow +4 -0
  6. package/lib/experimental.js +5 -2
  7. package/lib/handlers/connection/ConnectionHandler.js +1 -1
  8. package/lib/index.js +3 -0
  9. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +1 -1
  10. package/lib/mutations/RelayRecordProxy.js +14 -3
  11. package/lib/mutations/RelayRecordSourceMutator.js +17 -0
  12. package/lib/mutations/RelayRecordSourceProxy.js +2 -1
  13. package/lib/mutations/createUpdatableProxy.js +1 -1
  14. package/lib/mutations/validateMutation.js +2 -2
  15. package/lib/network/RelayObservable.js +1 -3
  16. package/lib/network/wrapNetworkWithLogObserver.js +2 -2
  17. package/lib/query/fetchQuery.js +1 -1
  18. package/lib/store/DataChecker.js +4 -5
  19. package/lib/store/OperationExecutor.js +11 -0
  20. package/lib/store/RelayModernEnvironment.js +13 -4
  21. package/lib/store/RelayModernFragmentSpecResolver.js +4 -4
  22. package/lib/store/RelayModernStore.js +43 -21
  23. package/lib/store/RelayPublishQueue.js +11 -15
  24. package/lib/store/RelayReader.js +244 -183
  25. package/lib/store/RelayReferenceMarker.js +3 -4
  26. package/lib/store/RelayResponseNormalizer.js +48 -26
  27. package/lib/store/RelayStoreSubscriptions.js +2 -2
  28. package/lib/store/RelayStoreUtils.js +8 -0
  29. package/lib/store/ResolverCache.js +1 -165
  30. package/lib/store/ResolverFragments.js +2 -2
  31. package/lib/store/createRelayLoggingContext.js +17 -0
  32. package/lib/store/generateTypenamePrefixedDataID.js +9 -0
  33. package/lib/store/live-resolvers/LiveResolverCache.js +5 -10
  34. package/lib/store/live-resolvers/resolverDataInjector.js +4 -4
  35. package/lib/store/observeFragmentExperimental.js +60 -13
  36. package/lib/store/observeQueryExperimental.js +21 -0
  37. package/lib/util/RelayFeatureFlags.js +7 -2
  38. package/lib/util/handlePotentialSnapshotErrors.js +12 -9
  39. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +1 -0
  40. package/mutations/RelayRecordProxy.js.flow +30 -3
  41. package/mutations/RelayRecordSourceMutator.js.flow +27 -0
  42. package/mutations/RelayRecordSourceProxy.js.flow +4 -0
  43. package/mutations/createUpdatableProxy.js.flow +1 -1
  44. package/mutations/validateMutation.js.flow +3 -3
  45. package/network/RelayNetworkTypes.js.flow +3 -0
  46. package/network/RelayObservable.js.flow +1 -5
  47. package/network/wrapNetworkWithLogObserver.js.flow +19 -1
  48. package/package.json +1 -1
  49. package/query/fetchQuery.js.flow +1 -1
  50. package/store/DataChecker.js.flow +5 -2
  51. package/store/OperationExecutor.js.flow +12 -1
  52. package/store/RelayExperimentalGraphResponseTransform.js.flow +4 -4
  53. package/store/RelayModernEnvironment.js.flow +22 -6
  54. package/store/RelayModernFragmentSpecResolver.js.flow +6 -6
  55. package/store/RelayModernRecord.js.flow +1 -1
  56. package/store/RelayModernSelector.js.flow +2 -0
  57. package/store/RelayModernStore.js.flow +74 -27
  58. package/store/RelayOptimisticRecordSource.js.flow +2 -0
  59. package/store/RelayPublishQueue.js.flow +32 -21
  60. package/store/RelayReader.js.flow +400 -145
  61. package/store/RelayRecordState.js.flow +1 -1
  62. package/store/RelayReferenceMarker.js.flow +3 -4
  63. package/store/RelayResponseNormalizer.js.flow +94 -62
  64. package/store/RelayStoreSubscriptions.js.flow +2 -2
  65. package/store/RelayStoreTypes.js.flow +45 -15
  66. package/store/RelayStoreUtils.js.flow +30 -1
  67. package/store/ResolverCache.js.flow +2 -271
  68. package/store/ResolverFragments.js.flow +5 -3
  69. package/store/StoreInspector.js.flow +5 -0
  70. package/store/createRelayContext.js.flow +3 -2
  71. package/store/createRelayLoggingContext.js.flow +46 -0
  72. package/store/generateTypenamePrefixedDataID.js.flow +25 -0
  73. package/store/live-resolvers/LiveResolverCache.js.flow +5 -10
  74. package/store/live-resolvers/resolverDataInjector.js.flow +10 -6
  75. package/store/observeFragmentExperimental.js.flow +82 -28
  76. package/store/observeQueryExperimental.js.flow +61 -0
  77. package/store/waitForFragmentExperimental.js.flow +4 -3
  78. package/util/NormalizationNode.js.flow +10 -1
  79. package/util/ReaderNode.js.flow +9 -3
  80. package/util/RelayConcreteNode.js.flow +3 -1
  81. package/util/RelayError.js.flow +1 -0
  82. package/util/RelayFeatureFlags.js.flow +31 -7
  83. package/util/RelayRuntimeTypes.js.flow +17 -3
  84. package/util/getPaginationVariables.js.flow +2 -0
  85. package/util/handlePotentialSnapshotErrors.js.flow +24 -12
  86. package/util/registerEnvironmentWithDevTools.js.flow +4 -2
  87. package/util/withProvidedVariables.js.flow +1 -0
  88. package/util/withStartAndDuration.js.flow +3 -0
  89. package/relay-runtime-experimental.js +0 -4
  90. package/relay-runtime-experimental.min.js +0 -9
  91. package/relay-runtime.js +0 -4
  92. package/relay-runtime.min.js +0 -9
@@ -18,30 +18,13 @@ import type {
18
18
  import type {DataID, Variables} from '../util/RelayRuntimeTypes';
19
19
  import type {
20
20
  DataIDSet,
21
- ErrorResponseFields,
22
- MutableRecordSource,
23
- Record,
21
+ FieldErrors,
24
22
  SingularReaderSelector,
25
23
  Snapshot,
26
24
  } from './RelayStoreTypes';
27
25
 
28
- const recycleNodesInto = require('../util/recycleNodesInto');
29
26
  const {RELAY_LIVE_RESOLVER} = require('../util/RelayConcreteNode');
30
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
31
- const shallowFreeze = require('../util/shallowFreeze');
32
- const {generateClientID} = require('./ClientID');
33
- const RelayModernRecord = require('./RelayModernRecord');
34
- const {
35
- RELAY_RESOLVER_ERROR_KEY,
36
- RELAY_RESOLVER_INVALIDATION_KEY,
37
- RELAY_RESOLVER_SNAPSHOT_KEY,
38
- RELAY_RESOLVER_VALUE_KEY,
39
- getStorageKey,
40
- } = require('./RelayStoreUtils');
41
27
  const invariant = require('invariant');
42
- const warning = require('warning');
43
-
44
- type ResolverID = string;
45
28
 
46
29
  export type EvaluationResult<T> = {
47
30
  resolverResult: ?T,
@@ -52,7 +35,7 @@ export type EvaluationResult<T> = {
52
35
  export type ResolverFragmentResult = {
53
36
  data: mixed,
54
37
  isMissingData: boolean,
55
- errorResponseFields: ?ErrorResponseFields,
38
+ fieldErrors: ?FieldErrors,
56
39
  };
57
40
 
58
41
  export type GetDataForResolverFragmentFn =
@@ -80,9 +63,6 @@ export interface ResolverCache {
80
63
  notifyUpdatedSubscribers(updatedDataIDs: DataIDSet): void;
81
64
  }
82
65
 
83
- // $FlowFixMe[unclear-type] - will always be empty
84
- const emptySet: $ReadOnlySet<any> = new Set();
85
-
86
66
  class NoopResolverCache implements ResolverCache {
87
67
  readFromCacheOrEvaluate<T>(
88
68
  recordID: DataID,
@@ -116,255 +96,6 @@ class NoopResolverCache implements ResolverCache {
116
96
  notifyUpdatedSubscribers(updatedDataIDs: DataIDSet): void {}
117
97
  }
118
98
 
119
- function addDependencyEdge(
120
- edges: Map<ResolverID, Set<DataID>> | Map<DataID, Set<ResolverID>>,
121
- from: ResolverID | DataID,
122
- to: ResolverID | DataID,
123
- ): void {
124
- let set = edges.get(from);
125
- if (!set) {
126
- set = new Set();
127
- edges.set(from, set);
128
- }
129
- set.add(to);
130
- }
131
-
132
- class RecordResolverCache implements ResolverCache {
133
- _resolverIDToRecordIDs: Map<ResolverID, Set<DataID>>;
134
- _recordIDToResolverIDs: Map<DataID, Set<ResolverID>>;
135
-
136
- _getRecordSource: () => MutableRecordSource;
137
-
138
- constructor(getRecordSource: () => MutableRecordSource) {
139
- this._resolverIDToRecordIDs = new Map();
140
- this._recordIDToResolverIDs = new Map();
141
- this._getRecordSource = getRecordSource;
142
- }
143
-
144
- readFromCacheOrEvaluate<T>(
145
- recordID: DataID,
146
- field: ReaderRelayResolver | ReaderRelayLiveResolver,
147
- variables: Variables,
148
- evaluate: () => EvaluationResult<T>,
149
- getDataForResolverFragment: GetDataForResolverFragmentFn,
150
- ): [
151
- ?T /* Answer */,
152
- ?DataID /* Seen record */,
153
- ?Error,
154
- ?Snapshot,
155
- ?DataID /* ID of record containing a suspended Live field */,
156
- ?DataIDSet /** Set of dirty records after read */,
157
- ] {
158
- const recordSource = this._getRecordSource();
159
-
160
- // NOTE: Be very careful with `record` in this scope. After `evaluate` has
161
- // been called, the `record` we have here may have been replaced in the
162
- // Relay store with a new record containing new information about nested
163
- // resolvers on this parent record.
164
- const record = recordSource.get(recordID);
165
- invariant(record != null, 'We expect record to exist in the store.');
166
-
167
- const storageKey = getStorageKey(field, variables);
168
- let linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
169
- let linkedRecord = linkedID == null ? null : recordSource.get(linkedID);
170
- if (
171
- linkedRecord == null ||
172
- this._isInvalid(linkedRecord, getDataForResolverFragment)
173
- ) {
174
- // Cache miss; evaluate the selector and store the result in a new record:
175
- linkedID = linkedID ?? generateClientID(recordID, storageKey);
176
- linkedRecord = RelayModernRecord.create(linkedID, '__RELAY_RESOLVER__');
177
-
178
- const evaluationResult = evaluate();
179
- shallowFreeze(evaluationResult.resolverResult);
180
- RelayModernRecord.setValue(
181
- linkedRecord,
182
- RELAY_RESOLVER_VALUE_KEY,
183
- evaluationResult.resolverResult,
184
- );
185
- RelayModernRecord.setValue(
186
- linkedRecord,
187
- RELAY_RESOLVER_SNAPSHOT_KEY,
188
- evaluationResult.snapshot,
189
- );
190
- RelayModernRecord.setValue(
191
- linkedRecord,
192
- RELAY_RESOLVER_ERROR_KEY,
193
- evaluationResult.error,
194
- );
195
- recordSource.set(linkedID, linkedRecord);
196
-
197
- // Link the resolver value record to the resolver field of the record being read:
198
-
199
- // Note: We get a fresh instance of the parent record from the record
200
- // source, because it may have been updated when we traversed into child
201
- // resolvers.
202
- const currentRecord = recordSource.get(recordID);
203
- invariant(
204
- currentRecord != null,
205
- 'Expected the parent record to still be in the record source.',
206
- );
207
- const nextRecord = RelayModernRecord.clone(currentRecord);
208
- RelayModernRecord.setLinkedRecordID(nextRecord, storageKey, linkedID);
209
- recordSource.set(recordID, nextRecord);
210
-
211
- if (field.fragment != null) {
212
- // Put records observed by the resolver into the dependency graph:
213
- const fragmentStorageKey = getStorageKey(field.fragment, variables);
214
- const resolverID = generateClientID(recordID, fragmentStorageKey);
215
- addDependencyEdge(this._resolverIDToRecordIDs, resolverID, linkedID);
216
- addDependencyEdge(this._recordIDToResolverIDs, recordID, resolverID);
217
- const seenRecordIds = evaluationResult.snapshot?.seenRecords;
218
- if (seenRecordIds != null) {
219
- for (const seenRecordID of seenRecordIds) {
220
- addDependencyEdge(
221
- this._recordIDToResolverIDs,
222
- seenRecordID,
223
- resolverID,
224
- );
225
- }
226
- }
227
- }
228
- }
229
-
230
- // $FlowFixMe[incompatible-type] - will always be empty
231
- const answer: T = RelayModernRecord.getValue(
232
- linkedRecord,
233
- RELAY_RESOLVER_VALUE_KEY,
234
- );
235
-
236
- // $FlowFixMe[incompatible-type] - casting mixed
237
- const snapshot: ?Snapshot = RelayModernRecord.getValue(
238
- linkedRecord,
239
- RELAY_RESOLVER_SNAPSHOT_KEY,
240
- );
241
-
242
- // $FlowFixMe[incompatible-type] - casting mixed
243
- const error: ?Error = RelayModernRecord.getValue(
244
- linkedRecord,
245
- RELAY_RESOLVER_ERROR_KEY,
246
- );
247
-
248
- return [answer, linkedID, error, snapshot, undefined, undefined];
249
- }
250
-
251
- invalidateDataIDs(
252
- updatedDataIDs: Set<DataID>, // Mutated in place
253
- ): void {
254
- const recordSource = this._getRecordSource();
255
- const visited: Set<string> = new Set();
256
- const recordsToVisit = Array.from(updatedDataIDs);
257
- while (recordsToVisit.length) {
258
- const recordID = recordsToVisit.pop();
259
- // $FlowFixMe[incompatible-call]
260
- updatedDataIDs.add(recordID);
261
- // $FlowFixMe[incompatible-call]
262
- for (const fragment of this._recordIDToResolverIDs.get(recordID) ??
263
- emptySet) {
264
- if (!visited.has(fragment)) {
265
- for (const anotherRecordID of this._resolverIDToRecordIDs.get(
266
- fragment,
267
- ) ?? emptySet) {
268
- this._markInvalidatedResolverRecord(
269
- anotherRecordID,
270
- recordSource,
271
- updatedDataIDs,
272
- );
273
- if (!visited.has(anotherRecordID)) {
274
- recordsToVisit.push(anotherRecordID);
275
- }
276
- }
277
- }
278
- }
279
- }
280
- }
281
-
282
- _markInvalidatedResolverRecord(
283
- dataID: DataID,
284
- recordSource: MutableRecordSource, // Written to
285
- updatedDataIDs: Set<DataID>, // Mutated in place
286
- ) {
287
- const record = recordSource.get(dataID);
288
- if (!record) {
289
- warning(
290
- false,
291
- 'Expected a resolver record with ID %s, but it was missing.',
292
- dataID,
293
- );
294
- return;
295
- }
296
- const nextRecord = RelayModernRecord.clone(record);
297
- RelayModernRecord.setValue(
298
- nextRecord,
299
- RELAY_RESOLVER_INVALIDATION_KEY,
300
- true,
301
- );
302
- recordSource.set(dataID, nextRecord);
303
- }
304
-
305
- _isInvalid(
306
- record: Record,
307
- getDataForResolverFragment: GetDataForResolverFragmentFn,
308
- ): boolean {
309
- if (!RelayModernRecord.getValue(record, RELAY_RESOLVER_INVALIDATION_KEY)) {
310
- return false;
311
- }
312
- // $FlowFixMe[incompatible-type] - storing values in records is not typed
313
- const snapshot: ?Snapshot = RelayModernRecord.getValue(
314
- record,
315
- RELAY_RESOLVER_SNAPSHOT_KEY,
316
- );
317
- const originalInputs = snapshot?.data;
318
- const readerSelector: ?SingularReaderSelector = snapshot?.selector;
319
- if (originalInputs == null || readerSelector == null) {
320
- warning(
321
- false,
322
- 'Expected previous inputs and reader selector on resolver record with ID %s, but they were missing.',
323
- RelayModernRecord.getDataID(record),
324
- );
325
- return true;
326
- }
327
-
328
- const {data: latestValues} = getDataForResolverFragment(readerSelector);
329
- const recycled = recycleNodesInto(originalInputs, latestValues);
330
- if (recycled !== originalInputs) {
331
- return true;
332
- }
333
-
334
- if (RelayFeatureFlags.MARK_RESOLVER_VALUES_AS_CLEAN_AFTER_FRAGMENT_REREAD) {
335
- // This record does not need to be recomputed, we can reuse the cached value.
336
- // For subsequent reads we can mark this record as "clean" so that they will
337
- // not need to re-read the fragment.
338
- const nextRecord = RelayModernRecord.clone(record);
339
- RelayModernRecord.setValue(
340
- nextRecord,
341
- RELAY_RESOLVER_INVALIDATION_KEY,
342
- false,
343
- );
344
-
345
- const recordSource = this._getRecordSource();
346
- recordSource.set(RelayModernRecord.getDataID(record), nextRecord);
347
- }
348
-
349
- return false;
350
- }
351
-
352
- ensureClientRecord(id: string, typename: string): DataID {
353
- invariant(
354
- false,
355
- 'Client Edges to Client Objects are not supported in this version of Relay Store',
356
- );
357
- }
358
-
359
- notifyUpdatedSubscribers(updatedDataIDs: DataIDSet): void {
360
- invariant(
361
- false,
362
- 'Processing @outputType records is not supported in this version of Relay Store',
363
- );
364
- }
365
- }
366
-
367
99
  module.exports = {
368
100
  NoopResolverCache,
369
- RecordResolverCache,
370
101
  };
@@ -112,12 +112,14 @@ function readFragment(
112
112
  fragmentSelector.kind === 'SingularReaderSelector',
113
113
  `Expected a singular reader selector for the fragment of the resolver ${fragmentNode.name}, but it was plural.`,
114
114
  );
115
- const {data, isMissingData, errorResponseFields} =
116
- context.getDataForResolverFragment(fragmentSelector, fragmentKey);
115
+ const {data, isMissingData, fieldErrors} = context.getDataForResolverFragment(
116
+ fragmentSelector,
117
+ fragmentKey,
118
+ );
117
119
 
118
120
  if (
119
121
  isMissingData ||
120
- (errorResponseFields != null && errorResponseFields.some(eventShouldThrow))
122
+ (fieldErrors != null && fieldErrors.some(eventShouldThrow))
121
123
  ) {
122
124
  throw RESOLVER_FRAGMENT_ERRORED_SENTINEL;
123
125
  }
@@ -32,11 +32,14 @@ if (__DEV__) {
32
32
  }
33
33
  formattersInstalled = true;
34
34
  // $FlowFixMe[incompatible-use] D61394600
35
+ // $FlowFixMe[cannot-resolve-name]
35
36
  if (window.devtoolsFormatters == null) {
36
37
  // $FlowFixMe[incompatible-use] D61394600
38
+ // $FlowFixMe[cannot-resolve-name]
37
39
  window.devtoolsFormatters = [];
38
40
  }
39
41
  // $FlowFixMe[incompatible-use] D61394600
42
+ // $FlowFixMe[cannot-resolve-name]
40
43
  if (!Array.isArray(window.devtoolsFormatters)) {
41
44
  return;
42
45
  }
@@ -47,6 +50,7 @@ if (__DEV__) {
47
50
  'section.',
48
51
  );
49
52
  // $FlowFixMe[incompatible-use] D61394600
53
+ // $FlowFixMe[cannot-resolve-name]
50
54
  window.devtoolsFormatters.push(...createFormatters());
51
55
  };
52
56
 
@@ -137,6 +141,7 @@ if (__DEV__) {
137
141
  ): ?{[string]: mixed} => {
138
142
  const record = source.get(dataID);
139
143
  if (record == null) {
144
+ // $FlowFixMe[incompatible-return]
140
145
  return record;
141
146
  }
142
147
  return new Proxy(
@@ -12,6 +12,7 @@
12
12
  'use strict';
13
13
 
14
14
  import type {RelayContext} from './RelayStoreTypes.js';
15
+ import type {Context} from 'react';
15
16
  import typeof {createContext} from 'react';
16
17
 
17
18
  const invariant = require('invariant');
@@ -24,10 +25,10 @@ type React = $ReadOnly<{
24
25
  ...
25
26
  }>;
26
27
 
27
- let relayContext: ?React$Context<RelayContext | null>;
28
+ let relayContext: ?Context<RelayContext | null>;
28
29
  let firstReact: ?React;
29
30
 
30
- function createRelayContext(react: React): React$Context<RelayContext | null> {
31
+ function createRelayContext(react: React): Context<RelayContext | null> {
31
32
  if (!relayContext) {
32
33
  relayContext = react.createContext(null);
33
34
  if (__DEV__) {
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {Context} from 'react';
15
+ import typeof {createContext} from 'react';
16
+
17
+ const invariant = require('invariant');
18
+
19
+ // Ideally, we'd just import the type of the react module, but this causes Flow
20
+ // problems.
21
+ type React = $ReadOnly<{
22
+ createContext: createContext<mixed | null>,
23
+ version: string,
24
+ ...
25
+ }>;
26
+
27
+ let relayLoggingContext: ?Context<mixed | null>;
28
+ let firstReact: ?React;
29
+
30
+ function createRelayLoggingContext(react: React): Context<mixed | null> {
31
+ if (!relayLoggingContext) {
32
+ relayLoggingContext = react.createContext(null);
33
+ if (__DEV__) {
34
+ relayLoggingContext.displayName = 'RelayLoggingContext';
35
+ }
36
+ firstReact = react;
37
+ }
38
+ invariant(
39
+ react === firstReact,
40
+ '[createRelayLoggingContext]: You are passing a different instance of React',
41
+ react.version,
42
+ );
43
+ return relayLoggingContext;
44
+ }
45
+
46
+ module.exports = createRelayLoggingContext;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {DataID} from 'relay-runtime/util/RelayRuntimeTypes';
15
+
16
+ const TYPENAME_PREFIX = '__type:';
17
+
18
+ function generateTypenamePrefixedDataID(
19
+ typeName: string,
20
+ dataID: DataID,
21
+ ): DataID {
22
+ return `${TYPENAME_PREFIX}${typeName}:${dataID}`;
23
+ }
24
+
25
+ module.exports = {generateTypenamePrefixedDataID};
@@ -48,6 +48,7 @@ const {
48
48
  RELAY_RESOLVER_OUTPUT_TYPE_RECORD_IDS,
49
49
  RELAY_RESOLVER_SNAPSHOT_KEY,
50
50
  RELAY_RESOLVER_VALUE_KEY,
51
+ getReadTimeResolverStorageKey,
51
52
  getStorageKey,
52
53
  } = require('../RelayStoreUtils');
53
54
  const getOutputTypeRecordIDs = require('./getOutputTypeRecordIDs');
@@ -129,7 +130,7 @@ class LiveResolverCache implements ResolverCache {
129
130
  // resolvers on this parent record.
130
131
  const record = expectRecord(recordSource, recordID);
131
132
 
132
- const storageKey = getStorageKey(field, variables);
133
+ const storageKey = getReadTimeResolverStorageKey(field, variables);
133
134
  let linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
134
135
  let linkedRecord = linkedID == null ? null : recordSource.get(linkedID);
135
136
 
@@ -656,9 +657,7 @@ class LiveResolverCache implements ResolverCache {
656
657
  while (recordsToVisit.length) {
657
658
  // $FlowFixMe[incompatible-type] We just checked length so we know this is not undefined
658
659
  const recordID: string = recordsToVisit.pop();
659
- if (RelayFeatureFlags.AVOID_CYCLES_IN_RESOLVER_NOTIFICATION) {
660
- visited.add(recordID);
661
- }
660
+ visited.add(recordID);
662
661
 
663
662
  // $FlowFixMe[incompatible-call]
664
663
  updatedDataIDs.add(recordID);
@@ -669,9 +668,7 @@ class LiveResolverCache implements ResolverCache {
669
668
  }
670
669
  for (const fragment of fragmentSet) {
671
670
  if (!visited.has(fragment)) {
672
- if (RelayFeatureFlags.AVOID_CYCLES_IN_RESOLVER_NOTIFICATION) {
673
- visited.add(fragment);
674
- }
671
+ visited.add(fragment);
675
672
 
676
673
  const recordSet = this._resolverIDToRecordIDs.get(fragment);
677
674
  if (recordSet == null) {
@@ -680,9 +677,7 @@ class LiveResolverCache implements ResolverCache {
680
677
  for (const anotherRecordID of recordSet) {
681
678
  markInvalidatedResolverRecord(anotherRecordID, recordSource);
682
679
  if (!visited.has(anotherRecordID)) {
683
- if (RelayFeatureFlags.AVOID_CYCLES_IN_RESOLVER_NOTIFICATION) {
684
- visited.add(anotherRecordID);
685
- }
680
+ visited.add(anotherRecordID);
686
681
  recordsToVisit.push(anotherRecordID);
687
682
  }
688
683
  }
@@ -12,12 +12,12 @@
12
12
  'use strict';
13
13
 
14
14
  import type {Fragment} from '../../util/RelayRuntimeTypes';
15
- import type {FragmentType} from '../RelayStoreTypes';
15
+ import type {FragmentType, ResolverContext} from '../RelayStoreTypes';
16
16
 
17
17
  const {readFragment} = require('../ResolverFragments');
18
18
  const invariant = require('invariant');
19
19
 
20
- type ResolverFn = ($FlowFixMe, ?$FlowFixMe) => mixed;
20
+ type ResolverFn = ($FlowFixMe, ?$FlowFixMe, ResolverContext) => mixed;
21
21
 
22
22
  /**
23
23
  *
@@ -40,7 +40,11 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
40
40
  isRequiredField?: boolean,
41
41
  ): (fragmentKey: TFragmentType, args: mixed) => mixed {
42
42
  const resolverFn: ResolverFn = _resolverFn;
43
- return (fragmentKey: TFragmentType, args: mixed): mixed => {
43
+ return (
44
+ fragmentKey: TFragmentType,
45
+ args: mixed,
46
+ resolverContext: ResolverContext,
47
+ ): mixed => {
44
48
  const data = readFragment(fragment, fragmentKey);
45
49
  if (fieldName != null) {
46
50
  if (data == null) {
@@ -52,7 +56,7 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
52
56
  fragment.name,
53
57
  );
54
58
  } else {
55
- return resolverFn(null, args);
59
+ return resolverFn(null, args, resolverContext); // TODO: This statement does not seem to be covered by a test?
56
60
  }
57
61
  }
58
62
 
@@ -70,7 +74,7 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
70
74
  }
71
75
 
72
76
  // $FlowFixMe[invalid-computed-prop]
73
- return resolverFn(data[fieldName], args);
77
+ return resolverFn(data[fieldName], args, resolverContext);
74
78
  } else {
75
79
  // If both `data` and `fieldName` is available, we expect the
76
80
  // `fieldName` field in the `data` object.
@@ -83,7 +87,7 @@ function resolverDataInjector<TFragmentType: FragmentType, TData: ?{...}>(
83
87
  }
84
88
  } else {
85
89
  // By default we will pass the full set of the fragment data to the resolver
86
- return resolverFn(data, args);
90
+ return resolverFn(null, args, resolverContext); // TODO: This statement does not seem to be covered by a test?
87
91
  }
88
92
  };
89
93
  }