@zudojs/validation 1.1.2 → 1.2.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/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/validationComposer/index.d.ts +0 -1
- package/dist/validationComposer/index.js +0 -1
- package/dist/validationComposer/validationComposer.combinators.d.ts +0 -1
- package/dist/validationComposer/validationComposer.combinators.js +0 -1
- package/dist/validationComposer/validationComposer.core.d.ts +0 -1
- package/dist/validationComposer/validationComposer.core.js +0 -1
- package/dist/validationComposer/validationComposer.wrappers.d.ts +0 -1
- package/dist/validationComposer/validationComposer.wrappers.js +0 -1
- package/dist/validationConstraints/collection/index.d.ts +0 -1
- package/dist/validationConstraints/collection/index.js +0 -1
- package/dist/validationConstraints/collection/validationConstraints.array.d.ts +6 -1
- package/dist/validationConstraints/collection/validationConstraints.array.js +7 -2
- package/dist/validationConstraints/collection/validationConstraints.collection.d.ts +0 -1
- package/dist/validationConstraints/collection/validationConstraints.collection.js +0 -1
- package/dist/validationConstraints/index.d.ts +0 -1
- package/dist/validationConstraints/index.js +0 -1
- package/dist/validationConstraints/scalar/index.d.ts +0 -1
- package/dist/validationConstraints/scalar/index.js +0 -1
- package/dist/validationConstraints/scalar/validationConstraints.date.d.ts +0 -1
- package/dist/validationConstraints/scalar/validationConstraints.date.js +0 -1
- package/dist/validationConstraints/scalar/validationConstraints.number.d.ts +0 -1
- package/dist/validationConstraints/scalar/validationConstraints.number.js +0 -1
- package/dist/validationConstraints/scalar/validationConstraints.string.d.ts +0 -1
- package/dist/validationConstraints/scalar/validationConstraints.string.js +1 -6
- package/dist/validationConstraints/structure/index.d.ts +0 -1
- package/dist/validationConstraints/structure/index.js +0 -1
- package/dist/validationConstraints/structure/validationConstraints.children.d.ts +0 -1
- package/dist/validationConstraints/structure/validationConstraints.children.js +0 -1
- package/dist/validationConstraints/structure/validationConstraints.circular.d.ts +0 -1
- package/dist/validationConstraints/structure/validationConstraints.circular.js +0 -1
- package/dist/validationConstraints/structure/validationConstraints.depth.d.ts +0 -1
- package/dist/validationConstraints/structure/validationConstraints.depth.js +0 -1
- package/dist/validationConstraints/structure/validationConstraints.size.d.ts +4 -1
- package/dist/validationConstraints/structure/validationConstraints.size.js +16 -4
- package/dist/validationConstraints/structure/validationConstraints.traverse.d.ts +0 -1
- package/dist/validationConstraints/structure/validationConstraints.traverse.js +0 -1
- package/dist/validationConstraints/validationConstraints.base.d.ts +9 -1
- package/dist/validationConstraints/validationConstraints.base.js +21 -3
- package/dist/validationErrors/index.d.ts +0 -1
- package/dist/validationErrors/index.js +0 -1
- package/dist/validationErrors/validationError.base.d.ts +6 -2
- package/dist/validationErrors/validationError.base.js +7 -3
- package/dist/validationErrors/validationError.types.d.ts +0 -1
- package/dist/validationErrors/validationError.types.js +0 -1
- package/dist/validationFactory/index.d.ts +0 -1
- package/dist/validationFactory/index.js +0 -1
- package/dist/validationFactory/validationFactory.core.d.ts +0 -1
- package/dist/validationFactory/validationFactory.core.js +0 -1
- package/dist/validationNormalizer/index.d.ts +0 -1
- package/dist/validationNormalizer/index.js +0 -1
- package/dist/validationNormalizer/validationNormalizer.core.d.ts +0 -1
- package/dist/validationNormalizer/validationNormalizer.core.js +0 -1
- package/dist/validationNormalizer/validationNormalizer.helpers.d.ts +0 -1
- package/dist/validationNormalizer/validationNormalizer.helpers.js +0 -1
- package/dist/validationParser/index.d.ts +0 -1
- package/dist/validationParser/index.js +0 -1
- package/dist/validationParser/validationParser.collection.d.ts +0 -1
- package/dist/validationParser/validationParser.collection.js +0 -1
- package/dist/validationParser/validationParser.core.d.ts +0 -1
- package/dist/validationParser/validationParser.core.js +0 -1
- package/dist/validationRegistry/index.d.ts +0 -1
- package/dist/validationRegistry/index.js +0 -1
- package/dist/validationRegistry/validationRegistry.core.d.ts +11 -2
- package/dist/validationRegistry/validationRegistry.core.js +35 -11
- package/dist/validationRegistry/validationRegistry.readonly.d.ts +0 -1
- package/dist/validationRegistry/validationRegistry.readonly.js +0 -1
- package/dist/validationResult/index.d.ts +0 -1
- package/dist/validationResult/index.js +0 -1
- package/dist/validationResult/validationResult.type.d.ts +6 -2
- package/dist/validationResult/validationResult.type.js +9 -3
- package/dist/validationSchema/index.d.ts +0 -1
- package/dist/validationSchema/index.js +0 -1
- package/dist/validationSchema/validationSchema.core.d.ts +8 -2
- package/dist/validationSchema/validationSchema.core.js +34 -4
- package/dist/validationTransformer/index.d.ts +0 -1
- package/dist/validationTransformer/index.js +0 -1
- package/dist/validationTransformer/validationTransformer.core.d.ts +0 -1
- package/dist/validationTransformer/validationTransformer.core.js +0 -1
- package/dist/validationTransformer/validationTransformer.helpers.d.ts +0 -1
- package/dist/validationTransformer/validationTransformer.helpers.js +0 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -18,4 +18,3 @@ export declare function any<T>(...validators: readonly ValidationStep<T>[]): Val
|
|
|
18
18
|
export declare function first<T>(...validators: readonly ValidationStep<T>[]): ValidationStep<T>;
|
|
19
19
|
/** Negates a validation step. */
|
|
20
20
|
export declare function negate<T>(validator: ValidationStep<T>): ValidationStep<T>;
|
|
21
|
-
//# sourceMappingURL=validationComposer.combinators.d.ts.map
|
|
@@ -34,4 +34,3 @@ export declare function schemaStep<T>(schema: ValidationSchema<T>): ValidationSt
|
|
|
34
34
|
export declare function constraintStep<T>(constraint: ValidationConstraint<T>): ValidationStep<T>;
|
|
35
35
|
/** Creates a validation step from multiple constraints. */
|
|
36
36
|
export declare function constraintsStep<T>(constraints: readonly ValidationConstraint<T>[]): ValidationStep<T>;
|
|
37
|
-
//# sourceMappingURL=validationComposer.core.d.ts.map
|
|
@@ -35,4 +35,3 @@ export declare function mapValidated<T, U>(validator: ValidationStep<T>, mapper:
|
|
|
35
35
|
* validated value without changing it.
|
|
36
36
|
*/
|
|
37
37
|
export declare function tapValidated<T>(validator: ValidationStep<T>, observe: (value: T) => void): ValidationStep<T>;
|
|
38
|
-
//# sourceMappingURL=validationComposer.wrappers.d.ts.map
|
|
@@ -13,10 +13,15 @@ export declare function maxItems<T>(maximum: number): ValidationConstraint<reado
|
|
|
13
13
|
export declare function exactItems<T>(length: number): ValidationConstraint<readonly T[]>;
|
|
14
14
|
/**
|
|
15
15
|
* Requires every array item to satisfy a constraint.
|
|
16
|
+
*
|
|
17
|
+
* The message says it is about the items ("Every item must satisfy: …").
|
|
18
|
+
* Reusing the item constraint's message verbatim told a caller who sent a
|
|
19
|
+
* number instead of an array that the "value must be a valid email address".
|
|
20
|
+
* The issue is reported at the array's path: a constraint yields one issue,
|
|
21
|
+
* so the failing index is not part of it.
|
|
16
22
|
*/
|
|
17
23
|
export declare function everyItem<T>(constraint: ValidationConstraint<T>): ValidationConstraint<readonly T[]>;
|
|
18
24
|
/**
|
|
19
25
|
* Requires at least one array item to satisfy a constraint.
|
|
20
26
|
*/
|
|
21
27
|
export declare function someItem<T>(constraint: ValidationConstraint<T>): ValidationConstraint<readonly T[]>;
|
|
22
|
-
//# sourceMappingURL=validationConstraints.array.d.ts.map
|
|
@@ -65,12 +65,18 @@ export function exactItems(length) {
|
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* Requires every array item to satisfy a constraint.
|
|
68
|
+
*
|
|
69
|
+
* The message says it is about the items ("Every item must satisfy: …").
|
|
70
|
+
* Reusing the item constraint's message verbatim told a caller who sent a
|
|
71
|
+
* number instead of an array that the "value must be a valid email address".
|
|
72
|
+
* The issue is reported at the array's path: a constraint yields one issue,
|
|
73
|
+
* so the failing index is not part of it.
|
|
68
74
|
*/
|
|
69
75
|
export function everyItem(constraint) {
|
|
70
76
|
return createConstraint((values) => !scanItems(values, constraint, false), {
|
|
71
77
|
name: `every_${constraint.name}`,
|
|
72
78
|
code: "item_constraint_failed",
|
|
73
|
-
message: constraint.message
|
|
79
|
+
message: `Every item must satisfy: ${constraint.message}`,
|
|
74
80
|
guard: isArrayOf,
|
|
75
81
|
});
|
|
76
82
|
}
|
|
@@ -85,4 +91,3 @@ export function someItem(constraint) {
|
|
|
85
91
|
guard: isArrayOf,
|
|
86
92
|
});
|
|
87
93
|
}
|
|
88
|
-
//# sourceMappingURL=validationConstraints.array.js.map
|
|
@@ -7,4 +7,3 @@ export declare function oneOf<T>(values: readonly T[]): ValidationConstraint<T>;
|
|
|
7
7
|
* Requires a value not to belong to a provided collection.
|
|
8
8
|
*/
|
|
9
9
|
export declare function noneOf<T>(values: readonly T[]): ValidationConstraint<T>;
|
|
10
|
-
//# sourceMappingURL=validationConstraints.collection.d.ts.map
|
|
@@ -10,4 +10,3 @@ export declare const futureDate: import("../validationConstraints.base.js").Vali
|
|
|
10
10
|
* Requires a date to be in the past.
|
|
11
11
|
*/
|
|
12
12
|
export declare const pastDate: import("../validationConstraints.base.js").ValidationConstraint<Date>;
|
|
13
|
-
//# sourceMappingURL=validationConstraints.date.d.ts.map
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { ValidationLength, ValidationPattern } from "@zudojs/constants";
|
|
2
|
-
import { formatCount } from "@zudojs/types";
|
|
2
|
+
import { characterLength, formatCount } from "@zudojs/types";
|
|
3
3
|
import { createConstraint, assertNonNegativeInteger, } from "../validationConstraints.base.js";
|
|
4
|
-
/** Counts Unicode code points rather than UTF-16 code units. */
|
|
5
|
-
function characterLength(value) {
|
|
6
|
-
return [...value].length;
|
|
7
|
-
}
|
|
8
4
|
/**
|
|
9
5
|
* Requires a string to be non-empty after trimming.
|
|
10
6
|
*/
|
|
@@ -160,4 +156,3 @@ export const slug = createConstraint((value) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/u.te
|
|
|
160
156
|
message: "Value must be a slug: lowercase letters and digits, separated by single hyphens.",
|
|
161
157
|
guard: (value) => typeof value === "string",
|
|
162
158
|
});
|
|
163
|
-
//# sourceMappingURL=validationConstraints.string.js.map
|
|
@@ -12,4 +12,3 @@ export declare function isContainer(value: unknown): value is object;
|
|
|
12
12
|
* is also how `JSON.stringify` writes it (as `null`).
|
|
13
13
|
*/
|
|
14
14
|
export declare function childrenOf(value: object): Array<[string, unknown]>;
|
|
15
|
-
//# sourceMappingURL=validationConstraints.children.d.ts.map
|
|
@@ -33,4 +33,3 @@ export declare function assertNoCircularReference(value: unknown, path?: string,
|
|
|
33
33
|
* Does not throw — use assertNoCircularReference for throwing behavior.
|
|
34
34
|
*/
|
|
35
35
|
export declare function hasCircularReference(value: unknown): boolean;
|
|
36
|
-
//# sourceMappingURL=validationConstraints.circular.d.ts.map
|
|
@@ -48,4 +48,3 @@ export declare function getSerializationDepth(value: unknown, limit?: number): n
|
|
|
48
48
|
* `statusCode` 400 and `expose: true` (see {@link UNTRUSTED_DEPTH_ERROR}).
|
|
49
49
|
*/
|
|
50
50
|
export declare function assertDepthWithinLimit(value: unknown, maxDepth: number): void;
|
|
51
|
-
//# sourceMappingURL=validationConstraints.depth.d.ts.map
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Estimate the byte size of a value as JSON without allocating a string.
|
|
9
9
|
*
|
|
10
|
+
* The estimate is meant to sit at or above the real UTF-8 size: strings are
|
|
11
|
+
* measured exactly, numbers are charged their worst case, and containers
|
|
12
|
+
* their punctuation.
|
|
13
|
+
*
|
|
10
14
|
* A value referenced from several places is charged once per occurrence, the
|
|
11
15
|
* way a serializer expands it. Counting it once let a compact payload built
|
|
12
16
|
* from shared subtrees estimate at a few hundred bytes while serializing to
|
|
@@ -36,4 +40,3 @@ export declare function estimateSerializedSize(value: unknown, maxBytes?: number
|
|
|
36
40
|
* @throws {SerializationPayloadTooLargeError} when the estimate exceeds maxSize.
|
|
37
41
|
*/
|
|
38
42
|
export declare function assertSizeWithinLimit(value: unknown, maxSize: number): void;
|
|
39
|
-
//# sourceMappingURL=validationConstraints.size.d.ts.map
|
|
@@ -6,15 +6,24 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { SerializationLimits } from "@zudojs/constants";
|
|
8
8
|
import { SerializationPayloadTooLargeError } from "@zudojs/errors";
|
|
9
|
+
import { jsonStringByteLength } from "@zudojs/types";
|
|
9
10
|
import { MAX_MEASURABLE_DEPTH } from "./validationConstraints.depth.js";
|
|
10
11
|
import { TraversalLimitError, traverse, } from "./validationConstraints.traverse.js";
|
|
11
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Bytes charged for a single node, excluding its children.
|
|
14
|
+
*
|
|
15
|
+
* Strings are charged their UTF-8 size as JSON writes them (quotes and
|
|
16
|
+
* escapes included). The previous `length * 2` was the UTF-16 size, which
|
|
17
|
+
* undercounts every character above U+07FF: a thousand "₦" estimated at
|
|
18
|
+
* 2,013 bytes against 3,002 on the wire, so `assertSizeWithinLimit` let
|
|
19
|
+
* bodies half again larger than the limit through.
|
|
20
|
+
*/
|
|
12
21
|
function chargeFor(value) {
|
|
13
22
|
if (value === null || value === undefined)
|
|
14
23
|
return 4;
|
|
15
24
|
switch (typeof value) {
|
|
16
25
|
case "string":
|
|
17
|
-
return value
|
|
26
|
+
return jsonStringByteLength(value);
|
|
18
27
|
case "number":
|
|
19
28
|
// JSON emits up to 21 characters for a double; charge the worst case
|
|
20
29
|
// rather than a flat 8, which under-counted every numeric field.
|
|
@@ -41,7 +50,7 @@ function chargeFor(value) {
|
|
|
41
50
|
return 2 + Math.max(0, value.size - 1);
|
|
42
51
|
const keys = Object.keys(value);
|
|
43
52
|
return (2 +
|
|
44
|
-
keys.reduce((total, key) => total + key
|
|
53
|
+
keys.reduce((total, key) => total + jsonStringByteLength(key) + 2, 0) +
|
|
45
54
|
Math.max(0, keys.length - 1));
|
|
46
55
|
}
|
|
47
56
|
/**
|
|
@@ -65,6 +74,10 @@ function resolveToJson(value) {
|
|
|
65
74
|
/**
|
|
66
75
|
* Estimate the byte size of a value as JSON without allocating a string.
|
|
67
76
|
*
|
|
77
|
+
* The estimate is meant to sit at or above the real UTF-8 size: strings are
|
|
78
|
+
* measured exactly, numbers are charged their worst case, and containers
|
|
79
|
+
* their punctuation.
|
|
80
|
+
*
|
|
68
81
|
* A value referenced from several places is charged once per occurrence, the
|
|
69
82
|
* way a serializer expands it. Counting it once let a compact payload built
|
|
70
83
|
* from shared subtrees estimate at a few hundred bytes while serializing to
|
|
@@ -127,4 +140,3 @@ export function assertSizeWithinLimit(value, maxSize) {
|
|
|
127
140
|
throw error;
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
|
-
//# sourceMappingURL=validationConstraints.size.js.map
|
|
@@ -56,4 +56,3 @@ export interface TraversalReport {
|
|
|
56
56
|
* @throws {TraversalLimitError} as soon as a bound is exceeded.
|
|
57
57
|
*/
|
|
58
58
|
export declare function traverse(root: unknown, visitor: TraversalVisitor, rootPath?: string): TraversalReport;
|
|
59
|
-
//# sourceMappingURL=validationConstraints.traverse.d.ts.map
|
|
@@ -45,10 +45,19 @@ export declare function checkConstraint<T>(constraint: ValidationConstraint<T>,
|
|
|
45
45
|
export declare function checkConstraints<T>(constraints: readonly ValidationConstraint<T>[], value: T, path?: readonly (string | number)[]): ValidationResult<T>;
|
|
46
46
|
/**
|
|
47
47
|
* Combines constraints into a single constraint.
|
|
48
|
+
*
|
|
49
|
+
* The combined message lists the member messages, so a failure says what
|
|
50
|
+
* was required ("Value must contain at least 3 characters. Value must
|
|
51
|
+
* contain only letters.") rather than "One or more validation constraints
|
|
52
|
+
* failed."
|
|
48
53
|
*/
|
|
49
54
|
export declare function combineConstraints<T>(...constraints: readonly ValidationConstraint<T>[]): ValidationConstraint<T>;
|
|
50
55
|
/**
|
|
51
56
|
* Creates a negated constraint.
|
|
57
|
+
*
|
|
58
|
+
* Without an explicit `message` the default names the inner constraint in
|
|
59
|
+
* words (`Value must not satisfy the "one of" constraint.`) instead of its
|
|
60
|
+
* internal identifier (`one_of`).
|
|
52
61
|
*/
|
|
53
62
|
export declare function not<T>(constraint: ValidationConstraint<T>, options?: ConstraintOptions<T>): ValidationConstraint<T>;
|
|
54
63
|
/**
|
|
@@ -59,4 +68,3 @@ export declare const required: ValidationConstraint<unknown>;
|
|
|
59
68
|
* Asserts that a value is a non-negative integer.
|
|
60
69
|
*/
|
|
61
70
|
export declare function assertNonNegativeInteger(value: number, name: string): void;
|
|
62
|
-
//# sourceMappingURL=validationConstraints.base.d.ts.map
|
|
@@ -73,19 +73,37 @@ export function checkConstraints(constraints, value, path = []) {
|
|
|
73
73
|
return failure(issues);
|
|
74
74
|
return success(value);
|
|
75
75
|
}
|
|
76
|
+
/** Turns an internal constraint name (`one_of`, `min_length_3`) into words. */
|
|
77
|
+
function describeConstraint(name) {
|
|
78
|
+
return name.replace(/_+/g, " ").trim() || "custom";
|
|
79
|
+
}
|
|
76
80
|
/**
|
|
77
81
|
* Combines constraints into a single constraint.
|
|
82
|
+
*
|
|
83
|
+
* The combined message lists the member messages, so a failure says what
|
|
84
|
+
* was required ("Value must contain at least 3 characters. Value must
|
|
85
|
+
* contain only letters.") rather than "One or more validation constraints
|
|
86
|
+
* failed."
|
|
78
87
|
*/
|
|
79
88
|
export function combineConstraints(...constraints) {
|
|
89
|
+
const messages = [
|
|
90
|
+
...new Set(constraints.map((constraint) => constraint.message)),
|
|
91
|
+
].filter((message) => message.length > 0);
|
|
80
92
|
return createConstraint((value) => constraints.every((constraint) => runConstraint(constraint, value)), {
|
|
81
93
|
name: constraints.map((constraint) => constraint.name).join("_and_") ||
|
|
82
94
|
"combined",
|
|
83
95
|
code: "combined_constraint_failed",
|
|
84
|
-
message:
|
|
96
|
+
message: messages.length > 0
|
|
97
|
+
? messages.join(" ")
|
|
98
|
+
: "One or more validation constraints failed.",
|
|
85
99
|
});
|
|
86
100
|
}
|
|
87
101
|
/**
|
|
88
102
|
* Creates a negated constraint.
|
|
103
|
+
*
|
|
104
|
+
* Without an explicit `message` the default names the inner constraint in
|
|
105
|
+
* words (`Value must not satisfy the "one of" constraint.`) instead of its
|
|
106
|
+
* internal identifier (`one_of`).
|
|
89
107
|
*/
|
|
90
108
|
export function not(constraint, options = {}) {
|
|
91
109
|
// Negation must not fail open. A value of the wrong type, or one that makes
|
|
@@ -103,7 +121,8 @@ export function not(constraint, options = {}) {
|
|
|
103
121
|
}, {
|
|
104
122
|
name: options.name ?? `not_${constraint.name}`,
|
|
105
123
|
code: options.code ?? "negated_constraint_failed",
|
|
106
|
-
message: options.message ??
|
|
124
|
+
message: options.message ??
|
|
125
|
+
`Value must not satisfy the "${describeConstraint(constraint.name)}" constraint.`,
|
|
107
126
|
...(guard ? { guard } : {}),
|
|
108
127
|
});
|
|
109
128
|
}
|
|
@@ -123,4 +142,3 @@ export function assertNonNegativeInteger(value, name) {
|
|
|
123
142
|
throw new RangeError(`${name} must be a non-negative integer.`);
|
|
124
143
|
}
|
|
125
144
|
}
|
|
126
|
-
//# sourceMappingURL=validationConstraints.base.js.map
|
|
@@ -67,6 +67,10 @@ export declare function toValidationError(error: unknown, fallbackMessage?: stri
|
|
|
67
67
|
export declare function isValidationError(error: unknown): error is ValidationError;
|
|
68
68
|
/** Returns whether a validation error has a specific code. */
|
|
69
69
|
export declare function hasValidationErrorCode(error: unknown, code: ValidationErrorCode): boolean;
|
|
70
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Creates a validation error from a collection of issues.
|
|
72
|
+
*
|
|
73
|
+
* The code defaults to `INVALID_INPUT` (the issues describe rejected input),
|
|
74
|
+
* not `UNKNOWN`, which is reserved for errors whose cause really is unknown.
|
|
75
|
+
*/
|
|
71
76
|
export declare function createValidationError(issues: readonly ValidationIssue[], options?: ValidationErrorOptions): ValidationError;
|
|
72
|
-
//# sourceMappingURL=validationError.base.d.ts.map
|
|
@@ -114,8 +114,12 @@ export function isValidationError(error) {
|
|
|
114
114
|
export function hasValidationErrorCode(error, code) {
|
|
115
115
|
return isValidationError(error) && error.validationCode === code;
|
|
116
116
|
}
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Creates a validation error from a collection of issues.
|
|
119
|
+
*
|
|
120
|
+
* The code defaults to `INVALID_INPUT` (the issues describe rejected input),
|
|
121
|
+
* not `UNKNOWN`, which is reserved for errors whose cause really is unknown.
|
|
122
|
+
*/
|
|
118
123
|
export function createValidationError(issues, options = {}) {
|
|
119
|
-
return new ValidationError(formatIssues(issues) || "Validation failed.", issues, options);
|
|
124
|
+
return new ValidationError(formatIssues(issues) || "Validation failed.", issues, { ...options, code: options.code ?? ValidationErrorCode.INVALID_INPUT });
|
|
120
125
|
}
|
|
121
|
-
//# sourceMappingURL=validationError.base.js.map
|
|
@@ -27,4 +27,3 @@ export declare class ConstraintValidationError extends ValidationError {
|
|
|
27
27
|
export declare class SchemaValidationError extends ValidationError {
|
|
28
28
|
constructor(message?: string, issues?: readonly ValidationIssue[], options?: Omit<ValidationErrorOptions, "code">);
|
|
29
29
|
}
|
|
30
|
-
//# sourceMappingURL=validationError.types.d.ts.map
|
|
@@ -38,4 +38,3 @@ export declare function createValidationFactory(options?: ValidationFactoryOptio
|
|
|
38
38
|
* calling `registry.clear()` silently removes rules another module registered.
|
|
39
39
|
*/
|
|
40
40
|
export declare function createScopedValidationFactory(parent: ValidationFactory): ValidationFactory;
|
|
41
|
-
//# sourceMappingURL=validationFactory.core.d.ts.map
|
|
@@ -24,4 +24,3 @@ export interface AsyncValidationNormalizer<T> {
|
|
|
24
24
|
export declare function createNormalizer<T>(normalizer: Normalizer<T>, options?: NormalizerOptions): ValidationNormalizer<T>;
|
|
25
25
|
/** Creates a reusable asynchronous normalizer. */
|
|
26
26
|
export declare function createAsyncNormalizer<T>(normalizer: AsyncNormalizer<T>, options?: NormalizerOptions): AsyncValidationNormalizer<T>;
|
|
27
|
-
//# sourceMappingURL=validationNormalizer.core.d.ts.map
|
|
@@ -68,4 +68,3 @@ export declare function normalizeOptionalString(value: string | undefined): stri
|
|
|
68
68
|
export declare function normalizeNullableString(value: string | null): string | null;
|
|
69
69
|
/** Normalizes an optional nullable string. */
|
|
70
70
|
export declare function normalizeOptionalNullableString(value: string | null | undefined): string | null | undefined;
|
|
71
|
-
//# sourceMappingURL=validationNormalizer.helpers.d.ts.map
|
|
@@ -25,4 +25,3 @@ export declare function parseOptional<T>(schema: ValidationSchema<T>, value: unk
|
|
|
25
25
|
export declare function parseNullable<T>(schema: ValidationSchema<T>, value: unknown, options?: ParseOptions): ValidationResult<T | null>;
|
|
26
26
|
/** Parses an optional nullable value. */
|
|
27
27
|
export declare function parseOptionalNullable<T>(schema: ValidationSchema<T>, value: unknown, options?: ParseOptions): ValidationResult<T | null | undefined>;
|
|
28
|
-
//# sourceMappingURL=validationParser.collection.d.ts.map
|
|
@@ -27,4 +27,3 @@ export declare function createAsyncValidationParser<T>(schema: ValidationSchema<
|
|
|
27
27
|
export declare function parseOr<T>(schema: ValidationSchema<T>, value: unknown, fallback: T): T;
|
|
28
28
|
/** Parses a value and returns a fallback produced by a function when validation fails. */
|
|
29
29
|
export declare function parseOrElse<T>(schema: ValidationSchema<T>, value: unknown, fallback: (error: SchemaValidationError) => T): T;
|
|
30
|
-
//# sourceMappingURL=validationParser.core.d.ts.map
|
|
@@ -13,7 +13,12 @@ export interface ValidationRule<T = unknown> {
|
|
|
13
13
|
export interface ValidationRuleOptions {
|
|
14
14
|
readonly overwrite?: boolean;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Registry of reusable validation schemas and constraints.
|
|
18
|
+
*
|
|
19
|
+
* Misuse (an empty or unknown rule name, a duplicate registration, a rule
|
|
20
|
+
* without a schema or constraints) throws `ConfigurationError`.
|
|
21
|
+
*/
|
|
17
22
|
export declare class ValidationRegistry {
|
|
18
23
|
private readonly rules;
|
|
19
24
|
register<T>(rule: ValidationRule<T>, options?: ValidationRuleOptions): this;
|
|
@@ -26,6 +31,11 @@ export declare class ValidationRegistry {
|
|
|
26
31
|
readonly overwrite?: boolean;
|
|
27
32
|
}): this;
|
|
28
33
|
get<T = unknown>(name: string): ValidationRule<T> | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Returns a registered rule.
|
|
36
|
+
*
|
|
37
|
+
* @throws {ConfigurationError} when no rule of that name is registered.
|
|
38
|
+
*/
|
|
29
39
|
require<T = unknown>(name: string): ValidationRule<T>;
|
|
30
40
|
has(name: string): boolean;
|
|
31
41
|
unregister(name: string): boolean;
|
|
@@ -52,4 +62,3 @@ export declare class ValidationRegistry {
|
|
|
52
62
|
export declare function createValidationRegistry(): ValidationRegistry;
|
|
53
63
|
/** Creates a registry from an initial collection of rules. */
|
|
54
64
|
export declare function createRegistryFromRules(rules: readonly ValidationRule[]): ValidationRegistry;
|
|
55
|
-
//# sourceMappingURL=validationRegistry.core.d.ts.map
|
|
@@ -1,22 +1,41 @@
|
|
|
1
|
+
import { ConfigurationError } from "@zudojs/errors";
|
|
1
2
|
import { checkConstraints } from "../validationConstraints/index.js";
|
|
2
3
|
import { validate } from "../validationSchema/validationSchema.core.js";
|
|
3
4
|
import { ReadonlyValidationRegistry } from "./validationRegistry.readonly.js";
|
|
4
5
|
/** Normalizes registry keys. */
|
|
5
6
|
function normalizeRuleName(name) {
|
|
6
|
-
const normalized = name.trim();
|
|
7
|
-
if (normalized.length === 0)
|
|
8
|
-
throw
|
|
7
|
+
const normalized = typeof name === "string" ? name.trim() : "";
|
|
8
|
+
if (normalized.length === 0) {
|
|
9
|
+
throw registryError("Validation rule name cannot be empty.");
|
|
10
|
+
}
|
|
9
11
|
return normalized;
|
|
10
12
|
}
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* A registry mistake is a wiring error in the service, not bad input: it is
|
|
15
|
+
* a `ConfigurationError` (500, not exposed) rather than a bare `Error`, so
|
|
16
|
+
* callers can tell it from a defect and from a validation failure.
|
|
17
|
+
*/
|
|
18
|
+
function registryError(message, ruleName) {
|
|
19
|
+
return new ConfigurationError(message, {
|
|
20
|
+
component: "ValidationRegistry",
|
|
21
|
+
...(ruleName !== undefined ? { configKey: ruleName } : {}),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Registry of reusable validation schemas and constraints.
|
|
26
|
+
*
|
|
27
|
+
* Misuse (an empty or unknown rule name, a duplicate registration, a rule
|
|
28
|
+
* without a schema or constraints) throws `ConfigurationError`.
|
|
29
|
+
*/
|
|
12
30
|
export class ValidationRegistry {
|
|
13
31
|
rules = new Map();
|
|
14
32
|
register(rule, options = {}) {
|
|
15
33
|
const name = normalizeRuleName(rule.name);
|
|
16
|
-
if (!options.overwrite && this.rules.has(name))
|
|
17
|
-
throw
|
|
34
|
+
if (!options.overwrite && this.rules.has(name)) {
|
|
35
|
+
throw registryError(`Validation rule "${name}" is already registered.`, name);
|
|
36
|
+
}
|
|
18
37
|
if (!rule.schema && (!rule.constraints || rule.constraints.length === 0)) {
|
|
19
|
-
throw
|
|
38
|
+
throw registryError(`Validation rule "${name}" must define a schema or at least one constraint.`, name);
|
|
20
39
|
}
|
|
21
40
|
this.rules.set(name, Object.freeze({
|
|
22
41
|
...rule,
|
|
@@ -36,10 +55,16 @@ export class ValidationRegistry {
|
|
|
36
55
|
get(name) {
|
|
37
56
|
return this.rules.get(normalizeRuleName(name));
|
|
38
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns a registered rule.
|
|
60
|
+
*
|
|
61
|
+
* @throws {ConfigurationError} when no rule of that name is registered.
|
|
62
|
+
*/
|
|
39
63
|
require(name) {
|
|
40
64
|
const rule = this.get(name);
|
|
41
|
-
if (!rule)
|
|
42
|
-
throw
|
|
65
|
+
if (!rule) {
|
|
66
|
+
throw registryError(`Validation rule "${name}" is not registered.`, name);
|
|
67
|
+
}
|
|
43
68
|
return rule;
|
|
44
69
|
}
|
|
45
70
|
has(name) {
|
|
@@ -83,7 +108,7 @@ export class ValidationRegistry {
|
|
|
83
108
|
}
|
|
84
109
|
if (constraints.length > 0)
|
|
85
110
|
return checkConstraints(constraints, value);
|
|
86
|
-
throw
|
|
111
|
+
throw registryError(`Validation rule "${name}" has no validation implementation.`, name);
|
|
87
112
|
}
|
|
88
113
|
clone() {
|
|
89
114
|
const registry = new ValidationRegistry();
|
|
@@ -111,4 +136,3 @@ export function createRegistryFromRules(rules) {
|
|
|
111
136
|
registry.register(rule);
|
|
112
137
|
return registry;
|
|
113
138
|
}
|
|
114
|
-
//# sourceMappingURL=validationRegistry.core.js.map
|
|
@@ -10,7 +10,12 @@ export interface ValidationIssue {
|
|
|
10
10
|
readonly expected?: unknown;
|
|
11
11
|
readonly received?: unknown;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Formats validation issues into a human-readable string.
|
|
15
|
+
*
|
|
16
|
+
* An issue with an empty path (one about the value as a whole) prints just
|
|
17
|
+
* its message rather than ": message".
|
|
18
|
+
*/
|
|
14
19
|
export declare function formatIssues(issues: readonly ValidationIssue[]): string;
|
|
15
20
|
/**
|
|
16
21
|
* Groups validation issues by their first path segment.
|
|
@@ -81,4 +86,3 @@ export declare class ValidationResultError extends SharedValidationError {
|
|
|
81
86
|
message: string;
|
|
82
87
|
};
|
|
83
88
|
}
|
|
84
|
-
//# sourceMappingURL=validationResult.type.d.ts.map
|
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
* Validation result types and helpers.
|
|
3
3
|
*/
|
|
4
4
|
import { ValidationError as SharedValidationError, ErrorCode, ErrorCategory, ErrorSeverity, } from "@zudojs/errors";
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Formats validation issues into a human-readable string.
|
|
7
|
+
*
|
|
8
|
+
* An issue with an empty path (one about the value as a whole) prints just
|
|
9
|
+
* its message rather than ": message".
|
|
10
|
+
*/
|
|
6
11
|
export function formatIssues(issues) {
|
|
7
12
|
if (issues.length === 0)
|
|
8
13
|
return "";
|
|
9
|
-
return issues
|
|
14
|
+
return issues
|
|
15
|
+
.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message)
|
|
16
|
+
.join("; ");
|
|
10
17
|
}
|
|
11
18
|
/**
|
|
12
19
|
* Groups validation issues by their first path segment.
|
|
@@ -122,4 +129,3 @@ export class ValidationResultError extends SharedValidationError {
|
|
|
122
129
|
};
|
|
123
130
|
}
|
|
124
131
|
}
|
|
125
|
-
//# sourceMappingURL=validationResult.type.js.map
|
|
@@ -8,7 +8,14 @@ export interface ParseOptions {
|
|
|
8
8
|
}
|
|
9
9
|
/** Maps a Zod error into the package's normalized issue format. */
|
|
10
10
|
export declare function mapZodIssues(error: ZodError, pathPrefix?: readonly (string | number)[]): ValidationIssue[];
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Safely parses input using a Zod schema.
|
|
13
|
+
*
|
|
14
|
+
* Invalid input is always a `{ success: false }` result. The one throw is a
|
|
15
|
+
* schema with async refinements (see `validateAsync`).
|
|
16
|
+
*
|
|
17
|
+
* @throws {ConfigurationError} when the schema needs `validateAsync()`.
|
|
18
|
+
*/
|
|
12
19
|
export declare function validate<T>(schema: ValidationSchema<T>, value: unknown, options?: ParseOptions): ValidationResult<T>;
|
|
13
20
|
/** Parses input using a schema and throws when validation fails. */
|
|
14
21
|
export declare function parse<T>(schema: ValidationSchema<T>, value: unknown, options?: ParseOptions): T;
|
|
@@ -36,4 +43,3 @@ export declare function assertValid<T>(result: ValidationResult<T>): asserts res
|
|
|
36
43
|
};
|
|
37
44
|
/** Re-exports Zod so consumers do not need to depend on a second version of Zod. */
|
|
38
45
|
export { z };
|
|
39
|
-
//# sourceMappingURL=validationSchema.core.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z, ZodError } from "zod";
|
|
2
|
+
import { ConfigurationError } from "@zudojs/errors";
|
|
2
3
|
import { failure, success } from "../validationResult/validationResult.type.js";
|
|
3
4
|
import { SchemaValidationError } from "../validationErrors/validationError.types.js";
|
|
4
5
|
/** Maps a Zod error into the package's normalized issue format. */
|
|
@@ -15,9 +16,39 @@ export function mapZodIssues(error, pathPrefix = []) {
|
|
|
15
16
|
: {}),
|
|
16
17
|
}));
|
|
17
18
|
}
|
|
18
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Runs a synchronous Zod parse, turning Zod's "async schema in a sync parse"
|
|
21
|
+
* fault into a framework error.
|
|
22
|
+
*
|
|
23
|
+
* A schema with an async `refine`/`transform` cannot be parsed synchronously;
|
|
24
|
+
* Zod throws a bare `Error`, which escaped `validate()` and `isValid()` as an
|
|
25
|
+
* anonymous 500. It is a wiring mistake, not invalid input, so it is reported
|
|
26
|
+
* as a `ConfigurationError` naming the fix rather than as a validation
|
|
27
|
+
* failure (which would return a 400 for a bug in the service).
|
|
28
|
+
*
|
|
29
|
+
* @throws {ConfigurationError} when the schema needs `validateAsync()`.
|
|
30
|
+
*/
|
|
31
|
+
function safeParseSync(schema, value) {
|
|
32
|
+
try {
|
|
33
|
+
return schema.safeParse(value);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof z.core.$ZodAsyncError) {
|
|
37
|
+
throw new ConfigurationError("The schema contains an async refine() or transform() and cannot be parsed synchronously; use validateAsync(), parseAsync() or createAsyncValidator().", { component: "@zudojs/validation", cause: error });
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Safely parses input using a Zod schema.
|
|
44
|
+
*
|
|
45
|
+
* Invalid input is always a `{ success: false }` result. The one throw is a
|
|
46
|
+
* schema with async refinements (see `validateAsync`).
|
|
47
|
+
*
|
|
48
|
+
* @throws {ConfigurationError} when the schema needs `validateAsync()`.
|
|
49
|
+
*/
|
|
19
50
|
export function validate(schema, value, options = {}) {
|
|
20
|
-
const result = schema
|
|
51
|
+
const result = safeParseSync(schema, value);
|
|
21
52
|
if (result.success)
|
|
22
53
|
return success(result.data);
|
|
23
54
|
return failure(mapZodIssues(result.error, options.pathPrefix));
|
|
@@ -45,7 +76,7 @@ export async function parseAsync(schema, value, options = {}) {
|
|
|
45
76
|
}
|
|
46
77
|
/** Determines whether a value matches a schema. */
|
|
47
78
|
export function isValid(schema, value) {
|
|
48
|
-
return schema
|
|
79
|
+
return safeParseSync(schema, value).success;
|
|
49
80
|
}
|
|
50
81
|
/** Creates a validation function from a schema. */
|
|
51
82
|
export function createValidator(schema) {
|
|
@@ -74,4 +105,3 @@ export function assertValid(result) {
|
|
|
74
105
|
}
|
|
75
106
|
/** Re-exports Zod so consumers do not need to depend on a second version of Zod. */
|
|
76
107
|
export { z };
|
|
77
|
-
//# sourceMappingURL=validationSchema.core.js.map
|
|
@@ -26,4 +26,3 @@ export interface AsyncValidationTransformer<T, U> {
|
|
|
26
26
|
export declare function createValidationTransformer<T, U>(transform: ValidationTransform<T, U>, options?: TransformerOptions): ValidationTransformer<T, U>;
|
|
27
27
|
/** Creates an asynchronous transformer. */
|
|
28
28
|
export declare function createAsyncValidationTransformer<T, U>(transform: AsyncValidationTransform<T, U>, options?: TransformerOptions): AsyncValidationTransformer<T, U>;
|
|
29
|
-
//# sourceMappingURL=validationTransformer.core.d.ts.map
|
|
@@ -20,4 +20,3 @@ export declare function transformArray<T, U>(values: readonly T[], transform: Va
|
|
|
20
20
|
export declare function transformArrayAsync<T, U>(values: readonly T[], transform: AsyncValidationTransform<T, U>): Promise<U[]>;
|
|
21
21
|
/** Creates a schema that transforms the validated value. */
|
|
22
22
|
export declare function withTransformer<T, U>(schema: ValidationSchema<T>, transform: ValidationTransform<T, U>): ValidationSchema<U>;
|
|
23
|
-
//# sourceMappingURL=validationTransformer.helpers.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/validation",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Schema validation with Zod integration, constraints, parsers, composers, circular detection, and depth/size checks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"!dist/.tsbuildinfo"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@zudojs/constants": "1.
|
|
29
|
-
"@zudojs/errors": "1.
|
|
30
|
-
"@zudojs/types": "1.
|
|
28
|
+
"@zudojs/constants": "1.2.0",
|
|
29
|
+
"@zudojs/errors": "1.4.0",
|
|
30
|
+
"@zudojs/types": "1.3.0",
|
|
31
31
|
"zod": "^4.6.5"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|