justus 0.0.7 → 0.1.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/NOTICE.md +1 -1
- package/README.md +35 -11
- package/dist/dts-generator.js +64 -14
- package/dist/dts-generator.js.map +2 -2
- package/dist/dts-generator.mjs +66 -15
- package/dist/dts-generator.mjs.map +2 -2
- package/dist/index.js +195 -152
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +188 -151
- package/dist/index.mjs.map +3 -3
- package/dts-generator.d.ts +3 -3
- package/index.d.ts +94 -135
- package/package.json +14 -14
- package/src/dts-generator.ts +39 -17
- package/src/index.ts +4 -2
- package/src/schema.ts +1 -72
- package/src/types.ts +53 -140
- package/src/utilities.ts +5 -11
- package/src/validators/any.ts +2 -2
- package/src/validators/array.ts +7 -15
- package/src/validators/boolean.ts +35 -3
- package/src/validators/constant.ts +2 -2
- package/src/validators/date.ts +5 -11
- package/src/validators/never.ts +18 -0
- package/src/validators/number.ts +20 -12
- package/src/validators/object.ts +15 -48
- package/src/validators/optional.ts +30 -0
- package/src/validators/string.ts +8 -13
- package/src/validators/tuple.ts +2 -12
- package/src/validators/union.ts +3 -2
- package/src/validators/url.ts +5 -9
package/src/dts-generator.ts
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import ts from 'typescript'
|
|
2
2
|
import {
|
|
3
3
|
AllOfValidator,
|
|
4
|
+
any,
|
|
4
5
|
AnyArrayValidator,
|
|
5
6
|
AnyNumberValidator,
|
|
6
7
|
AnyObjectValidator,
|
|
7
8
|
AnyStringValidator,
|
|
8
9
|
AnyValidator,
|
|
10
|
+
array,
|
|
9
11
|
ArrayValidator,
|
|
10
12
|
assertSchema,
|
|
13
|
+
boolean,
|
|
11
14
|
BooleanValidator,
|
|
12
15
|
ConstantValidator,
|
|
16
|
+
date,
|
|
13
17
|
DateValidator,
|
|
14
18
|
getValidator,
|
|
19
|
+
NeverValidator,
|
|
20
|
+
number,
|
|
15
21
|
NumberValidator,
|
|
22
|
+
object,
|
|
16
23
|
ObjectValidator,
|
|
17
24
|
OneOfValidator,
|
|
25
|
+
OptionalValidator,
|
|
26
|
+
string,
|
|
18
27
|
StringValidator,
|
|
19
28
|
TupleValidator,
|
|
29
|
+
url,
|
|
20
30
|
URLValidator,
|
|
21
31
|
Validation,
|
|
22
32
|
Validator,
|
|
@@ -42,14 +52,13 @@ type ValidatorConstructor<V extends Validator = Validator> = { // <T = any> = {
|
|
|
42
52
|
* ========================================================================== */
|
|
43
53
|
|
|
44
54
|
/** Registry of all `Validator` constructors and related `TypeGenerator`s. */
|
|
45
|
-
const generators = new Map<Function, TypeGenerator<any>>()
|
|
55
|
+
const generators = new Map<Function | Validator, TypeGenerator<any>>()
|
|
46
56
|
|
|
47
57
|
/** Register a `TypeGenerator` function for a `Validator`. */
|
|
48
58
|
export function registerTypeGenerator<V extends Validator>(
|
|
49
|
-
validator: ValidatorConstructor<V>,
|
|
59
|
+
validator: V | ValidatorConstructor<V>,
|
|
50
60
|
generator: TypeGenerator<V>,
|
|
51
61
|
): void {
|
|
52
|
-
assertSchema(validator.prototype instanceof Validator, 'Not a `Validator` class')
|
|
53
62
|
generators.set(validator, generator)
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -84,7 +93,7 @@ export function generateTypes(validations: Record<string, Validation>): string {
|
|
|
84
93
|
|
|
85
94
|
// Create a type alias declaration with the name of the export
|
|
86
95
|
const modifiers = [ ts.factory.createModifier(ts.SyntaxKind.ExportKeyword) ]
|
|
87
|
-
const decl = ts.factory.createTypeAliasDeclaration(
|
|
96
|
+
const decl = ts.factory.createTypeAliasDeclaration(modifiers, name, [], type)
|
|
88
97
|
types.push(decl)
|
|
89
98
|
}
|
|
90
99
|
|
|
@@ -107,7 +116,7 @@ function generateTypeNode(
|
|
|
107
116
|
const reference = references.get(validator)
|
|
108
117
|
if (reference) return ts.factory.createTypeReferenceNode(reference)
|
|
109
118
|
|
|
110
|
-
const generator = generators.get(validator.constructor)
|
|
119
|
+
const generator = generators.get(validator) || generators.get(validator.constructor)
|
|
111
120
|
assertSchema(!! generator, `Type generator for "${validator.constructor.name}" not found`)
|
|
112
121
|
return generator(validator, references)
|
|
113
122
|
}
|
|
@@ -125,15 +134,13 @@ const stringType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
|
125
134
|
const undefinedType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
|
|
126
135
|
const recordType = ts.factory.createMappedTypeNode(
|
|
127
136
|
undefined, // readonly
|
|
128
|
-
ts.factory.createTypeParameterDeclaration('key', stringType),
|
|
137
|
+
ts.factory.createTypeParameterDeclaration([], 'key', stringType),
|
|
129
138
|
undefined, // name type
|
|
130
139
|
undefined, // question token
|
|
131
140
|
anyType, // type of the mapped key
|
|
132
141
|
undefined) // members
|
|
133
142
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
const readonlyKeyword = [ ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword) ]
|
|
143
|
+
// "Optional" modifier (the "?" token )
|
|
137
144
|
const optionalKeyword = ts.factory.createToken(ts.SyntaxKind.QuestionToken)
|
|
138
145
|
|
|
139
146
|
|
|
@@ -147,9 +154,19 @@ registerTypeGenerator(AnyNumberValidator, () => numberType)
|
|
|
147
154
|
registerTypeGenerator(AnyObjectValidator, () => recordType)
|
|
148
155
|
registerTypeGenerator(AnyStringValidator, () => stringType)
|
|
149
156
|
registerTypeGenerator(BooleanValidator, () => booleanType)
|
|
157
|
+
registerTypeGenerator(NeverValidator, () => neverType)
|
|
150
158
|
registerTypeGenerator(DateValidator, () => ts.factory.createTypeReferenceNode('Date'))
|
|
151
159
|
registerTypeGenerator(URLValidator, () => ts.factory.createTypeReferenceNode('URL'))
|
|
152
160
|
|
|
161
|
+
registerTypeGenerator(any, () => anyType)
|
|
162
|
+
registerTypeGenerator(array, () => anyArrayType)
|
|
163
|
+
registerTypeGenerator(number, () => numberType)
|
|
164
|
+
registerTypeGenerator(object, () => recordType)
|
|
165
|
+
registerTypeGenerator(string, () => stringType)
|
|
166
|
+
registerTypeGenerator(boolean, () => booleanType)
|
|
167
|
+
registerTypeGenerator(date, () => ts.factory.createTypeReferenceNode('Date'))
|
|
168
|
+
registerTypeGenerator(url, () => ts.factory.createTypeReferenceNode('URL'))
|
|
169
|
+
|
|
153
170
|
/* ========================================================================== */
|
|
154
171
|
|
|
155
172
|
// Complex generator functions...
|
|
@@ -180,6 +197,11 @@ registerTypeGenerator(NumberValidator, (validator: NumberValidator) => {
|
|
|
180
197
|
return ts.factory.createIntersectionTypeNode([ numberType, literal ])
|
|
181
198
|
})
|
|
182
199
|
|
|
200
|
+
registerTypeGenerator(OptionalValidator, (validator: OptionalValidator, references) => {
|
|
201
|
+
const type = generateTypeNode(validator.validator, references)
|
|
202
|
+
return ts.factory.createUnionTypeNode([ type, undefinedType ])
|
|
203
|
+
})
|
|
204
|
+
|
|
183
205
|
registerTypeGenerator(StringValidator, (validator: StringValidator) => {
|
|
184
206
|
if (! validator.brand) return stringType
|
|
185
207
|
|
|
@@ -245,15 +267,15 @@ registerTypeGenerator(OneOfValidator, (validator, references) => {
|
|
|
245
267
|
registerTypeGenerator(ObjectValidator, (validator, references) => {
|
|
246
268
|
const properties: ts.PropertySignature[] = []
|
|
247
269
|
|
|
248
|
-
for (const [ key,
|
|
249
|
-
const
|
|
250
|
-
const
|
|
270
|
+
for (const [ key, valueValidator ] of validator.validators.entries()) {
|
|
271
|
+
const type = generateTypeNode(valueValidator, references)
|
|
272
|
+
const optional = valueValidator.optional
|
|
251
273
|
|
|
252
274
|
const signature = ts.factory.createPropertySignature(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
275
|
+
undefined,
|
|
276
|
+
key,
|
|
277
|
+
optional ? optionalKeyword : undefined,
|
|
278
|
+
type)
|
|
257
279
|
|
|
258
280
|
properties.push(signature)
|
|
259
281
|
}
|
|
@@ -264,7 +286,7 @@ registerTypeGenerator(ObjectValidator, (validator, references) => {
|
|
|
264
286
|
|
|
265
287
|
const extra = ts.factory.createMappedTypeNode(
|
|
266
288
|
undefined, // readonly
|
|
267
|
-
ts.factory.createTypeParameterDeclaration('key', stringType),
|
|
289
|
+
ts.factory.createTypeParameterDeclaration([], 'key', stringType),
|
|
268
290
|
undefined, // name type
|
|
269
291
|
undefined, // question token
|
|
270
292
|
optionalPropertyType, // (type | undefined)
|
package/src/index.ts
CHANGED
|
@@ -12,11 +12,13 @@ export * from './utilities'
|
|
|
12
12
|
export { allOf, oneOf, AllOfValidator, InferAllOfValidationType, InferOneOfValidationType, OneOfValidator, UnionArguments } from './validators/union'
|
|
13
13
|
export { any, AnyValidator } from './validators/any'
|
|
14
14
|
export { _array, array, arrayOf, AnyArrayValidator, ArrayConstraints, ArrayValidator } from './validators/array'
|
|
15
|
-
export { boolean, BooleanValidator } from './validators/boolean'
|
|
15
|
+
export { _boolean, boolean, BooleanConstraints, BooleanValidator } from './validators/boolean'
|
|
16
16
|
export { constant, ConstantValidator } from './validators/constant'
|
|
17
17
|
export { _date, date, DateConstraints, DateValidator } from './validators/date'
|
|
18
|
+
export { never, NeverValidator } from './validators/never'
|
|
18
19
|
export { _number, number, AnyNumberValidator, BrandedNumberConstraints, NumberConstraints, NumberValidator } from './validators/number'
|
|
19
|
-
export { _object, object, AnyObjectValidator,
|
|
20
|
+
export { _object, object, AnyObjectValidator, ObjectValidator } from './validators/object'
|
|
21
|
+
export { optional, OptionalValidator } from './validators/optional'
|
|
20
22
|
export { _string, string, AnyStringValidator, BrandedStringConstraints, StringConstraints, StringValidator } from './validators/string'
|
|
21
23
|
export { tuple, TupleMember, TupleValidator } from './validators/tuple'
|
|
22
24
|
export { _url, url, URLConstraints, URLValidator } from './validators/url'
|
package/src/schema.ts
CHANGED
|
@@ -3,15 +3,10 @@ import { getValidator } from './utilities'
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
AdditionalProperties,
|
|
6
|
-
CombinedModifier,
|
|
7
6
|
InferValidation,
|
|
8
|
-
Modifier,
|
|
9
|
-
OptionalModifier,
|
|
10
|
-
ReadonlyModifier,
|
|
11
7
|
Validation,
|
|
12
8
|
Validator,
|
|
13
9
|
additionalValidator,
|
|
14
|
-
modifierValidator,
|
|
15
10
|
} from './types'
|
|
16
11
|
|
|
17
12
|
/* ========================================================================== *
|
|
@@ -28,7 +23,7 @@ export function _allowAdditionalProperties(options?: Validation | boolean): Addi
|
|
|
28
23
|
if (options === false) return { [additionalValidator]: false }
|
|
29
24
|
if (options === true) return { [additionalValidator]: any }
|
|
30
25
|
|
|
31
|
-
return { [additionalValidator]: getValidator(options) }
|
|
26
|
+
return { [additionalValidator]: options ? getValidator(options) : any }
|
|
32
27
|
}
|
|
33
28
|
|
|
34
29
|
/**
|
|
@@ -44,69 +39,3 @@ export const allowAdditionalProperties = _allowAdditionalProperties as
|
|
|
44
39
|
typeof _allowAdditionalProperties & AdditionalProperties<Validator<any>>
|
|
45
40
|
|
|
46
41
|
allowAdditionalProperties[additionalValidator] = any
|
|
47
|
-
|
|
48
|
-
/* ========================================================================== *
|
|
49
|
-
* SCHEMA KEYS MODIFIERS *
|
|
50
|
-
* ========================================================================== */
|
|
51
|
-
|
|
52
|
-
export type CombineModifiers<M1 extends Modifier, M2 extends Modifier> =
|
|
53
|
-
M1 extends ReadonlyModifier ?
|
|
54
|
-
M2 extends ReadonlyModifier<infer V> ? ReadonlyModifier<V> :
|
|
55
|
-
M2 extends OptionalModifier<infer V> ? CombinedModifier<V> :
|
|
56
|
-
never :
|
|
57
|
-
M1 extends OptionalModifier ?
|
|
58
|
-
M2 extends ReadonlyModifier<infer V> ? CombinedModifier<V> :
|
|
59
|
-
M2 extends OptionalModifier<infer V> ? OptionalModifier<V> :
|
|
60
|
-
never :
|
|
61
|
-
never
|
|
62
|
-
|
|
63
|
-
/* -------------------------------------------------------------------------- */
|
|
64
|
-
|
|
65
|
-
/** Type guard for `Modifier` instances */
|
|
66
|
-
export function isModifier(what: any): what is Modifier<any> {
|
|
67
|
-
return (what && (typeof what === 'object') && (modifierValidator in what))
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/* -------------------------------------------------------------------------- */
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Ensure that the property is marked as _read only_ in the `Schema`.
|
|
74
|
-
*
|
|
75
|
-
* @param validation - A `Validation` to be marked as _read only_.
|
|
76
|
-
*/
|
|
77
|
-
export function readonly(): ReadonlyModifier<any>
|
|
78
|
-
export function readonly<V extends Validation>(validation: V): ReadonlyModifier<Validator<InferValidation<V>>>
|
|
79
|
-
export function readonly<M extends Modifier>(modifier: M): CombineModifiers<ReadonlyModifier, M>
|
|
80
|
-
|
|
81
|
-
export function readonly(options?: Modifier | Validation): Modifier {
|
|
82
|
-
const { [modifierValidator]: validation = any, optional = false } =
|
|
83
|
-
isModifier(options) ? options : { [modifierValidator]: options }
|
|
84
|
-
|
|
85
|
-
const validator = getValidator(validation)
|
|
86
|
-
|
|
87
|
-
return optional ?
|
|
88
|
-
{ [modifierValidator]: validator, optional, readonly: true } :
|
|
89
|
-
{ [modifierValidator]: validator, readonly: true }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/* -------------------------------------------------------------------------- */
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Ensure that the property is marked as _optional_ in the `Schema`.
|
|
96
|
-
*
|
|
97
|
-
* @param validation - A `Validation` to be marked as _optional_.
|
|
98
|
-
*/
|
|
99
|
-
export function optional(): OptionalModifier<any>
|
|
100
|
-
export function optional<V extends Validation>(validation: V): OptionalModifier<Validator<InferValidation<V>>>
|
|
101
|
-
export function optional<M extends Modifier>(modifier: M): CombineModifiers<OptionalModifier, M>
|
|
102
|
-
|
|
103
|
-
export function optional(options?: Modifier<any> | Validation): Modifier<any> {
|
|
104
|
-
const { [modifierValidator]: validation = any, readonly = false } =
|
|
105
|
-
isModifier(options) ? options : { [modifierValidator]: options }
|
|
106
|
-
|
|
107
|
-
const validator = getValidator(validation)
|
|
108
|
-
|
|
109
|
-
return readonly ?
|
|
110
|
-
{ [modifierValidator]: validator, readonly, optional: true } :
|
|
111
|
-
{ [modifierValidator]: validator, optional: true }
|
|
112
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -2,21 +2,18 @@
|
|
|
2
2
|
* SYMBOLS IDENTIFYING SPECIAL FUNCTIONALITIES *
|
|
3
3
|
* ========================================================================== */
|
|
4
4
|
|
|
5
|
+
/** A symbol indicating that an instance is (in fact) a `Validator`. */
|
|
6
|
+
export const isValidator = Symbol.for('justus.isValidator')
|
|
7
|
+
|
|
5
8
|
/** A symbol indicating the `Validator` for a `Tuple`'s rest parameter. */
|
|
6
9
|
export const restValidator = Symbol.for('justus.restValidator')
|
|
7
10
|
|
|
8
11
|
/** A symbol indicating the `Validator` for a `Schema`. */
|
|
9
12
|
export const schemaValidator = Symbol.for('justus.schemaValidator')
|
|
10
13
|
|
|
11
|
-
/** A symbol indicating the `Validator` for a `Modifier`. */
|
|
12
|
-
export const modifierValidator = Symbol.for('justus.modifierValidator')
|
|
13
|
-
|
|
14
14
|
/** A symbol indicating the `Validator` for a `Schema`'s additional properties. */
|
|
15
15
|
export const additionalValidator = Symbol.for('justus.additionalValidator')
|
|
16
16
|
|
|
17
|
-
/** A symbol indicating that a `Schema` property is _forbidden_. */
|
|
18
|
-
export const never = Symbol.for('justus.never')
|
|
19
|
-
|
|
20
17
|
/* ========================================================================== *
|
|
21
18
|
* BASIC VALIDATION TYPES *
|
|
22
19
|
* ========================================================================== */
|
|
@@ -37,7 +34,45 @@ export interface ValidationOptions {
|
|
|
37
34
|
* A `Validator` is an object capable of validating a given _value_ and
|
|
38
35
|
* (possibly) converting it the required type `T`.
|
|
39
36
|
*/
|
|
40
|
-
export
|
|
37
|
+
export interface Validator<T = any> extends Iterable<TupleRestParameter<T>> {
|
|
38
|
+
[isValidator]: true
|
|
39
|
+
|
|
40
|
+
optional?: boolean
|
|
41
|
+
|
|
42
|
+
/** Validate a _value_ and optionally convert it to the required `Type` */
|
|
43
|
+
validate(value: unknown, options: ValidationOptions): T
|
|
44
|
+
|
|
45
|
+
/** Allow any `Validator` to be used as a rest parameter in `Tuple`s */
|
|
46
|
+
[Symbol.iterator](): Generator<TupleRestParameter<T>>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create a validator "factory", that is a function that when invoked will
|
|
51
|
+
* create a new `Validator` according to the parameters specified. This function
|
|
52
|
+
* will also implement the `Validator` interface itself, using the `Validator`
|
|
53
|
+
* supplied as the first parameter.
|
|
54
|
+
*/
|
|
55
|
+
export function makeValidatorFactory<
|
|
56
|
+
V extends Validator,
|
|
57
|
+
F extends (...args: any[]) => Validator,
|
|
58
|
+
>(validator: V, factory: F): F & V {
|
|
59
|
+
return Object.assign(factory, {
|
|
60
|
+
optional: validator.optional,
|
|
61
|
+
validate: validator.validate.bind(validator),
|
|
62
|
+
[Symbol.iterator]: validator[Symbol.iterator].bind(validator),
|
|
63
|
+
[isValidator]: true,
|
|
64
|
+
}) as F & V
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A `Validator` is an object capable of validating a given _value_ and
|
|
69
|
+
* (possibly) converting it the required type `T`.
|
|
70
|
+
*/
|
|
71
|
+
export abstract class AbstractValidator<T = any> implements Iterable<TupleRestParameter<T>> {
|
|
72
|
+
[isValidator]: true = true
|
|
73
|
+
|
|
74
|
+
optional?: boolean = undefined
|
|
75
|
+
|
|
41
76
|
/** Validate a _value_ and optionally convert it to the required `Type` */
|
|
42
77
|
abstract validate(value: unknown, options: ValidationOptions): T
|
|
43
78
|
|
|
@@ -58,7 +93,7 @@ export abstract class Validator<T = any> implements Iterable<TupleRestParameter<
|
|
|
58
93
|
* * Either `null`, a `boolean`, a `number` or a `string` for constants
|
|
59
94
|
*/
|
|
60
95
|
export type Validation =
|
|
61
|
-
|
|
96
|
+
Validator | // Validator instances
|
|
62
97
|
Tuple | Schema | // Tuples or schemas (arrays, objects)
|
|
63
98
|
null | boolean | number | string // Primitives, mapped as constants
|
|
64
99
|
|
|
@@ -69,33 +104,6 @@ export type InferValidation<V> =
|
|
|
69
104
|
// Let `InferValidationType<T>` be liberal in the type it accepts and check
|
|
70
105
|
// here whether it extends `Validation`
|
|
71
106
|
V extends Validation ?
|
|
72
|
-
|
|
73
|
-
// `Validation` can be a function returning a `Validator`, here we need to
|
|
74
|
-
// extract the return value of its _zero-arguments_ overload
|
|
75
|
-
V extends {
|
|
76
|
-
(...args: infer A0): Validator<infer R0>;
|
|
77
|
-
(...args: infer A1): Validator<infer R1>;
|
|
78
|
-
(...args: infer A2): Validator<infer R2>;
|
|
79
|
-
(...args: infer A3): Validator<infer R3>;
|
|
80
|
-
(...args: infer A4): Validator<infer R4>;
|
|
81
|
-
(...args: infer A5): Validator<infer R5>;
|
|
82
|
-
(...args: infer A6): Validator<infer R6>;
|
|
83
|
-
(...args: infer A7): Validator<infer R7>;
|
|
84
|
-
(...args: infer A8): Validator<infer R8>;
|
|
85
|
-
(...args: infer A9): Validator<infer R9>;
|
|
86
|
-
} ?
|
|
87
|
-
A0 extends [] ? R0 :
|
|
88
|
-
A1 extends [] ? R1 :
|
|
89
|
-
A2 extends [] ? R2 :
|
|
90
|
-
A3 extends [] ? R3 :
|
|
91
|
-
A4 extends [] ? R4 :
|
|
92
|
-
A5 extends [] ? R5 :
|
|
93
|
-
A6 extends [] ? R6 :
|
|
94
|
-
A7 extends [] ? R7 :
|
|
95
|
-
A8 extends [] ? R8 :
|
|
96
|
-
A9 extends [] ? R9 :
|
|
97
|
-
never :
|
|
98
|
-
|
|
99
107
|
// Validators return their validation type
|
|
100
108
|
V extends Validator<infer T> ? T :
|
|
101
109
|
|
|
@@ -175,7 +183,7 @@ export type InferTuple<T> =
|
|
|
175
183
|
* how they should be validated.
|
|
176
184
|
*/
|
|
177
185
|
export interface Schema {
|
|
178
|
-
[ key: string ] : Validation
|
|
186
|
+
[ key: string ] : Validation
|
|
179
187
|
[ additionalValidator ]?: Validator | false
|
|
180
188
|
}
|
|
181
189
|
|
|
@@ -187,118 +195,23 @@ export interface AdditionalProperties<V extends Validator | false> {
|
|
|
187
195
|
[ additionalValidator ]: V
|
|
188
196
|
}
|
|
189
197
|
|
|
190
|
-
/* -------------------------------------------------------------------------- */
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* A `Modifier` marks a `Schema` property either _read only_ and/or _optional_.
|
|
194
|
-
*/
|
|
195
|
-
export interface Modifier<V extends Validator = Validator> {
|
|
196
|
-
[ modifierValidator ]: V
|
|
197
|
-
readonly?: true,
|
|
198
|
-
optional?: true,
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Mark a `Schema` property as _read only_.
|
|
203
|
-
*/
|
|
204
|
-
export interface ReadonlyModifier<V extends Validator = Validator> extends Modifier<V> {
|
|
205
|
-
readonly: true,
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Mark a `Schema` property as _optional_.
|
|
210
|
-
*/
|
|
211
|
-
export interface OptionalModifier<V extends Validator = Validator> extends Modifier<V> {
|
|
212
|
-
optional: true,
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Mark a `Schema` property as both _optional_ and _read only_.
|
|
217
|
-
*/
|
|
218
|
-
export interface CombinedModifier<V extends Validator = Validator>
|
|
219
|
-
extends ReadonlyModifier<V>, OptionalModifier<V>, Modifier<V> {
|
|
220
|
-
optional: true,
|
|
221
|
-
readonly: true,
|
|
222
|
-
}
|
|
223
|
-
|
|
224
198
|
/* ========================================================================== *
|
|
225
199
|
* INFER OBJECT TYPE FROM SCHEMA *
|
|
226
200
|
* ========================================================================== */
|
|
227
201
|
|
|
228
202
|
/** Infer the type validated by a `Schema` */
|
|
229
203
|
export type InferSchema<S extends Schema> =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
/* -------------------------------------------------------------------------- */
|
|
240
|
-
|
|
241
|
-
/** Infer the type of keys associated with `Validation`s */
|
|
242
|
-
export type InferRequired<S extends Schema> = {
|
|
243
|
-
[ key in keyof S as
|
|
244
|
-
key extends string ?
|
|
245
|
-
S[key] extends Validation ? key :
|
|
246
|
-
never :
|
|
247
|
-
never
|
|
248
|
-
] :
|
|
249
|
-
S[key] extends Validation ? InferValidation<S[key]> :
|
|
250
|
-
never
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/* -------------------------------------------------------------------------- */
|
|
254
|
-
|
|
255
|
-
/** Infer the type of _read only_ `Schema` properties */
|
|
256
|
-
export type InferReadonlyModifiers<S extends Schema> = {
|
|
257
|
-
readonly [ key in keyof S as
|
|
258
|
-
key extends string ?
|
|
259
|
-
S[key] extends OptionalModifier<Validator> ? never :
|
|
260
|
-
S[key] extends ReadonlyModifier<Validator> ? key :
|
|
261
|
-
never :
|
|
262
|
-
never
|
|
263
|
-
] :
|
|
264
|
-
S[key] extends ReadonlyModifier<infer V> ? InferValidation<V> : never
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** Infer the type of _optional_ `Schema` properties */
|
|
268
|
-
export type InferOptionalModifiers<S extends Schema> = {
|
|
269
|
-
[ key in keyof S as
|
|
270
|
-
key extends string ?
|
|
271
|
-
S[key] extends ReadonlyModifier<Validator> ? never :
|
|
272
|
-
S[key] extends OptionalModifier<Validator> ? key :
|
|
273
|
-
never :
|
|
274
|
-
never
|
|
275
|
-
] ? :
|
|
276
|
-
S[key] extends OptionalModifier<infer V> ? InferValidation<V> : never
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** Infer the type of _read only_ **and** _optional_ `Schema` properties */
|
|
280
|
-
export type InferCombinedModifiers<S extends Schema> = {
|
|
281
|
-
readonly [ key in keyof S as
|
|
282
|
-
key extends string ?
|
|
283
|
-
S[key] extends CombinedModifier ? key :
|
|
284
|
-
never :
|
|
285
|
-
never
|
|
286
|
-
] ? :
|
|
287
|
-
S[key] extends CombinedModifier<infer V> ? InferValidation<V> : never
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/* -------------------------------------------------------------------------- */
|
|
291
|
-
|
|
292
|
-
/** Ensure that we properly type `never` properties */
|
|
293
|
-
export type InferNever<S extends Schema> =
|
|
294
|
-
{ [ key in keyof S as
|
|
295
|
-
key extends string ?
|
|
296
|
-
S[key] extends typeof never ? key :
|
|
297
|
-
never :
|
|
298
|
-
never
|
|
299
|
-
] : never
|
|
204
|
+
S extends AdditionalProperties<Validator<infer V>> ?
|
|
205
|
+
{ [ key in string ] : V | undefined } & InferSchema2<S> :
|
|
206
|
+
InferSchema2<S>
|
|
207
|
+
|
|
208
|
+
/** Infer the property types described by a `Schema` */
|
|
209
|
+
export type InferSchema2<S extends Schema> =
|
|
210
|
+
{ [ key in keyof S as key extends string ? key : never ]:
|
|
211
|
+
InferValidation<S[key]>
|
|
300
212
|
}
|
|
301
213
|
|
|
214
|
+
|
|
302
215
|
/* ========================================================================== *
|
|
303
216
|
* TYPE BRANDING *
|
|
304
217
|
* ========================================================================== */
|
package/src/utilities.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { any } from './validators/any'
|
|
2
1
|
import { ConstantValidator, nullValidator } from './validators/constant'
|
|
3
|
-
import { Schema, schemaValidator, Validation, Validator } from './types'
|
|
2
|
+
import { isValidator, Schema, schemaValidator, Validation, Validator } from './types'
|
|
4
3
|
import { TupleValidator } from './validators/tuple'
|
|
5
4
|
import { ObjectValidator } from './validators/object'
|
|
6
5
|
|
|
@@ -13,13 +12,12 @@ import { ObjectValidator } from './validators/object'
|
|
|
13
12
|
*
|
|
14
13
|
* When `validation` is `undefined` it will return a `Validator<any>`,
|
|
15
14
|
*/
|
|
16
|
-
export function getValidator(validation
|
|
17
|
-
//
|
|
18
|
-
if (validation === undefined) return any
|
|
15
|
+
export function getValidator(validation: Validation): Validator {
|
|
16
|
+
// Null is a constant
|
|
19
17
|
if (validation === null) return nullValidator
|
|
20
18
|
|
|
21
|
-
// Validator
|
|
22
|
-
if (validation
|
|
19
|
+
// Validator instance (either object or function)
|
|
20
|
+
if ((<any> validation)[isValidator] === true) return validation as Validator
|
|
23
21
|
|
|
24
22
|
// Other types
|
|
25
23
|
switch (typeof validation) {
|
|
@@ -29,10 +27,6 @@ export function getValidator(validation?: Validation): Validator {
|
|
|
29
27
|
case 'number':
|
|
30
28
|
return new ConstantValidator(validation)
|
|
31
29
|
|
|
32
|
-
// validator generator
|
|
33
|
-
case 'function':
|
|
34
|
-
return validation()
|
|
35
|
-
|
|
36
30
|
// other objects...
|
|
37
31
|
case 'object':
|
|
38
32
|
// pre-compiled schema with validator
|
package/src/validators/any.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractValidator } from '../types'
|
|
2
2
|
|
|
3
3
|
/** A `Validator` validating _anything_. */
|
|
4
|
-
export class AnyValidator extends
|
|
4
|
+
export class AnyValidator extends AbstractValidator<any> {
|
|
5
5
|
validate(value: unknown): any {
|
|
6
6
|
return value
|
|
7
7
|
}
|
package/src/validators/array.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { assertSchema, assertValidation, ValidationErrorBuilder } from '../errors'
|
|
2
|
-
import { InferValidation, Validation, ValidationOptions, Validator } from '../types'
|
|
2
|
+
import { InferValidation, Validation, ValidationOptions, AbstractValidator, Validator, makeValidatorFactory } from '../types'
|
|
3
3
|
import { getValidator } from '../utilities'
|
|
4
4
|
import { any } from './any'
|
|
5
|
-
import { makeTupleRestIterable } from './tuple'
|
|
6
5
|
|
|
7
6
|
/* ========================================================================== *
|
|
8
7
|
* ARRAYS VALIDATION *
|
|
@@ -21,7 +20,7 @@ export interface ArrayConstraints<V extends Validation> {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
/** Basic validator for `Array` instances. */
|
|
24
|
-
export class AnyArrayValidator<T = any> extends
|
|
23
|
+
export class AnyArrayValidator<T = any> extends AbstractValidator<T[]> {
|
|
25
24
|
validate(value: unknown, options: ValidationOptions): T[] {
|
|
26
25
|
void options
|
|
27
26
|
assertValidation(Array.isArray(value), 'Value is not an "array"')
|
|
@@ -30,7 +29,7 @@ export class AnyArrayValidator<T = any> extends Validator<T[]> {
|
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
/** A validator for `Array` instances with constraints. */
|
|
33
|
-
export class ArrayValidator<T> extends
|
|
32
|
+
export class ArrayValidator<T> extends AbstractValidator<T[]> {
|
|
34
33
|
readonly maxItems: number
|
|
35
34
|
readonly minItems: number
|
|
36
35
|
readonly uniqueItems: boolean
|
|
@@ -87,22 +86,15 @@ export class ArrayValidator<T> extends Validator<T[]> {
|
|
|
87
86
|
}
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
const anyArrayValidator = new AnyArrayValidator()
|
|
91
|
-
|
|
92
89
|
/* -------------------------------------------------------------------------- */
|
|
93
90
|
|
|
94
|
-
export function _array():
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
export function _array(options?: ArrayConstraints<Validation>): Validator<any[]> {
|
|
98
|
-
if (! options) return anyArrayValidator
|
|
99
|
-
|
|
100
|
-
const items = getValidator(options.items)
|
|
101
|
-
return new ArrayValidator({ ...options, items })
|
|
91
|
+
export function _array<V extends Validation>(constraints: ArrayConstraints<V>): ArrayValidator<InferValidation<V>> {
|
|
92
|
+
const items = constraints.items ? getValidator(constraints.items) : any
|
|
93
|
+
return new ArrayValidator({ ...constraints, items })
|
|
102
94
|
}
|
|
103
95
|
|
|
104
96
|
/** Validate `Array`s. */
|
|
105
|
-
export const array =
|
|
97
|
+
export const array = makeValidatorFactory(new AnyArrayValidator(), _array)
|
|
106
98
|
|
|
107
99
|
/** Validate `Array`s containing only the specified elements. */
|
|
108
100
|
export function arrayOf<V extends Validation>(validation: V): ArrayValidator<InferValidation<V>> {
|
|
@@ -1,13 +1,45 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractValidator, makeValidatorFactory } from '../types'
|
|
2
2
|
import { assertValidation } from '../errors'
|
|
3
3
|
|
|
4
|
+
/** Constraints to validate a `boolean` with. */
|
|
5
|
+
export interface BooleanConstraints {
|
|
6
|
+
/**
|
|
7
|
+
* Allow booleans to be parsed from strings (default: `false`).
|
|
8
|
+
*
|
|
9
|
+
* The string in question _MUST_ be either `true` or `false`, and will be
|
|
10
|
+
* compared regardless of case.
|
|
11
|
+
*/
|
|
12
|
+
fromString?: boolean,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
4
16
|
/** A `Validator` validating `boolean`s. */
|
|
5
|
-
export class BooleanValidator extends
|
|
17
|
+
export class BooleanValidator extends AbstractValidator<boolean> {
|
|
18
|
+
readonly fromString: boolean
|
|
19
|
+
|
|
20
|
+
constructor(constraints: BooleanConstraints = {}) {
|
|
21
|
+
super()
|
|
22
|
+
const { fromString = false } = constraints
|
|
23
|
+
this.fromString = fromString
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
validate(value: unknown): boolean {
|
|
27
|
+
// Allow parsing from strings
|
|
28
|
+
if ((typeof value == 'string') && (this.fromString)) {
|
|
29
|
+
const string = value.toLowerCase()
|
|
30
|
+
const parsed = string === 'true' ? true : string === 'false' ? false : undefined
|
|
31
|
+
assertValidation(parsed !== undefined, 'Boolean can not be parsed from string')
|
|
32
|
+
value = parsed
|
|
33
|
+
}
|
|
34
|
+
|
|
7
35
|
assertValidation(typeof value === 'boolean', 'Value is not a "boolean"')
|
|
8
36
|
return value
|
|
9
37
|
}
|
|
10
38
|
}
|
|
11
39
|
|
|
40
|
+
export function _boolean(constraints: BooleanConstraints): BooleanValidator {
|
|
41
|
+
return new BooleanValidator(constraints)
|
|
42
|
+
}
|
|
43
|
+
|
|
12
44
|
/** The `Validator` for `boolean`s. */
|
|
13
|
-
export const boolean = new BooleanValidator()
|
|
45
|
+
export const boolean = makeValidatorFactory(new BooleanValidator(), _boolean)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Validator } from '../types'
|
|
1
|
+
import { AbstractValidator, Validator } from '../types'
|
|
2
2
|
import { assertValidation } from '../errors'
|
|
3
3
|
|
|
4
4
|
/** A `Validator` for _constants_. */
|
|
5
|
-
export class ConstantValidator<T extends string | number | boolean | null> extends
|
|
5
|
+
export class ConstantValidator<T extends string | number | boolean | null> extends AbstractValidator<T> {
|
|
6
6
|
readonly constant: T
|
|
7
7
|
|
|
8
8
|
constructor(constant: T) {
|