relay-runtime 8.0.0 → 10.0.1

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 (135) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +41 -0
  2. package/handlers/connection/ConnectionHandler.js.flow +549 -0
  3. package/handlers/connection/ConnectionInterface.js.flow +92 -0
  4. package/handlers/connection/MutationHandlers.js.flow +88 -0
  5. package/index.js +1 -1
  6. package/index.js.flow +320 -0
  7. package/lib/handlers/RelayDefaultHandlerProvider.js +13 -2
  8. package/lib/handlers/connection/{RelayConnectionHandler.js → ConnectionHandler.js} +33 -35
  9. package/lib/handlers/connection/{RelayConnectionInterface.js → ConnectionInterface.js} +2 -2
  10. package/lib/handlers/connection/MutationHandlers.js +86 -0
  11. package/lib/index.js +15 -19
  12. package/lib/mutations/RelayDeclarativeMutationConfig.js +29 -52
  13. package/lib/mutations/RelayRecordProxy.js +1 -3
  14. package/lib/mutations/RelayRecordSourceMutator.js +2 -9
  15. package/lib/mutations/RelayRecordSourceProxy.js +2 -4
  16. package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -13
  17. package/lib/mutations/commitMutation.js +13 -3
  18. package/lib/mutations/validateMutation.js +16 -9
  19. package/lib/network/RelayObservable.js +9 -9
  20. package/lib/network/RelayQueryResponseCache.js +8 -6
  21. package/lib/query/PreloadableQueryRegistry.js +70 -0
  22. package/lib/query/fetchQueryInternal.js +31 -23
  23. package/lib/store/DataChecker.js +122 -110
  24. package/lib/store/RelayConcreteVariables.js +6 -2
  25. package/lib/store/RelayModernEnvironment.js +121 -67
  26. package/lib/store/RelayModernFragmentSpecResolver.js +12 -16
  27. package/lib/store/RelayModernQueryExecutor.js +389 -314
  28. package/lib/store/RelayModernRecord.js +14 -9
  29. package/lib/store/RelayModernSelector.js +7 -3
  30. package/lib/store/RelayModernStore.js +289 -484
  31. package/lib/store/RelayOperationTracker.js +35 -78
  32. package/lib/store/RelayOptimisticRecordSource.js +7 -5
  33. package/lib/store/RelayPublishQueue.js +6 -33
  34. package/lib/store/RelayReader.js +113 -45
  35. package/lib/store/RelayRecordSource.js +2 -9
  36. package/lib/store/RelayRecordSourceMapImpl.js +13 -18
  37. package/lib/store/RelayReferenceMarker.js +40 -60
  38. package/lib/store/RelayResponseNormalizer.js +158 -193
  39. package/lib/store/RelayStoreUtils.js +1 -0
  40. package/lib/store/StoreInspector.js +8 -8
  41. package/lib/store/TypeID.js +28 -0
  42. package/lib/store/cloneRelayScalarHandleSourceField.js +44 -0
  43. package/lib/store/normalizeRelayPayload.js +6 -2
  44. package/lib/store/readInlineData.js +1 -1
  45. package/lib/subscription/requestSubscription.js +5 -3
  46. package/lib/util/RelayConcreteNode.js +9 -6
  47. package/lib/util/RelayError.js +39 -9
  48. package/lib/util/RelayFeatureFlags.js +2 -5
  49. package/lib/util/RelayReplaySubject.js +3 -3
  50. package/lib/util/createPayloadFor3DField.js +7 -2
  51. package/lib/util/getRequestIdentifier.js +2 -2
  52. package/lib/util/recycleNodesInto.js +2 -6
  53. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  54. package/mutations/RelayRecordProxy.js.flow +165 -0
  55. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  56. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  57. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  58. package/mutations/applyOptimisticMutation.js.flow +76 -0
  59. package/mutations/commitLocalUpdate.js.flow +24 -0
  60. package/mutations/commitMutation.js.flow +182 -0
  61. package/mutations/validateMutation.js.flow +213 -0
  62. package/network/ConvertToExecuteFunction.js.flow +49 -0
  63. package/network/RelayNetwork.js.flow +84 -0
  64. package/network/RelayNetworkTypes.js.flow +123 -0
  65. package/network/RelayObservable.js.flow +634 -0
  66. package/network/RelayQueryResponseCache.js.flow +111 -0
  67. package/package.json +1 -1
  68. package/query/GraphQLTag.js.flow +166 -0
  69. package/query/PreloadableQueryRegistry.js.flow +65 -0
  70. package/query/fetchQuery.js.flow +47 -0
  71. package/query/fetchQueryInternal.js.flow +348 -0
  72. package/relay-runtime.js +2 -2
  73. package/relay-runtime.min.js +2 -2
  74. package/store/ClientID.js.flow +43 -0
  75. package/store/DataChecker.js.flow +502 -0
  76. package/store/RelayConcreteVariables.js.flow +96 -0
  77. package/store/RelayModernEnvironment.js.flow +551 -0
  78. package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
  79. package/store/RelayModernOperationDescriptor.js.flow +88 -0
  80. package/store/RelayModernQueryExecutor.js.flow +1321 -0
  81. package/store/RelayModernRecord.js.flow +403 -0
  82. package/store/RelayModernSelector.js.flow +455 -0
  83. package/store/RelayModernStore.js.flow +842 -0
  84. package/store/RelayOperationTracker.js.flow +164 -0
  85. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  86. package/store/RelayPublishQueue.js.flow +401 -0
  87. package/store/RelayReader.js.flow +473 -0
  88. package/store/RelayRecordSource.js.flow +29 -0
  89. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  90. package/store/RelayRecordState.js.flow +37 -0
  91. package/store/RelayReferenceMarker.js.flow +257 -0
  92. package/store/RelayResponseNormalizer.js.flow +680 -0
  93. package/store/RelayStoreTypes.js.flow +899 -0
  94. package/store/RelayStoreUtils.js.flow +219 -0
  95. package/store/StoreInspector.js.flow +171 -0
  96. package/store/TypeID.js.flow +28 -0
  97. package/store/ViewerPattern.js.flow +26 -0
  98. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  99. package/store/cloneRelayScalarHandleSourceField.js.flow +62 -0
  100. package/store/createFragmentSpecResolver.js.flow +55 -0
  101. package/store/createRelayContext.js.flow +44 -0
  102. package/store/defaultGetDataID.js.flow +27 -0
  103. package/store/hasOverlappingIDs.js.flow +34 -0
  104. package/store/isRelayModernEnvironment.js.flow +27 -0
  105. package/store/normalizeRelayPayload.js.flow +51 -0
  106. package/store/readInlineData.js.flow +75 -0
  107. package/subscription/requestSubscription.js.flow +100 -0
  108. package/util/JSResourceTypes.flow.js.flow +20 -0
  109. package/util/NormalizationNode.js.flow +198 -0
  110. package/util/ReaderNode.js.flow +208 -0
  111. package/util/RelayConcreteNode.js.flow +93 -0
  112. package/util/RelayDefaultHandleKey.js.flow +17 -0
  113. package/util/RelayError.js.flow +62 -0
  114. package/util/RelayFeatureFlags.js.flow +30 -0
  115. package/util/RelayProfiler.js.flow +284 -0
  116. package/util/RelayReplaySubject.js.flow +135 -0
  117. package/util/RelayRuntimeTypes.js.flow +72 -0
  118. package/util/createPayloadFor3DField.js.flow +43 -0
  119. package/util/deepFreeze.js.flow +36 -0
  120. package/util/generateID.js.flow +21 -0
  121. package/util/getFragmentIdentifier.js.flow +52 -0
  122. package/util/getRelayHandleKey.js.flow +41 -0
  123. package/util/getRequestIdentifier.js.flow +42 -0
  124. package/util/isPromise.js.flow +21 -0
  125. package/util/isScalarAndEqual.js.flow +26 -0
  126. package/util/recycleNodesInto.js.flow +76 -0
  127. package/util/resolveImmediate.js.flow +30 -0
  128. package/util/stableCopy.js.flow +35 -0
  129. package/lib/handlers/RelayDefaultMissingFieldHandlers.js +0 -26
  130. package/lib/handlers/getRelayDefaultMissingFieldHandlers.js +0 -36
  131. package/lib/query/RelayModernGraphQLTag.js +0 -104
  132. package/lib/store/RelayConnection.js +0 -37
  133. package/lib/store/RelayConnectionResolver.js +0 -178
  134. package/lib/store/RelayRecordSourceObjectImpl.js +0 -79
  135. package/lib/util/getFragmentSpecIdentifier.js +0 -27
@@ -12,11 +12,15 @@
12
12
 
13
13
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
14
 
15
- var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
15
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
16
 
17
- var DataChecker = require('./DataChecker');
17
+ 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; } } }; }
18
+
19
+ 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); }
18
20
 
19
- var RelayFeatureFlags = require('../util/RelayFeatureFlags');
21
+ 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; }
22
+
23
+ var DataChecker = require('./DataChecker');
20
24
 
21
25
  var RelayModernRecord = require('./RelayModernRecord');
22
26
 
@@ -42,8 +46,9 @@ var recycleNodesInto = require('../util/recycleNodesInto');
42
46
 
43
47
  var resolveImmediate = require('../util/resolveImmediate');
44
48
 
45
- var _require = require('./RelayModernSelector'),
46
- createReaderSelector = _require.createReaderSelector;
49
+ var _require = require('./RelayStoreUtils'),
50
+ ROOT_ID = _require.ROOT_ID,
51
+ ROOT_TYPE = _require.ROOT_TYPE;
47
52
 
48
53
  var DEFAULT_RELEASE_BUFFER_SIZE = 0;
49
54
  /**
@@ -59,11 +64,21 @@ var DEFAULT_RELEASE_BUFFER_SIZE = 0;
59
64
  * is also enforced in development mode by freezing all records passed to a store.
60
65
  */
61
66
 
62
- var RelayModernStore =
63
- /*#__PURE__*/
64
- function () {
67
+ var RelayModernStore = /*#__PURE__*/function () {
65
68
  function RelayModernStore(source, options) {
66
- var _ref, _ref2, _ref3, _ref4;
69
+ var _this = this;
70
+
71
+ var _options$gcReleaseBuf, _options$gcScheduler, _options$UNSTABLE_DO_, _options$log, _options$operationLoa;
72
+
73
+ (0, _defineProperty2["default"])(this, "_gcStep", function () {
74
+ if (_this._gcRun) {
75
+ if (_this._gcRun.next().done) {
76
+ _this._gcRun = null;
77
+ } else {
78
+ _this._gcScheduler(_this._gcStep);
79
+ }
80
+ }
81
+ });
67
82
 
68
83
  // Prevent mutation of a record from outside the store.
69
84
  if (process.env.NODE_ENV !== "production") {
@@ -78,27 +93,26 @@ function () {
78
93
  }
79
94
  }
80
95
 
81
- this._connectionEvents = new Map();
82
- this._connectionSubscriptions = new Map();
83
96
  this._currentWriteEpoch = 0;
84
97
  this._gcHoldCounter = 0;
85
- this._gcReleaseBufferSize = (_ref = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _ref !== void 0 ? _ref : DEFAULT_RELEASE_BUFFER_SIZE;
86
- this._gcScheduler = (_ref2 = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _ref2 !== void 0 ? _ref2 : resolveImmediate;
87
- this._getDataID = (_ref3 = options === null || options === void 0 ? void 0 : options.UNSTABLE_DO_NOT_USE_getDataID) !== null && _ref3 !== void 0 ? _ref3 : defaultGetDataID;
98
+ this._gcReleaseBufferSize = (_options$gcReleaseBuf = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _options$gcReleaseBuf !== void 0 ? _options$gcReleaseBuf : DEFAULT_RELEASE_BUFFER_SIZE;
99
+ this._gcRun = null;
100
+ this._gcScheduler = (_options$gcScheduler = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _options$gcScheduler !== void 0 ? _options$gcScheduler : resolveImmediate;
101
+ 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;
88
102
  this._globalInvalidationEpoch = null;
89
- this._hasScheduledGC = false;
90
- this._index = 0;
91
103
  this._invalidationSubscriptions = new Set();
92
104
  this._invalidatedRecordIDs = new Set();
93
- this._operationLoader = (_ref4 = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _ref4 !== void 0 ? _ref4 : null;
105
+ this.__log = (_options$log = options === null || options === void 0 ? void 0 : options.log) !== null && _options$log !== void 0 ? _options$log : null;
106
+ this._queryCacheExpirationTime = options === null || options === void 0 ? void 0 : options.queryCacheExpirationTime;
107
+ this._operationLoader = (_options$operationLoa = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _options$operationLoa !== void 0 ? _options$operationLoa : null;
94
108
  this._optimisticSource = null;
95
109
  this._recordSource = source;
96
110
  this._releaseBuffer = [];
97
111
  this._roots = new Map();
98
112
  this._shouldScheduleGC = false;
99
113
  this._subscriptions = new Set();
100
- this._updatedConnectionIDs = {};
101
114
  this._updatedRecordIDs = {};
115
+ initializeRecordSource(this._recordSource);
102
116
  }
103
117
 
104
118
  var _proto = RelayModernStore.prototype;
@@ -109,20 +123,8 @@ function () {
109
123
  return (_this$_optimisticSour = this._optimisticSource) !== null && _this$_optimisticSour !== void 0 ? _this$_optimisticSour : this._recordSource;
110
124
  };
111
125
 
112
- _proto.getConnectionEvents_UNSTABLE = function getConnectionEvents_UNSTABLE(connectionID) {
113
- var events = this._connectionEvents.get(connectionID);
114
-
115
- if (events != null) {
116
- var _events$optimistic;
117
-
118
- return (_events$optimistic = events.optimistic) !== null && _events$optimistic !== void 0 ? _events$optimistic : events["final"];
119
- }
120
- };
121
-
122
126
  _proto.check = function check(operation, options) {
123
- var _this = this;
124
-
125
- var _this$_optimisticSour2, _ref5, _ref6;
127
+ var _this$_optimisticSour2, _options$target, _options$handlers;
126
128
 
127
129
  var selector = operation.root;
128
130
  var source = (_this$_optimisticSour2 = this._optimisticSource) !== null && _this$_optimisticSour2 !== void 0 ? _this$_optimisticSour2 : this._recordSource;
@@ -136,58 +138,68 @@ function () {
136
138
  // If so, check if the operation we're checking was last written
137
139
  // before or after invalidation occured.
138
140
  if (operationLastWrittenAt == null || operationLastWrittenAt <= globalInvalidationEpoch) {
139
- // If the operation was written /before/ global invalidation ocurred,
141
+ // If the operation was written /before/ global invalidation occurred,
140
142
  // or if this operation has never been written to the store before,
141
143
  // we will consider the data for this operation to be stale
142
- // (i.e. not resolvable from the store).
143
- return 'stale';
144
+ // (i.e. not resolvable from the store).
145
+ return {
146
+ status: 'stale'
147
+ };
144
148
  }
145
149
  }
146
150
 
147
- var target = (_ref5 = options === null || options === void 0 ? void 0 : options.target) !== null && _ref5 !== void 0 ? _ref5 : source;
148
- var handlers = (_ref6 = options === null || options === void 0 ? void 0 : options.handlers) !== null && _ref6 !== void 0 ? _ref6 : [];
149
- var operationAvailability = DataChecker.check(source, target, selector, handlers, this._operationLoader, this._getDataID, function (id) {
150
- return _this.getConnectionEvents_UNSTABLE(id);
151
- });
152
- return getAvailablityStatus(operationAvailability, operationLastWrittenAt);
151
+ var target = (_options$target = options === null || options === void 0 ? void 0 : options.target) !== null && _options$target !== void 0 ? _options$target : source;
152
+ var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : [];
153
+ var operationAvailability = DataChecker.check(source, target, selector, handlers, this._operationLoader, this._getDataID);
154
+ return getAvailabilityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime, this._queryCacheExpirationTime);
153
155
  };
154
156
 
155
157
  _proto.retain = function retain(operation) {
156
158
  var _this2 = this;
157
159
 
158
160
  var id = operation.request.identifier;
161
+ var disposed = false;
159
162
 
160
163
  var dispose = function dispose() {
161
- // When disposing, instead of immediately decrementing the refCount and
162
- // potentially deleting/collecting the root, move the operation onto
163
- // the release buffer. When the operation is extracted from the release
164
- // buffer, we will determine if it needs to be collected.
165
- _this2._releaseBuffer.push(id); // Only when the release buffer is full do we actually:
166
- // - extract the least recent operation in the release buffer
167
- // - attempt to release it and run GC if it's no longer referenced
168
- // (refCount reached 0).
164
+ // Ensure each retain can only dispose once
165
+ if (disposed) {
166
+ return;
167
+ }
169
168
 
169
+ disposed = true; // For Flow: guard against the entry somehow not existing
170
170
 
171
- if (_this2._releaseBuffer.length > _this2._gcReleaseBufferSize) {
172
- var _id = _this2._releaseBuffer.shift();
171
+ var rootEntry = _this2._roots.get(id);
173
172
 
174
- var _rootEntry = _this2._roots.get(_id);
173
+ if (rootEntry == null) {
174
+ return;
175
+ } // Decrement the ref count: if it becomes zero it is eligible
176
+ // for release.
175
177
 
176
- if (_rootEntry == null) {
177
- // If operation has already been fully released, we don't need
178
- // to do anything.
179
- return;
180
- }
181
178
 
182
- if (_rootEntry.refCount > 0) {
183
- // If the operation is still retained by other callers
184
- // decrement the refCount
185
- _rootEntry.refCount -= 1;
186
- } else {
187
- // Otherwise fully release the query and run GC.
188
- _this2._roots["delete"](_id);
179
+ rootEntry.refCount--;
180
+
181
+ if (rootEntry.refCount === 0) {
182
+ var _queryCacheExpirationTime = _this2._queryCacheExpirationTime;
183
+
184
+ var rootEntryIsStale = rootEntry.fetchTime != null && _queryCacheExpirationTime != null && rootEntry.fetchTime <= Date.now() - _queryCacheExpirationTime;
185
+
186
+ if (rootEntryIsStale) {
187
+ _this2._roots["delete"](id);
189
188
 
190
189
  _this2._scheduleGC();
190
+ } else {
191
+ _this2._releaseBuffer.push(id); // If the release buffer is now over-full, remove the least-recently
192
+ // added entry and schedule a GC. Note that all items in the release
193
+ // buffer have a refCount of 0.
194
+
195
+
196
+ if (_this2._releaseBuffer.length > _this2._gcReleaseBufferSize) {
197
+ var _id = _this2._releaseBuffer.shift();
198
+
199
+ _this2._roots["delete"](_id);
200
+
201
+ _this2._scheduleGC();
202
+ }
191
203
  }
192
204
  }
193
205
  };
@@ -195,14 +207,24 @@ function () {
195
207
  var rootEntry = this._roots.get(id);
196
208
 
197
209
  if (rootEntry != null) {
198
- // If we've previously retained this operation, inrement the refCount
210
+ if (rootEntry.refCount === 0) {
211
+ // This entry should be in the release buffer, but it no longer belongs
212
+ // there since it's retained. Remove it to maintain the invariant that
213
+ // all release buffer entries have a refCount of 0.
214
+ this._releaseBuffer = this._releaseBuffer.filter(function (_id) {
215
+ return _id !== id;
216
+ });
217
+ } // If we've previously retained this operation, increment the refCount
218
+
219
+
199
220
  rootEntry.refCount += 1;
200
221
  } else {
201
222
  // Otherwise create a new entry for the operation
202
223
  this._roots.set(id, {
203
224
  operation: operation,
204
- refCount: 0,
205
- epoch: null
225
+ refCount: 1,
226
+ epoch: null,
227
+ fetchTime: null
206
228
  });
207
229
  }
208
230
 
@@ -220,14 +242,22 @@ function () {
220
242
  }
221
243
 
222
244
  return snapshot;
223
- } // This method will return a list of updated owners form the subscriptions
245
+ } // This method will return a list of updated owners from the subscriptions
224
246
  ;
225
247
 
226
248
  _proto.notify = function notify(sourceOperation, invalidateStore) {
227
249
  var _this3 = this;
228
250
 
229
- // Increment the current write when notifying after executing
251
+ var log = this.__log;
252
+
253
+ if (log != null) {
254
+ log({
255
+ name: 'store.notify.start'
256
+ });
257
+ } // Increment the current write when notifying after executing
230
258
  // a set of changes to the store.
259
+
260
+
231
261
  this._currentWriteEpoch++;
232
262
 
233
263
  if (invalidateStore === true) {
@@ -249,14 +279,14 @@ function () {
249
279
  _this3._updateInvalidationSubscription(subscription, invalidateStore === true);
250
280
  });
251
281
 
252
- this._connectionSubscriptions.forEach(function (subscription, id) {
253
- if (subscription.stale) {
254
- subscription.stale = false;
255
- subscription.callback(subscription.snapshot);
256
- }
257
- });
282
+ if (log != null) {
283
+ log({
284
+ name: 'store.notify.complete',
285
+ updatedRecordIDs: this._updatedRecordIDs,
286
+ invalidatedRecordIDs: this._invalidatedRecordIDs
287
+ });
288
+ }
258
289
 
259
- this._updatedConnectionIDs = {};
260
290
  this._updatedRecordIDs = {};
261
291
 
262
292
  this._invalidatedRecordIDs.clear(); // If a source operation was provided (indicating the operation
@@ -276,6 +306,20 @@ function () {
276
306
 
277
307
  if (rootEntry != null) {
278
308
  rootEntry.epoch = this._currentWriteEpoch;
309
+ rootEntry.fetchTime = Date.now();
310
+ } else if (sourceOperation.request.node.params.operationKind === 'query' && this._gcReleaseBufferSize > 0 && this._releaseBuffer.length < this._gcReleaseBufferSize) {
311
+ // The operation isn't retained but there is space in the release buffer:
312
+ // temporarily track this operation in case the data can be reused soon.
313
+ var temporaryRootEntry = {
314
+ operation: sourceOperation,
315
+ refCount: 0,
316
+ epoch: this._currentWriteEpoch,
317
+ fetchTime: Date.now()
318
+ };
319
+
320
+ this._releaseBuffer.push(id);
321
+
322
+ this._roots.set(id, temporaryRootEntry);
279
323
  }
280
324
  }
281
325
 
@@ -283,34 +327,28 @@ function () {
283
327
  };
284
328
 
285
329
  _proto.publish = function publish(source, idsMarkedForInvalidation) {
286
- var _this4 = this;
287
-
288
330
  var _this$_optimisticSour3;
289
331
 
290
332
  var target = (_this$_optimisticSour3 = this._optimisticSource) !== null && _this$_optimisticSour3 !== void 0 ? _this$_optimisticSour3 : this._recordSource;
291
333
  updateTargetFromSource(target, source, // We increment the current epoch at the end of the set of updates,
292
334
  // in notify(). Here, we pass what will be the incremented value of
293
335
  // the epoch to use to write to invalidated records.
294
- this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs);
295
-
296
- this._connectionSubscriptions.forEach(function (subscription, id) {
297
- var hasStoreUpdates = hasOverlappingIDs(subscription.snapshot.seenRecords, _this4._updatedRecordIDs);
298
-
299
- if (!hasStoreUpdates) {
300
- return;
301
- }
336
+ this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs); // NOTE: log *after* processing the source so that even if a bad log function
337
+ // mutates the source, it doesn't affect Relay processing of it.
302
338
 
303
- var nextSnapshot = _this4._updateConnection_UNSTABLE(subscription.resolver, subscription.snapshot, source, null);
339
+ var log = this.__log;
304
340
 
305
- if (nextSnapshot) {
306
- subscription.snapshot = nextSnapshot;
307
- subscription.stale = true;
308
- }
309
- });
341
+ if (log != null) {
342
+ log({
343
+ name: 'store.publish',
344
+ source: source,
345
+ optimistic: target === this._optimisticSource
346
+ });
347
+ }
310
348
  };
311
349
 
312
350
  _proto.subscribe = function subscribe(snapshot, callback) {
313
- var _this5 = this;
351
+ var _this4 = this;
314
352
 
315
353
  var subscription = {
316
354
  backup: null,
@@ -320,7 +358,7 @@ function () {
320
358
  };
321
359
 
322
360
  var dispose = function dispose() {
323
- _this5._subscriptions["delete"](subscription);
361
+ _this4._subscriptions["delete"](subscription);
324
362
  };
325
363
 
326
364
  this._subscriptions.add(subscription);
@@ -331,18 +369,23 @@ function () {
331
369
  };
332
370
 
333
371
  _proto.holdGC = function holdGC() {
334
- var _this6 = this;
372
+ var _this5 = this;
373
+
374
+ if (this._gcRun) {
375
+ this._gcRun = null;
376
+ this._shouldScheduleGC = true;
377
+ }
335
378
 
336
379
  this._gcHoldCounter++;
337
380
 
338
381
  var dispose = function dispose() {
339
- if (_this6._gcHoldCounter > 0) {
340
- _this6._gcHoldCounter--;
382
+ if (_this5._gcHoldCounter > 0) {
383
+ _this5._gcHoldCounter--;
341
384
 
342
- if (_this6._gcHoldCounter === 0 && _this6._shouldScheduleGC) {
343
- _this6._scheduleGC();
385
+ if (_this5._gcHoldCounter === 0 && _this5._shouldScheduleGC) {
386
+ _this5._scheduleGC();
344
387
 
345
- _this6._shouldScheduleGC = false;
388
+ _this5._shouldScheduleGC = false;
346
389
  }
347
390
  }
348
391
  };
@@ -397,13 +440,13 @@ function () {
397
440
  };
398
441
 
399
442
  _proto.lookupInvalidationState = function lookupInvalidationState(dataIDs) {
400
- var _this7 = this;
443
+ var _this6 = this;
401
444
 
402
445
  var invalidations = new Map();
403
446
  dataIDs.forEach(function (dataID) {
404
447
  var _RelayModernRecord$ge;
405
448
 
406
- var record = _this7.getSource().get(dataID);
449
+ var record = _this6.getSource().get(dataID);
407
450
 
408
451
  invalidations.set(dataID, (_RelayModernRecord$ge = RelayModernRecord.getInvalidationEpoch(record)) !== null && _RelayModernRecord$ge !== void 0 ? _RelayModernRecord$ge : null);
409
452
  });
@@ -424,12 +467,11 @@ function () {
424
467
  } // Check if the invalidation state for any of the ids has changed.
425
468
 
426
469
 
427
- var _iteratorNormalCompletion = true;
428
- var _didIteratorError = false;
429
- var _iteratorError = undefined;
470
+ var _iterator = _createForOfIteratorHelper(prevInvalidationState.dataIDs),
471
+ _step;
430
472
 
431
473
  try {
432
- for (var _iterator = prevInvalidationState.dataIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
474
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
433
475
  var dataID = _step.value;
434
476
 
435
477
  if (currentInvalidations.get(dataID) !== prevInvalidations.get(dataID)) {
@@ -437,25 +479,16 @@ function () {
437
479
  }
438
480
  }
439
481
  } catch (err) {
440
- _didIteratorError = true;
441
- _iteratorError = err;
482
+ _iterator.e(err);
442
483
  } finally {
443
- try {
444
- if (!_iteratorNormalCompletion && _iterator["return"] != null) {
445
- _iterator["return"]();
446
- }
447
- } finally {
448
- if (_didIteratorError) {
449
- throw _iteratorError;
450
- }
451
- }
484
+ _iterator.f();
452
485
  }
453
486
 
454
487
  return false;
455
488
  };
456
489
 
457
490
  _proto.subscribeToInvalidationState = function subscribeToInvalidationState(invalidationState, callback) {
458
- var _this8 = this;
491
+ var _this7 = this;
459
492
 
460
493
  var subscription = {
461
494
  callback: callback,
@@ -463,7 +496,7 @@ function () {
463
496
  };
464
497
 
465
498
  var dispose = function dispose() {
466
- _this8._invalidationSubscriptions["delete"](subscription);
499
+ _this7._invalidationSubscriptions["delete"](subscription);
467
500
  };
468
501
 
469
502
  this._invalidationSubscriptions.add(subscription);
@@ -474,13 +507,13 @@ function () {
474
507
  };
475
508
 
476
509
  _proto._updateInvalidationSubscription = function _updateInvalidationSubscription(subscription, invalidatedStore) {
477
- var _this9 = this;
510
+ var _this8 = this;
478
511
 
479
512
  var callback = subscription.callback,
480
513
  invalidationState = subscription.invalidationState;
481
514
  var dataIDs = invalidationState.dataIDs;
482
515
  var isSubscribedToInvalidatedIDs = invalidatedStore || dataIDs.some(function (dataID) {
483
- return _this9._invalidatedRecordIDs.has(dataID);
516
+ return _this8._invalidatedRecordIDs.has(dataID);
484
517
  });
485
518
 
486
519
  if (!isSubscribedToInvalidatedIDs) {
@@ -490,286 +523,67 @@ function () {
490
523
  callback();
491
524
  };
492
525
 
493
- _proto.lookupConnection_UNSTABLE = function lookupConnection_UNSTABLE(connectionReference, resolver) {
494
- var _connectionEvents$opt;
495
-
496
- !RelayFeatureFlags.ENABLE_CONNECTION_RESOLVERS ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Connection resolvers are not yet supported.') : invariant(false) : void 0;
497
- var id = connectionReference.id;
498
- var initialState = resolver.initialize();
499
-
500
- var connectionEvents = this._connectionEvents.get(id);
501
-
502
- var events = connectionEvents != null ? (_connectionEvents$opt = connectionEvents.optimistic) !== null && _connectionEvents$opt !== void 0 ? _connectionEvents$opt : connectionEvents["final"] : null;
503
- var initialSnapshot = {
504
- edgeSnapshots: {},
505
- id: id,
506
- reference: connectionReference,
507
- seenRecords: {},
508
- state: initialState
509
- };
510
-
511
- if (events == null || events.length === 0) {
512
- return initialSnapshot;
513
- }
514
-
515
- return this._reduceConnection_UNSTABLE(resolver, connectionReference, initialSnapshot, events);
516
- };
517
-
518
- _proto.subscribeConnection_UNSTABLE = function subscribeConnection_UNSTABLE(snapshot, resolver, callback) {
519
- var _this10 = this;
520
-
521
- !RelayFeatureFlags.ENABLE_CONNECTION_RESOLVERS ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Connection resolvers are not yet supported.') : invariant(false) : void 0;
522
- var id = String(this._index++);
523
- var subscription = {
524
- backup: null,
525
- callback: callback,
526
- id: id,
527
- resolver: resolver,
528
- snapshot: snapshot,
529
- stale: false
530
- };
531
-
532
- var dispose = function dispose() {
533
- _this10._connectionSubscriptions["delete"](id);
534
- };
535
-
536
- this._connectionSubscriptions.set(id, subscription);
537
-
538
- return {
539
- dispose: dispose
540
- };
541
- };
526
+ _proto.snapshot = function snapshot() {
527
+ var _this9 = this;
542
528
 
543
- _proto.publishConnectionEvents_UNSTABLE = function publishConnectionEvents_UNSTABLE(events, final) {
544
- var _this11 = this;
529
+ !(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
530
+ var log = this.__log;
545
531
 
546
- if (events.length === 0) {
547
- return;
532
+ if (log != null) {
533
+ log({
534
+ name: 'store.snapshot'
535
+ });
548
536
  }
549
537
 
550
- !RelayFeatureFlags.ENABLE_CONNECTION_RESOLVERS ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Connection resolvers are not yet supported.') : invariant(false) : void 0;
551
- var pendingConnectionEvents = new Map();
552
- events.forEach(function (event) {
553
- var connectionID = event.connectionID;
554
- var pendingEvents = pendingConnectionEvents.get(connectionID);
555
-
556
- if (pendingEvents == null) {
557
- pendingEvents = [];
558
- pendingConnectionEvents.set(connectionID, pendingEvents);
559
- }
560
-
561
- pendingEvents.push(event);
562
-
563
- var connectionEvents = _this11._connectionEvents.get(connectionID);
564
-
565
- if (connectionEvents == null) {
566
- connectionEvents = {
567
- "final": [],
568
- optimistic: null
569
- };
570
-
571
- _this11._connectionEvents.set(connectionID, connectionEvents);
572
- }
573
-
574
- if (final) {
575
- connectionEvents["final"].push(event);
576
- } else {
577
- var optimisticEvents = connectionEvents.optimistic;
578
-
579
- if (optimisticEvents == null) {
580
- optimisticEvents = connectionEvents["final"].slice();
581
- connectionEvents.optimistic = optimisticEvents;
582
- }
583
-
584
- optimisticEvents.push(event);
585
- }
586
- });
587
-
588
- this._connectionSubscriptions.forEach(function (subscription, id) {
589
- var pendingEvents = pendingConnectionEvents.get(subscription.snapshot.reference.id);
590
-
591
- if (pendingEvents == null) {
538
+ this._subscriptions.forEach(function (subscription) {
539
+ // Backup occurs after writing a new "final" payload(s) and before (re)applying
540
+ // optimistic changes. Each subscription's `snapshot` represents what was *last
541
+ // published to the subscriber*, which notably may include previous optimistic
542
+ // updates. Therefore a subscription can be in any of the following states:
543
+ // - stale=true: This subscription was restored to a different value than
544
+ // `snapshot`. That means this subscription has changes relative to its base,
545
+ // but its base has changed (we just applied a final payload): recompute
546
+ // a backup so that we can later restore to the state the subscription
547
+ // should be in.
548
+ // - stale=false: This subscription was restored to the same value than
549
+ // `snapshot`. That means this subscription does *not* have changes relative
550
+ // to its base, so the current `snapshot` is valid to use as a backup.
551
+ if (!subscription.stale) {
552
+ subscription.backup = subscription.snapshot;
592
553
  return;
593
554
  }
594
555
 
595
- var nextSnapshot = _this11._updateConnection_UNSTABLE(subscription.resolver, subscription.snapshot, null, pendingEvents);
556
+ var snapshot = subscription.snapshot;
557
+ var backup = RelayReader.read(_this9.getSource(), snapshot.selector);
558
+ var nextData = recycleNodesInto(snapshot.data, backup.data);
559
+ backup.data = nextData; // backup owns the snapshot and can safely mutate
596
560
 
597
- if (nextSnapshot) {
598
- subscription.snapshot = nextSnapshot;
599
- subscription.stale = true;
600
- }
561
+ subscription.backup = backup;
601
562
  });
602
- };
603
-
604
- _proto._updateConnection_UNSTABLE = function _updateConnection_UNSTABLE(resolver, snapshot, source, pendingEvents) {
605
- var _pendingEvents;
606
563
 
607
- var nextSnapshot = this._reduceConnection_UNSTABLE(resolver, snapshot.reference, snapshot, (_pendingEvents = pendingEvents) !== null && _pendingEvents !== void 0 ? _pendingEvents : [], source);
608
-
609
- var state = recycleNodesInto(snapshot.state, nextSnapshot.state);
610
-
611
- if (process.env.NODE_ENV !== "production") {
612
- deepFreeze(nextSnapshot);
564
+ if (this._gcRun) {
565
+ this._gcRun = null;
566
+ this._shouldScheduleGC = true;
613
567
  }
614
568
 
615
- if (state !== snapshot.state) {
616
- return (0, _objectSpread2["default"])({}, nextSnapshot, {
617
- state: state
618
- });
619
- }
569
+ this._optimisticSource = RelayOptimisticRecordSource.create(this.getSource());
620
570
  };
621
571
 
622
- _proto._reduceConnection_UNSTABLE = function _reduceConnection_UNSTABLE(resolver, connectionReference, snapshot, events) {
623
- var _this12 = this;
624
-
625
- var _edgesField$concreteT;
626
-
627
- var source = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
628
- var edgesField = connectionReference.edgesField,
629
- id = connectionReference.id,
630
- variables = connectionReference.variables;
631
- var fragment = {
632
- kind: 'Fragment',
633
- name: edgesField.name,
634
- type: (_edgesField$concreteT = edgesField.concreteType) !== null && _edgesField$concreteT !== void 0 ? _edgesField$concreteT : '__Any',
635
- metadata: null,
636
- argumentDefinitions: [],
637
- selections: edgesField.selections
638
- };
639
- var seenRecords = {};
640
- var edgeSnapshots = (0, _objectSpread2["default"])({}, snapshot.edgeSnapshots);
641
- var initialState = snapshot.state;
642
-
643
- if (source) {
644
- var edgeData = {};
645
- Object.keys(edgeSnapshots).forEach(function (edgeID) {
646
- var prevSnapshot = edgeSnapshots[edgeID];
647
- var nextSnapshot = RelayReader.read(_this12.getSource(), createReaderSelector(fragment, edgeID, variables, prevSnapshot.selector.owner));
648
- var data = recycleNodesInto(prevSnapshot.data, nextSnapshot.data);
649
- nextSnapshot = {
650
- data: data,
651
- isMissingData: nextSnapshot.isMissingData,
652
- seenRecords: nextSnapshot.seenRecords,
653
- selector: nextSnapshot.selector
654
- };
655
-
656
- if (data !== prevSnapshot.data) {
657
- edgeData[edgeID] = data;
658
- /* $FlowFixMe(>=0.111.0) This comment suppresses an error found when
659
- * Flow v0.111.0 was deployed. To see the error, delete this comment
660
- * and run Flow. */
572
+ _proto.restore = function restore() {
573
+ !(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;
574
+ var log = this.__log;
661
575
 
662
- edgeSnapshots[edgeID] = nextSnapshot;
663
- }
576
+ if (log != null) {
577
+ log({
578
+ name: 'store.restore'
664
579
  });
665
-
666
- if (Object.keys(edgeData).length !== 0) {
667
- initialState = resolver.reduce(initialState, {
668
- kind: 'update',
669
- edgeData: edgeData
670
- });
671
- }
672
580
  }
673
581
 
674
- var state = events.reduce(function (prevState, event) {
675
- if (event.kind === 'fetch') {
676
- var edges = [];
677
- event.edgeIDs.forEach(function (edgeID) {
678
- if (edgeID == null) {
679
- edges.push(edgeID);
680
- return;
681
- }
682
-
683
- var edgeSnapshot = RelayReader.read(_this12.getSource(), createReaderSelector(fragment, edgeID, variables, event.request));
684
- Object.assign(seenRecords, edgeSnapshot.seenRecords);
685
- var itemData = edgeSnapshot.data;
686
- /* $FlowFixMe(>=0.111.0) This comment suppresses an error found
687
- * when Flow v0.111.0 was deployed. To see the error, delete this
688
- * comment and run Flow. */
689
-
690
- edgeSnapshots[edgeID] = edgeSnapshot;
691
- edges.push(itemData);
692
- });
693
- return resolver.reduce(prevState, {
694
- kind: 'fetch',
695
- args: event.args,
696
- edges: edges,
697
- pageInfo: event.pageInfo,
698
- stream: event.stream
699
- });
700
- } else if (event.kind === 'insert') {
701
- var edgeSnapshot = RelayReader.read(_this12.getSource(), createReaderSelector(fragment, event.edgeID, variables, event.request));
702
- Object.assign(seenRecords, edgeSnapshot.seenRecords);
703
- var itemData = edgeSnapshot.data;
704
- /* $FlowFixMe(>=0.111.0) This comment suppresses an error found when
705
- * Flow v0.111.0 was deployed. To see the error, delete this comment
706
- * and run Flow. */
707
-
708
- edgeSnapshots[event.edgeID] = edgeSnapshot;
709
- return resolver.reduce(prevState, {
710
- args: event.args,
711
- edge: itemData,
712
- kind: 'insert'
713
- });
714
- } else if (event.kind === 'stream.edge') {
715
- var _edgeSnapshot = RelayReader.read(_this12.getSource(), createReaderSelector(fragment, event.edgeID, variables, event.request));
716
-
717
- Object.assign(seenRecords, _edgeSnapshot.seenRecords);
718
- var _itemData = _edgeSnapshot.data;
719
- /* $FlowFixMe(>=0.111.0) This comment suppresses an error found when
720
- * Flow v0.111.0 was deployed. To see the error, delete this comment
721
- * and run Flow. */
722
-
723
- edgeSnapshots[event.edgeID] = _edgeSnapshot;
724
- return resolver.reduce(prevState, {
725
- args: event.args,
726
- edge: _itemData,
727
- index: event.index,
728
- kind: 'stream.edge'
729
- });
730
- } else if (event.kind === 'stream.pageInfo') {
731
- return resolver.reduce(prevState, {
732
- args: event.args,
733
- kind: 'stream.pageInfo',
734
- pageInfo: event.pageInfo
735
- });
736
- } else {
737
- event.kind;
738
- !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected connection event kind `%s`.', event.kind) : invariant(false) : void 0;
739
- }
740
- }, initialState);
741
- return {
742
- edgeSnapshots: edgeSnapshots,
743
- id: id,
744
- reference: connectionReference,
745
- seenRecords: seenRecords,
746
- state: state
747
- };
748
- };
749
-
750
- _proto.snapshot = function snapshot() {
751
- !(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
752
-
753
- this._connectionSubscriptions.forEach(function (subscription) {
754
- subscription.backup = subscription.snapshot;
755
- });
756
-
757
- this._subscriptions.forEach(function (subscription) {
758
- subscription.backup = subscription.snapshot;
759
- });
760
-
761
- this._optimisticSource = RelayOptimisticRecordSource.create(this.getSource());
762
- };
763
-
764
- _proto.restore = function restore() {
765
- var _this13 = this;
766
-
767
- !(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;
768
582
  this._optimisticSource = null;
769
583
 
770
- this._connectionEvents.forEach(function (events) {
771
- events.optimistic = null;
772
- });
584
+ if (this._shouldScheduleGC) {
585
+ this._scheduleGC();
586
+ }
773
587
 
774
588
  this._subscriptions.forEach(function (subscription) {
775
589
  var backup = subscription.backup;
@@ -790,127 +604,104 @@ function () {
790
604
  subscription.stale = true;
791
605
  }
792
606
  });
793
-
794
- this._connectionSubscriptions.forEach(function (subscription) {
795
- var backup = subscription.backup;
796
- subscription.backup = null;
797
-
798
- if (backup) {
799
- if (backup.state !== subscription.snapshot.state) {
800
- subscription.stale = true;
801
- }
802
-
803
- subscription.snapshot = backup;
804
- } else {
805
- // This subscription was established after the creation of the
806
- // connection snapshot so there's nothing to restore to. Recreate the
807
- // connection from scratch and check ifs value changes.
808
- var baseSnapshot = _this13.lookupConnection_UNSTABLE(subscription.snapshot.reference, subscription.resolver);
809
-
810
- var nextState = recycleNodesInto(subscription.snapshot.state, baseSnapshot.state);
811
-
812
- if (nextState !== subscription.snapshot.state) {
813
- subscription.stale = true;
814
- }
815
-
816
- subscription.snapshot = (0, _objectSpread2["default"])({}, baseSnapshot, {
817
- state: nextState
818
- });
819
- }
820
- });
821
607
  };
822
608
 
823
609
  _proto._scheduleGC = function _scheduleGC() {
824
- var _this14 = this;
825
-
826
610
  if (this._gcHoldCounter > 0) {
827
611
  this._shouldScheduleGC = true;
828
612
  return;
829
613
  }
830
614
 
831
- if (this._hasScheduledGC) {
615
+ if (this._gcRun) {
832
616
  return;
833
617
  }
834
618
 
835
- this._hasScheduledGC = true;
836
-
837
- this._gcScheduler(function () {
838
- _this14.__gc();
619
+ this._gcRun = this._collect();
839
620
 
840
- _this14._hasScheduledGC = false;
841
- });
842
- };
621
+ this._gcScheduler(this._gcStep);
622
+ }
623
+ /**
624
+ * Run a full GC synchronously.
625
+ */
626
+ ;
843
627
 
844
628
  _proto.__gc = function __gc() {
845
- var _this15 = this;
846
-
847
629
  // Don't run GC while there are optimistic updates applied
848
630
  if (this._optimisticSource != null) {
849
631
  return;
850
632
  }
851
633
 
852
- var references = new Set();
853
- var connectionReferences = new Set(); // Mark all records that are traversable from a root
854
-
855
- this._roots.forEach(function (_ref7) {
856
- var operation = _ref7.operation;
857
- var selector = operation.root;
858
- RelayReferenceMarker.mark(_this15._recordSource, selector, references, connectionReferences, function (id) {
859
- return _this15.getConnectionEvents_UNSTABLE(id);
860
- }, _this15._operationLoader);
861
- });
634
+ var gcRun = this._collect();
862
635
 
863
- if (references.size === 0) {
864
- // Short-circuit if *nothing* is referenced
865
- this._recordSource.clear();
866
- } else {
867
- // Evict any unreferenced nodes
868
- var storeIDs = this._recordSource.getRecordIDs();
869
-
870
- for (var ii = 0; ii < storeIDs.length; ii++) {
871
- var dataID = storeIDs[ii];
636
+ while (!gcRun.next().done) {}
637
+ };
872
638
 
873
- if (!references.has(dataID)) {
874
- this._recordSource.remove(dataID);
875
- }
876
- }
877
- }
639
+ _proto._collect = function* _collect() {
640
+ /* eslint-disable no-labels */
641
+ top: while (true) {
642
+ var startEpoch = this._currentWriteEpoch;
643
+ var references = new Set(); // Mark all records that are traversable from a root
878
644
 
879
- if (connectionReferences.size === 0) {
880
- this._connectionEvents.clear();
881
- } else {
882
- // Evict any unreferenced connections
883
- var _iteratorNormalCompletion2 = true;
884
- var _didIteratorError2 = false;
885
- var _iteratorError2 = undefined;
645
+ var _iterator2 = _createForOfIteratorHelper(this._roots.values()),
646
+ _step2;
886
647
 
887
648
  try {
888
- for (var _iterator2 = this._connectionEvents.keys()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
889
- var connectionID = _step2.value;
649
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
650
+ var operation = _step2.value.operation;
651
+ var selector = operation.root;
652
+ RelayReferenceMarker.mark(this._recordSource, selector, references, this._operationLoader); // Yield for other work after each operation
653
+
654
+ yield; // If the store was updated, restart
890
655
 
891
- if (!connectionReferences.has(connectionID)) {
892
- this._connectionEvents["delete"](connectionID);
656
+ if (startEpoch !== this._currentWriteEpoch) {
657
+ continue top;
893
658
  }
894
659
  }
895
660
  } catch (err) {
896
- _didIteratorError2 = true;
897
- _iteratorError2 = err;
661
+ _iterator2.e(err);
898
662
  } finally {
899
- try {
900
- if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
901
- _iterator2["return"]();
902
- }
903
- } finally {
904
- if (_didIteratorError2) {
905
- throw _iteratorError2;
663
+ _iterator2.f();
664
+ }
665
+
666
+ var log = this.__log;
667
+
668
+ if (log != null) {
669
+ log({
670
+ name: 'store.gc',
671
+ references: references
672
+ });
673
+ } // Sweep records without references
674
+
675
+
676
+ if (references.size === 0) {
677
+ // Short-circuit if *nothing* is referenced
678
+ this._recordSource.clear();
679
+ } else {
680
+ // Evict any unreferenced nodes
681
+ var storeIDs = this._recordSource.getRecordIDs();
682
+
683
+ for (var ii = 0; ii < storeIDs.length; ii++) {
684
+ var dataID = storeIDs[ii];
685
+
686
+ if (!references.has(dataID)) {
687
+ this._recordSource.remove(dataID);
906
688
  }
907
689
  }
908
690
  }
691
+
692
+ return;
909
693
  }
910
694
  };
911
695
 
912
696
  return RelayModernStore;
913
697
  }();
698
+
699
+ function initializeRecordSource(target) {
700
+ if (!target.has(ROOT_ID)) {
701
+ var rootRecord = RelayModernRecord.create(ROOT_ID, ROOT_TYPE);
702
+ target.set(ROOT_ID, rootRecord);
703
+ }
704
+ }
914
705
  /**
915
706
  * Updates the target with information from source, also updating a mapping of
916
707
  * which records in the target were changed as a result.
@@ -1003,7 +794,7 @@ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForI
1003
794
  /**
1004
795
  * Returns an OperationAvailability given the Availability returned
1005
796
  * by checking an operation, and when that operation was last written to the store.
1006
- * Specifically, the provided Availablity of a an operation will contain the
797
+ * Specifically, the provided Availability of an operation will contain the
1007
798
  * value of when a record referenced by the operation was most recently
1008
799
  * invalidated; given that value, and given when this operation was last
1009
800
  * written to the store, this function will return the overall
@@ -1011,32 +802,46 @@ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForI
1011
802
  */
1012
803
 
1013
804
 
1014
- function getAvailablityStatus(opearionAvailability, operationLastWrittenAt) {
1015
- var mostRecentlyInvalidatedAt = opearionAvailability.mostRecentlyInvalidatedAt,
1016
- status = opearionAvailability.status;
805
+ function getAvailabilityStatus(operationAvailability, operationLastWrittenAt, operationFetchTime, queryCacheExpirationTime) {
806
+ var mostRecentlyInvalidatedAt = operationAvailability.mostRecentlyInvalidatedAt,
807
+ status = operationAvailability.status;
808
+
809
+ if (typeof mostRecentlyInvalidatedAt === 'number') {
810
+ // If some record referenced by this operation is stale, then the operation itself is stale
811
+ // if either the operation itself was never written *or* the operation was last written
812
+ // before the most recent invalidation of its reachable records.
813
+ if (operationLastWrittenAt == null || mostRecentlyInvalidatedAt > operationLastWrittenAt) {
814
+ return {
815
+ status: 'stale'
816
+ };
817
+ }
818
+ }
1017
819
 
1018
- if (typeof mostRecentlyInvalidatedAt !== 'number') {
1019
- // If the record has never been invalidated, it isn't stale,
1020
- // so return the availability status of the operation.
1021
- return status;
820
+ if (status === 'missing') {
821
+ return {
822
+ status: 'missing'
823
+ };
1022
824
  }
1023
825
 
1024
- if (operationLastWrittenAt == null) {
1025
- // If we've never written this operation before and there was an invalidation,
1026
- // then we don't have enough information to determine staleness,
1027
- // so by default we will consider it stale.
1028
- return 'stale';
1029
- } // If the record was invalidated before the operation we're reading was
1030
- // last written, we can consider it not stale; otherwise consider it stale.
826
+ if (operationFetchTime != null && queryCacheExpirationTime != null) {
827
+ var isStale = operationFetchTime <= Date.now() - queryCacheExpirationTime;
828
+
829
+ if (isStale) {
830
+ return {
831
+ status: 'stale'
832
+ };
833
+ }
834
+ } // There were no invalidations of any reachable records *or* the operation is known to have
835
+ // been fetched after the most recent record invalidation.
1031
836
 
1032
837
 
1033
- return mostRecentlyInvalidatedAt > operationLastWrittenAt ? 'stale' : status;
838
+ return {
839
+ status: 'available',
840
+ fetchTime: operationFetchTime !== null && operationFetchTime !== void 0 ? operationFetchTime : null
841
+ };
1034
842
  }
1035
843
 
1036
844
  RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
1037
- lookup: 'RelayModernStore.prototype.lookup',
1038
- notify: 'RelayModernStore.prototype.notify',
1039
- publish: 'RelayModernStore.prototype.publish',
1040
- __gc: 'RelayModernStore.prototype.__gc'
845
+ lookup: 'RelayModernStore.prototype.lookup'
1041
846
  });
1042
847
  module.exports = RelayModernStore;