@zudojs/validation 0.1.0 → 1.0.1
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/LICENSE +21 -0
- 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 +78 -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 +118 -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 +24 -13
- 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
|
@@ -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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Scalar constraints.
|
|
3
|
+
*
|
|
4
|
+
* @module validationConstraints/scalar
|
|
5
|
+
*/
|
|
6
|
+
export * from "./validationConstraints.string.js";
|
|
7
|
+
export * from "./validationConstraints.number.js";
|
|
8
|
+
export * from "./validationConstraints.date.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Scalar constraints.
|
|
3
|
+
*
|
|
4
|
+
* @module validationConstraints/scalar
|
|
5
|
+
*/
|
|
6
|
+
export * from "./validationConstraints.string.js";
|
|
7
|
+
export * from "./validationConstraints.number.js";
|
|
8
|
+
export * from "./validationConstraints.date.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requires a string to be a valid ISO date representation.
|
|
3
|
+
*/
|
|
4
|
+
export declare const isoDate: import("../validationConstraints.base.js").ValidationConstraint<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Requires a date to be in the future.
|
|
7
|
+
*/
|
|
8
|
+
export declare const futureDate: import("../validationConstraints.base.js").ValidationConstraint<Date>;
|
|
9
|
+
/**
|
|
10
|
+
* Requires a date to be in the past.
|
|
11
|
+
*/
|
|
12
|
+
export declare const pastDate: import("../validationConstraints.base.js").ValidationConstraint<Date>;
|
|
13
|
+
//# sourceMappingURL=validationConstraints.date.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createConstraint } from "../validationConstraints.base.js";
|
|
2
|
+
/** Narrows an unknown value to a valid Date. */
|
|
3
|
+
const isDate = (value) => value instanceof Date && !Number.isNaN(value.getTime());
|
|
4
|
+
/**
|
|
5
|
+
* Requires a string to be a valid ISO date representation.
|
|
6
|
+
*/
|
|
7
|
+
export const isoDate = createConstraint((value) => {
|
|
8
|
+
if (!/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2})?)?$/u.test(value)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const date = new Date(value);
|
|
12
|
+
if (Number.isNaN(date.getTime()))
|
|
13
|
+
return false;
|
|
14
|
+
// Reject calendar-invalid dates such as 2024-02-31, which Date rolls over
|
|
15
|
+
// into the following month rather than rejecting.
|
|
16
|
+
const [year, month, day] = value.slice(0, 10).split("-").map(Number);
|
|
17
|
+
const asUtc = new Date(Date.UTC(year, month - 1, day));
|
|
18
|
+
return (asUtc.getUTCFullYear() === year &&
|
|
19
|
+
asUtc.getUTCMonth() === month - 1 &&
|
|
20
|
+
asUtc.getUTCDate() === day);
|
|
21
|
+
}, {
|
|
22
|
+
name: "iso_date",
|
|
23
|
+
code: "invalid_date",
|
|
24
|
+
message: "Value must be a valid ISO 8601 date.",
|
|
25
|
+
guard: (value) => typeof value === "string",
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Requires a date to be in the future.
|
|
29
|
+
*/
|
|
30
|
+
export const futureDate = createConstraint((value) => value.getTime() > Date.now(), {
|
|
31
|
+
name: "future_date",
|
|
32
|
+
code: "invalid_future_date",
|
|
33
|
+
message: "Date must be in the future.",
|
|
34
|
+
guard: isDate,
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Requires a date to be in the past.
|
|
38
|
+
*/
|
|
39
|
+
export const pastDate = createConstraint((value) => value.getTime() < Date.now(), {
|
|
40
|
+
name: "past_date",
|
|
41
|
+
code: "invalid_past_date",
|
|
42
|
+
message: "Date must be in the past.",
|
|
43
|
+
guard: isDate,
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=validationConstraints.date.js.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { createConstraint } from "
|
|
1
|
+
import { createConstraint } from "../validationConstraints.base.js";
|
|
2
|
+
/** Narrows an unknown value to a number. */
|
|
3
|
+
const isNumber = (value) => typeof value === "number";
|
|
2
4
|
/**
|
|
3
5
|
* Requires a numeric value to be greater than or equal to a minimum.
|
|
4
6
|
*/
|
|
@@ -10,6 +12,7 @@ export function min(minimum) {
|
|
|
10
12
|
name: `min_${minimum}`,
|
|
11
13
|
code: "min_value",
|
|
12
14
|
message: `Value must be greater than or equal to ${minimum}.`,
|
|
15
|
+
guard: isNumber,
|
|
13
16
|
});
|
|
14
17
|
}
|
|
15
18
|
/**
|
|
@@ -23,6 +26,7 @@ export function max(maximum) {
|
|
|
23
26
|
name: `max_${maximum}`,
|
|
24
27
|
code: "max_value",
|
|
25
28
|
message: `Value must be less than or equal to ${maximum}.`,
|
|
29
|
+
guard: isNumber,
|
|
26
30
|
});
|
|
27
31
|
}
|
|
28
32
|
/**
|
|
@@ -39,6 +43,7 @@ export function between(minimum, maximum) {
|
|
|
39
43
|
name: `between_${minimum}_${maximum}`,
|
|
40
44
|
code: "value_out_of_range",
|
|
41
45
|
message: `Value must be between ${minimum} and ${maximum}.`,
|
|
46
|
+
guard: isNumber,
|
|
42
47
|
});
|
|
43
48
|
}
|
|
44
49
|
/**
|
|
@@ -48,6 +53,7 @@ export const finiteNumber = createConstraint((value) => Number.isFinite(value),
|
|
|
48
53
|
name: "finite_number",
|
|
49
54
|
code: "invalid_number",
|
|
50
55
|
message: "Value must be a finite number.",
|
|
56
|
+
guard: isNumber,
|
|
51
57
|
});
|
|
52
58
|
/**
|
|
53
59
|
* Requires an integer.
|
|
@@ -56,6 +62,7 @@ export const integer = createConstraint((value) => Number.isInteger(value), {
|
|
|
56
62
|
name: "integer",
|
|
57
63
|
code: "invalid_integer",
|
|
58
64
|
message: "Value must be an integer.",
|
|
65
|
+
guard: isNumber,
|
|
59
66
|
});
|
|
60
67
|
/**
|
|
61
68
|
* Requires a positive number.
|
|
@@ -64,6 +71,7 @@ export const positive = createConstraint((value) => value > 0, {
|
|
|
64
71
|
name: "positive",
|
|
65
72
|
code: "invalid_positive",
|
|
66
73
|
message: "Value must be greater than zero.",
|
|
74
|
+
guard: isNumber,
|
|
67
75
|
});
|
|
68
76
|
/**
|
|
69
77
|
* Requires a non-negative number.
|
|
@@ -72,6 +80,7 @@ export const nonNegative = createConstraint((value) => value >= 0, {
|
|
|
72
80
|
name: "non_negative",
|
|
73
81
|
code: "invalid_non_negative",
|
|
74
82
|
message: "Value must be zero or greater.",
|
|
83
|
+
guard: isNumber,
|
|
75
84
|
});
|
|
76
85
|
/**
|
|
77
86
|
* Requires a number to be even.
|
|
@@ -80,6 +89,7 @@ export const even = createConstraint((value) => Number.isInteger(value) && value
|
|
|
80
89
|
name: "even",
|
|
81
90
|
code: "invalid_even",
|
|
82
91
|
message: "Value must be an even integer.",
|
|
92
|
+
guard: isNumber,
|
|
83
93
|
});
|
|
84
94
|
/**
|
|
85
95
|
* Requires a number to be odd.
|
|
@@ -88,5 +98,6 @@ export const odd = createConstraint((value) => Number.isInteger(value) && Math.a
|
|
|
88
98
|
name: "odd",
|
|
89
99
|
code: "invalid_odd",
|
|
90
100
|
message: "Value must be an odd integer.",
|
|
101
|
+
guard: isNumber,
|
|
91
102
|
});
|
|
92
103
|
//# sourceMappingURL=validationConstraints.number.js.map
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { ValidationConstraint } from "
|
|
1
|
+
import type { ValidationConstraint } from "../validationConstraints.base.js";
|
|
2
2
|
/**
|
|
3
3
|
* Requires a string to be non-empty after trimming.
|
|
4
4
|
*/
|
|
5
5
|
export declare const nonEmptyString: ValidationConstraint<string>;
|
|
6
6
|
/**
|
|
7
7
|
* Requires a string to contain at least the specified number of characters.
|
|
8
|
+
*
|
|
9
|
+
* Counts code points, so an emoji costs one character rather than two.
|
|
8
10
|
*/
|
|
9
11
|
export declare function minLength(minimum: number): ValidationConstraint<string>;
|
|
10
12
|
/**
|
|
@@ -17,14 +19,23 @@ export declare function maxLength(maximum: number): ValidationConstraint<string>
|
|
|
17
19
|
export declare function lengthBetween(minimum: number, maximum: number): ValidationConstraint<string>;
|
|
18
20
|
/**
|
|
19
21
|
* Requires a string to match a regular expression.
|
|
22
|
+
*
|
|
23
|
+
* The pattern is copied without the `g` and `y` flags. Those make `test()`
|
|
24
|
+
* stateful through `lastIndex`, so a shared pattern would alternate between
|
|
25
|
+
* accepting and rejecting the very same value.
|
|
20
26
|
*/
|
|
21
27
|
export declare function matches(pattern: RegExp, message?: string): ValidationConstraint<string>;
|
|
22
28
|
/**
|
|
23
29
|
* Requires a valid email-like format.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately structural, not a full RFC 5322 parser: it rejects the shapes
|
|
32
|
+
* that are certainly wrong and leaves deliverability to a verification step.
|
|
24
33
|
*/
|
|
25
34
|
export declare const email: ValidationConstraint<string>;
|
|
26
35
|
/**
|
|
27
36
|
* Requires a UUID-like format.
|
|
37
|
+
*
|
|
38
|
+
* Accepts versions 1 through 8, covering UUIDv7, plus the nil and max UUIDs.
|
|
28
39
|
*/
|
|
29
40
|
export declare const uuid: ValidationConstraint<string>;
|
|
30
41
|
/**
|
|
@@ -32,7 +43,11 @@ export declare const uuid: ValidationConstraint<string>;
|
|
|
32
43
|
*/
|
|
33
44
|
export declare const httpUrl: ValidationConstraint<string>;
|
|
34
45
|
/**
|
|
35
|
-
* Requires a string to contain only ASCII characters.
|
|
46
|
+
* Requires a string to contain only printable ASCII characters.
|
|
47
|
+
*
|
|
48
|
+
* Control characters are excluded. A value that passes this is safe to place
|
|
49
|
+
* in a header or a single log line, which is what callers assume of a check
|
|
50
|
+
* named "ascii".
|
|
36
51
|
*/
|
|
37
52
|
export declare const ascii: ValidationConstraint<string>;
|
|
38
53
|
/**
|
|
@@ -44,7 +59,7 @@ export declare const digits: ValidationConstraint<string>;
|
|
|
44
59
|
*/
|
|
45
60
|
export declare const letters: ValidationConstraint<string>;
|
|
46
61
|
/**
|
|
47
|
-
* Requires a
|
|
62
|
+
* Requires a lowercase, hyphen-separated slug.
|
|
48
63
|
*/
|
|
49
64
|
export declare const slug: ValidationConstraint<string>;
|
|
50
65
|
//# sourceMappingURL=validationConstraints.string.d.ts.map
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { createConstraint, assertNonNegativeInteger, } from "
|
|
1
|
+
import { createConstraint, assertNonNegativeInteger, } from "../validationConstraints.base.js";
|
|
2
|
+
/** Counts Unicode code points rather than UTF-16 code units. */
|
|
3
|
+
function characterLength(value) {
|
|
4
|
+
return [...value].length;
|
|
5
|
+
}
|
|
2
6
|
/**
|
|
3
7
|
* Requires a string to be non-empty after trimming.
|
|
4
8
|
*/
|
|
@@ -9,13 +13,16 @@ export const nonEmptyString = createConstraint((value) => typeof value === "stri
|
|
|
9
13
|
});
|
|
10
14
|
/**
|
|
11
15
|
* Requires a string to contain at least the specified number of characters.
|
|
16
|
+
*
|
|
17
|
+
* Counts code points, so an emoji costs one character rather than two.
|
|
12
18
|
*/
|
|
13
19
|
export function minLength(minimum) {
|
|
14
20
|
assertNonNegativeInteger(minimum, "minimum");
|
|
15
|
-
return createConstraint((value) => value
|
|
21
|
+
return createConstraint((value) => characterLength(value) >= minimum, {
|
|
16
22
|
name: `min_length_${minimum}`,
|
|
17
23
|
code: "min_length",
|
|
18
24
|
message: `Value must contain at least ${minimum} characters.`,
|
|
25
|
+
guard: (value) => typeof value === "string",
|
|
19
26
|
});
|
|
20
27
|
}
|
|
21
28
|
/**
|
|
@@ -23,10 +30,11 @@ export function minLength(minimum) {
|
|
|
23
30
|
*/
|
|
24
31
|
export function maxLength(maximum) {
|
|
25
32
|
assertNonNegativeInteger(maximum, "maximum");
|
|
26
|
-
return createConstraint((value) => value
|
|
33
|
+
return createConstraint((value) => characterLength(value) <= maximum, {
|
|
27
34
|
name: `max_length_${maximum}`,
|
|
28
35
|
code: "max_length",
|
|
29
36
|
message: `Value must contain at most ${maximum} characters.`,
|
|
37
|
+
guard: (value) => typeof value === "string",
|
|
30
38
|
});
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
@@ -38,37 +46,56 @@ export function lengthBetween(minimum, maximum) {
|
|
|
38
46
|
if (minimum > maximum) {
|
|
39
47
|
throw new RangeError("minimum cannot be greater than maximum.");
|
|
40
48
|
}
|
|
41
|
-
return createConstraint((value) =>
|
|
49
|
+
return createConstraint((value) => {
|
|
50
|
+
const length = characterLength(value);
|
|
51
|
+
return length >= minimum && length <= maximum;
|
|
52
|
+
}, {
|
|
42
53
|
name: `length_between_${minimum}_${maximum}`,
|
|
43
54
|
code: "length_between",
|
|
44
55
|
message: `Value must contain between ${minimum} and ${maximum} characters.`,
|
|
56
|
+
guard: (value) => typeof value === "string",
|
|
45
57
|
});
|
|
46
58
|
}
|
|
47
59
|
/**
|
|
48
60
|
* Requires a string to match a regular expression.
|
|
61
|
+
*
|
|
62
|
+
* The pattern is copied without the `g` and `y` flags. Those make `test()`
|
|
63
|
+
* stateful through `lastIndex`, so a shared pattern would alternate between
|
|
64
|
+
* accepting and rejecting the very same value.
|
|
49
65
|
*/
|
|
50
66
|
export function matches(pattern, message = "Value has an invalid format.") {
|
|
51
|
-
|
|
67
|
+
const stateless = new RegExp(pattern.source, pattern.flags.replace(/[gy]/gu, ""));
|
|
68
|
+
return createConstraint((value) => stateless.test(value), {
|
|
52
69
|
name: "matches",
|
|
53
70
|
code: "invalid_format",
|
|
54
71
|
message,
|
|
72
|
+
guard: (value) => typeof value === "string",
|
|
55
73
|
});
|
|
56
74
|
}
|
|
57
75
|
/**
|
|
58
76
|
* Requires a valid email-like format.
|
|
77
|
+
*
|
|
78
|
+
* Deliberately structural, not a full RFC 5322 parser: it rejects the shapes
|
|
79
|
+
* that are certainly wrong and leaves deliverability to a verification step.
|
|
59
80
|
*/
|
|
60
|
-
export const email = createConstraint((value) => /^[^\s
|
|
81
|
+
export const email = createConstraint((value) => /^[^\s@,;<>"[\]\\]+@[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/iu.test(value) && !value.includes(".."), {
|
|
61
82
|
name: "email",
|
|
62
83
|
code: "invalid_email",
|
|
63
84
|
message: "Value must be a valid email address.",
|
|
85
|
+
guard: (value) => typeof value === "string",
|
|
64
86
|
});
|
|
65
87
|
/**
|
|
66
88
|
* Requires a UUID-like format.
|
|
89
|
+
*
|
|
90
|
+
* Accepts versions 1 through 8, covering UUIDv7, plus the nil and max UUIDs.
|
|
67
91
|
*/
|
|
68
|
-
export const uuid = createConstraint((value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-
|
|
92
|
+
export const uuid = createConstraint((value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value) ||
|
|
93
|
+
value === "00000000-0000-0000-0000-000000000000" ||
|
|
94
|
+
value.toLowerCase() === "ffffffff-ffff-ffff-ffff-ffffffffffff", {
|
|
69
95
|
name: "uuid",
|
|
70
96
|
code: "invalid_uuid",
|
|
71
97
|
message: "Value must be a valid UUID.",
|
|
98
|
+
guard: (value) => typeof value === "string",
|
|
72
99
|
});
|
|
73
100
|
/**
|
|
74
101
|
* Requires an HTTP or HTTPS URL.
|
|
@@ -85,22 +112,29 @@ export const httpUrl = createConstraint((value) => {
|
|
|
85
112
|
name: "http_url",
|
|
86
113
|
code: "invalid_url",
|
|
87
114
|
message: "Value must be a valid HTTP or HTTPS URL.",
|
|
115
|
+
guard: (value) => typeof value === "string",
|
|
88
116
|
});
|
|
89
117
|
/**
|
|
90
|
-
* Requires a string to contain only ASCII characters.
|
|
118
|
+
* Requires a string to contain only printable ASCII characters.
|
|
119
|
+
*
|
|
120
|
+
* Control characters are excluded. A value that passes this is safe to place
|
|
121
|
+
* in a header or a single log line, which is what callers assume of a check
|
|
122
|
+
* named "ascii".
|
|
91
123
|
*/
|
|
92
|
-
export const ascii = createConstraint((value) => /^[\
|
|
124
|
+
export const ascii = createConstraint((value) => /^[\x20-\x7E]*$/u.test(value), {
|
|
93
125
|
name: "ascii",
|
|
94
126
|
code: "invalid_ascii",
|
|
95
|
-
message: "Value must contain only ASCII characters.",
|
|
127
|
+
message: "Value must contain only printable ASCII characters.",
|
|
128
|
+
guard: (value) => typeof value === "string",
|
|
96
129
|
});
|
|
97
130
|
/**
|
|
98
131
|
* Requires a string to contain only digits.
|
|
99
132
|
*/
|
|
100
|
-
export const digits = createConstraint((value) =>
|
|
133
|
+
export const digits = createConstraint((value) => /^[0-9]+$/u.test(value), {
|
|
101
134
|
name: "digits",
|
|
102
135
|
code: "invalid_digits",
|
|
103
136
|
message: "Value must contain only digits.",
|
|
137
|
+
guard: (value) => typeof value === "string",
|
|
104
138
|
});
|
|
105
139
|
/**
|
|
106
140
|
* Requires a string to contain only letters.
|
|
@@ -109,13 +143,15 @@ export const letters = createConstraint((value) => /^\p{L}+$/u.test(value), {
|
|
|
109
143
|
name: "letters",
|
|
110
144
|
code: "invalid_letters",
|
|
111
145
|
message: "Value must contain only letters.",
|
|
146
|
+
guard: (value) => typeof value === "string",
|
|
112
147
|
});
|
|
113
148
|
/**
|
|
114
|
-
* Requires a
|
|
149
|
+
* Requires a lowercase, hyphen-separated slug.
|
|
115
150
|
*/
|
|
116
151
|
export const slug = createConstraint((value) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(value), {
|
|
117
152
|
name: "slug",
|
|
118
153
|
code: "invalid_slug",
|
|
119
|
-
message: "Value must be a
|
|
154
|
+
message: "Value must be a slug: lowercase letters and digits, separated by single hyphens.",
|
|
155
|
+
guard: (value) => typeof value === "string",
|
|
120
156
|
});
|
|
121
157
|
//# sourceMappingURL=validationConstraints.string.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Structural guards for untrusted object graphs.
|
|
3
|
+
*
|
|
4
|
+
* @module validationConstraints/structure
|
|
5
|
+
*/
|
|
6
|
+
export * from "./validationConstraints.traverse.js";
|
|
7
|
+
export * from "./validationConstraints.depth.js";
|
|
8
|
+
export * from "./validationConstraints.size.js";
|
|
9
|
+
export * from "./validationConstraints.circular.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Structural guards for untrusted object graphs.
|
|
3
|
+
*
|
|
4
|
+
* @module validationConstraints/structure
|
|
5
|
+
*/
|
|
6
|
+
export * from "./validationConstraints.traverse.js";
|
|
7
|
+
export * from "./validationConstraints.depth.js";
|
|
8
|
+
export * from "./validationConstraints.size.js";
|
|
9
|
+
export * from "./validationConstraints.circular.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -7,14 +7,18 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Detect circular references in a value graph.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Only a value that references *itself* through the current path is a cycle.
|
|
11
|
+
* The same object appearing twice in sibling positions is an ordinary shared
|
|
12
|
+
* reference and is allowed: rejecting it turned away every valid payload that
|
|
13
|
+
* reused a config object or a lookup record.
|
|
12
14
|
*
|
|
13
15
|
* @param value - The value to check for circular references.
|
|
14
16
|
* @param path - Current traversal path for error reporting.
|
|
15
|
-
* @param
|
|
17
|
+
* @param maxDepth - Depth ceiling, so a deep graph cannot exhaust the stack
|
|
18
|
+
* before a cycle is reported.
|
|
19
|
+
* @throws {CircularReferenceError} on the first cycle found.
|
|
16
20
|
*/
|
|
17
|
-
export declare function assertNoCircularReference(value: unknown, path?: string,
|
|
21
|
+
export declare function assertNoCircularReference(value: unknown, path?: string, maxDepth?: number): void;
|
|
18
22
|
/**
|
|
19
23
|
* Check whether a value contains circular references.
|
|
20
24
|
*
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Circular reference detection.
|
|
3
|
+
*
|
|
4
|
+
* Detects circular references in object graphs before serialization
|
|
5
|
+
* or validation to prevent stack overflows and provide clear error messages.
|
|
6
|
+
*/
|
|
7
|
+
import { CircularReferenceError } from "@zudojs/errors";
|
|
8
|
+
import { MAX_MEASURABLE_DEPTH } from "./validationConstraints.depth.js";
|
|
9
|
+
import { TraversalLimitError, traverse, } from "./validationConstraints.traverse.js";
|
|
10
|
+
/**
|
|
11
|
+
* Detect circular references in a value graph.
|
|
12
|
+
*
|
|
13
|
+
* Only a value that references *itself* through the current path is a cycle.
|
|
14
|
+
* The same object appearing twice in sibling positions is an ordinary shared
|
|
15
|
+
* reference and is allowed: rejecting it turned away every valid payload that
|
|
16
|
+
* reused a config object or a lookup record.
|
|
17
|
+
*
|
|
18
|
+
* @param value - The value to check for circular references.
|
|
19
|
+
* @param path - Current traversal path for error reporting.
|
|
20
|
+
* @param maxDepth - Depth ceiling, so a deep graph cannot exhaust the stack
|
|
21
|
+
* before a cycle is reported.
|
|
22
|
+
* @throws {CircularReferenceError} on the first cycle found.
|
|
23
|
+
*/
|
|
24
|
+
export function assertNoCircularReference(value, path = "root", maxDepth = MAX_MEASURABLE_DEPTH) {
|
|
25
|
+
try {
|
|
26
|
+
traverse(value, { maxDepth, failOnCycle: true }, path);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error instanceof TraversalLimitError && error.halt === "cycle") {
|
|
30
|
+
throw new CircularReferenceError(error.path);
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof TraversalLimitError && error.halt === "depth") {
|
|
33
|
+
throw new CircularReferenceError(`${error.path} (exceeded ${maxDepth} levels)`);
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check whether a value contains circular references.
|
|
40
|
+
*
|
|
41
|
+
* Returns true if a cycle is found, false otherwise.
|
|
42
|
+
* Does not throw — use assertNoCircularReference for throwing behavior.
|
|
43
|
+
*/
|
|
44
|
+
export function hasCircularReference(value) {
|
|
45
|
+
try {
|
|
46
|
+
assertNoCircularReference(value);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof CircularReferenceError)
|
|
51
|
+
return true;
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=validationConstraints.circular.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Depth checking.
|
|
3
|
+
*
|
|
4
|
+
* Computes and validates the nesting depth of object graphs
|
|
5
|
+
* to prevent stack overflows from deeply nested payloads.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Hard ceiling on how deep {@link getSerializationDepth} will descend.
|
|
9
|
+
*
|
|
10
|
+
* The measurement itself is recursive, so it needs a bound of its own: a guard
|
|
11
|
+
* that overflows the stack on the input it was checking protects nothing.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MAX_MEASURABLE_DEPTH = 512;
|
|
14
|
+
/**
|
|
15
|
+
* Compute the maximum nesting depth of a value.
|
|
16
|
+
*
|
|
17
|
+
* Primitives return 0. Arrays and objects return 1 + the maximum
|
|
18
|
+
* depth of their children. Cycles are not followed.
|
|
19
|
+
*
|
|
20
|
+
* @param value - The value to measure.
|
|
21
|
+
* @param limit - Stop measuring beyond this depth. Defaults to
|
|
22
|
+
* {@link MAX_MEASURABLE_DEPTH}; the limit itself is returned when reached.
|
|
23
|
+
* @returns The observed depth, capped at `limit`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getSerializationDepth(value: unknown, limit?: number): number;
|
|
26
|
+
/**
|
|
27
|
+
* Assert that a value does not exceed the maximum allowed depth.
|
|
28
|
+
*
|
|
29
|
+
* Stops descending the moment the limit is passed, so the cost of the check is
|
|
30
|
+
* bounded by `maxDepth` rather than by the size of the input.
|
|
31
|
+
*
|
|
32
|
+
* @param value - The value to check.
|
|
33
|
+
* @param maxDepth - Maximum permitted nesting depth.
|
|
34
|
+
* @throws {SerializationDepthError} when depth exceeds the limit.
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertDepthWithinLimit(value: unknown, maxDepth: number): void;
|
|
37
|
+
//# sourceMappingURL=validationConstraints.depth.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Depth checking.
|
|
3
|
+
*
|
|
4
|
+
* Computes and validates the nesting depth of object graphs
|
|
5
|
+
* to prevent stack overflows from deeply nested payloads.
|
|
6
|
+
*/
|
|
7
|
+
import { SerializationDepthError } from "@zudojs/errors";
|
|
8
|
+
import { TraversalLimitError, traverse, } from "./validationConstraints.traverse.js";
|
|
9
|
+
/**
|
|
10
|
+
* Hard ceiling on how deep {@link getSerializationDepth} will descend.
|
|
11
|
+
*
|
|
12
|
+
* The measurement itself is recursive, so it needs a bound of its own: a guard
|
|
13
|
+
* that overflows the stack on the input it was checking protects nothing.
|
|
14
|
+
*/
|
|
15
|
+
export const MAX_MEASURABLE_DEPTH = 512;
|
|
16
|
+
/**
|
|
17
|
+
* Compute the maximum nesting depth of a value.
|
|
18
|
+
*
|
|
19
|
+
* Primitives return 0. Arrays and objects return 1 + the maximum
|
|
20
|
+
* depth of their children. Cycles are not followed.
|
|
21
|
+
*
|
|
22
|
+
* @param value - The value to measure.
|
|
23
|
+
* @param limit - Stop measuring beyond this depth. Defaults to
|
|
24
|
+
* {@link MAX_MEASURABLE_DEPTH}; the limit itself is returned when reached.
|
|
25
|
+
* @returns The observed depth, capped at `limit`.
|
|
26
|
+
*/
|
|
27
|
+
export function getSerializationDepth(value, limit = MAX_MEASURABLE_DEPTH) {
|
|
28
|
+
try {
|
|
29
|
+
return traverse(value, { maxDepth: limit }).depth;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error instanceof TraversalLimitError)
|
|
33
|
+
return limit;
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Assert that a value does not exceed the maximum allowed depth.
|
|
39
|
+
*
|
|
40
|
+
* Stops descending the moment the limit is passed, so the cost of the check is
|
|
41
|
+
* bounded by `maxDepth` rather than by the size of the input.
|
|
42
|
+
*
|
|
43
|
+
* @param value - The value to check.
|
|
44
|
+
* @param maxDepth - Maximum permitted nesting depth.
|
|
45
|
+
* @throws {SerializationDepthError} when depth exceeds the limit.
|
|
46
|
+
*/
|
|
47
|
+
export function assertDepthWithinLimit(value, maxDepth) {
|
|
48
|
+
try {
|
|
49
|
+
traverse(value, { maxDepth });
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (error instanceof TraversalLimitError && error.halt === "depth") {
|
|
53
|
+
throw new SerializationDepthError(error.observed, maxDepth);
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=validationConstraints.depth.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/validation — Size checking.
|
|
3
|
+
*
|
|
4
|
+
* Estimates serialized payload size to prevent memory exhaustion
|
|
5
|
+
* from excessively large payloads.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Estimate the byte size of a value as JSON without allocating a string.
|
|
9
|
+
*
|
|
10
|
+
* A value referenced from several places is charged once per occurrence, the
|
|
11
|
+
* way a serializer expands it. Counting it once let a compact payload built
|
|
12
|
+
* from shared subtrees estimate at a few hundred bytes while serializing to
|
|
13
|
+
* hundreds of megabytes.
|
|
14
|
+
*
|
|
15
|
+
* @param value - The value to measure.
|
|
16
|
+
* @param maxBytes - Stop counting past this budget; the budget is returned.
|
|
17
|
+
* @returns The estimated serialized size in bytes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function estimateSerializedSize(value: unknown, maxBytes?: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Assert that a value's estimated serialized size is within limits.
|
|
22
|
+
*
|
|
23
|
+
* Aborts as soon as the budget is passed, so an oversized payload is rejected
|
|
24
|
+
* without first being measured in full.
|
|
25
|
+
*
|
|
26
|
+
* @param value - The value to check.
|
|
27
|
+
* @param maxSize - Maximum permitted size in bytes.
|
|
28
|
+
* @throws {SerializationPayloadTooLargeError} when the estimate exceeds maxSize.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assertSizeWithinLimit(value: unknown, maxSize: number): void;
|
|
31
|
+
//# sourceMappingURL=validationConstraints.size.d.ts.map
|