Files
30-seconds-of-code/node_modules/relay-runtime/lib/RelayModernStore.js
2019-08-20 15:52:05 +02:00

301 lines
8.0 KiB
JavaScript

/**
* 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;