/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * strict-local * @format */ 'use strict'; var _objectSpread2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/objectSpread")); /** * @public * * An implementation of the `Store` interface defined in `RelayStoreTypes`. * * Note that a Store takes ownership of all records provided to it: other * objects may continue to hold a reference to such records but may not mutate * them. The static Relay core is architected to avoid mutating records that may have been * passed to a store: operations that mutate records will either create fresh * records or clone existing records and modify the clones. Record immutability * is also enforced in development mode by freezing all records passed to a store. */ var RelayModernStore = /*#__PURE__*/ function () { function RelayModernStore(source) { var gcScheduler = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : require("fbjs/lib/resolveImmediate"); var operationLoader = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; // Prevent mutation of a record from outside the store. if (process.env.NODE_ENV !== "production") { var storeIDs = source.getRecordIDs(); for (var ii = 0; ii < storeIDs.length; ii++) { var record = source.get(storeIDs[ii]); if (record) { require("./RelayModernRecord").freeze(record); } } } this._gcScheduler = gcScheduler; this._hasScheduledGC = false; this._index = 0; this._operationLoader = operationLoader; this._recordSource = source; this._roots = new Map(); this._subscriptions = new Set(); this._updatedRecordIDs = {}; this._gcHoldCounter = 0; this._shouldScheduleGC = false; } var _proto = RelayModernStore.prototype; _proto.getSource = function getSource() { return this._recordSource; }; _proto.check = function check(selector) { return require("./DataChecker").check(this._recordSource, this._recordSource, selector, [], this._operationLoader); }; _proto.retain = function retain(selector) { var _this = this; var index = this._index++; var dispose = function dispose() { _this._roots["delete"](index); _this._scheduleGC(); }; this._roots.set(index, selector); return { dispose: dispose }; }; _proto.lookup = function lookup(selector, owner) { var snapshot = require("./RelayReader").read(this._recordSource, selector, owner); if (process.env.NODE_ENV !== "production") { require("./deepFreeze")(snapshot); } return snapshot; }; _proto.notify = function notify() { var _this2 = this; this._subscriptions.forEach(function (subscription) { _this2._updateSubscription(subscription); }); this._updatedRecordIDs = {}; }; _proto.publish = function publish(source) { updateTargetFromSource(this._recordSource, source, this._updatedRecordIDs); }; _proto.subscribe = function subscribe(snapshot, callback) { var _this3 = this; var subscription = { callback: callback, snapshot: snapshot }; var dispose = function dispose() { _this3._subscriptions["delete"](subscription); }; this._subscriptions.add(subscription); return { dispose: dispose }; }; _proto.holdGC = function holdGC() { var _this4 = this; this._gcHoldCounter++; var dispose = function dispose() { if (_this4._gcHoldCounter > 0) { _this4._gcHoldCounter--; if (_this4._gcHoldCounter === 0 && _this4._shouldScheduleGC) { _this4._scheduleGC(); _this4._shouldScheduleGC = false; } } }; return { dispose: dispose }; }; _proto.toJSON = function toJSON() { return 'RelayModernStore()'; }; // Internal API _proto.__getUpdatedRecordIDs = function __getUpdatedRecordIDs() { return this._updatedRecordIDs; }; _proto._updateSubscription = function _updateSubscription(subscription) { var callback = subscription.callback, snapshot = subscription.snapshot; if (!require("./hasOverlappingIDs")(snapshot, this._updatedRecordIDs)) { return; } var _RelayReader$read = require("./RelayReader").read(this._recordSource, snapshot, snapshot.owner), data = _RelayReader$read.data, seenRecords = _RelayReader$read.seenRecords; var nextData = require("./recycleNodesInto")(snapshot.data, data); var nextSnapshot = (0, _objectSpread2["default"])({}, snapshot, { data: nextData, seenRecords: seenRecords }); if (process.env.NODE_ENV !== "production") { require("./deepFreeze")(nextSnapshot); } subscription.snapshot = nextSnapshot; if (nextSnapshot.data !== snapshot.data) { callback(nextSnapshot); } }; _proto._scheduleGC = function _scheduleGC() { var _this5 = this; if (this._gcHoldCounter > 0) { this._shouldScheduleGC = true; return; } if (this._hasScheduledGC) { return; } this._hasScheduledGC = true; this._gcScheduler(function () { _this5.__gc(); _this5._hasScheduledGC = false; }); }; _proto.__gc = function __gc() { var _this6 = this; var references = new Set(); // Mark all records that are traversable from a root this._roots.forEach(function (selector) { require("./RelayReferenceMarker").mark(_this6._recordSource, selector, references, _this6._operationLoader); }); // Short-circuit if *nothing* is referenced if (!references.size) { this._recordSource.clear(); return; } // Evict any unreferenced nodes var storeIDs = this._recordSource.getRecordIDs(); for (var ii = 0; ii < storeIDs.length; ii++) { var dataID = storeIDs[ii]; if (!references.has(dataID)) { this._recordSource.remove(dataID); } } }; return RelayModernStore; }(); /** * Updates the target with information from source, also updating a mapping of * which records in the target were changed as a result. */ function updateTargetFromSource(target, source, updatedRecordIDs) { var dataIDs = source.getRecordIDs(); for (var ii = 0; ii < dataIDs.length; ii++) { var dataID = dataIDs[ii]; var sourceRecord = source.get(dataID); var targetRecord = target.get(dataID); // Prevent mutation of a record from outside the store. if (process.env.NODE_ENV !== "production") { if (sourceRecord) { require("./RelayModernRecord").freeze(sourceRecord); } } if (sourceRecord === require("./RelayStoreUtils").UNPUBLISH_RECORD_SENTINEL) { // Unpublish a record target.remove(dataID); updatedRecordIDs[dataID] = true; } else if (sourceRecord && targetRecord) { var nextRecord = require("./RelayModernRecord").update(targetRecord, sourceRecord); if (nextRecord !== targetRecord) { // Prevent mutation of a record from outside the store. if (process.env.NODE_ENV !== "production") { require("./RelayModernRecord").freeze(nextRecord); } updatedRecordIDs[dataID] = true; target.set(dataID, nextRecord); } } else if (sourceRecord === null) { target["delete"](dataID); if (targetRecord !== null) { updatedRecordIDs[dataID] = true; } } else if (sourceRecord) { target.set(dataID, sourceRecord); updatedRecordIDs[dataID] = true; } // don't add explicit undefined } } require("./RelayProfiler").instrumentMethods(RelayModernStore.prototype, { lookup: 'RelayModernStore.prototype.lookup', notify: 'RelayModernStore.prototype.notify', publish: 'RelayModernStore.prototype.publish', retain: 'RelayModernStore.prototype.retain', subscribe: 'RelayModernStore.prototype.subscribe', __gc: 'RelayModernStore.prototype.__gc', holdGC: 'RelayModernStore.prototype.holdGC' }); module.exports = RelayModernStore;