valgen 6.2.2 → 7.0.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.
Files changed (67) hide show
  1. package/README.md +102 -15
  2. package/constants.js +1 -1
  3. package/core/context.js +15 -4
  4. package/core/utilities.js +4 -4
  5. package/index.js +5 -3
  6. package/package.json +1 -1
  7. package/rules/format-rules/is-alpha.js +1 -1
  8. package/rules/format-rules/is-alphanumeric.js +1 -1
  9. package/rules/format-rules/is-base64.js +1 -1
  10. package/rules/format-rules/is-email.js +3 -2
  11. package/rules/format-rules/is-iban.d.ts +2 -1
  12. package/rules/format-rules/is-iban.js +2 -2
  13. package/rules/format-rules/is-mobile-phone.d.ts +1 -1
  14. package/rules/format-rules/is-mobile-phone.js +2 -2
  15. package/rules/format-rules/is-passport-number.js +1 -1
  16. package/rules/format-rules/is-swift.js +1 -1
  17. package/rules/format-rules/is-time.js +1 -1
  18. package/rules/format-rules/matches.d.ts +2 -2
  19. package/rules/format-rules/matches.js +3 -2
  20. package/rules/logical-rules/is-defined.d.ts +2 -1
  21. package/rules/logical-rules/is-defined.js +2 -1
  22. package/rules/logical-rules/is-equal.d.ts +4 -2
  23. package/rules/logical-rules/is-equal.js +4 -2
  24. package/rules/logical-rules/is-gt.d.ts +2 -2
  25. package/rules/logical-rules/is-gt.js +2 -2
  26. package/rules/logical-rules/is-gte.d.ts +1 -1
  27. package/rules/logical-rules/is-gte.js +1 -1
  28. package/rules/logical-rules/is-lt.d.ts +1 -1
  29. package/rules/logical-rules/is-lt.js +2 -2
  30. package/rules/logical-rules/is-lte.d.ts +1 -1
  31. package/rules/logical-rules/is-lte.js +2 -2
  32. package/rules/type-rules/is-array.js +9 -7
  33. package/rules/type-rules/is-bigint.d.ts +2 -2
  34. package/rules/type-rules/is-bigint.js +9 -5
  35. package/rules/type-rules/is-boolean.js +2 -2
  36. package/rules/type-rules/is-date.d.ts +3 -2
  37. package/rules/type-rules/is-date.js +22 -15
  38. package/rules/type-rules/is-instanceof.d.ts +1 -1
  39. package/rules/type-rules/is-instanceof.js +1 -1
  40. package/rules/type-rules/is-integer.js +6 -3
  41. package/rules/type-rules/is-number.js +6 -3
  42. package/rules/type-rules/is-object.d.ts +7 -2
  43. package/rules/type-rules/is-object.js +27 -10
  44. package/rules/type-rules/is-record.js +10 -6
  45. package/rules/type-rules/is-string.d.ts +1 -1
  46. package/rules/type-rules/is-string.js +1 -1
  47. package/rules/type-rules/is-tuple.js +9 -9
  48. package/rules/type-rules/is-undefined.d.ts +4 -1
  49. package/rules/type-rules/is-undefined.js +4 -1
  50. package/rules/utility-rules/all-of.js +3 -2
  51. package/rules/utility-rules/exists.js +2 -1
  52. package/rules/utility-rules/fixed.d.ts +2 -2
  53. package/rules/utility-rules/fixed.js +2 -2
  54. package/rules/utility-rules/get-length.d.ts +2 -1
  55. package/rules/utility-rules/get-length.js +2 -1
  56. package/rules/utility-rules/nullable.d.ts +2 -2
  57. package/rules/utility-rules/nullable.js +3 -3
  58. package/rules/utility-rules/one-of.d.ts +4 -1
  59. package/rules/utility-rules/one-of.js +13 -5
  60. package/rules/utility-rules/optional.js +1 -1
  61. package/rules/utility-rules/pipe.d.ts +1 -1
  62. package/rules/utility-rules/pipe.js +8 -2
  63. package/rules/utility-rules/required.js +1 -1
  64. package/factories.d.ts +0 -1
  65. package/factories.js +0 -1
  66. package/helpers/object.utils.d.ts +0 -2
  67. package/helpers/object.utils.js +0 -14
package/README.md CHANGED
@@ -5,32 +5,119 @@
5
5
  [![CI Tests][ci-test-image]][ci-test-url]
6
6
  [![Test Coverage][coveralls-image]][coveralls-url]
7
7
 
8
- Fast runtime type validator, converter and io (encoding/decoding) library.
8
+ Fast runtime type validator, converter and io (encoding/decoding) library for TypeScript and
9
+ JavaScript.
10
+
11
+ - **Composable** - build complex schemas out of small validators with `allOf`, `oneOf`, `pipe`,
12
+ `optional`, `nullable`, `isObject`, `isArray`, ...
13
+ - **Coercing** - most rules can convert compatible input (`"42"` -> `42`, `"true"` -> `true`, ...)
14
+ instead of just rejecting it, via the `coerce` option.
15
+ - **Two calling conventions** - call a validator directly and catch a `ValidationError`, or use
16
+ `.silent(...)` to get back `{ value }` / `{ errors }` without throwing.
17
+ - **Typed** - every validator infers its output type, so a passing call narrows the type of its
18
+ return value.
19
+ - **Fast** - the core dispatch path and every rule are covered by a dedicated benchmark suite (see
20
+ [Benchmarking](#benchmarking) below); performance regressions are something this project
21
+ actively measures, not just hopes for.
9
22
 
10
23
  ## Installation
11
24
 
12
- `$ npm install valgen --save`
25
+ ```sh
26
+ npm install valgen --save
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ ```ts
32
+ import { vg, isEmail, ValidationError } from 'valgen';
33
+
34
+ // Individual, ready-to-use validators
35
+ isEmail('a@b.com'); // => 'a@b.com'
36
+ isEmail('not-an-email'); // throws ValidationError
37
+
38
+ // A full object schema, built from composable rules
39
+ const userSchema = vg.isObject({
40
+ id: vg.isUUID(),
41
+ email: vg.isEmail(),
42
+ age: vg.optional(vg.pipe([vg.isNumber({ coerce: true }), vg.isGt(0)])),
43
+ role: vg.isEnum(['admin', 'user']),
44
+ });
45
+
46
+ try {
47
+ const user = userSchema({
48
+ id: 'e6a3b1c0-70b6-4a3e-9b34-1e2f2e3d1a11',
49
+ email: 'a@b.com',
50
+ age: '30', // coerced to a number
51
+ role: 'admin',
52
+ });
53
+ } catch (e) {
54
+ if (e instanceof ValidationError) {
55
+ console.error(e.issues); // one entry per failing field
56
+ }
57
+ }
58
+
59
+ // Or avoid the try/catch entirely
60
+ const result = userSchema.silent({ email: 'not-an-email' });
61
+ if (result.errors) {
62
+ // result.errors: ErrorIssue[]
63
+ }
64
+ ```
65
+
66
+ ## Documentation
67
+
68
+ The full API reference lives under [`docs/`](docs/api.md):
69
+
70
+ - **[API overview](docs/api.md)** - the `Validator` shape, `.silent()`, pre-built instances vs.
71
+ factories, `ExecutionOptions`, error shape, composition patterns, and how to write a custom rule.
72
+ - **[Type Rules](docs/api/type-rules.md)** - `isString`, `isNumber`, `isObject`, `isArray`,
73
+ `isTuple`, `isEnum`, `isDate`, `isRecord`, `isInstanceOf`, ...
74
+ - **[Logical Rules](docs/api/logical-rules.md)** - `isEqual`, `isGt`/`isGte`/`isLt`/`isLte`,
75
+ `range`, `lengthMin`/`lengthMax`, `isEmpty`/`isNotEmpty`, `isDefined`.
76
+ - **[Utility Rules](docs/api/utility-rules.md)** - `allOf`, `oneOf`, `pipe`, `optional`,
77
+ `nullable`, `required`, `fixed`, `getLength`, `forwardRef`, `iif`, string helpers.
78
+ - **[Format Rules](docs/api/format-rules.md)** - `isEmail`, `isURL`, `isUUID`, `isIBAN`,
79
+ `isMACAddress`, `isCreditCard`, and every other string-format check.
80
+
81
+ ## Scripts
82
+
83
+ | Command | Description |
84
+ |---|---|
85
+ | `npm test` | Run the test suite (mocha). |
86
+ | `npm run citest` | Run the test suite with coverage (c8). |
87
+ | `npm run qc` | Lint + circular-dependency check. |
88
+ | `npm run bench` | Run the benchmark suite (see below). |
89
+ | `npm run build` | Type-check and compile to `build/`. |
90
+
91
+ ## Benchmarking
92
+
93
+ Every validator rule has a dedicated benchmark case measuring throughput (ops/sec) and per-call
94
+ memory allocation (heap/RSS), under `benchmark/rules/`.
95
+
96
+ ```sh
97
+ # Run every rule
98
+ npm run bench
99
+
100
+ # Run one or more rules (case-insensitive, comma-separated, "*" wildcards allowed)
101
+ npm run bench -- -s isEmail,isURL
102
+ npm run bench -- -s "is*"
103
+ ```
104
+
105
+ Results print to the console as they complete and are also written to [`BENCHMARKS.md`](BENCHMARKS.md)
106
+ at the end of the run.
13
107
 
14
108
  ## Node Compatibility
15
109
 
16
- - node `>= 16.0`;
17
-
110
+ - node `>= 20.0`
111
+
18
112
  ### License
113
+
19
114
  [MIT](LICENSE)
20
115
 
21
- [npm-image]: https://img.shields.io/npm/v/valgen.svg
116
+ [npm-image]: https://img.shields.io/npm/v/valgen
22
117
  [npm-url]: https://npmjs.org/package/valgen
118
+ [downloads-image]: https://img.shields.io/npm/dm/valgen.svg
119
+ [downloads-url]: https://npmjs.org/package/valgen
23
120
  [ci-test-image]: https://github.com/panates/valgen/actions/workflows/test.yml/badge.svg
24
121
  [ci-test-url]: https://github.com/panates/valgen/actions/workflows/test.yml
25
122
  [coveralls-image]: https://img.shields.io/coveralls/panates/valgen/master.svg
26
123
  [coveralls-url]: https://coveralls.io/r/panates/valgen
27
- [downloads-image]: https://img.shields.io/npm/dm/valgen.svg
28
- [downloads-url]: https://npmjs.org/package/valgen
29
- [gitter-image]: https://badges.gitter.im/panates/valgen.svg
30
- [gitter-url]: https://gitter.im/panates/valgen?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
31
- [dependencies-image]: https://david-dm.org/panates/valgen/status.svg
32
- [dependencies-url]:https://david-dm.org/panates/valgen
33
- [devdependencies-image]: https://david-dm.org/panates/valgen/dev-status.svg
34
- [devdependencies-url]:https://david-dm.org/panates/valgen?type=dev
35
- [quality-image]: http://npm.packagequality.com/shield/valgen.png
36
- [quality-url]: http://packagequality.com/#?package=valgen
package/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const version = '6.2.2';
1
+ export const version = '7.0.0';
2
2
  export const postValidation = Symbol('postValidation');
3
3
  export const preValidation = Symbol('preValidation');
4
4
  export const VALIDATE_METADATA = Symbol('VALIDATE_METADATA');
package/core/context.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { omitUndefined } from '../helpers/omit-undefined.js';
2
2
  import { ValidationError } from './validation-error.js';
3
3
  const VARIABLE_REPLACE_PATTERN = /{{([^}]*)}}/g;
4
- const OPTIONAL_VAR_PATTERN = /^([^?]+)(?:\||(.*))?$/;
4
+ const OPTIONAL_VAR_PATTERN = /^([^|]+)(?:\|(.*))?$/;
5
5
  export class Context {
6
6
  isRoot = true;
7
7
  errors = [];
@@ -66,15 +66,26 @@ export class Context {
66
66
  }
67
67
  }
68
68
  extend(options) {
69
- const extended = {};
69
+ // Object.create(this) sets the prototype at creation time. Setting it
70
+ // afterward via Object.setPrototypeOf() (the previous approach) is one
71
+ // of the slowest object operations in V8 - it invalidates hidden-class
72
+ // based optimizations for the object - and this runs on every nested
73
+ // validator call, so it showed up heavily in profiling.
74
+ const extended = Object.create(this);
70
75
  if (options) {
71
- for (const [k, v] of Object.entries(options)) {
76
+ // Same filtering as the old Object.entries()+destructure (skip
77
+ // undefined values, so an unset option falls through to the
78
+ // prototype chain instead of shadowing it) but without allocating an
79
+ // array of [key, value] pairs just to throw it away.
80
+ const keys = Object.keys(options);
81
+ for (let i = 0; i < keys.length; i++) {
82
+ const k = keys[i];
83
+ const v = options[k];
72
84
  if (v !== undefined)
73
85
  extended[k] = v;
74
86
  }
75
87
  }
76
88
  extended.isRoot = false;
77
- Object.setPrototypeOf(extended, this);
78
89
  return extended;
79
90
  }
80
91
  }
package/core/utilities.js CHANGED
@@ -6,21 +6,21 @@ import { isValidator, validator } from './validator.js';
6
6
  export function forwardRef(fn) {
7
7
  return validator('forwardRef', (input, context) => {
8
8
  const nested = fn(context);
9
- return nested(input, context);
9
+ return nested(input, undefined, context);
10
10
  });
11
11
  }
12
12
  export function iif(check, _then, _else) {
13
13
  return validator('iif', (input, context) => {
14
14
  let c = _else;
15
15
  try {
16
- if (check(input) !== undefined)
17
- c = _then;
16
+ check(input);
17
+ c = _then;
18
18
  }
19
19
  catch {
20
20
  // ignored
21
21
  }
22
22
  if (isValidator(c))
23
- return c(input, context);
23
+ return c(input, undefined, context);
24
24
  return c;
25
25
  });
26
26
  }
package/index.js CHANGED
@@ -73,15 +73,17 @@ const toDate = vg.isDate({ coerce: true });
73
73
  const toDateStringValidators = new Map();
74
74
  const toDateString = (input, options, ctx) => {
75
75
  const precisionMax = options?.trim ?? 'ms';
76
- let validator = toDateStringValidators.get(precisionMax);
76
+ const separators = options?.separators ?? true;
77
+ const cacheKey = precisionMax + '|' + separators;
78
+ let validator = toDateStringValidators.get(cacheKey);
77
79
  if (!validator) {
78
80
  validator = vg.isDateString({
79
81
  coerce: true,
80
82
  precisionMax,
81
83
  trim: true,
82
- separators: options?.separators,
84
+ separators,
83
85
  });
84
- toDateStringValidators.set(precisionMax, validator);
86
+ toDateStringValidators.set(cacheKey, validator);
85
87
  }
86
88
  return validator(input, options, ctx);
87
89
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "valgen",
3
3
  "description": "Fast runtime type validator, converter and io (encoding/decoding) library",
4
- "version": "6.2.2",
4
+ "version": "7.0.0",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
@@ -8,6 +8,6 @@ export function isAlpha(options) {
8
8
  return validator(isAlpha.name, (input, context, _this) => {
9
9
  if (typeof input === 'string' && validatorJS.isAlpha(input))
10
10
  return input;
11
- context.fail(_this, `"Value must be an alpha string`, input);
11
+ context.fail(_this, `Value must be an alpha string`, input);
12
12
  }, options);
13
13
  }
@@ -9,6 +9,6 @@ export function isAlphanumeric(options) {
9
9
  if (typeof input === 'string' && validatorJS.isAlphanumeric(input)) {
10
10
  return input;
11
11
  }
12
- context.fail(_this, `"Value must be an alphanumeric string`, input);
12
+ context.fail(_this, `Value must be an alphanumeric string`, input);
13
13
  }, options);
14
14
  }
@@ -9,6 +9,6 @@ export function isBase64(options) {
9
9
  if (typeof input === 'string' && validatorJS.isBase64(input, options)) {
10
10
  return input;
11
11
  }
12
- context.fail(_this, `"Value must be a Base64 string`, input);
12
+ context.fail(_this, `Value must be a Base64 string`, input);
13
13
  }, options);
14
14
  }
@@ -8,8 +8,9 @@ export function isEmail(options) {
8
8
  const emailOptions = {
9
9
  allow_display_name: true,
10
10
  allow_utf8_local_part: options?.utf8LocalPart,
11
- ignore_max_length: true,
11
+ ignore_max_length: options?.ignoreMaxLength,
12
12
  allow_ip_domain: options?.allowIpDomain,
13
+ domain_specific_validation: options?.domainSpecificValidation,
13
14
  };
14
15
  return validator(isEmail.name, (input, context, _this) => {
15
16
  if (typeof input === 'string' &&
@@ -57,6 +58,6 @@ export function isEmail(options) {
57
58
  }
58
59
  return input;
59
60
  }
60
- context.fail(_this, `Value must much required e-mail format`, input);
61
+ context.fail(_this, `Value must match required e-mail format`, input);
61
62
  }, options);
62
63
  }
@@ -1,3 +1,4 @@
1
+ import { type IsIBANOptions as _IsIBANOptions } from '@browsery/validator';
1
2
  import { type ValidationOptions } from '../../core/index.js';
2
3
  /**
3
4
  * Validates if value is an IBAN (International Bank Account Number)
@@ -5,6 +6,6 @@ import { type ValidationOptions } from '../../core/index.js';
5
6
  */
6
7
  export declare function isIBAN(options?: isIBAN.Options): import("../../core/validator.js").Validator<string, string, import("../../core/types.js").ExecutionOptions>;
7
8
  export declare namespace isIBAN {
8
- interface Options extends ValidationOptions {
9
+ interface Options extends ValidationOptions, _IsIBANOptions {
9
10
  }
10
11
  }
@@ -1,4 +1,4 @@
1
- import validatorJS from '@browsery/validator';
1
+ import validatorJS, {} from '@browsery/validator';
2
2
  import { validator, } from '../../core/index.js';
3
3
  /**
4
4
  * Validates if value is an IBAN (International Bank Account Number)
@@ -6,7 +6,7 @@ import { validator, } from '../../core/index.js';
6
6
  */
7
7
  export function isIBAN(options) {
8
8
  return validator(isIBAN.name, (input, context, _this) => {
9
- if (typeof input === 'string' && validatorJS.isIBAN(input))
9
+ if (typeof input === 'string' && validatorJS.isIBAN(input, options))
10
10
  return input;
11
11
  context.fail(_this, `Value must be a valid IBAN (International Bank Account Number)`, input);
12
12
  }, options);
@@ -1,7 +1,7 @@
1
1
  import { type MobilePhoneLocale as _MobilePhoneLocale } from '@browsery/validator';
2
2
  import { type ValidationOptions } from '../../core/index.js';
3
3
  /**
4
- * Validates if value is a valid Email
4
+ * Validates if value is a valid mobile phone number
5
5
  * @validator isMobilePhone
6
6
  */
7
7
  export declare function isMobilePhone(options?: isMobilePhone.Options): import("../../core/validator.js").Validator<string, string, import("../../core/types.js").ExecutionOptions>;
@@ -1,7 +1,7 @@
1
1
  import validatorJS, {} from '@browsery/validator';
2
2
  import { validator, } from '../../core/index.js';
3
3
  /**
4
- * Validates if value is a valid Email
4
+ * Validates if value is a valid mobile phone number
5
5
  * @validator isMobilePhone
6
6
  */
7
7
  export function isMobilePhone(options) {
@@ -13,6 +13,6 @@ export function isMobilePhone(options) {
13
13
  validatorJS.isMobilePhone(input, options?.locale, opts)) {
14
14
  return input;
15
15
  }
16
- context.fail(_this, `Value must be a valid a Mobile Phone Number`, input);
16
+ context.fail(_this, `Value must be a valid Mobile Phone Number`, input);
17
17
  }, options);
18
18
  }
@@ -10,6 +10,6 @@ export function isPassportNumber(countryCode, options) {
10
10
  validatorJS.isPassportNumber(input, countryCode)) {
11
11
  return input;
12
12
  }
13
- context.fail(_this, `Value must be a valid ${countryCode} PassportNumber)`, input);
13
+ context.fail(_this, `Value must be a valid ${countryCode} Passport Number`, input);
14
14
  }, options);
15
15
  }
@@ -8,6 +8,6 @@ export function isSWIFT(options) {
8
8
  return validator(isSWIFT.name, (input, context, _this) => {
9
9
  if (typeof input === 'string' && validatorJS.isBIC(input))
10
10
  return input;
11
- context.fail(_this, `Value must be a valid a BIC (Bank Identification Code) or SWIFT code`, input);
11
+ context.fail(_this, `Value must be a valid BIC (Bank Identification Code) or SWIFT code`, input);
12
12
  }, options);
13
13
  }
@@ -1,5 +1,5 @@
1
1
  import { validator, } from '../../core/index.js';
2
- const TIME_PATTERN = /^(\d{2}):?(\d{2})(?::?(\d{2})?(?:\.(\d{1,3}))?)?$/;
2
+ const TIME_PATTERN = /^(\d{2}):?(\d{2})(?::?(\d{2}))?(?:\.(\d{1,3}))?$/;
3
3
  /**
4
4
  * Validates if value is a time formatted string
5
5
  * @validator isTime
@@ -1,7 +1,7 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
- * Coerces given value to "UUID" format or returns undefined if nullish
4
- * @validator uuid
3
+ * Validates if value matches the given regular expression, or returns undefined if nullish
4
+ * @validator matches
5
5
  */
6
6
  export declare function matches(format: string | RegExp, options?: matches.Options): import("../../core/validator.js").Validator<string, string, import("../../core/types.js").ExecutionOptions>;
7
7
  export declare namespace matches {
@@ -1,7 +1,7 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- * Coerces given value to "UUID" format or returns undefined if nullish
4
- * @validator uuid
3
+ * Validates if value matches the given regular expression, or returns undefined if nullish
4
+ * @validator matches
5
5
  */
6
6
  export function matches(format, options) {
7
7
  const regExp = format instanceof RegExp ? format : new RegExp(format);
@@ -9,6 +9,7 @@ export function matches(format, options) {
9
9
  return validator(matches.name, (input, context, _this) => {
10
10
  if (input == null)
11
11
  return;
12
+ regExp.lastIndex = 0;
12
13
  if (typeof input === 'string' && regExp.test(input))
13
14
  return input;
14
15
  context.fail(_this, `Value must match ${formatName || 'requested'} format`, input, {
@@ -1,6 +1,7 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
- * Validates if value is not "undefined" nor "null"
3
+ * Validates if value is not "undefined". Note that "null" is considered
4
+ * defined; use `isNotNullish` to reject both "undefined" and "null".
4
5
  * @validator isDefined
5
6
  */
6
7
  export declare function isDefined(options?: isDefined.Options): import("../../core/validator.js").Validator<any, unknown, import("../../core/types.js").ExecutionOptions>;
@@ -1,6 +1,7 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- * Validates if value is not "undefined" nor "null"
3
+ * Validates if value is not "undefined". Note that "null" is considered
4
+ * defined; use `isNotNullish` to reject both "undefined" and "null".
4
5
  * @validator isDefined
5
6
  */
6
7
  export function isDefined(options) {
@@ -1,6 +1,7 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
- *
3
+ * Validates if the value is strictly equal ("===") to "compare".
4
+ * `null`/`undefined` input is passed through unchanged.
4
5
  * @validator isEqual
5
6
  */
6
7
  export declare function isEqual<T>(compare: T, options?: isEqual.Options): import("../../core/validator.js").Validator<any, any, import("../../core/types.js").ExecutionOptions>;
@@ -9,7 +10,8 @@ export declare namespace isEqual {
9
10
  }
10
11
  }
11
12
  /**
12
- *
13
+ * Validates if the value is not strictly equal ("===") to "compare".
14
+ * `null`/`undefined` input is passed through unchanged.
13
15
  * @validator isNotEqual
14
16
  */
15
17
  export declare function isNotEqual(compare: any, options?: isNotEqual.Options): import("../../core/validator.js").Validator<any, any, import("../../core/types.js").ExecutionOptions>;
@@ -1,6 +1,7 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- *
3
+ * Validates if the value is strictly equal ("===") to "compare".
4
+ * `null`/`undefined` input is passed through unchanged.
4
5
  * @validator isEqual
5
6
  */
6
7
  export function isEqual(compare, options) {
@@ -13,7 +14,8 @@ export function isEqual(compare, options) {
13
14
  }, options);
14
15
  }
15
16
  /**
16
- *
17
+ * Validates if the value is not strictly equal ("===") to "compare".
18
+ * `null`/`undefined` input is passed through unchanged.
17
19
  * @validator isNotEqual
18
20
  */
19
21
  export function isNotEqual(compare, options) {
@@ -1,8 +1,8 @@
1
1
  import { type ValidationOptions, type Validator } from '../../core/index.js';
2
2
  import type { range } from './range.js';
3
3
  /**
4
- * Checks if value is grater than "minValue"
5
- * @validator iGt
4
+ * Checks if value is greater than "minValue"
5
+ * @validator isGt
6
6
  */
7
7
  export declare function isGt<T extends range.Input>(minValue: T, options?: isGt.Options): Validator;
8
8
  export declare namespace isGt {
@@ -1,7 +1,7 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- * Checks if value is grater than "minValue"
4
- * @validator iGt
3
+ * Checks if value is greater than "minValue"
4
+ * @validator isGt
5
5
  */
6
6
  export function isGt(minValue, options) {
7
7
  return validator(isGt.name, (input, context, _this) => {
@@ -1,7 +1,7 @@
1
1
  import { type ValidationOptions, type Validator } from '../../core/index.js';
2
2
  import type { range } from './range.js';
3
3
  /**
4
- * Checks if value is grater than or equal to minValue
4
+ * Checks if value is greater than or equal to minValue
5
5
  * @validator isGte
6
6
  */
7
7
  export declare function isGte<T extends range.Input>(minValue: T, options?: isGte.Options): Validator<T>;
@@ -1,6 +1,6 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- * Checks if value is grater than or equal to minValue
3
+ * Checks if value is greater than or equal to minValue
4
4
  * @validator isGte
5
5
  */
6
6
  export function isGte(minValue, options) {
@@ -1,7 +1,7 @@
1
1
  import { type ValidationOptions, type Validator } from '../../core/index.js';
2
2
  import type { range } from './range.js';
3
3
  /**
4
- * Checks if the value is lover than maxValue
4
+ * Checks if the value is lower than maxValue
5
5
  * @validator isLt
6
6
  */
7
7
  export declare function isLt<T extends range.Input>(maxValue: T, options?: isLt.Options): Validator;
@@ -1,6 +1,6 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- * Checks if the value is lover than maxValue
3
+ * Checks if the value is lower than maxValue
4
4
  * @validator isLt
5
5
  */
6
6
  export function isLt(maxValue, options) {
@@ -22,6 +22,6 @@ export function isLt(maxValue, options) {
22
22
  input.toLowerCase() < maxValue.toLowerCase()))) {
23
23
  return input;
24
24
  }
25
- context.fail(_this, `Value must be lover than ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, input);
25
+ context.fail(_this, `Value must be lower than ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, input);
26
26
  }, options);
27
27
  }
@@ -1,7 +1,7 @@
1
1
  import { type ValidationOptions, type Validator } from '../../core/index.js';
2
2
  import type { range } from './range.js';
3
3
  /**
4
- * Checks if the value is lover than or equal to maxValue
4
+ * Checks if the value is lower than or equal to maxValue
5
5
  * @validator isLte
6
6
  */
7
7
  export declare function isLte<T extends range.Input>(maxValue: T, options?: isLte.Options): Validator;
@@ -1,6 +1,6 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
- * Checks if the value is lover than or equal to maxValue
3
+ * Checks if the value is lower than or equal to maxValue
4
4
  * @validator isLte
5
5
  */
6
6
  export function isLte(maxValue, options) {
@@ -22,6 +22,6 @@ export function isLte(maxValue, options) {
22
22
  input.toLowerCase() <= maxValue.toLowerCase()))) {
23
23
  return input;
24
24
  }
25
- context.fail(_this, `Value must be lover than or equal to ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, input);
25
+ context.fail(_this, `Value must be lower than or equal to ${typeof maxValue === 'string' ? `"${maxValue}"` : maxValue}`, input);
26
26
  }, options);
27
27
  }
@@ -16,25 +16,27 @@ export function isArray(itemValidator, options) {
16
16
  }
17
17
  if (!itemValidator)
18
18
  return output;
19
- // const location = context.location || '';
20
19
  const itemContext = context.extend();
21
20
  let i;
22
21
  let v;
23
22
  const l = output.length;
24
23
  const out = [];
24
+ // Set directly on the (already reused) context instead of passed as
25
+ // a fresh {onFail} options object on every call - `i` is read at call
26
+ // time (synchronously, before it's incremented), so a single closure
27
+ // works for the whole loop. This also lets the item call below pass
28
+ // `undefined` for options, which the validator wrapper needs in order
29
+ // to skip a needless context.extend() when the item rule has no
30
+ // options of its own.
31
+ itemContext.onFail = (issue) => `Item at index [${i}] is not valid. ` + issue.message;
25
32
  for (i = 0; i < l; i++) {
26
33
  v = output[i];
27
34
  itemContext.scope = output;
28
- // itemContext.location = location + '[' + i + ']';
29
35
  itemContext.location = context.location
30
36
  ? context.location + `[${i}]`
31
37
  : `<Array>[${i}]`;
32
38
  itemContext.index = i;
33
- v = itemValidator(v, {
34
- onFail(issue) {
35
- return `Item at index [${i}] is not valid. ` + issue.message;
36
- },
37
- }, itemContext);
39
+ v = itemValidator(v, undefined, itemContext);
38
40
  out.push(v);
39
41
  }
40
42
  return out;
@@ -1,8 +1,8 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
3
  * Validates if value is "BigInt".
4
- * Converts input value to number if the coerce option is set to 'true'.
5
- * @validator isNumber
4
+ * Converts input value to BigInt if the coerce option is set to 'true'.
5
+ * @validator isBigint
6
6
  */
7
7
  export declare function isBigint(options?: isBigint.Options): import("../../core/validator.js").Validator<bigint, unknown, import("../../core/types.js").ExecutionOptions>;
8
8
  export declare namespace isBigint {
@@ -1,17 +1,21 @@
1
1
  import { validator, } from '../../core/index.js';
2
2
  /**
3
3
  * Validates if value is "BigInt".
4
- * Converts input value to number if the coerce option is set to 'true'.
5
- * @validator isNumber
4
+ * Converts input value to BigInt if the coerce option is set to 'true'.
5
+ * @validator isBigint
6
6
  */
7
7
  export function isBigint(options) {
8
8
  return validator(isBigint.name, (input, context, _this) => {
9
9
  const coerce = options?.coerce ?? context.coerce;
10
10
  if (typeof input === 'bigint')
11
11
  return input;
12
- if ((typeof input === 'number' && !isNaN(input)) ||
13
- (typeof input === 'string' && coerce)) {
14
- return BigInt(input);
12
+ if (coerce && (typeof input === 'number' || typeof input === 'string')) {
13
+ try {
14
+ return BigInt(input);
15
+ }
16
+ catch {
17
+ // falls through to context.fail below
18
+ }
15
19
  }
16
20
  context.fail(_this, `Value must be a BigInt`, input);
17
21
  }, options);
@@ -1,6 +1,6 @@
1
1
  import { validator, } from '../../core/index.js';
2
- const TRUE_PATTERN = /^true|t|1|yes|y$/i;
3
- const FALSE_PATTERN = /^false|f|0|no|n$/i;
2
+ const TRUE_PATTERN = /^(?:true|t|1|yes|y)$/i;
3
+ const FALSE_PATTERN = /^(?:false|f|0|no|n)$/i;
4
4
  /**
5
5
  * Validates if value is "boolean".
6
6
  * Converts input value to boolean if the coerce option is set to 'true'.