vivisector
Advanced tools
Comparing version 1.4.0 to 1.5.0
@@ -64,14 +64,2 @@ | ||
var VX_EVENT_TYPE; | ||
(function (VX_EVENT_TYPE) { | ||
VX_EVENT_TYPE["ADD"] = "add"; | ||
VX_EVENT_TYPE["DEL"] = "del"; | ||
VX_EVENT_TYPE["SET"] = "set"; | ||
})(VX_EVENT_TYPE || (VX_EVENT_TYPE = {})); | ||
var VX_LISTENER_INTERNALS; | ||
(function (VX_LISTENER_INTERNALS) { | ||
VX_LISTENER_INTERNALS["ADD"] = "addEventListener"; | ||
VX_LISTENER_INTERNALS["REM"] = "removeEventListener"; | ||
})(VX_LISTENER_INTERNALS || (VX_LISTENER_INTERNALS = {})); | ||
var unboundedSlice = Array.prototype.slice; | ||
@@ -89,3 +77,3 @@ var slice = Function.prototype.call.bind(unboundedSlice); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; // eslint-disable-line @typescript-eslint/no-explicit-any | ||
var key = keys[i]; | ||
var desc = descriptors[key]; | ||
@@ -96,3 +84,3 @@ if (!desc.writable) { | ||
} | ||
if (desc.get || desc.set) | ||
if (desc.get || desc.set) { | ||
descriptors[key] = { | ||
@@ -104,2 +92,3 @@ configurable: true, | ||
}; | ||
} | ||
} | ||
@@ -130,4 +119,4 @@ return Object.create(Object.getPrototypeOf(base), descriptors); | ||
function isArrayProto(target, prop) { | ||
return Array.isArray(target) | ||
&& Object.getOwnPropertyNames(Array.prototype) | ||
return Array.isArray(target) && | ||
Object.getOwnPropertyNames(Array.prototype) | ||
.includes(prop); | ||
@@ -219,3 +208,3 @@ } | ||
function validateEventHandler(eventName, handler) { | ||
if (!(eventName in this.handlers)) { | ||
if (!(eventName in this.handlerStore)) { | ||
throw VxException.create(new VxException({ | ||
@@ -232,2 +221,15 @@ reason: "An unknown event name '" + eventName + "' was provided; there are no subscribable events matching this identifier" | ||
var VX_EVENT_TYPE; | ||
(function (VX_EVENT_TYPE) { | ||
VX_EVENT_TYPE["ADD"] = "add"; | ||
VX_EVENT_TYPE["DEL"] = "del"; | ||
VX_EVENT_TYPE["SET"] = "set"; | ||
VX_EVENT_TYPE["BATCHED"] = "batched"; | ||
})(VX_EVENT_TYPE || (VX_EVENT_TYPE = {})); | ||
var VX_LISTENER_INTERNALS; | ||
(function (VX_LISTENER_INTERNALS) { | ||
VX_LISTENER_INTERNALS["ADD"] = "addEventListener"; | ||
VX_LISTENER_INTERNALS["REM"] = "removeEventListener"; | ||
})(VX_LISTENER_INTERNALS || (VX_LISTENER_INTERNALS = {})); | ||
/** | ||
@@ -241,9 +243,10 @@ * Implements base state and shared functionality for a Vivisector observable | ||
/** | ||
* Event-correlated handlers; below are defaults | ||
* @property {VxEventHandlerStore} | ||
*/ | ||
this.handlers = (_a = {}, | ||
_a[VX_EVENT_TYPE.ADD] = [], | ||
_a[VX_EVENT_TYPE.DEL] = [], | ||
_a[VX_EVENT_TYPE.SET] = [], | ||
* Event-correlated handlers; below are defaults | ||
* @property {VxEventHandlerStore} | ||
*/ | ||
this.handlerStore = (_a = {}, | ||
_a[VX_EVENT_TYPE.ADD] = new Set(), | ||
_a[VX_EVENT_TYPE.DEL] = new Set(), | ||
_a[VX_EVENT_TYPE.SET] = new Set(), | ||
_a[VX_EVENT_TYPE.BATCHED] = new Set(), | ||
_a); | ||
@@ -261,13 +264,2 @@ this.internals = __spreadArray([], Object.values(VX_LISTENER_INTERNALS)); | ||
/** | ||
* @summary Serially invokes each handler of the given event type | ||
* @param {object} event An object containing data about the event | ||
* @param {object} context The `this` value on which to call each instance | ||
*/ | ||
BaseObservableFactory.prototype.raiseEvent = function (event, context) { | ||
this.handlers[event.type] | ||
.forEach(function (handler) { | ||
handler.call(context, event); | ||
}); | ||
}; | ||
/** | ||
* @summary Programmatically define `addEventListener`, `removeEventListener` on the proxied object | ||
@@ -279,6 +271,7 @@ * @param context The context (i.e. `this` instance) of the target object on which the | ||
var _this = this; | ||
defineNonConfigurableProp(context, VX_LISTENER_INTERNALS.ADD, function (eventName, handler) { | ||
defineNonConfigurableProp(context, VX_LISTENER_INTERNALS.ADD, function (eventName, handler, _a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.alwaysCommit, alwaysCommit = _c === void 0 ? false : _c; | ||
validateEventHandler.call(_this, eventName, handler); | ||
_this.handlers[eventName] | ||
.push(handler); | ||
_this.handlerStore[eventName] | ||
.add({ handler: handler, alwaysCommit: alwaysCommit }); | ||
return context; | ||
@@ -288,9 +281,8 @@ }); | ||
validateEventHandler.call(_this, eventName, handler); | ||
var handlerSet = _this.handlers[eventName]; | ||
var handlerSetLen = handlerSet.length; | ||
while (--handlerSetLen >= 0) { | ||
if (handlerSet[handlerSetLen] === handler) { | ||
handlerSet.splice(handlerSetLen, 1); | ||
var handlers = _this.handlerStore[eventName]; | ||
handlers.forEach(function (ref) { | ||
if (handler === ref.handler) { | ||
handlers["delete"](ref); | ||
} | ||
} | ||
}); | ||
return context; | ||
@@ -300,8 +292,127 @@ }); | ||
}; | ||
/** | ||
* @summary Serially invokes each handler of the given event type | ||
* @param {object} event An object containing data about the event | ||
* @param {object} context The `this` value on which to call each instance | ||
*/ | ||
BaseObservableFactory.prototype.raiseEvent = function (event, context, done) { | ||
this.handlerStore[event.type] | ||
.forEach(function (_a) { | ||
var handler = _a.handler, alwaysCommit = _a.alwaysCommit; | ||
var finalDoneFunction = done; | ||
if (alwaysCommit) { | ||
done(true); | ||
finalDoneFunction = function () { }; | ||
} | ||
handler.call(context, event, finalDoneFunction); | ||
}); | ||
}; | ||
return BaseObservableFactory; | ||
}()); | ||
var DoneFunctionBuilder = function (ret) { | ||
var done = function (commit) { | ||
if (commit) | ||
return ret(); | ||
}; | ||
return done; | ||
}; | ||
var eventedArrayPrototypeResolver = function (target, prop) { | ||
var _this = this; | ||
var ogMethod = target[prop]; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var nextState = shallowCopy(target); | ||
var prevState = shallowCopy(target); | ||
if (prop == 'shift') { | ||
var ret = nextState.shift(); | ||
var done = DoneFunctionBuilder(function () { return Array.prototype.shift.call(target); }); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return ret; | ||
} | ||
if (prop == 'pop') { | ||
var ret = nextState.pop(); | ||
var done = DoneFunctionBuilder(function () { | ||
target.length = target.length - 1; | ||
}); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return ret; | ||
} | ||
if (prop == 'unshift') { | ||
var ret = target.length; | ||
if (args.length) { | ||
var done = DoneFunctionBuilder(function () { | ||
var _a; | ||
return (_a = Array.prototype.unshift).call.apply(_a, __spreadArray([target], args)); | ||
}); | ||
ret = nextState.unshift.apply(nextState, args); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
} | ||
return ret; | ||
} | ||
if (prop == 'reverse') { | ||
var done = DoneFunctionBuilder(function () { return target.reverse(); }); | ||
nextState.reverse(); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return nextState; | ||
} | ||
if (prop == 'push') { | ||
var done = DoneFunctionBuilder(function () { return target.push.apply(target, args); }); | ||
nextState.push.apply(nextState, args); | ||
_this.raiseEvent({ | ||
type: args.length > 1 ? VX_EVENT_TYPE.BATCHED : VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return nextState.length; | ||
} | ||
if (args.length) { | ||
var _loop_1 = function (arg) { | ||
var done = DoneFunctionBuilder(function () { return ogMethod.apply(target, [arg]); }); | ||
ogMethod.apply(nextState, [arg]); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
prevState = shallowCopy(target); | ||
}; | ||
// raise the event for each argument | ||
for (var _a = 0, args_1 = args; _a < args_1.length; _a++) { | ||
var arg = args_1[_a]; | ||
_loop_1(arg); | ||
} | ||
return nextState; | ||
} | ||
return ogMethod.apply(target, args); | ||
}; | ||
}; | ||
var batchedMethods = [ | ||
'shift', | ||
'unshift' | ||
'unshift', | ||
'push', | ||
'reverse', | ||
'sort', | ||
'pop' | ||
]; | ||
@@ -319,3 +430,3 @@ /** | ||
// otherwise, a method like `splice` or the aforementioned `shift` will be handled by the proxy, | ||
// invoking traps for pulling an element out of the array, reindexing that array, | ||
// invoking traps for pulling an element out of the array, re-indexing that array, | ||
// and modifying the array's length property | ||
@@ -325,25 +436,3 @@ // so here, we listen for one of these methods, then allow the call to `fall through`, where it | ||
if (Array.isArray(target) && batchedMethods.includes(prop)) { | ||
var ogMethod_1 = target[prop]; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var prevState = shallowCopy(target); | ||
if (args.length) { | ||
// raise the event for each argument | ||
for (var _a = 0, _b = args.reverse(); _a < _b.length; _a++) { | ||
var arg = _b[_a]; | ||
ogMethod_1.apply(target, [arg]); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
prevState = shallowCopy(target); | ||
} | ||
return target.length; | ||
} | ||
return ogMethod_1.apply(target, args); | ||
}; | ||
return eventedArrayPrototypeResolver.call(_this, target, prop); | ||
} | ||
@@ -366,4 +455,4 @@ // we use reflection to mitigate violation of Proxy invariants, as described in the specification here: | ||
} | ||
var prevState = shallowCopy(target); | ||
var ret = Reflect.set(target, prop, value); | ||
var _a = [shallowCopy(target), shallowCopy(target)], prevState = _a[0], nextState = _a[1]; | ||
var ret = Reflect.set(nextState, prop, value); | ||
if (!(prop in prevState) || isArrayPropOutOfBounds(prevState, prop)) { | ||
@@ -373,4 +462,4 @@ _this.raiseEvent({ | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.set(target, prop, value); })); | ||
} | ||
@@ -381,4 +470,4 @@ else { | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.set(target, prop, value); })); | ||
} | ||
@@ -391,20 +480,20 @@ return ret; | ||
} | ||
var prevState = shallowCopy(target); | ||
var _a = [shallowCopy(target), shallowCopy(target)], prevState = _a[0], nextState = _a[1]; | ||
var ret = true; | ||
if (Array.isArray(target)) { | ||
target.splice(Number(prop), 1); | ||
if (Array.isArray(nextState)) { | ||
nextState.splice(Number(prop), 1); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return target.splice(Number(prop), 1); })); | ||
return ret; | ||
} | ||
else if (prop in prevState) { | ||
ret = Reflect.deleteProperty(target, prop); | ||
Reflect.deleteProperty(nextState, prop); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.deleteProperty(target, prop); })); | ||
return ret; | ||
@@ -428,23 +517,3 @@ } | ||
var excisedInitialState = shallowCopy(initialState); | ||
var proxy = this.defineListeners(new Proxy(excisedInitialState, this.rootHandler)); | ||
if (Array.isArray(excisedInitialState)) { | ||
var observableCtx_1 = this; | ||
Object.defineProperty(proxy, 'shift', { | ||
configurable: true, | ||
value: function () { | ||
// only actionable if array contains elements | ||
if (this.length > 0) { | ||
var prevState = shallowCopy(this); | ||
var item = Array.prototype.shift.call(this); | ||
observableCtx_1.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: this | ||
}, observableCtx_1); | ||
return item; | ||
} | ||
} | ||
}); | ||
} | ||
return proxy; | ||
return this.defineListeners(new Proxy(excisedInitialState, this.rootHandler)); | ||
}; | ||
@@ -451,0 +520,0 @@ return ProxiedObservableFactory; |
declare enum VX_EVENT_TYPE { | ||
ADD = "add", | ||
DEL = "del", | ||
SET = "set" | ||
SET = "set", | ||
BATCHED = "batched" | ||
} | ||
@@ -10,10 +11,2 @@ declare enum VX_LISTENER_INTERNALS { | ||
} | ||
declare type VxState = object | Array<any>; | ||
declare type VxEvent<T> = { | ||
type: VX_EVENT_TYPE; | ||
prevState: T; | ||
nextState: T; | ||
}; | ||
declare type VxEventHandler = (e: VxEvent<VxState>) => void; | ||
declare type VxEventRegistrar = (eventName: VX_EVENT_TYPE, handler: VxEventHandler) => VxEventedObject; | ||
interface VxEventedObject { | ||
@@ -24,2 +17,18 @@ readonly [VX_LISTENER_INTERNALS.ADD]: VxEventRegistrar; | ||
} | ||
interface DoneFunction { | ||
(commit: boolean): void; | ||
} | ||
interface VxAddEventListenerOpts { | ||
alwaysCommit?: boolean; | ||
} | ||
interface VxEventRegistrar { | ||
(eventName: VX_EVENT_TYPE, handler: VxEventHandler, opts?: VxAddEventListenerOpts): VxEventedObject; | ||
} | ||
declare type VxState = object | any[]; | ||
declare type VxEvent<T extends VxState> = { | ||
type: VX_EVENT_TYPE; | ||
prevState: T; | ||
nextState: T; | ||
}; | ||
declare type VxEventHandler = (e: VxEvent<VxState>, done: DoneFunction) => void; | ||
@@ -26,0 +35,0 @@ declare global { |
@@ -60,14 +60,2 @@ | ||
var VX_EVENT_TYPE; | ||
(function (VX_EVENT_TYPE) { | ||
VX_EVENT_TYPE["ADD"] = "add"; | ||
VX_EVENT_TYPE["DEL"] = "del"; | ||
VX_EVENT_TYPE["SET"] = "set"; | ||
})(VX_EVENT_TYPE || (VX_EVENT_TYPE = {})); | ||
var VX_LISTENER_INTERNALS; | ||
(function (VX_LISTENER_INTERNALS) { | ||
VX_LISTENER_INTERNALS["ADD"] = "addEventListener"; | ||
VX_LISTENER_INTERNALS["REM"] = "removeEventListener"; | ||
})(VX_LISTENER_INTERNALS || (VX_LISTENER_INTERNALS = {})); | ||
var unboundedSlice = Array.prototype.slice; | ||
@@ -85,3 +73,3 @@ var slice = Function.prototype.call.bind(unboundedSlice); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; // eslint-disable-line @typescript-eslint/no-explicit-any | ||
var key = keys[i]; | ||
var desc = descriptors[key]; | ||
@@ -92,3 +80,3 @@ if (!desc.writable) { | ||
} | ||
if (desc.get || desc.set) | ||
if (desc.get || desc.set) { | ||
descriptors[key] = { | ||
@@ -100,2 +88,3 @@ configurable: true, | ||
}; | ||
} | ||
} | ||
@@ -126,4 +115,4 @@ return Object.create(Object.getPrototypeOf(base), descriptors); | ||
function isArrayProto(target, prop) { | ||
return Array.isArray(target) | ||
&& Object.getOwnPropertyNames(Array.prototype) | ||
return Array.isArray(target) && | ||
Object.getOwnPropertyNames(Array.prototype) | ||
.includes(prop); | ||
@@ -215,3 +204,3 @@ } | ||
function validateEventHandler(eventName, handler) { | ||
if (!(eventName in this.handlers)) { | ||
if (!(eventName in this.handlerStore)) { | ||
throw VxException.create(new VxException({ | ||
@@ -228,2 +217,15 @@ reason: "An unknown event name '" + eventName + "' was provided; there are no subscribable events matching this identifier" | ||
var VX_EVENT_TYPE; | ||
(function (VX_EVENT_TYPE) { | ||
VX_EVENT_TYPE["ADD"] = "add"; | ||
VX_EVENT_TYPE["DEL"] = "del"; | ||
VX_EVENT_TYPE["SET"] = "set"; | ||
VX_EVENT_TYPE["BATCHED"] = "batched"; | ||
})(VX_EVENT_TYPE || (VX_EVENT_TYPE = {})); | ||
var VX_LISTENER_INTERNALS; | ||
(function (VX_LISTENER_INTERNALS) { | ||
VX_LISTENER_INTERNALS["ADD"] = "addEventListener"; | ||
VX_LISTENER_INTERNALS["REM"] = "removeEventListener"; | ||
})(VX_LISTENER_INTERNALS || (VX_LISTENER_INTERNALS = {})); | ||
/** | ||
@@ -237,9 +239,10 @@ * Implements base state and shared functionality for a Vivisector observable | ||
/** | ||
* Event-correlated handlers; below are defaults | ||
* @property {VxEventHandlerStore} | ||
*/ | ||
this.handlers = (_a = {}, | ||
_a[VX_EVENT_TYPE.ADD] = [], | ||
_a[VX_EVENT_TYPE.DEL] = [], | ||
_a[VX_EVENT_TYPE.SET] = [], | ||
* Event-correlated handlers; below are defaults | ||
* @property {VxEventHandlerStore} | ||
*/ | ||
this.handlerStore = (_a = {}, | ||
_a[VX_EVENT_TYPE.ADD] = new Set(), | ||
_a[VX_EVENT_TYPE.DEL] = new Set(), | ||
_a[VX_EVENT_TYPE.SET] = new Set(), | ||
_a[VX_EVENT_TYPE.BATCHED] = new Set(), | ||
_a); | ||
@@ -257,13 +260,2 @@ this.internals = __spreadArray([], Object.values(VX_LISTENER_INTERNALS)); | ||
/** | ||
* @summary Serially invokes each handler of the given event type | ||
* @param {object} event An object containing data about the event | ||
* @param {object} context The `this` value on which to call each instance | ||
*/ | ||
BaseObservableFactory.prototype.raiseEvent = function (event, context) { | ||
this.handlers[event.type] | ||
.forEach(function (handler) { | ||
handler.call(context, event); | ||
}); | ||
}; | ||
/** | ||
* @summary Programmatically define `addEventListener`, `removeEventListener` on the proxied object | ||
@@ -275,6 +267,7 @@ * @param context The context (i.e. `this` instance) of the target object on which the | ||
var _this = this; | ||
defineNonConfigurableProp(context, VX_LISTENER_INTERNALS.ADD, function (eventName, handler) { | ||
defineNonConfigurableProp(context, VX_LISTENER_INTERNALS.ADD, function (eventName, handler, _a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.alwaysCommit, alwaysCommit = _c === void 0 ? false : _c; | ||
validateEventHandler.call(_this, eventName, handler); | ||
_this.handlers[eventName] | ||
.push(handler); | ||
_this.handlerStore[eventName] | ||
.add({ handler: handler, alwaysCommit: alwaysCommit }); | ||
return context; | ||
@@ -284,9 +277,8 @@ }); | ||
validateEventHandler.call(_this, eventName, handler); | ||
var handlerSet = _this.handlers[eventName]; | ||
var handlerSetLen = handlerSet.length; | ||
while (--handlerSetLen >= 0) { | ||
if (handlerSet[handlerSetLen] === handler) { | ||
handlerSet.splice(handlerSetLen, 1); | ||
var handlers = _this.handlerStore[eventName]; | ||
handlers.forEach(function (ref) { | ||
if (handler === ref.handler) { | ||
handlers["delete"](ref); | ||
} | ||
} | ||
}); | ||
return context; | ||
@@ -296,8 +288,127 @@ }); | ||
}; | ||
/** | ||
* @summary Serially invokes each handler of the given event type | ||
* @param {object} event An object containing data about the event | ||
* @param {object} context The `this` value on which to call each instance | ||
*/ | ||
BaseObservableFactory.prototype.raiseEvent = function (event, context, done) { | ||
this.handlerStore[event.type] | ||
.forEach(function (_a) { | ||
var handler = _a.handler, alwaysCommit = _a.alwaysCommit; | ||
var finalDoneFunction = done; | ||
if (alwaysCommit) { | ||
done(true); | ||
finalDoneFunction = function () { }; | ||
} | ||
handler.call(context, event, finalDoneFunction); | ||
}); | ||
}; | ||
return BaseObservableFactory; | ||
}()); | ||
var DoneFunctionBuilder = function (ret) { | ||
var done = function (commit) { | ||
if (commit) | ||
return ret(); | ||
}; | ||
return done; | ||
}; | ||
var eventedArrayPrototypeResolver = function (target, prop) { | ||
var _this = this; | ||
var ogMethod = target[prop]; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var nextState = shallowCopy(target); | ||
var prevState = shallowCopy(target); | ||
if (prop == 'shift') { | ||
var ret = nextState.shift(); | ||
var done = DoneFunctionBuilder(function () { return Array.prototype.shift.call(target); }); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return ret; | ||
} | ||
if (prop == 'pop') { | ||
var ret = nextState.pop(); | ||
var done = DoneFunctionBuilder(function () { | ||
target.length = target.length - 1; | ||
}); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return ret; | ||
} | ||
if (prop == 'unshift') { | ||
var ret = target.length; | ||
if (args.length) { | ||
var done = DoneFunctionBuilder(function () { | ||
var _a; | ||
return (_a = Array.prototype.unshift).call.apply(_a, __spreadArray([target], args)); | ||
}); | ||
ret = nextState.unshift.apply(nextState, args); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
} | ||
return ret; | ||
} | ||
if (prop == 'reverse') { | ||
var done = DoneFunctionBuilder(function () { return target.reverse(); }); | ||
nextState.reverse(); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return nextState; | ||
} | ||
if (prop == 'push') { | ||
var done = DoneFunctionBuilder(function () { return target.push.apply(target, args); }); | ||
nextState.push.apply(nextState, args); | ||
_this.raiseEvent({ | ||
type: args.length > 1 ? VX_EVENT_TYPE.BATCHED : VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return nextState.length; | ||
} | ||
if (args.length) { | ||
var _loop_1 = function (arg) { | ||
var done = DoneFunctionBuilder(function () { return ogMethod.apply(target, [arg]); }); | ||
ogMethod.apply(nextState, [arg]); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
prevState = shallowCopy(target); | ||
}; | ||
// raise the event for each argument | ||
for (var _a = 0, args_1 = args; _a < args_1.length; _a++) { | ||
var arg = args_1[_a]; | ||
_loop_1(arg); | ||
} | ||
return nextState; | ||
} | ||
return ogMethod.apply(target, args); | ||
}; | ||
}; | ||
var batchedMethods = [ | ||
'shift', | ||
'unshift' | ||
'unshift', | ||
'push', | ||
'reverse', | ||
'sort', | ||
'pop' | ||
]; | ||
@@ -315,3 +426,3 @@ /** | ||
// otherwise, a method like `splice` or the aforementioned `shift` will be handled by the proxy, | ||
// invoking traps for pulling an element out of the array, reindexing that array, | ||
// invoking traps for pulling an element out of the array, re-indexing that array, | ||
// and modifying the array's length property | ||
@@ -321,25 +432,3 @@ // so here, we listen for one of these methods, then allow the call to `fall through`, where it | ||
if (Array.isArray(target) && batchedMethods.includes(prop)) { | ||
var ogMethod_1 = target[prop]; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var prevState = shallowCopy(target); | ||
if (args.length) { | ||
// raise the event for each argument | ||
for (var _a = 0, _b = args.reverse(); _a < _b.length; _a++) { | ||
var arg = _b[_a]; | ||
ogMethod_1.apply(target, [arg]); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
prevState = shallowCopy(target); | ||
} | ||
return target.length; | ||
} | ||
return ogMethod_1.apply(target, args); | ||
}; | ||
return eventedArrayPrototypeResolver.call(_this, target, prop); | ||
} | ||
@@ -362,4 +451,4 @@ // we use reflection to mitigate violation of Proxy invariants, as described in the specification here: | ||
} | ||
var prevState = shallowCopy(target); | ||
var ret = Reflect.set(target, prop, value); | ||
var _a = [shallowCopy(target), shallowCopy(target)], prevState = _a[0], nextState = _a[1]; | ||
var ret = Reflect.set(nextState, prop, value); | ||
if (!(prop in prevState) || isArrayPropOutOfBounds(prevState, prop)) { | ||
@@ -369,4 +458,4 @@ _this.raiseEvent({ | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.set(target, prop, value); })); | ||
} | ||
@@ -377,4 +466,4 @@ else { | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.set(target, prop, value); })); | ||
} | ||
@@ -387,20 +476,20 @@ return ret; | ||
} | ||
var prevState = shallowCopy(target); | ||
var _a = [shallowCopy(target), shallowCopy(target)], prevState = _a[0], nextState = _a[1]; | ||
var ret = true; | ||
if (Array.isArray(target)) { | ||
target.splice(Number(prop), 1); | ||
if (Array.isArray(nextState)) { | ||
nextState.splice(Number(prop), 1); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return target.splice(Number(prop), 1); })); | ||
return ret; | ||
} | ||
else if (prop in prevState) { | ||
ret = Reflect.deleteProperty(target, prop); | ||
Reflect.deleteProperty(nextState, prop); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.deleteProperty(target, prop); })); | ||
return ret; | ||
@@ -424,23 +513,3 @@ } | ||
var excisedInitialState = shallowCopy(initialState); | ||
var proxy = this.defineListeners(new Proxy(excisedInitialState, this.rootHandler)); | ||
if (Array.isArray(excisedInitialState)) { | ||
var observableCtx_1 = this; | ||
Object.defineProperty(proxy, 'shift', { | ||
configurable: true, | ||
value: function () { | ||
// only actionable if array contains elements | ||
if (this.length > 0) { | ||
var prevState = shallowCopy(this); | ||
var item = Array.prototype.shift.call(this); | ||
observableCtx_1.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: this | ||
}, observableCtx_1); | ||
return item; | ||
} | ||
} | ||
}); | ||
} | ||
return proxy; | ||
return this.defineListeners(new Proxy(excisedInitialState, this.rootHandler)); | ||
}; | ||
@@ -447,0 +516,0 @@ return ProxiedObservableFactory; |
@@ -66,14 +66,2 @@ | ||
var VX_EVENT_TYPE; | ||
(function (VX_EVENT_TYPE) { | ||
VX_EVENT_TYPE["ADD"] = "add"; | ||
VX_EVENT_TYPE["DEL"] = "del"; | ||
VX_EVENT_TYPE["SET"] = "set"; | ||
})(VX_EVENT_TYPE || (VX_EVENT_TYPE = {})); | ||
var VX_LISTENER_INTERNALS; | ||
(function (VX_LISTENER_INTERNALS) { | ||
VX_LISTENER_INTERNALS["ADD"] = "addEventListener"; | ||
VX_LISTENER_INTERNALS["REM"] = "removeEventListener"; | ||
})(VX_LISTENER_INTERNALS || (VX_LISTENER_INTERNALS = {})); | ||
var unboundedSlice = Array.prototype.slice; | ||
@@ -91,3 +79,3 @@ var slice = Function.prototype.call.bind(unboundedSlice); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = keys[i]; // eslint-disable-line @typescript-eslint/no-explicit-any | ||
var key = keys[i]; | ||
var desc = descriptors[key]; | ||
@@ -98,3 +86,3 @@ if (!desc.writable) { | ||
} | ||
if (desc.get || desc.set) | ||
if (desc.get || desc.set) { | ||
descriptors[key] = { | ||
@@ -106,2 +94,3 @@ configurable: true, | ||
}; | ||
} | ||
} | ||
@@ -132,4 +121,4 @@ return Object.create(Object.getPrototypeOf(base), descriptors); | ||
function isArrayProto(target, prop) { | ||
return Array.isArray(target) | ||
&& Object.getOwnPropertyNames(Array.prototype) | ||
return Array.isArray(target) && | ||
Object.getOwnPropertyNames(Array.prototype) | ||
.includes(prop); | ||
@@ -221,3 +210,3 @@ } | ||
function validateEventHandler(eventName, handler) { | ||
if (!(eventName in this.handlers)) { | ||
if (!(eventName in this.handlerStore)) { | ||
throw VxException.create(new VxException({ | ||
@@ -234,2 +223,15 @@ reason: "An unknown event name '" + eventName + "' was provided; there are no subscribable events matching this identifier" | ||
var VX_EVENT_TYPE; | ||
(function (VX_EVENT_TYPE) { | ||
VX_EVENT_TYPE["ADD"] = "add"; | ||
VX_EVENT_TYPE["DEL"] = "del"; | ||
VX_EVENT_TYPE["SET"] = "set"; | ||
VX_EVENT_TYPE["BATCHED"] = "batched"; | ||
})(VX_EVENT_TYPE || (VX_EVENT_TYPE = {})); | ||
var VX_LISTENER_INTERNALS; | ||
(function (VX_LISTENER_INTERNALS) { | ||
VX_LISTENER_INTERNALS["ADD"] = "addEventListener"; | ||
VX_LISTENER_INTERNALS["REM"] = "removeEventListener"; | ||
})(VX_LISTENER_INTERNALS || (VX_LISTENER_INTERNALS = {})); | ||
/** | ||
@@ -243,9 +245,10 @@ * Implements base state and shared functionality for a Vivisector observable | ||
/** | ||
* Event-correlated handlers; below are defaults | ||
* @property {VxEventHandlerStore} | ||
*/ | ||
this.handlers = (_a = {}, | ||
_a[VX_EVENT_TYPE.ADD] = [], | ||
_a[VX_EVENT_TYPE.DEL] = [], | ||
_a[VX_EVENT_TYPE.SET] = [], | ||
* Event-correlated handlers; below are defaults | ||
* @property {VxEventHandlerStore} | ||
*/ | ||
this.handlerStore = (_a = {}, | ||
_a[VX_EVENT_TYPE.ADD] = new Set(), | ||
_a[VX_EVENT_TYPE.DEL] = new Set(), | ||
_a[VX_EVENT_TYPE.SET] = new Set(), | ||
_a[VX_EVENT_TYPE.BATCHED] = new Set(), | ||
_a); | ||
@@ -263,13 +266,2 @@ this.internals = __spreadArray([], Object.values(VX_LISTENER_INTERNALS)); | ||
/** | ||
* @summary Serially invokes each handler of the given event type | ||
* @param {object} event An object containing data about the event | ||
* @param {object} context The `this` value on which to call each instance | ||
*/ | ||
BaseObservableFactory.prototype.raiseEvent = function (event, context) { | ||
this.handlers[event.type] | ||
.forEach(function (handler) { | ||
handler.call(context, event); | ||
}); | ||
}; | ||
/** | ||
* @summary Programmatically define `addEventListener`, `removeEventListener` on the proxied object | ||
@@ -281,6 +273,7 @@ * @param context The context (i.e. `this` instance) of the target object on which the | ||
var _this = this; | ||
defineNonConfigurableProp(context, VX_LISTENER_INTERNALS.ADD, function (eventName, handler) { | ||
defineNonConfigurableProp(context, VX_LISTENER_INTERNALS.ADD, function (eventName, handler, _a) { | ||
var _b = _a === void 0 ? {} : _a, _c = _b.alwaysCommit, alwaysCommit = _c === void 0 ? false : _c; | ||
validateEventHandler.call(_this, eventName, handler); | ||
_this.handlers[eventName] | ||
.push(handler); | ||
_this.handlerStore[eventName] | ||
.add({ handler: handler, alwaysCommit: alwaysCommit }); | ||
return context; | ||
@@ -290,9 +283,8 @@ }); | ||
validateEventHandler.call(_this, eventName, handler); | ||
var handlerSet = _this.handlers[eventName]; | ||
var handlerSetLen = handlerSet.length; | ||
while (--handlerSetLen >= 0) { | ||
if (handlerSet[handlerSetLen] === handler) { | ||
handlerSet.splice(handlerSetLen, 1); | ||
var handlers = _this.handlerStore[eventName]; | ||
handlers.forEach(function (ref) { | ||
if (handler === ref.handler) { | ||
handlers["delete"](ref); | ||
} | ||
} | ||
}); | ||
return context; | ||
@@ -302,8 +294,127 @@ }); | ||
}; | ||
/** | ||
* @summary Serially invokes each handler of the given event type | ||
* @param {object} event An object containing data about the event | ||
* @param {object} context The `this` value on which to call each instance | ||
*/ | ||
BaseObservableFactory.prototype.raiseEvent = function (event, context, done) { | ||
this.handlerStore[event.type] | ||
.forEach(function (_a) { | ||
var handler = _a.handler, alwaysCommit = _a.alwaysCommit; | ||
var finalDoneFunction = done; | ||
if (alwaysCommit) { | ||
done(true); | ||
finalDoneFunction = function () { }; | ||
} | ||
handler.call(context, event, finalDoneFunction); | ||
}); | ||
}; | ||
return BaseObservableFactory; | ||
}()); | ||
var DoneFunctionBuilder = function (ret) { | ||
var done = function (commit) { | ||
if (commit) | ||
return ret(); | ||
}; | ||
return done; | ||
}; | ||
var eventedArrayPrototypeResolver = function (target, prop) { | ||
var _this = this; | ||
var ogMethod = target[prop]; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var nextState = shallowCopy(target); | ||
var prevState = shallowCopy(target); | ||
if (prop == 'shift') { | ||
var ret = nextState.shift(); | ||
var done = DoneFunctionBuilder(function () { return Array.prototype.shift.call(target); }); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return ret; | ||
} | ||
if (prop == 'pop') { | ||
var ret = nextState.pop(); | ||
var done = DoneFunctionBuilder(function () { | ||
target.length = target.length - 1; | ||
}); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return ret; | ||
} | ||
if (prop == 'unshift') { | ||
var ret = target.length; | ||
if (args.length) { | ||
var done = DoneFunctionBuilder(function () { | ||
var _a; | ||
return (_a = Array.prototype.unshift).call.apply(_a, __spreadArray([target], args)); | ||
}); | ||
ret = nextState.unshift.apply(nextState, args); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
} | ||
return ret; | ||
} | ||
if (prop == 'reverse') { | ||
var done = DoneFunctionBuilder(function () { return target.reverse(); }); | ||
nextState.reverse(); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.BATCHED, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return nextState; | ||
} | ||
if (prop == 'push') { | ||
var done = DoneFunctionBuilder(function () { return target.push.apply(target, args); }); | ||
nextState.push.apply(nextState, args); | ||
_this.raiseEvent({ | ||
type: args.length > 1 ? VX_EVENT_TYPE.BATCHED : VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
return nextState.length; | ||
} | ||
if (args.length) { | ||
var _loop_1 = function (arg) { | ||
var done = DoneFunctionBuilder(function () { return ogMethod.apply(target, [arg]); }); | ||
ogMethod.apply(nextState, [arg]); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: nextState | ||
}, _this, done); | ||
prevState = shallowCopy(target); | ||
}; | ||
// raise the event for each argument | ||
for (var _a = 0, args_1 = args; _a < args_1.length; _a++) { | ||
var arg = args_1[_a]; | ||
_loop_1(arg); | ||
} | ||
return nextState; | ||
} | ||
return ogMethod.apply(target, args); | ||
}; | ||
}; | ||
var batchedMethods = [ | ||
'shift', | ||
'unshift' | ||
'unshift', | ||
'push', | ||
'reverse', | ||
'sort', | ||
'pop' | ||
]; | ||
@@ -321,3 +432,3 @@ /** | ||
// otherwise, a method like `splice` or the aforementioned `shift` will be handled by the proxy, | ||
// invoking traps for pulling an element out of the array, reindexing that array, | ||
// invoking traps for pulling an element out of the array, re-indexing that array, | ||
// and modifying the array's length property | ||
@@ -327,25 +438,3 @@ // so here, we listen for one of these methods, then allow the call to `fall through`, where it | ||
if (Array.isArray(target) && batchedMethods.includes(prop)) { | ||
var ogMethod_1 = target[prop]; | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var prevState = shallowCopy(target); | ||
if (args.length) { | ||
// raise the event for each argument | ||
for (var _a = 0, _b = args.reverse(); _a < _b.length; _a++) { | ||
var arg = _b[_a]; | ||
ogMethod_1.apply(target, [arg]); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.ADD, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
prevState = shallowCopy(target); | ||
} | ||
return target.length; | ||
} | ||
return ogMethod_1.apply(target, args); | ||
}; | ||
return eventedArrayPrototypeResolver.call(_this, target, prop); | ||
} | ||
@@ -368,4 +457,4 @@ // we use reflection to mitigate violation of Proxy invariants, as described in the specification here: | ||
} | ||
var prevState = shallowCopy(target); | ||
var ret = Reflect.set(target, prop, value); | ||
var _a = [shallowCopy(target), shallowCopy(target)], prevState = _a[0], nextState = _a[1]; | ||
var ret = Reflect.set(nextState, prop, value); | ||
if (!(prop in prevState) || isArrayPropOutOfBounds(prevState, prop)) { | ||
@@ -375,4 +464,4 @@ _this.raiseEvent({ | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.set(target, prop, value); })); | ||
} | ||
@@ -383,4 +472,4 @@ else { | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.set(target, prop, value); })); | ||
} | ||
@@ -393,20 +482,20 @@ return ret; | ||
} | ||
var prevState = shallowCopy(target); | ||
var _a = [shallowCopy(target), shallowCopy(target)], prevState = _a[0], nextState = _a[1]; | ||
var ret = true; | ||
if (Array.isArray(target)) { | ||
target.splice(Number(prop), 1); | ||
if (Array.isArray(nextState)) { | ||
nextState.splice(Number(prop), 1); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return target.splice(Number(prop), 1); })); | ||
return ret; | ||
} | ||
else if (prop in prevState) { | ||
ret = Reflect.deleteProperty(target, prop); | ||
Reflect.deleteProperty(nextState, prop); | ||
_this.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: target | ||
}, _this); | ||
nextState: nextState | ||
}, _this, DoneFunctionBuilder(function () { return Reflect.deleteProperty(target, prop); })); | ||
return ret; | ||
@@ -430,23 +519,3 @@ } | ||
var excisedInitialState = shallowCopy(initialState); | ||
var proxy = this.defineListeners(new Proxy(excisedInitialState, this.rootHandler)); | ||
if (Array.isArray(excisedInitialState)) { | ||
var observableCtx_1 = this; | ||
Object.defineProperty(proxy, 'shift', { | ||
configurable: true, | ||
value: function () { | ||
// only actionable if array contains elements | ||
if (this.length > 0) { | ||
var prevState = shallowCopy(this); | ||
var item = Array.prototype.shift.call(this); | ||
observableCtx_1.raiseEvent({ | ||
type: VX_EVENT_TYPE.DEL, | ||
prevState: prevState, | ||
nextState: this | ||
}, observableCtx_1); | ||
return item; | ||
} | ||
} | ||
}); | ||
} | ||
return proxy; | ||
return this.defineListeners(new Proxy(excisedInitialState, this.rootHandler)); | ||
}; | ||
@@ -453,0 +522,0 @@ return ProxiedObservableFactory; |
@@ -21,2 +21,2 @@ /** | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */var t,r,n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}!function(e){e.ADD="add",e.DEL="del",e.SET="set"}(t||(t={})),function(e){e.ADD="addEventListener",e.REM="removeEventListener"}(r||(r={}));var o=Array.prototype.slice,a=Function.prototype.call.bind(o);function u(e){if(Array.isArray(e))return a(e);for(var t=Object.getOwnPropertyDescriptors(e),r=Reflect.ownKeys(t),n=0;n<r.length;n++){var i=r[n],o=t[i];o.writable||(o.writable=!0,o.configurable=!0),(o.get||o.set)&&(t[i]={configurable:!0,writable:!!o.set,enumerable:o.enumerable,value:e[i]})}return Object.create(Object.getPrototypeOf(e),t)}function s(e,t,r){Object.defineProperty(e,t,{configurable:!1,enumerable:!1,writable:!1,value:r})}var c=function(e){function t(r){var n=e.call(this,r)||this;return Object.setPrototypeOf(n,t.prototype),n}return i(t,e),t}(function(e){function t(r){var n=e.call(this,r)||this;return Object.setPrototypeOf(n,t.prototype),n}return i(t,e),t}(Error)),f=function(){function e(e){var t=e.reason,r=e.source;this.reason=t,this.source=r}return e.create=function(e){return new c(e.serialize())},e.prototype.serializeSource=function(){if(!this.source)return"";var e=this.source;return"at "+e.filename+", Ln "+e.lineno},e.prototype.serialize=function(){return this.reason+" "+this.serializeSource()},e}();function l(e,t){if(!(e in this.handlers))throw f.create(new f({reason:"An unknown event name '"+e+"' was provided; there are no subscribable events matching this identifier"}));if("function"!=typeof t)throw f.create(new f({reason:"The provided event handler must be a function"}))}var p=function(){function e(){var e;this.handlers=((e={})[t.ADD]=[],e[t.DEL]=[],e[t.SET]=[],e),this.internals=function(e,t,r){if(r||2===arguments.length)for(var n,i=0,o=t.length;i<o;i++)!n&&i in t||(n||(n=Array.prototype.slice.call(t,0,i)),n[i]=t[i]);return e.concat(n||Array.prototype.slice.call(t))}([],Object.values(r))}return e.prototype.isConfigurableProp=function(e){return!this.internals.includes(e)},e.prototype.raiseEvent=function(e,t){this.handlers[e.type].forEach((function(r){r.call(t,e)}))},e.prototype.defineListeners=function(e){var t=this;return s(e,r.ADD,(function(r,n){return l.call(t,r,n),t.handlers[r].push(n),e})),s(e,r.REM,(function(r,n){l.call(t,r,n);for(var i=t.handlers[r],o=i.length;--o>=0;)i[o]===n&&i.splice(o,1);return e})),e},e}(),y=["shift","unshift"];function v(){var e=this,r={get:function(n,i,o){if(Array.isArray(n)&&y.includes(i)){var a=n[i];return function(){for(var r=[],i=0;i<arguments.length;i++)r[i]=arguments[i];var o=u(n);if(r.length){for(var s=0,c=r.reverse();s<c.length;s++){var f=c[s];a.apply(n,[f]),e.raiseEvent({type:t.ADD,prevState:o,nextState:n},e),o=u(n)}return n.length}return a.apply(n,r)}}var s=Reflect.get(n,i,o);return"object"==typeof s?new Proxy(s,r):s},set:function(r,n,i){if(!e.isConfigurableProp(n))return!1;if(function(e,t){return Array.isArray(e)&&Object.getOwnPropertyNames(Array.prototype).includes(t)}(r,n))return Reflect.set(r,n,i);var o=u(r),a=Reflect.set(r,n,i);return!(n in o)||function(e,t){var r=Number(t);return!!Number.isNaN(r)&&Array.isArray(e)&&r>e.length-1}(o,n)?e.raiseEvent({type:t.ADD,prevState:o,nextState:r},e):e.raiseEvent({type:t.SET,prevState:o,nextState:r},e),a},deleteProperty:function(r,n){if(!e.isConfigurableProp(n))return!1;var i=u(r),o=!0;return Array.isArray(r)?(r.splice(Number(n),1),e.raiseEvent({type:t.DEL,prevState:i,nextState:r},e),o):n in i?(o=Reflect.deleteProperty(r,n),e.raiseEvent({type:t.DEL,prevState:i,nextState:r},e),o):o}};return r}var h=function(e){function r(){var t=e.call(this)||this;return t.rootHandler=v.call(t),t}return i(r,e),r.prototype.create=function(e){var r=u(e),n=this.defineListeners(new Proxy(r,this.rootHandler));if(Array.isArray(r)){var i=this;Object.defineProperty(n,"shift",{configurable:!0,value:function(){if(this.length>0){var e=u(this),r=Array.prototype.shift.call(this);return i.raiseEvent({type:t.DEL,prevState:e,nextState:this},i),r}}})}return n},r}(p);e.vivisect=function(e){return(new h).create(e)},Object.defineProperty(e,"__esModule",{value:!0})})); | ||
***************************************************************************** */var t=function(e,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},t(e,r)};function r(e,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}function n(e,t,r){if(r||2===arguments.length)for(var n,i=0,o=t.length;i<o;i++)!n&&i in t||(n||(n=Array.prototype.slice.call(t,0,i)),n[i]=t[i]);return e.concat(n||Array.prototype.slice.call(t))}var i=Array.prototype.slice,o=Function.prototype.call.bind(i);function a(e){if(Array.isArray(e))return o(e);for(var t=Object.getOwnPropertyDescriptors(e),r=Reflect.ownKeys(t),n=0;n<r.length;n++){var i=r[n],a=t[i];a.writable||(a.writable=!0,a.configurable=!0),(a.get||a.set)&&(t[i]={configurable:!0,writable:!!a.set,enumerable:a.enumerable,value:e[i]})}return Object.create(Object.getPrototypeOf(e),t)}function u(e,t,r){Object.defineProperty(e,t,{configurable:!1,enumerable:!1,writable:!1,value:r})}var c,s,f=function(e){function t(r){var n=e.call(this,r)||this;return Object.setPrototypeOf(n,t.prototype),n}return r(t,e),t}(function(e){function t(r){var n=e.call(this,r)||this;return Object.setPrototypeOf(n,t.prototype),n}return r(t,e),t}(Error)),l=function(){function e(e){var t=e.reason,r=e.source;this.reason=t,this.source=r}return e.create=function(e){return new f(e.serialize())},e.prototype.serializeSource=function(){if(!this.source)return"";var e=this.source;return"at "+e.filename+", Ln "+e.lineno},e.prototype.serialize=function(){return this.reason+" "+this.serializeSource()},e}();function p(e,t){if(!(e in this.handlerStore))throw l.create(new l({reason:"An unknown event name '"+e+"' was provided; there are no subscribable events matching this identifier"}));if("function"!=typeof t)throw l.create(new l({reason:"The provided event handler must be a function"}))}!function(e){e.ADD="add",e.DEL="del",e.SET="set",e.BATCHED="batched"}(c||(c={})),function(e){e.ADD="addEventListener",e.REM="removeEventListener"}(s||(s={}));var y=function(){function e(){var e;this.handlerStore=((e={})[c.ADD]=new Set,e[c.DEL]=new Set,e[c.SET]=new Set,e[c.BATCHED]=new Set,e),this.internals=n([],Object.values(s))}return e.prototype.isConfigurableProp=function(e){return!this.internals.includes(e)},e.prototype.defineListeners=function(e){var t=this;return u(e,s.ADD,(function(r,n,i){var o=(void 0===i?{}:i).alwaysCommit,a=void 0!==o&&o;return p.call(t,r,n),t.handlerStore[r].add({handler:n,alwaysCommit:a}),e})),u(e,s.REM,(function(r,n){p.call(t,r,n);var i=t.handlerStore[r];return i.forEach((function(e){n===e.handler&&i.delete(e)})),e})),e},e.prototype.raiseEvent=function(e,t,r){this.handlerStore[e.type].forEach((function(n){var i=n.handler,o=n.alwaysCommit,a=r;o&&(r(!0),a=function(){}),i.call(t,e,a)}))},e}(),v=function(e){return function(t){if(t)return e()}},h=function(e,t){var r=this,i=e[t];return function(){for(var o=[],u=0;u<arguments.length;u++)o[u]=arguments[u];var s=a(e),f=a(e);if("shift"==t){var l=s.shift(),p=v((function(){return Array.prototype.shift.call(e)}));return r.raiseEvent({type:c.BATCHED,prevState:f,nextState:s},r,p),l}if("pop"==t){l=s.pop(),p=v((function(){e.length=e.length-1}));return r.raiseEvent({type:c.DEL,prevState:f,nextState:s},r,p),l}if("unshift"==t){l=e.length;if(o.length){p=v((function(){var t;return(t=Array.prototype.unshift).call.apply(t,n([e],o))}));l=s.unshift.apply(s,o),r.raiseEvent({type:c.BATCHED,prevState:f,nextState:s},r,p)}return l}if("reverse"==t){p=v((function(){return e.reverse()}));return s.reverse(),r.raiseEvent({type:c.BATCHED,prevState:f,nextState:s},r,p),s}if("push"==t){p=v((function(){return e.push.apply(e,o)}));return s.push.apply(s,o),r.raiseEvent({type:o.length>1?c.BATCHED:c.ADD,prevState:f,nextState:s},r,p),s.length}if(o.length){for(var y=function(t){var n=v((function(){return i.apply(e,[t])}));i.apply(s,[t]),r.raiseEvent({type:c.ADD,prevState:f,nextState:s},r,n),f=a(e)},h=0,d=o;h<d.length;h++){var b=d[h];y(b)}return s}return i.apply(e,o)}},d=["shift","unshift","push","reverse","sort","pop"];function b(){var e=this,t={get:function(r,n,i){if(Array.isArray(r)&&d.includes(n))return h.call(e,r,n);var o=Reflect.get(r,n,i);return"object"==typeof o?new Proxy(o,t):o},set:function(t,r,n){if(!e.isConfigurableProp(r))return!1;if(function(e,t){return Array.isArray(e)&&Object.getOwnPropertyNames(Array.prototype).includes(t)}(t,r))return Reflect.set(t,r,n);var i=[a(t),a(t)],o=i[0],u=i[1],s=Reflect.set(u,r,n);return!(r in o)||function(e,t){var r=Number(t);return!!Number.isNaN(r)&&Array.isArray(e)&&r>e.length-1}(o,r)?e.raiseEvent({type:c.ADD,prevState:o,nextState:u},e,v((function(){return Reflect.set(t,r,n)}))):e.raiseEvent({type:c.SET,prevState:o,nextState:u},e,v((function(){return Reflect.set(t,r,n)}))),s},deleteProperty:function(t,r){if(!e.isConfigurableProp(r))return!1;var n=[a(t),a(t)],i=n[0],o=n[1],u=!0;return Array.isArray(o)?(o.splice(Number(r),1),e.raiseEvent({type:c.DEL,prevState:i,nextState:o},e,v((function(){return t.splice(Number(r),1)}))),u):r in i?(Reflect.deleteProperty(o,r),e.raiseEvent({type:c.DEL,prevState:i,nextState:o},e,v((function(){return Reflect.deleteProperty(t,r)}))),u):u}};return t}var S=function(e){function t(){var t=e.call(this)||this;return t.rootHandler=b.call(t),t}return r(t,e),t.prototype.create=function(e){var t=a(e);return this.defineListeners(new Proxy(t,this.rootHandler))},t}(y);e.vivisect=function(e){return(new S).create(e)},Object.defineProperty(e,"__esModule",{value:!0})})); |
{ | ||
"name": "vivisector", | ||
"version": "1.4.0", | ||
"description": "add event listeners to js objects", | ||
"version": "1.5.0", | ||
"description": "reactive state management", | ||
"keywords": [ | ||
"observable", | ||
"observables", | ||
"event-driven", | ||
"reactive programming", | ||
"events" | ||
], | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/MatthewZito/vivisector-js.git" | ||
}, | ||
"author": "Matthew T Zito (goldmund)", | ||
"files": [ | ||
"dist/" | ||
], | ||
"exports": { | ||
"require": "./dist/vivisector.cjs.js", | ||
"import": "./dist/vivisector.es.js" | ||
}, | ||
"main": "./dist/vivisector.cjs.js", | ||
@@ -9,22 +29,14 @@ "browser": "./dist/vivisector.umd.js", | ||
"types": "dist/vivisector.d.ts", | ||
"exports": { | ||
"require": "./dist/vivisector.cjs.js", | ||
"import": "./dist/vivisector.es.js" | ||
}, | ||
"engines": { | ||
"node": ">= 10" | ||
}, | ||
"files": [ | ||
"dist/" | ||
], | ||
"scripts": { | ||
"coverage": "jest --coverage && npm run clean", | ||
"lint": "eslint \"lib/**/*.ts\" --no-fix", | ||
"lint:fix": "eslint \"lib/**/*.ts\" --fix", | ||
"coverage": "jest --coverage && pnpm clean", | ||
"lint": "eslint --ext .js,.ts,.json --no-fix .", | ||
"test": "jest --bail --passWithNoTests __tests__", | ||
"test:watch": "jest --watch", | ||
"prebuild": "npm run clean", | ||
"build": "npm run types && rollup -c", | ||
"prebuild": "pnpm clean", | ||
"build": "pnpm types && rollup -c", | ||
"clean": "rimraf coverage dist .build", | ||
"coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls && npm run clean", | ||
"coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls && pnpm clean", | ||
"prerelease": "npm pack && tar -xvzf *.tgz && rimraf package *.tgz", | ||
@@ -34,15 +46,2 @@ "semantic-release": "semantic-release", | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/MatthewZito/vivisector-js.git" | ||
}, | ||
"keywords": [ | ||
"observable", | ||
"observables", | ||
"event-driven", | ||
"reactive programming", | ||
"events" | ||
], | ||
"author": "Matthew T Zito (goldmund)", | ||
"license": "MIT", | ||
"bugs": { | ||
@@ -52,2 +51,8 @@ "url": "https://github.com/MatthewZito/vivisector-js/issues" | ||
"homepage": "https://github.com/MatthewZito/vivisector-js#readme", | ||
"lint-staged": { | ||
"lib/**/*.ts": [ | ||
"pnpm lint", | ||
"pnpm test" | ||
] | ||
}, | ||
"devDependencies": { | ||
@@ -59,2 +64,3 @@ "@babel/cli": "7.15.4", | ||
"@commitlint/config-conventional": "^13.1.0", | ||
"@magister_zito/eslint-config-typescript": "0.0.4", | ||
"@rollup/plugin-babel": "5.3.0", | ||
@@ -65,9 +71,7 @@ "@rollup/plugin-commonjs": "20.0.0", | ||
"@types/jest": "27.0.1", | ||
"@typescript-eslint/eslint-plugin": "4.31.0", | ||
"@typescript-eslint/parser": "4.31.0", | ||
"babel-eslint": "^10.1.0", | ||
"babel-jest": "27.1.0", | ||
"coveralls": "^3.1.1", | ||
"cz-conventional-changelog": "^3.3.0", | ||
"eslint": "^7.32.0", | ||
"eslint": "7.32.0", | ||
"eslint-plugin-jest": "24.4.0", | ||
"husky": "7.0.2", | ||
@@ -91,8 +95,2 @@ "jest": "27.1.0", | ||
}, | ||
"lint-staged": { | ||
"lib/**/*.ts": [ | ||
"npm run lint", | ||
"npm run test" | ||
] | ||
}, | ||
"commitlint": { | ||
@@ -99,0 +97,0 @@ "extends": [ |
@@ -1,2 +0,2 @@ | ||
![Vivisector Logo](/documentation/vx.png) | ||
![Vivisector Logo](/docs/vx.png) | ||
@@ -20,2 +20,3 @@ # Vivisector | Compact observables | ||
- [Event Types](#evtypes) | ||
- [Committing Mutations](#commit) | ||
- [Methods](#methods) | ||
@@ -26,6 +27,8 @@ - [Next](#next) | ||
For those moments when RXjs is overkill... | ||
*Note: this README is a WIP* | ||
*Vivisector* is a compact JavaScript library designed to support hassle-free reactive programming. *Vivisector* allows you to tether actions to specific types of mutation events, rendering your application's state event-bound. | ||
*Vivisector* is a compact TypeScript library for hassle-free reactive programming. *Vivisector* allows you to tether actions to specific types of mutation events, rendering your application's state event-bound. | ||
Furthermore, *Vivisector* grants you the ability to declaratively mutate state. Every event is accompanied by a `done` function which can be used to either commit the state change or reject it. | ||
Here's what that looks like: | ||
@@ -41,5 +44,9 @@ | ||
}) | ||
.addEventListener('set', ({ prevState, nextState }) => { | ||
if (nextState.email) { | ||
.addEventListener('set', ({ prevState, nextState, done }) => { | ||
if (!isValidEmail(nextState.email)) { | ||
emitErrorMessage(); | ||
done(false); | ||
} else { | ||
sendWelcomeEmail(); | ||
done(true); | ||
} | ||
@@ -49,9 +56,4 @@ | ||
}); | ||
state.email = '...'; // `sendWelcomeEmail` invoked | ||
``` | ||
*Vivisector* is flexible, compact, and straight-forward; it affords you fine-grained control by allowing you to decide when and what happens when state changes. | ||
### <a name="feat"></a> Features | ||
@@ -70,2 +72,3 @@ | ||
- harness the power of reactive programming without the excess boilerplate | ||
- declaratively cancel or commit state changes before they happen | ||
@@ -91,3 +94,3 @@ ## <a name="install"></a> Installation | ||
- `Vivisected` objects are COPIED by value, not reference | ||
- don't mutate state in callbacks - doing this will result in undefined behavior | ||
- don't mutate state in callbacks - doing this will result in undefined behavior; that's what the `done` function is for | ||
- nested objects become their own proxies | ||
@@ -113,3 +116,3 @@ | ||
Create a new *Observable* - in this example, an array - and register a handler to fire when any *new elements* are added: | ||
Create a new *Observable* - in this example, an array - and register a handler to fire when any *new elements* are added. We'll keep it simple for now by using the `alwaysCommit` option, which means any state changes associated with events of the given type will always be committed. | ||
@@ -121,3 +124,3 @@ ```js | ||
users.addEventListener('add', logAdditions); | ||
users.addEventListener('add', logAdditions, { alwaysCommit: true }); | ||
// every time an item is added to the array, fire `logAdditions` | ||
@@ -143,6 +146,6 @@ | ||
```js | ||
users.addEventListener(eventType, eventHandler); | ||
users.addEventListener(eventType, eventHandler, { alwaysCommit: true }); | ||
``` | ||
`addEventHandler` can listen to any of three events. | ||
`addEventListener` can listen to the following events... | ||
@@ -153,3 +156,3 @@ ### <a name="evtypes"></a> Event Types | ||
A new element or property has been added to the array or object. Callbacks will receive an object consisting of: | ||
A new element or property has been added to the array or object. Callbacks will receive a function, `done`, and an object consisting of: | ||
| Property | Value | | ||
@@ -169,3 +172,3 @@ | --- | --- | | ||
An existing element or property has changed. Callbacks will receive an object consisting of: | ||
An existing element or property has changed. Callbacks will receive a function, `done`, and an object consisting of: | ||
| Property | Value | | ||
@@ -183,13 +186,28 @@ | --- | --- | | ||
An element or property has been deleted. Callbacks will receive an object consisting of: | ||
An element or property has been deleted. Callbacks will receive a function, `done`, and an object consisting of: | ||
| Property | Value | | ||
| --- | --- | | ||
| **type** | String "itemremoved", denoting the event-type that was triggered | | ||
| **type** | String "del", denoting the event-type that was triggered | | ||
| **prevState** | the previous state | | ||
| **nextState** | the next state, after the event we are listening to | | ||
**Fires on:** methods such as *`pop`, `shift`, `splice`*; `delete` called on a property | ||
**Fires on:** methods such as `pop`; `delete` called on a property | ||
**Type (TypeScript only)** `VX_LISTENER_INTERNALS.DEL` | ||
#### batched | ||
A batched event has occurred. Batched events are those which carry several state changes as the result of a single action. For example, `Array.prototype.unshift` may prepend an element and shifts each element. | ||
Callbacks will receive a function, `done`, and an object consisting of: | ||
| Property | Value | | ||
| --- | --- | | ||
| **type** | String "batched", denoting the event-type that was triggered | | ||
| **prevState** | the previous state | | ||
| **nextState** | the next state, after the event we are listening to | | ||
**Fires on:** methods such as `shift`, `unshift`, `push` when called with multiple elements | ||
**Type (TypeScript only)** `VX_LISTENER_INTERNALS.BATCHED` | ||
### <a name="methods"></a> Methods | ||
@@ -199,3 +217,3 @@ | ||
#### addEventListener (eventName: VX_EVENT_TYPE, handler: VxEventHandler) => VxEventedObject | ||
#### addEventListener (eventName: VX_EVENT_TYPE, handler: VxEventHandler, opts: { alwaysCommit?: boolean }) => VxEventedObject | ||
@@ -220,3 +238,3 @@ Bind a callback to fire whenever a given event-type has been triggered. | ||
#### removeEventListener (eventName: VX_EVENT_TYPE, handler: VxEventHandler) => VxEventedObject | ||
#### removeEventListener (eventName: VX_EVENT_TYPE, handler: VxEventHandler, opts: { alwaysCommit?: boolean }) => VxEventedObject | ||
@@ -235,3 +253,3 @@ Remove an existing callback from the respective event-type to which it has been registered. | ||
const users = vivisect(['Alice','Bob']) | ||
.addEventListener('add', logMsg) | ||
.addEventListener('add', logMsg, { alwaysCommit: true }) | ||
.removeEventListener('add', logMsg); | ||
@@ -247,4 +265,4 @@ | ||
- cancellable state mutations | ||
- ~~cancellable state mutations~~ | ||
- deferred state mutations | ||
- custom event types (bind specific methods to event handlers) |
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
77785
28
1536
253
0