@oscarpalmer/jhunal 0.6.0 → 0.7.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 +21 -0
- package/dist/is.js +2 -1
- package/dist/jhunal.full.js +60 -37
- package/dist/schematic.js +2 -1
- package/dist/validation/schema.validation.js +13 -25
- package/dist/validation/value.validation.js +12 -1
- package/package.json +4 -4
- package/src/constants.ts +27 -0
- package/src/index.ts +1 -1
- package/src/is.ts +3 -3
- package/src/schematic.ts +5 -4
- package/src/validation/schema.validation.ts +22 -34
- package/src/validation/value.validation.ts +23 -3
- package/types/constants.d.ts +7 -0
- package/types/index.d.ts +1 -1
- package/types/schematic.d.ts +2 -2
- package/types/validation/schema.validation.d.ts +1 -1
- package/types/validation/value.validation.d.ts +1 -1
- /package/dist/{model.js → models.js} +0 -0
- /package/src/{model.ts → models.ts} +0 -0
- /package/types/{model.d.ts → models.d.ts} +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const PROPERTY_REQUIRED = "$required";
|
|
2
|
+
const PROPERTY_TYPE = "$type";
|
|
3
|
+
const SCHEMATIC_NAME = "$schematic";
|
|
4
|
+
const TYPE_OBJECT = "object";
|
|
5
|
+
const TYPE_UNDEFINED = "undefined";
|
|
6
|
+
const TYPE_ALL = new Set([
|
|
7
|
+
"array",
|
|
8
|
+
"bigint",
|
|
9
|
+
"boolean",
|
|
10
|
+
"date",
|
|
11
|
+
"date-like",
|
|
12
|
+
"function",
|
|
13
|
+
"null",
|
|
14
|
+
"number",
|
|
15
|
+
"numerical",
|
|
16
|
+
"string",
|
|
17
|
+
"symbol",
|
|
18
|
+
TYPE_OBJECT,
|
|
19
|
+
TYPE_UNDEFINED
|
|
20
|
+
]);
|
|
21
|
+
export { PROPERTY_REQUIRED, PROPERTY_TYPE, SCHEMATIC_NAME, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED };
|
package/dist/is.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { SCHEMATIC_NAME } from "./constants.js";
|
|
1
2
|
function isDateLike(value) {
|
|
2
3
|
if (value instanceof Date) return true;
|
|
3
4
|
if (typeof value === "number") return value >= -864e13 && value <= 864e13;
|
|
4
5
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
5
6
|
}
|
|
6
7
|
function isSchematic(value) {
|
|
7
|
-
return typeof value === "object" && value !== null && "$schematic" in value && value
|
|
8
|
+
return typeof value === "object" && value !== null && "$schematic" in value && value["$schematic"] === true;
|
|
8
9
|
}
|
|
9
10
|
export { isDateLike, isSchematic };
|
package/dist/jhunal.full.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
|
+
const PROPERTY_REQUIRED = "$required";
|
|
2
|
+
const PROPERTY_TYPE = "$type";
|
|
3
|
+
const SCHEMATIC_NAME = "$schematic";
|
|
4
|
+
const TYPE_OBJECT = "object";
|
|
5
|
+
const TYPE_UNDEFINED = "undefined";
|
|
6
|
+
const TYPE_ALL = new Set([
|
|
7
|
+
"array",
|
|
8
|
+
"bigint",
|
|
9
|
+
"boolean",
|
|
10
|
+
"date",
|
|
11
|
+
"date-like",
|
|
12
|
+
"function",
|
|
13
|
+
"null",
|
|
14
|
+
"number",
|
|
15
|
+
"numerical",
|
|
16
|
+
"string",
|
|
17
|
+
"symbol",
|
|
18
|
+
TYPE_OBJECT,
|
|
19
|
+
TYPE_UNDEFINED
|
|
20
|
+
]);
|
|
1
21
|
function compact(array, strict) {
|
|
2
22
|
if (!Array.isArray(array)) return [];
|
|
23
|
+
if (strict === true) return array.filter(Boolean);
|
|
3
24
|
const { length } = array;
|
|
4
|
-
const isStrict = strict ?? false;
|
|
5
25
|
const compacted = [];
|
|
6
26
|
for (let index = 0; index < length; index += 1) {
|
|
7
27
|
const item = array[index];
|
|
8
|
-
if (
|
|
28
|
+
if (item != null) compacted.push(item);
|
|
9
29
|
}
|
|
10
30
|
return compacted;
|
|
11
31
|
}
|
|
@@ -51,11 +71,17 @@ function flattenObject(value, depth, smushed, prefix) {
|
|
|
51
71
|
for (let index = 0; index < length; index += 1) {
|
|
52
72
|
const key = keys[index];
|
|
53
73
|
const val = value[key];
|
|
54
|
-
if (isArrayOrPlainObject(val))
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
74
|
+
if (isArrayOrPlainObject(val)) {
|
|
75
|
+
const prefixedKey = join([prefix, key], ".");
|
|
76
|
+
flattened[prefixedKey] = Array.isArray(val) ? [...val] : { ...val };
|
|
77
|
+
const nested = flattenObject(val, depth + 1, smushed, prefixedKey);
|
|
78
|
+
const nestedKeys = Object.keys(nested);
|
|
79
|
+
const nestedLength = nestedKeys.length;
|
|
80
|
+
for (let nestedIndex = 0; nestedIndex < nestedLength; nestedIndex += 1) {
|
|
81
|
+
const nestedKey = nestedKeys[nestedIndex];
|
|
82
|
+
flattened[nestedKey] = nested[nestedKey];
|
|
83
|
+
}
|
|
84
|
+
} else flattened[join([prefix, key], ".")] = val;
|
|
59
85
|
}
|
|
60
86
|
smushed.set(value, flattened);
|
|
61
87
|
return flattened;
|
|
@@ -70,11 +96,13 @@ function isDateLike(value) {
|
|
|
70
96
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
71
97
|
}
|
|
72
98
|
function isSchematic(value) {
|
|
73
|
-
return typeof value === "object" && value !== null &&
|
|
99
|
+
return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
|
|
74
100
|
}
|
|
75
101
|
function addPropertyType(to, key, values, required) {
|
|
76
|
-
if (to.keys.set.has(key))
|
|
77
|
-
|
|
102
|
+
if (to.keys.set.has(key)) {
|
|
103
|
+
const property = to.properties[key];
|
|
104
|
+
for (const type of values) if (!property.types.includes(type)) property.types.push(type);
|
|
105
|
+
} else {
|
|
78
106
|
to.keys.array.push(key);
|
|
79
107
|
to.keys.set.add(key);
|
|
80
108
|
to.properties[key] = {
|
|
@@ -82,7 +110,7 @@ function addPropertyType(to, key, values, required) {
|
|
|
82
110
|
types: values
|
|
83
111
|
};
|
|
84
112
|
}
|
|
85
|
-
if (!required && !to.properties[key].types.includes(
|
|
113
|
+
if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) to.properties[key].types.push(TYPE_UNDEFINED);
|
|
86
114
|
}
|
|
87
115
|
function getTypes(value, validated, prefix) {
|
|
88
116
|
const propertyTypes = [];
|
|
@@ -90,17 +118,17 @@ function getTypes(value, validated, prefix) {
|
|
|
90
118
|
const { length } = values;
|
|
91
119
|
for (let index = 0; index < length; index += 1) {
|
|
92
120
|
const type = values[index];
|
|
93
|
-
if (isSchematic(type) || typeof type === "string" &&
|
|
121
|
+
if (isSchematic(type) || typeof type === "string" && TYPE_ALL.has(type)) {
|
|
94
122
|
propertyTypes.push(type);
|
|
95
123
|
continue;
|
|
96
124
|
}
|
|
97
125
|
if (typeof type !== "object" || type === null) continue;
|
|
98
|
-
if (
|
|
99
|
-
propertyTypes.push(...getTypes(type
|
|
126
|
+
if (PROPERTY_TYPE in type) {
|
|
127
|
+
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
100
128
|
continue;
|
|
101
129
|
}
|
|
102
|
-
addPropertyType(validated, prefix, [
|
|
103
|
-
propertyTypes.push(
|
|
130
|
+
addPropertyType(validated, prefix, [TYPE_OBJECT], type[PROPERTY_REQUIRED] !== false);
|
|
131
|
+
propertyTypes.push(TYPE_OBJECT);
|
|
104
132
|
getValidatedSchema(type, validated, prefix);
|
|
105
133
|
}
|
|
106
134
|
return propertyTypes;
|
|
@@ -119,10 +147,10 @@ function getValidatedSchema(schema, validated, prefix) {
|
|
|
119
147
|
if (/\.\$(required|type)(\.|$)/.test(key)) continue;
|
|
120
148
|
if (/\d+/.test(key) && arrayKeys.has(key.replace(/\.\d+$/, ""))) continue;
|
|
121
149
|
let required = true;
|
|
122
|
-
if (typeof value === "object" && value !== null &&
|
|
150
|
+
if (typeof value === "object" && value !== null && PROPERTY_REQUIRED in value) required = typeof value[PROPERTY_REQUIRED] === "boolean" ? value[PROPERTY_REQUIRED] : true;
|
|
123
151
|
const prefixedKey = `${prefix}${key}`;
|
|
124
|
-
const types
|
|
125
|
-
if (types
|
|
152
|
+
const types = getTypes(value, validated, prefixedKey);
|
|
153
|
+
if (types.length > 0) addPropertyType(validated, prefixedKey, types, required);
|
|
126
154
|
}
|
|
127
155
|
if (noPrefix) validated.keys.array.sort();
|
|
128
156
|
return validated;
|
|
@@ -137,21 +165,6 @@ function validateSchema(schema) {
|
|
|
137
165
|
};
|
|
138
166
|
return typeof schema === "object" && schema !== null ? getValidatedSchema(schema, validated) : validated;
|
|
139
167
|
}
|
|
140
|
-
const types = new Set([
|
|
141
|
-
"array",
|
|
142
|
-
"bigint",
|
|
143
|
-
"boolean",
|
|
144
|
-
"date",
|
|
145
|
-
"date-like",
|
|
146
|
-
"function",
|
|
147
|
-
"null",
|
|
148
|
-
"number",
|
|
149
|
-
"numerical",
|
|
150
|
-
"object",
|
|
151
|
-
"string",
|
|
152
|
-
"symbol",
|
|
153
|
-
"undefined"
|
|
154
|
-
]);
|
|
155
168
|
function validateType(type, value) {
|
|
156
169
|
return typeof type === "string" ? validators[type](value) : type.is(value);
|
|
157
170
|
}
|
|
@@ -159,22 +172,32 @@ function validateValue(validated, obj) {
|
|
|
159
172
|
if (typeof obj !== "object" || obj === null) return false;
|
|
160
173
|
const { keys, properties } = validated;
|
|
161
174
|
const keysLength = keys.array.length;
|
|
175
|
+
const ignore = /* @__PURE__ */ new Set();
|
|
162
176
|
const smushed = smush(obj);
|
|
163
177
|
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
164
178
|
const key = keys.array[keyIndex];
|
|
179
|
+
const prefix = key.replace(EXPRESSION_SUFFIX, "");
|
|
180
|
+
if (ignore.has(prefix)) continue;
|
|
165
181
|
const property = properties[key];
|
|
166
182
|
const value = smushed[key];
|
|
167
|
-
if (value === void 0 && property.required && !property.types.includes(
|
|
183
|
+
if (value === void 0 && property.required && !property.types.includes(TYPE_UNDEFINED)) return false;
|
|
168
184
|
const typesLength = property.types.length;
|
|
169
185
|
if (typesLength === 1) {
|
|
170
186
|
if (!validateType(property.types[0], value)) return false;
|
|
171
187
|
continue;
|
|
172
188
|
}
|
|
173
|
-
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1)
|
|
189
|
+
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
190
|
+
const type = property.types[typeIndex];
|
|
191
|
+
if (validateType(type, value)) {
|
|
192
|
+
if (type !== TYPE_OBJECT) ignore.add(key);
|
|
193
|
+
continue outer;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
174
196
|
return false;
|
|
175
197
|
}
|
|
176
198
|
return true;
|
|
177
199
|
}
|
|
200
|
+
const EXPRESSION_SUFFIX = /\.\w+$/;
|
|
178
201
|
const validators = {
|
|
179
202
|
array: Array.isArray,
|
|
180
203
|
bigint: (value) => typeof value === "bigint",
|
|
@@ -197,7 +220,7 @@ var Schematic = class {
|
|
|
197
220
|
return this.#validatable;
|
|
198
221
|
}
|
|
199
222
|
constructor(schema) {
|
|
200
|
-
Object.defineProperty(this,
|
|
223
|
+
Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
|
|
201
224
|
this.#schema = validateSchema(schema);
|
|
202
225
|
this.#validatable = this.#schema.keys.array.length > 0;
|
|
203
226
|
}
|
package/dist/schematic.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SCHEMATIC_NAME } from "./constants.js";
|
|
1
2
|
import { validateSchema } from "./validation/schema.validation.js";
|
|
2
3
|
import { validateValue } from "./validation/value.validation.js";
|
|
3
4
|
var Schematic = class {
|
|
@@ -7,7 +8,7 @@ var Schematic = class {
|
|
|
7
8
|
return this.#validatable;
|
|
8
9
|
}
|
|
9
10
|
constructor(schema) {
|
|
10
|
-
Object.defineProperty(this,
|
|
11
|
+
Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
|
|
11
12
|
this.#schema = validateSchema(schema);
|
|
12
13
|
this.#validatable = this.#schema.keys.array.length > 0;
|
|
13
14
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { PROPERTY_REQUIRED, PROPERTY_TYPE, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED } from "../constants.js";
|
|
1
2
|
import { isSchematic } from "../is.js";
|
|
2
3
|
import { smush } from "@oscarpalmer/atoms/value";
|
|
3
4
|
function addPropertyType(to, key, values, required) {
|
|
4
|
-
if (to.keys.set.has(key))
|
|
5
|
-
|
|
5
|
+
if (to.keys.set.has(key)) {
|
|
6
|
+
const property = to.properties[key];
|
|
7
|
+
for (const type of values) if (!property.types.includes(type)) property.types.push(type);
|
|
8
|
+
} else {
|
|
6
9
|
to.keys.array.push(key);
|
|
7
10
|
to.keys.set.add(key);
|
|
8
11
|
to.properties[key] = {
|
|
@@ -10,7 +13,7 @@ function addPropertyType(to, key, values, required) {
|
|
|
10
13
|
types: values
|
|
11
14
|
};
|
|
12
15
|
}
|
|
13
|
-
if (!required && !to.properties[key].types.includes("undefined")) to.properties[key].types.push(
|
|
16
|
+
if (!required && !to.properties[key].types.includes("undefined")) to.properties[key].types.push(TYPE_UNDEFINED);
|
|
14
17
|
}
|
|
15
18
|
function getTypes(value, validated, prefix) {
|
|
16
19
|
const propertyTypes = [];
|
|
@@ -18,17 +21,17 @@ function getTypes(value, validated, prefix) {
|
|
|
18
21
|
const { length } = values;
|
|
19
22
|
for (let index = 0; index < length; index += 1) {
|
|
20
23
|
const type = values[index];
|
|
21
|
-
if (isSchematic(type) || typeof type === "string" &&
|
|
24
|
+
if (isSchematic(type) || typeof type === "string" && TYPE_ALL.has(type)) {
|
|
22
25
|
propertyTypes.push(type);
|
|
23
26
|
continue;
|
|
24
27
|
}
|
|
25
28
|
if (typeof type !== "object" || type === null) continue;
|
|
26
29
|
if ("$type" in type) {
|
|
27
|
-
propertyTypes.push(...getTypes(type
|
|
30
|
+
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
28
31
|
continue;
|
|
29
32
|
}
|
|
30
|
-
addPropertyType(validated, prefix, [
|
|
31
|
-
propertyTypes.push(
|
|
33
|
+
addPropertyType(validated, prefix, [TYPE_OBJECT], type[PROPERTY_REQUIRED] !== false);
|
|
34
|
+
propertyTypes.push(TYPE_OBJECT);
|
|
32
35
|
getValidatedSchema(type, validated, prefix);
|
|
33
36
|
}
|
|
34
37
|
return propertyTypes;
|
|
@@ -47,10 +50,10 @@ function getValidatedSchema(schema, validated, prefix) {
|
|
|
47
50
|
if (/\.\$(required|type)(\.|$)/.test(key)) continue;
|
|
48
51
|
if (/\d+/.test(key) && arrayKeys.has(key.replace(/\.\d+$/, ""))) continue;
|
|
49
52
|
let required = true;
|
|
50
|
-
if (typeof value === "object" && value !== null && "$required" in value) required = typeof value
|
|
53
|
+
if (typeof value === "object" && value !== null && "$required" in value) required = typeof value["$required"] === "boolean" ? value[PROPERTY_REQUIRED] : true;
|
|
51
54
|
const prefixedKey = `${prefix}${key}`;
|
|
52
|
-
const types
|
|
53
|
-
if (types
|
|
55
|
+
const types = getTypes(value, validated, prefixedKey);
|
|
56
|
+
if (types.length > 0) addPropertyType(validated, prefixedKey, types, required);
|
|
54
57
|
}
|
|
55
58
|
if (noPrefix) validated.keys.array.sort();
|
|
56
59
|
return validated;
|
|
@@ -65,19 +68,4 @@ function validateSchema(schema) {
|
|
|
65
68
|
};
|
|
66
69
|
return typeof schema === "object" && schema !== null ? getValidatedSchema(schema, validated) : validated;
|
|
67
70
|
}
|
|
68
|
-
var types = new Set([
|
|
69
|
-
"array",
|
|
70
|
-
"bigint",
|
|
71
|
-
"boolean",
|
|
72
|
-
"date",
|
|
73
|
-
"date-like",
|
|
74
|
-
"function",
|
|
75
|
-
"null",
|
|
76
|
-
"number",
|
|
77
|
-
"numerical",
|
|
78
|
-
"object",
|
|
79
|
-
"string",
|
|
80
|
-
"symbol",
|
|
81
|
-
"undefined"
|
|
82
|
-
]);
|
|
83
71
|
export { validateSchema };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TYPE_OBJECT, TYPE_UNDEFINED } from "../constants.js";
|
|
1
2
|
import { isDateLike } from "../is.js";
|
|
2
3
|
import { smush } from "@oscarpalmer/atoms/value";
|
|
3
4
|
function validateType(type, value) {
|
|
@@ -7,9 +8,12 @@ function validateValue(validated, obj) {
|
|
|
7
8
|
if (typeof obj !== "object" || obj === null) return false;
|
|
8
9
|
const { keys, properties } = validated;
|
|
9
10
|
const keysLength = keys.array.length;
|
|
11
|
+
const ignore = /* @__PURE__ */ new Set();
|
|
10
12
|
const smushed = smush(obj);
|
|
11
13
|
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
12
14
|
const key = keys.array[keyIndex];
|
|
15
|
+
const prefix = key.replace(EXPRESSION_SUFFIX, "");
|
|
16
|
+
if (ignore.has(prefix)) continue;
|
|
13
17
|
const property = properties[key];
|
|
14
18
|
const value = smushed[key];
|
|
15
19
|
if (value === void 0 && property.required && !property.types.includes("undefined")) return false;
|
|
@@ -18,11 +22,18 @@ function validateValue(validated, obj) {
|
|
|
18
22
|
if (!validateType(property.types[0], value)) return false;
|
|
19
23
|
continue;
|
|
20
24
|
}
|
|
21
|
-
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1)
|
|
25
|
+
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
26
|
+
const type = property.types[typeIndex];
|
|
27
|
+
if (validateType(type, value)) {
|
|
28
|
+
if (type !== "object") ignore.add(key);
|
|
29
|
+
continue outer;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
22
32
|
return false;
|
|
23
33
|
}
|
|
24
34
|
return true;
|
|
25
35
|
}
|
|
36
|
+
var EXPRESSION_SUFFIX = /\.\w+$/;
|
|
26
37
|
var validators = {
|
|
27
38
|
array: Array.isArray,
|
|
28
39
|
bigint: (value) => typeof value === "bigint",
|
package/package.json
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
"url": "https://oscarpalmer.se"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@oscarpalmer/atoms": "^0.
|
|
7
|
+
"@oscarpalmer/atoms": "^0.123"
|
|
8
8
|
},
|
|
9
9
|
"description": "Flies free beneath the glistening moons…",
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@types/node": "^25",
|
|
12
12
|
"@vitest/coverage-istanbul": "^4",
|
|
13
13
|
"jsdom": "^27.4",
|
|
14
|
-
"oxfmt": "^0.
|
|
15
|
-
"oxlint": "^1.
|
|
14
|
+
"oxfmt": "^0.21",
|
|
15
|
+
"oxlint": "^1.36",
|
|
16
16
|
"rolldown": "1.0.0-beta.57",
|
|
17
17
|
"tslib": "^2.8",
|
|
18
18
|
"typescript": "^5.9",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
},
|
|
46
46
|
"type": "module",
|
|
47
47
|
"types": "./types/index.d.ts",
|
|
48
|
-
"version": "0.
|
|
48
|
+
"version": "0.7.0"
|
|
49
49
|
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {Values} from './models';
|
|
2
|
+
|
|
3
|
+
export const PROPERTY_REQUIRED = '$required';
|
|
4
|
+
|
|
5
|
+
export const PROPERTY_TYPE = '$type';
|
|
6
|
+
|
|
7
|
+
export const SCHEMATIC_NAME = '$schematic';
|
|
8
|
+
|
|
9
|
+
export const TYPE_OBJECT = 'object';
|
|
10
|
+
|
|
11
|
+
export const TYPE_UNDEFINED = 'undefined';
|
|
12
|
+
|
|
13
|
+
export const TYPE_ALL = new Set<keyof Values>([
|
|
14
|
+
'array',
|
|
15
|
+
'bigint',
|
|
16
|
+
'boolean',
|
|
17
|
+
'date',
|
|
18
|
+
'date-like',
|
|
19
|
+
'function',
|
|
20
|
+
'null',
|
|
21
|
+
'number',
|
|
22
|
+
'numerical',
|
|
23
|
+
'string',
|
|
24
|
+
'symbol',
|
|
25
|
+
TYPE_OBJECT,
|
|
26
|
+
TYPE_UNDEFINED,
|
|
27
|
+
]);
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type {Schema, TypedSchema} from './
|
|
1
|
+
export type {Schema, TypedSchema} from './models';
|
|
2
2
|
export {schematic, type Schematic} from './schematic';
|
package/src/is.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import {SCHEMATIC_NAME} from './constants';
|
|
2
2
|
import type {Schematic} from './schematic';
|
|
3
3
|
|
|
4
4
|
export function isDateLike(value: unknown): value is Date {
|
|
@@ -17,7 +17,7 @@ export function isSchematic(value: unknown): value is Schematic<never> {
|
|
|
17
17
|
return (
|
|
18
18
|
typeof value === 'object' &&
|
|
19
19
|
value !== null &&
|
|
20
|
-
|
|
21
|
-
value
|
|
20
|
+
SCHEMATIC_NAME in value &&
|
|
21
|
+
value[SCHEMATIC_NAME] === true
|
|
22
22
|
);
|
|
23
23
|
}
|
package/src/schematic.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {PlainObject} from '@oscarpalmer/atoms';
|
|
2
|
-
import
|
|
1
|
+
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
|
+
import {SCHEMATIC_NAME} from './constants';
|
|
3
|
+
import type {Infer, Schema, TypedSchema, ValidatedSchema} from './models';
|
|
3
4
|
import {validateSchema} from './validation/schema.validation';
|
|
4
5
|
import {validateValue} from './validation/value.validation';
|
|
5
6
|
|
|
@@ -17,11 +18,11 @@ export class Schematic<Model> {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
constructor(schema: Model) {
|
|
20
|
-
Object.defineProperty(this,
|
|
21
|
+
Object.defineProperty(this, SCHEMATIC_NAME, {
|
|
21
22
|
value: true,
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
this.#schema = validateSchema(schema
|
|
25
|
+
this.#schema = validateSchema(schema);
|
|
25
26
|
|
|
26
27
|
this.#validatable = this.#schema.keys.array.length > 0;
|
|
27
28
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {smush} from '@oscarpalmer/atoms/value';
|
|
3
|
+
import {
|
|
4
|
+
PROPERTY_REQUIRED,
|
|
5
|
+
PROPERTY_TYPE,
|
|
6
|
+
TYPE_ALL,
|
|
7
|
+
TYPE_OBJECT,
|
|
8
|
+
TYPE_UNDEFINED,
|
|
9
|
+
} from '../constants';
|
|
3
10
|
import {isSchematic} from '../is';
|
|
4
|
-
import type {Schema, ValidatedPropertyType, ValidatedSchema
|
|
11
|
+
import type {Schema, ValidatedPropertyType, ValidatedSchema} from '../models';
|
|
5
12
|
|
|
6
13
|
function addPropertyType(
|
|
7
14
|
to: ValidatedSchema,
|
|
@@ -12,7 +19,11 @@ function addPropertyType(
|
|
|
12
19
|
if (to.keys.set.has(key)) {
|
|
13
20
|
const property = to.properties[key];
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
for (const type of values) {
|
|
23
|
+
if (!property.types.includes(type)) {
|
|
24
|
+
property.types.push(type);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
16
27
|
} else {
|
|
17
28
|
to.keys.array.push(key);
|
|
18
29
|
to.keys.set.add(key);
|
|
@@ -23,8 +34,8 @@ function addPropertyType(
|
|
|
23
34
|
};
|
|
24
35
|
}
|
|
25
36
|
|
|
26
|
-
if (!required && !to.properties[key].types.includes(
|
|
27
|
-
to.properties[key].types.push(
|
|
37
|
+
if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) {
|
|
38
|
+
to.properties[key].types.push(TYPE_UNDEFINED);
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
41
|
|
|
@@ -41,7 +52,7 @@ function getTypes(
|
|
|
41
52
|
for (let index = 0; index < length; index += 1) {
|
|
42
53
|
const type = values[index];
|
|
43
54
|
|
|
44
|
-
if (isSchematic(type) || (typeof type === 'string' &&
|
|
55
|
+
if (isSchematic(type) || (typeof type === 'string' && TYPE_ALL.has(type as never))) {
|
|
45
56
|
propertyTypes.push(type as never);
|
|
46
57
|
|
|
47
58
|
continue;
|
|
@@ -51,20 +62,15 @@ function getTypes(
|
|
|
51
62
|
continue;
|
|
52
63
|
}
|
|
53
64
|
|
|
54
|
-
if (
|
|
55
|
-
propertyTypes.push(...getTypes(type
|
|
65
|
+
if (PROPERTY_TYPE in type) {
|
|
66
|
+
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
56
67
|
|
|
57
68
|
continue;
|
|
58
69
|
}
|
|
59
70
|
|
|
60
|
-
addPropertyType(
|
|
61
|
-
validated,
|
|
62
|
-
prefix,
|
|
63
|
-
['object'],
|
|
64
|
-
typeof type.$required === 'boolean' ? type.$required : true,
|
|
65
|
-
);
|
|
71
|
+
addPropertyType(validated, prefix, [TYPE_OBJECT], type[PROPERTY_REQUIRED] !== false);
|
|
66
72
|
|
|
67
|
-
propertyTypes.push(
|
|
73
|
+
propertyTypes.push(TYPE_OBJECT);
|
|
68
74
|
|
|
69
75
|
getValidatedSchema(type as Schema, validated, prefix);
|
|
70
76
|
}
|
|
@@ -104,8 +110,8 @@ function getValidatedSchema(
|
|
|
104
110
|
|
|
105
111
|
let required = true;
|
|
106
112
|
|
|
107
|
-
if (typeof value === 'object' && value !== null &&
|
|
108
|
-
required = typeof value
|
|
113
|
+
if (typeof value === 'object' && value !== null && PROPERTY_REQUIRED in value) {
|
|
114
|
+
required = typeof value[PROPERTY_REQUIRED] === 'boolean' ? value[PROPERTY_REQUIRED] : true;
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
const prefixedKey = `${prefix}${key}`;
|
|
@@ -137,21 +143,3 @@ export function validateSchema(schema: unknown): ValidatedSchema {
|
|
|
137
143
|
? getValidatedSchema(schema as Schema, validated)
|
|
138
144
|
: validated;
|
|
139
145
|
}
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
const types = new Set<keyof Values>([
|
|
144
|
-
'array',
|
|
145
|
-
'bigint',
|
|
146
|
-
'boolean',
|
|
147
|
-
'date',
|
|
148
|
-
'date-like',
|
|
149
|
-
'function',
|
|
150
|
-
'null',
|
|
151
|
-
'number',
|
|
152
|
-
'numerical',
|
|
153
|
-
'object',
|
|
154
|
-
'string',
|
|
155
|
-
'symbol',
|
|
156
|
-
'undefined',
|
|
157
|
-
]);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {smush} from '@oscarpalmer/atoms/value';
|
|
3
|
+
import {TYPE_OBJECT, TYPE_UNDEFINED} from '../constants';
|
|
3
4
|
import {isDateLike} from '../is';
|
|
4
|
-
import type {ValidatedPropertyType, ValidatedSchema, Values} from '../
|
|
5
|
+
import type {ValidatedPropertyType, ValidatedSchema, Values} from '../models';
|
|
5
6
|
|
|
6
7
|
export function validateType(type: ValidatedPropertyType, value: unknown): boolean {
|
|
7
8
|
return typeof type === 'string' ? validators[type](value) : type.is(value);
|
|
@@ -15,14 +16,23 @@ export function validateValue(validated: ValidatedSchema, obj: unknown): boolean
|
|
|
15
16
|
const {keys, properties} = validated;
|
|
16
17
|
const keysLength = keys.array.length;
|
|
17
18
|
|
|
19
|
+
const ignore = new Set<string>();
|
|
20
|
+
|
|
18
21
|
const smushed = smush(obj as PlainObject);
|
|
19
22
|
|
|
20
23
|
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
21
24
|
const key = keys.array[keyIndex];
|
|
25
|
+
|
|
26
|
+
const prefix = key.replace(EXPRESSION_SUFFIX, '');
|
|
27
|
+
|
|
28
|
+
if (ignore.has(prefix)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
const property = properties[key];
|
|
23
33
|
const value = smushed[key];
|
|
24
34
|
|
|
25
|
-
if (value === undefined && property.required && !property.types.includes(
|
|
35
|
+
if (value === undefined && property.required && !property.types.includes(TYPE_UNDEFINED)) {
|
|
26
36
|
return false;
|
|
27
37
|
}
|
|
28
38
|
|
|
@@ -37,7 +47,13 @@ export function validateValue(validated: ValidatedSchema, obj: unknown): boolean
|
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
|
|
40
|
-
|
|
50
|
+
const type = property.types[typeIndex];
|
|
51
|
+
|
|
52
|
+
if (validateType(type, value)) {
|
|
53
|
+
if (type !== TYPE_OBJECT) {
|
|
54
|
+
ignore.add(key);
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
continue outer;
|
|
42
58
|
}
|
|
43
59
|
}
|
|
@@ -50,6 +66,10 @@ export function validateValue(validated: ValidatedSchema, obj: unknown): boolean
|
|
|
50
66
|
|
|
51
67
|
//
|
|
52
68
|
|
|
69
|
+
const EXPRESSION_SUFFIX = /\.\w+$/;
|
|
70
|
+
|
|
71
|
+
//
|
|
72
|
+
|
|
53
73
|
const validators: Record<keyof Values, (value: unknown) => boolean> = {
|
|
54
74
|
array: Array.isArray,
|
|
55
75
|
bigint: value => typeof value === 'bigint',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Values } from './models';
|
|
2
|
+
export declare const PROPERTY_REQUIRED = "$required";
|
|
3
|
+
export declare const PROPERTY_TYPE = "$type";
|
|
4
|
+
export declare const SCHEMATIC_NAME = "$schematic";
|
|
5
|
+
export declare const TYPE_OBJECT = "object";
|
|
6
|
+
export declare const TYPE_UNDEFINED = "undefined";
|
|
7
|
+
export declare const TYPE_ALL: Set<keyof Values>;
|
package/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { Schema, TypedSchema } from './
|
|
1
|
+
export type { Schema, TypedSchema } from './models';
|
|
2
2
|
export { schematic, type Schematic } from './schematic';
|
package/types/schematic.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { PlainObject } from '@oscarpalmer/atoms';
|
|
2
|
-
import type { Infer, Schema, TypedSchema } from './
|
|
1
|
+
import type { PlainObject } from '@oscarpalmer/atoms/models';
|
|
2
|
+
import type { Infer, Schema, TypedSchema } from './models';
|
|
3
3
|
/**
|
|
4
4
|
* A schematic for validating objects
|
|
5
5
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ValidatedSchema } from '../
|
|
1
|
+
import type { ValidatedSchema } from '../models';
|
|
2
2
|
export declare function validateSchema(schema: unknown): ValidatedSchema;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { ValidatedPropertyType, ValidatedSchema } from '../
|
|
1
|
+
import type { ValidatedPropertyType, ValidatedSchema } from '../models';
|
|
2
2
|
export declare function validateType(type: ValidatedPropertyType, value: unknown): boolean;
|
|
3
3
|
export declare function validateValue(validated: ValidatedSchema, obj: unknown): boolean;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|