@oscarpalmer/jhunal 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -70,6 +70,16 @@ type GetOptions<Errors extends ReportingType> = BaseOptions<Errors> & {
70
70
  */
71
71
  type IsOptions<Errors extends ReportingType> = BaseOptions<Errors>;
72
72
  type Validator = (input: unknown, parameters: ValidatorParameters, get: boolean) => true | ValidationInformation[];
73
+ type ValidatorDefaults = {
74
+ value: unknown;
75
+ };
76
+ type ValidatorItem = {
77
+ defaults: ValidatorDefaults | undefined;
78
+ key: ValidationInformationKey;
79
+ required: boolean;
80
+ types: ValidatorType[];
81
+ validator: Validator;
82
+ };
73
83
  type ValidatorParameters = {
74
84
  clone: boolean;
75
85
  information?: ValidationInformation[];
@@ -79,4 +89,4 @@ type ValidatorParameters = {
79
89
  };
80
90
  type ValidatorType = Function | PlainObject | Schematic<unknown> | ValueName;
81
91
  //#endregion
82
- export { GetOptions, IsOptions, NamedValidatorHandlers, NamedValidators, ReportingInformation, ReportingType, SchematicError, ValidationError, ValidationInformation, ValidationInformationKey, Validator, ValidatorParameters, ValidatorType };
92
+ export { GetOptions, IsOptions, NamedValidatorHandlers, NamedValidators, ReportingInformation, ReportingType, SchematicError, ValidationError, ValidationInformation, ValidationInformationKey, Validator, ValidatorDefaults, ValidatorItem, ValidatorParameters, ValidatorType };
@@ -1,6 +1,6 @@
1
1
  import { NamedValidatorHandlers } from "../models/validation.model.mjs";
2
2
 
3
3
  //#region src/validator/named.handler.d.ts
4
- declare function getNamedHandlers(original: unknown, prefix: string): NamedValidatorHandlers;
4
+ declare function getNamedHandlers(original: unknown, prefix: string, allowed: boolean): NamedValidatorHandlers;
5
5
  //#endregion
6
6
  export { getNamedHandlers };
@@ -1,9 +1,10 @@
1
- import { SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TYPE_ALL } from "../constants.mjs";
1
+ import { PROPERTY_VALIDATORS, SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TYPE_ALL } from "../constants.mjs";
2
2
  import { isPlainObject } from "@oscarpalmer/atoms/is";
3
3
  //#region src/validator/named.handler.ts
4
- function getNamedHandlers(original, prefix) {
4
+ function getNamedHandlers(original, prefix, allowed) {
5
5
  const handlers = {};
6
6
  if (original == null) return handlers;
7
+ if (!allowed) throw new TypeError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", prefix).replace("<>", PROPERTY_VALIDATORS));
7
8
  if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
8
9
  const keys = Object.keys(original);
9
10
  const { length } = keys;
@@ -1,5 +1,4 @@
1
- import "../constants.mjs";
2
- import { getInvalidValidatorMessage } from "../helpers/message.helper.mjs";
1
+ import { getInputPropertyValidatorMessage } from "../helpers/message.helper.mjs";
3
2
  //#region src/validator/named.validator.ts
4
3
  function getNamedValidator(key, name, handlers) {
5
4
  const validator = namedValidators[name];
@@ -13,7 +12,7 @@ function getNamedValidator(key, name, handlers) {
13
12
  const information = {
14
13
  key,
15
14
  validator,
16
- message: getInvalidValidatorMessage(key.full, name, index, length),
15
+ message: getInputPropertyValidatorMessage(key.full, name, index, length),
17
16
  value: input
18
17
  };
19
18
  parameters.information?.push(information);
@@ -1,7 +1,7 @@
1
- import { PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, 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, TYPE_ALL } from "../constants.mjs";
2
- import { isSchematic } from "../helpers/misc.helper.mjs";
1
+ import { PROPERTY_DEFAULT, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY, TYPE_ALL } from "../constants.mjs";
2
+ import { getParameters, isSchematic } from "../helpers/misc.helper.mjs";
3
3
  import { SchematicError, ValidationError } from "../models/validation.model.mjs";
4
- import { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getUnknownKeysMessage } from "../helpers/message.helper.mjs";
4
+ import { getDefaultRequiredMessage, getDefaultTypeMessage, getDisallowedMessage, getInputPropertyMissingMessage, getInputPropertyTypeMessage, getInputTypeMessage, getRequiredMessage, getSchematicPropertyNullableMessage, getSchematicPropertyTypeMessage, getUnknownKeysMessage } from "../helpers/message.helper.mjs";
5
5
  import { getBaseValidator } from "./base.validator.mjs";
6
6
  import { getFunctionValidator } from "./function.validator.mjs";
7
7
  import { getNamedHandlers } from "./named.handler.mjs";
@@ -11,7 +11,13 @@ import { isPlainObject } from "@oscarpalmer/atoms/is";
11
11
  import { join } from "@oscarpalmer/atoms/string";
12
12
  import { clone } from "@oscarpalmer/atoms/value/clone";
13
13
  //#region src/validator/object.validator.ts
14
+ function getDefaults(obj, key, allowed) {
15
+ if (!("$default" in obj)) return;
16
+ if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_DEFAULT));
17
+ return { value: obj[PROPERTY_DEFAULT] };
18
+ }
14
19
  function getDisallowedProperty(obj) {
20
+ if ("$default" in obj) return PROPERTY_DEFAULT;
15
21
  if ("$required" in obj) return PROPERTY_REQUIRED;
16
22
  if ("$type" in obj) return PROPERTY_TYPE;
17
23
  if ("$validators" in obj) return PROPERTY_VALIDATORS;
@@ -22,14 +28,14 @@ function getObjectValidator(original, origin, fromType) {
22
28
  if (keysLength === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY);
23
29
  if (fromType ?? false) {
24
30
  const property = getDisallowedProperty(original);
25
- if (property != null) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", origin.full).replace("<>", property));
31
+ if (property != null) throw new SchematicError(getDisallowedMessage(origin.full, property));
26
32
  }
27
33
  const set = /* @__PURE__ */ new Set();
28
34
  const items = [];
29
35
  for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
30
36
  const key = keys[keyIndex];
31
37
  const value = original[key];
32
- if (value == null) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace("<>", join([origin?.full, key], ".")));
38
+ if (value == null) throw new SchematicError(getSchematicPropertyNullableMessage(join([origin?.full, key], ".")));
33
39
  const prefixedKey = origin == null ? key : join([origin.full, key], ".");
34
40
  const fullKey = {
35
41
  full: prefixedKey,
@@ -38,16 +44,18 @@ function getObjectValidator(original, origin, fromType) {
38
44
  let handlers = {};
39
45
  let required = true;
40
46
  let typed = false;
47
+ let defaults;
41
48
  let types;
42
49
  const validators = [];
43
50
  if (isPlainObject(value)) {
44
51
  typed = PROPERTY_TYPE in value;
45
52
  const type = typed ? value[PROPERTY_TYPE] : value;
46
- handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey);
47
- required = getRequired(key, value) ?? required;
53
+ defaults = getDefaults(value, prefixedKey, typed);
54
+ handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey, typed);
55
+ required = getRequired(value, prefixedKey, typed) ?? required;
48
56
  types = Array.isArray(type) ? type : [type];
49
57
  } else types = Array.isArray(value) ? value : [value];
50
- if (types.length === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", prefixedKey).replace("<>", String(value)));
58
+ if (types.length === 0) throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
51
59
  const typesLength = types.length;
52
60
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
53
61
  const type = types[typeIndex];
@@ -65,17 +73,22 @@ function getObjectValidator(original, origin, fromType) {
65
73
  case TYPE_ALL.has(type):
66
74
  validator = getNamedValidator(fullKey, type, handlers);
67
75
  break;
68
- default: throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", prefixedKey).replace("<>", String(type)));
76
+ default: throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
69
77
  }
70
78
  validators.push(validator);
71
79
  }
72
- set.add(key);
80
+ required = required && !types.includes("undefined");
81
+ if (defaults != null && !required) throw new SchematicError(getDefaultRequiredMessage(prefixedKey));
82
+ const validator = getBaseValidator(validators);
83
+ if (defaults != null && Array.isArray(validator(defaults.value, getParameters(), false))) throw new SchematicError(getDefaultTypeMessage(prefixedKey, types));
73
84
  items.push({
85
+ defaults,
86
+ required,
74
87
  types,
75
- key: fullKey,
76
- required: required && !types.includes("undefined"),
77
- validator: getBaseValidator(validators)
88
+ validator,
89
+ key: fullKey
78
90
  });
91
+ set.add(key);
79
92
  }
80
93
  const validatorsLength = items.length;
81
94
  return (input, parameters, get) => {
@@ -87,7 +100,7 @@ function getObjectValidator(original, origin, fromType) {
87
100
  short: ""
88
101
  },
89
102
  value: input,
90
- message: getInvalidInputMessage(input)
103
+ message: getInputTypeMessage(input)
91
104
  };
92
105
  if (parameters.reporting.throw) throw new ValidationError([information]);
93
106
  parameters.information?.push(information);
@@ -112,15 +125,19 @@ function getObjectValidator(original, origin, fromType) {
112
125
  const allInformation = [];
113
126
  const output = {};
114
127
  for (let validatorIndex = 0; validatorIndex < validatorsLength; validatorIndex += 1) {
115
- const { key, required, types, validator } = items[validatorIndex];
128
+ const { defaults, key, required, types, validator } = items[validatorIndex];
116
129
  const value = input[key.short];
117
130
  if (value === void 0) {
118
131
  if (required) {
132
+ if (get && defaults != null) {
133
+ output[key.short] = clone(defaults.value);
134
+ continue;
135
+ }
119
136
  if (parameters.reporting.none) return [];
120
137
  const information = {
121
138
  key,
122
139
  value,
123
- message: getInvalidMissingMessage(key.full, types)
140
+ message: getInputPropertyMissingMessage(key.full, types)
124
141
  };
125
142
  if (parameters.reporting.throw) throw new ValidationError([information]);
126
143
  parameters.information?.push(information);
@@ -144,7 +161,7 @@ function getObjectValidator(original, origin, fromType) {
144
161
  const information = typeof result !== "boolean" && result.length > 0 ? result : [{
145
162
  key,
146
163
  value,
147
- message: getInvalidTypeMessage(key.full, types, value)
164
+ message: getInputPropertyTypeMessage(key.full, types, value)
148
165
  }];
149
166
  if (parameters.reporting.throw) throw new ValidationError(information);
150
167
  if (parameters.reporting.all) {
@@ -158,9 +175,10 @@ function getObjectValidator(original, origin, fromType) {
158
175
  return allInformation.length === 0 ? true : allInformation;
159
176
  };
160
177
  }
161
- function getRequired(key, obj) {
178
+ function getRequired(obj, key, allowed) {
162
179
  if (!("$required" in obj)) return;
163
- if (typeof obj["$required"] !== "boolean") throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
180
+ if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
181
+ if (typeof obj["$required"] !== "boolean") throw new SchematicError(getRequiredMessage(key));
164
182
  return obj[PROPERTY_REQUIRED];
165
183
  }
166
184
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/jhunal",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Flies free beneath the glistening moons…",
5
5
  "keywords": [
6
6
  "schema",
package/src/constants.ts CHANGED
@@ -35,6 +35,8 @@ export const NAME_ERROR_VALIDATION = 'ValidationError';
35
35
 
36
36
  // #region Properties
37
37
 
38
+ export const PROPERTY_DEFAULT = '$default';
39
+
38
40
  export const PROPERTY_REQUIRED = '$required';
39
41
 
40
42
  export const PROPERTY_SCHEMATIC = '$schematic';
@@ -83,10 +85,16 @@ export const REPORTING_TYPES = new Set<ReportingType>([
83
85
 
84
86
  // #region Schematic validation
85
87
 
88
+ export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED =
89
+ "'<>' has a default value but is not required";
90
+
91
+ export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE =
92
+ "Expected default value for property '<>' to be <>";
93
+
86
94
  export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one property';
87
95
 
88
96
  export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED =
89
- "'<>.<>' property is not allowed for schemas in $type";
97
+ "'<>.<>' property is not allowed for plain schemas";
90
98
 
91
99
  export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE =
92
100
  "'<>' property must not be 'null' or 'undefined'";
@@ -118,52 +126,40 @@ export const TEMPLATE_PATTERN = '<>';
118
126
 
119
127
  export const TYPE_ARRAY = 'array';
120
128
 
121
- const TYPE_BIGINT = 'bigint';
122
-
123
- const TYPE_BOOLEAN = 'boolean';
124
-
125
- const TYPE_DATE = 'date';
126
-
127
129
  export const TYPE_FUNCTION = 'function';
128
130
 
129
131
  export const TYPE_FUNCTION_RESULT = 'a validated value';
130
132
 
131
133
  export const TYPE_NULL = 'null';
132
134
 
133
- const TYPE_NUMBER = 'number';
134
-
135
135
  export const TYPE_OBJECT = 'object';
136
136
 
137
- const TYPE_STRING = 'string';
138
-
139
- const TYPE_SYMBOL = 'symbol';
140
-
141
137
  export const TYPE_UNDEFINED = 'undefined';
142
138
 
143
139
  export const VALIDATABLE_TYPES = new Set<ValueName>([
144
140
  TYPE_ARRAY,
145
- TYPE_BIGINT,
146
- TYPE_BOOLEAN,
147
- TYPE_DATE,
141
+ 'bigint',
142
+ 'boolean',
143
+ 'date',
148
144
  TYPE_FUNCTION,
149
- TYPE_NUMBER,
145
+ 'number',
150
146
  TYPE_OBJECT,
151
- TYPE_STRING,
152
- TYPE_SYMBOL,
147
+ 'string',
148
+ 'symbol',
153
149
  ]);
154
150
 
155
151
  export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES, TYPE_NULL, TYPE_UNDEFINED]);
156
152
 
157
153
  export const PREFIXED_TYPES: Record<ValueName, string> = {
158
154
  [TYPE_ARRAY]: `an ${TYPE_ARRAY}`,
159
- [TYPE_BIGINT]: `a ${TYPE_BIGINT}`,
160
- [TYPE_BOOLEAN]: `a ${TYPE_BOOLEAN}`,
161
- [TYPE_DATE]: `a ${TYPE_DATE}`,
155
+ bigint: `a bigint`,
156
+ boolean: `a boolean`,
157
+ date: `a date`,
162
158
  [TYPE_FUNCTION]: `a ${TYPE_FUNCTION}`,
163
159
  [TYPE_NULL]: TYPE_NULL,
164
- [TYPE_NUMBER]: `a ${TYPE_NUMBER}`,
165
- [TYPE_STRING]: `a ${TYPE_STRING}`,
166
- [TYPE_SYMBOL]: `a ${TYPE_SYMBOL}`,
160
+ number: `a number`,
161
+ string: `a string`,
162
+ symbol: `a symbol`,
167
163
  [TYPE_OBJECT]: `an ${TYPE_OBJECT}`,
168
164
  [TYPE_UNDEFINED]: TYPE_UNDEFINED,
169
165
  };
@@ -6,10 +6,15 @@ import {
6
6
  CONJUNCTION_OR,
7
7
  CONJUNCTION_OR_COMMA,
8
8
  PREFIXED_TYPES,
9
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED,
10
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE,
11
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED,
12
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE,
13
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED,
14
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE,
9
15
  TEMPLATE_PATTERN,
10
16
  TYPE_ALL,
11
17
  TYPE_ARRAY,
12
- TYPE_FUNCTION,
13
18
  TYPE_FUNCTION_RESULT,
14
19
  TYPE_NULL,
15
20
  TYPE_OBJECT,
@@ -23,11 +28,41 @@ import {
23
28
  import type {ValueName} from '../models/misc.model';
24
29
  import type {ValidatorType} from '../models/validation.model';
25
30
 
26
- export function getInvalidInputMessage(actual: unknown): string {
31
+ // #region Defaults
32
+
33
+ export function getDefaultRequiredMessage(key: string): string {
34
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED.replace(TEMPLATE_PATTERN, key);
35
+ }
36
+
37
+ export function getDefaultTypeMessage(key: string, types: ValidatorType[]): string {
38
+ let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE.replace(TEMPLATE_PATTERN, key);
39
+
40
+ message = message.replace(TEMPLATE_PATTERN, renderTypes(types));
41
+
42
+ return message;
43
+ }
44
+
45
+ // #endregion
46
+
47
+ // #region Disallowed
48
+
49
+ export function getDisallowedMessage(key: string, property: string): string {
50
+ let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace(TEMPLATE_PATTERN, key);
51
+
52
+ message = message.replace(TEMPLATE_PATTERN, property);
53
+
54
+ return message;
55
+ }
56
+
57
+ // #endregion
58
+
59
+ // #region Input
60
+
61
+ export function getInputTypeMessage(actual: unknown): string {
27
62
  return VALIDATION_MESSAGE_INVALID_INPUT.replace(TEMPLATE_PATTERN, getValueType(actual));
28
63
  }
29
64
 
30
- export function getInvalidMissingMessage(key: string, types: ValidatorType[]): string {
65
+ export function getInputPropertyMissingMessage(key: string, types: ValidatorType[]): string {
31
66
  let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace(TEMPLATE_PATTERN, renderTypes(types));
32
67
 
33
68
  message = message.replace(TEMPLATE_PATTERN, key);
@@ -35,7 +70,7 @@ export function getInvalidMissingMessage(key: string, types: ValidatorType[]): s
35
70
  return message;
36
71
  }
37
72
 
38
- export function getInvalidTypeMessage(
73
+ export function getInputPropertyTypeMessage(
39
74
  key: string,
40
75
  types: ValidatorType[],
41
76
  actual: unknown,
@@ -48,7 +83,7 @@ export function getInvalidTypeMessage(
48
83
  return message;
49
84
  }
50
85
 
51
- export function getInvalidValidatorMessage(
86
+ export function getInputPropertyValidatorMessage(
52
87
  key: string,
53
88
  type: ValueName,
54
89
  index: number,
@@ -65,6 +100,22 @@ export function getInvalidValidatorMessage(
65
100
  return message;
66
101
  }
67
102
 
103
+ // #endregion
104
+
105
+ // #region Schematic
106
+
107
+ export function getSchematicPropertyNullableMessage(key: string): string {
108
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace(TEMPLATE_PATTERN, key);
109
+ }
110
+
111
+ export function getSchematicPropertyTypeMessage(key: string): string {
112
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key);
113
+ }
114
+
115
+ // #endregion
116
+
117
+ // #region Misc.
118
+
68
119
  function getPropertyType(type: ValidatorType): string {
69
120
  switch (true) {
70
121
  case typeof type === 'function':
@@ -78,10 +129,6 @@ function getPropertyType(type: ValidatorType): string {
78
129
  }
79
130
  }
80
131
 
81
- export function getUnknownKeysMessage(keys: string[]): string {
82
- return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace(TEMPLATE_PATTERN, renderKeys(keys));
83
- }
84
-
85
132
  function getValueType(value: unknown): string {
86
133
  const valueType = typeof value;
87
134
 
@@ -150,3 +197,21 @@ function renderTypes(types: ValidatorType[]): string {
150
197
 
151
198
  return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
152
199
  }
200
+
201
+ // #endregion
202
+
203
+ // #region Required
204
+
205
+ export function getRequiredMessage(key: string): string {
206
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key);
207
+ }
208
+
209
+ // #endregion
210
+
211
+ // #region Strictness
212
+
213
+ export function getUnknownKeysMessage(keys: string[]): string {
214
+ return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace(TEMPLATE_PATTERN, renderKeys(keys));
215
+ }
216
+
217
+ // #endregion
@@ -1,5 +1,5 @@
1
1
  import {isConstructor, isPlainObject} from '@oscarpalmer/atoms/is';
2
- import type {Constructor, PlainObject} from '@oscarpalmer/atoms/models';
2
+ import type {Constructor} from '@oscarpalmer/atoms/models';
3
3
  import {
4
4
  MESSAGE_CONSTRUCTOR,
5
5
  PROPERTY_SCHEMATIC,
@@ -8,7 +8,6 @@ import {
8
8
  REPORTING_NONE,
9
9
  REPORTING_THROW,
10
10
  REPORTING_TYPES,
11
- TYPE_OBJECT,
12
11
  } from '../constants';
13
12
  import type {
14
13
  ReportingInformation,
@@ -22,7 +21,7 @@ export function getParameters(input?: unknown): ValidatorParameters {
22
21
  return {
23
22
  clone: true,
24
23
  output: {},
25
- reporting: getReporting(REPORTING_NONE),
24
+ reporting: getReporting(),
26
25
  strict: input,
27
26
  };
28
27
  }
@@ -46,7 +45,7 @@ export function getParameters(input?: unknown): ValidatorParameters {
46
45
  };
47
46
  }
48
47
 
49
- export function getReporting(value: unknown): ReportingInformation {
48
+ export function getReporting(value?: unknown): ReportingInformation {
50
49
  const type = REPORTING_TYPES.has(value as ReportingType)
51
50
  ? (value as ReportingType)
52
51
  : REPORTING_NONE;
@@ -85,9 +84,9 @@ export function instanceOf<Instance>(
85
84
  */
86
85
  export function isSchematic(value: unknown): value is Schematic<never> {
87
86
  return (
88
- typeof value === TYPE_OBJECT &&
87
+ typeof value === 'object' &&
89
88
  value !== null &&
90
- PROPERTY_SCHEMATIC in (value as PlainObject) &&
91
- (value as PlainObject)[PROPERTY_SCHEMATIC] === true
89
+ PROPERTY_SCHEMATIC in value &&
90
+ value[PROPERTY_SCHEMATIC] === true
92
91
  );
93
92
  }
@@ -8,6 +8,7 @@ import type {ExtractValueNames, ValueName, Values} from './misc.model';
8
8
  export type PlainSchema = {
9
9
  [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
10
10
  } & {
11
+ $default?: never;
11
12
  $required?: never;
12
13
  $type?: never;
13
14
  $validators?: never;
@@ -56,6 +57,7 @@ export type SchemaEntry =
56
57
  * ```
57
58
  */
58
59
  export type SchemaProperty = {
60
+ $default?: unknown;
59
61
  /**
60
62
  * Whether the property is required _(defaults to `true`)_
61
63
  */
@@ -17,6 +17,7 @@ import type {ToSchemaPropertyType, ToSchemaType} from './transform.model';
17
17
  * ```
18
18
  */
19
19
  export type TypedPropertyOptional<Value> = {
20
+ $default?: never;
20
21
  $required: false;
21
22
  $type: ToSchemaPropertyType<Exclude<Value, undefined>>;
22
23
  $validators?: PropertyValidators<ToSchemaPropertyType<Exclude<Value, undefined>>>;
@@ -35,6 +36,7 @@ export type TypedPropertyOptional<Value> = {
35
36
  * ```
36
37
  */
37
38
  export type TypedPropertyRequired<Value> = {
39
+ $default?: unknown;
38
40
  $required?: true;
39
41
  $type: ToSchemaPropertyType<Value>;
40
42
  $validators?: PropertyValidators<ToSchemaPropertyType<Value>>;
@@ -61,31 +63,11 @@ export type TypedPropertyRequired<Value> = {
61
63
  export type TypedSchema<Model extends PlainObject> = Simplify<
62
64
  {
63
65
  [Key in RequiredKeys<Model>]: Model[Key] extends PlainObject
64
- ? TypedSchemaRequired<Model[Key]> | Schematic<Model[Key]>
66
+ ? Schematic<Model[Key]>
65
67
  : ToSchemaType<Model[Key]> | TypedPropertyRequired<Model[Key]>;
66
68
  } & {
67
69
  [Key in OptionalKeys<Model>]: Exclude<Model[Key], undefined> extends PlainObject
68
- ?
69
- | TypedSchemaOptional<Exclude<Model[Key], undefined>>
70
- | Schematic<Exclude<Model[Key], undefined>>
70
+ ? Schematic<Exclude<Model[Key], undefined>>
71
71
  : TypedPropertyOptional<Model[Key]>;
72
72
  }
73
73
  >;
74
-
75
- /**
76
- * A {@link TypedSchema} variant for optional nested objects, with `$required` fixed to `false`
77
- *
78
- * @template Model Nested object type
79
- */
80
- type TypedSchemaOptional<Model extends PlainObject> = {
81
- $required: false;
82
- } & TypedSchema<Model>;
83
-
84
- /**
85
- * A {@link TypedSchema} variant for required nested objects, with `$required` defaulting to `true`
86
- *
87
- * @template Model Nested object type
88
- */
89
- type TypedSchemaRequired<Model extends PlainObject> = {
90
- $required?: true;
91
- } & TypedSchema<Model>;
@@ -127,6 +127,18 @@ export type Validator = (
127
127
  get: boolean,
128
128
  ) => true | ValidationInformation[];
129
129
 
130
+ export type ValidatorDefaults = {
131
+ value: unknown;
132
+ };
133
+
134
+ export type ValidatorItem = {
135
+ defaults: ValidatorDefaults | undefined;
136
+ key: ValidationInformationKey;
137
+ required: boolean;
138
+ types: ValidatorType[];
139
+ validator: Validator;
140
+ };
141
+
130
142
  export type ValidatorParameters = {
131
143
  clone: boolean;
132
144
  information?: ValidationInformation[];
@@ -1,5 +1,7 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import {
3
+ PROPERTY_VALIDATORS,
4
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED,
3
5
  SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY,
4
6
  SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE,
5
7
  SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE,
@@ -9,13 +11,26 @@ import {
9
11
  import type {ValueName} from '../models/misc.model';
10
12
  import type {NamedValidatorHandlers} from '../models/validation.model';
11
13
 
12
- export function getNamedHandlers(original: unknown, prefix: string): NamedValidatorHandlers {
14
+ export function getNamedHandlers(
15
+ original: unknown,
16
+ prefix: string,
17
+ allowed: boolean,
18
+ ): NamedValidatorHandlers {
13
19
  const handlers: NamedValidatorHandlers = {};
14
20
 
15
21
  if (original == null) {
16
22
  return handlers;
17
23
  }
18
24
 
25
+ if (!allowed) {
26
+ throw new TypeError(
27
+ SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace(
28
+ TEMPLATE_PATTERN,
29
+ prefix,
30
+ ).replace(TEMPLATE_PATTERN, PROPERTY_VALIDATORS),
31
+ );
32
+ }
33
+
19
34
  if (!isPlainObject(original)) {
20
35
  throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
21
36
  }
@@ -1,5 +1,4 @@
1
- import {TYPE_OBJECT} from '../constants';
2
- import {getInvalidValidatorMessage} from '../helpers/message.helper';
1
+ import {getInputPropertyValidatorMessage} from '../helpers/message.helper';
3
2
  import type {ValueName} from '../models/misc.model';
4
3
  import type {
5
4
  NamedValidatorHandlers,
@@ -34,7 +33,7 @@ export function getNamedValidator(
34
33
  const information: ValidationInformation = {
35
34
  key,
36
35
  validator,
37
- message: getInvalidValidatorMessage(key.full, name, index, length),
36
+ message: getInputPropertyValidatorMessage(key.full, name, index, length),
38
37
  value: input,
39
38
  };
40
39
 
@@ -55,7 +54,7 @@ const namedValidators: NamedValidators = {
55
54
  function: value => typeof value === 'function',
56
55
  null: value => value === null,
57
56
  number: value => typeof value === 'number',
58
- object: value => typeof value === TYPE_OBJECT && value !== null,
57
+ object: value => typeof value === 'object' && value !== null,
59
58
  string: value => typeof value === 'string',
60
59
  symbol: value => typeof value === 'symbol',
61
60
  undefined: value => value === undefined,