relay-runtime 11.0.0 → 13.0.0-rc.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 (169) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +2 -2
  2. package/handlers/connection/ConnectionHandler.js.flow +8 -10
  3. package/handlers/connection/MutationHandlers.js.flow +31 -7
  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 +8 -8
  8. package/lib/handlers/connection/MutationHandlers.js +61 -5
  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 +12 -5
  25. package/lib/network/ConvertToExecuteFunction.js +2 -1
  26. package/lib/network/RelayNetwork.js +3 -2
  27. package/lib/network/RelayQueryResponseCache.js +21 -4
  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 +141 -60
  35. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +532 -195
  36. package/lib/store/RelayConcreteVariables.js +24 -4
  37. package/lib/store/RelayModernEnvironment.js +175 -234
  38. package/lib/store/RelayModernFragmentSpecResolver.js +52 -26
  39. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  40. package/lib/store/RelayModernRecord.js +47 -12
  41. package/lib/store/RelayModernSelector.js +14 -8
  42. package/lib/store/RelayModernStore.js +58 -29
  43. package/lib/store/RelayOperationTracker.js +34 -24
  44. package/lib/store/RelayPublishQueue.js +41 -13
  45. package/lib/store/RelayReader.js +287 -46
  46. package/lib/store/RelayRecordSource.js +87 -3
  47. package/lib/store/RelayReferenceMarker.js +55 -31
  48. package/lib/store/RelayResponseNormalizer.js +250 -108
  49. package/lib/store/RelayStoreReactFlightUtils.js +8 -12
  50. package/lib/store/RelayStoreSubscriptions.js +14 -9
  51. package/lib/store/RelayStoreUtils.js +11 -5
  52. package/lib/store/ResolverCache.js +213 -0
  53. package/lib/store/ResolverFragments.js +61 -0
  54. package/lib/store/cloneRelayHandleSourceField.js +5 -4
  55. package/lib/store/cloneRelayScalarHandleSourceField.js +5 -4
  56. package/lib/store/createRelayContext.js +4 -2
  57. package/lib/store/defaultGetDataID.js +3 -1
  58. package/lib/store/readInlineData.js +6 -2
  59. package/lib/subscription/requestSubscription.js +35 -9
  60. package/lib/util/RelayConcreteNode.js +4 -0
  61. package/lib/util/RelayFeatureFlags.js +11 -4
  62. package/lib/util/RelayProfiler.js +17 -187
  63. package/lib/util/RelayReplaySubject.js +22 -7
  64. package/lib/util/RelayRuntimeTypes.js +0 -6
  65. package/lib/util/StringInterner.js +71 -0
  66. package/lib/util/deepFreeze.js +1 -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 +28 -16
  95. package/network/ConvertToExecuteFunction.js.flow +2 -2
  96. package/network/RelayNetwork.js.flow +4 -5
  97. package/network/RelayNetworkTypes.js.flow +17 -8
  98. package/network/RelayObservable.js.flow +1 -1
  99. package/network/RelayQueryResponseCache.js.flow +34 -20
  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 +162 -67
  111. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +616 -283
  112. package/store/RelayConcreteVariables.js.flow +27 -5
  113. package/store/RelayModernEnvironment.js.flow +176 -235
  114. package/store/RelayModernFragmentSpecResolver.js.flow +55 -31
  115. package/store/RelayModernOperationDescriptor.js.flow +12 -7
  116. package/store/RelayModernRecord.js.flow +67 -11
  117. package/store/RelayModernSelector.js.flow +24 -14
  118. package/store/RelayModernStore.js.flow +72 -36
  119. package/store/RelayOperationTracker.js.flow +59 -43
  120. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  121. package/store/RelayPublishQueue.js.flow +79 -34
  122. package/store/RelayReader.js.flow +351 -72
  123. package/store/RelayRecordSource.js.flow +72 -6
  124. package/store/RelayReferenceMarker.js.flow +60 -33
  125. package/store/RelayResponseNormalizer.js.flow +288 -102
  126. package/store/RelayStoreReactFlightUtils.js.flow +9 -13
  127. package/store/RelayStoreSubscriptions.js.flow +19 -11
  128. package/store/RelayStoreTypes.js.flow +210 -44
  129. package/store/RelayStoreUtils.js.flow +25 -11
  130. package/store/ResolverCache.js.flow +249 -0
  131. package/store/ResolverFragments.js.flow +121 -0
  132. package/store/StoreInspector.js.flow +2 -2
  133. package/store/TypeID.js.flow +1 -1
  134. package/store/ViewerPattern.js.flow +2 -2
  135. package/store/cloneRelayHandleSourceField.js.flow +5 -6
  136. package/store/cloneRelayScalarHandleSourceField.js.flow +5 -6
  137. package/store/createFragmentSpecResolver.js.flow +3 -4
  138. package/store/createRelayContext.js.flow +3 -3
  139. package/store/defaultGetDataID.js.flow +3 -1
  140. package/store/normalizeRelayPayload.js.flow +6 -7
  141. package/store/readInlineData.js.flow +7 -8
  142. package/subscription/requestSubscription.js.flow +54 -27
  143. package/util/NormalizationNode.js.flow +16 -3
  144. package/util/ReaderNode.js.flow +38 -2
  145. package/util/RelayConcreteNode.js.flow +4 -0
  146. package/util/RelayFeatureFlags.js.flow +24 -8
  147. package/util/RelayProfiler.js.flow +22 -194
  148. package/util/RelayReplaySubject.js.flow +9 -9
  149. package/util/RelayRuntimeTypes.js.flow +73 -4
  150. package/util/StringInterner.js.flow +69 -0
  151. package/util/createPayloadFor3DField.js.flow +3 -3
  152. package/util/deepFreeze.js.flow +2 -1
  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 +2 -1
  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,
@@ -103,6 +107,7 @@ class RelayModernStore implements Store {
103
107
  _shouldScheduleGC: boolean;
104
108
  _storeSubscriptions: StoreSubscriptions;
105
109
  _updatedRecordIDs: DataIDSet;
110
+ _shouldProcessClientComponents: ?boolean;
106
111
 
107
112
  constructor(
108
113
  source: MutableRecordSource,
@@ -113,6 +118,7 @@ class RelayModernStore implements Store {
113
118
  getDataID?: ?GetDataID,
114
119
  gcReleaseBufferSize?: ?number,
115
120
  queryCacheExpirationTime?: ?number,
121
+ shouldProcessClientComponents?: ?boolean,
116
122
  |},
117
123
  ) {
118
124
  // Prevent mutation of a record from outside the store.
@@ -143,11 +149,16 @@ class RelayModernStore implements Store {
143
149
  this._releaseBuffer = [];
144
150
  this._roots = new Map();
145
151
  this._shouldScheduleGC = false;
146
- this._storeSubscriptions =
147
- RelayFeatureFlags.ENABLE_STORE_SUBSCRIPTIONS_REFACTOR === true
148
- ? new RelayStoreSubscriptionsUsingMapByID(options?.log)
149
- : 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
+ );
150
159
  this._updatedRecordIDs = new Set();
160
+ this._shouldProcessClientComponents =
161
+ options?.shouldProcessClientComponents;
151
162
 
152
163
  initializeRecordSource(this._recordSource);
153
164
  }
@@ -156,12 +167,16 @@ class RelayModernStore implements Store {
156
167
  return this._optimisticSource ?? this._recordSource;
157
168
  }
158
169
 
170
+ _getMutableRecordSource(): MutableRecordSource {
171
+ return this._optimisticSource ?? this._recordSource;
172
+ }
173
+
159
174
  check(
160
175
  operation: OperationDescriptor,
161
176
  options?: CheckOptions,
162
177
  ): OperationAvailability {
163
178
  const selector = operation.root;
164
- const source = this._optimisticSource ?? this._recordSource;
179
+ const source = this._getMutableRecordSource();
165
180
  const globalInvalidationEpoch = this._globalInvalidationEpoch;
166
181
 
167
182
  const rootEntry = this._roots.get(operation.request.identifier);
@@ -170,7 +185,7 @@ class RelayModernStore implements Store {
170
185
  // Check if store has been globally invalidated
171
186
  if (globalInvalidationEpoch != null) {
172
187
  // If so, check if the operation we're checking was last written
173
- // before or after invalidation occured.
188
+ // before or after invalidation occurred.
174
189
  if (
175
190
  operationLastWrittenAt == null ||
176
191
  operationLastWrittenAt <= globalInvalidationEpoch
@@ -183,15 +198,29 @@ class RelayModernStore implements Store {
183
198
  }
184
199
  }
185
200
 
186
- const target = options?.target ?? source;
187
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
+
188
215
  const operationAvailability = DataChecker.check(
189
- source,
190
- target,
216
+ getSourceForActor,
217
+ getTargetForActor,
218
+ options?.defaultActorIdentifier ?? INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
191
219
  selector,
192
220
  handlers,
193
221
  this._operationLoader,
194
222
  this._getDataID,
223
+ this._shouldProcessClientComponents,
195
224
  );
196
225
 
197
226
  return getAvailabilityStatus(
@@ -270,7 +299,7 @@ class RelayModernStore implements Store {
270
299
 
271
300
  lookup(selector: SingularReaderSelector): Snapshot {
272
301
  const source = this.getSource();
273
- const snapshot = RelayReader.read(source, selector);
302
+ const snapshot = RelayReader.read(source, selector, this._resolverCache);
274
303
  if (__DEV__) {
275
304
  deepFreeze(snapshot);
276
305
  }
@@ -298,6 +327,13 @@ class RelayModernStore implements Store {
298
327
  this._globalInvalidationEpoch = this._currentWriteEpoch;
299
328
  }
300
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
+ }
301
337
  const source = this.getSource();
302
338
  const updatedOwners = [];
303
339
  this._storeSubscriptions.updateSubscriptions(
@@ -360,7 +396,7 @@ class RelayModernStore implements Store {
360
396
  }
361
397
 
362
398
  publish(source: RecordSource, idsMarkedForInvalidation?: DataIDSet): void {
363
- const target = this._optimisticSource ?? this._recordSource;
399
+ const target = this._getMutableRecordSource();
364
400
  updateTargetFromSource(
365
401
  target,
366
402
  source,
@@ -413,6 +449,10 @@ class RelayModernStore implements Store {
413
449
  return 'RelayModernStore()';
414
450
  }
415
451
 
452
+ getEpoch(): number {
453
+ return this._currentWriteEpoch;
454
+ }
455
+
416
456
  // Internal API
417
457
  __getUpdatedRecordIDs(): DataIDSet {
418
458
  return this._updatedRecordIDs;
@@ -574,6 +614,7 @@ class RelayModernStore implements Store {
574
614
  selector,
575
615
  references,
576
616
  this._operationLoader,
617
+ this._shouldProcessClientComponents,
577
618
  );
578
619
  // Yield for other work after each operation
579
620
  yield;
@@ -672,7 +713,6 @@ function updateTargetFromSource(
672
713
  currentWriteEpoch,
673
714
  );
674
715
  invalidatedRecordIDs.add(dataID);
675
- // $FlowFixMe[incompatible-call]
676
716
  target.set(dataID, nextRecord);
677
717
  });
678
718
  }
@@ -764,8 +804,4 @@ function getAvailabilityStatus(
764
804
  return {status: 'available', fetchTime: operationFetchTime ?? null};
765
805
  }
766
806
 
767
- RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
768
- lookup: 'RelayModernStore.prototype.lookup',
769
- });
770
-
771
807
  module.exports = RelayModernStore;