@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
|
@@ -46,21 +46,42 @@ type ValidationInformationKey = {
|
|
|
46
46
|
full: string;
|
|
47
47
|
short: string;
|
|
48
48
|
};
|
|
49
|
-
|
|
50
|
-
* Options for validation
|
|
51
|
-
*/
|
|
52
|
-
type ValidationOptions<Errors extends ReportingType> = {
|
|
49
|
+
type BaseOptions<Errors extends ReportingType> = {
|
|
53
50
|
/**
|
|
54
51
|
* How should validation failures be reported; see {@link ReportingType} _(defaults to `'none'`)_
|
|
55
52
|
*/
|
|
56
|
-
errors
|
|
53
|
+
errors: Errors;
|
|
57
54
|
/**
|
|
58
55
|
* Validate if unknown keys are present in the object? _(defaults to `false`)_
|
|
59
56
|
*/
|
|
60
57
|
strict?: boolean;
|
|
61
58
|
};
|
|
62
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Options for validating and getting a value from an input
|
|
61
|
+
*/
|
|
62
|
+
type GetOptions<Errors extends ReportingType> = BaseOptions<Errors> & {
|
|
63
|
+
/**
|
|
64
|
+
* Get a deeply cloned version of the input? _(defaults to `true`)_
|
|
65
|
+
*/
|
|
66
|
+
clone?: boolean;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Options for validation an input value
|
|
70
|
+
*/
|
|
71
|
+
type IsOptions<Errors extends ReportingType> = BaseOptions<Errors>;
|
|
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
|
+
};
|
|
63
83
|
type ValidatorParameters = {
|
|
84
|
+
clone: boolean;
|
|
64
85
|
information?: ValidationInformation[];
|
|
65
86
|
output: PlainObject;
|
|
66
87
|
reporting: ReportingInformation;
|
|
@@ -68,4 +89,4 @@ type ValidatorParameters = {
|
|
|
68
89
|
};
|
|
69
90
|
type ValidatorType = Function | PlainObject | Schematic<unknown> | ValueName;
|
|
70
91
|
//#endregion
|
|
71
|
-
export { NamedValidatorHandlers, NamedValidators, ReportingInformation, ReportingType, SchematicError, ValidationError, ValidationInformation, ValidationInformationKey,
|
|
92
|
+
export { GetOptions, IsOptions, NamedValidatorHandlers, NamedValidators, ReportingInformation, ReportingType, SchematicError, ValidationError, ValidationInformation, ValidationInformationKey, Validator, ValidatorDefaults, ValidatorItem, ValidatorParameters, ValidatorType };
|
package/dist/schematic.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Infer } from "./models/infer.model.mjs";
|
|
2
2
|
import { TypedSchema } from "./models/schema.typed.model.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { GetOptions, IsOptions, ValidationInformation, Validator } from "./models/validation.model.mjs";
|
|
4
4
|
import { Schema } from "./models/schema.plain.model.mjs";
|
|
5
5
|
import { PlainObject } from "@oscarpalmer/atoms/models";
|
|
6
6
|
import { Result } from "@oscarpalmer/atoms/result/models";
|
|
@@ -21,7 +21,7 @@ declare class Schematic<Model> {
|
|
|
21
21
|
* @param options Validation options
|
|
22
22
|
* @returns Deeply cloned version of the value if it matches the schema, otherwise throws an error
|
|
23
23
|
*/
|
|
24
|
-
get(value: unknown, options:
|
|
24
|
+
get(value: unknown, options: GetOptions<'throw'>): Model;
|
|
25
25
|
/**
|
|
26
26
|
* Parse a value according to the schema
|
|
27
27
|
*
|
|
@@ -39,7 +39,7 @@ declare class Schematic<Model> {
|
|
|
39
39
|
* @param options Validation options
|
|
40
40
|
* @returns Result holding deeply cloned value or all validation information
|
|
41
41
|
*/
|
|
42
|
-
get(value: unknown, options:
|
|
42
|
+
get(value: unknown, options: GetOptions<'all'>): Result<Model, ValidationInformation[]>;
|
|
43
43
|
/**
|
|
44
44
|
* Parse a value according to the schema
|
|
45
45
|
*
|
|
@@ -57,7 +57,7 @@ declare class Schematic<Model> {
|
|
|
57
57
|
* @param options Validation options
|
|
58
58
|
* @returns Result holding deeply cloned value or all validation information
|
|
59
59
|
*/
|
|
60
|
-
get(value: unknown, options:
|
|
60
|
+
get(value: unknown, options: GetOptions<'first'>): Result<Model, ValidationInformation>;
|
|
61
61
|
/**
|
|
62
62
|
* Parse a value according to the schema
|
|
63
63
|
*
|
|
@@ -67,6 +67,15 @@ declare class Schematic<Model> {
|
|
|
67
67
|
* @returns Result holding deeply cloned value or all validation information
|
|
68
68
|
*/
|
|
69
69
|
get(value: unknown, errors: 'first'): Result<Model, ValidationInformation>;
|
|
70
|
+
/**
|
|
71
|
+
* Parse a value according to the schema
|
|
72
|
+
*
|
|
73
|
+
* Returns a deeply cloned version of the value or `undefined` if the value does not match the schema
|
|
74
|
+
* @param value Value to parse
|
|
75
|
+
* @param options Validation options
|
|
76
|
+
* @returns Deeply cloned value, or `undefined` if it's invalid
|
|
77
|
+
*/
|
|
78
|
+
get(value: unknown, options: GetOptions<'none'>): Model | undefined;
|
|
70
79
|
/**
|
|
71
80
|
* Parse a value according to the schema
|
|
72
81
|
*
|
|
@@ -84,7 +93,7 @@ declare class Schematic<Model> {
|
|
|
84
93
|
* @param options Validation options
|
|
85
94
|
* @returns `true` if the value matches the schema, otherwise throws an error
|
|
86
95
|
*/
|
|
87
|
-
is(value: unknown, options:
|
|
96
|
+
is(value: unknown, options: IsOptions<'throw'>): asserts value is Model;
|
|
88
97
|
/**
|
|
89
98
|
* Does the value match the schema?
|
|
90
99
|
*
|
|
@@ -102,7 +111,7 @@ declare class Schematic<Model> {
|
|
|
102
111
|
* @param options Validation options
|
|
103
112
|
* @returns Result holding `true` or all validation information
|
|
104
113
|
*/
|
|
105
|
-
is(value: unknown, options:
|
|
114
|
+
is(value: unknown, options: IsOptions<'all'>): Result<true, ValidationInformation[]>;
|
|
106
115
|
/**
|
|
107
116
|
* Does the value match the schema?
|
|
108
117
|
*
|
|
@@ -120,7 +129,7 @@ declare class Schematic<Model> {
|
|
|
120
129
|
* @param options Validation options
|
|
121
130
|
* @returns `true` if the value matches the schema, otherwise `false`
|
|
122
131
|
*/
|
|
123
|
-
is(value: unknown, options:
|
|
132
|
+
is(value: unknown, options: IsOptions<'first'>): Result<true, ValidationInformation>;
|
|
124
133
|
/**
|
|
125
134
|
* Does the value match the schema?
|
|
126
135
|
*
|
|
@@ -130,6 +139,15 @@ declare class Schematic<Model> {
|
|
|
130
139
|
* @returns `true` if the value matches the schema, otherwise `false`
|
|
131
140
|
*/
|
|
132
141
|
is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
|
|
142
|
+
/**
|
|
143
|
+
* Does the value match the schema?
|
|
144
|
+
*
|
|
145
|
+
* Will validate that the value matches the schema and return `true` or `false`, without any validation information for validation failures
|
|
146
|
+
* @param value Value to validate
|
|
147
|
+
* @param options Validation options
|
|
148
|
+
* @returns `true` if the value matches the schema, otherwise `false`
|
|
149
|
+
*/
|
|
150
|
+
is(value: unknown, options: IsOptions<'none'>): value is Model;
|
|
133
151
|
/**
|
|
134
152
|
* Does the value match the schema?
|
|
135
153
|
*
|
package/dist/schematic.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PROPERTY_SCHEMATIC, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE } from "./constants.mjs";
|
|
2
|
-
import { getParameters, isSchematic } from "./helpers.mjs";
|
|
2
|
+
import { getParameters, isSchematic } from "./helpers/misc.helper.mjs";
|
|
3
3
|
import { SchematicError } from "./models/validation.model.mjs";
|
|
4
|
-
import { getObjectValidator } from "./
|
|
4
|
+
import { getObjectValidator } from "./validator/object.validator.mjs";
|
|
5
5
|
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
6
6
|
import { error, ok } from "@oscarpalmer/atoms/result/misc";
|
|
7
7
|
//#region src/schematic.ts
|
|
@@ -18,13 +18,15 @@ var Schematic = class {
|
|
|
18
18
|
get(value, options) {
|
|
19
19
|
const parameters = getParameters(options);
|
|
20
20
|
const result = this.#validator(value, parameters, true);
|
|
21
|
-
if (
|
|
21
|
+
if (result === true) return parameters.reporting.none || parameters.reporting.throw ? parameters.output : ok(parameters.output);
|
|
22
|
+
if (parameters.reporting.none) return;
|
|
22
23
|
return error(parameters.reporting.all ? result : result[0]);
|
|
23
24
|
}
|
|
24
25
|
is(value, options) {
|
|
25
26
|
const parameters = getParameters(options);
|
|
26
27
|
const result = this.#validator(value, parameters, false);
|
|
27
|
-
if (
|
|
28
|
+
if (result === true) return parameters.reporting.none || parameters.reporting.throw ? result : ok(result);
|
|
29
|
+
if (parameters.reporting.none) return false;
|
|
28
30
|
return error(parameters.reporting.all ? result : result[0]);
|
|
29
31
|
}
|
|
30
32
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/validator/base.validator.ts
|
|
2
|
+
function getBaseValidator(validators) {
|
|
3
|
+
const { length } = validators;
|
|
4
|
+
return (input, parameters, get) => {
|
|
5
|
+
const allInformation = [];
|
|
6
|
+
for (let index = 0; index < length; index += 1) {
|
|
7
|
+
const previousInformation = parameters.information;
|
|
8
|
+
parameters.information = [];
|
|
9
|
+
const result = validators[index](input, parameters, get);
|
|
10
|
+
parameters.information = previousInformation;
|
|
11
|
+
if (result === true) return true;
|
|
12
|
+
parameters.information?.push(...result);
|
|
13
|
+
allInformation.push(...result);
|
|
14
|
+
}
|
|
15
|
+
return allInformation;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { getBaseValidator };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { instanceOf } from "../helpers/misc.helper.mjs";
|
|
2
|
+
import { isConstructor } from "@oscarpalmer/atoms/is";
|
|
3
|
+
//#region src/validator/function.validator.ts
|
|
4
|
+
function getFunctionValidator(fn) {
|
|
5
|
+
const validator = isConstructor(fn) ? instanceOf(fn) : fn;
|
|
6
|
+
return (input) => validator(input) ? true : [];
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { getFunctionValidator };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { NamedValidatorHandlers } from "../models/validation.model.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/validator/named.handler.d.ts
|
|
4
|
+
declare function getNamedHandlers(original: unknown, prefix: string, allowed: boolean): NamedValidatorHandlers;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { getNamedHandlers };
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
3
|
+
//#region src/validator/named.handler.ts
|
|
4
|
+
function getNamedHandlers(original, prefix, allowed) {
|
|
5
|
+
const handlers = {};
|
|
6
|
+
if (original == null) return handlers;
|
|
7
|
+
if (!allowed) throw new TypeError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace("<>", prefix).replace("<>", PROPERTY_VALIDATORS));
|
|
8
|
+
if (!isPlainObject(original)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_TYPE);
|
|
9
|
+
const keys = Object.keys(original);
|
|
10
|
+
const { length } = keys;
|
|
11
|
+
for (let index = 0; index < length; index += 1) {
|
|
12
|
+
const key = keys[index];
|
|
13
|
+
if (!TYPE_ALL.has(key)) throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
|
|
14
|
+
const value = original[key];
|
|
15
|
+
handlers[key] = (Array.isArray(value) ? value : [value]).map((item) => {
|
|
16
|
+
if (typeof item !== "function") throw new TypeError(SCHEMATIC_MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key).replace("<>", prefix));
|
|
17
|
+
return item;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return handlers;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { getNamedHandlers };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NamedValidatorHandlers, ValidationInformationKey, Validator } from "../models/validation.model.mjs";
|
|
2
|
+
import { ValueName } from "../models/misc.model.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/validator/named.validator.d.ts
|
|
5
|
+
declare function getNamedValidator(key: ValidationInformationKey, name: ValueName, handlers: NamedValidatorHandlers): Validator;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { getNamedValidator };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getInputPropertyValidatorMessage } from "../helpers/message.helper.mjs";
|
|
2
|
+
//#region src/validator/named.validator.ts
|
|
3
|
+
function getNamedValidator(key, name, handlers) {
|
|
4
|
+
const validator = namedValidators[name];
|
|
5
|
+
const named = handlers[name] ?? [];
|
|
6
|
+
const { length } = named;
|
|
7
|
+
return (input, parameters) => {
|
|
8
|
+
if (!validator(input)) return [];
|
|
9
|
+
for (let index = 0; index < length; index += 1) {
|
|
10
|
+
const handler = named[index];
|
|
11
|
+
if (handler(input) === true) continue;
|
|
12
|
+
const information = {
|
|
13
|
+
key,
|
|
14
|
+
validator,
|
|
15
|
+
message: getInputPropertyValidatorMessage(key.full, name, index, length),
|
|
16
|
+
value: input
|
|
17
|
+
};
|
|
18
|
+
parameters.information?.push(information);
|
|
19
|
+
return parameters.reporting.none ? [] : [information];
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const namedValidators = {
|
|
25
|
+
array: Array.isArray,
|
|
26
|
+
bigint: (value) => typeof value === "bigint",
|
|
27
|
+
boolean: (value) => typeof value === "boolean",
|
|
28
|
+
date: (value) => value instanceof Date,
|
|
29
|
+
function: (value) => typeof value === "function",
|
|
30
|
+
null: (value) => value === null,
|
|
31
|
+
number: (value) => typeof value === "number",
|
|
32
|
+
object: (value) => typeof value === "object" && value !== null,
|
|
33
|
+
string: (value) => typeof value === "string",
|
|
34
|
+
symbol: (value) => typeof value === "symbol",
|
|
35
|
+
undefined: (value) => value === void 0
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { getNamedValidator };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ValidationInformationKey, Validator } from "../models/validation.model.mjs";
|
|
2
|
+
import { PlainObject } from "@oscarpalmer/atoms/models";
|
|
3
|
+
|
|
4
|
+
//#region src/validator/object.validator.d.ts
|
|
5
|
+
declare function getObjectValidator(original: PlainObject, origin?: ValidationInformationKey, fromType?: boolean): Validator;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { getObjectValidator };
|
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
import { SchematicError, ValidationError } from "../models/validation.model.mjs";
|
|
4
|
+
import { getDefaultRequiredMessage, getDefaultTypeMessage, getDisallowedMessage, getInputPropertyMissingMessage, getInputPropertyTypeMessage, getInputTypeMessage, getRequiredMessage, getSchematicPropertyNullableMessage, getSchematicPropertyTypeMessage, getUnknownKeysMessage } from "../helpers/message.helper.mjs";
|
|
5
|
+
import { getBaseValidator } from "./base.validator.mjs";
|
|
6
|
+
import { getFunctionValidator } from "./function.validator.mjs";
|
|
7
|
+
import { getNamedHandlers } from "./named.handler.mjs";
|
|
8
|
+
import { getNamedValidator } from "./named.validator.mjs";
|
|
9
|
+
import { getSchematicValidator } from "./schematic.validator.mjs";
|
|
10
|
+
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
11
|
+
import { join } from "@oscarpalmer/atoms/string";
|
|
12
|
+
import { clone } from "@oscarpalmer/atoms/value/clone";
|
|
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
|
+
}
|
|
19
|
+
function getDisallowedProperty(obj) {
|
|
20
|
+
if ("$default" in obj) return PROPERTY_DEFAULT;
|
|
21
|
+
if ("$required" in obj) return PROPERTY_REQUIRED;
|
|
22
|
+
if ("$type" in obj) return PROPERTY_TYPE;
|
|
23
|
+
if ("$validators" in obj) return PROPERTY_VALIDATORS;
|
|
24
|
+
}
|
|
25
|
+
function getObjectValidator(original, origin, fromType) {
|
|
26
|
+
const keys = Object.keys(original);
|
|
27
|
+
const keysLength = keys.length;
|
|
28
|
+
if (keysLength === 0) throw new SchematicError(SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
29
|
+
if (fromType ?? false) {
|
|
30
|
+
const property = getDisallowedProperty(original);
|
|
31
|
+
if (property != null) throw new SchematicError(getDisallowedMessage(origin.full, property));
|
|
32
|
+
}
|
|
33
|
+
const set = /* @__PURE__ */ new Set();
|
|
34
|
+
const items = [];
|
|
35
|
+
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
36
|
+
const key = keys[keyIndex];
|
|
37
|
+
const value = original[key];
|
|
38
|
+
if (value == null) throw new SchematicError(getSchematicPropertyNullableMessage(join([origin?.full, key], ".")));
|
|
39
|
+
const prefixedKey = origin == null ? key : join([origin.full, key], ".");
|
|
40
|
+
const fullKey = {
|
|
41
|
+
full: prefixedKey,
|
|
42
|
+
short: key
|
|
43
|
+
};
|
|
44
|
+
let handlers = {};
|
|
45
|
+
let required = true;
|
|
46
|
+
let typed = false;
|
|
47
|
+
let defaults;
|
|
48
|
+
let types;
|
|
49
|
+
const validators = [];
|
|
50
|
+
if (isPlainObject(value)) {
|
|
51
|
+
typed = PROPERTY_TYPE in value;
|
|
52
|
+
const type = typed ? value[PROPERTY_TYPE] : value;
|
|
53
|
+
defaults = getDefaults(value, prefixedKey, typed);
|
|
54
|
+
handlers = getNamedHandlers(value[PROPERTY_VALIDATORS], prefixedKey, typed);
|
|
55
|
+
required = getRequired(value, prefixedKey, typed) ?? required;
|
|
56
|
+
types = Array.isArray(type) ? type : [type];
|
|
57
|
+
} else types = Array.isArray(value) ? value : [value];
|
|
58
|
+
if (types.length === 0) throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
|
|
59
|
+
const typesLength = types.length;
|
|
60
|
+
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
61
|
+
const type = types[typeIndex];
|
|
62
|
+
let validator;
|
|
63
|
+
switch (true) {
|
|
64
|
+
case typeof type === "function":
|
|
65
|
+
validator = getFunctionValidator(type);
|
|
66
|
+
break;
|
|
67
|
+
case isPlainObject(type):
|
|
68
|
+
validator = getObjectValidator(type, fullKey, typed);
|
|
69
|
+
break;
|
|
70
|
+
case isSchematic(type):
|
|
71
|
+
validator = getSchematicValidator(type);
|
|
72
|
+
break;
|
|
73
|
+
case TYPE_ALL.has(type):
|
|
74
|
+
validator = getNamedValidator(fullKey, type, handlers);
|
|
75
|
+
break;
|
|
76
|
+
default: throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
|
|
77
|
+
}
|
|
78
|
+
validators.push(validator);
|
|
79
|
+
}
|
|
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));
|
|
84
|
+
items.push({
|
|
85
|
+
defaults,
|
|
86
|
+
required,
|
|
87
|
+
types,
|
|
88
|
+
validator,
|
|
89
|
+
key: fullKey
|
|
90
|
+
});
|
|
91
|
+
set.add(key);
|
|
92
|
+
}
|
|
93
|
+
const validatorsLength = items.length;
|
|
94
|
+
return (input, parameters, get) => {
|
|
95
|
+
if (!isPlainObject(input)) {
|
|
96
|
+
if (origin != null) return [];
|
|
97
|
+
const information = {
|
|
98
|
+
key: {
|
|
99
|
+
full: "",
|
|
100
|
+
short: ""
|
|
101
|
+
},
|
|
102
|
+
value: input,
|
|
103
|
+
message: getInputTypeMessage(input)
|
|
104
|
+
};
|
|
105
|
+
if (parameters.reporting.throw) throw new ValidationError([information]);
|
|
106
|
+
parameters.information?.push(information);
|
|
107
|
+
return [information];
|
|
108
|
+
}
|
|
109
|
+
if (parameters.strict) {
|
|
110
|
+
const unknownKeys = Object.keys(input).filter((key) => !set.has(key));
|
|
111
|
+
if (unknownKeys.length > 0) {
|
|
112
|
+
const information = {
|
|
113
|
+
key: origin ?? {
|
|
114
|
+
full: "",
|
|
115
|
+
short: ""
|
|
116
|
+
},
|
|
117
|
+
message: getUnknownKeysMessage(unknownKeys),
|
|
118
|
+
value: input
|
|
119
|
+
};
|
|
120
|
+
if (parameters.reporting.throw) throw new ValidationError([information]);
|
|
121
|
+
parameters.information?.push(information);
|
|
122
|
+
return [information];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const allInformation = [];
|
|
126
|
+
const output = {};
|
|
127
|
+
for (let validatorIndex = 0; validatorIndex < validatorsLength; validatorIndex += 1) {
|
|
128
|
+
const { defaults, key, required, types, validator } = items[validatorIndex];
|
|
129
|
+
const value = input[key.short];
|
|
130
|
+
if (value === void 0) {
|
|
131
|
+
if (required) {
|
|
132
|
+
if (get && defaults != null) {
|
|
133
|
+
output[key.short] = clone(defaults.value);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (parameters.reporting.none) return [];
|
|
137
|
+
const information = {
|
|
138
|
+
key,
|
|
139
|
+
value,
|
|
140
|
+
message: getInputPropertyMissingMessage(key.full, types)
|
|
141
|
+
};
|
|
142
|
+
if (parameters.reporting.throw) throw new ValidationError([information]);
|
|
143
|
+
parameters.information?.push(information);
|
|
144
|
+
if (parameters.reporting.all) {
|
|
145
|
+
allInformation.push(information);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
return [information];
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const previousOutput = parameters.output;
|
|
153
|
+
parameters.output = output;
|
|
154
|
+
const result = validator(value, parameters, get);
|
|
155
|
+
parameters.output = previousOutput;
|
|
156
|
+
if (result === true) {
|
|
157
|
+
if (get && !isPlainObject(value)) output[key.short] = parameters.clone ? clone(value) : value;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (parameters.reporting.none) return [];
|
|
161
|
+
const information = typeof result !== "boolean" && result.length > 0 ? result : [{
|
|
162
|
+
key,
|
|
163
|
+
value,
|
|
164
|
+
message: getInputPropertyTypeMessage(key.full, types, value)
|
|
165
|
+
}];
|
|
166
|
+
if (parameters.reporting.throw) throw new ValidationError(information);
|
|
167
|
+
if (parameters.reporting.all) {
|
|
168
|
+
allInformation.push(...information);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
return information;
|
|
172
|
+
}
|
|
173
|
+
if (get) if (origin == null) parameters.output = output;
|
|
174
|
+
else parameters.output[origin.short] = output;
|
|
175
|
+
return allInformation.length === 0 ? true : allInformation;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function getRequired(obj, key, allowed) {
|
|
179
|
+
if (!("$required" in obj)) return;
|
|
180
|
+
if (!allowed) throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
|
|
181
|
+
if (typeof obj["$required"] !== "boolean") throw new SchematicError(getRequiredMessage(key));
|
|
182
|
+
return obj[PROPERTY_REQUIRED];
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { getObjectValidator };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Validator } from "../models/validation.model.mjs";
|
|
2
|
+
import { Schematic } from "../schematic.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/validator/schematic.validator.d.ts
|
|
5
|
+
declare function getSchematicValidator(schematic: Schematic<unknown>): Validator;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { getSchematicValidator };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { schematicValidator } from "../schematic.mjs";
|
|
2
|
+
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
3
|
+
//#region src/validator/schematic.validator.ts
|
|
4
|
+
function getSchematicValidator(schematic) {
|
|
5
|
+
const validator = schematicValidator.get(schematic);
|
|
6
|
+
return (input, parameters, get) => {
|
|
7
|
+
let result;
|
|
8
|
+
if (isPlainObject(input)) result = validator(input, parameters, get);
|
|
9
|
+
else result = [];
|
|
10
|
+
if (result === true) return result;
|
|
11
|
+
parameters.information?.push(...result);
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { getSchematicValidator };
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -25,6 +25,8 @@ export const MESSAGE_CONSTRUCTOR = 'Expected a constructor function';
|
|
|
25
25
|
|
|
26
26
|
export const NAME_SCHEMATIC = 'Schematic';
|
|
27
27
|
|
|
28
|
+
export const NAME_SCHEMATIC_PREFIXED = 'a Schematic';
|
|
29
|
+
|
|
28
30
|
export const NAME_ERROR_SCHEMATIC = 'SchematicError';
|
|
29
31
|
|
|
30
32
|
export const NAME_ERROR_VALIDATION = 'ValidationError';
|
|
@@ -33,6 +35,8 @@ export const NAME_ERROR_VALIDATION = 'ValidationError';
|
|
|
33
35
|
|
|
34
36
|
// #region Properties
|
|
35
37
|
|
|
38
|
+
export const PROPERTY_DEFAULT = '$default';
|
|
39
|
+
|
|
36
40
|
export const PROPERTY_REQUIRED = '$required';
|
|
37
41
|
|
|
38
42
|
export const PROPERTY_SCHEMATIC = '$schematic';
|
|
@@ -45,7 +49,7 @@ export const PROPERTY_VALIDATORS = '$validators';
|
|
|
45
49
|
|
|
46
50
|
// #region Property validation
|
|
47
51
|
|
|
48
|
-
export const VALIDATION_MESSAGE_INVALID_INPUT =
|
|
52
|
+
export const VALIDATION_MESSAGE_INVALID_INPUT = 'Expected an object as input but received <>';
|
|
49
53
|
|
|
50
54
|
export const VALIDATION_MESSAGE_INVALID_REQUIRED = "Expected <> for required property '<>'";
|
|
51
55
|
|
|
@@ -81,10 +85,16 @@ export const REPORTING_TYPES = new Set<ReportingType>([
|
|
|
81
85
|
|
|
82
86
|
// #region Schematic validation
|
|
83
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
|
+
|
|
84
94
|
export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one property';
|
|
85
95
|
|
|
86
96
|
export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED =
|
|
87
|
-
"'<>.<>' property is not allowed for schemas
|
|
97
|
+
"'<>.<>' property is not allowed for plain schemas";
|
|
88
98
|
|
|
89
99
|
export const SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_NULLABLE =
|
|
90
100
|
"'<>' property must not be 'null' or 'undefined'";
|
|
@@ -116,6 +126,10 @@ export const TEMPLATE_PATTERN = '<>';
|
|
|
116
126
|
|
|
117
127
|
export const TYPE_ARRAY = 'array';
|
|
118
128
|
|
|
129
|
+
export const TYPE_FUNCTION = 'function';
|
|
130
|
+
|
|
131
|
+
export const TYPE_FUNCTION_RESULT = 'a validated value';
|
|
132
|
+
|
|
119
133
|
export const TYPE_NULL = 'null';
|
|
120
134
|
|
|
121
135
|
export const TYPE_OBJECT = 'object';
|
|
@@ -123,17 +137,31 @@ export const TYPE_OBJECT = 'object';
|
|
|
123
137
|
export const TYPE_UNDEFINED = 'undefined';
|
|
124
138
|
|
|
125
139
|
export const VALIDATABLE_TYPES = new Set<ValueName>([
|
|
126
|
-
|
|
140
|
+
TYPE_ARRAY,
|
|
127
141
|
'bigint',
|
|
128
142
|
'boolean',
|
|
129
143
|
'date',
|
|
130
|
-
|
|
144
|
+
TYPE_FUNCTION,
|
|
131
145
|
'number',
|
|
146
|
+
TYPE_OBJECT,
|
|
132
147
|
'string',
|
|
133
148
|
'symbol',
|
|
134
|
-
TYPE_OBJECT,
|
|
135
149
|
]);
|
|
136
150
|
|
|
137
|
-
export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES,
|
|
151
|
+
export const TYPE_ALL = new Set<ValueName>([...VALIDATABLE_TYPES, TYPE_NULL, TYPE_UNDEFINED]);
|
|
152
|
+
|
|
153
|
+
export const PREFIXED_TYPES: Record<ValueName, string> = {
|
|
154
|
+
[TYPE_ARRAY]: `an ${TYPE_ARRAY}`,
|
|
155
|
+
bigint: `a bigint`,
|
|
156
|
+
boolean: `a boolean`,
|
|
157
|
+
date: `a date`,
|
|
158
|
+
[TYPE_FUNCTION]: `a ${TYPE_FUNCTION}`,
|
|
159
|
+
[TYPE_NULL]: TYPE_NULL,
|
|
160
|
+
number: `a number`,
|
|
161
|
+
string: `a string`,
|
|
162
|
+
symbol: `a symbol`,
|
|
163
|
+
[TYPE_OBJECT]: `an ${TYPE_OBJECT}`,
|
|
164
|
+
[TYPE_UNDEFINED]: TYPE_UNDEFINED,
|
|
165
|
+
};
|
|
138
166
|
|
|
139
167
|
// #endregion
|