@oscarpalmer/jhunal 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/constants.d.mts +7 -3
  2. package/dist/constants.mjs +34 -12
  3. package/dist/helpers/message.helper.d.mts +11 -0
  4. package/dist/helpers/message.helper.mjs +70 -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 +304 -275
  8. package/dist/index.mjs +187 -154
  9. package/dist/models/validation.model.d.mts +18 -7
  10. package/dist/schematic.d.mts +25 -7
  11. package/dist/schematic.mjs +6 -4
  12. package/dist/validator/base.validator.d.mts +6 -0
  13. package/dist/validator/base.validator.mjs +19 -0
  14. package/dist/validator/function.validator.d.mts +6 -0
  15. package/dist/validator/function.validator.mjs +9 -0
  16. package/dist/validator/named.handler.d.mts +6 -0
  17. package/dist/validator/named.handler.mjs +22 -0
  18. package/dist/validator/named.validator.d.mts +7 -0
  19. package/dist/validator/named.validator.mjs +39 -0
  20. package/dist/validator/object.validator.d.mts +7 -0
  21. package/dist/{validation.mjs → validator/object.validator.mjs} +20 -98
  22. package/dist/validator/schematic.validator.d.mts +7 -0
  23. package/dist/validator/schematic.validator.mjs +16 -0
  24. package/package.json +1 -1
  25. package/src/constants.ts +42 -10
  26. package/src/helpers/message.helper.ts +152 -0
  27. package/src/helpers/misc.helper.ts +93 -0
  28. package/src/index.ts +3 -3
  29. package/src/models/validation.model.ts +19 -6
  30. package/src/schematic.ts +43 -16
  31. package/src/validator/base.validator.ts +31 -0
  32. package/src/validator/function.validator.ts +9 -0
  33. package/src/validator/named.handler.ts +50 -0
  34. package/src/validator/named.validator.ts +62 -0
  35. package/src/{validation.ts → validator/object.validator.ts} +23 -181
  36. package/src/validator/schematic.validator.ts +25 -0
  37. package/dist/helpers.d.mts +0 -28
  38. package/dist/helpers.mjs +0 -120
  39. package/dist/validation.d.mts +0 -7
  40. package/src/helpers.ts +0 -249
@@ -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): NamedValidatorHandlers;
5
+ //#endregion
6
+ export { getNamedHandlers };
@@ -0,0 +1,22 @@
1
+ import { 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) {
5
+ const handlers = {};
6
+ if (original == null) return handlers;
7
+ if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
8
+ const keys = Object.keys(original);
9
+ const { length } = keys;
10
+ for (let index = 0; index < length; index += 1) {
11
+ const key = keys[index];
12
+ if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
13
+ const value = original[key];
14
+ handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
15
+ if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
16
+ return item;
17
+ });
18
+ }
19
+ return handlers;
20
+ }
21
+ //#endregion
22
+ 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,39 @@
1
+ import "../constants.mjs";
2
+ import { getInvalidValidatorMessage } from "../helpers/message.helper.mjs";
3
+ //#region src/validator/named.validator.ts
4
+ function getNamedValidator(key, name, handlers) {
5
+ const validator = namedValidators[name];
6
+ const named = handlers[name] ?? [];
7
+ const { length } = named;
8
+ return (input, parameters) => {
9
+ if (!validator(input)) return [];
10
+ for (let index = 0; index < length; index += 1) {
11
+ const handler = named[index];
12
+ if (handler(input) === true) continue;
13
+ const information = {
14
+ key,
15
+ validator,
16
+ message: getInvalidValidatorMessage(key.full, name, index, length),
17
+ value: input
18
+ };
19
+ parameters.information?.push(information);
20
+ return parameters.reporting.none ? [] : [information];
21
+ }
22
+ return true;
23
+ };
24
+ }
25
+ const namedValidators = {
26
+ array: Array.isArray,
27
+ bigint: (value) => typeof value === "bigint",
28
+ boolean: (value) => typeof value === "boolean",
29
+ date: (value) => value instanceof Date,
30
+ function: (value) => typeof value === "function",
31
+ null: (value) => value === null,
32
+ number: (value) => typeof value === "number",
33
+ object: (value) => typeof value === "object" && value !== null,
34
+ string: (value) => typeof value === "string",
35
+ symbol: (value) => typeof value === "symbol",
36
+ undefined: (value) => value === void 0
37
+ };
38
+ //#endregion
39
+ 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 };
@@ -1,58 +1,21 @@
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, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE, SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE, TYPE_ALL } from "./constants.mjs";
2
- import { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getInvalidValidatorMessage, getUnknownKeysMessage, instanceOf, isSchematic } from "./helpers.mjs";
3
- import { SchematicError, ValidationError } from "./models/validation.model.mjs";
4
- import { schematicValidator } from "./schematic.mjs";
5
- import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
6
- import { join } from "@oscarpalmer/atoms";
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";
3
+ import { SchematicError, ValidationError } from "../models/validation.model.mjs";
4
+ import { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, 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";
7
12
  import { clone } from "@oscarpalmer/atoms/value/clone";
8
- //#region src/validation.ts
13
+ //#region src/validator/object.validator.ts
9
14
  function getDisallowedProperty(obj) {
10
15
  if ("$required" in obj) return PROPERTY_REQUIRED;
11
16
  if ("$type" in obj) return PROPERTY_TYPE;
12
17
  if ("$validators" in obj) return PROPERTY_VALIDATORS;
13
18
  }
14
- function getFunctionValidator(fn) {
15
- const validator = isConstructor(fn) ? instanceOf(fn) : fn;
16
- return (input) => validator(input) === true;
17
- }
18
- function getNamedValidator(key, name, handlers) {
19
- const validator = namedValidators[name];
20
- const named = handlers[name] ?? [];
21
- const { length } = named;
22
- return (input, parameters) => {
23
- if (!validator(input)) return false;
24
- for (let index = 0; index < length; index += 1) {
25
- const handler = named[index];
26
- if (handler(input) === true) continue;
27
- const information = {
28
- key,
29
- validator,
30
- message: getInvalidValidatorMessage(key.full, name, index, length),
31
- value: input
32
- };
33
- parameters.information?.push(information);
34
- return parameters.reporting.none ? false : [information];
35
- }
36
- return true;
37
- };
38
- }
39
- function getNamedHandlers(original, prefix) {
40
- const handlers = {};
41
- if (original == null) return handlers;
42
- if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
43
- const keys = Object.keys(original);
44
- const { length } = keys;
45
- for (let index = 0; index < length; index += 1) {
46
- const key = keys[index];
47
- if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
48
- const value = original[key];
49
- handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
50
- if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
51
- return item;
52
- });
53
- }
54
- return handlers;
55
- }
56
19
  function getObjectValidator(original, origin, fromType) {
57
20
  const keys = Object.keys(original);
58
21
  const keysLength = keys.length;
@@ -111,13 +74,13 @@ function getObjectValidator(original, origin, fromType) {
111
74
  types,
112
75
  key: fullKey,
113
76
  required: required && !types.includes("undefined"),
114
- validator: getValidator(validators)
77
+ validator: getBaseValidator(validators)
115
78
  });
116
79
  }
117
80
  const validatorsLength = items.length;
118
81
  return (input, parameters, get) => {
119
82
  if (!isPlainObject(input)) {
120
- if (origin != null) return false;
83
+ if (origin != null) return [];
121
84
  const information = {
122
85
  key: {
123
86
  full: "",
@@ -128,7 +91,7 @@ function getObjectValidator(original, origin, fromType) {
128
91
  };
129
92
  if (parameters.reporting.throw) throw new ValidationError([information]);
130
93
  parameters.information?.push(information);
131
- return parameters.reporting.none ? false : [information];
94
+ return [information];
132
95
  }
133
96
  if (parameters.strict) {
134
97
  const unknownKeys = Object.keys(input).filter((key) => !set.has(key));
@@ -143,7 +106,7 @@ function getObjectValidator(original, origin, fromType) {
143
106
  };
144
107
  if (parameters.reporting.throw) throw new ValidationError([information]);
145
108
  parameters.information?.push(information);
146
- return parameters.reporting.none ? false : [information];
109
+ return [information];
147
110
  }
148
111
  }
149
112
  const allInformation = [];
@@ -153,7 +116,7 @@ function getObjectValidator(original, origin, fromType) {
153
116
  const value = input[key.short];
154
117
  if (value === void 0) {
155
118
  if (required) {
156
- if (parameters.reporting.none) return false;
119
+ if (parameters.reporting.none) return [];
157
120
  const information = {
158
121
  key,
159
122
  value,
@@ -173,12 +136,11 @@ function getObjectValidator(original, origin, fromType) {
173
136
  parameters.output = output;
174
137
  const result = validator(value, parameters, get);
175
138
  parameters.output = previousOutput;
176
- if (result === false) continue;
177
139
  if (result === true) {
178
- if (get) output[key.short] = clone(value);
140
+ if (get && !isPlainObject(value)) output[key.short] = parameters.clone ? clone(value) : value;
179
141
  continue;
180
142
  }
181
- if (parameters.reporting.none) return false;
143
+ if (parameters.reporting.none) return [];
182
144
  const information = typeof result !== "boolean" && result.length > 0 ? result : [{
183
145
  key,
184
146
  value,
@@ -193,7 +155,7 @@ function getObjectValidator(original, origin, fromType) {
193
155
  }
194
156
  if (get) if (origin == null) parameters.output = output;
195
157
  else parameters.output[origin.short] = output;
196
- return parameters.reporting.none || allInformation.length === 0 ? true : allInformation;
158
+ return allInformation.length === 0 ? true : allInformation;
197
159
  };
198
160
  }
199
161
  function getRequired(key, obj) {
@@ -201,45 +163,5 @@ function getRequired(key, obj) {
201
163
  if (typeof obj["$required"] !== "boolean") throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
202
164
  return obj[PROPERTY_REQUIRED];
203
165
  }
204
- function getSchematicValidator(schematic) {
205
- const validator = schematicValidator.get(schematic);
206
- return (input, parameters, get) => {
207
- let result = false;
208
- if (isPlainObject(input)) result = validator(input, parameters, get);
209
- if (typeof result === "boolean") return result;
210
- parameters.information?.push(...result);
211
- return result.length === 0 ? true : result;
212
- };
213
- }
214
- function getValidator(validators) {
215
- const { length } = validators;
216
- return (input, parameters, get) => {
217
- const allInformation = [];
218
- for (let index = 0; index < length; index += 1) {
219
- const previousInformation = parameters.information;
220
- parameters.information = [];
221
- const result = validators[index](input, parameters, get);
222
- parameters.information = previousInformation;
223
- if (result === false) continue;
224
- if (result === true) return true;
225
- parameters.information?.push(...result);
226
- allInformation.push(...result);
227
- }
228
- return allInformation;
229
- };
230
- }
231
- const namedValidators = {
232
- array: Array.isArray,
233
- bigint: (value) => typeof value === "bigint",
234
- boolean: (value) => typeof value === "boolean",
235
- date: (value) => value instanceof Date,
236
- function: (value) => typeof value === "function",
237
- null: (value) => value === null,
238
- number: (value) => typeof value === "number",
239
- object: (value) => typeof value === "object" && value !== null,
240
- string: (value) => typeof value === "string",
241
- symbol: (value) => typeof value === "symbol",
242
- undefined: (value) => value === void 0
243
- };
244
166
  //#endregion
245
167
  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.23.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';
@@ -45,7 +47,7 @@ export const PROPERTY_VALIDATORS = '$validators';
45
47
 
46
48
  // #region Property validation
47
49
 
48
- export const VALIDATION_MESSAGE_INVALID_INPUT = "Expected 'object' as input but received <>";
50
+ export const VALIDATION_MESSAGE_INVALID_INPUT = 'Expected an object as input but received <>';
49
51
 
50
52
  export const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required property '<>'";
51
53
 
@@ -116,24 +118,54 @@ export const TEMPLATE_PATTERN = '<>';
116
118
 
117
119
  export const TYPE_ARRAY = 'array';
118
120
 
121
+ const TYPE_BIGINT = 'bigint';
122
+
123
+ const TYPE_BOOLEAN = 'boolean';
124
+
125
+ const TYPE_DATE = 'date';
126
+
127
+ export const TYPE_FUNCTION = 'function';
128
+
129
+ export const TYPE_FUNCTION_RESULT = 'a validated value';
130
+
119
131
  export const TYPE_NULL = 'null';
120
132
 
133
+ const TYPE_NUMBER = 'number';
134
+
121
135
  export const TYPE_OBJECT = 'object';
122
136
 
137
+ const TYPE_STRING = 'string';
138
+
139
+ const TYPE_SYMBOL = 'symbol';
140
+
123
141
  export const TYPE_UNDEFINED = 'undefined';
124
142
 
125
143
  export const VALIDATABLE_TYPES = new Set<ValueName>([
126
- 'array',
127
- 'bigint',
128
- 'boolean',
129
- 'date',
130
- 'function',
131
- 'number',
132
- 'string',
133
- 'symbol',
144
+ TYPE_ARRAY,
145
+ TYPE_BIGINT,
146
+ TYPE_BOOLEAN,
147
+ TYPE_DATE,
148
+ TYPE_FUNCTION,
149
+ TYPE_NUMBER,
134
150
  TYPE_OBJECT,
151
+ TYPE_STRING,
152
+ TYPE_SYMBOL,
135
153
  ]);
136
154
 
137
- export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES, 'null', TYPE_UNDEFINED]);
155
+ export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES, TYPE_NULL, TYPE_UNDEFINED]);
156
+
157
+ export const PREFIXED_TYPES: Record<ValueName, string> = {
158
+ [TYPE_ARRAY]: `an ${TYPE_ARRAY}`,
159
+ [TYPE_BIGINT]: `a ${TYPE_BIGINT}`,
160
+ [TYPE_BOOLEAN]: `a ${TYPE_BOOLEAN}`,
161
+ [TYPE_DATE]: `a ${TYPE_DATE}`,
162
+ [TYPE_FUNCTION]: `a ${TYPE_FUNCTION}`,
163
+ [TYPE_NULL]: TYPE_NULL,
164
+ [TYPE_NUMBER]: `a ${TYPE_NUMBER}`,
165
+ [TYPE_STRING]: `a ${TYPE_STRING}`,
166
+ [TYPE_SYMBOL]: `a ${TYPE_SYMBOL}`,
167
+ [TYPE_OBJECT]: `an ${TYPE_OBJECT}`,
168
+ [TYPE_UNDEFINED]: TYPE_UNDEFINED,
169
+ };
138
170
 
139
171
  // #endregion
@@ -0,0 +1,152 @@
1
+ import {isConstructor, isPlainObject} from '@oscarpalmer/atoms/is';
2
+ import {
3
+ COMMA,
4
+ CONJUNCTION_AND,
5
+ CONJUNCTION_AND_COMMA,
6
+ CONJUNCTION_OR,
7
+ CONJUNCTION_OR_COMMA,
8
+ PREFIXED_TYPES,
9
+ TEMPLATE_PATTERN,
10
+ TYPE_ALL,
11
+ TYPE_ARRAY,
12
+ TYPE_FUNCTION,
13
+ TYPE_FUNCTION_RESULT,
14
+ TYPE_NULL,
15
+ TYPE_OBJECT,
16
+ VALIDATION_MESSAGE_INVALID_INPUT,
17
+ VALIDATION_MESSAGE_INVALID_REQUIRED,
18
+ VALIDATION_MESSAGE_INVALID_TYPE,
19
+ VALIDATION_MESSAGE_INVALID_VALUE,
20
+ VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX,
21
+ VALIDATION_MESSAGE_UNKNOWN_KEYS,
22
+ } from '../constants';
23
+ import type {ValueName} from '../models/misc.model';
24
+ import type {ValidatorType} from '../models/validation.model';
25
+
26
+ export function getInvalidInputMessage(actual: unknown): string {
27
+ return VALIDATION_MESSAGE_INVALID_INPUT.replace(TEMPLATE_PATTERN, getValueType(actual));
28
+ }
29
+
30
+ export function getInvalidMissingMessage(key: string, types: ValidatorType[]): string {
31
+ let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace(TEMPLATE_PATTERN, renderTypes(types));
32
+
33
+ message = message.replace(TEMPLATE_PATTERN, key);
34
+
35
+ return message;
36
+ }
37
+
38
+ export function getInvalidTypeMessage(
39
+ key: string,
40
+ types: ValidatorType[],
41
+ actual: unknown,
42
+ ): string {
43
+ let message = VALIDATION_MESSAGE_INVALID_TYPE.replace(TEMPLATE_PATTERN, renderTypes(types));
44
+
45
+ message = message.replace(TEMPLATE_PATTERN, key);
46
+ message = message.replace(TEMPLATE_PATTERN, getValueType(actual));
47
+
48
+ return message;
49
+ }
50
+
51
+ export function getInvalidValidatorMessage(
52
+ key: string,
53
+ type: ValueName,
54
+ index: number,
55
+ length: number,
56
+ ): string {
57
+ let message = VALIDATION_MESSAGE_INVALID_VALUE.replace(TEMPLATE_PATTERN, key);
58
+
59
+ message = message.replace(TEMPLATE_PATTERN, type);
60
+
61
+ if (length > 1) {
62
+ message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace(TEMPLATE_PATTERN, String(index));
63
+ }
64
+
65
+ return message;
66
+ }
67
+
68
+ function getPropertyType(type: ValidatorType): string {
69
+ switch (true) {
70
+ case typeof type === 'function':
71
+ return isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT;
72
+
73
+ case TYPE_ALL.has(type as ValueName):
74
+ return PREFIXED_TYPES[type as ValueName];
75
+
76
+ default:
77
+ return PREFIXED_TYPES[TYPE_OBJECT];
78
+ }
79
+ }
80
+
81
+ export function getUnknownKeysMessage(keys: string[]): string {
82
+ return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace(TEMPLATE_PATTERN, renderKeys(keys));
83
+ }
84
+
85
+ function getValueType(value: unknown): string {
86
+ const valueType = typeof value;
87
+
88
+ switch (true) {
89
+ case value === null:
90
+ return TYPE_NULL;
91
+
92
+ case Array.isArray(value):
93
+ return PREFIXED_TYPES[TYPE_ARRAY];
94
+
95
+ case isPlainObject(value):
96
+ return PREFIXED_TYPES[TYPE_OBJECT];
97
+
98
+ case valueType !== TYPE_OBJECT:
99
+ return PREFIXED_TYPES[valueType as ValueName];
100
+
101
+ default:
102
+ return (value as object).constructor.name;
103
+ }
104
+ }
105
+
106
+ function renderKeys(keys: string[]): string {
107
+ return renderParts(
108
+ keys.map(key => `'${key}'`),
109
+ CONJUNCTION_AND,
110
+ CONJUNCTION_AND_COMMA,
111
+ );
112
+ }
113
+
114
+ function renderParts(parts: string[], delimiterShort: string, delimiterLong: string): string {
115
+ const {length} = parts;
116
+
117
+ if (length === 1) {
118
+ return parts[0];
119
+ }
120
+
121
+ let rendered = '';
122
+
123
+ for (let index = 0; index < length; index += 1) {
124
+ rendered += parts[index];
125
+
126
+ if (index < length - 2) {
127
+ rendered += COMMA;
128
+ } else if (index === length - 2) {
129
+ rendered += parts.length > 2 ? delimiterLong : delimiterShort;
130
+ }
131
+ }
132
+
133
+ return rendered;
134
+ }
135
+
136
+ function renderTypes(types: ValidatorType[]): string {
137
+ const unique = new Set<string>();
138
+ const parts: string[] = [];
139
+
140
+ for (let index = 0; index < types.length; index += 1) {
141
+ const rendered = getPropertyType(types[index]);
142
+
143
+ if (unique.has(rendered)) {
144
+ continue;
145
+ }
146
+
147
+ unique.add(rendered);
148
+ parts.push(rendered);
149
+ }
150
+
151
+ return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
152
+ }
@@ -0,0 +1,93 @@
1
+ import {isConstructor, isPlainObject} from '@oscarpalmer/atoms/is';
2
+ import type {Constructor, PlainObject} from '@oscarpalmer/atoms/models';
3
+ import {
4
+ MESSAGE_CONSTRUCTOR,
5
+ PROPERTY_SCHEMATIC,
6
+ REPORTING_ALL,
7
+ REPORTING_FIRST,
8
+ REPORTING_NONE,
9
+ REPORTING_THROW,
10
+ REPORTING_TYPES,
11
+ TYPE_OBJECT,
12
+ } from '../constants';
13
+ import type {
14
+ ReportingInformation,
15
+ ReportingType,
16
+ ValidatorParameters,
17
+ } from '../models/validation.model';
18
+ import type {Schematic} from '../schematic';
19
+
20
+ export function getParameters(input?: unknown): ValidatorParameters {
21
+ if (typeof input === 'boolean') {
22
+ return {
23
+ clone: true,
24
+ output: {},
25
+ reporting: getReporting(REPORTING_NONE),
26
+ strict: input,
27
+ };
28
+ }
29
+
30
+ if (REPORTING_TYPES.has(input as ReportingType)) {
31
+ return {
32
+ clone: true,
33
+ output: {},
34
+ reporting: getReporting(input as ReportingType),
35
+ strict: false,
36
+ };
37
+ }
38
+
39
+ const options = isPlainObject(input) ? input : {};
40
+
41
+ return {
42
+ clone: typeof options.clone === 'boolean' ? options.clone : true,
43
+ output: {},
44
+ reporting: getReporting(options.errors),
45
+ strict: typeof options.strict === 'boolean' ? options.strict : false,
46
+ };
47
+ }
48
+
49
+ export function getReporting(value: unknown): ReportingInformation {
50
+ const type = REPORTING_TYPES.has(value as ReportingType)
51
+ ? (value as ReportingType)
52
+ : REPORTING_NONE;
53
+
54
+ return {
55
+ type,
56
+ [REPORTING_ALL]: type === REPORTING_ALL,
57
+ [REPORTING_FIRST]: type === REPORTING_FIRST,
58
+ [REPORTING_NONE]: type === REPORTING_NONE,
59
+ [REPORTING_THROW]: type === REPORTING_THROW,
60
+ } as ReportingInformation;
61
+ }
62
+
63
+ /**
64
+ * Creates a validator function for a given constructor
65
+ * @param constructor - Constructor to check against
66
+ * @throws Will throw a `TypeError` if the provided argument is not a valid constructor
67
+ * @returns Validator function that checks if a value is an instance of the constructor
68
+ */
69
+ export function instanceOf<Instance>(
70
+ constructor: Constructor<Instance>,
71
+ ): (value: unknown) => value is Instance {
72
+ if (!isConstructor(constructor)) {
73
+ throw new TypeError(MESSAGE_CONSTRUCTOR);
74
+ }
75
+
76
+ return (value: unknown): value is Instance => {
77
+ return value instanceof constructor;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Is the value a schematic?
83
+ * @param value Value to check
84
+ * @returns `true` if the value is a schematic, `false` otherwise
85
+ */
86
+ export function isSchematic(value: unknown): value is Schematic<never> {
87
+ return (
88
+ typeof value === TYPE_OBJECT &&
89
+ value !== null &&
90
+ PROPERTY_SCHEMATIC in (value as PlainObject) &&
91
+ (value as PlainObject)[PROPERTY_SCHEMATIC] === true
92
+ );
93
+ }