relay-runtime 10.0.1 → 10.1.3

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 (82) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +6 -0
  2. package/handlers/connection/MutationHandlers.js.flow +152 -20
  3. package/index.js +1 -1
  4. package/index.js.flow +17 -1
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +9 -0
  6. package/lib/handlers/connection/MutationHandlers.js +185 -21
  7. package/lib/index.js +7 -0
  8. package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -7
  9. package/lib/mutations/commitMutation.js +1 -4
  10. package/lib/mutations/validateMutation.js +28 -12
  11. package/lib/network/RelayQueryResponseCache.js +3 -7
  12. package/lib/query/GraphQLTag.js +2 -1
  13. package/lib/query/fetchQuery.js +2 -3
  14. package/lib/query/fetchQueryInternal.js +2 -3
  15. package/lib/store/DataChecker.js +85 -10
  16. package/lib/store/RelayConcreteVariables.js +2 -6
  17. package/lib/store/RelayModernEnvironment.js +81 -72
  18. package/lib/store/RelayModernFragmentSpecResolver.js +14 -7
  19. package/lib/store/RelayModernOperationDescriptor.js +6 -5
  20. package/lib/store/RelayModernQueryExecutor.js +46 -33
  21. package/lib/store/RelayModernRecord.js +3 -7
  22. package/lib/store/RelayModernStore.js +39 -137
  23. package/lib/store/RelayOperationTracker.js +7 -9
  24. package/lib/store/RelayOptimisticRecordSource.js +2 -6
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +196 -33
  27. package/lib/store/RelayRecordSourceMapImpl.js +3 -5
  28. package/lib/store/RelayReferenceMarker.js +87 -5
  29. package/lib/store/RelayResponseNormalizer.js +115 -19
  30. package/lib/store/RelayStoreReactFlightUtils.js +47 -0
  31. package/lib/store/RelayStoreSubscriptions.js +162 -0
  32. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +258 -0
  33. package/lib/store/StoreInspector.js +2 -6
  34. package/lib/store/createRelayContext.js +5 -0
  35. package/lib/store/defaultRequiredFieldLogger.js +18 -0
  36. package/lib/store/normalizeRelayPayload.js +2 -6
  37. package/lib/subscription/requestSubscription.js +2 -3
  38. package/lib/util/NormalizationNode.js +1 -5
  39. package/lib/util/RelayConcreteNode.js +2 -0
  40. package/lib/util/RelayFeatureFlags.js +7 -2
  41. package/lib/util/createPayloadFor3DField.js +2 -7
  42. package/lib/util/getFragmentIdentifier.js +12 -3
  43. package/lib/util/getOperation.js +33 -0
  44. package/lib/util/isEmptyObject.js +25 -0
  45. package/lib/util/recycleNodesInto.js +4 -1
  46. package/lib/util/reportMissingRequiredFields.js +48 -0
  47. package/mutations/commitMutation.js.flow +1 -2
  48. package/mutations/validateMutation.js.flow +34 -5
  49. package/network/RelayNetworkTypes.js.flow +22 -0
  50. package/package.json +2 -2
  51. package/query/GraphQLTag.js.flow +3 -1
  52. package/query/fetchQuery.js.flow +2 -2
  53. package/query/fetchQueryInternal.js.flow +0 -5
  54. package/relay-runtime.js +2 -2
  55. package/relay-runtime.min.js +2 -2
  56. package/store/DataChecker.js.flow +68 -2
  57. package/store/RelayModernEnvironment.js.flow +107 -87
  58. package/store/RelayModernFragmentSpecResolver.js.flow +13 -1
  59. package/store/RelayModernOperationDescriptor.js.flow +5 -1
  60. package/store/RelayModernQueryExecutor.js.flow +47 -23
  61. package/store/RelayModernStore.js.flow +33 -107
  62. package/store/RelayPublishQueue.js.flow +1 -1
  63. package/store/RelayReader.js.flow +180 -15
  64. package/store/RelayReferenceMarker.js.flow +72 -5
  65. package/store/RelayResponseNormalizer.js.flow +130 -19
  66. package/store/RelayStoreReactFlightUtils.js.flow +64 -0
  67. package/store/RelayStoreSubscriptions.js.flow +168 -0
  68. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +259 -0
  69. package/store/RelayStoreTypes.js.flow +130 -37
  70. package/store/createRelayContext.js.flow +3 -0
  71. package/store/defaultRequiredFieldLogger.js.flow +23 -0
  72. package/subscription/requestSubscription.js.flow +5 -2
  73. package/util/NormalizationNode.js.flow +17 -2
  74. package/util/ReaderNode.js.flow +20 -1
  75. package/util/RelayConcreteNode.js.flow +6 -0
  76. package/util/RelayFeatureFlags.js.flow +12 -1
  77. package/util/getFragmentIdentifier.js.flow +33 -9
  78. package/util/getOperation.js.flow +40 -0
  79. package/util/getRequestIdentifier.js.flow +1 -1
  80. package/util/isEmptyObject.js.flow +25 -0
  81. package/util/recycleNodesInto.js.flow +11 -0
  82. package/util/reportMissingRequiredFields.js.flow +51 -0
@@ -16,6 +16,7 @@ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
16
  const RelayModernRecord = require('./RelayModernRecord');
17
17
  const RelayProfiler = require('../util/RelayProfiler');
18
18
 
19
+ const areEqual = require('areEqual');
19
20
  const invariant = require('invariant');
20
21
  const warning = require('warning');
21
22
 
@@ -23,6 +24,7 @@ const {
23
24
  CONDITION,
24
25
  CLIENT_EXTENSION,
25
26
  DEFER,
27
+ FLIGHT_FIELD,
26
28
  INLINE_FRAGMENT,
27
29
  LINKED_FIELD,
28
30
  LINKED_HANDLE,
@@ -34,6 +36,12 @@ const {
34
36
  } = require('../util/RelayConcreteNode');
35
37
  const {generateClientID, isClientID} = require('./ClientID');
36
38
  const {createNormalizationSelector} = require('./RelayModernSelector');
39
+ const {
40
+ refineToReactFlightPayloadData,
41
+ REACT_FLIGHT_QUERIES_STORAGE_KEY,
42
+ REACT_FLIGHT_TREE_STORAGE_KEY,
43
+ REACT_FLIGHT_TYPE_NAME,
44
+ } = require('./RelayStoreReactFlightUtils');
37
45
  const {
38
46
  getArgumentValues,
39
47
  getHandleStorageKey,
@@ -42,12 +50,14 @@ const {
42
50
  getStorageKey,
43
51
  TYPENAME_KEY,
44
52
  ROOT_ID,
53
+ ROOT_TYPE,
45
54
  } = require('./RelayStoreUtils');
46
55
  const {generateTypeID, TYPE_SCHEMA_TYPE} = require('./TypeID');
47
56
 
48
57
  import type {PayloadData} from '../network/RelayNetworkTypes';
49
58
  import type {
50
59
  NormalizationDefer,
60
+ NormalizationFlightField,
51
61
  NormalizationLinkedField,
52
62
  NormalizationModuleImport,
53
63
  NormalizationNode,
@@ -61,6 +71,8 @@ import type {
61
71
  ModuleImportPayload,
62
72
  MutableRecordSource,
63
73
  NormalizationSelector,
74
+ ReactFlightReachableQuery,
75
+ ReactFlightPayloadDeserializer,
64
76
  Record,
65
77
  RelayResponsePayload,
66
78
  } from './RelayStoreTypes';
@@ -74,6 +86,7 @@ export type NormalizationOptions = {|
74
86
  +getDataID: GetDataID,
75
87
  +treatMissingFieldsAsNull: boolean,
76
88
  +path?: $ReadOnlyArray<string>,
89
+ +reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
77
90
  |};
78
91
 
79
92
  /**
@@ -111,6 +124,7 @@ class RelayResponseNormalizer {
111
124
  _path: Array<string>;
112
125
  _recordSource: MutableRecordSource;
113
126
  _variables: Variables;
127
+ _reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
114
128
 
115
129
  constructor(
116
130
  recordSource: MutableRecordSource,
@@ -127,6 +141,8 @@ class RelayResponseNormalizer {
127
141
  this._path = options.path ? [...options.path] : [];
128
142
  this._recordSource = recordSource;
129
143
  this._variables = variables;
144
+ this._reactFlightPayloadDeserializer =
145
+ options.reactFlightPayloadDeserializer;
130
146
  }
131
147
 
132
148
  normalizeResponse(
@@ -277,6 +293,13 @@ class RelayResponseNormalizer {
277
293
  this._traverseSelections(selection, record, data);
278
294
  this._isClientExtension = isClientExtension;
279
295
  break;
296
+ case FLIGHT_FIELD:
297
+ if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
298
+ this._normalizeFlightField(node, selection, record, data);
299
+ } else {
300
+ throw new Error('Flight fields are not yet supported.');
301
+ }
302
+ break;
280
303
  default:
281
304
  (selection: empty);
282
305
  invariant(
@@ -441,23 +464,27 @@ class RelayResponseNormalizer {
441
464
  return;
442
465
  }
443
466
  }
444
- if (selection.kind === SCALAR_FIELD && __DEV__) {
445
- this._validateConflictingFieldsWithIdenticalId(
446
- record,
447
- storageKey,
448
- fieldValue,
449
- );
467
+ if (__DEV__) {
468
+ if (selection.kind === SCALAR_FIELD) {
469
+ this._validateConflictingFieldsWithIdenticalId(
470
+ record,
471
+ storageKey,
472
+ fieldValue,
473
+ );
474
+ }
450
475
  }
451
476
  RelayModernRecord.setValue(record, storageKey, null);
452
477
  return;
453
478
  }
454
479
 
455
480
  if (selection.kind === SCALAR_FIELD) {
456
- this._validateConflictingFieldsWithIdenticalId(
457
- record,
458
- storageKey,
459
- fieldValue,
460
- );
481
+ if (__DEV__) {
482
+ this._validateConflictingFieldsWithIdenticalId(
483
+ record,
484
+ storageKey,
485
+ fieldValue,
486
+ );
487
+ }
461
488
  RelayModernRecord.setValue(record, storageKey, fieldValue);
462
489
  } else if (selection.kind === LINKED_FIELD) {
463
490
  this._path.push(responseKey);
@@ -477,6 +504,84 @@ class RelayResponseNormalizer {
477
504
  }
478
505
  }
479
506
 
507
+ _normalizeFlightField(
508
+ parent: NormalizationNode,
509
+ selection: NormalizationFlightField,
510
+ record: Record,
511
+ data: PayloadData,
512
+ ) {
513
+ const responseKey = selection.alias || selection.name;
514
+ const storageKey = getStorageKey(selection, this._variables);
515
+ const fieldValue = data[responseKey];
516
+
517
+ if (fieldValue == null) {
518
+ RelayModernRecord.setValue(record, storageKey, null);
519
+ return;
520
+ }
521
+
522
+ const reactFlightPayload = refineToReactFlightPayloadData(fieldValue);
523
+
524
+ invariant(
525
+ reactFlightPayload != null,
526
+ 'RelayResponseNormalizer(): Expected React Flight payload data ' +
527
+ 'to be an object with `tree` and `queries` properties, got `%s`.',
528
+ fieldValue,
529
+ );
530
+ invariant(
531
+ typeof this._reactFlightPayloadDeserializer === 'function',
532
+ 'RelayResponseNormalizer: Expected reactFlightPayloadDeserializer to ' +
533
+ 'be a function, got `%s`.',
534
+ this._reactFlightPayloadDeserializer,
535
+ );
536
+
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
+ );
543
+ const reactFlightID = generateClientID(
544
+ RelayModernRecord.getDataID(record),
545
+ getStorageKey(selection, this._variables),
546
+ );
547
+ let reactFlightClientResponseRecord = this._recordSource.get(reactFlightID);
548
+ if (reactFlightClientResponseRecord == null) {
549
+ reactFlightClientResponseRecord = RelayModernRecord.create(
550
+ reactFlightID,
551
+ REACT_FLIGHT_TYPE_NAME,
552
+ );
553
+ this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
554
+ }
555
+ RelayModernRecord.setValue(
556
+ reactFlightClientResponseRecord,
557
+ REACT_FLIGHT_TREE_STORAGE_KEY,
558
+ reactFlightClientResponse,
559
+ );
560
+ const reachableQueries: Array<ReactFlightReachableQuery> = [];
561
+ for (const query of reactFlightPayload.queries) {
562
+ if (query.response.data != null) {
563
+ this._moduleImportPayloads.push({
564
+ data: query.response.data,
565
+ dataID: ROOT_ID,
566
+ operationReference: query.module,
567
+ path: [],
568
+ typeName: ROOT_TYPE,
569
+ variables: query.variables,
570
+ });
571
+ }
572
+ reachableQueries.push({
573
+ module: query.module,
574
+ variables: query.variables,
575
+ });
576
+ }
577
+ RelayModernRecord.setValue(
578
+ reactFlightClientResponseRecord,
579
+ REACT_FLIGHT_QUERIES_STORAGE_KEY,
580
+ reachableQueries,
581
+ );
582
+ RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
583
+ }
584
+
480
585
  _normalizeLink(
481
586
  field: NormalizationLinkedField,
482
587
  record: Record,
@@ -582,13 +687,17 @@ class RelayResponseNormalizer {
582
687
  } else if (__DEV__) {
583
688
  this._validateRecordType(nextRecord, field, item);
584
689
  }
585
- if (prevIDs && __DEV__) {
586
- this._validateConflictingLinkedFieldsWithIdenticalId(
587
- record,
588
- prevIDs[nextIndex],
589
- nextID,
590
- storageKey,
591
- );
690
+ // NOTE: the check to strip __DEV__ code only works for simple
691
+ // `if (__DEV__)`
692
+ if (__DEV__) {
693
+ if (prevIDs) {
694
+ this._validateConflictingLinkedFieldsWithIdenticalId(
695
+ record,
696
+ prevIDs[nextIndex],
697
+ nextID,
698
+ storageKey,
699
+ );
700
+ }
592
701
  }
593
702
  // $FlowFixMe[incompatible-variance]
594
703
  this._traverseSelections(field, nextRecord, item);
@@ -629,13 +738,14 @@ class RelayResponseNormalizer {
629
738
  storageKey: string,
630
739
  fieldValue: mixed,
631
740
  ): void {
741
+ // NOTE: Only call this function in DEV
632
742
  if (__DEV__) {
633
743
  const dataID = RelayModernRecord.getDataID(record);
634
744
  var previousValue = RelayModernRecord.getValue(record, storageKey);
635
745
  warning(
636
746
  storageKey === TYPENAME_KEY ||
637
747
  previousValue === undefined ||
638
- previousValue === fieldValue,
748
+ areEqual(previousValue, fieldValue),
639
749
  'RelayResponseNormalizer: Invalid record. The record contains two ' +
640
750
  'instances of the same id: `%s` with conflicting field, %s and its values: %s and %s. ' +
641
751
  'If two fields are different but share ' +
@@ -657,6 +767,7 @@ class RelayResponseNormalizer {
657
767
  nextID: DataID,
658
768
  storageKey: string,
659
769
  ): void {
770
+ // NOTE: Only call this function in DEV
660
771
  if (__DEV__) {
661
772
  warning(
662
773
  prevID === undefined || prevID === nextID,
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const invariant = require('invariant');
16
+
17
+ const {getType} = require('./RelayModernRecord');
18
+
19
+ import type {ReactFlightPayloadData} from '../network/RelayNetworkTypes';
20
+ import type {ReactFlightClientResponse, Record} from './RelayStoreTypes';
21
+
22
+ const REACT_FLIGHT_QUERIES_STORAGE_KEY = 'queries';
23
+ const REACT_FLIGHT_TREE_STORAGE_KEY = 'tree';
24
+ const REACT_FLIGHT_TYPE_NAME = 'ReactFlightComponent';
25
+
26
+ function refineToReactFlightPayloadData(
27
+ payload: mixed,
28
+ ): ?ReactFlightPayloadData {
29
+ if (
30
+ payload == null ||
31
+ typeof payload !== 'object' ||
32
+ !Array.isArray(payload.tree) ||
33
+ !Array.isArray(payload.queries)
34
+ ) {
35
+ return null;
36
+ }
37
+ return (payload: $FlowFixMe);
38
+ }
39
+
40
+ function getReactFlightClientResponse(
41
+ record: Record,
42
+ ): ?ReactFlightClientResponse {
43
+ invariant(
44
+ getType(record) === REACT_FLIGHT_TYPE_NAME,
45
+ 'getReactFlightClientResponse(): Expected a ReactFlightComponentRecord, ' +
46
+ 'got %s.',
47
+ record,
48
+ );
49
+ const response: ?ReactFlightClientResponse = (record[
50
+ REACT_FLIGHT_TREE_STORAGE_KEY
51
+ ]: $FlowFixMe);
52
+ if (response != null) {
53
+ return response;
54
+ }
55
+ return null;
56
+ }
57
+
58
+ module.exports = {
59
+ REACT_FLIGHT_QUERIES_STORAGE_KEY,
60
+ REACT_FLIGHT_TREE_STORAGE_KEY,
61
+ REACT_FLIGHT_TYPE_NAME,
62
+ getReactFlightClientResponse,
63
+ refineToReactFlightPayloadData,
64
+ };
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const RelayReader = require('./RelayReader');
16
+
17
+ const deepFreeze = require('../util/deepFreeze');
18
+ const hasOverlappingIDs = require('./hasOverlappingIDs');
19
+ const isEmptyObject = require('../util/isEmptyObject');
20
+ const recycleNodesInto = require('../util/recycleNodesInto');
21
+
22
+ import type {Disposable} from '../util/RelayRuntimeTypes';
23
+ import type {
24
+ RecordSource,
25
+ RequestDescriptor,
26
+ Snapshot,
27
+ StoreSubscriptions,
28
+ UpdatedRecords,
29
+ } from './RelayStoreTypes';
30
+
31
+ type Subscription = {|
32
+ callback: (snapshot: Snapshot) => void,
33
+ snapshot: Snapshot,
34
+ stale: boolean,
35
+ backup: ?Snapshot,
36
+ |};
37
+
38
+ class RelayStoreSubscriptions implements StoreSubscriptions {
39
+ _subscriptions: Set<Subscription>;
40
+
41
+ constructor() {
42
+ this._subscriptions = new Set();
43
+ }
44
+
45
+ subscribe(
46
+ snapshot: Snapshot,
47
+ callback: (snapshot: Snapshot) => void,
48
+ ): Disposable {
49
+ const subscription = {backup: null, callback, snapshot, stale: false};
50
+ const dispose = () => {
51
+ this._subscriptions.delete(subscription);
52
+ };
53
+ this._subscriptions.add(subscription);
54
+ return {dispose};
55
+ }
56
+
57
+ snapshotSubscriptions(source: RecordSource) {
58
+ this._subscriptions.forEach(subscription => {
59
+ // Backup occurs after writing a new "final" payload(s) and before (re)applying
60
+ // optimistic changes. Each subscription's `snapshot` represents what was *last
61
+ // published to the subscriber*, which notably may include previous optimistic
62
+ // updates. Therefore a subscription can be in any of the following states:
63
+ // - stale=true: This subscription was restored to a different value than
64
+ // `snapshot`. That means this subscription has changes relative to its base,
65
+ // but its base has changed (we just applied a final payload): recompute
66
+ // a backup so that we can later restore to the state the subscription
67
+ // should be in.
68
+ // - stale=false: This subscription was restored to the same value than
69
+ // `snapshot`. That means this subscription does *not* have changes relative
70
+ // to its base, so the current `snapshot` is valid to use as a backup.
71
+ if (!subscription.stale) {
72
+ subscription.backup = subscription.snapshot;
73
+ return;
74
+ }
75
+ const snapshot = subscription.snapshot;
76
+ const backup = RelayReader.read(source, snapshot.selector);
77
+ const nextData = recycleNodesInto(snapshot.data, backup.data);
78
+ (backup: $FlowFixMe).data = nextData; // backup owns the snapshot and can safely mutate
79
+ subscription.backup = backup;
80
+ });
81
+ }
82
+
83
+ restoreSubscriptions() {
84
+ this._subscriptions.forEach(subscription => {
85
+ const backup = subscription.backup;
86
+ subscription.backup = null;
87
+ if (backup) {
88
+ if (backup.data !== subscription.snapshot.data) {
89
+ subscription.stale = true;
90
+ }
91
+ subscription.snapshot = {
92
+ data: subscription.snapshot.data,
93
+ isMissingData: backup.isMissingData,
94
+ seenRecords: backup.seenRecords,
95
+ selector: backup.selector,
96
+ missingRequiredFields: backup.missingRequiredFields,
97
+ };
98
+ } else {
99
+ subscription.stale = true;
100
+ }
101
+ });
102
+ }
103
+
104
+ updateSubscriptions(
105
+ source: RecordSource,
106
+ updatedRecordIDs: UpdatedRecords,
107
+ updatedOwners: Array<RequestDescriptor>,
108
+ ) {
109
+ const hasUpdatedRecords = !isEmptyObject(updatedRecordIDs);
110
+ this._subscriptions.forEach(subscription => {
111
+ const owner = this._updateSubscription(
112
+ source,
113
+ subscription,
114
+ updatedRecordIDs,
115
+ hasUpdatedRecords,
116
+ );
117
+ if (owner != null) {
118
+ updatedOwners.push(owner);
119
+ }
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Notifies the callback for the subscription if the data for the associated
125
+ * snapshot has changed.
126
+ * Additionally, updates the subscription snapshot with the latest snapshot,
127
+ * and marks it as not stale.
128
+ * Returns the owner (RequestDescriptor) if the subscription was affected by the
129
+ * latest update, or null if it was not affected.
130
+ */
131
+ _updateSubscription(
132
+ source: RecordSource,
133
+ subscription: Subscription,
134
+ updatedRecordIDs: UpdatedRecords,
135
+ hasUpdatedRecords: boolean,
136
+ ): ?RequestDescriptor {
137
+ const {backup, callback, snapshot, stale} = subscription;
138
+ const hasOverlappingUpdates =
139
+ hasUpdatedRecords &&
140
+ hasOverlappingIDs(snapshot.seenRecords, updatedRecordIDs);
141
+ if (!stale && !hasOverlappingUpdates) {
142
+ return;
143
+ }
144
+ let nextSnapshot: Snapshot =
145
+ hasOverlappingUpdates || !backup
146
+ ? RelayReader.read(source, snapshot.selector)
147
+ : backup;
148
+ const nextData = recycleNodesInto(snapshot.data, nextSnapshot.data);
149
+ nextSnapshot = ({
150
+ data: nextData,
151
+ isMissingData: nextSnapshot.isMissingData,
152
+ seenRecords: nextSnapshot.seenRecords,
153
+ selector: nextSnapshot.selector,
154
+ missingRequiredFields: nextSnapshot.missingRequiredFields,
155
+ }: Snapshot);
156
+ if (__DEV__) {
157
+ deepFreeze(nextSnapshot);
158
+ }
159
+ subscription.snapshot = nextSnapshot;
160
+ subscription.stale = false;
161
+ if (nextSnapshot.data !== snapshot.data) {
162
+ callback(nextSnapshot);
163
+ return snapshot.selector.owner;
164
+ }
165
+ }
166
+ }
167
+
168
+ module.exports = RelayStoreSubscriptions;