@nx-js/observer-util
Advanced tools
Comparing version 3.1.3 to 3.1.4
@@ -5,10 +5,161 @@ 'use strict'; | ||
var promise = Promise.resolve(); | ||
var connectionStore = new WeakMap(); | ||
// schedule the given task as a microtask | ||
// (this used to leak into after the next task when mixed with MutationObservers in Safari) | ||
function nextTick(task) { | ||
return promise.then(task); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
var reactionsForObj = connectionStore.get(obj); | ||
var reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} else if (!reactionsForKey.has(reaction)) { | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} | ||
} | ||
function iterateReactionsForKey(obj, key, reactionHandler) { | ||
var reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
// create a static copy of the reactions, before iterating them | ||
// to avoid infinite (iterate items: remove -> readd) loops | ||
Array.from(reactionsForKey).forEach(reactionHandler); | ||
} | ||
} | ||
function releaseReaction(reaction) { | ||
if (reaction.cleaners) { | ||
reaction.cleaners.forEach(releaseReactionKeyConnection, reaction); | ||
} | ||
reaction.cleaners = undefined; | ||
} | ||
function releaseReactionKeyConnection(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
var runningReaction; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// throw an error if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
throw new Error(("Unobserved reactions can not be executed. You tried to run a reaction for " + fn)); | ||
} | ||
// release the (obj -> key -> reactions) connections | ||
// and reset the cleaner connections | ||
releaseReaction(reaction); | ||
reaction.cleaners = []; | ||
try { | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
runningReaction = reaction; | ||
return fn.apply(context, args); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
// queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
reaction.scheduler(reaction); | ||
} else if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.add(reaction); | ||
} else { | ||
reaction(); | ||
} | ||
} | ||
function hasRunningReaction() { | ||
return runningReaction !== undefined; | ||
} | ||
var IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options) { | ||
if ( options === void 0 ) options = {}; | ||
if (typeof fn !== 'function') { | ||
throw new TypeError(("The first argument must be a function instead of " + fn)); | ||
} | ||
if (fn[IS_REACTION]) { | ||
throw new TypeError('The first argument must not be an already observed reaction'); | ||
} | ||
if (typeof options !== 'object' || options === null) { | ||
throw new TypeError(("The second argument must be an options object instead of " + options)); | ||
} | ||
validateOptions(options); | ||
// create a reaction from the passed function | ||
function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
} | ||
// save the scheduler on the reaction | ||
reaction.scheduler = options.scheduler; | ||
// runId will serve as a unique (incremental) id, which identifies the reaction's last run | ||
reaction.runId = 0; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
if (!options.lazy) { | ||
reaction(); | ||
} | ||
return reaction; | ||
} | ||
function validateOptions(ref) { | ||
var lazy = ref.lazy; if ( lazy === void 0 ) lazy = false; | ||
var scheduler = ref.scheduler; | ||
if (typeof lazy !== 'boolean') { | ||
throw new TypeError(("options.lazy must be a boolean or undefined instead of " + lazy)); | ||
} | ||
if (typeof scheduler === 'object' && scheduler !== null) { | ||
if (typeof scheduler.add !== 'function' || typeof scheduler.delete !== 'function') { | ||
throw new TypeError('options.scheduler object must have an add and delete method'); | ||
} | ||
} else if (scheduler !== undefined && typeof scheduler !== 'function') { | ||
throw new TypeError(("options.scheduler must be a function, an object or undefined instead of " + scheduler)); | ||
} | ||
} | ||
function unobserve(reaction) { | ||
if (typeof reaction !== 'function' || !reaction[IS_REACTION]) { | ||
throw new TypeError(("The first argument must be a reaction instead of " + reaction)); | ||
} | ||
// do nothing, if the reaction is already unobserved | ||
if (!reaction.unobserved) { | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.delete(reaction); | ||
} | ||
} | ||
var proxyToRaw = new WeakMap(); | ||
@@ -30,3 +181,3 @@ var rawToProxy = new WeakMap(); | ||
function get$1(key) { | ||
function get(key) { | ||
var rawContext = proxyToRaw.get(this); | ||
@@ -47,10 +198,13 @@ var proto = getPrototypeOf(this); | ||
} | ||
if (!proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = !proto.has.call(rawContext, value); | ||
var result = proto.add.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.add.apply(rawContext, arguments); | ||
return result; | ||
} | ||
function set$1(key, value) { | ||
function set(key, value) { | ||
var rawContext = proxyToRaw.get(this); | ||
@@ -61,7 +215,10 @@ var proto = getPrototypeOf(this); | ||
} | ||
if (proto.get.call(rawContext, key) !== value) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = proto.get.call(rawContext, key) !== value; | ||
var result = proto.set.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, key); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.set.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -75,7 +232,10 @@ | ||
} | ||
if (proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = proto.has.call(rawContext, value); | ||
var result = proto.delete.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.delete.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -89,6 +249,9 @@ | ||
} | ||
if (rawContext.size) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = rawContext.size !== 0; | ||
var result = proto.clear.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.clear.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -158,4 +321,4 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
@@ -171,13 +334,13 @@ map.clear = clear; | ||
function instrumentSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
set.clear = clear; | ||
set.forEach = forEach; | ||
set.keys = keys; | ||
set.values = values; | ||
set.entries = entries; | ||
set[Symbol.iterator] = iterator; | ||
Object.defineProperty(set, 'size', { get: getSize }); | ||
function instrumentSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
set$$1.clear = clear; | ||
set$$1.forEach = forEach; | ||
set$$1.keys = keys; | ||
set$$1.values = values; | ||
set$$1.entries = entries; | ||
set$$1[Symbol.iterator] = iterator; | ||
Object.defineProperty(set$$1, 'size', { get: getSize }); | ||
} | ||
@@ -187,11 +350,11 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
} | ||
function instrumentWeakSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
function instrumentWeakSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
} | ||
@@ -206,72 +369,75 @@ | ||
var connectionStore = new WeakMap(); | ||
var cleanupStore = new WeakMap(); | ||
var ENUMERATE = Symbol('enumerate'); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get$1(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
var rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
var result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function storeReaction(reaction) { | ||
// this will be used to save data for cleaning up later | ||
cleanupStore.set(reaction, new Set()); | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
var reactionsForObj = connectionStore.get(obj); | ||
var reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set$1(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
reactionsForKey.add(reaction); | ||
cleanupStore.get(reaction).add(reactionsForKey); | ||
} | ||
function iterateReactionsForKey(obj, key, fn) { | ||
var reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
reactionsForKey.forEach(fn); | ||
// save if the value changed because of this set operation | ||
// array 'length' is an exception here, because of it's exotic nature | ||
var valueChanged = key === 'length' || value !== obj[key]; | ||
// execute the set operation before running any reaction | ||
var result = Reflect.set(obj, key, value, receiver); | ||
// emit a warning and do not queue anything when another reaction is queued | ||
// from an already running reaction | ||
if (hasRunningReaction()) { | ||
console.error(("Mutating observables in reactions is forbidden. You set " + key + " to " + value + ".")); | ||
return result; | ||
} | ||
// do not queue reactions if it is a symbol keyed property | ||
// or the set operation resulted in no value change | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key !== 'symbol' && valueChanged && obj === proxyToRaw.get(receiver)) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return result; | ||
} | ||
function releaseReaction(reaction) { | ||
cleanupStore.get(reaction).forEach(releaseReactionKeyConnections, reaction); | ||
} | ||
function releaseReactionKeyConnections(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
var ENUMERATE = Symbol('enumerate'); | ||
var queuedReactions = new Set(); | ||
var runningReaction; | ||
var handlers = { get: get, ownKeys: ownKeys, set: set, deleteProperty: deleteProperty }; | ||
function observe(reaction) { | ||
if (typeof reaction !== 'function') { | ||
throw new TypeError('Reactions must be functions.'); | ||
function deleteProperty(obj, key) { | ||
// save if the object had the key | ||
var hadKey = key in obj; | ||
// execute the delete operation before running any reaction | ||
var result = Reflect.deleteProperty(obj, key); | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && hadKey) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
// init basic data structures to save and cleanup (observable.prop -> reaction) connections later | ||
storeReaction(reaction); | ||
// run the reaction once to discover what observable properties it uses | ||
runReaction(reaction); | ||
return reaction; | ||
return result; | ||
} | ||
function unobserve(reaction) { | ||
// do not run this reaction anymore, even if it is already queued | ||
queuedReactions.delete(reaction); | ||
// release every (observable.prop -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
var handlers = { get: get$1, ownKeys: ownKeys, set: set$1, deleteProperty: deleteProperty }; | ||
function unqueue(reaction) { | ||
// do not run this reaction, if it is not queued again by a prop mutation | ||
queuedReactions.delete(reaction); | ||
} | ||
function exec(reaction) { | ||
runReaction(reaction); | ||
} | ||
function isObservable(obj) { | ||
@@ -285,5 +451,6 @@ if (typeof obj !== 'object') { | ||
function observable(obj) { | ||
obj = obj || {}; | ||
if ( obj === void 0 ) obj = {}; | ||
if (typeof obj !== 'object') { | ||
throw new TypeError('First argument must be an object or undefined'); | ||
throw new TypeError('Observable first argument must be an object or undefined'); | ||
} | ||
@@ -325,103 +492,5 @@ // if it is already an observable, return it | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
var rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
var result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (runningReaction && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
// do not register reactions if it is a symbol keyed property | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key === 'symbol' || obj !== proxyToRaw.get(receiver)) { | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
// only queue reactions if the set operation resulted in a value change | ||
// array 'length' property is an exception from this, because of it's exotic nature | ||
if (key === 'length' || value !== obj[key]) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
function deleteProperty(obj, key) { | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && key in obj) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.deleteProperty(obj, key); | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// register a new reaction running task, if there are no reactions queued yet | ||
if (!queuedReactions.size) { | ||
nextTick(runQueuedReactions); | ||
} | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
queuedReactions.add(reaction); | ||
} | ||
function runQueuedReactions() { | ||
queuedReactions.forEach(runReaction); | ||
queuedReactions.clear(); | ||
} | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
function runReaction(reaction) { | ||
try { | ||
runningReaction = reaction; | ||
reaction(); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
exports.nextTick = nextTick; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.observable = observable; | ||
exports.isObservable = isObservable; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.unqueue = unqueue; | ||
exports.exec = exec; |
@@ -1,1 +0,1 @@ | ||
'use strict';Object.defineProperty(exports,'__esModule',{value:!0});var promise=Promise.resolve();function nextTick(a){return promise.then(a)}var proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get$1(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)||(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.add.apply(b,arguments)):c.add.apply(this,arguments)}function set$1(a,b){var c=proxyToRaw.get(this),d=getPrototypeOf(this);return c?(d.get.call(c,a)!==b&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),d.set.apply(c,arguments)):d.set.apply(this,arguments)}function deleteFn(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.delete.apply(b,arguments)):c.delete.apply(this,arguments)}function clear(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(a.size&&queueReactionsForKey(a,ITERATE),b.clear.apply(a,arguments)):b.clear.apply(this,arguments)}function forEach(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]),connectionStore=new WeakMap,cleanupStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function storeReaction(a){cleanupStore.set(a,new Set)}function registerReactionForKey(a,b,c){var d=connectionStore.get(a),e=d[b];e||(d[b]=e=new Set),e.add(c),cleanupStore.get(c).add(e)}function iterateReactionsForKey(a,b,c){var d=connectionStore.get(a)[b];d&&d.forEach(c)}function releaseReaction(a){cleanupStore.get(a).forEach(releaseReactionKeyConnections,a)}function releaseReactionKeyConnections(a){a.delete(this)}var runningReaction,ENUMERATE=Symbol('enumerate'),queuedReactions=new Set,handlers={get:get,ownKeys:ownKeys,set:set,deleteProperty:deleteProperty};function observe(a){if('function'!=typeof a)throw new TypeError('Reactions must be functions.');return storeReaction(a),runReaction(a),a}function unobserve(a){queuedReactions.delete(a),releaseReaction(a)}function unqueue(a){queuedReactions.delete(a)}function exec(a){runReaction(a)}function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a){if(a=a||{},'object'!=typeof a)throw new TypeError('First argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){var b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){var b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}function get(a,b,c){var d=proxyToRaw.get(a)||a;if('$raw'===b)return d;var e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),runningReaction&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function set(a,b,c,d){return('object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c),'symbol'==typeof b||a!==proxyToRaw.get(d))?Reflect.set(a,b,c,d):(('length'===b||c!==a[b])&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.set(a,b,c,d))}function deleteProperty(a,b){return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.deleteProperty(a,b)}function queueReactionsForKey(a,b){queuedReactions.size||nextTick(runQueuedReactions),iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){queuedReactions.add(a)}function runQueuedReactions(){queuedReactions.forEach(runReaction),queuedReactions.clear()}function runReaction(a){try{runningReaction=a,a()}finally{runningReaction=void 0}}exports.nextTick=nextTick,exports.observable=observable,exports.isObservable=isObservable,exports.observe=observe,exports.unobserve=unobserve,exports.unqueue=unqueue,exports.exec=exec; | ||
'use strict';Object.defineProperty(exports,'__esModule',{value:!0});var connectionStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function registerReactionForKey(a,b,c){var d=connectionStore.get(a),e=d[b];e?!e.has(c)&&(e.add(c),c.cleaners.push(e)):(d[b]=e=new Set,e.add(c),c.cleaners.push(e))}function iterateReactionsForKey(a,b,c){var d=connectionStore.get(a)[b];d&&Array.from(d).forEach(c)}function releaseReaction(a){a.cleaners&&a.cleaners.forEach(releaseReactionKeyConnection,a),a.cleaners=void 0}function releaseReactionKeyConnection(a){a.delete(this)}var runningReaction;function runAsReaction(a,b,c,d){if(a.unobserved)throw new Error('Unobserved reactions can not be executed. You tried to run a reaction for '+b);releaseReaction(a),a.cleaners=[];try{return runningReaction=a,b.apply(c,d)}finally{runningReaction=void 0}}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function queueReactionsForKey(a,b){iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){'function'==typeof a.scheduler?a.scheduler(a):'object'==typeof a.scheduler?a.scheduler.add(a):a()}function hasRunningReaction(){return runningReaction!==void 0}var IS_REACTION=Symbol('is reaction');function observe(a,b){function c(){return runAsReaction(c,a,this,arguments)}if(void 0===b&&(b={}),'function'!=typeof a)throw new TypeError('The first argument must be a function instead of '+a);if(a[IS_REACTION])throw new TypeError('The first argument must not be an already observed reaction');if('object'!=typeof b||null===b)throw new TypeError('The second argument must be an options object instead of '+b);return validateOptions(b),c.scheduler=b.scheduler,c.runId=0,c[IS_REACTION]=!0,b.lazy||c(),c}function validateOptions(a){var b=a.lazy;void 0===b&&(b=!1);var c=a.scheduler;if('boolean'!=typeof b)throw new TypeError('options.lazy must be a boolean or undefined instead of '+b);if('object'==typeof c&&null!==c){if('function'!=typeof c.add||'function'!=typeof c.delete)throw new TypeError('options.scheduler object must have an add and delete method');}else if(void 0!==c&&'function'!=typeof c)throw new TypeError('options.scheduler must be a function, an object or undefined instead of '+c)}function unobserve(a){if('function'!=typeof a||!a[IS_REACTION])throw new TypeError('The first argument must be a reaction instead of '+a);a.unobserved||(a.unobserved=!0,releaseReaction(a)),'object'==typeof a.scheduler&&a.scheduler.delete(a)}var proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.add.apply(this,arguments);var d=!c.has.call(b,a),e=c.add.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function set(a,b){var c=proxyToRaw.get(this),d=getPrototypeOf(this);if(!c)return d.set.apply(this,arguments);var e=d.get.call(c,a)!==b,f=d.set.apply(c,arguments);return e&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),f}function deleteFn(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.delete.apply(this,arguments);var d=c.has.call(b,a),e=c.delete.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function clear(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);if(!a)return b.clear.apply(this,arguments);var c=0!==a.size,d=b.clear.apply(a,arguments);return c&&queueReactionsForKey(a,ITERATE),d}function forEach(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]),ENUMERATE=Symbol('enumerate');function get$1(a,b,c){var d=proxyToRaw.get(a)||a;if('$raw'===b)return d;var e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),hasRunningReaction()&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function set$1(a,b,c,d){'object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c);var e='length'===b||c!==a[b],f=Reflect.set(a,b,c,d);return hasRunningReaction()?(console.error('Mutating observables in reactions is forbidden. You set '+b+' to '+c+'.'),f):('symbol'!=typeof b&&e&&a===proxyToRaw.get(d)&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),f)}function deleteProperty(a,b){var c=Reflect.deleteProperty(a,b);return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),c}var handlers={get:get$1,ownKeys:ownKeys,set:set$1,deleteProperty:deleteProperty};function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a){if(void 0===a&&(a={}),'object'!=typeof a)throw new TypeError('Observable first argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){var b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){var b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}exports.observe=observe,exports.unobserve=unobserve,exports.observable=observable,exports.isObservable=isObservable; |
@@ -5,10 +5,156 @@ 'use strict'; | ||
const promise = Promise.resolve(); | ||
const connectionStore = new WeakMap(); | ||
// schedule the given task as a microtask | ||
// (this used to leak into after the next task when mixed with MutationObservers in Safari) | ||
function nextTick(task) { | ||
return promise.then(task); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
const reactionsForObj = connectionStore.get(obj); | ||
let reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} else if (!reactionsForKey.has(reaction)) { | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} | ||
} | ||
function iterateReactionsForKey(obj, key, reactionHandler) { | ||
const reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
// create a static copy of the reactions, before iterating them | ||
// to avoid infinite (iterate items: remove -> readd) loops | ||
Array.from(reactionsForKey).forEach(reactionHandler); | ||
} | ||
} | ||
function releaseReaction(reaction) { | ||
if (reaction.cleaners) { | ||
reaction.cleaners.forEach(releaseReactionKeyConnection, reaction); | ||
} | ||
reaction.cleaners = undefined; | ||
} | ||
function releaseReactionKeyConnection(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
let runningReaction; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// throw an error if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
throw new Error(`Unobserved reactions can not be executed. You tried to run a reaction for ${fn}`); | ||
} | ||
// release the (obj -> key -> reactions) connections | ||
// and reset the cleaner connections | ||
releaseReaction(reaction); | ||
reaction.cleaners = []; | ||
try { | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
runningReaction = reaction; | ||
return fn.apply(context, args); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
// queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
reaction.scheduler(reaction); | ||
} else if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.add(reaction); | ||
} else { | ||
reaction(); | ||
} | ||
} | ||
function hasRunningReaction() { | ||
return runningReaction !== undefined; | ||
} | ||
const IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options = {}) { | ||
if (typeof fn !== 'function') { | ||
throw new TypeError(`The first argument must be a function instead of ${fn}`); | ||
} | ||
if (fn[IS_REACTION]) { | ||
throw new TypeError('The first argument must not be an already observed reaction'); | ||
} | ||
if (typeof options !== 'object' || options === null) { | ||
throw new TypeError(`The second argument must be an options object instead of ${options}`); | ||
} | ||
validateOptions(options); | ||
// create a reaction from the passed function | ||
function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
} | ||
// save the scheduler on the reaction | ||
reaction.scheduler = options.scheduler; | ||
// runId will serve as a unique (incremental) id, which identifies the reaction's last run | ||
reaction.runId = 0; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
if (!options.lazy) { | ||
reaction(); | ||
} | ||
return reaction; | ||
} | ||
function validateOptions({ lazy = false, scheduler }) { | ||
if (typeof lazy !== 'boolean') { | ||
throw new TypeError(`options.lazy must be a boolean or undefined instead of ${lazy}`); | ||
} | ||
if (typeof scheduler === 'object' && scheduler !== null) { | ||
if (typeof scheduler.add !== 'function' || typeof scheduler.delete !== 'function') { | ||
throw new TypeError('options.scheduler object must have an add and delete method'); | ||
} | ||
} else if (scheduler !== undefined && typeof scheduler !== 'function') { | ||
throw new TypeError(`options.scheduler must be a function, an object or undefined instead of ${scheduler}`); | ||
} | ||
} | ||
function unobserve(reaction) { | ||
if (typeof reaction !== 'function' || !reaction[IS_REACTION]) { | ||
throw new TypeError(`The first argument must be a reaction instead of ${reaction}`); | ||
} | ||
// do nothing, if the reaction is already unobserved | ||
if (!reaction.unobserved) { | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.delete(reaction); | ||
} | ||
} | ||
const proxyToRaw = new WeakMap(); | ||
@@ -30,3 +176,3 @@ const rawToProxy = new WeakMap(); | ||
function get$1(key) { | ||
function get(key) { | ||
const rawContext = proxyToRaw.get(this); | ||
@@ -47,10 +193,13 @@ const proto = getPrototypeOf(this); | ||
} | ||
if (!proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = !proto.has.call(rawContext, value); | ||
const result = proto.add.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.add.apply(rawContext, arguments); | ||
return result; | ||
} | ||
function set$1(key, value) { | ||
function set(key, value) { | ||
const rawContext = proxyToRaw.get(this); | ||
@@ -61,7 +210,10 @@ const proto = getPrototypeOf(this); | ||
} | ||
if (proto.get.call(rawContext, key) !== value) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = proto.get.call(rawContext, key) !== value; | ||
const result = proto.set.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, key); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.set.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -75,7 +227,10 @@ | ||
} | ||
if (proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = proto.has.call(rawContext, value); | ||
const result = proto.delete.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.delete.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -89,6 +244,9 @@ | ||
} | ||
if (rawContext.size) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = rawContext.size !== 0; | ||
const result = proto.clear.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.clear.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -158,4 +316,4 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
@@ -171,13 +329,13 @@ map.clear = clear; | ||
function instrumentSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
set.clear = clear; | ||
set.forEach = forEach; | ||
set.keys = keys; | ||
set.values = values; | ||
set.entries = entries; | ||
set[Symbol.iterator] = iterator; | ||
Object.defineProperty(set, 'size', { get: getSize }); | ||
function instrumentSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
set$$1.clear = clear; | ||
set$$1.forEach = forEach; | ||
set$$1.keys = keys; | ||
set$$1.values = values; | ||
set$$1.entries = entries; | ||
set$$1[Symbol.iterator] = iterator; | ||
Object.defineProperty(set$$1, 'size', { get: getSize }); | ||
} | ||
@@ -187,11 +345,11 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
} | ||
function instrumentWeakSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
function instrumentWeakSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
} | ||
@@ -206,72 +364,75 @@ | ||
const connectionStore = new WeakMap(); | ||
const cleanupStore = new WeakMap(); | ||
const ENUMERATE = Symbol('enumerate'); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get$1(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
const rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
const result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function storeReaction(reaction) { | ||
// this will be used to save data for cleaning up later | ||
cleanupStore.set(reaction, new Set()); | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
const reactionsForObj = connectionStore.get(obj); | ||
let reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set$1(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
reactionsForKey.add(reaction); | ||
cleanupStore.get(reaction).add(reactionsForKey); | ||
} | ||
function iterateReactionsForKey(obj, key, fn) { | ||
const reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
reactionsForKey.forEach(fn); | ||
// save if the value changed because of this set operation | ||
// array 'length' is an exception here, because of it's exotic nature | ||
const valueChanged = key === 'length' || value !== obj[key]; | ||
// execute the set operation before running any reaction | ||
const result = Reflect.set(obj, key, value, receiver); | ||
// emit a warning and do not queue anything when another reaction is queued | ||
// from an already running reaction | ||
if (hasRunningReaction()) { | ||
console.error(`Mutating observables in reactions is forbidden. You set ${key} to ${value}.`); | ||
return result; | ||
} | ||
// do not queue reactions if it is a symbol keyed property | ||
// or the set operation resulted in no value change | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key !== 'symbol' && valueChanged && obj === proxyToRaw.get(receiver)) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return result; | ||
} | ||
function releaseReaction(reaction) { | ||
cleanupStore.get(reaction).forEach(releaseReactionKeyConnections, reaction); | ||
} | ||
function releaseReactionKeyConnections(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
const ENUMERATE = Symbol('enumerate'); | ||
const queuedReactions = new Set(); | ||
let runningReaction; | ||
const handlers = { get, ownKeys, set, deleteProperty }; | ||
function observe(reaction) { | ||
if (typeof reaction !== 'function') { | ||
throw new TypeError('Reactions must be functions.'); | ||
function deleteProperty(obj, key) { | ||
// save if the object had the key | ||
const hadKey = key in obj; | ||
// execute the delete operation before running any reaction | ||
const result = Reflect.deleteProperty(obj, key); | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && hadKey) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
// init basic data structures to save and cleanup (observable.prop -> reaction) connections later | ||
storeReaction(reaction); | ||
// run the reaction once to discover what observable properties it uses | ||
runReaction(reaction); | ||
return reaction; | ||
return result; | ||
} | ||
function unobserve(reaction) { | ||
// do not run this reaction anymore, even if it is already queued | ||
queuedReactions.delete(reaction); | ||
// release every (observable.prop -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
var handlers = { get: get$1, ownKeys, set: set$1, deleteProperty }; | ||
function unqueue(reaction) { | ||
// do not run this reaction, if it is not queued again by a prop mutation | ||
queuedReactions.delete(reaction); | ||
} | ||
function exec(reaction) { | ||
runReaction(reaction); | ||
} | ||
function isObservable(obj) { | ||
@@ -284,6 +445,5 @@ if (typeof obj !== 'object') { | ||
function observable(obj) { | ||
obj = obj || {}; | ||
function observable(obj = {}) { | ||
if (typeof obj !== 'object') { | ||
throw new TypeError('First argument must be an object or undefined'); | ||
throw new TypeError('Observable first argument must be an object or undefined'); | ||
} | ||
@@ -325,103 +485,5 @@ // if it is already an observable, return it | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
const rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
const result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (runningReaction && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
// do not register reactions if it is a symbol keyed property | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key === 'symbol' || obj !== proxyToRaw.get(receiver)) { | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
// only queue reactions if the set operation resulted in a value change | ||
// array 'length' property is an exception from this, because of it's exotic nature | ||
if (key === 'length' || value !== obj[key]) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
function deleteProperty(obj, key) { | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && key in obj) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.deleteProperty(obj, key); | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// register a new reaction running task, if there are no reactions queued yet | ||
if (!queuedReactions.size) { | ||
nextTick(runQueuedReactions); | ||
} | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
queuedReactions.add(reaction); | ||
} | ||
function runQueuedReactions() { | ||
queuedReactions.forEach(runReaction); | ||
queuedReactions.clear(); | ||
} | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
function runReaction(reaction) { | ||
try { | ||
runningReaction = reaction; | ||
reaction(); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
exports.nextTick = nextTick; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.observable = observable; | ||
exports.isObservable = isObservable; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.unqueue = unqueue; | ||
exports.exec = exec; |
@@ -1,1 +0,1 @@ | ||
'use strict';Object.defineProperty(exports,'__esModule',{value:!0});const promise=Promise.resolve();function nextTick(a){return promise.then(a)}const proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get$1(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)||(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.add.apply(b,arguments)):c.add.apply(this,arguments)}function set$1(a,b){const c=proxyToRaw.get(this),d=getPrototypeOf(this);return c?(d.get.call(c,a)!==b&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),d.set.apply(c,arguments)):d.set.apply(this,arguments)}function deleteFn(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.delete.apply(b,arguments)):c.delete.apply(this,arguments)}function clear(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(a.size&&queueReactionsForKey(a,ITERATE),b.clear.apply(a,arguments)):b.clear.apply(this,arguments)}function forEach(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]);const connectionStore=new WeakMap,cleanupStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function storeReaction(a){cleanupStore.set(a,new Set)}function registerReactionForKey(a,b,c){const d=connectionStore.get(a);let e=d[b];e||(d[b]=e=new Set),e.add(c),cleanupStore.get(c).add(e)}function iterateReactionsForKey(a,b,c){const d=connectionStore.get(a)[b];d&&d.forEach(c)}function releaseReaction(a){cleanupStore.get(a).forEach(releaseReactionKeyConnections,a)}function releaseReactionKeyConnections(a){a.delete(this)}const ENUMERATE=Symbol('enumerate'),queuedReactions=new Set;let runningReaction;const handlers={get,ownKeys,set,deleteProperty};function observe(a){if('function'!=typeof a)throw new TypeError('Reactions must be functions.');return storeReaction(a),runReaction(a),a}function unobserve(a){queuedReactions.delete(a),releaseReaction(a)}function unqueue(a){queuedReactions.delete(a)}function exec(a){runReaction(a)}function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a){if(a=a||{},'object'!=typeof a)throw new TypeError('First argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){const b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){const b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}function get(a,b,c){const d=proxyToRaw.get(a)||a;if('$raw'===b)return d;const e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),runningReaction&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function set(a,b,c,d){return('object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c),'symbol'==typeof b||a!==proxyToRaw.get(d))?Reflect.set(a,b,c,d):(('length'===b||c!==a[b])&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.set(a,b,c,d))}function deleteProperty(a,b){return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.deleteProperty(a,b)}function queueReactionsForKey(a,b){queuedReactions.size||nextTick(runQueuedReactions),iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){queuedReactions.add(a)}function runQueuedReactions(){queuedReactions.forEach(runReaction),queuedReactions.clear()}function runReaction(a){try{runningReaction=a,a()}finally{runningReaction=void 0}}exports.nextTick=nextTick,exports.observable=observable,exports.isObservable=isObservable,exports.observe=observe,exports.unobserve=unobserve,exports.unqueue=unqueue,exports.exec=exec; | ||
'use strict';Object.defineProperty(exports,'__esModule',{value:!0});const connectionStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function registerReactionForKey(a,b,c){const d=connectionStore.get(a);let e=d[b];e?!e.has(c)&&(e.add(c),c.cleaners.push(e)):(d[b]=e=new Set,e.add(c),c.cleaners.push(e))}function iterateReactionsForKey(a,b,c){const d=connectionStore.get(a)[b];d&&Array.from(d).forEach(c)}function releaseReaction(a){a.cleaners&&a.cleaners.forEach(releaseReactionKeyConnection,a),a.cleaners=void 0}function releaseReactionKeyConnection(a){a.delete(this)}let runningReaction;function runAsReaction(a,b,c,d){if(a.unobserved)throw new Error(`Unobserved reactions can not be executed. You tried to run a reaction for ${b}`);releaseReaction(a),a.cleaners=[];try{return runningReaction=a,b.apply(c,d)}finally{runningReaction=void 0}}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function queueReactionsForKey(a,b){iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){'function'==typeof a.scheduler?a.scheduler(a):'object'==typeof a.scheduler?a.scheduler.add(a):a()}function hasRunningReaction(){return runningReaction!==void 0}const IS_REACTION=Symbol('is reaction');function observe(a,b={}){function c(){return runAsReaction(c,a,this,arguments)}if('function'!=typeof a)throw new TypeError(`The first argument must be a function instead of ${a}`);if(a[IS_REACTION])throw new TypeError('The first argument must not be an already observed reaction');if('object'!=typeof b||null===b)throw new TypeError(`The second argument must be an options object instead of ${b}`);return validateOptions(b),c.scheduler=b.scheduler,c.runId=0,c[IS_REACTION]=!0,b.lazy||c(),c}function validateOptions({lazy:b=!1,scheduler:a}){if('boolean'!=typeof b)throw new TypeError(`options.lazy must be a boolean or undefined instead of ${b}`);if('object'==typeof a&&null!==a){if('function'!=typeof a.add||'function'!=typeof a.delete)throw new TypeError('options.scheduler object must have an add and delete method');}else if(void 0!==a&&'function'!=typeof a)throw new TypeError(`options.scheduler must be a function, an object or undefined instead of ${a}`)}function unobserve(a){if('function'!=typeof a||!a[IS_REACTION])throw new TypeError(`The first argument must be a reaction instead of ${a}`);a.unobserved||(a.unobserved=!0,releaseReaction(a)),'object'==typeof a.scheduler&&a.scheduler.delete(a)}const proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.add.apply(this,arguments);const d=!c.has.call(b,a),e=c.add.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function set(a,b){const c=proxyToRaw.get(this),d=getPrototypeOf(this);if(!c)return d.set.apply(this,arguments);const e=d.get.call(c,a)!==b,f=d.set.apply(c,arguments);return e&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),f}function deleteFn(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.delete.apply(this,arguments);const d=c.has.call(b,a),e=c.delete.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function clear(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);if(!a)return b.clear.apply(this,arguments);const c=0!==a.size,d=b.clear.apply(a,arguments);return c&&queueReactionsForKey(a,ITERATE),d}function forEach(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]);const ENUMERATE=Symbol('enumerate');function get$1(a,b,c){const d=proxyToRaw.get(a)||a;if('$raw'===b)return d;const e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),hasRunningReaction()&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function set$1(a,b,c,d){'object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c);const e='length'===b||c!==a[b],f=Reflect.set(a,b,c,d);return hasRunningReaction()?(console.error(`Mutating observables in reactions is forbidden. You set ${b} to ${c}.`),f):('symbol'!=typeof b&&e&&a===proxyToRaw.get(d)&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),f)}function deleteProperty(a,b){const c=Reflect.deleteProperty(a,b);return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),c}var handlers={get:get$1,ownKeys,set:set$1,deleteProperty};function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a={}){if('object'!=typeof a)throw new TypeError('Observable first argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){const b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){const b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}exports.observe=observe,exports.unobserve=unobserve,exports.observable=observable,exports.isObservable=isObservable; |
@@ -1,9 +0,160 @@ | ||
var promise = Promise.resolve(); | ||
var connectionStore = new WeakMap(); | ||
// schedule the given task as a microtask | ||
// (this used to leak into after the next task when mixed with MutationObservers in Safari) | ||
function nextTick(task) { | ||
return promise.then(task); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
var reactionsForObj = connectionStore.get(obj); | ||
var reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} else if (!reactionsForKey.has(reaction)) { | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} | ||
} | ||
function iterateReactionsForKey(obj, key, reactionHandler) { | ||
var reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
// create a static copy of the reactions, before iterating them | ||
// to avoid infinite (iterate items: remove -> readd) loops | ||
Array.from(reactionsForKey).forEach(reactionHandler); | ||
} | ||
} | ||
function releaseReaction(reaction) { | ||
if (reaction.cleaners) { | ||
reaction.cleaners.forEach(releaseReactionKeyConnection, reaction); | ||
} | ||
reaction.cleaners = undefined; | ||
} | ||
function releaseReactionKeyConnection(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
var runningReaction; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// throw an error if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
throw new Error(("Unobserved reactions can not be executed. You tried to run a reaction for " + fn)); | ||
} | ||
// release the (obj -> key -> reactions) connections | ||
// and reset the cleaner connections | ||
releaseReaction(reaction); | ||
reaction.cleaners = []; | ||
try { | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
runningReaction = reaction; | ||
return fn.apply(context, args); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
// queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
reaction.scheduler(reaction); | ||
} else if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.add(reaction); | ||
} else { | ||
reaction(); | ||
} | ||
} | ||
function hasRunningReaction() { | ||
return runningReaction !== undefined; | ||
} | ||
var IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options) { | ||
if ( options === void 0 ) options = {}; | ||
if (typeof fn !== 'function') { | ||
throw new TypeError(("The first argument must be a function instead of " + fn)); | ||
} | ||
if (fn[IS_REACTION]) { | ||
throw new TypeError('The first argument must not be an already observed reaction'); | ||
} | ||
if (typeof options !== 'object' || options === null) { | ||
throw new TypeError(("The second argument must be an options object instead of " + options)); | ||
} | ||
validateOptions(options); | ||
// create a reaction from the passed function | ||
function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
} | ||
// save the scheduler on the reaction | ||
reaction.scheduler = options.scheduler; | ||
// runId will serve as a unique (incremental) id, which identifies the reaction's last run | ||
reaction.runId = 0; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
if (!options.lazy) { | ||
reaction(); | ||
} | ||
return reaction; | ||
} | ||
function validateOptions(ref) { | ||
var lazy = ref.lazy; if ( lazy === void 0 ) lazy = false; | ||
var scheduler = ref.scheduler; | ||
if (typeof lazy !== 'boolean') { | ||
throw new TypeError(("options.lazy must be a boolean or undefined instead of " + lazy)); | ||
} | ||
if (typeof scheduler === 'object' && scheduler !== null) { | ||
if (typeof scheduler.add !== 'function' || typeof scheduler.delete !== 'function') { | ||
throw new TypeError('options.scheduler object must have an add and delete method'); | ||
} | ||
} else if (scheduler !== undefined && typeof scheduler !== 'function') { | ||
throw new TypeError(("options.scheduler must be a function, an object or undefined instead of " + scheduler)); | ||
} | ||
} | ||
function unobserve(reaction) { | ||
if (typeof reaction !== 'function' || !reaction[IS_REACTION]) { | ||
throw new TypeError(("The first argument must be a reaction instead of " + reaction)); | ||
} | ||
// do nothing, if the reaction is already unobserved | ||
if (!reaction.unobserved) { | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.delete(reaction); | ||
} | ||
} | ||
var proxyToRaw = new WeakMap(); | ||
@@ -25,3 +176,3 @@ var rawToProxy = new WeakMap(); | ||
function get$1(key) { | ||
function get(key) { | ||
var rawContext = proxyToRaw.get(this); | ||
@@ -42,10 +193,13 @@ var proto = getPrototypeOf(this); | ||
} | ||
if (!proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = !proto.has.call(rawContext, value); | ||
var result = proto.add.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.add.apply(rawContext, arguments); | ||
return result; | ||
} | ||
function set$1(key, value) { | ||
function set(key, value) { | ||
var rawContext = proxyToRaw.get(this); | ||
@@ -56,7 +210,10 @@ var proto = getPrototypeOf(this); | ||
} | ||
if (proto.get.call(rawContext, key) !== value) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = proto.get.call(rawContext, key) !== value; | ||
var result = proto.set.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, key); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.set.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -70,7 +227,10 @@ | ||
} | ||
if (proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = proto.has.call(rawContext, value); | ||
var result = proto.delete.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.delete.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -84,6 +244,9 @@ | ||
} | ||
if (rawContext.size) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = rawContext.size !== 0; | ||
var result = proto.clear.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.clear.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -153,4 +316,4 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
@@ -166,13 +329,13 @@ map.clear = clear; | ||
function instrumentSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
set.clear = clear; | ||
set.forEach = forEach; | ||
set.keys = keys; | ||
set.values = values; | ||
set.entries = entries; | ||
set[Symbol.iterator] = iterator; | ||
Object.defineProperty(set, 'size', { get: getSize }); | ||
function instrumentSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
set$$1.clear = clear; | ||
set$$1.forEach = forEach; | ||
set$$1.keys = keys; | ||
set$$1.values = values; | ||
set$$1.entries = entries; | ||
set$$1[Symbol.iterator] = iterator; | ||
Object.defineProperty(set$$1, 'size', { get: getSize }); | ||
} | ||
@@ -182,11 +345,11 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
} | ||
function instrumentWeakSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
function instrumentWeakSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
} | ||
@@ -201,72 +364,75 @@ | ||
var connectionStore = new WeakMap(); | ||
var cleanupStore = new WeakMap(); | ||
var ENUMERATE = Symbol('enumerate'); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get$1(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
var rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
var result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function storeReaction(reaction) { | ||
// this will be used to save data for cleaning up later | ||
cleanupStore.set(reaction, new Set()); | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
var reactionsForObj = connectionStore.get(obj); | ||
var reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set$1(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
reactionsForKey.add(reaction); | ||
cleanupStore.get(reaction).add(reactionsForKey); | ||
} | ||
function iterateReactionsForKey(obj, key, fn) { | ||
var reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
reactionsForKey.forEach(fn); | ||
// save if the value changed because of this set operation | ||
// array 'length' is an exception here, because of it's exotic nature | ||
var valueChanged = key === 'length' || value !== obj[key]; | ||
// execute the set operation before running any reaction | ||
var result = Reflect.set(obj, key, value, receiver); | ||
// emit a warning and do not queue anything when another reaction is queued | ||
// from an already running reaction | ||
if (hasRunningReaction()) { | ||
console.error(("Mutating observables in reactions is forbidden. You set " + key + " to " + value + ".")); | ||
return result; | ||
} | ||
// do not queue reactions if it is a symbol keyed property | ||
// or the set operation resulted in no value change | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key !== 'symbol' && valueChanged && obj === proxyToRaw.get(receiver)) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return result; | ||
} | ||
function releaseReaction(reaction) { | ||
cleanupStore.get(reaction).forEach(releaseReactionKeyConnections, reaction); | ||
} | ||
function releaseReactionKeyConnections(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
var ENUMERATE = Symbol('enumerate'); | ||
var queuedReactions = new Set(); | ||
var runningReaction; | ||
var handlers = { get: get, ownKeys: ownKeys, set: set, deleteProperty: deleteProperty }; | ||
function observe(reaction) { | ||
if (typeof reaction !== 'function') { | ||
throw new TypeError('Reactions must be functions.'); | ||
function deleteProperty(obj, key) { | ||
// save if the object had the key | ||
var hadKey = key in obj; | ||
// execute the delete operation before running any reaction | ||
var result = Reflect.deleteProperty(obj, key); | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && hadKey) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
// init basic data structures to save and cleanup (observable.prop -> reaction) connections later | ||
storeReaction(reaction); | ||
// run the reaction once to discover what observable properties it uses | ||
runReaction(reaction); | ||
return reaction; | ||
return result; | ||
} | ||
function unobserve(reaction) { | ||
// do not run this reaction anymore, even if it is already queued | ||
queuedReactions.delete(reaction); | ||
// release every (observable.prop -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
var handlers = { get: get$1, ownKeys: ownKeys, set: set$1, deleteProperty: deleteProperty }; | ||
function unqueue(reaction) { | ||
// do not run this reaction, if it is not queued again by a prop mutation | ||
queuedReactions.delete(reaction); | ||
} | ||
function exec(reaction) { | ||
runReaction(reaction); | ||
} | ||
function isObservable(obj) { | ||
@@ -280,5 +446,6 @@ if (typeof obj !== 'object') { | ||
function observable(obj) { | ||
obj = obj || {}; | ||
if ( obj === void 0 ) obj = {}; | ||
if (typeof obj !== 'object') { | ||
throw new TypeError('First argument must be an object or undefined'); | ||
throw new TypeError('Observable first argument must be an object or undefined'); | ||
} | ||
@@ -320,97 +487,2 @@ // if it is already an observable, return it | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
var rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
var result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (runningReaction && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
// do not register reactions if it is a symbol keyed property | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key === 'symbol' || obj !== proxyToRaw.get(receiver)) { | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
// only queue reactions if the set operation resulted in a value change | ||
// array 'length' property is an exception from this, because of it's exotic nature | ||
if (key === 'length' || value !== obj[key]) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
function deleteProperty(obj, key) { | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && key in obj) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.deleteProperty(obj, key); | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// register a new reaction running task, if there are no reactions queued yet | ||
if (!queuedReactions.size) { | ||
nextTick(runQueuedReactions); | ||
} | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
queuedReactions.add(reaction); | ||
} | ||
function runQueuedReactions() { | ||
queuedReactions.forEach(runReaction); | ||
queuedReactions.clear(); | ||
} | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
function runReaction(reaction) { | ||
try { | ||
runningReaction = reaction; | ||
reaction(); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
export { nextTick, observable, isObservable, observe, unobserve, unqueue, exec }; | ||
export { observe, unobserve, observable, isObservable }; |
@@ -1,1 +0,1 @@ | ||
var promise=Promise.resolve();function nextTick(a){return promise.then(a)}var proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get$1(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)||(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.add.apply(b,arguments)):c.add.apply(this,arguments)}function set$1(a,b){var c=proxyToRaw.get(this),d=getPrototypeOf(this);return c?(d.get.call(c,a)!==b&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),d.set.apply(c,arguments)):d.set.apply(this,arguments)}function deleteFn(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.delete.apply(b,arguments)):c.delete.apply(this,arguments)}function clear(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(a.size&&queueReactionsForKey(a,ITERATE),b.clear.apply(a,arguments)):b.clear.apply(this,arguments)}function forEach(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]),connectionStore=new WeakMap,cleanupStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function storeReaction(a){cleanupStore.set(a,new Set)}function registerReactionForKey(a,b,c){var d=connectionStore.get(a),e=d[b];e||(d[b]=e=new Set),e.add(c),cleanupStore.get(c).add(e)}function iterateReactionsForKey(a,b,c){var d=connectionStore.get(a)[b];d&&d.forEach(c)}function releaseReaction(a){cleanupStore.get(a).forEach(releaseReactionKeyConnections,a)}function releaseReactionKeyConnections(a){a.delete(this)}var runningReaction,ENUMERATE=Symbol('enumerate'),queuedReactions=new Set,handlers={get:get,ownKeys:ownKeys,set:set,deleteProperty:deleteProperty};function observe(a){if('function'!=typeof a)throw new TypeError('Reactions must be functions.');return storeReaction(a),runReaction(a),a}function unobserve(a){queuedReactions.delete(a),releaseReaction(a)}function unqueue(a){queuedReactions.delete(a)}function exec(a){runReaction(a)}function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a){if(a=a||{},'object'!=typeof a)throw new TypeError('First argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){var b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){var b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}function get(a,b,c){var d=proxyToRaw.get(a)||a;if('$raw'===b)return d;var e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),runningReaction&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function set(a,b,c,d){return('object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c),'symbol'==typeof b||a!==proxyToRaw.get(d))?Reflect.set(a,b,c,d):(('length'===b||c!==a[b])&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.set(a,b,c,d))}function deleteProperty(a,b){return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.deleteProperty(a,b)}function queueReactionsForKey(a,b){queuedReactions.size||nextTick(runQueuedReactions),iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){queuedReactions.add(a)}function runQueuedReactions(){queuedReactions.forEach(runReaction),queuedReactions.clear()}function runReaction(a){try{runningReaction=a,a()}finally{runningReaction=void 0}}export{nextTick,observable,isObservable,observe,unobserve,unqueue,exec}; | ||
var connectionStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function registerReactionForKey(a,b,c){var d=connectionStore.get(a),e=d[b];e?!e.has(c)&&(e.add(c),c.cleaners.push(e)):(d[b]=e=new Set,e.add(c),c.cleaners.push(e))}function iterateReactionsForKey(a,b,c){var d=connectionStore.get(a)[b];d&&Array.from(d).forEach(c)}function releaseReaction(a){a.cleaners&&a.cleaners.forEach(releaseReactionKeyConnection,a),a.cleaners=void 0}function releaseReactionKeyConnection(a){a.delete(this)}var runningReaction;function runAsReaction(a,b,c,d){if(a.unobserved)throw new Error('Unobserved reactions can not be executed. You tried to run a reaction for '+b);releaseReaction(a),a.cleaners=[];try{return runningReaction=a,b.apply(c,d)}finally{runningReaction=void 0}}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function queueReactionsForKey(a,b){iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){'function'==typeof a.scheduler?a.scheduler(a):'object'==typeof a.scheduler?a.scheduler.add(a):a()}function hasRunningReaction(){return runningReaction!==void 0}var IS_REACTION=Symbol('is reaction');function observe(a,b){function c(){return runAsReaction(c,a,this,arguments)}if(void 0===b&&(b={}),'function'!=typeof a)throw new TypeError('The first argument must be a function instead of '+a);if(a[IS_REACTION])throw new TypeError('The first argument must not be an already observed reaction');if('object'!=typeof b||null===b)throw new TypeError('The second argument must be an options object instead of '+b);return validateOptions(b),c.scheduler=b.scheduler,c.runId=0,c[IS_REACTION]=!0,b.lazy||c(),c}function validateOptions(a){var b=a.lazy;void 0===b&&(b=!1);var c=a.scheduler;if('boolean'!=typeof b)throw new TypeError('options.lazy must be a boolean or undefined instead of '+b);if('object'==typeof c&&null!==c){if('function'!=typeof c.add||'function'!=typeof c.delete)throw new TypeError('options.scheduler object must have an add and delete method');}else if(void 0!==c&&'function'!=typeof c)throw new TypeError('options.scheduler must be a function, an object or undefined instead of '+c)}function unobserve(a){if('function'!=typeof a||!a[IS_REACTION])throw new TypeError('The first argument must be a reaction instead of '+a);a.unobserved||(a.unobserved=!0,releaseReaction(a)),'object'==typeof a.scheduler&&a.scheduler.delete(a)}var proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.add.apply(this,arguments);var d=!c.has.call(b,a),e=c.add.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function set(a,b){var c=proxyToRaw.get(this),d=getPrototypeOf(this);if(!c)return d.set.apply(this,arguments);var e=d.get.call(c,a)!==b,f=d.set.apply(c,arguments);return e&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),f}function deleteFn(a){var b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.delete.apply(this,arguments);var d=c.has.call(b,a),e=c.delete.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function clear(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);if(!a)return b.clear.apply(this,arguments);var c=0!==a.size,d=b.clear.apply(a,arguments);return c&&queueReactionsForKey(a,ITERATE),d}function forEach(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){var a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]),ENUMERATE=Symbol('enumerate');function get$1(a,b,c){var d=proxyToRaw.get(a)||a;if('$raw'===b)return d;var e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),hasRunningReaction()&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function set$1(a,b,c,d){'object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c);var e='length'===b||c!==a[b],f=Reflect.set(a,b,c,d);return hasRunningReaction()?(console.error('Mutating observables in reactions is forbidden. You set '+b+' to '+c+'.'),f):('symbol'!=typeof b&&e&&a===proxyToRaw.get(d)&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),f)}function deleteProperty(a,b){var c=Reflect.deleteProperty(a,b);return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),c}var handlers={get:get$1,ownKeys:ownKeys,set:set$1,deleteProperty:deleteProperty};function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a){if(void 0===a&&(a={}),'object'!=typeof a)throw new TypeError('Observable first argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){var b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){var b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}export{observe,unobserve,observable,isObservable}; |
@@ -1,9 +0,155 @@ | ||
const promise = Promise.resolve(); | ||
const connectionStore = new WeakMap(); | ||
// schedule the given task as a microtask | ||
// (this used to leak into after the next task when mixed with MutationObservers in Safari) | ||
function nextTick(task) { | ||
return promise.then(task); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
const reactionsForObj = connectionStore.get(obj); | ||
let reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} else if (!reactionsForKey.has(reaction)) { | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} | ||
} | ||
function iterateReactionsForKey(obj, key, reactionHandler) { | ||
const reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
// create a static copy of the reactions, before iterating them | ||
// to avoid infinite (iterate items: remove -> readd) loops | ||
Array.from(reactionsForKey).forEach(reactionHandler); | ||
} | ||
} | ||
function releaseReaction(reaction) { | ||
if (reaction.cleaners) { | ||
reaction.cleaners.forEach(releaseReactionKeyConnection, reaction); | ||
} | ||
reaction.cleaners = undefined; | ||
} | ||
function releaseReactionKeyConnection(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
let runningReaction; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// throw an error if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
throw new Error(`Unobserved reactions can not be executed. You tried to run a reaction for ${fn}`); | ||
} | ||
// release the (obj -> key -> reactions) connections | ||
// and reset the cleaner connections | ||
releaseReaction(reaction); | ||
reaction.cleaners = []; | ||
try { | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
runningReaction = reaction; | ||
return fn.apply(context, args); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
// queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
reaction.scheduler(reaction); | ||
} else if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.add(reaction); | ||
} else { | ||
reaction(); | ||
} | ||
} | ||
function hasRunningReaction() { | ||
return runningReaction !== undefined; | ||
} | ||
const IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options = {}) { | ||
if (typeof fn !== 'function') { | ||
throw new TypeError(`The first argument must be a function instead of ${fn}`); | ||
} | ||
if (fn[IS_REACTION]) { | ||
throw new TypeError('The first argument must not be an already observed reaction'); | ||
} | ||
if (typeof options !== 'object' || options === null) { | ||
throw new TypeError(`The second argument must be an options object instead of ${options}`); | ||
} | ||
validateOptions(options); | ||
// create a reaction from the passed function | ||
function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
} | ||
// save the scheduler on the reaction | ||
reaction.scheduler = options.scheduler; | ||
// runId will serve as a unique (incremental) id, which identifies the reaction's last run | ||
reaction.runId = 0; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
if (!options.lazy) { | ||
reaction(); | ||
} | ||
return reaction; | ||
} | ||
function validateOptions({ lazy = false, scheduler }) { | ||
if (typeof lazy !== 'boolean') { | ||
throw new TypeError(`options.lazy must be a boolean or undefined instead of ${lazy}`); | ||
} | ||
if (typeof scheduler === 'object' && scheduler !== null) { | ||
if (typeof scheduler.add !== 'function' || typeof scheduler.delete !== 'function') { | ||
throw new TypeError('options.scheduler object must have an add and delete method'); | ||
} | ||
} else if (scheduler !== undefined && typeof scheduler !== 'function') { | ||
throw new TypeError(`options.scheduler must be a function, an object or undefined instead of ${scheduler}`); | ||
} | ||
} | ||
function unobserve(reaction) { | ||
if (typeof reaction !== 'function' || !reaction[IS_REACTION]) { | ||
throw new TypeError(`The first argument must be a reaction instead of ${reaction}`); | ||
} | ||
// do nothing, if the reaction is already unobserved | ||
if (!reaction.unobserved) { | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.delete(reaction); | ||
} | ||
} | ||
const proxyToRaw = new WeakMap(); | ||
@@ -25,3 +171,3 @@ const rawToProxy = new WeakMap(); | ||
function get$1(key) { | ||
function get(key) { | ||
const rawContext = proxyToRaw.get(this); | ||
@@ -42,10 +188,13 @@ const proto = getPrototypeOf(this); | ||
} | ||
if (!proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = !proto.has.call(rawContext, value); | ||
const result = proto.add.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.add.apply(rawContext, arguments); | ||
return result; | ||
} | ||
function set$1(key, value) { | ||
function set(key, value) { | ||
const rawContext = proxyToRaw.get(this); | ||
@@ -56,7 +205,10 @@ const proto = getPrototypeOf(this); | ||
} | ||
if (proto.get.call(rawContext, key) !== value) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = proto.get.call(rawContext, key) !== value; | ||
const result = proto.set.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, key); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.set.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -70,7 +222,10 @@ | ||
} | ||
if (proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = proto.has.call(rawContext, value); | ||
const result = proto.delete.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.delete.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -84,6 +239,9 @@ | ||
} | ||
if (rawContext.size) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = rawContext.size !== 0; | ||
const result = proto.clear.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.clear.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -153,4 +311,4 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
@@ -166,13 +324,13 @@ map.clear = clear; | ||
function instrumentSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
set.clear = clear; | ||
set.forEach = forEach; | ||
set.keys = keys; | ||
set.values = values; | ||
set.entries = entries; | ||
set[Symbol.iterator] = iterator; | ||
Object.defineProperty(set, 'size', { get: getSize }); | ||
function instrumentSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
set$$1.clear = clear; | ||
set$$1.forEach = forEach; | ||
set$$1.keys = keys; | ||
set$$1.values = values; | ||
set$$1.entries = entries; | ||
set$$1[Symbol.iterator] = iterator; | ||
Object.defineProperty(set$$1, 'size', { get: getSize }); | ||
} | ||
@@ -182,11 +340,11 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
} | ||
function instrumentWeakSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
function instrumentWeakSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
} | ||
@@ -201,72 +359,75 @@ | ||
const connectionStore = new WeakMap(); | ||
const cleanupStore = new WeakMap(); | ||
const ENUMERATE = Symbol('enumerate'); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get$1(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
const rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
const result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function storeReaction(reaction) { | ||
// this will be used to save data for cleaning up later | ||
cleanupStore.set(reaction, new Set()); | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
const reactionsForObj = connectionStore.get(obj); | ||
let reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set$1(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
reactionsForKey.add(reaction); | ||
cleanupStore.get(reaction).add(reactionsForKey); | ||
} | ||
function iterateReactionsForKey(obj, key, fn) { | ||
const reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
reactionsForKey.forEach(fn); | ||
// save if the value changed because of this set operation | ||
// array 'length' is an exception here, because of it's exotic nature | ||
const valueChanged = key === 'length' || value !== obj[key]; | ||
// execute the set operation before running any reaction | ||
const result = Reflect.set(obj, key, value, receiver); | ||
// emit a warning and do not queue anything when another reaction is queued | ||
// from an already running reaction | ||
if (hasRunningReaction()) { | ||
console.error(`Mutating observables in reactions is forbidden. You set ${key} to ${value}.`); | ||
return result; | ||
} | ||
// do not queue reactions if it is a symbol keyed property | ||
// or the set operation resulted in no value change | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key !== 'symbol' && valueChanged && obj === proxyToRaw.get(receiver)) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return result; | ||
} | ||
function releaseReaction(reaction) { | ||
cleanupStore.get(reaction).forEach(releaseReactionKeyConnections, reaction); | ||
} | ||
function releaseReactionKeyConnections(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
const ENUMERATE = Symbol('enumerate'); | ||
const queuedReactions = new Set(); | ||
let runningReaction; | ||
const handlers = { get, ownKeys, set, deleteProperty }; | ||
function observe(reaction) { | ||
if (typeof reaction !== 'function') { | ||
throw new TypeError('Reactions must be functions.'); | ||
function deleteProperty(obj, key) { | ||
// save if the object had the key | ||
const hadKey = key in obj; | ||
// execute the delete operation before running any reaction | ||
const result = Reflect.deleteProperty(obj, key); | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && hadKey) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
// init basic data structures to save and cleanup (observable.prop -> reaction) connections later | ||
storeReaction(reaction); | ||
// run the reaction once to discover what observable properties it uses | ||
runReaction(reaction); | ||
return reaction; | ||
return result; | ||
} | ||
function unobserve(reaction) { | ||
// do not run this reaction anymore, even if it is already queued | ||
queuedReactions.delete(reaction); | ||
// release every (observable.prop -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
var handlers = { get: get$1, ownKeys, set: set$1, deleteProperty }; | ||
function unqueue(reaction) { | ||
// do not run this reaction, if it is not queued again by a prop mutation | ||
queuedReactions.delete(reaction); | ||
} | ||
function exec(reaction) { | ||
runReaction(reaction); | ||
} | ||
function isObservable(obj) { | ||
@@ -279,6 +440,5 @@ if (typeof obj !== 'object') { | ||
function observable(obj) { | ||
obj = obj || {}; | ||
function observable(obj = {}) { | ||
if (typeof obj !== 'object') { | ||
throw new TypeError('First argument must be an object or undefined'); | ||
throw new TypeError('Observable first argument must be an object or undefined'); | ||
} | ||
@@ -320,97 +480,2 @@ // if it is already an observable, return it | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
const rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
const result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (runningReaction && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
// do not register reactions if it is a symbol keyed property | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key === 'symbol' || obj !== proxyToRaw.get(receiver)) { | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
// only queue reactions if the set operation resulted in a value change | ||
// array 'length' property is an exception from this, because of it's exotic nature | ||
if (key === 'length' || value !== obj[key]) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
function deleteProperty(obj, key) { | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && key in obj) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.deleteProperty(obj, key); | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// register a new reaction running task, if there are no reactions queued yet | ||
if (!queuedReactions.size) { | ||
nextTick(runQueuedReactions); | ||
} | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
queuedReactions.add(reaction); | ||
} | ||
function runQueuedReactions() { | ||
queuedReactions.forEach(runReaction); | ||
queuedReactions.clear(); | ||
} | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
function runReaction(reaction) { | ||
try { | ||
runningReaction = reaction; | ||
reaction(); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
export { nextTick, observable, isObservable, observe, unobserve, unqueue, exec }; | ||
export { observe, unobserve, observable, isObservable }; |
@@ -1,1 +0,1 @@ | ||
const promise=Promise.resolve();function nextTick(a){return promise.then(a)}const proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get$1(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)||(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.add.apply(b,arguments)):c.add.apply(this,arguments)}function set$1(a,b){const c=proxyToRaw.get(this),d=getPrototypeOf(this);return c?(d.get.call(c,a)!==b&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),d.set.apply(c,arguments)):d.set.apply(this,arguments)}function deleteFn(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(c.has.call(b,a)&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),c.delete.apply(b,arguments)):c.delete.apply(this,arguments)}function clear(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(a.size&&queueReactionsForKey(a,ITERATE),b.clear.apply(a,arguments)):b.clear.apply(this,arguments)}function forEach(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get$1,a.set=set$1,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]);const connectionStore=new WeakMap,cleanupStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function storeReaction(a){cleanupStore.set(a,new Set)}function registerReactionForKey(a,b,c){const d=connectionStore.get(a);let e=d[b];e||(d[b]=e=new Set),e.add(c),cleanupStore.get(c).add(e)}function iterateReactionsForKey(a,b,c){const d=connectionStore.get(a)[b];d&&d.forEach(c)}function releaseReaction(a){cleanupStore.get(a).forEach(releaseReactionKeyConnections,a)}function releaseReactionKeyConnections(a){a.delete(this)}const ENUMERATE=Symbol('enumerate'),queuedReactions=new Set;let runningReaction;const handlers={get,ownKeys,set,deleteProperty};function observe(a){if('function'!=typeof a)throw new TypeError('Reactions must be functions.');return storeReaction(a),runReaction(a),a}function unobserve(a){queuedReactions.delete(a),releaseReaction(a)}function unqueue(a){queuedReactions.delete(a)}function exec(a){runReaction(a)}function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a){if(a=a||{},'object'!=typeof a)throw new TypeError('First argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){const b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){const b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}function get(a,b,c){const d=proxyToRaw.get(a)||a;if('$raw'===b)return d;const e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),runningReaction&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function set(a,b,c,d){return('object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c),'symbol'==typeof b||a!==proxyToRaw.get(d))?Reflect.set(a,b,c,d):(('length'===b||c!==a[b])&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.set(a,b,c,d))}function deleteProperty(a,b){return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),Reflect.deleteProperty(a,b)}function queueReactionsForKey(a,b){queuedReactions.size||nextTick(runQueuedReactions),iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){queuedReactions.add(a)}function runQueuedReactions(){queuedReactions.forEach(runReaction),queuedReactions.clear()}function runReaction(a){try{runningReaction=a,a()}finally{runningReaction=void 0}}export{nextTick,observable,isObservable,observe,unobserve,unqueue,exec}; | ||
const connectionStore=new WeakMap;function storeObservable(a){connectionStore.set(a,Object.create(null))}function registerReactionForKey(a,b,c){const d=connectionStore.get(a);let e=d[b];e?!e.has(c)&&(e.add(c),c.cleaners.push(e)):(d[b]=e=new Set,e.add(c),c.cleaners.push(e))}function iterateReactionsForKey(a,b,c){const d=connectionStore.get(a)[b];d&&Array.from(d).forEach(c)}function releaseReaction(a){a.cleaners&&a.cleaners.forEach(releaseReactionKeyConnection,a),a.cleaners=void 0}function releaseReactionKeyConnection(a){a.delete(this)}let runningReaction;function runAsReaction(a,b,c,d){if(a.unobserved)throw new Error(`Unobserved reactions can not be executed. You tried to run a reaction for ${b}`);releaseReaction(a),a.cleaners=[];try{return runningReaction=a,b.apply(c,d)}finally{runningReaction=void 0}}function registerRunningReactionForKey(a,b){runningReaction&®isterReactionForKey(a,b,runningReaction)}function queueReactionsForKey(a,b){iterateReactionsForKey(a,b,queueReaction)}function queueReaction(a){'function'==typeof a.scheduler?a.scheduler(a):'object'==typeof a.scheduler?a.scheduler.add(a):a()}function hasRunningReaction(){return runningReaction!==void 0}const IS_REACTION=Symbol('is reaction');function observe(a,b={}){function c(){return runAsReaction(c,a,this,arguments)}if('function'!=typeof a)throw new TypeError(`The first argument must be a function instead of ${a}`);if(a[IS_REACTION])throw new TypeError('The first argument must not be an already observed reaction');if('object'!=typeof b||null===b)throw new TypeError(`The second argument must be an options object instead of ${b}`);return validateOptions(b),c.scheduler=b.scheduler,c.runId=0,c[IS_REACTION]=!0,b.lazy||c(),c}function validateOptions({lazy:b=!1,scheduler:a}){if('boolean'!=typeof b)throw new TypeError(`options.lazy must be a boolean or undefined instead of ${b}`);if('object'==typeof a&&null!==a){if('function'!=typeof a.add||'function'!=typeof a.delete)throw new TypeError('options.scheduler object must have an add and delete method');}else if(void 0!==a&&'function'!=typeof a)throw new TypeError(`options.scheduler must be a function, an object or undefined instead of ${a}`)}function unobserve(a){if('function'!=typeof a||!a[IS_REACTION])throw new TypeError(`The first argument must be a reaction instead of ${a}`);a.unobserved||(a.unobserved=!0,releaseReaction(a)),'object'==typeof a.scheduler&&a.scheduler.delete(a)}const proxyToRaw=new WeakMap,rawToProxy=new WeakMap,ITERATE=Symbol('iterate'),getPrototypeOf=Object.getPrototypeOf;function has(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function get(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);return b?(registerRunningReactionForKey(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function add(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.add.apply(this,arguments);const d=!c.has.call(b,a),e=c.add.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function set(a,b){const c=proxyToRaw.get(this),d=getPrototypeOf(this);if(!c)return d.set.apply(this,arguments);const e=d.get.call(c,a)!==b,f=d.set.apply(c,arguments);return e&&(queueReactionsForKey(c,a),queueReactionsForKey(c,ITERATE)),f}function deleteFn(a){const b=proxyToRaw.get(this),c=getPrototypeOf(this);if(!b)return c.delete.apply(this,arguments);const d=c.has.call(b,a),e=c.delete.apply(b,arguments);return d&&(queueReactionsForKey(b,a),queueReactionsForKey(b,ITERATE)),e}function clear(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);if(!a)return b.clear.apply(this,arguments);const c=0!==a.size,d=b.clear.apply(a,arguments);return c&&queueReactionsForKey(a,ITERATE),d}function forEach(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function keys(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function values(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function entries(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function iterator(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function getSize(){const a=proxyToRaw.get(this),b=getPrototypeOf(this);return a?(registerRunningReactionForKey(a,ITERATE),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function instrumentMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentSet(a){a.has=has,a.add=add,a.delete=deleteFn,a.clear=clear,a.forEach=forEach,a.keys=keys,a.values=values,a.entries=entries,a[Symbol.iterator]=iterator,Object.defineProperty(a,'size',{get:getSize})}function instrumentWeakMap(a){a.has=has,a.get=get,a.set=set,a.delete=deleteFn}function instrumentWeakSet(a){a.has=has,a.add=add,a.delete=deleteFn}var instrumentations=new Map([[Map.prototype,instrumentMap],[Set.prototype,instrumentSet],[WeakMap.prototype,instrumentWeakMap],[WeakSet.prototype,instrumentWeakSet],[Date.prototype,!1],[RegExp.prototype,!1]]);const ENUMERATE=Symbol('enumerate');function get$1(a,b,c){const d=proxyToRaw.get(a)||a;if('$raw'===b)return d;const e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(registerRunningReactionForKey(d,b),hasRunningReaction()&&'object'==typeof e&&null!==e?observable(e):rawToProxy.get(e)||e)}function ownKeys(a){return registerRunningReactionForKey(a,ENUMERATE),Reflect.ownKeys(a)}function set$1(a,b,c,d){'object'==typeof c&&null!==c&&(c=proxyToRaw.get(c)||c);const e='length'===b||c!==a[b],f=Reflect.set(a,b,c,d);return hasRunningReaction()?(console.error(`Mutating observables in reactions is forbidden. You set ${b} to ${c}.`),f):('symbol'!=typeof b&&e&&a===proxyToRaw.get(d)&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),f)}function deleteProperty(a,b){const c=Reflect.deleteProperty(a,b);return'symbol'!=typeof b&&b in a&&(queueReactionsForKey(a,b),queueReactionsForKey(a,ENUMERATE)),c}var handlers={get:get$1,ownKeys,set:set$1,deleteProperty};function isObservable(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return proxyToRaw.has(a)}function observable(a={}){if('object'!=typeof a)throw new TypeError('Observable first argument must be an object or undefined');return proxyToRaw.has(a)?a:rawToProxy.get(a)||instrumentObservable(a)||createObservable(a)}function instrumentObservable(a){const b=instrumentations.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function createObservable(a){const b=new Proxy(a,handlers);return storeObservable(a),proxyToRaw.set(b,a),rawToProxy.set(a,b),b}export{observe,unobserve,observable,isObservable}; |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(factory((global.easyState = {}))); | ||
(factory((global.observer = {}))); | ||
}(this, (function (exports) { 'use strict'; | ||
var promise = Promise.resolve(); | ||
var connectionStore = new WeakMap(); | ||
// schedule the given task as a microtask | ||
// (this used to leak into after the next task when mixed with MutationObservers in Safari) | ||
function nextTick(task) { | ||
return promise.then(task); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
var reactionsForObj = connectionStore.get(obj); | ||
var reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} else if (!reactionsForKey.has(reaction)) { | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} | ||
} | ||
function iterateReactionsForKey(obj, key, reactionHandler) { | ||
var reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
// create a static copy of the reactions, before iterating them | ||
// to avoid infinite (iterate items: remove -> readd) loops | ||
Array.from(reactionsForKey).forEach(reactionHandler); | ||
} | ||
} | ||
function releaseReaction(reaction) { | ||
if (reaction.cleaners) { | ||
reaction.cleaners.forEach(releaseReactionKeyConnection, reaction); | ||
} | ||
reaction.cleaners = undefined; | ||
} | ||
function releaseReactionKeyConnection(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
var runningReaction; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// throw an error if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
throw new Error(("Unobserved reactions can not be executed. You tried to run a reaction for " + fn)); | ||
} | ||
// release the (obj -> key -> reactions) connections | ||
// and reset the cleaner connections | ||
releaseReaction(reaction); | ||
reaction.cleaners = []; | ||
try { | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
runningReaction = reaction; | ||
return fn.apply(context, args); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
// queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
reaction.scheduler(reaction); | ||
} else if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.add(reaction); | ||
} else { | ||
reaction(); | ||
} | ||
} | ||
function hasRunningReaction() { | ||
return runningReaction !== undefined; | ||
} | ||
var IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options) { | ||
if ( options === void 0 ) options = {}; | ||
if (typeof fn !== 'function') { | ||
throw new TypeError(("The first argument must be a function instead of " + fn)); | ||
} | ||
if (fn[IS_REACTION]) { | ||
throw new TypeError('The first argument must not be an already observed reaction'); | ||
} | ||
if (typeof options !== 'object' || options === null) { | ||
throw new TypeError(("The second argument must be an options object instead of " + options)); | ||
} | ||
validateOptions(options); | ||
// create a reaction from the passed function | ||
function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
} | ||
// save the scheduler on the reaction | ||
reaction.scheduler = options.scheduler; | ||
// runId will serve as a unique (incremental) id, which identifies the reaction's last run | ||
reaction.runId = 0; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
if (!options.lazy) { | ||
reaction(); | ||
} | ||
return reaction; | ||
} | ||
function validateOptions(ref) { | ||
var lazy = ref.lazy; if ( lazy === void 0 ) lazy = false; | ||
var scheduler = ref.scheduler; | ||
if (typeof lazy !== 'boolean') { | ||
throw new TypeError(("options.lazy must be a boolean or undefined instead of " + lazy)); | ||
} | ||
if (typeof scheduler === 'object' && scheduler !== null) { | ||
if (typeof scheduler.add !== 'function' || typeof scheduler.delete !== 'function') { | ||
throw new TypeError('options.scheduler object must have an add and delete method'); | ||
} | ||
} else if (scheduler !== undefined && typeof scheduler !== 'function') { | ||
throw new TypeError(("options.scheduler must be a function, an object or undefined instead of " + scheduler)); | ||
} | ||
} | ||
function unobserve(reaction) { | ||
if (typeof reaction !== 'function' || !reaction[IS_REACTION]) { | ||
throw new TypeError(("The first argument must be a reaction instead of " + reaction)); | ||
} | ||
// do nothing, if the reaction is already unobserved | ||
if (!reaction.unobserved) { | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.delete(reaction); | ||
} | ||
} | ||
var proxyToRaw = new WeakMap(); | ||
@@ -31,3 +182,3 @@ var rawToProxy = new WeakMap(); | ||
function get$1(key) { | ||
function get(key) { | ||
var rawContext = proxyToRaw.get(this); | ||
@@ -48,10 +199,13 @@ var proto = getPrototypeOf(this); | ||
} | ||
if (!proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = !proto.has.call(rawContext, value); | ||
var result = proto.add.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.add.apply(rawContext, arguments); | ||
return result; | ||
} | ||
function set$1(key, value) { | ||
function set(key, value) { | ||
var rawContext = proxyToRaw.get(this); | ||
@@ -62,7 +216,10 @@ var proto = getPrototypeOf(this); | ||
} | ||
if (proto.get.call(rawContext, key) !== value) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = proto.get.call(rawContext, key) !== value; | ||
var result = proto.set.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, key); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.set.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -76,7 +233,10 @@ | ||
} | ||
if (proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = proto.has.call(rawContext, value); | ||
var result = proto.delete.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.delete.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -90,6 +250,9 @@ | ||
} | ||
if (rawContext.size) { | ||
// forward the operation before queueing reactions | ||
var valueChanged = rawContext.size !== 0; | ||
var result = proto.clear.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.clear.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -159,4 +322,4 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
@@ -172,13 +335,13 @@ map.clear = clear; | ||
function instrumentSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
set.clear = clear; | ||
set.forEach = forEach; | ||
set.keys = keys; | ||
set.values = values; | ||
set.entries = entries; | ||
set[Symbol.iterator] = iterator; | ||
Object.defineProperty(set, 'size', { get: getSize }); | ||
function instrumentSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
set$$1.clear = clear; | ||
set$$1.forEach = forEach; | ||
set$$1.keys = keys; | ||
set$$1.values = values; | ||
set$$1.entries = entries; | ||
set$$1[Symbol.iterator] = iterator; | ||
Object.defineProperty(set$$1, 'size', { get: getSize }); | ||
} | ||
@@ -188,11 +351,11 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
} | ||
function instrumentWeakSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
function instrumentWeakSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
} | ||
@@ -207,72 +370,75 @@ | ||
var connectionStore = new WeakMap(); | ||
var cleanupStore = new WeakMap(); | ||
var ENUMERATE = Symbol('enumerate'); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get$1(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
var rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
var result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function storeReaction(reaction) { | ||
// this will be used to save data for cleaning up later | ||
cleanupStore.set(reaction, new Set()); | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
var reactionsForObj = connectionStore.get(obj); | ||
var reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set$1(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
reactionsForKey.add(reaction); | ||
cleanupStore.get(reaction).add(reactionsForKey); | ||
} | ||
function iterateReactionsForKey(obj, key, fn) { | ||
var reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
reactionsForKey.forEach(fn); | ||
// save if the value changed because of this set operation | ||
// array 'length' is an exception here, because of it's exotic nature | ||
var valueChanged = key === 'length' || value !== obj[key]; | ||
// execute the set operation before running any reaction | ||
var result = Reflect.set(obj, key, value, receiver); | ||
// emit a warning and do not queue anything when another reaction is queued | ||
// from an already running reaction | ||
if (hasRunningReaction()) { | ||
console.error(("Mutating observables in reactions is forbidden. You set " + key + " to " + value + ".")); | ||
return result; | ||
} | ||
// do not queue reactions if it is a symbol keyed property | ||
// or the set operation resulted in no value change | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key !== 'symbol' && valueChanged && obj === proxyToRaw.get(receiver)) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return result; | ||
} | ||
function releaseReaction(reaction) { | ||
cleanupStore.get(reaction).forEach(releaseReactionKeyConnections, reaction); | ||
} | ||
function releaseReactionKeyConnections(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
var ENUMERATE = Symbol('enumerate'); | ||
var queuedReactions = new Set(); | ||
var runningReaction; | ||
var handlers = { get: get, ownKeys: ownKeys, set: set, deleteProperty: deleteProperty }; | ||
function observe(reaction) { | ||
if (typeof reaction !== 'function') { | ||
throw new TypeError('Reactions must be functions.'); | ||
function deleteProperty(obj, key) { | ||
// save if the object had the key | ||
var hadKey = key in obj; | ||
// execute the delete operation before running any reaction | ||
var result = Reflect.deleteProperty(obj, key); | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && hadKey) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
// init basic data structures to save and cleanup (observable.prop -> reaction) connections later | ||
storeReaction(reaction); | ||
// run the reaction once to discover what observable properties it uses | ||
runReaction(reaction); | ||
return reaction; | ||
return result; | ||
} | ||
function unobserve(reaction) { | ||
// do not run this reaction anymore, even if it is already queued | ||
queuedReactions.delete(reaction); | ||
// release every (observable.prop -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
var handlers = { get: get$1, ownKeys: ownKeys, set: set$1, deleteProperty: deleteProperty }; | ||
function unqueue(reaction) { | ||
// do not run this reaction, if it is not queued again by a prop mutation | ||
queuedReactions.delete(reaction); | ||
} | ||
function exec(reaction) { | ||
runReaction(reaction); | ||
} | ||
function isObservable(obj) { | ||
@@ -286,5 +452,6 @@ if (typeof obj !== 'object') { | ||
function observable(obj) { | ||
obj = obj || {}; | ||
if ( obj === void 0 ) obj = {}; | ||
if (typeof obj !== 'object') { | ||
throw new TypeError('First argument must be an object or undefined'); | ||
throw new TypeError('Observable first argument must be an object or undefined'); | ||
} | ||
@@ -326,104 +493,6 @@ // if it is already an observable, return it | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
var rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
var result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (runningReaction && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
// do not register reactions if it is a symbol keyed property | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key === 'symbol' || obj !== proxyToRaw.get(receiver)) { | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
// only queue reactions if the set operation resulted in a value change | ||
// array 'length' property is an exception from this, because of it's exotic nature | ||
if (key === 'length' || value !== obj[key]) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
function deleteProperty(obj, key) { | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && key in obj) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.deleteProperty(obj, key); | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// register a new reaction running task, if there are no reactions queued yet | ||
if (!queuedReactions.size) { | ||
nextTick(runQueuedReactions); | ||
} | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
queuedReactions.add(reaction); | ||
} | ||
function runQueuedReactions() { | ||
queuedReactions.forEach(runReaction); | ||
queuedReactions.clear(); | ||
} | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
function runReaction(reaction) { | ||
try { | ||
runningReaction = reaction; | ||
reaction(); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
exports.nextTick = nextTick; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.observable = observable; | ||
exports.isObservable = isObservable; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.unqueue = unqueue; | ||
exports.exec = exec; | ||
@@ -430,0 +499,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
@@ -1,1 +0,1 @@ | ||
(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports):'function'==typeof define&&define.amd?define(['exports'],b):b(a.easyState={})})(this,function(a){'use strict';function b(a){return D.then(a)}function c(a){var b=E.get(this),c=H(this);return b?(x(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function d(a){var b=E.get(this),c=H(this);return b?(x(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function e(a){var b=E.get(this),c=H(this);return b?(c.has.call(b,a)||(y(b,a),y(b,G)),c.add.apply(b,arguments)):c.add.apply(this,arguments)}function f(a,b){var c=E.get(this),d=H(this);return c?(d.get.call(c,a)!==b&&(y(c,a),y(c,G)),d.set.apply(c,arguments)):d.set.apply(this,arguments)}function g(a){var b=E.get(this),c=H(this);return b?(c.has.call(b,a)&&(y(b,a),y(b,G)),c.delete.apply(b,arguments)):c.delete.apply(this,arguments)}function h(){var a=E.get(this),b=H(this);return a?(a.size&&y(a,G),b.clear.apply(a,arguments)):b.clear.apply(this,arguments)}function i(){var a=E.get(this),b=H(this);return a?(x(a,G),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function j(){var a=E.get(this),b=H(this);return a?(x(a,G),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function k(){var a=E.get(this),b=H(this);return a?(x(a,G),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function l(){var a=E.get(this),b=H(this);return a?(x(a,G),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function m(){var a=E.get(this),b=H(this);return a?(x(a,G),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function n(){var a=E.get(this),b=H(this);return a?(x(a,G),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function o(a){J.set(a,Object.create(null))}function p(a){K.set(a,new Set)}function q(a,b,c){var d=J.get(a),e=d[b];e||(d[b]=e=new Set),e.add(c),K.get(c).add(e)}function r(a,b,c){var d=J.get(a)[b];d&&d.forEach(c)}function s(a){K.get(a).forEach(t,a)}function t(a){a.delete(this)}function u(a){if(a=a||{},'object'!=typeof a)throw new TypeError('First argument must be an object or undefined');return E.has(a)?a:F.get(a)||v(a)||w(a)}function v(a){var b=I.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function w(a){var b=new Proxy(a,N);return o(a),E.set(b,a),F.set(a,b),b}function x(a,b){C&&q(a,b,C)}function y(a,c){M.size||b(A),r(a,c,z)}function z(a){M.add(a)}function A(){M.forEach(B),M.clear()}function B(a){try{C=a,a()}finally{C=void 0}}var C,D=Promise.resolve(),E=new WeakMap,F=new WeakMap,G=Symbol('iterate'),H=Object.getPrototypeOf,I=new Map([[Map.prototype,function(a){a.has=c,a.get=d,a.set=f,a.delete=g,a.clear=h,a.forEach=i,a.keys=j,a.values=k,a.entries=l,a[Symbol.iterator]=m,Object.defineProperty(a,'size',{get:n})}],[Set.prototype,function(a){a.has=c,a.add=e,a.delete=g,a.clear=h,a.forEach=i,a.keys=j,a.values=k,a.entries=l,a[Symbol.iterator]=m,Object.defineProperty(a,'size',{get:n})}],[WeakMap.prototype,function(a){a.has=c,a.get=d,a.set=f,a.delete=g}],[WeakSet.prototype,function(a){a.has=c,a.add=e,a.delete=g}],[Date.prototype,!1],[RegExp.prototype,!1]]),J=new WeakMap,K=new WeakMap,L=Symbol('enumerate'),M=new Set,N={get:function(a,b,c){var d=E.get(a)||a;if('$raw'===b)return d;var e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(x(d,b),C&&'object'==typeof e&&null!==e?u(e):F.get(e)||e)},ownKeys:function(a){return x(a,L),Reflect.ownKeys(a)},set:function(a,b,c,d){return('object'==typeof c&&null!==c&&(c=E.get(c)||c),'symbol'==typeof b||a!==E.get(d))?Reflect.set(a,b,c,d):(('length'===b||c!==a[b])&&(y(a,b),y(a,L)),Reflect.set(a,b,c,d))},deleteProperty:function(a,b){return'symbol'!=typeof b&&b in a&&(y(a,b),y(a,L)),Reflect.deleteProperty(a,b)}};a.nextTick=b,a.observable=u,a.isObservable=function(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return E.has(a)},a.observe=function(a){if('function'!=typeof a)throw new TypeError('Reactions must be functions.');return p(a),B(a),a},a.unobserve=function(a){M.delete(a),s(a)},a.unqueue=function(a){M.delete(a)},a.exec=function(a){B(a)},Object.defineProperty(a,'__esModule',{value:!0})}); | ||
(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports):'function'==typeof define&&define.amd?define(['exports'],b):b(a.observer={})})(this,function(a){'use strict';function b(a){C.set(a,Object.create(null))}function c(a,b,c){var d=C.get(a),e=d[b];e?!e.has(c)&&(e.add(c),c.cleaners.push(e)):(d[b]=e=new Set,e.add(c),c.cleaners.push(e))}function d(a,b,c){var d=C.get(a)[b];d&&Array.from(d).forEach(c)}function e(a){a.cleaners&&a.cleaners.forEach(f,a),a.cleaners=void 0}function f(a){a.delete(this)}function g(a,b,c,d){if(a.unobserved)throw new Error('Unobserved reactions can not be executed. You tried to run a reaction for '+b);e(a),a.cleaners=[];try{return B=a,b.apply(c,d)}finally{B=void 0}}function h(a,b){B&&c(a,b,B)}function i(a,b){d(a,b,j)}function j(a){'function'==typeof a.scheduler?a.scheduler(a):'object'==typeof a.scheduler?a.scheduler.add(a):a()}function k(){return B!==void 0}function l(a){var b=a.lazy;void 0===b&&(b=!1);var c=a.scheduler;if('boolean'!=typeof b)throw new TypeError('options.lazy must be a boolean or undefined instead of '+b);if('object'==typeof c&&null!==c){if('function'!=typeof c.add||'function'!=typeof c.delete)throw new TypeError('options.scheduler object must have an add and delete method');}else if(void 0!==c&&'function'!=typeof c)throw new TypeError('options.scheduler must be a function, an object or undefined instead of '+c)}function m(a){var b=E.get(this),c=H(this);return b?(h(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function n(a){var b=E.get(this),c=H(this);return b?(h(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function o(a){var b=E.get(this),c=H(this);if(!b)return c.add.apply(this,arguments);var d=!c.has.call(b,a),e=c.add.apply(b,arguments);return d&&(i(b,a),i(b,G)),e}function p(a,b){var c=E.get(this),d=H(this);if(!c)return d.set.apply(this,arguments);var e=d.get.call(c,a)!==b,f=d.set.apply(c,arguments);return e&&(i(c,a),i(c,G)),f}function q(a){var b=E.get(this),c=H(this);if(!b)return c.delete.apply(this,arguments);var d=c.has.call(b,a),e=c.delete.apply(b,arguments);return d&&(i(b,a),i(b,G)),e}function r(){var a=E.get(this),b=H(this);if(!a)return b.clear.apply(this,arguments);var c=0!==a.size,d=b.clear.apply(a,arguments);return c&&i(a,G),d}function s(){var a=E.get(this),b=H(this);return a?(h(a,G),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function t(){var a=E.get(this),b=H(this);return a?(h(a,G),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function u(){var a=E.get(this),b=H(this);return a?(h(a,G),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function v(){var a=E.get(this),b=H(this);return a?(h(a,G),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function w(){var a=E.get(this),b=H(this);return a?(h(a,G),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function x(){var a=E.get(this),b=H(this);return a?(h(a,G),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function y(a){if(void 0===a&&(a={}),'object'!=typeof a)throw new TypeError('Observable first argument must be an object or undefined');return E.has(a)?a:F.get(a)||z(a)||A(a)}function z(a){var b=I.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function A(a){var c=new Proxy(a,K);return b(a),E.set(c,a),F.set(a,c),c}var B,C=new WeakMap,D=Symbol('is reaction'),E=new WeakMap,F=new WeakMap,G=Symbol('iterate'),H=Object.getPrototypeOf,I=new Map([[Map.prototype,function(a){a.has=m,a.get=n,a.set=p,a.delete=q,a.clear=r,a.forEach=s,a.keys=t,a.values=u,a.entries=v,a[Symbol.iterator]=w,Object.defineProperty(a,'size',{get:x})}],[Set.prototype,function(a){a.has=m,a.add=o,a.delete=q,a.clear=r,a.forEach=s,a.keys=t,a.values=u,a.entries=v,a[Symbol.iterator]=w,Object.defineProperty(a,'size',{get:x})}],[WeakMap.prototype,function(a){a.has=m,a.get=n,a.set=p,a.delete=q}],[WeakSet.prototype,function(a){a.has=m,a.add=o,a.delete=q}],[Date.prototype,!1],[RegExp.prototype,!1]]),J=Symbol('enumerate'),K={get:function(a,b,c){var d=E.get(a)||a;if('$raw'===b)return d;var e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(h(d,b),k()&&'object'==typeof e&&null!==e?y(e):F.get(e)||e)},ownKeys:function(a){return h(a,J),Reflect.ownKeys(a)},set:function(a,b,c,d){'object'==typeof c&&null!==c&&(c=E.get(c)||c);var e='length'===b||c!==a[b],f=Reflect.set(a,b,c,d);return k()?(console.error('Mutating observables in reactions is forbidden. You set '+b+' to '+c+'.'),f):('symbol'!=typeof b&&e&&a===E.get(d)&&(i(a,b),i(a,J)),f)},deleteProperty:function(a,b){var c=Reflect.deleteProperty(a,b);return'symbol'!=typeof b&&b in a&&(i(a,b),i(a,J)),c}};a.observe=function(a,b){function c(){return g(c,a,this,arguments)}if(void 0===b&&(b={}),'function'!=typeof a)throw new TypeError('The first argument must be a function instead of '+a);if(a[D])throw new TypeError('The first argument must not be an already observed reaction');if('object'!=typeof b||null===b)throw new TypeError('The second argument must be an options object instead of '+b);return l(b),c.scheduler=b.scheduler,c.runId=0,c[D]=!0,b.lazy||c(),c},a.unobserve=function(a){if('function'!=typeof a||!a[D])throw new TypeError('The first argument must be a reaction instead of '+a);a.unobserved||(a.unobserved=!0,e(a)),'object'==typeof a.scheduler&&a.scheduler.delete(a)},a.observable=y,a.isObservable=function(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return E.has(a)},Object.defineProperty(a,'__esModule',{value:!0})}); |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(factory((global.easyState = {}))); | ||
(factory((global.observer = {}))); | ||
}(this, (function (exports) { 'use strict'; | ||
const promise = Promise.resolve(); | ||
const connectionStore = new WeakMap(); | ||
// schedule the given task as a microtask | ||
// (this used to leak into after the next task when mixed with MutationObservers in Safari) | ||
function nextTick(task) { | ||
return promise.then(task); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
const reactionsForObj = connectionStore.get(obj); | ||
let reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} else if (!reactionsForKey.has(reaction)) { | ||
// save the fact that the key is used by the reaction during its current run | ||
reactionsForKey.add(reaction); | ||
reaction.cleaners.push(reactionsForKey); | ||
} | ||
} | ||
function iterateReactionsForKey(obj, key, reactionHandler) { | ||
const reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
// create a static copy of the reactions, before iterating them | ||
// to avoid infinite (iterate items: remove -> readd) loops | ||
Array.from(reactionsForKey).forEach(reactionHandler); | ||
} | ||
} | ||
function releaseReaction(reaction) { | ||
if (reaction.cleaners) { | ||
reaction.cleaners.forEach(releaseReactionKeyConnection, reaction); | ||
} | ||
reaction.cleaners = undefined; | ||
} | ||
function releaseReactionKeyConnection(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
let runningReaction; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// throw an error if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
throw new Error(`Unobserved reactions can not be executed. You tried to run a reaction for ${fn}`); | ||
} | ||
// release the (obj -> key -> reactions) connections | ||
// and reset the cleaner connections | ||
releaseReaction(reaction); | ||
reaction.cleaners = []; | ||
try { | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
runningReaction = reaction; | ||
return fn.apply(context, args); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
// queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
reaction.scheduler(reaction); | ||
} else if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.add(reaction); | ||
} else { | ||
reaction(); | ||
} | ||
} | ||
function hasRunningReaction() { | ||
return runningReaction !== undefined; | ||
} | ||
const IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options = {}) { | ||
if (typeof fn !== 'function') { | ||
throw new TypeError(`The first argument must be a function instead of ${fn}`); | ||
} | ||
if (fn[IS_REACTION]) { | ||
throw new TypeError('The first argument must not be an already observed reaction'); | ||
} | ||
if (typeof options !== 'object' || options === null) { | ||
throw new TypeError(`The second argument must be an options object instead of ${options}`); | ||
} | ||
validateOptions(options); | ||
// create a reaction from the passed function | ||
function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
} | ||
// save the scheduler on the reaction | ||
reaction.scheduler = options.scheduler; | ||
// runId will serve as a unique (incremental) id, which identifies the reaction's last run | ||
reaction.runId = 0; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
if (!options.lazy) { | ||
reaction(); | ||
} | ||
return reaction; | ||
} | ||
function validateOptions({ lazy = false, scheduler }) { | ||
if (typeof lazy !== 'boolean') { | ||
throw new TypeError(`options.lazy must be a boolean or undefined instead of ${lazy}`); | ||
} | ||
if (typeof scheduler === 'object' && scheduler !== null) { | ||
if (typeof scheduler.add !== 'function' || typeof scheduler.delete !== 'function') { | ||
throw new TypeError('options.scheduler object must have an add and delete method'); | ||
} | ||
} else if (scheduler !== undefined && typeof scheduler !== 'function') { | ||
throw new TypeError(`options.scheduler must be a function, an object or undefined instead of ${scheduler}`); | ||
} | ||
} | ||
function unobserve(reaction) { | ||
if (typeof reaction !== 'function' || !reaction[IS_REACTION]) { | ||
throw new TypeError(`The first argument must be a reaction instead of ${reaction}`); | ||
} | ||
// do nothing, if the reaction is already unobserved | ||
if (!reaction.unobserved) { | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
reaction.scheduler.delete(reaction); | ||
} | ||
} | ||
const proxyToRaw = new WeakMap(); | ||
@@ -31,3 +177,3 @@ const rawToProxy = new WeakMap(); | ||
function get$1(key) { | ||
function get(key) { | ||
const rawContext = proxyToRaw.get(this); | ||
@@ -48,10 +194,13 @@ const proto = getPrototypeOf(this); | ||
} | ||
if (!proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = !proto.has.call(rawContext, value); | ||
const result = proto.add.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.add.apply(rawContext, arguments); | ||
return result; | ||
} | ||
function set$1(key, value) { | ||
function set(key, value) { | ||
const rawContext = proxyToRaw.get(this); | ||
@@ -62,7 +211,10 @@ const proto = getPrototypeOf(this); | ||
} | ||
if (proto.get.call(rawContext, key) !== value) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = proto.get.call(rawContext, key) !== value; | ||
const result = proto.set.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, key); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.set.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -76,7 +228,10 @@ | ||
} | ||
if (proto.has.call(rawContext, value)) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = proto.has.call(rawContext, value); | ||
const result = proto.delete.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, value); | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.delete.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -90,6 +245,9 @@ | ||
} | ||
if (rawContext.size) { | ||
// forward the operation before queueing reactions | ||
const valueChanged = rawContext.size !== 0; | ||
const result = proto.clear.apply(rawContext, arguments); | ||
if (valueChanged) { | ||
queueReactionsForKey(rawContext, ITERATE); | ||
} | ||
return proto.clear.apply(rawContext, arguments); | ||
return result; | ||
} | ||
@@ -159,4 +317,4 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
@@ -172,13 +330,13 @@ map.clear = clear; | ||
function instrumentSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
set.clear = clear; | ||
set.forEach = forEach; | ||
set.keys = keys; | ||
set.values = values; | ||
set.entries = entries; | ||
set[Symbol.iterator] = iterator; | ||
Object.defineProperty(set, 'size', { get: getSize }); | ||
function instrumentSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
set$$1.clear = clear; | ||
set$$1.forEach = forEach; | ||
set$$1.keys = keys; | ||
set$$1.values = values; | ||
set$$1.entries = entries; | ||
set$$1[Symbol.iterator] = iterator; | ||
Object.defineProperty(set$$1, 'size', { get: getSize }); | ||
} | ||
@@ -188,11 +346,11 @@ | ||
map.has = has; | ||
map.get = get$1; | ||
map.set = set$1; | ||
map.get = get; | ||
map.set = set; | ||
map.delete = deleteFn; | ||
} | ||
function instrumentWeakSet(set) { | ||
set.has = has; | ||
set.add = add; | ||
set.delete = deleteFn; | ||
function instrumentWeakSet(set$$1) { | ||
set$$1.has = has; | ||
set$$1.add = add; | ||
set$$1.delete = deleteFn; | ||
} | ||
@@ -207,72 +365,75 @@ | ||
const connectionStore = new WeakMap(); | ||
const cleanupStore = new WeakMap(); | ||
const ENUMERATE = Symbol('enumerate'); | ||
function storeObservable(obj) { | ||
// this will be used to save (obj.key -> reaction) connections later | ||
connectionStore.set(obj, Object.create(null)); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get$1(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
const rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
const result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function storeReaction(reaction) { | ||
// this will be used to save data for cleaning up later | ||
cleanupStore.set(reaction, new Set()); | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
function registerReactionForKey(obj, key, reaction) { | ||
const reactionsForObj = connectionStore.get(obj); | ||
let reactionsForKey = reactionsForObj[key]; | ||
if (!reactionsForKey) { | ||
reactionsForObj[key] = reactionsForKey = new Set(); | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set$1(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
reactionsForKey.add(reaction); | ||
cleanupStore.get(reaction).add(reactionsForKey); | ||
} | ||
function iterateReactionsForKey(obj, key, fn) { | ||
const reactionsForKey = connectionStore.get(obj)[key]; | ||
if (reactionsForKey) { | ||
reactionsForKey.forEach(fn); | ||
// save if the value changed because of this set operation | ||
// array 'length' is an exception here, because of it's exotic nature | ||
const valueChanged = key === 'length' || value !== obj[key]; | ||
// execute the set operation before running any reaction | ||
const result = Reflect.set(obj, key, value, receiver); | ||
// emit a warning and do not queue anything when another reaction is queued | ||
// from an already running reaction | ||
if (hasRunningReaction()) { | ||
console.error(`Mutating observables in reactions is forbidden. You set ${key} to ${value}.`); | ||
return result; | ||
} | ||
// do not queue reactions if it is a symbol keyed property | ||
// or the set operation resulted in no value change | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key !== 'symbol' && valueChanged && obj === proxyToRaw.get(receiver)) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return result; | ||
} | ||
function releaseReaction(reaction) { | ||
cleanupStore.get(reaction).forEach(releaseReactionKeyConnections, reaction); | ||
} | ||
function releaseReactionKeyConnections(reactionsForKey) { | ||
reactionsForKey.delete(this); | ||
} | ||
const ENUMERATE = Symbol('enumerate'); | ||
const queuedReactions = new Set(); | ||
let runningReaction; | ||
const handlers = { get, ownKeys, set, deleteProperty }; | ||
function observe(reaction) { | ||
if (typeof reaction !== 'function') { | ||
throw new TypeError('Reactions must be functions.'); | ||
function deleteProperty(obj, key) { | ||
// save if the object had the key | ||
const hadKey = key in obj; | ||
// execute the delete operation before running any reaction | ||
const result = Reflect.deleteProperty(obj, key); | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && hadKey) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
// init basic data structures to save and cleanup (observable.prop -> reaction) connections later | ||
storeReaction(reaction); | ||
// run the reaction once to discover what observable properties it uses | ||
runReaction(reaction); | ||
return reaction; | ||
return result; | ||
} | ||
function unobserve(reaction) { | ||
// do not run this reaction anymore, even if it is already queued | ||
queuedReactions.delete(reaction); | ||
// release every (observable.prop -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
var handlers = { get: get$1, ownKeys, set: set$1, deleteProperty }; | ||
function unqueue(reaction) { | ||
// do not run this reaction, if it is not queued again by a prop mutation | ||
queuedReactions.delete(reaction); | ||
} | ||
function exec(reaction) { | ||
runReaction(reaction); | ||
} | ||
function isObservable(obj) { | ||
@@ -285,6 +446,5 @@ if (typeof obj !== 'object') { | ||
function observable(obj) { | ||
obj = obj || {}; | ||
function observable(obj = {}) { | ||
if (typeof obj !== 'object') { | ||
throw new TypeError('First argument must be an object or undefined'); | ||
throw new TypeError('Observable first argument must be an object or undefined'); | ||
} | ||
@@ -326,104 +486,6 @@ // if it is already an observable, return it | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(obj, key, receiver) { | ||
// make sure to use the raw object here | ||
const rawObj = proxyToRaw.get(obj) || obj; | ||
// expose the raw object on observable.$raw | ||
if (key === '$raw') { | ||
return rawObj; | ||
} | ||
const result = Reflect.get(obj, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for these cases | ||
if (typeof key === 'symbol' || typeof result === 'function') { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForKey(rawObj, key); | ||
// if we are inside a reaction and observable.prop is an object wrap it in an observable too | ||
// this is needed to intercept property access on that object too (dynamic observable tree) | ||
if (runningReaction && typeof result === 'object' && result !== null) { | ||
return observable(result); | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return rawToProxy.get(result) || result; | ||
} | ||
function ownKeys(obj) { | ||
registerRunningReactionForKey(obj, ENUMERATE); | ||
return Reflect.ownKeys(obj); | ||
} | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForKey(obj, key) { | ||
if (runningReaction) { | ||
registerReactionForKey(obj, key, runningReaction); | ||
} | ||
} | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(obj, key, value, receiver) { | ||
// make sure to do not pollute the raw object with observables | ||
if (typeof value === 'object' && value !== null) { | ||
value = proxyToRaw.get(value) || value; | ||
} | ||
// do not register reactions if it is a symbol keyed property | ||
// or if the target of the operation is not the raw object (possible because of prototypal inheritance) | ||
if (typeof key === 'symbol' || obj !== proxyToRaw.get(receiver)) { | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
// only queue reactions if the set operation resulted in a value change | ||
// array 'length' property is an exception from this, because of it's exotic nature | ||
if (key === 'length' || value !== obj[key]) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.set(obj, key, value, receiver); | ||
} | ||
function deleteProperty(obj, key) { | ||
// only queue reactions for non symbol keyed property delete which resulted in an actual change | ||
if (typeof key !== 'symbol' && key in obj) { | ||
queueReactionsForKey(obj, key); | ||
queueReactionsForKey(obj, ENUMERATE); | ||
} | ||
return Reflect.deleteProperty(obj, key); | ||
} | ||
function queueReactionsForKey(obj, key) { | ||
// register a new reaction running task, if there are no reactions queued yet | ||
if (!queuedReactions.size) { | ||
nextTick(runQueuedReactions); | ||
} | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
iterateReactionsForKey(obj, key, queueReaction); | ||
} | ||
function queueReaction(reaction) { | ||
queuedReactions.add(reaction); | ||
} | ||
function runQueuedReactions() { | ||
queuedReactions.forEach(runReaction); | ||
queuedReactions.clear(); | ||
} | ||
// set the reaction as the currently running one | ||
// this is required so that we can create (observable.prop -> reaction) pairs in the get trap | ||
function runReaction(reaction) { | ||
try { | ||
runningReaction = reaction; | ||
reaction(); | ||
} finally { | ||
// always remove the currently running flag from the reaction when it stops execution | ||
runningReaction = undefined; | ||
} | ||
} | ||
exports.nextTick = nextTick; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.observable = observable; | ||
exports.isObservable = isObservable; | ||
exports.observe = observe; | ||
exports.unobserve = unobserve; | ||
exports.unqueue = unqueue; | ||
exports.exec = exec; | ||
@@ -430,0 +492,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
@@ -1,1 +0,1 @@ | ||
(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports):'function'==typeof define&&define.amd?define(['exports'],b):b(a.easyState={})})(this,function(a){'use strict';function b(a){return C.then(a)}function c(a){const b=D.get(this),c=G(this);return b?(x(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function d(a){const b=D.get(this),c=G(this);return b?(x(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function e(a){const b=D.get(this),c=G(this);return b?(c.has.call(b,a)||(y(b,a),y(b,F)),c.add.apply(b,arguments)):c.add.apply(this,arguments)}function f(a,b){const c=D.get(this),d=G(this);return c?(d.get.call(c,a)!==b&&(y(c,a),y(c,F)),d.set.apply(c,arguments)):d.set.apply(this,arguments)}function g(a){const b=D.get(this),c=G(this);return b?(c.has.call(b,a)&&(y(b,a),y(b,F)),c.delete.apply(b,arguments)):c.delete.apply(this,arguments)}function h(){const a=D.get(this),b=G(this);return a?(a.size&&y(a,F),b.clear.apply(a,arguments)):b.clear.apply(this,arguments)}function i(){const a=D.get(this),b=G(this);return a?(x(a,F),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function j(){const a=D.get(this),b=G(this);return a?(x(a,F),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function k(){const a=D.get(this),b=G(this);return a?(x(a,F),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function l(){const a=D.get(this),b=G(this);return a?(x(a,F),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function m(){const a=D.get(this),b=G(this);return a?(x(a,F),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function n(){const a=D.get(this),b=G(this);return a?(x(a,F),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function o(a){I.set(a,Object.create(null))}function p(a){J.set(a,new Set)}function q(a,b,c){const d=I.get(a);let e=d[b];e||(d[b]=e=new Set),e.add(c),J.get(c).add(e)}function r(a,b,c){const d=I.get(a)[b];d&&d.forEach(c)}function s(a){J.get(a).forEach(t,a)}function t(a){a.delete(this)}function u(a){if(a=a||{},'object'!=typeof a)throw new TypeError('First argument must be an object or undefined');return D.has(a)?a:E.get(a)||v(a)||w(a)}function v(a){const b=H.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function w(a){const b=new Proxy(a,N);return o(a),D.set(b,a),E.set(a,b),b}function x(a,b){M&&q(a,b,M)}function y(a,c){L.size||b(A),r(a,c,z)}function z(a){L.add(a)}function A(){L.forEach(B),L.clear()}function B(a){try{M=a,a()}finally{M=void 0}}const C=Promise.resolve(),D=new WeakMap,E=new WeakMap,F=Symbol('iterate'),G=Object.getPrototypeOf;var H=new Map([[Map.prototype,function(a){a.has=c,a.get=d,a.set=f,a.delete=g,a.clear=h,a.forEach=i,a.keys=j,a.values=k,a.entries=l,a[Symbol.iterator]=m,Object.defineProperty(a,'size',{get:n})}],[Set.prototype,function(a){a.has=c,a.add=e,a.delete=g,a.clear=h,a.forEach=i,a.keys=j,a.values=k,a.entries=l,a[Symbol.iterator]=m,Object.defineProperty(a,'size',{get:n})}],[WeakMap.prototype,function(a){a.has=c,a.get=d,a.set=f,a.delete=g}],[WeakSet.prototype,function(a){a.has=c,a.add=e,a.delete=g}],[Date.prototype,!1],[RegExp.prototype,!1]]);const I=new WeakMap,J=new WeakMap,K=Symbol('enumerate'),L=new Set;let M;const N={get:function(a,b,c){const d=D.get(a)||a;if('$raw'===b)return d;const e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(x(d,b),M&&'object'==typeof e&&null!==e?u(e):E.get(e)||e)},ownKeys:function(a){return x(a,K),Reflect.ownKeys(a)},set:function(a,b,c,d){return('object'==typeof c&&null!==c&&(c=D.get(c)||c),'symbol'==typeof b||a!==D.get(d))?Reflect.set(a,b,c,d):(('length'===b||c!==a[b])&&(y(a,b),y(a,K)),Reflect.set(a,b,c,d))},deleteProperty:function(a,b){return'symbol'!=typeof b&&b in a&&(y(a,b),y(a,K)),Reflect.deleteProperty(a,b)}};a.nextTick=b,a.observable=u,a.isObservable=function(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return D.has(a)},a.observe=function(a){if('function'!=typeof a)throw new TypeError('Reactions must be functions.');return p(a),B(a),a},a.unobserve=function(a){L.delete(a),s(a)},a.unqueue=function(a){L.delete(a)},a.exec=function(a){B(a)},Object.defineProperty(a,'__esModule',{value:!0})}); | ||
(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports):'function'==typeof define&&define.amd?define(['exports'],b):b(a.observer={})})(this,function(a){'use strict';function b(a){B.set(a,Object.create(null))}function c(a,b,c){const d=B.get(a);let e=d[b];e?!e.has(c)&&(e.add(c),c.cleaners.push(e)):(d[b]=e=new Set,e.add(c),c.cleaners.push(e))}function d(a,b,c){const d=B.get(a)[b];d&&Array.from(d).forEach(c)}function e(a){a.cleaners&&a.cleaners.forEach(f,a),a.cleaners=void 0}function f(a){a.delete(this)}function g(a,b,c,d){if(a.unobserved)throw new Error(`Unobserved reactions can not be executed. You tried to run a reaction for ${b}`);e(a),a.cleaners=[];try{return C=a,b.apply(c,d)}finally{C=void 0}}function h(a,b){C&&c(a,b,C)}function i(a,b){d(a,b,j)}function j(a){'function'==typeof a.scheduler?a.scheduler(a):'object'==typeof a.scheduler?a.scheduler.add(a):a()}function k(){return C!==void 0}function l({lazy:b=!1,scheduler:a}){if('boolean'!=typeof b)throw new TypeError(`options.lazy must be a boolean or undefined instead of ${b}`);if('object'==typeof a&&null!==a){if('function'!=typeof a.add||'function'!=typeof a.delete)throw new TypeError('options.scheduler object must have an add and delete method');}else if(void 0!==a&&'function'!=typeof a)throw new TypeError(`options.scheduler must be a function, an object or undefined instead of ${a}`)}function m(a){const b=E.get(this),c=H(this);return b?(h(b,a),c.has.apply(b,arguments)):c.has.apply(this,arguments)}function n(a){const b=E.get(this),c=H(this);return b?(h(b,a),c.get.apply(b,arguments)):c.get.apply(this,arguments)}function o(a){const b=E.get(this),c=H(this);if(!b)return c.add.apply(this,arguments);const d=!c.has.call(b,a),e=c.add.apply(b,arguments);return d&&(i(b,a),i(b,G)),e}function p(a,b){const c=E.get(this),d=H(this);if(!c)return d.set.apply(this,arguments);const e=d.get.call(c,a)!==b,f=d.set.apply(c,arguments);return e&&(i(c,a),i(c,G)),f}function q(a){const b=E.get(this),c=H(this);if(!b)return c.delete.apply(this,arguments);const d=c.has.call(b,a),e=c.delete.apply(b,arguments);return d&&(i(b,a),i(b,G)),e}function r(){const a=E.get(this),b=H(this);if(!a)return b.clear.apply(this,arguments);const c=0!==a.size,d=b.clear.apply(a,arguments);return c&&i(a,G),d}function s(){const a=E.get(this),b=H(this);return a?(h(a,G),b.forEach.apply(a,arguments)):b.forEach.apply(this,arguments)}function t(){const a=E.get(this),b=H(this);return a?(h(a,G),b.keys.apply(a,arguments)):b.keys.apply(this,arguments)}function u(){const a=E.get(this),b=H(this);return a?(h(a,G),b.values.apply(a,arguments)):b.values.apply(this,arguments)}function v(){const a=E.get(this),b=H(this);return a?(h(a,G),b.entries.apply(a,arguments)):b.entries.apply(this,arguments)}function w(){const a=E.get(this),b=H(this);return a?(h(a,G),b[Symbol.iterator].apply(a,arguments)):b[Symbol.iterator].apply(this,arguments)}function x(){const a=E.get(this),b=H(this);return a?(h(a,G),Reflect.get(b,'size',a)):Reflect.get(b,'size',this)}function y(a={}){if('object'!=typeof a)throw new TypeError('Observable first argument must be an object or undefined');return E.has(a)?a:F.get(a)||z(a)||A(a)}function z(a){const b=I.get(Object.getPrototypeOf(a));return!1===b?a:void('function'==typeof b&&b(a))}function A(a){const c=new Proxy(a,K);return b(a),E.set(c,a),F.set(a,c),c}const B=new WeakMap;let C;const D=Symbol('is reaction'),E=new WeakMap,F=new WeakMap,G=Symbol('iterate'),H=Object.getPrototypeOf;var I=new Map([[Map.prototype,function(a){a.has=m,a.get=n,a.set=p,a.delete=q,a.clear=r,a.forEach=s,a.keys=t,a.values=u,a.entries=v,a[Symbol.iterator]=w,Object.defineProperty(a,'size',{get:x})}],[Set.prototype,function(a){a.has=m,a.add=o,a.delete=q,a.clear=r,a.forEach=s,a.keys=t,a.values=u,a.entries=v,a[Symbol.iterator]=w,Object.defineProperty(a,'size',{get:x})}],[WeakMap.prototype,function(a){a.has=m,a.get=n,a.set=p,a.delete=q}],[WeakSet.prototype,function(a){a.has=m,a.add=o,a.delete=q}],[Date.prototype,!1],[RegExp.prototype,!1]]);const J=Symbol('enumerate');var K={get:function(a,b,c){const d=E.get(a)||a;if('$raw'===b)return d;const e=Reflect.get(a,b,c);return'symbol'==typeof b||'function'==typeof e?e:(h(d,b),k()&&'object'==typeof e&&null!==e?y(e):F.get(e)||e)},ownKeys:function(a){return h(a,J),Reflect.ownKeys(a)},set:function(a,b,c,d){'object'==typeof c&&null!==c&&(c=E.get(c)||c);const e='length'===b||c!==a[b],f=Reflect.set(a,b,c,d);return k()?(console.error(`Mutating observables in reactions is forbidden. You set ${b} to ${c}.`),f):('symbol'!=typeof b&&e&&a===E.get(d)&&(i(a,b),i(a,J)),f)},deleteProperty:function(a,b){const c=Reflect.deleteProperty(a,b);return'symbol'!=typeof b&&b in a&&(i(a,b),i(a,J)),c}};a.observe=function(a,b={}){function c(){return g(c,a,this,arguments)}if('function'!=typeof a)throw new TypeError(`The first argument must be a function instead of ${a}`);if(a[D])throw new TypeError('The first argument must not be an already observed reaction');if('object'!=typeof b||null===b)throw new TypeError(`The second argument must be an options object instead of ${b}`);return l(b),c.scheduler=b.scheduler,c.runId=0,c[D]=!0,b.lazy||c(),c},a.unobserve=function(a){if('function'!=typeof a||!a[D])throw new TypeError(`The first argument must be a reaction instead of ${a}`);a.unobserved||(a.unobserved=!0,e(a)),'object'==typeof a.scheduler&&a.scheduler.delete(a)},a.observable=y,a.isObservable=function(a){if('object'!=typeof a)throw new TypeError('First argument must be an object');return E.has(a)},Object.defineProperty(a,'__esModule',{value:!0})}); |
{ | ||
"name": "@nx-js/observer-util", | ||
"version": "3.1.3", | ||
"version": "3.1.4", | ||
"description": "An NX utility, responsible for powerful transparent reactivity with ES6 Proxies.", | ||
@@ -11,8 +11,9 @@ "main": "dist/cjs.es5.js", | ||
"scripts": { | ||
"test": "karma start karma.conf.js", | ||
"test": "node ./scripts/test.js", | ||
"test-builds": "node ./scripts/testBuilds.js", | ||
"lint": "standard", | ||
"lint-fix": "prettier-standard tests/**/*.js src/**/*.js", | ||
"lint-fix": "prettier --ignore-path '.gitignore' --write '**/!(bundle).js' && standard --fix", | ||
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", | ||
"build": "node ./scripts/build.js" | ||
"build": "node ./scripts/build.js", | ||
"build-toc": "node ./scripts/buildToc.js" | ||
}, | ||
@@ -43,7 +44,4 @@ "author": { | ||
"devDependencies": { | ||
"babel-cli": "^6.24.1", | ||
"babel-core": "6.25.0", | ||
"babel-minify": "^0.2.0", | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", | ||
"babel-preset-es2016": "^6.24.1", | ||
@@ -63,6 +61,7 @@ "babel-preset-es2017": "^6.24.1", | ||
"karma-source-map-support": "^1.2.0", | ||
"markdown-toc": "^1.1.0", | ||
"mocha": "^3.5.0", | ||
"nyc": "11.1.0", | ||
"pre-push": "^0.1.1", | ||
"prettier-standard": "^6.0.0", | ||
"prettier": "^1.6.1", | ||
"rollup": "^0.49.0", | ||
@@ -84,5 +83,2 @@ "rollup-plugin-alias": "^1.3.1", | ||
"mocha" | ||
], | ||
"ignore": [ | ||
"dist/*" | ||
] | ||
@@ -89,0 +85,0 @@ }, |
@@ -9,12 +9,29 @@ # The observer utility | ||
## Table of contents | ||
<details> | ||
<summary><strong>Table of Contents</strong></summary> | ||
<!-- Do not edit the Table of Contents, instead regenerate with `npm run build-toc` --> | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [Features](#key-features) | ||
- [Platfrom Support](#platform-support) | ||
- [API](#api) | ||
- [Examples](#examples) | ||
- [Contributing](#contributing) | ||
<!-- toc --> | ||
* [Installation](#installation) | ||
* [Usage](#usage) | ||
* [Key features](#key-features) | ||
* [Platform support](#platform-support) | ||
* [API](#api) | ||
+ [const object = observable(object)](#const-object--observableobject) | ||
+ [const boolean = isObservable(object)](#const-boolean--isobservableobject) | ||
+ [const function = observe(function)](#const-function--observefunction) | ||
+ [unobserve(function)](#unobservefunction) | ||
+ [unqueue(function)](#unqueuefunction) | ||
+ [exec(function)](#execfunction) | ||
+ [const promise = nextTick(function)](#const-promise--nexttickfunction) | ||
+ [observable.$raw](#observableraw) | ||
* [Examples](#examples) | ||
* [Alternative builds](#alternative-builds) | ||
* [Contributing](#contributing) | ||
<!-- tocstop --> | ||
</details> | ||
## Installation | ||
@@ -336,2 +353,13 @@ | ||
## Alternative builds | ||
This library detects if you use ES6 or commonJS modules and serve the right format to you. The exposed bundles are transpiled to ES5 to support common tools - like UglifyJS minifying. If you would like a finer control over the provided build, you can specify them in your imports. | ||
- `@nx-js/observer-util/dist/es.es6.js` exposes an ES6 build with ES6 modules. | ||
- `@nx-js/observer-util/dist/es.es5.js` exposes an ES5 build with ES6 modules. | ||
- `@nx-js/observer-util/dist/cjs.es6.js` exposes an ES6 build with commonJS modules. | ||
- `@nx-js/observer-util/dist/cjs.es5.js` exposes an ES5 build with commonJS modules. | ||
If you use a bundler, set up an alias for `@nx-js/observer-util` to point to your desired build. You can learn how to do it with webpack [here](https://webpack.js.org/configuration/resolve/#resolve-alias) and with rollup [here](https://github.com/rollup/rollup-plugin-alias#usage). | ||
## Contributing | ||
@@ -338,0 +366,0 @@ |
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
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
150666
29
2574
367