schiva 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/package.json +45 -0
- package/src/closed-schema.js +11 -0
- package/src/compile.js +742 -0
- package/src/deep-equal.js +59 -0
- package/src/index.js +13 -0
- package/src/json-schema-refs.js +170 -0
- package/src/json-schema.js +420 -0
- package/src/schema.js +230 -0
- package/src/types/all-of.js +62 -0
- package/src/types/any-of.js +63 -0
- package/src/types/any.js +32 -0
- package/src/types/array-of.js +137 -0
- package/src/types/boolean.js +43 -0
- package/src/types/code-point-length.js +32 -0
- package/src/types/conditional.js +48 -0
- package/src/types/enum.js +51 -0
- package/src/types/float.js +84 -0
- package/src/types/has-duplicates.js +21 -0
- package/src/types/index.js +39 -0
- package/src/types/integer.js +45 -0
- package/src/types/never.js +36 -0
- package/src/types/not.js +49 -0
- package/src/types/obj.js +58 -0
- package/src/types/one-of.js +69 -0
- package/src/types/ref.js +49 -0
- package/src/types/string.js +86 -0
- package/src/types/validate-type.js +98 -0
- package/src/types/values.js +46 -0
- package/src/types/when.js +63 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const { ValidateType } = require('./validate-type');
|
|
2
|
+
|
|
3
|
+
class FloatType extends ValidateType {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super(options);
|
|
6
|
+
this.min = options.min;
|
|
7
|
+
this.max = options.max;
|
|
8
|
+
this.exclusiveMin = options.exclusiveMin;
|
|
9
|
+
this.exclusiveMax = options.exclusiveMax;
|
|
10
|
+
// Value divided by it must be an integer (floating point division, so 0.3 is not a multiple of 0.1).
|
|
11
|
+
this.multipleOf = options.multipleOf;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
validate(value, fieldName = 'Value') {
|
|
15
|
+
const result = super.validate(value, fieldName);
|
|
16
|
+
if (result) {
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
if (value !== undefined && value !== null) {
|
|
20
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
21
|
+
return `${fieldName} must be a number`;
|
|
22
|
+
}
|
|
23
|
+
if (this.min !== undefined && value < this.min) {
|
|
24
|
+
return `${fieldName} must be at least ${this.min}`;
|
|
25
|
+
}
|
|
26
|
+
if (this.max !== undefined && value > this.max) {
|
|
27
|
+
return `${fieldName} must be at most ${this.max}`;
|
|
28
|
+
}
|
|
29
|
+
if (this.exclusiveMin !== undefined && value <= this.exclusiveMin) {
|
|
30
|
+
return `${fieldName} must be greater than ${this.exclusiveMin}`;
|
|
31
|
+
}
|
|
32
|
+
if (this.exclusiveMax !== undefined && value >= this.exclusiveMax) {
|
|
33
|
+
return `${fieldName} must be less than ${this.exclusiveMax}`;
|
|
34
|
+
}
|
|
35
|
+
if (this.multipleOf !== undefined && !Number.isInteger(value / this.multipleOf)) {
|
|
36
|
+
return `${fieldName} must be a multiple of ${this.multipleOf}`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
isValid(value) {
|
|
43
|
+
const presence = this.checkPresence(value);
|
|
44
|
+
if (presence !== undefined) {
|
|
45
|
+
return presence;
|
|
46
|
+
}
|
|
47
|
+
return (
|
|
48
|
+
typeof value === 'number' &&
|
|
49
|
+
Number.isFinite(value) &&
|
|
50
|
+
(this.min === undefined || value >= this.min) &&
|
|
51
|
+
(this.max === undefined || value <= this.max) &&
|
|
52
|
+
(this.exclusiveMin === undefined || value > this.exclusiveMin) &&
|
|
53
|
+
(this.exclusiveMax === undefined || value < this.exclusiveMax) &&
|
|
54
|
+
(this.multipleOf === undefined || Number.isInteger(value / this.multipleOf))
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Float(options) {
|
|
60
|
+
return new FloatType(options);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function float(min, max, isMandatory = true, isNullable = false) {
|
|
64
|
+
if (min !== undefined && min !== null && typeof min === 'object') {
|
|
65
|
+
return new FloatType(min);
|
|
66
|
+
}
|
|
67
|
+
return new FloatType({ min, max, isMandatory, isNullable });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function ofloat(min, max, isMandatory = false, isNullable = false) {
|
|
71
|
+
if (min !== undefined && min !== null && typeof min === 'object') {
|
|
72
|
+
return new FloatType({ isMandatory: false, ...min });
|
|
73
|
+
}
|
|
74
|
+
return new FloatType({ min, max, isMandatory, isNullable });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
FloatType,
|
|
79
|
+
Float,
|
|
80
|
+
float,
|
|
81
|
+
ofloat,
|
|
82
|
+
num: float,
|
|
83
|
+
onum: ofloat,
|
|
84
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { deepEqual } = require('../deep-equal');
|
|
2
|
+
|
|
3
|
+
// An element is a duplicate when an earlier index (holes read as undefined) is deep-equal to it.
|
|
4
|
+
// Primitive arrays use a Set, which has the same equality as deepEqual for primitives (NaN included).
|
|
5
|
+
function hasDuplicates(value) {
|
|
6
|
+
if (value.some((item) => item !== null && typeof item === 'object')) {
|
|
7
|
+
return value.some((item, i) => value.findIndex((other) => deepEqual(item, other)) !== i);
|
|
8
|
+
}
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
11
|
+
if (i in value && seen.has(value[i])) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
seen.add(value[i]);
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
hasDuplicates,
|
|
21
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const allOf = require('./all-of');
|
|
2
|
+
const any = require('./any');
|
|
3
|
+
const anyOf = require('./any-of');
|
|
4
|
+
const arrayOf = require('./array-of');
|
|
5
|
+
const boolean = require('./boolean');
|
|
6
|
+
const conditional = require('./conditional');
|
|
7
|
+
const enums = require('./enum');
|
|
8
|
+
const float = require('./float');
|
|
9
|
+
const integer = require('./integer');
|
|
10
|
+
const never = require('./never');
|
|
11
|
+
const not = require('./not');
|
|
12
|
+
const obj = require('./obj');
|
|
13
|
+
const oneOf = require('./one-of');
|
|
14
|
+
const ref = require('./ref');
|
|
15
|
+
const string = require('./string');
|
|
16
|
+
const validateType = require('./validate-type');
|
|
17
|
+
const values = require('./values');
|
|
18
|
+
const when = require('./when');
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
...allOf,
|
|
22
|
+
...any,
|
|
23
|
+
...anyOf,
|
|
24
|
+
...arrayOf,
|
|
25
|
+
...boolean,
|
|
26
|
+
...conditional,
|
|
27
|
+
...enums,
|
|
28
|
+
...float,
|
|
29
|
+
...integer,
|
|
30
|
+
...never,
|
|
31
|
+
...not,
|
|
32
|
+
...obj,
|
|
33
|
+
...oneOf,
|
|
34
|
+
...ref,
|
|
35
|
+
...string,
|
|
36
|
+
...validateType,
|
|
37
|
+
...values,
|
|
38
|
+
...when,
|
|
39
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { FloatType } = require('./float');
|
|
2
|
+
|
|
3
|
+
class IntegerType extends FloatType {
|
|
4
|
+
validate(value, fieldName = 'Value') {
|
|
5
|
+
const result = super.validate(value, fieldName);
|
|
6
|
+
if (result) {
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
if (value !== undefined && value !== null) {
|
|
10
|
+
if (!Number.isInteger(value)) {
|
|
11
|
+
return `${fieldName} must be an integer`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
isValid(value) {
|
|
18
|
+
return super.isValid(value) && (value === undefined || value === null || Number.isInteger(value));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function Integer(options) {
|
|
23
|
+
return new IntegerType(options);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function int(min, max, isMandatory = true, isNullable = false) {
|
|
27
|
+
if (min !== undefined && min !== null && typeof min === 'object') {
|
|
28
|
+
return new IntegerType(min);
|
|
29
|
+
}
|
|
30
|
+
return new IntegerType({ min, max, isMandatory, isNullable });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function oint(min, max, isMandatory = false, isNullable = false) {
|
|
34
|
+
if (min !== undefined && min !== null && typeof min === 'object') {
|
|
35
|
+
return new IntegerType({ isMandatory: false, ...min });
|
|
36
|
+
}
|
|
37
|
+
return new IntegerType({ min, max, isMandatory, isNullable });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
IntegerType,
|
|
42
|
+
Integer,
|
|
43
|
+
int,
|
|
44
|
+
oint,
|
|
45
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { ValidateType } = require('./validate-type');
|
|
2
|
+
|
|
3
|
+
// No value is valid, like the JSON Schema false: only undefined (when not mandatory) and null (when nullable) pass.
|
|
4
|
+
class NeverType extends ValidateType {
|
|
5
|
+
validate(value, fieldName = 'Value') {
|
|
6
|
+
const result = super.validate(value, fieldName);
|
|
7
|
+
if (result) {
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
if (value !== undefined && value !== null) {
|
|
11
|
+
return `${fieldName} is not allowed`;
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
isValid(value) {
|
|
17
|
+
return this.checkPresence(value) ?? false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Never(options) {
|
|
22
|
+
return new NeverType(options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function never(isMandatory = false, isNullable = false) {
|
|
26
|
+
if (isMandatory !== undefined && isMandatory !== null && typeof isMandatory === 'object') {
|
|
27
|
+
return new NeverType({ isMandatory: false, ...isMandatory });
|
|
28
|
+
}
|
|
29
|
+
return new NeverType({ isMandatory, isNullable });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
NeverType,
|
|
34
|
+
Never,
|
|
35
|
+
never,
|
|
36
|
+
};
|
package/src/types/not.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { ValidateType } = require('./validate-type');
|
|
2
|
+
|
|
3
|
+
// Value must not satisfy `type`.
|
|
4
|
+
class NotType extends ValidateType {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super(options);
|
|
7
|
+
this.type = options.type;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
validate(value, fieldName = 'Value') {
|
|
11
|
+
const result = super.validate(value, fieldName);
|
|
12
|
+
if (result) {
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
if (value !== undefined && value !== null && this.type.isValid(value)) {
|
|
16
|
+
return `${fieldName} must not match the excluded schema`;
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
isValid(value) {
|
|
22
|
+
return this.checkPresence(value) ?? !this.type.isValid(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function Not(options) {
|
|
27
|
+
return new NotType(options);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function not(type, isMandatory = true, isNullable = false) {
|
|
31
|
+
if (type !== undefined && type !== null && !(type instanceof ValidateType) && typeof type === 'object') {
|
|
32
|
+
return new NotType(type);
|
|
33
|
+
}
|
|
34
|
+
return new NotType({ type, isMandatory, isNullable });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function onot(type, isMandatory = false, isNullable = false) {
|
|
38
|
+
if (type !== undefined && type !== null && !(type instanceof ValidateType) && typeof type === 'object') {
|
|
39
|
+
return new NotType({ isMandatory: false, ...type });
|
|
40
|
+
}
|
|
41
|
+
return new NotType({ type, isMandatory, isNullable });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
NotType,
|
|
46
|
+
Not,
|
|
47
|
+
not,
|
|
48
|
+
onot,
|
|
49
|
+
};
|
package/src/types/obj.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const { ValidateType } = require('./validate-type');
|
|
2
|
+
|
|
3
|
+
class ObjType extends ValidateType {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super(options);
|
|
6
|
+
this.schema = options.schema;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
validate(value, fieldName = 'Value') {
|
|
10
|
+
const result = super.validate(value, fieldName);
|
|
11
|
+
if (result) {
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
if (value !== undefined && value !== null) {
|
|
15
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
16
|
+
return `${fieldName} must be an object`;
|
|
17
|
+
}
|
|
18
|
+
if (this.schema) return this.schema.validate(value, fieldName);
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
isValid(value) {
|
|
24
|
+
const presence = this.checkPresence(value);
|
|
25
|
+
if (presence !== undefined) {
|
|
26
|
+
return presence;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return !this.schema || this.schema.isValid(value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Obj(options) {
|
|
36
|
+
return new ObjType(options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function obj(schema, isMandatory = true, isNullable = false) {
|
|
40
|
+
if (schema !== undefined && schema !== null && !Array.isArray(schema) && typeof schema === 'object') {
|
|
41
|
+
return new ObjType(schema);
|
|
42
|
+
}
|
|
43
|
+
return new ObjType({ schema, isMandatory, isNullable });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function oobj(schema, isMandatory = false, isNullable = false) {
|
|
47
|
+
if (schema !== undefined && schema !== null && !Array.isArray(schema) && typeof schema === 'object') {
|
|
48
|
+
return new ObjType({ isMandatory: false, ...schema });
|
|
49
|
+
}
|
|
50
|
+
return new ObjType({ schema, isMandatory, isNullable });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
ObjType,
|
|
55
|
+
Obj,
|
|
56
|
+
obj,
|
|
57
|
+
oobj,
|
|
58
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { ValidateType } = require('./validate-type');
|
|
2
|
+
|
|
3
|
+
// Value must satisfy exactly one of the types. When none does, reports the errors of every type, like AnyOfType.
|
|
4
|
+
class OneOfType extends ValidateType {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super(options);
|
|
7
|
+
this.types = options.types || [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Number of types the value satisfies, counting up to 2.
|
|
11
|
+
countMatches(value) {
|
|
12
|
+
let matches = 0;
|
|
13
|
+
for (let i = 0; i < this.types.length && matches < 2; i += 1) {
|
|
14
|
+
if (this.types[i].isValid(value)) {
|
|
15
|
+
matches += 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return matches;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
validate(value, fieldName = 'Value') {
|
|
22
|
+
const result = super.validate(value, fieldName);
|
|
23
|
+
if (result) {
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
if (value !== undefined && value !== null) {
|
|
27
|
+
const matches = this.countMatches(value);
|
|
28
|
+
if (this.types.length === 0) {
|
|
29
|
+
return `${fieldName} must match exactly one schema, but matches none`;
|
|
30
|
+
}
|
|
31
|
+
if (matches === 0) {
|
|
32
|
+
return this.types.map((type) => type.errors(value, fieldName));
|
|
33
|
+
}
|
|
34
|
+
if (matches > 1) {
|
|
35
|
+
return `${fieldName} must match exactly one schema, but matches more than one`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
isValid(value) {
|
|
42
|
+
return this.checkPresence(value) ?? this.countMatches(value) === 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function OneOf(options) {
|
|
47
|
+
return new OneOfType(options);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function oneOf(types, isMandatory = true, isNullable = false) {
|
|
51
|
+
if (types !== undefined && types !== null && !Array.isArray(types) && typeof types === 'object') {
|
|
52
|
+
return new OneOfType(types);
|
|
53
|
+
}
|
|
54
|
+
return new OneOfType({ types, isMandatory, isNullable });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function ooneOf(types, isMandatory = false, isNullable = false) {
|
|
58
|
+
if (types !== undefined && types !== null && !Array.isArray(types) && typeof types === 'object') {
|
|
59
|
+
return new OneOfType({ isMandatory: false, ...types });
|
|
60
|
+
}
|
|
61
|
+
return new OneOfType({ types, isMandatory, isNullable });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
OneOfType,
|
|
66
|
+
OneOf,
|
|
67
|
+
oneOf,
|
|
68
|
+
ooneOf,
|
|
69
|
+
};
|
package/src/types/ref.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { ValidateType } = require('./validate-type');
|
|
2
|
+
|
|
3
|
+
// Validates with the type it refers to, which is set once references are resolved; recursive schemas refer back to a
|
|
4
|
+
// type that contains the reference. Only undefined is handled here (isMandatory); null and other values go to the
|
|
5
|
+
// target. The field name is passed through unchanged, so the reference does not show in messages.
|
|
6
|
+
class RefType extends ValidateType {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
super(options);
|
|
9
|
+
this.ref = options.ref;
|
|
10
|
+
this.target = options.target;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getTarget() {
|
|
14
|
+
if (!this.target) {
|
|
15
|
+
throw new Error(`Reference "${this.ref}" is not resolved`);
|
|
16
|
+
}
|
|
17
|
+
return this.target;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
validate(value, fieldName) {
|
|
21
|
+
if (value === undefined) {
|
|
22
|
+
return super.validate(value, fieldName);
|
|
23
|
+
}
|
|
24
|
+
return this.getTarget().validate(value, fieldName);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
errors(value, fieldName) {
|
|
28
|
+
if (value === undefined) {
|
|
29
|
+
return super.validate(value, fieldName);
|
|
30
|
+
}
|
|
31
|
+
return this.getTarget().errors(value, fieldName);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isValid(value) {
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
return !this.isMandatory;
|
|
37
|
+
}
|
|
38
|
+
return this.getTarget().isValid(value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function Ref(options) {
|
|
43
|
+
return new RefType(options);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
RefType,
|
|
48
|
+
Ref,
|
|
49
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const { hasFewerCodePoints, hasMoreCodePoints } = require('./code-point-length');
|
|
2
|
+
const { ValidateType } = require('./validate-type');
|
|
3
|
+
|
|
4
|
+
class StringType extends ValidateType {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super(options);
|
|
7
|
+
this.min = options.min;
|
|
8
|
+
this.max = options.max;
|
|
9
|
+
this.pattern = options.pattern;
|
|
10
|
+
this.allowEmpty = options.allowEmpty;
|
|
11
|
+
// Count min/max in Unicode code points (as JSON Schema does) instead of UTF-16 units.
|
|
12
|
+
this.countCodePoints = options.countCodePoints;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isTooShort(value) {
|
|
16
|
+
return this.countCodePoints ? hasFewerCodePoints(value, this.min) : value.length < this.min;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
isTooLong(value) {
|
|
20
|
+
return this.countCodePoints ? hasMoreCodePoints(value, this.max) : value.length > this.max;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
validate(value, fieldName = 'Value') {
|
|
24
|
+
const result = super.validate(value, fieldName);
|
|
25
|
+
if (result) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
if (value !== undefined && value !== null) {
|
|
29
|
+
if (typeof value !== 'string') {
|
|
30
|
+
return `${fieldName} must be a string`;
|
|
31
|
+
}
|
|
32
|
+
const skipMin = value.length === 0 && (this.allowEmpty ?? !this.isMandatory);
|
|
33
|
+
if (this.min !== undefined && !skipMin && this.isTooShort(value)) {
|
|
34
|
+
return `${fieldName} must be at least ${this.min} characters long`;
|
|
35
|
+
}
|
|
36
|
+
if (this.max !== undefined && this.isTooLong(value)) {
|
|
37
|
+
return `${fieldName} must be at most ${this.max} characters long`;
|
|
38
|
+
}
|
|
39
|
+
if (this.pattern && !this.pattern.test(value)) {
|
|
40
|
+
return `${fieldName} does not match the required pattern`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
isValid(value) {
|
|
47
|
+
const presence = this.checkPresence(value);
|
|
48
|
+
if (presence !== undefined) {
|
|
49
|
+
return presence;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value !== 'string') {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const skipMin = value.length === 0 && (this.allowEmpty ?? !this.isMandatory);
|
|
55
|
+
return (
|
|
56
|
+
(this.min === undefined || skipMin || !this.isTooShort(value)) &&
|
|
57
|
+
(this.max === undefined || !this.isTooLong(value)) &&
|
|
58
|
+
(!this.pattern || this.pattern.test(value))
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function String(options) {
|
|
64
|
+
return new StringType(options);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function str(min, max, isMandatory = true, isNullable = false) {
|
|
68
|
+
if (min !== undefined && min !== null && typeof min === 'object') {
|
|
69
|
+
return new StringType(min);
|
|
70
|
+
}
|
|
71
|
+
return new StringType({ min, max, isMandatory, isNullable });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function ostr(min, max, isMandatory = false, isNullable = false) {
|
|
75
|
+
if (min !== undefined && min !== null && typeof min === 'object') {
|
|
76
|
+
return new StringType({ isMandatory: false, ...min });
|
|
77
|
+
}
|
|
78
|
+
return new StringType({ min, max, isMandatory, isNullable });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
StringType,
|
|
83
|
+
String,
|
|
84
|
+
str,
|
|
85
|
+
ostr,
|
|
86
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// A validate() result is undefined (valid), a string (one error) or a possibly empty array of errors.
|
|
2
|
+
function hasErrors(result) {
|
|
3
|
+
if (!Array.isArray(result)) {
|
|
4
|
+
return Boolean(result);
|
|
5
|
+
}
|
|
6
|
+
for (let i = 0; i < result.length; i += 1) {
|
|
7
|
+
const item = result[i];
|
|
8
|
+
if (!Array.isArray(item) || hasErrors(item)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class ValidateType {
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.isMandatory = options.isMandatory !== undefined ? options.isMandatory : true;
|
|
18
|
+
this.isNullable = options.isNullable !== undefined ? options.isNullable : false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
validate(value, fieldName = 'Value') {
|
|
22
|
+
if (this.isMandatory && value === undefined) {
|
|
23
|
+
return `${fieldName} is mandatory`;
|
|
24
|
+
}
|
|
25
|
+
if (!this.isNullable && value === null) {
|
|
26
|
+
return `${fieldName} cannot be null`;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Fast boolean check equivalent to !hasErrors(this.validate(value)) that builds no messages.
|
|
32
|
+
// Built-in types override it; custom subclasses that only override validate() fall back to it.
|
|
33
|
+
isValid(value) {
|
|
34
|
+
return !hasErrors(this.validate(value));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Error messages of a value already known to be invalid; containers call it on their failing children
|
|
38
|
+
// so types whose validate() starts with an isValid() fast path can skip it.
|
|
39
|
+
errors(value, fieldName = 'Value') {
|
|
40
|
+
return this.validate(value, fieldName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Compiles the type into generated code, several times faster than validate(): see compileType() in compile.js for
|
|
44
|
+
// the options. The compiled function does not see changes made to the type afterwards.
|
|
45
|
+
compile(options = {}) {
|
|
46
|
+
// eslint-disable-next-line global-require -- compile.js requires this module
|
|
47
|
+
return require('../compile').compileType(this, options);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Presence part of isValid: a boolean when undefined/null decide the result, undefined otherwise.
|
|
51
|
+
checkPresence(value) {
|
|
52
|
+
if (value === undefined) {
|
|
53
|
+
return !this.isMandatory;
|
|
54
|
+
}
|
|
55
|
+
if (value === null) {
|
|
56
|
+
return this.isNullable;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
mandatory(isMandatory = true) {
|
|
62
|
+
this.isMandatory = isMandatory;
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
nullable(isNullable = true) {
|
|
67
|
+
this.isNullable = isNullable;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
optional() {
|
|
72
|
+
this.isMandatory = false;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
required() {
|
|
77
|
+
this.isMandatory = true;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
notNull() {
|
|
82
|
+
this.isNullable = false;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function toErrors(result) {
|
|
88
|
+
if (Array.isArray(result)) {
|
|
89
|
+
return result.flat(Infinity);
|
|
90
|
+
}
|
|
91
|
+
return result ? [result] : [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
ValidateType,
|
|
96
|
+
hasErrors,
|
|
97
|
+
toErrors,
|
|
98
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { deepEqual } = require('../deep-equal');
|
|
2
|
+
const { ValidateType } = require('./validate-type');
|
|
3
|
+
|
|
4
|
+
function formatValue(value) {
|
|
5
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Value must be deep-equal to one of the given values, whatever their type.
|
|
9
|
+
class ValuesType extends ValidateType {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
super(options);
|
|
12
|
+
this.values = options.values || [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
validate(value, fieldName = 'Value') {
|
|
16
|
+
const result = super.validate(value, fieldName);
|
|
17
|
+
if (result) {
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
if (value !== undefined && value !== null && !this.values.some((item) => deepEqual(item, value))) {
|
|
21
|
+
if (this.values.length === 1) {
|
|
22
|
+
return `${fieldName} must be equal to ${formatValue(this.values[0])}`;
|
|
23
|
+
}
|
|
24
|
+
return `${fieldName} must be one of: ${this.values.map(formatValue).join(', ')}`;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
isValid(value) {
|
|
30
|
+
return this.checkPresence(value) ?? this.values.some((item) => deepEqual(item, value));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function Values(options) {
|
|
35
|
+
return new ValuesType(options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Const(value, options = {}) {
|
|
39
|
+
return new ValuesType({ ...options, values: [value] });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
ValuesType,
|
|
44
|
+
Values,
|
|
45
|
+
Const,
|
|
46
|
+
};
|