@oscarpalmer/jhunal 0.12.0 → 0.14.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 +4 -4
- package/dist/index.js +1 -1
- package/dist/jhunal.full.js +35 -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 +27 -33
- package/dist/validation/value.validation.js +9 -19
- package/package.json +1 -1
- package/src/constants.ts +9 -7
- package/src/index.ts +1 -1
- package/src/models.ts +12 -10
- package/src/schematic.ts +1 -1
- package/src/validation/property.validation.ts +55 -35
- package/src/validation/value.validation.ts +11 -27
- package/types/constants.d.ts +3 -3
- package/types/index.d.ts +1 -1
- package/types/models.d.ts +7 -9
- 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
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
const ERROR_NAME = "SchematicError";
|
|
2
|
-
const EXPRESSION_INDEX = /\.\d+$/;
|
|
3
|
-
const EXPRESSION_KEY_PREFIX = /\.\w+$/;
|
|
4
|
-
const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
5
2
|
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
6
3
|
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
7
4
|
const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
5
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<key>.<property>' property is not allowed for schemas in $type";
|
|
8
6
|
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
|
|
9
7
|
const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
10
8
|
const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
@@ -16,6 +14,8 @@ const PROPERTY_TYPE = "$type";
|
|
|
16
14
|
const PROPERTY_VALIDATORS = "$validators";
|
|
17
15
|
const SCHEMATIC_NAME = "$schematic";
|
|
18
16
|
const TEMPLATE_PATTERN = "<>";
|
|
17
|
+
const TEMPLATE_PATTERN_KEY = "<key>";
|
|
18
|
+
const TEMPLATE_PATTERN_PROPERTY = "<property>";
|
|
19
19
|
const TYPE_OBJECT = "object";
|
|
20
20
|
const TYPE_UNDEFINED = "undefined";
|
|
21
21
|
const VALIDATABLE_TYPES = new Set([
|
|
@@ -34,4 +34,4 @@ const TYPE_ALL = new Set([
|
|
|
34
34
|
"null",
|
|
35
35
|
TYPE_UNDEFINED
|
|
36
36
|
]);
|
|
37
|
-
export { ERROR_NAME,
|
|
37
|
+
export { ERROR_NAME, EXPRESSION_PROPERTY, MESSAGE_CONSTRUCTOR, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, 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, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, 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`
|
|
@@ -59,12 +51,10 @@ function join(value, delimiter) {
|
|
|
59
51
|
return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
|
|
60
52
|
}
|
|
61
53
|
const ERROR_NAME = "SchematicError";
|
|
62
|
-
const EXPRESSION_INDEX = /\.\d+$/;
|
|
63
|
-
const EXPRESSION_KEY_PREFIX = /\.\w+$/;
|
|
64
|
-
const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
65
54
|
const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
66
55
|
const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
67
56
|
const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
57
|
+
const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<key>.<property>' property is not allowed for schemas in $type";
|
|
68
58
|
const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
|
|
69
59
|
const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
70
60
|
const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
@@ -76,6 +66,8 @@ const PROPERTY_TYPE = "$type";
|
|
|
76
66
|
const PROPERTY_VALIDATORS = "$validators";
|
|
77
67
|
const SCHEMATIC_NAME = "$schematic";
|
|
78
68
|
const TEMPLATE_PATTERN = "<>";
|
|
69
|
+
const TEMPLATE_PATTERN_KEY = "<key>";
|
|
70
|
+
const TEMPLATE_PATTERN_PROPERTY = "<property>";
|
|
79
71
|
const TYPE_OBJECT = "object";
|
|
80
72
|
const TYPE_UNDEFINED = "undefined";
|
|
81
73
|
const VALIDATABLE_TYPES = new Set([
|
|
@@ -109,87 +101,49 @@ var SchematicError = class extends Error {
|
|
|
109
101
|
this.name = ERROR_NAME;
|
|
110
102
|
}
|
|
111
103
|
};
|
|
112
|
-
function
|
|
113
|
-
if (
|
|
114
|
-
if (
|
|
115
|
-
|
|
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;
|
|
104
|
+
function getDisallowedProperty(obj) {
|
|
105
|
+
if (PROPERTY_REQUIRED in obj) return PROPERTY_REQUIRED;
|
|
106
|
+
if (PROPERTY_TYPE in obj) return PROPERTY_TYPE;
|
|
107
|
+
if (PROPERTY_VALIDATORS in obj) return PROPERTY_VALIDATORS;
|
|
135
108
|
}
|
|
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) {
|
|
109
|
+
function getProperties(original, prefix, fromType) {
|
|
153
110
|
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
154
|
-
|
|
155
|
-
|
|
111
|
+
if (fromType ?? false) {
|
|
112
|
+
const property = getDisallowedProperty(original);
|
|
113
|
+
if (property != null) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace(TEMPLATE_PATTERN_KEY, prefix).replace(TEMPLATE_PATTERN_PROPERTY, property));
|
|
114
|
+
}
|
|
115
|
+
const keys = Object.keys(original);
|
|
156
116
|
const keysLength = keys.length;
|
|
157
117
|
const properties = [];
|
|
158
118
|
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
159
119
|
const key = keys[keyIndex];
|
|
160
|
-
if (
|
|
161
|
-
const
|
|
162
|
-
const keyValue = getKeyValue(key);
|
|
163
|
-
const value = smushed[key];
|
|
120
|
+
if (EXPRESSION_PROPERTY.test(key)) continue;
|
|
121
|
+
const value = original[key];
|
|
164
122
|
const types = [];
|
|
165
123
|
let required = true;
|
|
166
124
|
let validators = {};
|
|
167
125
|
if (isPlainObject(value)) {
|
|
168
126
|
required = getRequired(key, value) ?? required;
|
|
169
127
|
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));
|
|
128
|
+
if (PROPERTY_TYPE in value) types.push(TYPE_OBJECT, ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
129
|
+
else types.push(TYPE_OBJECT, ...getTypes(key, value, prefix));
|
|
130
|
+
} else types.push(...getTypes(key, value, prefix));
|
|
173
131
|
if (!required && !types.includes(TYPE_UNDEFINED)) types.push(TYPE_UNDEFINED);
|
|
174
132
|
properties.push({
|
|
133
|
+
key,
|
|
175
134
|
types,
|
|
176
135
|
validators,
|
|
177
|
-
key: {
|
|
178
|
-
full: key,
|
|
179
|
-
prefix: keyPrefix,
|
|
180
|
-
value: keyValue
|
|
181
|
-
},
|
|
182
136
|
required: required && !types.includes(TYPE_UNDEFINED)
|
|
183
137
|
});
|
|
184
138
|
}
|
|
185
139
|
return properties;
|
|
186
140
|
}
|
|
187
|
-
function getRequired(key,
|
|
188
|
-
if (!(PROPERTY_REQUIRED in
|
|
189
|
-
if (typeof
|
|
190
|
-
return
|
|
141
|
+
function getRequired(key, obj) {
|
|
142
|
+
if (!(PROPERTY_REQUIRED in obj)) return;
|
|
143
|
+
if (typeof obj[PROPERTY_REQUIRED] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key));
|
|
144
|
+
return obj[PROPERTY_REQUIRED];
|
|
191
145
|
}
|
|
192
|
-
function getTypes(key, original) {
|
|
146
|
+
function getTypes(key, original, prefix, fromType) {
|
|
193
147
|
const array = Array.isArray(original) ? original : [original];
|
|
194
148
|
const { length } = array;
|
|
195
149
|
const types = [];
|
|
@@ -200,7 +154,7 @@ function getTypes(key, original) {
|
|
|
200
154
|
types.push(isConstructor(value) ? instanceOf(value) : value);
|
|
201
155
|
break;
|
|
202
156
|
case isPlainObject(value):
|
|
203
|
-
types.push(
|
|
157
|
+
types.push(...getProperties(value, join([prefix, key], "."), fromType));
|
|
204
158
|
break;
|
|
205
159
|
case isSchematic(value):
|
|
206
160
|
types.push(value);
|
|
@@ -208,10 +162,10 @@ function getTypes(key, original) {
|
|
|
208
162
|
case TYPE_ALL.has(value):
|
|
209
163
|
types.push(value);
|
|
210
164
|
break;
|
|
211
|
-
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
165
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], ".")));
|
|
212
166
|
}
|
|
213
167
|
}
|
|
214
|
-
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key));
|
|
168
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], ".")));
|
|
215
169
|
return types;
|
|
216
170
|
}
|
|
217
171
|
function getValidators(original) {
|
|
@@ -233,27 +187,16 @@ function getValidators(original) {
|
|
|
233
187
|
}
|
|
234
188
|
function validateObject(obj, properties) {
|
|
235
189
|
if (!isPlainObject(obj)) return false;
|
|
236
|
-
const ignoredKeys = /* @__PURE__ */ new Set();
|
|
237
190
|
const propertiesLength = properties.length;
|
|
238
|
-
let key;
|
|
239
|
-
let value;
|
|
240
191
|
outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
|
|
241
192
|
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;
|
|
193
|
+
const { key, required, types } = property;
|
|
194
|
+
const value = obj[key];
|
|
195
|
+
if (value === void 0 && required) return false;
|
|
196
|
+
const typesLength = types.length;
|
|
251
197
|
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
|
-
}
|
|
198
|
+
const type = types[typeIndex];
|
|
199
|
+
if (validateValue(type, property, value)) continue outer;
|
|
257
200
|
}
|
|
258
201
|
return false;
|
|
259
202
|
}
|
|
@@ -261,8 +204,9 @@ function validateObject(obj, properties) {
|
|
|
261
204
|
}
|
|
262
205
|
function validateValue(type, property, value) {
|
|
263
206
|
switch (true) {
|
|
264
|
-
case typeof type === "function": return type(value);
|
|
265
207
|
case isSchematic(type): return type.is(value);
|
|
208
|
+
case typeof type === "function": return type(value);
|
|
209
|
+
case typeof type === "object": return validateObject(value, [type]);
|
|
266
210
|
default: return validators[type](value) && (property.validators[type]?.every((validator) => validator(value)) ?? true);
|
|
267
211
|
}
|
|
268
212
|
}
|
|
@@ -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,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { instanceOf, isSchematic } from "../
|
|
1
|
+
import { EXPRESSION_PROPERTY, MESSAGE_SCHEMA_INVALID_EMPTY, MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED, 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, TEMPLATE_PATTERN_KEY, TEMPLATE_PATTERN_PROPERTY, TYPE_ALL, TYPE_OBJECT, 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
function getDisallowedProperty(obj) {
|
|
7
|
+
if ("$required" in obj) return PROPERTY_REQUIRED;
|
|
8
|
+
if ("$type" in obj) return PROPERTY_TYPE;
|
|
9
|
+
if ("$validators" in obj) return PROPERTY_VALIDATORS;
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
return key.replace(EXPRESSION_KEY_VALUE, "$1");
|
|
13
|
-
}
|
|
14
|
-
function getProperties(original) {
|
|
11
|
+
function getProperties(original, prefix, fromType) {
|
|
15
12
|
if (Object.keys(original).length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
if (fromType ?? false) {
|
|
14
|
+
const property = getDisallowedProperty(original);
|
|
15
|
+
if (property != null) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace(TEMPLATE_PATTERN_KEY, prefix).replace(TEMPLATE_PATTERN_PROPERTY, property));
|
|
16
|
+
}
|
|
17
|
+
const keys = Object.keys(original);
|
|
18
18
|
const keysLength = keys.length;
|
|
19
19
|
const properties = [];
|
|
20
20
|
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
21
21
|
const key = keys[keyIndex];
|
|
22
|
-
if (
|
|
23
|
-
const
|
|
24
|
-
const keyValue = getKeyValue(key);
|
|
25
|
-
const value = smushed[key];
|
|
22
|
+
if (EXPRESSION_PROPERTY.test(key)) continue;
|
|
23
|
+
const value = original[key];
|
|
26
24
|
const types = [];
|
|
27
25
|
let required = true;
|
|
28
26
|
let validators = {};
|
|
29
27
|
if (isPlainObject(value)) {
|
|
30
28
|
required = getRequired(key, value) ?? required;
|
|
31
29
|
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));
|
|
30
|
+
if ("$type" in value) types.push(TYPE_OBJECT, ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
31
|
+
else types.push(TYPE_OBJECT, ...getTypes(key, value, prefix));
|
|
32
|
+
} else types.push(...getTypes(key, value, prefix));
|
|
35
33
|
if (!required && !types.includes("undefined")) types.push(TYPE_UNDEFINED);
|
|
36
34
|
properties.push({
|
|
35
|
+
key,
|
|
37
36
|
types,
|
|
38
37
|
validators,
|
|
39
|
-
key: {
|
|
40
|
-
full: key,
|
|
41
|
-
prefix: keyPrefix,
|
|
42
|
-
value: keyValue
|
|
43
|
-
},
|
|
44
38
|
required: required && !types.includes("undefined")
|
|
45
39
|
});
|
|
46
40
|
}
|
|
47
41
|
return properties;
|
|
48
42
|
}
|
|
49
|
-
function getRequired(key,
|
|
50
|
-
if (!("$required" in
|
|
51
|
-
if (typeof
|
|
52
|
-
return
|
|
43
|
+
function getRequired(key, obj) {
|
|
44
|
+
if (!("$required" in obj)) return;
|
|
45
|
+
if (typeof obj["$required"] !== "boolean") throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace("<>", key));
|
|
46
|
+
return obj[PROPERTY_REQUIRED];
|
|
53
47
|
}
|
|
54
|
-
function getTypes(key, original) {
|
|
48
|
+
function getTypes(key, original, prefix, fromType) {
|
|
55
49
|
const array = Array.isArray(original) ? original : [original];
|
|
56
50
|
const { length } = array;
|
|
57
51
|
const types = [];
|
|
@@ -62,7 +56,7 @@ function getTypes(key, original) {
|
|
|
62
56
|
types.push(isConstructor(value) ? instanceOf(value) : value);
|
|
63
57
|
break;
|
|
64
58
|
case isPlainObject(value):
|
|
65
|
-
types.push(
|
|
59
|
+
types.push(...getProperties(value, join([prefix, key], "."), fromType));
|
|
66
60
|
break;
|
|
67
61
|
case isSchematic(value):
|
|
68
62
|
types.push(value);
|
|
@@ -70,10 +64,10 @@ function getTypes(key, original) {
|
|
|
70
64
|
case TYPE_ALL.has(value):
|
|
71
65
|
types.push(value);
|
|
72
66
|
break;
|
|
73
|
-
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
|
|
67
|
+
default: throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
|
|
74
68
|
}
|
|
75
69
|
}
|
|
76
|
-
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", key));
|
|
70
|
+
if (types.length === 0) throw new SchematicError(MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace("<>", join([prefix, key], ".")));
|
|
77
71
|
return types;
|
|
78
72
|
}
|
|
79
73
|
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
|
@@ -2,19 +2,17 @@ import type {ValueName} from './models';
|
|
|
2
2
|
|
|
3
3
|
export const ERROR_NAME = 'SchematicError';
|
|
4
4
|
|
|
5
|
-
export const EXPRESSION_INDEX = /\.\d+$/;
|
|
6
|
-
|
|
7
|
-
export const EXPRESSION_KEY_PREFIX = /\.\w+$/;
|
|
8
|
-
|
|
9
|
-
export const EXPRESSION_KEY_VALUE = /^.*\.(\w+)$/;
|
|
10
|
-
|
|
11
5
|
export const EXPRESSION_PROPERTY = /(^|\.)\$(required|type|validators)(\.|$)/;
|
|
12
6
|
|
|
13
7
|
export const MESSAGE_CONSTRUCTOR = 'Expected a constructor function';
|
|
14
8
|
|
|
15
9
|
export const MESSAGE_SCHEMA_INVALID_EMPTY = 'Schema must have at least one property';
|
|
16
10
|
|
|
17
|
-
export const
|
|
11
|
+
export const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED =
|
|
12
|
+
"'<key>.<property>' property is not allowed for schemas in $type";
|
|
13
|
+
|
|
14
|
+
export const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED =
|
|
15
|
+
"'<>.$required' property must be a boolean";
|
|
18
16
|
|
|
19
17
|
export const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
20
18
|
|
|
@@ -37,6 +35,10 @@ export const SCHEMATIC_NAME = '$schematic';
|
|
|
37
35
|
|
|
38
36
|
export const TEMPLATE_PATTERN = '<>';
|
|
39
37
|
|
|
38
|
+
export const TEMPLATE_PATTERN_KEY = '<key>';
|
|
39
|
+
|
|
40
|
+
export const TEMPLATE_PATTERN_PROPERTY = '<property>';
|
|
41
|
+
|
|
40
42
|
export const TYPE_OBJECT = 'object';
|
|
41
43
|
|
|
42
44
|
export const TYPE_UNDEFINED = 'undefined';
|
package/src/index.ts
CHANGED
package/src/models.ts
CHANGED
|
@@ -109,6 +109,10 @@ type OptionalKeys<Value> = {
|
|
|
109
109
|
[Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never;
|
|
110
110
|
}[keyof Value];
|
|
111
111
|
|
|
112
|
+
type PlainSchema = {
|
|
113
|
+
[key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
|
|
114
|
+
};
|
|
115
|
+
|
|
112
116
|
type PropertyValidators<Value> = {
|
|
113
117
|
[Key in ExtractValueNames<Value>]?:
|
|
114
118
|
| ((value: Values[Key]) => boolean)
|
|
@@ -135,7 +139,7 @@ interface SchemaIndex {
|
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
/**
|
|
138
|
-
* A property definition with explicit type(s)
|
|
142
|
+
* A property definition with explicit type(s), optional requirement flag, and optional validators
|
|
139
143
|
*/
|
|
140
144
|
export type SchemaProperty = {
|
|
141
145
|
$required?: boolean;
|
|
@@ -145,7 +149,7 @@ export type SchemaProperty = {
|
|
|
145
149
|
|
|
146
150
|
type SchemaPropertyType =
|
|
147
151
|
| Constructor
|
|
148
|
-
|
|
|
152
|
+
| PlainSchema
|
|
149
153
|
| Schematic<unknown>
|
|
150
154
|
| ValueName
|
|
151
155
|
| ((value: unknown) => boolean);
|
|
@@ -290,19 +294,17 @@ type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only]
|
|
|
290
294
|
: Value;
|
|
291
295
|
|
|
292
296
|
export type ValidatedProperty = {
|
|
293
|
-
key:
|
|
297
|
+
key: string;
|
|
294
298
|
required: boolean;
|
|
295
299
|
types: ValidatedPropertyType[];
|
|
296
300
|
validators: ValidatedPropertyValidators;
|
|
297
301
|
};
|
|
298
302
|
|
|
299
|
-
export type
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValueName;
|
|
303
|
+
export type ValidatedPropertyType =
|
|
304
|
+
| GenericCallback
|
|
305
|
+
| Schematic<unknown>
|
|
306
|
+
| ValidatedProperty
|
|
307
|
+
| ValueName;
|
|
306
308
|
|
|
307
309
|
export type ValidatedPropertyValidators = {
|
|
308
310
|
[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,12 +1,10 @@
|
|
|
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
|
-
EXPRESSION_INDEX,
|
|
6
|
-
EXPRESSION_KEY_PREFIX,
|
|
7
|
-
EXPRESSION_KEY_VALUE,
|
|
8
5
|
EXPRESSION_PROPERTY,
|
|
9
6
|
MESSAGE_SCHEMA_INVALID_EMPTY,
|
|
7
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED,
|
|
10
8
|
MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED,
|
|
11
9
|
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE,
|
|
12
10
|
MESSAGE_VALIDATOR_INVALID_KEY,
|
|
@@ -16,12 +14,14 @@ import {
|
|
|
16
14
|
PROPERTY_TYPE,
|
|
17
15
|
PROPERTY_VALIDATORS,
|
|
18
16
|
TEMPLATE_PATTERN,
|
|
17
|
+
TEMPLATE_PATTERN_KEY,
|
|
18
|
+
TEMPLATE_PATTERN_PROPERTY,
|
|
19
19
|
TYPE_ALL,
|
|
20
20
|
TYPE_OBJECT,
|
|
21
21
|
TYPE_UNDEFINED,
|
|
22
22
|
VALIDATABLE_TYPES,
|
|
23
23
|
} from '../constants';
|
|
24
|
-
import {instanceOf, isSchematic} from '../
|
|
24
|
+
import {instanceOf, isSchematic} from '../helpers';
|
|
25
25
|
import {
|
|
26
26
|
SchematicError,
|
|
27
27
|
type ValidatedProperty,
|
|
@@ -29,25 +29,44 @@ import {
|
|
|
29
29
|
type ValidatedPropertyValidators,
|
|
30
30
|
type ValueName,
|
|
31
31
|
} from '../models';
|
|
32
|
-
import {schematic} from '../schematic';
|
|
33
32
|
|
|
34
|
-
function
|
|
35
|
-
|
|
33
|
+
function getDisallowedProperty(obj: PlainObject): string | undefined {
|
|
34
|
+
if (PROPERTY_REQUIRED in obj) {
|
|
35
|
+
return PROPERTY_REQUIRED;
|
|
36
|
+
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
if (PROPERTY_TYPE in obj) {
|
|
39
|
+
return PROPERTY_TYPE;
|
|
40
|
+
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
if (PROPERTY_VALIDATORS in obj) {
|
|
43
|
+
return PROPERTY_VALIDATORS;
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
export function getProperties(
|
|
47
|
+
export function getProperties(
|
|
48
|
+
original: PlainObject,
|
|
49
|
+
prefix?: string,
|
|
50
|
+
fromType?: boolean,
|
|
51
|
+
): ValidatedProperty[] {
|
|
45
52
|
if (Object.keys(original).length === 0) {
|
|
46
53
|
throw new SchematicError(MESSAGE_SCHEMA_INVALID_EMPTY);
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
if (fromType ?? false) {
|
|
57
|
+
const property = getDisallowedProperty(original);
|
|
58
|
+
|
|
59
|
+
if (property != null) {
|
|
60
|
+
throw new SchematicError(
|
|
61
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED.replace(TEMPLATE_PATTERN_KEY, prefix!).replace(
|
|
62
|
+
TEMPLATE_PATTERN_PROPERTY,
|
|
63
|
+
property,
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const keys = Object.keys(original);
|
|
51
70
|
const keysLength = keys.length;
|
|
52
71
|
|
|
53
72
|
const properties: ValidatedProperty[] = [];
|
|
@@ -55,13 +74,11 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
55
74
|
for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
56
75
|
const key = keys[keyIndex];
|
|
57
76
|
|
|
58
|
-
if (
|
|
77
|
+
if (EXPRESSION_PROPERTY.test(key)) {
|
|
59
78
|
continue;
|
|
60
79
|
}
|
|
61
80
|
|
|
62
|
-
const
|
|
63
|
-
const keyValue = getKeyValue(key);
|
|
64
|
-
const value = smushed[key];
|
|
81
|
+
const value = original[key];
|
|
65
82
|
|
|
66
83
|
const types: ValidatedPropertyType[] = [];
|
|
67
84
|
|
|
@@ -73,12 +90,12 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
73
90
|
validators = getValidators(value[PROPERTY_VALIDATORS]);
|
|
74
91
|
|
|
75
92
|
if (PROPERTY_TYPE in value) {
|
|
76
|
-
types.push(...getTypes(key, value[PROPERTY_TYPE]));
|
|
93
|
+
types.push(TYPE_OBJECT, ...getTypes(key, value[PROPERTY_TYPE], prefix, true));
|
|
77
94
|
} else {
|
|
78
|
-
types.push(TYPE_OBJECT);
|
|
95
|
+
types.push(TYPE_OBJECT, ...getTypes(key, value, prefix));
|
|
79
96
|
}
|
|
80
97
|
} else {
|
|
81
|
-
types.push(...getTypes(key, value));
|
|
98
|
+
types.push(...getTypes(key, value, prefix));
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
if (!required && !types.includes(TYPE_UNDEFINED)) {
|
|
@@ -86,13 +103,9 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
properties.push({
|
|
106
|
+
key,
|
|
89
107
|
types,
|
|
90
108
|
validators,
|
|
91
|
-
key: {
|
|
92
|
-
full: key,
|
|
93
|
-
prefix: keyPrefix,
|
|
94
|
-
value: keyValue,
|
|
95
|
-
},
|
|
96
109
|
required: required && !types.includes(TYPE_UNDEFINED),
|
|
97
110
|
});
|
|
98
111
|
}
|
|
@@ -100,21 +113,26 @@ export function getProperties(original: PlainObject): ValidatedProperty[] {
|
|
|
100
113
|
return properties;
|
|
101
114
|
}
|
|
102
115
|
|
|
103
|
-
function getRequired(key: string,
|
|
104
|
-
if (!(PROPERTY_REQUIRED in
|
|
116
|
+
function getRequired(key: string, obj: PlainObject): boolean | undefined {
|
|
117
|
+
if (!(PROPERTY_REQUIRED in obj)) {
|
|
105
118
|
return;
|
|
106
119
|
}
|
|
107
120
|
|
|
108
|
-
if (typeof
|
|
121
|
+
if (typeof obj[PROPERTY_REQUIRED] !== 'boolean') {
|
|
109
122
|
throw new SchematicError(
|
|
110
123
|
MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED.replace(TEMPLATE_PATTERN, key),
|
|
111
124
|
);
|
|
112
125
|
}
|
|
113
126
|
|
|
114
|
-
return
|
|
127
|
+
return obj[PROPERTY_REQUIRED];
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
function getTypes(
|
|
130
|
+
function getTypes(
|
|
131
|
+
key: string,
|
|
132
|
+
original: unknown,
|
|
133
|
+
prefix?: string,
|
|
134
|
+
fromType?: boolean,
|
|
135
|
+
): ValidatedPropertyType[] {
|
|
118
136
|
const array = Array.isArray(original) ? original : [original];
|
|
119
137
|
const {length} = array;
|
|
120
138
|
|
|
@@ -129,7 +147,7 @@ function getTypes(key: string, original: unknown): ValidatedPropertyType[] {
|
|
|
129
147
|
break;
|
|
130
148
|
|
|
131
149
|
case isPlainObject(value):
|
|
132
|
-
types.push(
|
|
150
|
+
types.push(...getProperties(value, join([prefix, key], '.'), fromType));
|
|
133
151
|
break;
|
|
134
152
|
|
|
135
153
|
case isSchematic(value):
|
|
@@ -142,13 +160,15 @@ function getTypes(key: string, original: unknown): ValidatedPropertyType[] {
|
|
|
142
160
|
|
|
143
161
|
default:
|
|
144
162
|
throw new SchematicError(
|
|
145
|
-
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, key),
|
|
163
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], '.')),
|
|
146
164
|
);
|
|
147
165
|
}
|
|
148
166
|
}
|
|
149
167
|
|
|
150
168
|
if (types.length === 0) {
|
|
151
|
-
throw new SchematicError(
|
|
169
|
+
throw new SchematicError(
|
|
170
|
+
MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE.replace(TEMPLATE_PATTERN, join([prefix, key], '.')),
|
|
171
|
+
);
|
|
152
172
|
}
|
|
153
173
|
|
|
154
174
|
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
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
export declare const ERROR_NAME = "SchematicError";
|
|
2
|
-
export declare const EXPRESSION_INDEX: RegExp;
|
|
3
|
-
export declare const EXPRESSION_KEY_PREFIX: RegExp;
|
|
4
|
-
export declare const EXPRESSION_KEY_VALUE: RegExp;
|
|
5
2
|
export declare const EXPRESSION_PROPERTY: RegExp;
|
|
6
3
|
export declare const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
|
|
7
4
|
export declare const MESSAGE_SCHEMA_INVALID_EMPTY = "Schema must have at least one property";
|
|
5
|
+
export declare const MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED = "'<key>.<property>' property is not allowed for schemas in $type";
|
|
8
6
|
export declare const MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED = "'<>.$required' property must be a boolean";
|
|
9
7
|
export declare const MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE = "'<>' property must be of a valid type";
|
|
10
8
|
export declare const MESSAGE_SCHEMA_INVALID_TYPE = "Schema must be an object";
|
|
@@ -16,6 +14,8 @@ export declare const PROPERTY_TYPE = "$type";
|
|
|
16
14
|
export declare const PROPERTY_VALIDATORS = "$validators";
|
|
17
15
|
export declare const SCHEMATIC_NAME = "$schematic";
|
|
18
16
|
export declare const TEMPLATE_PATTERN = "<>";
|
|
17
|
+
export declare const TEMPLATE_PATTERN_KEY = "<key>";
|
|
18
|
+
export declare const TEMPLATE_PATTERN_PROPERTY = "<property>";
|
|
19
19
|
export declare const TYPE_OBJECT = "object";
|
|
20
20
|
export declare const TYPE_UNDEFINED = "undefined";
|
|
21
21
|
export declare const VALIDATABLE_TYPES: Set<keyof import("./models").Values>;
|
package/types/index.d.ts
CHANGED
package/types/models.d.ts
CHANGED
|
@@ -42,6 +42,9 @@ export type NestedSchema = {
|
|
|
42
42
|
type OptionalKeys<Value> = {
|
|
43
43
|
[Key in keyof Value]-?: {} extends Pick<Value, Key> ? Key : never;
|
|
44
44
|
}[keyof Value];
|
|
45
|
+
type PlainSchema = {
|
|
46
|
+
[key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
|
|
47
|
+
};
|
|
45
48
|
type PropertyValidators<Value> = {
|
|
46
49
|
[Key in ExtractValueNames<Value>]?: ((value: Values[Key]) => boolean) | Array<(value: Values[Key]) => boolean>;
|
|
47
50
|
};
|
|
@@ -55,14 +58,14 @@ interface SchemaIndex {
|
|
|
55
58
|
[key: string]: NestedSchema | SchemaEntry | SchemaEntry[];
|
|
56
59
|
}
|
|
57
60
|
/**
|
|
58
|
-
* A property definition with explicit type(s)
|
|
61
|
+
* A property definition with explicit type(s), optional requirement flag, and optional validators
|
|
59
62
|
*/
|
|
60
63
|
export type SchemaProperty = {
|
|
61
64
|
$required?: boolean;
|
|
62
65
|
$type: SchemaPropertyType | SchemaPropertyType[];
|
|
63
66
|
$validators?: PropertyValidators<SchemaPropertyType | SchemaPropertyType[]>;
|
|
64
67
|
};
|
|
65
|
-
type SchemaPropertyType = Constructor |
|
|
68
|
+
type SchemaPropertyType = Constructor | PlainSchema | Schematic<unknown> | ValueName | ((value: unknown) => boolean);
|
|
66
69
|
export declare class SchematicError extends Error {
|
|
67
70
|
constructor(message: string);
|
|
68
71
|
}
|
|
@@ -123,17 +126,12 @@ type UnionToIntersection<Value> = (Value extends unknown ? (value: Value) => voi
|
|
|
123
126
|
type UnionToTuple<Value, Items extends unknown[] = []> = [Value] extends [never] ? Items : UnionToTuple<Exclude<Value, LastOfUnion<Value>>, [LastOfUnion<Value>, ...Items]>;
|
|
124
127
|
type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only] ? Only : Value['length'] extends 1 | 2 | 3 | 4 | 5 ? TuplePermutations<Value> : Value;
|
|
125
128
|
export type ValidatedProperty = {
|
|
126
|
-
key:
|
|
129
|
+
key: string;
|
|
127
130
|
required: boolean;
|
|
128
131
|
types: ValidatedPropertyType[];
|
|
129
132
|
validators: ValidatedPropertyValidators;
|
|
130
133
|
};
|
|
131
|
-
export type
|
|
132
|
-
full: string;
|
|
133
|
-
prefix: string | undefined;
|
|
134
|
-
value: string;
|
|
135
|
-
};
|
|
136
|
-
export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValueName;
|
|
134
|
+
export type ValidatedPropertyType = GenericCallback | Schematic<unknown> | ValidatedProperty | ValueName;
|
|
137
135
|
export type ValidatedPropertyValidators = {
|
|
138
136
|
[Key in ValueName]?: Array<(value: unknown) => boolean>;
|
|
139
137
|
};
|
|
@@ -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, fromType?: boolean): ValidatedProperty[];
|
|
File without changes
|
|
File without changes
|
|
File without changes
|