@oscarpalmer/jhunal 0.14.0 → 0.16.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 +27 -0
- package/dist/{constants.js → constants.mjs} +2 -0
- package/dist/helpers.d.mts +19 -0
- package/dist/helpers.mjs +25 -0
- package/dist/index.d.mts +553 -0
- package/dist/{jhunal.full.js → index.mjs} +49 -67
- package/dist/models.d.mts +507 -0
- package/dist/models.mjs +18 -0
- package/dist/schematic.d.mts +36 -0
- package/dist/{schematic.js → schematic.mjs} +9 -5
- package/dist/validation/property.validation.d.mts +7 -0
- package/dist/validation/{property.validation.js → property.validation.mjs} +6 -4
- package/dist/validation/value.validation.d.mts +6 -0
- package/dist/validation/{value.validation.js → value.validation.mjs} +4 -2
- package/package.json +53 -48
- package/src/constants.ts +1 -2
- package/src/helpers.ts +12 -1
- package/src/index.ts +1 -1
- package/src/models.ts +394 -36
- package/src/schematic.ts +10 -0
- package/src/validation/property.validation.ts +1 -1
- package/dist/helpers.js +0 -12
- package/dist/index.js +0 -4
- package/dist/models.js +0 -8
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/array/compact.js +0 -12
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/string.js +0 -24
- package/types/constants.d.ts +0 -22
- package/types/helpers.d.ts +0 -4
- package/types/index.d.ts +0 -3
- package/types/models.d.ts +0 -158
- package/types/schematic.d.ts +0 -22
- package/types/validation/property.validation.d.ts +0 -3
- package/types/validation/value.validation.d.ts +0 -2
|
@@ -1,55 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* @returns `true` if the value is a constructor function, otherwise `false`
|
|
5
|
-
*/
|
|
6
|
-
function isConstructor(value) {
|
|
7
|
-
return typeof value === "function" && value.prototype?.constructor === value;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Is the value a plain object?
|
|
11
|
-
* @param value Value to check
|
|
12
|
-
* @returns `true` if the value is a plain object, otherwise `false`
|
|
13
|
-
*/
|
|
14
|
-
function isPlainObject(value) {
|
|
15
|
-
if (value === null || typeof value !== "object") return false;
|
|
16
|
-
if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
|
|
17
|
-
const prototype = Object.getPrototypeOf(value);
|
|
18
|
-
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
19
|
-
}
|
|
20
|
-
function compact(array, strict) {
|
|
21
|
-
if (!Array.isArray(array)) return [];
|
|
22
|
-
if (strict === true) return array.filter(Boolean);
|
|
23
|
-
const { length } = array;
|
|
24
|
-
const compacted = [];
|
|
25
|
-
for (let index = 0; index < length; index += 1) {
|
|
26
|
-
const item = array[index];
|
|
27
|
-
if (item != null) compacted.push(item);
|
|
28
|
-
}
|
|
29
|
-
return compacted;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Get the string value from any value
|
|
33
|
-
* @param value Original value
|
|
34
|
-
* @returns String representation of the value
|
|
35
|
-
*/
|
|
36
|
-
function getString(value) {
|
|
37
|
-
if (typeof value === "string") return value;
|
|
38
|
-
if (value == null) return "";
|
|
39
|
-
if (typeof value === "function") return getString(value());
|
|
40
|
-
if (typeof value !== "object") return String(value);
|
|
41
|
-
const asString = String(value.valueOf?.() ?? value);
|
|
42
|
-
return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Join an array of values into a string
|
|
46
|
-
* @param value Array of values
|
|
47
|
-
* @param delimiter Delimiter to use between values
|
|
48
|
-
* @returns Joined string
|
|
49
|
-
*/
|
|
50
|
-
function join(value, delimiter) {
|
|
51
|
-
return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
|
|
52
|
-
}
|
|
1
|
+
import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
2
|
+
import { join } from "@oscarpalmer/atoms/string";
|
|
3
|
+
//#region src/constants.ts
|
|
53
4
|
const ERROR_NAME = "SchematicError";
|
|
54
5
|
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
55
6
|
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
@@ -65,7 +16,6 @@ const PROPERTY_REQUIRED = "$required";
|
|
|
65
16
|
const PROPERTY_TYPE = "$type";
|
|
66
17
|
const PROPERTY_VALIDATORS = "$validators";
|
|
67
18
|
const SCHEMATIC_NAME = "$schematic";
|
|
68
|
-
const TEMPLATE_PATTERN = "<>";
|
|
69
19
|
const TEMPLATE_PATTERN_KEY = "<key>";
|
|
70
20
|
const TEMPLATE_PATTERN_PROPERTY = "<property>";
|
|
71
21
|
const TYPE_OBJECT = "object";
|
|
@@ -86,25 +36,50 @@ const TYPE_ALL = new Set([
|
|
|
86
36
|
"null",
|
|
87
37
|
TYPE_UNDEFINED
|
|
88
38
|
]);
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/helpers.ts
|
|
41
|
+
/**
|
|
42
|
+
* Creates a validator function for a given constructor
|
|
43
|
+
* @param constructor - Constructor to check against
|
|
44
|
+
* @throws Will throw a `TypeError` if the provided argument is not a valid constructor
|
|
45
|
+
* @returns Validator function that checks if a value is an instance of the constructor
|
|
46
|
+
*/
|
|
89
47
|
function instanceOf(constructor) {
|
|
90
48
|
if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
|
|
91
49
|
return (value) => {
|
|
92
50
|
return value instanceof constructor;
|
|
93
51
|
};
|
|
94
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Is the value a schematic?
|
|
55
|
+
* @param value Value to check
|
|
56
|
+
* @returns `true` if the value is a schematic, `false` otherwise
|
|
57
|
+
*/
|
|
95
58
|
function isSchematic(value) {
|
|
96
|
-
return typeof value === "object" && value !== null &&
|
|
59
|
+
return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
|
|
97
60
|
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/models.ts
|
|
63
|
+
/**
|
|
64
|
+
* A custom error class for schema validation failures, with its `name` set to {@link ERROR_NAME}
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* throw new SchematicError('Expected a string, received a number');
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
98
71
|
var SchematicError = class extends Error {
|
|
99
72
|
constructor(message) {
|
|
100
73
|
super(message);
|
|
101
74
|
this.name = ERROR_NAME;
|
|
102
75
|
}
|
|
103
76
|
};
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/validation/property.validation.ts
|
|
104
79
|
function getDisallowedProperty(obj) {
|
|
105
|
-
if (
|
|
106
|
-
if (
|
|
107
|
-
if (
|
|
80
|
+
if ("$required" in obj) return PROPERTY_REQUIRED;
|
|
81
|
+
if ("$type" in obj) return PROPERTY_TYPE;
|
|
82
|
+
if ("$validators" in obj) return PROPERTY_VALIDATORS;
|
|
108
83
|
}
|
|
109
84
|
function getProperties(original, prefix, fromType) {
|
|
110
85
|
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
@@ -125,22 +100,22 @@ function getProperties(original, prefix, fromType) {
|
|
|
125
100
|
if (isPlainObject(value)) {
|
|
126
101
|
required = getRequired(key, value) ?? required;
|
|
127
102
|
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
128
|
-
if (
|
|
103
|
+
if ("$type" in value) types.push(TYPE_OBJECT, ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
129
104
|
else types.push(TYPE_OBJECT, ...getTypes(key, value, prefix));
|
|
130
105
|
} else types.push(...getTypes(key, value, prefix));
|
|
131
|
-
if (!required && !types.includes(
|
|
106
|
+
if (!required && !types.includes("undefined")) types.push(TYPE_UNDEFINED);
|
|
132
107
|
properties.push({
|
|
133
108
|
key,
|
|
134
109
|
types,
|
|
135
110
|
validators,
|
|
136
|
-
required: required && !types.includes(
|
|
111
|
+
required: required && !types.includes("undefined")
|
|
137
112
|
});
|
|
138
113
|
}
|
|
139
114
|
return properties;
|
|
140
115
|
}
|
|
141
116
|
function getRequired(key, obj) {
|
|
142
|
-
if (!(
|
|
143
|
-
if (typeof obj[
|
|
117
|
+
if (!("$required" in obj)) return;
|
|
118
|
+
if (typeof obj["$required"] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
|
|
144
119
|
return obj[PROPERTY_REQUIRED];
|
|
145
120
|
}
|
|
146
121
|
function getTypes(key, original, prefix, fromType) {
|
|
@@ -162,10 +137,10 @@ function getTypes(key, original, prefix, fromType) {
|
|
|
162
137
|
case TYPE_ALL.has(value):
|
|
163
138
|
types.push(value);
|
|
164
139
|
break;
|
|
165
|
-
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(
|
|
140
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
|
|
166
141
|
}
|
|
167
142
|
}
|
|
168
|
-
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(
|
|
143
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
|
|
169
144
|
return types;
|
|
170
145
|
}
|
|
171
146
|
function getValidators(original) {
|
|
@@ -176,15 +151,17 @@ function getValidators(original) {
|
|
|
176
151
|
const { length } = keys;
|
|
177
152
|
for (let index = 0; index < length; index += 1) {
|
|
178
153
|
const key = keys[index];
|
|
179
|
-
if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace(
|
|
154
|
+
if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
|
|
180
155
|
const value = original[key];
|
|
181
156
|
validators[key] = (Array.isArray(value) ? value : [value]).filter((item) => {
|
|
182
|
-
if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace(
|
|
157
|
+
if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key));
|
|
183
158
|
return true;
|
|
184
159
|
});
|
|
185
160
|
}
|
|
186
161
|
return validators;
|
|
187
162
|
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/validation/value.validation.ts
|
|
188
165
|
function validateObject(obj, properties) {
|
|
189
166
|
if (!isPlainObject(obj)) return false;
|
|
190
167
|
const propertiesLength = properties.length;
|
|
@@ -223,6 +200,8 @@ const validators = {
|
|
|
223
200
|
symbol: (value) => typeof value === "symbol",
|
|
224
201
|
undefined: (value) => value === void 0
|
|
225
202
|
};
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/schematic.ts
|
|
226
205
|
/**
|
|
227
206
|
* A schematic for validating objects
|
|
228
207
|
*/
|
|
@@ -234,6 +213,8 @@ var Schematic = class {
|
|
|
234
213
|
}
|
|
235
214
|
/**
|
|
236
215
|
* Does the value match the schema?
|
|
216
|
+
* @param value - Value to validate
|
|
217
|
+
* @returns `true` if the value matches the schema, otherwise `false`
|
|
237
218
|
*/
|
|
238
219
|
is(value) {
|
|
239
220
|
return validateObject(value, this.#properties);
|
|
@@ -244,4 +225,5 @@ function schematic(schema) {
|
|
|
244
225
|
if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
|
|
245
226
|
return new Schematic(getProperties(schema));
|
|
246
227
|
}
|
|
247
|
-
|
|
228
|
+
//#endregion
|
|
229
|
+
export { SchematicError, instanceOf, isSchematic, schematic };
|