relay-runtime 11.0.0-rc.0 → 12.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 (128) hide show
  1. package/handlers/connection/ConnectionHandler.js.flow +7 -0
  2. package/handlers/connection/MutationHandlers.js.flow +28 -0
  3. package/index.js +1 -1
  4. package/index.js.flow +20 -3
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
  6. package/lib/handlers/connection/ConnectionHandler.js +12 -6
  7. package/lib/handlers/connection/MutationHandlers.js +67 -8
  8. package/lib/index.js +15 -0
  9. package/lib/multi-actor-environment/ActorIdentifier.js +33 -0
  10. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +148 -0
  11. package/lib/multi-actor-environment/ActorUtils.js +27 -0
  12. package/lib/multi-actor-environment/MultiActorEnvironment.js +406 -0
  13. package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
  14. package/lib/multi-actor-environment/index.js +21 -0
  15. package/lib/mutations/RelayRecordProxy.js +1 -1
  16. package/lib/mutations/RelayRecordSourceMutator.js +1 -1
  17. package/lib/mutations/RelayRecordSourceProxy.js +1 -1
  18. package/lib/mutations/RelayRecordSourceSelectorProxy.js +7 -2
  19. package/lib/mutations/applyOptimisticMutation.js +1 -1
  20. package/lib/mutations/commitMutation.js +5 -2
  21. package/lib/mutations/validateMutation.js +39 -17
  22. package/lib/network/RelayNetwork.js +1 -1
  23. package/lib/network/RelayObservable.js +3 -1
  24. package/lib/network/RelayQueryResponseCache.js +20 -3
  25. package/lib/network/wrapNetworkWithLogObserver.js +78 -0
  26. package/lib/query/GraphQLTag.js +1 -1
  27. package/lib/query/fetchQuery.js +1 -1
  28. package/lib/query/fetchQueryInternal.js +1 -1
  29. package/lib/store/DataChecker.js +132 -50
  30. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +524 -187
  31. package/lib/store/RelayConcreteVariables.js +29 -4
  32. package/lib/store/RelayModernEnvironment.js +137 -220
  33. package/lib/store/RelayModernFragmentSpecResolver.js +49 -23
  34. package/lib/store/RelayModernRecord.js +36 -2
  35. package/lib/store/RelayModernSelector.js +1 -1
  36. package/lib/store/RelayModernStore.js +53 -22
  37. package/lib/store/RelayOperationTracker.js +34 -24
  38. package/lib/store/RelayPublishQueue.js +30 -8
  39. package/lib/store/RelayReader.js +177 -29
  40. package/lib/store/RelayRecordSource.js +87 -3
  41. package/lib/store/RelayReferenceMarker.js +53 -28
  42. package/lib/store/RelayResponseNormalizer.js +247 -108
  43. package/lib/store/RelayStoreReactFlightUtils.js +7 -11
  44. package/lib/store/RelayStoreSubscriptions.js +8 -5
  45. package/lib/store/RelayStoreUtils.js +10 -4
  46. package/lib/store/ResolverCache.js +213 -0
  47. package/lib/store/ResolverFragments.js +57 -0
  48. package/lib/store/cloneRelayHandleSourceField.js +1 -1
  49. package/lib/store/cloneRelayScalarHandleSourceField.js +1 -1
  50. package/lib/store/createRelayContext.js +2 -2
  51. package/lib/store/defaultGetDataID.js +3 -1
  52. package/lib/store/readInlineData.js +1 -1
  53. package/lib/subscription/requestSubscription.js +32 -6
  54. package/lib/util/RelayConcreteNode.js +3 -0
  55. package/lib/util/RelayFeatureFlags.js +5 -4
  56. package/lib/util/RelayProfiler.js +17 -187
  57. package/lib/util/RelayReplaySubject.js +22 -7
  58. package/lib/util/deepFreeze.js +1 -0
  59. package/lib/util/getPaginationMetadata.js +41 -0
  60. package/lib/util/getPaginationVariables.js +67 -0
  61. package/lib/util/getPendingOperationsForFragment.js +55 -0
  62. package/lib/util/getRefetchMetadata.js +36 -0
  63. package/lib/util/getRelayHandleKey.js +1 -1
  64. package/lib/util/getRequestIdentifier.js +1 -1
  65. package/lib/util/getValueAtPath.js +51 -0
  66. package/lib/util/isEmptyObject.js +1 -1
  67. package/lib/util/registerEnvironmentWithDevTools.js +26 -0
  68. package/lib/util/withDuration.js +31 -0
  69. package/multi-actor-environment/ActorIdentifier.js.flow +43 -0
  70. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +217 -0
  71. package/multi-actor-environment/ActorUtils.js.flow +33 -0
  72. package/multi-actor-environment/MultiActorEnvironment.js.flow +485 -0
  73. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +245 -0
  74. package/multi-actor-environment/index.js.flow +27 -0
  75. package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -2
  76. package/mutations/commitMutation.js.flow +3 -1
  77. package/mutations/validateMutation.js.flow +42 -16
  78. package/network/RelayNetworkTypes.js.flow +17 -8
  79. package/network/RelayObservable.js.flow +2 -0
  80. package/network/RelayQueryResponseCache.js.flow +31 -17
  81. package/network/wrapNetworkWithLogObserver.js.flow +99 -0
  82. package/package.json +3 -2
  83. package/relay-runtime.js +2 -2
  84. package/relay-runtime.min.js +2 -2
  85. package/store/ClientID.js.flow +5 -1
  86. package/store/DataChecker.js.flow +148 -44
  87. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +578 -237
  88. package/store/RelayConcreteVariables.js.flow +31 -1
  89. package/store/RelayModernEnvironment.js.flow +132 -220
  90. package/store/RelayModernFragmentSpecResolver.js.flow +40 -14
  91. package/store/RelayModernOperationDescriptor.js.flow +9 -3
  92. package/store/RelayModernRecord.js.flow +49 -0
  93. package/store/RelayModernStore.js.flow +57 -17
  94. package/store/RelayOperationTracker.js.flow +56 -34
  95. package/store/RelayPublishQueue.js.flow +37 -11
  96. package/store/RelayReader.js.flow +186 -27
  97. package/store/RelayRecordSource.js.flow +72 -6
  98. package/store/RelayReferenceMarker.js.flow +51 -21
  99. package/store/RelayResponseNormalizer.js.flow +251 -67
  100. package/store/RelayStoreReactFlightUtils.js.flow +6 -9
  101. package/store/RelayStoreSubscriptions.js.flow +10 -3
  102. package/store/RelayStoreTypes.js.flow +144 -21
  103. package/store/RelayStoreUtils.js.flow +19 -4
  104. package/store/ResolverCache.js.flow +247 -0
  105. package/store/ResolverFragments.js.flow +128 -0
  106. package/store/createRelayContext.js.flow +1 -1
  107. package/store/defaultGetDataID.js.flow +3 -1
  108. package/subscription/requestSubscription.js.flow +43 -8
  109. package/util/NormalizationNode.js.flow +16 -3
  110. package/util/ReaderNode.js.flow +29 -2
  111. package/util/RelayConcreteNode.js.flow +3 -0
  112. package/util/RelayFeatureFlags.js.flow +10 -6
  113. package/util/RelayProfiler.js.flow +22 -194
  114. package/util/RelayReplaySubject.js.flow +7 -6
  115. package/util/RelayRuntimeTypes.js.flow +4 -2
  116. package/util/deepFreeze.js.flow +2 -1
  117. package/util/getPaginationMetadata.js.flow +74 -0
  118. package/util/getPaginationVariables.js.flow +112 -0
  119. package/util/getPendingOperationsForFragment.js.flow +62 -0
  120. package/util/getRefetchMetadata.js.flow +80 -0
  121. package/util/getValueAtPath.js.flow +46 -0
  122. package/util/isEmptyObject.js.flow +2 -1
  123. package/util/registerEnvironmentWithDevTools.js.flow +33 -0
  124. package/util/withDuration.js.flow +32 -0
  125. package/lib/store/RelayRecordSourceMapImpl.js +0 -107
  126. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
  127. package/store/RelayRecordSourceMapImpl.js.flow +0 -91
  128. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
@@ -15,12 +15,13 @@
15
15
  const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
16
 
17
17
  const areEqual = require('areEqual');
18
+ const getPendingOperationsForFragment = require('../util/getPendingOperationsForFragment');
18
19
  const invariant = require('invariant');
19
20
  const isScalarAndEqual = require('../util/isScalarAndEqual');
21
+ const recycleNodesInto = require('../util/recycleNodesInto');
20
22
  const reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
21
23
  const warning = require('warning');
22
24
 
23
- const {getPromiseForActiveRequest} = require('../query/fetchQueryInternal');
24
25
  const {createRequestDescriptor} = require('./RelayModernOperationDescriptor');
25
26
  const {
26
27
  areEqualSelectors,
@@ -137,13 +138,16 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
137
138
  return this._data;
138
139
  }
139
140
 
140
- setCallback(callback: () => void): void {
141
+ setCallback(props: Props, callback: () => void): void {
141
142
  this._callback = callback;
143
+ if (RelayFeatureFlags.ENABLE_CONTAINERS_SUBSCRIBE_ON_COMMIT === true) {
144
+ this.setProps(props);
145
+ }
142
146
  }
143
147
 
144
148
  setProps(props: Props): void {
145
- const ownedSelectors = getSelectorsFromObject(this._fragments, props);
146
149
  this._props = {};
150
+ const ownedSelectors = getSelectorsFromObject(this._fragments, props);
147
151
 
148
152
  for (const key in ownedSelectors) {
149
153
  if (ownedSelectors.hasOwnProperty(key)) {
@@ -160,6 +164,7 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
160
164
  this._context.environment,
161
165
  this._rootIsQueryRenderer,
162
166
  ownedSelector,
167
+ this._callback != null,
163
168
  this._onChange,
164
169
  );
165
170
  } else {
@@ -176,6 +181,7 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
176
181
  this._context.environment,
177
182
  this._rootIsQueryRenderer,
178
183
  ownedSelector,
184
+ this._callback != null,
179
185
  this._onChange,
180
186
  );
181
187
  } else {
@@ -232,6 +238,7 @@ class SelectorResolver {
232
238
  environment: IEnvironment,
233
239
  rootIsQueryRenderer: boolean,
234
240
  selector: SingularReaderSelector,
241
+ subscribeOnConstruction: boolean,
235
242
  callback: () => void,
236
243
  ) {
237
244
  const snapshot = environment.lookup(selector);
@@ -242,7 +249,13 @@ class SelectorResolver {
242
249
  this._environment = environment;
243
250
  this._rootIsQueryRenderer = rootIsQueryRenderer;
244
251
  this._selector = selector;
245
- this._subscription = environment.subscribe(snapshot, this._onChange);
252
+ if (RelayFeatureFlags.ENABLE_CONTAINERS_SUBSCRIBE_ON_COMMIT === true) {
253
+ if (subscribeOnConstruction) {
254
+ this._subscription = environment.subscribe(snapshot, this._onChange);
255
+ }
256
+ } else {
257
+ this._subscription = environment.subscribe(snapshot, this._onChange);
258
+ }
246
259
  }
247
260
 
248
261
  dispose(): void {
@@ -253,10 +266,7 @@ class SelectorResolver {
253
266
  }
254
267
 
255
268
  resolve(): ?Object {
256
- if (
257
- RelayFeatureFlags.ENABLE_RELAY_CONTAINERS_SUSPENSE === true &&
258
- this._isMissingData === true
259
- ) {
269
+ if (this._isMissingData === true) {
260
270
  // NOTE: This branch exists to handle the case in which:
261
271
  // - A RelayModern container is rendered as a descendant of a Relay Hook
262
272
  // root using a "partial" renderPolicy (this means that eargerly
@@ -278,11 +288,12 @@ class SelectorResolver {
278
288
  // This should eventually go away with something like @optional, where we only
279
289
  // suspend at specific boundaries depending on whether the boundary
280
290
  // can be fulfilled or not.
281
- const promise =
282
- getPromiseForActiveRequest(this._environment, this._selector.owner) ??
283
- this._environment
284
- .getOperationTracker()
285
- .getPromiseForPendingOperationsAffectingOwner(this._selector.owner);
291
+ const pendingOperationsResult = getPendingOperationsForFragment(
292
+ this._environment,
293
+ this._selector.node,
294
+ this._selector.owner,
295
+ );
296
+ const promise: void | Promise<void> = pendingOperationsResult?.promise;
286
297
  if (promise != null) {
287
298
  if (this._rootIsQueryRenderer) {
288
299
  warning(
@@ -293,6 +304,8 @@ class SelectorResolver {
293
304
  this._selector.node.name,
294
305
  );
295
306
  } else {
307
+ const pendingOperations =
308
+ pendingOperationsResult?.pendingOperations ?? [];
296
309
  warning(
297
310
  false,
298
311
  'Relay: Relay Container for fragment `%s` suspended. When using ' +
@@ -300,6 +313,15 @@ class SelectorResolver {
300
313
  'of a Relay Container.',
301
314
  this._selector.node.name,
302
315
  );
316
+ this._environment.__log({
317
+ name: 'suspense.fragment',
318
+ data: this._data,
319
+ fragment: this._selector.node,
320
+ isRelayHooks: false,
321
+ isMissingData: this._isMissingData,
322
+ isPromiseCached: false,
323
+ pendingOperations,
324
+ });
303
325
  throw promise;
304
326
  }
305
327
  }
@@ -322,7 +344,7 @@ class SelectorResolver {
322
344
  }
323
345
  this.dispose();
324
346
  const snapshot = this._environment.lookup(selector);
325
- this._data = snapshot.data;
347
+ this._data = recycleNodesInto(this._data, snapshot.data);
326
348
  this._isMissingData = snapshot.isMissingData;
327
349
  this._missingRequiredFields = snapshot.missingRequiredFields;
328
350
  this._selector = selector;
@@ -375,11 +397,13 @@ class SelectorListResolver {
375
397
  _resolvers: Array<SelectorResolver>;
376
398
  _rootIsQueryRenderer: boolean;
377
399
  _stale: boolean;
400
+ _subscribeOnConstruction: boolean;
378
401
 
379
402
  constructor(
380
403
  environment: IEnvironment,
381
404
  rootIsQueryRenderer: boolean,
382
405
  selector: PluralReaderSelector,
406
+ subscribeOnConstruction: boolean,
383
407
  callback: () => void,
384
408
  ) {
385
409
  this._callback = callback;
@@ -388,6 +412,7 @@ class SelectorListResolver {
388
412
  this._resolvers = [];
389
413
  this._stale = true;
390
414
  this._rootIsQueryRenderer = rootIsQueryRenderer;
415
+ this._subscribeOnConstruction = subscribeOnConstruction;
391
416
 
392
417
  this.setSelector(selector);
393
418
  }
@@ -433,6 +458,7 @@ class SelectorListResolver {
433
458
  this._environment,
434
459
  this._rootIsQueryRenderer,
435
460
  selectors[ii],
461
+ this._subscribeOnConstruction,
436
462
  this._onChange,
437
463
  );
438
464
  }
@@ -23,7 +23,13 @@ const {
23
23
  const {ROOT_ID} = require('./RelayStoreUtils');
24
24
 
25
25
  import type {ConcreteRequest} from '../util/RelayConcreteNode';
26
- import type {CacheConfig, DataID, Variables} from '../util/RelayRuntimeTypes';
26
+ import type {
27
+ CacheConfig,
28
+ DataID,
29
+ Variables,
30
+ VariablesOf,
31
+ OperationType,
32
+ } from '../util/RelayRuntimeTypes';
27
33
  import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
28
34
 
29
35
  /**
@@ -32,9 +38,9 @@ import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
32
38
  * are filtered to exclude variables that do not match defined arguments on the
33
39
  * operation, and default values are populated for null values.
34
40
  */
35
- function createOperationDescriptor(
41
+ function createOperationDescriptor<TQuery: OperationType>(
36
42
  request: ConcreteRequest,
37
- variables: Variables,
43
+ variables: VariablesOf<TQuery>,
38
44
  cacheConfig?: ?CacheConfig,
39
45
  dataID?: DataID = ROOT_ID,
40
46
  ): OperationDescriptor {
@@ -19,6 +19,7 @@ const warning = require('warning');
19
19
 
20
20
  const {isClientID} = require('./ClientID');
21
21
  const {
22
+ ACTOR_IDENTIFIER_KEY,
22
23
  ID_KEY,
23
24
  REF_KEY,
24
25
  REFS_KEY,
@@ -27,6 +28,7 @@ const {
27
28
  ROOT_ID,
28
29
  } = require('./RelayStoreUtils');
29
30
 
31
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
30
32
  import type {DataID} from '../util/RelayRuntimeTypes';
31
33
  import type {Record} from './RelayStoreTypes';
32
34
 
@@ -384,6 +386,51 @@ function setLinkedRecordIDs(
384
386
  record[storageKey] = links;
385
387
  }
386
388
 
389
+ /**
390
+ * @public
391
+ *
392
+ * Set the value of a field to a reference to another record in the actor specific store.
393
+ */
394
+ function setActorLinkedRecordID(
395
+ record: Record,
396
+ storageKey: string,
397
+ actorIdentifier: ActorIdentifier,
398
+ linkedID: DataID,
399
+ ): void {
400
+ // See perf note above for why we aren't using computed property access.
401
+ const link = {};
402
+ link[REF_KEY] = linkedID;
403
+ link[ACTOR_IDENTIFIER_KEY] = actorIdentifier;
404
+ record[storageKey] = link;
405
+ }
406
+
407
+ /**
408
+ * @public
409
+ *
410
+ * Get link to a record and the actor identifier for the store.
411
+ */
412
+ function getActorLinkedRecordID(
413
+ record: Record,
414
+ storageKey: string,
415
+ ): ?[ActorIdentifier, DataID] {
416
+ const link = record[storageKey];
417
+ if (link == null) {
418
+ return link;
419
+ }
420
+ invariant(
421
+ typeof link === 'object' &&
422
+ typeof link[REF_KEY] === 'string' &&
423
+ link[ACTOR_IDENTIFIER_KEY] != null,
424
+ 'RelayModernRecord.getActorLinkedRecordID(): Expected `%s.%s` to be an actor specific linked ID, ' +
425
+ 'was `%s`.',
426
+ record[ID_KEY],
427
+ storageKey,
428
+ JSON.stringify(link),
429
+ );
430
+
431
+ return [(link[ACTOR_IDENTIFIER_KEY]: any), (link[REF_KEY]: any)];
432
+ }
433
+
387
434
  module.exports = {
388
435
  clone,
389
436
  copyFields,
@@ -400,4 +447,6 @@ module.exports = {
400
447
  setLinkedRecordID,
401
448
  setLinkedRecordIDs,
402
449
  update,
450
+ getActorLinkedRecordID,
451
+ setActorLinkedRecordID,
403
452
  };
@@ -16,12 +16,10 @@ const DataChecker = require('./DataChecker');
16
16
  const RelayFeatureFlags = require('../util/RelayFeatureFlags');
17
17
  const RelayModernRecord = require('./RelayModernRecord');
18
18
  const RelayOptimisticRecordSource = require('./RelayOptimisticRecordSource');
19
- const RelayProfiler = require('../util/RelayProfiler');
20
19
  const RelayReader = require('./RelayReader');
21
20
  const RelayReferenceMarker = require('./RelayReferenceMarker');
22
21
  const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
23
22
  const RelayStoreSubscriptions = require('./RelayStoreSubscriptions');
24
- const RelayStoreSubscriptionsUsingMapByID = require('./RelayStoreSubscriptionsUsingMapByID');
25
23
  const RelayStoreUtils = require('./RelayStoreUtils');
26
24
 
27
25
  const deepFreeze = require('../util/deepFreeze');
@@ -29,8 +27,14 @@ const defaultGetDataID = require('./defaultGetDataID');
29
27
  const invariant = require('invariant');
30
28
  const resolveImmediate = require('../util/resolveImmediate');
31
29
 
30
+ const {
31
+ INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
32
+ assertInternalActorIndentifier,
33
+ } = require('../multi-actor-environment/ActorIdentifier');
32
34
  const {ROOT_ID, ROOT_TYPE} = require('./RelayStoreUtils');
35
+ const {RecordResolverCache} = require('./ResolverCache');
33
36
 
37
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
34
38
  import type {DataID, Disposable} from '../util/RelayRuntimeTypes';
35
39
  import type {Availability} from './DataChecker';
36
40
  import type {GetDataID} from './RelayResponseNormalizer';
@@ -50,6 +54,7 @@ import type {
50
54
  StoreSubscriptions,
51
55
  DataIDSet,
52
56
  } from './RelayStoreTypes';
57
+ import type {ResolverCache} from './ResolverCache';
53
58
 
54
59
  export opaque type InvalidationState = {|
55
60
  dataIDs: $ReadOnlyArray<DataID>,
@@ -90,6 +95,7 @@ class RelayModernStore implements Store {
90
95
  _operationLoader: ?OperationLoader;
91
96
  _optimisticSource: ?MutableRecordSource;
92
97
  _recordSource: MutableRecordSource;
98
+ _resolverCache: ResolverCache;
93
99
  _releaseBuffer: Array<string>;
94
100
  _roots: Map<
95
101
  string,
@@ -103,6 +109,7 @@ class RelayModernStore implements Store {
103
109
  _shouldScheduleGC: boolean;
104
110
  _storeSubscriptions: StoreSubscriptions;
105
111
  _updatedRecordIDs: DataIDSet;
112
+ _shouldProcessClientComponents: ?boolean;
106
113
 
107
114
  constructor(
108
115
  source: MutableRecordSource,
@@ -113,6 +120,7 @@ class RelayModernStore implements Store {
113
120
  getDataID?: ?GetDataID,
114
121
  gcReleaseBufferSize?: ?number,
115
122
  queryCacheExpirationTime?: ?number,
123
+ shouldProcessClientComponents?: ?boolean,
116
124
  |},
117
125
  ) {
118
126
  // Prevent mutation of a record from outside the store.
@@ -143,11 +151,16 @@ class RelayModernStore implements Store {
143
151
  this._releaseBuffer = [];
144
152
  this._roots = new Map();
145
153
  this._shouldScheduleGC = false;
146
- this._storeSubscriptions =
147
- RelayFeatureFlags.ENABLE_STORE_SUBSCRIPTIONS_REFACTOR === true
148
- ? new RelayStoreSubscriptionsUsingMapByID(options?.log)
149
- : new RelayStoreSubscriptions(options?.log);
154
+ this._resolverCache = new RecordResolverCache(() =>
155
+ this._getMutableRecordSource(),
156
+ );
157
+ this._storeSubscriptions = new RelayStoreSubscriptions(
158
+ options?.log,
159
+ this._resolverCache,
160
+ );
150
161
  this._updatedRecordIDs = new Set();
162
+ this._shouldProcessClientComponents =
163
+ options?.shouldProcessClientComponents;
151
164
 
152
165
  initializeRecordSource(this._recordSource);
153
166
  }
@@ -156,12 +169,16 @@ class RelayModernStore implements Store {
156
169
  return this._optimisticSource ?? this._recordSource;
157
170
  }
158
171
 
172
+ _getMutableRecordSource(): MutableRecordSource {
173
+ return this._optimisticSource ?? this._recordSource;
174
+ }
175
+
159
176
  check(
160
177
  operation: OperationDescriptor,
161
178
  options?: CheckOptions,
162
179
  ): OperationAvailability {
163
180
  const selector = operation.root;
164
- const source = this._optimisticSource ?? this._recordSource;
181
+ const source = this._getMutableRecordSource();
165
182
  const globalInvalidationEpoch = this._globalInvalidationEpoch;
166
183
 
167
184
  const rootEntry = this._roots.get(operation.request.identifier);
@@ -170,7 +187,7 @@ class RelayModernStore implements Store {
170
187
  // Check if store has been globally invalidated
171
188
  if (globalInvalidationEpoch != null) {
172
189
  // If so, check if the operation we're checking was last written
173
- // before or after invalidation occured.
190
+ // before or after invalidation occurred.
174
191
  if (
175
192
  operationLastWrittenAt == null ||
176
193
  operationLastWrittenAt <= globalInvalidationEpoch
@@ -183,15 +200,29 @@ class RelayModernStore implements Store {
183
200
  }
184
201
  }
185
202
 
186
- const target = options?.target ?? source;
187
203
  const handlers = options?.handlers ?? [];
204
+ const getSourceForActor =
205
+ options?.getSourceForActor ??
206
+ (actorIdentifier => {
207
+ assertInternalActorIndentifier(actorIdentifier);
208
+ return source;
209
+ });
210
+ const getTargetForActor =
211
+ options?.getTargetForActor ??
212
+ (actorIdentifier => {
213
+ assertInternalActorIndentifier(actorIdentifier);
214
+ return source;
215
+ });
216
+
188
217
  const operationAvailability = DataChecker.check(
189
- source,
190
- target,
218
+ getSourceForActor,
219
+ getTargetForActor,
220
+ options?.defaultActorIdentifier ?? INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
191
221
  selector,
192
222
  handlers,
193
223
  this._operationLoader,
194
224
  this._getDataID,
225
+ this._shouldProcessClientComponents,
195
226
  );
196
227
 
197
228
  return getAvailabilityStatus(
@@ -270,7 +301,7 @@ class RelayModernStore implements Store {
270
301
 
271
302
  lookup(selector: SingularReaderSelector): Snapshot {
272
303
  const source = this.getSource();
273
- const snapshot = RelayReader.read(source, selector);
304
+ const snapshot = RelayReader.read(source, selector, this._resolverCache);
274
305
  if (__DEV__) {
275
306
  deepFreeze(snapshot);
276
307
  }
@@ -298,6 +329,13 @@ class RelayModernStore implements Store {
298
329
  this._globalInvalidationEpoch = this._currentWriteEpoch;
299
330
  }
300
331
 
332
+ if (RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
333
+ // When a record is updated, we need to also handle records that depend on it,
334
+ // specifically Relay Resolver result records containing results based on the
335
+ // updated records. This both adds to updatedRecordIDs and invalidates any
336
+ // cached data as needed.
337
+ this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
338
+ }
301
339
  const source = this.getSource();
302
340
  const updatedOwners = [];
303
341
  this._storeSubscriptions.updateSubscriptions(
@@ -360,7 +398,7 @@ class RelayModernStore implements Store {
360
398
  }
361
399
 
362
400
  publish(source: RecordSource, idsMarkedForInvalidation?: DataIDSet): void {
363
- const target = this._optimisticSource ?? this._recordSource;
401
+ const target = this._getMutableRecordSource();
364
402
  updateTargetFromSource(
365
403
  target,
366
404
  source,
@@ -413,6 +451,10 @@ class RelayModernStore implements Store {
413
451
  return 'RelayModernStore()';
414
452
  }
415
453
 
454
+ getEpoch(): number {
455
+ return this._currentWriteEpoch;
456
+ }
457
+
416
458
  // Internal API
417
459
  __getUpdatedRecordIDs(): DataIDSet {
418
460
  return this._updatedRecordIDs;
@@ -574,6 +616,7 @@ class RelayModernStore implements Store {
574
616
  selector,
575
617
  references,
576
618
  this._operationLoader,
619
+ this._shouldProcessClientComponents,
577
620
  );
578
621
  // Yield for other work after each operation
579
622
  yield;
@@ -672,6 +715,7 @@ function updateTargetFromSource(
672
715
  currentWriteEpoch,
673
716
  );
674
717
  invalidatedRecordIDs.add(dataID);
718
+ // $FlowFixMe[incompatible-call]
675
719
  target.set(dataID, nextRecord);
676
720
  });
677
721
  }
@@ -763,8 +807,4 @@ function getAvailabilityStatus(
763
807
  return {status: 'available', fetchTime: operationFetchTime ?? null};
764
808
  }
765
809
 
766
- RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
767
- lookup: 'RelayModernStore.prototype.lookup',
768
- });
769
-
770
810
  module.exports = RelayModernStore;
@@ -17,17 +17,21 @@ const invariant = require('invariant');
17
17
  import type {RequestDescriptor} from './RelayStoreTypes';
18
18
 
19
19
  class RelayOperationTracker {
20
- _ownersToPendingOperationsIdentifier: Map<string, Set<string>>;
21
- _pendingOperationsToOwnersIdentifier: Map<string, Set<string>>;
22
- _ownersIdentifierToPromise: Map<
20
+ _ownersToPendingOperations: Map<string, Map<string, RequestDescriptor>>;
21
+ _pendingOperationsToOwners: Map<string, Set<string>>;
22
+ _ownersToPendingPromise: Map<
23
23
  string,
24
- {|promise: Promise<void>, resolve: () => void|},
24
+ {|
25
+ promise: Promise<void>,
26
+ resolve: () => void,
27
+ pendingOperations: $ReadOnlyArray<RequestDescriptor>,
28
+ |},
25
29
  >;
26
30
 
27
31
  constructor() {
28
- this._ownersToPendingOperationsIdentifier = new Map();
29
- this._pendingOperationsToOwnersIdentifier = new Map();
30
- this._ownersIdentifierToPromise = new Map();
32
+ this._ownersToPendingOperations = new Map();
33
+ this._pendingOperationsToOwners = new Map();
34
+ this._ownersToPendingPromise = new Map();
31
35
  }
32
36
 
33
37
  /**
@@ -45,7 +49,7 @@ class RelayOperationTracker {
45
49
  const newlyAffectedOwnersIdentifier = new Set();
46
50
  for (const owner of affectedOwners) {
47
51
  const ownerIdentifier = owner.identifier;
48
- const pendingOperationsAffectingOwner = this._ownersToPendingOperationsIdentifier.get(
52
+ const pendingOperationsAffectingOwner = this._ownersToPendingOperations.get(
49
53
  ownerIdentifier,
50
54
  );
51
55
  if (pendingOperationsAffectingOwner != null) {
@@ -53,14 +57,17 @@ class RelayOperationTracker {
53
57
  // We just need to detect, is it the same operation that we already
54
58
  // have in the list, or it's a new operation
55
59
  if (!pendingOperationsAffectingOwner.has(pendingOperationIdentifier)) {
56
- pendingOperationsAffectingOwner.add(pendingOperationIdentifier);
60
+ pendingOperationsAffectingOwner.set(
61
+ pendingOperationIdentifier,
62
+ pendingOperation,
63
+ );
57
64
  newlyAffectedOwnersIdentifier.add(ownerIdentifier);
58
65
  }
59
66
  } else {
60
67
  // This is a new `ownerIdentifier` that is affected by the operation
61
- this._ownersToPendingOperationsIdentifier.set(
68
+ this._ownersToPendingOperations.set(
62
69
  ownerIdentifier,
63
- new Set([pendingOperationIdentifier]),
70
+ new Map([[pendingOperationIdentifier, pendingOperation]]),
64
71
  );
65
72
  newlyAffectedOwnersIdentifier.add(ownerIdentifier);
66
73
  }
@@ -72,19 +79,18 @@ class RelayOperationTracker {
72
79
  }
73
80
 
74
81
  // But, if some owners were affected we need to add them to
75
- // the `_pendingOperationsToOwnersIdentifier` set
76
- const ownersAffectedByOperationIdentifier =
77
- this._pendingOperationsToOwnersIdentifier.get(
78
- pendingOperationIdentifier,
79
- ) || new Set();
82
+ // the `_pendingOperationsToOwners` set
83
+ const ownersAffectedByPendingOperation =
84
+ this._pendingOperationsToOwners.get(pendingOperationIdentifier) ||
85
+ new Set();
80
86
 
81
87
  for (const ownerIdentifier of newlyAffectedOwnersIdentifier) {
82
88
  this._resolveOwnerResolvers(ownerIdentifier);
83
- ownersAffectedByOperationIdentifier.add(ownerIdentifier);
89
+ ownersAffectedByPendingOperation.add(ownerIdentifier);
84
90
  }
85
- this._pendingOperationsToOwnersIdentifier.set(
91
+ this._pendingOperationsToOwners.set(
86
92
  pendingOperationIdentifier,
87
- ownersAffectedByOperationIdentifier,
93
+ ownersAffectedByPendingOperation,
88
94
  );
89
95
  }
90
96
 
@@ -94,7 +100,7 @@ class RelayOperationTracker {
94
100
  */
95
101
  complete(pendingOperation: RequestDescriptor): void {
96
102
  const pendingOperationIdentifier = pendingOperation.identifier;
97
- const affectedOwnersIdentifier = this._pendingOperationsToOwnersIdentifier.get(
103
+ const affectedOwnersIdentifier = this._pendingOperationsToOwners.get(
98
104
  pendingOperationIdentifier,
99
105
  );
100
106
  if (affectedOwnersIdentifier == null) {
@@ -107,7 +113,7 @@ class RelayOperationTracker {
107
113
  // and some other operations
108
114
  const updatedOwnersIdentifier = new Set();
109
115
  for (const ownerIdentifier of affectedOwnersIdentifier) {
110
- const pendingOperationsAffectingOwner = this._ownersToPendingOperationsIdentifier.get(
116
+ const pendingOperationsAffectingOwner = this._ownersToPendingOperations.get(
111
117
  ownerIdentifier,
112
118
  );
113
119
  if (!pendingOperationsAffectingOwner) {
@@ -124,7 +130,7 @@ class RelayOperationTracker {
124
130
  // Complete subscriptions for all owners, affected by `pendingOperationIdentifier`
125
131
  for (const ownerIdentifier of completedOwnersIdentifier) {
126
132
  this._resolveOwnerResolvers(ownerIdentifier);
127
- this._ownersToPendingOperationsIdentifier.delete(ownerIdentifier);
133
+ this._ownersToPendingOperations.delete(ownerIdentifier);
128
134
  }
129
135
 
130
136
  // Update all ownerIdentifier that were updated by `pendingOperationIdentifier` but still
@@ -134,31 +140,42 @@ class RelayOperationTracker {
134
140
  }
135
141
 
136
142
  // Finally, remove pending operation identifier
137
- this._pendingOperationsToOwnersIdentifier.delete(
138
- pendingOperationIdentifier,
139
- );
143
+ this._pendingOperationsToOwners.delete(pendingOperationIdentifier);
140
144
  }
141
145
 
142
146
  _resolveOwnerResolvers(ownerIdentifier: string): void {
143
- const promiseEntry = this._ownersIdentifierToPromise.get(ownerIdentifier);
147
+ const promiseEntry = this._ownersToPendingPromise.get(ownerIdentifier);
144
148
  if (promiseEntry != null) {
145
149
  promiseEntry.resolve();
146
150
  }
147
- this._ownersIdentifierToPromise.delete(ownerIdentifier);
151
+ this._ownersToPendingPromise.delete(ownerIdentifier);
148
152
  }
149
153
 
150
- getPromiseForPendingOperationsAffectingOwner(
154
+ getPendingOperationsAffectingOwner(
151
155
  owner: RequestDescriptor,
152
- ): Promise<void> | null {
156
+ ): {|
157
+ promise: Promise<void>,
158
+ pendingOperations: $ReadOnlyArray<RequestDescriptor>,
159
+ |} | null {
153
160
  const ownerIdentifier = owner.identifier;
154
- if (!this._ownersToPendingOperationsIdentifier.has(ownerIdentifier)) {
161
+ const pendingOperationsForOwner = this._ownersToPendingOperations.get(
162
+ ownerIdentifier,
163
+ );
164
+ if (
165
+ pendingOperationsForOwner == null ||
166
+ pendingOperationsForOwner.size === 0
167
+ ) {
155
168
  return null;
156
169
  }
157
- const cachedPromiseEntry = this._ownersIdentifierToPromise.get(
170
+
171
+ const cachedPromiseEntry = this._ownersToPendingPromise.get(
158
172
  ownerIdentifier,
159
173
  );
160
174
  if (cachedPromiseEntry != null) {
161
- return cachedPromiseEntry.promise;
175
+ return {
176
+ promise: cachedPromiseEntry.promise,
177
+ pendingOperations: cachedPromiseEntry.pendingOperations,
178
+ };
162
179
  }
163
180
  let resolve;
164
181
  const promise = new Promise(r => {
@@ -169,8 +186,13 @@ class RelayOperationTracker {
169
186
  'RelayOperationTracker: Expected resolver to be defined. If you' +
170
187
  'are seeing this, it is likely a bug in Relay.',
171
188
  );
172
- this._ownersIdentifierToPromise.set(ownerIdentifier, {promise, resolve});
173
- return promise;
189
+ const pendingOperations = Array.from(pendingOperationsForOwner.values());
190
+ this._ownersToPendingPromise.set(ownerIdentifier, {
191
+ promise,
192
+ resolve,
193
+ pendingOperations,
194
+ });
195
+ return {promise, pendingOperations};
174
196
  }
175
197
  }
176
198