baridetta v1.5.0
Baridetta
Baridetta is a javascript universal single object validator. It provides validation and transformation utilities. Each validator is represented by a separated module, thats gives opportunity for treeshaking. Library has 5 types of modules: validators, processors, groupers, spreaders, containers. Each one has own specific behaviour.
You can use validators only for validation and processing data without error handling (e.g. url query params). Futhermore, you can use containers for error handling and provide your own errors processing.
You can easily extend library with your own specific validators or processors.
Minified library bundle with all modules takes less than 7kb. It doesn't require any external dependency.
Install
Usage
API
Types
Validators
array<T>(itemSpec?: Array<Validator<any, T>> | Validator<any, T>, error?: Error): Validator<Array<any>, Array<T>>
bool<T>(error?: Error): Validator<T, boolean>
date<T>(error?: Error): Validator<T, number>
defined<T>(error?: Error): Validator<T>
empty<T>(error?: Error): Validator<T>
equal<T>(match: T, error?: Error): Validator<T>
fields<T extends ObjectLike>(spec: FieldsSpec, error?: Error): Validator<T>
gte<T>(bound: T, error?: Error): Validator<T>
integer(error?: Error): Validator<number>
len<T extends Lengthy>(len: number, error?: Error): Validator<T>
lte<T>(bound: T, error?: Error): Validator<T>
maxLen<T extends Lengthy>(len: number, error?: Error): Validator<T>
minLen<T extends Lengthy>(len: number, error?: Error): Validator<T>
number<T>(error?: Error): Validator<T, number>
object<T extends ObjectLike, R = T>(spec?: ObjectSpec, error?: Error): Validator<T, R>
object2<T extends ObjectLike, R = T>(spec?: Array<[string | RegEx, ...Array<Validator<any, any>>]>, error?: Error): Validator<T, R>
oneOf<T>(candidates: Array<T>, error?: Error): Validator<T>
regex<T>(match: RegExp, error?: Error): Validator<T>
string<T>(error?: Error): Validator<T, string>
Processors
clamp<T>(min: T, max: T): Validator<T, T>
erase<T>(): Validator<T, null>
keysMap<T extends ObjectLike>(mapper: (key: string) => string): Validator<T, T>
lowercase(): Validator<string, string>
random(min: number, max: number, precision: number): Validator<any, number>
round(method?: 'floor' | 'ceil'): Validator<number, number>
strip<T extends ObjectLike, K>(field: string | RegExp, condition: boolean | ((value: K) => boolean) = true): Validator<T, T>
trim(method?: 'left' | 'right'): Validator<string, string>
uppercase(): Validator<string, string>
Groupers
Containers
withErrors<T, R>(validator: Validator<T, R>, commonErrorProcessor?: ((error?: Error, meta?: MetaData) => Error)): Validator<T, Result<R>>
withFallback<T, R>(fallback: R | ((initialValue: T, meta?: MetaData) => R), ...validators: Array<Validator<T | R, R>>): Validator<T | R, R>
withMeta<T, R>(validator: Validator<T, R>): Validator<T, R>
withOnError<T, R>(errorProcessor: ErrorCallback, ...validators: Array<Validator<any, T>>): Validator<T, R>
withPromise<T, R>(validator: Validator<T, R | Result<R>>): Validator<T, Promise<R | Array<Error>>>
Spreaders
getDep<T>(field: string, preValidator?: (dep: T) => Validator<T> | Array<Validator<T>>): Validator<T>
setDep<T>(field: string, extValue?: any | ((value: T, meta?: MetaData) => any)): Validator<T>
setVDep<T>(field: string, ...validators: Array<Validator<T>>): Validator<T>
useDefault<T, R>(defaultValue: R | ((meta?: MetaData) => R), ...validators: Array<Validator<T | R, R>>): Validator<T | R, R>
Custom validators
Examples
Install
npm install baridetta
//or
yarn add baridetta
Usage
import * as v from 'baridetta'; // for everything (recommended for better minification results e.g. in webpack)
// or
import { number, array } from 'baridetta'; // for only what you need
// or
const { object, setDep } = require('baridetta');
import * as v from 'baridetta';
v.number()(10);
// => 10
v.number()('abc');
// => null
const simpleObj = (
v.object({
id: [v.number(), v.gte(0)],
name: [v.string(), v.minLen(10)],
role: [v.string(), v.regex(/^[A-Z]{5,20}$/)]
})
);
// or extended solution (recommended)
const extObj = (
v.object2([
['id', v.number(), v.gte(0)],
['name', v.string(), v.minLen(10)],
['role', v.string(), v.regex(/^[A-Z]{5,20}$/)]
])
);
simpleObj({
id: 3, // right
name: 'YourAwesomeUserName', // right
role: 'invalidRole' // wrong. Will be null
});
// => { id: 3, name: 'YourAwesomeUserName', role: null }
API
Types
The main types used in the library.
ErrorCallback
Calls on validation error.
type ErrorCallback = (error: Error, meta?: MetaData, relevance?: Relevance) => void;
Error
Any type's error. Can be a function that accepts error metadata (available if 'meta' is provided in the validator) and returns an error.
type Error = (
string
| boolean
| number
| Record<any, any>
| Array<any>
| ((meta: MetaData) => any)
);
FieldsSpec
Specification for 'fields' validator.
type FieldsSpec = (
string
| [
('&' | '|' | '^'),
FieldsSpec | string,
FieldsSpec | string,
...Array<FieldsSpec | string>
]
);
MetaData
Internal data for errors and dependencies.
type MetaData = {
path: Array<string | number>;
validator?: string;
params: Array<any>;
_deps: Record<string, any>;
};
ObjectSpec
Specification for 'object' and 'object2' validators.
type ObjectSpec = Record<string, Array<Validator<any, any>> | Validator<any, any>>;
Relevance
Error's relevancy status.
type Relevance = {
value: boolean;
};
Result
'WithError' container's result. Will be null if no errors.
type Result<T> = {
result: T;
errors?: Array<any>;
};
Validator
Validates value.
type Validator<T> = (value: T, onError?: ErrorCallback, meta?: MetaData) => T;
Validators
Checks input with some conditions. Returns input value on success, otherwise 'null' will be returned.
array<T>(itemSpec?: Array<Validator<any, T>> | Validator<any, T>, error?: Error): Validator<Array<any>, Array<T>>
Checks value to be an array.
import * as v from 'baridetta';
const simpleOne = (
v.array([ // is array?
v.number(), // is element can be a number?
v.gte(0) // is element positive or zero?
])
);
simpleOne('abc' as any); // not an array.
// => null
simpleOne([0, 1, 2]);
// => [0, 1, 2]
simpleOne([0, -1, 2]); // '-1' is negative.
// => [0, null, 2]
simpleOne([0, 1, 'a']); // 'a' is a string.
// => [0, 1, null]
//but
simpleOne([0, 1, '2']);
// => [0, 1, 2]
const anotherOne = (
v.consecutive( // checks input sequentially with few validators.
v.array([
v.number(),
v.clamp(0, 10) // clamps value between provided boundaries.
]),
v.maxLen(3) // checks array's length.
)
);
anotherOne([0, 1, 2]);
// => [0, 1, 2]
anotherOne([0, 1, 20]); // '20' will be clamped to '10'.
// => [0, 1, 10]
anotherOne([0, 1, 2, 3]); // too long.
// => null
bool<T>(error?: Error): Validator<T, boolean>
Checks value to be a boolean compatible. Can be in CheckOnly mode with .check call.
import * as v from 'baridetta';
v.bool()(true);
// => true
v.bool()(1);
// => true
v.bool()('false');
// => false
v.bool()('0');
// => false
v.bool()(10);
// => null
v.bool()('abc');
// => null
v.bool.check()(true);
// => true
v.bool.check()(1);
// => 1
v.bool.check()('abc');
// => null
date<T>(error?: Error): Validator<T, number>
Checks value to be a date compatible. Can be in CheckOnly mode with .check call. Result in ms.
import * as v from 'baridetta';
v.date()('12.12.2020');
// => 1607720400000
v.date()([12, 12, 2020]);
// => 1607720400000
v.date()('99.12.2020');
// => null
v.date.check()('12.12.2020');
// => '12.12.2020'
v.date.check()('99.12.2020');
// => null
defined<T>(error?: Error): Validator<T>
Checks value to be defined. Can be inverted with .not call.
import * as v from 'baridetta';
v.defined()(null);
// => null
v.defined()(undefined);
// => null
v.defined()('');
// => ''
v.defined()(true);
// => true
v.defined.not()(null);
// => null
v.defined.not()(undefined);
// => undefined
v.defined.not()('');
// => null
v.defined.not()(true);
// => null
empty<T>(error?: Error): Validator<T>
Checks value to be empty. Can be inverted with .not call.
import * as v from 'baridetta';
v.empty()(null);
// => null
v.empty()(undefined);
// => undefined
v.empty()('');
// => ''
v.empty()(true);
// => null
v.empty()('abc');
// => null
v.empty()(0);
// => null
v.empty.not()(undefined);
// => null
v.empty.not()(0);
// => 0
equal<T>(match: T, error?: Error): Validator<T>
Checks value to be equal to 'match' param. Requires the same type. Shallow comparison. Can be inverted with .not call.
import * as v from 'baridetta';
v.equal(10)(10);
// => 10
v.equal('10')(10 as any);
// => null
v.equal([1, 2, 3])([1, 2, 3]); // it's not a deep equality. Only checks links.
// => null
v.equal.not(10)(10);
// => null
v.equal.not(10)(1);
// => 1
fields<T extends ObjectLike>(spec: FieldsSpec, error?: Error): Validator<T>
Checks for fields in the input object.
import * as v from 'baridetta';
v.fields('f1')({ f1: 1 }); // requires 'f1' field.
// => { f1: 1 }
v.fields('f1')({ f1: 1, f2: 2 });
// => { f1: 1 }
v.fields(['&', 'f1', 'f2'])({ f1: 1, f2: 2 }); // requires both of fields.
// => { f1: 1, f2: 2 }
v.fields(['&', 'f1', 'f2'])({ f1: 1, f2: null });
// => null
v.fields(['|', 'f1', 'f2'])({ f1: 1, f2: 2 }); // requires at least one field.
// => { f1: 1, f2: 2 }
v.fields(['|', 'f1', 'f2'])({ f1: 1, f2: null });
// => { f1: 1, f2: null }
v.fields(['|', 'f1', 'f2'])({ f1: null });
// => null
v.fields(['^', 'f1', 'f2'])({ f1: 1, f2: 2 }); // requires at only one field.
// => null
v.fields(['^', 'f1', 'f2'])({ f1: 1, f2: null });
// => { f1: 1, f2: null }
v.fields(['^', 'f1', 'f2'])({ f1: null });
// => null
// complex conditions
v.fields(['&', ['^', 'id', 'guid'], 'role', ['|', 'fullname', 'nickname']]);
// requires identifier ('id' either 'guid'), 'role', name ('fullname' or 'nickname' or both).
gte<T>(bound: T, error?: Error): Validator<T>
Checks value to be greater or equal to 'match' param. Requires the same type.
import * as v from 'baridetta';
v.gte(0)(1);
// => 1
v.gte('0')('1');
// => '1'
v.gte(false)(true);
// => true
v.gte(0)(-1);
// => null
v.gte('b')('a');
// => null
v.gte(true)(false);
// => null
v.gte(new Date())(new Date(Date.now() + 1000));
// => Date
v.gte(new Date())(new Date(Date.now() - 1000));
// => null
integer(error?: Error): Validator<number>
Checks number to be an integer. Can be inverted with .not call.
import * as v from 'baridetta';
v.integer()(1);
// => 1
v.integer()(1.1);
// => null
v.integer()('1' as any); // requires a number.
// => null
v.integer.not()(1);
// => null
v.integer.not()(1.1);
// => 1.1
len<T extends Lengthy>(len: number, error?: Error): Validator<T>
Checks length to be equal to 'len' param. Requires to be object like. Can be inverted with .not call.
import * as v from 'baridetta';
v.len(3)([0, 1, 2]);
// => [0, 1, 2]
v.len(3)('abc');
// => 'abc'
v.len(3)({ length: 3 });
// => { length: 3 }
v.len(3)(10 as any);
// => null
v.len(3)({ length: '3' } as any);
// => null
v.len.not(3)([0, 1, 2]);
// => null
v.len.not(3)('abc');
// => null
v.len.not(3)([0, 1, 2, 3]);
// => [0, 1, 2, 3]
v.len.not(3)('abcd');
// => 'abcd'
lte<T>(bound: T, error?: Error): Validator<T>
Checks value to be lower or equal to 'match' param. Requires the same type.
import * as v from 'baridetta';
v.lte(2)(1);
// => 1
v.lte('2')('1');
// => '1'
v.lte(true)(true);
// => true
v.lte(0)(1);
// => null
v.lte('a')('b');
// => null
v.lte(false)(true);
// => null
v.lte(new Date())(new Date(Date.now() - 1000));
// => Date
v.lte(new Date())(new Date(Date.now() + 1000));
// => null
maxLen<T extends Lengthy>(len: number, error?: Error): Validator<T>
Checks length to be equal to 'len' param. Requires to be object like.
import * as v from 'baridetta';
v.maxLen(3)([0, 1, 2]);
// => [0, 1, 2]
v.maxLen(3)([0, 1, 2, 3]);
// => null
v.maxLen(3)('abc');
// => 'abc'
v.maxLen(3)({ length: 3 });
// => { length: 3 }
minLen<T extends Lengthy>(len: number, error?: Error): Validator<T>
Checks length to be equal to 'len' param. Requires to be object like.
import * as v from 'baridetta';
v.minLen(3)([0, 1, 2]);
// => [0, 1, 2]
v.minLen(3)([0, 1]);
// => null
v.minLen(3)('abc');
// => 'abc'
v.minLen(3)({ length: 3 });
// => { length: 3 }
number<T>(error?: Error): Validator<T, number>
Checks value to be a number compatible. Can be in CheckOnly mode with .check call.
import * as v from 'baridetta';
v.number()(10);
// => 10
v.number()('10');
// => 10
v.number()(true);
// => 1
v.number()('true');
// => null
v.number()('12.1');
// => 12.1
v.number.check()(10);
// => 10
v.number.check()('10');
// => '10'
v.number.check()('true');
// => null
object<T extends ObjectLike, R = T>(spec?: ObjectSpec, error?: Error): Validator<T, R>
Checks value to be an object.
import * as v from 'baridetta';
const simpleObj = (
v.object({ // is object?
id: [v.number(), v.gte(0)],
name: [v.string(), v.minLen(10)],
role: [v.string(), v.regex(/^[A-Z]{5,20}$/)]
})
);
simpleObj({
id: 3,
name: 'YourAwesomeUserName',
role: 'invalidRole', // wrong. Will be null
status: 0 // will be skipped in output.
});
// => { id: 3, name: 'YourAwesomeUserName', role: null }
simpleObj([]);
// => null
simpleObj(10 as any);
// => null
const fieldsKeeper = (
v.object({
id: [], // just takes input value.
name: []
})
);
fieldsKeeper({
id: 3,
name: 'YourAwesomeUserName',
role: 'invalidRole',
status: 0
});
// => { id: 3, name: 'YourAwesomeUserName' }
object2<T extends ObjectLike, R = T>(spec?: Array<[string | RegEx, ...Array<Validator<any, any>>]>, error?: Error): Validator<T, R>
Checks value to be an object. Provides strict ordering. Each key can be a Regex.
import * as v from 'baridetta';
const simpleObj = (
v.object2([ // is object?
['id', v.number(), v.gte(0)],
['name', v.string(), v.minLen(10)],
['role', v.string(), v.regex(/^[A-Z]{5,20}$/)]
])
);
simpleObj({
id: 3,
name: 'YourAwesomeUserName',
role: 'invalidRole', // wrong. Will be null
status: 0 // will be skipped in output.
});
// => { id: 3, name: 'YourAwesomeUserName', role: null }
simpleObj([]);
// => null
simpleObj(10 as any);
// => null
const fieldsKeeper = (
v.object2([
['id'], // just takes input value.
['name']
])
);
fieldsKeeper({
id: 3,
name: 'YourAwesomeUserName',
role: 'invalidRole',
status: 0
});
// => { id: 3, name: 'YourAwesomeUserName' }
const advancedObj = (
v.object2([
['id', v.number(), v.gte(0)],
[/name|surname|thirdname/, v.string(), v.minLen(10)] // use regex for fields matching.
])
);
advancedObj({
id: 3,
name: 'YourAwesomeUserName',
surname: 'YourAwesomeUserSurname',
thirdname: 'YourAwesomeUserThirdname'
});
// => { id: 3, name: 'YourAwesomeUserName', surname: 'YourAwesomeUserSurname', thirdname: 'YourAwesomeUserThirdname' }
oneOf<T>(candidates: Array<T>, error?: Error): Validator<T>
Checks value to be one of expected. Shallow comparison. Can be inverted with .not call.
import * as v from 'baridetta';
v.oneOf([0, 1, 2])(1);
// => 1
v.oneOf([0, 1, 2])(3);
// => null
v.oneOf([0, 1, [1]])([1]); // not a deep equality.
// => null
v.oneOf.not([0, 1, 2])(1);
// => null
v.oneOf.not([0, 1, 2])(3);
// => 3
regex<T>(match: RegExp, error?: Error): Validator<T>
Checks value to match a pattern. Can be inverted with .not call.
import * as v from 'baridetta';
v.regex(/^[0-9]$/)(1);
// => 1
v.regex(/^[0-9]$/)(11);
// => null
v.regex.not(/^[0-9]$/)(1);
// => null
v.regex(/^[0-9]$/)(11);
// => 11
string<T>(error?: Error): Validator<T, string>
Checks value to be a string compatible. Can be in CheckOnly mode with .check call.
import * as v from 'baridetta';
v.string()(1);
// => '1'
v.string()('1');
// => '1'
v.string()(true);
// => 'true'
v.string()([1, 2]);
// => null
v.string.check()(1);
// => 1
v.string.check()('1');
// => '1'
v.string.check()([1, 2]);
// => null
Processors
Processes input value. No input types check. Recommended to use validators before.
clamp<T>(min: T, max: T): Validator<T, T>
Clamps value to required boundaries.
import * as v from 'baridetta';
v.clamp(0, 5)(2);
// => 2
v.clamp(0, 5)(-2);
// => 0
v.clamp(0, 5)(7);
// => 5
v.clamp('c', 'e')('d');
// => 'd'
v.clamp('c', 'e')('a');
// => 'c'
v.clamp('c', 'e')('f');
// => 'e'
erase<T>(): Validator<T, null>
Erase input.
import * as v from 'baridetta';
v.erase()(2);
// => null
keysMap<T extends ObjectLike>(mapper: (key: string) => string): Validator<T, T>
Maps object keys with custom mapper.
import * as v from 'baridetta';
v.keysMap((key: string) => `_${key}`)({ f1: 'abc', f2: 10 });
// => { _f1: 'abc', _f2: 10 }
v.keysMap((key: string) => key.toUpperCase())({ f1: 'abc', f2: 10 });
// => { F1: 'abc', F2: 10 }
v.keysMap((key: string) => key === 'f1' ? 'f2' : key)({ f1: 'abc' }); // moves/renames field
// => { f2: 'abc' }
lowercase(): Validator<string, string>
Lowercase input string.
import * as v from 'baridetta';
v.lowercase()('ABC');
// => 'abc'
random(min: number, max: number, precision: number): Validator<any, number>
Returns random value according to params.
import * as v from 'baridetta';
v.random()(null);
// => in [0...1]
v.random(5, 10)(null);
// => in [5...10]
v.random(5, 10, 0)(null);
// => in [5, 6, 7, 8, 9, 10]
v.random(0, 1, 0)(null);
// => in [0, 1]
round(method?: 'floor' | 'ceil'): Validator<number, number>
Round input number with specific method.
import * as v from 'baridetta';
v.round()(10);
// => 10
v.round()(10.2);
// => 10
v.round()(9.8);
// => 10
v.round('floor')(10);
// => 10
v.round('floor')(10.2);
// => 10
v.round('floor')(9.8);
// => 9
v.round('ceil')(10);
// => 10
v.round('ceil')(10.2);
// => 11
v.round('ceil')(9.8);
// => 10
strip<T extends ObjectLike, K>(field: string | RegExp, condition: boolean | ((value: K) => boolean) = true): Validator<T, T>
Removes field from object conditionally.
import * as v from 'baridetta';
v.strip('f1')({ f1: 'abc', f2: 10 });
// => { f2: 10 }
v.strip('f1', false)({ f1: 'abc', f2: 10 });
// => { f1: 'abc', f2: 10 }
v.strip('f1', (value: string) => value === 'abc')({ f1: 'abc', f2: 10 });
// => { f2: 10 }
v.strip('f1', (value: string) => value === 'a')({ f1: 'abc', f2: 10 });
// => { f1: 'abc', f2: 10 }
v.strip(/f1|f2/, (value: any) => value === null)({ f1: null, f2: 10 });
// => { f2: 10 }
v.strip(/f1|f2/, (value: any) => value === null)({ f1: null, f2: null });
// => {}
v.strip(/f1|f2/)({ f1: 10, f2: 'abc' });
// => {}
trim(method?: 'left' | 'right'): Validator<string, string>
Trim input string with specific method.
import * as v from 'baridetta';
v.trim()(' abc ');
// => 'abc'
v.trim('left')(' abc ');
// => 'abc '
v.trim('right')(' abc ');
// => ' abc'
uppercase(): Validator<string, string>
Uppercase input string.
import * as v from 'baridetta';
v.uppercase()('abc');
// => 'ABC'
Groupers
Groups validators in a specific way.
consecutive<T>(...validators: Array<Validator<any, T>>): Validator<any, T>
Groups validators sequentially. Passes value through a sequence of validators until an error occurs. Uses by default in 'object' and 'object2' validator's scheme for fields.
import * as v from 'baridetta';
const unchi = (
v.consecutive(
v.number(),
v.gte(0)
)
);
unchi(10);
// => 10
unchi(-1);
// => null
unchi('a');
// => null
or<T>(...validators: Array<Validator<any, any>>): Validator<any, any>
Groups validators sequentially. Searches for first successful validator's result.
import * as v from 'baridetta';
const unchi = (
v.or(
v.number(),
v.bool()
)
);
unchi(10);
// => 10
unchi('true');
// => 'true'
unchi('abc');
// => null
parallel<T>(...validators: Array<Validator<T>>): Validator<T>
Groups validators in parallel. The main goal is to catch all errors (pass value through a sequence of validators, even if an error occurred somewhere). Beware of using processors inside.
import * as v from 'baridetta';
const unchi = (
v.withErrors(
v.parallel(
v.lte(10, 'ERR1'),
v.gte(0, 'ERR2'),
v.integer('ERR3')
)
)
);
unchi(10);
// => { result: 10, errors: null }
unchi(-1);
// => { result: null, errors: ['ERR2'] }
unchi(11);
// => { result: null, errors: ['ERR1'] }
unchi(11.2);
// => { result: null, errors: ['ERR1', 'ERR3'] }
transform<T, R>(...processors: Array<Validator<T | R, R>>): Validator<T | R, R>
Groups processors sequentially. Passes value through a sequence of processors. Takes only processors (doesn't check errors).
import * as v from 'baridetta';
const unchi = (
v.transform(
v.round(),
v.clamp(0, 10)
)
);
unchi(10.5);
// => 10
unchi(8.3);
// => 8
const niUnchi = (
v.transform((value: any) => value + 1) // custom transform.
);
niUnchi(10.5);
// => 11.5
niUnchi(8.3);
// => 9.3
Containers
Embraces validators with additional data processing.
withErrors<T, R>(validator: Validator<T, R>, commonErrorProcessor?: ((error?: Error, meta?: MetaData) => Error)): Validator<T, Result<R>>
Provides error handling mechanism.
import * as v from 'baridetta';
const unchi = (
v.withErrors(
v.parallel(
v.lte(10, 'ERR1'),
v.gte(0, 'ERR2'),
v.integer('ERR3')
)
)
);
unchi(10);
// => { result: 10, errors: null }
unchi(-1);
// => { result: null, errors: ['ERR2'] }
unchi(11);
// => { result: null, errors: ['ERR1'] }
unchi(11.2);
// => { result: null, errors: ['ERR1', 'ERR3'] }
withFallback<T, R>(fallback: R | ((initialValue: T, meta?: MetaData) => R), ...validators: Array<Validator<T | R, R>>): Validator<T | R, R>
Provides fallback value on error.
import * as v from 'baridetta';
const simpleOne = (
v.withFallback('fallback', v.string(), v.minLen(10))
);
simpleOne(null);
// => 'fallback'
simpleOne('');
// => 'fallback'
simpleOne('Stringu'); // too short.
// => 'fallback'
simpleOne('Stringuuuuuuuuuu');
// => 'Stringuuuuuuuuuu'
withMeta<T, R>(validator: Validator<T, R>): Validator<T, R>
Provides meta structure.
import * as v from 'baridetta';
const unchi = (
v.withErrors(
v.withMeta( // provides meta object into schema.
v.parallel(
v.lte(10, ({ validator }) => validator), // returns validator name as error.
v.gte(0, ({ validator }) => validator),
v.integer(({ validator }) => validator)
)
)
)
);
unchi(10);
// => { result: 10, errors: null }
unchi(-1);
// => { result: null, errors: ['gte'] }
unchi(11);
// => { result: null, errors: ['lte'] }
unchi(11.2);
// => { result: null, errors: ['lte', 'integer'] }
withOnError<T, R>(errorProcessor: ErrorCallback, ...validators: Array<Validator<any, T>>): Validator<T, R>
Provides custom error handler.
import * as v from 'baridetta';
const unchi = (
v.withOnError(
(error) => { console.error(error); },
v.parallel(
v.lte(10, 'ERR1'),
v.gte(0, 'ERR2'),
v.integer('ERR3')
)
)
);
unchi(10);
// => 10
unchi(-1);
// => null
// console.error => 'ERR2'
unchi(11);
// => null
// console.error => 'ERR1'
unchi(11.2);
// => null
// console.error => 'ERR1'
// console.error => 'ERR3'
withPromise<T, R>(validator: Validator<T, R | Result<R>>): Validator<T, Promise<R | Array<Error>>>
Convert result to promise. Use it for async validation.
import * as v from 'baridetta';
const unchi = (
v.withPromise(
v.number('ERR')
)
);
await unchi(10);
// => 10
await unchi('abc'); // error only works with 'withError' container.
// => null
const withErrorUnchi = (
v.withPromise(
v.withErrors(
v.number('ERR')
)
)
);
await withErrorUnchi(10);
// => 10
try {
await withErrorUnchi('abc');
} catch (errors) {
// => ['ERR']
}
Spreaders
Spreads data through a validators scheme. Almost all spreaders requires meta schema to be provided with 'withMeta'.
getDep<T>(field: string, preValidator?: (dep: T) => Validator<T> | Array<Validator<T>>): Validator<T>
Takes value from spreaded structure. Might be used for dynamic validators creation. If 'preValidator' not provided, just replaces current value. Works only with provided meta object.
import * as v from 'baridetta';
const simpleOne = (
v.withMeta(
v.object({
pass: [v.string(), v.minLen(10), v.setDep('pass')],
pass2: [v.getDep('pass', (pass: string) => v.equal(pass))] // Compares password and password confirmation
})
)
);
simpleOne({ pass: 'YourAwesomePassword', pass2: 'YourAwesomePassword' });
// => { pass: 'YourAwesomePassword', pass2: 'YourAwesomePassword' }
simpleOne({ pass: 'YourAwesomePassword', pass2: 'YourAwesomePass..' });
// => { pass: 'YourAwesomePassword', pass2: null }
simpleOne({ pass: 'Your...', pass2: 'YourAwesomePassword' });
// => { pass: null, pass2: null }
setDep<T>(field: string, extValue?: any | ((value: T, meta?: MetaData) => any)): Validator<T>
Puts value into spreaded structure. If 'extValue' is provided, puts it instead of current value.
import * as v from 'baridetta';
v.withMeta( // meta schema required for dependencies.
v.object({
id: [v.number(), v.gte(0), v.setDep('id')] // if 'id' is valid, sets 'id' dependency into schema.
})
);
v.withMeta(
v.object({
id: [v.number(), v.gte(0), v.setDep('isIdValid', true)] // custom data for dependency.
})
);
v.withMeta(
v.consecutive(
v.setDep('beforeObjectValidation', true), // non conditional dependency.
v.object({
id: [v.number(), v.gte(0)]
})
)
);
setVDep<T>(field: string, ...validators: Array<Validator<T>>): Validator<T>
Puts validators into spreaded structure. Might be used for recursive schemes.
import * as v from 'baridetta';
const recursiveOne = (
v.withMeta( // meta schema is required.
v.setVDep('node', // sets validators into meta schema.
v.object({
id: [v.number(), v.gte(0)],
node: v.getDep('node', validators => validators)
})
)
)
);
recursiveOne({ id: 1, node: { id: 2, node: { id: 3, node: { id: 4 } } } });
// => { id: 1, node: { id: 2, node: { id: 3, node: { id: 4, node: null } } } }
recursiveOne({ id: 1, node: { id: -1, node: { id: 3, node: { id: 4 } } } });
// => { id: 1, node: { id: null, node: { id: 3, node: { id: 4, node: null } } } }
recursiveOne({ id: 1, node: { id: -1, node: [1] } });
// => { id: 1, node: { id: null, node: null } }
useDefault<T, R>(defaultValue: R | ((meta?: MetaData) => R), ...validators: Array<Validator<T | R, R>>): Validator<T | R, R>
Puts default value into spreaded structure. If input value is empty, puts default value instead, otherwise validates input values with provided validators. If you need fallback value on error use 'withFallback' container instead.
import * as v from 'baridetta';
const simpleOne = (
v.useDefault('default', v.string(), v.minLen(10))
);
simpleOne(null);
// => 'default'
simpleOne('');
// => 'default'
simpleOne('Stringu'); // too short.
// => null
simpleOne('Stringuuuuuuuuuu');
// => 'Stringuuuuuuuuuu'
Custom validators
You can create your own validator or processor.
Base validator template:
yourValidatorName(...yourProbableParams: Array<any>, error?: Error): Validator<any> =>
(
(value: any, onError?: ErrorCallback, meta?: MetaData): any =>
(
... check input value
)
? value : (onError && onError(error, meta), null)
);
Simple example:
const gte = (bound: number, error?: Error): Validator<number> =>
(
(value: number, onError?: ErrorCallback, meta?: MetaData): number =>
(
value >= bound
)
? value : (onError && onError(error, meta), null)
);
You must provide validator name and params into meta scheme for proper errors handling.
... onError(error, meta && { ...meta, validator: 'name', params: [... your params] }) ...
Processor injection example:
import * as v from 'baridetta';
const simpleOne = (
v.consecutive(
v.array([
v.number(),
v.gte(0)
]),
(data: Array<number>) => data.filter(value => !!value) // Remove null values.
)
);
Examples
All examples use advanced object schema 'object2' as recommended solution.
Schema with custom user errors
Custom error on each validator:
v.withErrors(
v.object2([
['id',
v.empty.not('Empty id'),
v.number('Not a number'),
v.parallel(
v.gte(0, 'Must not be negative'),
v.integer('Must be an integer')
)
],
['name',
v.empty.not('Empty name'),
v.string(),
v.minLen(10, 'Min length is 10')
]
])
)
Schema with common error processor
Each error will be represented as { path, validator, error }
:
v.withErrors(
v.object2([
['id',
v.empty.not(),
v.number('Custom error message'), // wanna add some info for common error processor?
v.parallel(
v.gte(0),
v.integer()
)
],
['name',
v.empty.not(),
v.string(),
v.minLen(10)
]
]), (error, { path, validator }) => ({ path, validator, error }) // catches all errors in the schema.
)
Fields validation
Before validation checks required fields existence:
v.consecutive(
v.fields(['&', ['^', 'id', 'guid'], 'login']),
v.object2([
['id', v.number(), v.gte(0)],
['guid', v.string(), v.len(36)],
['login', v.string(), v.minLen(10)]
])
)
Conditional validation
Id can be an integer or a GUID:
v.object2([
['id', v.or(
v.consecutive(v.number(), v.integer(), v.gte(0)),
v.consecutive(v.string(), v.len(36)) // !notice: prefer to use 'regex' for GUID validation.
)],
['name', v.string(), v.minLen(10)]
])
Conditional validators usage
v.withMeta(
v.object2([
['id', v.number(), v.gte(0), v.setDep('isIdValid', true)],
['name', getDep(
'isIdValid',
(isIdValid: boolean) => isIdValid && [v.string(), v.minLen(10)]
)]
])
)
Injections
Array with custom processor injection
v.consecutive(
v.array(
v.object2([
['id', v.number(), v.gte(0)],
['name', v.string(), v.minLen(10), v.regex.not(/invalid_name_regex/)]
])
),
(data: Array<number>) => data.filter(value => !!value)
)
Fields strip
Removes unnecessary fields
v.consecutive(
v.object2([
['id', v.number(), v.integer(), v.gte(0)],
['name', v.string(), v.minLen(10)],
[/createdAt|updatedAt/, v.date()]
]),
v.strip('role'), // just removes one field.
v.strip('address', (address: string) => address === null), // removes if empty.
v.strip(/createdAt|updatedAt/, () => (/* condition */)), // removes matched fields conditionally.
)
Keys transformations
Camelize object fields
v.consecutive(
v.object2([
['--id--', v.number(), v.integer(), v.gte(0)],
['--name--', v.string(), v.minLen(10)]
]),
v.keysMap(_.camelCase) // e.g. using lodash
)