@oscarpalmer/jhunal 0.10.0 → 0.12.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.js +20 -6
- package/dist/index.js +3 -2
- package/dist/is.js +4 -4
- package/dist/jhunal.full.js +136 -118
- package/dist/models.js +8 -0
- package/dist/schematic.js +13 -12
- package/dist/validation/property.validation.js +96 -0
- package/dist/validation/value.validation.js +28 -30
- package/package.json +1 -1
- package/src/constants.ts +29 -6
- package/src/index.ts +2 -2
- package/src/is.ts +3 -3
- package/src/models.ts +72 -41
- package/src/schematic.ts +25 -15
- package/src/validation/property.validation.ts +190 -0
- package/src/validation/value.validation.ts +45 -53
- package/types/constants.d.ts +14 -3
- package/types/index.d.ts +2 -2
- package/types/is.d.ts +1 -1
- package/types/models.d.ts +26 -20
- package/types/schematic.d.ts +2 -3
- package/types/validation/property.validation.d.ts +3 -0
- package/types/validation/value.validation.d.ts +2 -3
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/array/compact.js +0 -12
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/is.js +0 -20
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/string.js +0 -24
- package/dist/node_modules/@oscarpalmer/atoms/dist/value/smush.js +0 -36
- package/dist/validation/schema.validation.js +0 -92
- package/src/validation/schema.validation.ts +0 -192
- package/types/validation/schema.validation.d.ts +0 -2
package/dist/constants.js
CHANGED
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
const
|
|
1
|
+
const ERROR_NAME = "SchematicError";
|
|
2
2
|
const EXPRESSION_INDEX = /\.\d+$/;
|
|
3
|
-
const
|
|
3
|
+
const EXPRESSION_KEY_PREFIX = /\.\w+$/;
|
|
4
|
+
const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
5
|
+
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
6
|
+
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
7
|
+
const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
8
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
|
|
9
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
10
|
+
const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
11
|
+
const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
|
|
12
|
+
const MESSAGE_VALIDATOR_INVALID_TYPE = "Validators must be an object";
|
|
13
|
+
const MESSAGE_VALIDATOR_INVALID_VALUE = "Validator '<>' must be a function or an array of functions";
|
|
4
14
|
const PROPERTY_REQUIRED = "$required";
|
|
5
15
|
const PROPERTY_TYPE = "$type";
|
|
6
16
|
const PROPERTY_VALIDATORS = "$validators";
|
|
7
17
|
const SCHEMATIC_NAME = "$schematic";
|
|
18
|
+
const TEMPLATE_PATTERN = "<>";
|
|
8
19
|
const TYPE_OBJECT = "object";
|
|
9
20
|
const TYPE_UNDEFINED = "undefined";
|
|
10
|
-
const
|
|
21
|
+
const VALIDATABLE_TYPES = new Set([
|
|
11
22
|
"array",
|
|
12
23
|
"bigint",
|
|
13
24
|
"boolean",
|
|
14
25
|
"date",
|
|
15
26
|
"function",
|
|
16
|
-
"null",
|
|
17
27
|
"number",
|
|
18
28
|
"string",
|
|
19
29
|
"symbol",
|
|
20
|
-
TYPE_OBJECT
|
|
30
|
+
TYPE_OBJECT
|
|
31
|
+
]);
|
|
32
|
+
const TYPE_ALL = new Set([
|
|
33
|
+
...VALIDATABLE_TYPES,
|
|
34
|
+
"null",
|
|
21
35
|
TYPE_UNDEFINED
|
|
22
36
|
]);
|
|
23
|
-
export {
|
|
37
|
+
export { ERROR_NAME, EXPRESSION_INDEX, EXPRESSION_KEY_PREFIX, EXPRESSION_KEY_VALUE, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_SCHEMA_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, SCHEMATIC_NAME, TEMPLATE_PATTERN, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES };
|
package/dist/index.js
CHANGED
package/dist/is.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import "./constants.js";
|
|
1
|
+
import { MESSAGE_CONSTRUCTOR } from "./constants.js";
|
|
2
2
|
import { isConstructor } from "@oscarpalmer/atoms/is";
|
|
3
|
-
function
|
|
4
|
-
if (!isConstructor(constructor)) throw new TypeError(
|
|
3
|
+
function instanceOf(constructor) {
|
|
4
|
+
if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
|
|
5
5
|
return (value) => {
|
|
6
6
|
return value instanceof constructor;
|
|
7
7
|
};
|
|
@@ -9,4 +9,4 @@ function isInstance(constructor) {
|
|
|
9
9
|
function isSchematic(value) {
|
|
10
10
|
return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
|
|
11
11
|
}
|
|
12
|
-
export {
|
|
12
|
+
export { instanceOf, isSchematic };
|
package/dist/jhunal.full.js
CHANGED
|
@@ -58,30 +58,44 @@ function getString(value) {
|
|
|
58
58
|
function join(value, delimiter) {
|
|
59
59
|
return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
|
|
60
60
|
}
|
|
61
|
-
const
|
|
61
|
+
const ERROR_NAME = "SchematicError";
|
|
62
62
|
const EXPRESSION_INDEX = /\.\d+$/;
|
|
63
|
-
const
|
|
63
|
+
const EXPRESSION_KEY_PREFIX = /\.\w+$/;
|
|
64
|
+
const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
65
|
+
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
66
|
+
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
67
|
+
const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
68
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
|
|
69
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
70
|
+
const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
71
|
+
const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
|
|
72
|
+
const MESSAGE_VALIDATOR_INVALID_TYPE = "Validators must be an object";
|
|
73
|
+
const MESSAGE_VALIDATOR_INVALID_VALUE = "Validator '<>' must be a function or an array of functions";
|
|
64
74
|
const PROPERTY_REQUIRED = "$required";
|
|
65
75
|
const PROPERTY_TYPE = "$type";
|
|
66
76
|
const PROPERTY_VALIDATORS = "$validators";
|
|
67
77
|
const SCHEMATIC_NAME = "$schematic";
|
|
78
|
+
const TEMPLATE_PATTERN = "<>";
|
|
68
79
|
const TYPE_OBJECT = "object";
|
|
69
80
|
const TYPE_UNDEFINED = "undefined";
|
|
70
|
-
const
|
|
81
|
+
const VALIDATABLE_TYPES = new Set([
|
|
71
82
|
"array",
|
|
72
83
|
"bigint",
|
|
73
84
|
"boolean",
|
|
74
85
|
"date",
|
|
75
86
|
"function",
|
|
76
|
-
"null",
|
|
77
87
|
"number",
|
|
78
88
|
"string",
|
|
79
89
|
"symbol",
|
|
80
|
-
TYPE_OBJECT
|
|
90
|
+
TYPE_OBJECT
|
|
91
|
+
]);
|
|
92
|
+
const TYPE_ALL = new Set([
|
|
93
|
+
...VALIDATABLE_TYPES,
|
|
94
|
+
"null",
|
|
81
95
|
TYPE_UNDEFINED
|
|
82
96
|
]);
|
|
83
|
-
function
|
|
84
|
-
if (!isConstructor(constructor)) throw new TypeError(
|
|
97
|
+
function instanceOf(constructor) {
|
|
98
|
+
if (!isConstructor(constructor)) throw new TypeError(MESSAGE_CONSTRUCTOR);
|
|
85
99
|
return (value) => {
|
|
86
100
|
return value instanceof constructor;
|
|
87
101
|
};
|
|
@@ -89,6 +103,12 @@ function isInstance(constructor) {
|
|
|
89
103
|
function isSchematic(value) {
|
|
90
104
|
return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
|
|
91
105
|
}
|
|
106
|
+
var SchematicError = class extends Error {
|
|
107
|
+
constructor(message) {
|
|
108
|
+
super(message);
|
|
109
|
+
this.name = ERROR_NAME;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
92
112
|
function flattenObject(value, depth, smushed, prefix) {
|
|
93
113
|
if (depth >= MAX_DEPTH) return {};
|
|
94
114
|
if (smushed.has(value)) return smushed.get(value);
|
|
@@ -122,122 +142,116 @@ function smush(value) {
|
|
|
122
142
|
return typeof value === "object" && value !== null ? flattenObject(value, 0, /* @__PURE__ */ new WeakMap()) : {};
|
|
123
143
|
}
|
|
124
144
|
var MAX_DEPTH = 100;
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
for (const type of values) if (!property.types.includes(type)) property.types.push(type);
|
|
129
|
-
} else {
|
|
130
|
-
to.keys.array.push(key);
|
|
131
|
-
to.keys.set.add(key);
|
|
132
|
-
to.properties[key] = {
|
|
133
|
-
required,
|
|
134
|
-
types: values,
|
|
135
|
-
validators: {}
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) to.properties[key].types.push(TYPE_UNDEFINED);
|
|
139
|
-
to.properties[key].validators = validators;
|
|
145
|
+
function getKeyPrefix(key) {
|
|
146
|
+
const prefix = key.replace(EXPRESSION_KEY_PREFIX, "");
|
|
147
|
+
return prefix === key ? void 0 : prefix;
|
|
140
148
|
}
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
enabled: false,
|
|
144
|
-
keys: {
|
|
145
|
-
array: [],
|
|
146
|
-
set: /* @__PURE__ */ new Set()
|
|
147
|
-
},
|
|
148
|
-
properties: {}
|
|
149
|
-
};
|
|
150
|
-
return typeof schema === "object" && schema !== null ? getValidatedSchema(schema, validated) : validated;
|
|
149
|
+
function getKeyValue(key) {
|
|
150
|
+
return key.replace(EXPRESSION_KEY_VALUE, "$1");
|
|
151
151
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
const { length } = values;
|
|
156
|
-
for (let index = 0; index < length; index += 1) {
|
|
157
|
-
const type = values[index];
|
|
158
|
-
const typeOfType = typeof type;
|
|
159
|
-
if (isSchematic(type) || typeOfType === "string" && TYPE_ALL.has(type)) {
|
|
160
|
-
propertyTypes.push(type);
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
if (typeOfType === "function") {
|
|
164
|
-
propertyTypes.push(isConstructor(type) ? isInstance(type) : type);
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (typeOfType !== "object" || type === null) continue;
|
|
168
|
-
if (PROPERTY_TYPE in type) {
|
|
169
|
-
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
addPropertyType(validated, prefix, [TYPE_OBJECT], {}, type[PROPERTY_REQUIRED] !== false);
|
|
173
|
-
propertyTypes.push(TYPE_OBJECT);
|
|
174
|
-
getValidatedSchema(type, validated, prefix);
|
|
175
|
-
}
|
|
176
|
-
return propertyTypes;
|
|
177
|
-
}
|
|
178
|
-
function getValidatedSchema(schema, validated, prefix) {
|
|
179
|
-
const smushed = smush(schema);
|
|
152
|
+
function getProperties(original) {
|
|
153
|
+
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
154
|
+
const smushed = smush(original);
|
|
180
155
|
const keys = Object.keys(smushed);
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const
|
|
156
|
+
const keysLength = keys.length;
|
|
157
|
+
const properties = [];
|
|
158
|
+
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
159
|
+
const key = keys[keyIndex];
|
|
160
|
+
if (EXPRESSION_INDEX.test(key) || EXPRESSION_PROPERTY.test(key)) continue;
|
|
161
|
+
const keyPrefix = getKeyPrefix(key);
|
|
162
|
+
const keyValue = getKeyValue(key);
|
|
187
163
|
const value = smushed[key];
|
|
188
|
-
|
|
189
|
-
if (EXPRESSION_PROPERTY.test(key)) continue;
|
|
190
|
-
if (EXPRESSION_HAS_NUMBER.test(key) && arrayKeys.has(key.replace(EXPRESSION_INDEX, ""))) continue;
|
|
164
|
+
const types = [];
|
|
191
165
|
let required = true;
|
|
192
166
|
let validators = {};
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
167
|
+
if (isPlainObject(value)) {
|
|
168
|
+
required = getRequired(key, value) ?? required;
|
|
169
|
+
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
170
|
+
if (PROPERTY_TYPE in value) types.push(...getTypes(key, value[PROPERTY_TYPE]));
|
|
171
|
+
else types.push(TYPE_OBJECT);
|
|
172
|
+
} else types.push(...getTypes(key, value));
|
|
173
|
+
if (!required && !types.includes(TYPE_UNDEFINED)) types.push(TYPE_UNDEFINED);
|
|
174
|
+
properties.push({
|
|
175
|
+
types,
|
|
176
|
+
validators,
|
|
177
|
+
key: {
|
|
178
|
+
full: key,
|
|
179
|
+
prefix: keyPrefix,
|
|
180
|
+
value: keyValue
|
|
181
|
+
},
|
|
182
|
+
required: required && !types.includes(TYPE_UNDEFINED)
|
|
183
|
+
});
|
|
199
184
|
}
|
|
200
|
-
|
|
201
|
-
|
|
185
|
+
return properties;
|
|
186
|
+
}
|
|
187
|
+
function getRequired(key, value) {
|
|
188
|
+
if (!(PROPERTY_REQUIRED in value)) return;
|
|
189
|
+
if (typeof value[PROPERTY_REQUIRED] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key));
|
|
190
|
+
return value[PROPERTY_REQUIRED];
|
|
191
|
+
}
|
|
192
|
+
function getTypes(key, original) {
|
|
193
|
+
const array = Array.isArray(original) ? original : [original];
|
|
194
|
+
const { length } = array;
|
|
195
|
+
const types = [];
|
|
196
|
+
for (let index = 0; index < length; index += 1) {
|
|
197
|
+
const value = array[index];
|
|
198
|
+
switch (true) {
|
|
199
|
+
case typeof value === "function":
|
|
200
|
+
types.push(isConstructor(value) ? instanceOf(value) : value);
|
|
201
|
+
break;
|
|
202
|
+
case isPlainObject(value):
|
|
203
|
+
types.push(schematic(value));
|
|
204
|
+
break;
|
|
205
|
+
case isSchematic(value):
|
|
206
|
+
types.push(value);
|
|
207
|
+
break;
|
|
208
|
+
case TYPE_ALL.has(value):
|
|
209
|
+
types.push(value);
|
|
210
|
+
break;
|
|
211
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
215
|
+
return types;
|
|
202
216
|
}
|
|
203
217
|
function getValidators(original) {
|
|
204
218
|
const validators = {};
|
|
205
|
-
if (
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
219
|
+
if (original == null) return validators;
|
|
220
|
+
if (!isPlainObject(original)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_TYPE);
|
|
221
|
+
const keys = Object.keys(original);
|
|
222
|
+
const { length } = keys;
|
|
223
|
+
for (let index = 0; index < length; index += 1) {
|
|
224
|
+
const key = keys[index];
|
|
225
|
+
if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace(TEMPLATE_PATTERN, key));
|
|
226
|
+
const value = original[key];
|
|
227
|
+
validators[key] = (Array.isArray(value) ? value : [value]).filter((item) => {
|
|
228
|
+
if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace(TEMPLATE_PATTERN, key));
|
|
229
|
+
return true;
|
|
230
|
+
});
|
|
209
231
|
}
|
|
210
232
|
return validators;
|
|
211
233
|
}
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const ignore = /* @__PURE__ */ new Set();
|
|
224
|
-
const smushed = smush(obj);
|
|
225
|
-
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
226
|
-
const key = keys.array[keyIndex];
|
|
227
|
-
const prefix = key.replace(EXPRESSION_SUFFIX, "");
|
|
228
|
-
if (ignore.has(prefix)) continue;
|
|
229
|
-
const property = properties[key];
|
|
230
|
-
const value = smushed[key];
|
|
231
|
-
if (value === void 0 && property.required && !property.types.includes(TYPE_UNDEFINED)) return false;
|
|
232
|
-
const typesLength = property.types.length;
|
|
233
|
-
if (typesLength === 1) {
|
|
234
|
-
if (!validateType(property.types[0], property, value)) return false;
|
|
234
|
+
function validateObject(obj, properties) {
|
|
235
|
+
if (!isPlainObject(obj)) return false;
|
|
236
|
+
const ignoredKeys = /* @__PURE__ */ new Set();
|
|
237
|
+
const propertiesLength = properties.length;
|
|
238
|
+
let key;
|
|
239
|
+
let value;
|
|
240
|
+
outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
241
|
+
const property = properties[propertyIndex];
|
|
242
|
+
if (ignoredKeys.has(property.key.prefix)) {
|
|
243
|
+
key = void 0;
|
|
244
|
+
ignoredKeys.add(property.key.full);
|
|
235
245
|
continue;
|
|
236
246
|
}
|
|
247
|
+
key = property.key.full;
|
|
248
|
+
value = obj[key];
|
|
249
|
+
if (value === void 0 && property.required) return false;
|
|
250
|
+
const typesLength = property.types.length;
|
|
237
251
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
238
252
|
const type = property.types[typeIndex];
|
|
239
|
-
if (
|
|
240
|
-
|
|
253
|
+
if (validateValue(type, property, value)) {
|
|
254
|
+
ignoredKeys.add(property.key.full);
|
|
241
255
|
continue outer;
|
|
242
256
|
}
|
|
243
257
|
}
|
|
@@ -245,7 +259,13 @@ function validateValue(validated, obj) {
|
|
|
245
259
|
}
|
|
246
260
|
return true;
|
|
247
261
|
}
|
|
248
|
-
|
|
262
|
+
function validateValue(type, property, value) {
|
|
263
|
+
switch (true) {
|
|
264
|
+
case typeof type === "function": return type(value);
|
|
265
|
+
case isSchematic(type): return type.is(value);
|
|
266
|
+
default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
249
269
|
const validators = {
|
|
250
270
|
array: Array.isArray,
|
|
251
271
|
bigint: (value) => typeof value === "bigint",
|
|
@@ -253,7 +273,7 @@ const validators = {
|
|
|
253
273
|
date: (value) => value instanceof Date,
|
|
254
274
|
function: (value) => typeof value === "function",
|
|
255
275
|
null: (value) => value === null,
|
|
256
|
-
number: (value) => typeof value === "number"
|
|
276
|
+
number: (value) => typeof value === "number",
|
|
257
277
|
object: (value) => typeof value === "object" && value !== null,
|
|
258
278
|
string: (value) => typeof value === "string",
|
|
259
279
|
symbol: (value) => typeof value === "symbol",
|
|
@@ -263,23 +283,21 @@ const validators = {
|
|
|
263
283
|
* A schematic for validating objects
|
|
264
284
|
*/
|
|
265
285
|
var Schematic = class {
|
|
266
|
-
#
|
|
267
|
-
|
|
268
|
-
return this.#schema.enabled;
|
|
269
|
-
}
|
|
270
|
-
constructor(schema) {
|
|
286
|
+
#properties;
|
|
287
|
+
constructor(properties) {
|
|
271
288
|
Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
|
|
272
|
-
this.#
|
|
273
|
-
this.#schema.enabled = this.#schema.keys.array.length > 0;
|
|
289
|
+
this.#properties = properties;
|
|
274
290
|
}
|
|
275
291
|
/**
|
|
276
292
|
* Does the value match the schema?
|
|
277
293
|
*/
|
|
278
294
|
is(value) {
|
|
279
|
-
return
|
|
295
|
+
return validateObject(value, this.#properties);
|
|
280
296
|
}
|
|
281
297
|
};
|
|
282
298
|
function schematic(schema) {
|
|
283
|
-
|
|
299
|
+
if (isSchematic(schema)) return schema;
|
|
300
|
+
if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
|
|
301
|
+
return new Schematic(getProperties(schema));
|
|
284
302
|
}
|
|
285
|
-
export {
|
|
303
|
+
export { SchematicError, instanceOf, schematic };
|
package/dist/models.js
CHANGED
package/dist/schematic.js
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { SCHEMATIC_NAME } from "./constants.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_NAME } from "./constants.js";
|
|
2
|
+
import { isSchematic } from "./is.js";
|
|
3
|
+
import { SchematicError } from "./models.js";
|
|
4
|
+
import { getProperties } from "./validation/property.validation.js";
|
|
5
|
+
import { validateObject } from "./validation/value.validation.js";
|
|
6
|
+
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
4
7
|
/**
|
|
5
8
|
* A schematic for validating objects
|
|
6
9
|
*/
|
|
7
10
|
var Schematic = class {
|
|
8
|
-
#
|
|
9
|
-
|
|
10
|
-
return this.#schema.enabled;
|
|
11
|
-
}
|
|
12
|
-
constructor(schema) {
|
|
11
|
+
#properties;
|
|
12
|
+
constructor(properties) {
|
|
13
13
|
Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
|
|
14
|
-
this.#
|
|
15
|
-
this.#schema.enabled = this.#schema.keys.array.length > 0;
|
|
14
|
+
this.#properties = properties;
|
|
16
15
|
}
|
|
17
16
|
/**
|
|
18
17
|
* Does the value match the schema?
|
|
19
18
|
*/
|
|
20
19
|
is(value) {
|
|
21
|
-
return
|
|
20
|
+
return validateObject(value, this.#properties);
|
|
22
21
|
}
|
|
23
22
|
};
|
|
24
23
|
function schematic(schema) {
|
|
25
|
-
|
|
24
|
+
if (isSchematic(schema)) return schema;
|
|
25
|
+
if (!isPlainObject(schema)) throw new SchematicError(MESSAGE_SCHEMA_INVALID_TYPE);
|
|
26
|
+
return new Schematic(getProperties(schema));
|
|
26
27
|
}
|
|
27
28
|
export { Schematic, schematic };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EXPRESSION_INDEX, EXPRESSION_KEY_PREFIX, EXPRESSION_KEY_VALUE, EXPRESSION_PROPERTY, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED, MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE, MESSAGE_VALIDATOR_INVALID_KEY, MESSAGE_VALIDATOR_INVALID_TYPE, MESSAGE_VALIDATOR_INVALID_VALUE, PROPERTY_REQUIRED, PROPERTY_TYPE, PROPERTY_VALIDATORS, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED, VALIDATABLE_TYPES } from "../constants.js";
|
|
2
|
+
import { instanceOf, isSchematic } from "../is.js";
|
|
3
|
+
import { SchematicError } from "../models.js";
|
|
4
|
+
import { schematic } from "../schematic.js";
|
|
5
|
+
import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
6
|
+
import { smush } from "@oscarpalmer/atoms/value/misc";
|
|
7
|
+
function getKeyPrefix(key) {
|
|
8
|
+
const prefix = key.replace(EXPRESSION_KEY_PREFIX, "");
|
|
9
|
+
return prefix === key ? void 0 : prefix;
|
|
10
|
+
}
|
|
11
|
+
function getKeyValue(key) {
|
|
12
|
+
return key.replace(EXPRESSION_KEY_VALUE, "$1");
|
|
13
|
+
}
|
|
14
|
+
function getProperties(original) {
|
|
15
|
+
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
16
|
+
const smushed = smush(original);
|
|
17
|
+
const keys = Object.keys(smushed);
|
|
18
|
+
const keysLength = keys.length;
|
|
19
|
+
const properties = [];
|
|
20
|
+
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
21
|
+
const key = keys[keyIndex];
|
|
22
|
+
if (EXPRESSION_INDEX.test(key) || EXPRESSION_PROPERTY.test(key)) continue;
|
|
23
|
+
const keyPrefix = getKeyPrefix(key);
|
|
24
|
+
const keyValue = getKeyValue(key);
|
|
25
|
+
const value = smushed[key];
|
|
26
|
+
const types = [];
|
|
27
|
+
let required = true;
|
|
28
|
+
let validators = {};
|
|
29
|
+
if (isPlainObject(value)) {
|
|
30
|
+
required = getRequired(key, value) ?? required;
|
|
31
|
+
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
32
|
+
if ("$type" in value) types.push(...getTypes(key, value[PROPERTY_TYPE]));
|
|
33
|
+
else types.push(TYPE_OBJECT);
|
|
34
|
+
} else types.push(...getTypes(key, value));
|
|
35
|
+
if (!required && !types.includes("undefined")) types.push(TYPE_UNDEFINED);
|
|
36
|
+
properties.push({
|
|
37
|
+
types,
|
|
38
|
+
validators,
|
|
39
|
+
key: {
|
|
40
|
+
full: key,
|
|
41
|
+
prefix: keyPrefix,
|
|
42
|
+
value: keyValue
|
|
43
|
+
},
|
|
44
|
+
required: required && !types.includes("undefined")
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return properties;
|
|
48
|
+
}
|
|
49
|
+
function getRequired(key, value) {
|
|
50
|
+
if (!("$required" in value)) return;
|
|
51
|
+
if (typeof value["$required"] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
|
|
52
|
+
return value[PROPERTY_REQUIRED];
|
|
53
|
+
}
|
|
54
|
+
function getTypes(key, original) {
|
|
55
|
+
const array = Array.isArray(original) ? original : [original];
|
|
56
|
+
const { length } = array;
|
|
57
|
+
const types = [];
|
|
58
|
+
for (let index = 0; index < length; index += 1) {
|
|
59
|
+
const value = array[index];
|
|
60
|
+
switch (true) {
|
|
61
|
+
case typeof value === "function":
|
|
62
|
+
types.push(isConstructor(value) ? instanceOf(value) : value);
|
|
63
|
+
break;
|
|
64
|
+
case isPlainObject(value):
|
|
65
|
+
types.push(schematic(value));
|
|
66
|
+
break;
|
|
67
|
+
case isSchematic(value):
|
|
68
|
+
types.push(value);
|
|
69
|
+
break;
|
|
70
|
+
case TYPE_ALL.has(value):
|
|
71
|
+
types.push(value);
|
|
72
|
+
break;
|
|
73
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
|
|
77
|
+
return types;
|
|
78
|
+
}
|
|
79
|
+
function getValidators(original) {
|
|
80
|
+
const validators = {};
|
|
81
|
+
if (original == null) return validators;
|
|
82
|
+
if (!isPlainObject(original)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_TYPE);
|
|
83
|
+
const keys = Object.keys(original);
|
|
84
|
+
const { length } = keys;
|
|
85
|
+
for (let index = 0; index < length; index += 1) {
|
|
86
|
+
const key = keys[index];
|
|
87
|
+
if (!VALIDATABLE_TYPES.has(key)) throw new TypeError(MESSAGE_VALIDATOR_INVALID_KEY.replace("<>", key));
|
|
88
|
+
const value = original[key];
|
|
89
|
+
validators[key] = (Array.isArray(value) ? value : [value]).filter((item) => {
|
|
90
|
+
if (typeof item !== "function") throw new TypeError(MESSAGE_VALIDATOR_INVALID_VALUE.replace("<>", key));
|
|
91
|
+
return true;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return validators;
|
|
95
|
+
}
|
|
96
|
+
export { getProperties };
|
|
@@ -1,34 +1,26 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import {
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const ignore = /* @__PURE__ */ new Set();
|
|
15
|
-
const smushed = smush(obj);
|
|
16
|
-
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
17
|
-
const key = keys.array[keyIndex];
|
|
18
|
-
const prefix = key.replace(EXPRESSION_SUFFIX, "");
|
|
19
|
-
if (ignore.has(prefix)) continue;
|
|
20
|
-
const property = properties[key];
|
|
21
|
-
const value = smushed[key];
|
|
22
|
-
if (value === void 0 && property.required && !property.types.includes("undefined")) return false;
|
|
23
|
-
const typesLength = property.types.length;
|
|
24
|
-
if (typesLength === 1) {
|
|
25
|
-
if (!validateType(property.types[0], property, value)) return false;
|
|
1
|
+
import { isSchematic } from "../is.js";
|
|
2
|
+
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
3
|
+
function validateObject(obj, properties) {
|
|
4
|
+
if (!isPlainObject(obj)) return false;
|
|
5
|
+
const ignoredKeys = /* @__PURE__ */ new Set();
|
|
6
|
+
const propertiesLength = properties.length;
|
|
7
|
+
let key;
|
|
8
|
+
let value;
|
|
9
|
+
outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
10
|
+
const property = properties[propertyIndex];
|
|
11
|
+
if (ignoredKeys.has(property.key.prefix)) {
|
|
12
|
+
key = void 0;
|
|
13
|
+
ignoredKeys.add(property.key.full);
|
|
26
14
|
continue;
|
|
27
15
|
}
|
|
16
|
+
key = property.key.full;
|
|
17
|
+
value = obj[key];
|
|
18
|
+
if (value === void 0 && property.required) return false;
|
|
19
|
+
const typesLength = property.types.length;
|
|
28
20
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
29
21
|
const type = property.types[typeIndex];
|
|
30
|
-
if (
|
|
31
|
-
|
|
22
|
+
if (validateValue(type, property, value)) {
|
|
23
|
+
ignoredKeys.add(property.key.full);
|
|
32
24
|
continue outer;
|
|
33
25
|
}
|
|
34
26
|
}
|
|
@@ -36,7 +28,13 @@ function validateValue(validated, obj) {
|
|
|
36
28
|
}
|
|
37
29
|
return true;
|
|
38
30
|
}
|
|
39
|
-
|
|
31
|
+
function validateValue(type, property, value) {
|
|
32
|
+
switch (true) {
|
|
33
|
+
case typeof type === "function": return type(value);
|
|
34
|
+
case isSchematic(type): return type.is(value);
|
|
35
|
+
default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
40
38
|
var validators = {
|
|
41
39
|
array: Array.isArray,
|
|
42
40
|
bigint: (value) => typeof value === "bigint",
|
|
@@ -44,10 +42,10 @@ var validators = {
|
|
|
44
42
|
date: (value) => value instanceof Date,
|
|
45
43
|
function: (value) => typeof value === "function",
|
|
46
44
|
null: (value) => value === null,
|
|
47
|
-
number: (value) => typeof value === "number"
|
|
45
|
+
number: (value) => typeof value === "number",
|
|
48
46
|
object: (value) => typeof value === "object" && value !== null,
|
|
49
47
|
string: (value) => typeof value === "string",
|
|
50
48
|
symbol: (value) => typeof value === "symbol",
|
|
51
49
|
undefined: (value) => value === void 0
|
|
52
50
|
};
|
|
53
|
-
export {
|
|
51
|
+
export { validateObject };
|
package/package.json
CHANGED