@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.
- package/dist/constants.d.mts +11 -4
- package/dist/constants.mjs +27 -8
- package/dist/helpers/message.helper.d.mts +17 -0
- package/dist/helpers/message.helper.mjs +92 -0
- package/dist/helpers/misc.helper.d.mts +22 -0
- package/dist/helpers/misc.helper.mjs +56 -0
- package/dist/index.d.mts +309 -292
- package/dist/index.mjs +237 -166
- 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 +28 -7
- package/dist/schematic.d.mts +25 -7
- package/dist/schematic.mjs +6 -4
- package/dist/validator/base.validator.d.mts +6 -0
- package/dist/validator/base.validator.mjs +19 -0
- package/dist/validator/function.validator.d.mts +6 -0
- package/dist/validator/function.validator.mjs +9 -0
- package/dist/validator/named.handler.d.mts +6 -0
- package/dist/validator/named.handler.mjs +23 -0
- package/dist/validator/named.validator.d.mts +7 -0
- package/dist/validator/named.validator.mjs +38 -0
- package/dist/validator/object.validator.d.mts +7 -0
- package/dist/validator/object.validator.mjs +185 -0
- package/dist/validator/schematic.validator.d.mts +7 -0
- package/dist/validator/schematic.validator.mjs +16 -0
- package/package.json +1 -1
- package/src/constants.ts +34 -6
- package/src/helpers/message.helper.ts +217 -0
- package/src/helpers/misc.helper.ts +92 -0
- package/src/index.ts +3 -3
- package/src/models/schema.plain.model.ts +2 -0
- package/src/models/schema.typed.model.ts +4 -22
- package/src/models/validation.model.ts +31 -6
- package/src/schematic.ts +43 -16
- package/src/validator/base.validator.ts +31 -0
- package/src/validator/function.validator.ts +9 -0
- package/src/validator/named.handler.ts +65 -0
- package/src/validator/named.validator.ts +61 -0
- package/src/validator/object.validator.ts +366 -0
- package/src/validator/schematic.validator.ts +25 -0
- package/dist/helpers.d.mts +0 -28
- package/dist/helpers.mjs +0 -120
- package/dist/validation.d.mts +0 -7
- package/dist/validation.mjs +0 -245
- package/src/helpers.ts +0 -249
- package/src/validation.ts +0 -498
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
2
2
|
import { join } from "@oscarpalmer/atoms/string";
|
|
3
3
|
import { error, ok } from "@oscarpalmer/atoms/result/misc";
|
|
4
|
-
import { join as join$1 } from "@oscarpalmer/atoms";
|
|
5
4
|
import { clone } from "@oscarpalmer/atoms/value/clone";
|
|
6
5
|
//#region src/constants.ts
|
|
7
6
|
const CONJUNCTION_OR = " or ";
|
|
@@ -9,14 +8,14 @@ const CONJUNCTION_OR_COMMA = ", or ";
|
|
|
9
8
|
const CONJUNCTION_AND = " and ";
|
|
10
9
|
const CONJUNCTION_AND_COMMA = ", and ";
|
|
11
10
|
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
12
|
-
const NAME_SCHEMATIC = "Schematic";
|
|
13
11
|
const NAME_ERROR_SCHEMATIC = "SchematicError";
|
|
14
12
|
const NAME_ERROR_VALIDATION = "ValidationError";
|
|
13
|
+
const PROPERTY_DEFAULT = "$default";
|
|
15
14
|
const PROPERTY_REQUIRED = "$required";
|
|
16
15
|
const PROPERTY_SCHEMATIC = "$schematic";
|
|
17
16
|
const PROPERTY_TYPE = "$type";
|
|
18
17
|
const PROPERTY_VALIDATORS = "$validators";
|
|
19
|
-
const VALIDATION_MESSAGE_INVALID_INPUT = "Expected
|
|
18
|
+
const VALIDATION_MESSAGE_INVALID_INPUT = "Expected an object as input but received <>";
|
|
20
19
|
const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required property '<>'";
|
|
21
20
|
const VALIDATION_MESSAGE_INVALID_TYPE = "Expected <> for '<>' but received <>";
|
|
22
21
|
const VALIDATION_MESSAGE_INVALID_VALUE = "Value does not satisfy validator for '<>' and type '<>'";
|
|
@@ -31,8 +30,10 @@ const REPORTING_TYPES = new Set([
|
|
|
31
30
|
REPORTING_NONE,
|
|
32
31
|
REPORTING_THROW
|
|
33
32
|
]);
|
|
33
|
+
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED = "'<>' has a default value but is not required";
|
|
34
|
+
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE = "Expected default value for property '<>' to be <>";
|
|
34
35
|
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
35
|
-
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<>.<>' property is not allowed for schemas
|
|
36
|
+
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<>.<>' property is not allowed for plain schemas";
|
|
36
37
|
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE = "'<>' property must not be 'null' or 'undefined'";
|
|
37
38
|
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
|
|
38
39
|
const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
@@ -41,71 +42,62 @@ const SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
|
|
|
41
42
|
const SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE = "Validators must be an object";
|
|
42
43
|
const SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE = "Validator '<>' must be a function or an array of functions";
|
|
43
44
|
const TYPE_ARRAY = "array";
|
|
45
|
+
const TYPE_FUNCTION = "function";
|
|
46
|
+
const TYPE_FUNCTION_RESULT = "a validated value";
|
|
44
47
|
const TYPE_NULL = "null";
|
|
45
48
|
const TYPE_OBJECT = "object";
|
|
46
49
|
const TYPE_UNDEFINED = "undefined";
|
|
47
50
|
const TYPE_ALL = new Set([
|
|
48
51
|
...new Set([
|
|
49
|
-
|
|
52
|
+
TYPE_ARRAY,
|
|
50
53
|
"bigint",
|
|
51
54
|
"boolean",
|
|
52
55
|
"date",
|
|
53
|
-
|
|
56
|
+
TYPE_FUNCTION,
|
|
54
57
|
"number",
|
|
58
|
+
TYPE_OBJECT,
|
|
55
59
|
"string",
|
|
56
|
-
"symbol"
|
|
57
|
-
TYPE_OBJECT
|
|
60
|
+
"symbol"
|
|
58
61
|
]),
|
|
59
|
-
|
|
62
|
+
TYPE_NULL,
|
|
60
63
|
TYPE_UNDEFINED
|
|
61
64
|
]);
|
|
65
|
+
const PREFIXED_TYPES = {
|
|
66
|
+
[TYPE_ARRAY]: `an ${TYPE_ARRAY}`,
|
|
67
|
+
bigint: `a bigint`,
|
|
68
|
+
boolean: `a boolean`,
|
|
69
|
+
date: `a date`,
|
|
70
|
+
[TYPE_FUNCTION]: `a ${TYPE_FUNCTION}`,
|
|
71
|
+
[TYPE_NULL]: TYPE_NULL,
|
|
72
|
+
number: `a number`,
|
|
73
|
+
string: `a string`,
|
|
74
|
+
symbol: `a symbol`,
|
|
75
|
+
[TYPE_OBJECT]: `an ${TYPE_OBJECT}`,
|
|
76
|
+
[TYPE_UNDEFINED]: TYPE_UNDEFINED
|
|
77
|
+
};
|
|
62
78
|
//#endregion
|
|
63
|
-
//#region src/helpers.ts
|
|
64
|
-
function getInvalidInputMessage(actual) {
|
|
65
|
-
return VALIDATION_MESSAGE_INVALID_INPUT.replace("<>", getValueType(actual));
|
|
66
|
-
}
|
|
67
|
-
function getInvalidMissingMessage(key, types) {
|
|
68
|
-
let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace("<>", renderTypes(types));
|
|
69
|
-
message = message.replace("<>", key);
|
|
70
|
-
return message;
|
|
71
|
-
}
|
|
72
|
-
function getInvalidTypeMessage(key, types, actual) {
|
|
73
|
-
let message = VALIDATION_MESSAGE_INVALID_TYPE.replace("<>", renderTypes(types));
|
|
74
|
-
message = message.replace("<>", key);
|
|
75
|
-
message = message.replace("<>", getValueType(actual));
|
|
76
|
-
return message;
|
|
77
|
-
}
|
|
78
|
-
function getInvalidValidatorMessage(key, type, index, length) {
|
|
79
|
-
let message = VALIDATION_MESSAGE_INVALID_VALUE.replace("<>", key);
|
|
80
|
-
message = message.replace("<>", type);
|
|
81
|
-
if (length > 1) message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace("<>", String(index));
|
|
82
|
-
return message;
|
|
83
|
-
}
|
|
79
|
+
//#region src/helpers/misc.helper.ts
|
|
84
80
|
function getParameters(input) {
|
|
85
81
|
if (typeof input === "boolean") return {
|
|
82
|
+
clone: true,
|
|
86
83
|
output: {},
|
|
87
|
-
reporting: getReporting(
|
|
84
|
+
reporting: getReporting(),
|
|
88
85
|
strict: input
|
|
89
86
|
};
|
|
90
87
|
if (REPORTING_TYPES.has(input)) return {
|
|
88
|
+
clone: true,
|
|
91
89
|
output: {},
|
|
92
90
|
reporting: getReporting(input),
|
|
93
91
|
strict: false
|
|
94
92
|
};
|
|
95
93
|
const options = isPlainObject(input) ? input : {};
|
|
96
94
|
return {
|
|
95
|
+
clone: typeof options.clone === "boolean" ? options.clone : true,
|
|
97
96
|
output: {},
|
|
98
97
|
reporting: getReporting(options.errors),
|
|
99
98
|
strict: typeof options.strict === "boolean" ? options.strict : false
|
|
100
99
|
};
|
|
101
100
|
}
|
|
102
|
-
function getPropertyType(original) {
|
|
103
|
-
if (typeof original === "function") return "a validated value";
|
|
104
|
-
if (Array.isArray(original)) return `'array'`;
|
|
105
|
-
if (isPlainObject(original)) return `'${TYPE_OBJECT}'`;
|
|
106
|
-
if (isSchematic(original)) return `a ${NAME_SCHEMATIC}`;
|
|
107
|
-
return `'${String(original)}'`;
|
|
108
|
-
}
|
|
109
101
|
function getReporting(value) {
|
|
110
102
|
const type = REPORTING_TYPES.has(value) ? value : REPORTING_NONE;
|
|
111
103
|
return {
|
|
@@ -116,21 +108,6 @@ function getReporting(value) {
|
|
|
116
108
|
[REPORTING_THROW]: type === REPORTING_THROW
|
|
117
109
|
};
|
|
118
110
|
}
|
|
119
|
-
function getUnknownKeysMessage(keys) {
|
|
120
|
-
return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace("<>", renderKeys(keys));
|
|
121
|
-
}
|
|
122
|
-
function getValueType(value) {
|
|
123
|
-
const valueType = typeof value;
|
|
124
|
-
switch (true) {
|
|
125
|
-
case value === null: return `'${TYPE_NULL}'`;
|
|
126
|
-
case value === void 0: return `'${TYPE_UNDEFINED}'`;
|
|
127
|
-
case valueType !== TYPE_OBJECT: return `'${valueType}'`;
|
|
128
|
-
case Array.isArray(value): return `'${TYPE_ARRAY}'`;
|
|
129
|
-
case isPlainObject(value): return `'${TYPE_OBJECT}'`;
|
|
130
|
-
case isSchematic(value): return `a ${NAME_SCHEMATIC}`;
|
|
131
|
-
default: return value.constructor.name;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
111
|
/**
|
|
135
112
|
* Creates a validator function for a given constructor
|
|
136
113
|
* @param constructor - Constructor to check against
|
|
@@ -151,6 +128,85 @@ function instanceOf(constructor) {
|
|
|
151
128
|
function isSchematic(value) {
|
|
152
129
|
return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
|
|
153
130
|
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/models/validation.model.ts
|
|
133
|
+
/**
|
|
134
|
+
* Thrown when a schema definition is invalid
|
|
135
|
+
*/
|
|
136
|
+
var SchematicError = class extends Error {
|
|
137
|
+
constructor(message) {
|
|
138
|
+
super(message);
|
|
139
|
+
this.name = NAME_ERROR_SCHEMATIC;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Thrown in `'throw'` mode when one or more properties fail validation; `information` holds all failures
|
|
144
|
+
*/
|
|
145
|
+
var ValidationError = class extends Error {
|
|
146
|
+
constructor(information) {
|
|
147
|
+
super(join(information.map((item) => item.message), "; "));
|
|
148
|
+
this.information = information;
|
|
149
|
+
this.name = NAME_ERROR_VALIDATION;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/helpers/message.helper.ts
|
|
154
|
+
function getDefaultRequiredMessage(key) {
|
|
155
|
+
return SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED.replace("<>", key);
|
|
156
|
+
}
|
|
157
|
+
function getDefaultTypeMessage(key, types) {
|
|
158
|
+
let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE.replace("<>", key);
|
|
159
|
+
message = message.replace("<>", renderTypes(types));
|
|
160
|
+
return message;
|
|
161
|
+
}
|
|
162
|
+
function getDisallowedMessage(key, property) {
|
|
163
|
+
let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", key);
|
|
164
|
+
message = message.replace("<>", property);
|
|
165
|
+
return message;
|
|
166
|
+
}
|
|
167
|
+
function getInputTypeMessage(actual) {
|
|
168
|
+
return VALIDATION_MESSAGE_INVALID_INPUT.replace("<>", getValueType(actual));
|
|
169
|
+
}
|
|
170
|
+
function getInputPropertyMissingMessage(key, types) {
|
|
171
|
+
let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace("<>", renderTypes(types));
|
|
172
|
+
message = message.replace("<>", key);
|
|
173
|
+
return message;
|
|
174
|
+
}
|
|
175
|
+
function getInputPropertyTypeMessage(key, types, actual) {
|
|
176
|
+
let message = VALIDATION_MESSAGE_INVALID_TYPE.replace("<>", renderTypes(types));
|
|
177
|
+
message = message.replace("<>", key);
|
|
178
|
+
message = message.replace("<>", getValueType(actual));
|
|
179
|
+
return message;
|
|
180
|
+
}
|
|
181
|
+
function getInputPropertyValidatorMessage(key, type, index, length) {
|
|
182
|
+
let message = VALIDATION_MESSAGE_INVALID_VALUE.replace("<>", key);
|
|
183
|
+
message = message.replace("<>", type);
|
|
184
|
+
if (length > 1) message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace("<>", String(index));
|
|
185
|
+
return message;
|
|
186
|
+
}
|
|
187
|
+
function getSchematicPropertyNullableMessage(key) {
|
|
188
|
+
return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE.replace("<>", key);
|
|
189
|
+
}
|
|
190
|
+
function getSchematicPropertyTypeMessage(key) {
|
|
191
|
+
return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key);
|
|
192
|
+
}
|
|
193
|
+
function getPropertyType(type) {
|
|
194
|
+
switch (true) {
|
|
195
|
+
case typeof type === "function": return isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT;
|
|
196
|
+
case TYPE_ALL.has(type): return PREFIXED_TYPES[type];
|
|
197
|
+
default: return PREFIXED_TYPES[TYPE_OBJECT];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function getValueType(value) {
|
|
201
|
+
const valueType = typeof value;
|
|
202
|
+
switch (true) {
|
|
203
|
+
case value === null: return TYPE_NULL;
|
|
204
|
+
case Array.isArray(value): return PREFIXED_TYPES[TYPE_ARRAY];
|
|
205
|
+
case isPlainObject(value): return PREFIXED_TYPES[TYPE_OBJECT];
|
|
206
|
+
case valueType !== TYPE_OBJECT: return PREFIXED_TYPES[valueType];
|
|
207
|
+
default: return value.constructor.name;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
154
210
|
function renderKeys(keys) {
|
|
155
211
|
return renderParts(keys.map((key) => `'${key}'`), CONJUNCTION_AND, CONJUNCTION_AND_COMMA);
|
|
156
212
|
}
|
|
@@ -176,75 +232,117 @@ function renderTypes(types) {
|
|
|
176
232
|
}
|
|
177
233
|
return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
|
|
178
234
|
}
|
|
235
|
+
function getRequiredMessage(key) {
|
|
236
|
+
return SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key);
|
|
237
|
+
}
|
|
238
|
+
function getUnknownKeysMessage(keys) {
|
|
239
|
+
return VALIDATION_MESSAGE_UNKNOWN_KEYS.replace("<>", renderKeys(keys));
|
|
240
|
+
}
|
|
179
241
|
//#endregion
|
|
180
|
-
//#region src/
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.information = information;
|
|
197
|
-
this.name = NAME_ERROR_VALIDATION;
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
//#endregion
|
|
201
|
-
//#region src/validation.ts
|
|
202
|
-
function getDisallowedProperty(obj) {
|
|
203
|
-
if ("$required" in obj) return PROPERTY_REQUIRED;
|
|
204
|
-
if ("$type" in obj) return PROPERTY_TYPE;
|
|
205
|
-
if ("$validators" in obj) return PROPERTY_VALIDATORS;
|
|
242
|
+
//#region src/validator/base.validator.ts
|
|
243
|
+
function getBaseValidator(validators) {
|
|
244
|
+
const { length } = validators;
|
|
245
|
+
return (input, parameters, get) => {
|
|
246
|
+
const allInformation = [];
|
|
247
|
+
for (let index = 0; index < length; index += 1) {
|
|
248
|
+
const previousInformation = parameters.information;
|
|
249
|
+
parameters.information = [];
|
|
250
|
+
const result = validators[index](input, parameters, get);
|
|
251
|
+
parameters.information = previousInformation;
|
|
252
|
+
if (result === true) return true;
|
|
253
|
+
parameters.information?.push(...result);
|
|
254
|
+
allInformation.push(...result);
|
|
255
|
+
}
|
|
256
|
+
return allInformation;
|
|
257
|
+
};
|
|
206
258
|
}
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/validator/function.validator.ts
|
|
207
261
|
function getFunctionValidator(fn) {
|
|
208
262
|
const validator = isConstructor(fn) ? instanceOf(fn) : fn;
|
|
209
|
-
return (input) => validator(input)
|
|
263
|
+
return (input) => validator(input) ? true : [];
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/validator/named.handler.ts
|
|
267
|
+
function getNamedHandlers(original, prefix, allowed) {
|
|
268
|
+
const handlers = {};
|
|
269
|
+
if (original == null) return handlers;
|
|
270
|
+
if (!allowed) throw new TypeError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", prefix).replace("<>", PROPERTY_VALIDATORS));
|
|
271
|
+
if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
|
|
272
|
+
const keys = Object.keys(original);
|
|
273
|
+
const { length } = keys;
|
|
274
|
+
for (let index = 0; index < length; index += 1) {
|
|
275
|
+
const key = keys[index];
|
|
276
|
+
if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
|
|
277
|
+
const value = original[key];
|
|
278
|
+
handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
|
|
279
|
+
if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
|
|
280
|
+
return item;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return handlers;
|
|
210
284
|
}
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region src/validator/named.validator.ts
|
|
211
287
|
function getNamedValidator(key, name, handlers) {
|
|
212
288
|
const validator = namedValidators[name];
|
|
213
289
|
const named = handlers[name] ?? [];
|
|
214
290
|
const { length } = named;
|
|
215
291
|
return (input, parameters) => {
|
|
216
|
-
if (!validator(input)) return
|
|
292
|
+
if (!validator(input)) return [];
|
|
217
293
|
for (let index = 0; index < length; index += 1) {
|
|
218
294
|
const handler = named[index];
|
|
219
295
|
if (handler(input) === true) continue;
|
|
220
296
|
const information = {
|
|
221
297
|
key,
|
|
222
298
|
validator,
|
|
223
|
-
message:
|
|
299
|
+
message: getInputPropertyValidatorMessage(key.full, name, index, length),
|
|
224
300
|
value: input
|
|
225
301
|
};
|
|
226
302
|
parameters.information?.push(information);
|
|
227
|
-
return parameters.reporting.none ?
|
|
303
|
+
return parameters.reporting.none ? [] : [information];
|
|
228
304
|
}
|
|
229
305
|
return true;
|
|
230
306
|
};
|
|
231
307
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
308
|
+
const namedValidators = {
|
|
309
|
+
array: Array.isArray,
|
|
310
|
+
bigint: (value) => typeof value === "bigint",
|
|
311
|
+
boolean: (value) => typeof value === "boolean",
|
|
312
|
+
date: (value) => value instanceof Date,
|
|
313
|
+
function: (value) => typeof value === "function",
|
|
314
|
+
null: (value) => value === null,
|
|
315
|
+
number: (value) => typeof value === "number",
|
|
316
|
+
object: (value) => typeof value === "object" && value !== null,
|
|
317
|
+
string: (value) => typeof value === "string",
|
|
318
|
+
symbol: (value) => typeof value === "symbol",
|
|
319
|
+
undefined: (value) => value === void 0
|
|
320
|
+
};
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/validator/schematic.validator.ts
|
|
323
|
+
function getSchematicValidator(schematic) {
|
|
324
|
+
const validator = schematicValidator.get(schematic);
|
|
325
|
+
return (input, parameters, get) => {
|
|
326
|
+
let result;
|
|
327
|
+
if (isPlainObject(input)) result = validator(input, parameters, get);
|
|
328
|
+
else result = [];
|
|
329
|
+
if (result === true) return result;
|
|
330
|
+
parameters.information?.push(...result);
|
|
331
|
+
return result;
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
335
|
+
//#region src/validator/object.validator.ts
|
|
336
|
+
function getDefaults(obj, key, allowed) {
|
|
337
|
+
if (!("$default" in obj)) return;
|
|
338
|
+
if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_DEFAULT));
|
|
339
|
+
return { value: obj[PROPERTY_DEFAULT] };
|
|
340
|
+
}
|
|
341
|
+
function getDisallowedProperty(obj) {
|
|
342
|
+
if ("$default" in obj) return PROPERTY_DEFAULT;
|
|
343
|
+
if ("$required" in obj) return PROPERTY_REQUIRED;
|
|
344
|
+
if ("$type" in obj) return PROPERTY_TYPE;
|
|
345
|
+
if ("$validators" in obj) return PROPERTY_VALIDATORS;
|
|
248
346
|
}
|
|
249
347
|
function getObjectValidator(original, origin, fromType) {
|
|
250
348
|
const keys = Object.keys(original);
|
|
@@ -252,15 +350,15 @@ function getObjectValidator(original, origin, fromType) {
|
|
|
252
350
|
if (keysLength === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
253
351
|
if (fromType ?? false) {
|
|
254
352
|
const property = getDisallowedProperty(original);
|
|
255
|
-
if (property != null) throw new SchematicError(
|
|
353
|
+
if (property != null) throw new SchematicError(getDisallowedMessage(origin.full, property));
|
|
256
354
|
}
|
|
257
355
|
const set = /* @__PURE__ */ new Set();
|
|
258
356
|
const items = [];
|
|
259
357
|
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
260
358
|
const key = keys[keyIndex];
|
|
261
359
|
const value = original[key];
|
|
262
|
-
if (value == null) throw new SchematicError(
|
|
263
|
-
const prefixedKey = origin == null ? key : join
|
|
360
|
+
if (value == null) throw new SchematicError(getSchematicPropertyNullableMessage(join([origin?.full, key], ".")));
|
|
361
|
+
const prefixedKey = origin == null ? key : join([origin.full, key], ".");
|
|
264
362
|
const fullKey = {
|
|
265
363
|
full: prefixedKey,
|
|
266
364
|
short: key
|
|
@@ -268,16 +366,18 @@ function getObjectValidator(original, origin, fromType) {
|
|
|
268
366
|
let handlers = {};
|
|
269
367
|
let required = true;
|
|
270
368
|
let typed = false;
|
|
369
|
+
let defaults;
|
|
271
370
|
let types;
|
|
272
371
|
const validators = [];
|
|
273
372
|
if (isPlainObject(value)) {
|
|
274
373
|
typed = PROPERTY_TYPE in value;
|
|
275
374
|
const type = typed ? value[PROPERTY_TYPE] : value;
|
|
276
|
-
|
|
277
|
-
|
|
375
|
+
defaults = getDefaults(value, prefixedKey, typed);
|
|
376
|
+
handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey, typed);
|
|
377
|
+
required = getRequired(value, prefixedKey, typed) ?? required;
|
|
278
378
|
types = Array.isArray(type) ? type : [type];
|
|
279
379
|
} else types = Array.isArray(value) ? value : [value];
|
|
280
|
-
if (types.length === 0) throw new SchematicError(
|
|
380
|
+
if (types.length === 0) throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
|
|
281
381
|
const typesLength = types.length;
|
|
282
382
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
283
383
|
const type = types[typeIndex];
|
|
@@ -295,33 +395,38 @@ function getObjectValidator(original, origin, fromType) {
|
|
|
295
395
|
case TYPE_ALL.has(type):
|
|
296
396
|
validator = getNamedValidator(fullKey, type, handlers);
|
|
297
397
|
break;
|
|
298
|
-
default: throw new SchematicError(
|
|
398
|
+
default: throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
|
|
299
399
|
}
|
|
300
400
|
validators.push(validator);
|
|
301
401
|
}
|
|
302
|
-
|
|
402
|
+
required = required && !types.includes("undefined");
|
|
403
|
+
if (defaults != null && !required) throw new SchematicError(getDefaultRequiredMessage(prefixedKey));
|
|
404
|
+
const validator = getBaseValidator(validators);
|
|
405
|
+
if (defaults != null && Array.isArray(validator(defaults.value, getParameters(), false))) throw new SchematicError(getDefaultTypeMessage(prefixedKey, types));
|
|
303
406
|
items.push({
|
|
407
|
+
defaults,
|
|
408
|
+
required,
|
|
304
409
|
types,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
validator: getValidator(validators)
|
|
410
|
+
validator,
|
|
411
|
+
key: fullKey
|
|
308
412
|
});
|
|
413
|
+
set.add(key);
|
|
309
414
|
}
|
|
310
415
|
const validatorsLength = items.length;
|
|
311
416
|
return (input, parameters, get) => {
|
|
312
417
|
if (!isPlainObject(input)) {
|
|
313
|
-
if (origin != null) return
|
|
418
|
+
if (origin != null) return [];
|
|
314
419
|
const information = {
|
|
315
420
|
key: {
|
|
316
421
|
full: "",
|
|
317
422
|
short: ""
|
|
318
423
|
},
|
|
319
424
|
value: input,
|
|
320
|
-
message:
|
|
425
|
+
message: getInputTypeMessage(input)
|
|
321
426
|
};
|
|
322
427
|
if (parameters.reporting.throw) throw new ValidationError([information]);
|
|
323
428
|
parameters.information?.push(information);
|
|
324
|
-
return
|
|
429
|
+
return [information];
|
|
325
430
|
}
|
|
326
431
|
if (parameters.strict) {
|
|
327
432
|
const unknownKeys = Object.keys(input).filter((key) => !set.has(key));
|
|
@@ -336,21 +441,25 @@ function getObjectValidator(original, origin, fromType) {
|
|
|
336
441
|
};
|
|
337
442
|
if (parameters.reporting.throw) throw new ValidationError([information]);
|
|
338
443
|
parameters.information?.push(information);
|
|
339
|
-
return
|
|
444
|
+
return [information];
|
|
340
445
|
}
|
|
341
446
|
}
|
|
342
447
|
const allInformation = [];
|
|
343
448
|
const output = {};
|
|
344
449
|
for (let validatorIndex = 0; validatorIndex < validatorsLength; validatorIndex += 1) {
|
|
345
|
-
const { key, required, types, validator } = items[validatorIndex];
|
|
450
|
+
const { defaults, key, required, types, validator } = items[validatorIndex];
|
|
346
451
|
const value = input[key.short];
|
|
347
452
|
if (value === void 0) {
|
|
348
453
|
if (required) {
|
|
349
|
-
if (
|
|
454
|
+
if (get && defaults != null) {
|
|
455
|
+
output[key.short] = clone(defaults.value);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (parameters.reporting.none) return [];
|
|
350
459
|
const information = {
|
|
351
460
|
key,
|
|
352
461
|
value,
|
|
353
|
-
message:
|
|
462
|
+
message: getInputPropertyMissingMessage(key.full, types)
|
|
354
463
|
};
|
|
355
464
|
if (parameters.reporting.throw) throw new ValidationError([information]);
|
|
356
465
|
parameters.information?.push(information);
|
|
@@ -366,16 +475,15 @@ function getObjectValidator(original, origin, fromType) {
|
|
|
366
475
|
parameters.output = output;
|
|
367
476
|
const result = validator(value, parameters, get);
|
|
368
477
|
parameters.output = previousOutput;
|
|
369
|
-
if (result === false) continue;
|
|
370
478
|
if (result === true) {
|
|
371
|
-
if (get) output[key.short] = clone(value);
|
|
479
|
+
if (get && !isPlainObject(value)) output[key.short] = parameters.clone ? clone(value) : value;
|
|
372
480
|
continue;
|
|
373
481
|
}
|
|
374
|
-
if (parameters.reporting.none) return
|
|
482
|
+
if (parameters.reporting.none) return [];
|
|
375
483
|
const information = typeof result !== "boolean" && result.length > 0 ? result : [{
|
|
376
484
|
key,
|
|
377
485
|
value,
|
|
378
|
-
message:
|
|
486
|
+
message: getInputPropertyTypeMessage(key.full, types, value)
|
|
379
487
|
}];
|
|
380
488
|
if (parameters.reporting.throw) throw new ValidationError(information);
|
|
381
489
|
if (parameters.reporting.all) {
|
|
@@ -386,54 +494,15 @@ function getObjectValidator(original, origin, fromType) {
|
|
|
386
494
|
}
|
|
387
495
|
if (get) if (origin == null) parameters.output = output;
|
|
388
496
|
else parameters.output[origin.short] = output;
|
|
389
|
-
return
|
|
497
|
+
return allInformation.length === 0 ? true : allInformation;
|
|
390
498
|
};
|
|
391
499
|
}
|
|
392
|
-
function getRequired(key,
|
|
500
|
+
function getRequired(obj, key, allowed) {
|
|
393
501
|
if (!("$required" in obj)) return;
|
|
394
|
-
if (
|
|
502
|
+
if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
|
|
503
|
+
if (typeof obj["$required"] !== "boolean") throw new SchematicError(getRequiredMessage(key));
|
|
395
504
|
return obj[PROPERTY_REQUIRED];
|
|
396
505
|
}
|
|
397
|
-
function getSchematicValidator(schematic) {
|
|
398
|
-
const validator = schematicValidator.get(schematic);
|
|
399
|
-
return (input, parameters, get) => {
|
|
400
|
-
let result = false;
|
|
401
|
-
if (isPlainObject(input)) result = validator(input, parameters, get);
|
|
402
|
-
if (typeof result === "boolean") return result;
|
|
403
|
-
parameters.information?.push(...result);
|
|
404
|
-
return result.length === 0 ? true : result;
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
function getValidator(validators) {
|
|
408
|
-
const { length } = validators;
|
|
409
|
-
return (input, parameters, get) => {
|
|
410
|
-
const allInformation = [];
|
|
411
|
-
for (let index = 0; index < length; index += 1) {
|
|
412
|
-
const previousInformation = parameters.information;
|
|
413
|
-
parameters.information = [];
|
|
414
|
-
const result = validators[index](input, parameters, get);
|
|
415
|
-
parameters.information = previousInformation;
|
|
416
|
-
if (result === false) continue;
|
|
417
|
-
if (result === true) return true;
|
|
418
|
-
parameters.information?.push(...result);
|
|
419
|
-
allInformation.push(...result);
|
|
420
|
-
}
|
|
421
|
-
return allInformation;
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
const namedValidators = {
|
|
425
|
-
array: Array.isArray,
|
|
426
|
-
bigint: (value) => typeof value === "bigint",
|
|
427
|
-
boolean: (value) => typeof value === "boolean",
|
|
428
|
-
date: (value) => value instanceof Date,
|
|
429
|
-
function: (value) => typeof value === "function",
|
|
430
|
-
null: (value) => value === null,
|
|
431
|
-
number: (value) => typeof value === "number",
|
|
432
|
-
object: (value) => typeof value === "object" && value !== null,
|
|
433
|
-
string: (value) => typeof value === "string",
|
|
434
|
-
symbol: (value) => typeof value === "symbol",
|
|
435
|
-
undefined: (value) => value === void 0
|
|
436
|
-
};
|
|
437
506
|
//#endregion
|
|
438
507
|
//#region src/schematic.ts
|
|
439
508
|
/**
|
|
@@ -449,13 +518,15 @@ var Schematic = class {
|
|
|
449
518
|
get(value, options) {
|
|
450
519
|
const parameters = getParameters(options);
|
|
451
520
|
const result = this.#validator(value, parameters, true);
|
|
452
|
-
if (
|
|
521
|
+
if (result === true) return parameters.reporting.none || parameters.reporting.throw ? parameters.output : ok(parameters.output);
|
|
522
|
+
if (parameters.reporting.none) return;
|
|
453
523
|
return error(parameters.reporting.all ? result : result[0]);
|
|
454
524
|
}
|
|
455
525
|
is(value, options) {
|
|
456
526
|
const parameters = getParameters(options);
|
|
457
527
|
const result = this.#validator(value, parameters, false);
|
|
458
|
-
if (
|
|
528
|
+
if (result === true) return parameters.reporting.none || parameters.reporting.throw ? result : ok(result);
|
|
529
|
+
if (parameters.reporting.none) return false;
|
|
459
530
|
return error(parameters.reporting.all ? result : result[0]);
|
|
460
531
|
}
|
|
461
532
|
};
|
|
@@ -9,6 +9,7 @@ import { Constructor } from "@oscarpalmer/atoms/models";
|
|
|
9
9
|
type PlainSchema = {
|
|
10
10
|
[key: string]: PlainSchema | SchemaEntry | SchemaEntry[] | undefined;
|
|
11
11
|
} & {
|
|
12
|
+
$default?: never;
|
|
12
13
|
$required?: never;
|
|
13
14
|
$type?: never;
|
|
14
15
|
$validators?: never;
|
|
@@ -48,6 +49,7 @@ type SchemaEntry = Constructor | PlainSchema | SchemaProperty | Schematic<unknow
|
|
|
48
49
|
* ```
|
|
49
50
|
*/
|
|
50
51
|
type SchemaProperty = {
|
|
52
|
+
$default?: unknown;
|
|
51
53
|
/**
|
|
52
54
|
* Whether the property is required _(defaults to `true`)_
|
|
53
55
|
*/
|
|
@@ -18,6 +18,7 @@ import { PlainObject, Simplify } from "@oscarpalmer/atoms/models";
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
type TypedPropertyOptional<Value> = {
|
|
21
|
+
$default?: never;
|
|
21
22
|
$required: false;
|
|
22
23
|
$type: ToSchemaPropertyType<Exclude<Value, undefined>>;
|
|
23
24
|
$validators?: PropertyValidators<ToSchemaPropertyType<Exclude<Value, undefined>>>;
|
|
@@ -35,6 +36,7 @@ type TypedPropertyOptional<Value> = {
|
|
|
35
36
|
* ```
|
|
36
37
|
*/
|
|
37
38
|
type TypedPropertyRequired<Value> = {
|
|
39
|
+
$default?: unknown;
|
|
38
40
|
$required?: true;
|
|
39
41
|
$type: ToSchemaPropertyType<Value>;
|
|
40
42
|
$validators?: PropertyValidators<ToSchemaPropertyType<Value>>;
|
|
@@ -57,22 +59,6 @@ type TypedPropertyRequired<Value> = {
|
|
|
57
59
|
* };
|
|
58
60
|
* ```
|
|
59
61
|
*/
|
|
60
|
-
type TypedSchema<Model extends PlainObject> = Simplify<{ [Key in RequiredKeys<Model>]: Model[Key] extends PlainObject ?
|
|
61
|
-
/**
|
|
62
|
-
* A {@link TypedSchema} variant for optional nested objects, with `$required` fixed to `false`
|
|
63
|
-
*
|
|
64
|
-
* @template Model Nested object type
|
|
65
|
-
*/
|
|
66
|
-
type TypedSchemaOptional<Model extends PlainObject> = {
|
|
67
|
-
$required: false;
|
|
68
|
-
} & TypedSchema<Model>;
|
|
69
|
-
/**
|
|
70
|
-
* A {@link TypedSchema} variant for required nested objects, with `$required` defaulting to `true`
|
|
71
|
-
*
|
|
72
|
-
* @template Model Nested object type
|
|
73
|
-
*/
|
|
74
|
-
type TypedSchemaRequired<Model extends PlainObject> = {
|
|
75
|
-
$required?: true;
|
|
76
|
-
} & TypedSchema<Model>;
|
|
62
|
+
type TypedSchema<Model extends PlainObject> = Simplify<{ [Key in RequiredKeys<Model>]: Model[Key] extends PlainObject ? Schematic<Model[Key]> : ToSchemaType<Model[Key]> | TypedPropertyRequired<Model[Key]> } & { [Key in OptionalKeys<Model>]: Exclude<Model[Key], undefined> extends PlainObject ? Schematic<Exclude<Model[Key], undefined>> : TypedPropertyOptional<Model[Key]> }>;
|
|
77
63
|
//#endregion
|
|
78
64
|
export { TypedPropertyOptional, TypedPropertyRequired, TypedSchema };
|