AST

The Abstract Syntax Tree (AST) format for @zuze/schema is intuitive. It mirrors the functional API virtually completely.

Passing Arguments#

When it comes to tests and transforms, each item in the array is evaluated as a function name. Or, in the case of the item in the array being an array itself, the first item is used as the function name and all subsequent values in the array will be passed to it as arguments.

{
schema: 'array',
tests: [ 'required', ['min', 5, { message: 'At least 5 items required' } ] ],
transforms: [ 'compact' ]
}

Conditions#

Using the AST API conditions is an array of ASTCondition objects:

// ConditionObject
when: ObjectAST | ObjectAST[],
then?: Partial<AST>,
otherwise?: Partial<AST>

When when is an array, if ANY of the ObjectAST's are matched (using matches), the then will be applied (if present). If not, the otherwise will be applied (if present).

const schema = {
schema: 'mixed',
conditions: [
{
when: {
someField: { tests: [['is','jim']] }
},
then: {
schema: 'string',
tests: ['required']
},
otherwise: {
schema: 'number',
tests: [ ['min', 10] ]
}
}
]
}

Refs#

Refs are created in AST via ASTRef

const schema = {
schema: 'string',
conditions: [
{
when: {
fieldA: [['is',{ref:'fieldB'}]]
}
}
]
}

Custom Transforms/Validators#

By default, all transforms/validators available in @zuze/schema are available via the AST. But part of the beauty of @zuze/schema is being able to create your own transforms/validators.

When using the AST, each custom transform/validator must be a function (called with the options passed to createSchema(s)/matches that returns a function called with the arguments in the AST.

// validator
const customASTValidator = options => (...args) => ValidatorDefinition
// transform
const customASTTransform = options => (...args) => TransFormFunction

The user-supplied transforms/validators need to be given in options argument of createSchema(s)/matches.

const schema = {
schema:'mixed',
transforms:[ ['customASTTransform', 'arg1', 'arg2'] ],
tests:[ ['customASTValidator', 'arg1', 'arg2'] ]
}
const schema = createSchema(schema,{
transforms: { customASTTransform },
validators: { customASTValidator }
});

AST Transforms#

Some transforms in @zuze/schema have AST-specific implementations:

unique#

const schema = {
schema: 'array',
transforms: [['unique','id']]
}
const subject = [{ id: 1, a: 'a'},{id: 2, a: 'b'},{id: 1,a: 'c'}];
cast(createSchema(schema,subject)); // [ {id:1,a:'a'}, {id:2,a:'b'} ]

compact#

compact accepts a rejector function in the functional form. In the AST form it accepts an argument that will be passed to matches. Any value in the array that passes matches will be excluded.

const schema = {
schema: 'array',
transforms: [['compact', { tests:[['is', 'jim']] } ]]
}
const subject = ['first', 'jim', 'third', 9];
cast(createSchema(schema,subject)); // ['first', 'third', 9]

AST Validators#

There are some functional validators that require some tweaking to be mirrored by the AST - namely oneOfType, negate, and serial.

oneOfType#

const schema = {
schema: 'mixed',
tests: [['oneOfType',[
{
schema: 'string',
tests: [['min',5]]
},
{
schema: 'number',
tests: [['between',10,20]]
}
]]]
}

negate#

const schema = {
schema: 'number',
tests: [['negate',['between',10,20]]]
}

serial#

As you'll remember, serial is a validator that runs the ValidatorDefinitions passed to it sequentially, stopping after the first one fails. It's only necessary when dependent async validations.

const schema = {
schema: 'number',
tests: [['serial', [['between', 10, 20]]]]
}

API#

matches#

matches(AST | AST[], options?: ASTMatchesOptions): boolean | Promise<boolean>

Not to be confused with the matches validator:

import { ast } from '@zuze/schema';
const { matches } = ast;

matches runs synchronously BY DEFAULT unless sync:false is passed as an option. It is equivalent to running isValidSync on a SchemaDefinition

matches accepts AST or an array of ASTs and returns true (or a Promise resolving to true) if any of the ASTs are valid. If you pass {how:'all'} (see ASTMatchesOptions) as an option then it will only return true if all of the ASTs are valid.

const defs = [
{ schema:'string', tests:[['min',15]] },
{ schema:'string', tests:['email'] }
]
matches(defs, 'at least 15 chars'); // true
matches(defs, 'me@you.com'); // true
matches(defs, 'me@you.com', {how:'every'}); // false
matches(defs, 'me@muchlongeraddress.com', {how:'every'}); // true

createSchema#

createSchema(schema: AST, options?: ASTSchemaOptions): Schema

Converts an AST to a SchemaDefinition that can be passed to one of the functional methods like cast, validate/validateSync, isValid/isValidSync, validateAt/validateAtSync

createSchema({schema:'string'}); // equivalent to string()

createSchemas#

createSchemas(schemas: AST | AST[], options?: ASTSchemaOptions): Schema[]

Same as createSchema except it returns an array of SchemaDefinitions and can accept a single AST or an array

createSchema([{schema:'string'},{schema:'number'}]);
// returns eqivalent of [ string(), number() ]