@oscarpalmer/jhunal 0.19.0 → 0.20.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.
@@ -2,6 +2,11 @@ import { ReportingType } from "./models/validation.model.mjs";
2
2
  import { Values } from "./models/misc.model.mjs";
3
3
 
4
4
  //#region src/constants.d.ts
5
+ declare const COMMA = ", ";
6
+ declare const CONJUNCTION_OR = " or ";
7
+ declare const CONJUNCTION_OR_COMMA = ", or ";
8
+ declare const CONJUNCTION_AND = " and ";
9
+ declare const CONJUNCTION_AND_COMMA = ", and ";
5
10
  declare const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
6
11
  declare const NAME_SCHEMATIC = "Schematic";
7
12
  declare const NAME_ERROR_SCHEMATIC = "SchematicError";
@@ -15,6 +20,7 @@ declare const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required pr
15
20
  declare const VALIDATION_MESSAGE_INVALID_TYPE = "Expected <> for '<>' but received <>";
16
21
  declare const VALIDATION_MESSAGE_INVALID_VALUE = "Value does not satisfy validator for '<>' and type '<>'";
17
22
  declare const VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX = " at index <>";
23
+ declare const VALIDATION_MESSAGE_UNKNOWN_KEYS = "Found keys that are not defined in the schema: <>";
18
24
  declare const REPORTING_ALL: ReportingType;
19
25
  declare const REPORTING_FIRST: ReportingType;
20
26
  declare const REPORTING_NONE: ReportingType;
@@ -37,4 +43,4 @@ declare const TYPE_UNDEFINED = "undefined";
37
43
  declare const VALIDATABLE_TYPES: Set<keyof Values>;
38
44
  declare const TYPE_ALL: Set<keyof Values>;
39
45
  //#endregion
40
- export { MESSAGE_CONSTRUCTOR, NAME_ERROR_SCHEMATIC, NAME_ERROR_VALIDATION, NAME_SCHEMATIC, PROPERTY_REQUIRED, PROPERTY_SCHEMATIC, PROPERTY_TYPE, PROPERTY_VALIDATORS, REPORTING_ALL, REPORTING_FIRST, REPORTING_NONE, REPORTING_THROW, REPORTING_TYPES, SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TEMPLATE_PATTERN, TYPE_ALL, TYPE_ARRAY, TYPE_NULL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES, VALIDATION_MESSAGE_INVALID_INPUT, VALIDATION_MESSAGE_INVALID_REQUIRED, VALIDATION_MESSAGE_INVALID_TYPE, VALIDATION_MESSAGE_INVALID_VALUE, VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX };
46
+ export { COMMA, CONJUNCTION_AND, CONJUNCTION_AND_COMMA, CONJUNCTION_OR, CONJUNCTION_OR_COMMA, MESSAGE_CONSTRUCTOR, NAME_ERROR_SCHEMATIC, NAME_ERROR_VALIDATION, NAME_SCHEMATIC, PROPERTY_REQUIRED, PROPERTY_SCHEMATIC, PROPERTY_TYPE, PROPERTY_VALIDATORS, REPORTING_ALL, REPORTING_FIRST, REPORTING_NONE, REPORTING_THROW, REPORTING_TYPES, SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TEMPLATE_PATTERN, TYPE_ALL, TYPE_ARRAY, TYPE_NULL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES, VALIDATION_MESSAGE_INVALID_INPUT, VALIDATION_MESSAGE_INVALID_REQUIRED, VALIDATION_MESSAGE_INVALID_TYPE, VALIDATION_MESSAGE_INVALID_VALUE, VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX, VALIDATION_MESSAGE_UNKNOWN_KEYS };
@@ -1,4 +1,9 @@
1
1
  //#region src/constants.ts
2
+ const COMMA = ", ";
3
+ const CONJUNCTION_OR = " or ";
4
+ const CONJUNCTION_OR_COMMA = ", or ";
5
+ const CONJUNCTION_AND = " and ";
6
+ const CONJUNCTION_AND_COMMA = ", and ";
2
7
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
3
8
  const NAME_SCHEMATIC = "Schematic";
4
9
  const NAME_ERROR_SCHEMATIC = "SchematicError";
@@ -12,6 +17,7 @@ const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required property '
12
17
  const VALIDATION_MESSAGE_INVALID_TYPE = "Expected <> for '<>' but received <>";
13
18
  const VALIDATION_MESSAGE_INVALID_VALUE = "Value does not satisfy validator for '<>' and type '<>'";
14
19
  const VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX = " at index <>";
20
+ const VALIDATION_MESSAGE_UNKNOWN_KEYS = "Found keys that are not defined in the schema: <>";
15
21
  const REPORTING_ALL = "all";
16
22
  const REPORTING_FIRST = "first";
17
23
  const REPORTING_NONE = "none";
@@ -53,4 +59,4 @@ const TYPE_ALL = new Set([
53
59
  TYPE_UNDEFINED
54
60
  ]);
55
61
  //#endregion
56
- export { MESSAGE_CONSTRUCTOR, NAME_ERROR_SCHEMATIC, NAME_ERROR_VALIDATION, NAME_SCHEMATIC, PROPERTY_REQUIRED, PROPERTY_SCHEMATIC, PROPERTY_TYPE, PROPERTY_VALIDATORS, REPORTING_ALL, REPORTING_FIRST, REPORTING_NONE, REPORTING_THROW, REPORTING_TYPES, SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TEMPLATE_PATTERN, TYPE_ALL, TYPE_ARRAY, TYPE_NULL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES, VALIDATION_MESSAGE_INVALID_INPUT, VALIDATION_MESSAGE_INVALID_REQUIRED, VALIDATION_MESSAGE_INVALID_TYPE, VALIDATION_MESSAGE_INVALID_VALUE, VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX };
62
+ export { COMMA, CONJUNCTION_AND, CONJUNCTION_AND_COMMA, CONJUNCTION_OR, CONJUNCTION_OR_COMMA, MESSAGE_CONSTRUCTOR, NAME_ERROR_SCHEMATIC, NAME_ERROR_VALIDATION, NAME_SCHEMATIC, PROPERTY_REQUIRED, PROPERTY_SCHEMATIC, PROPERTY_TYPE, PROPERTY_VALIDATORS, REPORTING_ALL, REPORTING_FIRST, REPORTING_NONE, REPORTING_THROW, REPORTING_TYPES, SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TEMPLATE_PATTERN, TYPE_ALL, TYPE_ARRAY, TYPE_NULL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES, VALIDATION_MESSAGE_INVALID_INPUT, VALIDATION_MESSAGE_INVALID_REQUIRED, VALIDATION_MESSAGE_INVALID_TYPE, VALIDATION_MESSAGE_INVALID_VALUE, VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX, VALIDATION_MESSAGE_UNKNOWN_KEYS };
@@ -8,7 +8,12 @@ declare function getInvalidInputMessage(actual: unknown): string;
8
8
  declare function getInvalidMissingMessage(property: ValidatedProperty): string;
9
9
  declare function getInvalidTypeMessage(property: ValidatedProperty, actual: unknown): string;
10
10
  declare function getInvalidValidatorMessage(property: ValidatedProperty, type: ValueName, index: number, length: number): string;
11
+ declare function getOptions(input: unknown): {
12
+ reporting: ReportingInformation;
13
+ strict: boolean;
14
+ };
11
15
  declare function getReporting(value: unknown): ReportingInformation;
16
+ declare function getUnknownKeysMessage(keys: string[]): string;
12
17
  /**
13
18
  * Creates a validator function for a given constructor
14
19
  * @param constructor - Constructor to check against
@@ -23,4 +28,4 @@ declare function instanceOf<Instance>(constructor: Constructor<Instance>): (valu
23
28
  */
24
29
  declare function isSchematic(value: unknown): value is Schematic<never>;
25
30
  //#endregion
26
- export { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getInvalidValidatorMessage, getReporting, instanceOf, isSchematic };
31
+ export { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getInvalidValidatorMessage, getOptions, getReporting, getUnknownKeysMessage, instanceOf, isSchematic };
package/dist/helpers.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { MESSAGE_CONSTRUCTOR, NAME_SCHEMATIC, REPORTING_FIRST, REPORTING_NONE, REPORTING_THROW, REPORTING_TYPES, TYPE_ARRAY, TYPE_NULL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATION_MESSAGE_INVALID_INPUT, VALIDATION_MESSAGE_INVALID_REQUIRED, VALIDATION_MESSAGE_INVALID_TYPE, VALIDATION_MESSAGE_INVALID_VALUE, VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX } from "./constants.mjs";
1
+ import { CONJUNCTION_AND, CONJUNCTION_AND_COMMA, CONJUNCTION_OR, CONJUNCTION_OR_COMMA, MESSAGE_CONSTRUCTOR, NAME_SCHEMATIC, REPORTING_FIRST, REPORTING_NONE, REPORTING_THROW, REPORTING_TYPES, TYPE_ARRAY, TYPE_NULL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATION_MESSAGE_INVALID_INPUT, VALIDATION_MESSAGE_INVALID_REQUIRED, VALIDATION_MESSAGE_INVALID_TYPE, VALIDATION_MESSAGE_INVALID_VALUE, VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX, VALIDATION_MESSAGE_UNKNOWN_KEYS } from "./constants.mjs";
2
2
  import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
3
3
  //#region src/helpers.ts
4
4
  function getInvalidInputMessage(actual) {
@@ -21,6 +21,21 @@ function getInvalidValidatorMessage(property, type, index, length) {
21
21
  if (length > 1) message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace("<>", String(index));
22
22
  return message;
23
23
  }
24
+ function getOptions(input) {
25
+ if (typeof input === "boolean") return {
26
+ reporting: getReporting(REPORTING_NONE),
27
+ strict: input
28
+ };
29
+ if (REPORTING_TYPES.has(input)) return {
30
+ reporting: getReporting(input),
31
+ strict: false
32
+ };
33
+ const options = isPlainObject(input) ? input : {};
34
+ return {
35
+ reporting: getReporting(options.errors),
36
+ strict: typeof options.strict === "boolean" ? options.strict : false
37
+ };
38
+ }
24
39
  function getPropertyType(original) {
25
40
  if (typeof original === "function") return "a validated value";
26
41
  if (Array.isArray(original)) return `'${TYPE_OBJECT}'`;
@@ -30,12 +45,16 @@ function getPropertyType(original) {
30
45
  function getReporting(value) {
31
46
  const type = REPORTING_TYPES.has(value) ? value : REPORTING_NONE;
32
47
  return {
48
+ type,
33
49
  ["all"]: type === "all",
34
50
  [REPORTING_FIRST]: type === REPORTING_FIRST,
35
51
  [REPORTING_NONE]: type === REPORTING_NONE,
36
52
  [REPORTING_THROW]: type === REPORTING_THROW
37
53
  };
38
54
  }
55
+ function getUnknownKeysMessage(keys) {
56
+ return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace("<>", renderKeys(keys));
57
+ }
39
58
  function getValueType(value) {
40
59
  const valueType = typeof value;
41
60
  switch (true) {
@@ -68,6 +87,20 @@ function instanceOf(constructor) {
68
87
  function isSchematic(value) {
69
88
  return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
70
89
  }
90
+ function renderKeys(keys) {
91
+ return renderParts(keys.map((key) => `'${key}'`), CONJUNCTION_AND, CONJUNCTION_AND_COMMA);
92
+ }
93
+ function renderParts(parts, delimiterShort, delimiterLong) {
94
+ const { length } = parts;
95
+ if (length === 1) return parts[0];
96
+ let rendered = "";
97
+ for (let index = 0; index < length; index += 1) {
98
+ rendered += parts[index];
99
+ if (index < length - 2) rendered += ", ";
100
+ else if (index === length - 2) rendered += parts.length > 2 ? delimiterLong : delimiterShort;
101
+ }
102
+ return rendered;
103
+ }
71
104
  function renderTypes(types) {
72
105
  const unique = /* @__PURE__ */ new Set();
73
106
  const parts = [];
@@ -77,14 +110,7 @@ function renderTypes(types) {
77
110
  unique.add(rendered);
78
111
  parts.push(rendered);
79
112
  }
80
- const { length } = parts;
81
- let rendered = "";
82
- for (let index = 0; index < length; index += 1) {
83
- rendered += parts[index];
84
- if (index < length - 2) rendered += ", ";
85
- else if (index === length - 2) rendered += parts.length > 2 ? ", or " : " or ";
86
- }
87
- return rendered;
113
+ return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
88
114
  }
89
115
  //#endregion
90
- export { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getInvalidValidatorMessage, getReporting, instanceOf, isSchematic };
116
+ export { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getInvalidValidatorMessage, getOptions, getReporting, getUnknownKeysMessage, instanceOf, isSchematic };
package/dist/index.d.mts CHANGED
@@ -5,7 +5,7 @@ import { Result } from "@oscarpalmer/atoms/result/models";
5
5
  /**
6
6
  * Infers the TypeScript type from a {@link Schema} definition
7
7
  *
8
- * @template Model - Schema to infer types from
8
+ * @template Model Schema to infer types from
9
9
  *
10
10
  * @example
11
11
  * ```ts
@@ -27,37 +27,37 @@ type Infer<Model extends Schema> = Simplify<{ [Key in InferRequiredKeys<Model>]:
27
27
  */
28
28
  type InferOptionalKeys<Model extends Schema> = keyof { [Key in keyof Model as IsOptionalProperty<Model[Key]> extends true ? Key : never]: never };
29
29
  /**
30
- * Infers the TypeScript type of a {@link SchemaProperty}'s `$type` field, unwrapping arrays to infer their item type
30
+ * Infers the TypeScript type from a {@link SchemaProperty}'s `$type` field
31
31
  *
32
- * @template Value - `$type` value _(single or array)_
32
+ * @template Value `$type` value _(single or array)_
33
33
  */
34
34
  type InferPropertyType<Value> = Value extends (infer Item)[] ? InferPropertyValue<Item> : InferPropertyValue<Value>;
35
35
  /**
36
- * Maps a single type definition to its TypeScript equivalent
36
+ * Maps a single `$type` definition to its TypeScript equivalent
37
37
  *
38
- * Resolves, in order: {@link Constructor} instances, {@link Schematic} models, {@link ValueName} strings, and nested {@link Schema} objects
38
+ * Resolves, in order: {@link Constructor} instances, {@link Schematic} models, {@link ValueName} strings, and nested {@link PlainSchema} objects
39
39
  *
40
- * @template Value - single type definition
40
+ * @template Value single type definition
41
41
  */
42
42
  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;
43
43
  /**
44
44
  * Extracts keys from a {@link Schema} whose entries are required _(i.e., `$required` is not `false`)_
45
45
  *
46
- * @template Model - Schema to extract required keys from
46
+ * @template Model Schema to extract required keys from
47
47
  */
48
48
  type InferRequiredKeys<Model extends Schema> = keyof { [Key in keyof Model as IsOptionalProperty<Model[Key]> extends true ? never : Key]: never };
49
49
  /**
50
- * Infers the type for a top-level {@link Schema} entry, unwrapping arrays to infer their item type
50
+ * Infers the TypeScript type from a top-level {@link Schema} entry
51
51
  *
52
- * @template Value - Schema entry value _(single or array)_
52
+ * @template Value Schema entry value _(single or array)_
53
53
  */
54
54
  type InferSchemaEntry<Value> = Value extends (infer Item)[] ? InferSchemaEntryValue<Item> : InferSchemaEntryValue<Value>;
55
55
  /**
56
- * Resolves a single schema entry to its TypeScript type
56
+ * Maps a single top-level schema entry to its TypeScript type
57
57
  *
58
- * Handles, in order: {@link Constructor} instances, {@link Schematic} models, {@link SchemaProperty} objects, {@link NestedSchema} objects, {@link ValueName} strings, and plain {@link Schema} objects
58
+ * Resolves, in order: {@link Constructor} instances, {@link Schematic} models, {@link SchemaProperty} objects, {@link PlainSchema} objects, and {@link ValueName} strings
59
59
  *
60
- * @template Value - single schema entry
60
+ * @template Value single schema entry
61
61
  */
62
62
  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;
63
63
  //#endregion
@@ -65,37 +65,33 @@ type InferSchemaEntryValue<Value> = Value extends Constructor<infer Instance> ?
65
65
  /**
66
66
  * Maps each element of a tuple through {@link ToValueType}
67
67
  *
68
- * @template Value - Tuple of types to map
68
+ * @template Value Tuple of types to map
69
69
  */
70
70
  type MapToValueTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToValueType<Head>, ...MapToValueTypes<Tail>] : [];
71
71
  /**
72
72
  * Maps each element of a tuple through {@link ToSchemaPropertyTypeEach}
73
73
  *
74
- * @template Value - Tuple of types to map
74
+ * @template Value Tuple of types to map
75
75
  */
76
76
  type MapToSchemaPropertyTypes<Value extends unknown[]> = Value extends [infer Head, ...infer Tail] ? [ToSchemaPropertyTypeEach<Head>, ...MapToSchemaPropertyTypes<Tail>] : [];
77
77
  /**
78
- * Converts a type into its corresponding {@link SchemaPropertyType}-representation
78
+ * Converts a TypeScript type to its {@link SchemaPropertyType} representation, suitable for use in a typed schema
79
79
  *
80
- * Deduplicates and unwraps single-element tuples via {@link UnwrapSingle}
81
- *
82
- * @template Value - type to convert
80
+ * @template Value Type to convert
83
81
  */
84
82
  type ToSchemaPropertyType<Value> = UnwrapSingle<DeduplicateTuple<MapToSchemaPropertyTypes<UnionToTuple<Value>>>>;
85
83
  /**
86
84
  * Converts a single type to its schema property equivalent
87
85
  *
88
- * {@link NestedSchema} values have `$required` stripped, plain objects become {@link TypedSchema}, and primitives go through {@link ToValueType}
86
+ * Plain objects become {@link TypedSchema}; primitives go through {@link ToValueType}
89
87
  *
90
- * @template Value - type to convert
88
+ * @template Value Type to convert
91
89
  */
92
90
  type ToSchemaPropertyTypeEach<Value> = Value extends PlainObject ? TypedSchema<Value> : ToValueType<Value>;
93
91
  /**
94
- * Converts a type into its corresponding {@link ValueName}-representation
95
- *
96
- * Deduplicates and unwraps single-element tuples via {@link UnwrapSingle}
92
+ * Converts a TypeScript type to its {@link ValueName} representation, suitable for use as a top-level schema entry
97
93
  *
98
- * @template Value - type to convert
94
+ * @template Value Type to convert
99
95
  */
100
96
  type ToSchemaType<Value> = UnwrapSingle<DeduplicateTuple<MapToValueTypes<UnionToTuple<Value>>>>;
101
97
  /**
@@ -103,7 +99,7 @@ type ToSchemaType<Value> = UnwrapSingle<DeduplicateTuple<MapToValueTypes<UnionTo
103
99
  *
104
100
  * 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
105
101
  *
106
- * @template Value - type to map
102
+ * @template Value Type to map
107
103
  *
108
104
  * @example
109
105
  * ```ts
@@ -118,7 +114,7 @@ type ToValueType<Value> = Value extends Schematic<any> ? Value : { [Key in keyof
118
114
  /**
119
115
  * A typed optional property definition generated by {@link TypedSchema} for optional keys, with `$required` set to `false` and excludes `undefined` from the type
120
116
  *
121
- * @template Value - Property's type _(including `undefined`)_
117
+ * @template Value Property's type _(including `undefined`)_
122
118
  *
123
119
  * @example
124
120
  * ```ts
@@ -128,23 +124,14 @@ type ToValueType<Value> = Value extends Schematic<any> ? Value : { [Key in keyof
128
124
  * ```
129
125
  */
130
126
  type TypedPropertyOptional<Value> = {
131
- /**
132
- * The property is not required
133
- */
134
127
  $required: false;
135
- /**
136
- * The type(s) of the property
137
- */
138
128
  $type: ToSchemaPropertyType<Exclude<Value, undefined>>;
139
- /**
140
- * Custom validators for the property and its types
141
- */
142
129
  $validators?: PropertyValidators<ToSchemaPropertyType<Exclude<Value, undefined>>>;
143
130
  };
144
131
  /**
145
132
  * A typed required property definition generated by {@link TypedSchema} for required keys, with `$required` defaulting to `true`
146
133
  *
147
- * @template Value - Property's type
134
+ * @template Value Property's type
148
135
  *
149
136
  * @example
150
137
  * ```ts
@@ -154,17 +141,8 @@ type TypedPropertyOptional<Value> = {
154
141
  * ```
155
142
  */
156
143
  type TypedPropertyRequired<Value> = {
157
- /**
158
- * The property is required _(defaults to `true`)_
159
- */
160
144
  $required?: true;
161
- /**
162
- * The type(s) of the property
163
- */
164
145
  $type: ToSchemaPropertyType<Value>;
165
- /**
166
- * Custom validators for the property and its types
167
- */
168
146
  $validators?: PropertyValidators<ToSchemaPropertyType<Value>>;
169
147
  };
170
148
  /**
@@ -172,7 +150,7 @@ type TypedPropertyRequired<Value> = {
172
150
  *
173
151
  * 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}
174
152
  *
175
- * @template Model - Object type to generate a schema for
153
+ * @template Model Object type to generate a schema for
176
154
  *
177
155
  * @example
178
156
  * ```ts
@@ -189,7 +167,7 @@ type TypedSchema<Model extends PlainObject> = Simplify<{ [Key in RequiredKeys<Mo
189
167
  /**
190
168
  * A {@link TypedSchema} variant for optional nested objects, with `$required` fixed to `false`
191
169
  *
192
- * @template Model - Nested object type
170
+ * @template Model Nested object type
193
171
  */
194
172
  type TypedSchemaOptional<Model extends PlainObject> = {
195
173
  $required: false;
@@ -197,7 +175,7 @@ type TypedSchemaOptional<Model extends PlainObject> = {
197
175
  /**
198
176
  * A {@link TypedSchema} variant for required nested objects, with `$required` defaulting to `true`
199
177
  *
200
- * @template Model - Nested object type
178
+ * @template Model Nested object type
201
179
  */
202
180
  type TypedSchemaRequired<Model extends PlainObject> = {
203
181
  $required?: true;
@@ -205,7 +183,16 @@ type TypedSchemaRequired<Model extends PlainObject> = {
205
183
  //#endregion
206
184
  //#region src/models/validation.model.d.ts
207
185
  /**
208
- * A custom error class for schematic validation failures
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
209
196
  */
210
197
  declare class SchematicError extends Error {
211
198
  constructor(message: string);
@@ -242,16 +229,12 @@ type ValidatedProperty = {
242
229
  validators: ValidatedPropertyValidators;
243
230
  };
244
231
  /**
245
- * Property name in schema
232
+ * The full and short forms of a property's key path
233
+ *
234
+ * For a nested property `address.street`: `full` is `'address.street'`, `short` is `'street'`
246
235
  */
247
236
  type ValidatedPropertyKey = {
248
- /**
249
- * Full property key, including parent keys for nested properties _(e.g., `address.street`)_
250
- */
251
237
  full: string;
252
- /**
253
- * The last segment of the property key _(e.g., `street` for `address.street`)_
254
- */
255
238
  short: string;
256
239
  };
257
240
  /**
@@ -266,17 +249,39 @@ type ValidatedPropertyType = GenericCallback | ValidatedProperty[] | Schematic<u
266
249
  * Each key holds an array of validator functions that receive an `unknown` value and return a `boolean`
267
250
  */
268
251
  type ValidatedPropertyValidators = { [Key in ValueName]?: Array<(value: unknown) => boolean> };
252
+ /**
253
+ * Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
254
+ */
269
255
  declare class ValidationError extends Error {
270
256
  readonly information: ValidationInformation[];
271
257
  constructor(information: ValidationInformation[]);
272
258
  }
259
+ /**
260
+ * Describes a single validation failure
261
+ */
273
262
  type ValidationInformation = {
274
- key: ValidationInformationKey;
275
- message: string;
276
- validator?: GenericCallback;
263
+ /** The key path of the property that failed */key: ValidationInformationKey; /** Human-readable description of the failure */
264
+ message: string; /** The validator function that failed, if the failure was from a `$validators` entry */
265
+ validator?: GenericCallback; /** The value that was provided */
277
266
  value: unknown;
278
267
  };
268
+ /**
269
+ * Same shape as {@link ValidatedPropertyKey}; the key path of a failed property
270
+ */
279
271
  type ValidationInformationKey = ValidatedPropertyKey;
272
+ /**
273
+ * Options for validation
274
+ */
275
+ type ValidationOptions<Errors extends ReportingType> = {
276
+ /**
277
+ * How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
278
+ */
279
+ errors?: Errors;
280
+ /**
281
+ * Validate if unknown keys are present in the object? _(defaults to `false`)_
282
+ */
283
+ strict?: boolean;
284
+ };
280
285
  //#endregion
281
286
  //#region src/schematic.d.ts
282
287
  /**
@@ -291,7 +296,16 @@ declare class Schematic<Model> {
291
296
  *
292
297
  * 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.
293
298
  * @param value Value to validate
294
- * @param errors Throws an error for the first validation failure
299
+ * @param options Validation options
300
+ * @returns `true` if the value matches the schema, otherwise throws an error
301
+ */
302
+ is(value: unknown, options: ValidationOptions<'throw'>): asserts value is Model;
303
+ /**
304
+ * Does the value match the schema?
305
+ *
306
+ * 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.
307
+ * @param value Value to validate
308
+ * @param errors Reporting type
295
309
  * @returns `true` if the value matches the schema, otherwise throws an error
296
310
  */
297
311
  is(value: unknown, errors: 'throw'): asserts value is Model;
@@ -300,7 +314,16 @@ declare class Schematic<Model> {
300
314
  *
301
315
  * Will validate that the value matches the schema and return a result of `true` or all validation information for validation failures from the same depth in the object.
302
316
  * @param value Value to validate
303
- * @param errors All
317
+ * @param options Validation options
318
+ * @returns `true` if the value matches the schema, otherwise `false`
319
+ */
320
+ is(value: unknown, options: ValidationOptions<'all'>): Result<true, ValidationInformation[]>;
321
+ /**
322
+ * Does the value match the schema?
323
+ *
324
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for validation failures from the same depth in the object.
325
+ * @param value Value to validate
326
+ * @param errors Reporting type
304
327
  * @returns `true` if the value matches the schema, otherwise `false`
305
328
  */
306
329
  is(value: unknown, errors: 'all'): Result<true, ValidationInformation[]>;
@@ -309,7 +332,16 @@ declare class Schematic<Model> {
309
332
  *
310
333
  * Will validate that the value matches the schema and return a result of `true` or all validation information for the failing property.
311
334
  * @param value Value to validate
312
- * @param errors First
335
+ * @param options Validation options
336
+ * @returns `true` if the value matches the schema, otherwise `false`
337
+ */
338
+ is(value: unknown, options: ValidationOptions<'first'>): Result<true, ValidationInformation>;
339
+ /**
340
+ * Does the value match the schema?
341
+ *
342
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for the failing property.
343
+ * @param value Value to validate
344
+ * @param errors Reporting type
313
345
  * @returns `true` if the value matches the schema, otherwise `false`
314
346
  */
315
347
  is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
@@ -318,9 +350,10 @@ declare class Schematic<Model> {
318
350
  *
319
351
  * Will validate that the value matches the schema and return `true` or `false`, without any validation information for validation failures.
320
352
  * @param value Value to validate
353
+ * @param strict Validate if unknown keys are present in the object? _(defaults to `false`)_
321
354
  * @returns `true` if the value matches the schema, otherwise `false`
322
355
  */
323
- is(value: unknown): value is Model;
356
+ is(value: unknown, strict?: true): value is Model;
324
357
  }
325
358
  /**
326
359
  * Create a schematic from a schema
@@ -341,7 +374,7 @@ declare function schematic<Model extends PlainObject>(schema: TypedSchema<Model>
341
374
  //#endregion
342
375
  //#region src/models/schema.plain.model.d.ts
343
376
  /**
344
- * A generic schema allowing {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry} as values
377
+ * A generic schema allowing nested schemas, {@link SchemaEntry} values, or arrays of {@link SchemaEntry} as values
345
378
  */
346
379
  type PlainSchema = {
347
380
  [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
@@ -366,11 +399,11 @@ type Schema = SchemaIndex;
366
399
  /**
367
400
  * A union of all valid types for a single schema entry
368
401
  *
369
- * Can be a {@link Constructor}, nested {@link Schema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
402
+ * Can be a {@link Constructor}, {@link PlainSchema}, {@link SchemaProperty}, {@link Schematic}, {@link ValueName} string, or a custom validator function
370
403
  */
371
404
  type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
372
405
  /**
373
- * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link NestedSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
406
+ * Index signature interface backing {@link Schema}, allowing string-keyed entries of {@link PlainSchema}, {@link SchemaEntry}, or arrays of {@link SchemaEntry}
374
407
  */
375
408
  interface SchemaIndex {
376
409
  [key: string]: PlainSchema | SchemaEntry | SchemaEntry[];
@@ -415,7 +448,7 @@ type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | Value
415
448
  *
416
449
  * Each key may hold a single validator or an array of validators that receive the typed value
417
450
  *
418
- * @template Value - `$type` value(s) to derive validator keys from
451
+ * @template Value `$type` value(s) to derive validator keys from
419
452
  *
420
453
  * @example
421
454
  * ```ts
@@ -430,8 +463,8 @@ type PropertyValidators<Value> = { [Key in ExtractValueNames<Value>]?: ((value:
430
463
  /**
431
464
  * Removes duplicate types from a tuple, preserving first occurrence order
432
465
  *
433
- * @template Value - Tuple to deduplicate
434
- * @template Seen - Accumulator for already-seen types _(internal)_
466
+ * @template Value Tuple to deduplicate
467
+ * @template Seen Accumulator for already-seen types _(internal)_
435
468
  *
436
469
  * @example
437
470
  * ```ts
@@ -443,7 +476,7 @@ type DeduplicateTuple<Value extends unknown[], Seen extends unknown[] = []> = Va
443
476
  /**
444
477
  * Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
445
478
  *
446
- * @template Value - Type to extract value names from
479
+ * @template Value Type to extract value names from
447
480
  *
448
481
  * @example
449
482
  * ```ts
@@ -455,27 +488,27 @@ type ExtractValueNames<Value> = Value extends ValueName ? Value : Value extends
455
488
  /**
456
489
  * Determines whether a schema entry is optional
457
490
  *
458
- * Returns `true` if the entry is a {@link SchemaProperty} or {@link NestedSchema} with `$required` set to `false`; otherwise returns `false`
491
+ * Returns `true` if the entry is a {@link SchemaProperty} with `$required` set to `false`; otherwise returns `false`
459
492
  *
460
- * @template Value - Schema entry to check
493
+ * @template Value Schema entry to check
461
494
  */
462
495
  type IsOptionalProperty<Value> = Value extends SchemaProperty ? Value['$required'] extends false ? true : false : false;
463
496
  /**
464
- * Extracts the last member from a union type by leveraging intersection of function return types
497
+ * Extracts the last member from a union type by leveraging contravariance of function parameter types
465
498
  *
466
- * @template Value - Union type
499
+ * @template Value Union type
467
500
  */
468
501
  type LastOfUnion<Value> = UnionToIntersection<Value extends unknown ? () => Value : never> extends (() => infer Item) ? Item : never;
469
502
  /**
470
503
  * Extracts keys from an object type that are optional
471
504
  *
472
- * @template Value - Object type to inspect
505
+ * @template Value Object type to inspect
473
506
  */
474
507
  type OptionalKeys<Value> = { [Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never }[keyof Value];
475
508
  /**
476
509
  * Extracts keys from an object type that are required _(i.e., not optional)_
477
510
  *
478
- * @template Value - Object type to inspect
511
+ * @template Value Object type to inspect
479
512
  */
480
513
  type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
481
514
  /**
@@ -483,8 +516,8 @@ type RequiredKeys<Value> = Exclude<keyof Value, OptionalKeys<Value>>;
483
516
  *
484
517
  * Used by {@link UnwrapSingle} to allow schema types in any order for small tuples _(length ≤ 5)_
485
518
  *
486
- * @template Tuple - Tuple to permute
487
- * @template Elput - Accumulator for the current permutation _(internal; name is Tuple backwards)_
519
+ * @template Tuple Tuple to permute
520
+ * @template Elput Accumulator for the current permutation _(internal; name is Tuple backwards)_
488
521
  *
489
522
  * @example
490
523
  * ```ts
@@ -498,9 +531,9 @@ type TuplePermutations<Tuple extends unknown[], Elput extends unknown[] = []> =
498
531
  *
499
532
  * Used internally by {@link TuplePermutations}
500
533
  *
501
- * @template Items - Tuple to remove from
502
- * @template Item - Stringified index to remove
503
- * @template Prefix - Accumulator for elements before the target _(internal)_
534
+ * @template Items Tuple to remove from
535
+ * @template Item Index as a string literal
536
+ * @template Prefix Accumulator for elements before the target _(internal)_
504
537
  */
505
538
  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;
506
539
  /**
@@ -508,7 +541,7 @@ type TupleRemoveAt<Items extends unknown[], Item extends string, Prefix extends
508
541
  *
509
542
  * Uses the contravariance of function parameter types to collapse a union into an intersection
510
543
  *
511
- * @template Value - Union type to convert
544
+ * @template Value Union type to convert
512
545
  *
513
546
  * @example
514
547
  * ```ts
@@ -522,8 +555,8 @@ type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => voi
522
555
  *
523
556
  * Repeatedly extracts the {@link LastOfUnion} member and prepends it to the accumulator
524
557
  *
525
- * @template Value - Union type to convert
526
- * @template Items - Accumulator for the resulting tuple _(internal)_
558
+ * @template Value Union type to convert
559
+ * @template Items Accumulator for the resulting tuple _(internal)_
527
560
  *
528
561
  * @example
529
562
  * ```ts
@@ -537,7 +570,7 @@ type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never]
537
570
  *
538
571
  * For tuples of length 2–5, returns all {@link TuplePermutations} to allow types in any order. Longer tuples are returned as-is
539
572
  *
540
- * @template Value - Tuple to potentially unwrap
573
+ * @template Value Tuple to potentially unwrap
541
574
  *
542
575
  * @example
543
576
  * ```ts
@@ -547,20 +580,11 @@ type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never]
547
580
  */
548
581
  type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
549
582
  /**
550
- * Basic value types
583
+ * A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
551
584
  */
552
585
  type ValueName = keyof Values;
553
586
  /**
554
- * Maps type name strings to their TypeScript equivalents
555
- *
556
- * Used by the type system to resolve {@link ValueName} strings into actual types
557
- *
558
- * @example
559
- * ```ts
560
- * // Values['string'] => string
561
- * // Values['date'] => Date
562
- * // Values['null'] => null
563
- * ```
587
+ * Maps {@link ValueName} strings to their TypeScript equivalents
564
588
  */
565
589
  type Values = {
566
590
  array: unknown[];