@oscarpalmer/jhunal 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/constants.d.mts +11 -4
  2. package/dist/constants.mjs +27 -8
  3. package/dist/helpers/message.helper.d.mts +17 -0
  4. package/dist/helpers/message.helper.mjs +92 -0
  5. package/dist/helpers/misc.helper.d.mts +22 -0
  6. package/dist/helpers/misc.helper.mjs +56 -0
  7. package/dist/index.d.mts +309 -292
  8. package/dist/index.mjs +237 -166
  9. package/dist/models/schema.plain.model.d.mts +2 -0
  10. package/dist/models/schema.typed.model.d.mts +3 -17
  11. package/dist/models/validation.model.d.mts +28 -7
  12. package/dist/schematic.d.mts +25 -7
  13. package/dist/schematic.mjs +6 -4
  14. package/dist/validator/base.validator.d.mts +6 -0
  15. package/dist/validator/base.validator.mjs +19 -0
  16. package/dist/validator/function.validator.d.mts +6 -0
  17. package/dist/validator/function.validator.mjs +9 -0
  18. package/dist/validator/named.handler.d.mts +6 -0
  19. package/dist/validator/named.handler.mjs +23 -0
  20. package/dist/validator/named.validator.d.mts +7 -0
  21. package/dist/validator/named.validator.mjs +38 -0
  22. package/dist/validator/object.validator.d.mts +7 -0
  23. package/dist/validator/object.validator.mjs +185 -0
  24. package/dist/validator/schematic.validator.d.mts +7 -0
  25. package/dist/validator/schematic.validator.mjs +16 -0
  26. package/package.json +1 -1
  27. package/src/constants.ts +34 -6
  28. package/src/helpers/message.helper.ts +217 -0
  29. package/src/helpers/misc.helper.ts +92 -0
  30. package/src/index.ts +3 -3
  31. package/src/models/schema.plain.model.ts +2 -0
  32. package/src/models/schema.typed.model.ts +4 -22
  33. package/src/models/validation.model.ts +31 -6
  34. package/src/schematic.ts +43 -16
  35. package/src/validator/base.validator.ts +31 -0
  36. package/src/validator/function.validator.ts +9 -0
  37. package/src/validator/named.handler.ts +65 -0
  38. package/src/validator/named.validator.ts +61 -0
  39. package/src/validator/object.validator.ts +366 -0
  40. package/src/validator/schematic.validator.ts +25 -0
  41. package/dist/helpers.d.mts +0 -28
  42. package/dist/helpers.mjs +0 -120
  43. package/dist/validation.d.mts +0 -7
  44. package/dist/validation.mjs +0 -245
  45. package/src/helpers.ts +0 -249
  46. package/src/validation.ts +0 -498
package/dist/index.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
2
2
  import { join } from "@oscarpalmer/atoms/string";
3
3
  import { error, ok } from "@oscarpalmer/atoms/result/misc";
4
- import { join as join$1 } from "@oscarpalmer/atoms";
5
4
  import { clone } from "@oscarpalmer/atoms/value/clone";
6
5
  //#region src/constants.ts
7
6
  const CONJUNCTION_OR = " or ";
@@ -9,14 +8,14 @@ const CONJUNCTION_OR_COMMA = ", or ";
9
8
  const CONJUNCTION_AND = " and ";
10
9
  const CONJUNCTION_AND_COMMA = ", and ";
11
10
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
12
- const NAME_SCHEMATIC = "Schematic";
13
11
  const NAME_ERROR_SCHEMATIC = "SchematicError";
14
12
  const NAME_ERROR_VALIDATION = "ValidationError";
13
+ const PROPERTY_DEFAULT = "$default";
15
14
  const PROPERTY_REQUIRED = "$required";
16
15
  const PROPERTY_SCHEMATIC = "$schematic";
17
16
  const PROPERTY_TYPE = "$type";
18
17
  const PROPERTY_VALIDATORS = "$validators";
19
- const VALIDATION_MESSAGE_INVALID_INPUT = "Expected 'object' as input but received <>";
18
+ const VALIDATION_MESSAGE_INVALID_INPUT = "Expected an object as input but received <>";
20
19
  const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required property '<>'";
21
20
  const VALIDATION_MESSAGE_INVALID_TYPE = "Expected <> for '<>' but received <>";
22
21
  const VALIDATION_MESSAGE_INVALID_VALUE = "Value does not satisfy validator for '<>' and type '<>'";
@@ -31,8 +30,10 @@ const REPORTING_TYPES = new Set([
31
30
  REPORTING_NONE,
32
31
  REPORTING_THROW
33
32
  ]);
33
+ const SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED = "'<>' has a default value but is not required";
34
+ const SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE = "Expected default value for property '<>' to be <>";
34
35
  const SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
35
- const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<>.<>' property is not allowed for schemas in $type";
36
+ const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<>.<>' property is not allowed for plain schemas";
36
37
  const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE = "'<>' property must not be 'null' or 'undefined'";
37
38
  const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
38
39
  const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
@@ -41,71 +42,62 @@ const SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
41
42
  const SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE = "Validators must be an object";
42
43
  const SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE = "Validator '<>' must be a function or an array of functions";
43
44
  const TYPE_ARRAY = "array";
45
+ const TYPE_FUNCTION = "function";
46
+ const TYPE_FUNCTION_RESULT = "a validated value";
44
47
  const TYPE_NULL = "null";
45
48
  const TYPE_OBJECT = "object";
46
49
  const TYPE_UNDEFINED = "undefined";
47
50
  const TYPE_ALL = new Set([
48
51
  ...new Set([
49
- "array",
52
+ TYPE_ARRAY,
50
53
  "bigint",
51
54
  "boolean",
52
55
  "date",
53
- "function",
56
+ TYPE_FUNCTION,
54
57
  "number",
58
+ TYPE_OBJECT,
55
59
  "string",
56
- "symbol",
57
- TYPE_OBJECT
60
+ "symbol"
58
61
  ]),
59
- "null",
62
+ TYPE_NULL,
60
63
  TYPE_UNDEFINED
61
64
  ]);
65
+ const PREFIXED_TYPES = {
66
+ [TYPE_ARRAY]: `an ${TYPE_ARRAY}`,
67
+ bigint: `a bigint`,
68
+ boolean: `a boolean`,
69
+ date: `a date`,
70
+ [TYPE_FUNCTION]: `a ${TYPE_FUNCTION}`,
71
+ [TYPE_NULL]: TYPE_NULL,
72
+ number: `a number`,
73
+ string: `a string`,
74
+ symbol: `a symbol`,
75
+ [TYPE_OBJECT]: `an ${TYPE_OBJECT}`,
76
+ [TYPE_UNDEFINED]: TYPE_UNDEFINED
77
+ };
62
78
  //#endregion
63
- //#region src/helpers.ts
64
- function getInvalidInputMessage(actual) {
65
- return VALIDATION_MESSAGE_INVALID_INPUT.replace("<>", getValueType(actual));
66
- }
67
- function getInvalidMissingMessage(key, types) {
68
- let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace("<>", renderTypes(types));
69
- message = message.replace("<>", key);
70
- return message;
71
- }
72
- function getInvalidTypeMessage(key, types, actual) {
73
- let message = VALIDATION_MESSAGE_INVALID_TYPE.replace("<>", renderTypes(types));
74
- message = message.replace("<>", key);
75
- message = message.replace("<>", getValueType(actual));
76
- return message;
77
- }
78
- function getInvalidValidatorMessage(key, type, index, length) {
79
- let message = VALIDATION_MESSAGE_INVALID_VALUE.replace("<>", key);
80
- message = message.replace("<>", type);
81
- if (length > 1) message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace("<>", String(index));
82
- return message;
83
- }
79
+ //#region src/helpers/misc.helper.ts
84
80
  function getParameters(input) {
85
81
  if (typeof input === "boolean") return {
82
+ clone: true,
86
83
  output: {},
87
- reporting: getReporting(REPORTING_NONE),
84
+ reporting: getReporting(),
88
85
  strict: input
89
86
  };
90
87
  if (REPORTING_TYPES.has(input)) return {
88
+ clone: true,
91
89
  output: {},
92
90
  reporting: getReporting(input),
93
91
  strict: false
94
92
  };
95
93
  const options = isPlainObject(input) ? input : {};
96
94
  return {
95
+ clone: typeof options.clone === "boolean" ? options.clone : true,
97
96
  output: {},
98
97
  reporting: getReporting(options.errors),
99
98
  strict: typeof options.strict === "boolean" ? options.strict : false
100
99
  };
101
100
  }
102
- function getPropertyType(original) {
103
- if (typeof original === "function") return "a validated value";
104
- if (Array.isArray(original)) return `'array'`;
105
- if (isPlainObject(original)) return `'${TYPE_OBJECT}'`;
106
- if (isSchematic(original)) return `a ${NAME_SCHEMATIC}`;
107
- return `'${String(original)}'`;
108
- }
109
101
  function getReporting(value) {
110
102
  const type = REPORTING_TYPES.has(value) ? value : REPORTING_NONE;
111
103
  return {
@@ -116,21 +108,6 @@ function getReporting(value) {
116
108
  [REPORTING_THROW]: type === REPORTING_THROW
117
109
  };
118
110
  }
119
- function getUnknownKeysMessage(keys) {
120
- return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace("<>", renderKeys(keys));
121
- }
122
- function getValueType(value) {
123
- const valueType = typeof value;
124
- switch (true) {
125
- case value === null: return `'${TYPE_NULL}'`;
126
- case value === void 0: return `'${TYPE_UNDEFINED}'`;
127
- case valueType !== TYPE_OBJECT: return `'${valueType}'`;
128
- case Array.isArray(value): return `'${TYPE_ARRAY}'`;
129
- case isPlainObject(value): return `'${TYPE_OBJECT}'`;
130
- case isSchematic(value): return `a ${NAME_SCHEMATIC}`;
131
- default: return value.constructor.name;
132
- }
133
- }
134
111
  /**
135
112
  * Creates a validator function for a given constructor
136
113
  * @param constructor - Constructor to check against
@@ -151,6 +128,85 @@ function instanceOf(constructor) {
151
128
  function isSchematic(value) {
152
129
  return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
153
130
  }
131
+ //#endregion
132
+ //#region src/models/validation.model.ts
133
+ /**
134
+ * Thrown when a schema definition is invalid
135
+ */
136
+ var SchematicError = class extends Error {
137
+ constructor(message) {
138
+ super(message);
139
+ this.name = NAME_ERROR_SCHEMATIC;
140
+ }
141
+ };
142
+ /**
143
+ * Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
144
+ */
145
+ var ValidationError = class extends Error {
146
+ constructor(information) {
147
+ super(join(information.map((item) => item.message), "; "));
148
+ this.information = information;
149
+ this.name = NAME_ERROR_VALIDATION;
150
+ }
151
+ };
152
+ //#endregion
153
+ //#region src/helpers/message.helper.ts
154
+ function getDefaultRequiredMessage(key) {
155
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED.replace("<>", key);
156
+ }
157
+ function getDefaultTypeMessage(key, types) {
158
+ let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE.replace("<>", key);
159
+ message = message.replace("<>", renderTypes(types));
160
+ return message;
161
+ }
162
+ function getDisallowedMessage(key, property) {
163
+ let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", key);
164
+ message = message.replace("<>", property);
165
+ return message;
166
+ }
167
+ function getInputTypeMessage(actual) {
168
+ return VALIDATION_MESSAGE_INVALID_INPUT.replace("<>", getValueType(actual));
169
+ }
170
+ function getInputPropertyMissingMessage(key, types) {
171
+ let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace("<>", renderTypes(types));
172
+ message = message.replace("<>", key);
173
+ return message;
174
+ }
175
+ function getInputPropertyTypeMessage(key, types, actual) {
176
+ let message = VALIDATION_MESSAGE_INVALID_TYPE.replace("<>", renderTypes(types));
177
+ message = message.replace("<>", key);
178
+ message = message.replace("<>", getValueType(actual));
179
+ return message;
180
+ }
181
+ function getInputPropertyValidatorMessage(key, type, index, length) {
182
+ let message = VALIDATION_MESSAGE_INVALID_VALUE.replace("<>", key);
183
+ message = message.replace("<>", type);
184
+ if (length > 1) message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace("<>", String(index));
185
+ return message;
186
+ }
187
+ function getSchematicPropertyNullableMessage(key) {
188
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace("<>", key);
189
+ }
190
+ function getSchematicPropertyTypeMessage(key) {
191
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key);
192
+ }
193
+ function getPropertyType(type) {
194
+ switch (true) {
195
+ case typeof type === "function": return isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT;
196
+ case TYPE_ALL.has(type): return PREFIXED_TYPES[type];
197
+ default: return PREFIXED_TYPES[TYPE_OBJECT];
198
+ }
199
+ }
200
+ function getValueType(value) {
201
+ const valueType = typeof value;
202
+ switch (true) {
203
+ case value === null: return TYPE_NULL;
204
+ case Array.isArray(value): return PREFIXED_TYPES[TYPE_ARRAY];
205
+ case isPlainObject(value): return PREFIXED_TYPES[TYPE_OBJECT];
206
+ case valueType !== TYPE_OBJECT: return PREFIXED_TYPES[valueType];
207
+ default: return value.constructor.name;
208
+ }
209
+ }
154
210
  function renderKeys(keys) {
155
211
  return renderParts(keys.map((key) => `'${key}'`), CONJUNCTION_AND, CONJUNCTION_AND_COMMA);
156
212
  }
@@ -176,75 +232,117 @@ function renderTypes(types) {
176
232
  }
177
233
  return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
178
234
  }
235
+ function getRequiredMessage(key) {
236
+ return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key);
237
+ }
238
+ function getUnknownKeysMessage(keys) {
239
+ return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace("<>", renderKeys(keys));
240
+ }
179
241
  //#endregion
180
- //#region src/models/validation.model.ts
181
- /**
182
- * Thrown when a schema definition is invalid
183
- */
184
- var SchematicError = class extends Error {
185
- constructor(message) {
186
- super(message);
187
- this.name = NAME_ERROR_SCHEMATIC;
188
- }
189
- };
190
- /**
191
- * Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
192
- */
193
- var ValidationError = class extends Error {
194
- constructor(information) {
195
- super(join(information.map((item) => item.message), "; "));
196
- this.information = information;
197
- this.name = NAME_ERROR_VALIDATION;
198
- }
199
- };
200
- //#endregion
201
- //#region src/validation.ts
202
- function getDisallowedProperty(obj) {
203
- if ("$required" in obj) return PROPERTY_REQUIRED;
204
- if ("$type" in obj) return PROPERTY_TYPE;
205
- if ("$validators" in obj) return PROPERTY_VALIDATORS;
242
+ //#region src/validator/base.validator.ts
243
+ function getBaseValidator(validators) {
244
+ const { length } = validators;
245
+ return (input, parameters, get) => {
246
+ const allInformation = [];
247
+ for (let index = 0; index < length; index += 1) {
248
+ const previousInformation = parameters.information;
249
+ parameters.information = [];
250
+ const result = validators[index](input, parameters, get);
251
+ parameters.information = previousInformation;
252
+ if (result === true) return true;
253
+ parameters.information?.push(...result);
254
+ allInformation.push(...result);
255
+ }
256
+ return allInformation;
257
+ };
206
258
  }
259
+ //#endregion
260
+ //#region src/validator/function.validator.ts
207
261
  function getFunctionValidator(fn) {
208
262
  const validator = isConstructor(fn) ? instanceOf(fn) : fn;
209
- return (input) => validator(input) === true;
263
+ return (input) => validator(input) ? true : [];
264
+ }
265
+ //#endregion
266
+ //#region src/validator/named.handler.ts
267
+ function getNamedHandlers(original, prefix, allowed) {
268
+ const handlers = {};
269
+ if (original == null) return handlers;
270
+ if (!allowed) throw new TypeError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", prefix).replace("<>", PROPERTY_VALIDATORS));
271
+ if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
272
+ const keys = Object.keys(original);
273
+ const { length } = keys;
274
+ for (let index = 0; index < length; index += 1) {
275
+ const key = keys[index];
276
+ if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
277
+ const value = original[key];
278
+ handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
279
+ if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
280
+ return item;
281
+ });
282
+ }
283
+ return handlers;
210
284
  }
285
+ //#endregion
286
+ //#region src/validator/named.validator.ts
211
287
  function getNamedValidator(key, name, handlers) {
212
288
  const validator = namedValidators[name];
213
289
  const named = handlers[name] ?? [];
214
290
  const { length } = named;
215
291
  return (input, parameters) => {
216
- if (!validator(input)) return false;
292
+ if (!validator(input)) return [];
217
293
  for (let index = 0; index < length; index += 1) {
218
294
  const handler = named[index];
219
295
  if (handler(input) === true) continue;
220
296
  const information = {
221
297
  key,
222
298
  validator,
223
- message: getInvalidValidatorMessage(key.full, name, index, length),
299
+ message: getInputPropertyValidatorMessage(key.full, name, index, length),
224
300
  value: input
225
301
  };
226
302
  parameters.information?.push(information);
227
- return parameters.reporting.none ? false : [information];
303
+ return parameters.reporting.none ? [] : [information];
228
304
  }
229
305
  return true;
230
306
  };
231
307
  }
232
- function getNamedHandlers(original, prefix) {
233
- const handlers = {};
234
- if (original == null) return handlers;
235
- if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
236
- const keys = Object.keys(original);
237
- const { length } = keys;
238
- for (let index = 0; index < length; index += 1) {
239
- const key = keys[index];
240
- if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
241
- const value = original[key];
242
- handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
243
- if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
244
- return item;
245
- });
246
- }
247
- return handlers;
308
+ const namedValidators = {
309
+ array: Array.isArray,
310
+ bigint: (value) => typeof value === "bigint",
311
+ boolean: (value) => typeof value === "boolean",
312
+ date: (value) => value instanceof Date,
313
+ function: (value) => typeof value === "function",
314
+ null: (value) => value === null,
315
+ number: (value) => typeof value === "number",
316
+ object: (value) => typeof value === "object" && value !== null,
317
+ string: (value) => typeof value === "string",
318
+ symbol: (value) => typeof value === "symbol",
319
+ undefined: (value) => value === void 0
320
+ };
321
+ //#endregion
322
+ //#region src/validator/schematic.validator.ts
323
+ function getSchematicValidator(schematic) {
324
+ const validator = schematicValidator.get(schematic);
325
+ return (input, parameters, get) => {
326
+ let result;
327
+ if (isPlainObject(input)) result = validator(input, parameters, get);
328
+ else result = [];
329
+ if (result === true) return result;
330
+ parameters.information?.push(...result);
331
+ return result;
332
+ };
333
+ }
334
+ //#endregion
335
+ //#region src/validator/object.validator.ts
336
+ function getDefaults(obj, key, allowed) {
337
+ if (!("$default" in obj)) return;
338
+ if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_DEFAULT));
339
+ return { value: obj[PROPERTY_DEFAULT] };
340
+ }
341
+ function getDisallowedProperty(obj) {
342
+ if ("$default" in obj) return PROPERTY_DEFAULT;
343
+ if ("$required" in obj) return PROPERTY_REQUIRED;
344
+ if ("$type" in obj) return PROPERTY_TYPE;
345
+ if ("$validators" in obj) return PROPERTY_VALIDATORS;
248
346
  }
249
347
  function getObjectValidator(original, origin, fromType) {
250
348
  const keys = Object.keys(original);
@@ -252,15 +350,15 @@ function getObjectValidator(original, origin, fromType) {
252
350
  if (keysLength === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY);
253
351
  if (fromType ?? false) {
254
352
  const property = getDisallowedProperty(original);
255
- if (property != null) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", origin.full).replace("<>", property));
353
+ if (property != null) throw new SchematicError(getDisallowedMessage(origin.full, property));
256
354
  }
257
355
  const set = /* @__PURE__ */ new Set();
258
356
  const items = [];
259
357
  for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
260
358
  const key = keys[keyIndex];
261
359
  const value = original[key];
262
- if (value == null) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace("<>", join$1([origin?.full, key], ".")));
263
- const prefixedKey = origin == null ? key : join$1([origin.full, key], ".");
360
+ if (value == null) throw new SchematicError(getSchematicPropertyNullableMessage(join([origin?.full, key], ".")));
361
+ const prefixedKey = origin == null ? key : join([origin.full, key], ".");
264
362
  const fullKey = {
265
363
  full: prefixedKey,
266
364
  short: key
@@ -268,16 +366,18 @@ function getObjectValidator(original, origin, fromType) {
268
366
  let handlers = {};
269
367
  let required = true;
270
368
  let typed = false;
369
+ let defaults;
271
370
  let types;
272
371
  const validators = [];
273
372
  if (isPlainObject(value)) {
274
373
  typed = PROPERTY_TYPE in value;
275
374
  const type = typed ? value[PROPERTY_TYPE] : value;
276
- handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey);
277
- required = getRequired(key, value) ?? required;
375
+ defaults = getDefaults(value, prefixedKey, typed);
376
+ handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey, typed);
377
+ required = getRequired(value, prefixedKey, typed) ?? required;
278
378
  types = Array.isArray(type) ? type : [type];
279
379
  } else types = Array.isArray(value) ? value : [value];
280
- if (types.length === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", prefixedKey).replace("<>", String(value)));
380
+ if (types.length === 0) throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
281
381
  const typesLength = types.length;
282
382
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
283
383
  const type = types[typeIndex];
@@ -295,33 +395,38 @@ function getObjectValidator(original, origin, fromType) {
295
395
  case TYPE_ALL.has(type):
296
396
  validator = getNamedValidator(fullKey, type, handlers);
297
397
  break;
298
- default: throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", prefixedKey).replace("<>", String(type)));
398
+ default: throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
299
399
  }
300
400
  validators.push(validator);
301
401
  }
302
- set.add(key);
402
+ required = required && !types.includes("undefined");
403
+ if (defaults != null && !required) throw new SchematicError(getDefaultRequiredMessage(prefixedKey));
404
+ const validator = getBaseValidator(validators);
405
+ if (defaults != null && Array.isArray(validator(defaults.value, getParameters(), false))) throw new SchematicError(getDefaultTypeMessage(prefixedKey, types));
303
406
  items.push({
407
+ defaults,
408
+ required,
304
409
  types,
305
- key: fullKey,
306
- required: required && !types.includes("undefined"),
307
- validator: getValidator(validators)
410
+ validator,
411
+ key: fullKey
308
412
  });
413
+ set.add(key);
309
414
  }
310
415
  const validatorsLength = items.length;
311
416
  return (input, parameters, get) => {
312
417
  if (!isPlainObject(input)) {
313
- if (origin != null) return false;
418
+ if (origin != null) return [];
314
419
  const information = {
315
420
  key: {
316
421
  full: "",
317
422
  short: ""
318
423
  },
319
424
  value: input,
320
- message: getInvalidInputMessage(input)
425
+ message: getInputTypeMessage(input)
321
426
  };
322
427
  if (parameters.reporting.throw) throw new ValidationError([information]);
323
428
  parameters.information?.push(information);
324
- return parameters.reporting.none ? false : [information];
429
+ return [information];
325
430
  }
326
431
  if (parameters.strict) {
327
432
  const unknownKeys = Object.keys(input).filter((key) => !set.has(key));
@@ -336,21 +441,25 @@ function getObjectValidator(original, origin, fromType) {
336
441
  };
337
442
  if (parameters.reporting.throw) throw new ValidationError([information]);
338
443
  parameters.information?.push(information);
339
- return parameters.reporting.none ? false : [information];
444
+ return [information];
340
445
  }
341
446
  }
342
447
  const allInformation = [];
343
448
  const output = {};
344
449
  for (let validatorIndex = 0; validatorIndex < validatorsLength; validatorIndex += 1) {
345
- const { key, required, types, validator } = items[validatorIndex];
450
+ const { defaults, key, required, types, validator } = items[validatorIndex];
346
451
  const value = input[key.short];
347
452
  if (value === void 0) {
348
453
  if (required) {
349
- if (parameters.reporting.none) return false;
454
+ if (get && defaults != null) {
455
+ output[key.short] = clone(defaults.value);
456
+ continue;
457
+ }
458
+ if (parameters.reporting.none) return [];
350
459
  const information = {
351
460
  key,
352
461
  value,
353
- message: getInvalidMissingMessage(key.full, types)
462
+ message: getInputPropertyMissingMessage(key.full, types)
354
463
  };
355
464
  if (parameters.reporting.throw) throw new ValidationError([information]);
356
465
  parameters.information?.push(information);
@@ -366,16 +475,15 @@ function getObjectValidator(original, origin, fromType) {
366
475
  parameters.output = output;
367
476
  const result = validator(value, parameters, get);
368
477
  parameters.output = previousOutput;
369
- if (result === false) continue;
370
478
  if (result === true) {
371
- if (get) output[key.short] = clone(value);
479
+ if (get && !isPlainObject(value)) output[key.short] = parameters.clone ? clone(value) : value;
372
480
  continue;
373
481
  }
374
- if (parameters.reporting.none) return false;
482
+ if (parameters.reporting.none) return [];
375
483
  const information = typeof result !== "boolean" && result.length > 0 ? result : [{
376
484
  key,
377
485
  value,
378
- message: getInvalidTypeMessage(key.full, types, value)
486
+ message: getInputPropertyTypeMessage(key.full, types, value)
379
487
  }];
380
488
  if (parameters.reporting.throw) throw new ValidationError(information);
381
489
  if (parameters.reporting.all) {
@@ -386,54 +494,15 @@ function getObjectValidator(original, origin, fromType) {
386
494
  }
387
495
  if (get) if (origin == null) parameters.output = output;
388
496
  else parameters.output[origin.short] = output;
389
- return parameters.reporting.none || allInformation.length === 0 ? true : allInformation;
497
+ return allInformation.length === 0 ? true : allInformation;
390
498
  };
391
499
  }
392
- function getRequired(key, obj) {
500
+ function getRequired(obj, key, allowed) {
393
501
  if (!("$required" in obj)) return;
394
- if (typeof obj["$required"] !== "boolean") throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
502
+ if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
503
+ if (typeof obj["$required"] !== "boolean") throw new SchematicError(getRequiredMessage(key));
395
504
  return obj[PROPERTY_REQUIRED];
396
505
  }
397
- function getSchematicValidator(schematic) {
398
- const validator = schematicValidator.get(schematic);
399
- return (input, parameters, get) => {
400
- let result = false;
401
- if (isPlainObject(input)) result = validator(input, parameters, get);
402
- if (typeof result === "boolean") return result;
403
- parameters.information?.push(...result);
404
- return result.length === 0 ? true : result;
405
- };
406
- }
407
- function getValidator(validators) {
408
- const { length } = validators;
409
- return (input, parameters, get) => {
410
- const allInformation = [];
411
- for (let index = 0; index < length; index += 1) {
412
- const previousInformation = parameters.information;
413
- parameters.information = [];
414
- const result = validators[index](input, parameters, get);
415
- parameters.information = previousInformation;
416
- if (result === false) continue;
417
- if (result === true) return true;
418
- parameters.information?.push(...result);
419
- allInformation.push(...result);
420
- }
421
- return allInformation;
422
- };
423
- }
424
- const namedValidators = {
425
- array: Array.isArray,
426
- bigint: (value) => typeof value === "bigint",
427
- boolean: (value) => typeof value === "boolean",
428
- date: (value) => value instanceof Date,
429
- function: (value) => typeof value === "function",
430
- null: (value) => value === null,
431
- number: (value) => typeof value === "number",
432
- object: (value) => typeof value === "object" && value !== null,
433
- string: (value) => typeof value === "string",
434
- symbol: (value) => typeof value === "symbol",
435
- undefined: (value) => value === void 0
436
- };
437
506
  //#endregion
438
507
  //#region src/schematic.ts
439
508
  /**
@@ -449,13 +518,15 @@ var Schematic = class {
449
518
  get(value, options) {
450
519
  const parameters = getParameters(options);
451
520
  const result = this.#validator(value, parameters, true);
452
- if (typeof result === "boolean") return parameters.reporting.none ? result ? parameters.output : void 0 : ok(parameters.output);
521
+ if (result === true) return parameters.reporting.none || parameters.reporting.throw ? parameters.output : ok(parameters.output);
522
+ if (parameters.reporting.none) return;
453
523
  return error(parameters.reporting.all ? result : result[0]);
454
524
  }
455
525
  is(value, options) {
456
526
  const parameters = getParameters(options);
457
527
  const result = this.#validator(value, parameters, false);
458
- if (typeof result === "boolean") return parameters.reporting.none ? result : ok(result);
528
+ if (result === true) return parameters.reporting.none || parameters.reporting.throw ? result : ok(result);
529
+ if (parameters.reporting.none) return false;
459
530
  return error(parameters.reporting.all ? result : result[0]);
460
531
  }
461
532
  };
@@ -9,6 +9,7 @@ import { Constructor } from "@oscarpalmer/atoms/models";
9
9
  type PlainSchema = {
10
10
  [key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
11
11
  } & {
12
+ $default?: never;
12
13
  $required?: never;
13
14
  $type?: never;
14
15
  $validators?: never;
@@ -48,6 +49,7 @@ type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknow
48
49
  * ```
49
50
  */
50
51
  type SchemaProperty = {
52
+ $default?: unknown;
51
53
  /**
52
54
  * Whether the property is required _(defaults to `true`)_
53
55
  */
@@ -18,6 +18,7 @@ import { PlainObject, Simplify } from "@oscarpalmer/atoms/models";
18
18
  * ```
19
19
  */
20
20
  type TypedPropertyOptional<Value> = {
21
+ $default?: never;
21
22
  $required: false;
22
23
  $type: ToSchemaPropertyType<Exclude<Value, undefined>>;
23
24
  $validators?: PropertyValidators<ToSchemaPropertyType<Exclude<Value, undefined>>>;
@@ -35,6 +36,7 @@ type TypedPropertyOptional<Value> = {
35
36
  * ```
36
37
  */
37
38
  type TypedPropertyRequired<Value> = {
39
+ $default?: unknown;
38
40
  $required?: true;
39
41
  $type: ToSchemaPropertyType<Value>;
40
42
  $validators?: PropertyValidators<ToSchemaPropertyType<Value>>;
@@ -57,22 +59,6 @@ type TypedPropertyRequired<Value> = {
57
59
  * };
58
60
  * ```
59
61
  */
60
- 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]> }>;
61
- /**
62
- * A {@link TypedSchema} variant for optional nested objects, with `$required` fixed to `false`
63
- *
64
- * @template Model Nested object type
65
- */
66
- type TypedSchemaOptional<Model extends PlainObject> = {
67
- $required: false;
68
- } & TypedSchema<Model>;
69
- /**
70
- * A {@link TypedSchema} variant for required nested objects, with `$required` defaulting to `true`
71
- *
72
- * @template Model Nested object type
73
- */
74
- type TypedSchemaRequired<Model extends PlainObject> = {
75
- $required?: true;
76
- } & TypedSchema<Model>;
62
+ type TypedSchema<Model extends PlainObject> = Simplify<{ [Key in RequiredKeys<Model>]: Model[Key] extends PlainObject ? Schematic<Model[Key]> : ToSchemaType<Model[Key]> | TypedPropertyRequired<Model[Key]> } & { [Key in OptionalKeys<Model>]: Exclude<Model[Key], undefined> extends PlainObject ? Schematic<Exclude<Model[Key], undefined>> : TypedPropertyOptional<Model[Key]> }>;
77
63
  //#endregion
78
64
  export { TypedPropertyOptional, TypedPropertyRequired, TypedSchema };