valgen 3.2.0 → 4.0.0-alpha.2
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/CHANGELOG.md +6 -0
- package/README.md +12 -7
- package/cjs/core/constants.js +5 -0
- package/cjs/core/context.js +65 -0
- package/cjs/core/index.js +21 -0
- package/cjs/core/types.js +2 -0
- package/cjs/core/validation-error.js +11 -0
- package/cjs/core/validator.js +61 -0
- package/cjs/helpers/object.utils.js +19 -0
- package/cjs/helpers/string.utils.js +9 -0
- package/cjs/index.js +18 -0
- package/cjs/package.json +3 -0
- package/cjs/rules/index.js +38 -0
- package/cjs/rules/is-any.js +12 -0
- package/cjs/rules/is-array.js +38 -0
- package/cjs/rules/is-boolean.js +30 -0
- package/cjs/rules/is-date.js +63 -0
- package/cjs/rules/is-empty.js +68 -0
- package/cjs/rules/is-enum.js +30 -0
- package/cjs/rules/is-integer.js +26 -0
- package/cjs/rules/is-matches.js +24 -0
- package/cjs/rules/is-null.js +40 -0
- package/cjs/rules/is-number.js +26 -0
- package/cjs/rules/is-object-id.js +28 -0
- package/cjs/rules/is-object.js +103 -0
- package/cjs/rules/is-record.js +41 -0
- package/cjs/rules/is-string.js +79 -0
- package/cjs/rules/is-tuple.js +32 -0
- package/cjs/rules/length.js +41 -0
- package/cjs/rules/optional.js +44 -0
- package/cjs/rules/pipe.js +43 -0
- package/cjs/rules/range.js +89 -0
- package/cjs/rules/string-formats.js +296 -0
- package/cjs/rules/union.js +31 -0
- package/cjs/rules/utilities.js +31 -0
- package/esm/core/constants.js +2 -0
- package/esm/core/context.js +61 -0
- package/esm/core/index.js +5 -0
- package/esm/core/types.js +1 -0
- package/esm/core/validation-error.js +7 -0
- package/esm/core/validator.js +56 -0
- package/esm/helpers/object.utils.js +14 -0
- package/esm/helpers/string.utils.js +5 -0
- package/esm/index.js +2 -0
- package/esm/rules/index.js +22 -0
- package/esm/rules/is-any.js +8 -0
- package/esm/rules/is-array.js +34 -0
- package/esm/rules/is-boolean.js +26 -0
- package/esm/rules/is-date.js +55 -0
- package/esm/rules/is-empty.js +63 -0
- package/esm/rules/is-enum.js +26 -0
- package/esm/rules/is-integer.js +22 -0
- package/esm/rules/is-matches.js +20 -0
- package/esm/rules/is-null.js +34 -0
- package/esm/rules/is-number.js +22 -0
- package/esm/rules/is-object-id.js +21 -0
- package/esm/rules/is-object.js +99 -0
- package/esm/rules/is-record.js +37 -0
- package/esm/rules/is-string.js +70 -0
- package/esm/rules/is-tuple.js +28 -0
- package/esm/rules/length.js +35 -0
- package/esm/rules/optional.js +38 -0
- package/esm/rules/pipe.js +38 -0
- package/esm/rules/range.js +81 -0
- package/esm/rules/string-formats.js +266 -0
- package/esm/rules/union.js +27 -0
- package/esm/rules/utilities.js +26 -0
- package/package.json +33 -37
- package/typings/core/constants.d.ts +2 -0
- package/typings/core/context.d.ts +26 -0
- package/typings/core/index.d.ts +6 -0
- package/typings/core/types.d.ts +18 -0
- package/typings/core/validation-error.d.ts +5 -0
- package/typings/core/validator.d.ts +18 -0
- package/typings/helpers/object.utils.d.ts +2 -0
- package/typings/helpers/string.utils.d.ts +1 -0
- package/typings/index.d.ts +2 -0
- package/typings/rules/index.d.ts +22 -0
- package/typings/rules/is-any.d.ts +5 -0
- package/typings/rules/is-array.d.ts +7 -0
- package/typings/rules/is-boolean.d.ts +7 -0
- package/typings/rules/is-date.d.ts +19 -0
- package/typings/rules/is-empty.d.ts +13 -0
- package/typings/rules/is-enum.d.ts +9 -0
- package/typings/rules/is-integer.d.ts +7 -0
- package/typings/rules/is-matches.d.ts +9 -0
- package/typings/rules/is-null.d.ts +16 -0
- package/typings/rules/is-number.d.ts +7 -0
- package/typings/rules/is-object-id.d.ts +10 -0
- package/typings/rules/is-object.d.ts +22 -0
- package/typings/rules/is-record.d.ts +7 -0
- package/typings/rules/is-string.d.ts +42 -0
- package/typings/rules/is-tuple.d.ts +27 -0
- package/typings/rules/length.d.ts +21 -0
- package/typings/rules/optional.d.ts +17 -0
- package/typings/rules/pipe.d.ts +11 -0
- package/typings/rules/range.d.ts +48 -0
- package/typings/rules/string-formats.d.ts +140 -0
- package/typings/rules/union.d.ts +5 -0
- package/typings/rules/utilities.d.ts +11 -0
- package/lib/ArrayType.d.ts +0 -5
- package/lib/ArrayType.js +0 -43
- package/lib/DataType.d.ts +0 -25
- package/lib/DataType.js +0 -214
- package/lib/ObjectType.d.ts +0 -5
- package/lib/ObjectType.js +0 -78
- package/lib/SchemaError.d.ts +0 -3
- package/lib/SchemaError.js +0 -17
- package/lib/UnionType.d.ts +0 -5
- package/lib/UnionType.js +0 -54
- package/lib/Valgen.d.ts +0 -88
- package/lib/Valgen.js +0 -289
- package/lib/ValidationError.d.ts +0 -2
- package/lib/ValidationError.js +0 -4
- package/lib/factories/AnyFactory.d.ts +0 -28
- package/lib/factories/AnyFactory.js +0 -73
- package/lib/factories/ArrayFactory.d.ts +0 -16
- package/lib/factories/ArrayFactory.js +0 -148
- package/lib/factories/Base64Factory.d.ts +0 -12
- package/lib/factories/Base64Factory.js +0 -31
- package/lib/factories/BooleanFactory.d.ts +0 -12
- package/lib/factories/BooleanFactory.js +0 -49
- package/lib/factories/DateFactory.d.ts +0 -40
- package/lib/factories/DateFactory.js +0 -239
- package/lib/factories/FunctionFactory.d.ts +0 -12
- package/lib/factories/FunctionFactory.js +0 -28
- package/lib/factories/IntegerFactory.d.ts +0 -14
- package/lib/factories/IntegerFactory.js +0 -33
- package/lib/factories/NilFactory.d.ts +0 -4
- package/lib/factories/NilFactory.js +0 -20
- package/lib/factories/NumberFactory.d.ts +0 -36
- package/lib/factories/NumberFactory.js +0 -178
- package/lib/factories/ObjectFactory.d.ts +0 -38
- package/lib/factories/ObjectFactory.js +0 -326
- package/lib/factories/StringFactory.d.ts +0 -16
- package/lib/factories/StringFactory.js +0 -94
- package/lib/factories/UnionFactory.d.ts +0 -13
- package/lib/factories/UnionFactory.js +0 -62
- package/lib/index.d.ts +0 -19
- package/lib/index.js +0 -37
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if value is an empty. Value should be string, array, set, map or object
|
|
4
|
+
* @validator isEmpty
|
|
5
|
+
*/
|
|
6
|
+
export function isEmpty(options) {
|
|
7
|
+
return validator('isEmpty', function (input, context) {
|
|
8
|
+
if (input != null) {
|
|
9
|
+
if (typeof input === 'string') {
|
|
10
|
+
if (!input)
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
13
|
+
else if (Array.isArray(input)) {
|
|
14
|
+
if (!input.length)
|
|
15
|
+
return input;
|
|
16
|
+
}
|
|
17
|
+
else if (input instanceof Set) {
|
|
18
|
+
if (!input.size)
|
|
19
|
+
return input;
|
|
20
|
+
}
|
|
21
|
+
else if (input instanceof Map) {
|
|
22
|
+
if (!input.size)
|
|
23
|
+
return input;
|
|
24
|
+
}
|
|
25
|
+
else if (typeof input === 'object') {
|
|
26
|
+
if (!Object.keys(input).length)
|
|
27
|
+
return input;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
context.failure(`{{label}} is not empty`);
|
|
31
|
+
}, options);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Checks if value is a not empty. Value should be string, array, set, map or object
|
|
35
|
+
* @validator isNotEmpty
|
|
36
|
+
*/
|
|
37
|
+
export function isNotEmpty(options) {
|
|
38
|
+
return validator('isNotEmpty', function (input, context) {
|
|
39
|
+
if (input != null) {
|
|
40
|
+
if (typeof input === 'string') {
|
|
41
|
+
if (input)
|
|
42
|
+
return input;
|
|
43
|
+
}
|
|
44
|
+
else if (Array.isArray(input)) {
|
|
45
|
+
if (input.length)
|
|
46
|
+
return input;
|
|
47
|
+
}
|
|
48
|
+
else if (input instanceof Set) {
|
|
49
|
+
if (input.size)
|
|
50
|
+
return input;
|
|
51
|
+
}
|
|
52
|
+
else if (input instanceof Map) {
|
|
53
|
+
if (input.size)
|
|
54
|
+
return input;
|
|
55
|
+
}
|
|
56
|
+
else if (typeof input === 'object') {
|
|
57
|
+
if (Object.keys(input).length)
|
|
58
|
+
return input;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
context.failure(`{{label}} is empty`);
|
|
62
|
+
}, options);
|
|
63
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates if given value is one of enum values.
|
|
4
|
+
* @validator isEnum
|
|
5
|
+
*/
|
|
6
|
+
export function isEnum(values, options) {
|
|
7
|
+
const caseInSensitive = !!(options?.caseInSensitive);
|
|
8
|
+
const enumName = options?.enumName;
|
|
9
|
+
// Prepare an object for fast lookup
|
|
10
|
+
const valObj = (Array.isArray(values) ? values : [values])
|
|
11
|
+
.reduce((a, v) => {
|
|
12
|
+
if (typeof v === 'string' && caseInSensitive)
|
|
13
|
+
a[v.toUpperCase()] = true;
|
|
14
|
+
else
|
|
15
|
+
a[v] = true;
|
|
16
|
+
return a;
|
|
17
|
+
}, {});
|
|
18
|
+
return validator('isEnum', function (input, context) {
|
|
19
|
+
if (input != null && valObj[typeof input === 'string' && caseInSensitive ? input.toUpperCase() : input])
|
|
20
|
+
return input;
|
|
21
|
+
context.failure({
|
|
22
|
+
message: `Value must be one of enumeration member${enumName ? ` (${enumName})` : ''}`,
|
|
23
|
+
enum: enumName
|
|
24
|
+
});
|
|
25
|
+
}, options);
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates if value is "integer".
|
|
4
|
+
* Converts input value to integer number if coerce option is set to 'true'.
|
|
5
|
+
* @validator isInteger
|
|
6
|
+
*/
|
|
7
|
+
export function isInteger(options) {
|
|
8
|
+
return validator('isInteger', function (input, context) {
|
|
9
|
+
if (input != null && typeof input !== 'number' && context.coerce) {
|
|
10
|
+
if (typeof input === 'string')
|
|
11
|
+
input = parseFloat(input);
|
|
12
|
+
if (typeof input === 'bigint') {
|
|
13
|
+
const v = Number(input);
|
|
14
|
+
if (input === BigInt(v))
|
|
15
|
+
input = v;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (typeof input === 'number' && !isNaN(input) && Number.isInteger(input))
|
|
19
|
+
return input;
|
|
20
|
+
context.failure(`{{label}} is not a valid integer number`);
|
|
21
|
+
}, options);
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Coerces given value to "UUID" format or returns undefined if nullish
|
|
4
|
+
* @validator uuid
|
|
5
|
+
*/
|
|
6
|
+
export function isMatches(format, options) {
|
|
7
|
+
const regExp = format instanceof RegExp ? format : new RegExp(format);
|
|
8
|
+
const formatName = options?.formatName;
|
|
9
|
+
return validator('matches', function (input, context) {
|
|
10
|
+
if (input == null)
|
|
11
|
+
return;
|
|
12
|
+
if (typeof input === 'string' && regExp.test(input))
|
|
13
|
+
return input;
|
|
14
|
+
context.failure({
|
|
15
|
+
message: `{{label}} does not match ${formatName || 'requested'} format`,
|
|
16
|
+
format,
|
|
17
|
+
formatName
|
|
18
|
+
});
|
|
19
|
+
}, options);
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates if value is "null".
|
|
4
|
+
* @validator isNull
|
|
5
|
+
*/
|
|
6
|
+
export function isNull(options) {
|
|
7
|
+
return validator('isNull', function (input, context) {
|
|
8
|
+
if (input === null)
|
|
9
|
+
return input;
|
|
10
|
+
context.failure(`{{label}} is not null`);
|
|
11
|
+
}, options);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validates if value is not "undefined" nor "null"
|
|
15
|
+
* @validator isDefined
|
|
16
|
+
*/
|
|
17
|
+
export function isDefined(options) {
|
|
18
|
+
return validator('is-defined', function (input, context) {
|
|
19
|
+
if (input !== undefined)
|
|
20
|
+
return input;
|
|
21
|
+
context.failure(`{{label}} is not defined`);
|
|
22
|
+
}, options);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validates if value is "undefined"
|
|
26
|
+
* @validator isNotDefined
|
|
27
|
+
*/
|
|
28
|
+
export function isUndefined(options) {
|
|
29
|
+
return validator('isUndefined', function (input, context) {
|
|
30
|
+
if (input === undefined)
|
|
31
|
+
return;
|
|
32
|
+
context.failure(`{{label}} defined`);
|
|
33
|
+
}, options);
|
|
34
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates if value is "number".
|
|
4
|
+
* Converts input value to number if coerce option is set to 'true'.
|
|
5
|
+
* @validator isNumber
|
|
6
|
+
*/
|
|
7
|
+
export function isNumber(options) {
|
|
8
|
+
return validator('isNumber', function (input, context) {
|
|
9
|
+
if (input != null && typeof input !== 'number' && context.coerce) {
|
|
10
|
+
if (typeof input === 'string')
|
|
11
|
+
input = parseFloat(input);
|
|
12
|
+
if (typeof input === 'bigint') {
|
|
13
|
+
const v = Number(input);
|
|
14
|
+
if (input === BigInt(v))
|
|
15
|
+
input = v;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (typeof input === 'number' && !isNaN(input))
|
|
19
|
+
return input;
|
|
20
|
+
context.failure(`{{label}} not a valid number`);
|
|
21
|
+
}, options);
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import validatorJS from 'validator';
|
|
2
|
+
import { validator } from '../core/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validates if value is an "ObjectId".
|
|
5
|
+
* @validator isObjectId
|
|
6
|
+
*/
|
|
7
|
+
export function isObjectId(options) {
|
|
8
|
+
return validator('isObjectId', function (input, context) {
|
|
9
|
+
if (input != null &&
|
|
10
|
+
(_isObjectIdValue(input) ||
|
|
11
|
+
(typeof input === 'object' &&
|
|
12
|
+
typeof input.toHexString === 'function' &&
|
|
13
|
+
_isObjectIdValue(input.toHexString()))))
|
|
14
|
+
return input;
|
|
15
|
+
context.failure(`{{label}} is not a valid ObjectId`);
|
|
16
|
+
}, options);
|
|
17
|
+
}
|
|
18
|
+
function _isObjectIdValue(input) {
|
|
19
|
+
return (input instanceof Uint8Array && input.length === 12) ||
|
|
20
|
+
(typeof input === 'string' && input.length === 24 && validatorJS.isHexadecimal(input));
|
|
21
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { isValidator, kValidatorFn, validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates object according to schema
|
|
4
|
+
* Converts properties according to schema rules if coerce option is set to 'true'.
|
|
5
|
+
* @validator isObject
|
|
6
|
+
*/
|
|
7
|
+
export function isObject(schema, options) {
|
|
8
|
+
const ctor = options?.ctor;
|
|
9
|
+
const ctorName = options?.name || ctor?.name;
|
|
10
|
+
const additionalFields = options?.additionalFields ?? false;
|
|
11
|
+
const caseInSensitive = !!options?.caseInSensitive;
|
|
12
|
+
const detectCircular = !!options?.detectCircular;
|
|
13
|
+
if (options) {
|
|
14
|
+
delete options.name;
|
|
15
|
+
delete options.ctor;
|
|
16
|
+
delete options.additionalFields;
|
|
17
|
+
delete options.caseInSensitive;
|
|
18
|
+
delete options.detectCircular;
|
|
19
|
+
}
|
|
20
|
+
const propertyRules = {};
|
|
21
|
+
const propertyOptions = {};
|
|
22
|
+
Object.keys(schema).forEach(k => {
|
|
23
|
+
const n = schema[k];
|
|
24
|
+
const key = caseInSensitive ? k.toLowerCase() : k;
|
|
25
|
+
if (Array.isArray(n)) {
|
|
26
|
+
if (!isValidator(n[0]))
|
|
27
|
+
throw new TypeError(`Invalid tuple definition in validation schema (${k})`);
|
|
28
|
+
propertyRules[key] = n[0];
|
|
29
|
+
propertyOptions[key] = { as: k, ...n[1] };
|
|
30
|
+
}
|
|
31
|
+
else if (isValidator(n)) {
|
|
32
|
+
propertyRules[key] = n;
|
|
33
|
+
propertyOptions[key] = { as: k };
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
throw new TypeError(`Invalid definition in validation schema (${k})`);
|
|
37
|
+
});
|
|
38
|
+
const schemaKeys = Object.keys(propertyRules);
|
|
39
|
+
const _rule = validator('isObject', function (input, context) {
|
|
40
|
+
if (!(input && typeof input === 'object')) {
|
|
41
|
+
context.failure(`{{label}} must be an object`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const keyMap = [...Object.keys(input), ...schemaKeys]
|
|
45
|
+
.reduce((a, k) => {
|
|
46
|
+
if (caseInSensitive)
|
|
47
|
+
a[k.toLowerCase()] = k;
|
|
48
|
+
else
|
|
49
|
+
a[k] = k;
|
|
50
|
+
return a;
|
|
51
|
+
}, {});
|
|
52
|
+
const keys = Object.keys(keyMap);
|
|
53
|
+
const l = keys.length;
|
|
54
|
+
let i = 0;
|
|
55
|
+
let inputKey;
|
|
56
|
+
let schemaKey;
|
|
57
|
+
let v;
|
|
58
|
+
let _propRule;
|
|
59
|
+
const out = {};
|
|
60
|
+
if (ctor)
|
|
61
|
+
Object.setPrototypeOf(out, ctor.prototype);
|
|
62
|
+
if (detectCircular) {
|
|
63
|
+
context.circMap = context.circMap || new Map();
|
|
64
|
+
if (context.circMap.has(input))
|
|
65
|
+
return context.circMap.get(input);
|
|
66
|
+
context.circMap.set(input, out);
|
|
67
|
+
}
|
|
68
|
+
const rootName = context.root?.input.context;
|
|
69
|
+
const location = context.input.location || '';
|
|
70
|
+
// Iterate object keys and perform rules
|
|
71
|
+
for (i = 0; i < l; i++) {
|
|
72
|
+
inputKey = keys[i];
|
|
73
|
+
schemaKey = keyMap[inputKey];
|
|
74
|
+
v = input[inputKey];
|
|
75
|
+
_propRule = propertyRules[schemaKey] ||
|
|
76
|
+
(isValidator(additionalFields) ? additionalFields : undefined);
|
|
77
|
+
if (_propRule) {
|
|
78
|
+
const subCtx = context.extend(_propRule);
|
|
79
|
+
if (ctorName) {
|
|
80
|
+
if (rootName && rootName !== ctorName)
|
|
81
|
+
subCtx.input.root = rootName;
|
|
82
|
+
subCtx.input.context = ctorName;
|
|
83
|
+
}
|
|
84
|
+
subCtx.input.value = v;
|
|
85
|
+
subCtx.input.label = propertyOptions[schemaKey]?.label || schemaKey;
|
|
86
|
+
subCtx.input.location = location + (location ? '.' : '') + schemaKey;
|
|
87
|
+
subCtx.input.property = schemaKey;
|
|
88
|
+
v = _propRule[kValidatorFn](v, subCtx, _propRule);
|
|
89
|
+
}
|
|
90
|
+
else if (v !== undefined && !additionalFields)
|
|
91
|
+
context.failure(`${ctorName || 'Object'} does not accept additional fields`);
|
|
92
|
+
if (v !== undefined)
|
|
93
|
+
out[propertyOptions[schemaKey]?.as || schemaKey] = v;
|
|
94
|
+
}
|
|
95
|
+
return context.errors.length ? undefined : out;
|
|
96
|
+
}, options);
|
|
97
|
+
_rule.schema = schema;
|
|
98
|
+
return _rule;
|
|
99
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { kValidatorFn, validator, } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates record object according to given "key" and "value" rules
|
|
4
|
+
* Converts properties according to rules if coerce option is set to 'true'.
|
|
5
|
+
* @validator isRecord
|
|
6
|
+
*/
|
|
7
|
+
export function isRecord(keyRule, valueRule, options) {
|
|
8
|
+
return validator('isRecord', function (input, context) {
|
|
9
|
+
if (!(input && typeof input === 'object')) {
|
|
10
|
+
context.failure(`{{label}} is not an object`);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const location = context.input.location || '';
|
|
14
|
+
const keyContext = context.extend(keyRule);
|
|
15
|
+
const valueContext = context.extend(valueRule);
|
|
16
|
+
const keys = Object.keys(input);
|
|
17
|
+
const l = keys.length;
|
|
18
|
+
let i;
|
|
19
|
+
let k;
|
|
20
|
+
let v;
|
|
21
|
+
const out = {};
|
|
22
|
+
for (i = 0; i < l; i++) {
|
|
23
|
+
k = keys[i];
|
|
24
|
+
v = input[k];
|
|
25
|
+
keyContext.input.value = k;
|
|
26
|
+
keyContext.input.property = '@' + k;
|
|
27
|
+
keyContext.input.location = location + (location ? '.@' : '@') + k;
|
|
28
|
+
k = keyRule[kValidatorFn](k, keyContext, keyRule);
|
|
29
|
+
valueContext.input.value = v;
|
|
30
|
+
valueContext.input.property = k;
|
|
31
|
+
valueContext.input.location = location + (location ? '.' : '') + k;
|
|
32
|
+
v = valueRule[kValidatorFn](v, valueContext, valueRule);
|
|
33
|
+
out[k] = v;
|
|
34
|
+
}
|
|
35
|
+
return context.errors.length ? undefined : out;
|
|
36
|
+
}, options);
|
|
37
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates if value is "string".
|
|
4
|
+
* Converts input value to string if coerce option is set to 'true'.
|
|
5
|
+
* @validator isString
|
|
6
|
+
*/
|
|
7
|
+
export function isString(options) {
|
|
8
|
+
return validator('isString', function (input, context) {
|
|
9
|
+
if (input != null && typeof input !== 'string' && context.coerce) {
|
|
10
|
+
if (typeof input === 'object') {
|
|
11
|
+
if (typeof input.toJSON === 'function')
|
|
12
|
+
input = input.toJSON();
|
|
13
|
+
else
|
|
14
|
+
input = JSON.stringify(input);
|
|
15
|
+
}
|
|
16
|
+
else
|
|
17
|
+
input = String(input);
|
|
18
|
+
}
|
|
19
|
+
if (typeof input === 'string')
|
|
20
|
+
return input;
|
|
21
|
+
context.failure(`{{label}} is not a string`);
|
|
22
|
+
}, options);
|
|
23
|
+
}
|
|
24
|
+
export function stringReplace(searchValue, replacer) {
|
|
25
|
+
return validator('stringReplace', function (input) {
|
|
26
|
+
if (input == null)
|
|
27
|
+
return input;
|
|
28
|
+
return String(input).replace(searchValue, replacer);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function stringSplit(splitter, limit) {
|
|
32
|
+
return validator('stringSplit', function (input) {
|
|
33
|
+
if (input == null)
|
|
34
|
+
return input;
|
|
35
|
+
return String(input).split(splitter, limit);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Removes whitespace from both ends of a string
|
|
40
|
+
* @validator trim
|
|
41
|
+
*/
|
|
42
|
+
export function trim() {
|
|
43
|
+
return validator('trim', function (input) {
|
|
44
|
+
if (input == null)
|
|
45
|
+
return input;
|
|
46
|
+
return String(input).trim();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// *************************************************************
|
|
50
|
+
/**
|
|
51
|
+
* Removes whitespace from the end of a string
|
|
52
|
+
* @validator trimEnd
|
|
53
|
+
*/
|
|
54
|
+
export const trimEnd = () => trimEndRule;
|
|
55
|
+
const trimEndRule = validator('trimEnd', function (input) {
|
|
56
|
+
if (input == null)
|
|
57
|
+
return input;
|
|
58
|
+
return String(input).trimEnd();
|
|
59
|
+
});
|
|
60
|
+
// *************************************************************
|
|
61
|
+
/**
|
|
62
|
+
* Removes whitespace from the beginning of a string
|
|
63
|
+
* @validator trimStart
|
|
64
|
+
*/
|
|
65
|
+
export const trimStart = () => trimStartRule;
|
|
66
|
+
const trimStartRule = validator('trimStart', function (input) {
|
|
67
|
+
if (input == null)
|
|
68
|
+
return input;
|
|
69
|
+
return String(input).trimStart();
|
|
70
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { kValidatorFn, validator } from '../core/index.js';
|
|
2
|
+
export function isTuple(items, options) {
|
|
3
|
+
return validator('isTuple', function (input, context) {
|
|
4
|
+
if (input != null && context.coerce && !Array.isArray(input))
|
|
5
|
+
input = [input];
|
|
6
|
+
if (!Array.isArray(input)) {
|
|
7
|
+
context.failure(`{{label}} is not a valid tuple`);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const location = context.input.location || '';
|
|
11
|
+
let itemRule;
|
|
12
|
+
let i;
|
|
13
|
+
const l = input.length;
|
|
14
|
+
const out = [];
|
|
15
|
+
const nl = items.length;
|
|
16
|
+
for (i = 0; i < l && i < nl; i++) {
|
|
17
|
+
itemRule = items[i];
|
|
18
|
+
const itemContext = context.extend(itemRule);
|
|
19
|
+
itemContext.input.value = input[i];
|
|
20
|
+
itemContext.input.label = ((context.input.label || context.input.property) || 'Item') +
|
|
21
|
+
`[${i}]`;
|
|
22
|
+
itemContext.input.property = i;
|
|
23
|
+
itemContext.input.location = location + '[' + i + ']';
|
|
24
|
+
out.push(itemRule[kValidatorFn](input[i], itemContext, itemRule) ?? null);
|
|
25
|
+
}
|
|
26
|
+
return context.errors.length ? undefined : out;
|
|
27
|
+
}, options);
|
|
28
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
import { allOf, pipe } from './pipe.js';
|
|
3
|
+
import { isGte, isLte } from './range.js';
|
|
4
|
+
/**
|
|
5
|
+
* Returns length of an Array, String, ArrayBuffer, Buffer or any object with the "length" property.
|
|
6
|
+
* @validator getLength
|
|
7
|
+
*/
|
|
8
|
+
export const extractLength = () => extractLengthRule;
|
|
9
|
+
const extractLengthRule = validator('extractLength', function (input, context) {
|
|
10
|
+
if (typeof input === 'string')
|
|
11
|
+
return input.length;
|
|
12
|
+
if (Array.isArray(input))
|
|
13
|
+
return input.length;
|
|
14
|
+
if (input instanceof ArrayBuffer)
|
|
15
|
+
return input.byteLength;
|
|
16
|
+
if (input && typeof input === 'object' && typeof input.length === 'number')
|
|
17
|
+
return input.length;
|
|
18
|
+
if (input && typeof input === 'object' && typeof input.size === 'number')
|
|
19
|
+
return input.size;
|
|
20
|
+
context.failure(`Unable to extract length of {{label}}`);
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Checks the length is at least "minValue"
|
|
24
|
+
* @validator lengthMin
|
|
25
|
+
*/
|
|
26
|
+
export const lengthMin = (minValue) => allOf(pipe(extractLength(), isGte(minValue, {
|
|
27
|
+
onFail: () => `The length of {{label}} must be at least ${minValue}`
|
|
28
|
+
})));
|
|
29
|
+
/**
|
|
30
|
+
* Checks if the length is at most "maxValue"
|
|
31
|
+
* @validator charLengthMax
|
|
32
|
+
*/
|
|
33
|
+
export const lengthMax = (maxValue) => allOf(pipe(extractLength(), isLte(maxValue, {
|
|
34
|
+
onFail: () => `The length of {{label}} must be at most ${maxValue}`
|
|
35
|
+
})));
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { kValidatorFn, validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Makes sub-rule optional
|
|
4
|
+
* @validator optional
|
|
5
|
+
*/
|
|
6
|
+
export function optional(nested, options) {
|
|
7
|
+
return validator('optional', function (input, context) {
|
|
8
|
+
if (input == null)
|
|
9
|
+
return input;
|
|
10
|
+
return nested[kValidatorFn](input, context, nested);
|
|
11
|
+
}, options);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Makes sub-rule required
|
|
15
|
+
* @validator required
|
|
16
|
+
*/
|
|
17
|
+
export function required(nested, options) {
|
|
18
|
+
return validator('required', function (input, context) {
|
|
19
|
+
if (input == null) {
|
|
20
|
+
context.failure(`{{label}} is required`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
return nested[kValidatorFn](input, context, nested);
|
|
24
|
+
}, options);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validates if property exists
|
|
28
|
+
* @validator exists
|
|
29
|
+
*/
|
|
30
|
+
export function exists(options) {
|
|
31
|
+
return validator('exists', function (input, context) {
|
|
32
|
+
if (input !== undefined ||
|
|
33
|
+
(context.input.value && context.parent &&
|
|
34
|
+
Object.getOwnPropertyDescriptor(context.input.value, context.input.property)))
|
|
35
|
+
return input;
|
|
36
|
+
context.failure(`{{label}} must exist`);
|
|
37
|
+
}, options);
|
|
38
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { kValidatorFn, validator } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @validator pipe
|
|
5
|
+
*/
|
|
6
|
+
export function pipe(...rules) {
|
|
7
|
+
const l = rules.length;
|
|
8
|
+
return validator('pipe', function (input, context) {
|
|
9
|
+
let i;
|
|
10
|
+
let c;
|
|
11
|
+
let v = input;
|
|
12
|
+
for (i = 0; i < l; i++) {
|
|
13
|
+
c = rules[i];
|
|
14
|
+
const subContext = context.extend(c);
|
|
15
|
+
v = c[kValidatorFn](v, subContext, c);
|
|
16
|
+
if (context.errors.length)
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
return v;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Test given value against to all codecs and returns original input
|
|
24
|
+
* @validator allOf
|
|
25
|
+
*/
|
|
26
|
+
export function allOf(...rules) {
|
|
27
|
+
return validator('allOf', function (input, context) {
|
|
28
|
+
let i;
|
|
29
|
+
let c;
|
|
30
|
+
const l = rules.length;
|
|
31
|
+
for (i = 0; i < l; i++) {
|
|
32
|
+
c = rules[i];
|
|
33
|
+
const subContext = context.extend(c);
|
|
34
|
+
c[kValidatorFn](input, subContext, c);
|
|
35
|
+
}
|
|
36
|
+
return input;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { validator } from '../core/index.js';
|
|
2
|
+
export function isGt(minValue, options) {
|
|
3
|
+
return validator('isGt', function (input, context) {
|
|
4
|
+
if ((typeof minValue === 'number' || typeof minValue === 'bigint') &&
|
|
5
|
+
(typeof input === 'number' || typeof input === 'bigint') &&
|
|
6
|
+
input > minValue)
|
|
7
|
+
return input;
|
|
8
|
+
if (minValue instanceof Date && input instanceof Date && input > minValue)
|
|
9
|
+
return input;
|
|
10
|
+
if (typeof minValue === 'string' && typeof input === 'string' &&
|
|
11
|
+
(input > minValue ||
|
|
12
|
+
(options?.caseInsensitive && input.toLowerCase() > minValue.toLowerCase())))
|
|
13
|
+
return input;
|
|
14
|
+
context.failure(`{{label}} must be greater than ${(typeof minValue === 'string' ? `"${minValue}"` : minValue)}`);
|
|
15
|
+
}, options);
|
|
16
|
+
}
|
|
17
|
+
export function isGte(minValue, options) {
|
|
18
|
+
return validator('isGte', function (input, context) {
|
|
19
|
+
if ((typeof minValue === 'number' || typeof minValue === 'bigint') &&
|
|
20
|
+
(typeof input === 'number' || typeof input === 'bigint') &&
|
|
21
|
+
input >= minValue)
|
|
22
|
+
return input;
|
|
23
|
+
if (minValue instanceof Date && input instanceof Date && input >= minValue)
|
|
24
|
+
return input;
|
|
25
|
+
if (typeof minValue === 'string' && typeof input === 'string' &&
|
|
26
|
+
(input >= minValue ||
|
|
27
|
+
(options?.caseInsensitive && input.toLowerCase() >= minValue.toLowerCase())))
|
|
28
|
+
return input;
|
|
29
|
+
context.failure(`{{label}} must be greater than or equal to ${(typeof minValue === 'string' ? `"${minValue}"` : minValue)}`);
|
|
30
|
+
}, options);
|
|
31
|
+
}
|
|
32
|
+
export function isLt(maxValue, options) {
|
|
33
|
+
return validator('isLt', function (input, context) {
|
|
34
|
+
if ((typeof maxValue === 'number' || typeof maxValue === 'bigint') &&
|
|
35
|
+
(typeof input === 'number' || typeof input === 'bigint') &&
|
|
36
|
+
input < maxValue)
|
|
37
|
+
return input;
|
|
38
|
+
if (maxValue instanceof Date && input instanceof Date && input < maxValue)
|
|
39
|
+
return input;
|
|
40
|
+
if (typeof maxValue === 'string' && typeof input === 'string' &&
|
|
41
|
+
(input < maxValue ||
|
|
42
|
+
(options?.caseInsensitive && input.toLowerCase() < maxValue.toLowerCase())))
|
|
43
|
+
return input;
|
|
44
|
+
context.failure(`{{label}} must be lover than ${(typeof maxValue === 'string' ? `"${maxValue}"` : maxValue)}`);
|
|
45
|
+
}, options);
|
|
46
|
+
}
|
|
47
|
+
export function isLte(maxValue, options) {
|
|
48
|
+
return validator('isLt', function (input, context) {
|
|
49
|
+
if ((typeof maxValue === 'number' || typeof maxValue === 'bigint') &&
|
|
50
|
+
(typeof input === 'number' || typeof input === 'bigint') &&
|
|
51
|
+
input <= maxValue)
|
|
52
|
+
return input;
|
|
53
|
+
if (maxValue instanceof Date && input instanceof Date && input <= maxValue)
|
|
54
|
+
return input;
|
|
55
|
+
if (typeof maxValue === 'string' && typeof input === 'string' &&
|
|
56
|
+
(input <= maxValue ||
|
|
57
|
+
(options?.caseInsensitive && input.toLowerCase() <= maxValue.toLowerCase())))
|
|
58
|
+
return input;
|
|
59
|
+
context.failure(`{{label}} must be lover than or equal to ${(typeof maxValue === 'string' ? `"${maxValue}"` : maxValue)}`);
|
|
60
|
+
}, options);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Checks if value is between minValue and maxValue
|
|
64
|
+
* @validator range
|
|
65
|
+
*/
|
|
66
|
+
export function range(minValue, maxValue, options) {
|
|
67
|
+
return validator('range', function (input, context) {
|
|
68
|
+
if ((typeof minValue === 'number' || typeof minValue === 'bigint') &&
|
|
69
|
+
(typeof maxValue === 'number' || typeof maxValue === 'bigint') &&
|
|
70
|
+
(typeof input === 'number' || typeof input === 'bigint') &&
|
|
71
|
+
input >= minValue && input <= maxValue)
|
|
72
|
+
return input;
|
|
73
|
+
if (minValue instanceof Date && maxValue instanceof Date &&
|
|
74
|
+
input instanceof Date && input >= minValue && input <= maxValue)
|
|
75
|
+
return input;
|
|
76
|
+
if (typeof minValue === 'string' && typeof maxValue === 'string' &&
|
|
77
|
+
typeof input === 'string' && input >= minValue && input <= maxValue)
|
|
78
|
+
return input;
|
|
79
|
+
context.failure(`{{label}} must be between ${minValue} and ${maxValue}`);
|
|
80
|
+
}, options);
|
|
81
|
+
}
|