Most functional javascript object validation library
Sculp is a library that will help you cast and/or validate input data according to your schema. Typical applications include form management, validating object before saving to DB, casting HTTP request parameters to proper types, and many more.
Compared to other object validation libraries Sculp provides unique features such as conditional validation rules, incremental validation and object structure reuse.
- Features
- Installation
- Example
- Schema structure
- API
- Custom types and validations
- Field state object
- Declarative schema. Schemas are simple objects, with a very easy-to-read structure. This means you can reuse them, clone them, extend them, and do with them whatever you want.
- Support for custom types and validation rules. Any crazy rules you need. Array should be of odd length and its items should start with an underscore? No problem.
- Conditional rules. Some field is present only if some checkbox field is on? No problem.
- Fast incremental validation. You need to validate some gigantic form on the fly while user is editing it? Incremental validation ensures that only necessary fields and validation rules are recalculated.
- Object structure reuse. To find out if your objects or validation rules have changed after last update by user, all you need is strict comparison. This makes this library very React-friendly.
$ npm install --save sculp
import { validate, Type, Presence } from 'sculp';
const schema = {
type : Type.OBJECT,
properties : {
name : {
type : Type.STRING,
$lengthmin : 1
$presence : Presence.REQUIRED
},
gender : {
type : Type.STRING_NET,
transform : (v) => v && v.toLowerCase(),
$values : [ 'male', 'female' ]
},
age : {
type : Type.NUMBER,
$min : 0
},
isAdult : {
type : Type.BOOLEAN,
compute : (fieldAccessor) =>
fieldAccessor('^.age') >= 18
}
}
}
const result = validate({ name : 'John', gender : ' MALE ', age : '21' }, schema);
// returns { name : 'John', gender : 'male', age : 21, isAdult : true }
const result = validate({ age : -5 }, schema);
// throws sculp.ValidationError with errors array
This example schema defines the following constraints:
name
property- should be string
- should be not empty (minimum length is 1)
- should be defined
gender
property- should be string
- will be trimmed and converted to lowercase
- should be one of 2 values ("male", "female")
age
property- should be not negative
isAdult
property- boolean computed value
Schemas are plain javascript objects that can have following definition properties:
The type of a value. Should be string value.
If type is not defined is defaults to Type.ANY_VALUE
.
There are 11 available types and you can define your own new types through defining custom casts.
Predefined types available under Type
enumeration:
import { Type } from 'sculp';
List of available types:
Type.STRING
String type.Type.STRING_NET
String-not-empty-and-trimmed type. The same as Type.STRING but value gets trimmed and removed if it is an empty string.Type.NUMBER
Number type.Type.BOOLEAN
Boolean type.Type.FUNCTION
Function type.Type.DATE
Date type.Type.OBJECT
Object type. For this type you need to provide schemas for all properties usingproperties
field.Type.GROUP
The same asType.OBJECT
but this type represents an object as just a group of fields and not as some entity. The difference is the validation of properties forundefined
values. ForType.GROUP
properties are validated and forType.OBJECT
they are not.Type.ARRAY
Array type. For this type you need to provide schema for array item usingitems
field.Type.ANY_VALUE
Value of any type.Type.ANY_OBJECT
Object with any properties. Noproperties
field needed.
The name of a value/field. Should be string or function returning string.
Any additional information that you want to attach to this value/field.
Can be defined for Type.NUMBER
. Truncates the value to specified precision.
If any validation (other than type validation) fails on this field, value will be replaced by valid
value if it is defined.
Provide initial value. You can get initial value for whole schema with getInitial(schema)
function.
Used for Type.ARRAY
, Type.OBJECT
and Type.GROUP
only. Overrides removeEmpty
option for this value/field.
If removeEmpty
is true empty objects and arrays are removed.
Default value is false
.
Used for Type.OBJECT
and Type.GROUP
only.
The value should be an object that provides schemas for available properties in the form of { prop1 : prop1Schema, prop2 : prop2Schema, ... }
.
Used for Type.ARRAY
only. The value should be a schema for array items.
A function or array of functions to transform value (e.g. convert string to lowercase). Those functions take current value as an argument and should return transformed value.
A function of array of functions to compute value (e.g. set object field value to some value derived on the values of other fields). Those functions take field accessor function as an argument and should return computed value.
All schema properties that start with $
are validation rules. There are several predefined validations, and you can provide your custom validations using options or defining $custom
validation.
Any validation rule expects some rule value that value will be validated against. For example for $values
rule value is expected to be an array of possible values ({ $values : [ 1, 2, 3 ] }
).
The value $...
property should be a rule value or a function computing rule value.
Example:
const schema = {
type : Type.OBJECT,
properties : {
string : {
type : Type.STRING,
$lengthmin : 5,
$presence : {
value : Presence.REQUIRED,
message : '"String" property is required!'
}
},
longerString : {
type : Type.STRING,
$lengthmin : (fa) => {
const stringPropValue = fa('^.string') || '';
return stringPropValue.length + 1;
}
}
}
}
Available validations:
$presence
Validates value presence. Available rule values arePresence.REQUIRED
,Presence.OPTIONAL
andPresence.ABSENT
.$values
Validates that value is one of provided values. Rule value should be an array.$regexp
Validates that value matches regular expression. Rule value should be a string or RegExp.$length
Validates length of a value. Can be used for strings and arrays. Rule value is a number for exact length or an object withmin
and/ormax
properties for providing min and max limits (e.g.$length : { min : 1, max : 10 }
).$lengthmin
Validates that value.length >= rule value. Can be used for strings and arrays.$lengthmax
Validates that value.length <= rule value. Can be used for strings and arrays.$ne
Validates that value !== rule value.$min
Validates that number value >= rule value.$max
Validates that number value <= rule value.$gt
Validates that number value > rule value.$lt
Validates that number value < rule value.
The $custom
property value should be a function that returns error message string when validation fails and undefined
otherwise.
Function takes field accessor function as a first argument.
Example:
const schema = {
type : Type.STRING,
$custom : (fa) => {
const value = fa() || '';
return value[0] == 'A' ? undefined : 'String should start with an A letter'
}
}
validate(value: any, schema: object, ?options: object): any
Validates value
with schema
and returns validated value or throws ValidationError
when validation fails. Takes options as an optional argument.
Example:
validate('valid str', { type : Type.STRING, $length : 9 }, { strict : true });
// returns string 'valid str'
validate('invalid str', { type : Type.STRING, $length : 9 }, { strict : true });
// throws ValidationError
tryValidate(value: any, schema: object, ?options: object): object
Not throwing version of validate
.
Validates value
with schema
and returns object with result
, errors
and fieldsState
fields. Takes options as an optional argument.
getSchemaValue(value: any, schema: object, path: string, property: string): any
getFieldName(value: any, schema: object, path: string): string
getFieldPresence(value: any, schema: object, path: string): string
TODO : write description The use of this functions is discouraged.
getInitial(schema: object): any
Get initial value for schema. Constructed from initial
property values of schema.
getSubSchema(schema: object, path: string): object
Get sub-schema for schema.
Example:
getSubSchema(schema, '.some.nested.field'); // returns schema for subfield 'some.nested.field'
setDefaultOptions(options: object): undefined
Sets application-wide default options for validate
and tryValidate
methods as well as Sculp
constructor.
Example:
setDefaultOptions({
strict : true,
casts : { ... },
messages : russianLangMessages
});
Sculp(value: any, schema: object, ?options: object): undefined
Constructs new Sculp
instance.
Sculp instance allows you to perform fast incremental validations on your value when it changes. Takes options as an optional argument.
Example:
const value = { ... };
const sculp = new Sculp(value, schema);
sculp.validate(); // returns validation result for value
sculp.setField('.some.deep.nested.field', newValue); // mutates some field of value object
sculp.validate(); // returns validation result for current value
Sculp.prototype.validate(): any
The same as static validate
but runs validation on current value using schema
and options
provided to Sculp constructor.
Returns validated value or throws ValidationError
when validation fails.
Example:
const sculp = new Sculp({ field : 1 }, schema);
sculp.setField('.field', 2); // mutates field of value object
sculp.validate(); // returns validation result for current value
Sculp.prototype.tryValidate(): object
The same as static tryValidate
but runs validation on current value using schema
and options
provided to Sculp constructor.
Returns object with result
, errors
and fieldsState
fields.
Example:
const sculp = new Sculp({ field : 1 }, schema);
sculp.setField('.field', 2); // mutates field of value object
sculp.tryValidate(); // returns object with validation result and errors
Sculp.prototype.getValue(): any
Returns current value.
Example:
const sculp = new Sculp({ field : 1 }, schema);
sculp.setField('.field', 2);
sculp.getValue(); // returns { field : 2 }
Sculp.prototype.setValue(newValue: any): undefined
Replaces current value and clears all internal Sculp instance caches.
Example:
const sculp = new Sculp({ field : 1 }, schema);
sculp.validate(); // returns validation result for { field : 1 }
sculp.setValue({ newField : 2 }); // replaces
sculp.validate(); // returns validation result for { newField : 2 }
Sculp.prototype.setField(path: string, value: any): undefined
Sculp.prototype.setFields(values: object): undefined
Mutates current value according to provided parameters.
setField
mutates one field, setFields
can mutate several fields at once.
Note that path
parameter and values
keys for mutating fields must start with a dot (i.e. '.field'
and { '.field' : newValue }
).
Example:
const sculp = new Sculp({ list : [ {} ] }, schema);
sculp.setField('.list[0].a', 1);
sculp.setFields({ '.list[0].b' : 2, '.list[0].c' : 3 });
sculp.getValue(); // returns { list : [ { a : 1, b : 2, c : 3 } ] }
Sculp.prototype.getFieldState(path: string): object
Returns field state object for path path
of current value.
See Field state object.
Sculp.prototype.getSchemaValue(path: string, property: string): any
Sculp.prototype.getFieldName(path: string): string
Sculp.prototype.getFieldPresence(path: string): string
When you need to get value of some schema definition property or validation rule you can use getSchemaValue
method. Useful to get values of conditional rules. For example, if you have conditional $presence
rule for some field you can get current presence with getSchemaValue(path, '$presence')
.
Example:
const sculp = new Sculp(value, schema);
sculp.getSchemaValue('.someField', '$values');
sculp.getFieldName('.someField'); // the same as sculp.getSchemaValue('.someField', 'name');
sculp.getFieldPresence('.someField'); // the same as sculp.getSchemaValue('.someField', '$presence');
Field accessor is a function that is passed to rule value functions and compute
functions. Field accessor allows you to access other field values.
Field accessor usage:
fieldAccessor(); // returns value of current field
fieldAccessor('.prop'); // returns field 'prop' of current field
fieldAccessor('^.prop'); // returns field 'prop' of parent value
fieldAccessor('^^.prop'); // returns field 'prop' of root value
Examples:
const schema = {
type : Type.OBJECT,
properties : {
a : { type : Type.STRING },
b : {
type : Type.OBJECT,
properties : {
c : {
compute : (fa) => 'prefix' + fa() // returns value of this field
},
d : {
compute : (fa) => fa('^^.a') // returns field 'a'
},
e : {
compute : (fa) => fa('^.c') // returns field 'b.c'
}
}
}
}
}
These options can be provided to validate
, tryValidate
static functions and Sculp
constructor.
If you need to set options application-wide use setDefaultOptions
function.
For i18n purposes you can provide your custom strings to use as error messages. Only English and Russian message strings are provided with the library right now. Default value is an object with English language error messages.
Example:
import russianMessages from 'sculp/lib/i18n/ru';
validate(value, schema, { messages : ruMessages });
// or
setDefaultOptions({ messages : ruMessages });
By default Sculp will try to cast value to required type. For example it will return 42 when validating string '42' with { type : Type.NUMBER } schema.
If you don't want Sculp to try to cast values to required types you can set strict
option to true
.
Default value is false
.
Option for providing casts for custom types. See Custom types and validations.
Option for providing custom validations. See Custom types and validations.
Custom types are provided with casts
and castsStrict
options.
You should provide a cast function for each custom type. If value is not of your custom type your cast function should return special CAST_ERROR value.
casts
option is used for strict = false
(default value) validations and castsStrict
is used when strict = true
.
Custom validations are provided with validations
option. Validation function takes field accessor as first argument and validation rule value as second. Validation function should return error message string when validation fails and undefined
otherwise.
Example:
import { validate } from 'sculp';
import { CAST_ERROR } from 'sculp/lib/enums';
const options = {
casts : {
'INTEGER' : (v) => {
if (isNumber(v) && v === Math.round(v))
return v;
return CAST_ERROR;
}
},
validations : {
'divisible' : (fa, ruleValue) => {
if (value && value % ruleValue !== 0)
return `value is not divisible by ${ruleValue}`;
}
}
};
const schema = {
type : 'INTEGER',
$divisible : 3
}
validate(9, schema, options);
TODO: document.
- Partial validation support
- Computed schemas support
- Passing context
- Add popular validation rules
MIT © dettier