@oscarpalmer/jhunal 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.mts +7 -3
- package/dist/constants.mjs +34 -12
- package/dist/helpers/message.helper.d.mts +11 -0
- package/dist/helpers/message.helper.mjs +70 -0
- package/dist/helpers/misc.helper.d.mts +22 -0
- package/dist/helpers/misc.helper.mjs +56 -0
- package/dist/index.d.mts +304 -275
- package/dist/index.mjs +187 -154
- package/dist/models/validation.model.d.mts +18 -7
- package/dist/schematic.d.mts +25 -7
- package/dist/schematic.mjs +6 -4
- package/dist/validator/base.validator.d.mts +6 -0
- package/dist/validator/base.validator.mjs +19 -0
- package/dist/validator/function.validator.d.mts +6 -0
- package/dist/validator/function.validator.mjs +9 -0
- package/dist/validator/named.handler.d.mts +6 -0
- package/dist/validator/named.handler.mjs +22 -0
- package/dist/validator/named.validator.d.mts +7 -0
- package/dist/validator/named.validator.mjs +39 -0
- package/dist/validator/object.validator.d.mts +7 -0
- package/dist/{validation.mjs → validator/object.validator.mjs} +20 -98
- package/dist/validator/schematic.validator.d.mts +7 -0
- package/dist/validator/schematic.validator.mjs +16 -0
- package/package.json +1 -1
- package/src/constants.ts +42 -10
- package/src/helpers/message.helper.ts +152 -0
- package/src/helpers/misc.helper.ts +93 -0
- package/src/index.ts +3 -3
- package/src/models/validation.model.ts +19 -6
- package/src/schematic.ts +43 -16
- package/src/validator/base.validator.ts +31 -0
- package/src/validator/function.validator.ts +9 -0
- package/src/validator/named.handler.ts +50 -0
- package/src/validator/named.validator.ts +62 -0
- package/src/{validation.ts → validator/object.validator.ts} +23 -181
- package/src/validator/schematic.validator.ts +25 -0
- package/dist/helpers.d.mts +0 -28
- package/dist/helpers.mjs +0 -120
- package/dist/validation.d.mts +0 -7
- package/src/helpers.ts +0 -249
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,228 @@
|
|
|
1
1
|
import { Constructor, GenericCallback, PlainObject, Simplify } from "@oscarpalmer/atoms/models";
|
|
2
2
|
import { Result } from "@oscarpalmer/atoms/result/models";
|
|
3
3
|
|
|
4
|
+
//#region src/models/schema.plain.model.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A generic schema allowing nested schemas, {@link SchemaEntry} values, or arrays of {@link SchemaEntry} as values
|
|
7
|
+
*/
|
|
8
|
+
type PlainSchema = {
|
|
9
|
+
[key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
|
|
10
|
+
} & {
|
|
11
|
+
$required?: never;
|
|
12
|
+
$type?: never;
|
|
13
|
+
$validators?: never;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A schema for validating objects
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const schema: Schema = {
|
|
21
|
+
* name: 'string',
|
|
22
|
+
* age: 'number',
|
|
23
|
+
* tags: ['string', 'number'],
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
type Schema = PlainSchema;
|
|
28
|
+
/**
|
|
29
|
+
* A union of all valid types for a single schema entry
|
|
30
|
+
*
|
|
31
|
+
* Can be a {@link Constructor}, {@link PlainSchema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
|
|
32
|
+
*/
|
|
33
|
+
type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
|
|
34
|
+
/**
|
|
35
|
+
* A property definition with explicit type(s), an optional requirement flag, and optional validators
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const prop: SchemaProperty = {
|
|
40
|
+
* $required: false,
|
|
41
|
+
* $type: ['string', 'number'],
|
|
42
|
+
* $validators: {
|
|
43
|
+
* string: (v) => v.length > 0,
|
|
44
|
+
* number: (v) => v > 0,
|
|
45
|
+
* },
|
|
46
|
+
* };
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
type SchemaProperty = {
|
|
50
|
+
/**
|
|
51
|
+
* Whether the property is required _(defaults to `true`)_
|
|
52
|
+
*/
|
|
53
|
+
$required?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The type(s) the property value must match; a single {@link SchemaPropertyType} or an array
|
|
56
|
+
*/
|
|
57
|
+
$type: SchemaPropertyType | SchemaPropertyType[];
|
|
58
|
+
/**
|
|
59
|
+
* Optional validators keyed by {@link ValueName}, applied during validation
|
|
60
|
+
*/
|
|
61
|
+
$validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* A union of valid types for a {@link SchemaProperty}'s `$type` field
|
|
65
|
+
*
|
|
66
|
+
* Can be a {@link Constructor}, {@link PlainSchema}, {@link Schematic}, {@link ValueName} string, or a custom validator function
|
|
67
|
+
*/
|
|
68
|
+
type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
|
|
69
|
+
/**
|
|
70
|
+
* A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
|
|
71
|
+
*
|
|
72
|
+
* Each key may hold a single validator or an array of validators that receive the typed value
|
|
73
|
+
*
|
|
74
|
+
* @template Value `$type` value(s) to derive validator keys from
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* const validators: PropertyValidators<'string'> = {
|
|
79
|
+
* string: (value) => value.length > 0,
|
|
80
|
+
* };
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
type PropertyValidators<Value> = { [Key in ExtractValueNames<Value>]?: ((value: Values[Key]) => boolean) | Array<(value: Values[Key]) => boolean> };
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/models/misc.model.d.ts
|
|
86
|
+
/**
|
|
87
|
+
* Removes duplicate types from a tuple, preserving first occurrence order
|
|
88
|
+
*
|
|
89
|
+
* @template Value Tuple to deduplicate
|
|
90
|
+
* @template Seen Accumulator for already-seen types _(internal)_
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* // DeduplicateTuple<['string', 'number', 'string']>
|
|
95
|
+
* // => ['string', 'number']
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
type DeduplicateTuple<Value extends unknown[], Seen extends unknown[] = []> = Value extends [infer Head, ...infer Tail] ? Head extends Seen[number] ? DeduplicateTuple<Tail, Seen> : DeduplicateTuple<Tail, [...Seen, Head]> : Seen;
|
|
99
|
+
/**
|
|
100
|
+
* Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
|
|
101
|
+
*
|
|
102
|
+
* @template Value Type to extract value names from
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* // ExtractValueNames<'string'> => 'string'
|
|
107
|
+
* // ExtractValueNames<['string', 'number']> => 'string' | 'number'
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends (infer Item)[] ? ExtractValueNames<Item> : Value extends readonly (infer Item)[] ? ExtractValueNames<Item> : never;
|
|
111
|
+
/**
|
|
112
|
+
* Determines whether a schema entry is optional
|
|
113
|
+
*
|
|
114
|
+
* Returns `true` if the entry is a {@link SchemaProperty} with `$required` set to `false`; otherwise returns `false`
|
|
115
|
+
*
|
|
116
|
+
* @template Value Schema entry to check
|
|
117
|
+
*/
|
|
118
|
+
type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
|
|
119
|
+
/**
|
|
120
|
+
* Extracts the last member from a union type by leveraging contravariance of function parameter types
|
|
121
|
+
*
|
|
122
|
+
* @template Value Union type
|
|
123
|
+
*/
|
|
124
|
+
type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
|
|
125
|
+
/**
|
|
126
|
+
* Extracts keys from an object type that are optional
|
|
127
|
+
*
|
|
128
|
+
* @template Value Object type to inspect
|
|
129
|
+
*/
|
|
130
|
+
type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
|
|
131
|
+
/**
|
|
132
|
+
* Extracts keys from an object type that are required _(i.e., not optional)_
|
|
133
|
+
*
|
|
134
|
+
* @template Value Object type to inspect
|
|
135
|
+
*/
|
|
136
|
+
type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
|
|
137
|
+
/**
|
|
138
|
+
* Generates all permutations of a tuple type
|
|
139
|
+
*
|
|
140
|
+
* Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
|
|
141
|
+
*
|
|
142
|
+
* @template Tuple Tuple to permute
|
|
143
|
+
* @template Elput Accumulator for the current permutation _(internal; name is Tuple backwards)_
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* // TuplePermutations<['string', 'number']>
|
|
148
|
+
* // => ['string', 'number'] | ['number', 'string']
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
type TuplePermutations<Tuple extends unknown[], Elput extends unknown[] = []> = Tuple['length'] extends 0 ? Elput : { [Key in keyof Tuple]: TuplePermutations<TupleRemoveAt<Tuple, Key & `${number}`>, [...Elput, Tuple[Key]]> }[keyof Tuple & `${number}`];
|
|
152
|
+
/**
|
|
153
|
+
* Removes the element at a given index from a tuple
|
|
154
|
+
*
|
|
155
|
+
* Used internally by {@link TuplePermutations}
|
|
156
|
+
*
|
|
157
|
+
* @template Items Tuple to remove from
|
|
158
|
+
* @template Item Index as a string literal
|
|
159
|
+
* @template Prefix Accumulator for elements before the target _(internal)_
|
|
160
|
+
*/
|
|
161
|
+
type TupleRemoveAt<Items extends unknown[], Item extends string, Prefix extends unknown[] = []> = Items extends [infer Head, ...infer Tail] ? `${Prefix['length']}` extends Item ? [...Prefix, ...Tail] : TupleRemoveAt<Tail, Item, [...Prefix, Head]> : Prefix;
|
|
162
|
+
/**
|
|
163
|
+
* Converts a union type into an intersection
|
|
164
|
+
*
|
|
165
|
+
* Uses the contravariance of function parameter types to collapse a union into an intersection
|
|
166
|
+
*
|
|
167
|
+
* @template Value Union type to convert
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* // UnionToIntersection<{ a: 1 } | { b: 2 }>
|
|
172
|
+
* // => { a: 1 } & { b: 2 }
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => void : never) extends ((value: infer Item) => void) ? Item : never;
|
|
176
|
+
/**
|
|
177
|
+
* Converts a union type into an ordered tuple
|
|
178
|
+
*
|
|
179
|
+
* Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
|
|
180
|
+
*
|
|
181
|
+
* @template Value Union type to convert
|
|
182
|
+
* @template Items Accumulator for the resulting tuple _(internal)_
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* // UnionToTuple<'a' | 'b' | 'c'>
|
|
187
|
+
* // => ['a', 'b', 'c']
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
|
|
191
|
+
/**
|
|
192
|
+
* Unwraps a single-element tuple to its inner type
|
|
193
|
+
*
|
|
194
|
+
* For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
|
|
195
|
+
*
|
|
196
|
+
* @template Value Tuple to potentially unwrap
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* // UnwrapSingle<['string']> => 'string'
|
|
201
|
+
* // UnwrapSingle<['string', 'number']> => ['string', 'number'] | ['number', 'string']
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
|
|
205
|
+
/**
|
|
206
|
+
* A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
|
|
207
|
+
*/
|
|
208
|
+
type ValueName = keyof Values;
|
|
209
|
+
/**
|
|
210
|
+
* Maps {@link ValueName} strings to their TypeScript equivalents
|
|
211
|
+
*/
|
|
212
|
+
type Values = {
|
|
213
|
+
array: unknown[];
|
|
214
|
+
bigint: bigint;
|
|
215
|
+
boolean: boolean;
|
|
216
|
+
date: Date;
|
|
217
|
+
function: Function;
|
|
218
|
+
null: null;
|
|
219
|
+
number: number;
|
|
220
|
+
object: object;
|
|
221
|
+
string: string;
|
|
222
|
+
symbol: symbol;
|
|
223
|
+
undefined: undefined;
|
|
224
|
+
};
|
|
225
|
+
//#endregion
|
|
4
226
|
//#region src/models/infer.model.d.ts
|
|
5
227
|
/**
|
|
6
228
|
* Infers the TypeScript type from a {@link Schema} definition
|
|
@@ -181,72 +403,9 @@ type TypedSchemaRequired<Model extends PlainObject> = {
|
|
|
181
403
|
$required?: true;
|
|
182
404
|
} & TypedSchema<Model>;
|
|
183
405
|
//#endregion
|
|
184
|
-
//#region src/
|
|
185
|
-
type ReportingInformation = Record<ReportingType, boolean> & {
|
|
186
|
-
type: ReportingType;
|
|
187
|
-
};
|
|
188
|
-
/**
|
|
189
|
-
* Controls how validation failures are reported
|
|
190
|
-
*
|
|
191
|
-
* - `'none'`, returns a boolean _(default)_
|
|
192
|
-
* - `'first'`, returns the first failure as a `Result`
|
|
193
|
-
* - `'all'`, returns all failures as a `Result` _(from same level)_
|
|
194
|
-
* - `'throw'`, throws a {@link ValidationError} on failure
|
|
195
|
-
*/
|
|
196
|
-
type ReportingType = 'all' | 'first' | 'none' | 'throw';
|
|
197
|
-
/**
|
|
198
|
-
* Thrown when a schema definition is invalid
|
|
199
|
-
*/
|
|
200
|
-
declare class SchematicError extends Error {
|
|
201
|
-
constructor(message: string);
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
|
|
205
|
-
*/
|
|
206
|
-
declare class ValidationError extends Error {
|
|
207
|
-
readonly information: ValidationInformation[];
|
|
208
|
-
constructor(information: ValidationInformation[]);
|
|
209
|
-
}
|
|
406
|
+
//#region src/schematic.d.ts
|
|
210
407
|
/**
|
|
211
|
-
*
|
|
212
|
-
*/
|
|
213
|
-
type ValidationInformation = {
|
|
214
|
-
/** The key path of the property that failed */key: ValidationInformationKey; /** Human-readable description of the failure */
|
|
215
|
-
message: string; /** The validator function that failed, if the failure was from a `$validators` entry */
|
|
216
|
-
validator?: GenericCallback; /** The value that was provided */
|
|
217
|
-
value: unknown;
|
|
218
|
-
};
|
|
219
|
-
/**
|
|
220
|
-
*
|
|
221
|
-
*/
|
|
222
|
-
type ValidationInformationKey = {
|
|
223
|
-
full: string;
|
|
224
|
-
short: string;
|
|
225
|
-
};
|
|
226
|
-
/**
|
|
227
|
-
* Options for validation
|
|
228
|
-
*/
|
|
229
|
-
type ValidationOptions<Errors extends ReportingType> = {
|
|
230
|
-
/**
|
|
231
|
-
* How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
|
|
232
|
-
*/
|
|
233
|
-
errors?: Errors;
|
|
234
|
-
/**
|
|
235
|
-
* Validate if unknown keys are present in the object? _(defaults to `false`)_
|
|
236
|
-
*/
|
|
237
|
-
strict?: boolean;
|
|
238
|
-
};
|
|
239
|
-
type Validator = (input: unknown, parameters: ValidatorParameters, get: boolean) => boolean | ValidationInformation[];
|
|
240
|
-
type ValidatorParameters = {
|
|
241
|
-
information?: ValidationInformation[];
|
|
242
|
-
output: PlainObject;
|
|
243
|
-
reporting: ReportingInformation;
|
|
244
|
-
strict: boolean;
|
|
245
|
-
};
|
|
246
|
-
//#endregion
|
|
247
|
-
//#region src/schematic.d.ts
|
|
248
|
-
/**
|
|
249
|
-
* A schematic for validating objects
|
|
408
|
+
* A schematic for validating objects
|
|
250
409
|
*/
|
|
251
410
|
declare class Schematic<Model> {
|
|
252
411
|
#private;
|
|
@@ -260,7 +419,7 @@ declare class Schematic<Model> {
|
|
|
260
419
|
* @param options Validation options
|
|
261
420
|
* @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
|
|
262
421
|
*/
|
|
263
|
-
get(value: unknown, options:
|
|
422
|
+
get(value: unknown, options: GetOptions<'throw'>): Model;
|
|
264
423
|
/**
|
|
265
424
|
* Parse a value according to the schema
|
|
266
425
|
*
|
|
@@ -278,7 +437,7 @@ declare class Schematic<Model> {
|
|
|
278
437
|
* @param options Validation options
|
|
279
438
|
* @returns Result holding deeply cloned value or all validation information
|
|
280
439
|
*/
|
|
281
|
-
get(value: unknown, options:
|
|
440
|
+
get(value: unknown, options: GetOptions<'all'>): Result<Model, ValidationInformation[]>;
|
|
282
441
|
/**
|
|
283
442
|
* Parse a value according to the schema
|
|
284
443
|
*
|
|
@@ -296,7 +455,7 @@ declare class Schematic<Model> {
|
|
|
296
455
|
* @param options Validation options
|
|
297
456
|
* @returns Result holding deeply cloned value or all validation information
|
|
298
457
|
*/
|
|
299
|
-
get(value: unknown, options:
|
|
458
|
+
get(value: unknown, options: GetOptions<'first'>): Result<Model, ValidationInformation>;
|
|
300
459
|
/**
|
|
301
460
|
* Parse a value according to the schema
|
|
302
461
|
*
|
|
@@ -306,6 +465,15 @@ declare class Schematic<Model> {
|
|
|
306
465
|
* @returns Result holding deeply cloned value or all validation information
|
|
307
466
|
*/
|
|
308
467
|
get(value: unknown, errors: 'first'): Result<Model, ValidationInformation>;
|
|
468
|
+
/**
|
|
469
|
+
* Parse a value according to the schema
|
|
470
|
+
*
|
|
471
|
+
* Returns a deeply cloned version of the value or `undefined` if the value does not match the schema
|
|
472
|
+
* @param value Value to parse
|
|
473
|
+
* @param options Validation options
|
|
474
|
+
* @returns Deeply cloned value, or `undefined` if it's invalid
|
|
475
|
+
*/
|
|
476
|
+
get(value: unknown, options: GetOptions<'none'>): Model | undefined;
|
|
309
477
|
/**
|
|
310
478
|
* Parse a value according to the schema
|
|
311
479
|
*
|
|
@@ -323,7 +491,7 @@ declare class Schematic<Model> {
|
|
|
323
491
|
* @param options Validation options
|
|
324
492
|
* @returns `true` if the value matches the schema, otherwise throws an error
|
|
325
493
|
*/
|
|
326
|
-
is(value: unknown, options:
|
|
494
|
+
is(value: unknown, options: IsOptions<'throw'>): asserts value is Model;
|
|
327
495
|
/**
|
|
328
496
|
* Does the value match the schema?
|
|
329
497
|
*
|
|
@@ -341,7 +509,7 @@ declare class Schematic<Model> {
|
|
|
341
509
|
* @param options Validation options
|
|
342
510
|
* @returns Result holding `true` or all validation information
|
|
343
511
|
*/
|
|
344
|
-
is(value: unknown, options:
|
|
512
|
+
is(value: unknown, options: IsOptions<'all'>): Result<true, ValidationInformation[]>;
|
|
345
513
|
/**
|
|
346
514
|
* Does the value match the schema?
|
|
347
515
|
*
|
|
@@ -359,7 +527,7 @@ declare class Schematic<Model> {
|
|
|
359
527
|
* @param options Validation options
|
|
360
528
|
* @returns `true` if the value matches the schema, otherwise `false`
|
|
361
529
|
*/
|
|
362
|
-
is(value: unknown, options:
|
|
530
|
+
is(value: unknown, options: IsOptions<'first'>): Result<true, ValidationInformation>;
|
|
363
531
|
/**
|
|
364
532
|
* Does the value match the schema?
|
|
365
533
|
*
|
|
@@ -369,6 +537,15 @@ declare class Schematic<Model> {
|
|
|
369
537
|
* @returns `true` if the value matches the schema, otherwise `false`
|
|
370
538
|
*/
|
|
371
539
|
is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
|
|
540
|
+
/**
|
|
541
|
+
* Does the value match the schema?
|
|
542
|
+
*
|
|
543
|
+
* Will validate that the value matches the schema and return `true` or `false`, without any validation information for validation failures
|
|
544
|
+
* @param value Value to validate
|
|
545
|
+
* @param options Validation options
|
|
546
|
+
* @returns `true` if the value matches the schema, otherwise `false`
|
|
547
|
+
*/
|
|
548
|
+
is(value: unknown, options: IsOptions<'none'>): value is Model;
|
|
372
549
|
/**
|
|
373
550
|
* Does the value match the schema?
|
|
374
551
|
*
|
|
@@ -396,229 +573,81 @@ declare function schematic<Model extends Schema>(schema: Model): Schematic<Infer
|
|
|
396
573
|
*/
|
|
397
574
|
declare function schematic<Model extends PlainObject>(schema: TypedSchema<Model>): Schematic<Model>;
|
|
398
575
|
//#endregion
|
|
399
|
-
//#region src/models/
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
*/
|
|
403
|
-
type PlainSchema = {
|
|
404
|
-
[key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
|
|
405
|
-
} & {
|
|
406
|
-
$required?: never;
|
|
407
|
-
$type?: never;
|
|
408
|
-
$validators?: never;
|
|
576
|
+
//#region src/models/validation.model.d.ts
|
|
577
|
+
type ReportingInformation = Record<ReportingType, boolean> & {
|
|
578
|
+
type: ReportingType;
|
|
409
579
|
};
|
|
410
580
|
/**
|
|
411
|
-
*
|
|
581
|
+
* Controls how validation failures are reported
|
|
412
582
|
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* age: 'number',
|
|
418
|
-
* tags: ['string', 'number'],
|
|
419
|
-
* };
|
|
420
|
-
* ```
|
|
583
|
+
* - `'none'`, returns a boolean _(default)_
|
|
584
|
+
* - `'first'`, returns the first failure as a `Result`
|
|
585
|
+
* - `'all'`, returns all failures as a `Result` _(from same level)_
|
|
586
|
+
* - `'throw'`, throws a {@link ValidationError} on failure
|
|
421
587
|
*/
|
|
422
|
-
type
|
|
588
|
+
type ReportingType = 'all' | 'first' | 'none' | 'throw';
|
|
423
589
|
/**
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* Can be a {@link Constructor}, {@link PlainSchema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
|
|
590
|
+
* Thrown when a schema definition is invalid
|
|
427
591
|
*/
|
|
428
|
-
|
|
592
|
+
declare class SchematicError extends Error {
|
|
593
|
+
constructor(message: string);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
|
|
597
|
+
*/
|
|
598
|
+
declare class ValidationError extends Error {
|
|
599
|
+
readonly information: ValidationInformation[];
|
|
600
|
+
constructor(information: ValidationInformation[]);
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Describes a single validation failure
|
|
604
|
+
*/
|
|
605
|
+
type ValidationInformation = {
|
|
606
|
+
/** The key path of the property that failed */key: ValidationInformationKey; /** Human-readable description of the failure */
|
|
607
|
+
message: string; /** The validator function that failed, if the failure was from a `$validators` entry */
|
|
608
|
+
validator?: GenericCallback; /** The value that was provided */
|
|
609
|
+
value: unknown;
|
|
610
|
+
};
|
|
429
611
|
/**
|
|
430
|
-
* A property definition with explicit type(s), an optional requirement flag, and optional validators
|
|
431
612
|
*
|
|
432
|
-
* @example
|
|
433
|
-
* ```ts
|
|
434
|
-
* const prop: SchemaProperty = {
|
|
435
|
-
* $required: false,
|
|
436
|
-
* $type: ['string', 'number'],
|
|
437
|
-
* $validators: {
|
|
438
|
-
* string: (v) => v.length > 0,
|
|
439
|
-
* number: (v) => v > 0,
|
|
440
|
-
* },
|
|
441
|
-
* };
|
|
442
|
-
* ```
|
|
443
613
|
*/
|
|
444
|
-
type
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
614
|
+
type ValidationInformationKey = {
|
|
615
|
+
full: string;
|
|
616
|
+
short: string;
|
|
617
|
+
};
|
|
618
|
+
type BaseOptions<Errors extends ReportingType> = {
|
|
449
619
|
/**
|
|
450
|
-
*
|
|
620
|
+
* How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
|
|
451
621
|
*/
|
|
452
|
-
|
|
622
|
+
errors: Errors;
|
|
453
623
|
/**
|
|
454
|
-
*
|
|
624
|
+
* Validate if unknown keys are present in the object? _(defaults to `false`)_
|
|
455
625
|
*/
|
|
456
|
-
|
|
626
|
+
strict?: boolean;
|
|
457
627
|
};
|
|
458
628
|
/**
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
* Can be a {@link Constructor}, {@link PlainSchema}, {@link Schematic}, {@link ValueName} string, or a custom validator function
|
|
462
|
-
*/
|
|
463
|
-
type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
|
|
464
|
-
/**
|
|
465
|
-
* A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
|
|
466
|
-
*
|
|
467
|
-
* Each key may hold a single validator or an array of validators that receive the typed value
|
|
468
|
-
*
|
|
469
|
-
* @template Value `$type` value(s) to derive validator keys from
|
|
470
|
-
*
|
|
471
|
-
* @example
|
|
472
|
-
* ```ts
|
|
473
|
-
* const validators: PropertyValidators<'string'> = {
|
|
474
|
-
* string: (value) => value.length > 0,
|
|
475
|
-
* };
|
|
476
|
-
* ```
|
|
629
|
+
* Options for validating and getting a value from an input
|
|
477
630
|
*/
|
|
478
|
-
type
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
* @template Value Tuple to deduplicate
|
|
485
|
-
* @template Seen Accumulator for already-seen types _(internal)_
|
|
486
|
-
*
|
|
487
|
-
* @example
|
|
488
|
-
* ```ts
|
|
489
|
-
* // DeduplicateTuple<['string', 'number', 'string']>
|
|
490
|
-
* // => ['string', 'number']
|
|
491
|
-
* ```
|
|
492
|
-
*/
|
|
493
|
-
type DeduplicateTuple<Value extends unknown[], Seen extends unknown[] = []> = Value extends [infer Head, ...infer Tail] ? Head extends Seen[number] ? DeduplicateTuple<Tail, Seen> : DeduplicateTuple<Tail, [...Seen, Head]> : Seen;
|
|
494
|
-
/**
|
|
495
|
-
* Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
|
|
496
|
-
*
|
|
497
|
-
* @template Value Type to extract value names from
|
|
498
|
-
*
|
|
499
|
-
* @example
|
|
500
|
-
* ```ts
|
|
501
|
-
* // ExtractValueNames<'string'> => 'string'
|
|
502
|
-
* // ExtractValueNames<['string', 'number']> => 'string' | 'number'
|
|
503
|
-
* ```
|
|
504
|
-
*/
|
|
505
|
-
type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends (infer Item)[] ? ExtractValueNames<Item> : Value extends readonly (infer Item)[] ? ExtractValueNames<Item> : never;
|
|
506
|
-
/**
|
|
507
|
-
* Determines whether a schema entry is optional
|
|
508
|
-
*
|
|
509
|
-
* Returns `true` if the entry is a {@link SchemaProperty} with `$required` set to `false`; otherwise returns `false`
|
|
510
|
-
*
|
|
511
|
-
* @template Value Schema entry to check
|
|
512
|
-
*/
|
|
513
|
-
type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
|
|
514
|
-
/**
|
|
515
|
-
* Extracts the last member from a union type by leveraging contravariance of function parameter types
|
|
516
|
-
*
|
|
517
|
-
* @template Value Union type
|
|
518
|
-
*/
|
|
519
|
-
type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
|
|
520
|
-
/**
|
|
521
|
-
* Extracts keys from an object type that are optional
|
|
522
|
-
*
|
|
523
|
-
* @template Value Object type to inspect
|
|
524
|
-
*/
|
|
525
|
-
type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
|
|
526
|
-
/**
|
|
527
|
-
* Extracts keys from an object type that are required _(i.e., not optional)_
|
|
528
|
-
*
|
|
529
|
-
* @template Value Object type to inspect
|
|
530
|
-
*/
|
|
531
|
-
type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
|
|
532
|
-
/**
|
|
533
|
-
* Generates all permutations of a tuple type
|
|
534
|
-
*
|
|
535
|
-
* Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
|
|
536
|
-
*
|
|
537
|
-
* @template Tuple Tuple to permute
|
|
538
|
-
* @template Elput Accumulator for the current permutation _(internal; name is Tuple backwards)_
|
|
539
|
-
*
|
|
540
|
-
* @example
|
|
541
|
-
* ```ts
|
|
542
|
-
* // TuplePermutations<['string', 'number']>
|
|
543
|
-
* // => ['string', 'number'] | ['number', 'string']
|
|
544
|
-
* ```
|
|
545
|
-
*/
|
|
546
|
-
type TuplePermutations<Tuple extends unknown[], Elput extends unknown[] = []> = Tuple['length'] extends 0 ? Elput : { [Key in keyof Tuple]: TuplePermutations<TupleRemoveAt<Tuple, Key & `${number}`>, [...Elput, Tuple[Key]]> }[keyof Tuple & `${number}`];
|
|
547
|
-
/**
|
|
548
|
-
* Removes the element at a given index from a tuple
|
|
549
|
-
*
|
|
550
|
-
* Used internally by {@link TuplePermutations}
|
|
551
|
-
*
|
|
552
|
-
* @template Items Tuple to remove from
|
|
553
|
-
* @template Item Index as a string literal
|
|
554
|
-
* @template Prefix Accumulator for elements before the target _(internal)_
|
|
555
|
-
*/
|
|
556
|
-
type TupleRemoveAt<Items extends unknown[], Item extends string, Prefix extends unknown[] = []> = Items extends [infer Head, ...infer Tail] ? `${Prefix['length']}` extends Item ? [...Prefix, ...Tail] : TupleRemoveAt<Tail, Item, [...Prefix, Head]> : Prefix;
|
|
557
|
-
/**
|
|
558
|
-
* Converts a union type into an intersection
|
|
559
|
-
*
|
|
560
|
-
* Uses the contravariance of function parameter types to collapse a union into an intersection
|
|
561
|
-
*
|
|
562
|
-
* @template Value Union type to convert
|
|
563
|
-
*
|
|
564
|
-
* @example
|
|
565
|
-
* ```ts
|
|
566
|
-
* // UnionToIntersection<{ a: 1 } | { b: 2 }>
|
|
567
|
-
* // => { a: 1 } & { b: 2 }
|
|
568
|
-
* ```
|
|
569
|
-
*/
|
|
570
|
-
type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => void : never) extends ((value: infer Item) => void) ? Item : never;
|
|
571
|
-
/**
|
|
572
|
-
* Converts a union type into an ordered tuple
|
|
573
|
-
*
|
|
574
|
-
* Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
|
|
575
|
-
*
|
|
576
|
-
* @template Value Union type to convert
|
|
577
|
-
* @template Items Accumulator for the resulting tuple _(internal)_
|
|
578
|
-
*
|
|
579
|
-
* @example
|
|
580
|
-
* ```ts
|
|
581
|
-
* // UnionToTuple<'a' | 'b' | 'c'>
|
|
582
|
-
* // => ['a', 'b', 'c']
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
|
|
586
|
-
/**
|
|
587
|
-
* Unwraps a single-element tuple to its inner type
|
|
588
|
-
*
|
|
589
|
-
* For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
|
|
590
|
-
*
|
|
591
|
-
* @template Value Tuple to potentially unwrap
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```ts
|
|
595
|
-
* // UnwrapSingle<['string']> => 'string'
|
|
596
|
-
* // UnwrapSingle<['string', 'number']> => ['string', 'number'] | ['number', 'string']
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
|
|
600
|
-
/**
|
|
601
|
-
* A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
|
|
602
|
-
*/
|
|
603
|
-
type ValueName = keyof Values;
|
|
631
|
+
type GetOptions<Errors extends ReportingType> = BaseOptions<Errors> & {
|
|
632
|
+
/**
|
|
633
|
+
* Get a deeply cloned version of the input? _(defaults to `true`)_
|
|
634
|
+
*/
|
|
635
|
+
clone?: boolean;
|
|
636
|
+
};
|
|
604
637
|
/**
|
|
605
|
-
*
|
|
638
|
+
* Options for validation an input value
|
|
606
639
|
*/
|
|
607
|
-
type
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
object: object;
|
|
616
|
-
string: string;
|
|
617
|
-
symbol: symbol;
|
|
618
|
-
undefined: undefined;
|
|
640
|
+
type IsOptions<Errors extends ReportingType> = BaseOptions<Errors>;
|
|
641
|
+
type Validator = (input: unknown, parameters: ValidatorParameters, get: boolean) => true | ValidationInformation[];
|
|
642
|
+
type ValidatorParameters = {
|
|
643
|
+
clone: boolean;
|
|
644
|
+
information?: ValidationInformation[];
|
|
645
|
+
output: PlainObject;
|
|
646
|
+
reporting: ReportingInformation;
|
|
647
|
+
strict: boolean;
|
|
619
648
|
};
|
|
620
649
|
//#endregion
|
|
621
|
-
//#region src/helpers.d.ts
|
|
650
|
+
//#region src/helpers/misc.helper.d.ts
|
|
622
651
|
/**
|
|
623
652
|
* Creates a validator function for a given constructor
|
|
624
653
|
* @param constructor - Constructor to check against
|
|
@@ -633,4 +662,4 @@ declare function instanceOf<Instance>(constructor: Constructor<Instance>): (valu
|
|
|
633
662
|
*/
|
|
634
663
|
declare function isSchematic(value: unknown): value is Schematic<never>;
|
|
635
664
|
//#endregion
|
|
636
|
-
export { type
|
|
665
|
+
export { type GetOptions, type IsOptions, type Schema, type Schematic, SchematicError, type TypedSchema, ValidationError, instanceOf, isSchematic, schematic };
|