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
@@ -40,4 +40,8 @@ function generateUniqueClientID(): DataID {
40
40
  return `${PREFIX}local:${localID++}`;
41
41
  }
42
42
 
43
- module.exports = {generateClientID, generateUniqueClientID, isClientID};
43
+ module.exports = {
44
+ generateClientID,
45
+ generateUniqueClientID,
46
+ isClientID,
47
+ };
@@ -27,9 +27,11 @@ const getOperation = require('../util/getOperation');
27
27
  const invariant = require('invariant');
28
28
 
29
29
  const {isClientID} = require('./ClientID');
30
+ const {getLocalVariables} = require('./RelayConcreteVariables');
30
31
  const {EXISTENT, UNKNOWN} = require('./RelayRecordState');
31
32
  const {generateTypeID} = require('./TypeID');
32
33
 
34
+ import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
33
35
  import type {
34
36
  NormalizationField,
35
37
  NormalizationFlightField,
@@ -46,7 +48,7 @@ import type {
46
48
  MutableRecordSource,
47
49
  NormalizationSelector,
48
50
  OperationLoader,
49
- ReactFlightReachableQuery,
51
+ ReactFlightReachableExecutableDefinitions,
50
52
  Record,
51
53
  RecordSource,
52
54
  } from './RelayStoreTypes';
@@ -57,7 +59,9 @@ export type Availability = {|
57
59
  |};
58
60
 
59
61
  const {
62
+ ACTOR_CHANGE,
60
63
  CONDITION,
64
+ CLIENT_COMPONENT,
61
65
  CLIENT_EXTENSION,
62
66
  DEFER,
63
67
  FLIGHT_FIELD,
@@ -89,21 +93,25 @@ const {
89
93
  * If all records are present, returns `true`, otherwise `false`.
90
94
  */
91
95
  function check(
92
- source: RecordSource,
93
- target: MutableRecordSource,
96
+ getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource,
97
+ getTargetForActor: (actorIdentifier: ActorIdentifier) => MutableRecordSource,
98
+ defaultActorIdentifier: ActorIdentifier,
94
99
  selector: NormalizationSelector,
95
100
  handlers: $ReadOnlyArray<MissingFieldHandler>,
96
101
  operationLoader: ?OperationLoader,
97
102
  getDataID: GetDataID,
103
+ shouldProcessClientComponents: ?boolean,
98
104
  ): Availability {
99
105
  const {dataID, node, variables} = selector;
100
106
  const checker = new DataChecker(
101
- source,
102
- target,
107
+ getSourceForActor,
108
+ getTargetForActor,
109
+ defaultActorIdentifier,
103
110
  variables,
104
111
  handlers,
105
112
  operationLoader,
106
113
  getDataID,
114
+ shouldProcessClientComponents,
107
115
  );
108
116
  return checker.check(node, dataID);
109
117
  }
@@ -121,24 +129,66 @@ class DataChecker {
121
129
  _recordWasMissing: boolean;
122
130
  _source: RecordSource;
123
131
  _variables: Variables;
132
+ _shouldProcessClientComponents: ?boolean;
133
+ +_getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource;
134
+ +_getTargetForActor: (
135
+ actorIdentifier: ActorIdentifier,
136
+ ) => MutableRecordSource;
137
+ +_getDataID: GetDataID;
138
+ +_mutatorRecordSourceProxyCache: Map<
139
+ ActorIdentifier,
140
+ [RelayRecordSourceMutator, RelayRecordSourceProxy],
141
+ >;
124
142
 
125
143
  constructor(
126
- source: RecordSource,
127
- target: MutableRecordSource,
144
+ getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource,
145
+ getTargetForActor: (
146
+ actorIdentifier: ActorIdentifier,
147
+ ) => MutableRecordSource,
148
+ defaultActorIdentifier: ActorIdentifier,
128
149
  variables: Variables,
129
150
  handlers: $ReadOnlyArray<MissingFieldHandler>,
130
151
  operationLoader: ?OperationLoader,
131
152
  getDataID: GetDataID,
153
+ shouldProcessClientComponents: ?boolean,
132
154
  ) {
133
- const mutator = new RelayRecordSourceMutator(source, target);
155
+ this._getSourceForActor = getSourceForActor;
156
+ this._getTargetForActor = getTargetForActor;
157
+ this._getDataID = getDataID;
158
+ this._source = getSourceForActor(defaultActorIdentifier);
159
+ this._mutatorRecordSourceProxyCache = new Map();
160
+ const [mutator, recordSourceProxy] = this._getMutatorAndRecordProxyForActor(
161
+ defaultActorIdentifier,
162
+ );
134
163
  this._mostRecentlyInvalidatedAt = null;
135
164
  this._handlers = handlers;
136
165
  this._mutator = mutator;
137
166
  this._operationLoader = operationLoader ?? null;
138
- this._recordSourceProxy = new RelayRecordSourceProxy(mutator, getDataID);
167
+ this._recordSourceProxy = recordSourceProxy;
139
168
  this._recordWasMissing = false;
140
- this._source = source;
141
169
  this._variables = variables;
170
+ this._shouldProcessClientComponents = shouldProcessClientComponents;
171
+ }
172
+
173
+ _getMutatorAndRecordProxyForActor(
174
+ actorIdentifier: ActorIdentifier,
175
+ ): [RelayRecordSourceMutator, RelayRecordSourceProxy] {
176
+ let tuple = this._mutatorRecordSourceProxyCache.get(actorIdentifier);
177
+ if (tuple == null) {
178
+ const target = this._getTargetForActor(actorIdentifier);
179
+
180
+ const mutator = new RelayRecordSourceMutator(
181
+ this._getSourceForActor(actorIdentifier),
182
+ target,
183
+ );
184
+ const recordSourceProxy = new RelayRecordSourceProxy(
185
+ mutator,
186
+ this._getDataID,
187
+ );
188
+ tuple = [mutator, recordSourceProxy];
189
+ this._mutatorRecordSourceProxyCache.set(actorIdentifier, tuple);
190
+ }
191
+ return tuple;
142
192
  }
143
193
 
144
194
  check(node: NormalizationNode, dataID: DataID): Availability {
@@ -161,6 +211,7 @@ class DataChecker {
161
211
  'RelayAsyncLoader(): Undefined variable `%s`.',
162
212
  name,
163
213
  );
214
+ // $FlowFixMe[cannot-write]
164
215
  return this._variables[name];
165
216
  }
166
217
 
@@ -177,6 +228,8 @@ class DataChecker {
177
228
  ...
178
229
  } {
179
230
  return {
231
+ /* $FlowFixMe[class-object-subtyping] added when improving typing for
232
+ * this parameters */
180
233
  args: field.args ? getArgumentValues(field.args, this._variables) : {},
181
234
  // Getting a snapshot of the record state is potentially expensive since
182
235
  // we will need to merge the sink and source records. Since we do not create
@@ -302,8 +355,13 @@ class DataChecker {
302
355
  this._checkLink(selection, dataID);
303
356
  }
304
357
  break;
358
+ case ACTOR_CHANGE:
359
+ this._checkActorChange(selection.linkedField, dataID);
360
+ break;
305
361
  case CONDITION:
306
- const conditionValue = this._getVariableValue(selection.condition);
362
+ const conditionValue = Boolean(
363
+ this._getVariableValue(selection.condition),
364
+ );
307
365
  if (conditionValue === selection.passingValue) {
308
366
  this._traverseSelections(selection.selections, dataID);
309
367
  }
@@ -316,7 +374,7 @@ class DataChecker {
316
374
  if (typeName === selection.type) {
317
375
  this._traverseSelections(selection.selections, dataID);
318
376
  }
319
- } else if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
377
+ } else {
320
378
  // Abstract refinement: check data depending on whether the type
321
379
  // conforms to the interface/union or not:
322
380
  // - Type known to _not_ implement the interface: don't check the selections.
@@ -342,10 +400,6 @@ class DataChecker {
342
400
  // missing so don't bother reading the fragment
343
401
  this._handleMissing();
344
402
  } // else false: known to not implement the interface
345
- } else {
346
- // legacy behavior for abstract refinements: always check even
347
- // if the type doesn't conform
348
- this._traverseSelections(selection.selections, dataID);
349
403
  }
350
404
  break;
351
405
  }
@@ -381,9 +435,15 @@ class DataChecker {
381
435
  case STREAM:
382
436
  this._traverseSelections(selection.selections, dataID);
383
437
  break;
384
- // $FlowFixMe[incompatible-type]
385
438
  case FRAGMENT_SPREAD:
439
+ const prevVariables = this._variables;
440
+ this._variables = getLocalVariables(
441
+ this._variables,
442
+ selection.fragment.argumentDefinitions,
443
+ selection.args,
444
+ );
386
445
  this._traverseSelections(selection.fragment.selections, dataID);
446
+ this._variables = prevVariables;
387
447
  break;
388
448
  case CLIENT_EXTENSION:
389
449
  const recordWasMissing = this._recordWasMissing;
@@ -391,25 +451,23 @@ class DataChecker {
391
451
  this._recordWasMissing = recordWasMissing;
392
452
  break;
393
453
  case TYPE_DISCRIMINATOR:
394
- if (RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT) {
395
- const {abstractKey} = selection;
396
- const recordType = this._mutator.getType(dataID);
397
- invariant(
398
- recordType != null,
399
- 'DataChecker: Expected record `%s` to have a known type',
400
- dataID,
401
- );
402
- const typeID = generateTypeID(recordType);
403
- const implementsInterface = this._mutator.getValue(
404
- typeID,
405
- abstractKey,
406
- );
407
- if (implementsInterface == null) {
408
- // unsure if the type implements the interface: data is
409
- // missing
410
- this._handleMissing();
411
- } // else: if it does or doesn't implement, we don't need to check or skip anything else
412
- }
454
+ const {abstractKey} = selection;
455
+ const recordType = this._mutator.getType(dataID);
456
+ invariant(
457
+ recordType != null,
458
+ 'DataChecker: Expected record `%s` to have a known type',
459
+ dataID,
460
+ );
461
+ const typeID = generateTypeID(recordType);
462
+ const implementsInterface = this._mutator.getValue(
463
+ typeID,
464
+ abstractKey,
465
+ );
466
+ if (implementsInterface == null) {
467
+ // unsure if the type implements the interface: data is
468
+ // missing
469
+ this._handleMissing();
470
+ } // else: if it does or doesn't implement, we don't need to check or skip anything else
413
471
  break;
414
472
  case FLIGHT_FIELD:
415
473
  if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
@@ -418,6 +476,12 @@ class DataChecker {
418
476
  throw new Error('Flight fields are not yet supported.');
419
477
  }
420
478
  break;
479
+ case CLIENT_COMPONENT:
480
+ if (this._shouldProcessClientComponents === false) {
481
+ break;
482
+ }
483
+ this._traverseSelections(selection.fragment.selections, dataID);
484
+ break;
421
485
  default:
422
486
  (selection: empty);
423
487
  invariant(
@@ -449,7 +513,14 @@ class DataChecker {
449
513
  const normalizationRootNode = operationLoader.get(operationReference);
450
514
  if (normalizationRootNode != null) {
451
515
  const operation = getOperation(normalizationRootNode);
516
+ const prevVariables = this._variables;
517
+ this._variables = getLocalVariables(
518
+ this._variables,
519
+ operation.argumentDefinitions,
520
+ moduleImport.args,
521
+ );
452
522
  this._traverse(operation, dataID);
523
+ this._variables = prevVariables;
453
524
  } else {
454
525
  // If the fragment is not available, we assume that the data cannot have been
455
526
  // processed yet and must therefore be missing.
@@ -506,6 +577,39 @@ class DataChecker {
506
577
  }
507
578
  }
508
579
 
580
+ _checkActorChange(field: NormalizationLinkedField, dataID: DataID): void {
581
+ const storageKey = getStorageKey(field, this._variables);
582
+ const record = this._source.get(dataID);
583
+ const tuple =
584
+ record != null
585
+ ? RelayModernRecord.getActorLinkedRecordID(record, storageKey)
586
+ : record;
587
+
588
+ if (tuple == null) {
589
+ if (tuple === undefined) {
590
+ this._handleMissing();
591
+ }
592
+ } else {
593
+ const [actorIdentifier, linkedID] = tuple;
594
+ const prevSource = this._source;
595
+ const prevMutator = this._mutator;
596
+ const prevRecordSourceProxy = this._recordSourceProxy;
597
+
598
+ const [
599
+ mutator,
600
+ recordSourceProxy,
601
+ ] = this._getMutatorAndRecordProxyForActor(actorIdentifier);
602
+
603
+ this._source = this._getSourceForActor(actorIdentifier);
604
+ this._mutator = mutator;
605
+ this._recordSourceProxy = recordSourceProxy;
606
+ this._traverse(field, linkedID);
607
+ this._source = prevSource;
608
+ this._mutator = prevMutator;
609
+ this._recordSourceProxy = prevRecordSourceProxy;
610
+ }
611
+ }
612
+
509
613
  _checkFlightField(field: NormalizationFlightField, dataID: DataID): void {
510
614
  const storageKey = getStorageKey(field, this._variables);
511
615
  const linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
@@ -522,12 +626,12 @@ class DataChecker {
522
626
  linkedID,
523
627
  RelayStoreReactFlightUtils.REACT_FLIGHT_TREE_STORAGE_KEY,
524
628
  );
525
- const reachableQueries = this._mutator.getValue(
629
+ const reachableExecutableDefinitions = this._mutator.getValue(
526
630
  linkedID,
527
- RelayStoreReactFlightUtils.REACT_FLIGHT_QUERIES_STORAGE_KEY,
631
+ RelayStoreReactFlightUtils.REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
528
632
  );
529
633
 
530
- if (tree == null || !Array.isArray(reachableQueries)) {
634
+ if (tree == null || !Array.isArray(reachableExecutableDefinitions)) {
531
635
  this._handleMissing();
532
636
  return;
533
637
  }
@@ -538,13 +642,13 @@ class DataChecker {
538
642
  'DataChecker: Expected an operationLoader to be configured when using ' +
539
643
  'React Flight.',
540
644
  );
541
- // In Flight, the variables that are in scope for reachable queries aren't
542
- // the same as what's in scope for the outer query.
645
+ // In Flight, the variables that are in scope for reachable executable
646
+ // definitions aren't the same as what's in scope for the outer query.
543
647
  const prevVariables = this._variables;
544
648
  // $FlowFixMe[incompatible-cast]
545
- for (const query of (reachableQueries: Array<ReactFlightReachableQuery>)) {
546
- this._variables = query.variables;
547
- const normalizationRootNode = operationLoader.get(query.module);
649
+ for (const definition of (reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions>)) {
650
+ this._variables = definition.variables;
651
+ const normalizationRootNode = operationLoader.get(definition.module);
548
652
  if (normalizationRootNode != null) {
549
653
  const operation = getOperation(normalizationRootNode);
550
654
  this._traverseSelections(operation.selections, ROOT_ID);