relay-runtime 13.0.3 → 13.2.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 (50) hide show
  1. package/index.js +1 -1
  2. package/index.js.flow +4 -2
  3. package/lib/index.js +3 -3
  4. package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +3 -4
  5. package/lib/query/GraphQLTag.js +13 -0
  6. package/lib/query/fetchQuery.js +2 -5
  7. package/lib/store/RelayModernEnvironment.js +3 -3
  8. package/lib/store/RelayModernFragmentSpecResolver.js +6 -6
  9. package/lib/store/RelayModernSelector.js +16 -2
  10. package/lib/store/RelayReader.js +71 -6
  11. package/lib/store/RelayStoreSubscriptions.js +4 -2
  12. package/lib/store/RelayStoreUtils.js +1 -0
  13. package/lib/store/ResolverCache.js +25 -13
  14. package/lib/store/experimental-live-resolvers/LiveResolverCache.js +316 -0
  15. package/lib/store/experimental-live-resolvers/LiveResolverStore.js +787 -0
  16. package/lib/store/hasOverlappingIDs.js +1 -1
  17. package/lib/util/RelayConcreteNode.js +2 -0
  18. package/lib/util/RelayFeatureFlags.js +0 -3
  19. package/lib/util/handlePotentialSnapshotErrors.js +73 -0
  20. package/mutations/RelayRecordSourceProxy.js.flow +7 -3
  21. package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -3
  22. package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +14 -12
  23. package/network/RelayNetworkTypes.js.flow +1 -1
  24. package/package.json +1 -1
  25. package/query/GraphQLTag.js.flow +38 -3
  26. package/query/fetchQuery.js.flow +6 -4
  27. package/relay-runtime.js +2 -2
  28. package/relay-runtime.min.js +2 -2
  29. package/store/RelayModernEnvironment.js.flow +3 -3
  30. package/store/RelayModernFragmentSpecResolver.js.flow +11 -7
  31. package/store/RelayModernSelector.js.flow +32 -8
  32. package/store/RelayReader.js.flow +65 -23
  33. package/store/RelayResponseNormalizer.js.flow +1 -1
  34. package/store/RelayStoreSubscriptions.js.flow +2 -0
  35. package/store/RelayStoreTypes.js.flow +22 -8
  36. package/store/RelayStoreUtils.js.flow +2 -1
  37. package/store/ResolverCache.js.flow +45 -10
  38. package/store/defaultGetDataID.js.flow +1 -1
  39. package/store/experimental-live-resolvers/LiveResolverCache.js.flow +410 -0
  40. package/store/experimental-live-resolvers/LiveResolverStore.js.flow +822 -0
  41. package/store/hasOverlappingIDs.js.flow +1 -1
  42. package/util/ReaderNode.js.flow +17 -1
  43. package/util/RelayConcreteNode.js.flow +9 -1
  44. package/util/RelayFeatureFlags.js.flow +0 -6
  45. package/util/RelayRuntimeTypes.js.flow +20 -1
  46. package/util/deepFreeze.js.flow +1 -1
  47. package/util/handlePotentialSnapshotErrors.js.flow +63 -0
  48. package/util/isEmptyObject.js.flow +1 -1
  49. package/lib/util/reportMissingRequiredFields.js +0 -48
  50. package/util/reportMissingRequiredFields.js.flow +0 -51
@@ -0,0 +1,787 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ * @format
9
+ */
10
+ // flowlint ambiguous-object-type:error
11
+ 'use strict';
12
+
13
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
+
15
+ var _createForOfIteratorHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelper"));
16
+
17
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
18
+
19
+ var _require = require('../../multi-actor-environment/ActorIdentifier'),
20
+ INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE = _require.INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE,
21
+ assertInternalActorIndentifier = _require.assertInternalActorIndentifier;
22
+
23
+ var deepFreeze = require('../../util/deepFreeze');
24
+
25
+ var RelayFeatureFlags = require('../../util/RelayFeatureFlags');
26
+
27
+ var resolveImmediate = require('../../util/resolveImmediate');
28
+
29
+ var DataChecker = require('../DataChecker');
30
+
31
+ var defaultGetDataID = require('../defaultGetDataID');
32
+
33
+ var RelayModernRecord = require('../RelayModernRecord');
34
+
35
+ var RelayOptimisticRecordSource = require('../RelayOptimisticRecordSource');
36
+
37
+ var RelayReader = require('../RelayReader');
38
+
39
+ var RelayReferenceMarker = require('../RelayReferenceMarker');
40
+
41
+ var RelayStoreReactFlightUtils = require('../RelayStoreReactFlightUtils');
42
+
43
+ var RelayStoreSubscriptions = require('../RelayStoreSubscriptions');
44
+
45
+ var RelayStoreUtils = require('../RelayStoreUtils');
46
+
47
+ var _require2 = require('../RelayStoreUtils'),
48
+ ROOT_ID = _require2.ROOT_ID,
49
+ ROOT_TYPE = _require2.ROOT_TYPE;
50
+
51
+ var _require3 = require('./LiveResolverCache'),
52
+ LiveResolverCache = _require3.LiveResolverCache;
53
+
54
+ var invariant = require('invariant');
55
+
56
+ var DEFAULT_RELEASE_BUFFER_SIZE = 10;
57
+ /**
58
+ * @public
59
+ *
60
+ * Experimental fork of RelayModernStore
61
+ */
62
+
63
+ var LiveResolverStore = /*#__PURE__*/function () {
64
+ function LiveResolverStore(source, options) {
65
+ var _this = this;
66
+
67
+ var _options$gcReleaseBuf, _options$gcScheduler, _options$getDataID, _options$log, _options$operationLoa;
68
+
69
+ (0, _defineProperty2["default"])(this, "_gcStep", function () {
70
+ if (_this._gcRun) {
71
+ if (_this._gcRun.next().done) {
72
+ _this._gcRun = null;
73
+ } else {
74
+ _this._gcScheduler(_this._gcStep);
75
+ }
76
+ }
77
+ });
78
+
79
+ // Prevent mutation of a record from outside the store.
80
+ if (process.env.NODE_ENV !== "production") {
81
+ var storeIDs = source.getRecordIDs();
82
+
83
+ for (var ii = 0; ii < storeIDs.length; ii++) {
84
+ var record = source.get(storeIDs[ii]);
85
+
86
+ if (record) {
87
+ RelayModernRecord.freeze(record);
88
+ }
89
+ }
90
+ }
91
+
92
+ this._currentWriteEpoch = 0;
93
+ this._gcHoldCounter = 0;
94
+ this._gcReleaseBufferSize = (_options$gcReleaseBuf = options === null || options === void 0 ? void 0 : options.gcReleaseBufferSize) !== null && _options$gcReleaseBuf !== void 0 ? _options$gcReleaseBuf : DEFAULT_RELEASE_BUFFER_SIZE;
95
+ this._gcRun = null;
96
+ this._gcScheduler = (_options$gcScheduler = options === null || options === void 0 ? void 0 : options.gcScheduler) !== null && _options$gcScheduler !== void 0 ? _options$gcScheduler : resolveImmediate;
97
+ this._getDataID = (_options$getDataID = options === null || options === void 0 ? void 0 : options.getDataID) !== null && _options$getDataID !== void 0 ? _options$getDataID : defaultGetDataID;
98
+ this._globalInvalidationEpoch = null;
99
+ this._invalidationSubscriptions = new Set();
100
+ this._invalidatedRecordIDs = new Set();
101
+ this.__log = (_options$log = options === null || options === void 0 ? void 0 : options.log) !== null && _options$log !== void 0 ? _options$log : null;
102
+ this._queryCacheExpirationTime = options === null || options === void 0 ? void 0 : options.queryCacheExpirationTime;
103
+ this._operationLoader = (_options$operationLoa = options === null || options === void 0 ? void 0 : options.operationLoader) !== null && _options$operationLoa !== void 0 ? _options$operationLoa : null;
104
+ this._optimisticSource = null;
105
+ this._recordSource = source;
106
+ this._releaseBuffer = [];
107
+ this._roots = new Map();
108
+ this._shouldScheduleGC = false;
109
+ this._resolverCache = new LiveResolverCache(function () {
110
+ return _this._getMutableRecordSource();
111
+ }, this);
112
+ this._storeSubscriptions = new RelayStoreSubscriptions(options === null || options === void 0 ? void 0 : options.log, this._resolverCache);
113
+ this._updatedRecordIDs = new Set();
114
+ this._shouldProcessClientComponents = options === null || options === void 0 ? void 0 : options.shouldProcessClientComponents;
115
+ initializeRecordSource(this._recordSource);
116
+ }
117
+
118
+ var _proto = LiveResolverStore.prototype;
119
+
120
+ _proto.getSource = function getSource() {
121
+ var _this$_optimisticSour;
122
+
123
+ return (_this$_optimisticSour = this._optimisticSource) !== null && _this$_optimisticSour !== void 0 ? _this$_optimisticSour : this._recordSource;
124
+ };
125
+
126
+ _proto._getMutableRecordSource = function _getMutableRecordSource() {
127
+ var _this$_optimisticSour2;
128
+
129
+ return (_this$_optimisticSour2 = this._optimisticSource) !== null && _this$_optimisticSour2 !== void 0 ? _this$_optimisticSour2 : this._recordSource;
130
+ };
131
+
132
+ _proto.check = function check(operation, options) {
133
+ var _options$handlers, _options$getSourceFor, _options$getTargetFor, _options$defaultActor;
134
+
135
+ var selector = operation.root;
136
+
137
+ var source = this._getMutableRecordSource();
138
+
139
+ var globalInvalidationEpoch = this._globalInvalidationEpoch;
140
+
141
+ var rootEntry = this._roots.get(operation.request.identifier);
142
+
143
+ var operationLastWrittenAt = rootEntry != null ? rootEntry.epoch : null; // Check if store has been globally invalidated
144
+
145
+ if (globalInvalidationEpoch != null) {
146
+ // If so, check if the operation we're checking was last written
147
+ // before or after invalidation occurred.
148
+ if (operationLastWrittenAt == null || operationLastWrittenAt <= globalInvalidationEpoch) {
149
+ // If the operation was written /before/ global invalidation occurred,
150
+ // or if this operation has never been written to the store before,
151
+ // we will consider the data for this operation to be stale
152
+ // (i.e. not resolvable from the store).
153
+ return {
154
+ status: 'stale'
155
+ };
156
+ }
157
+ }
158
+
159
+ var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : [];
160
+ var getSourceForActor = (_options$getSourceFor = options === null || options === void 0 ? void 0 : options.getSourceForActor) !== null && _options$getSourceFor !== void 0 ? _options$getSourceFor : function (actorIdentifier) {
161
+ assertInternalActorIndentifier(actorIdentifier);
162
+ return source;
163
+ };
164
+ var getTargetForActor = (_options$getTargetFor = options === null || options === void 0 ? void 0 : options.getTargetForActor) !== null && _options$getTargetFor !== void 0 ? _options$getTargetFor : function (actorIdentifier) {
165
+ assertInternalActorIndentifier(actorIdentifier);
166
+ return source;
167
+ };
168
+ var operationAvailability = DataChecker.check(getSourceForActor, getTargetForActor, (_options$defaultActor = options === null || options === void 0 ? void 0 : options.defaultActorIdentifier) !== null && _options$defaultActor !== void 0 ? _options$defaultActor : INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, selector, handlers, this._operationLoader, this._getDataID, this._shouldProcessClientComponents);
169
+ return getAvailabilityStatus(operationAvailability, operationLastWrittenAt, rootEntry === null || rootEntry === void 0 ? void 0 : rootEntry.fetchTime, this._queryCacheExpirationTime);
170
+ };
171
+
172
+ _proto.retain = function retain(operation) {
173
+ var _this2 = this;
174
+
175
+ var id = operation.request.identifier;
176
+ var disposed = false;
177
+
178
+ var dispose = function dispose() {
179
+ // Ensure each retain can only dispose once
180
+ if (disposed) {
181
+ return;
182
+ }
183
+
184
+ disposed = true; // For Flow: guard against the entry somehow not existing
185
+
186
+ var rootEntry = _this2._roots.get(id);
187
+
188
+ if (rootEntry == null) {
189
+ return;
190
+ } // Decrement the ref count: if it becomes zero it is eligible
191
+ // for release.
192
+
193
+
194
+ rootEntry.refCount--;
195
+
196
+ if (rootEntry.refCount === 0) {
197
+ var _queryCacheExpirationTime = _this2._queryCacheExpirationTime;
198
+
199
+ var rootEntryIsStale = rootEntry.fetchTime != null && _queryCacheExpirationTime != null && rootEntry.fetchTime <= Date.now() - _queryCacheExpirationTime;
200
+
201
+ if (rootEntryIsStale) {
202
+ _this2._roots["delete"](id);
203
+
204
+ _this2.scheduleGC();
205
+ } else {
206
+ _this2._releaseBuffer.push(id); // If the release buffer is now over-full, remove the least-recently
207
+ // added entry and schedule a GC. Note that all items in the release
208
+ // buffer have a refCount of 0.
209
+
210
+
211
+ if (_this2._releaseBuffer.length > _this2._gcReleaseBufferSize) {
212
+ var _id = _this2._releaseBuffer.shift();
213
+
214
+ _this2._roots["delete"](_id);
215
+
216
+ _this2.scheduleGC();
217
+ }
218
+ }
219
+ }
220
+ };
221
+
222
+ var rootEntry = this._roots.get(id);
223
+
224
+ if (rootEntry != null) {
225
+ if (rootEntry.refCount === 0) {
226
+ // This entry should be in the release buffer, but it no longer belongs
227
+ // there since it's retained. Remove it to maintain the invariant that
228
+ // all release buffer entries have a refCount of 0.
229
+ this._releaseBuffer = this._releaseBuffer.filter(function (_id) {
230
+ return _id !== id;
231
+ });
232
+ } // If we've previously retained this operation, increment the refCount
233
+
234
+
235
+ rootEntry.refCount += 1;
236
+ } else {
237
+ // Otherwise create a new entry for the operation
238
+ this._roots.set(id, {
239
+ operation: operation,
240
+ refCount: 1,
241
+ epoch: null,
242
+ fetchTime: null
243
+ });
244
+ }
245
+
246
+ return {
247
+ dispose: dispose
248
+ };
249
+ };
250
+
251
+ _proto.lookup = function lookup(selector) {
252
+ var source = this.getSource();
253
+ var snapshot = RelayReader.read(source, selector, this._resolverCache);
254
+
255
+ if (process.env.NODE_ENV !== "production") {
256
+ deepFreeze(snapshot);
257
+ }
258
+
259
+ return snapshot;
260
+ } // This method will return a list of updated owners from the subscriptions
261
+ ;
262
+
263
+ _proto.notify = function notify(sourceOperation, invalidateStore) {
264
+ var _this3 = this;
265
+
266
+ var log = this.__log;
267
+
268
+ if (log != null) {
269
+ log({
270
+ name: 'store.notify.start',
271
+ sourceOperation: sourceOperation
272
+ });
273
+ } // Increment the current write when notifying after executing
274
+ // a set of changes to the store.
275
+
276
+
277
+ this._currentWriteEpoch++;
278
+
279
+ if (invalidateStore === true) {
280
+ this._globalInvalidationEpoch = this._currentWriteEpoch;
281
+ }
282
+
283
+ if (RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
284
+ // When a record is updated, we need to also handle records that depend on it,
285
+ // specifically Relay Resolver result records containing results based on the
286
+ // updated records. This both adds to updatedRecordIDs and invalidates any
287
+ // cached data as needed.
288
+ this._resolverCache.invalidateDataIDs(this._updatedRecordIDs);
289
+ }
290
+
291
+ var source = this.getSource();
292
+ var updatedOwners = [];
293
+
294
+ this._storeSubscriptions.updateSubscriptions(source, this._updatedRecordIDs, updatedOwners, sourceOperation);
295
+
296
+ this._invalidationSubscriptions.forEach(function (subscription) {
297
+ _this3._updateInvalidationSubscription(subscription, invalidateStore === true);
298
+ });
299
+
300
+ if (log != null) {
301
+ log({
302
+ name: 'store.notify.complete',
303
+ sourceOperation: sourceOperation,
304
+ updatedRecordIDs: this._updatedRecordIDs,
305
+ invalidatedRecordIDs: this._invalidatedRecordIDs
306
+ });
307
+ }
308
+
309
+ this._updatedRecordIDs.clear();
310
+
311
+ this._invalidatedRecordIDs.clear(); // If a source operation was provided (indicating the operation
312
+ // that produced this update to the store), record the current epoch
313
+ // at which this operation was written.
314
+
315
+
316
+ if (sourceOperation != null) {
317
+ // We only track the epoch at which the operation was written if
318
+ // it was previously retained, to keep the size of our operation
319
+ // epoch map bounded. If a query wasn't retained, we assume it can
320
+ // may be deleted at any moment and thus is not relevant for us to track
321
+ // for the purposes of invalidation.
322
+ var id = sourceOperation.request.identifier;
323
+
324
+ var rootEntry = this._roots.get(id);
325
+
326
+ if (rootEntry != null) {
327
+ rootEntry.epoch = this._currentWriteEpoch;
328
+ rootEntry.fetchTime = Date.now();
329
+ } else if (sourceOperation.request.node.params.operationKind === 'query' && this._gcReleaseBufferSize > 0 && this._releaseBuffer.length < this._gcReleaseBufferSize) {
330
+ // The operation isn't retained but there is space in the release buffer:
331
+ // temporarily track this operation in case the data can be reused soon.
332
+ var temporaryRootEntry = {
333
+ operation: sourceOperation,
334
+ refCount: 0,
335
+ epoch: this._currentWriteEpoch,
336
+ fetchTime: Date.now()
337
+ };
338
+
339
+ this._releaseBuffer.push(id);
340
+
341
+ this._roots.set(id, temporaryRootEntry);
342
+ }
343
+ }
344
+
345
+ return updatedOwners;
346
+ };
347
+
348
+ _proto.publish = function publish(source, idsMarkedForInvalidation) {
349
+ var target = this._getMutableRecordSource();
350
+
351
+ updateTargetFromSource(target, source, // We increment the current epoch at the end of the set of updates,
352
+ // in notify(). Here, we pass what will be the incremented value of
353
+ // the epoch to use to write to invalidated records.
354
+ this._currentWriteEpoch + 1, idsMarkedForInvalidation, this._updatedRecordIDs, this._invalidatedRecordIDs); // NOTE: log *after* processing the source so that even if a bad log function
355
+ // mutates the source, it doesn't affect Relay processing of it.
356
+
357
+ var log = this.__log;
358
+
359
+ if (log != null) {
360
+ log({
361
+ name: 'store.publish',
362
+ source: source,
363
+ optimistic: target === this._optimisticSource
364
+ });
365
+ }
366
+ };
367
+
368
+ _proto.subscribe = function subscribe(snapshot, callback) {
369
+ return this._storeSubscriptions.subscribe(snapshot, callback);
370
+ };
371
+
372
+ _proto.holdGC = function holdGC() {
373
+ var _this4 = this;
374
+
375
+ if (this._gcRun) {
376
+ this._gcRun = null;
377
+ this._shouldScheduleGC = true;
378
+ }
379
+
380
+ this._gcHoldCounter++;
381
+
382
+ var dispose = function dispose() {
383
+ if (_this4._gcHoldCounter > 0) {
384
+ _this4._gcHoldCounter--;
385
+
386
+ if (_this4._gcHoldCounter === 0 && _this4._shouldScheduleGC) {
387
+ _this4.scheduleGC();
388
+
389
+ _this4._shouldScheduleGC = false;
390
+ }
391
+ }
392
+ };
393
+
394
+ return {
395
+ dispose: dispose
396
+ };
397
+ };
398
+
399
+ _proto.toJSON = function toJSON() {
400
+ return 'LiveResolverStore()';
401
+ };
402
+
403
+ _proto.getEpoch = function getEpoch() {
404
+ return this._currentWriteEpoch;
405
+ } // Internal API
406
+ ;
407
+
408
+ _proto.__getUpdatedRecordIDs = function __getUpdatedRecordIDs() {
409
+ return this._updatedRecordIDs;
410
+ } // HACK
411
+ // This should return an InvalidationState, but that's an opaque type that only the
412
+ // real RelayModernStore can create. For now we just use any.
413
+ // $FlowFixMe
414
+ ;
415
+
416
+ _proto.lookupInvalidationState = function lookupInvalidationState(dataIDs) {
417
+ var _this5 = this;
418
+
419
+ var invalidations = new Map();
420
+ dataIDs.forEach(function (dataID) {
421
+ var _RelayModernRecord$ge;
422
+
423
+ var record = _this5.getSource().get(dataID);
424
+
425
+ invalidations.set(dataID, (_RelayModernRecord$ge = RelayModernRecord.getInvalidationEpoch(record)) !== null && _RelayModernRecord$ge !== void 0 ? _RelayModernRecord$ge : null);
426
+ });
427
+ invalidations.set('global', this._globalInvalidationEpoch);
428
+ var invalidationState = {
429
+ dataIDs: dataIDs,
430
+ invalidations: invalidations
431
+ };
432
+ return invalidationState;
433
+ } // HACK
434
+ // This should accept an InvalidationState, but that's an opaque type that only the
435
+ // real RelayModernStore can create. For now we just use any.
436
+ // $FlowFixMe
437
+ ;
438
+
439
+ _proto.checkInvalidationState = function checkInvalidationState(prevInvalidationState) {
440
+ var latestInvalidationState = this.lookupInvalidationState(prevInvalidationState.dataIDs);
441
+ var currentInvalidations = latestInvalidationState.invalidations;
442
+ var prevInvalidations = prevInvalidationState.invalidations; // Check if global invalidation has changed
443
+
444
+ if (currentInvalidations.get('global') !== prevInvalidations.get('global')) {
445
+ return true;
446
+ } // Check if the invalidation state for any of the ids has changed.
447
+
448
+
449
+ var _iterator = (0, _createForOfIteratorHelper2["default"])(prevInvalidationState.dataIDs),
450
+ _step;
451
+
452
+ try {
453
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
454
+ var dataID = _step.value;
455
+
456
+ if (currentInvalidations.get(dataID) !== prevInvalidations.get(dataID)) {
457
+ return true;
458
+ }
459
+ }
460
+ } catch (err) {
461
+ _iterator.e(err);
462
+ } finally {
463
+ _iterator.f();
464
+ }
465
+
466
+ return false;
467
+ } // HACK
468
+ // This should accept an InvalidationState, but that's an opaque type that only the
469
+ // real RelayModernStore can create. For now we just use any.
470
+ ;
471
+
472
+ _proto.subscribeToInvalidationState = function subscribeToInvalidationState( // $FlowFixMe
473
+ invalidationState, callback) {
474
+ var _this6 = this;
475
+
476
+ var subscription = {
477
+ callback: callback,
478
+ invalidationState: invalidationState
479
+ };
480
+
481
+ var dispose = function dispose() {
482
+ _this6._invalidationSubscriptions["delete"](subscription);
483
+ };
484
+
485
+ this._invalidationSubscriptions.add(subscription);
486
+
487
+ return {
488
+ dispose: dispose
489
+ };
490
+ };
491
+
492
+ _proto._updateInvalidationSubscription = function _updateInvalidationSubscription(subscription, invalidatedStore) {
493
+ var _this7 = this;
494
+
495
+ var callback = subscription.callback,
496
+ invalidationState = subscription.invalidationState;
497
+ var dataIDs = invalidationState.dataIDs;
498
+ var isSubscribedToInvalidatedIDs = invalidatedStore || dataIDs.some(function (dataID) {
499
+ return _this7._invalidatedRecordIDs.has(dataID);
500
+ });
501
+
502
+ if (!isSubscribedToInvalidatedIDs) {
503
+ return;
504
+ }
505
+
506
+ callback();
507
+ };
508
+
509
+ _proto.snapshot = function snapshot() {
510
+ !(this._optimisticSource == null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'LiveResolverStore: Unexpected call to snapshot() while a previous ' + 'snapshot exists.') : invariant(false) : void 0;
511
+ var log = this.__log;
512
+
513
+ if (log != null) {
514
+ log({
515
+ name: 'store.snapshot'
516
+ });
517
+ }
518
+
519
+ this._storeSubscriptions.snapshotSubscriptions(this.getSource());
520
+
521
+ if (this._gcRun) {
522
+ this._gcRun = null;
523
+ this._shouldScheduleGC = true;
524
+ }
525
+
526
+ this._optimisticSource = RelayOptimisticRecordSource.create(this.getSource());
527
+ };
528
+
529
+ _proto.restore = function restore() {
530
+ !(this._optimisticSource != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'LiveResolverStore: Unexpected call to restore(), expected a snapshot ' + 'to exist (make sure to call snapshot()).') : invariant(false) : void 0;
531
+ var log = this.__log;
532
+
533
+ if (log != null) {
534
+ log({
535
+ name: 'store.restore'
536
+ });
537
+ }
538
+
539
+ this._optimisticSource = null;
540
+
541
+ if (this._shouldScheduleGC) {
542
+ this.scheduleGC();
543
+ }
544
+
545
+ this._storeSubscriptions.restoreSubscriptions();
546
+ };
547
+
548
+ _proto.scheduleGC = function scheduleGC() {
549
+ if (this._gcHoldCounter > 0) {
550
+ this._shouldScheduleGC = true;
551
+ return;
552
+ }
553
+
554
+ if (this._gcRun) {
555
+ return;
556
+ }
557
+
558
+ this._gcRun = this._collect();
559
+
560
+ this._gcScheduler(this._gcStep);
561
+ }
562
+ /**
563
+ * Run a full GC synchronously.
564
+ */
565
+ ;
566
+
567
+ _proto.__gc = function __gc() {
568
+ // Don't run GC while there are optimistic updates applied
569
+ if (this._optimisticSource != null) {
570
+ return;
571
+ }
572
+
573
+ var gcRun = this._collect();
574
+
575
+ while (!gcRun.next().done) {}
576
+ };
577
+
578
+ _proto._collect = function* _collect() {
579
+ /* eslint-disable no-labels */
580
+ top: while (true) {
581
+ var startEpoch = this._currentWriteEpoch;
582
+ var references = new Set(); // Mark all records that are traversable from a root
583
+
584
+ var _iterator2 = (0, _createForOfIteratorHelper2["default"])(this._roots.values()),
585
+ _step2;
586
+
587
+ try {
588
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
589
+ var operation = _step2.value.operation;
590
+ var selector = operation.root;
591
+ RelayReferenceMarker.mark(this._recordSource, selector, references, this._operationLoader, this._shouldProcessClientComponents); // Yield for other work after each operation
592
+
593
+ yield; // If the store was updated, restart
594
+
595
+ if (startEpoch !== this._currentWriteEpoch) {
596
+ continue top;
597
+ }
598
+ }
599
+ } catch (err) {
600
+ _iterator2.e(err);
601
+ } finally {
602
+ _iterator2.f();
603
+ }
604
+
605
+ var log = this.__log;
606
+
607
+ if (log != null) {
608
+ log({
609
+ name: 'store.gc',
610
+ references: references
611
+ });
612
+ } // Sweep records without references
613
+
614
+
615
+ if (references.size === 0) {
616
+ // Short-circuit if *nothing* is referenced
617
+ this._recordSource.clear();
618
+ } else {
619
+ // Evict any unreferenced nodes
620
+ var storeIDs = this._recordSource.getRecordIDs();
621
+
622
+ for (var ii = 0; ii < storeIDs.length; ii++) {
623
+ var dataID = storeIDs[ii];
624
+
625
+ if (!references.has(dataID)) {
626
+ this._recordSource.remove(dataID);
627
+ }
628
+ }
629
+ }
630
+
631
+ return;
632
+ }
633
+ };
634
+
635
+ return LiveResolverStore;
636
+ }();
637
+
638
+ function initializeRecordSource(target) {
639
+ if (!target.has(ROOT_ID)) {
640
+ var rootRecord = RelayModernRecord.create(ROOT_ID, ROOT_TYPE);
641
+ target.set(ROOT_ID, rootRecord);
642
+ }
643
+ }
644
+ /**
645
+ * Updates the target with information from source, also updating a mapping of
646
+ * which records in the target were changed as a result.
647
+ * Additionally, will mark records as invalidated at the current write epoch
648
+ * given the set of record ids marked as stale in this update.
649
+ */
650
+
651
+
652
+ function updateTargetFromSource(target, source, currentWriteEpoch, idsMarkedForInvalidation, updatedRecordIDs, invalidatedRecordIDs) {
653
+ // First, update any records that were marked for invalidation.
654
+ // For each provided dataID that was invalidated, we write the
655
+ // INVALIDATED_AT_KEY on the record, indicating
656
+ // the epoch at which the record was invalidated.
657
+ if (idsMarkedForInvalidation) {
658
+ idsMarkedForInvalidation.forEach(function (dataID) {
659
+ var targetRecord = target.get(dataID);
660
+ var sourceRecord = source.get(dataID); // If record was deleted during the update (and also invalidated),
661
+ // we don't need to count it as an invalidated id
662
+
663
+ if (sourceRecord === null) {
664
+ return;
665
+ }
666
+
667
+ var nextRecord;
668
+
669
+ if (targetRecord != null) {
670
+ // If the target record exists, use it to set the epoch
671
+ // at which it was invalidated. This record will be updated with
672
+ // any changes from source in the section below
673
+ // where we update the target records based on the source.
674
+ nextRecord = RelayModernRecord.clone(targetRecord);
675
+ } else {
676
+ // If the target record doesn't exist, it means that a new record
677
+ // in the source was created (and also invalidated), so we use that
678
+ // record to set the epoch at which it was invalidated. This record
679
+ // will be updated with any changes from source in the section below
680
+ // where we update the target records based on the source.
681
+ nextRecord = sourceRecord != null ? RelayModernRecord.clone(sourceRecord) : null;
682
+ }
683
+
684
+ if (!nextRecord) {
685
+ return;
686
+ }
687
+
688
+ RelayModernRecord.setValue(nextRecord, RelayStoreUtils.INVALIDATED_AT_KEY, currentWriteEpoch);
689
+ invalidatedRecordIDs.add(dataID);
690
+ target.set(dataID, nextRecord);
691
+ });
692
+ } // Update the target based on the changes present in source
693
+
694
+
695
+ var dataIDs = source.getRecordIDs();
696
+
697
+ for (var ii = 0; ii < dataIDs.length; ii++) {
698
+ var dataID = dataIDs[ii];
699
+ var sourceRecord = source.get(dataID);
700
+ var targetRecord = target.get(dataID); // Prevent mutation of a record from outside the store.
701
+
702
+ if (process.env.NODE_ENV !== "production") {
703
+ if (sourceRecord) {
704
+ RelayModernRecord.freeze(sourceRecord);
705
+ }
706
+ }
707
+
708
+ if (sourceRecord && targetRecord) {
709
+ // ReactFlightClientResponses are lazy and only materialize when readRoot
710
+ // is called when we read the field, so if the record is a Flight field
711
+ // we always use the new record's data regardless of whether
712
+ // it actually changed. Let React take care of reconciliation instead.
713
+ var nextRecord = RelayModernRecord.getType(targetRecord) === RelayStoreReactFlightUtils.REACT_FLIGHT_TYPE_NAME ? sourceRecord : RelayModernRecord.update(targetRecord, sourceRecord);
714
+
715
+ if (nextRecord !== targetRecord) {
716
+ // Prevent mutation of a record from outside the store.
717
+ if (process.env.NODE_ENV !== "production") {
718
+ RelayModernRecord.freeze(nextRecord);
719
+ }
720
+
721
+ updatedRecordIDs.add(dataID);
722
+ target.set(dataID, nextRecord);
723
+ }
724
+ } else if (sourceRecord === null) {
725
+ target["delete"](dataID);
726
+
727
+ if (targetRecord !== null) {
728
+ updatedRecordIDs.add(dataID);
729
+ }
730
+ } else if (sourceRecord) {
731
+ target.set(dataID, sourceRecord);
732
+ updatedRecordIDs.add(dataID);
733
+ } // don't add explicit undefined
734
+
735
+ }
736
+ }
737
+ /**
738
+ * Returns an OperationAvailability given the Availability returned
739
+ * by checking an operation, and when that operation was last written to the store.
740
+ * Specifically, the provided Availability of an operation will contain the
741
+ * value of when a record referenced by the operation was most recently
742
+ * invalidated; given that value, and given when this operation was last
743
+ * written to the store, this function will return the overall
744
+ * OperationAvailability for the operation.
745
+ */
746
+
747
+
748
+ function getAvailabilityStatus(operationAvailability, operationLastWrittenAt, operationFetchTime, queryCacheExpirationTime) {
749
+ var mostRecentlyInvalidatedAt = operationAvailability.mostRecentlyInvalidatedAt,
750
+ status = operationAvailability.status;
751
+
752
+ if (typeof mostRecentlyInvalidatedAt === 'number') {
753
+ // If some record referenced by this operation is stale, then the operation itself is stale
754
+ // if either the operation itself was never written *or* the operation was last written
755
+ // before the most recent invalidation of its reachable records.
756
+ if (operationLastWrittenAt == null || mostRecentlyInvalidatedAt > operationLastWrittenAt) {
757
+ return {
758
+ status: 'stale'
759
+ };
760
+ }
761
+ }
762
+
763
+ if (status === 'missing') {
764
+ return {
765
+ status: 'missing'
766
+ };
767
+ }
768
+
769
+ if (operationFetchTime != null && queryCacheExpirationTime != null) {
770
+ var isStale = operationFetchTime <= Date.now() - queryCacheExpirationTime;
771
+
772
+ if (isStale) {
773
+ return {
774
+ status: 'stale'
775
+ };
776
+ }
777
+ } // There were no invalidations of any reachable records *or* the operation is known to have
778
+ // been fetched after the most recent record invalidation.
779
+
780
+
781
+ return {
782
+ status: 'available',
783
+ fetchTime: operationFetchTime !== null && operationFetchTime !== void 0 ? operationFetchTime : null
784
+ };
785
+ }
786
+
787
+ module.exports = LiveResolverStore;