@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.
Files changed (47) hide show
  1. package/dist/constants.d.mts +7 -3
  2. package/dist/constants.mjs +34 -12
  3. package/dist/helpers/message.helper.d.mts +11 -0
  4. package/dist/helpers/message.helper.mjs +70 -0
  5. package/dist/helpers/misc.helper.d.mts +22 -0
  6. package/dist/helpers/misc.helper.mjs +56 -0
  7. package/dist/index.d.mts +318 -328
  8. package/dist/index.mjs +324 -295
  9. package/dist/models/schema.plain.model.d.mts +2 -8
  10. package/dist/models/validation.model.d.mts +26 -59
  11. package/dist/schematic.d.mts +28 -10
  12. package/dist/schematic.mjs +15 -16
  13. package/dist/validator/base.validator.d.mts +6 -0
  14. package/dist/validator/base.validator.mjs +19 -0
  15. package/dist/validator/function.validator.d.mts +6 -0
  16. package/dist/validator/function.validator.mjs +9 -0
  17. package/dist/validator/named.handler.d.mts +6 -0
  18. package/dist/validator/named.handler.mjs +22 -0
  19. package/dist/validator/named.validator.d.mts +7 -0
  20. package/dist/validator/named.validator.mjs +39 -0
  21. package/dist/validator/object.validator.d.mts +7 -0
  22. package/dist/validator/object.validator.mjs +167 -0
  23. package/dist/validator/schematic.validator.d.mts +7 -0
  24. package/dist/validator/schematic.validator.mjs +16 -0
  25. package/package.json +1 -1
  26. package/src/constants.ts +42 -10
  27. package/src/helpers/message.helper.ts +152 -0
  28. package/src/helpers/misc.helper.ts +93 -0
  29. package/src/index.ts +7 -2
  30. package/src/models/schema.plain.model.ts +1 -8
  31. package/src/models/validation.model.ts +55 -77
  32. package/src/schematic.ts +49 -27
  33. package/src/validator/base.validator.ts +31 -0
  34. package/src/validator/function.validator.ts +9 -0
  35. package/src/validator/named.handler.ts +50 -0
  36. package/src/validator/named.validator.ts +62 -0
  37. package/src/validator/object.validator.ts +340 -0
  38. package/src/validator/schematic.validator.ts +25 -0
  39. package/dist/helpers.d.mts +0 -28
  40. package/dist/helpers.mjs +0 -119
  41. package/dist/validation/property.validation.d.mts +0 -7
  42. package/dist/validation/property.validation.mjs +0 -92
  43. package/dist/validation/value.validation.d.mts +0 -7
  44. package/dist/validation/value.validation.mjs +0 -162
  45. package/src/helpers.ts +0 -246
  46. package/src/validation/property.validation.ts +0 -217
  47. 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/models/validation.model.d.ts
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
- * The runtime representation of a parsed schema property, used internally during validation
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
- type ValidatedProperty = {
410
+ declare class Schematic<Model> {
411
+ #private;
412
+ private readonly $schematic;
413
+ constructor(validator: Validator);
214
414
  /**
215
- * The property name in the schema
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
- key: string;
422
+ get(value: unknown, options: GetOptions<'throw'>): Model;
218
423
  /**
219
- * Whether the property is required
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
- required: boolean;
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: ValidationOptions<'all'>): Result<Model, ValidationInformation[]>;
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: ValidationOptions<'first'>): Result<Model, ValidationInformation>;
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: ValidationOptions<'throw'>): asserts value is Model;
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: ValidationOptions<'all'>): Result<true, ValidationInformation[]>;
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: ValidationOptions<'first'>): Result<true, ValidationInformation>;
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/schema.plain.model.d.ts
433
- /**
434
- * A generic schema allowing nested schemas, {@link SchemaEntry} values, or arrays of {@link SchemaEntry} as values
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
- * A schema for validating objects
581
+ * Controls how validation failures are reported
445
582
  *
446
- * @example
447
- * ```ts
448
- * const schema: Schema = {
449
- * name: 'string',
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 Schema = SchemaIndex;
588
+ type ReportingType = 'all' | 'first' | 'none' | 'throw';
456
589
  /**
457
- * A union of all valid types for a single schema entry
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
- type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
592
+ declare class SchematicError extends Error {
593
+ constructor(message: string);
594
+ }
462
595
  /**
463
- * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link PlainSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
596
+ * Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
464
597
  */
465
- interface SchemaIndex {
466
- [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
598
+ declare class ValidationError extends Error {
599
+ readonly information: ValidationInformation[];
600
+ constructor(information: ValidationInformation[]);
467
601
  }
468
602
  /**
469
- * A property definition with explicit type(s), an optional requirement flag, and optional validators
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 SchemaProperty = {
484
- /**
485
- * Whether the property is required _(defaults to `true`)_
486
- */
487
- $required?: boolean;
614
+ type ValidationInformationKey = {
615
+ full: string;
616
+ short: string;
617
+ };
618
+ type BaseOptions<Errors extends ReportingType> = {
488
619
  /**
489
- * The type(s) the property value must match; a single {@link SchemaPropertyType} or an array
620
+ * How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
490
621
  */
491
- $type: SchemaPropertyType | SchemaPropertyType[];
622
+ errors: Errors;
492
623
  /**
493
- * Optional validators keyed by {@link ValueName}, applied during validation
624
+ * Validate if unknown keys are present in the object? _(defaults to `false`)_
494
625
  */
495
- $validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
626
+ strict?: boolean;
496
627
  };
497
628
  /**
498
- * A union of valid types for a {@link SchemaProperty}'s `$type` field
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 UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => void : never) extends ((value: infer Item) => void) ? Item : never;
610
- /**
611
- * Converts a union type into an ordered tuple
612
- *
613
- * Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
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
- * Maps {@link ValueName} strings to their TypeScript equivalents
638
+ * Options for validation an input value
645
639
  */
646
- type Values = {
647
- array: unknown[];
648
- bigint: bigint;
649
- boolean: boolean;
650
- date: Date;
651
- function: Function;
652
- null: null;
653
- number: number;
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 };