@oscarpalmer/jhunal 0.17.0 → 0.18.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 (40) hide show
  1. package/dist/constants.d.mts +28 -15
  2. package/dist/constants.mjs +31 -14
  3. package/dist/helpers.d.mts +8 -1
  4. package/dist/helpers.mjs +68 -3
  5. package/dist/index.d.mts +284 -240
  6. package/dist/index.mjs +188 -50
  7. package/dist/models/infer.model.d.mts +66 -0
  8. package/dist/models/infer.model.mjs +1 -0
  9. package/dist/models/misc.model.d.mts +153 -0
  10. package/dist/models/misc.model.mjs +1 -0
  11. package/dist/models/schema.plain.model.d.mts +92 -0
  12. package/dist/models/schema.plain.model.mjs +1 -0
  13. package/dist/models/schema.typed.model.d.mts +96 -0
  14. package/dist/models/schema.typed.model.mjs +1 -0
  15. package/dist/models/transform.model.d.mts +59 -0
  16. package/dist/models/transform.model.mjs +1 -0
  17. package/dist/models/validation.model.d.mts +81 -0
  18. package/dist/models/validation.model.mjs +21 -0
  19. package/dist/schematic.d.mts +15 -1
  20. package/dist/schematic.mjs +7 -12
  21. package/dist/validation/property.validation.d.mts +1 -1
  22. package/dist/validation/property.validation.mjs +21 -17
  23. package/dist/validation/value.validation.d.mts +2 -2
  24. package/dist/validation/value.validation.mjs +63 -11
  25. package/package.json +2 -2
  26. package/src/constants.ts +84 -19
  27. package/src/helpers.ts +162 -4
  28. package/src/index.ts +3 -1
  29. package/src/models/infer.model.ts +105 -0
  30. package/src/models/misc.model.ts +212 -0
  31. package/src/models/schema.plain.model.ts +110 -0
  32. package/src/models/schema.typed.model.ts +109 -0
  33. package/src/models/transform.model.ts +85 -0
  34. package/src/models/validation.model.ts +123 -0
  35. package/src/schematic.ts +24 -13
  36. package/src/validation/property.validation.ts +41 -36
  37. package/src/validation/value.validation.ts +115 -15
  38. package/dist/models.d.mts +0 -484
  39. package/dist/models.mjs +0 -13
  40. package/src/models.ts +0 -665
package/dist/index.d.mts CHANGED
@@ -1,31 +1,6 @@
1
1
  import { Constructor, GenericCallback, PlainObject, Simplify } from "@oscarpalmer/atoms/models";
2
2
 
3
- //#region src/models.d.ts
4
- /**
5
- * Removes duplicate types from a tuple, preserving first occurrence order
6
- *
7
- * @template Value - Tuple to deduplicate
8
- * @template Seen - Accumulator for already-seen types _(internal)_
9
- *
10
- * @example
11
- * ```ts
12
- * // DeduplicateTuple<['string', 'number', 'string']>
13
- * // => ['string', 'number']
14
- * ```
15
- */
16
- 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;
17
- /**
18
- * Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
19
- *
20
- * @template Value - Type to extract value names from
21
- *
22
- * @example
23
- * ```ts
24
- * // ExtractValueNames<'string'> => 'string'
25
- * // ExtractValueNames<['string', 'number']> => 'string' | 'number'
26
- * ```
27
- */
28
- type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends (infer Item)[] ? ExtractValueNames<Item> : Value extends readonly (infer Item)[] ? ExtractValueNames<Item> : never;
3
+ //#region src/models/infer.model.d.ts
29
4
  /**
30
5
  * Infers the TypeScript type from a {@link Schema} definition
31
6
  *
@@ -84,20 +59,8 @@ type InferSchemaEntry<Value> = Value extends (infer Item)[] ? InferSchemaEntryVa
84
59
  * @template Value - single schema entry
85
60
  */
86
61
  type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends SchemaProperty ? InferPropertyType<Value['$type']> : Value extends PlainSchema ? Infer<Value & Schema> : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
87
- /**
88
- * Determines whether a schema entry is optional
89
- *
90
- * Returns `true` if the entry is a {@link SchemaProperty} or {@link NestedSchema} with `$required` set to `false`; otherwise returns `false`
91
- *
92
- * @template Value - Schema entry to check
93
- */
94
- type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
95
- /**
96
- * Extracts the last member from a union type by leveraging intersection of function return types
97
- *
98
- * @template Value - Union type
99
- */
100
- type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
62
+ //#endregion
63
+ //#region src/models/transform.model.d.ts
101
64
  /**
102
65
  * Maps each element of a tuple through {@link ToValueType}
103
66
  *
@@ -110,109 +73,6 @@ type MapToValueTypes<Value extends unknown[]> = Value extends [infer Head, ...in
110
73
  * @template Value - Tuple of types to map
111
74
  */
112
75
  type MapToSchemaPropertyTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToSchemaPropertyTypeEach<Head>, ...MapToSchemaPropertyTypes<Tail>] : [];
113
- /**
114
- * Extracts keys from an object type that are optional
115
- *
116
- * @template Value - Object type to inspect
117
- */
118
- type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
119
- /**
120
- * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
121
- */
122
- type PlainSchema = {
123
- [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
124
- } & {
125
- $required?: never;
126
- $type?: never;
127
- $validators?: never;
128
- };
129
- /**
130
- * A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
131
- *
132
- * Each key may hold a single validator or an array of validators that receive the typed value
133
- *
134
- * @template Value - `$type` value(s) to derive validator keys from
135
- *
136
- * @example
137
- * ```ts
138
- * const validators: PropertyValidators<'string'> = {
139
- * string: (value) => value.length > 0,
140
- * };
141
- * ```
142
- */
143
- type PropertyValidators<Value> = { [Key in ExtractValueNames<Value>]?: ((value: Values[Key]) => boolean) | Array<(value: Values[Key]) => boolean> };
144
- /**
145
- * Extracts keys from an object type that are required _(i.e., not optional)_
146
- *
147
- * @template Value - Object type to inspect
148
- */
149
- type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
150
- /**
151
- * A schema for validating objects
152
- *
153
- * @example
154
- * ```ts
155
- * const schema: Schema = {
156
- * name: 'string',
157
- * age: 'number',
158
- * tags: ['string', 'number'],
159
- * };
160
- * ```
161
- */
162
- type Schema = SchemaIndex;
163
- /**
164
- * A union of all valid types for a single schema entry
165
- *
166
- * Can be a {@link Constructor}, nested {@link Schema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
167
- */
168
- type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
169
- /**
170
- * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
171
- */
172
- interface SchemaIndex {
173
- [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
174
- }
175
- /**
176
- * A property definition with explicit type(s), an optional requirement flag, and optional validators
177
- *
178
- * @example
179
- * ```ts
180
- * const prop: SchemaProperty = {
181
- * $required: false,
182
- * $type: ['string', 'number'],
183
- * $validators: {
184
- * string: (v) => v.length > 0,
185
- * number: (v) => v > 0,
186
- * },
187
- * };
188
- * ```
189
- */
190
- type SchemaProperty = {
191
- /**
192
- * Whether the property is required _(defaults to `true`)_
193
- */
194
- $required?: boolean;
195
- /**
196
- * The type(s) the property value must match; a single {@link SchemaPropertyType} or an array
197
- */
198
- $type: SchemaPropertyType | SchemaPropertyType[];
199
- /**
200
- * Optional validators keyed by {@link ValueName}, applied during validation
201
- */
202
- $validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
203
- };
204
- /**
205
- * A union of valid types for a {@link SchemaProperty}'s `$type` field
206
- *
207
- * Can be a {@link Constructor}, {@link PlainSchema}, {@link Schematic}, {@link ValueName} string, or a custom validator function
208
- */
209
- type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
210
- /**
211
- * A custom error class for schematic validation failures
212
- */
213
- declare class SchematicError extends Error {
214
- constructor(message: string);
215
- }
216
76
  /**
217
77
  * Converts a type into its corresponding {@link SchemaPropertyType}-representation
218
78
  *
@@ -252,31 +112,8 @@ type ToSchemaType<Value> = UnwrapSingle<DeduplicateTuple<MapToValueTypes<UnionTo
252
112
  * ```
253
113
  */
254
114
  type ToValueType<Value> = Value extends Schematic<any> ? Value : { [Key in keyof Omit<Values, 'object'>]: Value extends Values[Key] ? Key : never }[keyof Omit<Values, 'object'>] extends infer Match ? [Match] extends [never] ? Value extends object ? 'object' | ((value: unknown) => value is Value) : (value: unknown) => value is Value : Match : never;
255
- /**
256
- * Generates all permutations of a tuple type
257
- *
258
- * Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
259
- *
260
- * @template Tuple - Tuple to permute
261
- * @template Elput - Accumulator for the current permutation _(internal; name is Tuple backwards)_
262
- *
263
- * @example
264
- * ```ts
265
- * // TuplePermutations<['string', 'number']>
266
- * // => ['string', 'number'] | ['number', 'string']
267
- * ```
268
- */
269
- 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}`];
270
- /**
271
- * Removes the element at a given index from a tuple
272
- *
273
- * Used internally by {@link TuplePermutations}
274
- *
275
- * @template Items - Tuple to remove from
276
- * @template Item - Stringified index to remove
277
- * @template Prefix - Accumulator for elements before the target _(internal)_
278
- */
279
- 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;
115
+ //#endregion
116
+ //#region src/models/schema.typed.model.d.ts
280
117
  /**
281
118
  * A typed optional property definition generated by {@link TypedSchema} for optional keys, with `$required` set to `false` and excludes `undefined` from the type
282
119
  *
@@ -364,49 +201,14 @@ type TypedSchemaOptional<Model extends PlainObject> = {
364
201
  type TypedSchemaRequired<Model extends PlainObject> = {
365
202
  $required?: true;
366
203
  } & TypedSchema<Model>;
204
+ //#endregion
205
+ //#region src/models/validation.model.d.ts
367
206
  /**
368
- * Converts a union type into an intersection
369
- *
370
- * Uses the contravariance of function parameter types to collapse a union into an intersection
371
- *
372
- * @template Value - Union type to convert
373
- *
374
- * @example
375
- * ```ts
376
- * // UnionToIntersection<{ a: 1 } | { b: 2 }>
377
- * // => { a: 1 } & { b: 2 }
378
- * ```
379
- */
380
- type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => void : never) extends ((value: infer Item) => void) ? Item : never;
381
- /**
382
- * Converts a union type into an ordered tuple
383
- *
384
- * Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
385
- *
386
- * @template Value - Union type to convert
387
- * @template Items - Accumulator for the resulting tuple _(internal)_
388
- *
389
- * @example
390
- * ```ts
391
- * // UnionToTuple<'a' | 'b' | 'c'>
392
- * // => ['a', 'b', 'c']
393
- * ```
394
- */
395
- type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
396
- /**
397
- * Unwraps a single-element tuple to its inner type
398
- *
399
- * For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
400
- *
401
- * @template Value - Tuple to potentially unwrap
402
- *
403
- * @example
404
- * ```ts
405
- * // UnwrapSingle<['string']> => 'string'
406
- * // UnwrapSingle<['string', 'number']> => ['string', 'number'] | ['number', 'string']
407
- * ```
207
+ * A custom error class for schematic validation failures
408
208
  */
409
- type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
209
+ declare class SchematicError extends Error {
210
+ constructor(message: string);
211
+ }
410
212
  /**
411
213
  * The runtime representation of a parsed schema property, used internally during validation
412
214
  *
@@ -424,7 +226,7 @@ type ValidatedProperty = {
424
226
  /**
425
227
  * The property name in the schema
426
228
  */
427
- key: string;
229
+ key: ValidatedPropertyKey;
428
230
  /**
429
231
  * Whether the property is required
430
232
  */
@@ -438,47 +240,41 @@ type ValidatedProperty = {
438
240
  */
439
241
  validators: ValidatedPropertyValidators;
440
242
  };
243
+ /**
244
+ * Property name in schema
245
+ */
246
+ type ValidatedPropertyKey = {
247
+ /**
248
+ * Full property key, including parent keys for nested properties _(e.g., `address.street`)_
249
+ */
250
+ full: string;
251
+ /**
252
+ * The last segment of the property key _(e.g., `street` for `address.street`)_
253
+ */
254
+ short: string;
255
+ };
441
256
  /**
442
257
  * A union of valid types for a {@link ValidatedProperty}'s `types` array
443
258
  *
444
259
  * Can be a callback _(custom validator)_, a {@link Schematic}, a nested {@link ValidatedProperty}, or a {@link ValueName} string
445
260
  */
446
- type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValidatedProperty | ValueName;
261
+ type ValidatedPropertyType = GenericCallback | ValidatedProperty[] | Schematic<unknown> | ValueName;
447
262
  /**
448
263
  * A map of validator functions keyed by {@link ValueName}, used at runtime in {@link ValidatedProperty}
449
264
  *
450
265
  * Each key holds an array of validator functions that receive an `unknown` value and return a `boolean`
451
266
  */
452
267
  type ValidatedPropertyValidators = { [Key in ValueName]?: Array<(value: unknown) => boolean> };
453
- /**
454
- * Basic value types
455
- */
456
- type ValueName = keyof Values;
457
- /**
458
- * Maps type name strings to their TypeScript equivalents
459
- *
460
- * Used by the type system to resolve {@link ValueName} strings into actual types
461
- *
462
- * @example
463
- * ```ts
464
- * // Values['string'] => string
465
- * // Values['date'] => Date
466
- * // Values['null'] => null
467
- * ```
468
- */
469
- type Values = {
470
- array: unknown[];
471
- bigint: bigint;
472
- boolean: boolean;
473
- date: Date;
474
- function: Function;
475
- null: null;
476
- number: number;
477
- object: object;
478
- string: string;
479
- symbol: symbol;
480
- undefined: undefined;
268
+ declare class ValidationError extends Error {
269
+ readonly information: ValidationInformation[];
270
+ constructor(information: ValidationInformation[]);
271
+ }
272
+ type ValidationInformation = {
273
+ key: ValidationInformationKey;
274
+ message: string;
275
+ validator?: GenericCallback;
481
276
  };
277
+ type ValidationInformationKey = ValidatedPropertyKey;
482
278
  //#endregion
483
279
  //#region src/schematic.d.ts
484
280
  /**
@@ -490,6 +286,17 @@ declare class Schematic<Model> {
490
286
  constructor(properties: ValidatedProperty[]);
491
287
  /**
492
288
  * Does the value match the schema?
289
+ *
290
+ * Will assert that the values matches the schema and throw an error if it does not. The error will contain all validation information for the first property that fails validation.
291
+ * @param value Value to validate
292
+ * @param errors Throws an error for the first validation failure
293
+ * @returns `true` if the value matches the schema, otherwise throws an error
294
+ */
295
+ is(value: unknown, errors: 'throw'): asserts value is Model;
296
+ /**
297
+ * Does the value match the schema?
298
+ *
299
+ * Will validate that the value matches the schema and return `true` or `false`, without any validation information for validation failures.
493
300
  * @param value Value to validate
494
301
  * @returns `true` if the value matches the schema, otherwise `false`
495
302
  */
@@ -512,6 +319,243 @@ declare function schematic<Model extends Schema>(schema: Model): Schematic<Infer
512
319
  */
513
320
  declare function schematic<Model extends PlainObject>(schema: TypedSchema<Model>): Schematic<Model>;
514
321
  //#endregion
322
+ //#region src/models/schema.plain.model.d.ts
323
+ /**
324
+ * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
325
+ */
326
+ type PlainSchema = {
327
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
328
+ } & {
329
+ $required?: never;
330
+ $type?: never;
331
+ $validators?: never;
332
+ };
333
+ /**
334
+ * A schema for validating objects
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * const schema: Schema = {
339
+ * name: 'string',
340
+ * age: 'number',
341
+ * tags: ['string', 'number'],
342
+ * };
343
+ * ```
344
+ */
345
+ type Schema = SchemaIndex;
346
+ /**
347
+ * A union of all valid types for a single schema entry
348
+ *
349
+ * Can be a {@link Constructor}, nested {@link Schema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
350
+ */
351
+ type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
352
+ /**
353
+ * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
354
+ */
355
+ interface SchemaIndex {
356
+ [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
357
+ }
358
+ /**
359
+ * A property definition with explicit type(s), an optional requirement flag, and optional validators
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * const prop: SchemaProperty = {
364
+ * $required: false,
365
+ * $type: ['string', 'number'],
366
+ * $validators: {
367
+ * string: (v) => v.length > 0,
368
+ * number: (v) => v > 0,
369
+ * },
370
+ * };
371
+ * ```
372
+ */
373
+ type SchemaProperty = {
374
+ /**
375
+ * Whether the property is required _(defaults to `true`)_
376
+ */
377
+ $required?: boolean;
378
+ /**
379
+ * The type(s) the property value must match; a single {@link SchemaPropertyType} or an array
380
+ */
381
+ $type: SchemaPropertyType | SchemaPropertyType[];
382
+ /**
383
+ * Optional validators keyed by {@link ValueName}, applied during validation
384
+ */
385
+ $validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
386
+ };
387
+ /**
388
+ * A union of valid types for a {@link SchemaProperty}'s `$type` field
389
+ *
390
+ * Can be a {@link Constructor}, {@link PlainSchema}, {@link Schematic}, {@link ValueName} string, or a custom validator function
391
+ */
392
+ type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
393
+ /**
394
+ * A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
395
+ *
396
+ * Each key may hold a single validator or an array of validators that receive the typed value
397
+ *
398
+ * @template Value - `$type` value(s) to derive validator keys from
399
+ *
400
+ * @example
401
+ * ```ts
402
+ * const validators: PropertyValidators<'string'> = {
403
+ * string: (value) => value.length > 0,
404
+ * };
405
+ * ```
406
+ */
407
+ type PropertyValidators<Value> = { [Key in ExtractValueNames<Value>]?: ((value: Values[Key]) => boolean) | Array<(value: Values[Key]) => boolean> };
408
+ //#endregion
409
+ //#region src/models/misc.model.d.ts
410
+ /**
411
+ * Removes duplicate types from a tuple, preserving first occurrence order
412
+ *
413
+ * @template Value - Tuple to deduplicate
414
+ * @template Seen - Accumulator for already-seen types _(internal)_
415
+ *
416
+ * @example
417
+ * ```ts
418
+ * // DeduplicateTuple<['string', 'number', 'string']>
419
+ * // => ['string', 'number']
420
+ * ```
421
+ */
422
+ 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;
423
+ /**
424
+ * Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
425
+ *
426
+ * @template Value - Type to extract value names from
427
+ *
428
+ * @example
429
+ * ```ts
430
+ * // ExtractValueNames<'string'> => 'string'
431
+ * // ExtractValueNames<['string', 'number']> => 'string' | 'number'
432
+ * ```
433
+ */
434
+ type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends (infer Item)[] ? ExtractValueNames<Item> : Value extends readonly (infer Item)[] ? ExtractValueNames<Item> : never;
435
+ /**
436
+ * Determines whether a schema entry is optional
437
+ *
438
+ * Returns `true` if the entry is a {@link SchemaProperty} or {@link NestedSchema} with `$required` set to `false`; otherwise returns `false`
439
+ *
440
+ * @template Value - Schema entry to check
441
+ */
442
+ type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
443
+ /**
444
+ * Extracts the last member from a union type by leveraging intersection of function return types
445
+ *
446
+ * @template Value - Union type
447
+ */
448
+ type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
449
+ /**
450
+ * Extracts keys from an object type that are optional
451
+ *
452
+ * @template Value - Object type to inspect
453
+ */
454
+ type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
455
+ /**
456
+ * Extracts keys from an object type that are required _(i.e., not optional)_
457
+ *
458
+ * @template Value - Object type to inspect
459
+ */
460
+ type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
461
+ /**
462
+ * Generates all permutations of a tuple type
463
+ *
464
+ * Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
465
+ *
466
+ * @template Tuple - Tuple to permute
467
+ * @template Elput - Accumulator for the current permutation _(internal; name is Tuple backwards)_
468
+ *
469
+ * @example
470
+ * ```ts
471
+ * // TuplePermutations<['string', 'number']>
472
+ * // => ['string', 'number'] | ['number', 'string']
473
+ * ```
474
+ */
475
+ 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}`];
476
+ /**
477
+ * Removes the element at a given index from a tuple
478
+ *
479
+ * Used internally by {@link TuplePermutations}
480
+ *
481
+ * @template Items - Tuple to remove from
482
+ * @template Item - Stringified index to remove
483
+ * @template Prefix - Accumulator for elements before the target _(internal)_
484
+ */
485
+ 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;
486
+ /**
487
+ * Converts a union type into an intersection
488
+ *
489
+ * Uses the contravariance of function parameter types to collapse a union into an intersection
490
+ *
491
+ * @template Value - Union type to convert
492
+ *
493
+ * @example
494
+ * ```ts
495
+ * // UnionToIntersection<{ a: 1 } | { b: 2 }>
496
+ * // => { a: 1 } & { b: 2 }
497
+ * ```
498
+ */
499
+ type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => void : never) extends ((value: infer Item) => void) ? Item : never;
500
+ /**
501
+ * Converts a union type into an ordered tuple
502
+ *
503
+ * Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
504
+ *
505
+ * @template Value - Union type to convert
506
+ * @template Items - Accumulator for the resulting tuple _(internal)_
507
+ *
508
+ * @example
509
+ * ```ts
510
+ * // UnionToTuple<'a' | 'b' | 'c'>
511
+ * // => ['a', 'b', 'c']
512
+ * ```
513
+ */
514
+ type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
515
+ /**
516
+ * Unwraps a single-element tuple to its inner type
517
+ *
518
+ * For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
519
+ *
520
+ * @template Value - Tuple to potentially unwrap
521
+ *
522
+ * @example
523
+ * ```ts
524
+ * // UnwrapSingle<['string']> => 'string'
525
+ * // UnwrapSingle<['string', 'number']> => ['string', 'number'] | ['number', 'string']
526
+ * ```
527
+ */
528
+ type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
529
+ /**
530
+ * Basic value types
531
+ */
532
+ type ValueName = keyof Values;
533
+ /**
534
+ * Maps type name strings to their TypeScript equivalents
535
+ *
536
+ * Used by the type system to resolve {@link ValueName} strings into actual types
537
+ *
538
+ * @example
539
+ * ```ts
540
+ * // Values['string'] => string
541
+ * // Values['date'] => Date
542
+ * // Values['null'] => null
543
+ * ```
544
+ */
545
+ type Values = {
546
+ array: unknown[];
547
+ bigint: bigint;
548
+ boolean: boolean;
549
+ date: Date;
550
+ function: Function;
551
+ null: null;
552
+ number: number;
553
+ object: object;
554
+ string: string;
555
+ symbol: symbol;
556
+ undefined: undefined;
557
+ };
558
+ //#endregion
515
559
  //#region src/helpers.d.ts
516
560
  /**
517
561
  * Creates a validator function for a given constructor
@@ -527,4 +571,4 @@ declare function instanceOf<Instance>(constructor: Constructor<Instance>): (valu
527
571
  */
528
572
  declare function isSchematic(value: unknown): value is Schematic<never>;
529
573
  //#endregion
530
- export { type Schema, type Schematic, SchematicError, type TypedSchema, instanceOf, isSchematic, schematic };
574
+ export { type Schema, type Schematic, SchematicError, type TypedSchema, ValidationError, instanceOf, isSchematic, schematic };