Comparing version 0.1.1 to 0.2.0
261
lib/entry.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.Entry = undefined; | ||
var assert = require("assert"); | ||
var UNKNOWN_VALUE = Object.create(null); | ||
var emptySetPool = []; | ||
var _toConsumableArray2 = require("babel-runtime/helpers/toConsumableArray"); | ||
function Entry(fn, key, args) { | ||
assert(this instanceof Entry); | ||
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); | ||
this.fn = fn; | ||
this.key = key; | ||
this.args = args; | ||
this.value = UNKNOWN_VALUE; | ||
this.dirty = true; | ||
this.parents = new Set; | ||
this.childValues = new Map; | ||
var _map = require("babel-runtime/core-js/map"); | ||
// When this Entry has children that are dirty, this property becomes | ||
// a Set containing other Entry objects, borrowed from emptySetPool. | ||
// When the set becomes empty, it gets recycled back to emptySetPool. | ||
this.dirtyChildren = null; | ||
} | ||
var _map2 = _interopRequireDefault(_map); | ||
exports.Entry = Entry; | ||
var _set = require("babel-runtime/core-js/set"); | ||
var Ep = Entry.prototype; | ||
var _set2 = _interopRequireDefault(_set); | ||
Ep.setDirty = function setDirty() { | ||
this.dirty = true; | ||
this.parents.forEach(function (parent) { | ||
parent.reportDirtyChild(this); | ||
}, this); | ||
}; | ||
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); | ||
Ep.reportDirtyChild = function reportDirtyChild(child) { | ||
assert(this.childValues.has(child)); | ||
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); | ||
if (! this.dirtyChildren) { | ||
// Initialize this.dirtyChildren with an empty set drawn from the | ||
// emptySetPool if possible. | ||
this.dirtyChildren = emptySetPool.pop() || new Set; | ||
var _createClass2 = require("babel-runtime/helpers/createClass"); | ||
} else if (this.dirtyChildren.has(child)) { | ||
// If we already know this child is dirty, then we must have already | ||
// informed our own parents that we are dirty, so we can terminate | ||
// the recursion early. | ||
return; | ||
} | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
this.dirtyChildren.add(child); | ||
var _create = require("babel-runtime/core-js/object/create"); | ||
this.parents.forEach(function (parent) { | ||
parent.reportDirtyChild(this); | ||
}, this); | ||
}; | ||
var _create2 = _interopRequireDefault(_create); | ||
Ep.reportCleanChild = function reportCleanChild(child) { | ||
assert(! child.dirty); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
this.childValues.set(child, child.value); | ||
var assert = require("assert"); | ||
var UNKNOWN_VALUE = (0, _create2.default)(null); | ||
var currentParentEntry = void 0; | ||
var dc = this.dirtyChildren; | ||
if (dc) { | ||
dc.delete(child); | ||
if (dc.size === 0) { | ||
emptySetPool.push(dc); | ||
dc = this.dirtyChildren = null; | ||
} | ||
} | ||
var Entry = exports.Entry = function () { | ||
function Entry(fn, key, args) { | ||
(0, _classCallCheck3.default)(this, Entry); | ||
this.fn = fn; | ||
this.key = key; | ||
this.args = args; | ||
this.value = UNKNOWN_VALUE; | ||
this.dirty = true; | ||
this.parents = new _set2.default(); | ||
this.childValues = new _map2.default(); | ||
this.dirtyChildren = new _set2.default(); | ||
if (this.dirty || dc) { | ||
// This Entry is not clean, either because it's explicitly dirty or | ||
// because it still has dirty children, so we can't report it as | ||
// clean to its parents. | ||
return; | ||
} | ||
(0, _createClass3.default)(Entry, [{ | ||
key: "setDirty", | ||
value: function setDirty() { | ||
var _this = this; | ||
this.parents.forEach(function (parent) { | ||
parent.reportCleanChild(this); | ||
}, this); | ||
}; | ||
this.dirty = true; | ||
// This is the most important method of the Entry API, because it | ||
// determines whether the cached entry.value can be returned immediately, | ||
// or must be recomputed. The overall performance of the caching system | ||
// depends on the truth of the following observations: (1) this.dirty is | ||
// usually false, (2) this.dirtyChildren is usually null/empty, and thus | ||
// (3) this.value is usally returned very quickly, without recomputation. | ||
Ep.recomputeIfDirty = function recomputeIfDirty() { | ||
if (this.dirty) { | ||
// If this Entry is explicitly dirty because someone called | ||
// entry.setDirty(), recompute. | ||
return this.recompute(); | ||
} | ||
this.parents.forEach(function (parent) { | ||
parent.reportDirtyChild(_this); | ||
}); | ||
} | ||
}, { | ||
key: "reportDirtyChild", | ||
value: function reportDirtyChild(child) { | ||
var _this2 = this; | ||
assert(this.childValues.has(child)); | ||
if (this.dirtyChildren.has(child)) { | ||
// If we already know this child is dirty, then we must have already | ||
// informed our own parents that we are dirty, so we can terminate | ||
// the recursion early. | ||
return; | ||
if (this.dirtyChildren) { | ||
// Get fresh values for any dirty children, and if those values | ||
// disagree with this.childValues, mark this Entry explicitly dirty. | ||
this.dirtyChildren.forEach(function (child) { | ||
var oldValue = this.childValues.get(child); | ||
if (child.recomputeIfDirty() !== oldValue) { | ||
this.dirty = true; | ||
} | ||
}, this); | ||
} | ||
this.dirtyChildren.add(child); | ||
if (this.dirty) { | ||
// If this Entry has become explicitly dirty after comparing the fresh | ||
// values of its dirty children against this.childValues, recompute. | ||
return this.recompute(); | ||
} | ||
this.parents.forEach(function (parent) { | ||
parent.reportDirtyChild(_this2); | ||
}); | ||
} | ||
}, { | ||
key: "reportCleanChild", | ||
value: function reportCleanChild(child) { | ||
var _this3 = this; | ||
assert.notStrictEqual(this.value, UNKNOWN_VALUE); | ||
assert(!child.dirty); | ||
return this.value; | ||
}; | ||
this.childValues.set(child, child.value); | ||
this.dirtyChildren.delete(child); | ||
var currentParentEntry; | ||
if (this.dirty || this.dirtyChildren.size > 0) { | ||
return; | ||
} | ||
this.parents.forEach(function (parent) { | ||
parent.reportCleanChild(_this3); | ||
}); | ||
Ep.recompute = function recompute() { | ||
this.forgetChildren(); | ||
this.dirty = true; | ||
var oldParentEntry = currentParentEntry; | ||
currentParentEntry = this; | ||
try { | ||
this.value = this.fn.apply(null, this.args); | ||
this.dirty = false; | ||
} finally { | ||
currentParentEntry = oldParentEntry; | ||
if (! this.dirty) { | ||
this.updateParents(); | ||
return this.value; | ||
} | ||
}, { | ||
key: "recomputeIfDirty", | ||
value: function recomputeIfDirty() { | ||
var _this4 = this; | ||
} | ||
}; | ||
if (this.dirty) { | ||
return this.recompute(); | ||
} | ||
Ep.forgetChildren = function forgetChildren() { | ||
this.childValues.forEach(function (value, child) { | ||
child.parents.delete(this); | ||
}, this); | ||
this.dirtyChildren.forEach(function (child) { | ||
var oldValue = _this4.childValues.get(child); | ||
if (child.recomputeIfDirty() !== oldValue) { | ||
_this4.dirty = true; | ||
} | ||
}); | ||
this.childValues.clear(); | ||
if (this.dirty) { | ||
return this.recompute(); | ||
} | ||
var dc = this.dirtyChildren; | ||
if (dc) { | ||
dc.clear(); | ||
emptySetPool.push(dc); | ||
this.dirtyChildren = null; | ||
} | ||
}; | ||
return this.value; | ||
} | ||
}, { | ||
key: "recompute", | ||
value: function recompute() { | ||
this.forgetChildren(); | ||
this.dirty = true; | ||
var oldParentEntry = currentParentEntry; | ||
currentParentEntry = this; | ||
try { | ||
this.value = this.fn.apply(this, (0, _toConsumableArray3.default)(this.args)); | ||
this.dirty = false; | ||
} finally { | ||
currentParentEntry = oldParentEntry; | ||
if (!this.dirty) { | ||
this.updateParents(); | ||
return this.value; | ||
} | ||
} | ||
} | ||
}, { | ||
key: "forgetChildren", | ||
value: function forgetChildren() { | ||
var _this5 = this; | ||
Ep.updateParents = function updateParents() { | ||
if (currentParentEntry) { | ||
this.parents.add(currentParentEntry); | ||
} | ||
this.childValues.forEach(function (value, child) { | ||
return child.parents.delete(_this5); | ||
}); | ||
this.childValues.clear(); | ||
this.dirtyChildren.clear(); | ||
} | ||
}, { | ||
key: "updateParents", | ||
value: function updateParents() { | ||
var _this6 = this; | ||
if (currentParentEntry) { | ||
this.parents.add(currentParentEntry); | ||
} | ||
this.parents.forEach(function (parent) { | ||
parent.reportCleanChild(_this6); | ||
}); | ||
} | ||
}]); | ||
return Entry; | ||
}(); | ||
this.parents.forEach(function (parent) { | ||
parent.reportCleanChild(this); | ||
}, this); | ||
}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var LRU = require("lru-cache"); | ||
var Entry = require("./entry.js").Entry; | ||
var slice = Array.prototype.slice; | ||
var _create = require("babel-runtime/core-js/object/create"); | ||
var _create2 = _interopRequireDefault(_create); | ||
var _stringify = require("babel-runtime/core-js/json/stringify"); | ||
var _stringify2 = _interopRequireDefault(_stringify); | ||
exports.makeOptimistic = makeOptimistic; | ||
var _lruCache = require("lru-cache"); | ||
var _lruCache2 = _interopRequireDefault(_lruCache); | ||
var _entry = require("./entry.js"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function defaultMakeCacheKey() { | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return (0, _stringify2.default)(args); | ||
return JSON.stringify(slice.call(arguments)); | ||
} | ||
function makeOptimistic(fn, options) { | ||
options = options || (0, _create2.default)(null); | ||
function wrap(fn, options) { | ||
options = options || Object.create(null); | ||
if (typeof options.makeCacheKey !== "function") { | ||
@@ -39,4 +17,4 @@ options.makeCacheKey = defaultMakeCacheKey; | ||
var cache = new _lruCache2.default({ | ||
dispose: function dispose(entry, key) { | ||
var cache = new LRU({ | ||
dispose: function (entry, key) { | ||
// TODO | ||
@@ -47,13 +25,8 @@ } | ||
function optimistic() { | ||
var _options; | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
var key = options.makeCacheKey.apply(null, arguments); | ||
if (! key) { | ||
return fn.apply(null, arguments); | ||
} | ||
var key = (_options = options).makeCacheKey.apply(_options, args); | ||
if (!key) { | ||
return fn.apply(undefined, args); | ||
} | ||
var args = slice.call(arguments); | ||
var entry = cache.get(key); | ||
@@ -63,3 +36,3 @@ if (entry) { | ||
} else { | ||
cache.set(key, entry = new _entry.Entry(fn, key, args)); | ||
cache.set(key, entry = new Entry(fn, key, args)); | ||
} | ||
@@ -71,10 +44,8 @@ | ||
optimistic.dirty = function () { | ||
var _options2; | ||
var key = (_options2 = options).makeCacheKey.apply(_options2, arguments); | ||
if (!key) { | ||
var key = options.makeCacheKey.apply(null, arguments); | ||
if (! key) { | ||
return; | ||
} | ||
if (!cache.has(key)) { | ||
if (! cache.has(key)) { | ||
return; | ||
@@ -87,2 +58,4 @@ } | ||
return optimistic; | ||
} | ||
} | ||
exports.wrap = wrap; |
{ | ||
"name": "optimism", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"author": "Ben Newman <ben@benjamn.com>", | ||
@@ -28,24 +28,10 @@ "description": "Composable reactive caching with efficient invalidation.", | ||
"scripts": { | ||
"prepublish": "babel src/ --out-dir lib/", | ||
"test": "mocha --compilers js:babel-register --reporter spec --full-trace test/tests.js" | ||
"test": "mocha --reporter spec --full-trace test/tests.js" | ||
}, | ||
"dependencies": { | ||
"babel-runtime": "^6.11.6", | ||
"lru-cache": "^4.0.1" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.16.0", | ||
"babel-plugin-transform-runtime": "^6.15.0", | ||
"babel-preset-es2015": "^6.16.0", | ||
"babel-register": "^6.16.3", | ||
"mocha": "^3.1.0" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"es2015" | ||
], | ||
"plugins": [ | ||
"transform-runtime" | ||
] | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1
1
7625
7
175
1
- Removedbabel-runtime@^6.11.6
- Removedbabel-runtime@6.26.0(transitive)
- Removedcore-js@2.6.12(transitive)
- Removedregenerator-runtime@0.11.1(transitive)