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