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