Socket
Socket
Sign inDemoInstall

rttc

Package Overview
Dependencies
Maintainers
4
Versions
108
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rttc - npm Package Compare versions

Comparing version 2.0.6 to 3.0.0

265

lib/helpers/types.js

@@ -9,4 +9,2 @@ /**

/**

@@ -22,44 +20,19 @@ * Basic type definitions.

// *
'*': {
is: function(v) {
return !_.isUndefined(v);
},
to: function(v) {
if(_.isUndefined(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
return v;
},
getBase: function (){
return undefined;
}
},
// Boolean
boolean: {
is: _.isBoolean,
to: function(v) {
if(_.isBoolean(v)) return v;
if (_.isNumber(v)){
if(v === 1) return true;
if(v === 0) return false;
}
if (_.isString(v)) {
if(v === 'true') return true;
if(v === 'false') return false;
if(v === '1') return true;
if(v === '0') return false;
}
throw new Error('E_runtimeInputTypeCoercionError');
},
getBase: function (){
return false;
}
},
// String
// $$\ $$\
// $$ | \__|
// $$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$$$\
// $$ _____|\_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$\
// \$$$$$$\ $$ | $$ | \__|$$ |$$ | $$ |$$ / $$ |
// \____$$\ $$ |$$\ $$ | $$ |$$ | $$ |$$ | $$ |
// $$$$$$$ | \$$$$ |$$ | $$ |$$ | $$ |\$$$$$$$ |
// \_______/ \____/ \__| \__|\__| \__| \____$$ |
// $$\ $$ |
// \$$$$$$ |
// \______/
string: {

@@ -115,56 +88,17 @@ is: _.isString,

// Dictionary
dictionary: {
is: function(v) {
if (!_.isObject(v)) return false;
if (type.arr.is(v)) return false;
if (!_.isPlainObject(v)) return false;
return true;
},
to: function(v) {
if (_.isArray(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
if (!_.isObject(v)){
throw new Error('E_runtimeInputTypeCoercionError');
}
if (_.isPlainObject(v)){
return v;
}
// Don't tolerate regexps or dates.
if (_.isDate(v) || _.isRegExp(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
// But otherwise, if this is NOT a "plain object", determine whether
// it's JSON-compatible and if so, coerce it to that.
try {
return JSON.parse(JSON.stringify(v));
}
catch (e) {}
// If that doesn't work, give up
throw new Error('E_runtimeInputTypeCoercionError');
},
getBase: function (){
return {};
}
},
// Array
array: {
is: _.isArray,
to: function(v) {
if (!_.isArray(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
return v;
},
getBase: function (){
return [];
}
},
// Number
// $$\
// $$ |
// $$$$$$$\ $$\ $$\ $$$$$$\$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\
// $$ __$$\ $$ | $$ |$$ _$$ _$$\ $$ __$$\ $$ __$$\ $$ __$$\
// $$ | $$ |$$ | $$ |$$ / $$ / $$ |$$ | $$ |$$$$$$$$ |$$ | \__|
// $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ ____|$$ |
// $$ | $$ |\$$$$$$ |$$ | $$ | $$ |$$$$$$$ |\$$$$$$$\ $$ |
// \__| \__| \______/ \__| \__| \__|\_______/ \_______|\__|
//
//
//
number: {

@@ -225,2 +159,152 @@ is: function(v) {

// $$\ $$\
// $$ | $$ |
// $$$$$$$\ $$$$$$\ $$$$$$\ $$ | $$$$$$\ $$$$$$\ $$$$$$$\
// $$ __$$\ $$ __$$\ $$ __$$\ $$ |$$ __$$\ \____$$\ $$ __$$\
// $$ | $$ |$$ / $$ |$$ / $$ |$$ |$$$$$$$$ | $$$$$$$ |$$ | $$ |
// $$ | $$ |$$ | $$ |$$ | $$ |$$ |$$ ____|$$ __$$ |$$ | $$ |
// $$$$$$$ |\$$$$$$ |\$$$$$$ |$$ |\$$$$$$$\ \$$$$$$$ |$$ | $$ |
// \_______/ \______/ \______/ \__| \_______| \_______|\__| \__|
//
//
//
boolean: {
is: _.isBoolean,
to: function(v) {
if(_.isBoolean(v)) return v;
if (_.isNumber(v)){
if(v === 1) return true;
if(v === 0) return false;
}
if (_.isString(v)) {
if(v === 'true') return true;
if(v === 'false') return false;
if(v === '1') return true;
if(v === '0') return false;
}
throw new Error('E_runtimeInputTypeCoercionError');
},
getBase: function (){
return false;
}
},
// $$\ $$\ $$\ $$\
// $$ |\__| $$ | \__|
// $$$$$$$ |$$\ $$$$$$$\ $$$$$$\ $$\ $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
// $$ __$$ |$$ |$$ _____|\_$$ _| $$ |$$ __$$\ $$ __$$\ \____$$\ $$ __$$\ $$ | $$ |
// $$ / $$ |$$ |$$ / $$ | $$ |$$ / $$ |$$ | $$ | $$$$$$$ |$$ | \__|$$ | $$ |
// $$ | $$ |$$ |$$ | $$ |$$\ $$ |$$ | $$ |$$ | $$ |$$ __$$ |$$ | $$ | $$ |
// \$$$$$$$ |$$ |\$$$$$$$\ \$$$$ |$$ |\$$$$$$ |$$ | $$ |\$$$$$$$ |$$ | \$$$$$$$ |
// \_______|\__| \_______| \____/ \__| \______/ \__| \__| \_______|\__| \____$$ |
// $$\ $$ |
// \$$$$$$ |
// \______/
dictionary: {
is: function(v) {
if (!_.isObject(v)) return false;
if (type.arr.is(v)) return false;
if (!_.isPlainObject(v)) return false;
return true;
},
to: function(v) {
// Don't tolerate non-objects, or arrays, or regexps, or dates.
if (!_.isObject(v) || _.isArray(v) || _.isDate(v) || _.isRegExp(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
// Plain objects are ok, but we'll clone them.
// (TODO: this should be configurable)
if (_.isPlainObject(v)){
return _.clone(v);
}
// If this is NOT a "plain object" (i.e. some kind of prototypal thing)
// determine whether it's JSON-compatible and if so, coerce it to that.
try {
return JSON.parse(JSON.stringify(v));
}
catch (e) {}
// If that doesn't work, give up
throw new Error('E_runtimeInputTypeCoercionError');
},
getBase: function (){
return {};
}
},
//
//
// $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
// \____$$\ $$ __$$\ $$ __$$\ \____$$\ $$ | $$ |
// $$$$$$$ |$$ | \__|$$ | \__|$$$$$$$ |$$ | $$ |
// $$ __$$ |$$ | $$ | $$ __$$ |$$ | $$ |
// \$$$$$$$ |$$ | $$ | \$$$$$$$ |\$$$$$$$ |
// \_______|\__| \__| \_______| \____$$ |
// $$\ $$ |
// \$$$$$$ |
// \______/
array: {
is: _.isArray,
to: function(v) {
if (!_.isArray(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
return v;
},
getBase: function (){
return [];
}
},
// $$$\ $$\ $$\ $$\ $$$\
// $$\$$\ $$ _| $$ | $$ | \__| \$$\
// \$$$ | $$ / $$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$$$$$$\ $$\ $$$$$$$\ $$$$$$\ \$$\
// $$$$$$$\ $$ | \____$$\ $$ __$$\ $$ | $$ |\_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$\ $$ |
// \_$$$ __| $$ | $$$$$$$ |$$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |$$ | $$ |$$ / $$ | $$ |
// $$ $$\ \$$\ $$ __$$ |$$ | $$ |$$ | $$ | $$ |$$\ $$ | $$ |$$ |$$ | $$ |$$ | $$ | $$ |
// \__\__| \$$$\\$$$$$$$ |$$ | $$ |\$$$$$$$ | \$$$$ |$$ | $$ |$$ |$$ | $$ |\$$$$$$$ |$$$ /
// \___|\_______|\__| \__| \____$$ | \____/ \__| \__|\__|\__| \__| \____$$ |\___/
// $$\ $$ | $$\ $$ |
// \$$$$$$ | \$$$$$$ |
// \______/ \______/
'*': {
is: function(v) {
return !_.isUndefined(v);
},
to: function(v) {
if(_.isUndefined(v)) {
throw new Error('E_runtimeInputTypeCoercionError');
}
return v;
},
getBase: function (){
return undefined;
}
},
};

@@ -230,2 +314,5 @@

// Aliases

@@ -232,0 +319,0 @@ type.str = type.email = type.url = type.string;

@@ -45,11 +45,3 @@ /**

// // "*" (allow anything)
// if (expected === '*'){
// // Except if this is `undefined`, it's a E_INVALID_TYPE error
// // (i.e. won't matter if coercing, but will cause failure when validating)
// if (_.isUndefined(actual)){}
// return actual;
// }
// Flag '*' (allow anything that's not undefined)

@@ -59,5 +51,5 @@ if (expected === '*') {

}
// Normalize: [] or ['*'] or "array" (allow any array)
if (expected === 'array' || (_.isArray(expected) && (expected.length === 0 || expected[0] === '*'))) {
expected = ['*'];
// Normalize: [] or "array" (allow any array)
if (expected === 'array' || (_.isArray(expected) && expected.length === 0)) {
expected = [];
allowAnyArray = true;

@@ -109,2 +101,3 @@ }

// Build an E_INVALID_TYPE error

@@ -172,14 +165,23 @@ var newErr = (function (){

}
// else {
// console.log('actual:',actual,' IS EXPECTED TYPE.');
// }
// Build partial result
// (taking recursive step if necessary)
// ...expecting ANYTHING ('*')
if (isExpectingAnything) {
coercedValue = sanitizeGenericDictionary(coercedValue);
coercedValue = sanitizeGenericArray(coercedValue);
return coercedValue;
}
if (isExpectingArray) {
// ...expecting ANY array ([])
if (allowAnyArray) {
return sanitizeGenericArray(coercedValue);
return recursivelyCloneAndStripUndefinedKeysFromDictionaries(coercedValue);
}
// ...expecting a specific array example
var arrayItemTpl = expected[0];

@@ -191,6 +193,10 @@ return _.reduce(coercedValue, function (memo, coercedVal){

}
if (isExpectingDictionary) {
// ...expecting ANY dictionary ({})
if (allowAnyDictionary){
return sanitizeGenericDictionary(coercedValue);
return recursivelyCloneAndStripUndefinedKeysFromDictionaries(coercedValue);
}
// ...expecting a specific dictionary example
return _.reduce(expected, function (memo, expectedVal, expectedKey) {

@@ -202,2 +208,4 @@ var keyName = (meta && meta.keyName ? (meta.keyName + '.') : '') + expectedKey;

}
// ...expecting a primitive
return coercedValue;

@@ -209,31 +217,120 @@ };

/**
* sanitize a dictionary used in examples `['*']`, `{}`, or `'*'`
*
*/
function sanitizeGenericDictionary(val){
function recursivelyCloneAndStripUndefinedKeysFromDictionaries(val) {
// TODO: also consider doing all the things in the `dictionary`
// type `.to()` function here...
if (!_.isPlainObject(val)) return val;
// (note that the recursive validation will not penetrate deeper into this
// object, so we don't have to worry about this function running more than once
// and doing unnecessary extra deep copies at each successive level)
// Strip out keys w/ undefined values
return _.reduce(_.keys(val), function (memo, key){
if (!_.isUndefined(val[key])) {
memo[key] = val[key];
// If dictionary:
// ==============================================================================
// Sanitize a dictionary provided for a wildcard dictionary example (`example: {}`)
// The main recursive validation function will not descend into this dictionary because
// it's already met the minimum requirement of being an object. So we need to deep clone
// the provided value for safety; and in the process ensure that it meets the basic minimum
// quality requirements (i.e. no dictionaries within have any undefined keys)
//
// At this point, we can be sure that the provided value is a plain object
// (otherwise it would have been caught by the coercion step, since in order to
// be considered a "dictionary", a value has to pass a _.isPlainObject() check)
// If array:
// ==============================================================================
// Sanitize an array provided for a wildcard array example (`example: []`)
// The main recursive validation function will not descend into this array because
// it's already met the minimum requirement of being `_.isArray()`. So we need to
// deep clone the provided value for safety; and in the process ensure that it meets
// the basic minimum quality requirements (i.e. no dictionaries within have any undefined
// keys)
//
// (NOTE: `example: ['*']` won't make it here because it will be picked up
// by the recursive validation. And so it's different-- it will contain
// the original items, and therefore may contain dictionaries w/ undefined keys)
//
// At this point, we can be sure that the provided value is an array
// (otherwise it would have been caught by the coercion step, since in order to
// be considered an "array", a value has to pass a _.isArray() check)
function _recursivelyCloneAndStripUndefinedKeys (val) {
if (_.isArray(val)) {
return _.reduce(val,function (memo, item, i) {
memo.push(_recursivelyCloneAndStripUndefinedKeys(item));
return memo;
}, []);
}
return memo;
}, {});
else if (_.isObject(val)) {
return _.reduce(val,function (memo, subVal, key) {
if (!_.isUndefined(subVal)) {
memo[key] = _recursivelyCloneAndStripUndefinedKeys(subVal);
}
return memo;
}, {});
}
else {
return val;
}
}
// First, prevent against endless circular recursion:
// (this should never throw, but if it does, it needs to be handled
// by the caller of `recursivelyCloneAndStripUndefinedKeysFromDictionaries`)
val = JSON.parse(stringifySafe(val));
// TODO:
// could consolidate this cloning + stripping undefined keys + prevention against
// ∞-recursion into a single tree traversal, which would double the efficiency.
// Then build a deep copy
// (in the process, remove keys with undefined values from nested dictionaries recursively)
return _recursivelyCloneAndStripUndefinedKeys(val);
}
/**
* sanitize an array used in examples `['*']`, `{}`, or `'*'`
* This was modified by @mikermcneil from @isaacs' json-stringify-safe
* (see https://github.com/isaacs/json-stringify-safe/commit/02cfafd45f06d076ac4bf0dd28be6738a07a72f9#diff-c3fcfbed30e93682746088e2ce1a4a24)
* @param {*} val [description]
* @return {String} [description]
*/
function sanitizeGenericArray(val){
if (!_.isArray(val)) return val;
function stringifySafe(val) {
return JSON.stringify(val, serializer());
}
return _.reduce(val, function (memo, item){
item = sanitizeGenericDictionary(item);
memo.push(item);
return memo;
}, []);
function serializer(replacer, cycleReplacer) {
var stack = [];
var keys = [];
if (!cycleReplacer) {
cycleReplacer = function(key, value) {
if (stack[0] === value) return '[Circular ~]';
return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';
};
}
return function(key, value) {
if (stack.length > 0) {
var thisPos = stack.indexOf(this);
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value);
}
else stack.push(value);
if (!replacer) {
return value;
}
return replacer.call(this, key, value);
};
}
{
"name": "rttc",
"version": "2.0.6",
"version": "3.0.0",
"description": "Runtime type-checking for JavaScript.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -56,5 +56,5 @@ # rttc

+ For a generic "array" (`[]`), base value is `[]`, with a single archetypal item matching the expectation (recursive)
+ For "*", base value is `"undefined"`.
<!--

@@ -68,3 +68,3 @@ TODO:

+ `undefined` will always be coerced to the base value of the expected type.
+ `undefined` is never valid.
+ `null` is never valid.

@@ -71,0 +71,0 @@ + `NaN` is never valid.

@@ -188,2 +188,4 @@ // Export the array of tests below.

// ARRAYS
// (all of the tests below pass w/ either [] or ['*']
// however note they do have subtle differences re: strictEq)
////////////////////////////////////////////

@@ -219,3 +221,2 @@

{ example: [], actual: new (require('stream').Readable)(), result: [] }, // TODO: consider enhancing this behavior to concat the stream contents? Needs community discussion.
// Skip Buffer tests for now since the enumerable properties vary between Node.js versions.

@@ -227,4 +228,5 @@ // TODO: bring back support for this by explicitly filtering properties of buffers in `.exec()`

////////////////////////////////////////////
// MISC
// example: * (aka undefined)
////////////////////////////////////////////

@@ -273,7 +275,27 @@

{ example: undefined, actual: /some regexp/, result: /some regexp/ },
{ example: undefined, actual: new Date('November 5, 1605 GMT'), result: new Date('November 5, 1605 GMT') },
////////////////////////////////////////////
// RECURSIVE OBJECTS
////////////////////////////////////////////
// $$\
// \__|
// $$$$$$\ $$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$$$$$$\ $$\ $$\ $$\ $$$$$$\
// $$ __$$\ $$ __$$\ $$ _____|$$ | $$ |$$ __$$\ $$ _____|$$ |\$$\ $$ |$$ __$$\
// $$ | \__|$$$$$$$$ |$$ / $$ | $$ |$$ | \__|\$$$$$$\ $$ | \$$\$$ / $$$$$$$$ |
// $$ | $$ ____|$$ | $$ | $$ |$$ | \____$$\ $$ | \$$$ / $$ ____|
// $$ | \$$$$$$$\ \$$$$$$$\ \$$$$$$ |$$ | $$$$$$$ |$$ | \$ / \$$$$$$$\
// \__| \_______| \_______| \______/ \__| \_______/ \__| \_/ \_______|
//
//
//
// Some basic deep objects

@@ -290,2 +312,28 @@ { example: {a:1, b:'hi', c: false}, actual: {a: 23}, result: {a: 23, b: '', c: false} },

// Omit extra keys when coercing to `example: {...}`
{
example: { a:23 },
actual: {a: 23, d: true},
result: {a: 23}
},
// Coerce missing/undefined required keys to base value
{ example: {b: 235}, actual: {b: undefined}, result: {b: 0} },
{ example: {b: 235}, actual: {}, result: {b: 0} },
// Strip keys with `undefined` values (`{...}` case)
{ example: {b: 235}, actual: {a: undefined, b: 3}, result: {b: 3} },
// Strip nested keys with `undefined` values (`{...}` case)
{ example: {a: {}, b: 235}, actual: {a: {x: undefined}, b: 3}, result: {a: {}, b: 3} },
// Strip keys with `undefined` values (`{}` case)
{ example: {}, actual: {a: undefined, b: 3}, result: {b: 3} },
// Strip nested keys with `undefined` values (`{}` case)
{ example: {}, actual: {a: {x: undefined}, b: 3}, result: {a: {}, b: 3} },
// Don't strip keys or nested keys with `undefined` values (`*` and nested `*` cases)
{ example: '*', actual: {a: undefined, b: 3, c: {x: undefined}}, result: {a: undefined, b: 3, c: {x: undefined}} },
{ example: {c:'*'}, actual: {a: undefined, b: 3, c: {x: undefined}}, result: { c: {x: undefined}} },
// Ensure that this allows arbitary arrays when coercing to `example: []`

@@ -298,5 +346,81 @@ {

// Ensure that nested dictionaries inside of an array passed
// through `example: []` are stripped of keys with undefined values
{
example: [],
actual: [{a:3, b: undefined}, {a: undefined}],
result: [{a: 3},{}]
},
{
example: [],
actual: [{a:3,someStuff: [{x: undefined, y: 'foo'}, {x: 'bar', y: undefined}]},{a: 5, b: undefined}],
result: [{a: 3, someStuff: [{y:'foo'}, {x:'bar'}]}, {a: 5}]
},
// Complex multi-item array test
// Ensure that nested dictionaries inside of an array passed
// through `example: ['*']` are NOT stripped of keys with undefined values--
// and are left utterly alone
{
example: ['*'],
actual: [{a:3, b: undefined}, {a: undefined}],
result: [{a: 3, b: undefined},{a:undefined}]
},
{
example: ['*'],
actual: [{a:3,someStuff: [{x: undefined, y: 'foo'}, {x: 'bar', y: undefined}]},{a: 5, b: undefined}],
result: [{a:3,someStuff: [{x: undefined, y: 'foo'}, {x: 'bar', y: undefined}]},{a: 5, b: undefined}]
},
// Ensure the recursive cloning / undefined-key-stripping doesn't get
// stumped by circular dictionaries/arrays.
// • dict keys whose values point to a past reference should be deleted
// • array items that point to past references should be pruned
(function (){
var someDict = {};
var someArray = [];
someDict.x = {z: someDict, foo: undefined};
someDict.y = someArray;
someArray.push(someDict);
someArray.push(someDict.x);
var test = {
example: {},
actual: {
someDict: someDict,
someArray: someArray
},
result: {
someDict: {
x: {
z: '[Circular ~.someDict]'
},
y: [
'[Circular ~.someDict]',
{
z: '[Circular ~.someDict]'
}
]
},
someArray: [
{
x: {
z: '[Circular ~.someArray.0]'
},
y: '[Circular ~.someArray]'
},
{
z: {
x: '[Circular ~.someArray.1]',
y: '[Circular ~.someArray]'
}
}
]
}
};
return test;
})(),
// Wholistic, complex multi-item array test
{
example: [{

@@ -471,2 +595,146 @@ id: 123,

// $$\ $$\ $$\ $$$\ $$$\
// $$ | \__| $$ | $$ _| \$$\
// $$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$ /$$$$\ $$$$\ $$$$\ \$$\
// $$ _____|\_$$ _| $$ __$$\ $$ |$$ _____|\_$$ _| $$ __$$\ $$ __$$\ $$ | \____|\____|\____| $$ |
// \$$$$$$\ $$ | $$ | \__|$$ |$$ / $$ | $$$$$$$$ |$$ / $$ | $$ | $$$$\ $$$$\ $$$$\ $$ |
// \____$$\ $$ |$$\ $$ | $$ |$$ | $$ |$$\ $$ ____|$$ | $$ | \$$\ \____|\____|\____|$$ |
// $$$$$$$ | \$$$$ |$$ | $$ |\$$$$$$$\ \$$$$ | \$$$$$$$\ \$$$$$$$ | \$$$\ $$$ /
// \_______/ \____/ \__| \__| \_______| \____/ \_______| \____$$ | \___| \___/
// $$ |
// $$ |
// \__|
//
// (strictEq / isNew checks to assert for and
// against passing-by-reference in different
// situations)
////////////////////////////////////////////////
////////////////////////////////////////////////
// example: '*'
// result value should always be strictly equal (===)
////////////////////////////////////////////////
{ example: undefined, actual: {}, strictEq: true },
{ example: undefined, actual: {a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}, strictEq: true },
{ example: undefined, actual: [], strictEq: true },
{ example: undefined, actual: [{a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}], strictEq: true },
{ example: undefined, actual: /some regexp/, strictEq: true },
{ example: undefined, actual: function (){}, strictEq: true },
{ example: undefined, actual: new Date('November 5, 1605 GMT'), strictEq: true },
{ example: undefined, actual: new (require('stream').Readable)(), strictEq: true },
{ example: undefined, actual: new Buffer('asdf'), strictEq: true },
{ example: undefined, actual: new Error('asdf'), strictEq: true },
////////////////////////////////////////////////////////////////////////////////////////////////
// example: nested '*' in dictionaries/arrays
// TODO: needs to be tested some other way, since we'd be checking reference passing within another nested obj.
// also check against strict equality between sub-values (`!==` between nested things...)
// This is prbly eaiest if we just pull it out into a separate test; ie. don't make the test declarative.
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////
// example: {} should perform a deep copy on things
////////////////////////////////////////////////
{ example: {}, actual: {}, isNew: true },
{ example: {}, actual: {a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}, isNew: true },
////////////////////////////////////////////////
// example: {...} should perform a shallow copy
// Assert pass-by-reference behavior for more specific `example`
////////////////////////////////////////////////
{
example: { id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true },
actual: {},
result: { id: 0, title: '', body: '', votes: 0, resolved: false },
isNew: true
},
{
example: { id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true },
actual: {a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]},
result: { id: 0, title: '', body: '', votes: 0, resolved: false },
isNew: true
},
{
example: { id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' },
actual: {},
result: { id: 0, title: '', body: '', votes: 0, resolved: false, something: undefined },
isNew: true
},
{
example: { id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' },
actual: {a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]},
result: { id: 0, title: '', body: '', votes: 0, resolved: false, something: undefined },
isNew: true
},
{
example: { id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' },
actual: { something: new Date('November 5, 1605 GMT')},
result: { id: 0, title: '', body: '', votes: 0, resolved: false, something: new Date('November 5, 1605 GMT') },
isNew: true
},
{
example: { id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' },
actual: { something: new Date('November 5, 1605 GMT'), a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]},
result: { id: 0, title: '', body: '', votes: 0, resolved: false, something: new Date('November 5, 1605 GMT') },
isNew: true
},
////////////////////////////////////////////////
// example: [] should copy things
////////////////////////////////////////////////
{ example: [], actual: [], isNew: true },
{ example: [], actual: [{a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}], isNew: true },
////////////////////////////////////////////////
// example: {...} should perform a shallow copy
// Assert pass-by-reference behavior for more specific `example`s
////////////////////////////////////////////////
{
example: [{ id: 123, title: 'Scott', body: 'Scott', votes: 0, resolved: true }],
actual: [],
result: [],
isNew: true
},
{
example: [{ id: 123, title: 'Scott', body: 'Scott', votes: 0, resolved: true }],
actual: [{a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}],
result: [{ id: 0, title: '', body: '', votes: 0, resolved: false }],
isNew: true
},
{
example: [{ id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' }],
actual: [],
result: [],
isNew: true
},
{
example: [{ id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' }],
actual: [{a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}],
result: [{ id: 0, title: '', body: '', votes: 0, resolved: false, something: undefined }],
isNew: true
},
{
example: [{ id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' }],
actual: [{ something: new Date('November 5, 1605 GMT')}],
result: [{ id: 0, title: '', body: '', votes: 0, resolved: false, something: new Date('November 5, 1605 GMT') }],
isNew: true
},
{
example: [{ id: 123, title: 'Scott', body: 'Scott', votes: 33, resolved: true, something: '*' }],
actual: [{ something: new Date('November 5, 1605 GMT'), a:23,b:'asdg',c:true,d: {x:32,y:'sagd',z: [{a:2,b:'gsda',c:false}]}, e: [2]}],
result: [{ id: 0, title: '', body: '', votes: 0, resolved: false, something: new Date('November 5, 1605 GMT') }],
isNew: true
},
];

@@ -20,7 +20,16 @@ /**

if (_.isUndefined(test.example)) {
starTests.push({
var newTest = {
example: '*',
actual: _.cloneDeep(test.actual),
result: _.cloneDeep(test.result)
});
actual: test.actual
};
if (test.hasOwnProperty('result')) {
newTest.result = test.result;
}
if (test.hasOwnProperty('strictEq')) {
newTest.strictEq = _.cloneDeep(test.strictEq);
}
if (test.hasOwnProperty('isNew')) {
newTest.isNew = _.cloneDeep(test.isNew);
}
starTests.push(newTest);
}

@@ -40,4 +49,5 @@ });

// • tests that expect a result===`undefined`
// • tests that verify `strictEq` or `isNew`
// (nested behavior is different in these cases^)
if (!_.isUndefined(test.example) && !test.error && !_.isUndefined(test.result)) {
if (!test.error && !_.isUndefined(test.result) && !test.hasOwnProperty('strictEq') && !test.hasOwnProperty('isNew')) {

@@ -44,0 +54,0 @@ // test one level of additional array nesting

@@ -26,2 +26,6 @@ /**

if (test.strictEq && test.isNew) {
throw new Error('INVALID TEST: `isNew` and `strictEq` are mutually exclusive opposites- cannot use them together. For reference, this is test:\n'+util.inspect(test, false, null));
}
var actualDisplayName = (_.isObject(test.actual)&&test.actual.constructor && test.actual.constructor.name !== 'Object' && test.actual.constructor.name !== 'Array')?test.actual.constructor.name:util.inspect(test.actual, false, null);

@@ -38,3 +42,2 @@

}
if (!_.isUndefined(test.example)) {

@@ -49,3 +52,2 @@ msg += 'with a '+getDisplayType(test.example)+' example ('+util.inspect(test.example,false, null)+')';

}
return msg;

@@ -60,3 +62,18 @@ })(), function suite (){

it(util.format('should coerce %s', actualDisplayName, 'into '+util.inspect(test.result, false, null)+''), function (done){
var itMsg = 'should ';
if (test.strictEq) {
itMsg+='maintain strict equality (===) when ' + actualDisplayName + ' is provided';
}
else if (test.isNew) {
if (test.hasOwnProperty('result')) {
itMsg+='convert ' + actualDisplayName + ' into a new (!== original) value '+util.inspect(test.result, false, null);
}
else {
itMsg+='take ' + actualDisplayName + ' and yield a copy (which !== original)';
}
}
else {
itMsg+='convert ' + actualDisplayName + ' into '+util.inspect(test.result, false, null);
}
it(itMsg, function (done){
runTestFn(test, done);

@@ -63,0 +80,0 @@ });

@@ -196,2 +196,4 @@ // Export the array of tests below

// ARRAYS
// (all of the tests below pass w/ either [] or ['*']
// however note they do have subtle differences re: strictEq)
////////////////////////////////////////////

@@ -242,11 +244,16 @@

{ example: {a:1}, actual: {a: undefined}, error: true },
{ example: {a:1}, actual: {}, error: true },
// Missing keys (`*` case)
{ example: {a:'*'}, actual: {a: undefined}, error: true },
{ example: {a:'*'}, actual: {}, error: true },
// Keys with `undefined` values (`{}` case)
// Strip keys with `undefined` values (`{}` case)
{ example: {}, actual: {a: undefined, b: 3}, result: {b: 3} },
// Keys with `undefined` values (`*` case)
{ example: '*', actual: {a: undefined, b: 3}, result: {b: 3} },
{ example: [{}], actual: [{a: undefined, b: 3}], result: [{b: 3}] },
{ example: {x:{}}, actual: {x:{a: undefined, b: 3}}, result: {x:{b: 3}} },
// Don't strip keys with `undefined` values (`*` case)
{ example: '*', actual: {a: undefined, b: 3}, result: {a: undefined, b: 3} },
// Extra keys:

@@ -301,23 +308,179 @@ { example: {a:1, b:'hi'}, actual: {a: 23, b: 'stuff', d: true}, result: {a: 23, b: 'stuff'} },

// $$\
// \__|
// $$$$$$\ $$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$$$$$$\ $$\ $$\ $$\ $$$$$$\
// $$ __$$\ $$ __$$\ $$ _____|$$ | $$ |$$ __$$\ $$ _____|$$ |\$$\ $$ |$$ __$$\
// $$ | \__|$$$$$$$$ |$$ / $$ | $$ |$$ | \__|\$$$$$$\ $$ | \$$\$$ / $$$$$$$$ |
// $$ | $$ ____|$$ | $$ | $$ |$$ | \____$$\ $$ | \$$$ / $$ ____|
// $$ | \$$$$$$$\ \$$$$$$$\ \$$$$$$ |$$ | $$$$$$$ |$$ | \$ / \$$$$$$$\
// \__| \_______| \_______| \______/ \__| \_______/ \__| \_/ \_______|
//
//
//
// Some basic deep dicts and array
{
example: {a:1, b:'hi', c: false},
actual: {a: 23},
error: true
},
{
example: {a:1, b:'hi', c: false},
actual: {a: 23, d: true},
error: true
},
// Ensure that this allows extra keys when coercing to `example: {}`
{
example: {},
actual: {a: 23, d: true},
result: {a: 23, d: true}
},
// Omit extra keys when coercing to `example: {...}`
{
example: { a:23 },
actual: {a: 23, d: true},
result: {a: 23}
},
// Reject when there are missing or undefined required keys
{ example: {b: 235}, actual: {b: undefined}, error: true },
{ example: {b: 235}, actual: {}, error: true },
// Strip extra keys with `undefined` values (`{...}` case)
{ example: {b: 235}, actual: {a: undefined, b: 3}, result: {b: 3} },
// Strip extra nested keys with `undefined` values (`{...}` case)
{ example: {a: {}, b: 235}, actual: {a: {x: undefined}, b: 3}, result: {a: {}, b: 3} },
// Strip keys with `undefined` values (`{}` case)
{ example: {}, actual: {a: undefined, b: 3}, result: {b: 3} },
// Strip nested keys with `undefined` values (`{}` case)
{ example: {}, actual: {a: {x: undefined}, b: 3}, result: {a: {}, b: 3} },
// Don't strip keys or nested keys with `undefined` values (`*` and nested `*` cases)
{ example: '*', actual: {a: undefined, b: 3, c: {x: undefined}}, result: {a: undefined, b: 3, c: {x: undefined}} },
{ example: {c:'*'}, actual: {a: undefined, b: 3, c: {x: undefined}}, result: { c: {x: undefined}} },
// Ensure that this allows arbitary arrays when coercing to `example: []`
{
example: [],
actual: [{a: 23, d: true}],
result: [{a: 23, d: true}]
},
// Ensure that nested dictionaries inside of an array passed
// through `example: []` are stripped of keys with undefined values
{
example: [],
actual: [{a:3, b: undefined}, {a: undefined}],
result: [{a: 3},{}]
},
{
example: [],
actual: [{a:3,someStuff: [{x: undefined, y: 'foo'}, {x: 'bar', y: undefined}]},{a: 5, b: undefined}],
result: [{a: 3, someStuff: [{y:'foo'}, {x:'bar'}]}, {a: 5}]
},
// Ensure that nested dictionaries inside of an array passed
// through `example: ['*']` are NOT stripped of keys with undefined values--
// and are left utterly alone
{
example: ['*'],
actual: [{a:3, b: undefined}, {a: undefined}],
result: [{a: 3, b: undefined},{a:undefined}]
},
{
example: ['*'],
actual: [{a:3,someStuff: [{x: undefined, y: 'foo'}, {x: 'bar', y: undefined}]},{a: 5, b: undefined}],
result: [{a:3,someStuff: [{x: undefined, y: 'foo'}, {x: 'bar', y: undefined}]},{a: 5, b: undefined}]
},
// Ensure the recursive cloning / undefined-key-stripping doesn't get
// stumped by circular dictionaries/arrays.
// • dict keys whose values point to a past reference should be deleted
// • array items that point to past references should be pruned
(function (){
var regexp = /some regexp/;
return { example: '*', actual: regexp, result: regexp, };
var someDict = {};
var someArray = [];
someDict.x = {z: someDict, foo: undefined};
someDict.y = someArray;
someArray.push(someDict);
someArray.push(someDict.x);
var test = {
example: {},
actual: {
someDict: someDict,
someArray: someArray
},
result: {
someDict: {
x: {
z: '[Circular ~.someDict]'
},
y: [
'[Circular ~.someDict]',
{
z: '[Circular ~.someDict]'
}
]
},
someArray: [
{
x: {
z: '[Circular ~.someArray.0]'
},
y: '[Circular ~.someArray]'
},
{
z: {
x: '[Circular ~.someArray.1]',
y: '[Circular ~.someArray]'
}
}
]
}
};
return test;
})(),
(function (){
var fn = function (){};
return { example: '*', actual: fn, result: fn, };
})(),
{ example: '*', actual: new Date('November 5, 1605 GMT'), result: new Date('November 5, 1605 GMT'), },
{ example: '*', actual: new (require('stream').Readable)(), result: new (require('stream').Readable)(), },
(function (){
var buffer = new Buffer('asdf');
return { example: '*', actual: buffer, result: buffer };
})(),
(function (){
var err = new Error('asdf');
return { example: '*', actual: err, result: err, };
})()
// $$\ $$\ $$\ $$$\ $$$\
// $$ | \__| $$ | $$ _| \$$\
// $$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$ /$$$$\ $$$$\ $$$$\ \$$\
// $$ _____|\_$$ _| $$ __$$\ $$ |$$ _____|\_$$ _| $$ __$$\ $$ __$$\ $$ | \____|\____|\____| $$ |
// \$$$$$$\ $$ | $$ | \__|$$ |$$ / $$ | $$$$$$$$ |$$ / $$ | $$ | $$$$\ $$$$\ $$$$\ $$ |
// \____$$\ $$ |$$\ $$ | $$ |$$ | $$ |$$\ $$ ____|$$ | $$ | \$$\ \____|\____|\____|$$ |
// $$$$$$$ | \$$$$ |$$ | $$ |\$$$$$$$\ \$$$$ | \$$$$$$$\ \$$$$$$$ | \$$$\ $$$ /
// \_______/ \____/ \__| \__| \_______| \____/ \_______| \____$$ | \___| \___/
// $$ |
// $$ |
// \__|
//
// (strictEq / isNew checks to assert for and
// against passing-by-reference in different
// situations)
////////////////////////////////////////////////
{ example: '*', actual: /some regexp/, strictEq: true },
{ example: '*', actual: function (){}, strictEq: true },
{ example: '*', actual: new Date('November 5, 1605 GMT'), strictEq: true },
{ example: '*', actual: new (require('stream').Readable)(), strictEq: true },
{ example: '*', actual: new Buffer('asdf'), strictEq: true },
{ example: '*', actual: new Error('asdf'), strictEq: true },
];
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