@zudojs/validation 0.0.1 → 1.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.
- package/README.md +31 -0
- package/dist/validationComposer/index.d.ts +3 -1
- package/dist/validationComposer/index.js +3 -1
- package/dist/validationComposer/validationComposer.combinators.d.ts +21 -0
- package/dist/validationComposer/validationComposer.combinators.js +84 -0
- package/dist/validationComposer/validationComposer.core.d.ts +10 -24
- package/dist/validationComposer/validationComposer.core.js +2 -139
- package/dist/validationComposer/validationComposer.wrappers.d.ts +38 -0
- package/dist/validationComposer/validationComposer.wrappers.js +96 -0
- package/dist/validationConstraints/collection/index.d.ts +8 -0
- package/dist/validationConstraints/collection/index.js +8 -0
- package/dist/validationConstraints/{validationConstraints.array.d.ts → collection/validationConstraints.array.d.ts} +1 -1
- package/dist/validationConstraints/{validationConstraints.array.js → collection/validationConstraints.array.js} +23 -3
- package/dist/validationConstraints/{validationConstraints.collection.d.ts → collection/validationConstraints.collection.d.ts} +1 -1
- package/dist/validationConstraints/{validationConstraints.collection.js → collection/validationConstraints.collection.js} +1 -1
- package/dist/validationConstraints/index.d.ts +3 -8
- package/dist/validationConstraints/index.js +6 -16
- package/dist/validationConstraints/scalar/index.d.ts +9 -0
- package/dist/validationConstraints/scalar/index.js +9 -0
- package/dist/validationConstraints/scalar/validationConstraints.date.d.ts +13 -0
- package/dist/validationConstraints/scalar/validationConstraints.date.js +45 -0
- package/dist/validationConstraints/{validationConstraints.number.d.ts → scalar/validationConstraints.number.d.ts} +1 -1
- package/dist/validationConstraints/{validationConstraints.number.js → scalar/validationConstraints.number.js} +12 -1
- package/dist/validationConstraints/{validationConstraints.string.d.ts → scalar/validationConstraints.string.d.ts} +18 -3
- package/dist/validationConstraints/{validationConstraints.string.js → scalar/validationConstraints.string.js} +49 -13
- package/dist/validationConstraints/structure/index.d.ts +10 -0
- package/dist/validationConstraints/structure/index.js +10 -0
- package/dist/validationConstraints/{validationConstraints.circular.d.ts → structure/validationConstraints.circular.d.ts} +8 -4
- package/dist/validationConstraints/structure/validationConstraints.circular.js +55 -0
- package/dist/validationConstraints/structure/validationConstraints.depth.d.ts +37 -0
- package/dist/validationConstraints/structure/validationConstraints.depth.js +58 -0
- package/dist/validationConstraints/structure/validationConstraints.size.d.ts +31 -0
- package/dist/validationConstraints/structure/validationConstraints.size.js +101 -0
- package/dist/validationConstraints/structure/validationConstraints.traverse.d.ts +57 -0
- package/dist/validationConstraints/structure/validationConstraints.traverse.js +111 -0
- package/dist/validationConstraints/validationConstraints.base.d.ts +18 -3
- package/dist/validationConstraints/validationConstraints.base.js +50 -25
- package/dist/validationErrors/validationError.base.js +28 -1
- package/dist/validationFactory/validationFactory.core.d.ts +9 -8
- package/dist/validationFactory/validationFactory.core.js +11 -9
- package/dist/validationNormalizer/validationNormalizer.core.js +2 -4
- package/dist/validationNormalizer/validationNormalizer.helpers.d.ts +36 -3
- package/dist/validationNormalizer/validationNormalizer.helpers.js +48 -7
- package/dist/validationParser/index.d.ts +2 -1
- package/dist/validationParser/index.js +2 -1
- package/dist/validationParser/validationParser.collection.d.ts +28 -0
- package/dist/validationParser/validationParser.collection.js +98 -0
- package/dist/validationParser/validationParser.core.d.ts +0 -12
- package/dist/validationParser/validationParser.core.js +0 -71
- package/dist/validationRegistry/validationRegistry.core.d.ts +8 -0
- package/dist/validationRegistry/validationRegistry.core.js +15 -1
- package/dist/validationResult/validationResult.type.d.ts +8 -1
- package/dist/validationResult/validationResult.type.js +20 -5
- package/dist/validationTransformer/index.d.ts +2 -1
- package/dist/validationTransformer/index.js +2 -1
- package/dist/validationTransformer/validationTransformer.core.d.ts +0 -15
- package/dist/validationTransformer/validationTransformer.core.js +5 -56
- package/dist/validationTransformer/validationTransformer.helpers.d.ts +23 -0
- package/dist/validationTransformer/validationTransformer.helpers.js +67 -0
- package/package.json +13 -6
- package/dist/.tsbuildinfo +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/validationComposer/index.d.ts.map +0 -1
- package/dist/validationComposer/index.js.map +0 -1
- package/dist/validationComposer/validationComposer.core.d.ts.map +0 -1
- package/dist/validationComposer/validationComposer.core.js.map +0 -1
- package/dist/validationConstraints/index.d.ts.map +0 -1
- package/dist/validationConstraints/index.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.array.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.array.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.base.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.base.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.circular.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.circular.js +0 -73
- package/dist/validationConstraints/validationConstraints.circular.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.collection.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.collection.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.date.d.ts +0 -14
- package/dist/validationConstraints/validationConstraints.date.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.date.js +0 -30
- package/dist/validationConstraints/validationConstraints.date.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.depth.d.ts +0 -20
- package/dist/validationConstraints/validationConstraints.depth.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.depth.js +0 -70
- package/dist/validationConstraints/validationConstraints.depth.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.number.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.number.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.size.d.ts +0 -20
- package/dist/validationConstraints/validationConstraints.size.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.size.js +0 -82
- package/dist/validationConstraints/validationConstraints.size.js.map +0 -1
- package/dist/validationConstraints/validationConstraints.string.d.ts.map +0 -1
- package/dist/validationConstraints/validationConstraints.string.js.map +0 -1
- package/dist/validationErrors/index.d.ts.map +0 -1
- package/dist/validationErrors/index.js.map +0 -1
- package/dist/validationErrors/validationError.base.d.ts.map +0 -1
- package/dist/validationErrors/validationError.base.js.map +0 -1
- package/dist/validationErrors/validationError.types.d.ts.map +0 -1
- package/dist/validationErrors/validationError.types.js.map +0 -1
- package/dist/validationFactory/index.d.ts.map +0 -1
- package/dist/validationFactory/index.js.map +0 -1
- package/dist/validationFactory/validationFactory.core.d.ts.map +0 -1
- package/dist/validationFactory/validationFactory.core.js.map +0 -1
- package/dist/validationNormalizer/index.d.ts.map +0 -1
- package/dist/validationNormalizer/index.js.map +0 -1
- package/dist/validationNormalizer/validationNormalizer.core.d.ts.map +0 -1
- package/dist/validationNormalizer/validationNormalizer.core.js.map +0 -1
- package/dist/validationNormalizer/validationNormalizer.helpers.d.ts.map +0 -1
- package/dist/validationNormalizer/validationNormalizer.helpers.js.map +0 -1
- package/dist/validationParser/index.d.ts.map +0 -1
- package/dist/validationParser/index.js.map +0 -1
- package/dist/validationParser/validationParser.core.d.ts.map +0 -1
- package/dist/validationParser/validationParser.core.js.map +0 -1
- package/dist/validationRegistry/index.d.ts.map +0 -1
- package/dist/validationRegistry/index.js.map +0 -1
- package/dist/validationRegistry/validationRegistry.core.d.ts.map +0 -1
- package/dist/validationRegistry/validationRegistry.core.js.map +0 -1
- package/dist/validationRegistry/validationRegistry.readonly.d.ts.map +0 -1
- package/dist/validationRegistry/validationRegistry.readonly.js.map +0 -1
- package/dist/validationResult/index.d.ts.map +0 -1
- package/dist/validationResult/index.js.map +0 -1
- package/dist/validationResult/validationResult.type.d.ts.map +0 -1
- package/dist/validationResult/validationResult.type.js.map +0 -1
- package/dist/validationSchema/index.d.ts.map +0 -1
- package/dist/validationSchema/index.js.map +0 -1
- package/dist/validationSchema/validationSchema.core.d.ts.map +0 -1
- package/dist/validationSchema/validationSchema.core.js.map +0 -1
- package/dist/validationTransformer/index.d.ts.map +0 -1
- package/dist/validationTransformer/index.js.map +0 -1
- package/dist/validationTransformer/validationTransformer.core.d.ts.map +0 -1
- package/dist/validationTransformer/validationTransformer.core.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,6 +24,37 @@ if (!result.success) {
|
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
Structural guards for untrusted payloads:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import {
|
|
31
|
+
assertDepthWithinLimit,
|
|
32
|
+
assertSizeWithinLimit,
|
|
33
|
+
assertNoCircularReference,
|
|
34
|
+
} from "@zudojs/validation";
|
|
35
|
+
|
|
36
|
+
// Each aborts as soon as its bound is passed, so the cost is bounded by the
|
|
37
|
+
// limit rather than by the size of the input.
|
|
38
|
+
assertDepthWithinLimit(body, 32);
|
|
39
|
+
assertSizeWithinLimit(body, 1_000_000);
|
|
40
|
+
assertNoCircularReference(body);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Safety Notes
|
|
44
|
+
|
|
45
|
+
- Constraint failures do **not** carry the rejected value. `ValidationError` is
|
|
46
|
+
exposed with a 400, so echoing it would return rejected passwords and tokens
|
|
47
|
+
to the caller and write them to any log that serializes the error.
|
|
48
|
+
- `parseRecord` and `toFieldErrors` build on null-prototype objects, so a
|
|
49
|
+
`__proto__` key cannot hijack a validated result.
|
|
50
|
+
- `matches()` strips `g` and `y` from the pattern, which otherwise make
|
|
51
|
+
`test()` stateful and flip the answer on alternate calls.
|
|
52
|
+
- Constraints carry an optional type guard, so wrong-typed input at a trust
|
|
53
|
+
boundary reports as a validation failure rather than a `TypeError`.
|
|
54
|
+
- The depth and size guards count a shared subtree once per occurrence, the way
|
|
55
|
+
a serializer expands it, and walk iteratively so deeply nested input cannot
|
|
56
|
+
exhaust the stack inside the check.
|
|
57
|
+
|
|
27
58
|
## Features
|
|
28
59
|
|
|
29
60
|
- Zod schema integration
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @zudojs/validation/validationComposer
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Composition of validation steps into reusable pipelines.
|
|
5
5
|
*/
|
|
6
6
|
export * from "./validationComposer.core.js";
|
|
7
|
+
export * from "./validationComposer.combinators.js";
|
|
8
|
+
export * from "./validationComposer.wrappers.js";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @zudojs/validation/validationComposer
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Composition of validation steps into reusable pipelines.
|
|
5
5
|
*/
|
|
6
6
|
export * from "./validationComposer.core.js";
|
|
7
|
+
export * from "./validationComposer.combinators.js";
|
|
8
|
+
export * from "./validationComposer.wrappers.js";
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Validation step combinators.
|
|
3
|
+
*
|
|
4
|
+
* @module validationComposer/validationComposer.combinators
|
|
5
|
+
*/
|
|
6
|
+
import type { ValidationStep } from "./validationComposer.core.js";
|
|
7
|
+
/** Combines validators using logical AND semantics. Every validator must succeed. */
|
|
8
|
+
export declare function all<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
9
|
+
/** Combines validators using logical OR semantics. Succeeds when at least one validator succeeds. */
|
|
10
|
+
export declare function any<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Runs validators sequentially and returns the first successful result.
|
|
13
|
+
*
|
|
14
|
+
* Unlike {@link any}, only the *last* failure is reported when none succeed:
|
|
15
|
+
* a caller asking for the first match wants to know why the final fallback
|
|
16
|
+
* did not apply, not to read every alternative's complaint.
|
|
17
|
+
*/
|
|
18
|
+
export declare function first<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
19
|
+
/** Negates a validation step. */
|
|
20
|
+
export declare function negate<T>(validator: ValidationStep<T>): ValidationStep<T>;
|
|
21
|
+
//# sourceMappingURL=validationComposer.combinators.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Validation step combinators.
|
|
3
|
+
*
|
|
4
|
+
* @module validationComposer/validationComposer.combinators
|
|
5
|
+
*/
|
|
6
|
+
import { failure, success } from "../validationResult/validationResult.type.js";
|
|
7
|
+
/** The issue reported when no alternative accepted the value. */
|
|
8
|
+
function noValidatorSucceeded() {
|
|
9
|
+
return {
|
|
10
|
+
path: [],
|
|
11
|
+
code: "no_validator_succeeded",
|
|
12
|
+
message: "No validation rule accepted the value.",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Combines validators using logical AND semantics. Every validator must succeed. */
|
|
16
|
+
export function all(...validators) {
|
|
17
|
+
return (value) => {
|
|
18
|
+
const issues = [];
|
|
19
|
+
for (const validator of validators) {
|
|
20
|
+
const result = validator(value);
|
|
21
|
+
if (!result.success)
|
|
22
|
+
issues.push(...result.issues);
|
|
23
|
+
}
|
|
24
|
+
return issues.length > 0 ? failure(issues) : success(value);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Combines validators using logical OR semantics. Succeeds when at least one validator succeeds. */
|
|
28
|
+
export function any(...validators) {
|
|
29
|
+
return (value) => {
|
|
30
|
+
const issues = [];
|
|
31
|
+
for (const validator of validators) {
|
|
32
|
+
const result = validator(value);
|
|
33
|
+
if (result.success)
|
|
34
|
+
return result;
|
|
35
|
+
issues.push(...result.issues);
|
|
36
|
+
}
|
|
37
|
+
return failure(issues.length > 0
|
|
38
|
+
? issues
|
|
39
|
+
: [
|
|
40
|
+
{
|
|
41
|
+
path: [],
|
|
42
|
+
code: "no_validator_succeeded",
|
|
43
|
+
message: "No validation rule accepted the value.",
|
|
44
|
+
received: value,
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Runs validators sequentially and returns the first successful result.
|
|
51
|
+
*
|
|
52
|
+
* Unlike {@link any}, only the *last* failure is reported when none succeed:
|
|
53
|
+
* a caller asking for the first match wants to know why the final fallback
|
|
54
|
+
* did not apply, not to read every alternative's complaint.
|
|
55
|
+
*/
|
|
56
|
+
export function first(...validators) {
|
|
57
|
+
return (value) => {
|
|
58
|
+
let lastIssues = [];
|
|
59
|
+
for (const validator of validators) {
|
|
60
|
+
const result = validator(value);
|
|
61
|
+
if (result.success)
|
|
62
|
+
return result;
|
|
63
|
+
lastIssues = result.issues;
|
|
64
|
+
}
|
|
65
|
+
return failure(lastIssues.length > 0 ? lastIssues : [noValidatorSucceeded()]);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Negates a validation step. */
|
|
69
|
+
export function negate(validator) {
|
|
70
|
+
return (value) => {
|
|
71
|
+
const result = validator(value);
|
|
72
|
+
if (result.success) {
|
|
73
|
+
return failure([
|
|
74
|
+
{
|
|
75
|
+
path: [],
|
|
76
|
+
code: "negated_validation_failed",
|
|
77
|
+
message: "Value must not satisfy the supplied validation rule.",
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
return success(value);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=validationComposer.combinators.js.map
|
|
@@ -6,11 +6,21 @@ export type ValidationStep<T> = (value: T) => ValidationResult<T>;
|
|
|
6
6
|
/** Options for composing validation operations. */
|
|
7
7
|
export interface ValidationComposerOptions {
|
|
8
8
|
readonly name?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Stop after the first failing step. Defaults to true.
|
|
11
|
+
*
|
|
12
|
+
* Steps in a pipeline normally build on each other: a later step validating
|
|
13
|
+
* the shape an earlier coercion was supposed to produce will see the raw
|
|
14
|
+
* value instead if the pipeline runs on. Set to false only for pipelines of
|
|
15
|
+
* independent checks, where collecting every issue is the point.
|
|
16
|
+
*/
|
|
9
17
|
readonly stopOnFirstError?: boolean;
|
|
10
18
|
}
|
|
11
19
|
/** Reusable composed validator. */
|
|
12
20
|
export interface ValidationComposer<T> {
|
|
13
21
|
readonly name: string;
|
|
22
|
+
/** Whether the pipeline halts at the first failing step. */
|
|
23
|
+
readonly stopOnFirstError: boolean;
|
|
14
24
|
validate(value: T): ValidationResult<T>;
|
|
15
25
|
assert(value: T): T;
|
|
16
26
|
}
|
|
@@ -24,28 +34,4 @@ export declare function schemaStep<T>(schema: ValidationSchema<T>): ValidationSt
|
|
|
24
34
|
export declare function constraintStep<T>(constraint: ValidationConstraint<T>): ValidationStep<T>;
|
|
25
35
|
/** Creates a validation step from multiple constraints. */
|
|
26
36
|
export declare function constraintsStep<T>(constraints: readonly ValidationConstraint<T>[]): ValidationStep<T>;
|
|
27
|
-
/** Combines validators using logical AND semantics. Every validator must succeed. */
|
|
28
|
-
export declare function all<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
29
|
-
/** Combines validators using logical OR semantics. Succeeds when at least one validator succeeds. */
|
|
30
|
-
export declare function any<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
31
|
-
/** Runs validators sequentially and returns the first successful result. */
|
|
32
|
-
export declare function first<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
33
|
-
/** Negates a validation step. */
|
|
34
|
-
export declare function negate<T>(validator: ValidationStep<T>): ValidationStep<T>;
|
|
35
|
-
/** Makes a validation step optional. Undefined values bypass the validator. */
|
|
36
|
-
export declare function optional<T>(validator: ValidationStep<T>): ValidationStep<T | undefined>;
|
|
37
|
-
/** Makes a validation step nullable. Null values bypass the validator. */
|
|
38
|
-
export declare function nullable<T>(validator: ValidationStep<T>): ValidationStep<T | null>;
|
|
39
|
-
/** Makes a validation step optional and nullable. */
|
|
40
|
-
export declare function optionalNullable<T>(validator: ValidationStep<T>): ValidationStep<T | null | undefined>;
|
|
41
|
-
/** Adds a custom validation step to an existing pipeline. */
|
|
42
|
-
export declare function append<T>(composer: ValidationComposer<T>, step: ValidationStep<T>, options?: ValidationComposerOptions): ValidationComposer<T>;
|
|
43
|
-
/** Creates a pipeline that validates a value and returns the original value. */
|
|
44
|
-
export declare function tap<T>(validator: ValidationStep<T>): ValidationStep<T>;
|
|
45
|
-
/** Creates a validation step that only runs when a predicate matches. */
|
|
46
|
-
export declare function when<T>(predicate: (value: T) => boolean, validator: ValidationStep<T>): ValidationStep<T>;
|
|
47
|
-
/** Creates a validation step that runs only when a predicate does not match. */
|
|
48
|
-
export declare function unless<T>(predicate: (value: T) => boolean, validator: ValidationStep<T>): ValidationStep<T>;
|
|
49
|
-
/** Applies a mapping operation after successful validation. */
|
|
50
|
-
export declare function mapValidated<T, U>(validator: ValidationStep<T>, mapper: (value: T) => U): ValidationStep<T>;
|
|
51
37
|
//# sourceMappingURL=validationComposer.core.d.ts.map
|
|
@@ -5,9 +5,10 @@ import { ConstraintValidationError } from "../validationErrors/validationError.t
|
|
|
5
5
|
/** Creates a reusable validator from validation steps. */
|
|
6
6
|
export function createValidationComposer(steps, options = {}) {
|
|
7
7
|
const name = options.name ?? "ValidationComposer";
|
|
8
|
-
const stopOnFirstError = options.stopOnFirstError ??
|
|
8
|
+
const stopOnFirstError = options.stopOnFirstError ?? true;
|
|
9
9
|
return Object.freeze({
|
|
10
10
|
name,
|
|
11
|
+
stopOnFirstError,
|
|
11
12
|
validate(value) {
|
|
12
13
|
let current = value;
|
|
13
14
|
const issues = [];
|
|
@@ -50,142 +51,4 @@ export function constraintStep(constraint) {
|
|
|
50
51
|
export function constraintsStep(constraints) {
|
|
51
52
|
return (value) => checkConstraints(constraints, value);
|
|
52
53
|
}
|
|
53
|
-
/** Combines validators using logical AND semantics. Every validator must succeed. */
|
|
54
|
-
export function all(...validators) {
|
|
55
|
-
return (value) => {
|
|
56
|
-
const issues = [];
|
|
57
|
-
for (const validator of validators) {
|
|
58
|
-
const result = validator(value);
|
|
59
|
-
if (!result.success)
|
|
60
|
-
issues.push(...result.issues);
|
|
61
|
-
}
|
|
62
|
-
return issues.length > 0 ? failure(issues) : success(value);
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/** Combines validators using logical OR semantics. Succeeds when at least one validator succeeds. */
|
|
66
|
-
export function any(...validators) {
|
|
67
|
-
return (value) => {
|
|
68
|
-
const issues = [];
|
|
69
|
-
for (const validator of validators) {
|
|
70
|
-
const result = validator(value);
|
|
71
|
-
if (result.success)
|
|
72
|
-
return result;
|
|
73
|
-
issues.push(...result.issues);
|
|
74
|
-
}
|
|
75
|
-
return failure(issues.length > 0
|
|
76
|
-
? issues
|
|
77
|
-
: [
|
|
78
|
-
{
|
|
79
|
-
path: [],
|
|
80
|
-
code: "no_validator_succeeded",
|
|
81
|
-
message: "No validation rule accepted the value.",
|
|
82
|
-
received: value,
|
|
83
|
-
},
|
|
84
|
-
]);
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
/** Runs validators sequentially and returns the first successful result. */
|
|
88
|
-
export function first(...validators) {
|
|
89
|
-
return (value) => {
|
|
90
|
-
let issues = [];
|
|
91
|
-
for (const validator of validators) {
|
|
92
|
-
const result = validator(value);
|
|
93
|
-
if (result.success)
|
|
94
|
-
return result;
|
|
95
|
-
issues = [...issues, ...result.issues];
|
|
96
|
-
}
|
|
97
|
-
return failure(issues.length > 0
|
|
98
|
-
? issues
|
|
99
|
-
: [
|
|
100
|
-
{
|
|
101
|
-
path: [],
|
|
102
|
-
code: "no_validator_succeeded",
|
|
103
|
-
message: "No validation rule accepted the value.",
|
|
104
|
-
received: value,
|
|
105
|
-
},
|
|
106
|
-
]);
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/** Negates a validation step. */
|
|
110
|
-
export function negate(validator) {
|
|
111
|
-
return (value) => {
|
|
112
|
-
const result = validator(value);
|
|
113
|
-
if (result.success)
|
|
114
|
-
return failure([
|
|
115
|
-
{
|
|
116
|
-
path: [],
|
|
117
|
-
code: "negated_validation_failed",
|
|
118
|
-
message: "Value must not satisfy the supplied validation rule.",
|
|
119
|
-
received: value,
|
|
120
|
-
},
|
|
121
|
-
]);
|
|
122
|
-
return success(value);
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
/** Makes a validation step optional. Undefined values bypass the validator. */
|
|
126
|
-
export function optional(validator) {
|
|
127
|
-
return (value) => {
|
|
128
|
-
if (value === undefined)
|
|
129
|
-
return success(undefined);
|
|
130
|
-
return validator(value);
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
/** Makes a validation step nullable. Null values bypass the validator. */
|
|
134
|
-
export function nullable(validator) {
|
|
135
|
-
return (value) => {
|
|
136
|
-
if (value === null)
|
|
137
|
-
return success(null);
|
|
138
|
-
return validator(value);
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
/** Makes a validation step optional and nullable. */
|
|
142
|
-
export function optionalNullable(validator) {
|
|
143
|
-
return (value) => {
|
|
144
|
-
if (value === null || value === undefined)
|
|
145
|
-
return success(value);
|
|
146
|
-
return validator(value);
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
/** Adds a custom validation step to an existing pipeline. */
|
|
150
|
-
export function append(composer, step, options = {}) {
|
|
151
|
-
return createValidationComposer([composer.validate, step], {
|
|
152
|
-
...options,
|
|
153
|
-
name: options.name ?? composer.name,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
/** Creates a pipeline that validates a value and returns the original value. */
|
|
157
|
-
export function tap(validator) {
|
|
158
|
-
return (value) => {
|
|
159
|
-
const result = validator(value);
|
|
160
|
-
if (!result.success)
|
|
161
|
-
return result;
|
|
162
|
-
return success(value);
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
/** Creates a validation step that only runs when a predicate matches. */
|
|
166
|
-
export function when(predicate, validator) {
|
|
167
|
-
return (value) => {
|
|
168
|
-
if (!predicate(value))
|
|
169
|
-
return success(value);
|
|
170
|
-
return validator(value);
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
/** Creates a validation step that runs only when a predicate does not match. */
|
|
174
|
-
export function unless(predicate, validator) {
|
|
175
|
-
return (value) => {
|
|
176
|
-
if (predicate(value))
|
|
177
|
-
return success(value);
|
|
178
|
-
return validator(value);
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
/** Applies a mapping operation after successful validation. */
|
|
182
|
-
export function mapValidated(validator, mapper) {
|
|
183
|
-
return (value) => {
|
|
184
|
-
const result = validator(value);
|
|
185
|
-
if (!result.success)
|
|
186
|
-
return result;
|
|
187
|
-
mapper(result.data);
|
|
188
|
-
return success(result.data);
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
54
|
//# sourceMappingURL=validationComposer.core.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Wrappers that adapt a validation step.
|
|
3
|
+
*
|
|
4
|
+
* Optionality, conditionality and post-validation mapping, kept apart from the
|
|
5
|
+
* combinators that fold several steps into one.
|
|
6
|
+
*
|
|
7
|
+
* @module validationComposer/validationComposer.wrappers
|
|
8
|
+
*/
|
|
9
|
+
import type { ValidationResult } from "../validationResult/validationResult.type.js";
|
|
10
|
+
import type { ValidationComposer, ValidationComposerOptions, ValidationStep } from "./validationComposer.core.js";
|
|
11
|
+
/** Makes a validation step optional. Undefined values bypass the validator. */
|
|
12
|
+
export declare function optional<T>(validator: ValidationStep<T>): ValidationStep<T | undefined>;
|
|
13
|
+
/** Makes a validation step nullable. Null values bypass the validator. */
|
|
14
|
+
export declare function nullable<T>(validator: ValidationStep<T>): ValidationStep<T | null>;
|
|
15
|
+
/** Makes a validation step optional and nullable. */
|
|
16
|
+
export declare function optionalNullable<T>(validator: ValidationStep<T>): ValidationStep<T | null | undefined>;
|
|
17
|
+
/** Adds a custom validation step to an existing pipeline. */
|
|
18
|
+
export declare function append<T>(composer: ValidationComposer<T>, step: ValidationStep<T>, options?: ValidationComposerOptions): ValidationComposer<T>;
|
|
19
|
+
/** Creates a pipeline that validates a value and returns the original value. */
|
|
20
|
+
export declare function tap<T>(validator: ValidationStep<T>): ValidationStep<T>;
|
|
21
|
+
/** Creates a validation step that only runs when a predicate matches. */
|
|
22
|
+
export declare function when<T>(predicate: (value: T) => boolean, validator: ValidationStep<T>): ValidationStep<T>;
|
|
23
|
+
/** Creates a validation step that runs only when a predicate does not match. */
|
|
24
|
+
export declare function unless<T>(predicate: (value: T) => boolean, validator: ValidationStep<T>): ValidationStep<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Applies a mapping operation after successful validation.
|
|
27
|
+
*
|
|
28
|
+
* @returns A step producing the mapped value.
|
|
29
|
+
*/
|
|
30
|
+
export declare function mapValidated<T, U>(validator: ValidationStep<T>, mapper: (value: T) => U): (value: T) => ValidationResult<U>;
|
|
31
|
+
/**
|
|
32
|
+
* Applies a side effect after successful validation, keeping the value.
|
|
33
|
+
*
|
|
34
|
+
* The counterpart to {@link mapValidated} for callers that want to observe a
|
|
35
|
+
* validated value without changing it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function tapValidated<T>(validator: ValidationStep<T>, observe: (value: T) => void): ValidationStep<T>;
|
|
38
|
+
//# sourceMappingURL=validationComposer.wrappers.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Wrappers that adapt a validation step.
|
|
3
|
+
*
|
|
4
|
+
* Optionality, conditionality and post-validation mapping, kept apart from the
|
|
5
|
+
* combinators that fold several steps into one.
|
|
6
|
+
*
|
|
7
|
+
* @module validationComposer/validationComposer.wrappers
|
|
8
|
+
*/
|
|
9
|
+
import { success } from "../validationResult/validationResult.type.js";
|
|
10
|
+
import { createValidationComposer } from "./validationComposer.core.js";
|
|
11
|
+
/** Makes a validation step optional. Undefined values bypass the validator. */
|
|
12
|
+
export function optional(validator) {
|
|
13
|
+
return (value) => {
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return success(undefined);
|
|
16
|
+
return validator(value);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Makes a validation step nullable. Null values bypass the validator. */
|
|
20
|
+
export function nullable(validator) {
|
|
21
|
+
return (value) => {
|
|
22
|
+
if (value === null)
|
|
23
|
+
return success(null);
|
|
24
|
+
return validator(value);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Makes a validation step optional and nullable. */
|
|
28
|
+
export function optionalNullable(validator) {
|
|
29
|
+
return (value) => {
|
|
30
|
+
if (value === null || value === undefined)
|
|
31
|
+
return success(value);
|
|
32
|
+
return validator(value);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Adds a custom validation step to an existing pipeline. */
|
|
36
|
+
export function append(composer, step, options = {}) {
|
|
37
|
+
return createValidationComposer([(value) => composer.validate(value), step], {
|
|
38
|
+
...options,
|
|
39
|
+
name: options.name ?? composer.name,
|
|
40
|
+
stopOnFirstError: options.stopOnFirstError ?? composer.stopOnFirstError,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Creates a pipeline that validates a value and returns the original value. */
|
|
44
|
+
export function tap(validator) {
|
|
45
|
+
return (value) => {
|
|
46
|
+
const result = validator(value);
|
|
47
|
+
if (!result.success)
|
|
48
|
+
return result;
|
|
49
|
+
return success(value);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Creates a validation step that only runs when a predicate matches. */
|
|
53
|
+
export function when(predicate, validator) {
|
|
54
|
+
return (value) => {
|
|
55
|
+
if (!predicate(value))
|
|
56
|
+
return success(value);
|
|
57
|
+
return validator(value);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Creates a validation step that runs only when a predicate does not match. */
|
|
61
|
+
export function unless(predicate, validator) {
|
|
62
|
+
return (value) => {
|
|
63
|
+
if (predicate(value))
|
|
64
|
+
return success(value);
|
|
65
|
+
return validator(value);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Applies a mapping operation after successful validation.
|
|
70
|
+
*
|
|
71
|
+
* @returns A step producing the mapped value.
|
|
72
|
+
*/
|
|
73
|
+
export function mapValidated(validator, mapper) {
|
|
74
|
+
return (value) => {
|
|
75
|
+
const result = validator(value);
|
|
76
|
+
if (!result.success)
|
|
77
|
+
return result;
|
|
78
|
+
return success(mapper(result.data));
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Applies a side effect after successful validation, keeping the value.
|
|
83
|
+
*
|
|
84
|
+
* The counterpart to {@link mapValidated} for callers that want to observe a
|
|
85
|
+
* validated value without changing it.
|
|
86
|
+
*/
|
|
87
|
+
export function tapValidated(validator, observe) {
|
|
88
|
+
return (value) => {
|
|
89
|
+
const result = validator(value);
|
|
90
|
+
if (!result.success)
|
|
91
|
+
return result;
|
|
92
|
+
observe(result.data);
|
|
93
|
+
return success(result.data);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=validationComposer.wrappers.js.map
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import { createConstraint, assertNonNegativeInteger, } from "
|
|
1
|
+
import { createConstraint, assertNonNegativeInteger, } from "../validationConstraints.base.js";
|
|
2
|
+
/** Narrows an unknown value to a readonly array. */
|
|
3
|
+
function isArrayOf(value) {
|
|
4
|
+
return Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
/** Runs an item constraint without letting a type mismatch escape. */
|
|
7
|
+
function itemHolds(constraint, value) {
|
|
8
|
+
if (constraint.guard && !constraint.guard(value))
|
|
9
|
+
return false;
|
|
10
|
+
try {
|
|
11
|
+
return constraint.validate(value);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
2
17
|
/**
|
|
3
18
|
* Requires an array to contain at least a given number of items.
|
|
4
19
|
*/
|
|
@@ -8,6 +23,7 @@ export function minItems(minimum) {
|
|
|
8
23
|
name: `min_items_${minimum}`,
|
|
9
24
|
code: "min_items",
|
|
10
25
|
message: `Value must contain at least ${minimum} items.`,
|
|
26
|
+
guard: isArrayOf,
|
|
11
27
|
});
|
|
12
28
|
}
|
|
13
29
|
/**
|
|
@@ -19,6 +35,7 @@ export function maxItems(maximum) {
|
|
|
19
35
|
name: `max_items_${maximum}`,
|
|
20
36
|
code: "max_items",
|
|
21
37
|
message: `Value must contain at most ${maximum} items.`,
|
|
38
|
+
guard: isArrayOf,
|
|
22
39
|
});
|
|
23
40
|
}
|
|
24
41
|
/**
|
|
@@ -30,26 +47,29 @@ export function exactItems(length) {
|
|
|
30
47
|
name: `exact_items_${length}`,
|
|
31
48
|
code: "exact_items",
|
|
32
49
|
message: `Value must contain exactly ${length} items.`,
|
|
50
|
+
guard: isArrayOf,
|
|
33
51
|
});
|
|
34
52
|
}
|
|
35
53
|
/**
|
|
36
54
|
* Requires every array item to satisfy a constraint.
|
|
37
55
|
*/
|
|
38
56
|
export function everyItem(constraint) {
|
|
39
|
-
return createConstraint((values) => values.every((value) => constraint
|
|
57
|
+
return createConstraint((values) => values.every((value) => itemHolds(constraint, value)), {
|
|
40
58
|
name: `every_${constraint.name}`,
|
|
41
59
|
code: "item_constraint_failed",
|
|
42
60
|
message: constraint.message,
|
|
61
|
+
guard: isArrayOf,
|
|
43
62
|
});
|
|
44
63
|
}
|
|
45
64
|
/**
|
|
46
65
|
* Requires at least one array item to satisfy a constraint.
|
|
47
66
|
*/
|
|
48
67
|
export function someItem(constraint) {
|
|
49
|
-
return createConstraint((values) => values.some((value) => constraint
|
|
68
|
+
return createConstraint((values) => values.some((value) => itemHolds(constraint, value)), {
|
|
50
69
|
name: `some_${constraint.name}`,
|
|
51
70
|
code: "some_item_constraint_failed",
|
|
52
71
|
message: `At least one item must satisfy ${constraint.name}.`,
|
|
72
|
+
guard: isArrayOf,
|
|
53
73
|
});
|
|
54
74
|
}
|
|
55
75
|
//# sourceMappingURL=validationConstraints.array.js.map
|
|
@@ -5,12 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { createConstraint, checkConstraint, checkConstraints, combineConstraints, not, required, assertNonNegativeInteger, } from "./validationConstraints.base.js";
|
|
7
7
|
export type { ValidationConstraint, ConstraintOptions, } from "./validationConstraints.base.js";
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
11
|
-
export { isoDate, futureDate, pastDate } from "./validationConstraints.date.js";
|
|
12
|
-
export { minItems, maxItems, exactItems, everyItem, someItem, } from "./validationConstraints.array.js";
|
|
13
|
-
export { assertNoCircularReference, hasCircularReference, } from "./validationConstraints.circular.js";
|
|
14
|
-
export { getSerializationDepth, assertDepthWithinLimit, } from "./validationConstraints.depth.js";
|
|
15
|
-
export { estimateSerializedSize, assertSizeWithinLimit, } from "./validationConstraints.size.js";
|
|
8
|
+
export * from "./scalar/index.js";
|
|
9
|
+
export * from "./collection/index.js";
|
|
10
|
+
export * from "./structure/index.js";
|
|
16
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -5,20 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
// Base constraints
|
|
7
7
|
export { createConstraint, checkConstraint, checkConstraints, combineConstraints, not, required, assertNonNegativeInteger, } from "./validationConstraints.base.js";
|
|
8
|
-
//
|
|
9
|
-
export
|
|
10
|
-
//
|
|
11
|
-
export
|
|
12
|
-
//
|
|
13
|
-
export
|
|
14
|
-
// Date constraints
|
|
15
|
-
export { isoDate, futureDate, pastDate } from "./validationConstraints.date.js";
|
|
16
|
-
// Array constraints
|
|
17
|
-
export { minItems, maxItems, exactItems, everyItem, someItem, } from "./validationConstraints.array.js";
|
|
18
|
-
// Circular reference detection
|
|
19
|
-
export { assertNoCircularReference, hasCircularReference, } from "./validationConstraints.circular.js";
|
|
20
|
-
// Depth checking
|
|
21
|
-
export { getSerializationDepth, assertDepthWithinLimit, } from "./validationConstraints.depth.js";
|
|
22
|
-
// Size estimation
|
|
23
|
-
export { estimateSerializedSize, assertSizeWithinLimit, } from "./validationConstraints.size.js";
|
|
8
|
+
// Scalar constraints: strings, numbers, dates
|
|
9
|
+
export * from "./scalar/index.js";
|
|
10
|
+
// Collection constraints: arrays and membership
|
|
11
|
+
export * from "./collection/index.js";
|
|
12
|
+
// Structural guards: depth, size, and circular references
|
|
13
|
+
export * from "./structure/index.js";
|
|
24
14
|
//# sourceMappingURL=index.js.map
|