valgen 7.0.1 → 7.0.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/README.md +1 -1
  2. package/constants.js +1 -1
  3. package/core/constants.d.ts +11 -0
  4. package/core/constants.js +11 -0
  5. package/core/context.d.ts +57 -0
  6. package/core/context.js +57 -0
  7. package/core/types.d.ts +40 -0
  8. package/core/utilities.d.ts +42 -2
  9. package/core/utilities.js +21 -1
  10. package/core/validation-error.d.ts +13 -0
  11. package/core/validation-error.js +13 -0
  12. package/core/validator.d.ts +100 -0
  13. package/core/validator.js +6 -0
  14. package/package.json +1 -1
  15. package/rules/format-rules/is-alpha.d.ts +10 -1
  16. package/rules/format-rules/is-alpha.js +10 -1
  17. package/rules/format-rules/is-alphanumeric.d.ts +10 -1
  18. package/rules/format-rules/is-alphanumeric.js +10 -1
  19. package/rules/format-rules/is-ascii.d.ts +10 -1
  20. package/rules/format-rules/is-ascii.js +10 -1
  21. package/rules/format-rules/is-base64.d.ts +20 -1
  22. package/rules/format-rules/is-base64.js +10 -1
  23. package/rules/format-rules/is-btc-address.d.ts +10 -1
  24. package/rules/format-rules/is-btc-address.js +10 -1
  25. package/rules/format-rules/is-credit-card.d.ts +16 -1
  26. package/rules/format-rules/is-credit-card.js +11 -1
  27. package/rules/format-rules/is-decimal.d.ts +12 -2
  28. package/rules/format-rules/is-decimal.js +12 -2
  29. package/rules/format-rules/is-ean.d.ts +11 -1
  30. package/rules/format-rules/is-ean.js +11 -1
  31. package/rules/format-rules/is-email.d.ts +19 -7
  32. package/rules/format-rules/is-email.js +13 -1
  33. package/rules/format-rules/is-eth-address.d.ts +10 -1
  34. package/rules/format-rules/is-eth-address.js +10 -1
  35. package/rules/format-rules/is-fqdn.d.ts +13 -2
  36. package/rules/format-rules/is-fqdn.js +12 -1
  37. package/rules/format-rules/is-hash.d.ts +17 -1
  38. package/rules/format-rules/is-hash.js +16 -1
  39. package/rules/format-rules/is-hex-color.d.ts +13 -1
  40. package/rules/format-rules/is-hex-color.js +13 -1
  41. package/rules/format-rules/is-hex.d.ts +13 -1
  42. package/rules/format-rules/is-hex.js +13 -1
  43. package/rules/format-rules/is-iban.d.ts +33 -2
  44. package/rules/format-rules/is-iban.js +19 -1
  45. package/rules/format-rules/is-ip.d.ts +44 -2
  46. package/rules/format-rules/is-ip.js +42 -2
  47. package/rules/format-rules/is-issn.d.ts +19 -2
  48. package/rules/format-rules/is-issn.js +18 -1
  49. package/rules/format-rules/is-jwt.d.ts +16 -1
  50. package/rules/format-rules/is-jwt.js +16 -1
  51. package/rules/format-rules/is-lowercase.d.ts +13 -1
  52. package/rules/format-rules/is-lowercase.js +13 -1
  53. package/rules/format-rules/is-mac-address.d.ts +19 -2
  54. package/rules/format-rules/is-mac-address.js +16 -1
  55. package/rules/format-rules/is-mobile-phone.d.ts +19 -3
  56. package/rules/format-rules/is-mobile-phone.js +17 -1
  57. package/rules/format-rules/is-object-id.d.ts +20 -1
  58. package/rules/format-rules/is-object-id.js +19 -1
  59. package/rules/format-rules/is-passport-number.d.ts +17 -1
  60. package/rules/format-rules/is-passport-number.js +17 -1
  61. package/rules/format-rules/is-port.d.ts +16 -1
  62. package/rules/format-rules/is-port.js +16 -1
  63. package/rules/format-rules/is-swift.d.ts +15 -1
  64. package/rules/format-rules/is-swift.js +15 -1
  65. package/rules/format-rules/is-time.d.ts +22 -1
  66. package/rules/format-rules/is-time.js +22 -1
  67. package/rules/format-rules/is-uppercase.d.ts +15 -1
  68. package/rules/format-rules/is-uppercase.js +15 -1
  69. package/rules/format-rules/is-url.d.ts +99 -1
  70. package/rules/format-rules/is-url.js +19 -1
  71. package/rules/format-rules/is-uuid.d.ts +23 -1
  72. package/rules/format-rules/is-uuid.js +23 -1
  73. package/rules/format-rules/is-vat-number.d.ts +19 -2
  74. package/rules/format-rules/is-vat-number.js +19 -2
  75. package/rules/format-rules/matches.d.ts +27 -1
  76. package/rules/format-rules/matches.js +23 -1
  77. package/rules/logical-rules/is-defined.d.ts +14 -2
  78. package/rules/logical-rules/is-defined.js +13 -2
  79. package/rules/logical-rules/is-empty.d.ts +40 -2
  80. package/rules/logical-rules/is-empty.js +38 -2
  81. package/rules/logical-rules/is-equal.d.ts +31 -4
  82. package/rules/logical-rules/is-equal.js +29 -4
  83. package/rules/logical-rules/is-gt.d.ts +26 -1
  84. package/rules/logical-rules/is-gt.js +20 -1
  85. package/rules/logical-rules/is-gte.d.ts +25 -1
  86. package/rules/logical-rules/is-gte.js +20 -1
  87. package/rules/logical-rules/is-lt.d.ts +24 -1
  88. package/rules/logical-rules/is-lt.js +19 -1
  89. package/rules/logical-rules/is-lte.d.ts +25 -1
  90. package/rules/logical-rules/is-lte.js +20 -1
  91. package/rules/logical-rules/length.d.ts +38 -2
  92. package/rules/logical-rules/length.js +38 -2
  93. package/rules/logical-rules/range.d.ts +25 -1
  94. package/rules/logical-rules/range.js +23 -1
  95. package/rules/type-rules/is-any.d.ts +12 -1
  96. package/rules/type-rules/is-any.js +12 -1
  97. package/rules/type-rules/is-array.d.ts +23 -2
  98. package/rules/type-rules/is-array.js +23 -2
  99. package/rules/type-rules/is-bigint.d.ts +14 -2
  100. package/rules/type-rules/is-bigint.js +14 -2
  101. package/rules/type-rules/is-boolean.d.ts +17 -2
  102. package/rules/type-rules/is-boolean.js +17 -2
  103. package/rules/type-rules/is-date.d.ts +47 -5
  104. package/rules/type-rules/is-date.js +42 -5
  105. package/rules/type-rules/is-enum.d.ts +23 -1
  106. package/rules/type-rules/is-enum.js +21 -1
  107. package/rules/type-rules/is-instanceof.d.ts +21 -1
  108. package/rules/type-rules/is-instanceof.js +21 -1
  109. package/rules/type-rules/is-integer.d.ts +17 -2
  110. package/rules/type-rules/is-integer.js +17 -2
  111. package/rules/type-rules/is-null.d.ts +51 -4
  112. package/rules/type-rules/is-null.js +51 -4
  113. package/rules/type-rules/is-number.d.ts +17 -2
  114. package/rules/type-rules/is-number.js +17 -2
  115. package/rules/type-rules/is-object.d.ts +45 -7
  116. package/rules/type-rules/is-object.js +40 -7
  117. package/rules/type-rules/is-record.d.ts +21 -2
  118. package/rules/type-rules/is-record.js +21 -2
  119. package/rules/type-rules/is-string.d.ts +16 -2
  120. package/rules/type-rules/is-string.js +16 -2
  121. package/rules/type-rules/is-tuple.d.ts +21 -2
  122. package/rules/type-rules/is-undefined.d.ts +16 -4
  123. package/rules/type-rules/is-undefined.js +16 -4
  124. package/rules/utility-rules/all-of.d.ts +28 -1
  125. package/rules/utility-rules/all-of.js +27 -1
  126. package/rules/utility-rules/exists.d.ts +30 -1
  127. package/rules/utility-rules/exists.js +29 -1
  128. package/rules/utility-rules/fixed.d.ts +19 -1
  129. package/rules/utility-rules/fixed.js +19 -1
  130. package/rules/utility-rules/get-length.d.ts +25 -2
  131. package/rules/utility-rules/get-length.js +25 -2
  132. package/rules/utility-rules/nullable.d.ts +28 -1
  133. package/rules/utility-rules/nullable.js +27 -1
  134. package/rules/utility-rules/one-of.d.ts +52 -3
  135. package/rules/utility-rules/one-of.js +51 -3
  136. package/rules/utility-rules/optional.d.ts +29 -1
  137. package/rules/utility-rules/optional.js +28 -1
  138. package/rules/utility-rules/pipe.d.ts +38 -1
  139. package/rules/utility-rules/pipe.js +30 -1
  140. package/rules/utility-rules/required.d.ts +39 -1
  141. package/rules/utility-rules/required.js +32 -1
  142. package/rules/utility-rules/string-utils.d.ts +99 -5
  143. package/rules/utility-rules/string-utils.js +53 -3
package/README.md CHANGED
@@ -119,5 +119,5 @@ at the end of the run.
119
119
  [downloads-url]: https://npmjs.org/package/valgen
120
120
  [ci-test-image]: https://github.com/panates/valgen/actions/workflows/test.yml/badge.svg
121
121
  [ci-test-url]: https://github.com/panates/valgen/actions/workflows/test.yml
122
- [coveralls-image]: https://img.shields.io/coveralls/panates/valgen/master.svg
122
+ [coveralls-image]: https://img.shields.io/coveralls/panates/valgen/dev.svg
123
123
  [coveralls-url]: https://coveralls.io/r/panates/valgen
package/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const version = '7.0.1';
1
+ export const version = '7.0.3';
2
2
  export const postValidation = Symbol('postValidation');
3
3
  export const preValidation = Symbol('preValidation');
4
4
  export const VALIDATE_METADATA = Symbol('VALIDATE_METADATA');
@@ -1,2 +1,13 @@
1
+ /**
2
+ * Symbol key used to store a {@link Validator}'s underlying
3
+ * {@link ValidateFunction} on itself. Exported only because it appears in
4
+ * `Validator`'s type signature; not intended for direct use.
5
+ * @internal
6
+ */
1
7
  export declare const kValidatorFn: unique symbol;
8
+ /**
9
+ * Symbol key used to store a {@link Validator}'s construction-time
10
+ * {@link ValidationOptions} on itself. Not intended for direct use.
11
+ * @internal
12
+ */
2
13
  export declare const kOptions: unique symbol;
package/core/constants.js CHANGED
@@ -1,2 +1,13 @@
1
+ /**
2
+ * Symbol key used to store a {@link Validator}'s underlying
3
+ * {@link ValidateFunction} on itself. Exported only because it appears in
4
+ * `Validator`'s type signature; not intended for direct use.
5
+ * @internal
6
+ */
1
7
  export const kValidatorFn = Symbol.for('kValidatorFn');
8
+ /**
9
+ * Symbol key used to store a {@link Validator}'s construction-time
10
+ * {@link ValidationOptions} on itself. Not intended for direct use.
11
+ * @internal
12
+ */
2
13
  export const kOptions = Symbol.for('kOptions');
package/core/context.d.ts CHANGED
@@ -1,20 +1,77 @@
1
1
  import type { ErrorIssue, ExecutionOptions, OnFailFunction } from './types.js';
2
2
  import type { Validator } from './validator.js';
3
+ /**
4
+ * Carries execution options and accumulated failures through a single
5
+ * validation run, including across nested validator calls.
6
+ *
7
+ * A root `Context` is created once for a top-level `validator(...)` call;
8
+ * nested rules (properties of an `isObject` schema, items of an `isArray`,
9
+ * steps of a `pipe`, ...) share it via {@link Context.extend}, so failures
10
+ * from anywhere in the run accumulate into the same `errors` array and are
11
+ * thrown together as a single {@link ValidationError}.
12
+ */
3
13
  export declare class Context implements ExecutionOptions {
14
+ /** `true` for the context created at the top-level call; `false` for every context produced by {@link Context.extend}. */
4
15
  isRoot: boolean;
16
+ /** Issues recorded so far during this validation run (shared across nested contexts). */
5
17
  errors: ErrorIssue[];
18
+ /** Stops collecting further issues (and throws immediately) once this many have been recorded. */
6
19
  maxErrors?: number;
20
+ /** Intercepts every failure recorded through this context. */
7
21
  onFail?: OnFailFunction;
22
+ /** Enables coercion for rules that support it. */
8
23
  coerce?: boolean;
24
+ /** An identifying name for the root value being validated (e.g. a schema's class name), set once and inherited by every nested context. */
9
25
  root?: string;
26
+ /** A dotted/bracketed path to the value currently being validated, e.g. `"user.address"` or `"items[0]"`. */
10
27
  location?: string;
28
+ /** The object a nested property/item belongs to - used by rules like `exists` to look up property descriptors. */
11
29
  scope?: object;
30
+ /** A human-readable name for the object/schema the current value belongs to. */
12
31
  context?: string;
32
+ /** The name of the property currently being validated, when nested inside an object schema. */
13
33
  property?: string;
34
+ /** The index of the item currently being validated, when nested inside an array/tuple. */
14
35
  index?: number;
36
+ /** A human-readable label for the current value, used to resolve `{{label}}` in message templates. */
15
37
  label?: string;
16
38
  [key: string]: any;
39
+ /**
40
+ * @param options - Initial execution options, assigned directly onto the new context.
41
+ */
17
42
  constructor(options?: ExecutionOptions);
43
+ /**
44
+ * Records a validation failure as an {@link ErrorIssue}.
45
+ *
46
+ * Resolves `{{key}}` / `{{key|fallback}}` placeholders in `message` against
47
+ * the issue's own fields (falling back to this context's `location`/
48
+ * `property` for `{{label}}`), runs the context's `onFail` interceptor if
49
+ * set, then appends the result to `errors`. Throws immediately once
50
+ * `errors.length` reaches `maxErrors`; otherwise the root call throws a
51
+ * single {@link ValidationError} once the whole run completes.
52
+ *
53
+ * @param rule - The validator reporting the failure; its `id` is recorded on the issue.
54
+ * @param message - The failure message, or an `Error` whose `.message` is
55
+ * used instead. May contain `{{key}}`/`{{key|fallback}}` placeholders.
56
+ * @param value - The offending value.
57
+ * @param details - Additional fields to merge onto the recorded issue.
58
+ */
18
59
  fail(rule: Validator, message: string | Error, value: any, details?: Record<string, any>): void;
60
+ /**
61
+ * Creates a child context for a nested validator call, inheriting every
62
+ * field from this context via the prototype chain and overriding it with
63
+ * `options` where given.
64
+ *
65
+ * Nested rules (an object schema's properties, an array's items, a pipe's
66
+ * steps, ...) should reuse the *same* extended context across every call
67
+ * within one loop rather than calling `extend()` per call, and should
68
+ * prefer the 3-argument form `nestedRule(input, undefined, context)` so
69
+ * the validator wrapper doesn't extend the context a second time.
70
+ *
71
+ * @param options - Fields to override on the child context; `undefined`
72
+ * values are skipped so they fall through to the parent instead of
73
+ * shadowing it.
74
+ * @returns A new `Context` with `isRoot: false`, prototypically inheriting from this one.
75
+ */
19
76
  extend(options?: ExecutionOptions): Context;
20
77
  }
package/core/context.js CHANGED
@@ -2,22 +2,63 @@ import { omitUndefined } from '../helpers/omit-undefined.js';
2
2
  import { ValidationError } from './validation-error.js';
3
3
  const VARIABLE_REPLACE_PATTERN = /{{([^}]*)}}/g;
4
4
  const OPTIONAL_VAR_PATTERN = /^([^|]+)(?:\|(.*))?$/;
5
+ /**
6
+ * Carries execution options and accumulated failures through a single
7
+ * validation run, including across nested validator calls.
8
+ *
9
+ * A root `Context` is created once for a top-level `validator(...)` call;
10
+ * nested rules (properties of an `isObject` schema, items of an `isArray`,
11
+ * steps of a `pipe`, ...) share it via {@link Context.extend}, so failures
12
+ * from anywhere in the run accumulate into the same `errors` array and are
13
+ * thrown together as a single {@link ValidationError}.
14
+ */
5
15
  export class Context {
16
+ /** `true` for the context created at the top-level call; `false` for every context produced by {@link Context.extend}. */
6
17
  isRoot = true;
18
+ /** Issues recorded so far during this validation run (shared across nested contexts). */
7
19
  errors = [];
20
+ /** Stops collecting further issues (and throws immediately) once this many have been recorded. */
8
21
  maxErrors;
22
+ /** Intercepts every failure recorded through this context. */
9
23
  onFail;
24
+ /** Enables coercion for rules that support it. */
10
25
  coerce;
26
+ /** An identifying name for the root value being validated (e.g. a schema's class name), set once and inherited by every nested context. */
11
27
  root;
28
+ /** A dotted/bracketed path to the value currently being validated, e.g. `"user.address"` or `"items[0]"`. */
12
29
  location;
30
+ /** The object a nested property/item belongs to - used by rules like `exists` to look up property descriptors. */
13
31
  scope;
32
+ /** A human-readable name for the object/schema the current value belongs to. */
14
33
  context;
34
+ /** The name of the property currently being validated, when nested inside an object schema. */
15
35
  property;
36
+ /** The index of the item currently being validated, when nested inside an array/tuple. */
16
37
  index;
38
+ /** A human-readable label for the current value, used to resolve `{{label}}` in message templates. */
17
39
  label;
40
+ /**
41
+ * @param options - Initial execution options, assigned directly onto the new context.
42
+ */
18
43
  constructor(options) {
19
44
  Object.assign(this, options);
20
45
  }
46
+ /**
47
+ * Records a validation failure as an {@link ErrorIssue}.
48
+ *
49
+ * Resolves `{{key}}` / `{{key|fallback}}` placeholders in `message` against
50
+ * the issue's own fields (falling back to this context's `location`/
51
+ * `property` for `{{label}}`), runs the context's `onFail` interceptor if
52
+ * set, then appends the result to `errors`. Throws immediately once
53
+ * `errors.length` reaches `maxErrors`; otherwise the root call throws a
54
+ * single {@link ValidationError} once the whole run completes.
55
+ *
56
+ * @param rule - The validator reporting the failure; its `id` is recorded on the issue.
57
+ * @param message - The failure message, or an `Error` whose `.message` is
58
+ * used instead. May contain `{{key}}`/`{{key|fallback}}` placeholders.
59
+ * @param value - The offending value.
60
+ * @param details - Additional fields to merge onto the recorded issue.
61
+ */
21
62
  fail(rule, message, value, details) {
22
63
  const issue = omitUndefined({
23
64
  message: message instanceof Error ? message.message : String(message),
@@ -65,6 +106,22 @@ export class Context {
65
106
  throw new ValidationError(this.errors);
66
107
  }
67
108
  }
109
+ /**
110
+ * Creates a child context for a nested validator call, inheriting every
111
+ * field from this context via the prototype chain and overriding it with
112
+ * `options` where given.
113
+ *
114
+ * Nested rules (an object schema's properties, an array's items, a pipe's
115
+ * steps, ...) should reuse the *same* extended context across every call
116
+ * within one loop rather than calling `extend()` per call, and should
117
+ * prefer the 3-argument form `nestedRule(input, undefined, context)` so
118
+ * the validator wrapper doesn't extend the context a second time.
119
+ *
120
+ * @param options - Fields to override on the child context; `undefined`
121
+ * values are skipped so they fall through to the parent instead of
122
+ * shadowing it.
123
+ * @returns A new `Context` with `isRoot: false`, prototypically inheriting from this one.
124
+ */
68
125
  extend(options) {
69
126
  // Object.create(this) sets the prototype at creation time. Setting it
70
127
  // afterward via Object.setPrototypeOf() (the previous approach) is one
package/core/types.d.ts CHANGED
@@ -1,24 +1,64 @@
1
1
  import type { Context } from './context.js';
2
+ /**
3
+ * Describes a single validation failure recorded during a validator run.
4
+ *
5
+ * One `ErrorIssue` is created per failed check - a single {@link ValidationError}
6
+ * can carry more than one, e.g. when `isObject` validates several properties.
7
+ */
2
8
  export interface ErrorIssue {
9
+ /** The (already template-resolved) human-readable failure message. */
3
10
  message: string;
11
+ /** The id of the validator that reported this issue, e.g. `"isEmail"`. */
4
12
  rule: string;
13
+ /** The offending value that failed validation. */
5
14
  value: any;
15
+ /** An identifying name for the root value being validated (e.g. a schema's class name), inherited from the root {@link Context}. */
6
16
  root?: string;
17
+ /** A dotted/bracketed path to the failing value, e.g. `"user.email"` or `"items[0]"`. */
7
18
  location?: string;
19
+ /** A human-readable name for the object/schema the failing property belongs to. */
8
20
  context?: string;
21
+ /** The name of the failing property, when the issue originates from an object schema. */
9
22
  property?: string;
23
+ /** The index of the failing item, when the issue originates from an array/tuple. */
10
24
  index?: number;
25
+ /** A human-readable label for the failing value, used to resolve `{{label}}` in message templates. */
11
26
  label?: string;
12
27
  [key: string]: any;
13
28
  }
29
+ /**
30
+ * Intercepts a validation failure before it is recorded.
31
+ *
32
+ * Return a `string` to replace the issue's message, a partial {@link ErrorIssue}
33
+ * to merge onto it, or a falsy value to discard the issue entirely (as if
34
+ * validation had passed for this particular check).
35
+ *
36
+ * @param issue - The issue that is about to be recorded.
37
+ * @param context - The {@link Context} the failure occurred in.
38
+ * @returns A replacement message, a partial issue to merge in, or a falsy value to discard the issue.
39
+ */
14
40
  export type OnFailFunction = (issue: ErrorIssue, context: Context) => string | Omit<ErrorIssue, 'id' | 'input'>;
41
+ /**
42
+ * Options accepted by the {@link validator} factory - as opposed to per-call
43
+ * {@link ExecutionOptions}, these configure a rule's default behavior for
44
+ * every call unless overridden.
45
+ */
15
46
  export interface ValidationOptions {
47
+ /** Intercepts every failure this rule records. */
16
48
  onFail?: OnFailFunction;
49
+ /** Enables coercion by default, for rules that support it. */
17
50
  coerce?: boolean;
18
51
  }
52
+ /**
53
+ * Options accepted on a per-call basis by every {@link Validator}, in
54
+ * addition to whatever rule-specific options a given validator documents.
55
+ */
19
56
  export interface ExecutionOptions extends ValidationOptions {
57
+ /** When `true`, and the rule supports it, coerces compatible input into the target type instead of rejecting it. */
20
58
  coerce?: boolean;
59
+ /** Stops collecting further issues (and throws immediately) once this many have been recorded. Defaults to unlimited. */
21
60
  maxErrors?: number;
61
+ /** A human-readable name for the value being validated, used to resolve `{{label}}` in message templates instead of the raw property name. */
22
62
  label?: string;
23
63
  [key: string]: any;
24
64
  }
@@ -1,12 +1,52 @@
1
1
  import type { Context } from './context.js';
2
2
  import { type Validator } from './validator.js';
3
3
  /**
4
- * Forwards codec process to a sub codec. Useful for circular checks
4
+ * Defers to a validator produced lazily by `fn`, so a schema can reference
5
+ * itself (directly, or through a cycle of other schemas) without a
6
+ * "used before defined" error.
7
+ *
8
+ * @typeParam T - The output type of the resolved validator.
9
+ * @typeParam I - The input type of the resolved validator.
10
+ * @param fn - Called on every validation to resolve the actual validator to
11
+ * delegate to - receives the current {@link Context} in case the
12
+ * resolution needs it.
13
+ * @returns A validator that resolves and delegates to `fn(context)` on every call.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { forwardRef, vg } from 'valgen';
18
+ *
19
+ * // self-referential schema, e.g. a tree node
20
+ * const treeNode = vg.isObject({
21
+ * value: vg.isNumber(),
22
+ * children: vg.optional(vg.isArray(forwardRef(() => treeNode))),
23
+ * });
24
+ * ```
5
25
  * @validator forwardRef
6
26
  */
7
27
  export declare function forwardRef<T, I>(fn: (context: Context) => Validator<T, I>): Validator<T, I, import("./types.js").ExecutionOptions>;
8
28
  /**
9
- * if "check" codec passes returns "then", "else" otherwise
29
+ * Picks between two validators (or plain values) based on whether `check`
30
+ * passes against the input.
31
+ *
32
+ * @typeParam TOutput1 - The output type when `check` passes and `than_` is a validator.
33
+ * @typeParam TOutput2 - The output type when `check` fails and `else_` is a validator.
34
+ * @typeParam TDefault1 - The type of `than_` when it is a plain (non-validator) value.
35
+ * @typeParam TDefault2 - The type of `else_` when it is a plain (non-validator) value.
36
+ * @param check - Tried against the input; success (no throw) selects `than_`, failure selects `else_`.
37
+ * @param than_ - Used when `check` passes: a validator to run against the
38
+ * input, or a constant value to return as-is (ignoring the input).
39
+ * @param else_ - Used when `check` fails: a validator to run against the
40
+ * input, or a constant value to return as-is. Defaults to `undefined` when omitted.
41
+ * @returns A validator that resolves to whichever branch `check` selects.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { iif, isDefined, isNumber, isString } from 'valgen';
46
+ *
47
+ * // coerce differently depending on whether the value is present
48
+ * iif(isDefined, isString, isNumber)(1, { coerce: true }); // => '1'
49
+ * ```
10
50
  * @validator iif
11
51
  */
12
52
  export declare function iif<TOutput1, TOutput2, TDefault1, TDefault2>(check: Validator<any>, than_: TDefault1 | Validator<TOutput1, any>, else_?: TDefault2 | Validator<TOutput2, any>): any;
package/core/utilities.js CHANGED
@@ -1,6 +1,26 @@
1
1
  import { isValidator, validator } from './validator.js';
2
2
  /**
3
- * Forwards codec process to a sub codec. Useful for circular checks
3
+ * Defers to a validator produced lazily by `fn`, so a schema can reference
4
+ * itself (directly, or through a cycle of other schemas) without a
5
+ * "used before defined" error.
6
+ *
7
+ * @typeParam T - The output type of the resolved validator.
8
+ * @typeParam I - The input type of the resolved validator.
9
+ * @param fn - Called on every validation to resolve the actual validator to
10
+ * delegate to - receives the current {@link Context} in case the
11
+ * resolution needs it.
12
+ * @returns A validator that resolves and delegates to `fn(context)` on every call.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { forwardRef, vg } from 'valgen';
17
+ *
18
+ * // self-referential schema, e.g. a tree node
19
+ * const treeNode = vg.isObject({
20
+ * value: vg.isNumber(),
21
+ * children: vg.optional(vg.isArray(forwardRef(() => treeNode))),
22
+ * });
23
+ * ```
4
24
  * @validator forwardRef
5
25
  */
6
26
  export function forwardRef(fn) {
@@ -1,5 +1,18 @@
1
1
  import { type ErrorIssue } from './types.js';
2
+ /**
3
+ * Thrown by a {@link Validator} when validation fails at the root call (as
4
+ * opposed to `.silent()`, which returns a `{ errors }` result instead).
5
+ *
6
+ * Carries every {@link ErrorIssue} recorded during the run, not just the
7
+ * first one - inspect `issues` for the full list of failures, e.g. every
8
+ * failing property of an `isObject` schema.
9
+ */
2
10
  export declare class ValidationError extends Error {
11
+ /** Every issue recorded during this validation run, in the order they occurred. */
3
12
  issues: ErrorIssue[];
13
+ /**
14
+ * @param issues - The recorded issues. The error's own `message` (and
15
+ * optional `\n at <location>` suffix) is derived from `issues[0]`.
16
+ */
4
17
  constructor(issues: ErrorIssue[]);
5
18
  }
@@ -1,6 +1,19 @@
1
1
  import {} from './types.js';
2
+ /**
3
+ * Thrown by a {@link Validator} when validation fails at the root call (as
4
+ * opposed to `.silent()`, which returns a `{ errors }` result instead).
5
+ *
6
+ * Carries every {@link ErrorIssue} recorded during the run, not just the
7
+ * first one - inspect `issues` for the full list of failures, e.g. every
8
+ * failing property of an `isObject` schema.
9
+ */
2
10
  export class ValidationError extends Error {
11
+ /** Every issue recorded during this validation run, in the order they occurred. */
3
12
  issues = [];
13
+ /**
14
+ * @param issues - The recorded issues. The error's own `message` (and
15
+ * optional `\n at <location>` suffix) is derived from `issues[0]`.
16
+ */
4
17
  constructor(issues) {
5
18
  super(issues[0].message +
6
19
  (issues[0].location ? `\n at ${issues[0].location}` : ''));
@@ -2,17 +2,117 @@ import type { Nullish } from 'ts-gems';
2
2
  import { kValidatorFn } from './constants.js';
3
3
  import { Context } from './context.js';
4
4
  import type { ErrorIssue, ExecutionOptions, ValidationOptions } from './types.js';
5
+ /**
6
+ * The validation logic a {@link Validator} is built from - the function
7
+ * passed to {@link validator}.
8
+ *
9
+ * @typeParam T - The output type on success.
10
+ * @typeParam I - The accepted input type.
11
+ * @typeParam R - The `Validator` this function is built into.
12
+ * @param input - The value to validate.
13
+ * @param context - The current {@link Context}: carries execution options
14
+ * (`coerce`, `label`, ...) and is used to report failures via `context.fail(...)`.
15
+ * @param _this - The `Validator` this function was built into - pass it to
16
+ * `context.fail()` so a reported issue is attributed to the right rule.
17
+ * @returns The validated (and possibly coerced) value. To reject the input,
18
+ * call `context.fail(_this, message, input)` instead of throwing directly.
19
+ */
5
20
  export type ValidateFunction<T, I = T, R extends Validator<T, I> = Validator<T, I>> = (input: I, context: Context, _this: R) => Nullish<T>;
21
+ /**
22
+ * A callable validation rule, as produced by {@link validator}. Every rule in
23
+ * this library - and any custom rule built with `validator()` - has this shape.
24
+ *
25
+ * @typeParam T - The validated output type.
26
+ * @typeParam I - The accepted input type.
27
+ * @typeParam O - The shape of the per-call options this validator accepts.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { isEmail } from 'valgen';
32
+ *
33
+ * isEmail('a@b.com'); // => 'a@b.com'
34
+ * isEmail('invalid'); // throws ValidationError
35
+ * isEmail.silent('invalid'); // => { errors: [...] }
36
+ * ```
37
+ */
6
38
  export interface Validator<T = any, I = any, O extends ExecutionOptions = ExecutionOptions> {
39
+ /**
40
+ * Validates `input`, returning the (possibly coerced) value on success.
41
+ *
42
+ * @param input - The value to validate.
43
+ * @param options - Per-call execution options (`coerce`, `label`, ...).
44
+ * @param context - An existing {@link Context} to nest this call under
45
+ * another validator's run. When calling a validator *from inside another
46
+ * rule*, pass the shared context here (3rd argument) and `undefined` for
47
+ * `options`, so the wrapper can skip an unneeded `context.extend()`.
48
+ * @returns The validated (and possibly coerced) value.
49
+ * @throws {@link ValidationError} if validation fails at the root call
50
+ * (i.e. `context` was not passed in, meaning this is the outermost call).
51
+ */
7
52
  (input: I, options?: O, context?: Context): T;
53
+ /**
54
+ * Like calling the validator directly, but never throws.
55
+ *
56
+ * @returns `{ value }` on success, or `{ errors }` (the recorded {@link ErrorIssue}s) on failure.
57
+ */
8
58
  silent(input: I, options?: O, context?: Context): {
9
59
  value?: T;
10
60
  errors?: ErrorIssue[];
11
61
  };
62
+ /** This rule's identifier (e.g. `"isEmail"`), used to attribute reported issues to it. */
12
63
  id: string;
13
64
  args?: Record<string, any>;
14
65
  [kValidatorFn]: ValidateFunction<T, I>;
15
66
  }
67
+ /**
68
+ * Builds a {@link Validator} from a validation function.
69
+ *
70
+ * This is the single factory every rule in this library - and any custom
71
+ * rule you write - is built with. See {@link ValidateFunction} for the shape
72
+ * expected of `fn`.
73
+ *
74
+ * @param fn - The validation logic: receives `(input, context, self)` and
75
+ * should return the validated value, or call `context.fail(self, message, input)`
76
+ * to record a failure. The rule's id defaults to `fn.name`.
77
+ * @param validatorOptions - Default options for this rule (e.g. `coerce`,
78
+ * `onFail`), applied to every call unless overridden per-call.
79
+ * @returns A callable {@link Validator}.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * import { validator, type Context, type Nullish, type ValidationOptions } from 'valgen';
84
+ *
85
+ * function isEven(options?: isEven.Options) {
86
+ * return validator<number, unknown>(
87
+ * isEven.name,
88
+ * (input, context, self): Nullish<number> => {
89
+ * const n = Number(input);
90
+ * if (Number.isInteger(n) && n % 2 === 0) return n;
91
+ * context.fail(self, 'Value must be an even number', input);
92
+ * },
93
+ * options,
94
+ * );
95
+ * }
96
+ * namespace isEven {
97
+ * export interface Options extends ValidationOptions {}
98
+ * }
99
+ * ```
100
+ */
16
101
  export declare function validator<T, I = T, O extends ExecutionOptions = ExecutionOptions>(fn: ValidateFunction<T, I>, validatorOptions?: ValidationOptions): Validator<T, I, O>;
102
+ /**
103
+ * Builds a {@link Validator} from a validation function, with an explicit id.
104
+ *
105
+ * @param id - The rule's identifier, used for `Validator.id` and (unless `fn`
106
+ * is itself named) as the built function's name.
107
+ * @param fn - The validation logic - see the other overload for details.
108
+ * @param validatorOptions - Default options for this rule.
109
+ * @returns A callable {@link Validator}.
110
+ */
17
111
  export declare function validator<T, I = T, O extends ExecutionOptions = ExecutionOptions>(id: string, fn: ValidateFunction<T, I>, validatorOptions?: ValidationOptions): Validator<T, I, O>;
112
+ /**
113
+ * Checks whether `x` is a {@link Validator} built with {@link validator}.
114
+ *
115
+ * @param x - The value to check.
116
+ * @returns `true` if `x` is a validator, narrowing its type accordingly.
117
+ */
18
118
  export declare function isValidator(x: any): x is Validator;
package/core/validator.js CHANGED
@@ -62,6 +62,12 @@ export function validator(arg0, arg1, arg2) {
62
62
  };
63
63
  return _rule;
64
64
  }
65
+ /**
66
+ * Checks whether `x` is a {@link Validator} built with {@link validator}.
67
+ *
68
+ * @param x - The value to check.
69
+ * @returns `true` if `x` is a validator, narrowing its type accordingly.
70
+ */
65
71
  export function isValidator(x) {
66
72
  return !!(typeof x === 'function' && x.id && x[kValidatorFn]);
67
73
  }
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": "7.0.1",
4
+ "version": "7.0.3",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
@@ -1,7 +1,16 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
- * Check if the string contains only letters (a-zA-Z).
3
+ * Validates that a string contains only letters (`a-zA-Z`). Delegates to
4
+ * `validatorJS.isAlpha`.
4
5
  * @validator isAlpha
6
+ * @param options - Validation options.
7
+ * @returns The input string, unchanged, if valid.
8
+ * @throws if `input` is not a string of letters only: `Value must be an alpha string`
9
+ * @example
10
+ * ```ts
11
+ * isAlpha('abcDEF'); // => 'abcDEF'
12
+ * isAlpha('abc123'); // throws ValidationError: "Value must be an alpha string"
13
+ * ```
5
14
  */
6
15
  export declare function isAlpha(options?: isAlpha.Options): import("../../core/validator.js").Validator<string, string, import("../../core/types.js").ExecutionOptions>;
7
16
  export declare namespace isAlpha {
@@ -1,8 +1,17 @@
1
1
  import validatorJS from '@browsery/validator';
2
2
  import { validator, } from '../../core/index.js';
3
3
  /**
4
- * Check if the string contains only letters (a-zA-Z).
4
+ * Validates that a string contains only letters (`a-zA-Z`). Delegates to
5
+ * `validatorJS.isAlpha`.
5
6
  * @validator isAlpha
7
+ * @param options - Validation options.
8
+ * @returns The input string, unchanged, if valid.
9
+ * @throws if `input` is not a string of letters only: `Value must be an alpha string`
10
+ * @example
11
+ * ```ts
12
+ * isAlpha('abcDEF'); // => 'abcDEF'
13
+ * isAlpha('abc123'); // throws ValidationError: "Value must be an alpha string"
14
+ * ```
6
15
  */
7
16
  export function isAlpha(options) {
8
17
  return validator(isAlpha.name, (input, context, _this) => {
@@ -1,7 +1,16 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
- * Check if the string contains only letters and numbers.
3
+ * Validates that a string contains only letters and numbers. Delegates to
4
+ * `validatorJS.isAlphanumeric`.
4
5
  * @validator isAlphanumeric
6
+ * @param options - Validation options.
7
+ * @returns The input string, unchanged, if valid.
8
+ * @throws if `input` is not a string of letters/numbers only: `Value must be an alphanumeric string`
9
+ * @example
10
+ * ```ts
11
+ * isAlphanumeric('abc123'); // => 'abc123'
12
+ * isAlphanumeric('abc-123'); // throws ValidationError: "Value must be an alphanumeric string"
13
+ * ```
5
14
  */
6
15
  export declare function isAlphanumeric(options?: isAlphanumeric.Options): import("../../core/validator.js").Validator<string, string, import("../../core/types.js").ExecutionOptions>;
7
16
  export declare namespace isAlphanumeric {
@@ -1,8 +1,17 @@
1
1
  import validatorJS from '@browsery/validator';
2
2
  import { validator, } from '../../core/index.js';
3
3
  /**
4
- * Check if the string contains only letters and numbers.
4
+ * Validates that a string contains only letters and numbers. Delegates to
5
+ * `validatorJS.isAlphanumeric`.
5
6
  * @validator isAlphanumeric
7
+ * @param options - Validation options.
8
+ * @returns The input string, unchanged, if valid.
9
+ * @throws if `input` is not a string of letters/numbers only: `Value must be an alphanumeric string`
10
+ * @example
11
+ * ```ts
12
+ * isAlphanumeric('abc123'); // => 'abc123'
13
+ * isAlphanumeric('abc-123'); // throws ValidationError: "Value must be an alphanumeric string"
14
+ * ```
6
15
  */
7
16
  export function isAlphanumeric(options) {
8
17
  return validator(isAlphanumeric.name, (input, context, _this) => {
@@ -1,7 +1,16 @@
1
1
  import { type ValidationOptions } from '../../core/index.js';
2
2
  /**
3
- * Check if the string contains ASCII chars only.
3
+ * Validates that a string contains only ASCII characters. Delegates to
4
+ * `validatorJS.isAscii`.
4
5
  * @validator isAscii
6
+ * @param options - Validation options.
7
+ * @returns The input string, unchanged, if valid.
8
+ * @throws if `input` contains non-ASCII characters: `Value must be an ascii string`
9
+ * @example
10
+ * ```ts
11
+ * isAscii('abc123!@#'); // => 'abc123!@#'
12
+ * isAscii('şiir'); // throws ValidationError: "Value must be an ascii string"
13
+ * ```
5
14
  */
6
15
  export declare function isAscii(options?: isAscii.Options): import("../../core/validator.js").Validator<string, string, import("../../core/types.js").ExecutionOptions>;
7
16
  export declare namespace isAscii {
@@ -1,8 +1,17 @@
1
1
  import validatorJS from '@browsery/validator';
2
2
  import { validator, } from '../../core/index.js';
3
3
  /**
4
- * Check if the string contains ASCII chars only.
4
+ * Validates that a string contains only ASCII characters. Delegates to
5
+ * `validatorJS.isAscii`.
5
6
  * @validator isAscii
7
+ * @param options - Validation options.
8
+ * @returns The input string, unchanged, if valid.
9
+ * @throws if `input` contains non-ASCII characters: `Value must be an ascii string`
10
+ * @example
11
+ * ```ts
12
+ * isAscii('abc123!@#'); // => 'abc123!@#'
13
+ * isAscii('şiir'); // throws ValidationError: "Value must be an ascii string"
14
+ * ```
6
15
  */
7
16
  export function isAscii(options) {
8
17
  return validator(isAscii.name, (input, context, _this) => {