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
npm install baridetta
//or
yarn add baridetta
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 }
The main types used in the library.
Calls on validation error.
type ErrorCallback = (error: Error, meta?: MetaData, relevance?: Relevance) => void;
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)
);
Specification for 'fields' validator.
type FieldsSpec = (
string
| [
('&' | '|' | '^'),
FieldsSpec | string,
FieldsSpec | string,
...Array<FieldsSpec | string>
]
);
Internal data for errors and dependencies.
type MetaData = {
path: Array<string | number>;
validator?: string;
params: Array<any>;
_deps: Record<string, any>;
};
Specification for 'object' and 'object2' validators.
type ObjectSpec = Record<string, Array<Validator<any, any>> | Validator<any, any>>;
Error's relevancy status.
type Relevance = {
value: boolean;
};
'WithError' container's result. Will be null if no errors.
type Result<T> = {
result: T;
errors?: Array<any>;
};
Validates value.
type Validator<T> = (value: T, onError?: ErrorCallback, meta?: MetaData) => T;
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
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
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
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
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
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
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).
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
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
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'
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
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 }
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 }
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
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' }
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
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
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
Processes input value. No input types check. Recommended to use validators before.
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 input.
import * as v from 'baridetta';
v.erase()(2);
// => null
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 input string.
import * as v from 'baridetta';
v.lowercase()('ABC');
// => 'abc'
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 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 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 input string.
import * as v from 'baridetta';
v.uppercase()('abc');
// => 'ABC'
Groups validators in a specific way.
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
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
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'] }
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
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'
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'
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']
}
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 }
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)]
})
)
);
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'
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.
)
);
All examples use advanced object schema 'object2' as recommended solution.
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')
]
])
)
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.
)
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)]
])
)
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)]
)]
])
)
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)
)
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.
)
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
)