Comparing version 2.2.0 to 3.0.0
@@ -0,1 +1,11 @@ | ||
type Coercion = [ | ||
string, | ||
() => void | ||
]; | ||
type ValidationState = { | ||
p?: string; | ||
errors?: string[]; | ||
coercions?: Coercion[]; | ||
coercion?: (val: any) => void; | ||
}; | ||
type Trait<Type> = { | ||
@@ -5,4 +15,4 @@ __trait: Type; | ||
type InferType<U> = U extends Trait<infer V> ? V : never; | ||
type LooseTest<U> = (value: U, errors?: string[] | null, p?: string) => boolean; | ||
type StrictTest<U, V extends U> = (value: U, errors?: string[] | null, p?: string) => value is V; | ||
type LooseTest<U> = (value: U, test?: ValidationState) => boolean; | ||
type StrictTest<U, V extends U> = (value: U, test?: ValidationState) => value is V; | ||
type LooseValidator<U, V> = LooseTest<U> & Trait<V>; | ||
@@ -25,12 +35,16 @@ type StrictValidator<U, V extends U> = StrictTest<U, V> & Trait<V>; | ||
declare function getPrintable(value: unknown): string; | ||
declare function addKey(p: string | undefined, key: string | number): string; | ||
declare function pushError(errors: string[] | null | undefined, p: string | undefined, message: string): boolean; | ||
declare function computeKey(state: ValidationState | undefined, key: string | number): string; | ||
declare function makeSetter(target: any, key: any): (v: any) => void; | ||
declare function pushError({ errors, p }: ValidationState | undefined, message: string): boolean; | ||
declare const isUnknown: () => StrictValidator<unknown, unknown>; | ||
declare const isLiteral: <T>(expected: T) => StrictValidator<unknown, T>; | ||
declare function isLiteral(expected: null): StrictTest<unknown, null>; | ||
declare function isLiteral(expected: true): StrictTest<unknown, true>; | ||
declare function isLiteral(expected: false): StrictTest<unknown, false>; | ||
declare function isLiteral<T extends number>(expected: T): StrictTest<unknown, T>; | ||
declare function isLiteral<T extends string>(expected: T): StrictTest<unknown, T>; | ||
declare function isLiteral<T>(expected: T): StrictTest<unknown, T>; | ||
declare const isString: () => StrictValidator<unknown, string>; | ||
declare const isBoolean: () => StrictValidator<unknown, boolean>; | ||
declare const isNumber: () => StrictValidator<unknown, number>; | ||
declare const isPositiveInteger: ({ allowZero }?: { | ||
allowZero?: boolean | undefined; | ||
}) => StrictValidator<unknown, number>; | ||
declare const isDate: () => StrictValidator<unknown, Date>; | ||
declare const isArray: <T extends StrictValidator<any, any>>(spec: T) => StrictValidator<unknown, InferType<T>[]>; | ||
@@ -88,2 +102,2 @@ type DeriveIndexUnlessNull<T extends AnyStrictValidator | null> = T extends null ? {} : InferType<T>; | ||
declare const isJSON: (spec?: AnyStrictValidator) => LooseValidator<string, string>; | ||
export { Trait, InferType, LooseTest, StrictTest, LooseValidator, StrictValidator, AnyStrictValidator, simpleKeyRegExp, colorStringRegExp, colorStringAlphaRegExp, base64RegExp, uuid4RegExp, iso8601RegExp, makeTrait, makeValidator, getPrintable, addKey, pushError, isUnknown, isLiteral, isString, isBoolean, isNumber, isPositiveInteger, isArray, isDict, isObject, isInstanceOf, isOneOf, applyCascade, isOptional, isNullable, hasMinLength, hasMaxLength, hasExactLength, hasUniqueItems, isNegative, isPositive, isAtLeast, isAtMost, isInInclusiveRange, isInExclusiveRange, isInteger, matchesRegExp, isLowerCase, isUpperCase, isUUID4, isISO8601, isHexColor, isBase64, isJSON }; | ||
export { Coercion, ValidationState, Trait, InferType, LooseTest, StrictTest, LooseValidator, StrictValidator, AnyStrictValidator, simpleKeyRegExp, colorStringRegExp, colorStringAlphaRegExp, base64RegExp, uuid4RegExp, iso8601RegExp, makeTrait, makeValidator, getPrintable, computeKey, makeSetter, pushError, isUnknown, isLiteral, isString, isBoolean, isNumber, isDate, isArray, isDict, isObject, isInstanceOf, isOneOf, applyCascade, isOptional, isNullable, hasMinLength, hasMaxLength, hasExactLength, hasUniqueItems, isNegative, isPositive, isAtLeast, isAtMost, isInInclusiveRange, isInExclusiveRange, isInteger, matchesRegExp, isLowerCase, isUpperCase, isUUID4, isISO8601, isHexColor, isBase64, isJSON }; |
338
lib/index.js
@@ -29,14 +29,20 @@ 'use strict'; | ||
} | ||
function addKey(p, key) { | ||
function computeKey(state, key) { | ||
var _a, _b, _c; | ||
if (typeof key === `number`) { | ||
return `${p !== null && p !== void 0 ? p : `.`}[${key}]`; | ||
return `${(_a = state === null || state === void 0 ? void 0 : state.p) !== null && _a !== void 0 ? _a : `.`}[${key}]`; | ||
} | ||
else if (simpleKeyRegExp.test(key)) { | ||
return `${p !== null && p !== void 0 ? p : ``}.${key}`; | ||
return `${(_b = state === null || state === void 0 ? void 0 : state.p) !== null && _b !== void 0 ? _b : ``}.${key}`; | ||
} | ||
else { | ||
return `${p !== null && p !== void 0 ? p : `.`}[${JSON.stringify(key)}]`; | ||
return `${(_c = state === null || state === void 0 ? void 0 : state.p) !== null && _c !== void 0 ? _c : `.`}[${JSON.stringify(key)}]`; | ||
} | ||
} | ||
function pushError(errors, p, message) { | ||
function makeSetter(target, key) { | ||
return (v) => { | ||
target[key] = v; | ||
}; | ||
} | ||
function pushError({ errors, p } = {}, message) { | ||
errors === null || errors === void 0 ? void 0 : errors.push(`${p !== null && p !== void 0 ? p : `.`}: ${message}`); | ||
@@ -46,24 +52,47 @@ return false; | ||
const isUnknown = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
return true; | ||
}, | ||
}); | ||
const isLiteral = (expected) => makeValidator({ | ||
test: (value, errors, p) => { | ||
if (value !== expected) | ||
return pushError(errors, p, `Expected a literal (got ${getPrintable(expected)})`); | ||
return true; | ||
}, | ||
}); | ||
function isLiteral(expected) { | ||
return makeValidator({ | ||
test: (value, state) => { | ||
if (value !== expected) | ||
return pushError(state, `Expected a literal (got ${getPrintable(expected)})`); | ||
return true; | ||
}, | ||
}); | ||
} | ||
const isString = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (typeof value !== `string`) | ||
return pushError(errors, p, `Expected a string (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected a string (got ${getPrintable(value)})`); | ||
return true; | ||
}, | ||
}); | ||
const BOOLEAN_COERCIONS = new Map([ | ||
[`true`, true], | ||
[`True`, true], | ||
[`1`, true], | ||
[1, true], | ||
[`false`, false], | ||
[`False`, false], | ||
[`0`, false], | ||
[0, false], | ||
]); | ||
const isBoolean = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
if (typeof value !== `boolean`) | ||
return pushError(errors, p, `Expected a boolean (got ${getPrintable(value)})`); | ||
test: (value, state) => { | ||
var _a; | ||
if (typeof value !== `boolean`) { | ||
if (typeof (state === null || state === void 0 ? void 0 : state.coercions) !== `undefined`) { | ||
if (typeof (state === null || state === void 0 ? void 0 : state.coercion) === `undefined`) | ||
return pushError(state, `Unbound coercion result`); | ||
const coercion = BOOLEAN_COERCIONS.get(value); | ||
if (typeof coercion !== `undefined`) { | ||
state.coercions.push([(_a = state.p) !== null && _a !== void 0 ? _a : `.`, state.coercion.bind(null, coercion)]); | ||
return true; | ||
} | ||
} | ||
return pushError(state, `Expected a boolean (got ${getPrintable(value)})`); | ||
} | ||
return true; | ||
@@ -73,20 +102,88 @@ }, | ||
const isNumber = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
if (typeof value !== `number`) | ||
return pushError(errors, p, `Expected a number (got ${getPrintable(value)})`); | ||
test: (value, state) => { | ||
var _a; | ||
if (typeof value !== `number`) { | ||
if (typeof (state === null || state === void 0 ? void 0 : state.coercions) !== `undefined`) { | ||
if (typeof (state === null || state === void 0 ? void 0 : state.coercion) === `undefined`) | ||
return pushError(state, `Unbound coercion result`); | ||
let coercion; | ||
if (typeof value === `string`) { | ||
let val; | ||
try { | ||
val = JSON.parse(value); | ||
} | ||
catch (_b) { } | ||
// We check against JSON.stringify that the output is the same to ensure that the number can be safely represented in JS | ||
if (typeof val === `number`) { | ||
if (JSON.stringify(val) === value) { | ||
coercion = val; | ||
} | ||
else { | ||
return pushError(state, `Received a number that can't be safely represented by the runtime (${value})`); | ||
} | ||
} | ||
} | ||
if (typeof coercion !== `undefined`) { | ||
state.coercions.push([(_a = state.p) !== null && _a !== void 0 ? _a : `.`, state.coercion.bind(null, coercion)]); | ||
return true; | ||
} | ||
} | ||
return pushError(state, `Expected a number (got ${getPrintable(value)})`); | ||
} | ||
return true; | ||
}, | ||
}); | ||
const isPositiveInteger = ({ allowZero = false } = {}) => applyCascade(isNumber(), [ | ||
isInteger(), | ||
isAtLeast(allowZero ? 0 : 1), | ||
]); | ||
const isDate = () => makeValidator({ | ||
test: (value, state) => { | ||
var _a; | ||
if (!(value instanceof Date)) { | ||
if (typeof (state === null || state === void 0 ? void 0 : state.coercions) !== `undefined`) { | ||
if (typeof (state === null || state === void 0 ? void 0 : state.coercion) === `undefined`) | ||
return pushError(state, `Unbound coercion result`); | ||
let coercion; | ||
if (typeof value === `string` && iso8601RegExp.test(value)) { | ||
coercion = new Date(value); | ||
} | ||
else { | ||
let timestamp; | ||
if (typeof value === `string`) { | ||
let val; | ||
try { | ||
val = JSON.parse(value); | ||
} | ||
catch (_b) { } | ||
if (typeof val === `number`) { | ||
timestamp = val; | ||
} | ||
} | ||
else if (typeof value === `number`) { | ||
timestamp = value; | ||
} | ||
if (typeof timestamp !== `undefined`) { | ||
if (Number.isSafeInteger(timestamp) || !Number.isSafeInteger(timestamp * 1000)) { | ||
coercion = new Date(timestamp * 1000); | ||
} | ||
else { | ||
return pushError(state, `Received a timestamp that can't be safely represented by the runtime (${value})`); | ||
} | ||
} | ||
} | ||
if (typeof coercion !== `undefined`) { | ||
state.coercions.push([(_a = state.p) !== null && _a !== void 0 ? _a : `.`, state.coercion.bind(null, coercion)]); | ||
return true; | ||
} | ||
} | ||
return pushError(state, `Expected a date (got ${getPrintable(value)})`); | ||
} | ||
return true; | ||
}, | ||
}); | ||
const isArray = (spec) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!Array.isArray(value)) | ||
return pushError(errors, p, `Expected an array (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected an array (got ${getPrintable(value)})`); | ||
let valid = true; | ||
for (let t = 0, T = value.length; t < T; ++t) { | ||
valid = spec(value[t], errors, addKey(p, t)) && valid; | ||
if (!valid && errors == null) { | ||
valid = spec(value[t], Object.assign(Object.assign({}, state), { p: computeKey(state, t), coercion: makeSetter(value, t) })) && valid; | ||
if (!valid && (state === null || state === void 0 ? void 0 : state.errors) == null) { | ||
break; | ||
@@ -99,15 +196,19 @@ } | ||
const isDict = (spec, { keys: keySpec = null, } = {}) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (typeof value !== `object` || value === null) | ||
return pushError(errors, p, `Expected an object (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected an object (got ${getPrintable(value)})`); | ||
const keys = Object.keys(value); | ||
let valid = true; | ||
for (let t = 0, T = keys.length && (valid || errors != null); t < T; ++t) { | ||
for (let t = 0, T = keys.length && (valid || (state === null || state === void 0 ? void 0 : state.errors) != null); t < T; ++t) { | ||
const key = keys[t]; | ||
const sub = value[key]; | ||
if (keySpec !== null && !keySpec(key, errors, p)) { | ||
if (key === `__proto__` || key === `constructor`) { | ||
valid = pushError(Object.assign(Object.assign({}, state), { p: computeKey(state, key) }), `Unsafe property name`); | ||
continue; | ||
} | ||
if (keySpec !== null && !keySpec(key, state)) { | ||
valid = false; | ||
continue; | ||
} | ||
if (!spec(sub, errors, addKey(p, key))) { | ||
if (!spec(sub, Object.assign(Object.assign({}, state), { p: computeKey(state, key), coercion: makeSetter(value, key) }))) { | ||
valid = false; | ||
@@ -123,5 +224,5 @@ continue; | ||
return makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (typeof value !== `object` || value === null) | ||
return pushError(errors, p, `Expected an object (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected an object (got ${getPrintable(value)})`); | ||
const keys = new Set([...specKeys, ...Object.keys(value)]); | ||
@@ -131,23 +232,32 @@ const extra = {}; | ||
for (const key of keys) { | ||
const spec = Object.prototype.hasOwnProperty.call(props, key) | ||
? props[key] | ||
: undefined; | ||
const sub = Object.prototype.hasOwnProperty.call(value, key) | ||
? value[key] | ||
: undefined; | ||
if (typeof spec !== `undefined`) { | ||
valid = spec(sub, errors, addKey(p, key)) && valid; | ||
if (key === `constructor` || key === `__proto__`) { | ||
valid = pushError(Object.assign(Object.assign({}, state), { p: computeKey(state, key) }), `Unsafe property name`); | ||
} | ||
else if (extraSpec === null) { | ||
valid = pushError(errors, addKey(p, key), `Extraneous property (got ${getPrintable(sub)})`); | ||
} | ||
else { | ||
extra[key] = sub; | ||
const spec = Object.prototype.hasOwnProperty.call(props, key) | ||
? props[key] | ||
: undefined; | ||
const sub = Object.prototype.hasOwnProperty.call(value, key) | ||
? value[key] | ||
: undefined; | ||
if (typeof spec !== `undefined`) { | ||
valid = spec(sub, Object.assign(Object.assign({}, state), { p: computeKey(state, key), coercion: makeSetter(value, key) })) && valid; | ||
} | ||
else if (extraSpec === null) { | ||
valid = pushError(Object.assign(Object.assign({}, state), { p: computeKey(state, key) }), `Extraneous property (got ${getPrintable(sub)})`); | ||
} | ||
else { | ||
Object.defineProperty(extra, key, { | ||
enumerable: true, | ||
get: () => sub, | ||
set: makeSetter(value, key) | ||
}); | ||
} | ||
} | ||
if (!valid && errors == null) { | ||
if (!valid && (state === null || state === void 0 ? void 0 : state.errors) == null) { | ||
break; | ||
} | ||
} | ||
if (extraSpec !== null && (valid || errors != null)) | ||
valid = extraSpec(extra, errors, p) && valid; | ||
if (extraSpec !== null && (valid || (state === null || state === void 0 ? void 0 : state.errors) != null)) | ||
valid = extraSpec(extra, state) && valid; | ||
return valid; | ||
@@ -158,5 +268,5 @@ }, | ||
const isInstanceOf = (constructor) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value instanceof constructor)) | ||
return pushError(errors, p, `Expected an instance of ${constructor.name} (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected an instance of ${constructor.name} (got ${getPrintable(value)})`); | ||
return true; | ||
@@ -166,11 +276,14 @@ }, | ||
const isOneOf = (specs, { exclusive = false, } = {}) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
var _a, _b, _c; | ||
const matches = []; | ||
const errorBuffer = typeof errors !== `undefined` | ||
const errorBuffer = typeof (state === null || state === void 0 ? void 0 : state.errors) !== `undefined` | ||
? [] : undefined; | ||
for (let t = 0, T = specs.length; t < T; ++t) { | ||
const subErrors = typeof errors !== `undefined` | ||
const subErrors = typeof (state === null || state === void 0 ? void 0 : state.errors) !== `undefined` | ||
? [] : undefined; | ||
if (specs[t](value, subErrors, `${p !== null && p !== void 0 ? p : `.`}#${t + 1}`)) { | ||
matches.push(`#${t + 1}`); | ||
const subCoercions = typeof (state === null || state === void 0 ? void 0 : state.coercions) !== `undefined` | ||
? [] : undefined; | ||
if (specs[t](value, Object.assign(Object.assign({}, state), { errors: subErrors, coercions: subCoercions, p: `${(_a = state === null || state === void 0 ? void 0 : state.p) !== null && _a !== void 0 ? _a : `.`}#${t + 1}` }))) { | ||
matches.push([`#${t + 1}`, subCoercions]); | ||
if (!exclusive) { | ||
@@ -184,8 +297,12 @@ break; | ||
} | ||
if (matches.length === 1) | ||
if (matches.length === 1) { | ||
const [, subCoercions] = matches[0]; | ||
if (typeof subCoercions !== `undefined`) | ||
(_b = state === null || state === void 0 ? void 0 : state.coercions) === null || _b === void 0 ? void 0 : _b.push(...subCoercions); | ||
return true; | ||
} | ||
if (matches.length > 1) | ||
pushError(errors, p, `Expected to match exactly a single predicate (matched ${matches.join(`, `)})`); | ||
pushError(state, `Expected to match exactly a single predicate (matched ${matches.join(`, `)})`); | ||
else | ||
errors === null || errors === void 0 ? void 0 : errors.push(...errorBuffer); | ||
(_c = state === null || state === void 0 ? void 0 : state.errors) === null || _c === void 0 ? void 0 : _c.push(...errorBuffer); | ||
return false; | ||
@@ -195,7 +312,7 @@ }, | ||
const applyCascade = (spec, followups) => makeValidator({ | ||
test: (value, errors, p) => { | ||
if (!spec(value, errors, p)) | ||
test: (value, state) => { | ||
if (!spec(value, state)) | ||
return false; | ||
return followups.every(spec => { | ||
return spec(value, errors, p); | ||
return spec(value, state); | ||
}); | ||
@@ -205,19 +322,19 @@ }, | ||
const isOptional = (spec) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (typeof value === `undefined`) | ||
return true; | ||
return spec(value, errors, p); | ||
return spec(value, state); | ||
}, | ||
}); | ||
const isNullable = (spec) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (value === null) | ||
return true; | ||
return spec(value, errors, p); | ||
return spec(value, state); | ||
}, | ||
}); | ||
const hasMinLength = (length) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value.length >= length)) | ||
return pushError(errors, p, `Expected to have a length of at least ${length} elements (got ${value.length})`); | ||
return pushError(state, `Expected to have a length of at least ${length} elements (got ${value.length})`); | ||
return true; | ||
@@ -227,5 +344,5 @@ }, | ||
const hasMaxLength = (length) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value.length <= length)) | ||
return pushError(errors, p, `Expected to have a length of at most ${length} elements (got ${value.length})`); | ||
return pushError(state, `Expected to have a length of at most ${length} elements (got ${value.length})`); | ||
return true; | ||
@@ -235,5 +352,5 @@ }, | ||
const hasExactLength = (length) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value.length <= length)) | ||
return pushError(errors, p, `Expected to have a length of exactly ${length} elements (got ${value.length})`); | ||
return pushError(state, `Expected to have a length of exactly ${length} elements (got ${value.length})`); | ||
return true; | ||
@@ -243,3 +360,3 @@ }, | ||
const hasUniqueItems = ({ map, } = {}) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
const set = new Set(); | ||
@@ -255,3 +372,3 @@ const dup = new Set(); | ||
continue; | ||
pushError(errors, p, `Expected to contain unique elements; got a duplicate with ${getPrintable(value)}`); | ||
pushError(state, `Expected to contain unique elements; got a duplicate with ${getPrintable(value)}`); | ||
dup.add(key); | ||
@@ -267,5 +384,5 @@ } | ||
const isNegative = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value <= 0)) | ||
return pushError(errors, p, `Expected to be negative (got ${value})`); | ||
return pushError(state, `Expected to be negative (got ${value})`); | ||
return true; | ||
@@ -275,5 +392,5 @@ }, | ||
const isPositive = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value >= 0)) | ||
return pushError(errors, p, `Expected to be positive (got ${value})`); | ||
return pushError(state, `Expected to be positive (got ${value})`); | ||
return true; | ||
@@ -283,5 +400,5 @@ }, | ||
const isAtLeast = (n) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value >= n)) | ||
return pushError(errors, p, `Expected to be at least ${n} (got ${value})`); | ||
return pushError(state, `Expected to be at least ${n} (got ${value})`); | ||
return true; | ||
@@ -291,5 +408,5 @@ }, | ||
const isAtMost = (n) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value <= n)) | ||
return pushError(errors, p, `Expected to be at most ${n} (got ${value})`); | ||
return pushError(state, `Expected to be at most ${n} (got ${value})`); | ||
return true; | ||
@@ -299,5 +416,5 @@ }, | ||
const isInInclusiveRange = (a, b) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value >= a && value <= b)) | ||
return pushError(errors, p, `Expected to be in the [${a}; ${b}] range (got ${value})`); | ||
return pushError(state, `Expected to be in the [${a}; ${b}] range (got ${value})`); | ||
return true; | ||
@@ -307,5 +424,5 @@ }, | ||
const isInExclusiveRange = (a, b) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!(value >= a && value < b)) | ||
return pushError(errors, p, `Expected to be in the [${a}; ${b}[ range (got ${value})`); | ||
return pushError(state, `Expected to be in the [${a}; ${b}[ range (got ${value})`); | ||
return true; | ||
@@ -315,7 +432,7 @@ }, | ||
const isInteger = ({ unsafe = false, } = {}) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (value !== Math.round(value)) | ||
return pushError(errors, p, `Expected to be an integer (got ${value})`); | ||
return pushError(state, `Expected to be an integer (got ${value})`); | ||
if (!Number.isSafeInteger(value)) | ||
return pushError(errors, p, `Expected to be a safe integer (got ${value})`); | ||
return pushError(state, `Expected to be a safe integer (got ${value})`); | ||
return true; | ||
@@ -325,5 +442,5 @@ }, | ||
const matchesRegExp = (regExp) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!regExp.test(value)) | ||
return pushError(errors, p, `Expected to match the pattern ${regExp.toString()} (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected to match the pattern ${regExp.toString()} (got ${getPrintable(value)})`); | ||
return true; | ||
@@ -333,5 +450,5 @@ }, | ||
const isLowerCase = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (value !== value.toLowerCase()) | ||
return pushError(errors, p, `Expected to be all-lowercase (got ${value})`); | ||
return pushError(state, `Expected to be all-lowercase (got ${value})`); | ||
return true; | ||
@@ -341,5 +458,5 @@ }, | ||
const isUpperCase = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (value !== value.toUpperCase()) | ||
return pushError(errors, p, `Expected to be all-uppercase (got ${value})`); | ||
return pushError(state, `Expected to be all-uppercase (got ${value})`); | ||
return true; | ||
@@ -349,5 +466,5 @@ }, | ||
const isUUID4 = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!uuid4RegExp.test(value)) | ||
return pushError(errors, p, `Expected to be a valid UUID v4 (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected to be a valid UUID v4 (got ${getPrintable(value)})`); | ||
return true; | ||
@@ -357,5 +474,5 @@ }, | ||
const isISO8601 = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!iso8601RegExp.test(value)) | ||
return pushError(errors, p, `Expected to be a valid ISO 8601 date string (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected to be a valid ISO 8601 date string (got ${getPrintable(value)})`); | ||
return false; | ||
@@ -365,3 +482,3 @@ }, | ||
const isHexColor = ({ alpha = false, }) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
const res = alpha | ||
@@ -371,3 +488,3 @@ ? colorStringRegExp.test(value) | ||
if (!res) | ||
return pushError(errors, p, `Expected to be a valid hexadecimal color string (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected to be a valid hexadecimal color string (got ${getPrintable(value)})`); | ||
return true; | ||
@@ -377,5 +494,5 @@ }, | ||
const isBase64 = () => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
if (!base64RegExp.test(value)) | ||
return pushError(errors, p, `Expected to be a valid base 64 string (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected to be a valid base 64 string (got ${getPrintable(value)})`); | ||
return true; | ||
@@ -385,3 +502,3 @@ }, | ||
const isJSON = (spec = isUnknown()) => makeValidator({ | ||
test: (value, errors, p) => { | ||
test: (value, state) => { | ||
let data; | ||
@@ -392,9 +509,8 @@ try { | ||
catch (_a) { | ||
return pushError(errors, p, `Expected to be a valid JSON string (got ${getPrintable(value)})`); | ||
return pushError(state, `Expected to be a valid JSON string (got ${getPrintable(value)})`); | ||
} | ||
return spec(data, errors, p); | ||
return spec(data, state); | ||
}, | ||
}); | ||
exports.addKey = addKey; | ||
exports.applyCascade = applyCascade; | ||
@@ -404,2 +520,3 @@ exports.base64RegExp = base64RegExp; | ||
exports.colorStringRegExp = colorStringRegExp; | ||
exports.computeKey = computeKey; | ||
exports.getPrintable = getPrintable; | ||
@@ -415,2 +532,3 @@ exports.hasExactLength = hasExactLength; | ||
exports.isBoolean = isBoolean; | ||
exports.isDate = isDate; | ||
exports.isDict = isDict; | ||
@@ -433,3 +551,2 @@ exports.isHexColor = isHexColor; | ||
exports.isPositive = isPositive; | ||
exports.isPositiveInteger = isPositiveInteger; | ||
exports.isString = isString; | ||
@@ -440,2 +557,3 @@ exports.isUUID4 = isUUID4; | ||
exports.iso8601RegExp = iso8601RegExp; | ||
exports.makeSetter = makeSetter; | ||
exports.makeTrait = makeTrait; | ||
@@ -442,0 +560,0 @@ exports.makeValidator = makeValidator; |
{ | ||
"name": "typanion", | ||
"version": "2.2.0", | ||
"main": "lib/index.js", | ||
"version": "3.0.0", | ||
"main": "lib/index", | ||
"license": "MIT", | ||
@@ -20,3 +20,3 @@ "sideEffects": false, | ||
"tslib": "^2.0.0", | ||
"typescript": "^3.9.5" | ||
"typescript": "^4.1.2" | ||
}, | ||
@@ -29,10 +29,7 @@ "scripts": { | ||
"publishConfig": { | ||
"main": "lib/index.js", | ||
"module": "lib/index.mjs", | ||
"types": "lib/index.d.ts" | ||
"main": "lib/index" | ||
}, | ||
"files": [ | ||
"lib" | ||
], | ||
"module": "lib/index.mjs" | ||
] | ||
} |
@@ -51,3 +51,3 @@ # Typanion | ||
if (!isMovie(userData, errors)) { | ||
if (!isMovie(userData, {errors})) { | ||
console.log(errors); | ||
@@ -57,2 +57,17 @@ } | ||
You can also apply coercion over the user input: | ||
```ts | ||
const userData = JSON.parse(input); | ||
const coercions: Coercion[] = []; | ||
if (isMovie(userData, {coercions})) { | ||
// Coercions aren't flushed by default | ||
for (const [p, op] of coercions) op(); | ||
// All relevant fields have now been coerced | ||
// ... | ||
} | ||
``` | ||
You can derive the type from the schema and use it in other functions: | ||
@@ -98,9 +113,11 @@ | ||
- `isBoolean()` will ensure that the values are all booleans. Note that to specifically check for either `true` or `false`, you can look for `isLiteral`. | ||
- `isBoolean()` will ensure that the values are all booleans. Prefer `isLiteral` if you wish to specifically check for one of `true` or `false`. This predicate supports coercion. | ||
- `isDate()` will ensure that the values are proper `Date` instances. This predicate supports coercion via either ISO8601, or raw numbers (in which case they're interpreted as the number of *seconds* since epoch, not milliseconds). | ||
- `isDict(values, {keys?})` will ensure that the values are all a standard JavaScript objects containing an arbitrary number of fields whose values all match the given schema. The `keys` option can be used to apply a schema on the keys as well (this will always have to be strings, so you'll likely want to use `applyCascade(isString(), [...])` to define the pattern). | ||
- `isLiteral(value)` will ensure that the values are strictly equal to the specified expected value. It's an handy tool that you can combine with `oneOf` and `object` to parse structures similar to Redux actions, etc. Note that you'll need to annotate your value with `as const` in order to let TypeScript know that the exact value matters (otherwise it'll cast it to `string` instead). | ||
- `isLiteral(value)` will ensure that the values are strictly equal to the specified expected value. It's an handy tool that you can combine with `oneOf` and `object` to parse structures similar to Redux actions, etc. | ||
- `isNumber()` will ensure that the values are all numbers. | ||
- `isNumber()` will ensure that the values are all numbers. This predicate supports coercion. | ||
@@ -125,4 +142,2 @@ - `isObject(props, {extra?})` will ensure that the values are plain old objects whose properties match the given shape. Extraneous properties will be aggregated and validated against the optional `extra` schema. | ||
- `isPositiveInteger({allowZero?})` will allow positive integers only and will type them as numbers. By default it won't allow 0 (zero), but this can be toggled using `allowZero`. | ||
### Cascading predicates | ||
@@ -187,3 +202,3 @@ | ||
const isDiv = t.isObject({ | ||
tagName: t.literal(`DIV` as const), | ||
tagName: t.literal(`DIV`), | ||
}, { | ||
@@ -190,0 +205,0 @@ extra: t.isUnknown(), |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
57136
1110
227