relay-runtime 11.0.0-rc.0 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/handlers/connection/ConnectionHandler.js.flow +7 -0
  2. package/handlers/connection/MutationHandlers.js.flow +28 -0
  3. package/index.js +1 -1
  4. package/index.js.flow +20 -3
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
  6. package/lib/handlers/connection/ConnectionHandler.js +12 -6
  7. package/lib/handlers/connection/MutationHandlers.js +67 -8
  8. package/lib/index.js +15 -0
  9. package/lib/multi-actor-environment/ActorIdentifier.js +33 -0
  10. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +148 -0
  11. package/lib/multi-actor-environment/ActorUtils.js +27 -0
  12. package/lib/multi-actor-environment/MultiActorEnvironment.js +406 -0
  13. package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
  14. package/lib/multi-actor-environment/index.js +21 -0
  15. package/lib/mutations/RelayRecordProxy.js +1 -1
  16. package/lib/mutations/RelayRecordSourceMutator.js +1 -1
  17. package/lib/mutations/RelayRecordSourceProxy.js +1 -1
  18. package/lib/mutations/RelayRecordSourceSelectorProxy.js +7 -2
  19. package/lib/mutations/applyOptimisticMutation.js +1 -1
  20. package/lib/mutations/commitMutation.js +5 -2
  21. package/lib/mutations/validateMutation.js +39 -17
  22. package/lib/network/RelayNetwork.js +1 -1
  23. package/lib/network/RelayObservable.js +3 -1
  24. package/lib/network/RelayQueryResponseCache.js +20 -3
  25. package/lib/network/wrapNetworkWithLogObserver.js +78 -0
  26. package/lib/query/GraphQLTag.js +1 -1
  27. package/lib/query/fetchQuery.js +1 -1
  28. package/lib/query/fetchQueryInternal.js +1 -1
  29. package/lib/store/DataChecker.js +132 -50
  30. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +524 -187
  31. package/lib/store/RelayConcreteVariables.js +29 -4
  32. package/lib/store/RelayModernEnvironment.js +137 -220
  33. package/lib/store/RelayModernFragmentSpecResolver.js +49 -23
  34. package/lib/store/RelayModernRecord.js +36 -2
  35. package/lib/store/RelayModernSelector.js +1 -1
  36. package/lib/store/RelayModernStore.js +53 -22
  37. package/lib/store/RelayOperationTracker.js +34 -24
  38. package/lib/store/RelayPublishQueue.js +30 -8
  39. package/lib/store/RelayReader.js +177 -29
  40. package/lib/store/RelayRecordSource.js +87 -3
  41. package/lib/store/RelayReferenceMarker.js +53 -28
  42. package/lib/store/RelayResponseNormalizer.js +247 -108
  43. package/lib/store/RelayStoreReactFlightUtils.js +7 -11
  44. package/lib/store/RelayStoreSubscriptions.js +8 -5
  45. package/lib/store/RelayStoreUtils.js +10 -4
  46. package/lib/store/ResolverCache.js +213 -0
  47. package/lib/store/ResolverFragments.js +57 -0
  48. package/lib/store/cloneRelayHandleSourceField.js +1 -1
  49. package/lib/store/cloneRelayScalarHandleSourceField.js +1 -1
  50. package/lib/store/createRelayContext.js +2 -2
  51. package/lib/store/defaultGetDataID.js +3 -1
  52. package/lib/store/readInlineData.js +1 -1
  53. package/lib/subscription/requestSubscription.js +32 -6
  54. package/lib/util/RelayConcreteNode.js +3 -0
  55. package/lib/util/RelayFeatureFlags.js +5 -4
  56. package/lib/util/RelayProfiler.js +17 -187
  57. package/lib/util/RelayReplaySubject.js +22 -7
  58. package/lib/util/deepFreeze.js +1 -0
  59. package/lib/util/getPaginationMetadata.js +41 -0
  60. package/lib/util/getPaginationVariables.js +67 -0
  61. package/lib/util/getPendingOperationsForFragment.js +55 -0
  62. package/lib/util/getRefetchMetadata.js +36 -0
  63. package/lib/util/getRelayHandleKey.js +1 -1
  64. package/lib/util/getRequestIdentifier.js +1 -1
  65. package/lib/util/getValueAtPath.js +51 -0
  66. package/lib/util/isEmptyObject.js +1 -1
  67. package/lib/util/registerEnvironmentWithDevTools.js +26 -0
  68. package/lib/util/withDuration.js +31 -0
  69. package/multi-actor-environment/ActorIdentifier.js.flow +43 -0
  70. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +217 -0
  71. package/multi-actor-environment/ActorUtils.js.flow +33 -0
  72. package/multi-actor-environment/MultiActorEnvironment.js.flow +485 -0
  73. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +245 -0
  74. package/multi-actor-environment/index.js.flow +27 -0
  75. package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -2
  76. package/mutations/commitMutation.js.flow +3 -1
  77. package/mutations/validateMutation.js.flow +42 -16
  78. package/network/RelayNetworkTypes.js.flow +17 -8
  79. package/network/RelayObservable.js.flow +2 -0
  80. package/network/RelayQueryResponseCache.js.flow +31 -17
  81. package/network/wrapNetworkWithLogObserver.js.flow +99 -0
  82. package/package.json +3 -2
  83. package/relay-runtime.js +2 -2
  84. package/relay-runtime.min.js +2 -2
  85. package/store/ClientID.js.flow +5 -1
  86. package/store/DataChecker.js.flow +148 -44
  87. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +578 -237
  88. package/store/RelayConcreteVariables.js.flow +31 -1
  89. package/store/RelayModernEnvironment.js.flow +132 -220
  90. package/store/RelayModernFragmentSpecResolver.js.flow +40 -14
  91. package/store/RelayModernOperationDescriptor.js.flow +9 -3
  92. package/store/RelayModernRecord.js.flow +49 -0
  93. package/store/RelayModernStore.js.flow +57 -17
  94. package/store/RelayOperationTracker.js.flow +56 -34
  95. package/store/RelayPublishQueue.js.flow +37 -11
  96. package/store/RelayReader.js.flow +186 -27
  97. package/store/RelayRecordSource.js.flow +72 -6
  98. package/store/RelayReferenceMarker.js.flow +51 -21
  99. package/store/RelayResponseNormalizer.js.flow +251 -67
  100. package/store/RelayStoreReactFlightUtils.js.flow +6 -9
  101. package/store/RelayStoreSubscriptions.js.flow +10 -3
  102. package/store/RelayStoreTypes.js.flow +144 -21
  103. package/store/RelayStoreUtils.js.flow +19 -4
  104. package/store/ResolverCache.js.flow +247 -0
  105. package/store/ResolverFragments.js.flow +128 -0
  106. package/store/createRelayContext.js.flow +1 -1
  107. package/store/defaultGetDataID.js.flow +3 -1
  108. package/subscription/requestSubscription.js.flow +43 -8
  109. package/util/NormalizationNode.js.flow +16 -3
  110. package/util/ReaderNode.js.flow +29 -2
  111. package/util/RelayConcreteNode.js.flow +3 -0
  112. package/util/RelayFeatureFlags.js.flow +10 -6
  113. package/util/RelayProfiler.js.flow +22 -194
  114. package/util/RelayReplaySubject.js.flow +7 -6
  115. package/util/RelayRuntimeTypes.js.flow +4 -2
  116. package/util/deepFreeze.js.flow +2 -1
  117. package/util/getPaginationMetadata.js.flow +74 -0
  118. package/util/getPaginationVariables.js.flow +112 -0
  119. package/util/getPendingOperationsForFragment.js.flow +62 -0
  120. package/util/getRefetchMetadata.js.flow +80 -0
  121. package/util/getValueAtPath.js.flow +46 -0
  122. package/util/isEmptyObject.js.flow +2 -1
  123. package/util/registerEnvironmentWithDevTools.js.flow +33 -0
  124. package/util/withDuration.js.flow +32 -0
  125. package/lib/store/RelayRecordSourceMapImpl.js +0 -107
  126. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
  127. package/store/RelayRecordSourceMapImpl.js.flow +0 -91
  128. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
@@ -12,7 +12,6 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const ErrorUtils = require('ErrorUtils');
16
15
  const RelayReader = require('./RelayReader');
17
16
  const RelayRecordSource = require('./RelayRecordSource');
18
17
  const RelayRecordSourceMutator = require('../mutations/RelayRecordSourceMutator');
@@ -55,6 +54,10 @@ type PendingUpdater = {|
55
54
  +updater: StoreUpdater,
56
55
  |};
57
56
 
57
+ const applyWithGuard =
58
+ global?.ErrorUtils?.applyWithGuard ??
59
+ ((callback, context, args, onError, name) => callback.apply(context, args));
60
+
58
61
  /**
59
62
  * Coordinates the concurrent modification of a `Store` due to optimistic and
60
63
  * non-revertable client updates and server payloads:
@@ -182,7 +185,20 @@ class RelayPublishQueue implements PublishQueue {
182
185
  run(
183
186
  sourceOperation?: OperationDescriptor,
184
187
  ): $ReadOnlyArray<RequestDescriptor> {
188
+ const runWillClearGcHold =
189
+ this._appliedOptimisticUpdates === 0 && !!this._gcHold;
190
+ const runIsANoop =
191
+ // this._pendingBackupRebase is true if an applied optimistic
192
+ // update has potentially been reverted or if this._pendingData is not empty.
193
+ !this._pendingBackupRebase &&
194
+ this._pendingOptimisticUpdates.size === 0 &&
195
+ !runWillClearGcHold;
196
+
185
197
  if (__DEV__) {
198
+ warning(
199
+ !runIsANoop,
200
+ 'RelayPublishQueue.run was called, but the call would have been a noop.',
201
+ );
186
202
  warning(
187
203
  this._isRunning !== true,
188
204
  'A store update was detected within another store update. Please ' +
@@ -191,6 +207,14 @@ class RelayPublishQueue implements PublishQueue {
191
207
  );
192
208
  this._isRunning = true;
193
209
  }
210
+
211
+ if (runIsANoop) {
212
+ if (__DEV__) {
213
+ this._isRunning = false;
214
+ }
215
+ return [];
216
+ }
217
+
194
218
  if (this._pendingBackupRebase) {
195
219
  if (this._hasStoreSnapshot) {
196
220
  this._store.restore();
@@ -299,7 +323,7 @@ class RelayPublishQueue implements PublishQueue {
299
323
  mutator,
300
324
  this._getDataID,
301
325
  );
302
- ErrorUtils.applyWithGuard(
326
+ applyWithGuard(
303
327
  updater,
304
328
  null,
305
329
  [recordSourceProxy],
@@ -334,7 +358,7 @@ class RelayPublishQueue implements PublishQueue {
334
358
  const processUpdate = optimisticUpdate => {
335
359
  if (optimisticUpdate.storeUpdater) {
336
360
  const {storeUpdater} = optimisticUpdate;
337
- ErrorUtils.applyWithGuard(
361
+ applyWithGuard(
338
362
  storeUpdater,
339
363
  null,
340
364
  [recordSourceProxy],
@@ -344,18 +368,20 @@ class RelayPublishQueue implements PublishQueue {
344
368
  } else {
345
369
  const {operation, payload, updater} = optimisticUpdate;
346
370
  const {source, fieldPayloads} = payload;
347
- const recordSourceSelectorProxy = new RelayRecordSourceSelectorProxy(
348
- mutator,
349
- recordSourceProxy,
350
- operation.fragment,
351
- );
352
- let selectorData;
353
371
  if (source) {
354
372
  recordSourceProxy.publishSource(source, fieldPayloads);
355
- selectorData = lookupSelector(source, operation.fragment);
356
373
  }
357
374
  if (updater) {
358
- ErrorUtils.applyWithGuard(
375
+ let selectorData;
376
+ if (source) {
377
+ selectorData = lookupSelector(source, operation.fragment);
378
+ }
379
+ const recordSourceSelectorProxy = new RelayRecordSourceSelectorProxy(
380
+ mutator,
381
+ recordSourceProxy,
382
+ operation.fragment,
383
+ );
384
+ applyWithGuard(
359
385
  updater,
360
386
  null,
361
387
  [recordSourceSelectorProxy, selectorData],
@@ -12,12 +12,14 @@
12
12
 
13
13
  'use strict';
14
14
 
15
+ const ClientID = require('./ClientID');
15
16
  const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
17
  const RelayModernRecord = require('./RelayModernRecord');
17
18
 
18
19
  const invariant = require('invariant');
19
20
 
20
21
  const {
22
+ ACTOR_CHANGE,
21
23
  CLIENT_EXTENSION,
22
24
  CONDITION,
23
25
  DEFER,
@@ -28,6 +30,7 @@ const {
28
30
  LINKED_FIELD,
29
31
  MODULE_IMPORT,
30
32
  REQUIRED_FIELD,
33
+ RELAY_RESOLVER,
31
34
  SCALAR_FIELD,
32
35
  STREAM,
33
36
  } = require('../util/RelayConcreteNode');
@@ -44,15 +47,20 @@ const {
44
47
  getStorageKey,
45
48
  getModuleComponentKey,
46
49
  } = require('./RelayStoreUtils');
50
+ const {NoopResolverCache} = require('./ResolverCache');
51
+ const {withResolverContext} = require('./ResolverFragments');
47
52
  const {generateTypeID} = require('./TypeID');
48
53
 
49
54
  import type {
50
55
  ReaderFlightField,
56
+ ReaderFragment,
51
57
  ReaderFragmentSpread,
52
58
  ReaderInlineDataFragmentSpread,
53
59
  ReaderLinkedField,
60
+ ReaderActorChange,
54
61
  ReaderModuleImport,
55
62
  ReaderNode,
63
+ ReaderRelayResolver,
56
64
  ReaderRequiredField,
57
65
  ReaderScalarField,
58
66
  ReaderSelection,
@@ -68,12 +76,18 @@ import type {
68
76
  MissingRequiredFields,
69
77
  DataIDSet,
70
78
  } from './RelayStoreTypes';
79
+ import type {ResolverCache} from './ResolverCache';
71
80
 
72
81
  function read(
73
82
  recordSource: RecordSource,
74
83
  selector: SingularReaderSelector,
84
+ resolverCache?: ResolverCache,
75
85
  ): Snapshot {
76
- const reader = new RelayReader(recordSource, selector);
86
+ const reader = new RelayReader(
87
+ recordSource,
88
+ selector,
89
+ resolverCache ?? new NoopResolverCache(),
90
+ );
77
91
  return reader.read();
78
92
  }
79
93
 
@@ -89,8 +103,13 @@ class RelayReader {
89
103
  _seenRecords: DataIDSet;
90
104
  _selector: SingularReaderSelector;
91
105
  _variables: Variables;
106
+ _resolverCache: ResolverCache;
92
107
 
93
- constructor(recordSource: RecordSource, selector: SingularReaderSelector) {
108
+ constructor(
109
+ recordSource: RecordSource,
110
+ selector: SingularReaderSelector,
111
+ resolverCache: ResolverCache,
112
+ ) {
94
113
  this._isMissingData = false;
95
114
  this._isWithinUnmatchedTypeRefinement = false;
96
115
  this._missingRequiredFields = null;
@@ -99,6 +118,7 @@ class RelayReader {
99
118
  this._seenRecords = new Set();
100
119
  this._selector = selector;
101
120
  this._variables = selector.variables;
121
+ this._resolverCache = resolverCache;
102
122
  }
103
123
 
104
124
  read(): Snapshot {
@@ -122,7 +142,18 @@ class RelayReader {
122
142
  // match, then no data is expected to be present.
123
143
  if (isDataExpectedToBePresent && abstractKey == null && record != null) {
124
144
  const recordType = RelayModernRecord.getType(record);
125
- if (recordType !== node.type && dataID !== ROOT_ID) {
145
+ if (
146
+ recordType !== node.type &&
147
+ // The root record type is a special `__Root` type and may not match the
148
+ // type on the ast, so ignore type mismatches at the root.
149
+ // We currently detect whether we're at the root by checking against ROOT_ID,
150
+ // but this does not work for mutations/subscriptions which generate unique
151
+ // root ids. This is acceptable in practice as we don't read data for mutations/
152
+ // subscriptions in a situation where we would use isMissingData to decide whether
153
+ // to suspend or not.
154
+ // TODO T96653810: Correctly detect reading from root of mutation/subscription
155
+ dataID !== ROOT_ID
156
+ ) {
126
157
  isDataExpectedToBePresent = false;
127
158
  }
128
159
  }
@@ -131,12 +162,7 @@ class RelayReader {
131
162
  // then data is only expected to be present if the record type is known to
132
163
  // implement the interface. If we aren't sure whether the record implements
133
164
  // the interface, that itself constitutes "expected" data being missing.
134
- if (
135
- isDataExpectedToBePresent &&
136
- abstractKey != null &&
137
- record != null &&
138
- RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT
139
- ) {
165
+ if (isDataExpectedToBePresent && abstractKey != null && record != null) {
140
166
  const recordType = RelayModernRecord.getType(record);
141
167
  const typeID = generateTypeID(recordType);
142
168
  const typeRecord = this._recordSource.get(typeID);
@@ -192,6 +218,7 @@ class RelayReader {
192
218
  'RelayReader(): Undefined variable `%s`.',
193
219
  name,
194
220
  );
221
+ // $FlowFixMe[cannot-write]
195
222
  return this._variables[name];
196
223
  }
197
224
 
@@ -262,7 +289,9 @@ class RelayReader {
262
289
  }
263
290
  break;
264
291
  case CONDITION:
265
- const conditionValue = this._getVariableValue(selection.condition);
292
+ const conditionValue = Boolean(
293
+ this._getVariableValue(selection.condition),
294
+ );
266
295
  if (conditionValue === selection.passingValue) {
267
296
  const hasExpectedData = this._traverseSelections(
268
297
  selection.selections,
@@ -289,7 +318,7 @@ class RelayReader {
289
318
  return false;
290
319
  }
291
320
  }
292
- } else if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
321
+ } else {
293
322
  // Similar to the logic in read(): data is only expected to be present
294
323
  // if the record is known to conform to the interface. If we don't know
295
324
  // whether the type conforms or not, that constitutes missing data.
@@ -319,13 +348,16 @@ class RelayReader {
319
348
  // Don't know if the type implements the interface or not
320
349
  this._isMissingData = true;
321
350
  }
322
- } else {
323
- // legacy behavior for abstract refinements: always read even
324
- // if the type doesn't conform and don't reset isMissingData
325
- this._traverseSelections(selection.selections, record, data);
326
351
  }
327
352
  break;
328
353
  }
354
+ case RELAY_RESOLVER: {
355
+ if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
356
+ throw new Error('Relay Resolver fields are not yet supported.');
357
+ }
358
+ this._readResolverField(selection, record, data);
359
+ break;
360
+ }
329
361
  case FRAGMENT_SPREAD:
330
362
  this._createFragmentPointer(selection, record, data);
331
363
  break;
@@ -333,7 +365,11 @@ class RelayReader {
333
365
  this._readModuleImport(selection, record, data);
334
366
  break;
335
367
  case INLINE_DATA_FRAGMENT_SPREAD:
336
- this._createInlineDataFragmentPointer(selection, record, data);
368
+ this._createInlineDataOrResolverFragmentPointer(
369
+ selection,
370
+ record,
371
+ data,
372
+ );
337
373
  break;
338
374
  case DEFER:
339
375
  case CLIENT_EXTENSION: {
@@ -367,6 +403,9 @@ class RelayReader {
367
403
  throw new Error('Flight fields are not yet supported.');
368
404
  }
369
405
  break;
406
+ case ACTOR_CHANGE:
407
+ this._readActorChange(selection, record, data);
408
+ break;
370
409
  default:
371
410
  (selection: empty);
372
411
  invariant(
@@ -393,6 +432,12 @@ class RelayReader {
393
432
  } else {
394
433
  return this._readLink(selection.field, record, data);
395
434
  }
435
+ case RELAY_RESOLVER:
436
+ if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
437
+ throw new Error('Relay Resolver fields are not yet supported.');
438
+ }
439
+ this._readResolverField(selection.field, record, data);
440
+ break;
396
441
  default:
397
442
  (selection.field.kind: empty);
398
443
  invariant(
@@ -403,6 +448,87 @@ class RelayReader {
403
448
  }
404
449
  }
405
450
 
451
+ _readResolverField(
452
+ field: ReaderRelayResolver,
453
+ record: Record,
454
+ data: SelectorData,
455
+ ): ?mixed {
456
+ const {resolverModule, fragment} = field;
457
+ const storageKey = getStorageKey(field, this._variables);
458
+ const resolverID = ClientID.generateClientID(
459
+ RelayModernRecord.getDataID(record),
460
+ storageKey,
461
+ );
462
+
463
+ // Found when reading the resolver fragment, which can happen either when
464
+ // evaluating the resolver and it calls readFragment, or when checking if the
465
+ // inputs have changed since a previous evaluation:
466
+ let fragmentValue;
467
+ let fragmentReaderSelector;
468
+ const fragmentSeenRecordIDs = new Set();
469
+
470
+ const getDataForResolverFragment = singularReaderSelector => {
471
+ if (fragmentValue != null) {
472
+ // It was already read when checking for input staleness; no need to read it again.
473
+ // Note that the variables like fragmentSeenRecordIDs in the outer closure will have
474
+ // already been set and will still be used in this case.
475
+ return fragmentValue;
476
+ }
477
+ fragmentReaderSelector = singularReaderSelector;
478
+ const existingSeenRecords = this._seenRecords;
479
+ try {
480
+ this._seenRecords = fragmentSeenRecordIDs;
481
+ const resolverFragmentData = {};
482
+ this._createInlineDataOrResolverFragmentPointer(
483
+ singularReaderSelector.node,
484
+ record,
485
+ resolverFragmentData,
486
+ );
487
+ fragmentValue = resolverFragmentData[FRAGMENTS_KEY]?.[fragment.name];
488
+ invariant(
489
+ typeof fragmentValue === 'object' && fragmentValue !== null,
490
+ `Expected reader data to contain a __fragments property with a property for the fragment named ${fragment.name}, but it is missing.`,
491
+ );
492
+ return fragmentValue;
493
+ } finally {
494
+ this._seenRecords = existingSeenRecords;
495
+ }
496
+ };
497
+ const resolverContext = {getDataForResolverFragment};
498
+
499
+ const [result, seenRecord] = this._resolverCache.readFromCacheOrEvaluate(
500
+ record,
501
+ field,
502
+ this._variables,
503
+ () => {
504
+ const key = {
505
+ __id: RelayModernRecord.getDataID(record),
506
+ __fragmentOwner: this._owner,
507
+ __fragments: {
508
+ [fragment.name]: {}, // Arguments to this fragment; not yet supported.
509
+ },
510
+ };
511
+ return withResolverContext(resolverContext, () => {
512
+ // $FlowFixMe[prop-missing] - resolver module's type signature is a lie
513
+ const resolverResult = resolverModule(key);
514
+ return {
515
+ resolverResult,
516
+ fragmentValue,
517
+ resolverID,
518
+ seenRecordIDs: fragmentSeenRecordIDs,
519
+ readerSelector: fragmentReaderSelector,
520
+ };
521
+ });
522
+ },
523
+ getDataForResolverFragment,
524
+ );
525
+ if (seenRecord != null) {
526
+ this._seenRecords.add(seenRecord);
527
+ }
528
+ data[storageKey] = result;
529
+ return result;
530
+ }
531
+
406
532
  _readFlightField(
407
533
  field: ReaderFlightField,
408
534
  record: Record,
@@ -485,6 +611,42 @@ class RelayReader {
485
611
  return value;
486
612
  }
487
613
 
614
+ _readActorChange(
615
+ field: ReaderActorChange,
616
+ record: Record,
617
+ data: SelectorData,
618
+ ): ?mixed {
619
+ const applicationName = field.alias ?? field.name;
620
+ const storageKey = getStorageKey(field, this._variables);
621
+ const externalRef = RelayModernRecord.getActorLinkedRecordID(
622
+ record,
623
+ storageKey,
624
+ );
625
+
626
+ if (externalRef == null) {
627
+ data[applicationName] = externalRef;
628
+ if (externalRef === undefined) {
629
+ this._isMissingData = true;
630
+ }
631
+ return data[applicationName];
632
+ }
633
+ const [actorIdentifier, dataID] = externalRef;
634
+
635
+ const fragmentRef = {};
636
+ this._createFragmentPointer(
637
+ field.fragmentSpread,
638
+ {
639
+ __id: dataID,
640
+ },
641
+ fragmentRef,
642
+ );
643
+ data[applicationName] = {
644
+ __fragmentRef: fragmentRef,
645
+ __viewer: actorIdentifier,
646
+ };
647
+ return data[applicationName];
648
+ }
649
+
488
650
  _readPluralLink(
489
651
  field: ReaderLinkedField,
490
652
  record: Record,
@@ -567,7 +729,7 @@ class RelayReader {
567
729
  {
568
730
  kind: 'FragmentSpread',
569
731
  name: moduleImport.fragmentName,
570
- args: null,
732
+ args: moduleImport.args,
571
733
  },
572
734
  record,
573
735
  data,
@@ -598,16 +760,13 @@ class RelayReader {
598
760
  ? getArgumentValues(fragmentSpread.args, this._variables)
599
761
  : {};
600
762
  data[FRAGMENT_OWNER_KEY] = this._owner;
601
-
602
- if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
603
- data[
604
- IS_WITHIN_UNMATCHED_TYPE_REFINEMENT
605
- ] = this._isWithinUnmatchedTypeRefinement;
606
- }
763
+ data[
764
+ IS_WITHIN_UNMATCHED_TYPE_REFINEMENT
765
+ ] = this._isWithinUnmatchedTypeRefinement;
607
766
  }
608
767
 
609
- _createInlineDataFragmentPointer(
610
- inlineDataFragmentSpread: ReaderInlineDataFragmentSpread,
768
+ _createInlineDataOrResolverFragmentPointer(
769
+ fragmentSpreadOrFragment: ReaderInlineDataFragmentSpread | ReaderFragment,
611
770
  record: Record,
612
771
  data: SelectorData,
613
772
  ): void {
@@ -625,12 +784,12 @@ class RelayReader {
625
784
  }
626
785
  const inlineData = {};
627
786
  this._traverseSelections(
628
- inlineDataFragmentSpread.selections,
787
+ fragmentSpreadOrFragment.selections,
629
788
  record,
630
789
  inlineData,
631
790
  );
632
791
  // $FlowFixMe[cannot-write] - writing into read-only field
633
- fragmentPointers[inlineDataFragmentSpread.name] = inlineData;
792
+ fragmentPointers[fragmentSpreadOrFragment.name] = inlineData;
634
793
  }
635
794
  }
636
795
 
@@ -12,17 +12,83 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- const RelayRecordSourceMapImpl = require('./RelayRecordSourceMapImpl');
15
+ const RelayRecordState = require('./RelayRecordState');
16
16
 
17
- import type {MutableRecordSource, RecordObjectMap} from './RelayStoreTypes';
17
+ import type {DataID} from '../util/RelayRuntimeTypes';
18
+ import type {RecordState} from './RelayRecordState';
19
+ import type {
20
+ MutableRecordSource,
21
+ Record,
22
+ RecordObjectMap,
23
+ } from './RelayStoreTypes';
18
24
 
19
- class RelayRecordSource {
20
- constructor(records?: RecordObjectMap): MutableRecordSource {
21
- return RelayRecordSource.create(records);
25
+ const {EXISTENT, NONEXISTENT, UNKNOWN} = RelayRecordState;
26
+
27
+ /**
28
+ * An implementation of the `MutableRecordSource` interface (defined in
29
+ * `RelayStoreTypes`) that holds all records in memory (JS Map).
30
+ */
31
+ class RelayRecordSource implements MutableRecordSource {
32
+ _records: Map<DataID, ?Record>;
33
+
34
+ constructor(records?: RecordObjectMap) {
35
+ this._records = new Map();
36
+ if (records != null) {
37
+ Object.keys(records).forEach(key => {
38
+ this._records.set(key, records[key]);
39
+ });
40
+ }
22
41
  }
23
42
 
24
43
  static create(records?: RecordObjectMap): MutableRecordSource {
25
- return new RelayRecordSourceMapImpl(records);
44
+ return new RelayRecordSource(records);
45
+ }
46
+
47
+ clear(): void {
48
+ this._records = new Map();
49
+ }
50
+
51
+ delete(dataID: DataID): void {
52
+ this._records.set(dataID, null);
53
+ }
54
+
55
+ get(dataID: DataID): ?Record {
56
+ return this._records.get(dataID);
57
+ }
58
+
59
+ getRecordIDs(): Array<DataID> {
60
+ return Array.from(this._records.keys());
61
+ }
62
+
63
+ getStatus(dataID: DataID): RecordState {
64
+ if (!this._records.has(dataID)) {
65
+ return UNKNOWN;
66
+ }
67
+ return this._records.get(dataID) == null ? NONEXISTENT : EXISTENT;
68
+ }
69
+
70
+ has(dataID: DataID): boolean {
71
+ return this._records.has(dataID);
72
+ }
73
+
74
+ remove(dataID: DataID): void {
75
+ this._records.delete(dataID);
76
+ }
77
+
78
+ set(dataID: DataID, record: Record): void {
79
+ this._records.set(dataID, record);
80
+ }
81
+
82
+ size(): number {
83
+ return this._records.size;
84
+ }
85
+
86
+ toJSON(): {[DataID]: ?Record, ...} {
87
+ const obj = {};
88
+ for (const [key, value] of this._records) {
89
+ obj[key] = value;
90
+ }
91
+ return obj;
26
92
  }
27
93
  }
28
94