@atproto/lex-schema 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/dist/core/$type.d.ts +6 -3
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +1 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +3 -3
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js +12 -6
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +6 -0
- package/dist/core/result.js.map +1 -1
- package/dist/core/string-format.d.ts +30 -27
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +56 -42
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/types.d.ts +9 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/external.d.ts +31 -28
- package/dist/external.d.ts.map +1 -1
- package/dist/external.js +33 -17
- package/dist/external.js.map +1 -1
- package/dist/schema/_parameters.d.ts +2 -2
- package/dist/schema/_parameters.d.ts.map +1 -1
- package/dist/schema/array.d.ts +5 -6
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +5 -6
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +2 -3
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +1 -2
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +4 -5
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +2 -3
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +3 -4
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +2 -3
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +13 -6
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +2 -4
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +3 -4
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +4 -3
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +3 -3
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +1 -1
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +15 -24
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +40 -64
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +8 -4
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +5 -3
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +3 -4
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +3 -4
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +22 -14
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +12 -22
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +7 -3
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +5 -3
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +2 -2
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +1 -1
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +2 -3
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +1 -2
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +7 -0
- package/dist/schema/nullable.d.ts.map +1 -0
- package/dist/schema/nullable.js +19 -0
- package/dist/schema/nullable.js.map +1 -0
- package/dist/schema/object.d.ts +10 -44
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +10 -46
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +7 -0
- package/dist/schema/optional.d.ts.map +1 -0
- package/dist/schema/optional.js +25 -0
- package/dist/schema/optional.js.map +1 -0
- package/dist/schema/params.d.ts +14 -19
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +10 -24
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +4 -4
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +6 -6
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +1 -2
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +0 -1
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +0 -1
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +8 -9
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +0 -1
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +7 -8
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +0 -1
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +34 -28
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +1 -2
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +2 -3
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +1 -2
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +18 -0
- package/dist/schema/refine.d.ts.map +1 -0
- package/dist/schema/refine.js +33 -0
- package/dist/schema/refine.js.map +1 -0
- package/dist/schema/regexp.d.ts +7 -0
- package/dist/schema/regexp.d.ts.map +1 -0
- package/dist/schema/regexp.js +22 -0
- package/dist/schema/regexp.js.map +1 -0
- package/dist/schema/string.d.ts +4 -8
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +6 -3
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +7 -6
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +2 -3
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +1 -2
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +29 -27
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +1 -2
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +2 -2
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +1 -1
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +3 -4
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +3 -10
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +2 -2
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +1 -1
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown-object.d.ts +2 -3
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +1 -2
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +2 -2
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +1 -1
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema.d.ts +4 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -1
- package/dist/schema.js.map +1 -1
- package/dist/util/array-agg.d.ts.map +1 -1
- package/dist/util/array-agg.js +1 -0
- package/dist/util/array-agg.js.map +1 -1
- package/dist/util/lazy-property.d.ts +2 -0
- package/dist/util/lazy-property.d.ts.map +1 -0
- package/dist/util/lazy-property.js +14 -0
- package/dist/util/lazy-property.js.map +1 -0
- package/dist/validation/schema.d.ts +24 -0
- package/dist/validation/schema.d.ts.map +1 -0
- package/dist/validation/schema.js +57 -0
- package/dist/validation/schema.js.map +1 -0
- package/dist/validation/validation-error.d.ts +3 -3
- package/dist/validation/validation-error.d.ts.map +1 -1
- package/dist/validation/validation-error.js +32 -4
- package/dist/validation/validation-error.js.map +1 -1
- package/dist/validation/validation-issue.d.ts +32 -24
- package/dist/validation/validation-issue.d.ts.map +1 -1
- package/dist/validation/validation-issue.js +136 -92
- package/dist/validation/validation-issue.js.map +1 -1
- package/dist/validation/validator.d.ts +20 -50
- package/dist/validation/validator.d.ts.map +1 -1
- package/dist/validation/validator.js +40 -134
- package/dist/validation/validator.js.map +1 -1
- package/dist/validation.d.ts +1 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -0
- package/dist/validation.js.map +1 -1
- package/package.json +8 -4
- package/src/core/$type.ts +7 -4
- package/src/core/record-key.ts +12 -5
- package/src/core/result.ts +6 -0
- package/src/core/string-format.ts +97 -61
- package/src/core/types.ts +12 -6
- package/src/external.ts +92 -70
- package/src/schema/_parameters.test.ts +416 -0
- package/src/schema/array.test.ts +237 -0
- package/src/schema/array.ts +17 -11
- package/src/schema/blob.test.ts +506 -0
- package/src/schema/blob.ts +3 -5
- package/src/schema/boolean.test.ts +116 -0
- package/src/schema/boolean.ts +5 -7
- package/src/schema/bytes.test.ts +226 -0
- package/src/schema/bytes.ts +4 -6
- package/src/schema/cid.test.ts +155 -0
- package/src/schema/cid.ts +14 -8
- package/src/schema/custom.test.ts +413 -0
- package/src/schema/custom.ts +10 -8
- package/src/schema/dict.test.ts +198 -0
- package/src/schema/dict.ts +6 -8
- package/src/schema/discriminated-union.test.ts +675 -0
- package/src/schema/discriminated-union.ts +68 -95
- package/src/schema/enum.test.ts +396 -0
- package/src/schema/enum.ts +12 -5
- package/src/schema/integer.test.ts +312 -0
- package/src/schema/integer.ts +5 -7
- package/src/schema/intersection.test.ts +32 -0
- package/src/schema/intersection.ts +37 -40
- package/src/schema/literal.test.ts +531 -0
- package/src/schema/literal.ts +12 -5
- package/src/schema/never.test.ts +174 -0
- package/src/schema/never.ts +3 -10
- package/src/schema/null.test.ts +79 -0
- package/src/schema/null.ts +3 -5
- package/src/schema/nullable.test.ts +480 -0
- package/src/schema/nullable.ts +23 -0
- package/src/schema/object.test.ts +47 -115
- package/src/schema/object.ts +19 -123
- package/src/schema/optional.test.ts +485 -0
- package/src/schema/optional.ts +31 -0
- package/src/schema/params.test.ts +582 -0
- package/src/schema/params.ts +37 -55
- package/src/schema/payload.test.ts +345 -0
- package/src/schema/payload.ts +5 -5
- package/src/schema/permission-set.test.ts +679 -0
- package/src/schema/permission-set.ts +6 -8
- package/src/schema/permission.test.ts +536 -0
- package/src/schema/permission.ts +0 -2
- package/src/schema/procedure.test.ts +443 -0
- package/src/schema/procedure.ts +11 -13
- package/src/schema/query.test.ts +408 -0
- package/src/schema/query.ts +9 -11
- package/src/schema/record.test.ts +694 -0
- package/src/schema/record.ts +38 -36
- package/src/schema/ref.test.ts +365 -0
- package/src/schema/ref.ts +8 -5
- package/src/schema/refine.test.ts +578 -0
- package/src/schema/refine.ts +85 -0
- package/src/schema/regexp.test.ts +580 -0
- package/src/schema/regexp.ts +22 -0
- package/src/schema/string.test.ts +612 -0
- package/src/schema/string.ts +11 -17
- package/src/schema/subscription.test.ts +689 -0
- package/src/schema/subscription.ts +13 -8
- package/src/schema/token.test.ts +428 -0
- package/src/schema/token.ts +3 -5
- package/src/schema/typed-object.test.ts +612 -0
- package/src/schema/typed-object.ts +23 -20
- package/src/schema/typed-ref.test.ts +823 -0
- package/src/schema/typed-ref.ts +10 -5
- package/src/schema/typed-union.test.ts +378 -0
- package/src/schema/typed-union.ts +6 -15
- package/src/schema/union.test.ts +200 -0
- package/src/schema/union.ts +5 -4
- package/src/schema/unknown-object.test.ts +592 -0
- package/src/schema/unknown-object.ts +3 -5
- package/src/schema/unknown.test.ts +312 -0
- package/src/schema/unknown.ts +3 -3
- package/src/schema.ts +7 -1
- package/src/util/array-agg.ts +1 -0
- package/src/util/lazy-property.ts +14 -0
- package/src/validation/schema.ts +92 -0
- package/src/validation/validation-error.ts +60 -9
- package/src/validation/validation-issue.ts +141 -144
- package/src/validation/validator.ts +67 -206
- package/src/validation.ts +1 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +7 -0
- package/tsconfig.tests.json +9 -0
package/src/schema/object.ts
CHANGED
|
@@ -1,134 +1,51 @@
|
|
|
1
1
|
import { isPlainObject } from '@atproto/lex-data'
|
|
2
|
-
import {
|
|
2
|
+
import { WithOptionalProperties } from '../core.js'
|
|
3
|
+
import { lazyProperty } from '../util/lazy-property.js'
|
|
3
4
|
import {
|
|
4
5
|
Infer,
|
|
6
|
+
Schema,
|
|
5
7
|
ValidationResult,
|
|
6
8
|
Validator,
|
|
7
9
|
ValidatorContext,
|
|
8
10
|
} from '../validation.js'
|
|
9
|
-
import { DictSchema } from './dict.js'
|
|
10
11
|
|
|
11
|
-
export type
|
|
12
|
-
export type ObjectSchemaOptions = {
|
|
13
|
-
required?: readonly string[]
|
|
14
|
-
nullable?: readonly string[]
|
|
15
|
-
unknownProperties?: 'strict' | DictSchema
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type ObjectSchemaNullValue<
|
|
19
|
-
O extends ObjectSchemaOptions,
|
|
20
|
-
K extends string,
|
|
21
|
-
> = O extends { nullable: readonly (infer N extends string)[] }
|
|
22
|
-
? K extends N
|
|
23
|
-
? null
|
|
24
|
-
: never
|
|
25
|
-
: never
|
|
26
|
-
|
|
27
|
-
export type ObjectSchemaPropertiesOutput<
|
|
28
|
-
P extends ObjectSchemaProperties,
|
|
29
|
-
O extends ObjectSchemaOptions,
|
|
30
|
-
> = O extends { required: readonly (infer R extends string)[] }
|
|
31
|
-
? {
|
|
32
|
-
-readonly [K in string & keyof P & R]-?:
|
|
33
|
-
| Infer<P[K]>
|
|
34
|
-
| ObjectSchemaNullValue<O, K>
|
|
35
|
-
} & {
|
|
36
|
-
-readonly [K in Exclude<string & keyof P, R>]?:
|
|
37
|
-
| Infer<P[K]>
|
|
38
|
-
| ObjectSchemaNullValue<O, K>
|
|
39
|
-
}
|
|
40
|
-
: {
|
|
41
|
-
-readonly [K in string & keyof P]?:
|
|
42
|
-
| Infer<P[K]>
|
|
43
|
-
| ObjectSchemaNullValue<O, K>
|
|
44
|
-
}
|
|
12
|
+
export type ObjectSchemaShape = Record<string, Validator>
|
|
45
13
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*
|
|
51
|
-
* @see {@link https://www.typescriptlang.org/play/?#code/C4TwDgpgBAglC8UDeUBmB7dAuKByARgIYBOuUAvlAGTJQDaA+lAJYB2UAzsMWwOYC6OVgFcAtvgjEKAKGkATCAGMANiWiL0rLlEI4YsjVuBQA1hBA4uPVrwRQARBnT2Dm7QDdCy4dESE6ZiD8UAD0IVAi4pJQABQcABbowspyUBIORMT2AJSyEAAeYOjExqCQUACSrMCSHErAzJoAPNJQsFAFNaxyHFAASkrFck1WfAA0UMKsJqzoAO6sAHxjrVAAQh35XT39g8TDozYTUzPzSyuLdqtwVKttMYHoqO00j88bnRDdvawQ7pJ3NpQAD860BbRwSHBQLadAA0ix2G91oJ1vDggAfWABcxPF5QOH8aFtci5aRlaAwVDMfIQVKIKo1Yh1RQNZq0Jw4AgkMjkCYoRiIzjcPioyISKTkRayBQqNRQQzaQgAMRpdL01NpclcRignm8EFVWrsKrVchxQVC4XF0SxmSAA Playground link}
|
|
52
|
-
*/
|
|
53
|
-
type Intersect<
|
|
54
|
-
A extends Record<string, unknown>,
|
|
55
|
-
B extends Record<string, unknown>,
|
|
56
|
-
> = B[keyof B] extends never
|
|
57
|
-
? A
|
|
58
|
-
: keyof A & keyof B extends never
|
|
59
|
-
? // If A and B don't overlap, just return A & B
|
|
60
|
-
A & B
|
|
61
|
-
: // Otherwise, properly represent the fact that accessing using an
|
|
62
|
-
// index signature could return a value from either A or B
|
|
63
|
-
A & { [K in keyof B]: B[K] | A[keyof A & K] }
|
|
64
|
-
|
|
65
|
-
export type ObjectSchemaOutput<
|
|
66
|
-
P extends ObjectSchemaProperties,
|
|
67
|
-
O extends ObjectSchemaOptions,
|
|
68
|
-
> = O extends {
|
|
69
|
-
unknownProperties: Validator<infer D extends Record<string, unknown>>
|
|
70
|
-
}
|
|
71
|
-
? Simplify<Intersect<ObjectSchemaPropertiesOutput<P, O>, D>>
|
|
72
|
-
: Simplify<ObjectSchemaPropertiesOutput<P, O>>
|
|
14
|
+
export type ObjectSchemaOutput<Shape extends ObjectSchemaShape> =
|
|
15
|
+
WithOptionalProperties<{
|
|
16
|
+
[K in keyof Shape]: Infer<Shape[K]>
|
|
17
|
+
}>
|
|
73
18
|
|
|
74
19
|
export class ObjectSchema<
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
Validators,
|
|
79
|
-
Options
|
|
80
|
-
> = ObjectSchemaOutput<Validators, Options>,
|
|
81
|
-
> extends Validator<Output> {
|
|
82
|
-
constructor(
|
|
83
|
-
readonly validators: Validators,
|
|
84
|
-
readonly options: Options,
|
|
85
|
-
) {
|
|
20
|
+
const Shape extends ObjectSchemaShape = any,
|
|
21
|
+
> extends Schema<ObjectSchemaOutput<Shape>> {
|
|
22
|
+
constructor(readonly shape: Shape) {
|
|
86
23
|
super()
|
|
87
24
|
}
|
|
88
25
|
|
|
89
26
|
get validatorsMap(): Map<string, Validator> {
|
|
90
|
-
const map = new Map(Object.entries(this.
|
|
91
|
-
|
|
92
|
-
// Cache the map on the instance (to avoid re-creating it)
|
|
93
|
-
Object.defineProperty(this, 'validatorsMap', {
|
|
94
|
-
value: map,
|
|
95
|
-
writable: false,
|
|
96
|
-
enumerable: false,
|
|
97
|
-
configurable: true,
|
|
98
|
-
})
|
|
27
|
+
const map = new Map(Object.entries(this.shape))
|
|
99
28
|
|
|
100
|
-
return map
|
|
29
|
+
return lazyProperty(this, 'validatorsMap', map)
|
|
101
30
|
}
|
|
102
31
|
|
|
103
|
-
|
|
32
|
+
validateInContext(
|
|
104
33
|
input: unknown,
|
|
105
34
|
ctx: ValidatorContext,
|
|
106
|
-
): ValidationResult<
|
|
35
|
+
): ValidationResult<ObjectSchemaOutput<Shape>> {
|
|
107
36
|
if (!isPlainObject(input)) {
|
|
108
|
-
return ctx.issueInvalidType(input,
|
|
37
|
+
return ctx.issueInvalidType(input, 'object')
|
|
109
38
|
}
|
|
110
39
|
|
|
111
40
|
// Lazily copy value
|
|
112
41
|
let copy: undefined | Record<string, unknown>
|
|
113
42
|
|
|
114
43
|
for (const [key, propDef] of this.validatorsMap) {
|
|
115
|
-
if (input[key] === null && this.options.nullable?.includes(key)) {
|
|
116
|
-
continue
|
|
117
|
-
}
|
|
118
|
-
|
|
119
44
|
const result = ctx.validateChild(input, key, propDef)
|
|
120
45
|
if (!result.success) {
|
|
121
|
-
// Because default values are provided by child validators, we need to
|
|
122
|
-
// run the validator to get the default value and, in case of failure,
|
|
123
|
-
// ignore validation error that were caused by missing keys.
|
|
124
46
|
if (!(key in input)) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return ctx.issueRequiredKey(input, key)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Ignore missing non-required key
|
|
131
|
-
continue
|
|
47
|
+
// Transform into "required key" issue
|
|
48
|
+
return ctx.issueRequiredKey(input, key)
|
|
132
49
|
}
|
|
133
50
|
|
|
134
51
|
return result
|
|
@@ -136,11 +53,6 @@ export class ObjectSchema<
|
|
|
136
53
|
|
|
137
54
|
// Skip copying if key is not present in input (and value is undefined)
|
|
138
55
|
if (result.value === undefined && !(key in input)) {
|
|
139
|
-
// Except if the key is required
|
|
140
|
-
if (this.options.required?.includes(key)) {
|
|
141
|
-
return ctx.issueRequiredKey(input, key)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
56
|
continue
|
|
145
57
|
}
|
|
146
58
|
|
|
@@ -150,23 +62,7 @@ export class ObjectSchema<
|
|
|
150
62
|
}
|
|
151
63
|
}
|
|
152
64
|
|
|
153
|
-
|
|
154
|
-
for (const key of Object.keys(input)) {
|
|
155
|
-
if (!this.validatorsMap.has(key)) {
|
|
156
|
-
return ctx.issueInvalidPropertyType(input, key, 'undefined')
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
} else if (this.options.unknownProperties) {
|
|
160
|
-
const result = this.options.unknownProperties.validateInContext(
|
|
161
|
-
copy ?? input,
|
|
162
|
-
ctx,
|
|
163
|
-
{ ignoredKeys: this.validatorsMap },
|
|
164
|
-
)
|
|
165
|
-
if (!result.success) return result
|
|
166
|
-
if (result.value !== input) copy = result.value
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const output = (copy ?? input) as Output
|
|
65
|
+
const output = (copy ?? input) as ObjectSchemaOutput<Shape>
|
|
170
66
|
|
|
171
67
|
return ctx.success(output)
|
|
172
68
|
}
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
import { BooleanSchema } from './boolean.js'
|
|
2
|
+
import { IntegerSchema } from './integer.js'
|
|
3
|
+
import { OptionalSchema } from './optional.js'
|
|
4
|
+
import { StringSchema } from './string.js'
|
|
5
|
+
|
|
6
|
+
describe('OptionalSchema', () => {
|
|
7
|
+
describe('basic validation with string schema', () => {
|
|
8
|
+
const schema = new OptionalSchema(new StringSchema({}))
|
|
9
|
+
|
|
10
|
+
it('validates defined string values', () => {
|
|
11
|
+
const result = schema.safeParse('hello')
|
|
12
|
+
expect(result.success).toBe(true)
|
|
13
|
+
if (result.success) {
|
|
14
|
+
expect(result.value).toBe('hello')
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('validates empty strings', () => {
|
|
19
|
+
const result = schema.safeParse('')
|
|
20
|
+
expect(result.success).toBe(true)
|
|
21
|
+
if (result.success) {
|
|
22
|
+
expect(result.value).toBe('')
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('validates undefined', () => {
|
|
27
|
+
const result = schema.safeParse(undefined)
|
|
28
|
+
expect(result.success).toBe(true)
|
|
29
|
+
if (result.success) {
|
|
30
|
+
expect(result.value).toBe(undefined)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('rejects invalid types for the inner schema', () => {
|
|
35
|
+
const result = schema.safeParse(123)
|
|
36
|
+
expect(result.success).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('rejects null', () => {
|
|
40
|
+
const result = schema.safeParse(null)
|
|
41
|
+
expect(result.success).toBe(false)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('rejects booleans', () => {
|
|
45
|
+
const result = schema.safeParse(true)
|
|
46
|
+
expect(result.success).toBe(false)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('rejects objects', () => {
|
|
50
|
+
const result = schema.safeParse({ value: 'hello' })
|
|
51
|
+
expect(result.success).toBe(false)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('rejects arrays', () => {
|
|
55
|
+
const result = schema.safeParse(['hello'])
|
|
56
|
+
expect(result.success).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('basic validation with integer schema', () => {
|
|
61
|
+
const schema = new OptionalSchema(new IntegerSchema({}))
|
|
62
|
+
|
|
63
|
+
it('validates defined integer values', () => {
|
|
64
|
+
const result = schema.safeParse(42)
|
|
65
|
+
expect(result.success).toBe(true)
|
|
66
|
+
if (result.success) {
|
|
67
|
+
expect(result.value).toBe(42)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('validates zero', () => {
|
|
72
|
+
const result = schema.safeParse(0)
|
|
73
|
+
expect(result.success).toBe(true)
|
|
74
|
+
if (result.success) {
|
|
75
|
+
expect(result.value).toBe(0)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('validates negative integers', () => {
|
|
80
|
+
const result = schema.safeParse(-42)
|
|
81
|
+
expect(result.success).toBe(true)
|
|
82
|
+
if (result.success) {
|
|
83
|
+
expect(result.value).toBe(-42)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('validates undefined', () => {
|
|
88
|
+
const result = schema.safeParse(undefined)
|
|
89
|
+
expect(result.success).toBe(true)
|
|
90
|
+
if (result.success) {
|
|
91
|
+
expect(result.value).toBe(undefined)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('rejects invalid types for the inner schema', () => {
|
|
96
|
+
const result = schema.safeParse('not a number')
|
|
97
|
+
expect(result.success).toBe(false)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('rejects floats', () => {
|
|
101
|
+
const result = schema.safeParse(3.14)
|
|
102
|
+
expect(result.success).toBe(false)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('rejects null', () => {
|
|
106
|
+
const result = schema.safeParse(null)
|
|
107
|
+
expect(result.success).toBe(false)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('basic validation with boolean schema', () => {
|
|
112
|
+
const schema = new OptionalSchema(new BooleanSchema({}))
|
|
113
|
+
|
|
114
|
+
it('validates true', () => {
|
|
115
|
+
const result = schema.safeParse(true)
|
|
116
|
+
expect(result.success).toBe(true)
|
|
117
|
+
if (result.success) {
|
|
118
|
+
expect(result.value).toBe(true)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('validates false', () => {
|
|
123
|
+
const result = schema.safeParse(false)
|
|
124
|
+
expect(result.success).toBe(true)
|
|
125
|
+
if (result.success) {
|
|
126
|
+
expect(result.value).toBe(false)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('validates undefined', () => {
|
|
131
|
+
const result = schema.safeParse(undefined)
|
|
132
|
+
expect(result.success).toBe(true)
|
|
133
|
+
if (result.success) {
|
|
134
|
+
expect(result.value).toBe(undefined)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('rejects strings', () => {
|
|
139
|
+
const result = schema.safeParse('true')
|
|
140
|
+
expect(result.success).toBe(false)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('rejects numbers', () => {
|
|
144
|
+
const result = schema.safeParse(1)
|
|
145
|
+
expect(result.success).toBe(false)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('rejects null', () => {
|
|
149
|
+
const result = schema.safeParse(null)
|
|
150
|
+
expect(result.success).toBe(false)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('inner schema with constraints', () => {
|
|
155
|
+
const schema = new OptionalSchema(
|
|
156
|
+
new StringSchema({ minLength: 5, maxLength: 10 }),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
it('validates values meeting inner schema constraints', () => {
|
|
160
|
+
const result = schema.safeParse('hello')
|
|
161
|
+
expect(result.success).toBe(true)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('validates values at minimum boundary', () => {
|
|
165
|
+
const result = schema.safeParse('abcde')
|
|
166
|
+
expect(result.success).toBe(true)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('validates values at maximum boundary', () => {
|
|
170
|
+
const result = schema.safeParse('1234567890')
|
|
171
|
+
expect(result.success).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('validates undefined', () => {
|
|
175
|
+
const result = schema.safeParse(undefined)
|
|
176
|
+
expect(result.success).toBe(true)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('rejects values violating inner schema minimum constraint', () => {
|
|
180
|
+
const result = schema.safeParse('hi')
|
|
181
|
+
expect(result.success).toBe(false)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('rejects values violating inner schema maximum constraint', () => {
|
|
185
|
+
const result = schema.safeParse('this is too long')
|
|
186
|
+
expect(result.success).toBe(false)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('rejects empty strings when inner schema has minLength', () => {
|
|
190
|
+
const result = schema.safeParse('')
|
|
191
|
+
expect(result.success).toBe(false)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
describe('inner schema with default value', () => {
|
|
196
|
+
const schema = new OptionalSchema(new StringSchema({ default: 'default' }))
|
|
197
|
+
|
|
198
|
+
it('applies default value when undefined is provided', () => {
|
|
199
|
+
const result = schema.safeParse(undefined)
|
|
200
|
+
expect(result.success).toBe(true)
|
|
201
|
+
if (result.success) {
|
|
202
|
+
expect(result.value).toBe('default')
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('does not apply default when explicit value is provided', () => {
|
|
207
|
+
const result = schema.safeParse('explicit')
|
|
208
|
+
expect(result.success).toBe(true)
|
|
209
|
+
if (result.success) {
|
|
210
|
+
expect(result.value).toBe('explicit')
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('does not apply default when empty string is provided', () => {
|
|
215
|
+
const result = schema.safeParse('')
|
|
216
|
+
expect(result.success).toBe(true)
|
|
217
|
+
if (result.success) {
|
|
218
|
+
expect(result.value).toBe('')
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe('inner schema with default value and constraints', () => {
|
|
224
|
+
const schema = new OptionalSchema(
|
|
225
|
+
new StringSchema({ default: 'default', minLength: 5 }),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
it('applies default value when undefined is provided', () => {
|
|
229
|
+
const result = schema.safeParse(undefined)
|
|
230
|
+
expect(result.success).toBe(true)
|
|
231
|
+
if (result.success) {
|
|
232
|
+
expect(result.value).toBe('default')
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('validates explicit values against constraints', () => {
|
|
237
|
+
const result = schema.safeParse('hello')
|
|
238
|
+
expect(result.success).toBe(true)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('rejects explicit values violating constraints', () => {
|
|
242
|
+
const result = schema.safeParse('hi')
|
|
243
|
+
expect(result.success).toBe(false)
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
describe('inner schema with invalid default value', () => {
|
|
248
|
+
const schema = new OptionalSchema(
|
|
249
|
+
new StringSchema({ default: 'bad', minLength: 5 }),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
it('returns undefined when default value violates constraints', () => {
|
|
253
|
+
const result = schema.safeParse(undefined)
|
|
254
|
+
expect(result.success).toBe(true)
|
|
255
|
+
if (result.success) {
|
|
256
|
+
expect(result.value).toBe(undefined)
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
it('still validates conforming explicit values', () => {
|
|
261
|
+
const result = schema.safeParse('valid')
|
|
262
|
+
expect(result.success).toBe(true)
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('inner schema with integer default', () => {
|
|
267
|
+
const schema = new OptionalSchema(new IntegerSchema({ default: 42 }))
|
|
268
|
+
|
|
269
|
+
it('applies default value when undefined is provided', () => {
|
|
270
|
+
const result = schema.safeParse(undefined)
|
|
271
|
+
expect(result.success).toBe(true)
|
|
272
|
+
if (result.success) {
|
|
273
|
+
expect(result.value).toBe(42)
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('does not apply default when explicit value is provided', () => {
|
|
278
|
+
const result = schema.safeParse(100)
|
|
279
|
+
expect(result.success).toBe(true)
|
|
280
|
+
if (result.success) {
|
|
281
|
+
expect(result.value).toBe(100)
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('does not apply default when zero is provided', () => {
|
|
286
|
+
const result = schema.safeParse(0)
|
|
287
|
+
expect(result.success).toBe(true)
|
|
288
|
+
if (result.success) {
|
|
289
|
+
expect(result.value).toBe(0)
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe('inner schema with boolean default', () => {
|
|
295
|
+
const schema = new OptionalSchema(new BooleanSchema({ default: true }))
|
|
296
|
+
|
|
297
|
+
it('applies default value when undefined is provided', () => {
|
|
298
|
+
const result = schema.safeParse(undefined)
|
|
299
|
+
expect(result.success).toBe(true)
|
|
300
|
+
if (result.success) {
|
|
301
|
+
expect(result.value).toBe(true)
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('does not apply default when explicit true is provided', () => {
|
|
306
|
+
const result = schema.safeParse(true)
|
|
307
|
+
expect(result.success).toBe(true)
|
|
308
|
+
if (result.success) {
|
|
309
|
+
expect(result.value).toBe(true)
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('does not apply default when explicit false is provided', () => {
|
|
314
|
+
const result = schema.safeParse(false)
|
|
315
|
+
expect(result.success).toBe(true)
|
|
316
|
+
if (result.success) {
|
|
317
|
+
expect(result.value).toBe(false)
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
describe('edge cases', () => {
|
|
323
|
+
const schema = new OptionalSchema(new StringSchema({}))
|
|
324
|
+
|
|
325
|
+
it('handles very long strings', () => {
|
|
326
|
+
const longString = 'a'.repeat(10000)
|
|
327
|
+
const result = schema.safeParse(longString)
|
|
328
|
+
expect(result.success).toBe(true)
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it('handles strings with special characters', () => {
|
|
332
|
+
const result = schema.safeParse('hello\nworld\ttab')
|
|
333
|
+
expect(result.success).toBe(true)
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
it('handles strings with unicode characters', () => {
|
|
337
|
+
const result = schema.safeParse('Hello 世界 🌍')
|
|
338
|
+
expect(result.success).toBe(true)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('handles empty string distinctly from undefined', () => {
|
|
342
|
+
const emptyResult = schema.safeParse('')
|
|
343
|
+
expect(emptyResult.success).toBe(true)
|
|
344
|
+
if (emptyResult.success) {
|
|
345
|
+
expect(emptyResult.value).toBe('')
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const undefinedResult = schema.safeParse(undefined)
|
|
349
|
+
expect(undefinedResult.success).toBe(true)
|
|
350
|
+
if (undefinedResult.success) {
|
|
351
|
+
expect(undefinedResult.value).toBe(undefined)
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
describe('type distinctions', () => {
|
|
357
|
+
it('distinguishes between zero and undefined for integers', () => {
|
|
358
|
+
const schema = new OptionalSchema(new IntegerSchema({}))
|
|
359
|
+
|
|
360
|
+
const zeroResult = schema.safeParse(0)
|
|
361
|
+
expect(zeroResult.success).toBe(true)
|
|
362
|
+
if (zeroResult.success) {
|
|
363
|
+
expect(zeroResult.value).toBe(0)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const undefinedResult = schema.safeParse(undefined)
|
|
367
|
+
expect(undefinedResult.success).toBe(true)
|
|
368
|
+
if (undefinedResult.success) {
|
|
369
|
+
expect(undefinedResult.value).toBe(undefined)
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
it('distinguishes between false and undefined for booleans', () => {
|
|
374
|
+
const schema = new OptionalSchema(new BooleanSchema({}))
|
|
375
|
+
|
|
376
|
+
const falseResult = schema.safeParse(false)
|
|
377
|
+
expect(falseResult.success).toBe(true)
|
|
378
|
+
if (falseResult.success) {
|
|
379
|
+
expect(falseResult.value).toBe(false)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const undefinedResult = schema.safeParse(undefined)
|
|
383
|
+
expect(undefinedResult.success).toBe(true)
|
|
384
|
+
if (undefinedResult.success) {
|
|
385
|
+
expect(undefinedResult.value).toBe(undefined)
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('distinguishes between empty string and undefined for strings', () => {
|
|
390
|
+
const schema = new OptionalSchema(new StringSchema({}))
|
|
391
|
+
|
|
392
|
+
const emptyResult = schema.safeParse('')
|
|
393
|
+
expect(emptyResult.success).toBe(true)
|
|
394
|
+
if (emptyResult.success) {
|
|
395
|
+
expect(emptyResult.value).toBe('')
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const undefinedResult = schema.safeParse(undefined)
|
|
399
|
+
expect(undefinedResult.success).toBe(true)
|
|
400
|
+
if (undefinedResult.success) {
|
|
401
|
+
expect(undefinedResult.value).toBe(undefined)
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
describe('nested optional schemas', () => {
|
|
407
|
+
const schema = new OptionalSchema(new OptionalSchema(new StringSchema({})))
|
|
408
|
+
|
|
409
|
+
it('validates defined values through nested optionals', () => {
|
|
410
|
+
const result = schema.safeParse('hello')
|
|
411
|
+
expect(result.success).toBe(true)
|
|
412
|
+
if (result.success) {
|
|
413
|
+
expect(result.value).toBe('hello')
|
|
414
|
+
}
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it('validates undefined through nested optionals', () => {
|
|
418
|
+
const result = schema.safeParse(undefined)
|
|
419
|
+
expect(result.success).toBe(true)
|
|
420
|
+
if (result.success) {
|
|
421
|
+
expect(result.value).toBe(undefined)
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('rejects invalid types through nested optionals', () => {
|
|
426
|
+
const result = schema.safeParse(123)
|
|
427
|
+
expect(result.success).toBe(false)
|
|
428
|
+
})
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
describe('inner schema format constraints', () => {
|
|
432
|
+
const schema = new OptionalSchema(new StringSchema({ format: 'uri' }))
|
|
433
|
+
|
|
434
|
+
it('validates values meeting format constraint', () => {
|
|
435
|
+
const result = schema.safeParse('https://example.com')
|
|
436
|
+
expect(result.success).toBe(true)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
it('validates undefined', () => {
|
|
440
|
+
const result = schema.safeParse(undefined)
|
|
441
|
+
expect(result.success).toBe(true)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('rejects values violating format constraint', () => {
|
|
445
|
+
const result = schema.safeParse('not a uri')
|
|
446
|
+
expect(result.success).toBe(false)
|
|
447
|
+
})
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
describe('integer constraint validation', () => {
|
|
451
|
+
const schema = new OptionalSchema(
|
|
452
|
+
new IntegerSchema({ minimum: 0, maximum: 100 }),
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
it('validates values within range', () => {
|
|
456
|
+
const result = schema.safeParse(50)
|
|
457
|
+
expect(result.success).toBe(true)
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
it('validates values at minimum boundary', () => {
|
|
461
|
+
const result = schema.safeParse(0)
|
|
462
|
+
expect(result.success).toBe(true)
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
it('validates values at maximum boundary', () => {
|
|
466
|
+
const result = schema.safeParse(100)
|
|
467
|
+
expect(result.success).toBe(true)
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
it('validates undefined', () => {
|
|
471
|
+
const result = schema.safeParse(undefined)
|
|
472
|
+
expect(result.success).toBe(true)
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
it('rejects values below minimum', () => {
|
|
476
|
+
const result = schema.safeParse(-1)
|
|
477
|
+
expect(result.success).toBe(false)
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
it('rejects values above maximum', () => {
|
|
481
|
+
const result = schema.safeParse(101)
|
|
482
|
+
expect(result.success).toBe(false)
|
|
483
|
+
})
|
|
484
|
+
})
|
|
485
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Schema,
|
|
3
|
+
ValidationResult,
|
|
4
|
+
Validator,
|
|
5
|
+
ValidatorContext,
|
|
6
|
+
} from '../validation.js'
|
|
7
|
+
|
|
8
|
+
export class OptionalSchema<V> extends Schema<V | undefined> {
|
|
9
|
+
constructor(readonly schema: Validator<V>) {
|
|
10
|
+
super()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
validateInContext(
|
|
14
|
+
input: unknown,
|
|
15
|
+
ctx: ValidatorContext,
|
|
16
|
+
): ValidationResult<V | undefined> {
|
|
17
|
+
// @NOTE The inner schema might apply a default value so we need to run it
|
|
18
|
+
// first, even if input is undefined.
|
|
19
|
+
const result = ctx.validate(input, this.schema)
|
|
20
|
+
|
|
21
|
+
if (result.success) {
|
|
22
|
+
return result
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (input === undefined) {
|
|
26
|
+
return ctx.success(input)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
31
|
+
}
|