README
adv-parser
Parser for special simplified syntax of json schema.
Default json schema
schema = {
type: "object",
additionalProperties: false,
required: ['id', /* "name", */ 'enabled', 'list', 'user', 'enumOfStrings'],
properties: {
id: {
type: "number",
},
name: {
type: "string",
},
enabled: {
type: "boolean",
},
list: {
type: "array",
items: {type: "number"}
},
user: {
type: "object",
additionalProperties: false,
required: ['id', 'type'],
properties: {
id: {type: 'number'},
type: {type: 'string'},
}
},
enumOfStrings: {
type: "string",
enum: ["user", "guest", "owner"]
},
}
}
Simplified syntax of same schema
schema = {
id: number,
[name]: string,
enabled: boolean,
list: [number],
user: {
id: number,
type: string,
},
enumOfStrings: "user" || "guest" || "owner",
}
- Usage
- Optional object fields
- Array syntax
- Number patterns
- String patterns
- Inject external schema
- anyOf schema
- allOf schema
- Extend schema
- Switch syntax
- Pure syntax
- Schema methods
- Schema options as methods
- Object schema inline options
Usage
const parser = require('adv-parser');
let schema = parser(`{id: number}`);
// or as arrow function (which will be converted to string and parsed)
// if you want free syntax highlighting
schema = parser(() => ({id: number}));
schema == {
type: 'object',
additionalProperties: false,
required: ['id'],
properties: {
id: {type: 'number'}
}
};
Schemas cache
const parser = require('adv-parser');
const defaultSchemas = require('adv-parser/schemas');
const schemas = {
...defaultSchemas
};
const schema1 = parser(`User = {id: number}`, {schemas});
const schema2 = parser(`Test.SubTest = {name: string}`, {schemas});
const schema3 = parser(Product => ({id: uuid}), {schemas});
const schema4 = parser(() => Company = {name: /^\w+$/}, {schemas});
schema1 == schemas.User;
schema2 == schemas['Test.SubTest'];
schema3 == schemas.Product;
schema4 == schemas.Company;
Custom methods
All methods work with the schema as AST. It gives you ability to create your own meta programming language
More about default methods see Schema methods and Schema options as methods
const parser = require('adv-parser');
const defaultMethods = require('adv-parser/methods');
const {set} = defaultMethods;
const schema = parser(`number.test(true)`, {
methods: {
...defaultMethods,
test: function (schema, args, params) {
return set(schema, ['test', args[0]], params);
}
}
});
schema == {
type: 'number',
test: true,
};
Custom functions
You can define custom functions like
const parser = require('adv-parser');
const t = require('@babel/types');
const schema = parser(`{id: test(1, 2)}`, {
functions: {
test: function (args) {
return t.numericLiteral(
args.reduce((sum, item) => sum + item.value, 0)
);
}
}
});
schema == {
type: 'object',
test: true,
additionalProperties: false,
required: ['id'],
properties: {
id: 3
}
};
Custom object inline options
More about default object inline options see Object schema inline options
const parser = require('adv-parser');
const defaultObjectOptions = require('adv-parser/methods/object');
const set = require('adv-parser/methods/set');
const schema = parser(`{id: number, $test: true}`, {
objectOptions: {
...defaultObjectOptions,
test: function (schema, args, params) {
return set(schema, ['test', args[0]], params);
}
}
});
schema == {
type: 'object',
test: true,
additionalProperties: false,
required: ['id'],
properties: {
id: {type: 'number'}
}
};
Optional object fields
By default, all fields in an object are required. To make field optional just put it in brackets.
schema = {
id: number,
[name]: string,
}
schema == {
type: "object",
additionalProperties: false,
required: ["id"],
properties: {
id: {type: "number"},
name: {type: "string"},
},
}
Array syntax
Here example of array where all items should be validated with one schema
schema = [number]
schema == {
type: 'array',
items: {type: 'number'}
}
Here example how we can validate items through many schemas
schema = [number || string || {id: number}]
schema == {
type: 'array',
items: {
anyOf: [
{type: 'number'},
{type: 'string'},
{
type: 'object',
additionalProperties: false,
required: ['id'],
properties: {
id: {type: 'number'}
}
},
]
}
}
Here index relative validation
schema = [number, string]
Which means that first element must be a number and second a string. Rest elements validation depends on array options like additionalItems
.
In this example valid will be: [1]
, [1, "abc"]
, [1, "abc", 2]
, []
. Not valid: ["abc", 1]
, ["abc"]
schema == {
type: 'array',
items: [
{type: 'number'},
{type: 'string'}
]
}
You can add any array option with it methods
schema = [number, string].additionalItems(false)
schema == {
type: 'array',
items: [
{type: 'number'},
{type: 'string'}
],
additionalItems: false,
}
If you need one index relative element validation than you can use items
method like
firstNumber = [].items([number])
firstString = array.items([string])
firstNumber == {
type: 'array',
items: [{type: 'number'}]
}
firstString == {
type: 'array',
items: [{type: 'string'}]
}
This example means that at least one element in an array must be valid
list = [...string]
listOr = [...(string || boolean)]
list == {
type: 'array',
contains: {type: 'string'},
}
listOr == {
type: 'array',
contains: {anyOf: [{type: 'string'}, {type: 'boolean'}]},
}
Combination of index relative validation and contains
schema = [number, ...(string || boolean)]
schema == {
type: 'array',
items: [
{type: 'number'}
],
contains: {anyOf: [{type: 'string'}, {type: 'boolean'}]},
}
Number patterns
Instead of short number
validator you can use one of following number patterns as value of object field.
int
number without floating-pointpositive
positive number including0
negative
negative number excluding0
id
integer more than0
schema = {
id: id,
price: positive,
list: [int],
}
schema == {
type: "object",
additionalProperties: false,
required: ['id', 'price', 'list'],
properties: {
id: {
type: "integer",
minimum: 1,
},
price: {
type: "number",
minimum: 0,
},
list: {
type: "array",
items: {
type: "integer",
}
},
},
}
String patterns
Instead of short string
validator you can use one of following string patterns as value of object field.
date
full-date according to RFC3339.time
time with optional time-zone.date-time
date-time from the same source (time-zone is optional, in ajv it's mandatory)date-time-tz
date-time with time-zone requireduri
full URI.uri-reference
URI reference, including full and relative URIs.uri-template
URI template according to RFC6570email
email address.hostname
host name according to RFC1034.filename
name (words with dashes) with extensionipv4
IP address v4.ipv6
IP address v6.regex
tests whether a string is a valid regular expression by passing it to RegExp constructor.uuid
Universally Unique Identifier according to RFC4122.
Also, regexp will be converted to {pattern: "regexp"}
schema = {
id: uuid,
email: email,
created_at: date-time,
phone: /^\+?\d+$/,
days: [date],
}
schema == {
type: "object",
additionalProperties: false,
required: ['id', 'email', 'created_at', 'phone', 'days'],
properties: {
id: {
type: "string",
format: "uuid",
},
email: {
type: "string",
format: "email",
},
created_at: {
type: "string",
format: "date-time",
},
phone: {
type: "string",
pattern: "^\\+?\\d+