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

blork

Package Overview
Dependencies
Maintainers
1
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

blork - npm Package Compare versions

Comparing version 6.0.0 to 7.0.0

lib/constants.js

194

lib/checkers.js

@@ -5,2 +5,12 @@ /* eslint-disable prettier/prettier */

// Regexes.
const R_UPPER = /^[A-Z][A-Z0-9]*$/;
const R_LOWER = /^[a-z][a-z0-9]*$/;
const R_CAMEL = /^[a-z][a-zA-Z0-9]*$/;
const R_PASCAL = /^[A-Z][a-zA-Z0-9]*$/;
const R_SNAKE = /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/;
const R_SCREAMING = /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/;
const R_KEBAB = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
const R_TRAIN = /^[A-Z][a-zA-Z0-9]*(-[A-Z0-9][a-zA-Z0-9]+)*$/;
// Checkers.

@@ -18,21 +28,23 @@ function isNull(v) { return v === null; }

function isNaN(v) { return Number.isNaN(v); }
function isFiniteNumber(v) { return typeof v === "number" && Number.isFinite(v); }
function isPositiveNumber(v) { return typeof v === "number" && Number.isFinite(v) && v >= 0; }
function isNegativeNumber(v) { return typeof v === "number" && Number.isFinite(v) && v <= 0; }
function isInteger(v) { return typeof v === "number" && Number.isInteger(v) && v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER; }
function isPositiveInteger(v) { return typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= Number.MAX_SAFE_INTEGER; }
function isNegativeInteger(v) { return typeof v === "number" && Number.isInteger(v) && v >= Number.MIN_SAFE_INTEGER && v <= 0; }
function isFiniteNumber(v) { return Number.isFinite(v); }
function isPositiveNumber(v) { return Number.isFinite(v) && v >= 0; }
function isNegativeNumber(v) { return Number.isFinite(v) && v <= 0; }
function isInteger(v) { return Number.isInteger(v) && v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER; }
function isPositiveInteger(v) { return Number.isInteger(v) && v >= 0 && v <= Number.MAX_SAFE_INTEGER; }
function isNegativeInteger(v) { return Number.isInteger(v) && v >= Number.MIN_SAFE_INTEGER && v <= 0; }
function isString(v) { return typeof v === "string"; }
function isNonEmptyString(v) { return typeof v === "string" && v.length > 0; }
function isLowerString(v) { return typeof v === "string" && v === v.toLowerCase(); }
function isNonEmptyLowerString(v) { return typeof v === "string" && v.length > 0 && v === v.toLowerCase(); }
function isUpperString(v) { return typeof v === "string" && v === v.toUpperCase(); }
function isNonEmptyUpperString(v) { return typeof v === "string" && v.length > 0 && v === v.toUpperCase(); }
function isLower(v) { return typeof v === "string" && R_LOWER.test(v); }
function isUpper(v) { return typeof v === "string" && R_UPPER.test(v); }
function isCamel(v) { return typeof v === "string" && R_CAMEL.test(v); }
function isPascal(v) { return typeof v === "string" && R_PASCAL.test(v); }
function isSnake(v) { return typeof v === "string" && R_SNAKE.test(v); }
function isScreaming(v) { return typeof v === "string" && R_SCREAMING.test(v); }
function isKebab(v) { return typeof v === "string" && R_KEBAB.test(v); }
function isTrain(v) { return typeof v === "string" && R_TRAIN.test(v); }
function isPrimitive(v) { return v === undefined || v === null || typeof v === "boolean" || typeof v === "string" || Number.isFinite(v); }
function isFunction(v) { return typeof v === "function"; }
function isObject(v) { return typeof v === "object" && v !== null; }
function isPlainObject(v) { return v instanceof Object && v.constructor === Object; }
function isNonEmptyPlainObject(v) { return v instanceof Object && v.constructor === Object && Object.keys(v).length > 0; }
function isIterable(v) { return typeof v === "object" && typeof v[Symbol.iterator] === "function"; }
function isPlainArray(v) { return v instanceof Array && v.constructor === Array; }
function isNonEmptyPlainArray(v) { return v instanceof Array && v.constructor === Array && v.length > 0; }
function isPlainObject(v) { return typeof v === "object" && v !== null && Object.getPrototypeOf(v).constructor === Object; }
function isIterable(v) { return typeof v === "object" && v !== null && typeof v[Symbol.iterator] === "function"; }
function isPlainArray(v) { return v instanceof Array && Object.getPrototypeOf(v).constructor === Array; }
function isArraylike(v) { return typeof v === "object" && v !== null && v.hasOwnProperty("length") && typeof v.length === "number" && Number.isInteger(v.length) && v.length >= 0 && v.length <= Number.MAX_SAFE_INTEGER; }

@@ -42,8 +54,6 @@ function isDate(v) { return v instanceof Date; }

function isPastDate(v) { return v instanceof Date && v.getTime() < Date.now(); }
function isMap(v) { return v instanceof Map && v.constructor === Map; }
function isNonEmptyMap(v) { return v instanceof Map && v.constructor === Map && v.size > 0; }
function isWeakMap(v) { return v instanceof WeakMap && v.constructor === WeakMap; }
function isSet(v) { return v instanceof Set && v.constructor === Set; }
function isNonEmptySet(v) { return v instanceof Set && v.constructor === Set && v.size > 0; }
function isWeakSet(v) { return v instanceof WeakSet && v.constructor === WeakSet; }
function isMap(v) { return v instanceof Map; }
function isWeakMap(v) { return v instanceof WeakMap; }
function isSet(v) { return v instanceof Set; }
function isWeakSet(v) { return v instanceof WeakSet; }
function isPromise(v) { return v instanceof Promise; }

@@ -55,32 +65,33 @@ function isRegExp(v) { return v instanceof RegExp; }

// Descriptions.
isNull.desc = "null"
isUndefined.desc = "undefined"
isDefined.desc = "defined"
isBoolean.desc = "true or false"
isTrue.desc = "true"
isFalse.desc = "false"
isTruthy.desc = "truthy"
isFalsy.desc = "falsy"
isZero.desc = "zero"
isOne.desc = "one"
isNaN.desc = "NaN"
isFiniteNumber.desc = "finite number"
isPositiveNumber.desc = "positive finite number"
isNegativeNumber.desc = "negative finite number"
isInteger.desc = "integer"
isPositiveInteger.desc = "positive integer"
isNegativeInteger.desc = "negative integer"
isString.desc = "string"
isNonEmptyString.desc = "non-empty string"
isLowerString.desc = "lowercase string"
isNonEmptyLowerString.desc = "non-empty lowercase string"
isUpperString.desc = "uppercase string"
isNonEmptyUpperString.desc = "non-empty uppercase string"
isNull.desc = "null";
isUndefined.desc = "undefined";
isDefined.desc = "defined";
isBoolean.desc = "true or false";
isTrue.desc = "true";
isFalse.desc = "false";
isTruthy.desc = "truthy";
isFalsy.desc = "falsy";
isZero.desc = "zero";
isOne.desc = "one";
isNaN.desc = "NaN";
isFiniteNumber.desc = "finite number";
isPositiveNumber.desc = "positive finite number";
isNegativeNumber.desc = "negative finite number";
isInteger.desc = "integer";
isPositiveInteger.desc = "positive integer";
isNegativeInteger.desc = "negative integer";
isString.desc = "string";
isLower.desc = "lowercase-only string";
isUpper.desc = "UPPERCASE-only string";
isCamel.desc = "camelCase string";
isPascal.desc = "PascalCase string";
isSnake.desc = "snake_case string";
isScreaming.desc = "SCREAMING_SNAKE_CASE string";
isKebab.desc = "kebab-case string";
isTrain.desc = "Camel-Kebab-Case string";
isFunction.desc = "function"
isObject.desc = "object"
isPlainObject.desc = "plain object"
isNonEmptyPlainObject.desc = "plain object with one or more enumerable properties"
isIterable.desc = "iterable object"
isPlainArray.desc = "plain array"
isNonEmptyPlainArray.desc = "plain non-empty array"
isArraylike.desc = "arraylike object with a numeric length property"

@@ -91,6 +102,4 @@ isDate.desc = "date"

isMap.desc = "map"
isNonEmptyMap.desc = "non-empty map"
isWeakMap.desc = "weak map"
isSet.desc = "set"
isNonEmptySet.desc = "non-empty set"
isWeakSet.desc = "weak set"

@@ -104,6 +113,14 @@ isPromise.desc = "promise"

const checkers = {
// Primitives.
"primitive": isPrimitive,
"null": isNull,
"undefined": isUndefined, "void": isUndefined, "undef": isUndefined,
"defined": isDefined, "def": isDefined,
"boolean": isBoolean, "bool": isBoolean,
"undefined": isUndefined,
"void": isUndefined,
"undef": isUndefined,
"defined": isDefined,
"def": isDefined,
// Booleans.
"boolean": isBoolean,
"bool": isBoolean,
"true": isTrue,

@@ -113,40 +130,63 @@ "false": isFalse,

"falsy": isFalsy,
// Numbers.
"zero": isZero,
"one": isOne,
"nan": isNaN,
"number": isFiniteNumber, "num": isFiniteNumber,
"number+": isPositiveNumber, "num+": isPositiveNumber,
"number-": isNegativeNumber, "num-": isNegativeNumber,
"integer": isInteger, "int": isInteger,
"integer+": isPositiveInteger, "int+": isPositiveInteger,
"integer-": isNegativeInteger, "int-": isNegativeInteger,
"string": isString, "str": isString,
"string+": isNonEmptyString, "str+": isNonEmptyString,
"lowercase": isLowerString, "lower": isLowerString,
"lowercase+": isNonEmptyLowerString, "lower+": isNonEmptyLowerString,
"uppercase": isUpperString, "upper": isUpperString,
"uppercase+": isNonEmptyUpperString, "upper+": isNonEmptyUpperString,
"function": isFunction, "func": isFunction,
"number": isFiniteNumber,
"num": isFiniteNumber,
"+number": isPositiveNumber,
"+num": isPositiveNumber,
"-number": isNegativeNumber,
"-num": isNegativeNumber,
"integer": isInteger,
"int": isInteger,
"+integer": isPositiveInteger,
"+int": isPositiveInteger,
"-integer": isNegativeInteger,
"-int": isNegativeInteger,
// Strings.
"string": isString,
"str": isString,
"lower": isLower,
"upper": isUpper,
"camel": isCamel,
"pascal": isPascal,
"snake": isSnake,
"screaming": isScreaming,
"kebab": isKebab,
"slug": isKebab,
"train": isTrain,
// Objects.
"function": isFunction,
"func": isFunction,
"objectlike": isObject,
"object": isPlainObject, "obj": isPlainObject,
"object+": isNonEmptyPlainObject, "obj+": isNonEmptyPlainObject,
"object": isPlainObject,
"obj": isPlainObject,
"iterable": isIterable,
"array": isPlainArray, "arr": isPlainArray,
"array+": isNonEmptyPlainArray, "arr+": isNonEmptyPlainArray,
"arraylike": isArraylike, "arguments": isArraylike, "args": isArraylike,
"circular": isCircular,
"array": isPlainArray,
"arr": isPlainArray,
"arraylike": isArraylike,
"arguments": isArraylike,
"args": isArraylike,
"date": isDate,
"date+": isFutureDate, "future": isFutureDate,
"date-": isPastDate, "past": isPastDate,
"future": isFutureDate,
"past": isPastDate,
"map": isMap,
"map+": isNonEmptyMap,
"weakmap": isWeakMap,
"set": isSet,
"set+": isNonEmptySet,
"weakset": isWeakSet,
"promise": isPromise,
"regex": isRegExp, "regexp": isRegExp,
"regex": isRegExp,
"regexp": isRegExp,
"symbol": isSymbol,
"any": isAny, "mixed": isAny,
"circular": isCircular,
// Other.
"any": isAny,
"mixed": isAny,
"json": isJSONable,
"jsonable": isJSONable,
};

@@ -153,0 +193,0 @@

@@ -26,3 +26,3 @@ /**

// Stop if not plain Array.
if (value.constructor !== Array) return false;
if (Object.getPrototypeOf(value).constructor !== Array) return false;

@@ -45,3 +45,3 @@ // Stop if circular reference.

// Only plain Objects.
if (value.constructor !== Object) return false;
if (Object.getPrototypeOf(value).constructor !== Object) return false;

@@ -48,0 +48,0 @@ // Stop if circular reference.

@@ -5,2 +5,3 @@ // @flow

const format = require("../functions/format");
const { CLASS, KEYS, VALUES } = require("../constants");

@@ -11,2 +12,3 @@ // Vars.

const R_INVERT = /^!/;
const R_NONEMPTY = /\+$/;
const R_OPTIONAL = /\?+$/;

@@ -82,3 +84,3 @@

// Not found.
throwError(BlorkError, "Checker not found", type, "arguments[0]");
throwError(BlorkError, "Checker not found", type, "checker(): type");
}

@@ -98,3 +100,3 @@

// Check args.
runChecker(this._checkers.string, prefix, "check(): value", BlorkError);
runChecker(this._checkers.string, prefix, "check(): prefix", BlorkError);

@@ -119,10 +121,8 @@ // Check the value against the type.

// Vars.
const l = types.length;
// Recurse into each type.
for (let i = 0; i < l; i++) this._check(args[i], types[i], `arguments[${i}]`, this._error);
types.forEach((t, i) => this._check(args[i], t, `arguments[${i}]`, this._error));
// No excess arguments.
if (args.length > l) throwError(this._error, `Too many arguments (expected ${l})`, args.length, "arguments");
if (args.length > types.length)
throwError(this._error, `Must have ${types.length} arguments`, args.length, "arguments");
}

@@ -136,3 +136,3 @@

*
* @param {string} name The type reference for the checker.
* @param {string} name The type reference for the checker in kebab-case format.
* @param {function} checker A checker function: Takes a single argument (value), tests it, and returns either true (success) or an error message string in the 'Must be X' format.

@@ -145,3 +145,3 @@ * @param {string} description="" A description of the type of value that's valid for this checker, e.g. "positive number" or "unique string". Defaults to same value as name.

// Check args.
runChecker(this._checkers["lower+"], name, "add(): name", BlorkError);
runChecker(this._checkers.kebab, name, "add(): name", BlorkError);
runChecker(this._checkers.function, checker, "add(): checker", BlorkError);

@@ -167,3 +167,3 @@ runChecker(this._checkers.string, description, "add(): description", BlorkError);

// Check args.
runChecker(this._checkers.function, error, "throws(): error", BlorkError, this);
runChecker(this._checkers.function, error, "throws(): error", BlorkError);

@@ -186,4 +186,4 @@ // Save err.

// Check args.
runChecker(this._checkers.objectlike, object, "props(): object", BlorkError, this);
runChecker(this._checkers["object+"], props, "props(): props", BlorkError, this);
runChecker(this._checkers.objectlike, object, "props(): object", BlorkError);
runChecker(this._checkers.object, props, "props(): props", BlorkError);

@@ -263,3 +263,2 @@ // Loop through every property in props.

* @param {string} type A single stringy type reference (e.g. 'str'), functional shorthand type reference (e.g. `String`), or an object/array with list of types (e.g. `{name:'str'}` or `['str', 'num']`).
* @param {Blorker} checkers An object listing checkers for a Blork isntance.
* @returns {function|undefined} The checker that was lazily created, or undefined if a checker could not be created.

@@ -271,3 +270,3 @@ */

// Split type and get corresponding checker for each.
const andCheckers = type.split(R_AND).map(this._find, this);
const ands = type.split(R_AND).map(this._find, this);

@@ -277,3 +276,3 @@ // Check each checker.

// Loop through and call each checker.
for (const checker of andCheckers) if (!checker(value)) return false; // Fail.
for (const checker of ands) if (!checker(value)) return false; // Fail.
return true; // Otherwise pass.

@@ -283,3 +282,3 @@ };

// Description message joins the descriptions for the checkers.
andChecker.desc = andCheckers.map(checker => checker.desc).join(" and ");
andChecker.desc = ands.map(checker => checker.desc).join(" and ");

@@ -294,3 +293,3 @@ // Add the AND checker to the list of checkers now it has been created.

// Split type and get corresponding checker for each.
const orCheckers = type.split(R_OR).map(this._find, this);
const ors = type.split(R_OR).map(this._find, this);

@@ -300,3 +299,3 @@ // Check each checker.

// Loop through and call each checker.
for (const checker of orCheckers) if (checker(value)) return true; // Pass.
for (const checker of ors) if (checker(value)) return true; // Pass.
return false; // Otherwise fail.

@@ -306,3 +305,3 @@ };

// Description message joins the descriptions for the checkers.
orChecker.desc = orCheckers.map(checker => checker.desc).join(" or ");
orChecker.desc = ors.map(checker => checker.desc).join(" or ");

@@ -317,10 +316,10 @@ // Add the OR checker to the list of checkers now it has been created.

// Find non optional checker (strip '!').
const normalChecker = this._find(type.replace(R_INVERT, ""));
const checker = this._find(type.replace(R_INVERT, ""));
// Create an optional checker for this optional type.
// Returns 0 if undefined, or passes through to the normal checker.
const invertedChecker = v => !normalChecker(v);
const invertedChecker = v => !checker(v);
// Description message joins the descriptions for the checkers.
invertedChecker.desc = `not ${normalChecker.desc}`;
invertedChecker.desc = `not ${checker.desc}`;

@@ -332,13 +331,41 @@ // Add the invertedChecker to the list and return it.

// Non-empty value (ends with '+'), e.g. "str+"
if (R_NONEMPTY.test(type)) {
// Find non optional checker (strip '+').
const checker = this._find(type.replace(R_NONEMPTY, ""));
// Create a length checker for this optional type.
// Returns true if checker passes and there's a numeric length or size property with a value of >0.
const lengthChecker = v => {
// Must pass the checker.
if (!checker(v)) return false;
// Map and Set use .size
if (v instanceof Map || v instanceof Set) return v.size > 0;
// String and Array use .length
if (typeof v === "string" || v instanceof Array) return v.length > 0;
// Objects use key length.
if (typeof v === "object" && v !== null) return Object.keys(v).length > 0;
// Everything else (numbers, booleans, null, undefined) do a falsy check.
return !!v;
};
// Description message joins the descriptions for the checkers.
lengthChecker.desc = `non-empty ${checker.desc}`;
// Add the lengthChecker to the list and return it.
this._checkers[type] = lengthChecker;
return lengthChecker;
}
// Optional value (ends with '?'), e.g. "num?"
if (R_OPTIONAL.test(type)) {
// Find non optional checker (strip '?').
const nonOptionalChecker = this._find(type.replace(R_OPTIONAL, ""));
const checker = this._find(type.replace(R_OPTIONAL, ""));
// Create an optional checker for this optional type.
// Returns 0 if undefined, or passes through to the normal checker.
const optionalChecker = v => (v === undefined ? true : nonOptionalChecker(v));
const optionalChecker = v => (v === undefined ? true : checker(v));
// Description message joins the descriptions for the checkers.
optionalChecker.desc = `${nonOptionalChecker.desc} or empty`;
optionalChecker.desc = `${checker.desc} or empty`;

@@ -410,3 +437,3 @@ // Add the optionalChecker to the list and return it.

if (stack.indexOf(type) >= 0)
throwError(BlorkError, "Blork type must not contain circular references", type, prefix);
throwError(BlorkError, "Blork type must not contain circular references", type);

@@ -417,4 +444,3 @@ // Value can have circular references, but don't keep checking it over and over.

// Push type and value into the stack.
stack.push(type);
stack.push(value);
stack = [...stack, type, value];
} else {

@@ -429,17 +455,11 @@ // First loop. Start a stack.

// Loop through items and check they match type[0]
const length = value.length;
for (let i = 0; i < length; i++) this._check(value[i], type[0], `${prefix}[${i}]`, error, stack);
value.forEach((v, i) => this._check(v, type[0], `${prefix}[${i}]`, error, stack));
} else {
// Tuple array
// Loop through types and match each with a value recursively.
const length = type.length;
for (let i = 0; i < length; i++) this._check(value[i], type[i], `${prefix}[${i}]`, error, stack);
type.forEach((t, i) => this._check(value[i], t, `${prefix}[${i}]`, error, stack));
// No excess items in a tuple.
if (value.length > length) throwError(error, `Must have ${length} items`, value.length, prefix);
if (value.length > type.length) throwError(error, `Must have ${type.length} items`, value.length, prefix);
}
// Pass.
stack.pop();
stack.pop();
}

@@ -460,4 +480,11 @@

_checkObject(value, type, prefix, error, stack) {
// Value itself must be an object (check using the object checker).
runChecker(this._checkers.object, value, prefix, error);
// CLASS type key.
const objectType = type[CLASS];
if (objectType) {
// Must be plain checker.
this._check(value, objectType, prefix, error, stack);
} else {
// Must be plain checker.
runChecker(this._checkers.object, value, prefix, error);
}

@@ -468,3 +495,3 @@ // Prevent infinite loops.

if (stack.indexOf(type) >= 0)
throwError(BlorkError, "Blork type must not contain circular references", type, prefix);
throwError(BlorkError, "Blork type must not contain circular references", type);

@@ -475,4 +502,3 @@ // Value can have circular references, but don't keep checking it over and over.

// Push type and value into the stack.
stack.push(type);
stack.push(value);
stack = [...stack, type, value];
} else {

@@ -487,30 +513,30 @@ // First loop. Kick off a stack.

// Loop through each item in the types object.
for (let i = 0; i < typeKeys.length; i++) {
const key = typeKeys[i];
// Ignore the ANY key.
if (key !== "_any") this._check(value[key], type[key], `${prefix}.${key}`, error, stack);
}
typeKeys.forEach(key => {
// Check that the value matches the specified key.
this._check(value[key], type[key], `${prefix}.${key}`, error, stack);
});
// Is there an ANY key?
if (type.hasOwnProperty("_any")) {
// Get the KEYS and VALUES types.
// KEYS is used to check all keys against a checker, e.g. checking that keys are camelCase.
// VALUES is used to check that excess values on the object match a specified type.
const keysType = type[KEYS];
const valuesType = type[VALUES];
// Is there an KEYS or VALUES type?
if (keysType || valuesType) {
// Vars.
const valueKeys = Object.keys(value);
// Check that we actually need to do this loop by comparing the lengths.
// -1 subtracts the _any key itself.
if (valueKeys.length > typeKeys.length - 1) {
// Make a list of the excess keys (that are in valueKeys but not in typeKeys).
const excessKeys = valueKeys.filter(v => !~typeKeys.indexOf(v));
// Check that we ACTUALLY need to do this loop by comparing the lengths.
if (valueKeys.length > typeKeys.length) {
// Loop through excess keys (that are in valueKeys but not in typeKeys).
valueKeys.filter(v => !~typeKeys.indexOf(v)).forEach(key => {
// If there's a KEYS type, check the key against that.
if (keysType) this._check(key, keysType, `${prefix}.${key}: Key`, error, stack);
// Loop through all excess keys and check against the ANY key.
for (let i = 0; i < excessKeys.length; i++) {
const key = excessKeys[i];
this._check(value[key], type._any, `${prefix}.${key}`, error, stack);
}
// Check the value against the VALUES type.
if (valuesType) this._check(value[key], valuesType, `${prefix}.${key}`, error, stack);
});
}
}
// Pass.
stack.pop();
stack.pop();
}

@@ -517,0 +543,0 @@ }

@@ -6,2 +6,3 @@ const ValueError = require("./errors/ValueError");

const format = require("./functions/format");
const { CLASS, KEYS, VALUES } = require("./constants");

@@ -23,1 +24,4 @@ // Make a default instance.

exports.ValueError = ValueError;
exports.CLASS = CLASS;
exports.KEYS = KEYS;
exports.VALUES = VALUES;
{
"name": "blork",
"description": "Blork! Mini runtime type checking in Javascript",
"version": "6.0.0",
"version": "7.0.0",
"license": "0BSD",

@@ -6,0 +6,0 @@ "author": "Dave Houlbrooke <dave@shax.com>",

@@ -167,6 +167,6 @@ # Blork! Mini runtime type checking in Javascript

1. `name` The name of the custom checker you'll use to reference it later
1. `name` The name of the custom checker (only kebab-case strings allowed).
2. `checker` A function that accepts a single argument, `value`, and returns `true` or `false`.
3. `description=""` An description for the value the checker will accept, e.g. "lowercase string" or "unique username", that is shown in the error message. Defaults to the value of `name`.
4. `error=undefined` A custom class that is thrown when this checker fails (can be _any_ class, not just classes extending `Error`). An error set with `add() takes precedence for this checker over the error set through `throws()`.
4. `error=undefined` A custom class that is thrown when this checker fails (can be [VALUES]_ class, not just classes extending `Error`). An error set with `add() takes precedence for this checker over the error set through `throws()`.

@@ -179,3 +179,3 @@ ```js

// Name of checker.
"catty",
"catty",
// Checker to validate a string containing "cat".

@@ -224,3 +224,3 @@ (v) => typeof v === "string" && v.strToLower().indexOf("cat") >= 0,

To change the error object Blork throws when a type doesn't match, use the `throws()` function. It accepts a single argument a custom class (can be _any_ class, not just classes extending `Error`).
To change the error object Blork throws when a type doesn't match, use the `throws()` function. It accepts a single argument a custom class (can be [VALUES]_ class, not just classes extending `Error`).

@@ -264,3 +264,3 @@ ```js

The `props()` function can define an object properties (like `Object.defineProperties()`) that are readable and writable, BUT the value must always match the type it was initially defined with.
The `props()` function can define an object properties (like `Object.defineProperties()`) that are readable and writable, BUT the value must always match the type it was initially defined with.

@@ -307,58 +307,75 @@ This allows you to create objects with properties that have a guaranteed type. This makes your object more robust and removes the need to check the type of the property before using it.

## Types
## Type reference
### String types
This section lists all types that are available in Blork. A number of different formats can be used for types:
Types are generally accessed via a string reference. This list shows all Blork built-in checkers:
- **String types** (e.g. `"promise"` and `"integer"`)
- **String modifiers** that modify those string types (e.g. `"?"` and `"!"`)
- **Constant** and **constructor** shorthand types (e.g. `null` and `String`)
- **Object** and **Array** literal types (e.g. `{}` and `[]`)
| Type string reference | Description
|--------------------------------------|-------------------
| `null` | Value is **null**
| `undefined`, `undef`, `void` | Value is **undefined**
| `defined`, `def` | Value is **not undefined**
| `boolean`, `bool` | Value is **true** or **false**
| `true` | Value is **true**
| `false` | Value is **false**
| `truthy` | Any truthy values (i.e. **== true**)
| `falsy` | Any falsy values (i.e. **== false**)
| `zero` | Value is **0**
| `one` | Value is **1**
| `nan` | Value is **NaN**
| `number`, `num` | Numbers excluding NaN/Infinity (using **typeof** and finite check)
| `number+`, `num+`, | Numbers more than or equal to zero
| `number-`, `num-` | Numbers less than or equal to zero
| `integer`, `int` | Integers (using **Number.isInteger()**)
| `integer+`, `int+` | Positive integers including zero
| `integer-`, `int-` | Negative integers including zero
| `string`, `str` | Strings (using **typeof**)
| `string+`, `str+` | Non-empty strings (using **str.length**)
| `lowercase`, `lower` | Strings with no uppercase characters
| `lowercase+`, `lower+` | Non-empty strings with no uppercase characters
| `uppercase`, `upper` | Strings with no lowercase characters
| `uppercase+`, `upper+` | Non-empty strings with no lowercase characters
| `function`, `func` | Functions (using **instanceof Function**)
| `object`, `obj` | Plain objects (using **instanceof Object** and constructor check)
| `object+`, `obj+` | Plain objects with one or more properties (using **Object.keys().length**)
| `objectlike` | Any object-like object (using **instanceof Object**)
| `iterable` | Objects with a **Symbol.iterator** method (that can be used with **for..of** loops)
| `circular` | Objects with one or more _circular references_ (use `!circular` to disallow circular references)
| `array`, `arr` | Plain instances of Array (using **instanceof Array** and constructor check)
| `array+`, `arr+` | Plain instances of **Array** with one or more items
| `arraylike` | Any object, not just arrays, with numeric **.length** property
| `arguments`, `args` | Arguments objects (any object, not just arrays, with numeric **.length** property)
| `map` | Instances of **Map**
| `map+` | Instances of **Map** with one or more items
| `weakmap` | Instances of **WeakMap**
| `set` | Instances of **Set**
| `set+` | Instances of **Set** with one or more items
| `weakset` | Instances of **WeakSet**
| `promise` | Instances of **Promise**
| `date` | Instances of **Date**
| `date+`, `future` | Instances of **Date** with a value in the future
| `date-`, `past` | Instances of **Date** with a value in the past
| `regex`, `regexp` | Instances of **RegExp** (regular expressions)
| `symbol` | Value is **Symbol** (using **typeof**)
| `any`, `mixed` | Allow any value (transparently passes through with no error)
| `json`, `jsonable` | **JSON-friendly** values (null, true, false, finite numbers, strings, plain objects, plain arrays)
### String types: primitives
| `primitive` | Any **primitive** value (undefined, null, booleans, strings, finite numbers)
| `null` | Value is **null**
| `undefined`, `undef`, `void` | Value is **undefined**
| `defined`, `def` | Value is **not undefined**
### String types: booleans
| `boolean`, `bool` | Value is **true** or **false**
| `true` | Value is **true**
| `false` | Value is **false**
| `truthy` | Any truthy values (i.e. **== true**)
| `falsy` | Any falsy values (i.e. **== false**)
### String types: numbers
| `zero` | Value is **0**
| `one` | Value is **1**
| `nan` | Value is **NaN**
| `number`, `num` | Any numbers except NaN/Infinity (using **Number.isFinite()**)
| `+number`, `+num`, | Numbers more than or equal to zero
| `-number`, `-num` | Numbers less than or equal to zero
| `integer`, `int` | Integers (using **Number.isInteger()**)
| `+integer`, `+int` | Positive integers including zero
| `-integer`, `-int` | Negative integers including zero
### String types: strings
| `string`, `str` | Any strings (using **typeof**)
| `lower` | lowercase string (non-empty and alphanumeric only)
| `upper` | UPPERCASE strings (non-empty and alphanumeric only)
| `camel` | camelCase strings e.g. variable/function names (non-empty alphanumeric with lowercase first letter)
| `pascal` | PascalCase strings e.g. class names (non-empty alphanumeric with uppercase first letter)
| `snake` | snake_case strings (non-empty alphanumeric lowercase)
| `screaming` | SCREAMING_SNAKE_CASE strings e.g. environment vars (non-empty uppercase alphanumeric)
| `kebab`, `slug` | kebab-case strings e.g. URL slugs (non-empty alphanumeric lowercase)
| `train` | Train-Case strings e.g. HTTP-Headers (non-empty with uppercase first letters)
### String types: objects
| `function`, `func` | Functions (using **instanceof Function**)
| `object`, `obj` | Plain objects (using **typeof && !null** and constructor check)
| `objectlike` | Any object-like object (using **typeof && !null**)
| `iterable` | Objects with a **Symbol.iterator** method (that can be used with **for..of** loops)
| `circular` | Objects with one or more _circular references_ (use `!circular` to disallow circular references)
| `array`, `arr` | Plain arrays (using **instanceof Array** and constructor check)
| `arraylike`, `arguments`, `args` | Array-like objects, e.g. **arguments** (any object with numeric **.length** property, not just arrays)
| `map` | Instances of **Map**
| `weakmap` | Instances of **WeakMap**
| `set` | Instances of **Set**
| `weakset` | Instances of **WeakSet**
| `promise` | Instances of **Promise**
| `date` | Instances of **Date**
| `future` | Instances of **Date** with a value in the future
| `past` | Instances of **Date** with a value in the past
| `regex`, `regexp` | Instances of **RegExp** (regular expressions)
| `symbol` | Value is **Symbol** (using **typeof**)
### String types: other
| `any`, `mixed` | Allow any value (transparently passes through with no error)
| `json`, `jsonable` | **JSON-friendly** values (null, true, false, finite numbers, strings, plain objects, plain arrays)
```js

@@ -379,3 +396,3 @@ // Pass.

### Optional string types
### String modifiers: Optional types

@@ -396,4 +413,20 @@ Any string type can be made optional by appending a `?` question mark to the type reference. This means the check will also accept `undefined` in addition to the specified type.

### Inverted string types
### String modifiers: Non-empty types
Any type can be made non-empty by appending a `+` plus sign to the type reference. This means the check will only pass if the value is non-empty and has length.
```js
// Pass.
check("abc", "str+"); // No error.
check([1], "arr+"); // No error.
check({ a: 1 }, "obj+"); // No error.
// Fail.
check(123, "str+"); // Throws ValueError "Must be non-empty string (received "")"
check([], "arr+"); // Throws ValueError "Must be non-empty plain array (received [])"
check({}, "obj+"); // Throws ValueError "Must be non-empty plain object (received {})"
```
### String modifiers: Inverted types
Any string type can be made optional by prepending a `!` question mark to the type reference. This means the check will only pass if the _inverse_ of its type is true.

@@ -414,3 +447,3 @@

### Combined string types
### String modifiers: Combined types

@@ -434,3 +467,4 @@ You can use `&` and `|` to join string types together, to form AND and OR chains of allowed types. This allows you to compose together more complex types like `number | string` or `date | number | null` or `string && custom-checker`

```js
add("catty", v => v.toLowerCase().indexOf("cat")); // Checks that cat
// Add a checker that confirms a string contains the word "cat"
add("catty", v => v.toLowerCase().indexOf("cat") >= 0);

@@ -466,3 +500,3 @@ // Pass.

You can pass in _any_ class name, and Blork will check the value using `instanceof` and generate a corresponding error message if the type doesn't match.
You can pass in [VALUES]_ class name, and Blork will check the value using `instanceof` and generate a corresponding error message if the type doesn't match.

@@ -508,27 +542,93 @@ Using `Object` and `Array` constructors will work also and will allow any object that is `instanceof Object` or `instanceof Array`. _Note: this is not the same as e.g. the `'object'` and `'array'` string types, which only allow plain objects an arrays (but will reject objects of custom classes extending `Object` or `Array`)._

### Object literal type (with additional properties)
### Object literal type: additional values
To check that the type of **any** properties conform to a single type, use an `_any` key. This allows you to check objects that don't have known keys (e.g. from user generated data). This is similar to how indexer keys work in Flow or Typescript.
To check that the type of **any** properties conform to a single type, use the `VALUES` symbol and create a `[VALUES]` key. This allows you to check objects that don't have known keys (e.g. from user generated data). This is similar to how indexer keys work in Flow or Typescript.
```js
import { check } from "blork";
import { check, VALUES } from "blork";
// Pass.
check({ a: 1, b: 2, c: 3 }, { _any: "num" }); // No error.
check({ name: "Dan", a: 1, b: 2, c: 3 }, { name: "str", _any: "num" }); // No error.
check(
{ a: 1, b: 2, c: 3 },
{ [VALUES]: "num" }
); // No error.
check(
{ name: "Dan", a: 1, b: 2, c: 3 },
{ name: "str", [VALUES]: "num" }
); // No error.
// Fail.
check({ a: 1, b: 2, c: "abc" }, { _any: "num" }); // Throws ValueError "c: Must be number (received "abc")"
check(
{ a: 1, b: 2, c: "abc" },
{ [VALUES]: "num" }
); // Throws ValueError "c: Must be number..."
check(
{ name: "Dan", a: 1, b: 2, c: 3 },
{ name: "str", [VALUES]: "bool" }
); // Throws ValueError "a: Must be boolean..."
```
If you wish you can use this functionality with the `undefined` type to ensure objects **do not** contain additional properties (object literal types by default are allowed to contain additional properties).
You can use this functionality with the `undefined` type to ensure objects **do not** contain additional properties (object literal types by default are allowed to contain additional properties).
```js
// Pass.
check({ name: "Carl" }, { name: "str", _any: "undefined" }); // No error.
check(
{ name: "Carl" },
{ name: "str", [VALUES]: "undefined" }
); // No error.
// Fail.
check({ name: "Jess", another: 28 }, { name: "str", _any: "undefined" }); // Throws ValueError "another: Must be undefined (received 28)"
check(
{ name: "Jess", another: 28 },
{ name: "str", [VALUES]: "undefined" }
); // Throws ValueError "another: Must be undefined..."
```
### Object literal type: additional keys
To check that the keys of any additional properties conform to a single type, use the `KEYS` symbol and create a `[KEYS]` key. This allows you to ensure that keys conform to a specific string type, e.g. **camelCase**, **kebab-case** or **UPPERCASE** (see string types above).
```js
import { check, VALUES } from "blork";
// Pass.
check({ MYVAL: 1 }, { [KEYS]: "upper" }); // UPPERCASE keys — no error.
check({ myVal: 1 }, { [KEYS]: "camel" }); // camelCase keys — no error.
check({ MyVal: 1 }, { [KEYS]: "pascal" }); // PascalCase keys — no error.
check({ my-val: 1 }, { [KEYS]: "kebab" }); // kebab-case keys — no error.
// Fail.
check({ MYVAL: 1 }, { [KEYS]: "upper" }); // UPPERCASE keys — no error.
check({ myVal: 1 }, { [KEYS]: "camel" }); // camelCase keys — no error.
check({ MyVal: 1 }, { [KEYS]: "pascal" }); // PascalCase keys — no error.
check({ my-val: 1 }, { [KEYS]: "kebab" }); // kebab-case keys — no error.
```
### Object literal type: custom constructor
Normally object literal types check that the object is a **plain object**. If you wish to allow the object to be a different object, use the `CLASS` symbol and create a `[CLASS]` key.
```js
import { check, CLASS } from "blork";
// Make a fancy new class.
class MyClass {
constructor () {
this.num = 123;
}
}
// Pass.
check(
new MyClass,
{ num: 123, [CLASS]: MyClass }
); // No error.
// Fail.
check(
{ num: 123, },
{ num: 123, [CLASS]: MyClass }
); // Throws ValueError "Must be instance of MyClass..."
```
### Array literal type

@@ -574,2 +674,13 @@

- 7.0.0
- Add `VALUES`, `KEYS`, and `CLASS` symbol constants
- Remove `_any` key and use `VALUES` to provide the same functionality
- Add `KEYS` functionality to check type or case of object keys, e.g. camelCase or kebab-case
- Add `CLASS` functionality to check the class of an object
- Add string case checkers for e.g. variable names (kebab-case, camelCase, snake_case etc)
- `upper` and `lower` checkers work differently (all characters must be UPPERCASE/lowercase)
- Rename `int+`, `int-` checkers to `+int` and `-int`
- Add '+' modifier to check for non-empty values with any checker
- Remove hardcoded '+' checkers like `lower+`, `object+`
- Remove `uppercase` and `lowercase` checkers for consistency
- 6.0.0

@@ -580,3 +691,3 @@ - Remove `prop()` function and add `props()` function instead (`prop()` was impossible to type with Flow)

- 5.0.0
- Change from symbol `[ANY]` key to `_any` key for indexer property (for convenience and better Flow compatibility)
- Change from symbol `[ANY]` key to `[VALUES]` key for indexer property (for convenience and better Flow compatibility)
- 4.5.0

@@ -583,0 +694,0 @@ - Add `checker()` function to return the boolean checker function itself.

@@ -11,189 +11,187 @@ const checkers = require("../lib/checkers");

test("Every checker passes correctly", () => {
expect.assertions(Object.keys(checkers).length);
// Mock check() so we can check we tested everything.
const called = [];
function mockCheck(v, k) {
called.push(k);
return check(v, k);
}
// Primatives.
expect(check(null, "null")).toBe(undefined);
expect(check(undefined, "undefined")).toBe(undefined);
expect(check(undefined, "void")).toBe(undefined);
expect(check(undefined, "undef")).toBe(undefined);
expect(check(true, "defined")).toBe(undefined);
expect(check(true, "def")).toBe(undefined);
expect(check(true, "boolean")).toBe(undefined);
expect(check(true, "bool")).toBe(undefined);
expect(check(true, "true")).toBe(undefined);
expect(check(false, "false")).toBe(undefined);
expect(check(true, "truthy")).toBe(undefined);
expect(check(false, "falsy")).toBe(undefined);
// Primitives.
expect(mockCheck(null, "primitive")).toBe(undefined);
expect(mockCheck(null, "null")).toBe(undefined);
expect(mockCheck(undefined, "undefined")).toBe(undefined);
expect(mockCheck(undefined, "void")).toBe(undefined);
expect(mockCheck(undefined, "undef")).toBe(undefined);
expect(mockCheck(true, "defined")).toBe(undefined);
expect(mockCheck(true, "def")).toBe(undefined);
// Booleans.
expect(mockCheck(true, "boolean")).toBe(undefined);
expect(mockCheck(true, "bool")).toBe(undefined);
expect(mockCheck(true, "true")).toBe(undefined);
expect(mockCheck(false, "false")).toBe(undefined);
expect(mockCheck(true, "truthy")).toBe(undefined);
expect(mockCheck(false, "falsy")).toBe(undefined);
// Numbers.
expect(check(0, "zero")).toBe(undefined);
expect(check(1, "one")).toBe(undefined);
expect(check(NaN, "nan")).toBe(undefined);
expect(check(1.5, "number")).toBe(undefined);
expect(check(1.5, "num")).toBe(undefined);
expect(check(1.5, "number+")).toBe(undefined);
expect(check(0.5, "num+")).toBe(undefined);
expect(check(-1.5, "number-")).toBe(undefined);
expect(check(-1.5, "num-")).toBe(undefined);
expect(check(1, "integer")).toBe(undefined);
expect(check(1, "int")).toBe(undefined);
expect(check(1, "integer+")).toBe(undefined);
expect(check(1, "int+")).toBe(undefined);
expect(check(-1, "integer-")).toBe(undefined);
expect(check(-1, "int-")).toBe(undefined);
expect(mockCheck(0, "zero")).toBe(undefined);
expect(mockCheck(1, "one")).toBe(undefined);
expect(mockCheck(NaN, "nan")).toBe(undefined);
expect(mockCheck(1.5, "number")).toBe(undefined);
expect(mockCheck(1.5, "num")).toBe(undefined);
expect(mockCheck(1.5, "+number")).toBe(undefined);
expect(mockCheck(0.5, "+num")).toBe(undefined);
expect(mockCheck(-1.5, "-number")).toBe(undefined);
expect(mockCheck(-1.5, "-num")).toBe(undefined);
expect(mockCheck(1, "integer")).toBe(undefined);
expect(mockCheck(1, "int")).toBe(undefined);
expect(mockCheck(1, "+integer")).toBe(undefined);
expect(mockCheck(1, "+int")).toBe(undefined);
expect(mockCheck(-1, "-integer")).toBe(undefined);
expect(mockCheck(-1, "-int")).toBe(undefined);
// Strings.
expect(check("a", "string")).toBe(undefined);
expect(check("a", "str")).toBe(undefined);
expect(check("a", "string+")).toBe(undefined);
expect(check("a", "str+")).toBe(undefined);
expect(check("a", "lowercase")).toBe(undefined);
expect(check("a", "lower")).toBe(undefined);
expect(check("a", "lowercase+")).toBe(undefined);
expect(check("a", "lower+")).toBe(undefined);
expect(check("A", "uppercase")).toBe(undefined);
expect(check("A", "upper")).toBe(undefined);
expect(check("A", "uppercase+")).toBe(undefined);
expect(check("A", "upper+")).toBe(undefined);
expect(mockCheck("a", "string")).toBe(undefined);
expect(mockCheck("a", "str")).toBe(undefined);
expect(mockCheck("myvar", "lower")).toBe(undefined);
expect(mockCheck("MYVAR", "upper")).toBe(undefined);
expect(mockCheck("myVar", "camel")).toBe(undefined);
expect(mockCheck("MyVar", "pascal")).toBe(undefined);
expect(mockCheck("my_var", "snake")).toBe(undefined);
expect(mockCheck("MY_VAR", "screaming")).toBe(undefined);
expect(mockCheck("my-var", "kebab")).toBe(undefined);
expect(mockCheck("my-var", "slug")).toBe(undefined);
expect(mockCheck("My-Var", "train")).toBe(undefined);
// Functions.
expect(check(function() {}, "function")).toBe(undefined);
expect(check(function() {}, "func")).toBe(undefined);
// Objects.
expect(check({}, "object")).toBe(undefined);
expect(check({ a: 1 }, "obj")).toBe(undefined);
expect(check({ a: 1 }, "object+")).toBe(undefined);
expect(check({ a: 1 }, "obj+")).toBe(undefined);
expect(check({}, "objectlike")).toBe(undefined);
expect(check({ [Symbol.iterator]: () => {} }, "iterable")).toBe(undefined);
expect(mockCheck(function() {}, "function")).toBe(undefined);
expect(mockCheck(function() {}, "func")).toBe(undefined);
expect(mockCheck({}, "object")).toBe(undefined);
expect(mockCheck({ a: 1 }, "obj")).toBe(undefined);
expect(mockCheck({}, "objectlike")).toBe(undefined);
expect(mockCheck({ [Symbol.iterator]: () => {} }, "iterable")).toBe(undefined);
expect(mockCheck(circular, "circular")).toBe(undefined);
expect(mockCheck([], "array")).toBe(undefined);
expect(mockCheck([], "arr")).toBe(undefined);
expect(mockCheck({ "0": "abc", length: 1 }, "arraylike")).toBe(undefined);
expect(mockCheck(arguments, "arguments")).toBe(undefined);
expect(mockCheck(arguments, "args")).toBe(undefined);
expect(mockCheck(new Date(), "date")).toBe(undefined);
expect(mockCheck(new Date(2080, 0, 1), "future")).toBe(undefined);
expect(mockCheck(new Date(1980, 0, 1), "past")).toBe(undefined);
expect(mockCheck(new Map(), "map")).toBe(undefined);
expect(mockCheck(new WeakMap(), "weakmap")).toBe(undefined);
expect(mockCheck(new Set(), "set")).toBe(undefined);
expect(mockCheck(new WeakSet(), "weakset")).toBe(undefined);
expect(mockCheck(Promise.resolve(), "promise")).toBe(undefined);
expect(mockCheck(/[abc]+/g, "regexp")).toBe(undefined);
expect(mockCheck(/[abc]+/g, "regex")).toBe(undefined);
expect(mockCheck(Symbol(), "symbol")).toBe(undefined);
// Arrays.
expect(check([], "array")).toBe(undefined);
expect(check([], "arr")).toBe(undefined);
expect(check([1], "array+")).toBe(undefined);
expect(check([1], "arr+")).toBe(undefined);
expect(check({ "0": "abc", length: 1 }, "arraylike")).toBe(undefined);
expect(check(arguments, "arguments")).toBe(undefined);
expect(check(arguments, "args")).toBe(undefined);
// Dates.
expect(check(new Date(), "date")).toBe(undefined);
expect(check(new Date(2080, 0, 1), "date+")).toBe(undefined);
expect(check(new Date(2080, 0, 1), "future")).toBe(undefined);
expect(check(new Date(1980, 0, 1), "date-")).toBe(undefined);
expect(check(new Date(1980, 0, 1), "past")).toBe(undefined);
// Other.
expect(check(new Map(), "map")).toBe(undefined);
expect(check(new Map([[1, 1]]), "map+")).toBe(undefined);
expect(check(new WeakMap(), "weakmap")).toBe(undefined);
expect(check(new Set(), "set")).toBe(undefined);
expect(check(new Set([1]), "set+")).toBe(undefined);
expect(check(new WeakSet(), "weakset")).toBe(undefined);
expect(check(Promise.resolve(), "promise")).toBe(undefined);
expect(check(/[abc]+/g, "regexp")).toBe(undefined);
expect(check(/[abc]+/g, "regex")).toBe(undefined);
expect(check(Symbol(), "symbol")).toBe(undefined);
expect(check(false, "any")).toBe(undefined);
expect(check("abc", "mixed")).toBe(undefined);
expect(mockCheck(false, "any")).toBe(undefined);
expect(mockCheck("abc", "mixed")).toBe(undefined);
expect(mockCheck({ num: 123, str: "abc" }, "json")).toBe(undefined);
expect(mockCheck({ num: 123, str: "abc" }, "jsonable")).toBe(undefined);
// Advanced.
expect(check(circular, "circular")).toBe(undefined);
expect(check({ num: 123, str: "abc" }, "json")).toBe(undefined);
// Check we called every checker.
// Done in this awkward way so we get an error that helps us find the one we're missing.
const checkerNames = Object.keys(checkers);
checkerNames.forEach(name => expect(called).toContain(name));
called.forEach(name => expect(checkerNames).toContain(name));
expect(called.length).toBe(checkerNames.length);
});
test("Every named type fails correctly", () => {
expect.assertions(Object.keys(checkers).length);
// Mock check() so we can check we tested everything.
const called = [];
function mockCheck(v, k) {
called.push(k);
return check(v, k);
}
// Primatives..
expect(() => check(0, "null")).toThrow(TypeError);
expect(() => check(null, "undefined")).toThrow(TypeError);
expect(() => check(null, "void")).toThrow(TypeError);
expect(() => check(null, "undef")).toThrow(TypeError);
expect(() => check(undefined, "defined")).toThrow(TypeError);
expect(() => check(undefined, "def")).toThrow(TypeError);
expect(() => check(9, "boolean")).toThrow(TypeError);
expect(() => check(9, "bool")).toThrow(TypeError);
expect(() => check(1, "true")).toThrow(TypeError);
expect(() => check(9, "false")).toThrow(TypeError);
expect(() => check(0, "truthy")).toThrow(TypeError);
expect(() => check(1, "falsy")).toThrow(TypeError);
expect(() => mockCheck(Symbol(), "primitive")).toThrow(TypeError);
expect(() => mockCheck(0, "null")).toThrow(TypeError);
expect(() => mockCheck(null, "undefined")).toThrow(TypeError);
expect(() => mockCheck(null, "void")).toThrow(TypeError);
expect(() => mockCheck(null, "undef")).toThrow(TypeError);
expect(() => mockCheck(undefined, "defined")).toThrow(TypeError);
expect(() => mockCheck(undefined, "def")).toThrow(TypeError);
// Booleans.
expect(() => mockCheck(9, "boolean")).toThrow(TypeError);
expect(() => mockCheck(9, "bool")).toThrow(TypeError);
expect(() => mockCheck(1, "true")).toThrow(TypeError);
expect(() => mockCheck(9, "false")).toThrow(TypeError);
expect(() => mockCheck(0, "truthy")).toThrow(TypeError);
expect(() => mockCheck(1, "falsy")).toThrow(TypeError);
// Numbers.
expect(() => check(1, "zero")).toThrow(TypeError);
expect(() => check(0, "one")).toThrow(TypeError);
expect(() => check(1, "nan")).toThrow(TypeError);
expect(() => check("1", "number")).toThrow(TypeError);
expect(() => check("1", "num")).toThrow(TypeError);
expect(() => check(-1, "number+")).toThrow(TypeError);
expect(() => check(-1, "num+")).toThrow(TypeError);
expect(() => check(1, "number-")).toThrow(TypeError);
expect(() => check(1, "num-")).toThrow(TypeError);
expect(() => check(1.5, "integer")).toThrow(TypeError);
expect(() => check(1.5, "int")).toThrow(TypeError);
expect(() => check(1.5, "integer+")).toThrow(TypeError);
expect(() => check(2.5, "int+")).toThrow(TypeError);
expect(() => check(-1.5, "integer-")).toThrow(TypeError);
expect(() => check(-2.5, "int-")).toThrow(TypeError);
expect(() => mockCheck(1, "zero")).toThrow(TypeError);
expect(() => mockCheck(0, "one")).toThrow(TypeError);
expect(() => mockCheck(1, "nan")).toThrow(TypeError);
expect(() => mockCheck("1", "number")).toThrow(TypeError);
expect(() => mockCheck("1", "num")).toThrow(TypeError);
expect(() => mockCheck("1", "+number")).toThrow(TypeError);
expect(() => mockCheck("1", "+num")).toThrow(TypeError);
expect(() => mockCheck("1", "-number")).toThrow(TypeError);
expect(() => mockCheck("1", "-num")).toThrow(TypeError);
expect(() => mockCheck(1.5, "integer")).toThrow(TypeError);
expect(() => mockCheck(1.5, "int")).toThrow(TypeError);
expect(() => mockCheck(-1, "+integer")).toThrow(TypeError);
expect(() => mockCheck(-1, "+int")).toThrow(TypeError);
expect(() => mockCheck(1, "-integer")).toThrow(TypeError);
expect(() => mockCheck(1, "-int")).toThrow(TypeError);
// Strings.
expect(() => check(1, "string")).toThrow(TypeError);
expect(() => check(1, "str")).toThrow(TypeError);
expect(() => check("", "string+")).toThrow(TypeError);
expect(() => check("", "str+")).toThrow(TypeError);
expect(() => check("A", "lowercase")).toThrow(TypeError);
expect(() => check("A", "lower")).toThrow(TypeError);
expect(() => check("A", "lowercase+")).toThrow(TypeError);
expect(() => check("A", "lower+")).toThrow(TypeError);
expect(() => check("a", "uppercase")).toThrow(TypeError);
expect(() => check("a", "upper")).toThrow(TypeError);
expect(() => check("a", "uppercase+")).toThrow(TypeError);
expect(() => check("a", "upper+")).toThrow(TypeError);
expect(() => mockCheck(1, "string")).toThrow(TypeError);
expect(() => mockCheck(1, "str")).toThrow(TypeError);
expect(() => mockCheck("A", "lower")).toThrow(TypeError);
expect(() => mockCheck("a", "upper")).toThrow(TypeError);
expect(() => mockCheck("my-var", "camel")).toThrow(TypeError);
expect(() => mockCheck("my-var", "pascal")).toThrow(TypeError);
expect(() => mockCheck("MY_VAR", "snake")).toThrow(TypeError);
expect(() => mockCheck("MY-VAR", "screaming")).toThrow(TypeError);
expect(() => mockCheck("MY-VAR", "kebab")).toThrow(TypeError);
expect(() => mockCheck("my-VAR", "slug")).toThrow(TypeError);
expect(() => mockCheck("my-var", "train")).toThrow(TypeError);
// Functions.
expect(() => check({}, "function")).toThrow(TypeError);
expect(() => check({}, "func")).toThrow(TypeError);
// Objects.
expect(() => check(1, "object")).toThrow(TypeError);
expect(() => check(1, "obj")).toThrow(TypeError);
expect(() => check({}, "object+")).toThrow(TypeError);
expect(() => check({}, "obj+")).toThrow(TypeError);
expect(() => check("a", "objectlike")).toThrow(TypeError);
expect(() => check({}, "iterable")).toThrow(TypeError);
expect(() => mockCheck({}, "function")).toThrow(TypeError);
expect(() => mockCheck({}, "func")).toThrow(TypeError);
expect(() => mockCheck(1, "object")).toThrow(TypeError);
expect(() => mockCheck(1, "obj")).toThrow(TypeError);
expect(() => mockCheck("a", "objectlike")).toThrow(TypeError);
expect(() => mockCheck({}, "iterable")).toThrow(TypeError);
expect(() => mockCheck([], "circular")).toThrow(TypeError);
expect(() => mockCheck({}, "array")).toThrow(TypeError);
expect(() => mockCheck({}, "arr")).toThrow(TypeError);
expect(() => mockCheck({}, "arraylike")).toThrow(TypeError);
expect(() => mockCheck({}, "arguments")).toThrow(TypeError);
expect(() => mockCheck({}, "args")).toThrow(TypeError);
expect(() => mockCheck("2016", "date")).toThrow(TypeError);
expect(() => mockCheck(new Date(1080, 0, 1), "future")).toThrow(TypeError);
expect(() => mockCheck(new Date(2980, 0, 1), "past")).toThrow(TypeError);
expect(() => mockCheck([], "map")).toThrow(TypeError);
expect(() => mockCheck([], "weakmap")).toThrow(TypeError);
expect(() => mockCheck([], "set")).toThrow(TypeError);
expect(() => mockCheck([], "weakset")).toThrow(TypeError);
expect(() => mockCheck(true, "promise")).toThrow(TypeError);
expect(() => mockCheck("/[abc]+/g", "regexp")).toThrow(TypeError);
expect(() => mockCheck("/[abc]+/g", "regex")).toThrow(TypeError);
expect(() => mockCheck("symbol", "symbol")).toThrow(TypeError);
// Arrays.
expect(() => check({}, "array")).toThrow(TypeError);
expect(() => check({}, "arr")).toThrow(TypeError);
expect(() => check({}, "array+")).toThrow(TypeError);
expect(() => check({}, "arr+")).toThrow(TypeError);
expect(() => check({}, "arraylike")).toThrow(TypeError);
expect(() => check({}, "arguments")).toThrow(TypeError);
expect(() => check({}, "args")).toThrow(TypeError);
// Dates.
expect(() => check("2016", "date")).toThrow(TypeError);
expect(() => check(new Date(1080, 0, 1), "date+")).toThrow(TypeError);
expect(() => check(new Date(1080, 0, 1), "future")).toThrow(TypeError);
expect(() => check(new Date(2980, 0, 1), "date-")).toThrow(TypeError);
expect(() => check(new Date(2980, 0, 1), "past")).toThrow(TypeError);
// Other.
expect(() => check([], "map")).toThrow(TypeError);
expect(() => check(new Map(), "map+")).toThrow(TypeError);
expect(() => check([], "weakmap")).toThrow(TypeError);
expect(() => check([], "set")).toThrow(TypeError);
expect(() => check(new Set(), "set+")).toThrow(TypeError);
expect(() => check([], "weakset")).toThrow(TypeError);
expect(() => check(true, "promise")).toThrow(TypeError);
expect(() => check("/[abc]+/g", "regexp")).toThrow(TypeError);
expect(() => check("/[abc]+/g", "regex")).toThrow(TypeError);
expect(() => check("symbol", "symbol")).toThrow(TypeError);
expect(check(false, "any")).toBe(undefined);
expect(check("abc", "mixed")).toBe(undefined);
expect(mockCheck(false, "any")).toBe(undefined);
expect(mockCheck("abc", "mixed")).toBe(undefined);
expect(() => mockCheck(undefined, "json")).toThrow(TypeError);
expect(() => mockCheck({ a: undefined }, "jsonable")).toThrow(TypeError);
// Advanced.
expect(() => check([], "circular")).toThrow(TypeError);
expect(() => check(undefined, "json")).toThrow(TypeError);
// Check we called every checker.
// Done in this awkward way so we get an error that helps us find the one we're missing.
const checkerNames = Object.keys(checkers);
checkerNames.forEach(name => expect(called).toContain(name));
called.forEach(name => expect(checkerNames).toContain(name));
expect(called.length).toBe(checkerNames.length);
});
});

@@ -8,22 +8,22 @@ const ValueError = require("../lib/errors/ValueError");

test("Add and run a custom checker (no description)", () => {
// Define a checker called '11218c'.
expect(add("11218c", v => typeof v === "string")).toBeUndefined();
// Define a checker called 'a11218'.
expect(add("a11218", v => typeof v === "string")).toBeUndefined();
// Check a passing value.
expect(check("abc", "11218c")).toBe(undefined);
expect(check("abc", "a11218")).toBe(undefined);
// Check a failing value.
expect(() => check(123, "11218c")).toThrow(TypeError);
expect(() => check(123, "11218c")).toThrow(/11218c/); // Must contain name.
expect(() => check(123, "a11218")).toThrow(TypeError);
expect(() => check(123, "a11218")).toThrow(/a11218/); // Must contain name.
});
test("Add and run a custom checker (with description)", () => {
// Define a checker called '618e0e'.
expect(add("618e0e", v => typeof v === "string", "a43829")).toBeUndefined();
// Define a checker called 'e618e0'.
expect(add("e618e0", v => typeof v === "string", "e618e0")).toBeUndefined();
// Check a passing value.
expect(check("abc", "618e0e")).toBe(undefined);
expect(check("abc", "e618e0")).toBe(undefined);
// Check a failing value.
expect(() => check(123, "618e0e")).toThrow(TypeError);
expect(() => check(123, "618e0e")).toThrow(/a43829/); // Must contain description.
expect(() => check(123, "e618e0")).toThrow(TypeError);
expect(() => check(123, "e618e0")).toThrow(/e618e0/); // Must contain description.
});

@@ -48,26 +48,39 @@ test("Add and run a custom checker (with custom Error)", () => {

// Define a checker called '013e93'.
expect(add("013e93", v => typeof v === "string", "824b7c", IsNullError)).toBeUndefined();
// Define a checker called 't01393'.
expect(add("t01393", v => typeof v === "string", "824b7c", IsNullError)).toBeUndefined();
// Check a passing value.
expect(check("abc", "013e93")).toBe(undefined);
expect(check("abc", "t01393")).toBe(undefined);
// Check a failing value.
expect(() => check(123, "013e93")).toThrow(IsNullError);
expect(() => check(123, "013e93")).toThrow(/824b7c/); // Must contain description.
expect(() => check(123, "t01393")).toThrow(IsNullError);
expect(() => check(123, "t01393")).toThrow(/824b7c/); // Must contain description.
});
test("Throw BlorkError if not non-empty lowercase string", () => {
test("Throw BlorkError if name is not kebab-case string", () => {
const func = () => {};
expect(() => add(123, func, "func")).toThrow(BlorkError);
expect(() => add("", func, "func")).toThrow(BlorkError);
expect(() => add("name_name", func, "func")).toThrow(BlorkError);
expect(() => add("UPPER", func, "func")).toThrow(BlorkError);
expect(() => add("UPPER", func, "func")).toThrow(/add\(\):/);
expect(() => add("UPPER", func, "func")).toThrow(/name:/);
expect(() => add("UPPER", func, "func")).toThrow(/kebab-case/);
expect(() => add("UPPER", func, "func")).toThrow(/"UPPER"/);
});
test("Throw BlorkError if passing a non-function", () => {
expect(() => add("test.checker.nonfunction", true)).toThrow(BlorkError);
expect(() => add("dc63b8", true)).toThrow(BlorkError);
expect(() => add("dc63b8", true)).toThrow(/add\(\):/);
expect(() => add("dc63b8", true)).toThrow(/checker:/);
expect(() => add("dc63b8", true)).toThrow(/function/);
expect(() => add("dc63b8", true)).toThrow(/true/);
});
test("Throw BlorkError if same name as existing", () => {
const func = () => {};
add("test.checker.samename", func, "samename");
expect(() => add("test.checker.samename", func)).toThrow(BlorkError);
add("b89441", func, "samename");
expect(() => add("b89441", func)).toThrow(BlorkError);
expect(() => add("b89441", func)).toThrow(/add\(\):/);
expect(() => add("b89441", func)).toThrow(/name:/);
expect(() => add("b89441", func)).toThrow(/exists/);
expect(() => add("b89441", func)).toThrow(/"b89441"/);
});
});

@@ -25,5 +25,10 @@ const BlorkError = require("../lib/errors/BlorkError");

expect(() => args(argsObj, [Boolean, Boolean])).toThrow(TypeError);
expect(() => args(argsObj, [Boolean, Boolean])).toThrow(/3/i);
expect(() => args(argsObj, [Boolean, Boolean])).toThrow(/arguments/i);
});
test("Throw BlorkError if passing non-arguments-like object", () => {
expect(() => args({}, [Number])).toThrow(BlorkError);
expect(() => args({}, [Number])).toThrow(/args\(\):/);
expect(() => args({}, [Number])).toThrow(/arraylike/);
expect(() => args({}, [Number])).toThrow(/\{\}/);
});

@@ -30,0 +35,0 @@ test("Throw BlorkError if types is not array", () => {

const BlorkError = require("../lib/errors/BlorkError");
const { check } = require("../lib/exports");
const { check, CLASS, KEYS, VALUES } = require("../lib/exports");

@@ -34,50 +34,88 @@ // Tests.

});
test("Object literal types with _any property pass correctly", () => {
expect(check({ a: 1, b: 1, c: 1 }, { a: "num", _any: "num" })).toBe(undefined);
expect(check({ a: "abc", b: "abc", c: "abc" }, { a: "str", _any: "str" })).toBe(undefined);
expect(check({ a: 1, b: 2, c: undefined }, { a: "num", _any: "num?" })).toBe(undefined);
expect(check({ a: new Map(), b: new Map(), c: new Map() }, { _any: Map })).toBe(undefined);
describe("CLASS property", () => {
class MyClass {
constructor() {
this.a = "abc";
}
}
test("Object literal types with CLASS property pass correctly", () => {
expect(check(new MyClass(), { [CLASS]: MyClass, a: "string" })).toBe(undefined);
});
test("Object literal types with CLASS property fails correctly", () => {
expect(() => check({ a: "abc" }, { [CLASS]: MyClass, a: "string" })).toThrow(TypeError);
expect(() => check({ a: "abc" }, { [CLASS]: MyClass, a: "string" })).toThrow(/instance of MyClass/);
expect(() => check({ a: "abc" }, { [CLASS]: MyClass, a: "string" })).toThrow(/{ "a": "abc" }/);
});
test("Object literal types without CLASS property fails correctly", () => {
expect(() => check(new MyClass(), { a: "string" })).toThrow(TypeError);
});
});
test("Object literal types with _any property pass correctly when _any isn't used", () => {
expect(check({ a: "abc" }, { a: "str", _any: "str" })).toBe(undefined);
describe("KEYS property", () => {
test("Object literal types with KEYS property pass correctly", () => {
expect(check({ A: 1, B: 1, C: 1 }, { A: "num", [KEYS]: "upper" })).toBe(undefined);
expect(check({ aA: "abc", bB: "abc", cC: "abc" }, { aA: "str", [KEYS]: "camel" })).toBe(undefined);
expect(check({ a: 1, b: 2, c: undefined }, { a: "num", [KEYS]: "lower" })).toBe(undefined);
});
test("Object literal types with KEYS property pass correctly when KEYS isn't used", () => {
expect(check({ A: "abc" }, { A: "str", [KEYS]: "lower" })).toBe(undefined);
});
test("Object literal types with KEYS property fail correctly", () => {
expect(() => check({ a: 1, b: 2 }, { a: "num", [KEYS]: "upper" })).toThrow(TypeError);
expect(() => check({ a: 1, b: 2 }, { a: "num", [KEYS]: "upper" })).toThrow(/\.b:/);
expect(() => check({ a: 1, b: 2 }, { a: "num", [KEYS]: "upper" })).toThrow(/UPPERCASE/i);
expect(() => check({ a: 1, b: 2 }, { a: "num", [KEYS]: "upper" }, "doc")).toThrow(TypeError);
expect(() => check({ a: 1, b: 2 }, { a: "num", [KEYS]: "upper" }, "doc")).toThrow(/doc\.b:/);
expect(() => check({ a: 1, b: 2 }, { a: "num", [KEYS]: "upper" }, "doc")).toThrow(/UPPERCASE/i);
expect(() => check({ a: 1, b: 2, c: "c" }, { a: "num", [KEYS]: "num" })).toThrow(TypeError);
});
});
test("Using undefined as any ", () => {
expect(check({ a: "abc" }, { a: "str", _any: "str" })).toBe(undefined);
describe("VALUES property", () => {
test("Object literal types with VALUES property pass correctly", () => {
expect(check({ a: 1, b: 1, c: 1 }, { a: "num", [VALUES]: "num" })).toBe(undefined);
expect(check({ a: "abc", b: "abc", c: "abc" }, { a: "str", [VALUES]: "str" })).toBe(undefined);
expect(check({ a: 1, b: 2, c: undefined }, { a: "num", [VALUES]: "num?" })).toBe(undefined);
expect(check({ a: new Map(), b: new Map(), c: new Map() }, { [VALUES]: Map })).toBe(undefined);
});
test("Object literal types with VALUES property pass correctly when VALUES isn't used", () => {
expect(check({ a: "abc" }, { a: "str", [VALUES]: "str" })).toBe(undefined);
});
test("Object literal types with VALUES property fail correctly", () => {
expect(() => check({ a: 1 }, { [VALUES]: "str" })).toThrow(TypeError);
expect(() => check({ a: 1 }, { [VALUES]: "str" })).toThrow(/\.a:/);
expect(() => check({ a: 1 }, { [VALUES]: "str" })).toThrow(/must be string/i);
expect(() => check({ a: 1 }, { [VALUES]: "str" }, "doc")).toThrow(TypeError);
expect(() => check({ a: 1 }, { [VALUES]: "str" }, "doc")).toThrow(/doc.a:/);
expect(() => check({ a: 1 }, { [VALUES]: "str" }, "doc")).toThrow(/must be string/i);
expect(() => check({ a: 1, b: 2, c: "c" }, { a: "num", [VALUES]: "num" })).toThrow(TypeError);
expect(() => check({ a: new Map() }, { [VALUES]: Set })).toThrow(TypeError);
expect(() => check({ a: new Map(), b: new Set(), c: new Set() }, { [VALUES]: Set })).toThrow(TypeError);
});
test("Deep object literal types with VALUES property pass correctly", () => {
expect(check({ a: "a", b: { bb: 22, bc: 23 } }, { a: "str", b: { [VALUES]: "num" } })).toBe(undefined);
});
});
test("Object literal types with _any property fail correctly", () => {
expect(() => check({ a: 1 }, { _any: "str" })).toThrow(TypeError);
expect(() => check({ a: 1 }, { _any: "str" })).toThrow(/a:/);
expect(() => check({ a: 1 }, { _any: "str" })).toThrow(/must be string/i);
expect(() => check({ a: 1 }, { _any: "str" }, "doc")).toThrow(TypeError);
expect(() => check({ a: 1 }, { _any: "str" }, "doc")).toThrow(/doc.a:/);
expect(() => check({ a: 1 }, { _any: "str" }, "doc")).toThrow(/must be string/i);
expect(() => check({ a: 1, b: 2, c: "c" }, { a: "num", _any: "num" })).toThrow(TypeError);
expect(() => check({ a: new Map() }, { _any: Set })).toThrow(TypeError);
expect(() => check({ a: new Map(), b: new Set(), c: new Set() }, { _any: Set })).toThrow(TypeError);
describe("Circular references", () => {
test("No infinite loop when value contains circular references", () => {
const value = {};
value.c = value;
expect(check(value, { c: { c: Object } })).toBe(undefined);
});
test("No infinite loop when value contains deep circular references", () => {
const value = { c: { c: { c: {} } } };
value.c.c.c.c = value;
expect(check(value, { c: { c: { c: { c: { c: Object } } } } })).toBe(undefined);
});
test("Throw BlorkError when type contains circular references", () => {
const type = {};
type.c = type;
expect(() => check({ c: { c: {} } }, type)).toThrow(BlorkError);
expect(() => check({ c: { c: {} } }, type)).toThrow(/circular references/);
});
test("Throw BlorkError when type contains deep circular references", () => {
const type = { c: { c: { c: {} } } };
type.c = type;
expect(() => check({ c: { c: { c: { c: { c: true } } } } }, type)).toThrow(BlorkError);
expect(() => check({ c: { c: { c: { c: { c: true } } } } }, type)).toThrow(/circular references/);
});
});
test("Deep object literal types with _any property pass correctly", () => {
expect(check({ a: "a", b: { bb: 22, bc: 23 } }, { a: "str", b: { _any: "num" } })).toBe(undefined);
});
test("No infinite loop when value contains circular references", () => {
const value = {};
value.c = value;
expect(check(value, { c: { c: Object } })).toBe(undefined);
});
test("No infinite loop when value contains deep circular references", () => {
const value = { c: { c: { c: {} } } };
value.c.c.c.c = value;
expect(check(value, { c: { c: { c: { c: { c: Object } } } } })).toBe(undefined);
});
test("Throw BlorkError when type contains circular references", () => {
const type = {};
type.c = type;
expect(() => check({ c: { c: {} } }, type)).toThrow(BlorkError);
expect(() => check({ c: { c: {} } }, type)).toThrow(/circular references/);
});
test("Throw BlorkError when type contains deep circular references", () => {
const type = { c: { c: { c: {} } } };
type.c = type;
expect(() => check({ c: { c: { c: { c: { c: true } } } } }, type)).toThrow(BlorkError);
expect(() => check({ c: { c: { c: { c: { c: true } } } } }, type)).toThrow(/circular references/);
});
});

@@ -17,74 +17,116 @@ const BlorkError = require("../lib/errors/BlorkError");

});
test("Optional types pass correctly", () => {
expect(check(1, "number?")).toBe(undefined);
expect(check("a", "string?")).toBe(undefined);
expect(check({}, "object?")).toBe(undefined);
expect(check(undefined, "number?")).toBe(undefined);
expect(check(undefined, "string?")).toBe(undefined);
expect(check(undefined, "object?")).toBe(undefined);
describe("Optional types", () => {
test("Optional types pass correctly", () => {
expect(check(1, "number?")).toBe(undefined);
expect(check("a", "string?")).toBe(undefined);
expect(check({}, "object?")).toBe(undefined);
expect(check(undefined, "number?")).toBe(undefined);
expect(check(undefined, "string?")).toBe(undefined);
expect(check(undefined, "object?")).toBe(undefined);
});
test("Optional types fail correctly", () => {
expect(() => check("a", "number?")).toThrow(TypeError);
expect(() => check(1, "string?")).toThrow(TypeError);
expect(() => check(1, "object?")).toThrow(TypeError);
});
test("Optional types have correct error message", () => {
expect(() => check(true, "string?")).toThrow(/Must be string or empty/);
expect(() => check("abc", "boolean?")).toThrow(/Must be true or false or empty/);
});
});
test("Optional types fail correctly", () => {
expect(() => check("a", "number?")).toThrow(TypeError);
expect(() => check(1, "string?")).toThrow(TypeError);
expect(() => check(1, "object?")).toThrow(TypeError);
describe("Invert types", () => {
test("Invert types pass correctly", () => {
expect(check("abc", "!number")).toBe(undefined);
expect(check(123, "!string")).toBe(undefined);
expect(check([], "!object")).toBe(undefined);
expect(check(NaN, "!number")).toBe(undefined);
expect(check(123, "!string")).toBe(undefined);
expect(check({}, "!array")).toBe(undefined);
});
test("Invert types fail correctly", () => {
expect(() => check("abc", "!string")).toThrow(TypeError);
expect(() => check(123, "!num")).toThrow(TypeError);
expect(() => check({}, "!object")).toThrow(TypeError);
});
test("Invert types have correct error message", () => {
expect(() => check("abc", "!string")).toThrow(/Must be not string/);
expect(() => check(true, "!boolean")).toThrow(/Must be not true or false/);
});
});
test("Optional types have correct error message", () => {
expect(() => check(true, "string?")).toThrow(/Must be string or empty/);
expect(() => check("abc", "boolean?")).toThrow(/Must be true or false or empty/);
describe("Non-empty types", () => {
test("Non-empty types pass correctly", () => {
expect(check("a", "string+")).toBe(undefined);
expect(check("a", "str+")).toBe(undefined);
expect(check("a", "lower+")).toBe(undefined);
expect(check("A", "upper+")).toBe(undefined);
expect(check({ a: 1 }, "object+")).toBe(undefined);
expect(check({ a: 1 }, "obj+")).toBe(undefined);
expect(check([1], "array+")).toBe(undefined);
expect(check([1], "arr+")).toBe(undefined);
expect(check(new Map([[1, 1]]), "map+")).toBe(undefined);
expect(check(new Set([1]), "set+")).toBe(undefined);
expect(check(true, "bool+")).toBe(undefined); // Not relevant.
expect(check(123, "number+")).toBe(undefined); // Not relevant.
});
test("Non-empty types fail correctly", () => {
expect(() => check("", "string+")).toThrow(TypeError);
expect(() => check("", "str+")).toThrow(TypeError);
expect(() => check("A", "lower+")).toThrow(TypeError);
expect(() => check("a", "upper+")).toThrow(TypeError);
expect(() => check({}, "object+")).toThrow(TypeError);
expect(() => check({}, "obj+")).toThrow(TypeError);
expect(() => check([], "array+")).toThrow(TypeError);
expect(() => check([], "arr+")).toThrow(TypeError);
expect(() => check(new Map(), "map+")).toThrow(TypeError);
expect(() => check(new Set(), "set+")).toThrow(TypeError);
expect(() => check(false, "bool+")).toThrow(TypeError); // Not relevant.
expect(() => check(0, "number+")).toThrow(TypeError); // Not relevant.
});
});
test("Invert types pass correctly", () => {
expect(check("abc", "!number")).toBe(undefined);
expect(check(123, "!string")).toBe(undefined);
expect(check([], "!object")).toBe(undefined);
expect(check(NaN, "!number")).toBe(undefined);
expect(check(123, "!string")).toBe(undefined);
expect(check({}, "!array")).toBe(undefined);
describe("Combined types", () => {
test("AND combined types pass correctly", () => {
expect(check(1, "number & integer")).toBe(undefined);
expect(check(1, "num & +int")).toBe(undefined);
expect(check("abc", "str & lower+")).toBe(undefined);
expect(check("ABC", "str & upper+")).toBe(undefined);
});
test("AND combined types fail correctly", () => {
expect(() => check("a", "number & string")).toThrow(TypeError);
expect(() => check("a", "number & string")).toThrow(/Must be finite number and string/);
});
test("OR combined types pass correctly", () => {
expect(check(1, "number|string")).toBe(undefined);
expect(check("a", "number|string")).toBe(undefined);
expect(check("ABC", "string | lower+")).toBe(undefined);
expect(check(null, "string | number | null")).toBe(undefined);
});
test("OR combined types fail correctly", () => {
expect(() => check(true, "number|string")).toThrow(TypeError);
expect(() => check(Symbol(), "number|string")).toThrow(TypeError);
expect(() => check(Symbol(), "number|string")).toThrow(/finite number or string/);
});
test("AND and OR combined types combine correctly", () => {
// `&` has higher precedence than `|`
expect(check("abc", "string & lower | upper")).toBe(undefined);
expect(check("ABC", "string & lower | upper")).toBe(undefined);
expect(() => check("ABCabc", "string & lower | upper")).toThrow(TypeError);
expect(check("abc", "lower | upper & string")).toBe(undefined);
expect(check("ABC", "lower | upper & string")).toBe(undefined);
expect(() => check("ABCabc", "lower | upper & string")).toThrow(TypeError);
});
test("AND and OR combined types have correct error message", () => {
expect(() => check("ABCdef", "string & lower | upper")).toThrow(/string/);
expect(() => check("ABCdef", "string & lower | upper")).toThrow(/and/);
expect(() => check("ABCdef", "string & lower | upper")).toThrow(/or/);
expect(() => check("ABCdef", "string & lower | upper")).toThrow(/UPPERCASE/);
expect(() => check("ABCdef", "string & lower | upper")).toThrow(/lowercase/);
expect(() => check("ABCdef", "string & lower | upper")).toThrow(/string/);
expect(() => check("ABCdef", "lower | upper & string")).toThrow(/string/);
expect(() => check("ABCdef", "lower | upper & string")).toThrow(/and/);
expect(() => check("ABCdef", "lower | upper & string")).toThrow(/or/);
expect(() => check("ABCdef", "lower | upper & string")).toThrow(/UPPERCASE/);
expect(() => check("ABCdef", "lower | upper & string")).toThrow(/lowercase/);
expect(() => check("ABCdef", "lower | upper & string")).toThrow(/string/);
});
});
test("Invert types fail correctly", () => {
expect(() => check("abc", "!string")).toThrow(TypeError);
expect(() => check(123, "!num")).toThrow(TypeError);
expect(() => check({}, "!object")).toThrow(TypeError);
});
test("Invert types have correct error message", () => {
expect(() => check("abc", "!string")).toThrow(/Must be not string/);
expect(() => check(true, "!boolean")).toThrow(/Must be not true or false/);
});
test("AND combined types pass correctly", () => {
expect(check(1, "number & integer")).toBe(undefined);
expect(check(1, "num & int+")).toBe(undefined);
expect(check("abc", "str & lower+")).toBe(undefined);
expect(check("ABC", "str & upper+")).toBe(undefined);
});
test("AND combined types fail correctly", () => {
expect(() => check("a", "number & string")).toThrow(TypeError);
expect(() => check("a", "number & string")).toThrow(/Must be finite number and string/);
});
test("OR combined types pass correctly", () => {
expect(check(1, "number|string")).toBe(undefined);
expect(check("a", "number|string")).toBe(undefined);
expect(check("ABC", "string | lower+")).toBe(undefined);
expect(check(null, "string | number | null")).toBe(undefined);
});
test("OR combined types fail correctly", () => {
expect(() => check(true, "number|string")).toThrow(TypeError);
expect(() => check(Symbol(), "number|string")).toThrow(TypeError);
expect(() => check(Symbol(), "number|string")).toThrow(/finite number or string/);
});
test("AND and OR combined types combine correctly", () => {
// `&` has higher precedence than `|`
expect(check("abc", "string & lower | upper")).toBe(undefined);
expect(check("ABC", "string & lower | upper")).toBe(undefined);
expect(() => check("ABCabc", "string & lower | upper")).toThrow(TypeError);
expect(check("abc", "lower | upper & string")).toBe(undefined);
expect(check("ABC", "lower | upper & string")).toBe(undefined);
expect(() => check("ABCabc", "lower | upper & string")).toThrow(TypeError);
});
test("AND and OR combined types have correct error message", () => {
expect(() => check("ABCabc", "string & lower | upper")).toThrow(
/Must be string and lowercase string or uppercase string/
);
expect(() => check("ABCabc", "lower | upper & string")).toThrow(
/Must be lowercase string or uppercase string and string/
);
});
});

@@ -15,3 +15,3 @@ const BlorkError = require("../lib/errors/BlorkError");

});
test("Do not throw error if passing string name", () => {
test("Do not throw error if passing string prefix", () => {
expect(check(true, "bool", "myValue")).toBe(undefined);

@@ -22,5 +22,9 @@ expect(check(true, Boolean, "myValue")).toBe(undefined);

});
test("Throw BlorkError if passing non-string name", () => {
test("Throw BlorkError if passing non-string prefix", () => {
expect(() => check(1, "bool", 123)).toThrow(BlorkError);
expect(() => check(1, "bool", 123)).toThrow(/check\(\):/);
expect(() => check(1, "bool", 123)).toThrow(/prefix:/);
expect(() => check(1, "bool", 123)).toThrow(/string/);
expect(() => check(1, "bool", 123)).toThrow(/123/);
});
});

@@ -5,3 +5,3 @@ const BlorkError = require("../lib/errors/BlorkError");

// Tests.
describe("exports.check()", () => {
describe("exports.checker()", () => {
test("Getting and using a checker works correctly", () => {

@@ -17,3 +17,6 @@ expect(checker("string")("abc")).toBe(true);

expect(() => checker("abc")).toThrow(BlorkError);
expect(() => checker("abc")).toThrow(/checker\(\):/);
expect(() => checker("abc")).toThrow(/not found/);
expect(() => checker("abc")).toThrow(/"abc"/);
});
});

@@ -32,5 +32,9 @@ const ValueError = require("../lib/errors/ValueError");

expect(() => throws(false)).toThrow(BlorkError);
expect(() => throws({})).toThrow(BlorkError);
expect(() => throws(123)).toThrow(BlorkError);
expect(() => throws({})).toThrow(BlorkError);
expect(() => throws(123)).toThrow("throws():");
expect(() => throws(123)).toThrow("error:");
expect(() => throws(123)).toThrow("function");
expect(() => throws(123)).toThrow("123");
});
});
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