relay-runtime 9.0.0 → 10.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 (142) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +47 -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 +199 -0
  5. package/index.js +1 -1
  6. package/index.js.flow +335 -0
  7. package/lib/handlers/RelayDefaultHandlerProvider.js +20 -0
  8. package/lib/handlers/connection/ConnectionHandler.js +1 -3
  9. package/lib/handlers/connection/MutationHandlers.js +212 -0
  10. package/lib/index.js +14 -2
  11. package/lib/mutations/RelayDeclarativeMutationConfig.js +22 -45
  12. package/lib/mutations/RelayRecordProxy.js +1 -3
  13. package/lib/mutations/RelayRecordSourceMutator.js +1 -3
  14. package/lib/mutations/RelayRecordSourceProxy.js +1 -3
  15. package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -3
  16. package/lib/mutations/commitMutation.js +2 -3
  17. package/lib/mutations/validateMutation.js +40 -9
  18. package/lib/network/RelayObservable.js +9 -9
  19. package/lib/network/RelayQueryResponseCache.js +8 -6
  20. package/lib/query/GraphQLTag.js +2 -1
  21. package/lib/query/PreloadableQueryRegistry.js +70 -0
  22. package/lib/query/fetchQuery.js +2 -3
  23. package/lib/query/fetchQueryInternal.js +5 -14
  24. package/lib/store/DataChecker.js +200 -71
  25. package/lib/store/RelayConcreteVariables.js +6 -2
  26. package/lib/store/RelayModernEnvironment.js +124 -65
  27. package/lib/store/RelayModernFragmentSpecResolver.js +19 -14
  28. package/lib/store/RelayModernOperationDescriptor.js +6 -5
  29. package/lib/store/RelayModernQueryExecutor.js +122 -73
  30. package/lib/store/RelayModernRecord.js +14 -9
  31. package/lib/store/RelayModernSelector.js +6 -2
  32. package/lib/store/RelayModernStore.js +281 -131
  33. package/lib/store/RelayOperationTracker.js +35 -78
  34. package/lib/store/RelayOptimisticRecordSource.js +7 -5
  35. package/lib/store/RelayPublishQueue.js +2 -4
  36. package/lib/store/RelayReader.js +304 -52
  37. package/lib/store/RelayRecordSource.js +1 -3
  38. package/lib/store/RelayRecordSourceMapImpl.js +13 -18
  39. package/lib/store/RelayReferenceMarker.js +125 -14
  40. package/lib/store/RelayResponseNormalizer.js +261 -66
  41. package/lib/store/RelayStoreReactFlightUtils.js +47 -0
  42. package/lib/store/RelayStoreUtils.js +1 -0
  43. package/lib/store/StoreInspector.js +8 -8
  44. package/lib/store/TypeID.js +28 -0
  45. package/lib/store/cloneRelayScalarHandleSourceField.js +44 -0
  46. package/lib/store/defaultRequiredFieldLogger.js +18 -0
  47. package/lib/store/normalizeRelayPayload.js +6 -2
  48. package/lib/store/readInlineData.js +1 -1
  49. package/lib/subscription/requestSubscription.js +4 -3
  50. package/lib/util/NormalizationNode.js +1 -5
  51. package/lib/util/RelayConcreteNode.js +11 -6
  52. package/lib/util/RelayError.js +39 -9
  53. package/lib/util/RelayFeatureFlags.js +6 -3
  54. package/lib/util/RelayReplaySubject.js +3 -3
  55. package/lib/util/createPayloadFor3DField.js +7 -2
  56. package/lib/util/getFragmentIdentifier.js +12 -3
  57. package/lib/util/getOperation.js +33 -0
  58. package/lib/util/getRequestIdentifier.js +2 -2
  59. package/lib/util/isEmptyObject.js +25 -0
  60. package/lib/util/recycleNodesInto.js +6 -7
  61. package/lib/util/reportMissingRequiredFields.js +48 -0
  62. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  63. package/mutations/RelayRecordProxy.js.flow +165 -0
  64. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  65. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  66. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  67. package/mutations/applyOptimisticMutation.js.flow +76 -0
  68. package/mutations/commitLocalUpdate.js.flow +24 -0
  69. package/mutations/commitMutation.js.flow +181 -0
  70. package/mutations/validateMutation.js.flow +242 -0
  71. package/network/ConvertToExecuteFunction.js.flow +49 -0
  72. package/network/RelayNetwork.js.flow +84 -0
  73. package/network/RelayNetworkTypes.js.flow +145 -0
  74. package/network/RelayObservable.js.flow +634 -0
  75. package/network/RelayQueryResponseCache.js.flow +111 -0
  76. package/package.json +2 -2
  77. package/query/GraphQLTag.js.flow +168 -0
  78. package/query/PreloadableQueryRegistry.js.flow +65 -0
  79. package/query/fetchQuery.js.flow +47 -0
  80. package/query/fetchQueryInternal.js.flow +343 -0
  81. package/relay-runtime.js +2 -2
  82. package/relay-runtime.min.js +2 -2
  83. package/store/ClientID.js.flow +43 -0
  84. package/store/DataChecker.js.flow +568 -0
  85. package/store/RelayConcreteVariables.js.flow +96 -0
  86. package/store/RelayModernEnvironment.js.flow +571 -0
  87. package/store/RelayModernFragmentSpecResolver.js.flow +438 -0
  88. package/store/RelayModernOperationDescriptor.js.flow +92 -0
  89. package/store/RelayModernQueryExecutor.js.flow +1345 -0
  90. package/store/RelayModernRecord.js.flow +403 -0
  91. package/store/RelayModernSelector.js.flow +455 -0
  92. package/store/RelayModernStore.js.flow +858 -0
  93. package/store/RelayOperationTracker.js.flow +164 -0
  94. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  95. package/store/RelayPublishQueue.js.flow +401 -0
  96. package/store/RelayReader.js.flow +638 -0
  97. package/store/RelayRecordSource.js.flow +29 -0
  98. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  99. package/store/RelayRecordState.js.flow +37 -0
  100. package/store/RelayReferenceMarker.js.flow +324 -0
  101. package/store/RelayResponseNormalizer.js.flow +791 -0
  102. package/store/RelayStoreReactFlightUtils.js.flow +64 -0
  103. package/store/RelayStoreTypes.js.flow +958 -0
  104. package/store/RelayStoreUtils.js.flow +219 -0
  105. package/store/StoreInspector.js.flow +171 -0
  106. package/store/TypeID.js.flow +28 -0
  107. package/store/ViewerPattern.js.flow +26 -0
  108. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  109. package/store/cloneRelayScalarHandleSourceField.js.flow +62 -0
  110. package/store/createFragmentSpecResolver.js.flow +55 -0
  111. package/store/createRelayContext.js.flow +44 -0
  112. package/store/defaultGetDataID.js.flow +27 -0
  113. package/store/defaultRequiredFieldLogger.js.flow +23 -0
  114. package/store/hasOverlappingIDs.js.flow +34 -0
  115. package/store/isRelayModernEnvironment.js.flow +27 -0
  116. package/store/normalizeRelayPayload.js.flow +51 -0
  117. package/store/readInlineData.js.flow +75 -0
  118. package/subscription/requestSubscription.js.flow +103 -0
  119. package/util/JSResourceTypes.flow.js.flow +20 -0
  120. package/util/NormalizationNode.js.flow +213 -0
  121. package/util/ReaderNode.js.flow +227 -0
  122. package/util/RelayConcreteNode.js.flow +99 -0
  123. package/util/RelayDefaultHandleKey.js.flow +17 -0
  124. package/util/RelayError.js.flow +62 -0
  125. package/util/RelayFeatureFlags.js.flow +37 -0
  126. package/util/RelayProfiler.js.flow +284 -0
  127. package/util/RelayReplaySubject.js.flow +135 -0
  128. package/util/RelayRuntimeTypes.js.flow +72 -0
  129. package/util/createPayloadFor3DField.js.flow +43 -0
  130. package/util/deepFreeze.js.flow +36 -0
  131. package/util/generateID.js.flow +21 -0
  132. package/util/getFragmentIdentifier.js.flow +76 -0
  133. package/util/getOperation.js.flow +40 -0
  134. package/util/getRelayHandleKey.js.flow +41 -0
  135. package/util/getRequestIdentifier.js.flow +42 -0
  136. package/util/isEmptyObject.js.flow +25 -0
  137. package/util/isPromise.js.flow +21 -0
  138. package/util/isScalarAndEqual.js.flow +26 -0
  139. package/util/recycleNodesInto.js.flow +87 -0
  140. package/util/reportMissingRequiredFields.js.flow +51 -0
  141. package/util/resolveImmediate.js.flow +30 -0
  142. package/util/stableCopy.js.flow +35 -0
@@ -10,6 +10,16 @@
10
10
  // flowlint ambiguous-object-type:error
11
11
  'use strict';
12
12
 
13
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
+
15
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
+
17
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; 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 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(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
20
+
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
+
13
23
  var DataChecker = require('./DataChecker');
14
24
 
15
25
  var RelayModernRecord = require('./RelayModernRecord');
@@ -22,6 +32,8 @@ var RelayReader = require('./RelayReader');
22
32
 
23
33
  var RelayReferenceMarker = require('./RelayReferenceMarker');
24
34
 
35
+ var RelayStoreReactFlightUtils = require('./RelayStoreReactFlightUtils');
36
+
25
37
  var RelayStoreUtils = require('./RelayStoreUtils');
26
38
 
27
39
  var deepFreeze = require('../util/deepFreeze');
@@ -32,6 +44,8 @@ var hasOverlappingIDs = require('./hasOverlappingIDs');
32
44
 
33
45
  var invariant = require("fbjs/lib/invariant");
34
46
 
47
+ var isEmptyObject = require('../util/isEmptyObject');
48
+
35
49
  var recycleNodesInto = require('../util/recycleNodesInto');
36
50
 
37
51
  var resolveImmediate = require('../util/resolveImmediate');
@@ -54,11 +68,21 @@ var DEFAULT_RELEASE_BUFFER_SIZE = 0;
54
68
  * is also enforced in development mode by freezing all records passed to a store.
55
69
  */
56
70
 
57
- var RelayModernStore =
58
- /*#__PURE__*/
59
- function () {
71
+ var RelayModernStore = /*#__PURE__*/function () {
60
72
  function RelayModernStore(source, options) {
61
- var _ref, _ref2, _ref3, _ref4;
73
+ var _this = this;
74
+
75
+ var _options$gcReleaseBuf, _options$gcScheduler, _options$UNSTABLE_DO_, _options$log, _options$operationLoa;
76
+
77
+ (0, _defineProperty2["default"])(this, "_gcStep", function () {
78
+ if (_this._gcRun) {
79
+ if (_this._gcRun.next().done) {
80
+ _this._gcRun = null;
81
+ } else {
82
+ _this._gcScheduler(_this._gcStep);
83
+ }
84
+ }
85
+ });
62
86
 
63
87
  // Prevent mutation of a record from outside the store.
64
88
  if (process.env.NODE_ENV !== "production") {
@@ -75,15 +99,16 @@ function () {
75
99
 
76
100
  this._currentWriteEpoch = 0;
77
101
  this._gcHoldCounter = 0;
78
- this._gcReleaseBufferSize = (_ref = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _ref !== void 0 ? _ref : DEFAULT_RELEASE_BUFFER_SIZE;
79
- this._gcScheduler = (_ref2 = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _ref2 !== void 0 ? _ref2 : resolveImmediate;
80
- this._getDataID = (_ref3 = options === null || options === void 0 ? void 0 : options.UNSTABLE_DO_NOT_USE_getDataID) !== null && _ref3 !== void 0 ? _ref3 : defaultGetDataID;
102
+ this._gcReleaseBufferSize = (_options$gcReleaseBuf = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _options$gcReleaseBuf !== void 0 ? _options$gcReleaseBuf : DEFAULT_RELEASE_BUFFER_SIZE;
103
+ this._gcRun = null;
104
+ this._gcScheduler = (_options$gcScheduler = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _options$gcScheduler !== void 0 ? _options$gcScheduler : resolveImmediate;
105
+ 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;
81
106
  this._globalInvalidationEpoch = null;
82
- this._hasScheduledGC = false;
83
- this._index = 0;
84
107
  this._invalidationSubscriptions = new Set();
85
108
  this._invalidatedRecordIDs = new Set();
86
- this._operationLoader = (_ref4 = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _ref4 !== void 0 ? _ref4 : null;
109
+ this.__log = (_options$log = options === null || options === void 0 ? void 0 : options.log) !== null && _options$log !== void 0 ? _options$log : null;
110
+ this._queryCacheExpirationTime = options === null || options === void 0 ? void 0 : options.queryCacheExpirationTime;
111
+ this._operationLoader = (_options$operationLoa = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _options$operationLoa !== void 0 ? _options$operationLoa : null;
87
112
  this._optimisticSource = null;
88
113
  this._recordSource = source;
89
114
  this._releaseBuffer = [];
@@ -103,7 +128,7 @@ function () {
103
128
  };
104
129
 
105
130
  _proto.check = function check(operation, options) {
106
- var _this$_optimisticSour2, _ref5, _ref6;
131
+ var _this$_optimisticSour2, _options$target, _options$handlers;
107
132
 
108
133
  var selector = operation.root;
109
134
  var source = (_this$_optimisticSour2 = this._optimisticSource) !== null && _this$_optimisticSour2 !== void 0 ? _this$_optimisticSour2 : this._recordSource;
@@ -117,58 +142,68 @@ function () {
117
142
  // If so, check if the operation we're checking was last written
118
143
  // before or after invalidation occured.
119
144
  if (operationLastWrittenAt == null || operationLastWrittenAt <= globalInvalidationEpoch) {
120
- // If the operation was written /before/ global invalidation ocurred,
145
+ // If the operation was written /before/ global invalidation occurred,
121
146
  // or if this operation has never been written to the store before,
122
147
  // we will consider the data for this operation to be stale
123
- // (i.e. not resolvable from the store).
148
+ // (i.e. not resolvable from the store).
124
149
  return {
125
150
  status: 'stale'
126
151
  };
127
152
  }
128
153
  }
129
154
 
130
- var target = (_ref5 = options === null || options === void 0 ? void 0 : options.target) !== null && _ref5 !== void 0 ? _ref5 : source;
131
- var handlers = (_ref6 = options === null || options === void 0 ? void 0 : options.handlers) !== null && _ref6 !== void 0 ? _ref6 : [];
155
+ var target = (_options$target = options === null || options === void 0 ? void 0 : options.target) !== null && _options$target !== void 0 ? _options$target : source;
156
+ var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : [];
132
157
  var operationAvailability = DataChecker.check(source, target, selector, handlers, this._operationLoader, this._getDataID);
133
- return getAvailablityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime);
158
+ return getAvailabilityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime, this._queryCacheExpirationTime);
134
159
  };
135
160
 
136
161
  _proto.retain = function retain(operation) {
137
- var _this = this;
162
+ var _this2 = this;
138
163
 
139
164
  var id = operation.request.identifier;
165
+ var disposed = false;
140
166
 
141
167
  var dispose = function dispose() {
142
- // When disposing, instead of immediately decrementing the refCount and
143
- // potentially deleting/collecting the root, move the operation onto
144
- // the release buffer. When the operation is extracted from the release
145
- // buffer, we will determine if it needs to be collected.
146
- _this._releaseBuffer.push(id); // Only when the release buffer is full do we actually:
147
- // - extract the least recent operation in the release buffer
148
- // - attempt to release it and run GC if it's no longer referenced
149
- // (refCount reached 0).
168
+ // Ensure each retain can only dispose once
169
+ if (disposed) {
170
+ return;
171
+ }
172
+
173
+ disposed = true; // For Flow: guard against the entry somehow not existing
150
174
 
175
+ var rootEntry = _this2._roots.get(id);
151
176
 
152
- if (_this._releaseBuffer.length > _this._gcReleaseBufferSize) {
153
- var _id = _this._releaseBuffer.shift();
177
+ if (rootEntry == null) {
178
+ return;
179
+ } // Decrement the ref count: if it becomes zero it is eligible
180
+ // for release.
154
181
 
155
- var _rootEntry = _this._roots.get(_id);
156
182
 
157
- if (_rootEntry == null) {
158
- // If operation has already been fully released, we don't need
159
- // to do anything.
160
- return;
161
- }
183
+ rootEntry.refCount--;
184
+
185
+ if (rootEntry.refCount === 0) {
186
+ var _queryCacheExpirationTime = _this2._queryCacheExpirationTime;
187
+
188
+ var rootEntryIsStale = rootEntry.fetchTime != null && _queryCacheExpirationTime != null && rootEntry.fetchTime <= Date.now() - _queryCacheExpirationTime;
162
189
 
163
- if (_rootEntry.refCount > 0) {
164
- // If the operation is still retained by other callers
165
- // decrement the refCount
166
- _rootEntry.refCount -= 1;
190
+ if (rootEntryIsStale) {
191
+ _this2._roots["delete"](id);
192
+
193
+ _this2.scheduleGC();
167
194
  } else {
168
- // Otherwise fully release the query and run GC.
169
- _this._roots["delete"](_id);
195
+ _this2._releaseBuffer.push(id); // If the release buffer is now over-full, remove the least-recently
196
+ // added entry and schedule a GC. Note that all items in the release
197
+ // buffer have a refCount of 0.
198
+
199
+
200
+ if (_this2._releaseBuffer.length > _this2._gcReleaseBufferSize) {
201
+ var _id = _this2._releaseBuffer.shift();
202
+
203
+ _this2._roots["delete"](_id);
170
204
 
171
- _this._scheduleGC();
205
+ _this2.scheduleGC();
206
+ }
172
207
  }
173
208
  }
174
209
  };
@@ -176,13 +211,22 @@ function () {
176
211
  var rootEntry = this._roots.get(id);
177
212
 
178
213
  if (rootEntry != null) {
179
- // If we've previously retained this operation, inrement the refCount
214
+ if (rootEntry.refCount === 0) {
215
+ // This entry should be in the release buffer, but it no longer belongs
216
+ // there since it's retained. Remove it to maintain the invariant that
217
+ // all release buffer entries have a refCount of 0.
218
+ this._releaseBuffer = this._releaseBuffer.filter(function (_id) {
219
+ return _id !== id;
220
+ });
221
+ } // If we've previously retained this operation, increment the refCount
222
+
223
+
180
224
  rootEntry.refCount += 1;
181
225
  } else {
182
226
  // Otherwise create a new entry for the operation
183
227
  this._roots.set(id, {
184
228
  operation: operation,
185
- refCount: 0,
229
+ refCount: 1,
186
230
  epoch: null,
187
231
  fetchTime: null
188
232
  });
@@ -202,14 +246,22 @@ function () {
202
246
  }
203
247
 
204
248
  return snapshot;
205
- } // This method will return a list of updated owners form the subscriptions
249
+ } // This method will return a list of updated owners from the subscriptions
206
250
  ;
207
251
 
208
252
  _proto.notify = function notify(sourceOperation, invalidateStore) {
209
- var _this2 = this;
253
+ var _this3 = this;
254
+
255
+ var log = this.__log;
210
256
 
211
- // Increment the current write when notifying after executing
257
+ if (log != null) {
258
+ log({
259
+ name: 'store.notify.start'
260
+ });
261
+ } // Increment the current write when notifying after executing
212
262
  // a set of changes to the store.
263
+
264
+
213
265
  this._currentWriteEpoch++;
214
266
 
215
267
  if (invalidateStore === true) {
@@ -218,9 +270,10 @@ function () {
218
270
 
219
271
  var source = this.getSource();
220
272
  var updatedOwners = [];
273
+ var hasUpdatedRecords = !isEmptyObject(this._updatedRecordIDs);
221
274
 
222
275
  this._subscriptions.forEach(function (subscription) {
223
- var owner = _this2._updateSubscription(source, subscription);
276
+ var owner = _this3._updateSubscription(source, subscription, hasUpdatedRecords);
224
277
 
225
278
  if (owner != null) {
226
279
  updatedOwners.push(owner);
@@ -228,9 +281,17 @@ function () {
228
281
  });
229
282
 
230
283
  this._invalidationSubscriptions.forEach(function (subscription) {
231
- _this2._updateInvalidationSubscription(subscription, invalidateStore === true);
284
+ _this3._updateInvalidationSubscription(subscription, invalidateStore === true);
232
285
  });
233
286
 
287
+ if (log != null) {
288
+ log({
289
+ name: 'store.notify.complete',
290
+ updatedRecordIDs: this._updatedRecordIDs,
291
+ invalidatedRecordIDs: this._invalidatedRecordIDs
292
+ });
293
+ }
294
+
234
295
  this._updatedRecordIDs = {};
235
296
 
236
297
  this._invalidatedRecordIDs.clear(); // If a source operation was provided (indicating the operation
@@ -249,10 +310,21 @@ function () {
249
310
  var rootEntry = this._roots.get(id);
250
311
 
251
312
  if (rootEntry != null) {
252
- var _rootEntry$fetchTime;
253
-
254
313
  rootEntry.epoch = this._currentWriteEpoch;
255
- rootEntry.fetchTime = (_rootEntry$fetchTime = rootEntry.fetchTime) !== null && _rootEntry$fetchTime !== void 0 ? _rootEntry$fetchTime : Date.now();
314
+ rootEntry.fetchTime = Date.now();
315
+ } else if (sourceOperation.request.node.params.operationKind === 'query' && this._gcReleaseBufferSize > 0 && this._releaseBuffer.length < this._gcReleaseBufferSize) {
316
+ // The operation isn't retained but there is space in the release buffer:
317
+ // temporarily track this operation in case the data can be reused soon.
318
+ var temporaryRootEntry = {
319
+ operation: sourceOperation,
320
+ refCount: 0,
321
+ epoch: this._currentWriteEpoch,
322
+ fetchTime: Date.now()
323
+ };
324
+
325
+ this._releaseBuffer.push(id);
326
+
327
+ this._roots.set(id, temporaryRootEntry);
256
328
  }
257
329
  }
258
330
 
@@ -266,11 +338,22 @@ function () {
266
338
  updateTargetFromSource(target, source, // We increment the current epoch at the end of the set of updates,
267
339
  // in notify(). Here, we pass what will be the incremented value of
268
340
  // the epoch to use to write to invalidated records.
269
- this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs);
341
+ this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs); // NOTE: log *after* processing the source so that even if a bad log function
342
+ // mutates the source, it doesn't affect Relay processing of it.
343
+
344
+ var log = this.__log;
345
+
346
+ if (log != null) {
347
+ log({
348
+ name: 'store.publish',
349
+ source: source,
350
+ optimistic: target === this._optimisticSource
351
+ });
352
+ }
270
353
  };
271
354
 
272
355
  _proto.subscribe = function subscribe(snapshot, callback) {
273
- var _this3 = this;
356
+ var _this4 = this;
274
357
 
275
358
  var subscription = {
276
359
  backup: null,
@@ -280,7 +363,7 @@ function () {
280
363
  };
281
364
 
282
365
  var dispose = function dispose() {
283
- _this3._subscriptions["delete"](subscription);
366
+ _this4._subscriptions["delete"](subscription);
284
367
  };
285
368
 
286
369
  this._subscriptions.add(subscription);
@@ -291,18 +374,23 @@ function () {
291
374
  };
292
375
 
293
376
  _proto.holdGC = function holdGC() {
294
- var _this4 = this;
377
+ var _this5 = this;
378
+
379
+ if (this._gcRun) {
380
+ this._gcRun = null;
381
+ this._shouldScheduleGC = true;
382
+ }
295
383
 
296
384
  this._gcHoldCounter++;
297
385
 
298
386
  var dispose = function dispose() {
299
- if (_this4._gcHoldCounter > 0) {
300
- _this4._gcHoldCounter--;
387
+ if (_this5._gcHoldCounter > 0) {
388
+ _this5._gcHoldCounter--;
301
389
 
302
- if (_this4._gcHoldCounter === 0 && _this4._shouldScheduleGC) {
303
- _this4._scheduleGC();
390
+ if (_this5._gcHoldCounter === 0 && _this5._shouldScheduleGC) {
391
+ _this5.scheduleGC();
304
392
 
305
- _this4._shouldScheduleGC = false;
393
+ _this5._shouldScheduleGC = false;
306
394
  }
307
395
  }
308
396
  };
@@ -323,12 +411,12 @@ function () {
323
411
  // latest update, or null if it was not affected.
324
412
  ;
325
413
 
326
- _proto._updateSubscription = function _updateSubscription(source, subscription) {
414
+ _proto._updateSubscription = function _updateSubscription(source, subscription, hasUpdatedRecords) {
327
415
  var backup = subscription.backup,
328
416
  callback = subscription.callback,
329
417
  snapshot = subscription.snapshot,
330
418
  stale = subscription.stale;
331
- var hasOverlappingUpdates = hasOverlappingIDs(snapshot.seenRecords, this._updatedRecordIDs);
419
+ var hasOverlappingUpdates = hasUpdatedRecords && hasOverlappingIDs(snapshot.seenRecords, this._updatedRecordIDs);
332
420
 
333
421
  if (!stale && !hasOverlappingUpdates) {
334
422
  return;
@@ -340,7 +428,8 @@ function () {
340
428
  data: nextData,
341
429
  isMissingData: nextSnapshot.isMissingData,
342
430
  seenRecords: nextSnapshot.seenRecords,
343
- selector: nextSnapshot.selector
431
+ selector: nextSnapshot.selector,
432
+ missingRequiredFields: nextSnapshot.missingRequiredFields
344
433
  };
345
434
 
346
435
  if (process.env.NODE_ENV !== "production") {
@@ -357,13 +446,13 @@ function () {
357
446
  };
358
447
 
359
448
  _proto.lookupInvalidationState = function lookupInvalidationState(dataIDs) {
360
- var _this5 = this;
449
+ var _this6 = this;
361
450
 
362
451
  var invalidations = new Map();
363
452
  dataIDs.forEach(function (dataID) {
364
453
  var _RelayModernRecord$ge;
365
454
 
366
- var record = _this5.getSource().get(dataID);
455
+ var record = _this6.getSource().get(dataID);
367
456
 
368
457
  invalidations.set(dataID, (_RelayModernRecord$ge = RelayModernRecord.getInvalidationEpoch(record)) !== null && _RelayModernRecord$ge !== void 0 ? _RelayModernRecord$ge : null);
369
458
  });
@@ -384,12 +473,11 @@ function () {
384
473
  } // Check if the invalidation state for any of the ids has changed.
385
474
 
386
475
 
387
- var _iteratorNormalCompletion = true;
388
- var _didIteratorError = false;
389
- var _iteratorError = undefined;
476
+ var _iterator = _createForOfIteratorHelper(prevInvalidationState.dataIDs),
477
+ _step;
390
478
 
391
479
  try {
392
- for (var _iterator = prevInvalidationState.dataIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
480
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
393
481
  var dataID = _step.value;
394
482
 
395
483
  if (currentInvalidations.get(dataID) !== prevInvalidations.get(dataID)) {
@@ -397,25 +485,16 @@ function () {
397
485
  }
398
486
  }
399
487
  } catch (err) {
400
- _didIteratorError = true;
401
- _iteratorError = err;
488
+ _iterator.e(err);
402
489
  } finally {
403
- try {
404
- if (!_iteratorNormalCompletion && _iterator["return"] != null) {
405
- _iterator["return"]();
406
- }
407
- } finally {
408
- if (_didIteratorError) {
409
- throw _iteratorError;
410
- }
411
- }
490
+ _iterator.f();
412
491
  }
413
492
 
414
493
  return false;
415
494
  };
416
495
 
417
496
  _proto.subscribeToInvalidationState = function subscribeToInvalidationState(invalidationState, callback) {
418
- var _this6 = this;
497
+ var _this7 = this;
419
498
 
420
499
  var subscription = {
421
500
  callback: callback,
@@ -423,7 +502,7 @@ function () {
423
502
  };
424
503
 
425
504
  var dispose = function dispose() {
426
- _this6._invalidationSubscriptions["delete"](subscription);
505
+ _this7._invalidationSubscriptions["delete"](subscription);
427
506
  };
428
507
 
429
508
  this._invalidationSubscriptions.add(subscription);
@@ -434,13 +513,13 @@ function () {
434
513
  };
435
514
 
436
515
  _proto._updateInvalidationSubscription = function _updateInvalidationSubscription(subscription, invalidatedStore) {
437
- var _this7 = this;
516
+ var _this8 = this;
438
517
 
439
518
  var callback = subscription.callback,
440
519
  invalidationState = subscription.invalidationState;
441
520
  var dataIDs = invalidationState.dataIDs;
442
521
  var isSubscribedToInvalidatedIDs = invalidatedStore || dataIDs.some(function (dataID) {
443
- return _this7._invalidatedRecordIDs.has(dataID);
522
+ return _this8._invalidatedRecordIDs.has(dataID);
444
523
  });
445
524
 
446
525
  if (!isSubscribedToInvalidatedIDs) {
@@ -451,9 +530,16 @@ function () {
451
530
  };
452
531
 
453
532
  _proto.snapshot = function snapshot() {
454
- var _this8 = this;
533
+ var _this9 = this;
455
534
 
456
535
  !(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayModernStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
536
+ var log = this.__log;
537
+
538
+ if (log != null) {
539
+ log({
540
+ name: 'store.snapshot'
541
+ });
542
+ }
457
543
 
458
544
  this._subscriptions.forEach(function (subscription) {
459
545
  // Backup occurs after writing a new "final" payload(s) and before (re)applying
@@ -474,20 +560,37 @@ function () {
474
560
  }
475
561
 
476
562
  var snapshot = subscription.snapshot;
477
- var backup = RelayReader.read(_this8.getSource(), snapshot.selector);
563
+ var backup = RelayReader.read(_this9.getSource(), snapshot.selector);
478
564
  var nextData = recycleNodesInto(snapshot.data, backup.data);
479
565
  backup.data = nextData; // backup owns the snapshot and can safely mutate
480
566
 
481
567
  subscription.backup = backup;
482
568
  });
483
569
 
570
+ if (this._gcRun) {
571
+ this._gcRun = null;
572
+ this._shouldScheduleGC = true;
573
+ }
574
+
484
575
  this._optimisticSource = RelayOptimisticRecordSource.create(this.getSource());
485
576
  };
486
577
 
487
578
  _proto.restore = function restore() {
488
579
  !(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;
580
+ var log = this.__log;
581
+
582
+ if (log != null) {
583
+ log({
584
+ name: 'store.restore'
585
+ });
586
+ }
587
+
489
588
  this._optimisticSource = null;
490
589
 
590
+ if (this._shouldScheduleGC) {
591
+ this.scheduleGC();
592
+ }
593
+
491
594
  this._subscriptions.forEach(function (subscription) {
492
595
  var backup = subscription.backup;
493
596
  subscription.backup = null;
@@ -501,7 +604,8 @@ function () {
501
604
  data: subscription.snapshot.data,
502
605
  isMissingData: backup.isMissingData,
503
606
  seenRecords: backup.seenRecords,
504
- selector: backup.selector
607
+ selector: backup.selector,
608
+ missingRequiredFields: backup.missingRequiredFields
505
609
  };
506
610
  } else {
507
611
  subscription.stale = true;
@@ -509,57 +613,90 @@ function () {
509
613
  });
510
614
  };
511
615
 
512
- _proto._scheduleGC = function _scheduleGC() {
513
- var _this9 = this;
514
-
616
+ _proto.scheduleGC = function scheduleGC() {
515
617
  if (this._gcHoldCounter > 0) {
516
618
  this._shouldScheduleGC = true;
517
619
  return;
518
620
  }
519
621
 
520
- if (this._hasScheduledGC) {
622
+ if (this._gcRun) {
521
623
  return;
522
624
  }
523
625
 
524
- this._hasScheduledGC = true;
525
-
526
- this._gcScheduler(function () {
527
- _this9.__gc();
626
+ this._gcRun = this._collect();
528
627
 
529
- _this9._hasScheduledGC = false;
530
- });
531
- };
628
+ this._gcScheduler(this._gcStep);
629
+ }
630
+ /**
631
+ * Run a full GC synchronously.
632
+ */
633
+ ;
532
634
 
533
635
  _proto.__gc = function __gc() {
534
- var _this10 = this;
535
-
536
636
  // Don't run GC while there are optimistic updates applied
537
637
  if (this._optimisticSource != null) {
538
638
  return;
539
639
  }
540
640
 
541
- var references = new Set(); // Mark all records that are traversable from a root
641
+ var gcRun = this._collect();
542
642
 
543
- this._roots.forEach(function (_ref7) {
544
- var operation = _ref7.operation;
545
- var selector = operation.root;
546
- RelayReferenceMarker.mark(_this10._recordSource, selector, references, _this10._operationLoader);
547
- });
643
+ while (!gcRun.next().done) {}
644
+ };
548
645
 
549
- if (references.size === 0) {
550
- // Short-circuit if *nothing* is referenced
551
- this._recordSource.clear();
552
- } else {
553
- // Evict any unreferenced nodes
554
- var storeIDs = this._recordSource.getRecordIDs();
646
+ _proto._collect = function* _collect() {
647
+ /* eslint-disable no-labels */
648
+ top: while (true) {
649
+ var startEpoch = this._currentWriteEpoch;
650
+ var references = new Set(); // Mark all records that are traversable from a root
555
651
 
556
- for (var ii = 0; ii < storeIDs.length; ii++) {
557
- var dataID = storeIDs[ii];
652
+ var _iterator2 = _createForOfIteratorHelper(this._roots.values()),
653
+ _step2;
654
+
655
+ try {
656
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
657
+ var operation = _step2.value.operation;
658
+ var selector = operation.root;
659
+ RelayReferenceMarker.mark(this._recordSource, selector, references, this._operationLoader); // Yield for other work after each operation
660
+
661
+ yield; // If the store was updated, restart
662
+
663
+ if (startEpoch !== this._currentWriteEpoch) {
664
+ continue top;
665
+ }
666
+ }
667
+ } catch (err) {
668
+ _iterator2.e(err);
669
+ } finally {
670
+ _iterator2.f();
671
+ }
672
+
673
+ var log = this.__log;
674
+
675
+ if (log != null) {
676
+ log({
677
+ name: 'store.gc',
678
+ references: references
679
+ });
680
+ } // Sweep records without references
681
+
682
+
683
+ if (references.size === 0) {
684
+ // Short-circuit if *nothing* is referenced
685
+ this._recordSource.clear();
686
+ } else {
687
+ // Evict any unreferenced nodes
688
+ var storeIDs = this._recordSource.getRecordIDs();
689
+
690
+ for (var ii = 0; ii < storeIDs.length; ii++) {
691
+ var dataID = storeIDs[ii];
558
692
 
559
- if (!references.has(dataID)) {
560
- this._recordSource.remove(dataID);
693
+ if (!references.has(dataID)) {
694
+ this._recordSource.remove(dataID);
695
+ }
561
696
  }
562
697
  }
698
+
699
+ return;
563
700
  }
564
701
  };
565
702
 
@@ -575,7 +712,7 @@ function initializeRecordSource(target) {
575
712
  /**
576
713
  * Updates the target with information from source, also updating a mapping of
577
714
  * which records in the target were changed as a result.
578
- * Additionally, will marc records as invalidated at the current write epoch
715
+ * Additionally, will mark records as invalidated at the current write epoch
579
716
  * given the set of record ids marked as stale in this update.
580
717
  */
581
718
 
@@ -637,7 +774,11 @@ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForI
637
774
  }
638
775
 
639
776
  if (sourceRecord && targetRecord) {
640
- var nextRecord = RelayModernRecord.update(targetRecord, sourceRecord);
777
+ // ReactFlightClientResponses are lazy and only materialize when readRoot
778
+ // is called when we read the field, so if the record is a Flight field
779
+ // we always use the new record's data regardless of whether
780
+ // it actually changed. Let React take care of reconciliation instead.
781
+ var nextRecord = RelayModernRecord.getType(targetRecord) === RelayStoreReactFlightUtils.REACT_FLIGHT_TYPE_NAME ? sourceRecord : RelayModernRecord.update(targetRecord, sourceRecord);
641
782
 
642
783
  if (nextRecord !== targetRecord) {
643
784
  // Prevent mutation of a record from outside the store.
@@ -664,7 +805,7 @@ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForI
664
805
  /**
665
806
  * Returns an OperationAvailability given the Availability returned
666
807
  * by checking an operation, and when that operation was last written to the store.
667
- * Specifically, the provided Availablity of a an operation will contain the
808
+ * Specifically, the provided Availability of an operation will contain the
668
809
  * value of when a record referenced by the operation was most recently
669
810
  * invalidated; given that value, and given when this operation was last
670
811
  * written to the store, this function will return the overall
@@ -672,11 +813,9 @@ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForI
672
813
  */
673
814
 
674
815
 
675
- function getAvailablityStatus(opearionAvailability, operationLastWrittenAt, operationFetchTime) {
676
- var _operationFetchTime;
677
-
678
- var mostRecentlyInvalidatedAt = opearionAvailability.mostRecentlyInvalidatedAt,
679
- status = opearionAvailability.status;
816
+ function getAvailabilityStatus(operationAvailability, operationLastWrittenAt, operationFetchTime, queryCacheExpirationTime) {
817
+ var mostRecentlyInvalidatedAt = operationAvailability.mostRecentlyInvalidatedAt,
818
+ status = operationAvailability.status;
680
819
 
681
820
  if (typeof mostRecentlyInvalidatedAt === 'number') {
682
821
  // If some record referenced by this operation is stale, then the operation itself is stale
@@ -687,22 +826,33 @@ function getAvailablityStatus(opearionAvailability, operationLastWrittenAt, oper
687
826
  status: 'stale'
688
827
  };
689
828
  }
829
+ }
830
+
831
+ if (status === 'missing') {
832
+ return {
833
+ status: 'missing'
834
+ };
835
+ }
836
+
837
+ if (operationFetchTime != null && queryCacheExpirationTime != null) {
838
+ var isStale = operationFetchTime <= Date.now() - queryCacheExpirationTime;
839
+
840
+ if (isStale) {
841
+ return {
842
+ status: 'stale'
843
+ };
844
+ }
690
845
  } // There were no invalidations of any reachable records *or* the operation is known to have
691
846
  // been fetched after the most recent record invalidation.
692
847
 
693
848
 
694
- return status === 'missing' ? {
695
- status: 'missing'
696
- } : {
849
+ return {
697
850
  status: 'available',
698
- fetchTime: (_operationFetchTime = operationFetchTime) !== null && _operationFetchTime !== void 0 ? _operationFetchTime : null
851
+ fetchTime: operationFetchTime !== null && operationFetchTime !== void 0 ? operationFetchTime : null
699
852
  };
700
853
  }
701
854
 
702
855
  RelayProfiler.instrumentMethods(RelayModernStore.prototype, {
703
- lookup: 'RelayModernStore.prototype.lookup',
704
- notify: 'RelayModernStore.prototype.notify',
705
- publish: 'RelayModernStore.prototype.publish',
706
- __gc: 'RelayModernStore.prototype.__gc'
856
+ lookup: 'RelayModernStore.prototype.lookup'
707
857
  });
708
858
  module.exports = RelayModernStore;