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