Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

boxed-immutable

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

boxed-immutable - npm Package Compare versions

Comparing version 0.1.6 to 0.2.0

test/config.json

446

lib/boxed-immutable.js

@@ -12,2 +12,5 @@ /** internal

const isNumericInteger = util.isNumericInteger;
const isArrayIndex = util.isArrayIndex;
const toArrayIndex = util.toArrayIndex;
const toNumberIfArrayIndex = util.toNumberIfArrayIndex;
const unwrap = util.unwrap;

@@ -32,54 +35,54 @@ const wrap = util.wrap;

const RESERVED_PROPS = {
// "@@BOXED_THIS": {
// get: function () {return this;},
// set: function (value) {return false;},
// delete: function () {return false;},
// ownPropertyDescriptor: function () {return { value: this, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
// },
forEach: {
get: function () {return this.boxedForEach;},
set: function (value) {return false;},
delete: function () {return false;},
ownPropertyDescriptor: function () {return { value: this.boxedForEach, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
forEachKey: {
get: function (wrapped) {return wrapped ? this.forEachKey : this.forEachKeyBoxed;},
set: function (value, wrapped) {return false;},
delete: function (wrapped) {return false;},
ownPropertyDescriptor: function () {return { value: this.value, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
unboxed: {
get: function () {return this.valueOf();},
set: function (value) {return this.setValueOf(value);},
delete: function () {return this.deleteValueOf();},
get: function (wrapped) {return this.valueOf();},
set: function (value, wrapped) {return this.setValueOf(value);},
delete: function (wrapped) {return this.deleteValueOf();},
ownPropertyDescriptor: function () {return { value: this.value, writable: true, /*enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
get: {
get: function () {return this.getProp;},
set: function (value) {return false;},
delete: function () {return false;},
get: function (wrapped) {return this.getProp;},
set: function (value, wrapped) {return false;},
delete: function (wrapped) {return false;},
ownPropertyDescriptor: function () {return { value: this.getProp, /* writable: true, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
default: {
get: function () {return this.setDefaultValue;},
set: function (value) {return this.setDefaultValue(value);},
delete: function () {return false;},
get: function (wrapped) {return wrapped ? this.setValueOfDefaultBoxed : this.setValueOfDefault;},
set: function (value, wrapped) {return this.setValueOfDefaultMagic(value);},
delete: function (wrapped) {return false;},
ownPropertyDescriptor: function () {return { value: this.value, writable: true, /*enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
boolean: {
get: function (wrapped) {return this.valueOfBoolean();},
set: function (value, wrapped) {return this.setValueOfBoolean(value);},
delete: function (wrapped) {return false;},
ownPropertyDescriptor: function () {return { value: this.valueOfBoolean(), writable: true, /*enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
"": { // $boxed with after stripping off prefix/suffix _$
get: function () {return this.proxiedThis;},
set: function (value) {return false;}, // this does not get here, intercepted in set, it is boxedValue[$] = ..., or boxedValue.$ = ..., ie. we treat it as append to end of array!
delete: function () {return false;}, // this is delete boxedValue[$]; or delete boxedValue.$; in either case we don't have a meaning for this
ownPropertyDescriptor: function () {return { value: this.boxedWith, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
get: function (wrapped) {return this.proxiedThis;},
set: function (value, wrapped) {return false;}, // this does not get here, intercepted in set, it is boxedValue[$] = ..., or boxedValue.$ = ..., ie. we treat it as append to end of array!
delete: function (wrapped) {return false;}, // this is delete boxedValue[$]; or delete boxedValue.$; in either case we don't have a meaning for this
ownPropertyDescriptor: function () {return { value: this.proxiedThis, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
modified: {
get: function () {return this.valueOfModified();},
set: function (value) {return this.setValueOfModified(value);},
delete: function () {return this.deleteValueOfModified();},
get: function (wrapped) {return this.valueOfModified();},
set: function (value, wrapped) {return this.setValueOfModified(value);},
delete: function (wrapped) {return this.deleteValueOfModified();},
ownPropertyDescriptor: function () {return { value: this.value, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
delta: {
get: function () {return this.unboxedDelta();},
set: function (value) {return this.setUnboxedDelta(value);},
delete: function () {return this.deleteUnboxedDelta();},
get: function (wrapped) {return this.unboxedDelta();},
set: function (value, wrapped) {return this.setUnboxedDelta(value);},
delete: function (wrapped) {return this.deleteUnboxedDelta();},
ownPropertyDescriptor: function () {return { value: this.value, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},
},
deepDelta: {
get: function () {return this.unboxedDeepDelta();},
set: function (value) {return this.setUnboxedDeepDelta(value);},
delete: function () {return this.deleteUnboxedDeepDelta();},
get: function (wrapped) {return this.unboxedDeepDelta();},
set: function (value, wrapped) {return this.setUnboxedDeepDelta(value);},
delete: function (wrapped) {return this.deleteUnboxedDeepDelta();},
ownPropertyDescriptor: function () {return { value: this.value, /*writable: false, enumerable: false, configurable: false, get:UNDEFINED, set:UNDEFINED,*/ };},

@@ -108,3 +111,3 @@ },

} else {
this.globalBox = undefined; // will be set by box
this.globalBox = UNDEFINED; // will be set by box
}

@@ -117,15 +120,15 @@

this.arrayDeltaPartials = firstDefined(options.arrayDeltaPartials, false);
this.arrayDeltaObjectMarker = firstDefined(options.arrayDeltaObjectMarker, undefined);
this.arrayDeltaObjectMarkerValue = firstDefined(options.arrayDeltaObjectMarkerValue, undefined);
this.arrayDeltaObjectMarker = firstDefined(options.arrayDeltaObjectMarker, UNDEFINED);
this.arrayDeltaObjectMarkerValue = firstDefined(options.arrayDeltaObjectMarkerValue, UNDEFINED);
this.arrayDeepDeltaObjects = firstDefined(options.arrayDeepDeltaObjects, false);
this.arrayDeepDeltaObjectMarker = firstDefined(options.arrayDeepDeltaObjectMarker, undefined);
this.arrayDeepDeltaObjectMarkerValue = firstDefined(options.arrayDeepDeltaObjectMarkerValue, undefined);
this.arrayDeepDeltaPartials = firstDefined(options.arrayDeepDeltaPartials, false);
this.arrayDeepDeltaObjectMarker = firstDefined(options.arrayDeepDeltaObjectMarker, UNDEFINED);
this.arrayDeepDeltaObjectMarkerValue = firstDefined(options.arrayDeepDeltaObjectMarkerValue, UNDEFINED);
this.arrayDeepDeltaPartials = firstDefined(options.arrayDeepDeltaPartials, true);
this.prefixChars = (options.prefixChars || BOXED_PREFIX);
this.suffixChars = (options.suffixChars || BOXED_SUFFIX);
this.extraPrefixChars = (options.extraPrefixChars || "");
this.extraSuffixChars = (options.extraSuffixChars || "$");
this.magicPrefixChars = (options.magicPrefixChars || "");
this.magicSuffixChars = (options.magicSuffixChars || "$");
this.boxedArrayEnd = this.wrapProp(BOXED_ARRAY_END);
this.magicParamsKey = this.wrapMagicProp("|");
this.boxedParamsKey = this.wrapProp("|" + this.magicParamsKey + "|");
// this.boxedParamsKey = this.wrapProp("|" + this.magicParamsKey + "|");

@@ -144,12 +147,13 @@ if (!util.hasOwnProperty(wrappedProps, this.magicParamsKey)) {

this.reservedProps = wrappedProps[this.magicParamsKey];
this.reservedPropsList = Object.keys(this.reservedProps);
if (!util.hasOwnProperty(wrappedPropsLists, this.boxedParamsKey)) {
let i = RESERVED_PROPS_LIST.length;
let reservedPropsList = [];
while (i--) {
reservedPropsList[i] = this.wrapProp(this.wrapMagicProp(RESERVED_PROPS_LIST[i]));
}
wrappedPropsLists[this.boxedParamsKey] = reservedPropsList;
}
this.reservedPropsList = wrappedPropsLists[this.boxedParamsKey];
// if (!util.hasOwnProperty(wrappedPropsLists, this.boxedParamsKey)) {
// let i = RESERVED_PROPS_LIST.length;
// let reservedPropsList = [];
// while (i--) {
// reservedPropsList[i] = this.wrapProp(this.wrapMagicProp(RESERVED_PROPS_LIST[i]));
// }
// wrappedPropsLists[this.boxedParamsKey] = reservedPropsList;
// }
// this.reservedPropsList = wrappedPropsLists[this.boxedParamsKey];
}

@@ -166,3 +170,3 @@

BoxedContext.prototype.wrapMagicProp = function (prop) {
return wrap(prop, this.extraPrefixChars, this.extraSuffixChars);
return wrap(prop, this.magicPrefixChars, this.magicSuffixChars);
};

@@ -318,6 +322,11 @@

Boxed.prototype.get = function (wrappedProp) {
// accept both unwrapped magic props and wrapped ones
if (this.reservedProps.hasOwnProperty(wrappedProp)) {
return this.reservedProps[wrappedProp].get.call(this, false);
}
let prop = this.context.unwrappedProp(wrappedProp);
if (prop !== wrappedProp) {
if (this.reservedProps.hasOwnProperty(prop)) {
return this.reservedProps[prop].get.call(this);
return this.reservedProps[prop].get.call(this, true);
}

@@ -341,6 +350,10 @@ let boxed = this.getBoxedProp(prop);

Boxed.prototype.set = function (wrappedProp, value, boxed) {
if (wrappedProp !== BOXED_ARRAY_END && this.reservedProps.hasOwnProperty(wrappedProp)) {
return this.reservedProps[wrappedProp].set.call(this, value, false);
}
let prop = this.context.unwrappedProp(wrappedProp);
if (prop !== wrappedProp) {
if (prop !== BOXED_ARRAY_END && this.reservedProps.hasOwnProperty(prop)) {
return this.reservedProps[prop].set.call(this, value);
if (prop !== wrappedProp && prop !== BOXED_ARRAY_END) {
if (this.reservedProps.hasOwnProperty(prop)) {
return this.reservedProps[prop].set.call(this, value, true);
}

@@ -357,7 +370,5 @@ }

const isNumeric = isNumericInteger(prop);
if (isNumeric) {
prop = Number.parseInt(prop);
}
let n = toArrayIndex(prop);
const isNumeric = n !== UNDEFINED;
prop = n || prop;
let updateParent = !this.isCopy;

@@ -393,5 +404,5 @@

while (i--) {
let key = keys[i];
if (isNumericInteger(key)) {
let index = Number.parseInt(key) + 1;
let key = toArrayIndex(keys[i]);
if (key !== UNDEFINED) {
let index = key + 1;
if (maxKey < index) maxKey = index;

@@ -431,6 +442,10 @@ }

Boxed.prototype.delete = function (wrappedProp) {
if (this.reservedProps.hasOwnProperty(wrappedProp)) {
return this.reservedProps[wrappedProp].delete.call(this, false);
}
let prop = this.context.unwrappedProp(wrappedProp);
if (prop !== wrappedProp) {
if (this.reservedProps.hasOwnProperty(prop)) {
return this.reservedProps[prop].delete.call(this);
return this.reservedProps[prop].delete.call(this, true);
}

@@ -524,6 +539,27 @@ }

// only set value if undefined
Boxed.prototype.setDefaultValue = function (value) {
Boxed.prototype.setValueOfDefaultMagic = function (value) {
return this.value !== UNDEFINED || value === UNDEFINED || this.setValueOf(value);
};
// only set value if undefined
Boxed.prototype.setValueOfDefault = function (value) {
if (this.value === UNDEFINED && value !== UNDEFINED) this.setValueOf(value);
return this.value;
};
// only set value if undefined
Boxed.prototype.setValueOfDefaultBoxed = function (value) {
if (this.value === UNDEFINED && value !== UNDEFINED) this.setValueOf(value);
return this.proxiedThis;
};
Boxed.prototype.valueOfBoolean = function () {
return !!this.value;
};
// convert to boolean before setting
Boxed.prototype.setValueOfBoolean = function (value) {
return this.setValueOf(!!value);
};
// if value is object or array: return only first level children that were modified or UNDEFINED if no mods

@@ -547,8 +583,9 @@ // if otherwise same as valueOfModified return value if modified or UNDEFINED if not modified

if (isArrayDelta) {
let maxIndex = undefined;
let maxIndex = UNDEFINED;
while (i--) {
let prop = keys[i];
// these are modified, we get our prop value for them
if (isNumericInteger(prop) && isNullOrUndefined(maxIndex) || prop > maxIndex) {
maxIndex = Number.parseInt(prop);
let n = toArrayIndex(prop);
if (n !== UNDEFINED && maxIndex === UNDEFINED || n > maxIndex) {
maxIndex = n;
} else {

@@ -561,3 +598,3 @@ let value = this.value[prop];

if (maxIndex !== undefined) {
if (maxIndex !== UNDEFINED) {
// copy from 0 to maxIndex

@@ -616,18 +653,20 @@ i = maxIndex + 1;

// arrayDeltaPartials: when true array deltas will contain undefined for all unmodified fields or deleted fields
let isArrayDelta = !this.context.arrayDeepDeltaObjects && !this.context.arrayDeepDeltaPartials && isArray(this.value);
let delta = !isArrayDelta ? {} : this.value.constructor(); // always something or we would not be here
let isArrayDelta = !this.context.arrayDeepDeltaObjects && isArray(this.value);
let isPureArrayDelta = isArrayDelta && !this.context.arrayDeepDeltaPartials;
let delta = !isArrayDelta ? {} : this.value.constructor();
let keys = Object.keys(this.modifiedProps);
let i = keys.length;
if (isArrayDelta) {
let maxIndex = undefined;
if (isPureArrayDelta) {
let maxIndex = UNDEFINED;
while (i--) {
let prop = keys[i];
// these are modified, we get our prop value for them
if (isNumericInteger(prop) && isNullOrUndefined(maxIndex) || prop > maxIndex) {
maxIndex = Number.parseInt(prop);
let n = toArrayIndex(prop);
if (n !== UNDEFINED && maxIndex === UNDEFINED || n > maxIndex) {
maxIndex = n;
} else {
let value = this.value[prop];
// TEST: that undefined values are not added to delta && deepDelta unless this.ignoreUndefinedProperties is false
// TEST: undefined values are not added to delta && deepDelta unless this.ignoreUndefinedProperties is false
if (value !== UNDEFINED || !this.ignoreUndefinedProperties) delta[prop] = value;

@@ -637,3 +676,3 @@ }

if (maxIndex !== undefined) {
if (maxIndex !== UNDEFINED) {
// copy from 0 to maxIndex

@@ -697,67 +736,149 @@ i = maxIndex + 1;

/**
* For all real values of unboxed item. Makes it easier
* to loop over all real values.
* For all real values of unboxed item. Makes it easier to loop over all real values.
*
* @param param if function then gets (boxedValue, prop, unboxedValue)
* if object or array then will crate a copy of these in a new boxed parent and
* return it as result.
* For arrays will only include numeric keys (ie. indices)
*
* @returns {Boxed}
* @param wrapped true if wrapped version of each key function
* @param args {Array} arguments, last one is the callback function
*
* arg: strings & numbers are keys of interest, will be pre-filtered, saves on creating a proxy for undesired properties
* arg: object, then assumed to hold options:
* props: default false, true to have arrays include non-numeric keys
* undefs: default false, true to have keys and array indices with undefined values included
*
* NOT IMPLEMENTED:
* arg: array, contains args other than callback
*
* @returns {Proxy} of self for chaining
*
* TODO: refactor and add options for: exclude: keys, so that both would filter keys
*
*/
Boxed.prototype.boxedForEach = function (param) {
// TODO: generalize parameter processing. array or object, function, many individual strings or numbers
// const callBack = argumentList[0];
function forEachKeyImpl(wrapped, args) {
let value = this.value;
if ((isArray(args) || typeof args === typeof arguments) && isObject(value)) {
let argCount = args.length - 1;
const callBack = args[argCount];
if (isFunction(param)) {
if (isObject(value)) {
let keys = Object.keys(value);
let i = keys.length;
while (i--) {
let prop = keys[i];
const item = value[prop];
if (item !== UNDEFINED) {
const boxedItem = this.get(prop);
param.call(UNDEFINED, boxedItem, prop, item);
if (isFunction(callBack)) {
// can work with it
let i;
let filteredKeys;
let options = {};
const isArrayValue = isArray(value);
let wantIntegers = isArrayValue && !options.props;
let n;
if (argCount) {
i = argCount;
filteredKeys = [];
while (i--) {
let arg = args[i];
if (isObject(arg)) {
options.props = arg.props;
options.undefs = arg.undefs;
} else if (wantIntegers && (n = toArrayIndex(arg))) {
filteredKeys.push(n);
} else {
filteredKeys.push(arg);
}
}
}
} else if (isArray(value)) {
let i = value.length;
while (i--) {
const item = value[i];
if (item !== UNDEFINED) {
const boxedItem = this.get(i);
param.call(UNDEFINED, boxedItem, i, item);
if (filteredKeys) {
// both arrays and objects, filtered keys has only integers if so requested
let keys = filteredKeys;
let i = keys.length;
while (i--) {
let prop = keys[i];
if (value.hasOwnProperty(prop)) {
if (isArrayValue) prop = toNumberIfArrayIndex(prop);
let item = value[prop];
if (item !== UNDEFINED || options.undefs) {
if (wrapped) {
callBack(prop, this.getProp(prop), item);
} else {
callBack(prop, item);
}
}
}
}
}
}
} else if (isObject(param)) {
const result = {};
} else {
if (isArrayValue && !options.props) {
let i = value.length;
while (i--) {
const item = value[i];
if (item !== UNDEFINED || options.undefs) {
if (wrapped) {
callBack(i, this.getProp(i), item);
} else {
callBack(i, item);
}
}
}
} else {
let keys = Object.keys(value);
let i = keys.length;
while (i--) {
let prop = keys[i];
if (isArrayValue) prop = toNumberIfArrayIndex(prop);
const item = value[prop];
if (item !== UNDEFINED || options.undefs) {
if (wrapped) {
callBack(prop, this.getProp(prop), item);
} else {
callBack(prop, item);
}
}
}
}
let keys = Object.keys(param);
let i = keys.length;
while (i--) {
let prop = keys[i];
if (value.hasOwnProperty(param)) {
const item = value[prop];
if (item !== UNDEFINED) {
result[prop] = item;
}
}
}
let boxed = new Boxed(result, UNDEFINED, UNDEFINED, this.context);
boxedProxy(boxed);
return boxed.proxiedThis;
} else if (!isNullOrUndefined(param)) {
const result = {};
if (this.value.hasOwnProperty(param)) {
result[param] = this.value[param];
}
let boxed = new Boxed(result, UNDEFINED, UNDEFINED, this.context);
boxedProxy(boxed);
return boxed.proxiedThis;
}
return this.proxiedThis;
}
/**
* For all real values of unboxed item. Makes it easier
* to loop over all real values.
*
* @param args see forEachKeyImpl
*
* @returns {Boxed}
*/
Boxed.prototype.forEachKey = function () {
forEachKeyImpl.call(this, false, arguments);
};
/**
* For all real values of unboxed item. Makes it easier
* to loop over all real values.
*
* @param args see forEachKeyImpl
*
* @returns {Boxed}
*/
Boxed.prototype.forEachKeyBoxed = function () {
forEachKeyImpl.call(this, true, arguments);
};
function isBoxed(target) {
return isObject(target) && target.constructor === Boxed;
}
module.exports.isBoxed = isBoxed;
function boxedThis(target) {
return isBoxedProxy(target) ? target[BOXED_GET_THIS] : UNDEFINED;
}
module.exports.boxedThis = boxedThis;
function isBoxedProxy(target) {
return target && isBoxed(target[BOXED_GET_THIS]);
}
module.exports.isBoxedProxy = isBoxedProxy;
const BoxedHandler = {

@@ -788,3 +909,3 @@ // target is the box function with

if (prop === BOXED_GET_THIS) {
return false;
return true;
}

@@ -836,30 +957,35 @@ return target[BOXED_GET_THIS].has(prop, value);

boxed.boxedWith = boxed.boxedWith.bind(boxed);
boxed.detachFromParent = boxed.detachFromParent.bind(boxed);
boxed.detachProp = boxed.detachProp.bind(boxed);
boxed.detachAllProps = boxed.detachAllProps.bind(boxed);
boxed.has = boxed.has.bind(boxed);
boxed.ownKeys = boxed.ownKeys.bind(boxed);
boxed.getOwnPropertyDescriptor = boxed.getOwnPropertyDescriptor.bind(boxed);
boxed.getBoxedProp = boxed.getBoxedProp.bind(boxed);
boxed.get = boxed.get.bind(boxed);
// boxed.detachFromParent = boxed.detachFromParent.bind(boxed);
// boxed.detachProp = boxed.detachProp.bind(boxed);
// boxed.detachAllProps = boxed.detachAllProps.bind(boxed);
// boxed.has = boxed.has.bind(boxed);
// boxed.ownKeys = boxed.ownKeys.bind(boxed);
// boxed.getOwnPropertyDescriptor = boxed.getOwnPropertyDescriptor.bind(boxed);
// boxed.getBoxedProp = boxed.getBoxedProp.bind(boxed);
// boxed.get = boxed.get.bind(boxed);
boxed.getProp = boxed.getProp.bind(boxed);
boxed.copyOfValue = boxed.copyOfValue.bind(boxed);
boxed.set = boxed.set.bind(boxed);
boxed.delete = boxed.delete.bind(boxed);
boxed.valueOf = boxed.valueOf.bind(boxed);
boxed.setValueOf = boxed.setValueOf.bind(boxed);
boxed.deleteValueOf = boxed.deleteValueOf.bind(boxed);
boxed.isModified = boxed.isModified.bind(boxed);
boxed.valueOfModified = boxed.valueOfModified.bind(boxed);
boxed.setValueOfModified = boxed.setValueOfModified.bind(boxed);
boxed.deleteValueOfModified = boxed.deleteValueOfModified.bind(boxed);
boxed.setDefaultValue = boxed.setDefaultValue.bind(boxed);
boxed.unboxedDelta = boxed.unboxedDelta.bind(boxed);
boxed.setUnboxedDelta = boxed.setUnboxedDelta.bind(boxed);
boxed.deleteUnboxedDelta = boxed.deleteUnboxedDelta.bind(boxed);
boxed.unboxedDeepDelta = boxed.unboxedDeepDelta.bind(boxed);
boxed.setUnboxedDeepDelta = boxed.setUnboxedDeepDelta.bind(boxed);
boxed.deleteUnboxedDeepDelta = boxed.deleteUnboxedDeepDelta.bind(boxed);
boxed.boxedForEach = boxed.boxedForEach.bind(boxed);
// boxed.copyOfValue = boxed.copyOfValue.bind(boxed);
// boxed.set = boxed.set.bind(boxed);
// boxed.delete = boxed.delete.bind(boxed);
// boxed.valueOf = boxed.valueOf.bind(boxed);
// boxed.setValueOf = boxed.setValueOf.bind(boxed);
// boxed.deleteValueOf = boxed.deleteValueOf.bind(boxed);
// boxed.isModified = boxed.isModified.bind(boxed);
// boxed.valueOfModified = boxed.valueOfModified.bind(boxed);
// boxed.setValueOfModified = boxed.setValueOfModified.bind(boxed);
// boxed.deleteValueOfModified = boxed.deleteValueOfModified.bind(boxed);
boxed.setValueOfDefault = boxed.setValueOfDefault.bind(boxed);
boxed.setValueOfDefaultBoxed = boxed.setValueOfDefaultBoxed.bind(boxed);
// boxed.setValueOfDefaultMagic = boxed.setValueOfDefaultMagic.bind(boxed);
// boxed.valueOfBoolean = boxed.valueOfBoolean.bind(boxed);
// boxed.setValueOfBoolean = boxed.setValueOfBoolean.bind(boxed);
// boxed.unboxedDelta = boxed.unboxedDelta.bind(boxed);
// boxed.setUnboxedDelta = boxed.setUnboxedDelta.bind(boxed);
// boxed.deleteUnboxedDelta = boxed.deleteUnboxedDelta.bind(boxed);
// boxed.unboxedDeepDelta = boxed.unboxedDeepDelta.bind(boxed);
// boxed.setUnboxedDeepDelta = boxed.setUnboxedDeepDelta.bind(boxed);
// boxed.deleteUnboxedDeepDelta = boxed.deleteUnboxedDeepDelta.bind(boxed);
boxed.forEachKey = boxed.forEachKey.bind(boxed);
boxed.forEachKeyBoxed = boxed.forEachKeyBoxed.bind(boxed);
boxed.boxedWith[BOXED_GET_THIS] = boxed;

@@ -906,3 +1032,5 @@ boxed.proxiedThis = new Proxy(boxed.boxedWith, BoxedHandler);

if (!globalBoxKey) {
globalBoxKey = box + "";
let obj = {};
obj[box] = 0;
globalBoxKey = Object.keys(obj)[0];
}

@@ -909,0 +1037,0 @@ context.globalBox = globalBoxKey;

@@ -21,3 +21,3 @@ "use strict";

this.boxed = UNDEFINED;
if (this.saveState) {
if (this.saveState) {
return this.saveState(modified, boxed);

@@ -24,0 +24,0 @@ } else {

@@ -6,2 +6,10 @@ "use strict";

const UNDEFINED = void 0;
module.exports.UNDEFINED = UNDEFINED;
module.exports.isNumber = function (arg) {
return typeof arg === 'number';
};
module.exports.extend = function (other, add) {

@@ -29,3 +37,3 @@ // Don't do anything if add isn't an object

// noinspection JSUnfilteredForInLoop
if (hasOwnProperty(arg, key) && arg[key] !== undefined) {
if (hasOwnProperty(arg, key) && arg[key] !== UNDEFINED) {
return true;

@@ -67,2 +75,20 @@ }

function toArrayIndex(arg) {
const n = Number.parseFloat(arg);
return Number.isInteger(n) && +n === n && n >= 0 ? n : UNDEFINED;
}
module.exports.toArrayIndex = toArrayIndex;
module.exports.isArrayIndex = function isArrayIndex(arg) {
return toArrayIndex !== UNDEFINED;
};
function toNumberIfArrayIndex(arg) {
const n = Number.parseFloat(arg);
return Number.isInteger(n) && +n === n && n >= 0 ? n : arg;
}
module.exports.toNumberIfArrayIndex = toNumberIfArrayIndex;
function returnsAlwaysFalse() {

@@ -175,3 +201,3 @@ return false;

// need to copy the non numeric fields, object assign does not copy them
copyFilteredProperties(newValue, src, key => !isNumericInteger(key));
copyFilteredProperties(newValue, src, key => !isNumericInteger(key) || key < 0);
}

@@ -197,5 +223,10 @@ return newValue;

while (--i) {
let index = arr.indexOf(arguments[i]);
if (index >= 0) {
arr.splice(index, 1);
const arg = arguments[i];
if (isArray(arg)) {
deleteItems(arr, ...arg)
} else if (isObject(arg)) {
deleteItems(arr, ...Object.keys(arg))
} else {
let index = arr.indexOf(arg);
if (index >= 0) arr.splice(index, 1);
}

@@ -209,1 +240,2 @@ }

module.exports.deleteItems = deleteItems;
{
"name": "boxed-immutable",
"version": "0.1.6",
"version": "0.2.0",
"private": false,

@@ -18,3 +18,5 @@ "description": "Immutable proxy wrapper with auto-vivification",

},
"devDependencies": {},
"devDependencies": {
"jest-each": "^0.3.1"
},
"scripts": {},

@@ -21,0 +23,0 @@ "keywords": [

@@ -9,14 +9,2 @@ # boxed-immutable

## Install
Use [npm](https://npmjs.com/) to install.
```sh
npm install boxed-immutable --save
```
## Usage
[![NPM](https://nodei.co/npm/boxed-immutable.png)](https://www.npmjs.com/package/boxed-immutable)
Create a boxed-immutable object proxy then access and/or modify its nested properties, ignoring

@@ -36,3 +24,3 @@ whether intermediate values are objects/arrays or whether they exist.

Trying to access a field or array index when the property is not valid will throw
`TypeError`.
`TypeError` or `ReferenceError`.
2. Use original property name with `_$` appended to the end of it to get access to a proxy which

@@ -45,2 +33,18 @@ will auto-vivify the property container when the first property is set.

Easily customize the suffix/prefix combination that will not conflict with property names in
your project's state objects.
## Install
Use [npm](https://npmjs.com/) to install.
```sh
npm install boxed-immutable --save
```
## Usage
[![NPM](https://nodei.co/npm/boxed-immutable.png)](https://www.npmjs.com/package/boxed-immutable)
```javascript

@@ -60,6 +64,6 @@ const _$ = require('boxed-immutable')._$;

// get all the first level children to update state
this.setState(state.delta$_$);
this.setState(state.delta$);
// get only the changed leaf fields and their parents, minimal update image
this.setState(state.deepDelta$_$);
this.setState(state.deepDelta$);
}

@@ -100,4 +104,5 @@ ```

// all are equivalent
empty[""] = 5;
empty["_$"] = 5;
empty[_$] = 5; // may not work, depends on how functions is converted to string
empty[_$] = 5; // depending on how function is converted to string this may accept any function, not just box.
empty._$ = 5; // simplest and safest alternative

@@ -110,3 +115,3 @@ // result: [5]

empty._$ = 20;
let result = empty.unboxed$_$;
let result = empty.unboxed$;
// result: [5, 10, 20]

@@ -129,8 +134,9 @@ ```

empty._$ = 20;
let result = empty.unboxed$_$;
let result = empty.unboxed$;
// result: {0: 10, 1: 20, field: 5}
```
For symmetry, you can also use `_$` on objects. In case of objects `_$` is equal to the greatest
integer key in the object or 0 if no integer keys. Here is why:
For symmetry, you can also use the end of array index `_$` on objects. In case of objects `_$`
is equal to the greatest integer key in the object or 0 if no integer keys. The goal is to make
boxed values allow setting properties like their unboxed JavaScript counterparts.

@@ -145,3 +151,3 @@ ```javascript

obj[2] = 25;
let result = obj.unboxed$_$;
let result = obj.unboxed$;
// result: {0: 5, 1:15, 2:25, prop: "a"}

@@ -151,2 +157,3 @@

let obj = _$({});
obj.prop = "a";

@@ -161,3 +168,3 @@ obj._$ = 5;

You can change a couple of options on how the boxing handles properties whose value is
`undefined` and also modify the prefix/suffix used for accessing boxed properties:
`undefined` and modify the prefix/suffix used for accessing boxed properties or magic properties:

@@ -169,5 +176,5 @@ Use the `createBox(options)` function from the module to create a boxing function with

const createBox = require('boxed-immutable').createBox;
const $__$ = createBox({prefixChars: "$_", suffixChars:"_$"});
const $__$ = createBox({prefixChars: "$_", suffixChars:"_$", magicPrefixChars: "", magicSuffixChars: "$$"});
// Now all your properties are wrapped
// Now all your properties are wrapped and magic properties end with two $
let obj = $__$();

@@ -180,22 +187,22 @@

let result = obj.$_unboxed$_$;
let result = obj.unboxed$$;
// result: { field: { subField: 4 }, prop: [ "a", "b", "c"] };
```
| Option | Default | Description |
|:-----------------------------------|:------------|:-----------------------------------------------------------------------------------------|
| `deleteEmptyCollections:` | `true` | if deleting a property results in an empty collection, delete that too |
| `ignoreUndefinedProperties:` | `true` | when copying delta and deepDelta ignore properties with `undefined` value |
| `arrayDeltaObjects:` | `false` | return array delta as objects with index as key |
| `arrayDeltaObjectMarker:` | `undefined` | field name to set when returning array delta as objects, `undefined` means don't set |
| `arrayDeltaObjectMarkerValue:` | `undefined` | value for above |
| `arrayDeltaPartials:` | `false` | return array delta as partials, any unset indices will be `undefined` |
| `arrayDeepDeltaObjects:` | `false` | return array deepDelta as objects with index as key |
| `arrayDeepDeltaObjectMarker:` | `undefined` | field name to set when returning array deepDelta as objects, `undefined` means don't set |
| `arrayDeepDeltaObjectMarkerValue:` | `undefined` | value for above |
| `arrayDeepDeltaPartials:` | `false` | return array deepDelta as partials, any unset indices will be `undefined` |
| `prefixChars:`<sup>\[1]</sup> | `""` | prefix for boxed properties. |
| `suffixChars:`<sup>\[1]</sup> | `"_$"` | suffix for boxed properties. |
| `magicPrefixChars:`<sup>\[1]</sup> | `""` | prefix for magic properties, applied after prefixChars |
| `magicSuffixChars:`<sup>\[1]</sup> | `"$"` | suffix for magic properties, applied before suffixChars |
| Option | Default | Description |
|:-----------------------------------|:------------|:---------------------------------------------------------------------------------------------------------------------------------------------|
| `deleteEmptyCollections`: | `true` | if deleting a property results in an empty collection, delete that too |
| `ignoreUndefinedProperties`: | `true` | when copying delta and deepDelta ignore properties with `undefined` value |
| `arrayDeltaObjects`: | `false` | return array delta as objects with index as key |
| `arrayDeltaObjectMarker`: | `undefined` | field name to set when returning array delta as objects, `undefined` means don't set |
| `arrayDeltaObjectMarkerValue`: | `undefined` | value for above |
| `arrayDeltaPartials`: | `false` | return array delta as partials, any unset indices will be `undefined` |
| `arrayDeepDeltaObjects`: | `false` | return array deepDelta as objects with index as key |
| `arrayDeepDeltaObjectMarker`: | `undefined` | field name to set when returning array deepDelta as objects, `undefined` means don't set |
| `arrayDeepDeltaObjectMarkerValue`: | `undefined` | value for above |
| `arrayDeepDeltaPartials`: | `true` | return array deepDelta as partials, any unset indices will be `undefined`, false will return array delta from index 0 to last modified index |
| `prefixChars`: <sup>\[1]</sup> | `""` | prefix for boxed properties. |
| `suffixChars`: <sup>\[1]</sup> | `"_$"` | suffix for boxed properties. |
| `magicPrefixChars`: <sup>\[1]</sup> | `""` | prefix for magic properties, applied after prefixChars |
| `magicSuffixChars`: <sup>\[1]</sup> | `"$"` | suffix for magic properties, applied before suffixChars |

@@ -212,18 +219,27 @@ \* \[1]: Note use of `[_$]` is not affected by prefixes or suffixes since the function is passed

With default settings all magic properties except `_$` get an extra `$` added because of the
default `magicSuffixChars` being `"$"`
Magic properties except `_$` are wrapped in `magicPrefixChars` and `magicSuffixChars`,
properties and empty string `""` which represents array end are wrapped in `prefixChars` and
`suffixChars`
Magic Properties of boxed properties:
Magic properties are accessible with or without the property wrapper (`prefixChars` and
`suffixChars`) for some it affect whether they provide/return regular values or boxed proxies
for these values.
| Property | Get | Set | Delete | Call |
|:---------------|:------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------|:--------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
| `_$` | proxy of the boxed object, ie. boxed === boxed.\_$, so you can do boxed.\_$() or boxed() | append end of array | error | does a call on first argument, use: `boxed._$(_$ => { });` returns `boxed` |
| `get$_$` | function | error | error | `.get$_$("prop")` is same as `["prop" + "_$"]`, convenience function when you have a property name in a variabel and need a boxed version of it |
| `forEach$_$` | function | error | error | functions executes callback for each own property, passes `.forEach$_$((boxedValue, prop, unboxedValue) =>{});` |
| `unboxed$_$` | unboxed value | set value of boxed property and mark as modified | delete property in parent | error |
| `modified$_$` | value if modified else undefined | same as above | same as above | error |
| `default$_$` | function | set value if it is undefined, otherwise do nothing | error | error |
| `delta$_$` | modified properties of first level, full props thereafter: shallow delta | do shallow delta update of properties, all properties after first level will be changed | error | error |
| `deepDelta$_$` | modified properties only of all levels: deep delta | do deep delta update with value, only modified properties of all levels are changed. | error | error |
Where it makes a difference, both wrapped and unwrapped magic properties are given.
Magic Properties of boxed object shown with defaults for wrapping prefix/suffix options:
| Property | Get | Set | Delete | Call |
|:----------------|:-------------------------------------------------------------------------|:----------------------------------------------------------------------------------------|:--------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `_$` | proxy for the boxed object | append end of array | error | does a call on first argument, use: `boxed._$(_$ => { });` returns `boxed` |
| `get$` | function | error | error | `.get$("prop")` is same as `["prop" + "_$"]`, convenience function when you have a property name in a variable and need a boxed version of it |
| `forEachKey$` | function | error | error | functions executes callback for each property key `.forEach$((prop, unboxedValue) =>{});` skip `undefined` values, addtionally for arrays prop is integers >=0 |
| `forEachKey$_$` | function | error | error | functions executes callback for each property key `.forEach$((prop, boxedValue, unboxedValue) =>{});` skip `undefined` values, addtionally for arrays prop is integers >=0 |
| `unboxed$` | unboxed value | set value of boxed property and mark as modified | delete property in parent | error |
| `modified$` | value if modified else undefined | same as above | same as above | error |
| `default$` | function | set value if it is undefined, otherwise do nothing | error | error |
| `boolean$` | value converted to true or false | convert passed value to true or false before setting | error | error |
| `delta$` | modified properties of first level, full props thereafter: shallow delta | do shallow delta update of properties, all properties after first level will be changed | error | error |
| `deepDelta$` | modified properties only of all levels: deep delta | do deep delta update with value, only modified properties of all levels are changed. | error | error |
Use of `._$()`, sometimes you need to modify deep properties based on programming logic. Instead

@@ -279,2 +295,5 @@ of creating an object then adding it to your modified state, you can use this option and benefit

Applying partial changes to component's state is as easy as setting a value in boxedOnDemand
instance and invoking `.save()`
```javascript

@@ -332,6 +351,6 @@ const boxOnDemand = require('boxed-immutable').boxOnDemand;

| Property | Get | Set | Delete | Call |
|:---------|:---------|:------|:-------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Property | Get | Set | Delete | Call |
|:---------|:---------|:------|:-------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `save` | function | error | error | calls the saveState callback passed to boxOnDemand function, returns value returned from callback, callback only called if there were changes made to boxed object |
| `cancel` | function | error | error | cancels any changes and destroys the boxed object, it is recreated on next access with a fresh copy of the immutable state, returns proxy this for chaining calls |
| `cancel` | function | error | error | cancels any changes and destroys the boxed object, it is recreated on next access with a fresh copy of the immutable state, returns proxy this for chaining calls |

@@ -338,0 +357,0 @@ #### boxOnDemand(getState, saveState, options)

"use strict";
const each = require('jest-each');
const boxedImmutable = require("boxed-immutable");
const testUtil = require('./testUtil');
const _$ = boxedImmutable._$;
const createBox = boxedImmutable.createBox;
const Boxed = boxedImmutable.boxed.Boxed;
const BOXED_GET_THIS = boxedImmutable.boxed.BOXED_GET_THIS;
const isProxy = boxedImmutable.boxed.isBoxedProxy;
const generateTestParams = testUtil.generateTestParams;
const paramStringException = testUtil.paramStringException;
const createBoxed = testUtil.createBoxed;
const createOnDemandBoxed = testUtil.createOnDemandBoxed;
function createBoxed(val) {
const boxedProxy = _$(val);
return {
origVal: val,
boxedProxy: boxedProxy,
boxedVal: boxedProxy[BOXED_GET_THIS],
};
}
describe('Create Array first level', () => {

@@ -226,3 +222,3 @@ let origVal;

test('deepDelta$_$ == deepDelta', () => {
expect(boxedProxy.deepDelta$_$).toEqual(deepDeltaValue);
expect(boxedProxy.deepDelta$_$).toEqual([undefined, undefined, 12, undefined, undefined, 15, undefined, 17]);
});

@@ -240,3 +236,3 @@ });

beforeAll(() => {
let vals = createBoxed({field: ""});
let vals = createBoxed({ field: "" });
origVal = vals.origVal;

@@ -250,3 +246,3 @@ boxedVal = vals.boxedVal;

expectedValue = {field: 3};
expectedValue = { field: 3 };
deepDeltaValue = deltaValue = expectedValue;

@@ -282,3 +278,3 @@ });

beforeAll(() => {
let vals = createBoxed({field1: ""});
let vals = createBoxed({ field1: "" });
origVal = vals.origVal;

@@ -295,4 +291,4 @@ boxedVal = vals.boxedVal;

expectedValue = {field1: "", field2: 2, field3:1, };
deepDeltaValue = deltaValue = {field2: 2, field3:1, };
expectedValue = { field1: "", field2: 2, field3: 1, };
deepDeltaValue = deltaValue = { field2: 2, field3: 1, };
});

@@ -327,3 +323,3 @@

beforeAll(() => {
let vals = createBoxed({field1: ""});
let vals = createBoxed({ field1: "" });
origVal = vals.origVal;

@@ -340,8 +336,8 @@ boxedVal = vals.boxedVal;

expectedValue = {field1: "", field2: 2, field3:1, };
deepDeltaValue = deltaValue = {field2: 2, field3:1, };
expectedValue = { field1: "", field2: 2, field3: 1, };
deepDeltaValue = deltaValue = { field2: 2, field3: 1, };
});
test('[prop].prop fails', () => {
expect(()=>{let t= boxedProxy[field10].flag}).toThrow(ReferenceError);
expect(() => {let t = boxedProxy[field10].flag}).toThrow(ReferenceError);
});

@@ -348,0 +344,0 @@

"use strict";
const each = require('jest-each');
const boxedImmutable = require("boxed-immutable");
const testUtil = require('./testUtil');
const boxedImmutable = require("boxed-immutable");
const _$ = boxedImmutable._$;
const createBox = boxedImmutable.createBox;
const Boxed = boxedImmutable.boxed.Boxed;
const BOXED_GET_THIS = boxedImmutable.boxed.BOXED_GET_THIS;
const isProxy = boxedImmutable.boxed.isBoxedProxy;
const generateTestParams = testUtil.generateTestParams;
const paramStringException = testUtil.paramStringException;
const createBoxed = testUtil.createBoxed;
const createOnDemandBoxed = testUtil.createOnDemandBoxed;
function createBoxed(val) {
const boxedProxy = _$(val);
return {
origVal: val,
boxedProxy: boxedProxy,
boxedVal: boxedProxy[BOXED_GET_THIS],
};
}
describe('Boxed undefined Unmodified', () => {

@@ -767,3 +762,3 @@ let origVal;

test('deepDelta$_$ == value', () => {
expect(boxedProxy.deepDelta$_$).toEqual([10, 5]);
expect(boxedProxy.deepDelta$_$).toEqual([undefined, 5]);
});

@@ -877,3 +872,3 @@ });

test('deepDelta$_$ == delta$_$', () => {
expect(boxedProxy.deepDelta$_$).toEqual(boxedVal.unboxedDelta());
expect(boxedProxy.deepDelta$_$).toEqual([5, undefined, 15]);
});

@@ -1071,3 +1066,3 @@ });

newValue2: { field: 25, },
},
},
// oldValue2: "aValue",

@@ -1229,3 +1224,3 @@ oldValue3: {

newValue2: { field: 25, },
},
},
// oldValue2: "aValue",

@@ -1242,3 +1237,2 @@ oldValue3: {

origDeepDeltaValue = {

@@ -1250,3 +1244,3 @@ oldValue: { newValue1: [5] },

newValue2: { field: 25, },
},
},
// oldValue2: "aValue",

@@ -1263,3 +1257,2 @@ oldValue3: {

origVal = vals.origVal;

@@ -1386,3 +1379,3 @@ boxedVal = vals.boxedVal;

newValue2: { field: 25, },
},
},
// oldValue2: "aValue",

@@ -1399,3 +1392,2 @@ oldValue3: {

origDeepDeltaValue = {

@@ -1407,3 +1399,3 @@ oldValue: { newValue1: [5] },

newValue2: { field: 25, },
},
},
// oldValue2: "aValue",

@@ -1420,3 +1412,2 @@ oldValue3: {

origVal = vals.origVal;

@@ -1423,0 +1414,0 @@ boxedVal = vals.boxedVal;

"use strict";
const each = require('jest-each');
const boxedImmutable = require("boxed-immutable");
const testUtil = require('./testUtil');
const _$ = boxedImmutable._$;
const createBox = boxedImmutable.createBox;
const BOXED_GET_THIS = boxedImmutable.boxed.BOXED_GET_THIS;
const boxOnDemand = boxedImmutable.boxOnDemand;
const BoxedOnDemand = boxedImmutable.boxed.BoxedOnDemand;
const Boxed = boxedImmutable.boxed.Boxed;
const isProxy = boxedImmutable.boxed.isBoxedProxy;
const generateTestParams = testUtil.generateTestParams;
const paramStringException = testUtil.paramStringException;
const createBoxed = testUtil.createBoxed;
const createOnDemandBoxed = testUtil.createOnDemandBoxed;
function createBoxed(get, set) {
const boxedProxy = boxOnDemand(get, set);
return {
boxedProxy: boxedProxy,
};
}
describe('boxed on demand empty no changes', () => {

@@ -30,6 +26,6 @@ let onDemand;

let vals = createBoxed(()=>{
let vals = createOnDemandBoxed(() => {
getCalled++;
return onDemand || origVal;
}, (modified, boxed)=>{
return onDemand || origVal;
}, (modified, boxed) => {
saveCalled++;

@@ -49,11 +45,11 @@ onDemand = {

let tmp;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
tmp = boxedProxy.value;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(onDemand).toEqual(undefined);

@@ -63,13 +59,13 @@ expect(origVal).toEqual(undefined);

boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
let boxed2 = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxed2).toBe(boxed);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
});

@@ -90,6 +86,6 @@ });

let vals = createBoxed(()=>{
let vals = createOnDemandBoxed(() => {
getCalled++;
return onDemand || origVal;
}, (modified, boxed)=>{
return onDemand || origVal;
}, (modified, boxed) => {
saveCalled++;

@@ -108,7 +104,7 @@ onDemand = {

let boxed = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(onDemand).toEqual(undefined);

@@ -120,18 +116,18 @@ expect(origVal).toEqual(undefined);

expect(origVal).toEqual(undefined);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([1,1]);
expect(onDemand).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect([getCalled, saveCalled]).toEqual([1, 1]);
expect(onDemand).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
expect(origVal).toEqual(undefined);
let boxed2 = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
expect(boxed2).not.toBe(boxed);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([2,1]);
expect(onDemand).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect([getCalled, saveCalled]).toEqual([2, 1]);
expect(onDemand).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
expect(origVal).toEqual(undefined);

@@ -153,6 +149,6 @@ });

let vals = createBoxed(()=>{
let vals = createOnDemandBoxed(() => {
getCalled++;
return onDemand || origVal;
}, (modified, boxed)=>{
return onDemand || origVal;
}, (modified, boxed) => {
saveCalled++;

@@ -171,5 +167,5 @@ onDemand = {

expect(boxedProxy.unboxed$_$).toBe(undefined);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy.field).toEqual(undefined);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);

@@ -182,6 +178,6 @@ onDemand = { field: 0 };

expect(boxedProxy.unboxed$_$).toEqual({ field: 0 });
expect([getCalled, saveCalled]).toEqual([2,0]);
expect([getCalled, saveCalled]).toEqual([2, 0]);
expect(boxedProxy.field).toEqual(0);
expect(boxedProxy.unboxed$_$).toEqual({ field: 0 });
expect([getCalled, saveCalled]).toEqual([2,0]);
expect([getCalled, saveCalled]).toEqual([2, 0]);
});

@@ -198,11 +194,11 @@ });

beforeAll(() => {
origVal = { state: { } };
onDemand = { };
origVal = { state: {} };
onDemand = {};
saveCalled = 0;
getCalled = 0;
let vals = createBoxed(()=>{
let vals = createOnDemandBoxed(() => {
getCalled++;
return onDemand.state;
}, (modified, boxed)=>{
return onDemand.state;
}, (modified, boxed) => {
saveCalled++;

@@ -219,21 +215,21 @@ origVal.state = modified;

let boxed = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect(origVal).toEqual({state: {}, });
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(origVal).toEqual({ state: {}, });
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
let boxed2 = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxed2).toBe(boxed);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
});

@@ -250,11 +246,11 @@ });

beforeAll(() => {
origVal = { state: { } };
onDemand = { };
origVal = { state: {} };
onDemand = {};
saveCalled = 0;
getCalled = 0;
let vals = createBoxed(()=>{
let vals = createOnDemandBoxed(() => {
getCalled++;
return onDemand.state;
}, (modified, boxed)=>{
return onDemand.state;
}, (modified, boxed) => {
saveCalled++;

@@ -271,25 +267,25 @@ origVal.state = modified;

let boxed = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(boxedProxy._$).toBe(boxed);
expect([getCalled, saveCalled]).toEqual([1,0]);
expect(origVal).toEqual({state: {}, });
expect([getCalled, saveCalled]).toEqual([1, 0]);
expect(origVal).toEqual({ state: {}, });
boxedProxy._$.simple = 0;
expect(origVal).toEqual({state: {}, });
expect([getCalled, saveCalled]).toEqual([1,0]);
expect(origVal).toEqual({ state: {}, });
expect([getCalled, saveCalled]).toEqual([1, 0]);
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([1,1]);
expect(origVal).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect([getCalled, saveCalled]).toEqual([1, 1]);
expect(origVal).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
let boxed2 = boxedProxy._$;
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
expect(boxed2).not.toBe(boxed);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
expect(boxedProxy._$).toBe(boxed2);
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
boxedProxy.save();
expect([getCalled, saveCalled]).toEqual([2,1]);
expect([getCalled, saveCalled]).toEqual([2, 1]);
});

@@ -304,8 +300,8 @@ });

beforeEach(() => {
origVal = { state: { } };
onDemand = { };
origVal = { state: {} };
onDemand = {};
let vals = createBoxed(()=>{
return onDemand.state;
}, (modified, boxed)=>{
let vals = createOnDemandBoxed(() => {
return onDemand.state;
}, (modified, boxed) => {
origVal.state = modified;

@@ -321,6 +317,6 @@ origVal.delta = boxed.delta$_$;

boxedProxy._$.simple = 0;
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
boxedProxy.save();
expect(origVal).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect(origVal).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
});

@@ -332,6 +328,6 @@

boxedProxy._$.simple = 2;
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
expect(boxedProxy._$).toBe(boxed);
boxedProxy._$.simple = 1;
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
expect(boxedProxy._$).toBe(boxed);

@@ -343,3 +339,3 @@ boxedProxy._$.simple = 0;

expect(origVal).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect(origVal).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
});

@@ -350,9 +346,9 @@

boxedProxy.save();
expect(origVal).toEqual({state: {simple: 2}, delta: {simple: 2}, deepDelta: {simple: 2}, });
expect(origVal).toEqual({ state: { simple: 2 }, delta: { simple: 2 }, deepDelta: { simple: 2 }, });
boxedProxy._$.simple = 1;
boxedProxy.save();
expect(origVal).toEqual({state: {simple: 1}, delta: {simple: 1}, deepDelta: {simple: 1}, });
expect(origVal).toEqual({ state: { simple: 1 }, delta: { simple: 1 }, deepDelta: { simple: 1 }, });
boxedProxy._$.simple = 0;
boxedProxy.save();
expect(origVal).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect(origVal).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
});

@@ -362,14 +358,14 @@

boxedProxy._$.simple = 2;
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
boxedProxy.cancel();
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
boxedProxy._$.simple = 1;
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
boxedProxy.cancel();
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
boxedProxy._$.simple = 0;
expect(origVal).toEqual({state: {}});
expect(origVal).toEqual({ state: {} });
boxedProxy.save();
expect(origVal).toEqual({state: {simple: 0}, delta: {simple: 0}, deepDelta: {simple: 0}, });
expect(origVal).toEqual({ state: { simple: 0 }, delta: { simple: 0 }, deepDelta: { simple: 0 }, });
});

@@ -388,3 +384,3 @@

retVal.save();
expect(origVal).toEqual({"deepDelta": {"simple": 0}, "delta": {"simple": 0}, "state": {"simple": 0}});
expect(origVal).toEqual({ "deepDelta": { "simple": 0 }, "delta": { "simple": 0 }, "state": { "simple": 0 } });
});

@@ -397,5 +393,5 @@

retVal.save();
expect(origVal).toEqual({"deepDelta": {"simple": null}, "delta": {"simple": null}, "state": {"simple": null}});
expect(origVal).toEqual({ "deepDelta": { "simple": null }, "delta": { "simple": null }, "state": { "simple": null } });
});
});

@@ -6,2 +6,3 @@ # Version History

- [0.2.0](#020)
- [0.1.6](#016)

@@ -16,2 +17,28 @@ - [0.1.5](#015)

## 0.2.0
* Fix: forEach$ did not box item passed to callback.
* Delete: `.forEach$` magic property. Was not debugged and not thought through. `.forEachKey$()`
and `.forEachKey$_$()` instead.
* Change: magic wrapped properties can be accessed with or without the parameter prefix/suffix
wrapper. Having both was not relevant since the magic property would hide its unwrapped
counterpart and only made mode typing necessary.
Now `_$().unboxed$` and `_$().unboxed$_$` are the same. However, some magic properties have
meaning attached to a parameter wrapped version. For example: `var_$.forEachKey$` provides
`(key, _$var[key]` to callback and `_$().forEachKey$_$()` provides `(key, _$var[key + "_$"])`,
the latter being the boxed property. Which makes sense based on the `_$` suffix convention of
this library.
Another is `default$(val)` sets to `val` if was undefined, returns unboxed value.
`default$_$(val)` sets to `val` if was undefined, returns boxed proxy for chaining. Quick way
to set defaults then proceed to modify them.
* Add: `boolean$` magic, returns boolean of value, when you absolutely need a boolean and
`undefined`, `null` and other stuff won't do. `_$().boolean$ = value` will convert `value` to
boolean before setting. `boolean$` and `boolean$_$` are equivalent.
* Change: `arrayDeepDeltaPartials` default to `true`
## 0.1.6

@@ -28,20 +55,17 @@

* Fix: deep delta would not include properties for which proxy was
created after the property was already modified in the parent.
* Add: `default$_$` magic property which only changes value if it is
`undefined`, otherwise a noop. Use: `boxed.field_$.default$_$ =
value;` or `boxed.field_$.default$_$(value)`
* Add: `boxOnDemand` proxy wrapper to allow creating a boxed state with
`save()` and `cancel()` methods. It provides a new copy of the state
if it has changed from last access.
* Fix: deep delta would not include properties for which proxy was created after the property
was already modified in the parent.
* Add: `default$_$` magic property which only changes value if it is `undefined`, otherwise a
noop. Use: `boxed.field_$.default$_$ = value;` or `boxed.field_$.default$_$(value)`
* Add: `boxOnDemand` proxy wrapper to allow creating a boxed state with `save()` and `cancel()`
methods. It provides a new copy of the state if it has changed from last access.
## 0.1.3
* Fix: remove endsWith/startsWith on strings. Need to get babel and
polyfill presets figured out. for now works in a react app.
* Fix: remove endsWith/startsWith on strings. Need to get babel and polyfill presets figured
out. for now works in a react app.
## 0.1.2
* Fix: clean out node_modules to detect extraneous requires which are
not used
* Fix: clean out node_modules to detect extraneous requires which are not used

@@ -48,0 +72,0 @@ ## 0.1.1

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc