relay-runtime 0.0.0-main-ca31ec10 → 0.0.0-main-3070fa31

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