relay-runtime 10.1.3 → 11.0.2

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 (106) hide show
  1. package/handlers/connection/ConnectionHandler.js.flow +60 -0
  2. package/handlers/connection/MutationHandlers.js.flow +28 -0
  3. package/index.js +1 -1
  4. package/index.js.flow +9 -3
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
  6. package/lib/handlers/connection/ConnectionHandler.js +68 -6
  7. package/lib/handlers/connection/MutationHandlers.js +67 -8
  8. package/lib/index.js +3 -0
  9. package/lib/multi-actor-environment/ActorIdentifier.js +23 -0
  10. package/lib/multi-actor-environment/ActorSpecificEnvironment.js +108 -0
  11. package/lib/multi-actor-environment/MultiActorEnvironment.js +156 -0
  12. package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
  13. package/lib/multi-actor-environment/index.js +17 -0
  14. package/lib/mutations/RelayRecordProxy.js +1 -1
  15. package/lib/mutations/RelayRecordSourceMutator.js +1 -1
  16. package/lib/mutations/RelayRecordSourceProxy.js +1 -1
  17. package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -1
  18. package/lib/mutations/applyOptimisticMutation.js +1 -1
  19. package/lib/mutations/commitMutation.js +1 -1
  20. package/lib/mutations/validateMutation.js +36 -15
  21. package/lib/network/RelayNetwork.js +1 -1
  22. package/lib/network/RelayQueryResponseCache.js +3 -2
  23. package/lib/query/GraphQLTag.js +1 -1
  24. package/lib/query/fetchQuery.js +129 -13
  25. package/lib/query/fetchQueryInternal.js +3 -4
  26. package/lib/query/fetchQuery_DEPRECATED.js +39 -0
  27. package/lib/store/DataChecker.js +26 -14
  28. package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +117 -47
  29. package/lib/store/RelayConcreteVariables.js +8 -4
  30. package/lib/store/RelayModernEnvironment.js +105 -136
  31. package/lib/store/RelayModernFragmentSpecResolver.js +16 -9
  32. package/lib/store/RelayModernRecord.js +1 -1
  33. package/lib/store/RelayModernSelector.js +1 -1
  34. package/lib/store/RelayModernStore.js +19 -20
  35. package/lib/store/RelayOperationTracker.js +55 -49
  36. package/lib/store/RelayPublishQueue.js +9 -5
  37. package/lib/store/RelayReader.js +68 -14
  38. package/lib/store/RelayReferenceMarker.js +28 -14
  39. package/lib/store/RelayResponseNormalizer.js +109 -15
  40. package/lib/store/RelayStoreReactFlightUtils.js +6 -4
  41. package/lib/store/RelayStoreSubscriptions.js +18 -8
  42. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +90 -30
  43. package/lib/store/RelayStoreUtils.js +3 -2
  44. package/lib/store/ResolverFragments.js +57 -0
  45. package/lib/store/cloneRelayHandleSourceField.js +1 -1
  46. package/lib/store/cloneRelayScalarHandleSourceField.js +1 -1
  47. package/lib/store/createFragmentSpecResolver.js +2 -2
  48. package/lib/store/createRelayContext.js +1 -1
  49. package/lib/store/defaultGetDataID.js +3 -1
  50. package/lib/store/hasOverlappingIDs.js +11 -3
  51. package/lib/store/readInlineData.js +1 -1
  52. package/lib/subscription/requestSubscription.js +33 -5
  53. package/lib/util/RelayConcreteNode.js +2 -0
  54. package/lib/util/RelayFeatureFlags.js +8 -3
  55. package/lib/util/RelayProfiler.js +17 -187
  56. package/lib/util/RelayReplaySubject.js +1 -1
  57. package/lib/util/deepFreeze.js +1 -0
  58. package/lib/util/getRelayHandleKey.js +1 -1
  59. package/lib/util/getRequestIdentifier.js +1 -1
  60. package/multi-actor-environment/ActorIdentifier.js.flow +27 -0
  61. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +189 -0
  62. package/multi-actor-environment/MultiActorEnvironment.js.flow +233 -0
  63. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +196 -0
  64. package/multi-actor-environment/index.js.flow +24 -0
  65. package/mutations/RelayRecordSourceProxy.js.flow +3 -2
  66. package/mutations/commitMutation.js.flow +1 -1
  67. package/mutations/validateMutation.js.flow +40 -15
  68. package/network/RelayNetworkTypes.js.flow +31 -11
  69. package/network/RelayQueryResponseCache.js.flow +2 -1
  70. package/package.json +3 -2
  71. package/query/fetchQuery.js.flow +147 -20
  72. package/query/fetchQueryInternal.js.flow +2 -3
  73. package/query/fetchQuery_DEPRECATED.js.flow +47 -0
  74. package/relay-runtime.js +2 -2
  75. package/relay-runtime.min.js +2 -2
  76. package/store/DataChecker.js.flow +23 -15
  77. package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +128 -40
  78. package/store/RelayConcreteVariables.js.flow +5 -0
  79. package/store/RelayModernEnvironment.js.flow +100 -130
  80. package/store/RelayModernFragmentSpecResolver.js.flow +30 -8
  81. package/store/RelayModernStore.js.flow +28 -24
  82. package/store/RelayOperationTracker.js.flow +69 -56
  83. package/store/RelayPublishQueue.js.flow +7 -4
  84. package/store/RelayReader.js.flow +63 -11
  85. package/store/RelayRecordSource.js.flow +3 -3
  86. package/store/RelayRecordSourceMapImpl.js.flow +6 -2
  87. package/store/RelayReferenceMarker.js.flow +28 -18
  88. package/store/RelayResponseNormalizer.js.flow +134 -23
  89. package/store/RelayStoreReactFlightUtils.js.flow +9 -4
  90. package/store/RelayStoreSubscriptions.js.flow +22 -7
  91. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +36 -12
  92. package/store/RelayStoreTypes.js.flow +51 -22
  93. package/store/RelayStoreUtils.js.flow +2 -1
  94. package/store/ResolverFragments.js.flow +125 -0
  95. package/store/createFragmentSpecResolver.js.flow +2 -0
  96. package/store/defaultGetDataID.js.flow +3 -1
  97. package/store/hasOverlappingIDs.js.flow +11 -9
  98. package/subscription/requestSubscription.js.flow +25 -2
  99. package/util/NormalizationNode.js.flow +13 -0
  100. package/util/ReaderNode.js.flow +14 -1
  101. package/util/RelayConcreteNode.js.flow +2 -0
  102. package/util/RelayFeatureFlags.js.flow +12 -2
  103. package/util/RelayProfiler.js.flow +22 -194
  104. package/util/RelayRuntimeTypes.js.flow +4 -5
  105. package/util/deepFreeze.js.flow +2 -1
  106. package/util/isEmptyObject.js.flow +1 -1
@@ -14,7 +14,6 @@
14
14
 
15
15
  const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
16
  const RelayModernRecord = require('./RelayModernRecord');
17
- const RelayProfiler = require('../util/RelayProfiler');
18
17
 
19
18
  const areEqual = require('areEqual');
20
19
  const invariant = require('invariant');
@@ -22,9 +21,11 @@ const warning = require('warning');
22
21
 
23
22
  const {
24
23
  CONDITION,
24
+ CLIENT_COMPONENT,
25
25
  CLIENT_EXTENSION,
26
26
  DEFER,
27
27
  FLIGHT_FIELD,
28
+ FRAGMENT_SPREAD,
28
29
  INLINE_FRAGMENT,
29
30
  LINKED_FIELD,
30
31
  LINKED_HANDLE,
@@ -38,7 +39,7 @@ const {generateClientID, isClientID} = require('./ClientID');
38
39
  const {createNormalizationSelector} = require('./RelayModernSelector');
39
40
  const {
40
41
  refineToReactFlightPayloadData,
41
- REACT_FLIGHT_QUERIES_STORAGE_KEY,
42
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
42
43
  REACT_FLIGHT_TREE_STORAGE_KEY,
43
44
  REACT_FLIGHT_TYPE_NAME,
44
45
  } = require('./RelayStoreReactFlightUtils');
@@ -71,14 +72,15 @@ import type {
71
72
  ModuleImportPayload,
72
73
  MutableRecordSource,
73
74
  NormalizationSelector,
74
- ReactFlightReachableQuery,
75
+ ReactFlightReachableExecutableDefinitions,
75
76
  ReactFlightPayloadDeserializer,
77
+ ReactFlightServerErrorHandler,
76
78
  Record,
77
79
  RelayResponsePayload,
78
80
  } from './RelayStoreTypes';
79
81
 
80
82
  export type GetDataID = (
81
- fieldValue: {[string]: mixed, ...},
83
+ fieldValue: interface {[string]: mixed},
82
84
  typeName: string,
83
85
  ) => mixed;
84
86
 
@@ -87,6 +89,8 @@ export type NormalizationOptions = {|
87
89
  +treatMissingFieldsAsNull: boolean,
88
90
  +path?: $ReadOnlyArray<string>,
89
91
  +reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
92
+ +reactFlightServerErrorHandler?: ?ReactFlightServerErrorHandler,
93
+ +shouldProcessClientComponents?: ?boolean,
90
94
  |};
91
95
 
92
96
  /**
@@ -125,6 +129,8 @@ class RelayResponseNormalizer {
125
129
  _recordSource: MutableRecordSource;
126
130
  _variables: Variables;
127
131
  _reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
132
+ _reactFlightServerErrorHandler: ?ReactFlightServerErrorHandler;
133
+ _shouldProcessClientComponents: ?boolean;
128
134
 
129
135
  constructor(
130
136
  recordSource: MutableRecordSource,
@@ -143,6 +149,8 @@ class RelayResponseNormalizer {
143
149
  this._variables = variables;
144
150
  this._reactFlightPayloadDeserializer =
145
151
  options.reactFlightPayloadDeserializer;
152
+ this._reactFlightServerErrorHandler = options.reactFlightServerErrorHandler;
153
+ this._shouldProcessClientComponents = options.shouldProcessClientComponents;
146
154
  }
147
155
 
148
156
  normalizeResponse(
@@ -173,6 +181,7 @@ class RelayResponseNormalizer {
173
181
  'RelayResponseNormalizer(): Undefined variable `%s`.',
174
182
  name,
175
183
  );
184
+ // $FlowFixMe[cannot-write]
176
185
  return this._variables[name];
177
186
  }
178
187
 
@@ -204,6 +213,10 @@ class RelayResponseNormalizer {
204
213
  this._traverseSelections(selection, record, data);
205
214
  }
206
215
  break;
216
+ case FRAGMENT_SPREAD: {
217
+ this._traverseSelections(selection.fragment, record, data);
218
+ break;
219
+ }
207
220
  case INLINE_FRAGMENT: {
208
221
  const {abstractKey} = selection;
209
222
  if (abstractKey == null) {
@@ -293,6 +306,12 @@ class RelayResponseNormalizer {
293
306
  this._traverseSelections(selection, record, data);
294
307
  this._isClientExtension = isClientExtension;
295
308
  break;
309
+ case CLIENT_COMPONENT:
310
+ if (this._shouldProcessClientComponents === false) {
311
+ break;
312
+ }
313
+ this._traverseSelections(selection.fragment, record, data);
314
+ break;
296
315
  case FLIGHT_FIELD:
297
316
  if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
298
317
  this._normalizeFlightField(node, selection, record, data);
@@ -515,31 +534,73 @@ class RelayResponseNormalizer {
515
534
  const fieldValue = data[responseKey];
516
535
 
517
536
  if (fieldValue == null) {
537
+ if (fieldValue === undefined) {
538
+ // Flight field may be missing in the response if:
539
+ // - It is inside an abstract type refinement where the concrete type does
540
+ // not conform to the interface/union.
541
+ // However an otherwise-required field may also be missing if the server
542
+ // is configured to skip fields with `null` values, in which case the
543
+ // client is assumed to be correctly configured with
544
+ // treatMissingFieldsAsNull=true.
545
+ if (this._isUnmatchedAbstractType) {
546
+ // Field not expected to exist regardless of whether the server is pruning null
547
+ // fields or not.
548
+ return;
549
+ } else if (!this._treatMissingFieldsAsNull) {
550
+ // Not optional and the server is not pruning null fields: field is expected
551
+ // to be present
552
+ if (__DEV__) {
553
+ warning(
554
+ false,
555
+ 'RelayResponseNormalizer: Payload did not contain a value ' +
556
+ 'for field `%s: %s`. Check that you are parsing with the same ' +
557
+ 'query that was used to fetch the payload.',
558
+ responseKey,
559
+ storageKey,
560
+ );
561
+ }
562
+ return;
563
+ }
564
+ }
518
565
  RelayModernRecord.setValue(record, storageKey, null);
519
566
  return;
520
567
  }
521
568
 
522
569
  const reactFlightPayload = refineToReactFlightPayloadData(fieldValue);
570
+ const reactFlightPayloadDeserializer = this._reactFlightPayloadDeserializer;
523
571
 
524
572
  invariant(
525
573
  reactFlightPayload != null,
526
- 'RelayResponseNormalizer(): Expected React Flight payload data ' +
527
- 'to be an object with `tree` and `queries` properties, got `%s`.',
574
+ 'RelayResponseNormalizer: Expected React Flight payload data to be an ' +
575
+ 'object with `status`, tree`, `queries` and `errors` properties, got ' +
576
+ '`%s`.',
528
577
  fieldValue,
529
578
  );
530
579
  invariant(
531
- typeof this._reactFlightPayloadDeserializer === 'function',
580
+ typeof reactFlightPayloadDeserializer === 'function',
532
581
  'RelayResponseNormalizer: Expected reactFlightPayloadDeserializer to ' +
533
582
  'be a function, got `%s`.',
534
- this._reactFlightPayloadDeserializer,
583
+ reactFlightPayloadDeserializer,
535
584
  );
536
585
 
537
- // We store the deserialized reactFlightClientResponse in a separate
538
- // record and link it to the parent record. This is so we can GC the Flight
539
- // tree later even if the parent record is still reachable.
540
- const reactFlightClientResponse = this._reactFlightPayloadDeserializer(
541
- reactFlightPayload.tree,
542
- );
586
+ if (reactFlightPayload.errors.length > 0) {
587
+ if (typeof this._reactFlightServerErrorHandler === 'function') {
588
+ this._reactFlightServerErrorHandler(
589
+ reactFlightPayload.status,
590
+ reactFlightPayload.errors,
591
+ );
592
+ } else {
593
+ warning(
594
+ false,
595
+ 'RelayResponseNormalizer: Received server errors for field `%s`.\n\n' +
596
+ '%s\n%s',
597
+ responseKey,
598
+ reactFlightPayload.errors[0].message,
599
+ reactFlightPayload.errors[0].stack,
600
+ );
601
+ }
602
+ }
603
+
543
604
  const reactFlightID = generateClientID(
544
605
  RelayModernRecord.getDataID(record),
545
606
  getStorageKey(selection, this._variables),
@@ -552,12 +613,49 @@ class RelayResponseNormalizer {
552
613
  );
553
614
  this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
554
615
  }
616
+
617
+ if (reactFlightPayload.tree == null) {
618
+ // This typically indicates that a fatal server error prevented rows from
619
+ // being written. When this occurs, we should not continue normalization of
620
+ // the Flight field because the row response is malformed.
621
+ //
622
+ // Receiving empty rows is OK because it can indicate the start of a stream.
623
+ warning(
624
+ false,
625
+ 'RelayResponseNormalizer: Expected `tree` not to be null. This ' +
626
+ 'typically indicates that a fatal server error prevented any Server ' +
627
+ 'Component rows from being written.',
628
+ );
629
+ // We create the flight record with a null value for the tree
630
+ // and empty reachable definitions
631
+ RelayModernRecord.setValue(
632
+ reactFlightClientResponseRecord,
633
+ REACT_FLIGHT_TREE_STORAGE_KEY,
634
+ null,
635
+ );
636
+ RelayModernRecord.setValue(
637
+ reactFlightClientResponseRecord,
638
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
639
+ [],
640
+ );
641
+ RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
642
+ return;
643
+ }
644
+
645
+ // We store the deserialized reactFlightClientResponse in a separate
646
+ // record and link it to the parent record. This is so we can GC the Flight
647
+ // tree later even if the parent record is still reachable.
648
+ const reactFlightClientResponse = reactFlightPayloadDeserializer(
649
+ reactFlightPayload.tree,
650
+ );
651
+
555
652
  RelayModernRecord.setValue(
556
653
  reactFlightClientResponseRecord,
557
654
  REACT_FLIGHT_TREE_STORAGE_KEY,
558
655
  reactFlightClientResponse,
559
656
  );
560
- const reachableQueries: Array<ReactFlightReachableQuery> = [];
657
+
658
+ const reachableExecutableDefinitions: Array<ReactFlightReachableExecutableDefinitions> = [];
561
659
  for (const query of reactFlightPayload.queries) {
562
660
  if (query.response.data != null) {
563
661
  this._moduleImportPayloads.push({
@@ -569,15 +667,31 @@ class RelayResponseNormalizer {
569
667
  variables: query.variables,
570
668
  });
571
669
  }
572
- reachableQueries.push({
670
+ reachableExecutableDefinitions.push({
573
671
  module: query.module,
574
672
  variables: query.variables,
575
673
  });
576
674
  }
675
+ for (const fragment of reactFlightPayload.fragments) {
676
+ if (fragment.response.data != null) {
677
+ this._moduleImportPayloads.push({
678
+ data: fragment.response.data,
679
+ dataID: fragment.__id,
680
+ operationReference: fragment.module,
681
+ path: [],
682
+ typeName: fragment.__typename,
683
+ variables: fragment.variables,
684
+ });
685
+ }
686
+ reachableExecutableDefinitions.push({
687
+ module: fragment.module,
688
+ variables: fragment.variables,
689
+ });
690
+ }
577
691
  RelayModernRecord.setValue(
578
692
  reactFlightClientResponseRecord,
579
- REACT_FLIGHT_QUERIES_STORAGE_KEY,
580
- reachableQueries,
693
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
694
+ reachableExecutableDefinitions,
581
695
  );
582
696
  RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
583
697
  }
@@ -783,9 +897,6 @@ class RelayResponseNormalizer {
783
897
  }
784
898
  }
785
899
 
786
- const instrumentedNormalize: typeof normalize = RelayProfiler.instrument(
787
- 'RelayResponseNormalizer.normalize',
900
+ module.exports = {
788
901
  normalize,
789
- );
790
-
791
- module.exports = {normalize: instrumentedNormalize};
902
+ };
@@ -19,7 +19,9 @@ const {getType} = require('./RelayModernRecord');
19
19
  import type {ReactFlightPayloadData} from '../network/RelayNetworkTypes';
20
20
  import type {ReactFlightClientResponse, Record} from './RelayStoreTypes';
21
21
 
22
- const REACT_FLIGHT_QUERIES_STORAGE_KEY = 'queries';
22
+ // Reachable (client) executable definitions encountered while server component
23
+ // rendering
24
+ const REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY = 'executableDefinitions';
23
25
  const REACT_FLIGHT_TREE_STORAGE_KEY = 'tree';
24
26
  const REACT_FLIGHT_TYPE_NAME = 'ReactFlightComponent';
25
27
 
@@ -29,8 +31,11 @@ function refineToReactFlightPayloadData(
29
31
  if (
30
32
  payload == null ||
31
33
  typeof payload !== 'object' ||
32
- !Array.isArray(payload.tree) ||
33
- !Array.isArray(payload.queries)
34
+ typeof payload.status !== 'string' ||
35
+ (!Array.isArray(payload.tree) && payload.tree !== null) ||
36
+ !Array.isArray(payload.queries) ||
37
+ !Array.isArray(payload.fragments) ||
38
+ !Array.isArray(payload.errors)
34
39
  ) {
35
40
  return null;
36
41
  }
@@ -56,7 +61,7 @@ function getReactFlightClientResponse(
56
61
  }
57
62
 
58
63
  module.exports = {
59
- REACT_FLIGHT_QUERIES_STORAGE_KEY,
64
+ REACT_FLIGHT_EXECUTABLE_DEFINITIONS_STORAGE_KEY,
60
65
  REACT_FLIGHT_TREE_STORAGE_KEY,
61
66
  REACT_FLIGHT_TYPE_NAME,
62
67
  getReactFlightClientResponse,
@@ -12,20 +12,22 @@
12
12
 
13
13
  'use strict';
14
14
 
15
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
15
16
  const RelayReader = require('./RelayReader');
16
17
 
17
18
  const deepFreeze = require('../util/deepFreeze');
18
19
  const hasOverlappingIDs = require('./hasOverlappingIDs');
19
- const isEmptyObject = require('../util/isEmptyObject');
20
20
  const recycleNodesInto = require('../util/recycleNodesInto');
21
21
 
22
22
  import type {Disposable} from '../util/RelayRuntimeTypes';
23
23
  import type {
24
+ LogFunction,
25
+ OperationDescriptor,
26
+ DataIDSet,
24
27
  RecordSource,
25
28
  RequestDescriptor,
26
29
  Snapshot,
27
30
  StoreSubscriptions,
28
- UpdatedRecords,
29
31
  } from './RelayStoreTypes';
30
32
 
31
33
  type Subscription = {|
@@ -37,9 +39,11 @@ type Subscription = {|
37
39
 
38
40
  class RelayStoreSubscriptions implements StoreSubscriptions {
39
41
  _subscriptions: Set<Subscription>;
42
+ __log: ?LogFunction;
40
43
 
41
- constructor() {
44
+ constructor(log?: ?LogFunction) {
42
45
  this._subscriptions = new Set();
46
+ this.__log = log;
43
47
  }
44
48
 
45
49
  subscribe(
@@ -65,7 +69,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
65
69
  // but its base has changed (we just applied a final payload): recompute
66
70
  // a backup so that we can later restore to the state the subscription
67
71
  // should be in.
68
- // - stale=false: This subscription was restored to the same value than
72
+ // - stale=false: This subscription was restored to the same value as
69
73
  // `snapshot`. That means this subscription does *not* have changes relative
70
74
  // to its base, so the current `snapshot` is valid to use as a backup.
71
75
  if (!subscription.stale) {
@@ -103,16 +107,18 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
103
107
 
104
108
  updateSubscriptions(
105
109
  source: RecordSource,
106
- updatedRecordIDs: UpdatedRecords,
110
+ updatedRecordIDs: DataIDSet,
107
111
  updatedOwners: Array<RequestDescriptor>,
112
+ sourceOperation?: OperationDescriptor,
108
113
  ) {
109
- const hasUpdatedRecords = !isEmptyObject(updatedRecordIDs);
114
+ const hasUpdatedRecords = updatedRecordIDs.size !== 0;
110
115
  this._subscriptions.forEach(subscription => {
111
116
  const owner = this._updateSubscription(
112
117
  source,
113
118
  subscription,
114
119
  updatedRecordIDs,
115
120
  hasUpdatedRecords,
121
+ sourceOperation,
116
122
  );
117
123
  if (owner != null) {
118
124
  updatedOwners.push(owner);
@@ -131,8 +137,9 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
131
137
  _updateSubscription(
132
138
  source: RecordSource,
133
139
  subscription: Subscription,
134
- updatedRecordIDs: UpdatedRecords,
140
+ updatedRecordIDs: DataIDSet,
135
141
  hasUpdatedRecords: boolean,
142
+ sourceOperation?: OperationDescriptor,
136
143
  ): ?RequestDescriptor {
137
144
  const {backup, callback, snapshot, stale} = subscription;
138
145
  const hasOverlappingUpdates =
@@ -159,6 +166,14 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
159
166
  subscription.snapshot = nextSnapshot;
160
167
  subscription.stale = false;
161
168
  if (nextSnapshot.data !== snapshot.data) {
169
+ if (this.__log && RelayFeatureFlags.ENABLE_NOTIFY_SUBSCRIPTION) {
170
+ this.__log({
171
+ name: 'store.notify.subscription',
172
+ sourceOperation,
173
+ snapshot,
174
+ nextSnapshot,
175
+ });
176
+ }
162
177
  callback(nextSnapshot);
163
178
  return snapshot.selector.owner;
164
179
  }
@@ -12,6 +12,7 @@
12
12
 
13
13
  'use strict';
14
14
 
15
+ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
15
16
  const RelayReader = require('./RelayReader');
16
17
 
17
18
  const deepFreeze = require('../util/deepFreeze');
@@ -19,12 +20,13 @@ const recycleNodesInto = require('../util/recycleNodesInto');
19
20
 
20
21
  import type {DataID, Disposable} from '../util/RelayRuntimeTypes';
21
22
  import type {
22
- RecordMap,
23
+ LogFunction,
24
+ OperationDescriptor,
23
25
  RecordSource,
24
26
  RequestDescriptor,
25
27
  Snapshot,
26
28
  StoreSubscriptions,
27
- UpdatedRecords,
29
+ DataIDSet,
28
30
  } from './RelayStoreTypes';
29
31
 
30
32
  type Subscription = {|
@@ -40,12 +42,14 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
40
42
  _snapshotRevision: number;
41
43
  _subscriptionsByDataId: Map<DataID, Set<Subscription>>;
42
44
  _staleSubscriptions: Set<Subscription>;
45
+ __log: ?LogFunction;
43
46
 
44
- constructor() {
47
+ constructor(log?: ?LogFunction) {
45
48
  this._notifiedRevision = 0;
46
49
  this._snapshotRevision = 0;
47
50
  this._subscriptionsByDataId = new Map();
48
51
  this._staleSubscriptions = new Set();
52
+ this.__log = log;
49
53
  }
50
54
 
51
55
  subscribe(
@@ -60,7 +64,7 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
60
64
  snapshot,
61
65
  };
62
66
  const dispose = () => {
63
- for (const dataId in snapshot.seenRecords) {
67
+ for (const dataId of snapshot.seenRecords) {
64
68
  const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
65
69
  if (subscriptionsForDataId != null) {
66
70
  subscriptionsForDataId.delete(subscription);
@@ -71,7 +75,7 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
71
75
  }
72
76
  };
73
77
 
74
- for (const dataId in snapshot.seenRecords) {
78
+ for (const dataId of snapshot.seenRecords) {
75
79
  const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
76
80
  if (subscriptionsForDataId != null) {
77
81
  subscriptionsForDataId.add(subscription);
@@ -150,11 +154,12 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
150
154
 
151
155
  updateSubscriptions(
152
156
  source: RecordSource,
153
- updatedRecordIDs: UpdatedRecords,
157
+ updatedRecordIDs: DataIDSet,
154
158
  updatedOwners: Array<RequestDescriptor>,
159
+ sourceOperation?: OperationDescriptor,
155
160
  ) {
156
161
  this._notifiedRevision++;
157
- Object.keys(updatedRecordIDs).forEach(updatedRecordId => {
162
+ updatedRecordIDs.forEach(updatedRecordId => {
158
163
  const subcriptionsForDataId = this._subscriptionsByDataId.get(
159
164
  updatedRecordId,
160
165
  );
@@ -165,7 +170,12 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
165
170
  if (subscription.notifiedRevision === this._notifiedRevision) {
166
171
  return;
167
172
  }
168
- const owner = this._updateSubscription(source, subscription, false);
173
+ const owner = this._updateSubscription(
174
+ source,
175
+ subscription,
176
+ false,
177
+ sourceOperation,
178
+ );
169
179
  if (owner != null) {
170
180
  updatedOwners.push(owner);
171
181
  }
@@ -175,7 +185,12 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
175
185
  if (subscription.notifiedRevision === this._notifiedRevision) {
176
186
  return;
177
187
  }
178
- const owner = this._updateSubscription(source, subscription, true);
188
+ const owner = this._updateSubscription(
189
+ source,
190
+ subscription,
191
+ true,
192
+ sourceOperation,
193
+ );
179
194
  if (owner != null) {
180
195
  updatedOwners.push(owner);
181
196
  }
@@ -196,6 +211,7 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
196
211
  source: RecordSource,
197
212
  subscription: Subscription,
198
213
  stale: boolean,
214
+ sourceOperation?: OperationDescriptor,
199
215
  ): ?RequestDescriptor {
200
216
  const {backup, callback, snapshot} = subscription;
201
217
  let nextSnapshot: Snapshot =
@@ -220,6 +236,14 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
220
236
  this._updateSubscriptionsMap(subscription, prevSeenRecords);
221
237
 
222
238
  if (nextSnapshot.data !== snapshot.data) {
239
+ if (this.__log && RelayFeatureFlags.ENABLE_NOTIFY_SUBSCRIPTION) {
240
+ this.__log({
241
+ name: 'store.notify.subscription',
242
+ sourceOperation,
243
+ snapshot,
244
+ nextSnapshot,
245
+ });
246
+ }
223
247
  callback(nextSnapshot);
224
248
  return snapshot.selector.owner;
225
249
  }
@@ -233,9 +257,9 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
233
257
  */
234
258
  _updateSubscriptionsMap(
235
259
  subscription: Subscription,
236
- prevSeenRecords: RecordMap,
260
+ prevSeenRecords: DataIDSet,
237
261
  ) {
238
- for (const dataId in prevSeenRecords) {
262
+ for (const dataId of prevSeenRecords) {
239
263
  const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
240
264
  if (subscriptionsForDataId != null) {
241
265
  subscriptionsForDataId.delete(subscription);
@@ -245,7 +269,7 @@ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
245
269
  }
246
270
  }
247
271
 
248
- for (const dataId in subscription.snapshot.seenRecords) {
272
+ for (const dataId of subscription.snapshot.seenRecords) {
249
273
  const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
250
274
  if (subscriptionsForDataId != null) {
251
275
  subscriptionsForDataId.add(subscription);