@nx-js/observer-util
Advanced tools
Comparing version 4.3.0-alpha.0 to 4.3.0-alpha.1
@@ -5,5 +5,4 @@ 'use strict'; | ||
const connectionStore = new WeakMap(); | ||
const ITERATION_KEY = Symbol('iteration key'); | ||
var connectionStore = new WeakMap(); | ||
var ITERATION_KEY = Symbol('iteration key'); | ||
function storeObservable(obj) { | ||
@@ -13,4 +12,7 @@ // this will be used to save (obj.key -> reaction) connections later | ||
} | ||
function registerReactionForOperation(reaction, { target, key, type }) { | ||
function registerReactionForOperation(reaction, { | ||
target, | ||
key, | ||
type | ||
}) { | ||
if (type === 'iterate') { | ||
@@ -20,9 +22,11 @@ key = ITERATION_KEY; | ||
const reactionsForObj = connectionStore.get(target); | ||
let reactionsForKey = reactionsForObj.get(key); | ||
var reactionsForObj = connectionStore.get(target); | ||
var reactionsForKey = reactionsForObj.get(key); | ||
if (!reactionsForKey) { | ||
reactionsForKey = new Set(); | ||
reactionsForObj.set(key, reactionsForKey); | ||
} | ||
// save the fact that the key is used by the reaction during its current run | ||
} // save the fact that the key is used by the reaction during its current run | ||
if (!reactionsForKey.has(reaction)) { | ||
@@ -33,7 +37,10 @@ reactionsForKey.add(reaction); | ||
} | ||
function getReactionsForOperation({ | ||
target, | ||
key, | ||
type | ||
}) { | ||
var reactionsForTarget = connectionStore.get(target); | ||
var reactionsForKey = new Set(); | ||
function getReactionsForOperation({ target, key, type }) { | ||
const reactionsForTarget = connectionStore.get(target); | ||
const reactionsForKey = new Set(); | ||
if (type === 'clear') { | ||
@@ -48,3 +55,3 @@ reactionsForTarget.forEach((_, key) => { | ||
if (type === 'add' || type === 'delete' || type === 'clear') { | ||
const iterationKey = Array.isArray(target) ? 'length' : ITERATION_KEY; | ||
var iterationKey = Array.isArray(target) ? 'length' : ITERATION_KEY; | ||
addReactionsForKey(reactionsForKey, reactionsForTarget, iterationKey); | ||
@@ -57,3 +64,3 @@ } | ||
function addReactionsForKey(reactionsForKey, reactionsForTarget, key) { | ||
const reactions = reactionsForTarget.get(key); | ||
var reactions = reactionsForTarget.get(key); | ||
reactions && reactions.forEach(reactionsForKey.add, reactionsForKey); | ||
@@ -66,2 +73,3 @@ } | ||
} | ||
reaction.cleaners = []; | ||
@@ -74,56 +82,109 @@ } | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
// default handlers are exposed in the public API | ||
// to allow devs to augment them instead of overwriting them | ||
// they are frozen as they can only be used but must not be overwritten | ||
// devs should use the setHandlers function for that | ||
const defaultHandlers = Object.freeze({ | ||
orderReactions(reactions) { | ||
return reactions; | ||
}, | ||
runReaction(target, context, args) { | ||
return Reflect.apply(target, context, args); | ||
}, | ||
get(target, key, receiver) { | ||
return Reflect.get(target, key, receiver); | ||
}, | ||
has(target, key) { | ||
return Reflect.has(target, key); | ||
}, | ||
ownKeys(target) { | ||
return Reflect.ownKeys(target); | ||
}, | ||
set(target, key, value, receiver) { | ||
return Reflect.set(target, key, value, receiver); | ||
}, | ||
deleteProperty(target, key) { | ||
return Reflect.deleteProperty(target, key); | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
if (enumerableOnly) symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
}); | ||
keys.push.apply(keys, symbols); | ||
} | ||
}); | ||
let handlers = defaultHandlers; | ||
return keys; | ||
} | ||
// add the new handlers to the existing ones | ||
function setHandlers(newHandlers) { | ||
handlers = _extends({}, handlers, newHandlers); | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
if (i % 2) { | ||
ownKeys(Object(source), true).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}); | ||
} else if (Object.getOwnPropertyDescriptors) { | ||
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); | ||
} else { | ||
ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
} | ||
return target; | ||
} | ||
// reset all handlers to the default ones | ||
function clearHandlers() { | ||
handlers = _extends({}, defaultHandlers); | ||
var proxyToRaw = new WeakMap(); | ||
var rawToProxy = new WeakMap(); // stores custom proxy handlers for observables | ||
var rawToOptions = new WeakMap(); | ||
// this is a copy of the built-in Reflect object | ||
// Reflect keys are not enumerable, so a simple { ...Reflect } spread does not work here | ||
// we have to copy all Reflect handlers to the object instead | ||
var proxyHandlers = Object.freeze(Object.getOwnPropertyNames(Reflect).reduce((handlers, key) => _objectSpread2(_objectSpread2({}, handlers), {}, { | ||
[key]: Reflect[key] | ||
}), {})); // ES6 collection method related handlers | ||
var collectionHandlers = Object.freeze({ | ||
has: (target, ...args) => target.has(...args), | ||
get: (target, ...args) => target.get(...args), | ||
add: (target, ...args) => target.add(...args), | ||
set: (target, ...args) => target.set(...args), | ||
delete: (target, ...args) => target.delete(...args), | ||
clear: (target, ...args) => target.clear(...args), | ||
forEach: (target, ...args) => target.forEach(...args), | ||
keys: (target, ...args) => target.keys(...args), | ||
values: (target, ...args) => target.values(...args), | ||
entries: (target, ...args) => target.entries(...args), | ||
[Symbol.iterator]: (target, ...args) => target[Symbol.iterator](...args), | ||
size: target => target.size | ||
}); | ||
var reactionHandlers = Object.freeze({ | ||
// order/filter reactions triggered by an atomic observable mutation | ||
transformReactions: (target, key, reactions) => reactions | ||
}); | ||
var defaultHandlers = { | ||
proxyHandlers, | ||
collectionHandlers, | ||
reactionHandlers | ||
}; | ||
var runProxyHandler = (...args) => runHandler('proxyHandlers', ...args); | ||
var runCollectionHandler = (...args) => runHandler('collectionHandlers', ...args); | ||
var runReactionHandler = (...args) => runHandler('reactionHandlers', ...args); // runs the default or custom (user-provided) handler for the specific operation | ||
function runHandler(handlers, name, target, ...args) { | ||
var _options$handlers; | ||
var options = rawToOptions.get(target); | ||
var handler = (options === null || options === void 0 ? void 0 : (_options$handlers = options[handlers]) === null || _options$handlers === void 0 ? void 0 : _options$handlers[name]) || defaultHandlers[handlers][name]; | ||
return handler(target, ...args); | ||
} | ||
// reactions can call each other and form a call stack | ||
const reactionStack = []; | ||
let isDebugging = false; | ||
var reactionStack = []; | ||
var isDebugging = false; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// do not build reactive relations, if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
return handlers.runReaction(fn, context, args); | ||
} | ||
return Reflect.apply(fn, context, args); | ||
} // only run the reaction if it is not already in the reaction stack | ||
// TODO: improve this to allow explicitly recursive reactions | ||
// only run the reaction if it is not already in the reaction stack | ||
// TODO: improve this to allow explicitly recursive reactions | ||
if (reactionStack.indexOf(reaction) === -1) { | ||
@@ -138,3 +199,3 @@ // release the (obj -> key -> reactions) connections | ||
reactionStack.push(reaction); | ||
return handlers.runReaction(fn, context, args); | ||
return Reflect.apply(fn, context, args); | ||
} finally { | ||
@@ -145,8 +206,8 @@ // always remove the currently running flag from the reaction when it stops execution | ||
} | ||
} | ||
} // register the currently running reaction to be queued again on obj.key mutations | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForOperation(operation) { | ||
// get the current reaction from the top of the stack | ||
const runningReaction = reactionStack[reactionStack.length - 1]; | ||
var runningReaction = reactionStack[reactionStack.length - 1]; | ||
if (runningReaction) { | ||
@@ -157,11 +218,13 @@ debugOperation(runningReaction, operation); | ||
} | ||
function queueReactionsForOperation(operation) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
handlers.orderReactions(getReactionsForOperation(operation)).forEach(queueReaction, operation); | ||
var target = operation.target, | ||
key = operation.key; | ||
var reactions = getReactionsForOperation(operation); | ||
runReactionHandler('transformReactions', target, key, Array.from(reactions)).forEach(queueReaction, operation); | ||
} | ||
function queueReaction(reaction) { | ||
debugOperation(reaction, this); | ||
// queue the reaction for later execution or run it immediately | ||
debugOperation(reaction, this); // queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
@@ -187,25 +250,20 @@ reaction.scheduler(reaction); | ||
function hasRunningReaction() { | ||
return reactionStack.length > 0; | ||
} | ||
const IS_REACTION = Symbol('is reaction'); | ||
var IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options = {}) { | ||
// wrap the passed function in a reaction, if it is not already one | ||
const reaction = fn[IS_REACTION] ? fn : function reaction() { | ||
var reaction = fn[IS_REACTION] ? fn : function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
}; | ||
// save the scheduler and debugger on the reaction | ||
}; // save the scheduler and debugger on the reaction | ||
reaction.scheduler = options.scheduler; | ||
reaction.debugger = options.debugger; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
reaction.debugger = options.debugger; // 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 unobserve(reaction) { | ||
@@ -215,7 +273,8 @@ // do nothing, if the reaction is already unobserved | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
reaction.unobserved = true; // release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
} // unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
@@ -226,232 +285,305 @@ reaction.scheduler.delete(reaction); | ||
const proxyToRaw = new WeakMap(); | ||
const rawToProxy = new WeakMap(); | ||
function patchIterator(iterator, target, isEntries) { | ||
var originalNext = iterator.next; | ||
const hasOwnProperty = Object.prototype.hasOwnProperty; | ||
iterator.next = () => { | ||
var _originalNext$call = originalNext.call(iterator), | ||
done = _originalNext$call.done, | ||
value = _originalNext$call.value; | ||
function findObservable(obj) { | ||
const observableObj = rawToProxy.get(obj); | ||
if (hasRunningReaction() && typeof obj === 'object' && obj !== null) { | ||
if (observableObj) { | ||
return observableObj; | ||
} | ||
return observable(obj); | ||
} | ||
return observableObj || obj; | ||
} | ||
function patchIterator(iterator, isEntries) { | ||
const originalNext = iterator.next; | ||
iterator.next = () => { | ||
let { done, value } = originalNext.call(iterator); | ||
if (!done) { | ||
if (isEntries) { | ||
value[1] = findObservable(value[1]); | ||
value[1] = observableChild(value[1], target); | ||
} else { | ||
value = findObservable(value); | ||
value = observableChild(value, target); | ||
} | ||
} | ||
return { done, value }; | ||
return { | ||
done, | ||
value | ||
}; | ||
}; | ||
return iterator; | ||
} | ||
const instrumentations = { | ||
var collectionHandlers$1 = { | ||
has(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, key, type: 'has' }); | ||
return proto.has.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
type: 'has' | ||
}); | ||
return runCollectionHandler('has', target, ...arguments); | ||
}, | ||
get(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, key, type: 'get' }); | ||
return findObservable(proto.get.apply(target, arguments)); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
type: 'get' | ||
}); | ||
return observableChild(runCollectionHandler('get', target, ...arguments), target); | ||
}, | ||
add(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadKey = proto.has.call(target, key); | ||
// forward the operation before queueing reactions | ||
const result = proto.add.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadKey = target.has(key); // forward the operation before queueing reactions | ||
var result = runCollectionHandler('add', target, ...arguments); | ||
if (!hadKey) { | ||
queueReactionsForOperation({ target, key, value: key, type: 'add' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value: key, | ||
type: 'add' | ||
}); | ||
} | ||
return result; | ||
}, | ||
set(key, value) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadKey = proto.has.call(target, key); | ||
const oldValue = proto.get.call(target, key); | ||
// forward the operation before queueing reactions | ||
const result = proto.set.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadKey = target.has(key); | ||
var oldValue = target.get(key); // forward the operation before queueing reactions | ||
var result = runCollectionHandler('set', target, ...arguments); | ||
if (!hadKey) { | ||
queueReactionsForOperation({ target, key, value, type: 'add' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value, | ||
type: 'add' | ||
}); | ||
} else if (value !== oldValue) { | ||
queueReactionsForOperation({ target, key, value, oldValue, type: 'set' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value, | ||
oldValue, | ||
type: 'set' | ||
}); | ||
} | ||
return result; | ||
}, | ||
delete(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadKey = proto.has.call(target, key); | ||
const oldValue = proto.get ? proto.get.call(target, key) : undefined; | ||
// forward the operation before queueing reactions | ||
const result = proto.delete.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadKey = target.has(key); | ||
var oldValue = target.get ? target.get(key) : undefined; // forward the operation before queueing reactions | ||
var result = runCollectionHandler('delete', target, ...arguments); | ||
if (hadKey) { | ||
queueReactionsForOperation({ target, key, oldValue, type: 'delete' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
oldValue, | ||
type: 'delete' | ||
}); | ||
} | ||
return result; | ||
}, | ||
clear() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadItems = target.size !== 0; | ||
const oldTarget = target instanceof Map ? new Map(target) : new Set(target); | ||
// forward the operation before queueing reactions | ||
const result = proto.clear.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadItems = target.size !== 0; | ||
var oldTarget = target instanceof Map ? new Map(target) : new Set(target); // forward the operation before queueing reactions | ||
var result = runCollectionHandler('clear', target, ...arguments); | ||
if (hadItems) { | ||
queueReactionsForOperation({ target, oldTarget, type: 'clear' }); | ||
queueReactionsForOperation({ | ||
target, | ||
oldTarget, | ||
type: 'clear' | ||
}); | ||
} | ||
return result; | ||
}, | ||
forEach(cb, ...args) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
// swap out the raw values with their observable pairs | ||
forEach(callback, ...args) { | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); // swap out the raw values with their observable pairs | ||
// before passing them to the callback | ||
const wrappedCb = (value, ...rest) => cb(findObservable(value), ...rest); | ||
return proto.forEach.call(target, wrappedCb, ...args); | ||
var wrappedCallback = (value, ...rest) => callback(observableChild(value, target), ...rest); | ||
return runCollectionHandler('forEach', target, wrappedCallback, ...args); | ||
}, | ||
keys() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
return proto.keys.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); // TODO: no need to patch this? | ||
return runCollectionHandler('keys', target, ...arguments); | ||
}, | ||
values() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
const iterator = proto.values.apply(target, arguments); | ||
return patchIterator(iterator, false); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
var iterator = runCollectionHandler('values', target, ...arguments); | ||
return patchIterator(iterator, target, false); | ||
}, | ||
entries() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
const iterator = proto.entries.apply(target, arguments); | ||
return patchIterator(iterator, true); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
var iterator = runCollectionHandler('entries', target, ...arguments); | ||
return patchIterator(iterator, target, true); | ||
}, | ||
[Symbol.iterator]() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
const iterator = proto[Symbol.iterator].apply(target, arguments); | ||
return patchIterator(iterator, target instanceof Map); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
var iterator = runCollectionHandler(Symbol.iterator, target, ...arguments); | ||
return patchIterator(iterator, target, target instanceof Map); | ||
}, | ||
get size() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
return Reflect.get(proto, 'size', target); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
return runCollectionHandler('size', target); | ||
} | ||
}; | ||
var collectionHandlers = { | ||
var collectionHandlers$2 = { | ||
get(target, key, receiver) { | ||
// instrument methods and property accessors to be reactive | ||
target = hasOwnProperty.call(instrumentations, key) ? instrumentations : target; | ||
// eslint-disable-next-line no-prototype-builtins | ||
target = collectionHandlers$1.hasOwnProperty(key) ? collectionHandlers$1 : target; | ||
return Reflect.get(target, key, receiver); | ||
} | ||
}; | ||
const globalObj = | ||
// eslint-disable-next-line | ||
typeof window === "object" ? window : Function("return this")(); | ||
var globalObj = // eslint-disable-next-line no-new-func | ||
typeof window === 'object' ? window : Function('return this')(); // these stateful built-in objects can and should be wrapped by Proxies if they are part of a store | ||
// simple ones - like arrays - ar wrapped with the normal observable Proxy | ||
// complex ones - like Map and Set - are wrapped with a Proxy of instrumented methods | ||
// built-in object can not be wrapped by Proxies | ||
// their methods expect the object instance as the 'this' instead of the Proxy wrapper | ||
// complex objects are wrapped with a Proxy of instrumented methods | ||
// which switch the proxy to the raw object and to add reactive wiring | ||
const handlers$1 = new Map([[Map, collectionHandlers], [Set, collectionHandlers], [WeakMap, collectionHandlers], [WeakSet, collectionHandlers], [Object, false], [Array, false], [Int8Array, false], [Uint8Array, false], [Uint8ClampedArray, false], [Int16Array, false], [Uint16Array, false], [Int32Array, false], [Uint32Array, false], [Float32Array, false], [Float64Array, false]]); | ||
var handlers = new Map([[Map, collectionHandlers$2], [Set, collectionHandlers$2], [WeakMap, collectionHandlers$2], [WeakSet, collectionHandlers$2], [Object, false], [Array, false], [Int8Array, false], [Uint8Array, false], [Uint8ClampedArray, false], [Int16Array, false], [Uint16Array, false], [Int32Array, false], [Uint32Array, false], [Float32Array, false], [Float64Array, false]]); // some (usually stateless) built-in objects can not be and should not be wrapped by Proxies | ||
// their methods expect the object instance as the receiver ('this') instead of the Proxy wrapper | ||
// wrapping them and calling their methods causes erros like: "TypeError: this is not a Date object." | ||
function shouldInstrument({ constructor }) { | ||
const isBuiltIn = typeof constructor === 'function' && constructor.name in globalObj && globalObj[constructor.name] === constructor; | ||
return !isBuiltIn || handlers$1.has(constructor); | ||
function shouldInstrument(obj) { | ||
var constructor = obj.constructor; // functions and objects in the above handlers array are safe to instrument | ||
if (typeof obj === 'function' || handlers.has(constructor)) { | ||
return true; | ||
} // other built-in objects should not be implemented | ||
var isBuiltIn = typeof constructor === 'function' && constructor.name in globalObj && globalObj[constructor.name] === constructor; | ||
return !isBuiltIn; | ||
} | ||
function getHandlers(obj) { | ||
return handlers$1.get(obj.constructor); | ||
return handlers.get(obj.constructor); | ||
} | ||
var _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
var wellKnownSymbols = new Set(Object.getOwnPropertyNames(Symbol).map(key => Symbol[key]).filter(value => typeof value === 'symbol')); // intercept get operations on observables to know which reaction uses their properties | ||
const hasOwnProperty$1 = Object.prototype.hasOwnProperty; | ||
const wellKnownSymbols = new Set(Object.getOwnPropertyNames(Symbol).map(key => Symbol[key]).filter(value => typeof value === 'symbol')); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(target, key, receiver) { | ||
const result = handlers.get(target, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for well known symbols | ||
var result = runProxyHandler('get', target, key, receiver); // do not register (observable.prop -> reaction) pairs for well known symbols | ||
// these symbols are frequently retrieved in low level JavaScript under the hood | ||
if (typeof key === 'symbol' && wellKnownSymbols.has(key)) { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForOperation({ target, key, receiver, type: 'get' }); | ||
// 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) | ||
const observableResult = rawToProxy.get(result); | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
if (observableResult) { | ||
return observableResult; | ||
} | ||
// do not violate the none-configurable none-writable prop get handler invariant | ||
// fall back to none reactive mode in this case, instead of letting the Proxy throw a TypeError | ||
const descriptor = Reflect.getOwnPropertyDescriptor(target, key); | ||
if (!descriptor || !(descriptor.writable === false && descriptor.configurable === false)) { | ||
return observable(result); | ||
} | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return observableResult || result; | ||
} // register and save the (observable.prop -> runningReaction) relation | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
receiver, | ||
type: 'get' | ||
}); // do not violate the none-configurable none-writable prop get handler invariant | ||
// fall back to none reactive mode in this case, instead of letting the Proxy throw a TypeError | ||
var descriptor = Reflect.getOwnPropertyDescriptor(target, key); | ||
if (descriptor && descriptor.writable === false && descriptor.configurable === false) { | ||
return result; | ||
} // otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return observableChild(result, target); | ||
} | ||
function has(target, key) { | ||
const result = handlers.has(target, key); | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForOperation({ target, key, type: 'has' }); | ||
var result = runProxyHandler('has', target, key); // register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
type: 'has' | ||
}); | ||
return result; | ||
} | ||
function ownKeys(target) { | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
return handlers.ownKeys(target); | ||
} | ||
function ownKeys$1(target) { | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
return runProxyHandler('ownKeys', target); | ||
} // intercept set operations on observables to know when to trigger reactions | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(target, 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; | ||
} | ||
// save if the object had a descriptor for this key | ||
const hadKey = hasOwnProperty$1.call(target, key); | ||
// save if the value changed because of this set operation | ||
const oldValue = target[key]; | ||
// execute the set operation before running any reaction | ||
const result = handlers.set(target, key, value, receiver); | ||
// do not queue reactions if the target of the operation is not the raw receiver | ||
// (possible because of prototypal inheritance) | ||
value = proxyToRaw.get(value) || value; // save if the object had a descriptor for this key | ||
var hadKey = hasOwnProperty.call(target, key); // save if the value changed because of this set operation | ||
var oldValue = target[key]; // execute the set operation before running any reaction | ||
var result = runProxyHandler('set', target, key, value, receiver); // do not queue reactions if the target of the operation is not the raw receiver | ||
// this possible because of prototypal inheritance | ||
// when the prototype has a setter the set operation traverses the whole prototype chain | ||
// and calls the set trap on every object until it finds the setter | ||
// this is undesired, it is enough for us to trigger the reactions in the set trap of | ||
// the receiver (child) object to avoid duplicate reactions | ||
if (target !== proxyToRaw.get(receiver)) { | ||
return result; | ||
} | ||
// queue a reaction if it's a new property or its value changed | ||
} // queue a reaction if it's a new property or its value changed | ||
if (!hadKey) { | ||
queueReactionsForOperation({ target, key, value, receiver, type: 'add' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value, | ||
receiver, | ||
type: 'add' | ||
}); | ||
} else if (value !== oldValue) { | ||
@@ -467,2 +599,3 @@ queueReactionsForOperation({ | ||
} | ||
return result; | ||
@@ -473,44 +606,76 @@ } | ||
// save if the object had the key | ||
const hadKey = hasOwnProperty$1.call(target, key); | ||
const oldValue = target[key]; | ||
// execute the delete operation before running any reaction | ||
const result = handlers.deleteProperty(target, key); | ||
// only queue reactions for delete operations which resulted in an actual change | ||
var hadKey = hasOwnProperty.call(target, key); | ||
var oldValue = target[key]; // execute the delete operation before running any reaction | ||
var result = runProxyHandler('deleteProperty', target, key); // only queue reactions for delete operations which resulted in an actual change | ||
if (hadKey) { | ||
queueReactionsForOperation({ target, key, oldValue, type: 'delete' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
oldValue, | ||
type: 'delete' | ||
}); | ||
} | ||
return result; | ||
} // return an observable object instance when an observable class is instantiated | ||
function construct(target, args, newTarget) { | ||
return observable(runProxyHandler('construct', target, args, newTarget)); | ||
} | ||
// allow custom handlers for proxy traps that this lib does not use | ||
// this prevents the need for double Proxy wrapping for users | ||
// who wish to further extend JS behavior with Proxy traps | ||
var baseHandlers = _extends$1({}, handlers, { get, has, ownKeys, set, deleteProperty }); | ||
var proxyHandlers$1 = { | ||
get, | ||
has, | ||
ownKeys: ownKeys$1, | ||
set, | ||
deleteProperty, | ||
construct | ||
}; | ||
function observable(obj = {}) { | ||
function observable(obj = {}, options) { | ||
// if it is already an observable or it should not be wrapped, return it | ||
if (proxyToRaw.has(obj) || !shouldInstrument(obj)) { | ||
return obj; | ||
} | ||
// if it already has a cached observable wrapper, return it | ||
} // if it already has a cached observable wrapper, return it | ||
// otherwise create a new observable | ||
return rawToProxy.get(obj) || createObservable(obj); | ||
return rawToProxy.get(obj) || createObservable(obj, options); | ||
} | ||
function createObservable(obj) { | ||
function createObservable(obj, options) { | ||
// if it is a complex built-in object or a normal object, wrap it | ||
const handlers = getHandlers(obj) || baseHandlers; | ||
const observable = new Proxy(obj, handlers); | ||
// save these to switch between the raw object and the wrapped object with ease later | ||
var baseHandlers = getHandlers(obj) || proxyHandlers$1; | ||
var observable = new Proxy(obj, _objectSpread2(_objectSpread2({}, options === null || options === void 0 ? void 0 : options.proxyHandlers), baseHandlers)); // save these to switch between the raw object and the wrapped object with ease later | ||
rawToProxy.set(obj, observable); | ||
proxyToRaw.set(observable, obj); | ||
// init basic data structures to save and cleanup later (observable.prop -> reaction) connections | ||
proxyToRaw.set(observable, obj); // add custom options to the raw object | ||
if (options) { | ||
rawToOptions.set(obj, options); | ||
} // init basic data structures to save and cleanup later (observable.prop -> reaction) connections | ||
storeObservable(obj); | ||
return observable; | ||
} // if observable.prop is an object, wrap it in an observable too | ||
// this is needed to intercept property access on that object too | ||
function observableChild(child, parent) { | ||
if (typeof child === 'object' && child !== null || typeof child === 'function') { | ||
// pass the parent's options to the child object | ||
// this creates a 'deep proxy' which shares custom handlers deeply with its object children | ||
var options = rawToOptions.get(parent); | ||
return observable(child, options); | ||
} | ||
return child; | ||
} | ||
function isObservable(obj) { | ||
return proxyToRaw.has(obj); | ||
} | ||
function raw(obj) { | ||
@@ -520,9 +685,9 @@ return proxyToRaw.get(obj) || obj; | ||
exports.collectionHandlers = collectionHandlers; | ||
exports.isObservable = isObservable; | ||
exports.observable = observable; | ||
exports.observe = observe; | ||
exports.proxyHandlers = proxyHandlers; | ||
exports.raw = raw; | ||
exports.reactionHandlers = reactionHandlers; | ||
exports.unobserve = unobserve; | ||
exports.observable = observable; | ||
exports.isObservable = isObservable; | ||
exports.raw = raw; | ||
exports.setHandlers = setHandlers; | ||
exports.clearHandlers = clearHandlers; | ||
exports.defaultHandlers = defaultHandlers; |
@@ -1,4 +0,3 @@ | ||
const connectionStore = new WeakMap(); | ||
const ITERATION_KEY = Symbol('iteration key'); | ||
var connectionStore = new WeakMap(); | ||
var ITERATION_KEY = Symbol('iteration key'); | ||
function storeObservable(obj) { | ||
@@ -8,4 +7,7 @@ // this will be used to save (obj.key -> reaction) connections later | ||
} | ||
function registerReactionForOperation(reaction, { target, key, type }) { | ||
function registerReactionForOperation(reaction, { | ||
target, | ||
key, | ||
type | ||
}) { | ||
if (type === 'iterate') { | ||
@@ -15,9 +17,11 @@ key = ITERATION_KEY; | ||
const reactionsForObj = connectionStore.get(target); | ||
let reactionsForKey = reactionsForObj.get(key); | ||
var reactionsForObj = connectionStore.get(target); | ||
var reactionsForKey = reactionsForObj.get(key); | ||
if (!reactionsForKey) { | ||
reactionsForKey = new Set(); | ||
reactionsForObj.set(key, reactionsForKey); | ||
} | ||
// save the fact that the key is used by the reaction during its current run | ||
} // save the fact that the key is used by the reaction during its current run | ||
if (!reactionsForKey.has(reaction)) { | ||
@@ -28,7 +32,10 @@ reactionsForKey.add(reaction); | ||
} | ||
function getReactionsForOperation({ | ||
target, | ||
key, | ||
type | ||
}) { | ||
var reactionsForTarget = connectionStore.get(target); | ||
var reactionsForKey = new Set(); | ||
function getReactionsForOperation({ target, key, type }) { | ||
const reactionsForTarget = connectionStore.get(target); | ||
const reactionsForKey = new Set(); | ||
if (type === 'clear') { | ||
@@ -43,3 +50,3 @@ reactionsForTarget.forEach((_, key) => { | ||
if (type === 'add' || type === 'delete' || type === 'clear') { | ||
const iterationKey = Array.isArray(target) ? 'length' : ITERATION_KEY; | ||
var iterationKey = Array.isArray(target) ? 'length' : ITERATION_KEY; | ||
addReactionsForKey(reactionsForKey, reactionsForTarget, iterationKey); | ||
@@ -52,3 +59,3 @@ } | ||
function addReactionsForKey(reactionsForKey, reactionsForTarget, key) { | ||
const reactions = reactionsForTarget.get(key); | ||
var reactions = reactionsForTarget.get(key); | ||
reactions && reactions.forEach(reactionsForKey.add, reactionsForKey); | ||
@@ -61,2 +68,3 @@ } | ||
} | ||
reaction.cleaners = []; | ||
@@ -69,56 +77,109 @@ } | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
// default handlers are exposed in the public API | ||
// to allow devs to augment them instead of overwriting them | ||
// they are frozen as they can only be used but must not be overwritten | ||
// devs should use the setHandlers function for that | ||
const defaultHandlers = Object.freeze({ | ||
orderReactions(reactions) { | ||
return reactions; | ||
}, | ||
runReaction(target, context, args) { | ||
return Reflect.apply(target, context, args); | ||
}, | ||
get(target, key, receiver) { | ||
return Reflect.get(target, key, receiver); | ||
}, | ||
has(target, key) { | ||
return Reflect.has(target, key); | ||
}, | ||
ownKeys(target) { | ||
return Reflect.ownKeys(target); | ||
}, | ||
set(target, key, value, receiver) { | ||
return Reflect.set(target, key, value, receiver); | ||
}, | ||
deleteProperty(target, key) { | ||
return Reflect.deleteProperty(target, key); | ||
return obj; | ||
} | ||
function ownKeys(object, enumerableOnly) { | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
var symbols = Object.getOwnPropertySymbols(object); | ||
if (enumerableOnly) symbols = symbols.filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(object, sym).enumerable; | ||
}); | ||
keys.push.apply(keys, symbols); | ||
} | ||
}); | ||
let handlers = defaultHandlers; | ||
return keys; | ||
} | ||
// add the new handlers to the existing ones | ||
function setHandlers(newHandlers) { | ||
handlers = _extends({}, handlers, newHandlers); | ||
function _objectSpread2(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
if (i % 2) { | ||
ownKeys(Object(source), true).forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}); | ||
} else if (Object.getOwnPropertyDescriptors) { | ||
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); | ||
} else { | ||
ownKeys(Object(source)).forEach(function (key) { | ||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); | ||
}); | ||
} | ||
} | ||
return target; | ||
} | ||
// reset all handlers to the default ones | ||
function clearHandlers() { | ||
handlers = _extends({}, defaultHandlers); | ||
var proxyToRaw = new WeakMap(); | ||
var rawToProxy = new WeakMap(); // stores custom proxy handlers for observables | ||
var rawToOptions = new WeakMap(); | ||
// this is a copy of the built-in Reflect object | ||
// Reflect keys are not enumerable, so a simple { ...Reflect } spread does not work here | ||
// we have to copy all Reflect handlers to the object instead | ||
var proxyHandlers = Object.freeze(Object.getOwnPropertyNames(Reflect).reduce((handlers, key) => _objectSpread2(_objectSpread2({}, handlers), {}, { | ||
[key]: Reflect[key] | ||
}), {})); // ES6 collection method related handlers | ||
var collectionHandlers = Object.freeze({ | ||
has: (target, ...args) => target.has(...args), | ||
get: (target, ...args) => target.get(...args), | ||
add: (target, ...args) => target.add(...args), | ||
set: (target, ...args) => target.set(...args), | ||
delete: (target, ...args) => target.delete(...args), | ||
clear: (target, ...args) => target.clear(...args), | ||
forEach: (target, ...args) => target.forEach(...args), | ||
keys: (target, ...args) => target.keys(...args), | ||
values: (target, ...args) => target.values(...args), | ||
entries: (target, ...args) => target.entries(...args), | ||
[Symbol.iterator]: (target, ...args) => target[Symbol.iterator](...args), | ||
size: target => target.size | ||
}); | ||
var reactionHandlers = Object.freeze({ | ||
// order/filter reactions triggered by an atomic observable mutation | ||
transformReactions: (target, key, reactions) => reactions | ||
}); | ||
var defaultHandlers = { | ||
proxyHandlers, | ||
collectionHandlers, | ||
reactionHandlers | ||
}; | ||
var runProxyHandler = (...args) => runHandler('proxyHandlers', ...args); | ||
var runCollectionHandler = (...args) => runHandler('collectionHandlers', ...args); | ||
var runReactionHandler = (...args) => runHandler('reactionHandlers', ...args); // runs the default or custom (user-provided) handler for the specific operation | ||
function runHandler(handlers, name, target, ...args) { | ||
var _options$handlers; | ||
var options = rawToOptions.get(target); | ||
var handler = (options === null || options === void 0 ? void 0 : (_options$handlers = options[handlers]) === null || _options$handlers === void 0 ? void 0 : _options$handlers[name]) || defaultHandlers[handlers][name]; | ||
return handler(target, ...args); | ||
} | ||
// reactions can call each other and form a call stack | ||
const reactionStack = []; | ||
let isDebugging = false; | ||
var reactionStack = []; | ||
var isDebugging = false; | ||
function runAsReaction(reaction, fn, context, args) { | ||
// do not build reactive relations, if the reaction is unobserved | ||
if (reaction.unobserved) { | ||
return handlers.runReaction(fn, context, args); | ||
} | ||
return Reflect.apply(fn, context, args); | ||
} // only run the reaction if it is not already in the reaction stack | ||
// TODO: improve this to allow explicitly recursive reactions | ||
// only run the reaction if it is not already in the reaction stack | ||
// TODO: improve this to allow explicitly recursive reactions | ||
if (reactionStack.indexOf(reaction) === -1) { | ||
@@ -133,3 +194,3 @@ // release the (obj -> key -> reactions) connections | ||
reactionStack.push(reaction); | ||
return handlers.runReaction(fn, context, args); | ||
return Reflect.apply(fn, context, args); | ||
} finally { | ||
@@ -140,8 +201,8 @@ // always remove the currently running flag from the reaction when it stops execution | ||
} | ||
} | ||
} // register the currently running reaction to be queued again on obj.key mutations | ||
// register the currently running reaction to be queued again on obj.key mutations | ||
function registerRunningReactionForOperation(operation) { | ||
// get the current reaction from the top of the stack | ||
const runningReaction = reactionStack[reactionStack.length - 1]; | ||
var runningReaction = reactionStack[reactionStack.length - 1]; | ||
if (runningReaction) { | ||
@@ -152,11 +213,13 @@ debugOperation(runningReaction, operation); | ||
} | ||
function queueReactionsForOperation(operation) { | ||
// iterate and queue every reaction, which is triggered by obj.key mutation | ||
handlers.orderReactions(getReactionsForOperation(operation)).forEach(queueReaction, operation); | ||
var target = operation.target, | ||
key = operation.key; | ||
var reactions = getReactionsForOperation(operation); | ||
runReactionHandler('transformReactions', target, key, Array.from(reactions)).forEach(queueReaction, operation); | ||
} | ||
function queueReaction(reaction) { | ||
debugOperation(reaction, this); | ||
// queue the reaction for later execution or run it immediately | ||
debugOperation(reaction, this); // queue the reaction for later execution or run it immediately | ||
if (typeof reaction.scheduler === 'function') { | ||
@@ -182,25 +245,20 @@ reaction.scheduler(reaction); | ||
function hasRunningReaction() { | ||
return reactionStack.length > 0; | ||
} | ||
const IS_REACTION = Symbol('is reaction'); | ||
var IS_REACTION = Symbol('is reaction'); | ||
function observe(fn, options = {}) { | ||
// wrap the passed function in a reaction, if it is not already one | ||
const reaction = fn[IS_REACTION] ? fn : function reaction() { | ||
var reaction = fn[IS_REACTION] ? fn : function reaction() { | ||
return runAsReaction(reaction, fn, this, arguments); | ||
}; | ||
// save the scheduler and debugger on the reaction | ||
}; // save the scheduler and debugger on the reaction | ||
reaction.scheduler = options.scheduler; | ||
reaction.debugger = options.debugger; | ||
// save the fact that this is a reaction | ||
reaction[IS_REACTION] = true; | ||
// run the reaction once if it is not a lazy one | ||
reaction.debugger = options.debugger; // 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 unobserve(reaction) { | ||
@@ -210,7 +268,8 @@ // do nothing, if the reaction is already unobserved | ||
// indicate that the reaction should not be triggered any more | ||
reaction.unobserved = true; | ||
// release (obj -> key -> reaction) connections | ||
reaction.unobserved = true; // release (obj -> key -> reaction) connections | ||
releaseReaction(reaction); | ||
} | ||
// unschedule the reaction, if it is scheduled | ||
} // unschedule the reaction, if it is scheduled | ||
if (typeof reaction.scheduler === 'object') { | ||
@@ -221,232 +280,305 @@ reaction.scheduler.delete(reaction); | ||
const proxyToRaw = new WeakMap(); | ||
const rawToProxy = new WeakMap(); | ||
function patchIterator(iterator, target, isEntries) { | ||
var originalNext = iterator.next; | ||
const hasOwnProperty = Object.prototype.hasOwnProperty; | ||
iterator.next = () => { | ||
var _originalNext$call = originalNext.call(iterator), | ||
done = _originalNext$call.done, | ||
value = _originalNext$call.value; | ||
function findObservable(obj) { | ||
const observableObj = rawToProxy.get(obj); | ||
if (hasRunningReaction() && typeof obj === 'object' && obj !== null) { | ||
if (observableObj) { | ||
return observableObj; | ||
} | ||
return observable(obj); | ||
} | ||
return observableObj || obj; | ||
} | ||
function patchIterator(iterator, isEntries) { | ||
const originalNext = iterator.next; | ||
iterator.next = () => { | ||
let { done, value } = originalNext.call(iterator); | ||
if (!done) { | ||
if (isEntries) { | ||
value[1] = findObservable(value[1]); | ||
value[1] = observableChild(value[1], target); | ||
} else { | ||
value = findObservable(value); | ||
value = observableChild(value, target); | ||
} | ||
} | ||
return { done, value }; | ||
return { | ||
done, | ||
value | ||
}; | ||
}; | ||
return iterator; | ||
} | ||
const instrumentations = { | ||
var collectionHandlers$1 = { | ||
has(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, key, type: 'has' }); | ||
return proto.has.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
type: 'has' | ||
}); | ||
return runCollectionHandler('has', target, ...arguments); | ||
}, | ||
get(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, key, type: 'get' }); | ||
return findObservable(proto.get.apply(target, arguments)); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
type: 'get' | ||
}); | ||
return observableChild(runCollectionHandler('get', target, ...arguments), target); | ||
}, | ||
add(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadKey = proto.has.call(target, key); | ||
// forward the operation before queueing reactions | ||
const result = proto.add.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadKey = target.has(key); // forward the operation before queueing reactions | ||
var result = runCollectionHandler('add', target, ...arguments); | ||
if (!hadKey) { | ||
queueReactionsForOperation({ target, key, value: key, type: 'add' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value: key, | ||
type: 'add' | ||
}); | ||
} | ||
return result; | ||
}, | ||
set(key, value) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadKey = proto.has.call(target, key); | ||
const oldValue = proto.get.call(target, key); | ||
// forward the operation before queueing reactions | ||
const result = proto.set.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadKey = target.has(key); | ||
var oldValue = target.get(key); // forward the operation before queueing reactions | ||
var result = runCollectionHandler('set', target, ...arguments); | ||
if (!hadKey) { | ||
queueReactionsForOperation({ target, key, value, type: 'add' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value, | ||
type: 'add' | ||
}); | ||
} else if (value !== oldValue) { | ||
queueReactionsForOperation({ target, key, value, oldValue, type: 'set' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value, | ||
oldValue, | ||
type: 'set' | ||
}); | ||
} | ||
return result; | ||
}, | ||
delete(key) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadKey = proto.has.call(target, key); | ||
const oldValue = proto.get ? proto.get.call(target, key) : undefined; | ||
// forward the operation before queueing reactions | ||
const result = proto.delete.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadKey = target.has(key); | ||
var oldValue = target.get ? target.get(key) : undefined; // forward the operation before queueing reactions | ||
var result = runCollectionHandler('delete', target, ...arguments); | ||
if (hadKey) { | ||
queueReactionsForOperation({ target, key, oldValue, type: 'delete' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
oldValue, | ||
type: 'delete' | ||
}); | ||
} | ||
return result; | ||
}, | ||
clear() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
const hadItems = target.size !== 0; | ||
const oldTarget = target instanceof Map ? new Map(target) : new Set(target); | ||
// forward the operation before queueing reactions | ||
const result = proto.clear.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
var hadItems = target.size !== 0; | ||
var oldTarget = target instanceof Map ? new Map(target) : new Set(target); // forward the operation before queueing reactions | ||
var result = runCollectionHandler('clear', target, ...arguments); | ||
if (hadItems) { | ||
queueReactionsForOperation({ target, oldTarget, type: 'clear' }); | ||
queueReactionsForOperation({ | ||
target, | ||
oldTarget, | ||
type: 'clear' | ||
}); | ||
} | ||
return result; | ||
}, | ||
forEach(cb, ...args) { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
// swap out the raw values with their observable pairs | ||
forEach(callback, ...args) { | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); // swap out the raw values with their observable pairs | ||
// before passing them to the callback | ||
const wrappedCb = (value, ...rest) => cb(findObservable(value), ...rest); | ||
return proto.forEach.call(target, wrappedCb, ...args); | ||
var wrappedCallback = (value, ...rest) => callback(observableChild(value, target), ...rest); | ||
return runCollectionHandler('forEach', target, wrappedCallback, ...args); | ||
}, | ||
keys() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
return proto.keys.apply(target, arguments); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); // TODO: no need to patch this? | ||
return runCollectionHandler('keys', target, ...arguments); | ||
}, | ||
values() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
const iterator = proto.values.apply(target, arguments); | ||
return patchIterator(iterator, false); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
var iterator = runCollectionHandler('values', target, ...arguments); | ||
return patchIterator(iterator, target, false); | ||
}, | ||
entries() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
const iterator = proto.entries.apply(target, arguments); | ||
return patchIterator(iterator, true); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
var iterator = runCollectionHandler('entries', target, ...arguments); | ||
return patchIterator(iterator, target, true); | ||
}, | ||
[Symbol.iterator]() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
const iterator = proto[Symbol.iterator].apply(target, arguments); | ||
return patchIterator(iterator, target instanceof Map); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
var iterator = runCollectionHandler(Symbol.iterator, target, ...arguments); | ||
return patchIterator(iterator, target, target instanceof Map); | ||
}, | ||
get size() { | ||
const target = proxyToRaw.get(this); | ||
const proto = Reflect.getPrototypeOf(this); | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
return Reflect.get(proto, 'size', target); | ||
var target = proxyToRaw.get(this); | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
return runCollectionHandler('size', target); | ||
} | ||
}; | ||
var collectionHandlers = { | ||
var collectionHandlers$2 = { | ||
get(target, key, receiver) { | ||
// instrument methods and property accessors to be reactive | ||
target = hasOwnProperty.call(instrumentations, key) ? instrumentations : target; | ||
// eslint-disable-next-line no-prototype-builtins | ||
target = collectionHandlers$1.hasOwnProperty(key) ? collectionHandlers$1 : target; | ||
return Reflect.get(target, key, receiver); | ||
} | ||
}; | ||
const globalObj = | ||
// eslint-disable-next-line | ||
typeof window === "object" ? window : Function("return this")(); | ||
var globalObj = // eslint-disable-next-line no-new-func | ||
typeof window === 'object' ? window : Function('return this')(); // these stateful built-in objects can and should be wrapped by Proxies if they are part of a store | ||
// simple ones - like arrays - ar wrapped with the normal observable Proxy | ||
// complex ones - like Map and Set - are wrapped with a Proxy of instrumented methods | ||
// built-in object can not be wrapped by Proxies | ||
// their methods expect the object instance as the 'this' instead of the Proxy wrapper | ||
// complex objects are wrapped with a Proxy of instrumented methods | ||
// which switch the proxy to the raw object and to add reactive wiring | ||
const handlers$1 = new Map([[Map, collectionHandlers], [Set, collectionHandlers], [WeakMap, collectionHandlers], [WeakSet, collectionHandlers], [Object, false], [Array, false], [Int8Array, false], [Uint8Array, false], [Uint8ClampedArray, false], [Int16Array, false], [Uint16Array, false], [Int32Array, false], [Uint32Array, false], [Float32Array, false], [Float64Array, false]]); | ||
var handlers = new Map([[Map, collectionHandlers$2], [Set, collectionHandlers$2], [WeakMap, collectionHandlers$2], [WeakSet, collectionHandlers$2], [Object, false], [Array, false], [Int8Array, false], [Uint8Array, false], [Uint8ClampedArray, false], [Int16Array, false], [Uint16Array, false], [Int32Array, false], [Uint32Array, false], [Float32Array, false], [Float64Array, false]]); // some (usually stateless) built-in objects can not be and should not be wrapped by Proxies | ||
// their methods expect the object instance as the receiver ('this') instead of the Proxy wrapper | ||
// wrapping them and calling their methods causes erros like: "TypeError: this is not a Date object." | ||
function shouldInstrument({ constructor }) { | ||
const isBuiltIn = typeof constructor === 'function' && constructor.name in globalObj && globalObj[constructor.name] === constructor; | ||
return !isBuiltIn || handlers$1.has(constructor); | ||
function shouldInstrument(obj) { | ||
var constructor = obj.constructor; // functions and objects in the above handlers array are safe to instrument | ||
if (typeof obj === 'function' || handlers.has(constructor)) { | ||
return true; | ||
} // other built-in objects should not be implemented | ||
var isBuiltIn = typeof constructor === 'function' && constructor.name in globalObj && globalObj[constructor.name] === constructor; | ||
return !isBuiltIn; | ||
} | ||
function getHandlers(obj) { | ||
return handlers$1.get(obj.constructor); | ||
return handlers.get(obj.constructor); | ||
} | ||
var _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
var wellKnownSymbols = new Set(Object.getOwnPropertyNames(Symbol).map(key => Symbol[key]).filter(value => typeof value === 'symbol')); // intercept get operations on observables to know which reaction uses their properties | ||
const hasOwnProperty$1 = Object.prototype.hasOwnProperty; | ||
const wellKnownSymbols = new Set(Object.getOwnPropertyNames(Symbol).map(key => Symbol[key]).filter(value => typeof value === 'symbol')); | ||
// intercept get operations on observables to know which reaction uses their properties | ||
function get(target, key, receiver) { | ||
const result = handlers.get(target, key, receiver); | ||
// do not register (observable.prop -> reaction) pairs for well known symbols | ||
var result = runProxyHandler('get', target, key, receiver); // do not register (observable.prop -> reaction) pairs for well known symbols | ||
// these symbols are frequently retrieved in low level JavaScript under the hood | ||
if (typeof key === 'symbol' && wellKnownSymbols.has(key)) { | ||
return result; | ||
} | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForOperation({ target, key, receiver, type: 'get' }); | ||
// 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) | ||
const observableResult = rawToProxy.get(result); | ||
if (hasRunningReaction() && typeof result === 'object' && result !== null) { | ||
if (observableResult) { | ||
return observableResult; | ||
} | ||
// do not violate the none-configurable none-writable prop get handler invariant | ||
// fall back to none reactive mode in this case, instead of letting the Proxy throw a TypeError | ||
const descriptor = Reflect.getOwnPropertyDescriptor(target, key); | ||
if (!descriptor || !(descriptor.writable === false && descriptor.configurable === false)) { | ||
return observable(result); | ||
} | ||
} | ||
// otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return observableResult || result; | ||
} // register and save the (observable.prop -> runningReaction) relation | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
receiver, | ||
type: 'get' | ||
}); // do not violate the none-configurable none-writable prop get handler invariant | ||
// fall back to none reactive mode in this case, instead of letting the Proxy throw a TypeError | ||
var descriptor = Reflect.getOwnPropertyDescriptor(target, key); | ||
if (descriptor && descriptor.writable === false && descriptor.configurable === false) { | ||
return result; | ||
} // otherwise return the observable wrapper if it is already created and cached or the raw object | ||
return observableChild(result, target); | ||
} | ||
function has(target, key) { | ||
const result = handlers.has(target, key); | ||
// register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForOperation({ target, key, type: 'has' }); | ||
var result = runProxyHandler('has', target, key); // register and save (observable.prop -> runningReaction) | ||
registerRunningReactionForOperation({ | ||
target, | ||
key, | ||
type: 'has' | ||
}); | ||
return result; | ||
} | ||
function ownKeys(target) { | ||
registerRunningReactionForOperation({ target, type: 'iterate' }); | ||
return handlers.ownKeys(target); | ||
} | ||
function ownKeys$1(target) { | ||
registerRunningReactionForOperation({ | ||
target, | ||
type: 'iterate' | ||
}); | ||
return runProxyHandler('ownKeys', target); | ||
} // intercept set operations on observables to know when to trigger reactions | ||
// intercept set operations on observables to know when to trigger reactions | ||
function set(target, 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; | ||
} | ||
// save if the object had a descriptor for this key | ||
const hadKey = hasOwnProperty$1.call(target, key); | ||
// save if the value changed because of this set operation | ||
const oldValue = target[key]; | ||
// execute the set operation before running any reaction | ||
const result = handlers.set(target, key, value, receiver); | ||
// do not queue reactions if the target of the operation is not the raw receiver | ||
// (possible because of prototypal inheritance) | ||
value = proxyToRaw.get(value) || value; // save if the object had a descriptor for this key | ||
var hadKey = hasOwnProperty.call(target, key); // save if the value changed because of this set operation | ||
var oldValue = target[key]; // execute the set operation before running any reaction | ||
var result = runProxyHandler('set', target, key, value, receiver); // do not queue reactions if the target of the operation is not the raw receiver | ||
// this possible because of prototypal inheritance | ||
// when the prototype has a setter the set operation traverses the whole prototype chain | ||
// and calls the set trap on every object until it finds the setter | ||
// this is undesired, it is enough for us to trigger the reactions in the set trap of | ||
// the receiver (child) object to avoid duplicate reactions | ||
if (target !== proxyToRaw.get(receiver)) { | ||
return result; | ||
} | ||
// queue a reaction if it's a new property or its value changed | ||
} // queue a reaction if it's a new property or its value changed | ||
if (!hadKey) { | ||
queueReactionsForOperation({ target, key, value, receiver, type: 'add' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
value, | ||
receiver, | ||
type: 'add' | ||
}); | ||
} else if (value !== oldValue) { | ||
@@ -462,2 +594,3 @@ queueReactionsForOperation({ | ||
} | ||
return result; | ||
@@ -468,44 +601,76 @@ } | ||
// save if the object had the key | ||
const hadKey = hasOwnProperty$1.call(target, key); | ||
const oldValue = target[key]; | ||
// execute the delete operation before running any reaction | ||
const result = handlers.deleteProperty(target, key); | ||
// only queue reactions for delete operations which resulted in an actual change | ||
var hadKey = hasOwnProperty.call(target, key); | ||
var oldValue = target[key]; // execute the delete operation before running any reaction | ||
var result = runProxyHandler('deleteProperty', target, key); // only queue reactions for delete operations which resulted in an actual change | ||
if (hadKey) { | ||
queueReactionsForOperation({ target, key, oldValue, type: 'delete' }); | ||
queueReactionsForOperation({ | ||
target, | ||
key, | ||
oldValue, | ||
type: 'delete' | ||
}); | ||
} | ||
return result; | ||
} // return an observable object instance when an observable class is instantiated | ||
function construct(target, args, newTarget) { | ||
return observable(runProxyHandler('construct', target, args, newTarget)); | ||
} | ||
// allow custom handlers for proxy traps that this lib does not use | ||
// this prevents the need for double Proxy wrapping for users | ||
// who wish to further extend JS behavior with Proxy traps | ||
var baseHandlers = _extends$1({}, handlers, { get, has, ownKeys, set, deleteProperty }); | ||
var proxyHandlers$1 = { | ||
get, | ||
has, | ||
ownKeys: ownKeys$1, | ||
set, | ||
deleteProperty, | ||
construct | ||
}; | ||
function observable(obj = {}) { | ||
function observable(obj = {}, options) { | ||
// if it is already an observable or it should not be wrapped, return it | ||
if (proxyToRaw.has(obj) || !shouldInstrument(obj)) { | ||
return obj; | ||
} | ||
// if it already has a cached observable wrapper, return it | ||
} // if it already has a cached observable wrapper, return it | ||
// otherwise create a new observable | ||
return rawToProxy.get(obj) || createObservable(obj); | ||
return rawToProxy.get(obj) || createObservable(obj, options); | ||
} | ||
function createObservable(obj) { | ||
function createObservable(obj, options) { | ||
// if it is a complex built-in object or a normal object, wrap it | ||
const handlers = getHandlers(obj) || baseHandlers; | ||
const observable = new Proxy(obj, handlers); | ||
// save these to switch between the raw object and the wrapped object with ease later | ||
var baseHandlers = getHandlers(obj) || proxyHandlers$1; | ||
var observable = new Proxy(obj, _objectSpread2(_objectSpread2({}, options === null || options === void 0 ? void 0 : options.proxyHandlers), baseHandlers)); // save these to switch between the raw object and the wrapped object with ease later | ||
rawToProxy.set(obj, observable); | ||
proxyToRaw.set(observable, obj); | ||
// init basic data structures to save and cleanup later (observable.prop -> reaction) connections | ||
proxyToRaw.set(observable, obj); // add custom options to the raw object | ||
if (options) { | ||
rawToOptions.set(obj, options); | ||
} // init basic data structures to save and cleanup later (observable.prop -> reaction) connections | ||
storeObservable(obj); | ||
return observable; | ||
} // if observable.prop is an object, wrap it in an observable too | ||
// this is needed to intercept property access on that object too | ||
function observableChild(child, parent) { | ||
if (typeof child === 'object' && child !== null || typeof child === 'function') { | ||
// pass the parent's options to the child object | ||
// this creates a 'deep proxy' which shares custom handlers deeply with its object children | ||
var options = rawToOptions.get(parent); | ||
return observable(child, options); | ||
} | ||
return child; | ||
} | ||
function isObservable(obj) { | ||
return proxyToRaw.has(obj); | ||
} | ||
function raw(obj) { | ||
@@ -515,2 +680,2 @@ return proxyToRaw.get(obj) || obj; | ||
export { observe, unobserve, observable, isObservable, raw, setHandlers, clearHandlers, defaultHandlers }; | ||
export { collectionHandlers, isObservable, observable, observe, proxyHandlers, raw, reactionHandlers, unobserve }; |
{ | ||
"name": "@nx-js/observer-util", | ||
"version": "4.3.0-alpha.0", | ||
"version": "4.3.0-alpha.1", | ||
"description": "Simple transparent reactivity with 100% language coverage. Made with ES6 Proxies.", | ||
@@ -13,3 +13,3 @@ "main": "dist/cjs.es5.js", | ||
"scripts": { | ||
"test": "node ./scripts/test.js", | ||
"test": "jest --config jest.config.json", | ||
"test-builds": "node ./scripts/testBuilds.js", | ||
@@ -20,3 +20,3 @@ "debug": "node ./scripts/debug.js", | ||
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", | ||
"build": "node ./scripts/build.js", | ||
"build": "rollup --config rollup.config.js", | ||
"build-toc": "node ./scripts/buildToc.js" | ||
@@ -48,31 +48,17 @@ }, | ||
"devDependencies": { | ||
"babel-core": "6.26.3", | ||
"babel-preset-es2016": "^6.24.1", | ||
"babel-preset-es2017": "^6.24.1", | ||
"babel-preset-react": "^6.24.1", | ||
"babel-preset-stage-0": "^6.24.1", | ||
"buble": "^0.15.2", | ||
"chai": "^4.1.2", | ||
"coveralls": "^3.0.1", | ||
"karma": "^2.0.2", | ||
"karma-chai": "^0.1.0", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-coverage": "^1.1.2", | ||
"karma-mocha": "^1.3.0", | ||
"karma-mocha-reporter": "^2.2.5", | ||
"karma-rollup-preprocessor": "^6.0.0", | ||
"karma-source-map-support": "^1.3.0", | ||
"@babel/core": "^7.9.6", | ||
"@babel/plugin-proposal-class-properties": "^7.8.3", | ||
"@babel/preset-env": "^7.9.6", | ||
"@babel/register": "^7.9.0", | ||
"babel-eslint": "^10.1.0", | ||
"coveralls": "^3.1.0", | ||
"jest": "^26.0.1", | ||
"markdown-toc": "^1.2.0", | ||
"mocha": "^5.2.0", | ||
"nyc": "12.0.2", | ||
"pre-push": "^0.1.1", | ||
"prettier": "^1.13.5", | ||
"rollup": "^0.60.1", | ||
"rollup-plugin-alias": "^1.4.0", | ||
"rollup-plugin-auto-external": "^1.2.0", | ||
"rollup-plugin-babel": "^3.0.4", | ||
"rollup-plugin-commonjs": "^9.1.3", | ||
"rollup-plugin-coverage": "^0.1.4", | ||
"rollup-plugin-node-resolve": "^3.3.0", | ||
"standard": "^11.0.1" | ||
"prettier": "^2.0.5", | ||
"rollup": "^2.10.4", | ||
"rollup-plugin-auto-external": "^2.0.0", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"standard": "^14.3.4" | ||
}, | ||
@@ -83,5 +69,6 @@ "engines": { | ||
"standard": { | ||
"parser": "babel-eslint", | ||
"env": [ | ||
"browser", | ||
"mocha" | ||
"jest" | ||
] | ||
@@ -88,0 +75,0 @@ }, |
@@ -27,3 +27,2 @@ # The Observer Utility | ||
* [Platform support](#platform-support) | ||
* [Alternative builds](#alternative-builds) | ||
* [Contributing](#contributing) | ||
@@ -441,13 +440,2 @@ | ||
## 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 | ||
@@ -454,0 +442,0 @@ |
declare module '@nx-js/observer-util' { | ||
function observable<Observable extends object>(obj?: Observable): Observable | ||
interface ProxyHandlers { | ||
apply?(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any; | ||
construct?(target: Function, argumentsList: ArrayLike<any>, newTarget?: any): any; | ||
defineProperty?(target: object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean; | ||
deleteProperty?(target: object, propertyKey: PropertyKey): boolean; | ||
get?(target: object, propertyKey: PropertyKey, receiver?: any): any; | ||
getOwnPropertyDescriptor?(target: object, propertyKey: PropertyKey): PropertyDescriptor | undefined; | ||
getPrototypeOf?(target: object): object; | ||
has?(target: object, propertyKey: PropertyKey): boolean; | ||
isExtensible?(target: object): boolean; | ||
ownKeys?(target: object): PropertyKey[]; | ||
preventExtensions?(target: object): boolean; | ||
set?(target: object, propertyKey: PropertyKey, value: any, receiver?: any): boolean; | ||
setPrototypeOf?(target: object, proto: any): boolean; | ||
} | ||
type Collection = Map<any, any> | WeakMap<any, any> | Set<any> | WeakSet<any> | ||
interface CollectionHandlers { | ||
add?<Target extends Collection>(target: Target, value: any): Target; | ||
clear?(target: Collection): void; | ||
delete?(target: Collection, key: any): boolean; | ||
get?(target: Collection, key: any): any; | ||
has?(target: Collection, key: any): boolean; | ||
set?<Target extends Collection>(target: Target,key: any, value: any): Target; | ||
size?(target: Collection): number; | ||
forEach?(target: Collection,callbackfn: (value: any, key: any, map: Map<any, any>) => void, thisArg?: any): void; | ||
[Symbol.iterator]?(): IterableIterator<any>; | ||
entries?(): IterableIterator<[any, any]>; | ||
keys?(): IterableIterator<any>; | ||
values?(): IterableIterator<any>; | ||
} | ||
interface ReactionHandlers { | ||
transformReactions?(target: any, propertyKey: PropertyKey, reactions: [Function]): [Function] | ||
} | ||
interface ObservableOptions { | ||
proxyHandlers?: ProxyHandlers; | ||
collectionHandlers?: CollectionHandlers; | ||
reactionHandlers?: ReactionHandlers; | ||
} | ||
function observable<Observable extends object>(obj?: Observable, options?: ObservableOptions): Observable | ||
function isObservable(obj: object): boolean | ||
@@ -7,10 +51,10 @@ function raw<Observable extends object>(obj: Observable): Observable | ||
interface Scheduler { | ||
add: Function | ||
delete: Function | ||
add: Function; | ||
delete: Function; | ||
} | ||
interface ObserveOptions { | ||
scheduler?: Scheduler | Function | ||
debugger?: Function | ||
lazy?: boolean | ||
scheduler?: Scheduler | Function; | ||
debugger?: Function; | ||
lazy?: boolean; | ||
} | ||
@@ -17,0 +61,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
15
61599
6
1149
443
1