@oscarpalmer/jhunal 0.21.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 +318 -328
- package/dist/index.mjs +324 -295
- package/dist/models/schema.plain.model.d.mts +2 -8
- package/dist/models/validation.model.d.mts +26 -59
- package/dist/schematic.d.mts +28 -10
- package/dist/schematic.mjs +15 -16
- 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/validator/object.validator.mjs +167 -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 +42 -10
- package/src/helpers/message.helper.ts +152 -0
- package/src/helpers/misc.helper.ts +93 -0
- package/src/index.ts +7 -2
- package/src/models/schema.plain.model.ts +1 -8
- package/src/models/validation.model.ts +55 -77
- package/src/schematic.ts +49 -27
- 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/validator/object.validator.ts +340 -0
- package/src/validator/schematic.validator.ts +25 -0
- package/dist/helpers.d.mts +0 -28
- package/dist/helpers.mjs +0 -119
- package/dist/validation/property.validation.d.mts +0 -7
- package/dist/validation/property.validation.mjs +0 -92
- package/dist/validation/value.validation.d.mts +0 -7
- package/dist/validation/value.validation.mjs +0 -162
- package/src/helpers.ts +0 -246
- package/src/validation/property.validation.ts +0 -217
- package/src/validation/value.validation.ts +0 -293
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,128 +403,32 @@ type TypedSchemaRequired<Model extends PlainObject> = {
|
|
|
181
403
|
$required?: true;
|
|
182
404
|
} & TypedSchema<Model>;
|
|
183
405
|
//#endregion
|
|
184
|
-
//#region src/
|
|
185
|
-
/**
|
|
186
|
-
* Controls how validation failures are reported
|
|
187
|
-
*
|
|
188
|
-
* - `'none'` — returns a boolean _(default)_
|
|
189
|
-
* - `'first'` — returns the first failure as a `Result`
|
|
190
|
-
* - `'all'` — returns all failures as a `Result` _(from same level)_
|
|
191
|
-
* - `'throw'` — throws a {@link ValidationError} on failure
|
|
192
|
-
*/
|
|
193
|
-
type ReportingType = 'all' | 'first' | 'none' | 'throw';
|
|
194
|
-
/**
|
|
195
|
-
* Thrown when a schema definition is invalid
|
|
196
|
-
*/
|
|
197
|
-
declare class SchematicError extends Error {
|
|
198
|
-
constructor(message: string);
|
|
199
|
-
}
|
|
406
|
+
//#region src/schematic.d.ts
|
|
200
407
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* @example
|
|
204
|
-
* ```ts
|
|
205
|
-
* const parsed: ValidatedProperty = {
|
|
206
|
-
* key: 'age',
|
|
207
|
-
* required: true,
|
|
208
|
-
* types: ['number'],
|
|
209
|
-
* validators: { number: [(v) => v > 0] },
|
|
210
|
-
* };
|
|
211
|
-
* ```
|
|
408
|
+
* A schematic for validating objects
|
|
212
409
|
*/
|
|
213
|
-
|
|
410
|
+
declare class Schematic<Model> {
|
|
411
|
+
#private;
|
|
412
|
+
private readonly $schematic;
|
|
413
|
+
constructor(validator: Validator);
|
|
214
414
|
/**
|
|
215
|
-
*
|
|
415
|
+
* Parse a value according to the schema
|
|
416
|
+
*
|
|
417
|
+
* Returns a deeply cloned version of the value or throws an error for the first property that fails validation
|
|
418
|
+
* @param value Value to parse
|
|
419
|
+
* @param options Validation options
|
|
420
|
+
* @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
|
|
216
421
|
*/
|
|
217
|
-
|
|
422
|
+
get(value: unknown, options: GetOptions<'throw'>): Model;
|
|
218
423
|
/**
|
|
219
|
-
*
|
|
424
|
+
* Parse a value according to the schema
|
|
425
|
+
*
|
|
426
|
+
* Returns a deeply cloned version of the value or throws an error for the first property that fails validation
|
|
427
|
+
* @param value Value to parse
|
|
428
|
+
* @param errors Reporting type
|
|
429
|
+
* @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
|
|
220
430
|
*/
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* The allowed types for this property
|
|
224
|
-
*/
|
|
225
|
-
types: ValidatedPropertyType[];
|
|
226
|
-
/**
|
|
227
|
-
* Custom validators grouped by {@link ValueName}
|
|
228
|
-
*/
|
|
229
|
-
validators: ValidatedPropertyValidators;
|
|
230
|
-
};
|
|
231
|
-
/**
|
|
232
|
-
* A union of valid types for a {@link ValidatedProperty}'s `types` array
|
|
233
|
-
*
|
|
234
|
-
* Can be a callback _(custom validator)_, a {@link Schematic}, a nested {@link ValidatedProperty}, or a {@link ValueName} string
|
|
235
|
-
*/
|
|
236
|
-
type ValidatedPropertyType = GenericCallback | ValidatedProperty[] | Schematic<unknown> | ValueName;
|
|
237
|
-
/**
|
|
238
|
-
* A map of validator functions keyed by {@link ValueName}, used at runtime in {@link ValidatedProperty}
|
|
239
|
-
*
|
|
240
|
-
* Each key holds an array of validator functions that receive an `unknown` value and return a `boolean`
|
|
241
|
-
*/
|
|
242
|
-
type ValidatedPropertyValidators = { [Key in ValueName]?: Array<(value: unknown) => boolean> };
|
|
243
|
-
/**
|
|
244
|
-
* Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
|
|
245
|
-
*/
|
|
246
|
-
declare class ValidationError extends Error {
|
|
247
|
-
readonly information: ValidationInformation[];
|
|
248
|
-
constructor(information: ValidationInformation[]);
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Describes a single validation failure
|
|
252
|
-
*/
|
|
253
|
-
type ValidationInformation = {
|
|
254
|
-
/** The key path of the property that failed */key: ValidationInformationKey; /** Human-readable description of the failure */
|
|
255
|
-
message: string; /** The validator function that failed, if the failure was from a `$validators` entry */
|
|
256
|
-
validator?: GenericCallback; /** The value that was provided */
|
|
257
|
-
value: unknown;
|
|
258
|
-
};
|
|
259
|
-
/**
|
|
260
|
-
*
|
|
261
|
-
*/
|
|
262
|
-
type ValidationInformationKey = {
|
|
263
|
-
full: string;
|
|
264
|
-
short: string;
|
|
265
|
-
};
|
|
266
|
-
/**
|
|
267
|
-
* Options for validation
|
|
268
|
-
*/
|
|
269
|
-
type ValidationOptions<Errors extends ReportingType> = {
|
|
270
|
-
/**
|
|
271
|
-
* How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
|
|
272
|
-
*/
|
|
273
|
-
errors?: Errors;
|
|
274
|
-
/**
|
|
275
|
-
* Validate if unknown keys are present in the object? _(defaults to `false`)_
|
|
276
|
-
*/
|
|
277
|
-
strict?: boolean;
|
|
278
|
-
};
|
|
279
|
-
//#endregion
|
|
280
|
-
//#region src/schematic.d.ts
|
|
281
|
-
/**
|
|
282
|
-
* A schematic for validating objects
|
|
283
|
-
*/
|
|
284
|
-
declare class Schematic<Model> {
|
|
285
|
-
#private;
|
|
286
|
-
private readonly $schematic;
|
|
287
|
-
constructor(properties: ValidatedProperty[]);
|
|
288
|
-
/**
|
|
289
|
-
* Parse a value according to the schema
|
|
290
|
-
*
|
|
291
|
-
* Returns a deeply cloned version of the value or throws an error for the first property that fails validation
|
|
292
|
-
* @param value Value to parse
|
|
293
|
-
* @param options Validation options
|
|
294
|
-
* @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
|
|
295
|
-
*/
|
|
296
|
-
get(value: unknown, options: ValidationOptions<'throw'>): Model;
|
|
297
|
-
/**
|
|
298
|
-
* Parse a value according to the schema
|
|
299
|
-
*
|
|
300
|
-
* Returns a deeply cloned version of the value or throws an error for the first property that fails validation
|
|
301
|
-
* @param value Value to parse
|
|
302
|
-
* @param errors Reporting type
|
|
303
|
-
* @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
|
|
304
|
-
*/
|
|
305
|
-
get(value: unknown, errors: 'throw'): Model;
|
|
431
|
+
get(value: unknown, errors: 'throw'): Model;
|
|
306
432
|
/**
|
|
307
433
|
* Parse a value according to the schema
|
|
308
434
|
*
|
|
@@ -311,7 +437,7 @@ declare class Schematic<Model> {
|
|
|
311
437
|
* @param options Validation options
|
|
312
438
|
* @returns Result holding deeply cloned value or all validation information
|
|
313
439
|
*/
|
|
314
|
-
get(value: unknown, options:
|
|
440
|
+
get(value: unknown, options: GetOptions<'all'>): Result<Model, ValidationInformation[]>;
|
|
315
441
|
/**
|
|
316
442
|
* Parse a value according to the schema
|
|
317
443
|
*
|
|
@@ -329,7 +455,7 @@ declare class Schematic<Model> {
|
|
|
329
455
|
* @param options Validation options
|
|
330
456
|
* @returns Result holding deeply cloned value or all validation information
|
|
331
457
|
*/
|
|
332
|
-
get(value: unknown, options:
|
|
458
|
+
get(value: unknown, options: GetOptions<'first'>): Result<Model, ValidationInformation>;
|
|
333
459
|
/**
|
|
334
460
|
* Parse a value according to the schema
|
|
335
461
|
*
|
|
@@ -339,6 +465,15 @@ declare class Schematic<Model> {
|
|
|
339
465
|
* @returns Result holding deeply cloned value or all validation information
|
|
340
466
|
*/
|
|
341
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;
|
|
342
477
|
/**
|
|
343
478
|
* Parse a value according to the schema
|
|
344
479
|
*
|
|
@@ -356,7 +491,7 @@ declare class Schematic<Model> {
|
|
|
356
491
|
* @param options Validation options
|
|
357
492
|
* @returns `true` if the value matches the schema, otherwise throws an error
|
|
358
493
|
*/
|
|
359
|
-
is(value: unknown, options:
|
|
494
|
+
is(value: unknown, options: IsOptions<'throw'>): asserts value is Model;
|
|
360
495
|
/**
|
|
361
496
|
* Does the value match the schema?
|
|
362
497
|
*
|
|
@@ -374,7 +509,7 @@ declare class Schematic<Model> {
|
|
|
374
509
|
* @param options Validation options
|
|
375
510
|
* @returns Result holding `true` or all validation information
|
|
376
511
|
*/
|
|
377
|
-
is(value: unknown, options:
|
|
512
|
+
is(value: unknown, options: IsOptions<'all'>): Result<true, ValidationInformation[]>;
|
|
378
513
|
/**
|
|
379
514
|
* Does the value match the schema?
|
|
380
515
|
*
|
|
@@ -392,7 +527,7 @@ declare class Schematic<Model> {
|
|
|
392
527
|
* @param options Validation options
|
|
393
528
|
* @returns `true` if the value matches the schema, otherwise `false`
|
|
394
529
|
*/
|
|
395
|
-
is(value: unknown, options:
|
|
530
|
+
is(value: unknown, options: IsOptions<'first'>): Result<true, ValidationInformation>;
|
|
396
531
|
/**
|
|
397
532
|
* Does the value match the schema?
|
|
398
533
|
*
|
|
@@ -402,6 +537,15 @@ declare class Schematic<Model> {
|
|
|
402
537
|
* @returns `true` if the value matches the schema, otherwise `false`
|
|
403
538
|
*/
|
|
404
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;
|
|
405
549
|
/**
|
|
406
550
|
* Does the value match the schema?
|
|
407
551
|
*
|
|
@@ -429,235 +573,81 @@ declare function schematic<Model extends Schema>(schema: Model): Schematic<Infer
|
|
|
429
573
|
*/
|
|
430
574
|
declare function schematic<Model extends PlainObject>(schema: TypedSchema<Model>): Schematic<Model>;
|
|
431
575
|
//#endregion
|
|
432
|
-
//#region src/models/
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
*/
|
|
436
|
-
type PlainSchema = {
|
|
437
|
-
[key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
|
|
438
|
-
} & {
|
|
439
|
-
$required?: never;
|
|
440
|
-
$type?: never;
|
|
441
|
-
$validators?: never;
|
|
576
|
+
//#region src/models/validation.model.d.ts
|
|
577
|
+
type ReportingInformation = Record<ReportingType, boolean> & {
|
|
578
|
+
type: ReportingType;
|
|
442
579
|
};
|
|
443
580
|
/**
|
|
444
|
-
*
|
|
581
|
+
* Controls how validation failures are reported
|
|
445
582
|
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* age: 'number',
|
|
451
|
-
* tags: ['string', 'number'],
|
|
452
|
-
* };
|
|
453
|
-
* ```
|
|
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
|
|
454
587
|
*/
|
|
455
|
-
type
|
|
588
|
+
type ReportingType = 'all' | 'first' | 'none' | 'throw';
|
|
456
589
|
/**
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
* 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
|
|
460
591
|
*/
|
|
461
|
-
|
|
592
|
+
declare class SchematicError extends Error {
|
|
593
|
+
constructor(message: string);
|
|
594
|
+
}
|
|
462
595
|
/**
|
|
463
|
-
*
|
|
596
|
+
* Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
|
|
464
597
|
*/
|
|
465
|
-
|
|
466
|
-
|
|
598
|
+
declare class ValidationError extends Error {
|
|
599
|
+
readonly information: ValidationInformation[];
|
|
600
|
+
constructor(information: ValidationInformation[]);
|
|
467
601
|
}
|
|
468
602
|
/**
|
|
469
|
-
*
|
|
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
|
+
};
|
|
611
|
+
/**
|
|
470
612
|
*
|
|
471
|
-
* @example
|
|
472
|
-
* ```ts
|
|
473
|
-
* const prop: SchemaProperty = {
|
|
474
|
-
* $required: false,
|
|
475
|
-
* $type: ['string', 'number'],
|
|
476
|
-
* $validators: {
|
|
477
|
-
* string: (v) => v.length > 0,
|
|
478
|
-
* number: (v) => v > 0,
|
|
479
|
-
* },
|
|
480
|
-
* };
|
|
481
|
-
* ```
|
|
482
613
|
*/
|
|
483
|
-
type
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
614
|
+
type ValidationInformationKey = {
|
|
615
|
+
full: string;
|
|
616
|
+
short: string;
|
|
617
|
+
};
|
|
618
|
+
type BaseOptions<Errors extends ReportingType> = {
|
|
488
619
|
/**
|
|
489
|
-
*
|
|
620
|
+
* How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
|
|
490
621
|
*/
|
|
491
|
-
|
|
622
|
+
errors: Errors;
|
|
492
623
|
/**
|
|
493
|
-
*
|
|
624
|
+
* Validate if unknown keys are present in the object? _(defaults to `false`)_
|
|
494
625
|
*/
|
|
495
|
-
|
|
626
|
+
strict?: boolean;
|
|
496
627
|
};
|
|
497
628
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
* Can be a {@link Constructor}, {@link PlainSchema}, {@link Schematic}, {@link ValueName} string, or a custom validator function
|
|
501
|
-
*/
|
|
502
|
-
type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
|
|
503
|
-
/**
|
|
504
|
-
* A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
|
|
505
|
-
*
|
|
506
|
-
* Each key may hold a single validator or an array of validators that receive the typed value
|
|
507
|
-
*
|
|
508
|
-
* @template Value `$type` value(s) to derive validator keys from
|
|
509
|
-
*
|
|
510
|
-
* @example
|
|
511
|
-
* ```ts
|
|
512
|
-
* const validators: PropertyValidators<'string'> = {
|
|
513
|
-
* string: (value) => value.length > 0,
|
|
514
|
-
* };
|
|
515
|
-
* ```
|
|
516
|
-
*/
|
|
517
|
-
type PropertyValidators<Value> = { [Key in ExtractValueNames<Value>]?: ((value: Values[Key]) => boolean) | Array<(value: Values[Key]) => boolean> };
|
|
518
|
-
//#endregion
|
|
519
|
-
//#region src/models/misc.model.d.ts
|
|
520
|
-
/**
|
|
521
|
-
* Removes duplicate types from a tuple, preserving first occurrence order
|
|
522
|
-
*
|
|
523
|
-
* @template Value Tuple to deduplicate
|
|
524
|
-
* @template Seen Accumulator for already-seen types _(internal)_
|
|
525
|
-
*
|
|
526
|
-
* @example
|
|
527
|
-
* ```ts
|
|
528
|
-
* // DeduplicateTuple<['string', 'number', 'string']>
|
|
529
|
-
* // => ['string', 'number']
|
|
530
|
-
* ```
|
|
531
|
-
*/
|
|
532
|
-
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;
|
|
533
|
-
/**
|
|
534
|
-
* Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
|
|
535
|
-
*
|
|
536
|
-
* @template Value Type to extract value names from
|
|
537
|
-
*
|
|
538
|
-
* @example
|
|
539
|
-
* ```ts
|
|
540
|
-
* // ExtractValueNames<'string'> => 'string'
|
|
541
|
-
* // ExtractValueNames<['string', 'number']> => 'string' | 'number'
|
|
542
|
-
* ```
|
|
543
|
-
*/
|
|
544
|
-
type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends (infer Item)[] ? ExtractValueNames<Item> : Value extends readonly (infer Item)[] ? ExtractValueNames<Item> : never;
|
|
545
|
-
/**
|
|
546
|
-
* Determines whether a schema entry is optional
|
|
547
|
-
*
|
|
548
|
-
* Returns `true` if the entry is a {@link SchemaProperty} with `$required` set to `false`; otherwise returns `false`
|
|
549
|
-
*
|
|
550
|
-
* @template Value Schema entry to check
|
|
551
|
-
*/
|
|
552
|
-
type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
|
|
553
|
-
/**
|
|
554
|
-
* Extracts the last member from a union type by leveraging contravariance of function parameter types
|
|
555
|
-
*
|
|
556
|
-
* @template Value Union type
|
|
557
|
-
*/
|
|
558
|
-
type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
|
|
559
|
-
/**
|
|
560
|
-
* Extracts keys from an object type that are optional
|
|
561
|
-
*
|
|
562
|
-
* @template Value Object type to inspect
|
|
563
|
-
*/
|
|
564
|
-
type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
|
|
565
|
-
/**
|
|
566
|
-
* Extracts keys from an object type that are required _(i.e., not optional)_
|
|
567
|
-
*
|
|
568
|
-
* @template Value Object type to inspect
|
|
569
|
-
*/
|
|
570
|
-
type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
|
|
571
|
-
/**
|
|
572
|
-
* Generates all permutations of a tuple type
|
|
573
|
-
*
|
|
574
|
-
* Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
|
|
575
|
-
*
|
|
576
|
-
* @template Tuple Tuple to permute
|
|
577
|
-
* @template Elput Accumulator for the current permutation _(internal; name is Tuple backwards)_
|
|
578
|
-
*
|
|
579
|
-
* @example
|
|
580
|
-
* ```ts
|
|
581
|
-
* // TuplePermutations<['string', 'number']>
|
|
582
|
-
* // => ['string', 'number'] | ['number', 'string']
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
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}`];
|
|
586
|
-
/**
|
|
587
|
-
* Removes the element at a given index from a tuple
|
|
588
|
-
*
|
|
589
|
-
* Used internally by {@link TuplePermutations}
|
|
590
|
-
*
|
|
591
|
-
* @template Items Tuple to remove from
|
|
592
|
-
* @template Item Index as a string literal
|
|
593
|
-
* @template Prefix Accumulator for elements before the target _(internal)_
|
|
594
|
-
*/
|
|
595
|
-
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;
|
|
596
|
-
/**
|
|
597
|
-
* Converts a union type into an intersection
|
|
598
|
-
*
|
|
599
|
-
* Uses the contravariance of function parameter types to collapse a union into an intersection
|
|
600
|
-
*
|
|
601
|
-
* @template Value Union type to convert
|
|
602
|
-
*
|
|
603
|
-
* @example
|
|
604
|
-
* ```ts
|
|
605
|
-
* // UnionToIntersection<{ a: 1 } | { b: 2 }>
|
|
606
|
-
* // => { a: 1 } & { b: 2 }
|
|
607
|
-
* ```
|
|
629
|
+
* Options for validating and getting a value from an input
|
|
608
630
|
*/
|
|
609
|
-
type
|
|
610
|
-
/**
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
* @template Value Union type to convert
|
|
616
|
-
* @template Items Accumulator for the resulting tuple _(internal)_
|
|
617
|
-
*
|
|
618
|
-
* @example
|
|
619
|
-
* ```ts
|
|
620
|
-
* // UnionToTuple<'a' | 'b' | 'c'>
|
|
621
|
-
* // => ['a', 'b', 'c']
|
|
622
|
-
* ```
|
|
623
|
-
*/
|
|
624
|
-
type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
|
|
625
|
-
/**
|
|
626
|
-
* Unwraps a single-element tuple to its inner type
|
|
627
|
-
*
|
|
628
|
-
* For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
|
|
629
|
-
*
|
|
630
|
-
* @template Value Tuple to potentially unwrap
|
|
631
|
-
*
|
|
632
|
-
* @example
|
|
633
|
-
* ```ts
|
|
634
|
-
* // UnwrapSingle<['string']> => 'string'
|
|
635
|
-
* // UnwrapSingle<['string', 'number']> => ['string', 'number'] | ['number', 'string']
|
|
636
|
-
* ```
|
|
637
|
-
*/
|
|
638
|
-
type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
|
|
639
|
-
/**
|
|
640
|
-
* A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
|
|
641
|
-
*/
|
|
642
|
-
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
|
+
};
|
|
643
637
|
/**
|
|
644
|
-
*
|
|
638
|
+
* Options for validation an input value
|
|
645
639
|
*/
|
|
646
|
-
type
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
object: object;
|
|
655
|
-
string: string;
|
|
656
|
-
symbol: symbol;
|
|
657
|
-
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;
|
|
658
648
|
};
|
|
659
649
|
//#endregion
|
|
660
|
-
//#region src/helpers.d.ts
|
|
650
|
+
//#region src/helpers/misc.helper.d.ts
|
|
661
651
|
/**
|
|
662
652
|
* Creates a validator function for a given constructor
|
|
663
653
|
* @param constructor - Constructor to check against
|
|
@@ -672,4 +662,4 @@ declare function instanceOf<Instance>(constructor: Constructor<Instance>): (valu
|
|
|
672
662
|
*/
|
|
673
663
|
declare function isSchematic(value: unknown): value is Schematic<never>;
|
|
674
664
|
//#endregion
|
|
675
|
-
export { type Schema, type Schematic, SchematicError, type TypedSchema, ValidationError, instanceOf, isSchematic, schematic };
|
|
665
|
+
export { type GetOptions, type IsOptions, type Schema, type Schematic, SchematicError, type TypedSchema, ValidationError, instanceOf, isSchematic, schematic };
|