@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
@@ -46,21 +46,42 @@ type ValidationInformationKey = {
46
46
  full: string;
47
47
  short: string;
48
48
  };
49
- /**
50
- * Options for validation
51
- */
52
- type ValidationOptions<Errors extends ReportingType> = {
49
+ type BaseOptions<Errors extends ReportingType> = {
53
50
  /**
54
51
  * How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
55
52
  */
56
- errors?: Errors;
53
+ errors: Errors;
57
54
  /**
58
55
  * Validate if unknown keys are present in the object? _(defaults to `false`)_
59
56
  */
60
57
  strict?: boolean;
61
58
  };
62
- type Validator = (input: unknown, parameters: ValidatorParameters, get: boolean) => boolean | ValidationInformation[];
59
+ /**
60
+ * Options for validating and getting a value from an input
61
+ */
62
+ type GetOptions<Errors extends ReportingType> = BaseOptions<Errors> & {
63
+ /**
64
+ * Get a deeply cloned version of the input? _(defaults to `true`)_
65
+ */
66
+ clone?: boolean;
67
+ };
68
+ /**
69
+ * Options for validation an input value
70
+ */
71
+ type IsOptions<Errors extends ReportingType> = BaseOptions<Errors>;
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
+ };
63
83
  type ValidatorParameters = {
84
+ clone: boolean;
64
85
  information?: ValidationInformation[];
65
86
  output: PlainObject;
66
87
  reporting: ReportingInformation;
@@ -68,4 +89,4 @@ type ValidatorParameters = {
68
89
  };
69
90
  type ValidatorType = Function | PlainObject | Schematic<unknown> | ValueName;
70
91
  //#endregion
71
- export { NamedValidatorHandlers, NamedValidators, ReportingInformation, ReportingType, SchematicError, ValidationError, ValidationInformation, ValidationInformationKey, ValidationOptions, 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 { Infer } from "./models/infer.model.mjs";
2
2
  import { TypedSchema } from "./models/schema.typed.model.mjs";
3
- import { ValidationInformation, ValidationOptions, Validator } from "./models/validation.model.mjs";
3
+ import { GetOptions, IsOptions, ValidationInformation, Validator } from "./models/validation.model.mjs";
4
4
  import { Schema } from "./models/schema.plain.model.mjs";
5
5
  import { PlainObject } from "@oscarpalmer/atoms/models";
6
6
  import { Result } from "@oscarpalmer/atoms/result/models";
@@ -21,7 +21,7 @@ declare class Schematic<Model> {
21
21
  * @param options Validation options
22
22
  * @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
23
23
  */
24
- get(value: unknown, options: ValidationOptions<'throw'>): Model;
24
+ get(value: unknown, options: GetOptions<'throw'>): Model;
25
25
  /**
26
26
  * Parse a value according to the schema
27
27
  *
@@ -39,7 +39,7 @@ declare class Schematic<Model> {
39
39
  * @param options Validation options
40
40
  * @returns Result holding deeply cloned value or all validation information
41
41
  */
42
- get(value: unknown, options: ValidationOptions<'all'>): Result<Model, ValidationInformation[]>;
42
+ get(value: unknown, options: GetOptions<'all'>): Result<Model, ValidationInformation[]>;
43
43
  /**
44
44
  * Parse a value according to the schema
45
45
  *
@@ -57,7 +57,7 @@ declare class Schematic<Model> {
57
57
  * @param options Validation options
58
58
  * @returns Result holding deeply cloned value or all validation information
59
59
  */
60
- get(value: unknown, options: ValidationOptions<'first'>): Result<Model, ValidationInformation>;
60
+ get(value: unknown, options: GetOptions<'first'>): Result<Model, ValidationInformation>;
61
61
  /**
62
62
  * Parse a value according to the schema
63
63
  *
@@ -67,6 +67,15 @@ declare class Schematic<Model> {
67
67
  * @returns Result holding deeply cloned value or all validation information
68
68
  */
69
69
  get(value: unknown, errors: 'first'): Result<Model, ValidationInformation>;
70
+ /**
71
+ * Parse a value according to the schema
72
+ *
73
+ * Returns a deeply cloned version of the value or `undefined` if the value does not match the schema
74
+ * @param value Value to parse
75
+ * @param options Validation options
76
+ * @returns Deeply cloned value, or `undefined` if it's invalid
77
+ */
78
+ get(value: unknown, options: GetOptions<'none'>): Model | undefined;
70
79
  /**
71
80
  * Parse a value according to the schema
72
81
  *
@@ -84,7 +93,7 @@ declare class Schematic<Model> {
84
93
  * @param options Validation options
85
94
  * @returns `true` if the value matches the schema, otherwise throws an error
86
95
  */
87
- is(value: unknown, options: ValidationOptions<'throw'>): asserts value is Model;
96
+ is(value: unknown, options: IsOptions<'throw'>): asserts value is Model;
88
97
  /**
89
98
  * Does the value match the schema?
90
99
  *
@@ -102,7 +111,7 @@ declare class Schematic<Model> {
102
111
  * @param options Validation options
103
112
  * @returns Result holding `true` or all validation information
104
113
  */
105
- is(value: unknown, options: ValidationOptions<'all'>): Result<true, ValidationInformation[]>;
114
+ is(value: unknown, options: IsOptions<'all'>): Result<true, ValidationInformation[]>;
106
115
  /**
107
116
  * Does the value match the schema?
108
117
  *
@@ -120,7 +129,7 @@ declare class Schematic<Model> {
120
129
  * @param options Validation options
121
130
  * @returns `true` if the value matches the schema, otherwise `false`
122
131
  */
123
- is(value: unknown, options: ValidationOptions<'first'>): Result<true, ValidationInformation>;
132
+ is(value: unknown, options: IsOptions<'first'>): Result<true, ValidationInformation>;
124
133
  /**
125
134
  * Does the value match the schema?
126
135
  *
@@ -130,6 +139,15 @@ declare class Schematic<Model> {
130
139
  * @returns `true` if the value matches the schema, otherwise `false`
131
140
  */
132
141
  is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
142
+ /**
143
+ * Does the value match the schema?
144
+ *
145
+ * Will validate that the value matches the schema and return `true` or `false`, without any validation information for validation failures
146
+ * @param value Value to validate
147
+ * @param options Validation options
148
+ * @returns `true` if the value matches the schema, otherwise `false`
149
+ */
150
+ is(value: unknown, options: IsOptions<'none'>): value is Model;
133
151
  /**
134
152
  * Does the value match the schema?
135
153
  *
@@ -1,7 +1,7 @@
1
1
  import { PROPERTY_SCHEMATIC, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE } from "./constants.mjs";
2
- import { getParameters, isSchematic } from "./helpers.mjs";
2
+ import { getParameters, isSchematic } from "./helpers/misc.helper.mjs";
3
3
  import { SchematicError } from "./models/validation.model.mjs";
4
- import { getObjectValidator } from "./validation.mjs";
4
+ import { getObjectValidator } from "./validator/object.validator.mjs";
5
5
  import { isPlainObject } from "@oscarpalmer/atoms/is";
6
6
  import { error, ok } from "@oscarpalmer/atoms/result/misc";
7
7
  //#region src/schematic.ts
@@ -18,13 +18,15 @@ var Schematic = class {
18
18
  get(value, options) {
19
19
  const parameters = getParameters(options);
20
20
  const result = this.#validator(value, parameters, true);
21
- if (typeof result === "boolean") return parameters.reporting.none ? result ? parameters.output : void 0 : ok(parameters.output);
21
+ if (result === true) return parameters.reporting.none || parameters.reporting.throw ? parameters.output : ok(parameters.output);
22
+ if (parameters.reporting.none) return;
22
23
  return error(parameters.reporting.all ? result : result[0]);
23
24
  }
24
25
  is(value, options) {
25
26
  const parameters = getParameters(options);
26
27
  const result = this.#validator(value, parameters, false);
27
- if (typeof result === "boolean") return parameters.reporting.none ? result : ok(result);
28
+ if (result === true) return parameters.reporting.none || parameters.reporting.throw ? result : ok(result);
29
+ if (parameters.reporting.none) return false;
28
30
  return error(parameters.reporting.all ? result : result[0]);
29
31
  }
30
32
  };
@@ -0,0 +1,6 @@
1
+ import { Validator } from "../models/validation.model.mjs";
2
+
3
+ //#region src/validator/base.validator.d.ts
4
+ declare function getBaseValidator(validators: Validator[]): Validator;
5
+ //#endregion
6
+ export { getBaseValidator };
@@ -0,0 +1,19 @@
1
+ //#region src/validator/base.validator.ts
2
+ function getBaseValidator(validators) {
3
+ const { length } = validators;
4
+ return (input, parameters, get) => {
5
+ const allInformation = [];
6
+ for (let index = 0; index < length; index += 1) {
7
+ const previousInformation = parameters.information;
8
+ parameters.information = [];
9
+ const result = validators[index](input, parameters, get);
10
+ parameters.information = previousInformation;
11
+ if (result === true) return true;
12
+ parameters.information?.push(...result);
13
+ allInformation.push(...result);
14
+ }
15
+ return allInformation;
16
+ };
17
+ }
18
+ //#endregion
19
+ export { getBaseValidator };
@@ -0,0 +1,6 @@
1
+ import { Validator } from "../models/validation.model.mjs";
2
+
3
+ //#region src/validator/function.validator.d.ts
4
+ declare function getFunctionValidator(fn: Function): Validator;
5
+ //#endregion
6
+ export { getFunctionValidator };
@@ -0,0 +1,9 @@
1
+ import { instanceOf } from "../helpers/misc.helper.mjs";
2
+ import { isConstructor } from "@oscarpalmer/atoms/is";
3
+ //#region src/validator/function.validator.ts
4
+ function getFunctionValidator(fn) {
5
+ const validator = isConstructor(fn) ? instanceOf(fn) : fn;
6
+ return (input) => validator(input) ? true : [];
7
+ }
8
+ //#endregion
9
+ export { getFunctionValidator };
@@ -0,0 +1,6 @@
1
+ import { NamedValidatorHandlers } from "../models/validation.model.mjs";
2
+
3
+ //#region src/validator/named.handler.d.ts
4
+ declare function getNamedHandlers(original: unknown, prefix: string, allowed: boolean): NamedValidatorHandlers;
5
+ //#endregion
6
+ export { getNamedHandlers };
@@ -0,0 +1,23 @@
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
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
3
+ //#region src/validator/named.handler.ts
4
+ function getNamedHandlers(original, prefix, allowed) {
5
+ const handlers = {};
6
+ if (original == null) return handlers;
7
+ if (!allowed) throw new TypeError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", prefix).replace("<>", PROPERTY_VALIDATORS));
8
+ if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
9
+ const keys = Object.keys(original);
10
+ const { length } = keys;
11
+ for (let index = 0; index < length; index += 1) {
12
+ const key = keys[index];
13
+ if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
14
+ const value = original[key];
15
+ handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
16
+ if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
17
+ return item;
18
+ });
19
+ }
20
+ return handlers;
21
+ }
22
+ //#endregion
23
+ export { getNamedHandlers };
@@ -0,0 +1,7 @@
1
+ import { NamedValidatorHandlers, ValidationInformationKey, Validator } from "../models/validation.model.mjs";
2
+ import { ValueName } from "../models/misc.model.mjs";
3
+
4
+ //#region src/validator/named.validator.d.ts
5
+ declare function getNamedValidator(key: ValidationInformationKey, name: ValueName, handlers: NamedValidatorHandlers): Validator;
6
+ //#endregion
7
+ export { getNamedValidator };
@@ -0,0 +1,38 @@
1
+ import { getInputPropertyValidatorMessage } from "../helpers/message.helper.mjs";
2
+ //#region src/validator/named.validator.ts
3
+ function getNamedValidator(key, name, handlers) {
4
+ const validator = namedValidators[name];
5
+ const named = handlers[name] ?? [];
6
+ const { length } = named;
7
+ return (input, parameters) => {
8
+ if (!validator(input)) return [];
9
+ for (let index = 0; index < length; index += 1) {
10
+ const handler = named[index];
11
+ if (handler(input) === true) continue;
12
+ const information = {
13
+ key,
14
+ validator,
15
+ message: getInputPropertyValidatorMessage(key.full, name, index, length),
16
+ value: input
17
+ };
18
+ parameters.information?.push(information);
19
+ return parameters.reporting.none ? [] : [information];
20
+ }
21
+ return true;
22
+ };
23
+ }
24
+ const namedValidators = {
25
+ array: Array.isArray,
26
+ bigint: (value) => typeof value === "bigint",
27
+ boolean: (value) => typeof value === "boolean",
28
+ date: (value) => value instanceof Date,
29
+ function: (value) => typeof value === "function",
30
+ null: (value) => value === null,
31
+ number: (value) => typeof value === "number",
32
+ object: (value) => typeof value === "object" && value !== null,
33
+ string: (value) => typeof value === "string",
34
+ symbol: (value) => typeof value === "symbol",
35
+ undefined: (value) => value === void 0
36
+ };
37
+ //#endregion
38
+ export { getNamedValidator };
@@ -0,0 +1,7 @@
1
+ import { ValidationInformationKey, Validator } from "../models/validation.model.mjs";
2
+ import { PlainObject } from "@oscarpalmer/atoms/models";
3
+
4
+ //#region src/validator/object.validator.d.ts
5
+ declare function getObjectValidator(original: PlainObject, origin?: ValidationInformationKey, fromType?: boolean): Validator;
6
+ //#endregion
7
+ export { getObjectValidator };
@@ -0,0 +1,185 @@
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
+ import { SchematicError, ValidationError } from "../models/validation.model.mjs";
4
+ import { getDefaultRequiredMessage, getDefaultTypeMessage, getDisallowedMessage, getInputPropertyMissingMessage, getInputPropertyTypeMessage, getInputTypeMessage, getRequiredMessage, getSchematicPropertyNullableMessage, getSchematicPropertyTypeMessage, getUnknownKeysMessage } from "../helpers/message.helper.mjs";
5
+ import { getBaseValidator } from "./base.validator.mjs";
6
+ import { getFunctionValidator } from "./function.validator.mjs";
7
+ import { getNamedHandlers } from "./named.handler.mjs";
8
+ import { getNamedValidator } from "./named.validator.mjs";
9
+ import { getSchematicValidator } from "./schematic.validator.mjs";
10
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
11
+ import { join } from "@oscarpalmer/atoms/string";
12
+ import { clone } from "@oscarpalmer/atoms/value/clone";
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
+ }
19
+ function getDisallowedProperty(obj) {
20
+ if ("$default" in obj) return PROPERTY_DEFAULT;
21
+ if ("$required" in obj) return PROPERTY_REQUIRED;
22
+ if ("$type" in obj) return PROPERTY_TYPE;
23
+ if ("$validators" in obj) return PROPERTY_VALIDATORS;
24
+ }
25
+ function getObjectValidator(original, origin, fromType) {
26
+ const keys = Object.keys(original);
27
+ const keysLength = keys.length;
28
+ if (keysLength === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY);
29
+ if (fromType ?? false) {
30
+ const property = getDisallowedProperty(original);
31
+ if (property != null) throw new SchematicError(getDisallowedMessage(origin.full, property));
32
+ }
33
+ const set = /* @__PURE__ */ new Set();
34
+ const items = [];
35
+ for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
36
+ const key = keys[keyIndex];
37
+ const value = original[key];
38
+ if (value == null) throw new SchematicError(getSchematicPropertyNullableMessage(join([origin?.full, key], ".")));
39
+ const prefixedKey = origin == null ? key : join([origin.full, key], ".");
40
+ const fullKey = {
41
+ full: prefixedKey,
42
+ short: key
43
+ };
44
+ let handlers = {};
45
+ let required = true;
46
+ let typed = false;
47
+ let defaults;
48
+ let types;
49
+ const validators = [];
50
+ if (isPlainObject(value)) {
51
+ typed = PROPERTY_TYPE in value;
52
+ const type = typed ? value[PROPERTY_TYPE] : value;
53
+ defaults = getDefaults(value, prefixedKey, typed);
54
+ handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey, typed);
55
+ required = getRequired(value, prefixedKey, typed) ?? required;
56
+ types = Array.isArray(type) ? type : [type];
57
+ } else types = Array.isArray(value) ? value : [value];
58
+ if (types.length === 0) throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
59
+ const typesLength = types.length;
60
+ for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
61
+ const type = types[typeIndex];
62
+ let validator;
63
+ switch (true) {
64
+ case typeof type === "function":
65
+ validator = getFunctionValidator(type);
66
+ break;
67
+ case isPlainObject(type):
68
+ validator = getObjectValidator(type, fullKey, typed);
69
+ break;
70
+ case isSchematic(type):
71
+ validator = getSchematicValidator(type);
72
+ break;
73
+ case TYPE_ALL.has(type):
74
+ validator = getNamedValidator(fullKey, type, handlers);
75
+ break;
76
+ default: throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
77
+ }
78
+ validators.push(validator);
79
+ }
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));
84
+ items.push({
85
+ defaults,
86
+ required,
87
+ types,
88
+ validator,
89
+ key: fullKey
90
+ });
91
+ set.add(key);
92
+ }
93
+ const validatorsLength = items.length;
94
+ return (input, parameters, get) => {
95
+ if (!isPlainObject(input)) {
96
+ if (origin != null) return [];
97
+ const information = {
98
+ key: {
99
+ full: "",
100
+ short: ""
101
+ },
102
+ value: input,
103
+ message: getInputTypeMessage(input)
104
+ };
105
+ if (parameters.reporting.throw) throw new ValidationError([information]);
106
+ parameters.information?.push(information);
107
+ return [information];
108
+ }
109
+ if (parameters.strict) {
110
+ const unknownKeys = Object.keys(input).filter((key) => !set.has(key));
111
+ if (unknownKeys.length > 0) {
112
+ const information = {
113
+ key: origin ?? {
114
+ full: "",
115
+ short: ""
116
+ },
117
+ message: getUnknownKeysMessage(unknownKeys),
118
+ value: input
119
+ };
120
+ if (parameters.reporting.throw) throw new ValidationError([information]);
121
+ parameters.information?.push(information);
122
+ return [information];
123
+ }
124
+ }
125
+ const allInformation = [];
126
+ const output = {};
127
+ for (let validatorIndex = 0; validatorIndex < validatorsLength; validatorIndex += 1) {
128
+ const { defaults, key, required, types, validator } = items[validatorIndex];
129
+ const value = input[key.short];
130
+ if (value === void 0) {
131
+ if (required) {
132
+ if (get && defaults != null) {
133
+ output[key.short] = clone(defaults.value);
134
+ continue;
135
+ }
136
+ if (parameters.reporting.none) return [];
137
+ const information = {
138
+ key,
139
+ value,
140
+ message: getInputPropertyMissingMessage(key.full, types)
141
+ };
142
+ if (parameters.reporting.throw) throw new ValidationError([information]);
143
+ parameters.information?.push(information);
144
+ if (parameters.reporting.all) {
145
+ allInformation.push(information);
146
+ continue;
147
+ }
148
+ return [information];
149
+ }
150
+ continue;
151
+ }
152
+ const previousOutput = parameters.output;
153
+ parameters.output = output;
154
+ const result = validator(value, parameters, get);
155
+ parameters.output = previousOutput;
156
+ if (result === true) {
157
+ if (get && !isPlainObject(value)) output[key.short] = parameters.clone ? clone(value) : value;
158
+ continue;
159
+ }
160
+ if (parameters.reporting.none) return [];
161
+ const information = typeof result !== "boolean" && result.length > 0 ? result : [{
162
+ key,
163
+ value,
164
+ message: getInputPropertyTypeMessage(key.full, types, value)
165
+ }];
166
+ if (parameters.reporting.throw) throw new ValidationError(information);
167
+ if (parameters.reporting.all) {
168
+ allInformation.push(...information);
169
+ continue;
170
+ }
171
+ return information;
172
+ }
173
+ if (get) if (origin == null) parameters.output = output;
174
+ else parameters.output[origin.short] = output;
175
+ return allInformation.length === 0 ? true : allInformation;
176
+ };
177
+ }
178
+ function getRequired(obj, key, allowed) {
179
+ if (!("$required" in obj)) return;
180
+ if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
181
+ if (typeof obj["$required"] !== "boolean") throw new SchematicError(getRequiredMessage(key));
182
+ return obj[PROPERTY_REQUIRED];
183
+ }
184
+ //#endregion
185
+ export { getObjectValidator };
@@ -0,0 +1,7 @@
1
+ import { Validator } from "../models/validation.model.mjs";
2
+ import { Schematic } from "../schematic.mjs";
3
+
4
+ //#region src/validator/schematic.validator.d.ts
5
+ declare function getSchematicValidator(schematic: Schematic<unknown>): Validator;
6
+ //#endregion
7
+ export { getSchematicValidator };
@@ -0,0 +1,16 @@
1
+ import { schematicValidator } from "../schematic.mjs";
2
+ import { isPlainObject } from "@oscarpalmer/atoms/is";
3
+ //#region src/validator/schematic.validator.ts
4
+ function getSchematicValidator(schematic) {
5
+ const validator = schematicValidator.get(schematic);
6
+ return (input, parameters, get) => {
7
+ let result;
8
+ if (isPlainObject(input)) result = validator(input, parameters, get);
9
+ else result = [];
10
+ if (result === true) return result;
11
+ parameters.information?.push(...result);
12
+ return result;
13
+ };
14
+ }
15
+ //#endregion
16
+ export { getSchematicValidator };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/jhunal",
3
- "version": "0.22.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
@@ -25,6 +25,8 @@ export const MESSAGE_CONSTRUCTOR = 'Expected a constructor function';
25
25
 
26
26
  export const NAME_SCHEMATIC = 'Schematic';
27
27
 
28
+ export const NAME_SCHEMATIC_PREFIXED = 'a Schematic';
29
+
28
30
  export const NAME_ERROR_SCHEMATIC = 'SchematicError';
29
31
 
30
32
  export const NAME_ERROR_VALIDATION = 'ValidationError';
@@ -33,6 +35,8 @@ export const NAME_ERROR_VALIDATION = 'ValidationError';
33
35
 
34
36
  // #region Properties
35
37
 
38
+ export const PROPERTY_DEFAULT = '$default';
39
+
36
40
  export const PROPERTY_REQUIRED = '$required';
37
41
 
38
42
  export const PROPERTY_SCHEMATIC = '$schematic';
@@ -45,7 +49,7 @@ export const PROPERTY_VALIDATORS = '$validators';
45
49
 
46
50
  // #region Property validation
47
51
 
48
- export const VALIDATION_MESSAGE_INVALID_INPUT = "Expected 'object' as input but received <>";
52
+ export const VALIDATION_MESSAGE_INVALID_INPUT = 'Expected an object as input but received <>';
49
53
 
50
54
  export const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required property '<>'";
51
55
 
@@ -81,10 +85,16 @@ export const REPORTING_TYPES = new Set<ReportingType>([
81
85
 
82
86
  // #region Schematic validation
83
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
+
84
94
  export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one property';
85
95
 
86
96
  export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED =
87
- "'<>.<>' property is not allowed for schemas in $type";
97
+ "'<>.<>' property is not allowed for plain schemas";
88
98
 
89
99
  export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE =
90
100
  "'<>' property must not be 'null' or 'undefined'";
@@ -116,6 +126,10 @@ export const TEMPLATE_PATTERN = '<>';
116
126
 
117
127
  export const TYPE_ARRAY = 'array';
118
128
 
129
+ export const TYPE_FUNCTION = 'function';
130
+
131
+ export const TYPE_FUNCTION_RESULT = 'a validated value';
132
+
119
133
  export const TYPE_NULL = 'null';
120
134
 
121
135
  export const TYPE_OBJECT = 'object';
@@ -123,17 +137,31 @@ export const TYPE_OBJECT = 'object';
123
137
  export const TYPE_UNDEFINED = 'undefined';
124
138
 
125
139
  export const VALIDATABLE_TYPES = new Set<ValueName>([
126
- 'array',
140
+ TYPE_ARRAY,
127
141
  'bigint',
128
142
  'boolean',
129
143
  'date',
130
- 'function',
144
+ TYPE_FUNCTION,
131
145
  'number',
146
+ TYPE_OBJECT,
132
147
  'string',
133
148
  'symbol',
134
- TYPE_OBJECT,
135
149
  ]);
136
150
 
137
- export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES, 'null', TYPE_UNDEFINED]);
151
+ export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES, TYPE_NULL, TYPE_UNDEFINED]);
152
+
153
+ export const PREFIXED_TYPES: Record<ValueName, string> = {
154
+ [TYPE_ARRAY]: `an ${TYPE_ARRAY}`,
155
+ bigint: `a bigint`,
156
+ boolean: `a boolean`,
157
+ date: `a date`,
158
+ [TYPE_FUNCTION]: `a ${TYPE_FUNCTION}`,
159
+ [TYPE_NULL]: TYPE_NULL,
160
+ number: `a number`,
161
+ string: `a string`,
162
+ symbol: `a symbol`,
163
+ [TYPE_OBJECT]: `an ${TYPE_OBJECT}`,
164
+ [TYPE_UNDEFINED]: TYPE_UNDEFINED,
165
+ };
138
166
 
139
167
  // #endregion