relay-runtime 11.0.1 → 13.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +2 -2
  2. package/handlers/connection/ConnectionHandler.js.flow +8 -17
  3. package/handlers/connection/MutationHandlers.js.flow +7 -11
  4. package/index.js +1 -1
  5. package/index.js.flow +60 -36
  6. package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
  7. package/lib/handlers/connection/ConnectionHandler.js +13 -19
  8. package/lib/handlers/connection/MutationHandlers.js +4 -7
  9. package/lib/index.js +58 -43
  10. package/lib/multi-actor-environment/ActorIdentifier.js +33 -0
  11. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +152 -0
  12. package/lib/multi-actor-environment/ActorUtils.js +27 -0
  13. package/lib/multi-actor-environment/MultiActorEnvironment.js +419 -0
  14. package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
  15. package/lib/multi-actor-environment/index.js +21 -0
  16. package/lib/mutations/RelayDeclarativeMutationConfig.js +4 -1
  17. package/lib/mutations/RelayRecordProxy.js +3 -2
  18. package/lib/mutations/RelayRecordSourceMutator.js +3 -2
  19. package/lib/mutations/RelayRecordSourceProxy.js +12 -4
  20. package/lib/mutations/RelayRecordSourceSelectorProxy.js +18 -5
  21. package/lib/mutations/applyOptimisticMutation.js +6 -6
  22. package/lib/mutations/commitMutation.js +14 -10
  23. package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +238 -0
  24. package/lib/mutations/validateMutation.js +10 -5
  25. package/lib/network/ConvertToExecuteFunction.js +2 -1
  26. package/lib/network/RelayNetwork.js +3 -2
  27. package/lib/network/RelayQueryResponseCache.js +21 -5
  28. package/lib/network/wrapNetworkWithLogObserver.js +79 -0
  29. package/lib/query/GraphQLTag.js +3 -2
  30. package/lib/query/fetchQuery.js +6 -5
  31. package/lib/query/fetchQueryInternal.js +1 -1
  32. package/lib/query/fetchQuery_DEPRECATED.js +2 -1
  33. package/lib/store/ClientID.js +7 -1
  34. package/lib/store/DataChecker.js +123 -54
  35. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +518 -200
  36. package/lib/store/RelayConcreteVariables.js +26 -8
  37. package/lib/store/RelayExperimentalGraphResponseHandler.js +153 -0
  38. package/lib/store/RelayExperimentalGraphResponseTransform.js +391 -0
  39. package/lib/store/RelayModernEnvironment.js +175 -240
  40. package/lib/store/RelayModernFragmentSpecResolver.js +52 -26
  41. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  42. package/lib/store/RelayModernRecord.js +47 -12
  43. package/lib/store/RelayModernSelector.js +14 -8
  44. package/lib/store/RelayModernStore.js +56 -28
  45. package/lib/store/RelayOperationTracker.js +34 -24
  46. package/lib/store/RelayPublishQueue.js +41 -13
  47. package/lib/store/RelayReader.js +288 -48
  48. package/lib/store/RelayRecordSource.js +87 -3
  49. package/lib/store/RelayReferenceMarker.js +34 -22
  50. package/lib/store/RelayResponseNormalizer.js +211 -110
  51. package/lib/store/RelayStoreReactFlightUtils.js +4 -10
  52. package/lib/store/RelayStoreSubscriptions.js +14 -9
  53. package/lib/store/RelayStoreUtils.js +12 -7
  54. package/lib/store/ResolverCache.js +213 -0
  55. package/lib/store/ResolverFragments.js +61 -0
  56. package/lib/store/cloneRelayHandleSourceField.js +5 -4
  57. package/lib/store/cloneRelayScalarHandleSourceField.js +5 -4
  58. package/lib/store/createRelayContext.js +4 -2
  59. package/lib/store/readInlineData.js +6 -2
  60. package/lib/subscription/requestSubscription.js +34 -25
  61. package/lib/util/RelayConcreteNode.js +3 -0
  62. package/lib/util/RelayFeatureFlags.js +10 -4
  63. package/lib/util/RelayProfiler.js +17 -187
  64. package/lib/util/RelayReplaySubject.js +22 -7
  65. package/lib/util/RelayRuntimeTypes.js +0 -6
  66. package/lib/util/StringInterner.js +71 -0
  67. package/lib/util/getFragmentIdentifier.js +15 -7
  68. package/lib/util/getOperation.js +2 -1
  69. package/lib/util/getPaginationMetadata.js +41 -0
  70. package/lib/util/getPaginationVariables.js +66 -0
  71. package/lib/util/getPendingOperationsForFragment.js +55 -0
  72. package/lib/util/getRefetchMetadata.js +36 -0
  73. package/lib/util/getRelayHandleKey.js +2 -2
  74. package/lib/util/getRequestIdentifier.js +2 -2
  75. package/lib/util/getValueAtPath.js +51 -0
  76. package/lib/util/isEmptyObject.js +1 -1
  77. package/lib/util/registerEnvironmentWithDevTools.js +26 -0
  78. package/lib/util/withDuration.js +31 -0
  79. package/multi-actor-environment/ActorIdentifier.js.flow +43 -0
  80. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +225 -0
  81. package/multi-actor-environment/ActorUtils.js.flow +33 -0
  82. package/multi-actor-environment/MultiActorEnvironment.js.flow +506 -0
  83. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +261 -0
  84. package/multi-actor-environment/index.js.flow +26 -0
  85. package/mutations/RelayDeclarativeMutationConfig.js.flow +32 -26
  86. package/mutations/RelayRecordProxy.js.flow +4 -5
  87. package/mutations/RelayRecordSourceMutator.js.flow +4 -6
  88. package/mutations/RelayRecordSourceProxy.js.flow +19 -10
  89. package/mutations/RelayRecordSourceSelectorProxy.js.flow +22 -7
  90. package/mutations/applyOptimisticMutation.js.flow +13 -14
  91. package/mutations/commitLocalUpdate.js.flow +1 -1
  92. package/mutations/commitMutation.js.flow +35 -46
  93. package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +309 -0
  94. package/mutations/validateMutation.js.flow +26 -16
  95. package/network/ConvertToExecuteFunction.js.flow +2 -2
  96. package/network/RelayNetwork.js.flow +4 -5
  97. package/network/RelayNetworkTypes.js.flow +5 -4
  98. package/network/RelayObservable.js.flow +1 -1
  99. package/network/RelayQueryResponseCache.js.flow +34 -21
  100. package/network/wrapNetworkWithLogObserver.js.flow +100 -0
  101. package/package.json +3 -2
  102. package/query/GraphQLTag.js.flow +9 -9
  103. package/query/PreloadableQueryRegistry.js.flow +2 -1
  104. package/query/fetchQuery.js.flow +11 -13
  105. package/query/fetchQueryInternal.js.flow +6 -9
  106. package/query/fetchQuery_DEPRECATED.js.flow +6 -6
  107. package/relay-runtime.js +2 -2
  108. package/relay-runtime.min.js +2 -2
  109. package/store/ClientID.js.flow +14 -3
  110. package/store/DataChecker.js.flow +141 -59
  111. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +605 -303
  112. package/store/RelayConcreteVariables.js.flow +27 -8
  113. package/store/RelayExperimentalGraphResponseHandler.js.flow +124 -0
  114. package/store/RelayExperimentalGraphResponseTransform.js.flow +475 -0
  115. package/store/RelayModernEnvironment.js.flow +173 -240
  116. package/store/RelayModernFragmentSpecResolver.js.flow +55 -31
  117. package/store/RelayModernOperationDescriptor.js.flow +12 -7
  118. package/store/RelayModernRecord.js.flow +67 -11
  119. package/store/RelayModernSelector.js.flow +24 -14
  120. package/store/RelayModernStore.js.flow +66 -36
  121. package/store/RelayOperationTracker.js.flow +59 -43
  122. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  123. package/store/RelayPublishQueue.js.flow +79 -34
  124. package/store/RelayReader.js.flow +351 -73
  125. package/store/RelayRecordSource.js.flow +72 -6
  126. package/store/RelayReferenceMarker.js.flow +40 -26
  127. package/store/RelayResponseNormalizer.js.flow +258 -99
  128. package/store/RelayStoreReactFlightUtils.js.flow +4 -11
  129. package/store/RelayStoreSubscriptions.js.flow +19 -11
  130. package/store/RelayStoreTypes.js.flow +209 -43
  131. package/store/RelayStoreUtils.js.flow +24 -11
  132. package/store/ResolverCache.js.flow +249 -0
  133. package/store/ResolverFragments.js.flow +121 -0
  134. package/store/StoreInspector.js.flow +2 -2
  135. package/store/TypeID.js.flow +1 -1
  136. package/store/ViewerPattern.js.flow +2 -2
  137. package/store/cloneRelayHandleSourceField.js.flow +5 -6
  138. package/store/cloneRelayScalarHandleSourceField.js.flow +5 -6
  139. package/store/createFragmentSpecResolver.js.flow +3 -4
  140. package/store/createRelayContext.js.flow +3 -3
  141. package/store/normalizeRelayPayload.js.flow +6 -7
  142. package/store/readInlineData.js.flow +7 -8
  143. package/subscription/requestSubscription.js.flow +53 -41
  144. package/util/NormalizationNode.js.flow +10 -3
  145. package/util/ReaderNode.js.flow +38 -2
  146. package/util/RelayConcreteNode.js.flow +5 -0
  147. package/util/RelayFeatureFlags.js.flow +24 -10
  148. package/util/RelayProfiler.js.flow +22 -194
  149. package/util/RelayReplaySubject.js.flow +9 -9
  150. package/util/RelayRuntimeTypes.js.flow +72 -3
  151. package/util/StringInterner.js.flow +69 -0
  152. package/util/createPayloadFor3DField.js.flow +3 -3
  153. package/util/getFragmentIdentifier.js.flow +27 -15
  154. package/util/getOperation.js.flow +2 -2
  155. package/util/getPaginationMetadata.js.flow +72 -0
  156. package/util/getPaginationVariables.js.flow +108 -0
  157. package/util/getPendingOperationsForFragment.js.flow +62 -0
  158. package/util/getRefetchMetadata.js.flow +79 -0
  159. package/util/getRelayHandleKey.js.flow +1 -2
  160. package/util/getRequestIdentifier.js.flow +3 -3
  161. package/util/getValueAtPath.js.flow +46 -0
  162. package/util/isEmptyObject.js.flow +1 -0
  163. package/util/registerEnvironmentWithDevTools.js.flow +33 -0
  164. package/util/resolveImmediate.js.flow +1 -1
  165. package/util/withDuration.js.flow +32 -0
  166. package/lib/store/RelayRecordSourceMapImpl.js +0 -107
  167. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
  168. package/store/RelayRecordSourceMapImpl.js.flow +0 -91
  169. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
@@ -12,29 +12,13 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
-
17
- const areEqual = require('areEqual');
18
- const invariant = require('invariant');
19
- const isScalarAndEqual = require('../util/isScalarAndEqual');
20
- const reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
21
- const warning = require('warning');
22
-
23
- const {getPromiseForActiveRequest} = require('../query/fetchQueryInternal');
24
- const {createRequestDescriptor} = require('./RelayModernOperationDescriptor');
25
- const {
26
- areEqualSelectors,
27
- createReaderSelector,
28
- getSelectorsFromObject,
29
- } = require('./RelayModernSelector');
30
-
31
15
  import type {ConcreteRequest} from '../util/RelayConcreteNode';
32
16
  import type {Disposable, Variables} from '../util/RelayRuntimeTypes';
33
17
  import type {
34
- IEnvironment,
35
18
  FragmentMap,
36
19
  FragmentSpecResolver,
37
20
  FragmentSpecResults,
21
+ IEnvironment,
38
22
  MissingRequiredFields,
39
23
  PluralReaderSelector,
40
24
  RelayContext,
@@ -43,10 +27,25 @@ import type {
43
27
  Snapshot,
44
28
  } from './RelayStoreTypes';
45
29
 
30
+ const getPendingOperationsForFragment = require('../util/getPendingOperationsForFragment');
31
+ const isScalarAndEqual = require('../util/isScalarAndEqual');
32
+ const recycleNodesInto = require('../util/recycleNodesInto');
33
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
34
+ const reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
35
+ const {createRequestDescriptor} = require('./RelayModernOperationDescriptor');
36
+ const {
37
+ areEqualSelectors,
38
+ createReaderSelector,
39
+ getSelectorsFromObject,
40
+ } = require('./RelayModernSelector');
41
+ const areEqual = require('areEqual');
42
+ const invariant = require('invariant');
43
+ const warning = require('warning');
44
+
46
45
  type Props = {[key: string]: mixed, ...};
47
46
  type Resolvers = {
48
47
  [key: string]: ?(SelectorListResolver | SelectorResolver),
49
- ...,
48
+ ...
50
49
  };
51
50
 
52
51
  /**
@@ -137,13 +136,16 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
137
136
  return this._data;
138
137
  }
139
138
 
140
- setCallback(callback: () => void): void {
139
+ setCallback(props: Props, callback: () => void): void {
141
140
  this._callback = callback;
141
+ if (RelayFeatureFlags.ENABLE_CONTAINERS_SUBSCRIBE_ON_COMMIT === true) {
142
+ this.setProps(props);
143
+ }
142
144
  }
143
145
 
144
146
  setProps(props: Props): void {
145
- const ownedSelectors = getSelectorsFromObject(this._fragments, props);
146
147
  this._props = {};
148
+ const ownedSelectors = getSelectorsFromObject(this._fragments, props);
147
149
 
148
150
  for (const key in ownedSelectors) {
149
151
  if (ownedSelectors.hasOwnProperty(key)) {
@@ -160,6 +162,7 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
160
162
  this._context.environment,
161
163
  this._rootIsQueryRenderer,
162
164
  ownedSelector,
165
+ this._callback != null,
163
166
  this._onChange,
164
167
  );
165
168
  } else {
@@ -176,6 +179,7 @@ class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
176
179
  this._context.environment,
177
180
  this._rootIsQueryRenderer,
178
181
  ownedSelector,
182
+ this._callback != null,
179
183
  this._onChange,
180
184
  );
181
185
  } else {
@@ -232,6 +236,7 @@ class SelectorResolver {
232
236
  environment: IEnvironment,
233
237
  rootIsQueryRenderer: boolean,
234
238
  selector: SingularReaderSelector,
239
+ subscribeOnConstruction: boolean,
235
240
  callback: () => void,
236
241
  ) {
237
242
  const snapshot = environment.lookup(selector);
@@ -242,7 +247,13 @@ class SelectorResolver {
242
247
  this._environment = environment;
243
248
  this._rootIsQueryRenderer = rootIsQueryRenderer;
244
249
  this._selector = selector;
245
- this._subscription = environment.subscribe(snapshot, this._onChange);
250
+ if (RelayFeatureFlags.ENABLE_CONTAINERS_SUBSCRIBE_ON_COMMIT === true) {
251
+ if (subscribeOnConstruction) {
252
+ this._subscription = environment.subscribe(snapshot, this._onChange);
253
+ }
254
+ } else {
255
+ this._subscription = environment.subscribe(snapshot, this._onChange);
256
+ }
246
257
  }
247
258
 
248
259
  dispose(): void {
@@ -253,10 +264,7 @@ class SelectorResolver {
253
264
  }
254
265
 
255
266
  resolve(): ?Object {
256
- if (
257
- RelayFeatureFlags.ENABLE_RELAY_CONTAINERS_SUSPENSE === true &&
258
- this._isMissingData === true
259
- ) {
267
+ if (this._isMissingData === true) {
260
268
  // NOTE: This branch exists to handle the case in which:
261
269
  // - A RelayModern container is rendered as a descendant of a Relay Hook
262
270
  // root using a "partial" renderPolicy (this means that eargerly
@@ -278,11 +286,12 @@ class SelectorResolver {
278
286
  // This should eventually go away with something like @optional, where we only
279
287
  // suspend at specific boundaries depending on whether the boundary
280
288
  // 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);
289
+ const pendingOperationsResult = getPendingOperationsForFragment(
290
+ this._environment,
291
+ this._selector.node,
292
+ this._selector.owner,
293
+ );
294
+ const promise: void | Promise<void> = pendingOperationsResult?.promise;
286
295
  if (promise != null) {
287
296
  if (this._rootIsQueryRenderer) {
288
297
  warning(
@@ -293,6 +302,8 @@ class SelectorResolver {
293
302
  this._selector.node.name,
294
303
  );
295
304
  } else {
305
+ const pendingOperations =
306
+ pendingOperationsResult?.pendingOperations ?? [];
296
307
  warning(
297
308
  false,
298
309
  'Relay: Relay Container for fragment `%s` suspended. When using ' +
@@ -300,6 +311,15 @@ class SelectorResolver {
300
311
  'of a Relay Container.',
301
312
  this._selector.node.name,
302
313
  );
314
+ this._environment.__log({
315
+ name: 'suspense.fragment',
316
+ data: this._data,
317
+ fragment: this._selector.node,
318
+ isRelayHooks: false,
319
+ isMissingData: this._isMissingData,
320
+ isPromiseCached: false,
321
+ pendingOperations,
322
+ });
303
323
  throw promise;
304
324
  }
305
325
  }
@@ -322,7 +342,7 @@ class SelectorResolver {
322
342
  }
323
343
  this.dispose();
324
344
  const snapshot = this._environment.lookup(selector);
325
- this._data = snapshot.data;
345
+ this._data = recycleNodesInto(this._data, snapshot.data);
326
346
  this._isMissingData = snapshot.isMissingData;
327
347
  this._missingRequiredFields = snapshot.missingRequiredFields;
328
348
  this._selector = selector;
@@ -375,11 +395,13 @@ class SelectorListResolver {
375
395
  _resolvers: Array<SelectorResolver>;
376
396
  _rootIsQueryRenderer: boolean;
377
397
  _stale: boolean;
398
+ _subscribeOnConstruction: boolean;
378
399
 
379
400
  constructor(
380
401
  environment: IEnvironment,
381
402
  rootIsQueryRenderer: boolean,
382
403
  selector: PluralReaderSelector,
404
+ subscribeOnConstruction: boolean,
383
405
  callback: () => void,
384
406
  ) {
385
407
  this._callback = callback;
@@ -388,6 +410,7 @@ class SelectorListResolver {
388
410
  this._resolvers = [];
389
411
  this._stale = true;
390
412
  this._rootIsQueryRenderer = rootIsQueryRenderer;
413
+ this._subscribeOnConstruction = subscribeOnConstruction;
391
414
 
392
415
  this.setSelector(selector);
393
416
  }
@@ -433,6 +456,7 @@ class SelectorListResolver {
433
456
  this._environment,
434
457
  this._rootIsQueryRenderer,
435
458
  selectors[ii],
459
+ this._subscribeOnConstruction,
436
460
  this._onChange,
437
461
  );
438
462
  }
@@ -12,9 +12,18 @@
12
12
 
13
13
  'use strict';
14
14
 
15
+ import type {ConcreteRequest} from '../util/RelayConcreteNode';
16
+ import type {
17
+ CacheConfig,
18
+ DataID,
19
+ OperationType,
20
+ Variables,
21
+ VariablesOf,
22
+ } from '../util/RelayRuntimeTypes';
23
+ import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
24
+
15
25
  const deepFreeze = require('../util/deepFreeze');
16
26
  const getRequestIdentifier = require('../util/getRequestIdentifier');
17
-
18
27
  const {getOperationVariables} = require('./RelayConcreteVariables');
19
28
  const {
20
29
  createNormalizationSelector,
@@ -22,19 +31,15 @@ const {
22
31
  } = require('./RelayModernSelector');
23
32
  const {ROOT_ID} = require('./RelayStoreUtils');
24
33
 
25
- import type {ConcreteRequest} from '../util/RelayConcreteNode';
26
- import type {CacheConfig, DataID, Variables} from '../util/RelayRuntimeTypes';
27
- import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
28
-
29
34
  /**
30
35
  * Creates an instance of the `OperationDescriptor` type defined in
31
36
  * `RelayStoreTypes` given an operation and some variables. The input variables
32
37
  * are filtered to exclude variables that do not match defined arguments on the
33
38
  * operation, and default values are populated for null values.
34
39
  */
35
- function createOperationDescriptor(
40
+ function createOperationDescriptor<TQuery: OperationType>(
36
41
  request: ConcreteRequest,
37
- variables: Variables,
42
+ variables: VariablesOf<TQuery>,
38
43
  cacheConfig?: ?CacheConfig,
39
44
  dataID?: DataID = ROOT_ID,
40
45
  ): OperationDescriptor {
@@ -12,23 +12,24 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const areEqual = require('areEqual');
16
- const deepFreeze = require('../util/deepFreeze');
17
- const invariant = require('invariant');
18
- const warning = require('warning');
15
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
16
+ import type {DataID} from '../util/RelayRuntimeTypes';
17
+ import type {Record} from './RelayStoreTypes';
19
18
 
19
+ const deepFreeze = require('../util/deepFreeze');
20
20
  const {isClientID} = require('./ClientID');
21
21
  const {
22
+ ACTOR_IDENTIFIER_KEY,
22
23
  ID_KEY,
24
+ INVALIDATED_AT_KEY,
23
25
  REF_KEY,
24
26
  REFS_KEY,
25
- TYPENAME_KEY,
26
- INVALIDATED_AT_KEY,
27
27
  ROOT_ID,
28
+ TYPENAME_KEY,
28
29
  } = require('./RelayStoreUtils');
29
-
30
- import type {DataID} from '../util/RelayRuntimeTypes';
31
- import type {Record} from './RelayStoreTypes';
30
+ const areEqual = require('areEqual');
31
+ const invariant = require('invariant');
32
+ const warning = require('warning');
32
33
 
33
34
  /**
34
35
  * @public
@@ -172,10 +173,14 @@ function getLinkedRecordID(record: Record, storageKey: string): ?DataID {
172
173
  invariant(
173
174
  typeof link === 'object' && link && typeof link[REF_KEY] === 'string',
174
175
  'RelayModernRecord.getLinkedRecordID(): Expected `%s.%s` to be a linked ID, ' +
175
- 'was `%s`.',
176
+ 'was `%s`.%s',
176
177
  record[ID_KEY],
177
178
  storageKey,
178
179
  JSON.stringify(link),
180
+ typeof link === 'object' && link[REFS_KEY] !== undefined
181
+ ? ' It appears to be a plural linked record: did you mean to call ' +
182
+ 'getLinkedRecords() instead of getLinkedRecord()?'
183
+ : '',
179
184
  );
180
185
  return link[REF_KEY];
181
186
  }
@@ -197,10 +202,14 @@ function getLinkedRecordIDs(
197
202
  invariant(
198
203
  typeof links === 'object' && Array.isArray(links[REFS_KEY]),
199
204
  'RelayModernRecord.getLinkedRecordIDs(): Expected `%s.%s` to contain an array ' +
200
- 'of linked IDs, got `%s`.',
205
+ 'of linked IDs, got `%s`.%s',
201
206
  record[ID_KEY],
202
207
  storageKey,
203
208
  JSON.stringify(links),
209
+ typeof links === 'object' && links[REF_KEY] !== undefined
210
+ ? ' It appears to be a singular linked record: did you mean to call ' +
211
+ 'getLinkedRecord() instead of getLinkedRecords()?'
212
+ : '',
204
213
  );
205
214
  // assume items of the array are ids
206
215
  return (links[REFS_KEY]: any);
@@ -384,6 +393,51 @@ function setLinkedRecordIDs(
384
393
  record[storageKey] = links;
385
394
  }
386
395
 
396
+ /**
397
+ * @public
398
+ *
399
+ * Set the value of a field to a reference to another record in the actor specific store.
400
+ */
401
+ function setActorLinkedRecordID(
402
+ record: Record,
403
+ storageKey: string,
404
+ actorIdentifier: ActorIdentifier,
405
+ linkedID: DataID,
406
+ ): void {
407
+ // See perf note above for why we aren't using computed property access.
408
+ const link = {};
409
+ link[REF_KEY] = linkedID;
410
+ link[ACTOR_IDENTIFIER_KEY] = actorIdentifier;
411
+ record[storageKey] = link;
412
+ }
413
+
414
+ /**
415
+ * @public
416
+ *
417
+ * Get link to a record and the actor identifier for the store.
418
+ */
419
+ function getActorLinkedRecordID(
420
+ record: Record,
421
+ storageKey: string,
422
+ ): ?[ActorIdentifier, DataID] {
423
+ const link = record[storageKey];
424
+ if (link == null) {
425
+ return link;
426
+ }
427
+ invariant(
428
+ typeof link === 'object' &&
429
+ typeof link[REF_KEY] === 'string' &&
430
+ link[ACTOR_IDENTIFIER_KEY] != null,
431
+ 'RelayModernRecord.getActorLinkedRecordID(): Expected `%s.%s` to be an actor specific linked ID, ' +
432
+ 'was `%s`.',
433
+ record[ID_KEY],
434
+ storageKey,
435
+ JSON.stringify(link),
436
+ );
437
+
438
+ return [(link[ACTOR_IDENTIFIER_KEY]: any), (link[REF_KEY]: any)];
439
+ }
440
+
387
441
  module.exports = {
388
442
  clone,
389
443
  copyFields,
@@ -400,4 +454,6 @@ module.exports = {
400
454
  setLinkedRecordID,
401
455
  setLinkedRecordIDs,
402
456
  update,
457
+ getActorLinkedRecordID,
458
+ setActorLinkedRecordID,
403
459
  };
@@ -12,22 +12,11 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const areEqual = require('areEqual');
16
- const invariant = require('invariant');
17
- const warning = require('warning');
18
-
19
- const {getFragmentVariables} = require('./RelayConcreteVariables');
20
- const {
21
- FRAGMENT_OWNER_KEY,
22
- FRAGMENTS_KEY,
23
- ID_KEY,
24
- IS_WITHIN_UNMATCHED_TYPE_REFINEMENT,
25
- } = require('./RelayStoreUtils');
26
-
27
15
  import type {NormalizationSelectableNode} from '../util/NormalizationNode';
28
16
  import type {ReaderFragment} from '../util/ReaderNode';
29
17
  import type {DataID, Variables} from '../util/RelayRuntimeTypes';
30
18
  import type {
19
+ ClientEdgeTraversalPath,
31
20
  NormalizationSelector,
32
21
  PluralReaderSelector,
33
22
  ReaderSelector,
@@ -35,6 +24,18 @@ import type {
35
24
  SingularReaderSelector,
36
25
  } from './RelayStoreTypes';
37
26
 
27
+ const {getFragmentVariables} = require('./RelayConcreteVariables');
28
+ const {
29
+ CLIENT_EDGE_TRAVERSAL_PATH,
30
+ FRAGMENT_OWNER_KEY,
31
+ FRAGMENTS_KEY,
32
+ ID_KEY,
33
+ IS_WITHIN_UNMATCHED_TYPE_REFINEMENT,
34
+ } = require('./RelayStoreUtils');
35
+ const areEqual = require('areEqual');
36
+ const invariant = require('invariant');
37
+ const warning = require('warning');
38
+
38
39
  /**
39
40
  * @public
40
41
  *
@@ -80,6 +81,7 @@ function getSingularSelector(
80
81
  const mixedOwner = item[FRAGMENT_OWNER_KEY];
81
82
  const isWithinUnmatchedTypeRefinement =
82
83
  item[IS_WITHIN_UNMATCHED_TYPE_REFINEMENT] === true;
84
+ const mixedClientEdgeTraversalPath = item[CLIENT_EDGE_TRAVERSAL_PATH];
83
85
  if (
84
86
  typeof dataID === 'string' &&
85
87
  typeof fragments === 'object' &&
@@ -87,22 +89,28 @@ function getSingularSelector(
87
89
  typeof fragments[fragment.name] === 'object' &&
88
90
  fragments[fragment.name] !== null &&
89
91
  typeof mixedOwner === 'object' &&
90
- mixedOwner !== null
92
+ mixedOwner !== null &&
93
+ (mixedClientEdgeTraversalPath == null ||
94
+ Array.isArray(mixedClientEdgeTraversalPath))
91
95
  ) {
92
96
  const owner: RequestDescriptor = (mixedOwner: $FlowFixMe);
93
- const argumentVariables = fragments[fragment.name];
97
+ const clientEdgeTraversalPath: ?ClientEdgeTraversalPath =
98
+ (mixedClientEdgeTraversalPath: $FlowFixMe);
94
99
 
100
+ const argumentVariables = fragments[fragment.name];
95
101
  const fragmentVariables = getFragmentVariables(
96
102
  fragment,
97
103
  owner.variables,
98
104
  argumentVariables,
99
105
  );
106
+
100
107
  return createReaderSelector(
101
108
  fragment,
102
109
  dataID,
103
110
  fragmentVariables,
104
111
  owner,
105
112
  isWithinUnmatchedTypeRefinement,
113
+ clientEdgeTraversalPath,
106
114
  );
107
115
  }
108
116
 
@@ -419,11 +427,13 @@ function createReaderSelector(
419
427
  variables: Variables,
420
428
  request: RequestDescriptor,
421
429
  isWithinUnmatchedTypeRefinement: boolean = false,
430
+ clientEdgeTraversalPath: ?ClientEdgeTraversalPath,
422
431
  ): SingularReaderSelector {
423
432
  return {
424
433
  kind: 'SingularReaderSelector',
425
434
  dataID,
426
435
  isWithinUnmatchedTypeRefinement,
436
+ clientEdgeTraversalPath: clientEdgeTraversalPath ?? null,
427
437
  node: fragment,
428
438
  variables,
429
439
  owner: request,
@@ -12,30 +12,13 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const DataChecker = require('./DataChecker');
16
- const RelayFeatureFlags = require('../util/RelayFeatureFlags');
17
- const RelayModernRecord = require('./RelayModernRecord');
18
- const RelayOptimisticRecordSource = require('./RelayOptimisticRecordSource');
19
- const RelayProfiler = require('../util/RelayProfiler');
20
- const RelayReader = require('./RelayReader');
21
- const RelayReferenceMarker = require('./RelayReferenceMarker');
22
- const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
23
- const RelayStoreSubscriptions = require('./RelayStoreSubscriptions');
24
- const RelayStoreSubscriptionsUsingMapByID = require('./RelayStoreSubscriptionsUsingMapByID');
25
- const RelayStoreUtils = require('./RelayStoreUtils');
26
-
27
- const deepFreeze = require('../util/deepFreeze');
28
- const defaultGetDataID = require('./defaultGetDataID');
29
- const invariant = require('invariant');
30
- const resolveImmediate = require('../util/resolveImmediate');
31
-
32
- const {ROOT_ID, ROOT_TYPE} = require('./RelayStoreUtils');
33
-
15
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
34
16
  import type {DataID, Disposable} from '../util/RelayRuntimeTypes';
35
17
  import type {Availability} from './DataChecker';
36
18
  import type {GetDataID} from './RelayResponseNormalizer';
37
19
  import type {
38
20
  CheckOptions,
21
+ DataIDSet,
39
22
  LogFunction,
40
23
  MutableRecordSource,
41
24
  OperationAvailability,
@@ -48,8 +31,28 @@ import type {
48
31
  Snapshot,
49
32
  Store,
50
33
  StoreSubscriptions,
51
- DataIDSet,
52
34
  } from './RelayStoreTypes';
35
+ import type {ResolverCache} from './ResolverCache';
36
+
37
+ const {
38
+ INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
39
+ assertInternalActorIndentifier,
40
+ } = require('../multi-actor-environment/ActorIdentifier');
41
+ const deepFreeze = require('../util/deepFreeze');
42
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
43
+ const resolveImmediate = require('../util/resolveImmediate');
44
+ const DataChecker = require('./DataChecker');
45
+ const defaultGetDataID = require('./defaultGetDataID');
46
+ const RelayModernRecord = require('./RelayModernRecord');
47
+ const RelayOptimisticRecordSource = require('./RelayOptimisticRecordSource');
48
+ const RelayReader = require('./RelayReader');
49
+ const RelayReferenceMarker = require('./RelayReferenceMarker');
50
+ const RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
51
+ const RelayStoreSubscriptions = require('./RelayStoreSubscriptions');
52
+ const RelayStoreUtils = require('./RelayStoreUtils');
53
+ const {ROOT_ID, ROOT_TYPE} = require('./RelayStoreUtils');
54
+ const {RecordResolverCache} = require('./ResolverCache');
55
+ const invariant = require('invariant');
53
56
 
54
57
  export opaque type InvalidationState = {|
55
58
  dataIDs: $ReadOnlyArray<DataID>,
@@ -90,6 +93,7 @@ class RelayModernStore implements Store {
90
93
  _operationLoader: ?OperationLoader;
91
94
  _optimisticSource: ?MutableRecordSource;
92
95
  _recordSource: MutableRecordSource;
96
+ _resolverCache: ResolverCache;
93
97
  _releaseBuffer: Array<string>;
94
98
  _roots: Map<
95
99
  string,
@@ -145,10 +149,13 @@ class RelayModernStore implements Store {
145
149
  this._releaseBuffer = [];
146
150
  this._roots = new Map();
147
151
  this._shouldScheduleGC = false;
148
- this._storeSubscriptions =
149
- RelayFeatureFlags.ENABLE_STORE_SUBSCRIPTIONS_REFACTOR === true
150
- ? new RelayStoreSubscriptionsUsingMapByID(options?.log)
151
- : new RelayStoreSubscriptions(options?.log);
152
+ this._resolverCache = new RecordResolverCache(() =>
153
+ this._getMutableRecordSource(),
154
+ );
155
+ this._storeSubscriptions = new RelayStoreSubscriptions(
156
+ options?.log,
157
+ this._resolverCache,
158
+ );
152
159
  this._updatedRecordIDs = new Set();
153
160
  this._shouldProcessClientComponents =
154
161
  options?.shouldProcessClientComponents;
@@ -160,12 +167,16 @@ class RelayModernStore implements Store {
160
167
  return this._optimisticSource ?? this._recordSource;
161
168
  }
162
169
 
170
+ _getMutableRecordSource(): MutableRecordSource {
171
+ return this._optimisticSource ?? this._recordSource;
172
+ }
173
+
163
174
  check(
164
175
  operation: OperationDescriptor,
165
176
  options?: CheckOptions,
166
177
  ): OperationAvailability {
167
178
  const selector = operation.root;
168
- const source = this._optimisticSource ?? this._recordSource;
179
+ const source = this._getMutableRecordSource();
169
180
  const globalInvalidationEpoch = this._globalInvalidationEpoch;
170
181
 
171
182
  const rootEntry = this._roots.get(operation.request.identifier);
@@ -174,7 +185,7 @@ class RelayModernStore implements Store {
174
185
  // Check if store has been globally invalidated
175
186
  if (globalInvalidationEpoch != null) {
176
187
  // If so, check if the operation we're checking was last written
177
- // before or after invalidation occured.
188
+ // before or after invalidation occurred.
178
189
  if (
179
190
  operationLastWrittenAt == null ||
180
191
  operationLastWrittenAt <= globalInvalidationEpoch
@@ -187,11 +198,24 @@ class RelayModernStore implements Store {
187
198
  }
188
199
  }
189
200
 
190
- const target = options?.target ?? source;
191
201
  const handlers = options?.handlers ?? [];
202
+ const getSourceForActor =
203
+ options?.getSourceForActor ??
204
+ (actorIdentifier => {
205
+ assertInternalActorIndentifier(actorIdentifier);
206
+ return source;
207
+ });
208
+ const getTargetForActor =
209
+ options?.getTargetForActor ??
210
+ (actorIdentifier => {
211
+ assertInternalActorIndentifier(actorIdentifier);
212
+ return source;
213
+ });
214
+
192
215
  const operationAvailability = DataChecker.check(
193
- source,
194
- target,
216
+ getSourceForActor,
217
+ getTargetForActor,
218
+ options?.defaultActorIdentifier ?? INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
195
219
  selector,
196
220
  handlers,
197
221
  this._operationLoader,
@@ -275,7 +299,7 @@ class RelayModernStore implements Store {
275
299
 
276
300
  lookup(selector: SingularReaderSelector): Snapshot {
277
301
  const source = this.getSource();
278
- const snapshot = RelayReader.read(source, selector);
302
+ const snapshot = RelayReader.read(source, selector, this._resolverCache);
279
303
  if (__DEV__) {
280
304
  deepFreeze(snapshot);
281
305
  }
@@ -303,6 +327,13 @@ class RelayModernStore implements Store {
303
327
  this._globalInvalidationEpoch = this._currentWriteEpoch;
304
328
  }
305
329
 
330
+ if (RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
331
+ // When a record is updated, we need to also handle records that depend on it,
332
+ // specifically Relay Resolver result records containing results based on the
333
+ // updated records. This both adds to updatedRecordIDs and invalidates any
334
+ // cached data as needed.
335
+ this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
336
+ }
306
337
  const source = this.getSource();
307
338
  const updatedOwners = [];
308
339
  this._storeSubscriptions.updateSubscriptions(
@@ -365,7 +396,7 @@ class RelayModernStore implements Store {
365
396
  }
366
397
 
367
398
  publish(source: RecordSource, idsMarkedForInvalidation?: DataIDSet): void {
368
- const target = this._optimisticSource ?? this._recordSource;
399
+ const target = this._getMutableRecordSource();
369
400
  updateTargetFromSource(
370
401
  target,
371
402
  source,
@@ -418,6 +449,10 @@ class RelayModernStore implements Store {
418
449
  return 'RelayModernStore()';
419
450
  }
420
451
 
452
+ getEpoch(): number {
453
+ return this._currentWriteEpoch;
454
+ }
455
+
421
456
  // Internal API
422
457
  __getUpdatedRecordIDs(): DataIDSet {
423
458
  return this._updatedRecordIDs;
@@ -678,7 +713,6 @@ function updateTargetFromSource(
678
713
  currentWriteEpoch,
679
714
  );
680
715
  invalidatedRecordIDs.add(dataID);
681
- // $FlowFixMe[incompatible-call]
682
716
  target.set(dataID, nextRecord);
683
717
  });
684
718
  }
@@ -770,8 +804,4 @@ function getAvailabilityStatus(
770
804
  return {status: 'available', fetchTime: operationFetchTime ?? null};
771
805
  }
772
806
 
773
- RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
774
- lookup: 'RelayModernStore.prototype.lookup',
775
- });
776
-
777
807
  module.exports = RelayModernStore;