relay-runtime 7.0.0 → 9.1.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 (157) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +34 -0
  2. package/handlers/connection/ConnectionHandler.js.flow +549 -0
  3. package/handlers/connection/ConnectionInterface.js.flow +92 -0
  4. package/index.js +1 -1
  5. package/index.js.flow +314 -0
  6. package/lib/handlers/RelayDefaultHandlerProvider.js +3 -2
  7. package/lib/handlers/connection/{RelayConnectionHandler.js → ConnectionHandler.js} +34 -35
  8. package/lib/handlers/connection/{RelayConnectionInterface.js → ConnectionInterface.js} +3 -30
  9. package/lib/index.js +29 -27
  10. package/lib/mutations/RelayDeclarativeMutationConfig.js +30 -52
  11. package/lib/mutations/RelayRecordProxy.js +6 -3
  12. package/lib/mutations/RelayRecordSourceMutator.js +3 -9
  13. package/lib/mutations/RelayRecordSourceProxy.js +21 -24
  14. package/lib/mutations/RelayRecordSourceSelectorProxy.js +18 -14
  15. package/lib/mutations/applyOptimisticMutation.js +2 -1
  16. package/lib/mutations/commitLocalUpdate.js +1 -0
  17. package/lib/mutations/commitMutation.js +26 -8
  18. package/lib/mutations/validateMutation.js +21 -11
  19. package/lib/network/ConvertToExecuteFunction.js +1 -0
  20. package/lib/network/RelayNetwork.js +1 -0
  21. package/lib/network/RelayNetworkTypes.js +1 -0
  22. package/lib/network/RelayObservable.js +10 -9
  23. package/lib/network/RelayQueryResponseCache.js +9 -7
  24. package/lib/query/{RelayModernGraphQLTag.js → GraphQLTag.js} +15 -8
  25. package/lib/query/fetchQuery.js +2 -1
  26. package/lib/query/fetchQueryInternal.js +30 -20
  27. package/lib/store/ClientID.js +1 -0
  28. package/lib/store/DataChecker.js +47 -97
  29. package/lib/store/RelayConcreteVariables.js +7 -2
  30. package/lib/store/RelayModernEnvironment.js +82 -41
  31. package/lib/store/RelayModernFragmentSpecResolver.js +61 -21
  32. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  33. package/lib/store/RelayModernQueryExecutor.js +476 -333
  34. package/lib/store/RelayModernRecord.js +39 -9
  35. package/lib/store/RelayModernSelector.js +2 -1
  36. package/lib/store/RelayModernStore.js +359 -371
  37. package/lib/store/RelayOperationTracker.js +36 -78
  38. package/lib/store/RelayOptimisticRecordSource.js +8 -5
  39. package/lib/store/RelayPublishQueue.js +66 -53
  40. package/lib/store/RelayReader.js +2 -24
  41. package/lib/store/RelayRecordSource.js +3 -9
  42. package/lib/store/RelayRecordSourceMapImpl.js +14 -18
  43. package/lib/store/RelayRecordState.js +1 -0
  44. package/lib/store/RelayReferenceMarker.js +8 -58
  45. package/lib/store/RelayResponseNormalizer.js +15 -144
  46. package/lib/store/RelayStoreTypes.js +1 -0
  47. package/lib/store/RelayStoreUtils.js +34 -10
  48. package/lib/store/StoreInspector.js +11 -5
  49. package/lib/store/ViewerPattern.js +1 -0
  50. package/lib/store/cloneRelayHandleSourceField.js +1 -0
  51. package/lib/store/createFragmentSpecResolver.js +1 -0
  52. package/lib/store/createRelayContext.js +1 -0
  53. package/lib/store/defaultGetDataID.js +1 -0
  54. package/lib/store/hasOverlappingIDs.js +1 -0
  55. package/lib/store/isRelayModernEnvironment.js +1 -0
  56. package/lib/store/normalizeRelayPayload.js +8 -4
  57. package/lib/store/readInlineData.js +2 -1
  58. package/lib/subscription/requestSubscription.js +6 -3
  59. package/lib/util/JSResourceTypes.flow.js +12 -0
  60. package/lib/util/NormalizationNode.js +1 -0
  61. package/lib/util/ReaderNode.js +1 -0
  62. package/lib/util/RelayConcreteNode.js +3 -0
  63. package/lib/util/RelayDefaultHandleKey.js +1 -0
  64. package/lib/util/RelayError.js +2 -1
  65. package/lib/util/RelayFeatureFlags.js +3 -2
  66. package/lib/util/RelayProfiler.js +1 -0
  67. package/lib/util/RelayReplaySubject.js +2 -3
  68. package/lib/util/RelayRuntimeTypes.js +1 -0
  69. package/lib/util/createPayloadFor3DField.js +34 -0
  70. package/lib/util/deepFreeze.js +1 -0
  71. package/lib/util/generateID.js +1 -0
  72. package/lib/util/getFragmentIdentifier.js +1 -0
  73. package/lib/util/getRelayHandleKey.js +1 -0
  74. package/lib/util/getRequestIdentifier.js +1 -0
  75. package/lib/util/isPromise.js +1 -0
  76. package/lib/util/isScalarAndEqual.js +1 -0
  77. package/lib/util/recycleNodesInto.js +1 -0
  78. package/lib/util/resolveImmediate.js +1 -0
  79. package/lib/util/stableCopy.js +1 -0
  80. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  81. package/mutations/RelayRecordProxy.js.flow +165 -0
  82. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  83. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  84. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  85. package/mutations/applyOptimisticMutation.js.flow +76 -0
  86. package/mutations/commitLocalUpdate.js.flow +24 -0
  87. package/mutations/commitMutation.js.flow +184 -0
  88. package/mutations/validateMutation.js.flow +211 -0
  89. package/network/ConvertToExecuteFunction.js.flow +49 -0
  90. package/network/RelayNetwork.js.flow +84 -0
  91. package/network/RelayNetworkTypes.js.flow +123 -0
  92. package/network/RelayObservable.js.flow +634 -0
  93. package/network/RelayQueryResponseCache.js.flow +111 -0
  94. package/package.json +1 -1
  95. package/query/GraphQLTag.js.flow +166 -0
  96. package/query/fetchQuery.js.flow +47 -0
  97. package/query/fetchQueryInternal.js.flow +349 -0
  98. package/relay-runtime.js +2 -2
  99. package/relay-runtime.min.js +2 -2
  100. package/store/ClientID.js.flow +43 -0
  101. package/store/DataChecker.js.flow +426 -0
  102. package/store/RelayConcreteVariables.js.flow +96 -0
  103. package/store/RelayModernEnvironment.js.flow +526 -0
  104. package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
  105. package/store/RelayModernOperationDescriptor.js.flow +88 -0
  106. package/store/RelayModernQueryExecutor.js.flow +1327 -0
  107. package/store/RelayModernRecord.js.flow +403 -0
  108. package/store/RelayModernSelector.js.flow +444 -0
  109. package/store/RelayModernStore.js.flow +757 -0
  110. package/store/RelayOperationTracker.js.flow +164 -0
  111. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  112. package/store/RelayPublishQueue.js.flow +401 -0
  113. package/store/RelayReader.js.flow +376 -0
  114. package/store/RelayRecordSource.js.flow +29 -0
  115. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  116. package/store/RelayRecordState.js.flow +37 -0
  117. package/store/RelayReferenceMarker.js.flow +236 -0
  118. package/store/RelayResponseNormalizer.js.flow +556 -0
  119. package/store/RelayStoreTypes.js.flow +873 -0
  120. package/store/RelayStoreUtils.js.flow +218 -0
  121. package/store/StoreInspector.js.flow +173 -0
  122. package/store/ViewerPattern.js.flow +26 -0
  123. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  124. package/store/createFragmentSpecResolver.js.flow +55 -0
  125. package/store/createRelayContext.js.flow +44 -0
  126. package/store/defaultGetDataID.js.flow +27 -0
  127. package/store/hasOverlappingIDs.js.flow +34 -0
  128. package/store/isRelayModernEnvironment.js.flow +27 -0
  129. package/store/normalizeRelayPayload.js.flow +51 -0
  130. package/store/readInlineData.js.flow +75 -0
  131. package/subscription/requestSubscription.js.flow +100 -0
  132. package/util/JSResourceTypes.flow.js.flow +20 -0
  133. package/util/NormalizationNode.js.flow +191 -0
  134. package/util/ReaderNode.js.flow +208 -0
  135. package/util/RelayConcreteNode.js.flow +80 -0
  136. package/util/RelayDefaultHandleKey.js.flow +17 -0
  137. package/util/RelayError.js.flow +33 -0
  138. package/util/RelayFeatureFlags.js.flow +30 -0
  139. package/util/RelayProfiler.js.flow +284 -0
  140. package/util/RelayReplaySubject.js.flow +134 -0
  141. package/util/RelayRuntimeTypes.js.flow +70 -0
  142. package/util/createPayloadFor3DField.js.flow +43 -0
  143. package/util/deepFreeze.js.flow +36 -0
  144. package/util/generateID.js.flow +21 -0
  145. package/util/getFragmentIdentifier.js.flow +52 -0
  146. package/util/getRelayHandleKey.js.flow +41 -0
  147. package/util/getRequestIdentifier.js.flow +41 -0
  148. package/util/isPromise.js.flow +21 -0
  149. package/util/isScalarAndEqual.js.flow +26 -0
  150. package/util/recycleNodesInto.js.flow +80 -0
  151. package/util/resolveImmediate.js.flow +30 -0
  152. package/util/stableCopy.js.flow +35 -0
  153. package/lib/handlers/RelayDefaultMissingFieldHandlers.js +0 -26
  154. package/lib/store/RelayConnection.js +0 -36
  155. package/lib/store/RelayConnectionResolver.js +0 -177
  156. package/lib/store/RelayRecordSourceObjectImpl.js +0 -78
  157. package/lib/util/getFragmentSpecIdentifier.js +0 -26
@@ -7,15 +7,16 @@
7
7
  *
8
8
  * @format
9
9
  */
10
+ // flowlint ambiguous-object-type:error
10
11
  'use strict';
11
12
 
12
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
13
+ function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
13
14
 
14
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
15
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
15
16
 
16
- var DataChecker = require('./DataChecker');
17
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
17
18
 
18
- var RelayFeatureFlags = require('../util/RelayFeatureFlags');
19
+ var DataChecker = require('./DataChecker');
19
20
 
20
21
  var RelayModernRecord = require('./RelayModernRecord');
21
22
 
@@ -27,6 +28,8 @@ var RelayReader = require('./RelayReader');
27
28
 
28
29
  var RelayReferenceMarker = require('./RelayReferenceMarker');
29
30
 
31
+ var RelayStoreUtils = require('./RelayStoreUtils');
32
+
30
33
  var deepFreeze = require('../util/deepFreeze');
31
34
 
32
35
  var defaultGetDataID = require('./defaultGetDataID');
@@ -39,8 +42,9 @@ var recycleNodesInto = require('../util/recycleNodesInto');
39
42
 
40
43
  var resolveImmediate = require('../util/resolveImmediate');
41
44
 
42
- var _require = require('./RelayModernSelector'),
43
- createReaderSelector = _require.createReaderSelector;
45
+ var _require = require('./RelayStoreUtils'),
46
+ ROOT_ID = _require.ROOT_ID,
47
+ ROOT_TYPE = _require.ROOT_TYPE;
44
48
 
45
49
  var DEFAULT_RELEASE_BUFFER_SIZE = 0;
46
50
  /**
@@ -56,11 +60,9 @@ var DEFAULT_RELEASE_BUFFER_SIZE = 0;
56
60
  * is also enforced in development mode by freezing all records passed to a store.
57
61
  */
58
62
 
59
- var RelayModernStore =
60
- /*#__PURE__*/
61
- function () {
63
+ var RelayModernStore = /*#__PURE__*/function () {
62
64
  function RelayModernStore(source, options) {
63
- var _ref, _ref2, _ref3, _ref4;
65
+ var _options$gcReleaseBuf, _options$gcScheduler, _options$UNSTABLE_DO_, _options$operationLoa;
64
66
 
65
67
  // Prevent mutation of a record from outside the store.
66
68
  if (process.env.NODE_ENV !== "production") {
@@ -75,23 +77,26 @@ function () {
75
77
  }
76
78
  }
77
79
 
78
- this._connectionEvents = new Map();
79
- this._connectionSubscriptions = new Map();
80
+ this._currentWriteEpoch = 0;
80
81
  this._gcHoldCounter = 0;
81
- this._gcReleaseBufferSize = (_ref = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _ref !== void 0 ? _ref : DEFAULT_RELEASE_BUFFER_SIZE;
82
- this._gcScheduler = (_ref2 = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _ref2 !== void 0 ? _ref2 : resolveImmediate;
83
- this._getDataID = (_ref3 = options === null || options === void 0 ? void 0 : options.UNSTABLE_DO_NOT_USE_getDataID) !== null && _ref3 !== void 0 ? _ref3 : defaultGetDataID;
82
+ this._gcReleaseBufferSize = (_options$gcReleaseBuf = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _options$gcReleaseBuf !== void 0 ? _options$gcReleaseBuf : DEFAULT_RELEASE_BUFFER_SIZE;
83
+ this._gcScheduler = (_options$gcScheduler = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _options$gcScheduler !== void 0 ? _options$gcScheduler : resolveImmediate;
84
+ this._getDataID = (_options$UNSTABLE_DO_ = options === null || options === void 0 ? void 0 : options.UNSTABLE_DO_NOT_USE_getDataID) !== null && _options$UNSTABLE_DO_ !== void 0 ? _options$UNSTABLE_DO_ : defaultGetDataID;
85
+ this._globalInvalidationEpoch = null;
84
86
  this._hasScheduledGC = false;
85
87
  this._index = 0;
86
- this._operationLoader = (_ref4 = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _ref4 !== void 0 ? _ref4 : null;
88
+ this._invalidationSubscriptions = new Set();
89
+ this._invalidatedRecordIDs = new Set();
90
+ this._queryCacheExpirationTime = options === null || options === void 0 ? void 0 : options.queryCacheExpirationTime;
91
+ this._operationLoader = (_options$operationLoa = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _options$operationLoa !== void 0 ? _options$operationLoa : null;
87
92
  this._optimisticSource = null;
88
93
  this._recordSource = source;
89
94
  this._releaseBuffer = [];
90
95
  this._roots = new Map();
91
96
  this._shouldScheduleGC = false;
92
97
  this._subscriptions = new Set();
93
- this._updatedConnectionIDs = {};
94
98
  this._updatedRecordIDs = {};
99
+ initializeRecordSource(this._recordSource);
95
100
  }
96
101
 
97
102
  var _proto = RelayModernStore.prototype;
@@ -102,48 +107,110 @@ function () {
102
107
  return (_this$_optimisticSour = this._optimisticSource) !== null && _this$_optimisticSour !== void 0 ? _this$_optimisticSour : this._recordSource;
103
108
  };
104
109
 
105
- _proto.getConnectionEvents_UNSTABLE = function getConnectionEvents_UNSTABLE(connectionID) {
106
- var events = this._connectionEvents.get(connectionID);
107
-
108
- if (events != null) {
109
- var _events$optimistic;
110
+ _proto.check = function check(operation, options) {
111
+ var _this$_optimisticSour2, _options$target, _options$handlers;
110
112
 
111
- return (_events$optimistic = events.optimistic) !== null && _events$optimistic !== void 0 ? _events$optimistic : events["final"];
113
+ var selector = operation.root;
114
+ var source = (_this$_optimisticSour2 = this._optimisticSource) !== null && _this$_optimisticSour2 !== void 0 ? _this$_optimisticSour2 : this._recordSource;
115
+ var globalInvalidationEpoch = this._globalInvalidationEpoch;
116
+
117
+ var rootEntry = this._roots.get(operation.request.identifier);
118
+
119
+ var operationLastWrittenAt = rootEntry != null ? rootEntry.epoch : null; // Check if store has been globally invalidated
120
+
121
+ if (globalInvalidationEpoch != null) {
122
+ // If so, check if the operation we're checking was last written
123
+ // before or after invalidation occured.
124
+ if (operationLastWrittenAt == null || operationLastWrittenAt <= globalInvalidationEpoch) {
125
+ // If the operation was written /before/ global invalidation occurred,
126
+ // or if this operation has never been written to the store before,
127
+ // we will consider the data for this operation to be stale
128
+ // (i.e. not resolvable from the store).
129
+ return {
130
+ status: 'stale'
131
+ };
132
+ }
112
133
  }
134
+
135
+ var target = (_options$target = options === null || options === void 0 ? void 0 : options.target) !== null && _options$target !== void 0 ? _options$target : source;
136
+ var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : [];
137
+ var operationAvailability = DataChecker.check(source, target, selector, handlers, this._operationLoader, this._getDataID);
138
+ return getAvailabilityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime, this._queryCacheExpirationTime);
113
139
  };
114
140
 
115
- _proto.check = function check(selector) {
141
+ _proto.retain = function retain(operation) {
116
142
  var _this = this;
117
143
 
118
- var _this$_optimisticSour2;
144
+ var id = operation.request.identifier;
145
+ var disposed = false;
119
146
 
120
- var source = (_this$_optimisticSour2 = this._optimisticSource) !== null && _this$_optimisticSour2 !== void 0 ? _this$_optimisticSour2 : this._recordSource;
121
- return DataChecker.check(source, source, selector, [], this._operationLoader, this._getDataID, function (id) {
122
- return _this.getConnectionEvents_UNSTABLE(id);
123
- });
124
- };
147
+ var dispose = function dispose() {
148
+ // Ensure each retain can only dispose once
149
+ if (disposed) {
150
+ return;
151
+ }
125
152
 
126
- _proto.retain = function retain(selector) {
127
- var _this2 = this;
153
+ disposed = true; // For Flow: guard against the entry somehow not existing
128
154
 
129
- var index = this._index++;
155
+ var rootEntry = _this._roots.get(id);
130
156
 
131
- var dispose = function dispose() {
132
- // When disposing, move the selector onto the release buffer
133
- _this2._releaseBuffer.push(index); // Only when the release buffer is full do we actually
134
- // release the selector and run GC
157
+ if (rootEntry == null) {
158
+ return;
159
+ } // Decrement the ref count: if it becomes zero it is eligible
160
+ // for release.
161
+
162
+
163
+ rootEntry.refCount--;
164
+
165
+ if (rootEntry.refCount === 0) {
166
+ var _queryCacheExpirationTime = _this._queryCacheExpirationTime;
167
+
168
+ var rootEntryIsStale = rootEntry.fetchTime != null && _queryCacheExpirationTime != null && rootEntry.fetchTime <= Date.now() - _queryCacheExpirationTime;
135
169
 
170
+ if (rootEntryIsStale) {
171
+ _this._roots["delete"](id);
136
172
 
137
- if (_this2._releaseBuffer.length > _this2._gcReleaseBufferSize) {
138
- var idx = _this2._releaseBuffer.shift();
173
+ _this._scheduleGC();
174
+ } else {
175
+ _this._releaseBuffer.push(id); // If the release buffer is now over-full, remove the least-recently
176
+ // added entry and schedule a GC. Note that all items in the release
177
+ // buffer have a refCount of 0.
139
178
 
140
- _this2._roots["delete"](idx);
141
179
 
142
- _this2._scheduleGC();
180
+ if (_this._releaseBuffer.length > _this._gcReleaseBufferSize) {
181
+ var _id = _this._releaseBuffer.shift();
182
+
183
+ _this._roots["delete"](_id);
184
+
185
+ _this._scheduleGC();
186
+ }
187
+ }
143
188
  }
144
189
  };
145
190
 
146
- this._roots.set(index, selector);
191
+ var rootEntry = this._roots.get(id);
192
+
193
+ if (rootEntry != null) {
194
+ if (rootEntry.refCount === 0) {
195
+ // This entry should be in the release buffer, but it no longer belongs
196
+ // there since it's retained. Remove it to maintain the invariant that
197
+ // all release buffer entries have a refCount of 0.
198
+ this._releaseBuffer = this._releaseBuffer.filter(function (_id) {
199
+ return _id !== id;
200
+ });
201
+ } // If we've previously retained this operation, increment the refCount
202
+
203
+
204
+ rootEntry.refCount += 1;
205
+ } else {
206
+ // Otherwise create a new entry for the operation
207
+ this._roots.set(id, {
208
+ operation: operation,
209
+ refCount: 1,
210
+ epoch: null,
211
+ fetchTime: null
212
+ });
213
+ }
147
214
 
148
215
  return {
149
216
  dispose: dispose
@@ -159,61 +226,86 @@ function () {
159
226
  }
160
227
 
161
228
  return snapshot;
162
- } // This method will return a list of updated owners form the subscriptions
229
+ } // This method will return a list of updated owners from the subscriptions
163
230
  ;
164
231
 
165
- _proto.notify = function notify() {
166
- var _this3 = this;
232
+ _proto.notify = function notify(sourceOperation, invalidateStore) {
233
+ var _this2 = this;
234
+
235
+ // Increment the current write when notifying after executing
236
+ // a set of changes to the store.
237
+ this._currentWriteEpoch++;
238
+
239
+ if (invalidateStore === true) {
240
+ this._globalInvalidationEpoch = this._currentWriteEpoch;
241
+ }
167
242
 
168
243
  var source = this.getSource();
169
244
  var updatedOwners = [];
170
245
 
171
246
  this._subscriptions.forEach(function (subscription) {
172
- var owner = _this3._updateSubscription(source, subscription);
247
+ var owner = _this2._updateSubscription(source, subscription);
173
248
 
174
249
  if (owner != null) {
175
250
  updatedOwners.push(owner);
176
251
  }
177
252
  });
178
253
 
179
- this._connectionSubscriptions.forEach(function (subscription, id) {
180
- if (subscription.stale) {
181
- subscription.stale = false;
182
- subscription.callback(subscription.snapshot);
183
- }
254
+ this._invalidationSubscriptions.forEach(function (subscription) {
255
+ _this2._updateInvalidationSubscription(subscription, invalidateStore === true);
184
256
  });
185
257
 
186
- this._updatedConnectionIDs = {};
187
258
  this._updatedRecordIDs = {};
188
- return updatedOwners;
189
- };
190
259
 
191
- _proto.publish = function publish(source) {
192
- var _this4 = this;
193
-
194
- var _this$_optimisticSour3;
195
-
196
- var target = (_this$_optimisticSour3 = this._optimisticSource) !== null && _this$_optimisticSour3 !== void 0 ? _this$_optimisticSour3 : this._recordSource;
197
- updateTargetFromSource(target, source, this._updatedRecordIDs);
260
+ this._invalidatedRecordIDs.clear(); // If a source operation was provided (indicating the operation
261
+ // that produced this update to the store), record the current epoch
262
+ // at which this operation was written.
263
+
264
+
265
+ if (sourceOperation != null) {
266
+ // We only track the epoch at which the operation was written if
267
+ // it was previously retained, to keep the size of our operation
268
+ // epoch map bounded. If a query wasn't retained, we assume it can
269
+ // may be deleted at any moment and thus is not relevant for us to track
270
+ // for the purposes of invalidation.
271
+ var id = sourceOperation.request.identifier;
272
+
273
+ var rootEntry = this._roots.get(id);
274
+
275
+ if (rootEntry != null) {
276
+ rootEntry.epoch = this._currentWriteEpoch;
277
+ rootEntry.fetchTime = Date.now();
278
+ } else if (sourceOperation.request.node.params.operationKind === 'query' && this._gcReleaseBufferSize > 0 && this._releaseBuffer.length < this._gcReleaseBufferSize) {
279
+ // The operation isn't retained but there is space in the release buffer:
280
+ // temporarily track this operation in case the data can be reused soon.
281
+ var temporaryRootEntry = {
282
+ operation: sourceOperation,
283
+ refCount: 0,
284
+ epoch: this._currentWriteEpoch,
285
+ fetchTime: Date.now()
286
+ };
198
287
 
199
- this._connectionSubscriptions.forEach(function (subscription, id) {
200
- var hasStoreUpdates = hasOverlappingIDs(subscription.snapshot.seenRecords, _this4._updatedRecordIDs);
288
+ this._releaseBuffer.push(id);
201
289
 
202
- if (!hasStoreUpdates) {
203
- return;
290
+ this._roots.set(id, temporaryRootEntry);
204
291
  }
292
+ }
205
293
 
206
- var nextSnapshot = _this4._updateConnection_UNSTABLE(subscription.resolver, subscription.snapshot, source, null);
294
+ return updatedOwners;
295
+ };
207
296
 
208
- if (nextSnapshot) {
209
- subscription.snapshot = nextSnapshot;
210
- subscription.stale = true;
211
- }
212
- });
297
+ _proto.publish = function publish(source, idsMarkedForInvalidation) {
298
+ var _this$_optimisticSour3;
299
+
300
+ var target = (_this$_optimisticSour3 = this._optimisticSource) !== null && _this$_optimisticSour3 !== void 0 ? _this$_optimisticSour3 : this._recordSource;
301
+ updateTargetFromSource(target, source, // We increment the current epoch at the end of the set of updates,
302
+ // in notify(). Here, we pass what will be the incremented value of
303
+ // the epoch to use to write to invalidated records.
304
+ this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs);
213
305
  };
214
306
 
215
307
  _proto.subscribe = function subscribe(snapshot, callback) {
216
- var _this5 = this;
308
+ var _this3 = this;
217
309
 
218
310
  var subscription = {
219
311
  backup: null,
@@ -223,7 +315,7 @@ function () {
223
315
  };
224
316
 
225
317
  var dispose = function dispose() {
226
- _this5._subscriptions["delete"](subscription);
318
+ _this3._subscriptions["delete"](subscription);
227
319
  };
228
320
 
229
321
  this._subscriptions.add(subscription);
@@ -234,18 +326,18 @@ function () {
234
326
  };
235
327
 
236
328
  _proto.holdGC = function holdGC() {
237
- var _this6 = this;
329
+ var _this4 = this;
238
330
 
239
331
  this._gcHoldCounter++;
240
332
 
241
333
  var dispose = function dispose() {
242
- if (_this6._gcHoldCounter > 0) {
243
- _this6._gcHoldCounter--;
334
+ if (_this4._gcHoldCounter > 0) {
335
+ _this4._gcHoldCounter--;
244
336
 
245
- if (_this6._gcHoldCounter === 0 && _this6._shouldScheduleGC) {
246
- _this6._scheduleGC();
337
+ if (_this4._gcHoldCounter === 0 && _this4._shouldScheduleGC) {
338
+ _this4._scheduleGC();
247
339
 
248
- _this6._shouldScheduleGC = false;
340
+ _this4._shouldScheduleGC = false;
249
341
  }
250
342
  }
251
343
  };
@@ -299,271 +391,128 @@ function () {
299
391
  }
300
392
  };
301
393
 
302
- _proto.lookupConnection_UNSTABLE = function lookupConnection_UNSTABLE(connectionReference, resolver) {
303
- var _connectionEvents$opt;
394
+ _proto.lookupInvalidationState = function lookupInvalidationState(dataIDs) {
395
+ var _this5 = this;
304
396
 
305
- !RelayFeatureFlags.ENABLE_CONNECTION_RESOLVERS ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Connection resolvers are not yet supported.') : invariant(false) : void 0;
306
- var id = connectionReference.id;
307
- var initialState = resolver.initialize();
397
+ var invalidations = new Map();
398
+ dataIDs.forEach(function (dataID) {
399
+ var _RelayModernRecord$ge;
308
400
 
309
- var connectionEvents = this._connectionEvents.get(id);
401
+ var record = _this5.getSource().get(dataID);
310
402
 
311
- var events = connectionEvents != null ? (_connectionEvents$opt = connectionEvents.optimistic) !== null && _connectionEvents$opt !== void 0 ? _connectionEvents$opt : connectionEvents["final"] : null;
312
- var initialSnapshot = {
313
- edgeSnapshots: {},
314
- id: id,
315
- reference: connectionReference,
316
- seenRecords: {},
317
- state: initialState
403
+ invalidations.set(dataID, (_RelayModernRecord$ge = RelayModernRecord.getInvalidationEpoch(record)) !== null && _RelayModernRecord$ge !== void 0 ? _RelayModernRecord$ge : null);
404
+ });
405
+ invalidations.set('global', this._globalInvalidationEpoch);
406
+ return {
407
+ dataIDs: dataIDs,
408
+ invalidations: invalidations
318
409
  };
410
+ };
411
+
412
+ _proto.checkInvalidationState = function checkInvalidationState(prevInvalidationState) {
413
+ var latestInvalidationState = this.lookupInvalidationState(prevInvalidationState.dataIDs);
414
+ var currentInvalidations = latestInvalidationState.invalidations;
415
+ var prevInvalidations = prevInvalidationState.invalidations; // Check if global invalidation has changed
416
+
417
+ if (currentInvalidations.get('global') !== prevInvalidations.get('global')) {
418
+ return true;
419
+ } // Check if the invalidation state for any of the ids has changed.
420
+
421
+
422
+ var _iterator = _createForOfIteratorHelper(prevInvalidationState.dataIDs),
423
+ _step;
319
424
 
320
- if (events == null || events.length === 0) {
321
- return initialSnapshot;
425
+ try {
426
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
427
+ var dataID = _step.value;
428
+
429
+ if (currentInvalidations.get(dataID) !== prevInvalidations.get(dataID)) {
430
+ return true;
431
+ }
432
+ }
433
+ } catch (err) {
434
+ _iterator.e(err);
435
+ } finally {
436
+ _iterator.f();
322
437
  }
323
438
 
324
- return this._reduceConnection_UNSTABLE(resolver, connectionReference, initialSnapshot, events);
439
+ return false;
325
440
  };
326
441
 
327
- _proto.subscribeConnection_UNSTABLE = function subscribeConnection_UNSTABLE(snapshot, resolver, callback) {
328
- var _this7 = this;
442
+ _proto.subscribeToInvalidationState = function subscribeToInvalidationState(invalidationState, callback) {
443
+ var _this6 = this;
329
444
 
330
- !RelayFeatureFlags.ENABLE_CONNECTION_RESOLVERS ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Connection resolvers are not yet supported.') : invariant(false) : void 0;
331
- var id = String(this._index++);
332
445
  var subscription = {
333
- backup: null,
334
446
  callback: callback,
335
- id: id,
336
- resolver: resolver,
337
- snapshot: snapshot,
338
- stale: false
447
+ invalidationState: invalidationState
339
448
  };
340
449
 
341
450
  var dispose = function dispose() {
342
- _this7._connectionSubscriptions["delete"](id);
451
+ _this6._invalidationSubscriptions["delete"](subscription);
343
452
  };
344
453
 
345
- this._connectionSubscriptions.set(id, subscription);
454
+ this._invalidationSubscriptions.add(subscription);
346
455
 
347
456
  return {
348
457
  dispose: dispose
349
458
  };
350
459
  };
351
460
 
352
- _proto.publishConnectionEvents_UNSTABLE = function publishConnectionEvents_UNSTABLE(events, final) {
353
- var _this8 = this;
354
-
355
- if (events.length === 0) {
356
- return;
357
- }
358
-
359
- !RelayFeatureFlags.ENABLE_CONNECTION_RESOLVERS ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Connection resolvers are not yet supported.') : invariant(false) : void 0;
360
- var pendingConnectionEvents = new Map();
361
- events.forEach(function (event) {
362
- var connectionID = event.connectionID;
363
- var pendingEvents = pendingConnectionEvents.get(connectionID);
364
-
365
- if (pendingEvents == null) {
366
- pendingEvents = [];
367
- pendingConnectionEvents.set(connectionID, pendingEvents);
368
- }
369
-
370
- pendingEvents.push(event);
371
-
372
- var connectionEvents = _this8._connectionEvents.get(connectionID);
373
-
374
- if (connectionEvents == null) {
375
- connectionEvents = {
376
- "final": [],
377
- optimistic: null
378
- };
379
-
380
- _this8._connectionEvents.set(connectionID, connectionEvents);
381
- }
382
-
383
- if (final) {
384
- connectionEvents["final"].push(event);
385
- } else {
386
- var optimisticEvents = connectionEvents.optimistic;
387
-
388
- if (optimisticEvents == null) {
389
- optimisticEvents = connectionEvents["final"].slice();
390
- connectionEvents.optimistic = optimisticEvents;
391
- }
392
-
393
- optimisticEvents.push(event);
394
- }
395
- });
396
-
397
- this._connectionSubscriptions.forEach(function (subscription, id) {
398
- var pendingEvents = pendingConnectionEvents.get(subscription.snapshot.reference.id);
399
-
400
- if (pendingEvents == null) {
401
- return;
402
- }
403
-
404
- var nextSnapshot = _this8._updateConnection_UNSTABLE(subscription.resolver, subscription.snapshot, null, pendingEvents);
461
+ _proto._updateInvalidationSubscription = function _updateInvalidationSubscription(subscription, invalidatedStore) {
462
+ var _this7 = this;
405
463
 
406
- if (nextSnapshot) {
407
- subscription.snapshot = nextSnapshot;
408
- subscription.stale = true;
409
- }
464
+ var callback = subscription.callback,
465
+ invalidationState = subscription.invalidationState;
466
+ var dataIDs = invalidationState.dataIDs;
467
+ var isSubscribedToInvalidatedIDs = invalidatedStore || dataIDs.some(function (dataID) {
468
+ return _this7._invalidatedRecordIDs.has(dataID);
410
469
  });
411
- };
412
-
413
- _proto._updateConnection_UNSTABLE = function _updateConnection_UNSTABLE(resolver, snapshot, source, pendingEvents) {
414
- var _pendingEvents;
415
-
416
- var nextSnapshot = this._reduceConnection_UNSTABLE(resolver, snapshot.reference, snapshot, (_pendingEvents = pendingEvents) !== null && _pendingEvents !== void 0 ? _pendingEvents : [], source);
417
470
 
418
- var state = recycleNodesInto(snapshot.state, nextSnapshot.state);
419
-
420
- if (process.env.NODE_ENV !== "production") {
421
- deepFreeze(nextSnapshot);
471
+ if (!isSubscribedToInvalidatedIDs) {
472
+ return;
422
473
  }
423
474
 
424
- if (state !== snapshot.state) {
425
- return (0, _objectSpread2["default"])({}, nextSnapshot, {
426
- state: state
427
- });
428
- }
475
+ callback();
429
476
  };
430
477
 
431
- _proto._reduceConnection_UNSTABLE = function _reduceConnection_UNSTABLE(resolver, connectionReference, snapshot, events) {
432
- var _this9 = this;
433
-
434
- var _edgesField$concreteT;
435
-
436
- var source = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
437
- var edgesField = connectionReference.edgesField,
438
- id = connectionReference.id,
439
- variables = connectionReference.variables;
440
- var fragment = {
441
- kind: 'Fragment',
442
- name: edgesField.name,
443
- type: (_edgesField$concreteT = edgesField.concreteType) !== null && _edgesField$concreteT !== void 0 ? _edgesField$concreteT : '__Any',
444
- metadata: null,
445
- argumentDefinitions: [],
446
- selections: edgesField.selections
447
- };
448
- var seenRecords = {};
449
- var edgeSnapshots = (0, _objectSpread2["default"])({}, snapshot.edgeSnapshots);
450
- var initialState = snapshot.state;
451
-
452
- if (source) {
453
- var edgeData = {};
454
- Object.keys(edgeSnapshots).forEach(function (edgeID) {
455
- var prevSnapshot = edgeSnapshots[edgeID];
456
- var nextSnapshot = RelayReader.read(_this9.getSource(), createReaderSelector(fragment, edgeID, variables, prevSnapshot.selector.owner));
457
- var data = recycleNodesInto(prevSnapshot.data, nextSnapshot.data);
458
- nextSnapshot = {
459
- data: data,
460
- isMissingData: nextSnapshot.isMissingData,
461
- seenRecords: nextSnapshot.seenRecords,
462
- selector: nextSnapshot.selector
463
- };
464
-
465
- if (data !== prevSnapshot.data) {
466
- edgeData[edgeID] = data;
467
- edgeSnapshots[edgeID] = nextSnapshot;
468
- }
469
- });
470
-
471
- if (Object.keys(edgeData).length !== 0) {
472
- initialState = resolver.reduce(initialState, {
473
- kind: 'update',
474
- edgeData: edgeData
475
- });
476
- }
477
- }
478
+ _proto.snapshot = function snapshot() {
479
+ var _this8 = this;
478
480
 
479
- var state = events.reduce(function (prevState, event) {
480
- if (event.kind === 'fetch') {
481
- var edges = [];
482
- event.edgeIDs.forEach(function (edgeID) {
483
- if (edgeID == null) {
484
- edges.push(edgeID);
485
- return;
486
- }
481
+ !(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
487
482
 
488
- var edgeSnapshot = RelayReader.read(_this9.getSource(), createReaderSelector(fragment, edgeID, variables, event.request));
489
- Object.assign(seenRecords, edgeSnapshot.seenRecords);
490
- var itemData = edgeSnapshot.data;
491
- edgeSnapshots[edgeID] = edgeSnapshot;
492
- edges.push(itemData);
493
- });
494
- return resolver.reduce(prevState, {
495
- kind: 'fetch',
496
- args: event.args,
497
- edges: edges,
498
- pageInfo: event.pageInfo,
499
- stream: event.stream
500
- });
501
- } else if (event.kind === 'insert') {
502
- var edgeSnapshot = RelayReader.read(_this9.getSource(), createReaderSelector(fragment, event.edgeID, variables, event.request));
503
- Object.assign(seenRecords, edgeSnapshot.seenRecords);
504
- var itemData = edgeSnapshot.data;
505
- edgeSnapshots[event.edgeID] = edgeSnapshot;
506
- return resolver.reduce(prevState, {
507
- args: event.args,
508
- edge: itemData,
509
- kind: 'insert'
510
- });
511
- } else if (event.kind === 'stream.edge') {
512
- var _edgeSnapshot = RelayReader.read(_this9.getSource(), createReaderSelector(fragment, event.edgeID, variables, event.request));
513
-
514
- Object.assign(seenRecords, _edgeSnapshot.seenRecords);
515
- var _itemData = _edgeSnapshot.data;
516
- edgeSnapshots[event.edgeID] = _edgeSnapshot;
517
- return resolver.reduce(prevState, {
518
- args: event.args,
519
- edge: _itemData,
520
- index: event.index,
521
- kind: 'stream.edge'
522
- });
523
- } else if (event.kind === 'stream.pageInfo') {
524
- return resolver.reduce(prevState, {
525
- args: event.args,
526
- kind: 'stream.pageInfo',
527
- pageInfo: event.pageInfo
528
- });
529
- } else {
530
- event.kind;
531
- !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected connection event kind `%s`.', event.kind) : invariant(false) : void 0;
483
+ this._subscriptions.forEach(function (subscription) {
484
+ // Backup occurs after writing a new "final" payload(s) and before (re)applying
485
+ // optimistic changes. Each subscription's `snapshot` represents what was *last
486
+ // published to the subscriber*, which notably may include previous optimistic
487
+ // updates. Therefore a subscription can be in any of the following states:
488
+ // - stale=true: This subscription was restored to a different value than
489
+ // `snapshot`. That means this subscription has changes relative to its base,
490
+ // but its base has changed (we just applied a final payload): recompute
491
+ // a backup so that we can later restore to the state the subscription
492
+ // should be in.
493
+ // - stale=false: This subscription was restored to the same value than
494
+ // `snapshot`. That means this subscription does *not* have changes relative
495
+ // to its base, so the current `snapshot` is valid to use as a backup.
496
+ if (!subscription.stale) {
497
+ subscription.backup = subscription.snapshot;
498
+ return;
532
499
  }
533
- }, initialState);
534
- return {
535
- edgeSnapshots: edgeSnapshots,
536
- id: id,
537
- reference: connectionReference,
538
- seenRecords: seenRecords,
539
- state: state
540
- };
541
- };
542
500
 
543
- _proto.snapshot = function snapshot() {
544
- !(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
501
+ var snapshot = subscription.snapshot;
502
+ var backup = RelayReader.read(_this8.getSource(), snapshot.selector);
503
+ var nextData = recycleNodesInto(snapshot.data, backup.data);
504
+ backup.data = nextData; // backup owns the snapshot and can safely mutate
545
505
 
546
- this._connectionSubscriptions.forEach(function (subscription) {
547
- subscription.backup = subscription.snapshot;
548
- });
549
-
550
- this._subscriptions.forEach(function (subscription) {
551
- subscription.backup = subscription.snapshot;
506
+ subscription.backup = backup;
552
507
  });
553
508
 
554
509
  this._optimisticSource = RelayOptimisticRecordSource.create(this.getSource());
555
510
  };
556
511
 
557
512
  _proto.restore = function restore() {
558
- var _this10 = this;
559
-
560
513
  !(this._optimisticSource != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected call to restore(), expected a snapshot ' + 'to exist (make sure to call snapshot()).') : invariant(false) : void 0;
561
514
  this._optimisticSource = null;
562
515
 
563
- this._connectionEvents.forEach(function (events) {
564
- events.optimistic = null;
565
- });
566
-
567
516
  this._subscriptions.forEach(function (subscription) {
568
517
  var backup = subscription.backup;
569
518
  subscription.backup = null;
@@ -583,38 +532,10 @@ function () {
583
532
  subscription.stale = true;
584
533
  }
585
534
  });
586
-
587
- this._connectionSubscriptions.forEach(function (subscription) {
588
- var backup = subscription.backup;
589
- subscription.backup = null;
590
-
591
- if (backup) {
592
- if (backup.state !== subscription.snapshot.state) {
593
- subscription.stale = true;
594
- }
595
-
596
- subscription.snapshot = backup;
597
- } else {
598
- // This subscription was established after the creation of the
599
- // connection snapshot so there's nothing to restore to. Recreate the
600
- // connection from scratch and check ifs value changes.
601
- var baseSnapshot = _this10.lookupConnection_UNSTABLE(subscription.snapshot.reference, subscription.resolver);
602
-
603
- var nextState = recycleNodesInto(subscription.snapshot.state, baseSnapshot.state);
604
-
605
- if (nextState !== subscription.snapshot.state) {
606
- subscription.stale = true;
607
- }
608
-
609
- subscription.snapshot = (0, _objectSpread2["default"])({}, baseSnapshot, {
610
- state: nextState
611
- });
612
- }
613
- });
614
535
  };
615
536
 
616
537
  _proto._scheduleGC = function _scheduleGC() {
617
- var _this11 = this;
538
+ var _this9 = this;
618
539
 
619
540
  if (this._gcHoldCounter > 0) {
620
541
  this._shouldScheduleGC = true;
@@ -628,27 +549,26 @@ function () {
628
549
  this._hasScheduledGC = true;
629
550
 
630
551
  this._gcScheduler(function () {
631
- _this11.__gc();
552
+ _this9.__gc();
632
553
 
633
- _this11._hasScheduledGC = false;
554
+ _this9._hasScheduledGC = false;
634
555
  });
635
556
  };
636
557
 
637
558
  _proto.__gc = function __gc() {
638
- var _this12 = this;
559
+ var _this10 = this;
639
560
 
640
561
  // Don't run GC while there are optimistic updates applied
641
562
  if (this._optimisticSource != null) {
642
563
  return;
643
564
  }
644
565
 
645
- var references = new Set();
646
- var connectionReferences = new Set(); // Mark all records that are traversable from a root
566
+ var references = new Set(); // Mark all records that are traversable from a root
647
567
 
648
- this._roots.forEach(function (selector) {
649
- RelayReferenceMarker.mark(_this12._recordSource, selector, references, connectionReferences, function (id) {
650
- return _this12.getConnectionEvents_UNSTABLE(id);
651
- }, _this12._operationLoader);
568
+ this._roots.forEach(function (_ref) {
569
+ var operation = _ref.operation;
570
+ var selector = operation.root;
571
+ RelayReferenceMarker.mark(_this10._recordSource, selector, references, _this10._operationLoader);
652
572
  });
653
573
 
654
574
  if (references.size === 0) {
@@ -666,49 +586,68 @@ function () {
666
586
  }
667
587
  }
668
588
  }
669
-
670
- if (connectionReferences.size === 0) {
671
- this._connectionEvents.clear();
672
- } else {
673
- // Evict any unreferenced connections
674
- var _iteratorNormalCompletion = true;
675
- var _didIteratorError = false;
676
- var _iteratorError = undefined;
677
-
678
- try {
679
- for (var _iterator = this._connectionEvents.keys()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
680
- var connectionID = _step.value;
681
-
682
- if (!connectionReferences.has(connectionID)) {
683
- this._connectionEvents["delete"](connectionID);
684
- }
685
- }
686
- } catch (err) {
687
- _didIteratorError = true;
688
- _iteratorError = err;
689
- } finally {
690
- try {
691
- if (!_iteratorNormalCompletion && _iterator["return"] != null) {
692
- _iterator["return"]();
693
- }
694
- } finally {
695
- if (_didIteratorError) {
696
- throw _iteratorError;
697
- }
698
- }
699
- }
700
- }
701
589
  };
702
590
 
703
591
  return RelayModernStore;
704
592
  }();
593
+
594
+ function initializeRecordSource(target) {
595
+ if (!target.has(ROOT_ID)) {
596
+ var rootRecord = RelayModernRecord.create(ROOT_ID, ROOT_TYPE);
597
+ target.set(ROOT_ID, rootRecord);
598
+ }
599
+ }
705
600
  /**
706
601
  * Updates the target with information from source, also updating a mapping of
707
602
  * which records in the target were changed as a result.
603
+ * Additionally, will marc records as invalidated at the current write epoch
604
+ * given the set of record ids marked as stale in this update.
708
605
  */
709
606
 
710
607
 
711
- function updateTargetFromSource(target, source, updatedRecordIDs) {
608
+ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForInvalidation, updatedRecordIDs, invalidatedRecordIDs) {
609
+ // First, update any records that were marked for invalidation.
610
+ // For each provided dataID that was invalidated, we write the
611
+ // INVALIDATED_AT_KEY on the record, indicating
612
+ // the epoch at which the record was invalidated.
613
+ if (idsMarkedForInvalidation) {
614
+ idsMarkedForInvalidation.forEach(function (dataID) {
615
+ var targetRecord = target.get(dataID);
616
+ var sourceRecord = source.get(dataID); // If record was deleted during the update (and also invalidated),
617
+ // we don't need to count it as an invalidated id
618
+
619
+ if (sourceRecord === null) {
620
+ return;
621
+ }
622
+
623
+ var nextRecord;
624
+
625
+ if (targetRecord != null) {
626
+ // If the target record exists, use it to set the epoch
627
+ // at which it was invalidated. This record will be updated with
628
+ // any changes from source in the section below
629
+ // where we update the target records based on the source.
630
+ nextRecord = RelayModernRecord.clone(targetRecord);
631
+ } else {
632
+ // If the target record doesn't exist, it means that a new record
633
+ // in the source was created (and also invalidated), so we use that
634
+ // record to set the epoch at which it was invalidated. This record
635
+ // will be updated with any changes from source in the section below
636
+ // where we update the target records based on the source.
637
+ nextRecord = sourceRecord != null ? RelayModernRecord.clone(sourceRecord) : null;
638
+ }
639
+
640
+ if (!nextRecord) {
641
+ return;
642
+ }
643
+
644
+ RelayModernRecord.setValue(nextRecord, RelayStoreUtils.INVALIDATED_AT_KEY, currentWriteEpoch);
645
+ invalidatedRecordIDs.add(dataID);
646
+ target.set(dataID, nextRecord);
647
+ });
648
+ } // Update the target based on the changes present in source
649
+
650
+
712
651
  var dataIDs = source.getRecordIDs();
713
652
 
714
653
  for (var ii = 0; ii < dataIDs.length; ii++) {
@@ -747,6 +686,55 @@ function updateTargetFromSource(target, source, updatedRecordIDs) {
747
686
 
748
687
  }
749
688
  }
689
+ /**
690
+ * Returns an OperationAvailability given the Availability returned
691
+ * by checking an operation, and when that operation was last written to the store.
692
+ * Specifically, the provided Availability of an operation will contain the
693
+ * value of when a record referenced by the operation was most recently
694
+ * invalidated; given that value, and given when this operation was last
695
+ * written to the store, this function will return the overall
696
+ * OperationAvailability for the operation.
697
+ */
698
+
699
+
700
+ function getAvailabilityStatus(operationAvailability, operationLastWrittenAt, operationFetchTime, queryCacheExpirationTime) {
701
+ var mostRecentlyInvalidatedAt = operationAvailability.mostRecentlyInvalidatedAt,
702
+ status = operationAvailability.status;
703
+
704
+ if (typeof mostRecentlyInvalidatedAt === 'number') {
705
+ // If some record referenced by this operation is stale, then the operation itself is stale
706
+ // if either the operation itself was never written *or* the operation was last written
707
+ // before the most recent invalidation of its reachable records.
708
+ if (operationLastWrittenAt == null || mostRecentlyInvalidatedAt > operationLastWrittenAt) {
709
+ return {
710
+ status: 'stale'
711
+ };
712
+ }
713
+ }
714
+
715
+ if (status === 'missing') {
716
+ return {
717
+ status: 'missing'
718
+ };
719
+ }
720
+
721
+ if (operationFetchTime != null && queryCacheExpirationTime != null) {
722
+ var isStale = operationFetchTime <= Date.now() - queryCacheExpirationTime;
723
+
724
+ if (isStale) {
725
+ return {
726
+ status: 'stale'
727
+ };
728
+ }
729
+ } // There were no invalidations of any reachable records *or* the operation is known to have
730
+ // been fetched after the most recent record invalidation.
731
+
732
+
733
+ return {
734
+ status: 'available',
735
+ fetchTime: operationFetchTime !== null && operationFetchTime !== void 0 ? operationFetchTime : null
736
+ };
737
+ }
750
738
 
751
739
  RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
752
740
  lookup: 'RelayModernStore.prototype.lookup',