justus 0.0.6 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE.md +1 -1
- package/README.md +13 -6
- package/dist/dts-generator.js +67 -14
- package/dist/dts-generator.js.map +2 -2
- package/dist/dts-generator.mjs +69 -15
- package/dist/dts-generator.mjs.map +2 -2
- package/dist/index.js +169 -151
- package/dist/index.js.map +3 -3
- package/dist/index.mjs +163 -150
- package/dist/index.mjs.map +3 -3
- package/dts-generator.d.ts +3 -3
- package/index.d.ts +75 -134
- package/package.json +14 -14
- package/src/dts-generator.ts +44 -18
- package/src/index.ts +3 -1
- 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 +2 -2
- 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 +8 -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
|
}
|
|
@@ -122,17 +131,16 @@ const booleanType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeywor
|
|
|
122
131
|
const numberType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
|
|
123
132
|
const neverType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)
|
|
124
133
|
const stringType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
|
134
|
+
const undefinedType = ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
|
|
125
135
|
const recordType = ts.factory.createMappedTypeNode(
|
|
126
136
|
undefined, // readonly
|
|
127
|
-
ts.factory.createTypeParameterDeclaration('key', stringType),
|
|
137
|
+
ts.factory.createTypeParameterDeclaration([], 'key', stringType),
|
|
128
138
|
undefined, // name type
|
|
129
139
|
undefined, // question token
|
|
130
140
|
anyType, // type of the mapped key
|
|
131
141
|
undefined) // members
|
|
132
142
|
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
const readonlyKeyword = [ ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword) ]
|
|
143
|
+
// "Optional" modifier (the "?" token )
|
|
136
144
|
const optionalKeyword = ts.factory.createToken(ts.SyntaxKind.QuestionToken)
|
|
137
145
|
|
|
138
146
|
|
|
@@ -146,9 +154,19 @@ registerTypeGenerator(AnyNumberValidator, () => numberType)
|
|
|
146
154
|
registerTypeGenerator(AnyObjectValidator, () => recordType)
|
|
147
155
|
registerTypeGenerator(AnyStringValidator, () => stringType)
|
|
148
156
|
registerTypeGenerator(BooleanValidator, () => booleanType)
|
|
157
|
+
registerTypeGenerator(NeverValidator, () => neverType)
|
|
149
158
|
registerTypeGenerator(DateValidator, () => ts.factory.createTypeReferenceNode('Date'))
|
|
150
159
|
registerTypeGenerator(URLValidator, () => ts.factory.createTypeReferenceNode('URL'))
|
|
151
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
|
+
|
|
152
170
|
/* ========================================================================== */
|
|
153
171
|
|
|
154
172
|
// Complex generator functions...
|
|
@@ -179,6 +197,11 @@ registerTypeGenerator(NumberValidator, (validator: NumberValidator) => {
|
|
|
179
197
|
return ts.factory.createIntersectionTypeNode([ numberType, literal ])
|
|
180
198
|
})
|
|
181
199
|
|
|
200
|
+
registerTypeGenerator(OptionalValidator, (validator: OptionalValidator, references) => {
|
|
201
|
+
const type = generateTypeNode(validator.validator, references)
|
|
202
|
+
return ts.factory.createUnionTypeNode([ type, undefinedType ])
|
|
203
|
+
})
|
|
204
|
+
|
|
182
205
|
registerTypeGenerator(StringValidator, (validator: StringValidator) => {
|
|
183
206
|
if (! validator.brand) return stringType
|
|
184
207
|
|
|
@@ -244,26 +267,29 @@ registerTypeGenerator(OneOfValidator, (validator, references) => {
|
|
|
244
267
|
registerTypeGenerator(ObjectValidator, (validator, references) => {
|
|
245
268
|
const properties: ts.PropertySignature[] = []
|
|
246
269
|
|
|
247
|
-
for (const [ key,
|
|
248
|
-
const
|
|
249
|
-
const
|
|
270
|
+
for (const [ key, valueValidator ] of validator.validators.entries()) {
|
|
271
|
+
const type = generateTypeNode(valueValidator, references)
|
|
272
|
+
const optional = valueValidator.optional
|
|
250
273
|
|
|
251
274
|
const signature = ts.factory.createPropertySignature(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
275
|
+
undefined,
|
|
276
|
+
key,
|
|
277
|
+
optional ? optionalKeyword : undefined,
|
|
278
|
+
type)
|
|
256
279
|
|
|
257
280
|
properties.push(signature)
|
|
258
281
|
}
|
|
259
282
|
|
|
260
283
|
if (validator.additionalProperties) {
|
|
284
|
+
const propertyType = generateTypeNode(validator.additionalProperties, references)
|
|
285
|
+
const optionalPropertyType = ts.factory.createUnionTypeNode([ propertyType, undefinedType ])
|
|
286
|
+
|
|
261
287
|
const extra = ts.factory.createMappedTypeNode(
|
|
262
288
|
undefined, // readonly
|
|
263
|
-
ts.factory.createTypeParameterDeclaration('key', stringType),
|
|
289
|
+
ts.factory.createTypeParameterDeclaration([], 'key', stringType),
|
|
264
290
|
undefined, // name type
|
|
265
291
|
undefined, // question token
|
|
266
|
-
|
|
292
|
+
optionalPropertyType, // (type | undefined)
|
|
267
293
|
undefined) // members
|
|
268
294
|
|
|
269
295
|
if (properties.length == 0) return extra
|
package/src/index.ts
CHANGED
|
@@ -15,8 +15,10 @@ export { _array, array, arrayOf, AnyArrayValidator, ArrayConstraints, ArrayValid
|
|
|
15
15
|
export { boolean, 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,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractValidator } from '../types'
|
|
2
2
|
import { assertValidation } from '../errors'
|
|
3
3
|
|
|
4
4
|
/** A `Validator` validating `boolean`s. */
|
|
5
|
-
export class BooleanValidator extends
|
|
5
|
+
export class BooleanValidator extends AbstractValidator<boolean> {
|
|
6
6
|
validate(value: unknown): boolean {
|
|
7
7
|
assertValidation(typeof value === 'boolean', 'Value is not a "boolean"')
|
|
8
8
|
return value
|
|
@@ -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) {
|
package/src/validators/date.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { assertSchema, assertValidation, ValidationError } from '../errors'
|
|
2
|
-
import {
|
|
3
|
-
import { makeTupleRestIterable } from './tuple'
|
|
2
|
+
import { AbstractValidator, makeValidatorFactory } from '../types'
|
|
4
3
|
|
|
5
4
|
/** Lifted from AngularJS: matches a valid RFC 3339 string. */
|
|
6
5
|
const ISO_8601_REGEX = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))?)?$/
|
|
@@ -16,7 +15,7 @@ export interface DateConstraints {
|
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
/** A `Validator` validating dates and converting them to `Date` instances. */
|
|
19
|
-
export class DateValidator extends
|
|
18
|
+
export class DateValidator extends AbstractValidator<Date> {
|
|
20
19
|
readonly format?: 'iso' | 'timestamp'
|
|
21
20
|
readonly from?: Date
|
|
22
21
|
readonly until?: Date
|
|
@@ -65,14 +64,9 @@ export class DateValidator extends Validator<Date> {
|
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
export function _date(): DateValidator
|
|
71
|
-
export function _date(constraints: DateConstraints): DateValidator
|
|
72
|
-
|
|
73
|
-
export function _date(constraints?: DateConstraints): DateValidator {
|
|
74
|
-
return constraints ? new DateValidator(constraints) : anyDateValidator
|
|
67
|
+
export function _date(constraints: DateConstraints): DateValidator {
|
|
68
|
+
return new DateValidator(constraints)
|
|
75
69
|
}
|
|
76
70
|
|
|
77
71
|
/** Validate dates and convert them to `Date` instances. */
|
|
78
|
-
export const date =
|
|
72
|
+
export const date = makeValidatorFactory(new DateValidator(), _date)
|