relay-runtime 11.0.2 → 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 (100) hide show
  1. package/index.js +1 -1
  2. package/index.js.flow +16 -1
  3. package/lib/index.js +15 -0
  4. package/lib/multi-actor-environment/ActorIdentifier.js +11 -1
  5. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +59 -19
  6. package/lib/multi-actor-environment/ActorUtils.js +27 -0
  7. package/lib/multi-actor-environment/MultiActorEnvironment.js +305 -55
  8. package/lib/multi-actor-environment/index.js +5 -1
  9. package/lib/mutations/RelayRecordSourceSelectorProxy.js +6 -1
  10. package/lib/mutations/commitMutation.js +4 -1
  11. package/lib/mutations/validateMutation.js +6 -1
  12. package/lib/network/RelayObservable.js +3 -1
  13. package/lib/network/RelayQueryResponseCache.js +19 -3
  14. package/lib/network/wrapNetworkWithLogObserver.js +78 -0
  15. package/lib/store/DataChecker.js +110 -40
  16. package/lib/store/OperationExecutor.js +478 -204
  17. package/lib/store/RelayConcreteVariables.js +21 -0
  18. package/lib/store/RelayModernEnvironment.js +41 -85
  19. package/lib/store/RelayModernFragmentSpecResolver.js +48 -22
  20. package/lib/store/RelayModernRecord.js +35 -1
  21. package/lib/store/RelayModernStore.js +48 -14
  22. package/lib/store/RelayOperationTracker.js +33 -23
  23. package/lib/store/RelayPublishQueue.js +23 -5
  24. package/lib/store/RelayReader.js +138 -44
  25. package/lib/store/RelayRecordSource.js +87 -3
  26. package/lib/store/RelayReferenceMarker.js +28 -15
  27. package/lib/store/RelayResponseNormalizer.js +164 -91
  28. package/lib/store/RelayStoreReactFlightUtils.js +1 -7
  29. package/lib/store/RelayStoreSubscriptions.js +8 -5
  30. package/lib/store/RelayStoreUtils.js +7 -2
  31. package/lib/store/ResolverCache.js +213 -0
  32. package/lib/store/ResolverFragments.js +1 -1
  33. package/lib/store/createRelayContext.js +1 -1
  34. package/lib/subscription/requestSubscription.js +27 -29
  35. package/lib/util/RelayConcreteNode.js +1 -0
  36. package/lib/util/RelayFeatureFlags.js +3 -5
  37. package/lib/util/RelayReplaySubject.js +21 -6
  38. package/lib/util/getPaginationMetadata.js +41 -0
  39. package/lib/util/getPaginationVariables.js +67 -0
  40. package/lib/util/getPendingOperationsForFragment.js +55 -0
  41. package/lib/util/getRefetchMetadata.js +36 -0
  42. package/lib/util/getValueAtPath.js +51 -0
  43. package/lib/util/isEmptyObject.js +1 -1
  44. package/lib/util/registerEnvironmentWithDevTools.js +26 -0
  45. package/lib/util/withDuration.js +31 -0
  46. package/multi-actor-environment/ActorIdentifier.js.flow +17 -1
  47. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +72 -44
  48. package/multi-actor-environment/ActorUtils.js.flow +33 -0
  49. package/multi-actor-environment/MultiActorEnvironment.js.flow +332 -80
  50. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +61 -12
  51. package/multi-actor-environment/index.js.flow +3 -0
  52. package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -2
  53. package/mutations/commitMutation.js.flow +2 -0
  54. package/mutations/validateMutation.js.flow +8 -0
  55. package/network/RelayObservable.js.flow +2 -0
  56. package/network/RelayQueryResponseCache.js.flow +31 -18
  57. package/network/wrapNetworkWithLogObserver.js.flow +99 -0
  58. package/package.json +1 -1
  59. package/relay-runtime.js +2 -2
  60. package/relay-runtime.min.js +2 -2
  61. package/store/ClientID.js.flow +5 -1
  62. package/store/DataChecker.js.flow +126 -35
  63. package/store/OperationExecutor.js.flow +528 -265
  64. package/store/RelayConcreteVariables.js.flow +26 -1
  65. package/store/RelayModernEnvironment.js.flow +41 -94
  66. package/store/RelayModernFragmentSpecResolver.js.flow +40 -14
  67. package/store/RelayModernOperationDescriptor.js.flow +9 -3
  68. package/store/RelayModernRecord.js.flow +49 -0
  69. package/store/RelayModernStore.js.flow +50 -12
  70. package/store/RelayOperationTracker.js.flow +56 -34
  71. package/store/RelayPublishQueue.js.flow +31 -8
  72. package/store/RelayReader.js.flow +148 -42
  73. package/store/RelayRecordSource.js.flow +72 -6
  74. package/store/RelayReferenceMarker.js.flow +29 -12
  75. package/store/RelayResponseNormalizer.js.flow +164 -48
  76. package/store/RelayStoreReactFlightUtils.js.flow +1 -7
  77. package/store/RelayStoreSubscriptions.js.flow +10 -3
  78. package/store/RelayStoreTypes.js.flow +128 -12
  79. package/store/RelayStoreUtils.js.flow +17 -3
  80. package/store/ResolverCache.js.flow +247 -0
  81. package/store/ResolverFragments.js.flow +6 -3
  82. package/store/createRelayContext.js.flow +1 -1
  83. package/subscription/requestSubscription.js.flow +41 -29
  84. package/util/NormalizationNode.js.flow +10 -3
  85. package/util/ReaderNode.js.flow +15 -1
  86. package/util/RelayConcreteNode.js.flow +1 -0
  87. package/util/RelayFeatureFlags.js.flow +8 -10
  88. package/util/RelayReplaySubject.js.flow +7 -6
  89. package/util/getPaginationMetadata.js.flow +74 -0
  90. package/util/getPaginationVariables.js.flow +112 -0
  91. package/util/getPendingOperationsForFragment.js.flow +62 -0
  92. package/util/getRefetchMetadata.js.flow +80 -0
  93. package/util/getValueAtPath.js.flow +46 -0
  94. package/util/isEmptyObject.js.flow +1 -0
  95. package/util/registerEnvironmentWithDevTools.js.flow +33 -0
  96. package/util/withDuration.js.flow +32 -0
  97. package/lib/store/RelayRecordSourceMapImpl.js +0 -107
  98. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
  99. package/store/RelayRecordSourceMapImpl.js.flow +0 -91
  100. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
@@ -14,7 +14,13 @@
14
14
 
15
15
  const invariant = require('invariant');
16
16
 
17
- import type {NormalizationOperation} from '../util/NormalizationNode';
17
+ const {getArgumentValues} = require('./RelayStoreUtils');
18
+
19
+ import type {
20
+ NormalizationLocalArgumentDefinition,
21
+ NormalizationArgument,
22
+ NormalizationOperation,
23
+ } from '../util/NormalizationNode';
18
24
  import type {ReaderFragment} from '../util/ReaderNode';
19
25
  import type {Variables} from '../util/RelayRuntimeTypes';
20
26
 
@@ -95,7 +101,26 @@ function getOperationVariables(
95
101
  return operationVariables;
96
102
  }
97
103
 
104
+ function getLocalVariables(
105
+ currentVariables: Variables,
106
+ argumentDefinitions: ?$ReadOnlyArray<NormalizationLocalArgumentDefinition>,
107
+ args: ?$ReadOnlyArray<NormalizationArgument>,
108
+ ): Variables {
109
+ if (argumentDefinitions == null) {
110
+ return currentVariables;
111
+ }
112
+ const nextVariables = {...currentVariables};
113
+ const nextArgs = args ? getArgumentValues(args, currentVariables) : {};
114
+ argumentDefinitions.forEach(def => {
115
+ // $FlowFixMe[cannot-write]
116
+ const value = nextArgs[def.name] ?? def.defaultValue;
117
+ nextVariables[def.name] = value;
118
+ });
119
+ return nextVariables;
120
+ }
121
+
98
122
  module.exports = {
123
+ getLocalVariables,
99
124
  getFragmentVariables,
100
125
  getOperationVariables,
101
126
  };
@@ -23,23 +23,23 @@ const RelayRecordSource = require('./RelayRecordSource');
23
23
 
24
24
  const defaultGetDataID = require('./defaultGetDataID');
25
25
  const defaultRequiredFieldLogger = require('./defaultRequiredFieldLogger');
26
- const generateID = require('../util/generateID');
27
26
  const invariant = require('invariant');
27
+ const registerEnvironmentWithDevTools = require('../util/registerEnvironmentWithDevTools');
28
+ const wrapNetworkWithLogObserver = require('../network/wrapNetworkWithLogObserver');
29
+
30
+ const {
31
+ INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
32
+ assertInternalActorIndentifier,
33
+ } = require('../multi-actor-environment/ActorIdentifier');
28
34
 
29
35
  import type {HandlerProvider} from '../handlers/RelayDefaultHandlerProvider';
36
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
30
37
  import type {
31
38
  GraphQLResponse,
32
39
  INetwork,
33
40
  PayloadData,
34
- UploadableMap,
35
41
  } from '../network/RelayNetworkTypes';
36
- import type {RequestParameters} from '../util/RelayConcreteNode';
37
- import type {
38
- CacheConfig,
39
- Disposable,
40
- RenderPolicy,
41
- Variables,
42
- } from '../util/RelayRuntimeTypes';
42
+ import type {Disposable, RenderPolicy} from '../util/RelayRuntimeTypes';
43
43
  import type {ActiveState, TaskScheduler} from './OperationExecutor';
44
44
  import type {GetDataID} from './RelayResponseNormalizer';
45
45
  import type {
@@ -143,7 +143,7 @@ class RelayModernEnvironment implements IEnvironment {
143
143
  : 'full';
144
144
  this._operationLoader = operationLoader;
145
145
  this._operationExecutions = new Map();
146
- this._network = this.__wrapNetworkWithLogObserver(config.network);
146
+ this._network = wrapNetworkWithLogObserver(this, config.network);
147
147
  this._getDataID = config.getDataID ?? defaultGetDataID;
148
148
  this._publishQueue = new RelayPublishQueue(
149
149
  config.store,
@@ -156,31 +156,23 @@ class RelayModernEnvironment implements IEnvironment {
156
156
  this._isServer = config.isServer ?? false;
157
157
 
158
158
  (this: any).__setNet = newNet =>
159
- (this._network = this.__wrapNetworkWithLogObserver(newNet));
159
+ (this._network = wrapNetworkWithLogObserver(this, newNet));
160
160
 
161
161
  if (__DEV__) {
162
162
  const {inspect} = require('./StoreInspector');
163
163
  (this: any).DEBUG_inspect = (dataID: ?string) => inspect(this, dataID);
164
164
  }
165
165
 
166
- // Register this Relay Environment with Relay DevTools if it exists.
167
- // Note: this must always be the last step in the constructor.
168
- const _global =
169
- typeof global !== 'undefined'
170
- ? global
171
- : typeof window !== 'undefined'
172
- ? window
173
- : undefined;
174
- const devToolsHook = _global && _global.__RELAY_DEVTOOLS_HOOK__;
175
- if (devToolsHook) {
176
- devToolsHook.registerEnvironment(this);
177
- }
178
166
  this._missingFieldHandlers = config.missingFieldHandlers;
179
167
  this._operationTracker =
180
168
  config.operationTracker ?? new RelayOperationTracker();
181
169
  this._reactFlightPayloadDeserializer = reactFlightPayloadDeserializer;
182
170
  this._reactFlightServerErrorHandler = reactFlightServerErrorHandler;
183
171
  this._shouldProcessClientComponents = config.shouldProcessClientComponents;
172
+
173
+ // Register this Relay Environment with Relay DevTools if it exists.
174
+ // Note: this must always be the last step in the constructor.
175
+ registerEnvironmentWithDevTools(this);
184
176
  }
185
177
 
186
178
  getStore(): Store {
@@ -303,7 +295,19 @@ class RelayModernEnvironment implements IEnvironment {
303
295
  handlers: $ReadOnlyArray<MissingFieldHandler>,
304
296
  ): OperationAvailability {
305
297
  const target = RelayRecordSource.create();
306
- const result = this._store.check(operation, {target, handlers});
298
+ const source = this._store.getSource();
299
+ const result = this._store.check(operation, {
300
+ handlers,
301
+ defaultActorIdentifier: INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
302
+ getSourceForActor(actorIdentifier: ActorIdentifier) {
303
+ assertInternalActorIndentifier(actorIdentifier);
304
+ return source;
305
+ },
306
+ getTargetForActor(actorIdentifier: ActorIdentifier) {
307
+ assertInternalActorIndentifier(actorIdentifier);
308
+ return target;
309
+ },
310
+ });
307
311
  if (target.size() > 0) {
308
312
  this._scheduleUpdates(() => {
309
313
  this._publishQueue.commitSource(target);
@@ -398,7 +402,7 @@ class RelayModernEnvironment implements IEnvironment {
398
402
  /**
399
403
  * Returns an Observable of GraphQLResponse resulting from executing the
400
404
  * provided Query or Subscription operation responses, the result of which is
401
- * then normalized and comitted to the publish queue.
405
+ * then normalized and committed to the publish queue.
402
406
  *
403
407
  * Note: Observables are lazy, so calling this method will do nothing until
404
408
  * the result is subscribed to:
@@ -437,16 +441,23 @@ class RelayModernEnvironment implements IEnvironment {
437
441
  optimisticConfig: ?OptimisticResponseConfig,
438
442
  updater: ?SelectorStoreUpdater,
439
443
  |}): RelayObservable<GraphQLResponse> {
444
+ const publishQueue = this._publishQueue;
445
+ const store = this._store;
440
446
  return RelayObservable.create(sink => {
441
447
  const executor = OperationExecutor.execute({
448
+ actorIdentifier: INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
442
449
  getDataID: this._getDataID,
443
450
  isClientPayload,
451
+ log: this.__log,
444
452
  operation,
445
453
  operationExecutions: this._operationExecutions,
446
454
  operationLoader: this._operationLoader,
447
455
  operationTracker: this._operationTracker,
448
456
  optimisticConfig,
449
- publishQueue: this._publishQueue,
457
+ getPublishQueue(actorIdentifier: ActorIdentifier) {
458
+ assertInternalActorIndentifier(actorIdentifier);
459
+ return publishQueue;
460
+ },
450
461
  reactFlightPayloadDeserializer: this._reactFlightPayloadDeserializer,
451
462
  reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
452
463
  scheduler: this._scheduler,
@@ -455,80 +466,16 @@ class RelayModernEnvironment implements IEnvironment {
455
466
  // NOTE: Some product tests expect `Network.execute` to be called only
456
467
  // when the Observable is executed.
457
468
  source: createSource(),
458
- store: this._store,
469
+ getStore(actorIdentifier: ActorIdentifier) {
470
+ assertInternalActorIndentifier(actorIdentifier);
471
+ return store;
472
+ },
459
473
  treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
460
474
  updater,
461
475
  });
462
476
  return () => executor.cancel();
463
477
  });
464
478
  }
465
-
466
- /**
467
- * Wraps the network with logging to ensure that network requests are
468
- * always logged. Relying on each network callsite to be wrapped is
469
- * untenable and will eventually lead to holes in the logging.
470
- */
471
- __wrapNetworkWithLogObserver(network: INetwork): INetwork {
472
- const that = this;
473
- return {
474
- execute(
475
- params: RequestParameters,
476
- variables: Variables,
477
- cacheConfig: CacheConfig,
478
- uploadables?: ?UploadableMap,
479
- ): RelayObservable<GraphQLResponse> {
480
- const transactionID = generateID();
481
- const log = that.__log;
482
- const logObserver = {
483
- start: subscription => {
484
- log({
485
- name: 'network.start',
486
- transactionID,
487
- params,
488
- variables,
489
- cacheConfig,
490
- });
491
- },
492
- next: response => {
493
- log({
494
- name: 'network.next',
495
- transactionID,
496
- response,
497
- });
498
- },
499
- error: error => {
500
- log({
501
- name: 'network.error',
502
- transactionID,
503
- error,
504
- });
505
- },
506
- complete: () => {
507
- log({
508
- name: 'network.complete',
509
- transactionID,
510
- });
511
- },
512
- unsubscribe: () => {
513
- log({
514
- name: 'network.unsubscribe',
515
- transactionID,
516
- });
517
- },
518
- };
519
- const logRequestInfo = info => {
520
- log({
521
- name: 'network.info',
522
- transactionID,
523
- info,
524
- });
525
- };
526
- return network
527
- .execute(params, variables, cacheConfig, uploadables, logRequestInfo)
528
- .do(logObserver);
529
- },
530
- };
531
- }
532
479
  }
533
480
 
534
481
  // Add a sigil for detection by `isRelayModernEnvironment()` to avoid a
@@ -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
  };
@@ -20,7 +20,6 @@ const RelayReader = require('./RelayReader');
20
20
  const RelayReferenceMarker = require('./RelayReferenceMarker');
21
21
  const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
22
22
  const RelayStoreSubscriptions = require('./RelayStoreSubscriptions');
23
- const RelayStoreSubscriptionsUsingMapByID = require('./RelayStoreSubscriptionsUsingMapByID');
24
23
  const RelayStoreUtils = require('./RelayStoreUtils');
25
24
 
26
25
  const deepFreeze = require('../util/deepFreeze');
@@ -28,8 +27,14 @@ const defaultGetDataID = require('./defaultGetDataID');
28
27
  const invariant = require('invariant');
29
28
  const resolveImmediate = require('../util/resolveImmediate');
30
29
 
30
+ const {
31
+ INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
32
+ assertInternalActorIndentifier,
33
+ } = require('../multi-actor-environment/ActorIdentifier');
31
34
  const {ROOT_ID, ROOT_TYPE} = require('./RelayStoreUtils');
35
+ const {RecordResolverCache} = require('./ResolverCache');
32
36
 
37
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
33
38
  import type {DataID, Disposable} from '../util/RelayRuntimeTypes';
34
39
  import type {Availability} from './DataChecker';
35
40
  import type {GetDataID} from './RelayResponseNormalizer';
@@ -49,6 +54,7 @@ import type {
49
54
  StoreSubscriptions,
50
55
  DataIDSet,
51
56
  } from './RelayStoreTypes';
57
+ import type {ResolverCache} from './ResolverCache';
52
58
 
53
59
  export opaque type InvalidationState = {|
54
60
  dataIDs: $ReadOnlyArray<DataID>,
@@ -89,6 +95,7 @@ class RelayModernStore implements Store {
89
95
  _operationLoader: ?OperationLoader;
90
96
  _optimisticSource: ?MutableRecordSource;
91
97
  _recordSource: MutableRecordSource;
98
+ _resolverCache: ResolverCache;
92
99
  _releaseBuffer: Array<string>;
93
100
  _roots: Map<
94
101
  string,
@@ -144,10 +151,13 @@ class RelayModernStore implements Store {
144
151
  this._releaseBuffer = [];
145
152
  this._roots = new Map();
146
153
  this._shouldScheduleGC = false;
147
- this._storeSubscriptions =
148
- RelayFeatureFlags.ENABLE_STORE_SUBSCRIPTIONS_REFACTOR === true
149
- ? new RelayStoreSubscriptionsUsingMapByID(options?.log)
150
- : 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
+ );
151
161
  this._updatedRecordIDs = new Set();
152
162
  this._shouldProcessClientComponents =
153
163
  options?.shouldProcessClientComponents;
@@ -159,12 +169,16 @@ class RelayModernStore implements Store {
159
169
  return this._optimisticSource ?? this._recordSource;
160
170
  }
161
171
 
172
+ _getMutableRecordSource(): MutableRecordSource {
173
+ return this._optimisticSource ?? this._recordSource;
174
+ }
175
+
162
176
  check(
163
177
  operation: OperationDescriptor,
164
178
  options?: CheckOptions,
165
179
  ): OperationAvailability {
166
180
  const selector = operation.root;
167
- const source = this._optimisticSource ?? this._recordSource;
181
+ const source = this._getMutableRecordSource();
168
182
  const globalInvalidationEpoch = this._globalInvalidationEpoch;
169
183
 
170
184
  const rootEntry = this._roots.get(operation.request.identifier);
@@ -173,7 +187,7 @@ class RelayModernStore implements Store {
173
187
  // Check if store has been globally invalidated
174
188
  if (globalInvalidationEpoch != null) {
175
189
  // If so, check if the operation we're checking was last written
176
- // before or after invalidation occured.
190
+ // before or after invalidation occurred.
177
191
  if (
178
192
  operationLastWrittenAt == null ||
179
193
  operationLastWrittenAt <= globalInvalidationEpoch
@@ -186,11 +200,24 @@ class RelayModernStore implements Store {
186
200
  }
187
201
  }
188
202
 
189
- const target = options?.target ?? source;
190
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
+
191
217
  const operationAvailability = DataChecker.check(
192
- source,
193
- target,
218
+ getSourceForActor,
219
+ getTargetForActor,
220
+ options?.defaultActorIdentifier ?? INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
194
221
  selector,
195
222
  handlers,
196
223
  this._operationLoader,
@@ -274,7 +301,7 @@ class RelayModernStore implements Store {
274
301
 
275
302
  lookup(selector: SingularReaderSelector): Snapshot {
276
303
  const source = this.getSource();
277
- const snapshot = RelayReader.read(source, selector);
304
+ const snapshot = RelayReader.read(source, selector, this._resolverCache);
278
305
  if (__DEV__) {
279
306
  deepFreeze(snapshot);
280
307
  }
@@ -302,6 +329,13 @@ class RelayModernStore implements Store {
302
329
  this._globalInvalidationEpoch = this._currentWriteEpoch;
303
330
  }
304
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
+ }
305
339
  const source = this.getSource();
306
340
  const updatedOwners = [];
307
341
  this._storeSubscriptions.updateSubscriptions(
@@ -364,7 +398,7 @@ class RelayModernStore implements Store {
364
398
  }
365
399
 
366
400
  publish(source: RecordSource, idsMarkedForInvalidation?: DataIDSet): void {
367
- const target = this._optimisticSource ?? this._recordSource;
401
+ const target = this._getMutableRecordSource();
368
402
  updateTargetFromSource(
369
403
  target,
370
404
  source,
@@ -417,6 +451,10 @@ class RelayModernStore implements Store {
417
451
  return 'RelayModernStore()';
418
452
  }
419
453
 
454
+ getEpoch(): number {
455
+ return this._currentWriteEpoch;
456
+ }
457
+
420
458
  // Internal API
421
459
  __getUpdatedRecordIDs(): DataIDSet {
422
460
  return this._updatedRecordIDs;