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.jsfile from the latest release and include it in your project. - Link to it from UNPKG CDN
https://unpkg.com/vahvista/dist/index.iife.jsorhttps://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.jsorhttps://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 valuenullorundefined.value: is the value a simple type such as boolean, number, bigint, or string or boxed instance.boolean: is the value a boolean or boxedBooleaninstance.number: is the value a number of boxedNumberinstance.nan: is the value aNaN.symbol: is the value a symbol.arrayLike: is the value array-like, an object withlengthand indexed elements.array: is the value anArray.string: is the value a string or boxedStringinstance.arrayBuffer: is the value anArrayBufferinstance.buffer: is the value aBufferinstance.dataView: is the value aDataViewinstance.typedArray: is the value one of the typed array, such asUint32Array,Int32Array,Float64Array, etc.uint32Array: is the value anUint32Arrayinstance.uint16Array: is the value anUint16Arrayinstance.uint8Array: is the value anUint8Arrayinstance.int32Array: is the value anInt32Arrayinstance.int16Array: is the value anInt16Arrayinstance.int8Array: is the value anInt8Arrayinstance.float64Array: is the value anFloat64Arrayinstance.float32Array: is the value anFloat32Arrayinstance.uint8ClampedArray: is the value anUint8ClampedArrayinstance.function: is the value a function.regExp: is the value aRegExpinstance.object: is the value an object.error: is the value anErroror derivative instance.date: is the value aDateinstance.iterable: is the value an iterable object.iterator: is the value anIteratorimplementation.promise: is the value aPromiseinstance or promise-like object.set: is the value aSetinstance.weakSet: is the value aWeakSetinstance.map: is the value aMapinstance.weakMap: is the value aWeakMapinstance.enum([...values]): is the value one of the providedvalues.typeOf(type): does the value identify astypewith thetypeofoperator. 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 toInfinityor-Infinity.infinite: is the value infinite, meaning equal toInfinityor-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 betweenminandmax.
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 havenenumerable elements.maxSize(max): does the value have no more thanmaxenumerable elements.minSize(min): does the value have at leastminenumerable 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. Arrays are definitely considered array-like, but objects may implement the semantics of an array.
length(n): does the value have alengthofn.maxLength(max): does the value have alengthno greater thanmax.minLength(min): does the value have alengthof 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 thetargetstring.endsWith(target): does the value end with thetargetstring.startsWith(target): does the value start with thetargetstring.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. Sets 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 anEvalErroror derivative instance.rangeError: is the value aRangeErroror derivative instance.referenceError: is the value aReferenceErroror derivative instance.syntaxError: is the value aSyntaxErroror derivative instance.typeError: is the value aTypeErroror derivative instance.uriError: is the value anURIErroror derivative instance.
Named objects
A named object is any object that has a name property. Some examples are functions, Errors, and Files.
name(match): does the valuenamematch thematchstring 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
definitionsor predicates. - An array with a single element containing a
definitionor 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
factoryandregisterdenote the expected input of the rule, though this is not guaranteed. - The generic argument for
Predicateis the confirmed type after being verified by the rule. - The generic argument
TforRuleis current type predicate chain.