@oscarpalmer/jhunal 0.25.0 → 0.27.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 +15 -7
- package/dist/constants.mjs +15 -6
- package/dist/handler/base.handler.d.mts +6 -0
- package/dist/{validator/base.validator.mjs → handler/base.handler.mjs} +5 -5
- package/dist/handler/function.handler.d.mts +6 -0
- package/dist/handler/function.handler.mjs +9 -0
- package/dist/handler/object.handler.d.mts +7 -0
- package/dist/handler/object.handler.mjs +130 -0
- package/dist/handler/schema.handler.d.mts +7 -0
- package/dist/handler/schema.handler.mjs +16 -0
- package/dist/handler/type.handler.d.mts +9 -0
- package/dist/handler/type.handler.mjs +71 -0
- package/dist/handler/validator.handler.d.mts +10 -0
- package/dist/handler/validator.handler.mjs +34 -0
- package/dist/handler/value.handler.d.mts +14 -0
- package/dist/handler/value.handler.mjs +98 -0
- package/dist/helpers/message.helper.d.mts +9 -7
- package/dist/helpers/message.helper.mjs +34 -16
- package/dist/helpers/misc.helper.d.mts +13 -6
- package/dist/helpers/misc.helper.mjs +12 -4
- package/dist/helpers/report.helper.d.mts +23 -0
- package/dist/helpers/report.helper.mjs +19 -0
- package/dist/helpers/result.helper.d.mts +7 -0
- package/dist/helpers/result.helper.mjs +17 -0
- package/dist/index.d.mts +172 -73
- package/dist/index.mjs +396 -235
- package/dist/models/infer.model.d.mts +11 -8
- package/dist/models/misc.model.d.mts +8 -8
- package/dist/models/schematic.plain.model.d.mts +10 -9
- package/dist/models/schematic.typed.model.d.mts +1 -1
- package/dist/models/transform.model.d.mts +2 -2
- package/dist/models/validation.model.d.mts +56 -25
- package/dist/models/validation.model.mjs +11 -2
- package/dist/schema.d.mts +34 -34
- package/dist/schema.mjs +11 -19
- package/dist/validator.d.mts +83 -0
- package/dist/validator.mjs +25 -0
- package/package.json +2 -2
- package/src/constants.ts +30 -8
- package/src/{validator/base.validator.ts → handler/base.handler.ts} +6 -6
- package/src/handler/function.handler.ts +9 -0
- package/src/handler/object.handler.ts +245 -0
- package/src/handler/schema.handler.ts +25 -0
- package/src/handler/type.handler.ts +160 -0
- package/src/handler/validator.handler.ts +49 -0
- package/src/handler/value.handler.ts +202 -0
- package/src/helpers/message.helper.ts +72 -30
- package/src/helpers/misc.helper.ts +23 -6
- package/src/helpers/report.helper.ts +72 -0
- package/src/helpers/result.helper.ts +33 -0
- package/src/index.ts +1 -0
- package/src/models/infer.model.ts +31 -13
- package/src/models/misc.model.ts +9 -9
- package/src/models/schematic.plain.model.ts +12 -9
- package/src/models/schematic.typed.model.ts +3 -3
- package/src/models/transform.model.ts +2 -2
- package/src/models/validation.model.ts +75 -37
- package/src/schema.ts +44 -70
- package/src/validator.ts +135 -0
- package/dist/validator/base.validator.d.mts +0 -6
- package/dist/validator/function.validator.d.mts +0 -6
- package/dist/validator/function.validator.mjs +0 -9
- package/dist/validator/named.handler.d.mts +0 -6
- package/dist/validator/named.handler.mjs +0 -23
- package/dist/validator/named.validator.d.mts +0 -7
- package/dist/validator/named.validator.mjs +0 -38
- package/dist/validator/object.validator.d.mts +0 -7
- package/dist/validator/object.validator.mjs +0 -185
- package/dist/validator/schematic.validator.d.mts +0 -7
- package/dist/validator/schematic.validator.mjs +0 -16
- package/src/validator/function.validator.ts +0 -9
- package/src/validator/named.handler.ts +0 -65
- package/src/validator/named.validator.ts +0 -61
- package/src/validator/object.validator.ts +0 -366
- package/src/validator/schematic.validator.ts +0 -25
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
3
|
+
import {join} from '@oscarpalmer/atoms/string';
|
|
4
|
+
import {
|
|
5
|
+
PROPERTY_DEFAULT,
|
|
6
|
+
PROPERTY_REQUIRED,
|
|
7
|
+
PROPERTY_TYPE,
|
|
8
|
+
PROPERTY_VALIDATORS,
|
|
9
|
+
TYPE_UNDEFINED,
|
|
10
|
+
TYPES_ALL,
|
|
11
|
+
VALIDATOR_MESSAGE_INVALID_PROPERTY_NULLABLE,
|
|
12
|
+
VALIDATOR_MESSAGE_INVALID_PROPERTY_TYPE,
|
|
13
|
+
} from '../constants';
|
|
14
|
+
import {
|
|
15
|
+
getDefaultRequiredMessage,
|
|
16
|
+
getDefaultTypeMessage,
|
|
17
|
+
getDisallowedMessage,
|
|
18
|
+
getRequiredMessage,
|
|
19
|
+
getSchematicPropertyNullableMessage,
|
|
20
|
+
getSchematicPropertyTypeMessage,
|
|
21
|
+
} from '../helpers/message.helper';
|
|
22
|
+
import {getParameters, isSchema, isValidator} from '../helpers/misc.helper';
|
|
23
|
+
import type {ValueType} from '../models/misc.model';
|
|
24
|
+
import {
|
|
25
|
+
type PropertyValidationKey,
|
|
26
|
+
SchematicError,
|
|
27
|
+
type ValidationHandler,
|
|
28
|
+
type ValidationHandlerDefaults,
|
|
29
|
+
type ValidationHandlerItem,
|
|
30
|
+
type ValidationHandlerType,
|
|
31
|
+
ValidatorError,
|
|
32
|
+
type Validators,
|
|
33
|
+
} from '../models/validation.model';
|
|
34
|
+
import {getBaseHandler} from './base.handler';
|
|
35
|
+
import {getFunctionHandler} from './function.handler';
|
|
36
|
+
import {getObjectHandler} from './object.handler';
|
|
37
|
+
import {getSchemaHandler} from './schema.handler';
|
|
38
|
+
import {getTypeHandler, getTypeValidators, getValidators} from './type.handler';
|
|
39
|
+
import {validatorHandlers} from '../validator';
|
|
40
|
+
|
|
41
|
+
type Input = {
|
|
42
|
+
validators?: unknown;
|
|
43
|
+
value: unknown;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type PropertyInformation = {
|
|
47
|
+
key: string;
|
|
48
|
+
origin?: PropertyValidationKey;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function getDefaults(
|
|
52
|
+
obj: PlainObject,
|
|
53
|
+
key: string,
|
|
54
|
+
allowed: boolean,
|
|
55
|
+
): ValidationHandlerDefaults | undefined {
|
|
56
|
+
if (!(PROPERTY_DEFAULT in obj)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!allowed) {
|
|
61
|
+
throw new SchematicError(getDisallowedMessage(key, PROPERTY_DEFAULT));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
value: obj[PROPERTY_DEFAULT],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getRequired(obj: PlainObject, key: string, allowed: boolean): boolean | undefined {
|
|
70
|
+
if (!(PROPERTY_REQUIRED in obj)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!allowed) {
|
|
75
|
+
throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (typeof obj[PROPERTY_REQUIRED] !== 'boolean') {
|
|
79
|
+
throw new SchematicError(getRequiredMessage(key));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return obj[PROPERTY_REQUIRED];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function getValueHandler(
|
|
86
|
+
input: Input,
|
|
87
|
+
property?: PropertyInformation,
|
|
88
|
+
): ValidationHandlerItem {
|
|
89
|
+
const isProperty = property != null;
|
|
90
|
+
|
|
91
|
+
const prefixedKey = isProperty
|
|
92
|
+
? property.origin == null
|
|
93
|
+
? property.key
|
|
94
|
+
: join([property.origin.full, property.key], '.')
|
|
95
|
+
: '';
|
|
96
|
+
|
|
97
|
+
const {value} = input;
|
|
98
|
+
|
|
99
|
+
if (value == null) {
|
|
100
|
+
if (isProperty) {
|
|
101
|
+
throw new SchematicError(getSchematicPropertyNullableMessage(prefixedKey));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new ValidatorError(VALIDATOR_MESSAGE_INVALID_PROPERTY_NULLABLE);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const fullKey: PropertyValidationKey = {
|
|
108
|
+
full: prefixedKey,
|
|
109
|
+
short: property?.key ?? '',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
let required = true;
|
|
113
|
+
let typed = false;
|
|
114
|
+
let validators: Validators = {};
|
|
115
|
+
|
|
116
|
+
let defaults: ValidationHandlerDefaults | undefined;
|
|
117
|
+
|
|
118
|
+
let types: ValidationHandlerType[];
|
|
119
|
+
|
|
120
|
+
const handlers: ValidationHandler[] = [];
|
|
121
|
+
|
|
122
|
+
if (isProperty && isPlainObject(value)) {
|
|
123
|
+
typed = PROPERTY_TYPE in value;
|
|
124
|
+
|
|
125
|
+
const type = typed ? value[PROPERTY_TYPE] : value;
|
|
126
|
+
|
|
127
|
+
defaults = getDefaults(value, prefixedKey, typed);
|
|
128
|
+
required = getRequired(value, prefixedKey, typed) ?? required;
|
|
129
|
+
validators = getValidators(value[PROPERTY_VALIDATORS], typed, prefixedKey);
|
|
130
|
+
|
|
131
|
+
types = Array.isArray(type) ? type : [type];
|
|
132
|
+
} else {
|
|
133
|
+
types = Array.isArray(value) ? value : [value];
|
|
134
|
+
|
|
135
|
+
if (input.validators != null) {
|
|
136
|
+
validators = getTypeValidators(types, input.validators);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const typesLength = types.length;
|
|
141
|
+
|
|
142
|
+
let invalid = false;
|
|
143
|
+
|
|
144
|
+
typeLoop: for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
145
|
+
const type = types[typeIndex];
|
|
146
|
+
|
|
147
|
+
let handler: ValidationHandler;
|
|
148
|
+
|
|
149
|
+
switch (true) {
|
|
150
|
+
case typeof type === 'function':
|
|
151
|
+
handler = getFunctionHandler(type);
|
|
152
|
+
break;
|
|
153
|
+
|
|
154
|
+
case isProperty && isPlainObject(type):
|
|
155
|
+
handler = getObjectHandler(type, fullKey, typed);
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case isProperty && isSchema(type):
|
|
159
|
+
handler = getSchemaHandler(type);
|
|
160
|
+
break;
|
|
161
|
+
|
|
162
|
+
case isProperty && isValidator(type):
|
|
163
|
+
handler = validatorHandlers.get(type)!;
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case TYPES_ALL.has(type as ValueType):
|
|
167
|
+
handler = getTypeHandler(type as ValueType, validators, isProperty ? fullKey : undefined);
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
default:
|
|
171
|
+
invalid = true;
|
|
172
|
+
break typeLoop;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
handlers.push(handler);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (invalid || handlers.length === 0) {
|
|
179
|
+
if (isProperty) {
|
|
180
|
+
throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
throw new ValidatorError(VALIDATOR_MESSAGE_INVALID_PROPERTY_TYPE);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
required = required && !types.includes(TYPE_UNDEFINED);
|
|
187
|
+
|
|
188
|
+
if (defaults != null && !required) {
|
|
189
|
+
throw new SchematicError(getDefaultRequiredMessage(prefixedKey));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const handler = getBaseHandler(handlers);
|
|
193
|
+
|
|
194
|
+
if (
|
|
195
|
+
defaults != null &&
|
|
196
|
+
Array.isArray(handler(defaults.value, getParameters(isProperty), false))
|
|
197
|
+
) {
|
|
198
|
+
throw new SchematicError(getDefaultTypeMessage(prefixedKey, types));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {defaults, handler, required, types, key: fullKey};
|
|
202
|
+
}
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
CONJUNCTION_AND_COMMA,
|
|
6
6
|
CONJUNCTION_OR,
|
|
7
7
|
CONJUNCTION_OR_COMMA,
|
|
8
|
-
PREFIXED_TYPES,
|
|
9
8
|
SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED,
|
|
10
9
|
SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE,
|
|
11
10
|
SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED,
|
|
@@ -13,20 +12,25 @@ import {
|
|
|
13
12
|
SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED,
|
|
14
13
|
SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE,
|
|
15
14
|
TEMPLATE_PATTERN,
|
|
16
|
-
|
|
15
|
+
TYPES_ALL,
|
|
16
|
+
TYPES_PREFIXED,
|
|
17
17
|
TYPE_ARRAY,
|
|
18
18
|
TYPE_FUNCTION_RESULT,
|
|
19
19
|
TYPE_NULL,
|
|
20
20
|
TYPE_OBJECT,
|
|
21
21
|
VALIDATION_MESSAGE_INVALID_INPUT,
|
|
22
|
+
VALIDATION_MESSAGE_INVALID_PROPERTY_TYPE,
|
|
23
|
+
VALIDATION_MESSAGE_INVALID_PROPERTY_VALIDATOR,
|
|
22
24
|
VALIDATION_MESSAGE_INVALID_REQUIRED,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
VALIDATION_MESSAGE_INVALID_VALIDATOR_SUFFIX,
|
|
26
|
+
VALIDATION_MESSAGE_INVALID_VALUE_TYPE,
|
|
27
|
+
VALIDATION_MESSAGE_INVALID_VALUE_VALIDATOR,
|
|
26
28
|
VALIDATION_MESSAGE_UNKNOWN_KEYS,
|
|
27
29
|
} from '../constants';
|
|
28
|
-
import type {
|
|
29
|
-
import type {
|
|
30
|
+
import type {ValueType} from '../models/misc.model';
|
|
31
|
+
import type {ValidationHandlerType} from '../models/validation.model';
|
|
32
|
+
import {isValidator} from './misc.helper';
|
|
33
|
+
import {validatorTypes} from '../validator';
|
|
30
34
|
|
|
31
35
|
// #region Defaults
|
|
32
36
|
|
|
@@ -34,7 +38,7 @@ export function getDefaultRequiredMessage(key: string): string {
|
|
|
34
38
|
return SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED.replace(TEMPLATE_PATTERN, key);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
export function getDefaultTypeMessage(key: string, types:
|
|
41
|
+
export function getDefaultTypeMessage(key: string, types: ValidationHandlerType[]): string {
|
|
38
42
|
let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE.replace(TEMPLATE_PATTERN, key);
|
|
39
43
|
|
|
40
44
|
message = message.replace(TEMPLATE_PATTERN, renderTypes(types));
|
|
@@ -62,7 +66,10 @@ export function getInputTypeMessage(actual: unknown): string {
|
|
|
62
66
|
return VALIDATION_MESSAGE_INVALID_INPUT.replace(TEMPLATE_PATTERN, getValueType(actual));
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
export function getInputPropertyMissingMessage(
|
|
69
|
+
export function getInputPropertyMissingMessage(
|
|
70
|
+
key: string,
|
|
71
|
+
types: ValidationHandlerType[],
|
|
72
|
+
): string {
|
|
66
73
|
let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace(TEMPLATE_PATTERN, renderTypes(types));
|
|
67
74
|
|
|
68
75
|
message = message.replace(TEMPLATE_PATTERN, key);
|
|
@@ -72,10 +79,13 @@ export function getInputPropertyMissingMessage(key: string, types: ValidatorType
|
|
|
72
79
|
|
|
73
80
|
export function getInputPropertyTypeMessage(
|
|
74
81
|
key: string,
|
|
75
|
-
types:
|
|
82
|
+
types: ValidationHandlerType[],
|
|
76
83
|
actual: unknown,
|
|
77
84
|
): string {
|
|
78
|
-
let message =
|
|
85
|
+
let message = VALIDATION_MESSAGE_INVALID_PROPERTY_TYPE.replace(
|
|
86
|
+
TEMPLATE_PATTERN,
|
|
87
|
+
renderTypes(types),
|
|
88
|
+
);
|
|
79
89
|
|
|
80
90
|
message = message.replace(TEMPLATE_PATTERN, key);
|
|
81
91
|
message = message.replace(TEMPLATE_PATTERN, getValueType(actual));
|
|
@@ -85,16 +95,38 @@ export function getInputPropertyTypeMessage(
|
|
|
85
95
|
|
|
86
96
|
export function getInputPropertyValidatorMessage(
|
|
87
97
|
key: string,
|
|
88
|
-
type:
|
|
98
|
+
type: ValueType,
|
|
89
99
|
index: number,
|
|
90
100
|
length: number,
|
|
91
101
|
): string {
|
|
92
|
-
let message =
|
|
102
|
+
let message = VALIDATION_MESSAGE_INVALID_PROPERTY_VALIDATOR.replace(TEMPLATE_PATTERN, key);
|
|
93
103
|
|
|
94
104
|
message = message.replace(TEMPLATE_PATTERN, type);
|
|
95
105
|
|
|
96
106
|
if (length > 1) {
|
|
97
|
-
message +=
|
|
107
|
+
message += VALIDATION_MESSAGE_INVALID_VALIDATOR_SUFFIX.replace(TEMPLATE_PATTERN, String(index));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return message;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getInputValueTypeMessage(types: ValidationHandlerType[], actual: unknown): string {
|
|
114
|
+
let message = VALIDATION_MESSAGE_INVALID_VALUE_TYPE.replace(TEMPLATE_PATTERN, renderTypes(types));
|
|
115
|
+
|
|
116
|
+
message = message.replace(TEMPLATE_PATTERN, getValueType(actual));
|
|
117
|
+
|
|
118
|
+
return message;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function getInputValueValidatorMessage(
|
|
122
|
+
type: ValueType,
|
|
123
|
+
index: number,
|
|
124
|
+
length: number,
|
|
125
|
+
): string {
|
|
126
|
+
let message = VALIDATION_MESSAGE_INVALID_VALUE_VALIDATOR.replace(TEMPLATE_PATTERN, type);
|
|
127
|
+
|
|
128
|
+
if (length > 1) {
|
|
129
|
+
message += VALIDATION_MESSAGE_INVALID_VALIDATOR_SUFFIX.replace(TEMPLATE_PATTERN, String(index));
|
|
98
130
|
}
|
|
99
131
|
|
|
100
132
|
return message;
|
|
@@ -116,16 +148,19 @@ export function getSchematicPropertyTypeMessage(key: string): string {
|
|
|
116
148
|
|
|
117
149
|
// #region Misc.
|
|
118
150
|
|
|
119
|
-
function getPropertyType(type:
|
|
151
|
+
function getPropertyType(type: ValidationHandlerType): string[] {
|
|
120
152
|
switch (true) {
|
|
121
153
|
case typeof type === 'function':
|
|
122
|
-
return isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT;
|
|
154
|
+
return [isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT];
|
|
155
|
+
|
|
156
|
+
case isValidator(type):
|
|
157
|
+
return validatorTypes.get(type)!.flatMap(getPropertyType);
|
|
123
158
|
|
|
124
|
-
case
|
|
125
|
-
return
|
|
159
|
+
case TYPES_ALL.has(type as ValueType):
|
|
160
|
+
return [TYPES_PREFIXED[type as ValueType]];
|
|
126
161
|
|
|
127
162
|
default:
|
|
128
|
-
return
|
|
163
|
+
return [TYPES_PREFIXED[TYPE_OBJECT]];
|
|
129
164
|
}
|
|
130
165
|
}
|
|
131
166
|
|
|
@@ -137,13 +172,13 @@ function getValueType(value: unknown): string {
|
|
|
137
172
|
return TYPE_NULL;
|
|
138
173
|
|
|
139
174
|
case Array.isArray(value):
|
|
140
|
-
return
|
|
175
|
+
return TYPES_PREFIXED[TYPE_ARRAY];
|
|
141
176
|
|
|
142
177
|
case isPlainObject(value):
|
|
143
|
-
return
|
|
178
|
+
return TYPES_PREFIXED[TYPE_OBJECT];
|
|
144
179
|
|
|
145
180
|
case valueType !== TYPE_OBJECT:
|
|
146
|
-
return
|
|
181
|
+
return TYPES_PREFIXED[valueType as ValueType];
|
|
147
182
|
|
|
148
183
|
default:
|
|
149
184
|
return (value as object).constructor.name;
|
|
@@ -180,19 +215,26 @@ function renderParts(parts: string[], delimiterShort: string, delimiterLong: str
|
|
|
180
215
|
return rendered;
|
|
181
216
|
}
|
|
182
217
|
|
|
183
|
-
function renderTypes(types:
|
|
218
|
+
function renderTypes(types: ValidationHandlerType[]): string {
|
|
184
219
|
const unique = new Set<string>();
|
|
185
220
|
const parts: string[] = [];
|
|
186
221
|
|
|
187
|
-
|
|
188
|
-
const rendered = getPropertyType(types[index]);
|
|
222
|
+
const typesLength = types.length;
|
|
189
223
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
224
|
+
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
225
|
+
const properties = getPropertyType(types[typeIndex]);
|
|
226
|
+
const propertiesLength = properties.length;
|
|
193
227
|
|
|
194
|
-
|
|
195
|
-
|
|
228
|
+
for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
229
|
+
const property = properties[propertyIndex];
|
|
230
|
+
|
|
231
|
+
if (unique.has(property)) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
unique.add(property);
|
|
236
|
+
parts.push(property);
|
|
237
|
+
}
|
|
196
238
|
}
|
|
197
239
|
|
|
198
240
|
return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
|
|
@@ -3,20 +3,23 @@ import type {Constructor} from '@oscarpalmer/atoms/models';
|
|
|
3
3
|
import {
|
|
4
4
|
MESSAGE_CONSTRUCTOR,
|
|
5
5
|
PROPERTY_SCHEMA,
|
|
6
|
+
PROPERTY_VALIDATOR,
|
|
6
7
|
REPORTING_ALL,
|
|
7
8
|
REPORTING_FIRST,
|
|
8
9
|
REPORTING_NONE,
|
|
10
|
+
REPORTING_RESULT,
|
|
9
11
|
REPORTING_THROW,
|
|
10
12
|
REPORTING_TYPES,
|
|
11
13
|
} from '../constants';
|
|
12
14
|
import type {
|
|
13
15
|
ReportingInformation,
|
|
14
16
|
ReportingType,
|
|
15
|
-
|
|
17
|
+
ValidationHandlerParameters,
|
|
16
18
|
} from '../models/validation.model';
|
|
17
19
|
import type {Schema} from '../schema';
|
|
20
|
+
import type {Validator} from '../validator';
|
|
18
21
|
|
|
19
|
-
export function getParameters(input?: unknown):
|
|
22
|
+
export function getParameters(input?: unknown): ValidationHandlerParameters {
|
|
20
23
|
if (typeof input === 'boolean') {
|
|
21
24
|
return {
|
|
22
25
|
clone: true,
|
|
@@ -53,7 +56,7 @@ export function getReporting(value?: unknown): ReportingInformation {
|
|
|
53
56
|
return {
|
|
54
57
|
type,
|
|
55
58
|
[REPORTING_ALL]: type === REPORTING_ALL,
|
|
56
|
-
[REPORTING_FIRST]: type === REPORTING_FIRST,
|
|
59
|
+
[REPORTING_FIRST]: type === REPORTING_FIRST || type === REPORTING_RESULT,
|
|
57
60
|
[REPORTING_NONE]: type === REPORTING_NONE,
|
|
58
61
|
[REPORTING_THROW]: type === REPORTING_THROW,
|
|
59
62
|
} as ReportingInformation;
|
|
@@ -78,11 +81,11 @@ export function instanceOf<Instance>(
|
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
/**
|
|
81
|
-
* Is the value a
|
|
84
|
+
* Is the value a schema?
|
|
82
85
|
* @param value Value to check
|
|
83
|
-
* @returns `true` if the value is a
|
|
86
|
+
* @returns `true` if the value is a schema, `false` otherwise
|
|
84
87
|
*/
|
|
85
|
-
export function isSchema(value: unknown): value is Schema<
|
|
88
|
+
export function isSchema(value: unknown): value is Schema<unknown> {
|
|
86
89
|
return (
|
|
87
90
|
typeof value === 'object' &&
|
|
88
91
|
value !== null &&
|
|
@@ -90,3 +93,17 @@ export function isSchema(value: unknown): value is Schema<never> {
|
|
|
90
93
|
value[PROPERTY_SCHEMA] === true
|
|
91
94
|
);
|
|
92
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Is the value a validator?
|
|
99
|
+
* @param value Value to check
|
|
100
|
+
* @returns `true` if the value is a validator, `false` otherwise
|
|
101
|
+
*/
|
|
102
|
+
export function isValidator(value: unknown): value is Validator<unknown> {
|
|
103
|
+
return (
|
|
104
|
+
typeof value === 'object' &&
|
|
105
|
+
value !== null &&
|
|
106
|
+
PROPERTY_VALIDATOR in value &&
|
|
107
|
+
value[PROPERTY_VALIDATOR] === true
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationError,
|
|
3
|
+
type ValidationHandlerParameters,
|
|
4
|
+
type PropertyValidation,
|
|
5
|
+
type PropertyValidationKey,
|
|
6
|
+
} from '../models/validation.model';
|
|
7
|
+
|
|
8
|
+
type ReportParameters<Callback extends (...args: any[]) => string> = {
|
|
9
|
+
extract?: boolean;
|
|
10
|
+
information?: ReportParametersInformation;
|
|
11
|
+
key?: PropertyValidationKey;
|
|
12
|
+
message: ReportParametersMessage<Callback>;
|
|
13
|
+
original: ValidationHandlerParameters;
|
|
14
|
+
value: unknown;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ReportParametersMessage<Callback extends (...args: any[]) => string> = {
|
|
18
|
+
arguments: Parameters<Callback>;
|
|
19
|
+
callback: Callback;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type ReportParametersInformation = {
|
|
23
|
+
all: PropertyValidation[];
|
|
24
|
+
existing?: PropertyValidation[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function report<Callback extends (...args: any[]) => string>(
|
|
28
|
+
parameters: ReportParameters<Callback>,
|
|
29
|
+
getReports: true,
|
|
30
|
+
): PropertyValidation[];
|
|
31
|
+
|
|
32
|
+
export function report<Callback extends (...args: any[]) => string>(
|
|
33
|
+
parameters: ReportParameters<Callback>,
|
|
34
|
+
): PropertyValidation[] | undefined;
|
|
35
|
+
|
|
36
|
+
export function report<Callback extends (...args: any[]) => string>(
|
|
37
|
+
parameters: ReportParameters<Callback>,
|
|
38
|
+
getReports?: boolean,
|
|
39
|
+
): PropertyValidation[] | undefined {
|
|
40
|
+
const {information, message, original} = parameters;
|
|
41
|
+
|
|
42
|
+
let reported: PropertyValidation[];
|
|
43
|
+
|
|
44
|
+
if (information?.existing == null) {
|
|
45
|
+
reported = [
|
|
46
|
+
{
|
|
47
|
+
value: parameters.value,
|
|
48
|
+
message: message.callback(...message.arguments),
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
if (parameters.key != null) {
|
|
53
|
+
reported[0].key = parameters.key;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
reported = information.existing;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (original.reporting.throw) {
|
|
60
|
+
throw new ValidationError(reported);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
information?.all.push(...reported);
|
|
64
|
+
|
|
65
|
+
if (parameters.extract ?? true) {
|
|
66
|
+
original.information?.push(...reported);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if ((getReports ?? false) || !original.reporting.all) {
|
|
70
|
+
return reported;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {error, ok} from '@oscarpalmer/atoms/result/misc';
|
|
2
|
+
import type {ValidationHandler} from '../models/validation.model';
|
|
3
|
+
import {getParameters} from './misc.helper';
|
|
4
|
+
|
|
5
|
+
export function getResult(handler: ValidationHandler, value: unknown, options?: unknown): unknown {
|
|
6
|
+
const parameters = getParameters(options);
|
|
7
|
+
|
|
8
|
+
const result = handler(value, parameters, true);
|
|
9
|
+
|
|
10
|
+
if (result === true) {
|
|
11
|
+
return parameters.reporting.none || parameters.reporting.throw
|
|
12
|
+
? parameters.clone
|
|
13
|
+
? parameters.output
|
|
14
|
+
: value
|
|
15
|
+
: ok(parameters.clone ? parameters.output : value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!parameters.reporting.none) {
|
|
19
|
+
return error(parameters.reporting.all ? result : result[0]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isResult(handler: ValidationHandler, value: unknown, options?: unknown): unknown {
|
|
24
|
+
const parameters = getParameters(options);
|
|
25
|
+
|
|
26
|
+
const result = handler(value, parameters, false);
|
|
27
|
+
|
|
28
|
+
if (result === true) {
|
|
29
|
+
return parameters.reporting.none || parameters.reporting.throw ? result : ok(result);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return parameters.reporting.none ? false : error(parameters.reporting.all ? result : result[0]);
|
|
33
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type {Constructor, Simplify} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import type {Schema} from '../schema';
|
|
3
|
-
import type {IsOptionalProperty,
|
|
3
|
+
import type {IsOptionalProperty, ValueType, Values} from './misc.model';
|
|
4
4
|
import type {PlainSchematic, Schematic, SchematicProperty} from './schematic.plain.model';
|
|
5
|
+
import type {Validator} from '../validator';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Infers the TypeScript type from a {@link Schematic} definition
|
|
@@ -49,8 +50,6 @@ export type InferPropertyType<Value> = Value extends (infer Item)[]
|
|
|
49
50
|
/**
|
|
50
51
|
* Maps a single `$type` definition to its TypeScript equivalent
|
|
51
52
|
*
|
|
52
|
-
* Resolves, in order: {@link Constructor}s, {@link Schema} instances, {@link ValueName} values, and nested {@link PlainSchematic} objects
|
|
53
|
-
*
|
|
54
53
|
* @template Value single type definition
|
|
55
54
|
*/
|
|
56
55
|
export type InferPropertyValue<Value> =
|
|
@@ -58,11 +57,13 @@ export type InferPropertyValue<Value> =
|
|
|
58
57
|
? Instance
|
|
59
58
|
: Value extends Schema<infer Model>
|
|
60
59
|
? Model
|
|
61
|
-
: Value extends
|
|
62
|
-
?
|
|
63
|
-
: Value extends
|
|
64
|
-
?
|
|
65
|
-
:
|
|
60
|
+
: Value extends Validator<infer Type>
|
|
61
|
+
? Type
|
|
62
|
+
: Value extends ValueType
|
|
63
|
+
? Values[Value & ValueType]
|
|
64
|
+
: Value extends PlainSchematic
|
|
65
|
+
? Infer<Value>
|
|
66
|
+
: never;
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
* Extracts keys from a {@link Schematic} whose entries are required _(i.e., `$required` is not `false`)_
|
|
@@ -85,8 +86,6 @@ export type InferSchemaEntry<Value> = Value extends (infer Item)[]
|
|
|
85
86
|
/**
|
|
86
87
|
* Maps a single top-level schema entry to its TypeScript type
|
|
87
88
|
*
|
|
88
|
-
* Resolves, in order: {@link Constructor}s, {@link Schema} instances, {@link SchemaProperty} objects, {@link PlainSchematic} objects, and {@link ValueName} values
|
|
89
|
-
*
|
|
90
89
|
* @template Value single schema entry
|
|
91
90
|
*/
|
|
92
91
|
export type InferSchemaEntryValue<Value> =
|
|
@@ -98,6 +97,25 @@ export type InferSchemaEntryValue<Value> =
|
|
|
98
97
|
? InferPropertyType<Value['$type']>
|
|
99
98
|
: Value extends PlainSchematic
|
|
100
99
|
? Infer<Value & Schematic>
|
|
101
|
-
: Value extends
|
|
102
|
-
?
|
|
103
|
-
:
|
|
100
|
+
: Value extends Validator<infer Type>
|
|
101
|
+
? Type
|
|
102
|
+
: Value extends ValueType
|
|
103
|
+
? Values[Value & ValueType]
|
|
104
|
+
: never;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Infers the TypeScript type from a {@link Validator} definition
|
|
108
|
+
*
|
|
109
|
+
* @template Value Validator to infer type from
|
|
110
|
+
*/
|
|
111
|
+
export type InferValidatorValue<Value> = Value extends (infer Item)[]
|
|
112
|
+
? InferValidatorValue<Item>
|
|
113
|
+
: Value extends Constructor<infer Instance>
|
|
114
|
+
? Instance
|
|
115
|
+
: Value extends ((value: unknown) => value is infer Type)
|
|
116
|
+
? Type
|
|
117
|
+
: Value extends (value: unknown) => boolean
|
|
118
|
+
? 'xyz'
|
|
119
|
+
: Value extends ValueType
|
|
120
|
+
? Values[Value & ValueType]
|
|
121
|
+
: never;
|
package/src/models/misc.model.ts
CHANGED
|
@@ -22,22 +22,22 @@ export type DeduplicateTuple<Value extends unknown[], Seen extends unknown[] = [
|
|
|
22
22
|
: Seen;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Recursively extracts {@link
|
|
25
|
+
* Recursively extracts {@link ValueType} strings from a type, unwrapping arrays and readonly arrays
|
|
26
26
|
*
|
|
27
|
-
* @template Value Type to extract value
|
|
27
|
+
* @template Value Type to extract value types from
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
30
|
* ```ts
|
|
31
|
-
* //
|
|
32
|
-
* //
|
|
31
|
+
* // ExtractValueTypes<'string'> => 'string'
|
|
32
|
+
* // ExtractValueTypes<['string', 'number']> => 'string' | 'number'
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
|
-
export type
|
|
35
|
+
export type ExtractValueTypes<Value> = Value extends ValueType
|
|
36
36
|
? Value
|
|
37
37
|
: Value extends (infer Item)[]
|
|
38
|
-
?
|
|
38
|
+
? ExtractValueTypes<Item>
|
|
39
39
|
: Value extends readonly (infer Item)[]
|
|
40
|
-
?
|
|
40
|
+
? ExtractValueTypes<Item>
|
|
41
41
|
: never;
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -183,10 +183,10 @@ export type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only]
|
|
|
183
183
|
/**
|
|
184
184
|
* A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
|
|
185
185
|
*/
|
|
186
|
-
export type
|
|
186
|
+
export type ValueType = keyof Values;
|
|
187
187
|
|
|
188
188
|
/**
|
|
189
|
-
* Maps {@link
|
|
189
|
+
* Maps {@link ValueType} strings to their TypeScript equivalents
|
|
190
190
|
*/
|
|
191
191
|
export type Values = {
|
|
192
192
|
array: unknown[];
|