valgen 3.2.1 → 4.0.0-alpha.3

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +12 -7
  3. package/cjs/core/constants.js +5 -0
  4. package/cjs/core/context.js +65 -0
  5. package/cjs/core/index.js +21 -0
  6. package/cjs/core/types.js +2 -0
  7. package/cjs/core/validation-error.js +11 -0
  8. package/cjs/core/validator.js +61 -0
  9. package/cjs/helpers/object.utils.js +19 -0
  10. package/cjs/helpers/string.utils.js +9 -0
  11. package/cjs/index.js +18 -0
  12. package/cjs/package.json +3 -0
  13. package/cjs/rules/index.js +39 -0
  14. package/cjs/rules/is-any.js +12 -0
  15. package/cjs/rules/is-array.js +38 -0
  16. package/cjs/rules/is-bigint.js +21 -0
  17. package/cjs/rules/is-boolean.js +30 -0
  18. package/cjs/rules/is-date.js +63 -0
  19. package/cjs/rules/is-empty.js +68 -0
  20. package/cjs/rules/is-enum.js +30 -0
  21. package/cjs/rules/is-integer.js +26 -0
  22. package/cjs/rules/is-matches.js +24 -0
  23. package/cjs/rules/is-null.js +40 -0
  24. package/cjs/rules/is-number.js +26 -0
  25. package/cjs/rules/is-object-id.js +28 -0
  26. package/cjs/rules/is-object.js +103 -0
  27. package/cjs/rules/is-record.js +41 -0
  28. package/cjs/rules/is-string.js +79 -0
  29. package/cjs/rules/is-tuple.js +32 -0
  30. package/cjs/rules/length.js +41 -0
  31. package/cjs/rules/optional.js +44 -0
  32. package/cjs/rules/pipe.js +43 -0
  33. package/cjs/rules/range.js +89 -0
  34. package/cjs/rules/string-formats.js +296 -0
  35. package/cjs/rules/union.js +31 -0
  36. package/cjs/rules/utilities.js +31 -0
  37. package/esm/core/constants.js +2 -0
  38. package/esm/core/context.js +61 -0
  39. package/esm/core/index.js +5 -0
  40. package/esm/core/types.js +1 -0
  41. package/esm/core/validation-error.js +7 -0
  42. package/esm/core/validator.js +56 -0
  43. package/esm/helpers/object.utils.js +14 -0
  44. package/esm/helpers/string.utils.js +5 -0
  45. package/esm/index.js +2 -0
  46. package/esm/rules/index.js +23 -0
  47. package/esm/rules/is-any.js +8 -0
  48. package/esm/rules/is-array.js +34 -0
  49. package/esm/rules/is-bigint.js +17 -0
  50. package/esm/rules/is-boolean.js +26 -0
  51. package/esm/rules/is-date.js +55 -0
  52. package/esm/rules/is-empty.js +63 -0
  53. package/esm/rules/is-enum.js +26 -0
  54. package/esm/rules/is-integer.js +22 -0
  55. package/esm/rules/is-matches.js +20 -0
  56. package/esm/rules/is-null.js +34 -0
  57. package/esm/rules/is-number.js +22 -0
  58. package/esm/rules/is-object-id.js +21 -0
  59. package/esm/rules/is-object.js +99 -0
  60. package/esm/rules/is-record.js +37 -0
  61. package/esm/rules/is-string.js +70 -0
  62. package/esm/rules/is-tuple.js +28 -0
  63. package/esm/rules/length.js +35 -0
  64. package/esm/rules/optional.js +38 -0
  65. package/esm/rules/pipe.js +38 -0
  66. package/esm/rules/range.js +81 -0
  67. package/esm/rules/string-formats.js +266 -0
  68. package/esm/rules/union.js +27 -0
  69. package/esm/rules/utilities.js +26 -0
  70. package/package.json +33 -37
  71. package/typings/core/constants.d.ts +2 -0
  72. package/typings/core/context.d.ts +26 -0
  73. package/typings/core/index.d.ts +6 -0
  74. package/typings/core/types.d.ts +18 -0
  75. package/typings/core/validation-error.d.ts +5 -0
  76. package/typings/core/validator.d.ts +18 -0
  77. package/typings/helpers/object.utils.d.ts +2 -0
  78. package/typings/helpers/string.utils.d.ts +1 -0
  79. package/typings/index.d.ts +2 -0
  80. package/typings/rules/index.d.ts +23 -0
  81. package/typings/rules/is-any.d.ts +5 -0
  82. package/typings/rules/is-array.d.ts +7 -0
  83. package/typings/rules/is-bigint.d.ts +7 -0
  84. package/typings/rules/is-boolean.d.ts +7 -0
  85. package/typings/rules/is-date.d.ts +19 -0
  86. package/typings/rules/is-empty.d.ts +13 -0
  87. package/typings/rules/is-enum.d.ts +9 -0
  88. package/typings/rules/is-integer.d.ts +7 -0
  89. package/typings/rules/is-matches.d.ts +9 -0
  90. package/typings/rules/is-null.d.ts +16 -0
  91. package/typings/rules/is-number.d.ts +7 -0
  92. package/typings/rules/is-object-id.d.ts +10 -0
  93. package/typings/rules/is-object.d.ts +22 -0
  94. package/typings/rules/is-record.d.ts +7 -0
  95. package/typings/rules/is-string.d.ts +42 -0
  96. package/typings/rules/is-tuple.d.ts +27 -0
  97. package/typings/rules/length.d.ts +21 -0
  98. package/typings/rules/optional.d.ts +17 -0
  99. package/typings/rules/pipe.d.ts +11 -0
  100. package/typings/rules/range.d.ts +48 -0
  101. package/typings/rules/string-formats.d.ts +140 -0
  102. package/typings/rules/union.d.ts +5 -0
  103. package/typings/rules/utilities.d.ts +11 -0
  104. package/lib/ArrayType.d.ts +0 -5
  105. package/lib/ArrayType.js +0 -43
  106. package/lib/DataType.d.ts +0 -25
  107. package/lib/DataType.js +0 -214
  108. package/lib/ObjectType.d.ts +0 -5
  109. package/lib/ObjectType.js +0 -78
  110. package/lib/SchemaError.d.ts +0 -3
  111. package/lib/SchemaError.js +0 -17
  112. package/lib/UnionType.d.ts +0 -5
  113. package/lib/UnionType.js +0 -54
  114. package/lib/Valgen.d.ts +0 -88
  115. package/lib/Valgen.js +0 -289
  116. package/lib/ValidationError.d.ts +0 -2
  117. package/lib/ValidationError.js +0 -4
  118. package/lib/factories/AnyFactory.d.ts +0 -28
  119. package/lib/factories/AnyFactory.js +0 -73
  120. package/lib/factories/ArrayFactory.d.ts +0 -16
  121. package/lib/factories/ArrayFactory.js +0 -148
  122. package/lib/factories/Base64Factory.d.ts +0 -12
  123. package/lib/factories/Base64Factory.js +0 -31
  124. package/lib/factories/BooleanFactory.d.ts +0 -12
  125. package/lib/factories/BooleanFactory.js +0 -49
  126. package/lib/factories/DateFactory.d.ts +0 -40
  127. package/lib/factories/DateFactory.js +0 -239
  128. package/lib/factories/FunctionFactory.d.ts +0 -12
  129. package/lib/factories/FunctionFactory.js +0 -28
  130. package/lib/factories/IntegerFactory.d.ts +0 -14
  131. package/lib/factories/IntegerFactory.js +0 -33
  132. package/lib/factories/NilFactory.d.ts +0 -4
  133. package/lib/factories/NilFactory.js +0 -20
  134. package/lib/factories/NumberFactory.d.ts +0 -36
  135. package/lib/factories/NumberFactory.js +0 -178
  136. package/lib/factories/ObjectFactory.d.ts +0 -38
  137. package/lib/factories/ObjectFactory.js +0 -326
  138. package/lib/factories/StringFactory.d.ts +0 -16
  139. package/lib/factories/StringFactory.js +0 -94
  140. package/lib/factories/UnionFactory.d.ts +0 -13
  141. package/lib/factories/UnionFactory.js +0 -62
  142. package/lib/index.d.ts +0 -19
  143. package/lib/index.js +0 -37
@@ -0,0 +1,55 @@
1
+ import dayjs from 'dayjs';
2
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
3
+ import { validator } from '../core/index.js';
4
+ import { omitKeys } from '../helpers/object.utils.js';
5
+ dayjs.extend(customParseFormat);
6
+ /**
7
+ * Validates if value is instance of "Date".
8
+ * Converts input value to Date if coerce option is set to 'true'.
9
+ * @validator isDate
10
+ */
11
+ export function isDate(options) {
12
+ return validator('isDate', function (input, context) {
13
+ if (input != null && !(input instanceof Date) && context.coerce) {
14
+ if (typeof input === 'string') {
15
+ const d = dayjs(input, options?.format);
16
+ if (d.isValid())
17
+ input = d.toDate();
18
+ }
19
+ else if (typeof input === 'number')
20
+ input = new Date(input);
21
+ }
22
+ if (input && input instanceof Date && !isNaN(input.getTime()))
23
+ return input;
24
+ context.failure({
25
+ message: `{{label}} is not a valid Date instance` +
26
+ (context.coerce
27
+ ? ` or a date formatted${options?.format ? " (" + options.format + ")" : ''} string`
28
+ : ''),
29
+ format: options?.format
30
+ });
31
+ }, omitKeys(options || {}, ['format']));
32
+ }
33
+ /**
34
+ * Validates if value is DFS (date formatted string).
35
+ * Converts input value to DFS if coerce option is set to 'true'.
36
+ * @validator isDateString
37
+ */
38
+ export function isDateString(options) {
39
+ const inputFormat = options?.format;
40
+ const coerceFormat = Array.isArray(options?.format) ? options?.format[0] : options?.format;
41
+ return validator('isDateString', function (input, context) {
42
+ if (typeof input === 'string' || input instanceof Date) {
43
+ const d = dayjs(input, inputFormat);
44
+ if (d.isValid()) {
45
+ if (context.coerce && options?.format || input instanceof Date)
46
+ return coerceFormat ? d.format(coerceFormat) : d.toISOString();
47
+ return input;
48
+ }
49
+ }
50
+ context.failure({
51
+ message: `{{label}} is not a valid date formatted${options?.format ? " (" + options.format + ")" : ''} string`,
52
+ format: options?.format
53
+ });
54
+ }, omitKeys(options || {}, ['format']));
55
+ }
@@ -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}} is 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
+ }