README
vahvista
Simple composable validation framework
License
vahvista is licensed under the MIT license.
Getting Started
Requirements
Under node.js, or a bundler such as webpack or rollup; vahvista has no additional requirement.
Installation
Bundler or node build
If you want to use vahvista with a bundler like webpack or rollup, or with node.js;
- Install vahvista with your favorite package manager;
Browser ready or CDN build
If you wish to use vahvista in an environment that does not use a bundler or modules; you have two options
- Download the
vahvista.iife.js
file from the latest release and include it in your project. - Link to it from UNPKG CDN
https://unpkg.com/vahvista/dist/index.iife.js
orhttps://unpkg.com/vahvista@{version}/dist/index.iife.js
. See UNPKG for more versioning options. - Link to it from JSDELIVR CDN
https://cdn.jsdelivr.net/npm/vahvista/dist/index.iife.js
orhttps://cdn.jsdelivr.net/npm/vahvista@{version}/dist/index.iife.js
. See JSDELIVR for more versioning options.
Basic validation
To use vahvista, simply import vahvista from "vahvista";
. Or you may use a shorter name with
import v from "vahvista";
vahvista is relatively simple to use for validation. It does provide any messaging feedback, but does allow you to chain a set of rules together and verify that a value passes those rules.
Does x = 4?
v.equals(4)(x)
. Is x between 2 and 10 and an integer?v.integer.inRange(2, 10)(x)
Is name an all lower-case alphabetic name?v.alphabetic.lowerCase(name)
.
Core concepts
vahvista is based on two concepts, rules and predicates. Although these concepts externally seem the same, one builds the other.
Rules
Rules are the basic building blocks of vahvista validation. Rules are used to build predicates and verify some aspect of a value to be tested. Rules are wrapped into predicates by vahvista and accessed via their names from vahvista itself.
Predicates
A predicate is any object from vahvista than can be called to verify a value. Predicates may also be chained to form new predicates that will ensure a value is verified by all rules in the predicate chain.
Rules
Type checks
Basic type checking rules.
undefined
: is the valueundefined
.null
: is the valuenull
.nil
: is the valuenull
orundefined
.value
: is the value a simple type such as boolean, number, bigint, or string or boxed instance.boolean
: is the value a boolean or boxedBoolean
instance.number
: is the value a number of boxedNumber
instance.nan
: is the value aNaN
.symbol
: is the value a symbol.arrayLike
: is the value array-like, an object withlength
and indexed elements.array
: is the value anArray
.string
: is the value a string or boxedString
instance.arrayBuffer
: is the value anArrayBuffer
instance.buffer
: is the value aBuffer
instance.dataView
: is the value aDataView
instance.typedArray
: is the value one of the typed array, such asUint32Array
,Int32Array
,Float64Array
, etc.uint32Array
: is the value anUint32Array
instance.uint16Array
: is the value anUint16Array
instance.uint8Array
: is the value anUint8Array
instance.int32Array
: is the value anInt32Array
instance.int16Array
: is the value anInt16Array
instance.int8Array
: is the value anInt8Array
instance.float64Array
: is the value anFloat64Array
instance.float32Array
: is the value anFloat32Array
instance.uint8ClampedArray
: is the value anUint8ClampedArray
instance.function
: is the value a function.regExp
: is the value aRegExp
instance.object
: is the value an object.error
: is the value anError
or derivative instance.date
: is the value aDate
instance.iterable
: is the value an iterable object.iterator
: is the value anIterator
implementation.promise
: is the value aPromise
instance or promise-like object.set
: is the value aSet
instance.weakSet
: is the value aWeakSet
instance.map
: is the value aMap
instance.weakMap
: is the value aWeakMap
instance.enum([...values])
: is the value one of the providedvalues
.typeOf(type)
: does the value identify astype
with thetypeof
operator. Example are"string"
,"object"
.instanceOf(constructor)
: is the value an instance create by the provided class orconstructor
.
Values
Many values maybe compared with certain basic equality or relational comparisons. Such values include numbers and strings. Object references also may be strictly equal if a value references the exact same object.
oneOf(...values)
: is the value one of the providedvalues
.equal(target)
: is the value strictly equal totarget
.notEqual(target)
: is the value not strictly equal totarget
.greaterThan(target)
: is the value greater thantarget
.greaterThanOrEqual(target)
: is the value greater than or equal totarget
.lessThan(target)
: is the value less thantarget
.lessThanOrEqual(target)
: is the value less than or equal totarget
.
Boolean logic
The boolean rule can check whether a value is strictly a boolean value; or if they are truthy or falsy.
true
: is the value strictlytrue
.truthy
: is the value truthy.false
: is the value strictlyfalse
.falsy
: is the value falsy.
Numbers
negative
: is the value negative, meaning less than zero.positive
: is the value positive, meaning greater than zero.finite
: is the value finite, meaning not equal toInfinity
or-Infinity
.infinite
: is the value infinite, meaning equal toInfinity
or-Infinity
.uint32
: is the value within the range of the 32-bit unsigned integer, meaning inclusively between 0 and 4294967295.uint16
: is the value within the range of the 16-bit unsigned integer, meaning inclusively between 0 and 65535.uint8
: is the value within the range of the 8-bit unsigned integer, meaning inclusively between 0 and 255.int32
: is the value within the range of the 32-bit signed integer, meaning inclusively between -2147483648 and 2147483647.int16
: is the value within the range of the 32-bit signed integer, meaning inclusively between -32768 and 32767.int8
: is the value within the range of the 32-bit signed integer, meaning inclusively between -128 and 127.integer
: is the value an integer, meaning it has no fractional part.integerOrInfinite
: is the value an integer or infinite.inRange(min, max)
: is the value inclusively betweenmin
andmax
.
Collections
Collection are objects that have enumerable elements within them. Examples would be array-like objects with indexed
element within its length
, objects with named enumerable elements or properties, and maps with keyed elements.
empty
: is the value empty, meaning it is not a collection or contains no enumerable elements.notEmpty
: is the value not empty, meaning it contains one or more enumerable elements.size(n)
: does the value haven
enumerable elements.maxSize(max)
: does the value have no more thanmax
enumerable elements.minSize(min)
: does the value have at leastmin
enumerable elements.
Array-like objects
Array-like objects are values or objects with a readable length
property and indexed elements. One such value is a
string. Array
s are definitely considered array-like, but objects may implement the semantics of an array.
length(n)
: does the value have alength
ofn
.maxLength(max)
: does the value have alength
no greater thanmax
.minLength(min)
: does the value have alength
of at leastmin
.
Arrays
includes(...items)
: does the value include all the items.includesAny(...items)
: does the value include any of the items.
Strings
alphabetic
: is the value an alphabetic string.alphaNumeric
: is the value an alpha-numeric string.dateLike
: is the value a date-like string, meaning that it may be parsed withDate#parse
.numeric
: is the value a numeric string.integral
: is the value an integer string.lowerCase
: is the value a completely lower-case string.upperCase
: is the value a completely upper-case string.url
: is the value a URL, meaning it can be parsed withnew URL
.contains(target)
: does the value contain thetarget
string.endsWith(target)
: does the value end with thetarget
string.startsWith(target)
: does the value start with thetarget
string.matches(expression)
: does the value match the regularexpression
.
Keyed collections
A keyed collection is any object that posses some means of access values based on a key. A key may be a string,
number, or with certain collections such as Map
, any kind of value. Objects and arrays may be considered keyed
collects since some string or numeric value may be used as a key to another value of any kind. Set
s may also be
considered keyed collection since a value is its own key.
has(...keys)
: does the value have all the specifiedkeys
.hasAny(...keys)
: does the value have any of the specifiedkeys
.
Errors
evalError
: is the value anEvalError
or derivative instance.rangeError
: is the value aRangeError
or derivative instance.referenceError
: is the value aReferenceError
or derivative instance.syntaxError
: is the value aSyntaxError
or derivative instance.typeError
: is the value aTypeError
or derivative instance.uriError
: is the value anURIError
or derivative instance.
Named objects
A named object is any object that has a name
property. Some examples are functions, Error
s, and File
s.
name(match)
: does the valuename
match thematch
string or regular expression.
Tuples
Tuples are collections of fixed length and element types. In JavaScript the closest thing to this would be an array of a
specific length
.
tuple(...predicates)
: does the array have the exact number of elements matches thepredicates
.
Object shapes
shape(definition)
tests whether a value conform to definition
.
The definition may be composed of the following:
- An object which contains properties with other
definitions
or predicates. - An array with a single element containing a
definition
or predicate. - A single lone predicate.
An example shape definition could be:
shape({
name: v.string.notEmpty.maxLength(45),
dataPoints: [v.integer.inRange(-256, 256)],
meta: v.object,
style: {
color: v.enum(knownColors),
fill: v.enum(knownColors),
thickness: v.number.finite,
},
})
Custom rules
vahvista may be extended with custom rules. This is done with vahvista.register
and vahvista.factory
.
Basic rules
Basic rule do not require any additional information to verify a value. They are simply a function that receives the
value and returns true
if the value passes or false
if it fails. An example of a basic rule may be one that ensure
an ID value is a string containing a positive integer.
v.register("id", value => (/\d+/u).test(value));
Rules with inputs
If a rule needs more information to verify a value, they may be given arguments. Such rules must be created from a "rule factory". A rule factory is a function that accepts the information needed for the rule to do its job and returns that rule with the needed information. An example of such a rule may be one that verifies a date is later than another.
v.factory("after", date => value => toDate(dateTime).valueOf() < toDate(value).valueOf());
// use as `v.after(Date.now())(tomorrow)` which would be true.
TypeScript
vahvista works well with TypeScript since it was built in TypeScript. There is one caveat of using vahvista in TypeScript that you must contend with when adding custom rules. You must tell TypeScript the predicate for those rules now exist in vahvista. To do so, you must use interface declaration merging. An example using those the prior two examples:
v.register<string>("id", value => (/\d+/u).test(value));
v.factory<string|Date>("after", (date: string|Date) => value => toDate(dateTime).valueOf() < toDate(value).valueOf());
declare module "vahvista" {
interface Rules<T> {
id: Predicate<string>;
after(date: string|Date): Predicate<string|Date>;
}
}
Other things to note about rules registered in TypeScript;
- The generic argument for
factory
andregister
denote the expected input of the rule, though this is not guaranteed. - The generic argument for
Predicate
is the confirmed type after being verified by the rule. - The generic argument
T
forRule
is current type predicate chain.