рдЬрдм рдЪреМрдХрдбрд╝реА рдбреЗрдЯрд╛ рд╕рддреНрдпрд╛рдкрди рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдмрдирд╛рдИ рдЧрдИ рдереА , рддреЛ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕реНрдерд▓ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд┐рдП рдЧрдП рдереЗ:
рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдкреНрд░рджрд░реНрд╢рди quartet
рдФрд░ рдЗрд╕рдХреЗ рдХрд╛рд░рдгреЛрдВ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ ред
рдмреНрд░реЗрд╡рд┐рдЯрд┐рдЯреА рдПрдВрдб рд╕рд┐рдВрдкрд▓рд┐рд╕рд┐рдЯреА рдкрд░ рдПрдХ рд▓реЗрдЦ 4 рдЕрдкреНрд░реИрд▓ рдХреЛ рд╣реЛрдЧрд╛ред
рд╣рдо рдЗрд╕ рдкрд╣рд▓реВ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ quartet
рдФрд░ рдПрдХ рдФрд░ рдЕрдзрд┐рдХ рд▓реЛрдХрдкреНрд░рд┐рдп ajv рдХреЗ рдмреАрдЪ рдХреА рдЦреЛрдЬ рдХрд░реЗрдВрдЧреЗ ред
рдирдорд╕реНрддреЗ рджреБрдирд┐рдпрд╛
рд╣рдо рд╕рдмрд╕реЗ рд╕рд░рд▓ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦреЗрдВрдЧреЗ - рдХреНрдпрд╛ рдореВрд▓реНрдп рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реИ "рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб!"ред
рд╕рддреНрдпрд╛рдкрди рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдбреЗрдЯрд╛ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬрд┐рд╕реЗ рд╣рдо рдорд╛рдиреНрдп рдХрд░реЗрдВрдЧреЗред рддрджрдиреБрд╕рд╛рд░, рдЗрд╕ рдХрд╛рд░реНрдп рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╡реИрдз рдФрд░ рдЧреИрд░-рд╡реИрдз рдбреЗрдЯрд╛ рдХреЗ рдРрд╕реЗ рд╕реЗрдЯ рд╣реИрдВред
const valids = ["Hello World!"];
const invalids = [null, false, undefined, "", 1, Infinity, "Hello World"];
AJV
рд╣рдореЗрд╢рд╛ рдХреА рддрд░рд╣, рдпрд╣ рд╕рдм рдЖрдпрд╛рдд рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ:
const Ajv = require("ajv");
"рд╕рдВрдХрд▓рдХ" рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдмрдирд╛рдПрдБ:
const ajv = new Ajv();
Ajv рдЗрдирдкреБрдЯ JSON рдпреЛрдЬрдирд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рдорд╛рдиреНрдп рдкреНрд░рдХрд╛рд░ рдХреЗ рд╡рд┐рд╡рд░рдг рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ ред
рдЖрдЗрдП рдЕрдкрдиреЗ рдХрд╛рд░реНрдп рдХреЗ рд▓рд┐рдП рдПрдХ рдЙрдкрдпреБрдХреНрдд рдпреЛрдЬрдирд╛ рдмрдирд╛рдПрдВред
const helloWorldSchema = {
type: "string",
enum: ["Hello World!"]
};
рдЕрдЧрд▓рд╛, рд╕рддреНрдпрд╛рдкрди рдлрд╝рдВрдХреНрд╢рди рдХреЛ "рд╕рдВрдХрд▓рд┐рдд" рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдЕрд░реНрдерд╛рддреН, рд╕рд░реНрдХрд┐рдЯ рд╕реЗ, рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ рдЬреЛ рдЗрдирдкреБрдЯ рдбреЗрдЯрд╛ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдЧрд╛, рдФрд░ рдЖрдЙрдЯрдкреБрдЯ true
рд╕рдлрд▓ рд╣реЛрдиреЗ рдкрд░ рд╡рд╛рдкрд╕ рдЖ рдЬрд╛рдПрдЧрд╛, рдЕрдиреНрдпрдерд╛ рд╕рддреНрдпрд╛рдкрди рд╕рдлрд▓ рд╣реЛрдЧрд╛ false
ред
const ajvValidator = ajv.compile(helloWorldSchema);
рдХрд┐рдпрд╛ рд╣реБрдЖ!
benchmark.
, :
Ajv Build
661,639 ops/sec
354,725 ops/sec
628,443 ops/sec
659,900 ops/sec
557,037 ops/sec
: 572,349 ops/sec
:
for (let i = 0; i < valids.length; i++) {
ajvValidator(valids[i]);
}
for (let i = 0; i < invalids.length; i++) {
ajvValidator(invalids[i]);
}
:
Ajv Validation
21,452,228 ops/sec
3,066,770 ops/sec
4,522,850 ops/sec
2,522,777 ops/sec
2,741,310 ops/sec
: 6,861,187 ops/sec
тАФ , .
quartet
╤Ф ┬л┬╗:
const { v } = require("quartet");
:
const quartetValidator = v("Hello World!");
:
Quartet 9: Allegro Build
6,019,078 ops/sec
3,893,780 ops/sec
2,712,363 ops/sec
5,926,415 ops/sec
2,729,369 ops/sec
: 4,256,201 ops/sec
:
for (let i = 0; i < valids.length; i++) {
quartetValidator(valids[i]);
}
for (let i = 0; i < invalids.length; i++) {
quartetValidator(invalids[i]);
}
:
Quartet 9: Allegro Validation
15,073,432 ops/sec
13,711,573 ops/sec
13,123,812 ops/sec
25,617,225 ops/sec
17,588,846 ops/sec
: 17,022,977 ops/sec
:


:
console.log("Function");
console.log(quartetValidator.toString());
:
function (value) { return value === c; }
c
тАФ , .
. , , .
. API. , :
interface Person {
id: number;
name: string;
phone: string | null;
phoneBook: {
[name: string]: string;
};
gender: "male" | "female";
}
const valids = [
{
id: 1,
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 2,
name: "bohdan",
phone: null,
phoneBook: {},
gender: "male"
},
{
id: 3,
name: "Elena",
phone: null,
phoneBook: {
siroja: "380975003434"
},
gender: "female"
}
];
const invalids = [
null,
false,
undefined,
"",
1,
Infinity,
"Hello World",
{
id: 0,
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 1.5,
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 1,
name: "",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 1,
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 1,
name: "andrew",
phone: "38097500434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 1,
name: "andrew",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "male"
},
{
id: 1,
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "38097503434"
},
gender: "male"
},
{
id: 1,
name: "andrew",
phone: "380975003434",
gender: "male"
},
{
id: 1,
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
},
gender: "Male"
},
{
id: 1,
name: "andrew",
phone: "380975003434",
phoneBook: {
andrew: "380975003434",
bohdan: "380975003434",
vasilina: "380975003434"
}
}
];
ajv
:
const personSchema = {
type: "object",
required: ["id", "name", "phone", "phoneBook", "gender"],
properties: {
id: {
type: "integer",
exclusiveMinimum: 0
},
name: {
type: "string",
minLength: 1
},
phone: {
anyOf: [
{ type: "null" },
{
type: "string",
pattern: "^\\d{12}$"
}
]
},
phoneBook: {
type: "object",
additionalProperties: {
type: "string",
pattern: "^\\d{12}$"
}
},
gender: {
type: "string",
enum: ["male", "female"]
}
}
};
:
const ajvCheckPerson = ajv.compile(personSchema);
:
Ajv Build
79,476 ops/sec
78,334 ops/sec
61,752 ops/sec
77,395 ops/sec
78,539 ops/sec
51,922 ops/sec
80,031 ops/sec
77,687 ops/sec
65,439 ops/sec
79,805 ops/sec
: 73,038 ops/sec
:
for (let i = 0; i < valids.length; i++) {
ajvCheckPerson(valids[i]);
}
for (let i = 0; i < invalids.length; i++) {
ajvCheckPerson(invalids[i]);
}
:
Ajv Validation
227,640 ops/sec
301,134 ops/sec
190,450 ops/sec
195,595 ops/sec
384,380 ops/sec
193,358 ops/sec
385,280 ops/sec
239,009 ops/sec
193,832 ops/sec
392,808 ops/sec
: 270,349 ops/sec
quartet
:
const quartetCheckPerson = v({
id: v.and(v.safeInteger, v.positive),
name: v.and(v.string, v.minLength(1)),
phone: [null, v.test(/^\d{12}$/)],
phoneBook: {
[v.rest]: v.test(/^\d{12}$/)
},
gender: ["male", "female"]
});
:
Quartet 9: Allegro Build
35,564 ops/sec
14,401 ops/sec
15,438 ops/sec
26,852 ops/sec
33,935 ops/sec
16,010 ops/sec
34,550 ops/sec
33,148 ops/sec
16,037 ops/sec
36,828 ops/sec
: 26,276 ops/sec
:
for (let i = 0; i < valids.length; i++) {
quartetCheckPerson(valids[i]);
}
for (let i = 0; i < invalids.length; i++) {
quartetCheckPerson(invalids[i]);
}
, :
Quartet 9: Allegro Validation
237,059 ops/sec
435,844 ops/sec
248,021 ops/sec
238,931 ops/sec
416,993 ops/sec
281,904 ops/sec
439,975 ops/sec
242,074 ops/sec
330,487 ops/sec
421,704 ops/sec
: 329,299 ops/sec
:


, , quartetCheckPerson
.
console.log(quartetCheckPerson.toString());
console.log({ ...quartetCheckPerson });
function validator(value) {
if (value == null) return false
if (!Number.isSafeInteger(value.id)) return false
if (value.id <= 0) return false
if (typeof value.name !== 'string') return false
if (value.name == null || value.name.length < 1) return false
if (!validator["value.phone"](value.phone)) return false
if (value.phoneBook == null) return false
validator.keys = Object.keys(value.phoneBook)
for (let i = 0; i < validator.keys.length; i++) {
validator.elem = value.phoneBook[validator.keys[i]]
if (!validator["tester-1"].test(validator.elem)) return false
}
if (!validator["value.gender"](value.gender)) return false
return true
};
{
'value.phone': function validator(value) {
if (value === null) return true;
if (validator.tester.test(value)) return true;
return false
}
['value.phone']['tester']: /^\d{12}$/,
'tester-1': /^\d{12}$/,
'value.gender': function validator(value) {
if (validator.__validValuesDict[value] === true) return true
return false
},
['value.gender']['__validValuesDict']: {
male: true,
female: true
}
}
тАФ , тАФ .
:
ajv
тАФ ajv , errors
.
quartet
quartet
- .
:
1) v.custom
. тАФ , explanations
, . тАФ тАФ
:
const checkId = v(v.and(v.safeInteger, v.positive));
const checkName = v(v.and(v.string, v.minLength(1)));
const checkPhone = v([null, v.test(/^\d{12}$/)]);
const checkPhoneBookItem = v(v.test(/^\d{12}$/));
const checkGender = v(["male", "female"]);
const quartetCheckPerson = v({
id: v.custom(checkId, "id"),
name: v.custom(checkName, "name"),
phone: v.custom(checkPhone, "phone"),
phoneBook: {
[v.rest]: v.custom(checkPhoneBookItem, "phoneBook")
},
gender: v.custom(checkGender, "gender")
});
рдорд╛рдк рдХрд░реЗрдВ:


2) рджреВрд╕рд░реА рд╡рд┐рдзрд┐ рдЕрдзрд┐рдХ рд╡рд┐рдЪрд╛рд░рдзрд╛рд░рд╛рддреНрдордХ рд╣реИ - рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдХрд╕реНрдЯрдо рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ред рдЬреИрд╕реЗ рд╣реА рд╕рддреНрдпрд╛рдкрдирдХрд░реНрддрд╛рдУрдВ рдореЗрдВ рд╕реЗ рдХреЛрдИ рдПрдХ рдЧрд▓рдд рд╣реЛрддрд╛ рд╣реИ рддреНрд░реБрдЯрд┐рдмрд╛рдЙрдВрдбрд░реА рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдмреБрд▓рд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ред
import { quartet } from 'quartet'
const v = quartet({
errorBoundary(explanations, { value, id, schema, innerExplanations }) {
explanations.push(...innerExplanations, { value, id, schema })
}
})
const checkPerson = v({
id: v.and(v.safeInteger, v.positive),
name: v.and(v.string, v.minLength(1)),
phone: [null, v.test(/^\d{12}$/)],
phoneBook: {
[v.rest]: v.test(/^\d{12}$/)
},
gender: ["male", "female"]
});
рдорд╛рдк рдХрд░реЗрдВ:


рд╕рд╛рд░рд╛рдВрд╢
рддреБрд▓рдирд╛ рдорд┐рд╢реНрд░рд┐рдд рдкрд░рд┐рдгрд╛рдо рджреЗрддреА рд╣реИред рдЙрди рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЬрд┐рдиреНрд╣реЗрдВ рдкреНрд░рджрд░реНрд╢рди рдФрд░ рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ - ajv рдЪреБрдиреЗрдВред рдЙрди рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЬрд┐рдиреНрд╣реЗрдВ рд╡рд┐рдХрд▓рд╛рдВрдЧреЛрдВ рдХреЗ рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ - рдЪреМрдХрдбрд╝реА рд▓реЗрддреЗ рд╣реИрдВ - рдФрд░ рдЕрдзрд┐рдХ рдкрдардиреАрдп рдФрд░ рдЕрднрд┐рд╡реНрдпрдВрдЬрдХ рдпреЛрдЬрдирд╛ рдХреЗ рд╕рд╛рде рдФрд░ рднреА рдЕрдзрд┐рдХ рдкреНрд░рджрд░реНрд╢рди рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВред
рдореИрдВ рдЗрд╕ рдкрд░рд┐рдгрд╛рдо рд╕реЗ рдкреНрд░реЛрддреНрд╕рд╛рд╣рд┐рдд рд╣реВрдВред рдореБрдЭреЗ рдЖрд╢рд╛ рд╣реИ рдХрд┐ рдкрд╛рдардХ quartet@9
рд╡реНрдпрд╡рд╣рд╛рд░ рдореЗрдВ рдкреНрд░рдпрд╛рд╕ рдХрд░рдирд╛ рдЪрд╛рд╣реЗрдВрдЧреЗ ред
рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж, рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдХреЛ рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рджрд┐рд▓рдЪрд╕реНрдкред