@oscarpalmer/jhunal 0.16.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 +283 -262
  6. package/dist/index.mjs +189 -56
  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 +20 -6
  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 +3 -3
  26. package/src/constants.ts +84 -18
  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 +29 -18
  36. package/src/validation/property.validation.ts +46 -36
  37. package/src/validation/value.validation.ts +115 -15
  38. package/dist/models.d.mts +0 -507
  39. package/dist/models.mjs +0 -18
  40. package/src/models.ts +0 -691
@@ -1,9 +1,72 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
- import {isSchematic} from '../helpers';
3
- import type {ValidatedProperty, ValidatedPropertyType, ValueName} from '../models';
2
+ import type {GenericCallback} from '@oscarpalmer/atoms/models';
3
+ import {
4
+ getInvalidInputMessage,
5
+ getInvalidMissingMessage,
6
+ getInvalidTypeMessage,
7
+ getInvalidValidatorMessage,
8
+ isSchematic,
9
+ } from '../helpers';
10
+ import type {ValueName} from '../models/misc.model';
11
+ import {
12
+ ValidationError,
13
+ type ReportingInformation,
14
+ type ValidatedProperty,
15
+ type ValidatedPropertyType,
16
+ type ValidationInformation,
17
+ } from '../models/validation.model';
18
+
19
+ function validateNamed(
20
+ property: ValidatedProperty,
21
+ name: ValueName,
22
+ value: unknown,
23
+ validation: ValidationInformation[],
24
+ ): boolean {
25
+ if (!validators[name](value)) {
26
+ return false;
27
+ }
28
+
29
+ const propertyValidators = property.validators[name];
30
+
31
+ if (propertyValidators == null || propertyValidators.length === 0) {
32
+ return true;
33
+ }
34
+
35
+ const {length} = propertyValidators;
36
+
37
+ for (let index = 0; index < length; index += 1) {
38
+ const validator = propertyValidators[index];
4
39
 
5
- export function validateObject(obj: unknown, properties: ValidatedProperty[]): boolean {
40
+ if (!validator(value)) {
41
+ validation.push({
42
+ key: {...property.key},
43
+ message: getInvalidValidatorMessage(property, name, index, length),
44
+ validator: validator as GenericCallback,
45
+ });
46
+
47
+ return false;
48
+ }
49
+ }
50
+
51
+ return true;
52
+ }
53
+
54
+ export function validateObject(
55
+ obj: unknown,
56
+ properties: ValidatedProperty[],
57
+ reporting: ReportingInformation,
58
+ validation?: ValidationInformation[],
59
+ ): boolean {
6
60
  if (!isPlainObject(obj)) {
61
+ if (reporting.throw && validation == null) {
62
+ throw new ValidationError([
63
+ {
64
+ key: {full: '', short: ''},
65
+ message: getInvalidInputMessage(obj),
66
+ },
67
+ ]);
68
+ }
69
+
7
70
  return false;
8
71
  }
9
72
 
@@ -14,22 +77,52 @@ export function validateObject(obj: unknown, properties: ValidatedProperty[]): b
14
77
 
15
78
  const {key, required, types} = property;
16
79
 
17
- const value = obj[key];
80
+ const value = obj[key.short];
18
81
 
19
82
  if (value === undefined && required) {
83
+ const information: ValidationInformation = {
84
+ key: {...key},
85
+ message: getInvalidMissingMessage(property),
86
+ };
87
+
88
+ if (reporting.throw && validation == null) {
89
+ throw new ValidationError([information]);
90
+ }
91
+
92
+ if (validation != null) {
93
+ validation.push(information);
94
+ }
95
+
20
96
  return false;
21
97
  }
22
98
 
23
99
  const typesLength = types.length;
24
100
 
101
+ const information: ValidationInformation[] = [];
102
+
25
103
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
26
104
  const type = types[typeIndex];
27
105
 
28
- if (validateValue(type, property, value)) {
106
+ if (validateValue(type, property, value, reporting, information)) {
29
107
  continue outer;
30
108
  }
31
109
  }
32
110
 
111
+ if (reporting.throw && validation == null) {
112
+ throw new ValidationError(
113
+ information.length === 0
114
+ ? [
115
+ {
116
+ key: {...key},
117
+ message: getInvalidTypeMessage(property, value),
118
+ },
119
+ ]
120
+ : information,
121
+ );
122
+ }
123
+
124
+ validation?.push(...information);
125
+
33
126
  return false;
34
127
  }
35
128
 
@@ -40,23 +133,30 @@ function validateValue(
40
133
  type: ValidatedPropertyType,
41
134
  property: ValidatedProperty,
42
135
  value: unknown,
136
+ reporting: ReportingInformation,
137
+ validation: ValidationInformation[],
43
138
  ): boolean {
44
- switch (true) {
45
- case isSchematic(type):
46
- return type.is(value);
139
+ let result: boolean;
47
140
 
141
+ switch (true) {
48
142
  case typeof type === 'function':
49
- return (type as (value: unknown) => boolean)(value);
143
+ result = (type as GenericCallback)(value);
144
+ break;
50
145
 
51
- case typeof type === 'object':
52
- return validateObject(value, [type] as ValidatedProperty[]);
146
+ case Array.isArray(type):
147
+ result = validateObject(value, type, reporting, validation);
148
+ break;
149
+
150
+ case isSchematic(type):
151
+ result = type.is(value, reporting as never) as unknown as boolean;
152
+ break;
53
153
 
54
154
  default:
55
- return (
56
- validators[type as ValueName](value) &&
57
- (property.validators[type as ValueName]?.every(validator => validator(value)) ?? true)
58
- );
155
+ result = validateNamed(property, type as ValueName, value, validation);
156
+ break;
59
157
  }
158
+
159
+ return result;
60
160
  }
61
161
 
62
162
  //
package/dist/models.d.mts DELETED
@@ -1,507 +0,0 @@
1
- import { Schematic } from "./schematic.mjs";
2
- import { Constructor, GenericCallback, PlainObject, Simplify } from "@oscarpalmer/atoms/models";
3
-
4
- //#region src/models.d.ts
5
- /**
6
- * Removes duplicate types from a tuple, preserving first occurrence order
7
- *
8
- * @template Value - Tuple to deduplicate
9
- * @template Seen - Accumulator for already-seen types _(internal)_
10
- *
11
- * @example
12
- * ```ts
13
- * // DeduplicateTuple<['string', 'number', 'string']>
14
- * // => ['string', 'number']
15
- * ```
16
- */
17
- 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;
18
- /**
19
- * Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
20
- *
21
- * @template Value - Type to extract value names from
22
- *
23
- * @example
24
- * ```ts
25
- * // ExtractValueNames<'string'> => 'string'
26
- * // ExtractValueNames<['string', 'number']> => 'string' | 'number'
27
- * ```
28
- */
29
- type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends (infer Item)[] ? ExtractValueNames<Item> : Value extends readonly (infer Item)[] ? ExtractValueNames<Item> : never;
30
- /**
31
- * Infers the TypeScript type from a {@link Schema} definition
32
- *
33
- * @template Model - Schema to infer types from
34
- *
35
- * @example
36
- * ```ts
37
- * const userSchema = {
38
- * name: 'string',
39
- * age: 'number',
40
- * address: { $required: false, $type: 'string' },
41
- * } satisfies Schema;
42
- *
43
- * type User = Infer<typeof userSchema>;
44
- * // { name: string; age: number; address?: string }
45
- * ```
46
- */
47
- type Infer<Model extends Schema> = Simplify<{ [Key in InferRequiredKeys<Model>]: InferSchemaEntry<Model[Key]> } & { [Key in InferOptionalKeys<Model>]?: InferSchemaEntry<Model[Key]> }>;
48
- /**
49
- * Extracts keys from a {@link Schema} whose entries are optional _(i.e., `$required` is `false`)_
50
- *
51
- * @template Model - {@link Schema} to extract optional keys from
52
- */
53
- type InferOptionalKeys<Model extends Schema> = keyof { [Key in keyof Model as IsOptionalProperty<Model[Key]> extends true ? Key : never]: never };
54
- /**
55
- * Infers the TypeScript type of a {@link SchemaProperty}'s `$type` field, unwrapping arrays to infer their item type
56
- *
57
- * @template Value - `$type` value _(single or array)_
58
- */
59
- type InferPropertyType<Value> = Value extends (infer Item)[] ? InferPropertyValue<Item> : InferPropertyValue<Value>;
60
- /**
61
- * Maps a single type definition to its TypeScript equivalent
62
- *
63
- * Resolves, in order: {@link Constructor} instances, {@link Schematic} models, {@link ValueName} strings, and nested {@link Schema} objects
64
- *
65
- * @template Value - single type definition
66
- */
67
- type InferPropertyValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
68
- /**
69
- * Extracts keys from a {@link Schema} whose entries are required _(i.e., `$required` is not `false`)_
70
- *
71
- * @template Model - Schema to extract required keys from
72
- */
73
- type InferRequiredKeys<Model extends Schema> = keyof { [Key in keyof Model as IsOptionalProperty<Model[Key]> extends true ? never : Key]: never };
74
- /**
75
- * Infers the type for a top-level {@link Schema} entry, unwrapping arrays to infer their item type
76
- *
77
- * @template Value - Schema entry value _(single or array)_
78
- */
79
- type InferSchemaEntry<Value> = Value extends (infer Item)[] ? InferSchemaEntryValue<Item> : InferSchemaEntryValue<Value>;
80
- /**
81
- * Resolves a single schema entry to its TypeScript type
82
- *
83
- * Handles, in order: {@link Constructor} instances, {@link Schematic} models, {@link SchemaProperty} objects, {@link NestedSchema} objects, {@link ValueName} strings, and plain {@link Schema} objects
84
- *
85
- * @template Value - single schema entry
86
- */
87
- type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ? Instance : Value extends Schematic<infer Model> ? Model : Value extends SchemaProperty ? InferPropertyType<Value['$type']> : Value extends NestedSchema ? Infer<Omit<Value, '$required'>> : Value extends ValueName ? Values[Value & ValueName] : Value extends Schema ? Infer<Value> : never;
88
- /**
89
- * Determines whether a schema entry is optional
90
- *
91
- * Returns `true` if the entry is a {@link SchemaProperty} or {@link NestedSchema} with `$required` set to `false`; otherwise returns `false`
92
- *
93
- * @template Value - Schema entry to check
94
- */
95
- type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : Value extends {
96
- $required?: boolean;
97
- } ? Value extends {
98
- $required: false;
99
- } ? true : false : false;
100
- /**
101
- * Extracts the last member from a union type by leveraging intersection of function return types
102
- *
103
- * @template Value - Union type
104
- */
105
- type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
106
- /**
107
- * Maps each element of a tuple through {@link ToValueType}
108
- *
109
- * @template Value - Tuple of types to map
110
- */
111
- type MapToValueTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToValueType<Head>, ...MapToValueTypes<Tail>] : [];
112
- /**
113
- * Maps each element of a tuple through {@link ToSchemaPropertyTypeEach}
114
- *
115
- * @template Value - Tuple of types to map
116
- */
117
- type MapToSchemaPropertyTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToSchemaPropertyTypeEach<Head>, ...MapToSchemaPropertyTypes<Tail>] : [];
118
- /**
119
- * A nested schema definition that may include a `$required` flag alongside arbitrary string-keyed properties
120
- *
121
- * @example
122
- * ```ts
123
- * const address: NestedSchema = {
124
- * $required: false,
125
- * street: 'string',
126
- * city: 'string',
127
- * };
128
- * ```
129
- */
130
- type NestedSchema = {
131
- /**
132
- * Whether the nested schema is required (defaults to `true`)
133
- */
134
- $required?: boolean;
135
- } & Schema;
136
- /**
137
- * Extracts keys from an object type that are optional
138
- *
139
- * @template Value - Object type to inspect
140
- */
141
- type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
142
- /**
143
- * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
144
- */
145
- type PlainSchema = {
146
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
147
- };
148
- /**
149
- * A map of optional validator functions keyed by {@link ValueName}, used to add custom validation to {@link SchemaProperty} definitions
150
- *
151
- * Each key may hold a single validator or an array of validators that receive the typed value
152
- *
153
- * @template Value - `$type` value(s) to derive validator keys from
154
- *
155
- * @example
156
- * ```ts
157
- * const validators: PropertyValidators<'string'> = {
158
- * string: (value) => value.length > 0,
159
- * };
160
- * ```
161
- */
162
- type PropertyValidators<Value> = { [Key in ExtractValueNames<Value>]?: ((value: Values[Key]) => boolean) | Array<(value: Values[Key]) => boolean> };
163
- /**
164
- * Extracts keys from an object type that are required _(i.e., not optional)_
165
- *
166
- * @template Value - Object type to inspect
167
- */
168
- type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
169
- /**
170
- * A schema for validating objects
171
- *
172
- * @example
173
- * ```ts
174
- * const schema: Schema = {
175
- * name: 'string',
176
- * age: 'number',
177
- * tags: ['string', 'number'],
178
- * };
179
- * ```
180
- */
181
- type Schema = SchemaIndex;
182
- /**
183
- * A union of all valid types for a single schema entry
184
- *
185
- * Can be a {@link Constructor}, nested {@link Schema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
186
- */
187
- type SchemaEntry = Constructor | Schema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
188
- /**
189
- * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
190
- */
191
- interface SchemaIndex {
192
- [key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
193
- }
194
- /**
195
- * A property definition with explicit type(s), an optional requirement flag, and optional validators
196
- *
197
- * @example
198
- * ```ts
199
- * const prop: SchemaProperty = {
200
- * $required: false,
201
- * $type: ['string', 'number'],
202
- * $validators: {
203
- * string: (v) => v.length > 0,
204
- * number: (v) => v > 0,
205
- * },
206
- * };
207
- * ```
208
- */
209
- type SchemaProperty = {
210
- /**
211
- * Whether the property is required _(defaults to `true`)_
212
- */
213
- $required?: boolean;
214
- /**
215
- * The type(s) the property value must match; a single {@link SchemaPropertyType} or an array
216
- */
217
- $type: SchemaPropertyType | SchemaPropertyType[];
218
- /**
219
- * Optional validators keyed by {@link ValueName}, applied during validation
220
- */
221
- $validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
222
- };
223
- /**
224
- * A union of valid types for a {@link SchemaProperty}'s `$type` field
225
- *
226
- * Can be a {@link Constructor}, {@link PlainSchema}, {@link Schematic}, {@link ValueName} string, or a custom validator function
227
- */
228
- type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
229
- /**
230
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
231
- *
232
- * @example
233
- * ```ts
234
- * throw new SchematicError('Expected a string, received a number');
235
- * ```
236
- */
237
- declare class SchematicError extends Error {
238
- constructor(message: string);
239
- }
240
- /**
241
- * Converts a type into its corresponding {@link SchemaPropertyType}-representation
242
- *
243
- * Deduplicates and unwraps single-element tuples via {@link UnwrapSingle}
244
- *
245
- * @template Value - type to convert
246
- */
247
- type ToSchemaPropertyType<Value> = UnwrapSingle<DeduplicateTuple<MapToSchemaPropertyTypes<UnionToTuple<Value>>>>;
248
- /**
249
- * Converts a single type to its schema property equivalent
250
- *
251
- * {@link NestedSchema} values have `$required` stripped, plain objects become {@link TypedSchema}, and primitives go through {@link ToValueType}
252
- *
253
- * @template Value - type to convert
254
- */
255
- type ToSchemaPropertyTypeEach<Value> = Value extends NestedSchema ? Omit<Value, '$required'> : Value extends PlainObject ? TypedSchema<Value> : ToValueType<Value>;
256
- /**
257
- * Converts a type into its corresponding {@link ValueName}-representation
258
- *
259
- * Deduplicates and unwraps single-element tuples via {@link UnwrapSingle}
260
- *
261
- * @template Value - type to convert
262
- */
263
- type ToSchemaType<Value> = UnwrapSingle<DeduplicateTuple<MapToValueTypes<UnionToTuple<Value>>>>;
264
- /**
265
- * Maps a type to its {@link ValueName} string equivalent
266
- *
267
- * Resolves {@link Schematic} types as-is, then performs a reverse-lookup against {@link Values} _(excluding `'object'`)_ to find a matching key. If no match is found, `object` types resolve to `'object'` or a type-guard function, and all other unrecognised types resolve to a type-guard function
268
- *
269
- * @template Value - type to map
270
- *
271
- * @example
272
- * ```ts
273
- * // ToValueType<string> => 'string'
274
- * // ToValueType<number[]> => 'array'
275
- * // ToValueType<Date> => 'date'
276
- * ```
277
- */
278
- 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;
279
- /**
280
- * Generates all permutations of a tuple type
281
- *
282
- * Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
283
- *
284
- * @template Tuple - Tuple to permute
285
- * @template Elput - Accumulator for the current permutation _(internal; name is Tuple backwards)_
286
- *
287
- * @example
288
- * ```ts
289
- * // TuplePermutations<['string', 'number']>
290
- * // => ['string', 'number'] | ['number', 'string']
291
- * ```
292
- */
293
- 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}`];
294
- /**
295
- * Removes the element at a given index from a tuple
296
- *
297
- * Used internally by {@link TuplePermutations}
298
- *
299
- * @template Items - Tuple to remove from
300
- * @template Item - Stringified index to remove
301
- * @template Prefix - Accumulator for elements before the target _(internal)_
302
- */
303
- 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;
304
- /**
305
- * A typed optional property definition generated by {@link TypedSchema} for optional keys, with `$required` set to `false` and excludes `undefined` from the type
306
- *
307
- * @template Value - Property's type _(including `undefined`)_
308
- *
309
- * @example
310
- * ```ts
311
- * // For `{ name?: string }`, the `name` key produces:
312
- * // TypedPropertyOptional<string | undefined>
313
- * // => { $required: false; $type: 'string'; ... }
314
- * ```
315
- */
316
- type TypedPropertyOptional<Value> = {
317
- /**
318
- * The property is not required
319
- */
320
- $required: false;
321
- /**
322
- * The type(s) of the property
323
- */
324
- $type: ToSchemaPropertyType<Exclude<Value, undefined>>;
325
- /**
326
- * Custom validators for the property and its types
327
- */
328
- $validators?: PropertyValidators<ToSchemaPropertyType<Exclude<Value, undefined>>>;
329
- };
330
- /**
331
- * A typed required property definition generated by {@link TypedSchema} for required keys, with `$required` defaulting to `true`
332
- *
333
- * @template Value - Property's type
334
- *
335
- * @example
336
- * ```ts
337
- * // For `{ name: string }`, the `name` key produces:
338
- * // TypedPropertyRequired<string>
339
- * // => { $required?: true; $type: 'string'; ... }
340
- * ```
341
- */
342
- type TypedPropertyRequired<Value> = {
343
- /**
344
- * The property is required _(defaults to `true`)_
345
- */
346
- $required?: true;
347
- /**
348
- * The type(s) of the property
349
- */
350
- $type: ToSchemaPropertyType<Value>;
351
- /**
352
- * Custom validators for the property and its types
353
- */
354
- $validators?: PropertyValidators<ToSchemaPropertyType<Value>>;
355
- };
356
- /**
357
- * Creates a schema type constrained to match a TypeScript type
358
- *
359
- * Required keys map to {@link ToSchemaType} or {@link TypedPropertyRequired}; plain object values may also use {@link Schematic}. Optional keys map to {@link TypedPropertyOptional} or, for plain objects, {@link TypedSchemaOptional}
360
- *
361
- * @template Model - Object type to generate a schema for
362
- *
363
- * @example
364
- * ```ts
365
- * type User = { name: string; age: number; bio?: string };
366
- *
367
- * const schema: TypedSchema<User> = {
368
- * name: 'string',
369
- * age: 'number',
370
- * bio: { $required: false, $type: 'string' },
371
- * };
372
- * ```
373
- */
374
- 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]> }>;
375
- /**
376
- * A {@link TypedSchema} variant for optional nested objects, with `$required` fixed to `false`
377
- *
378
- * @template Model - Nested object type
379
- */
380
- type TypedSchemaOptional<Model extends PlainObject> = {
381
- $required: false;
382
- } & TypedSchema<Model>;
383
- /**
384
- * A {@link TypedSchema} variant for required nested objects, with `$required` defaulting to `true`
385
- *
386
- * @template Model - Nested object type
387
- */
388
- type TypedSchemaRequired<Model extends PlainObject> = {
389
- $required?: true;
390
- } & TypedSchema<Model>;
391
- /**
392
- * Converts a union type into an intersection
393
- *
394
- * Uses the contravariance of function parameter types to collapse a union into an intersection
395
- *
396
- * @template Value - Union type to convert
397
- *
398
- * @example
399
- * ```ts
400
- * // UnionToIntersection<{ a: 1 } | { b: 2 }>
401
- * // => { a: 1 } & { b: 2 }
402
- * ```
403
- */
404
- type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => void : never) extends ((value: infer Item) => void) ? Item : never;
405
- /**
406
- * Converts a union type into an ordered tuple
407
- *
408
- * Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
409
- *
410
- * @template Value - Union type to convert
411
- * @template Items - Accumulator for the resulting tuple _(internal)_
412
- *
413
- * @example
414
- * ```ts
415
- * // UnionToTuple<'a' | 'b' | 'c'>
416
- * // => ['a', 'b', 'c']
417
- * ```
418
- */
419
- type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
420
- /**
421
- * Unwraps a single-element tuple to its inner type
422
- *
423
- * For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
424
- *
425
- * @template Value - Tuple to potentially unwrap
426
- *
427
- * @example
428
- * ```ts
429
- * // UnwrapSingle<['string']> => 'string'
430
- * // UnwrapSingle<['string', 'number']> => ['string', 'number'] | ['number', 'string']
431
- * ```
432
- */
433
- type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
434
- /**
435
- * The runtime representation of a parsed schema property, used internally during validation
436
- *
437
- * @example
438
- * ```ts
439
- * const parsed: ValidatedProperty = {
440
- * key: 'age',
441
- * required: true,
442
- * types: ['number'],
443
- * validators: { number: [(v) => v > 0] },
444
- * };
445
- * ```
446
- */
447
- type ValidatedProperty = {
448
- /**
449
- * The property name in the schema
450
- */
451
- key: string;
452
- /**
453
- * Whether the property is required
454
- */
455
- required: boolean;
456
- /**
457
- * The allowed types for this property
458
- */
459
- types: ValidatedPropertyType[];
460
- /**
461
- * Custom validators grouped by {@link ValueName}
462
- */
463
- validators: ValidatedPropertyValidators;
464
- };
465
- /**
466
- * A union of valid types for a {@link ValidatedProperty}'s `types` array
467
- *
468
- * Can be a callback _(custom validator)_, a {@link Schematic}, a nested {@link ValidatedProperty}, or a {@link ValueName} string
469
- */
470
- type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValidatedProperty | ValueName;
471
- /**
472
- * A map of validator functions keyed by {@link ValueName}, used at runtime in {@link ValidatedProperty}
473
- *
474
- * Each key holds an array of validator functions that receive an `unknown` value and return a `boolean`
475
- */
476
- type ValidatedPropertyValidators = { [Key in ValueName]?: Array<(value: unknown) => boolean> };
477
- /**
478
- * Basic value types
479
- */
480
- type ValueName = keyof Values;
481
- /**
482
- * Maps type name strings to their TypeScript equivalents
483
- *
484
- * Used by the type system to resolve {@link ValueName} strings into actual types
485
- *
486
- * @example
487
- * ```ts
488
- * // Values['string'] => string
489
- * // Values['date'] => Date
490
- * // Values['null'] => null
491
- * ```
492
- */
493
- type Values = {
494
- array: unknown[];
495
- bigint: bigint;
496
- boolean: boolean;
497
- date: Date;
498
- function: Function;
499
- null: null;
500
- number: number;
501
- object: object;
502
- string: string;
503
- symbol: symbol;
504
- undefined: undefined;
505
- };
506
- //#endregion
507
- export { Infer, NestedSchema, Schema, SchemaProperty, SchematicError, TypedPropertyOptional, TypedPropertyRequired, TypedSchema, ValidatedProperty, ValidatedPropertyType, ValidatedPropertyValidators, ValueName, Values };
package/dist/models.mjs DELETED
@@ -1,18 +0,0 @@
1
- import { ERROR_NAME } from "./constants.mjs";
2
- //#region src/models.ts
3
- /**
4
- * A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
5
- *
6
- * @example
7
- * ```ts
8
- * throw new SchematicError('Expected a string, received a number');
9
- * ```
10
- */
11
- var SchematicError = class extends Error {
12
- constructor(message) {
13
- super(message);
14
- this.name = ERROR_NAME;
15
- }
16
- };
17
- //#endregion
18
- export { SchematicError };