@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.
Files changed (46) hide show
  1. package/dist/constants.d.mts +11 -4
  2. package/dist/constants.mjs +27 -8
  3. package/dist/helpers/message.helper.d.mts +17 -0
  4. package/dist/helpers/message.helper.mjs +92 -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 +309 -292
  8. package/dist/index.mjs +237 -166
  9. package/dist/models/schema.plain.model.d.mts +2 -0
  10. package/dist/models/schema.typed.model.d.mts +3 -17
  11. package/dist/models/validation.model.d.mts +28 -7
  12. package/dist/schematic.d.mts +25 -7
  13. package/dist/schematic.mjs +6 -4
  14. package/dist/validator/base.validator.d.mts +6 -0
  15. package/dist/validator/base.validator.mjs +19 -0
  16. package/dist/validator/function.validator.d.mts +6 -0
  17. package/dist/validator/function.validator.mjs +9 -0
  18. package/dist/validator/named.handler.d.mts +6 -0
  19. package/dist/validator/named.handler.mjs +23 -0
  20. package/dist/validator/named.validator.d.mts +7 -0
  21. package/dist/validator/named.validator.mjs +38 -0
  22. package/dist/validator/object.validator.d.mts +7 -0
  23. package/dist/validator/object.validator.mjs +185 -0
  24. package/dist/validator/schematic.validator.d.mts +7 -0
  25. package/dist/validator/schematic.validator.mjs +16 -0
  26. package/package.json +1 -1
  27. package/src/constants.ts +34 -6
  28. package/src/helpers/message.helper.ts +217 -0
  29. package/src/helpers/misc.helper.ts +92 -0
  30. package/src/index.ts +3 -3
  31. package/src/models/schema.plain.model.ts +2 -0
  32. package/src/models/schema.typed.model.ts +4 -22
  33. package/src/models/validation.model.ts +31 -6
  34. package/src/schematic.ts +43 -16
  35. package/src/validator/base.validator.ts +31 -0
  36. package/src/validator/function.validator.ts +9 -0
  37. package/src/validator/named.handler.ts +65 -0
  38. package/src/validator/named.validator.ts +61 -0
  39. package/src/validator/object.validator.ts +366 -0
  40. package/src/validator/schematic.validator.ts +25 -0
  41. package/dist/helpers.d.mts +0 -28
  42. package/dist/helpers.mjs +0 -120
  43. package/dist/validation.d.mts +0 -7
  44. package/dist/validation.mjs +0 -245
  45. package/src/helpers.ts +0 -249
  46. 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 ? TypedSchemaRequired<Model[Key]> | Schematic<Model[Key]> : ToSchemaType<Model[Key]> | TypedPropertyRequired<Model[Key]> } & { [Key in OptionalKeys<Model>]: Exclude<Model[Key], undefined> extends PlainObject ? TypedSchemaOptional<Exclude<Model[Key], undefined>> | Schematic<Exclude<Model[Key], undefined>> : TypedPropertyOptional<Model[Key]> }>;
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/models/validation.model.d.ts
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
- * Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
205
- */
206
- declare class ValidationError extends Error {
207
- readonly information: ValidationInformation[];
208
- constructor(information: ValidationInformation[]);
209
- }
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: ValidationOptions<'throw'>): Model;
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: ValidationOptions<'all'>): Result<Model, ValidationInformation[]>;
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: ValidationOptions<'first'>): Result<Model, ValidationInformation>;
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: ValidationOptions<'throw'>): asserts value is Model;
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: ValidationOptions<'all'>): Result<true, ValidationInformation[]>;
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: ValidationOptions<'first'>): Result<true, ValidationInformation>;
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/schema.plain.model.d.ts
400
- /**
401
- * A generic schema allowing nested schemas, {@link SchemaEntry} values, or arrays of {@link SchemaEntry} as values
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
- * A schema for validating objects
569
+ * Controls how validation failures are reported
412
570
  *
413
- * @example
414
- * ```ts
415
- * const schema: Schema = {
416
- * name: 'string',
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 Schema = PlainSchema;
576
+ type ReportingType = 'all' | 'first' | 'none' | 'throw';
423
577
  /**
424
- * A union of all valid types for a single schema entry
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
- type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
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 SchemaProperty = {
445
- /**
446
- * Whether the property is required _(defaults to `true`)_
447
- */
448
- $required?: boolean;
602
+ type ValidationInformationKey = {
603
+ full: string;
604
+ short: string;
605
+ };
606
+ type BaseOptions<Errors extends ReportingType> = {
449
607
  /**
450
- * The type(s) the property value must match; a single {@link SchemaPropertyType} or an array
608
+ * How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
451
609
  */
452
- $type: SchemaPropertyType | SchemaPropertyType[];
610
+ errors: Errors;
453
611
  /**
454
- * Optional validators keyed by {@link ValueName}, applied during validation
612
+ * Validate if unknown keys are present in the object? _(defaults to `false`)_
455
613
  */
456
- $validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
614
+ strict?: boolean;
457
615
  };
458
616
  /**
459
- * A union of valid types for a {@link SchemaProperty}'s `$type` field
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 SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
464
- /**
465
- * A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
466
- *
467
- * Each key may hold a single validator or an array of validators that receive the typed value
468
- *
469
- * @template Value `$type` value(s) to derive validator keys from
470
- *
471
- * @example
472
- * ```ts
473
- * const validators: PropertyValidators<'string'> = {
474
- * string: (value) => value.length > 0,
475
- * };
476
- * ```
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
- * A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
626
+ * Options for validation an input value
602
627
  */
603
- type ValueName = keyof Values;
604
- /**
605
- * Maps {@link ValueName} strings to their TypeScript equivalents
606
- */
607
- type Values = {
608
- array: unknown[];
609
- bigint: bigint;
610
- boolean: boolean;
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 Schema, type Schematic, SchematicError, type TypedSchema, ValidationError, type ValidationInformation, type ValidationOptions, instanceOf, isSchematic, schematic };
653
+ export { type GetOptions, type IsOptions, type Schema, type Schematic, SchematicError, type TypedSchema, ValidationError, instanceOf, isSchematic, schematic };