@oscarpalmer/jhunal 0.12.0 → 0.13.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 +3 -2
- package/dist/index.js +1 -1
- package/dist/jhunal.full.js +26 -91
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/array/compact.js +12 -0
- package/dist/node_modules/@oscarpalmer/atoms/dist/internal/string.js +24 -0
- package/dist/schematic.js +1 -1
- package/dist/validation/property.validation.js +19 -33
- package/dist/validation/value.validation.js +9 -19
- package/package.json +1 -1
- package/src/constants.ts +5 -1
- package/src/index.ts +1 -1
- package/src/models.ts +6 -8
- package/src/schematic.ts +1 -1
- package/src/validation/property.validation.ts +37 -40
- package/src/validation/value.validation.ts +11 -27
- package/types/constants.d.ts +2 -1
- package/types/index.d.ts +1 -1
- package/types/models.d.ts +2 -7
- package/types/validation/property.validation.d.ts +1 -1
- /package/dist/{is.js → helpers.js} +0 -0
- /package/src/{is.ts → helpers.ts} +0 -0
- /package/types/{is.d.ts → helpers.d.ts} +0 -0
package/dist/constants.js
CHANGED
|
@@ -5,7 +5,8 @@ const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
|
5
5
|
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
6
6
|
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
7
7
|
const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
8
|
-
const
|
|
8
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED = "'<>.$required' property is not allowed for schemas in $type";
|
|
9
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE = "'<>.$required' property must be a boolean";
|
|
9
10
|
const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
10
11
|
const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
11
12
|
const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
|
|
@@ -34,4 +35,4 @@ const TYPE_ALL = new Set([
|
|
|
34
35
|
"null",
|
|
35
36
|
TYPE_UNDEFINED
|
|
36
37
|
]);
|
|
37
|
-
export { ERROR_NAME, EXPRESSION_INDEX, EXPRESSION_KEY_PREFIX, EXPRESSION_KEY_VALUE, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY,
|
|
38
|
+
export { ERROR_NAME, EXPRESSION_INDEX, EXPRESSION_KEY_PREFIX, EXPRESSION_KEY_VALUE, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE, 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/jhunal.full.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Is the value an array or a record?
|
|
3
|
-
* @param value Value to check
|
|
4
|
-
* @returns `true` if the value is an array or a record, otherwise `false`
|
|
5
|
-
*/
|
|
6
|
-
function isArrayOrPlainObject(value) {
|
|
7
|
-
return Array.isArray(value) || isPlainObject(value);
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
2
|
* Is the value a constructor function?
|
|
11
3
|
* @param value Value to check
|
|
12
4
|
* @returns `true` if the value is a constructor function, otherwise `false`
|
|
@@ -60,12 +52,11 @@ function join(value, delimiter) {
|
|
|
60
52
|
}
|
|
61
53
|
const ERROR_NAME = "SchematicError";
|
|
62
54
|
const EXPRESSION_INDEX = /\.\d+$/;
|
|
63
|
-
const EXPRESSION_KEY_PREFIX = /\.\w+$/;
|
|
64
|
-
const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
65
55
|
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
66
56
|
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
67
57
|
const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
68
|
-
const
|
|
58
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED = "'<>.$required' property is not allowed for schemas in $type";
|
|
59
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE = "'<>.$required' property must be a boolean";
|
|
69
60
|
const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
70
61
|
const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
71
62
|
const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
|
|
@@ -109,87 +100,41 @@ var SchematicError = class extends Error {
|
|
|
109
100
|
this.name = ERROR_NAME;
|
|
110
101
|
}
|
|
111
102
|
};
|
|
112
|
-
function
|
|
113
|
-
if (depth >= MAX_DEPTH) return {};
|
|
114
|
-
if (smushed.has(value)) return smushed.get(value);
|
|
115
|
-
const keys = Object.keys(value);
|
|
116
|
-
const { length } = keys;
|
|
117
|
-
const flattened = {};
|
|
118
|
-
for (let index = 0; index < length; index += 1) {
|
|
119
|
-
const key = keys[index];
|
|
120
|
-
const val = value[key];
|
|
121
|
-
if (isArrayOrPlainObject(val)) {
|
|
122
|
-
const prefixedKey = join([prefix, key], ".");
|
|
123
|
-
flattened[prefixedKey] = Array.isArray(val) ? [...val] : { ...val };
|
|
124
|
-
const nested = flattenObject(val, depth + 1, smushed, prefixedKey);
|
|
125
|
-
const nestedKeys = Object.keys(nested);
|
|
126
|
-
const nestedLength = nestedKeys.length;
|
|
127
|
-
for (let nestedIndex = 0; nestedIndex < nestedLength; nestedIndex += 1) {
|
|
128
|
-
const nestedKey = nestedKeys[nestedIndex];
|
|
129
|
-
flattened[nestedKey] = nested[nestedKey];
|
|
130
|
-
}
|
|
131
|
-
} else flattened[join([prefix, key], ".")] = val;
|
|
132
|
-
}
|
|
133
|
-
smushed.set(value, flattened);
|
|
134
|
-
return flattened;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Smush an object into a flat object that uses dot notation keys
|
|
138
|
-
* @param value Object to smush
|
|
139
|
-
* @returns Smushed object with dot notation keys
|
|
140
|
-
*/
|
|
141
|
-
function smush(value) {
|
|
142
|
-
return typeof value === "object" && value !== null ? flattenObject(value, 0, /* @__PURE__ */ new WeakMap()) : {};
|
|
143
|
-
}
|
|
144
|
-
var MAX_DEPTH = 100;
|
|
145
|
-
function getKeyPrefix(key) {
|
|
146
|
-
const prefix = key.replace(EXPRESSION_KEY_PREFIX, "");
|
|
147
|
-
return prefix === key ? void 0 : prefix;
|
|
148
|
-
}
|
|
149
|
-
function getKeyValue(key) {
|
|
150
|
-
return key.replace(EXPRESSION_KEY_VALUE, "$1");
|
|
151
|
-
}
|
|
152
|
-
function getProperties(original) {
|
|
103
|
+
function getProperties(original, prefix, fromTypes) {
|
|
153
104
|
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
154
|
-
|
|
155
|
-
const keys = Object.keys(
|
|
105
|
+
if (PROPERTY_REQUIRED in original && (fromTypes ?? false) && prefix != null) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED.replace(TEMPLATE_PATTERN, prefix));
|
|
106
|
+
const keys = Object.keys(original);
|
|
156
107
|
const keysLength = keys.length;
|
|
157
108
|
const properties = [];
|
|
158
109
|
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
159
110
|
const key = keys[keyIndex];
|
|
160
111
|
if (EXPRESSION_INDEX.test(key) || EXPRESSION_PROPERTY.test(key)) continue;
|
|
161
|
-
const
|
|
162
|
-
const keyValue = getKeyValue(key);
|
|
163
|
-
const value = smushed[key];
|
|
112
|
+
const value = original[key];
|
|
164
113
|
const types = [];
|
|
165
114
|
let required = true;
|
|
166
115
|
let validators = {};
|
|
167
116
|
if (isPlainObject(value)) {
|
|
168
117
|
required = getRequired(key, value) ?? required;
|
|
169
118
|
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
170
|
-
if (PROPERTY_TYPE in value) types.push(...getTypes(key, value[PROPERTY_TYPE]));
|
|
171
|
-
else types.push(
|
|
172
|
-
} else types.push(...getTypes(key, value));
|
|
119
|
+
if (PROPERTY_TYPE in value) types.push("object", ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
120
|
+
else types.push("object", ...getTypes(key, value, prefix));
|
|
121
|
+
} else types.push(...getTypes(key, value, prefix));
|
|
173
122
|
if (!required && !types.includes(TYPE_UNDEFINED)) types.push(TYPE_UNDEFINED);
|
|
174
123
|
properties.push({
|
|
124
|
+
key,
|
|
175
125
|
types,
|
|
176
126
|
validators,
|
|
177
|
-
key: {
|
|
178
|
-
full: key,
|
|
179
|
-
prefix: keyPrefix,
|
|
180
|
-
value: keyValue
|
|
181
|
-
},
|
|
182
127
|
required: required && !types.includes(TYPE_UNDEFINED)
|
|
183
128
|
});
|
|
184
129
|
}
|
|
185
130
|
return properties;
|
|
186
131
|
}
|
|
187
|
-
function getRequired(key,
|
|
188
|
-
if (!(PROPERTY_REQUIRED in
|
|
189
|
-
if (typeof
|
|
190
|
-
return
|
|
132
|
+
function getRequired(key, obj) {
|
|
133
|
+
if (!(PROPERTY_REQUIRED in obj)) return;
|
|
134
|
+
if (typeof obj[PROPERTY_REQUIRED] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
135
|
+
return obj[PROPERTY_REQUIRED];
|
|
191
136
|
}
|
|
192
|
-
function getTypes(key, original) {
|
|
137
|
+
function getTypes(key, original, prefix, fromTypes) {
|
|
193
138
|
const array = Array.isArray(original) ? original : [original];
|
|
194
139
|
const { length } = array;
|
|
195
140
|
const types = [];
|
|
@@ -200,7 +145,7 @@ function getTypes(key, original) {
|
|
|
200
145
|
types.push(isConstructor(value) ? instanceOf(value) : value);
|
|
201
146
|
break;
|
|
202
147
|
case isPlainObject(value):
|
|
203
|
-
types.push(
|
|
148
|
+
types.push(...getProperties(value, join([prefix, key], "."), fromTypes));
|
|
204
149
|
break;
|
|
205
150
|
case isSchematic(value):
|
|
206
151
|
types.push(value);
|
|
@@ -208,10 +153,10 @@ function getTypes(key, original) {
|
|
|
208
153
|
case TYPE_ALL.has(value):
|
|
209
154
|
types.push(value);
|
|
210
155
|
break;
|
|
211
|
-
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
156
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], ".")));
|
|
212
157
|
}
|
|
213
158
|
}
|
|
214
|
-
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
159
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], ".")));
|
|
215
160
|
return types;
|
|
216
161
|
}
|
|
217
162
|
function getValidators(original) {
|
|
@@ -233,27 +178,16 @@ function getValidators(original) {
|
|
|
233
178
|
}
|
|
234
179
|
function validateObject(obj, properties) {
|
|
235
180
|
if (!isPlainObject(obj)) return false;
|
|
236
|
-
const ignoredKeys = /* @__PURE__ */ new Set();
|
|
237
181
|
const propertiesLength = properties.length;
|
|
238
|
-
let key;
|
|
239
|
-
let value;
|
|
240
182
|
outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
241
183
|
const property = properties[propertyIndex];
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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;
|
|
184
|
+
const { key, required, types } = property;
|
|
185
|
+
const value = obj[key];
|
|
186
|
+
if (value === void 0 && required) return false;
|
|
187
|
+
const typesLength = types.length;
|
|
251
188
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
252
|
-
const type =
|
|
253
|
-
if (validateValue(type, property, value))
|
|
254
|
-
ignoredKeys.add(property.key.full);
|
|
255
|
-
continue outer;
|
|
256
|
-
}
|
|
189
|
+
const type = types[typeIndex];
|
|
190
|
+
if (validateValue(type, property, value)) continue outer;
|
|
257
191
|
}
|
|
258
192
|
return false;
|
|
259
193
|
}
|
|
@@ -261,8 +195,9 @@ function validateObject(obj, properties) {
|
|
|
261
195
|
}
|
|
262
196
|
function validateValue(type, property, value) {
|
|
263
197
|
switch (true) {
|
|
264
|
-
case typeof type === "function": return type(value);
|
|
265
198
|
case isSchematic(type): return type.is(value);
|
|
199
|
+
case typeof type === "function": return type(value);
|
|
200
|
+
case typeof type === "object": return validateObject(value, [type]);
|
|
266
201
|
default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
|
|
267
202
|
}
|
|
268
203
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function compact(array, strict) {
|
|
2
|
+
if (!Array.isArray(array)) return [];
|
|
3
|
+
if (strict === true) return array.filter(Boolean);
|
|
4
|
+
const { length } = array;
|
|
5
|
+
const compacted = [];
|
|
6
|
+
for (let index = 0; index < length; index += 1) {
|
|
7
|
+
const item = array[index];
|
|
8
|
+
if (item != null) compacted.push(item);
|
|
9
|
+
}
|
|
10
|
+
return compacted;
|
|
11
|
+
}
|
|
12
|
+
export { compact };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { compact } from "./array/compact.js";
|
|
2
|
+
/**
|
|
3
|
+
* Get the string value from any value
|
|
4
|
+
* @param value Original value
|
|
5
|
+
* @returns String representation of the value
|
|
6
|
+
*/
|
|
7
|
+
function getString(value) {
|
|
8
|
+
if (typeof value === "string") return value;
|
|
9
|
+
if (value == null) return "";
|
|
10
|
+
if (typeof value === "function") return getString(value());
|
|
11
|
+
if (typeof value !== "object") return String(value);
|
|
12
|
+
const asString = String(value.valueOf?.() ?? value);
|
|
13
|
+
return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Join an array of values into a string
|
|
17
|
+
* @param value Array of values
|
|
18
|
+
* @param delimiter Delimiter to use between values
|
|
19
|
+
* @returns Joined string
|
|
20
|
+
*/
|
|
21
|
+
function join(value, delimiter) {
|
|
22
|
+
return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
|
|
23
|
+
}
|
|
24
|
+
export { join };
|
package/dist/schematic.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_NAME } from "./constants.js";
|
|
2
|
-
import { isSchematic } from "./
|
|
2
|
+
import { isSchematic } from "./helpers.js";
|
|
3
3
|
import { SchematicError } from "./models.js";
|
|
4
4
|
import { getProperties } from "./validation/property.validation.js";
|
|
5
5
|
import { validateObject } from "./validation/value.validation.js";
|
|
@@ -1,57 +1,43 @@
|
|
|
1
|
-
import { EXPRESSION_INDEX,
|
|
2
|
-
import { instanceOf, isSchematic } from "../
|
|
1
|
+
import { EXPRESSION_INDEX, EXPRESSION_PROPERTY, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED, MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE, 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_UNDEFINED, VALIDATABLE_TYPES } from "../constants.js";
|
|
2
|
+
import { instanceOf, isSchematic } from "../helpers.js";
|
|
3
3
|
import { SchematicError } from "../models.js";
|
|
4
|
-
import {
|
|
4
|
+
import { join } from "../node_modules/@oscarpalmer/atoms/dist/internal/string.js";
|
|
5
5
|
import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
6
|
-
|
|
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) {
|
|
6
|
+
function getProperties(original, prefix, fromTypes) {
|
|
15
7
|
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
16
|
-
|
|
17
|
-
const keys = Object.keys(
|
|
8
|
+
if ("$required" in original && (fromTypes ?? false) && prefix != null) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED.replace("<>", prefix));
|
|
9
|
+
const keys = Object.keys(original);
|
|
18
10
|
const keysLength = keys.length;
|
|
19
11
|
const properties = [];
|
|
20
12
|
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
21
13
|
const key = keys[keyIndex];
|
|
22
14
|
if (EXPRESSION_INDEX.test(key) || EXPRESSION_PROPERTY.test(key)) continue;
|
|
23
|
-
const
|
|
24
|
-
const keyValue = getKeyValue(key);
|
|
25
|
-
const value = smushed[key];
|
|
15
|
+
const value = original[key];
|
|
26
16
|
const types = [];
|
|
27
17
|
let required = true;
|
|
28
18
|
let validators = {};
|
|
29
19
|
if (isPlainObject(value)) {
|
|
30
20
|
required = getRequired(key, value) ?? required;
|
|
31
21
|
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
32
|
-
if ("$type" in value) types.push(...getTypes(key, value[PROPERTY_TYPE]));
|
|
33
|
-
else types.push(
|
|
34
|
-
} else types.push(...getTypes(key, value));
|
|
22
|
+
if ("$type" in value) types.push("object", ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
23
|
+
else types.push("object", ...getTypes(key, value, prefix));
|
|
24
|
+
} else types.push(...getTypes(key, value, prefix));
|
|
35
25
|
if (!required && !types.includes("undefined")) types.push(TYPE_UNDEFINED);
|
|
36
26
|
properties.push({
|
|
27
|
+
key,
|
|
37
28
|
types,
|
|
38
29
|
validators,
|
|
39
|
-
key: {
|
|
40
|
-
full: key,
|
|
41
|
-
prefix: keyPrefix,
|
|
42
|
-
value: keyValue
|
|
43
|
-
},
|
|
44
30
|
required: required && !types.includes("undefined")
|
|
45
31
|
});
|
|
46
32
|
}
|
|
47
33
|
return properties;
|
|
48
34
|
}
|
|
49
|
-
function getRequired(key,
|
|
50
|
-
if (!("$required" in
|
|
51
|
-
if (typeof
|
|
52
|
-
return
|
|
35
|
+
function getRequired(key, obj) {
|
|
36
|
+
if (!("$required" in obj)) return;
|
|
37
|
+
if (typeof obj["$required"] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE.replace("<>", key));
|
|
38
|
+
return obj[PROPERTY_REQUIRED];
|
|
53
39
|
}
|
|
54
|
-
function getTypes(key, original) {
|
|
40
|
+
function getTypes(key, original, prefix, fromTypes) {
|
|
55
41
|
const array = Array.isArray(original) ? original : [original];
|
|
56
42
|
const { length } = array;
|
|
57
43
|
const types = [];
|
|
@@ -62,7 +48,7 @@ function getTypes(key, original) {
|
|
|
62
48
|
types.push(isConstructor(value) ? instanceOf(value) : value);
|
|
63
49
|
break;
|
|
64
50
|
case isPlainObject(value):
|
|
65
|
-
types.push(
|
|
51
|
+
types.push(...getProperties(value, join([prefix, key], "."), fromTypes));
|
|
66
52
|
break;
|
|
67
53
|
case isSchematic(value):
|
|
68
54
|
types.push(value);
|
|
@@ -70,10 +56,10 @@ function getTypes(key, original) {
|
|
|
70
56
|
case TYPE_ALL.has(value):
|
|
71
57
|
types.push(value);
|
|
72
58
|
break;
|
|
73
|
-
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
|
|
59
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
|
|
74
60
|
}
|
|
75
61
|
}
|
|
76
|
-
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
|
|
62
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
|
|
77
63
|
return types;
|
|
78
64
|
}
|
|
79
65
|
function getValidators(original) {
|
|
@@ -1,28 +1,17 @@
|
|
|
1
|
-
import { isSchematic } from "../
|
|
1
|
+
import { isSchematic } from "../helpers.js";
|
|
2
2
|
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
3
3
|
function validateObject(obj, properties) {
|
|
4
4
|
if (!isPlainObject(obj)) return false;
|
|
5
|
-
const ignoredKeys = /* @__PURE__ */ new Set();
|
|
6
5
|
const propertiesLength = properties.length;
|
|
7
|
-
let key;
|
|
8
|
-
let value;
|
|
9
6
|
outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
10
7
|
const property = properties[propertyIndex];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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;
|
|
8
|
+
const { key, required, types } = property;
|
|
9
|
+
const value = obj[key];
|
|
10
|
+
if (value === void 0 && required) return false;
|
|
11
|
+
const typesLength = types.length;
|
|
20
12
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
21
|
-
const type =
|
|
22
|
-
if (validateValue(type, property, value))
|
|
23
|
-
ignoredKeys.add(property.key.full);
|
|
24
|
-
continue outer;
|
|
25
|
-
}
|
|
13
|
+
const type = types[typeIndex];
|
|
14
|
+
if (validateValue(type, property, value)) continue outer;
|
|
26
15
|
}
|
|
27
16
|
return false;
|
|
28
17
|
}
|
|
@@ -30,8 +19,9 @@ function validateObject(obj, properties) {
|
|
|
30
19
|
}
|
|
31
20
|
function validateValue(type, property, value) {
|
|
32
21
|
switch (true) {
|
|
33
|
-
case typeof type === "function": return type(value);
|
|
34
22
|
case isSchematic(type): return type.is(value);
|
|
23
|
+
case typeof type === "function": return type(value);
|
|
24
|
+
case typeof type === "object": return validateObject(value, [type]);
|
|
35
25
|
default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
|
|
36
26
|
}
|
|
37
27
|
}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -14,7 +14,11 @@ export const MESSAGE_CONSTRUCTOR = 'Expected a constructor function';
|
|
|
14
14
|
|
|
15
15
|
export const MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one property';
|
|
16
16
|
|
|
17
|
-
export const
|
|
17
|
+
export const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED =
|
|
18
|
+
"'<>.$required' property is not allowed for schemas in $type";
|
|
19
|
+
|
|
20
|
+
export const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE =
|
|
21
|
+
"'<>.$required' property must be a boolean";
|
|
18
22
|
|
|
19
23
|
export const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
20
24
|
|
package/src/index.ts
CHANGED
package/src/models.ts
CHANGED
|
@@ -290,19 +290,17 @@ type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only]
|
|
|
290
290
|
: Value;
|
|
291
291
|
|
|
292
292
|
export type ValidatedProperty = {
|
|
293
|
-
key:
|
|
293
|
+
key: string;
|
|
294
294
|
required: boolean;
|
|
295
295
|
types: ValidatedPropertyType[];
|
|
296
296
|
validators: ValidatedPropertyValidators;
|
|
297
297
|
};
|
|
298
298
|
|
|
299
|
-
export type
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValueName;
|
|
299
|
+
export type ValidatedPropertyType =
|
|
300
|
+
| GenericCallback
|
|
301
|
+
| Schematic<unknown>
|
|
302
|
+
| ValidatedProperty
|
|
303
|
+
| ValueName;
|
|
306
304
|
|
|
307
305
|
export type ValidatedPropertyValidators = {
|
|
308
306
|
[Key in ValueName]?: Array<(value: unknown) => boolean>;
|
package/src/schematic.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
3
3
|
import {MESSAGE_SCHEMA_INVALID_TYPE, SCHEMATIC_NAME} from './constants';
|
|
4
|
-
import {isSchematic} from './
|
|
4
|
+
import {isSchematic} from './helpers';
|
|
5
5
|
import {
|
|
6
6
|
SchematicError,
|
|
7
7
|
type Infer,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import {isConstructor, isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
3
|
-
import {
|
|
3
|
+
import {join} from '@oscarpalmer/atoms/string/misc';
|
|
4
4
|
import {
|
|
5
5
|
EXPRESSION_INDEX,
|
|
6
|
-
EXPRESSION_KEY_PREFIX,
|
|
7
|
-
EXPRESSION_KEY_VALUE,
|
|
8
6
|
EXPRESSION_PROPERTY,
|
|
9
7
|
MESSAGE_SCHEMA_INVALID_EMPTY,
|
|
10
|
-
|
|
8
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED,
|
|
9
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE,
|
|
11
10
|
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE,
|
|
12
11
|
MESSAGE_VALIDATOR_INVALID_KEY,
|
|
13
12
|
MESSAGE_VALIDATOR_INVALID_TYPE,
|
|
@@ -17,11 +16,10 @@ import {
|
|
|
17
16
|
PROPERTY_VALIDATORS,
|
|
18
17
|
TEMPLATE_PATTERN,
|
|
19
18
|
TYPE_ALL,
|
|
20
|
-
TYPE_OBJECT,
|
|
21
19
|
TYPE_UNDEFINED,
|
|
22
20
|
VALIDATABLE_TYPES,
|
|
23
21
|
} from '../constants';
|
|
24
|
-
import {instanceOf, isSchematic} from '../
|
|
22
|
+
import {instanceOf, isSchematic} from '../helpers';
|
|
25
23
|
import {
|
|
26
24
|
SchematicError,
|
|
27
25
|
type ValidatedProperty,
|
|
@@ -29,25 +27,23 @@ import {
|
|
|
29
27
|
type ValidatedPropertyValidators,
|
|
30
28
|
type ValueName,
|
|
31
29
|
} from '../models';
|
|
32
|
-
import {schematic} from '../schematic';
|
|
33
30
|
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function getKeyValue(key: string): string {
|
|
41
|
-
return key.replace(EXPRESSION_KEY_VALUE, '$1');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
31
|
+
export function getProperties(
|
|
32
|
+
original: PlainObject,
|
|
33
|
+
prefix?: string,
|
|
34
|
+
fromTypes?: boolean,
|
|
35
|
+
): ValidatedProperty[] {
|
|
45
36
|
if (Object.keys(original).length === 0) {
|
|
46
37
|
throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
47
38
|
}
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
if (PROPERTY_REQUIRED in original && (fromTypes ?? false) && prefix != null) {
|
|
41
|
+
throw new SchematicError(
|
|
42
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED.replace(TEMPLATE_PATTERN, prefix),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const keys = Object.keys(original);
|
|
51
47
|
const keysLength = keys.length;
|
|
52
48
|
|
|
53
49
|
const properties: ValidatedProperty[] = [];
|
|
@@ -59,9 +55,7 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
59
55
|
continue;
|
|
60
56
|
}
|
|
61
57
|
|
|
62
|
-
const
|
|
63
|
-
const keyValue = getKeyValue(key);
|
|
64
|
-
const value = smushed[key];
|
|
58
|
+
const value = original[key];
|
|
65
59
|
|
|
66
60
|
const types: ValidatedPropertyType[] = [];
|
|
67
61
|
|
|
@@ -73,12 +67,12 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
73
67
|
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
74
68
|
|
|
75
69
|
if (PROPERTY_TYPE in value) {
|
|
76
|
-
types.push(...getTypes(key, value[PROPERTY_TYPE]));
|
|
70
|
+
types.push('object', ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
77
71
|
} else {
|
|
78
|
-
types.push(
|
|
72
|
+
types.push('object', ...getTypes(key, value, prefix));
|
|
79
73
|
}
|
|
80
74
|
} else {
|
|
81
|
-
types.push(...getTypes(key, value));
|
|
75
|
+
types.push(...getTypes(key, value, prefix));
|
|
82
76
|
}
|
|
83
77
|
|
|
84
78
|
if (!required && !types.includes(TYPE_UNDEFINED)) {
|
|
@@ -86,13 +80,9 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
properties.push({
|
|
83
|
+
key,
|
|
89
84
|
types,
|
|
90
85
|
validators,
|
|
91
|
-
key: {
|
|
92
|
-
full: key,
|
|
93
|
-
prefix: keyPrefix,
|
|
94
|
-
value: keyValue,
|
|
95
|
-
},
|
|
96
86
|
required: required && !types.includes(TYPE_UNDEFINED),
|
|
97
87
|
});
|
|
98
88
|
}
|
|
@@ -100,21 +90,26 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
100
90
|
return properties;
|
|
101
91
|
}
|
|
102
92
|
|
|
103
|
-
function getRequired(key: string,
|
|
104
|
-
if (!(PROPERTY_REQUIRED in
|
|
93
|
+
function getRequired(key: string, obj: PlainObject): boolean | undefined {
|
|
94
|
+
if (!(PROPERTY_REQUIRED in obj)) {
|
|
105
95
|
return;
|
|
106
96
|
}
|
|
107
97
|
|
|
108
|
-
if (typeof
|
|
98
|
+
if (typeof obj[PROPERTY_REQUIRED] !== 'boolean') {
|
|
109
99
|
throw new SchematicError(
|
|
110
|
-
|
|
100
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE.replace(TEMPLATE_PATTERN, key),
|
|
111
101
|
);
|
|
112
102
|
}
|
|
113
103
|
|
|
114
|
-
return
|
|
104
|
+
return obj[PROPERTY_REQUIRED];
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
function getTypes(
|
|
107
|
+
function getTypes(
|
|
108
|
+
key: string,
|
|
109
|
+
original: unknown,
|
|
110
|
+
prefix?: string,
|
|
111
|
+
fromTypes?: boolean,
|
|
112
|
+
): ValidatedPropertyType[] {
|
|
118
113
|
const array = Array.isArray(original) ? original : [original];
|
|
119
114
|
const {length} = array;
|
|
120
115
|
|
|
@@ -129,7 +124,7 @@ function getTypes(key: string, original: unknown): ValidatedPropertyType[] {
|
|
|
129
124
|
break;
|
|
130
125
|
|
|
131
126
|
case isPlainObject(value):
|
|
132
|
-
types.push(
|
|
127
|
+
types.push(...getProperties(value, join([prefix, key], '.'), fromTypes));
|
|
133
128
|
break;
|
|
134
129
|
|
|
135
130
|
case isSchematic(value):
|
|
@@ -142,13 +137,15 @@ function getTypes(key: string, original: unknown): ValidatedPropertyType[] {
|
|
|
142
137
|
|
|
143
138
|
default:
|
|
144
139
|
throw new SchematicError(
|
|
145
|
-
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key),
|
|
140
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], '.')),
|
|
146
141
|
);
|
|
147
142
|
}
|
|
148
143
|
}
|
|
149
144
|
|
|
150
145
|
if (types.length === 0) {
|
|
151
|
-
throw new SchematicError(
|
|
146
|
+
throw new SchematicError(
|
|
147
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], '.')),
|
|
148
|
+
);
|
|
152
149
|
}
|
|
153
150
|
|
|
154
151
|
return types;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {isPlainObject} from '@oscarpalmer/atoms/is';
|
|
2
|
-
import {isSchematic} from '../
|
|
2
|
+
import {isSchematic} from '../helpers';
|
|
3
3
|
import type {ValidatedProperty, ValidatedPropertyType, ValueName} from '../models';
|
|
4
4
|
|
|
5
5
|
export function validateObject(obj: unknown, properties: ValidatedProperty[]): boolean {
|
|
@@ -7,44 +7,25 @@ export function validateObject(obj: unknown, properties: ValidatedProperty[]): b
|
|
|
7
7
|
return false;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const ignoredKeys = new Set<string>();
|
|
11
10
|
const propertiesLength = properties.length;
|
|
12
11
|
|
|
13
|
-
let key!: string;
|
|
14
|
-
let value!: unknown;
|
|
15
|
-
|
|
16
12
|
outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
17
13
|
const property = properties[propertyIndex];
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
key = undefined as never;
|
|
21
|
-
|
|
22
|
-
ignoredKeys.add(property.key.full);
|
|
23
|
-
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* if (key == null || !property.key.full.startsWith(key)) {
|
|
28
|
-
value = obj[property.key.full];
|
|
29
|
-
} else {
|
|
30
|
-
value = (value as PlainObject)?.[property.key.value];
|
|
31
|
-
} */
|
|
15
|
+
const {key, required, types} = property;
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
value = obj[key];
|
|
17
|
+
const value = obj[key];
|
|
35
18
|
|
|
36
|
-
if (value === undefined &&
|
|
19
|
+
if (value === undefined && required) {
|
|
37
20
|
return false;
|
|
38
21
|
}
|
|
39
22
|
|
|
40
|
-
const typesLength =
|
|
23
|
+
const typesLength = types.length;
|
|
41
24
|
|
|
42
25
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
43
|
-
const type =
|
|
26
|
+
const type = types[typeIndex];
|
|
44
27
|
|
|
45
28
|
if (validateValue(type, property, value)) {
|
|
46
|
-
ignoredKeys.add(property.key.full);
|
|
47
|
-
|
|
48
29
|
continue outer;
|
|
49
30
|
}
|
|
50
31
|
}
|
|
@@ -61,11 +42,14 @@ function validateValue(
|
|
|
61
42
|
value: unknown,
|
|
62
43
|
): boolean {
|
|
63
44
|
switch (true) {
|
|
45
|
+
case isSchematic(type):
|
|
46
|
+
return type.is(value);
|
|
47
|
+
|
|
64
48
|
case typeof type === 'function':
|
|
65
49
|
return (type as (value: unknown) => boolean)(value);
|
|
66
50
|
|
|
67
|
-
case
|
|
68
|
-
return
|
|
51
|
+
case typeof type === 'object':
|
|
52
|
+
return validateObject(value, [type] as ValidatedProperty[]);
|
|
69
53
|
|
|
70
54
|
default:
|
|
71
55
|
return (
|
package/types/constants.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export declare const EXPRESSION_KEY_VALUE: RegExp;
|
|
|
5
5
|
export declare const EXPRESSION_PROPERTY: RegExp;
|
|
6
6
|
export declare const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
7
7
|
export declare const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_DISALLOWED = "'<>.$required' property is not allowed for schemas in $type";
|
|
9
|
+
export declare const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED_TYPE = "'<>.$required' property must be a boolean";
|
|
9
10
|
export declare const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
10
11
|
export declare const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
11
12
|
export declare const MESSAGE_VALIDATOR_INVALID_KEY = "Validator '<>' does not exist";
|
package/types/index.d.ts
CHANGED
package/types/models.d.ts
CHANGED
|
@@ -123,17 +123,12 @@ type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => voi
|
|
|
123
123
|
type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
|
|
124
124
|
type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
|
|
125
125
|
export type ValidatedProperty = {
|
|
126
|
-
key:
|
|
126
|
+
key: string;
|
|
127
127
|
required: boolean;
|
|
128
128
|
types: ValidatedPropertyType[];
|
|
129
129
|
validators: ValidatedPropertyValidators;
|
|
130
130
|
};
|
|
131
|
-
export type
|
|
132
|
-
full: string;
|
|
133
|
-
prefix: string | undefined;
|
|
134
|
-
value: string;
|
|
135
|
-
};
|
|
136
|
-
export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValueName;
|
|
131
|
+
export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValidatedProperty | ValueName;
|
|
137
132
|
export type ValidatedPropertyValidators = {
|
|
138
133
|
[Key in ValueName]?: Array<(value: unknown) => boolean>;
|
|
139
134
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { PlainObject } from '@oscarpalmer/atoms/models';
|
|
2
2
|
import { type ValidatedProperty } from '../models';
|
|
3
|
-
export declare function getProperties(original: PlainObject): ValidatedProperty[];
|
|
3
|
+
export declare function getProperties(original: PlainObject, prefix?: string, fromTypes?: boolean): ValidatedProperty[];
|
|
File without changes
|
|
File without changes
|
|
File without changes
|