@oscarpalmer/jhunal 0.5.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 +169 -69
- package/dist/schematic.js +3 -2
- package/dist/validation/schema.validation.js +64 -29
- package/dist/validation/value.validation.js +23 -10
- package/package.json +7 -8
- package/src/constants.ts +27 -0
- package/src/index.ts +1 -1
- package/src/is.ts +3 -2
- package/src/models.ts +202 -0
- package/src/schematic.ts +8 -6
- package/src/validation/schema.validation.ts +119 -37
- package/src/validation/value.validation.ts +36 -18
- package/types/constants.d.ts +7 -0
- package/types/index.d.ts +1 -1
- package/types/models.d.ts +117 -0
- package/types/schematic.d.ts +4 -3
- package/types/validation/schema.validation.d.ts +1 -2
- package/types/validation/value.validation.d.ts +1 -1
- package/dist/helpers.js +0 -39
- package/src/helpers.ts +0 -49
- package/src/model.ts +0 -185
- package/types/helpers.d.ts +0 -2
- package/types/model.d.ts +0 -93
- /package/dist/{model.js → models.js} +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,103 +1,203 @@
|
|
|
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
|
+
function compact(array, strict) {
|
|
22
|
+
if (!Array.isArray(array)) return [];
|
|
23
|
+
if (strict === true) return array.filter(Boolean);
|
|
24
|
+
const { length } = array;
|
|
25
|
+
const compacted = [];
|
|
26
|
+
for (let index = 0; index < length; index += 1) {
|
|
27
|
+
const item = array[index];
|
|
28
|
+
if (item != null) compacted.push(item);
|
|
29
|
+
}
|
|
30
|
+
return compacted;
|
|
31
|
+
}
|
|
32
|
+
function getString(value) {
|
|
33
|
+
if (typeof value === "string") return value;
|
|
34
|
+
if (value == null) return "";
|
|
35
|
+
if (typeof value === "function") return getString(value());
|
|
36
|
+
if (typeof value !== "object") return String(value);
|
|
37
|
+
const asString = String(value.valueOf?.() ?? value);
|
|
38
|
+
return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
|
|
39
|
+
}
|
|
40
|
+
function join(value, delimiter) {
|
|
41
|
+
return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
|
|
42
|
+
}
|
|
43
|
+
function isArrayOrPlainObject(value) {
|
|
44
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
45
|
+
}
|
|
46
|
+
function isPlainObject(value) {
|
|
47
|
+
if (value === null || typeof value !== "object") return false;
|
|
48
|
+
if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
|
|
49
|
+
const prototype = Object.getPrototypeOf(value);
|
|
50
|
+
return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
|
|
51
|
+
}
|
|
52
|
+
new Set([
|
|
53
|
+
Int8Array,
|
|
54
|
+
Uint8Array,
|
|
55
|
+
Uint8ClampedArray,
|
|
56
|
+
Int16Array,
|
|
57
|
+
Uint16Array,
|
|
58
|
+
Int32Array,
|
|
59
|
+
Uint32Array,
|
|
60
|
+
Float32Array,
|
|
61
|
+
Float64Array,
|
|
62
|
+
BigInt64Array,
|
|
63
|
+
BigUint64Array
|
|
64
|
+
]);
|
|
65
|
+
function flattenObject(value, depth, smushed, prefix) {
|
|
66
|
+
if (depth >= MAX_DEPTH) return {};
|
|
67
|
+
if (smushed.has(value)) return smushed.get(value);
|
|
68
|
+
const keys = Object.keys(value);
|
|
69
|
+
const { length } = keys;
|
|
70
|
+
const flattened = {};
|
|
71
|
+
for (let index = 0; index < length; index += 1) {
|
|
72
|
+
const key = keys[index];
|
|
73
|
+
const val = value[key];
|
|
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;
|
|
85
|
+
}
|
|
86
|
+
smushed.set(value, flattened);
|
|
87
|
+
return flattened;
|
|
88
|
+
}
|
|
89
|
+
function smush(value) {
|
|
90
|
+
return typeof value === "object" && value !== null ? flattenObject(value, 0, /* @__PURE__ */ new WeakMap()) : {};
|
|
91
|
+
}
|
|
92
|
+
var MAX_DEPTH = 100;
|
|
1
93
|
function isDateLike(value) {
|
|
2
94
|
if (value instanceof Date) return true;
|
|
3
95
|
if (typeof value === "number") return value >= -864e13 && value <= 864e13;
|
|
4
96
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
5
97
|
}
|
|
6
98
|
function isSchematic(value) {
|
|
7
|
-
return typeof value === "object" && value !== null &&
|
|
99
|
+
return typeof value === "object" && value !== null && SCHEMATIC_NAME in value && value[SCHEMATIC_NAME] === true;
|
|
100
|
+
}
|
|
101
|
+
function addPropertyType(to, key, values, required) {
|
|
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 {
|
|
106
|
+
to.keys.array.push(key);
|
|
107
|
+
to.keys.set.add(key);
|
|
108
|
+
to.properties[key] = {
|
|
109
|
+
required,
|
|
110
|
+
types: values
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (!required && !to.properties[key].types.includes(TYPE_UNDEFINED)) to.properties[key].types.push(TYPE_UNDEFINED);
|
|
8
114
|
}
|
|
9
|
-
function getTypes(value) {
|
|
10
|
-
const
|
|
115
|
+
function getTypes(value, validated, prefix) {
|
|
116
|
+
const propertyTypes = [];
|
|
11
117
|
const values = Array.isArray(value) ? value : [value];
|
|
12
118
|
const { length } = values;
|
|
13
119
|
for (let index = 0; index < length; index += 1) {
|
|
14
120
|
const type = values[index];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
returned.push(validateSchema(type));
|
|
24
|
-
break;
|
|
25
|
-
default: break;
|
|
121
|
+
if (isSchematic(type) || typeof type === "string" && TYPE_ALL.has(type)) {
|
|
122
|
+
propertyTypes.push(type);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (typeof type !== "object" || type === null) continue;
|
|
126
|
+
if (PROPERTY_TYPE in type) {
|
|
127
|
+
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
128
|
+
continue;
|
|
26
129
|
}
|
|
130
|
+
addPropertyType(validated, prefix, [TYPE_OBJECT], type[PROPERTY_REQUIRED] !== false);
|
|
131
|
+
propertyTypes.push(TYPE_OBJECT);
|
|
132
|
+
getValidatedSchema(type, validated, prefix);
|
|
27
133
|
}
|
|
28
|
-
return
|
|
134
|
+
return propertyTypes;
|
|
29
135
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"boolean",
|
|
34
|
-
"date",
|
|
35
|
-
"date-like",
|
|
36
|
-
"function",
|
|
37
|
-
"null",
|
|
38
|
-
"number",
|
|
39
|
-
"numerical",
|
|
40
|
-
"object",
|
|
41
|
-
"string",
|
|
42
|
-
"symbol",
|
|
43
|
-
"undefined"
|
|
44
|
-
]);
|
|
45
|
-
function validateSchema(schema) {
|
|
46
|
-
if (validatedSchemas.has(schema)) return validatedSchemas.get(schema);
|
|
47
|
-
const validated = {
|
|
48
|
-
keys: [],
|
|
49
|
-
length: 0,
|
|
50
|
-
properties: {}
|
|
51
|
-
};
|
|
52
|
-
if (typeof schema !== "object" || schema === null) return validated;
|
|
53
|
-
const keys = Object.keys(schema);
|
|
136
|
+
function getValidatedSchema(schema, validated, prefix) {
|
|
137
|
+
const smushed = smush(schema);
|
|
138
|
+
const keys = Object.keys(smushed);
|
|
54
139
|
const { length } = keys;
|
|
140
|
+
const arrayKeys = /* @__PURE__ */ new Set();
|
|
141
|
+
const noPrefix = prefix == null;
|
|
142
|
+
prefix = noPrefix ? "" : `${prefix}.`;
|
|
55
143
|
for (let index = 0; index < length; index += 1) {
|
|
56
144
|
const key = keys[index];
|
|
57
|
-
const value =
|
|
145
|
+
const value = smushed[key];
|
|
146
|
+
if (Array.isArray(value)) arrayKeys.add(key);
|
|
147
|
+
if (/\.\$(required|type)(\.|$)/.test(key)) continue;
|
|
148
|
+
if (/\d+/.test(key) && arrayKeys.has(key.replace(/\.\d+$/, ""))) continue;
|
|
58
149
|
let required = true;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
types$1 = getTypes(value.type);
|
|
64
|
-
} else types$1 = getTypes(value);
|
|
65
|
-
if (types$1.length > 0) {
|
|
66
|
-
if (!required && !types$1.includes("undefined")) types$1.push("undefined");
|
|
67
|
-
validated.keys.push(key);
|
|
68
|
-
validated.properties[key] = {
|
|
69
|
-
required,
|
|
70
|
-
types: types$1
|
|
71
|
-
};
|
|
72
|
-
validated.length += 1;
|
|
73
|
-
}
|
|
150
|
+
if (typeof value === "object" && value !== null && PROPERTY_REQUIRED in value) required = typeof value[PROPERTY_REQUIRED] === "boolean" ? value[PROPERTY_REQUIRED] : true;
|
|
151
|
+
const prefixedKey = `${prefix}${key}`;
|
|
152
|
+
const types = getTypes(value, validated, prefixedKey);
|
|
153
|
+
if (types.length > 0) addPropertyType(validated, prefixedKey, types, required);
|
|
74
154
|
}
|
|
75
|
-
|
|
155
|
+
if (noPrefix) validated.keys.array.sort();
|
|
76
156
|
return validated;
|
|
77
157
|
}
|
|
78
|
-
|
|
158
|
+
function validateSchema(schema) {
|
|
159
|
+
const validated = {
|
|
160
|
+
keys: {
|
|
161
|
+
array: [],
|
|
162
|
+
set: /* @__PURE__ */ new Set()
|
|
163
|
+
},
|
|
164
|
+
properties: {}
|
|
165
|
+
};
|
|
166
|
+
return typeof schema === "object" && schema !== null ? getValidatedSchema(schema, validated) : validated;
|
|
167
|
+
}
|
|
79
168
|
function validateType(type, value) {
|
|
80
|
-
|
|
81
|
-
if (isSchematic(type)) return type.is(value);
|
|
82
|
-
return validateValue(type, value);
|
|
169
|
+
return typeof type === "string" ? validators[type](value) : type.is(value);
|
|
83
170
|
}
|
|
84
171
|
function validateValue(validated, obj) {
|
|
85
172
|
if (typeof obj !== "object" || obj === null) return false;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
173
|
+
const { keys, properties } = validated;
|
|
174
|
+
const keysLength = keys.array.length;
|
|
175
|
+
const ignore = /* @__PURE__ */ new Set();
|
|
176
|
+
const smushed = smush(obj);
|
|
177
|
+
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
178
|
+
const key = keys.array[keyIndex];
|
|
179
|
+
const prefix = key.replace(EXPRESSION_SUFFIX, "");
|
|
180
|
+
if (ignore.has(prefix)) continue;
|
|
181
|
+
const property = properties[key];
|
|
182
|
+
const value = smushed[key];
|
|
183
|
+
if (value === void 0 && property.required && !property.types.includes(TYPE_UNDEFINED)) return false;
|
|
91
184
|
const typesLength = property.types.length;
|
|
92
185
|
if (typesLength === 1) {
|
|
93
186
|
if (!validateType(property.types[0], value)) return false;
|
|
94
187
|
continue;
|
|
95
188
|
}
|
|
96
|
-
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
|
+
}
|
|
97
196
|
return false;
|
|
98
197
|
}
|
|
99
198
|
return true;
|
|
100
199
|
}
|
|
200
|
+
const EXPRESSION_SUFFIX = /\.\w+$/;
|
|
101
201
|
const validators = {
|
|
102
202
|
array: Array.isArray,
|
|
103
203
|
bigint: (value) => typeof value === "bigint",
|
|
@@ -106,7 +206,7 @@ const validators = {
|
|
|
106
206
|
"date-like": isDateLike,
|
|
107
207
|
function: (value) => typeof value === "function",
|
|
108
208
|
null: (value) => value === null,
|
|
109
|
-
number: (value) => typeof value === "number",
|
|
209
|
+
number: (value) => typeof value === "number" && !Number.isNaN(value),
|
|
110
210
|
numerical: (value) => validators.bigint(value) || validators.number(value),
|
|
111
211
|
object: (value) => typeof value === "object" && value !== null,
|
|
112
212
|
string: (value) => typeof value === "string",
|
|
@@ -120,9 +220,9 @@ var Schematic = class {
|
|
|
120
220
|
return this.#validatable;
|
|
121
221
|
}
|
|
122
222
|
constructor(schema) {
|
|
123
|
-
Object.defineProperty(this,
|
|
223
|
+
Object.defineProperty(this, SCHEMATIC_NAME, { value: true });
|
|
124
224
|
this.#schema = validateSchema(schema);
|
|
125
|
-
this.#validatable = this.#schema.length > 0;
|
|
225
|
+
this.#validatable = this.#schema.keys.array.length > 0;
|
|
126
226
|
}
|
|
127
227
|
is(value) {
|
|
128
228
|
return this.#validatable && validateValue(this.#schema, value);
|
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,9 +8,9 @@ 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
|
-
this.#validatable = this.#schema.length > 0;
|
|
13
|
+
this.#validatable = this.#schema.keys.array.length > 0;
|
|
13
14
|
}
|
|
14
15
|
is(value) {
|
|
15
16
|
return this.#validatable && validateValue(this.#schema, value);
|
|
@@ -1,36 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { PROPERTY_REQUIRED, PROPERTY_TYPE, TYPE_ALL, TYPE_OBJECT, TYPE_UNDEFINED } from "../constants.js";
|
|
2
|
+
import { isSchematic } from "../is.js";
|
|
3
|
+
import { smush } from "@oscarpalmer/atoms/value";
|
|
4
|
+
function addPropertyType(to, key, values, required) {
|
|
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 {
|
|
9
|
+
to.keys.array.push(key);
|
|
10
|
+
to.keys.set.add(key);
|
|
11
|
+
to.properties[key] = {
|
|
12
|
+
required,
|
|
13
|
+
types: values
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (!required && !to.properties[key].types.includes("undefined")) to.properties[key].types.push(TYPE_UNDEFINED);
|
|
17
|
+
}
|
|
18
|
+
function getTypes(value, validated, prefix) {
|
|
19
|
+
const propertyTypes = [];
|
|
20
|
+
const values = Array.isArray(value) ? value : [value];
|
|
21
|
+
const { length } = values;
|
|
22
|
+
for (let index = 0; index < length; index += 1) {
|
|
23
|
+
const type = values[index];
|
|
24
|
+
if (isSchematic(type) || typeof type === "string" && TYPE_ALL.has(type)) {
|
|
25
|
+
propertyTypes.push(type);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (typeof type !== "object" || type === null) continue;
|
|
29
|
+
if ("$type" in type) {
|
|
30
|
+
propertyTypes.push(...getTypes(type[PROPERTY_TYPE], validated, prefix));
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
addPropertyType(validated, prefix, [TYPE_OBJECT], type[PROPERTY_REQUIRED] !== false);
|
|
34
|
+
propertyTypes.push(TYPE_OBJECT);
|
|
35
|
+
getValidatedSchema(type, validated, prefix);
|
|
36
|
+
}
|
|
37
|
+
return propertyTypes;
|
|
38
|
+
}
|
|
39
|
+
function getValidatedSchema(schema, validated, prefix) {
|
|
40
|
+
const smushed = smush(schema);
|
|
41
|
+
const keys = Object.keys(smushed);
|
|
11
42
|
const { length } = keys;
|
|
43
|
+
const arrayKeys = /* @__PURE__ */ new Set();
|
|
44
|
+
const noPrefix = prefix == null;
|
|
45
|
+
prefix = noPrefix ? "" : `${prefix}.`;
|
|
12
46
|
for (let index = 0; index < length; index += 1) {
|
|
13
47
|
const key = keys[index];
|
|
14
|
-
const value =
|
|
48
|
+
const value = smushed[key];
|
|
49
|
+
if (Array.isArray(value)) arrayKeys.add(key);
|
|
50
|
+
if (/\.\$(required|type)(\.|$)/.test(key)) continue;
|
|
51
|
+
if (/\d+/.test(key) && arrayKeys.has(key.replace(/\.\d+$/, ""))) continue;
|
|
15
52
|
let required = true;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
types = getTypes(value.type);
|
|
21
|
-
} else types = getTypes(value);
|
|
22
|
-
if (types.length > 0) {
|
|
23
|
-
if (!required && !types.includes("undefined")) types.push("undefined");
|
|
24
|
-
validated.keys.push(key);
|
|
25
|
-
validated.properties[key] = {
|
|
26
|
-
required,
|
|
27
|
-
types
|
|
28
|
-
};
|
|
29
|
-
validated.length += 1;
|
|
30
|
-
}
|
|
53
|
+
if (typeof value === "object" && value !== null && "$required" in value) required = typeof value["$required"] === "boolean" ? value[PROPERTY_REQUIRED] : true;
|
|
54
|
+
const prefixedKey = `${prefix}${key}`;
|
|
55
|
+
const types = getTypes(value, validated, prefixedKey);
|
|
56
|
+
if (types.length > 0) addPropertyType(validated, prefixedKey, types, required);
|
|
31
57
|
}
|
|
32
|
-
|
|
58
|
+
if (noPrefix) validated.keys.array.sort();
|
|
33
59
|
return validated;
|
|
34
60
|
}
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
function validateSchema(schema) {
|
|
62
|
+
const validated = {
|
|
63
|
+
keys: {
|
|
64
|
+
array: [],
|
|
65
|
+
set: /* @__PURE__ */ new Set()
|
|
66
|
+
},
|
|
67
|
+
properties: {}
|
|
68
|
+
};
|
|
69
|
+
return typeof schema === "object" && schema !== null ? getValidatedSchema(schema, validated) : validated;
|
|
70
|
+
}
|
|
71
|
+
export { validateSchema };
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TYPE_OBJECT, TYPE_UNDEFINED } from "../constants.js";
|
|
2
|
+
import { isDateLike } from "../is.js";
|
|
3
|
+
import { smush } from "@oscarpalmer/atoms/value";
|
|
2
4
|
function validateType(type, value) {
|
|
3
|
-
|
|
4
|
-
if (isSchematic(type)) return type.is(value);
|
|
5
|
-
return validateValue(type, value);
|
|
5
|
+
return typeof type === "string" ? validators[type](value) : type.is(value);
|
|
6
6
|
}
|
|
7
7
|
function validateValue(validated, obj) {
|
|
8
8
|
if (typeof obj !== "object" || obj === null) return false;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const { keys, properties } = validated;
|
|
10
|
+
const keysLength = keys.array.length;
|
|
11
|
+
const ignore = /* @__PURE__ */ new Set();
|
|
12
|
+
const smushed = smush(obj);
|
|
13
|
+
outer: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
|
|
14
|
+
const key = keys.array[keyIndex];
|
|
15
|
+
const prefix = key.replace(EXPRESSION_SUFFIX, "");
|
|
16
|
+
if (ignore.has(prefix)) continue;
|
|
17
|
+
const property = properties[key];
|
|
18
|
+
const value = smushed[key];
|
|
13
19
|
if (value === void 0 && property.required && !property.types.includes("undefined")) return false;
|
|
14
20
|
const typesLength = property.types.length;
|
|
15
21
|
if (typesLength === 1) {
|
|
16
22
|
if (!validateType(property.types[0], value)) return false;
|
|
17
23
|
continue;
|
|
18
24
|
}
|
|
19
|
-
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
|
+
}
|
|
20
32
|
return false;
|
|
21
33
|
}
|
|
22
34
|
return true;
|
|
23
35
|
}
|
|
36
|
+
var EXPRESSION_SUFFIX = /\.\w+$/;
|
|
24
37
|
var validators = {
|
|
25
38
|
array: Array.isArray,
|
|
26
39
|
bigint: (value) => typeof value === "bigint",
|
|
@@ -29,7 +42,7 @@ var validators = {
|
|
|
29
42
|
"date-like": isDateLike,
|
|
30
43
|
function: (value) => typeof value === "function",
|
|
31
44
|
null: (value) => value === null,
|
|
32
|
-
number: (value) => typeof value === "number",
|
|
45
|
+
number: (value) => typeof value === "number" && !Number.isNaN(value),
|
|
33
46
|
numerical: (value) => validators.bigint(value) || validators.number(value),
|
|
34
47
|
object: (value) => typeof value === "object" && value !== null,
|
|
35
48
|
string: (value) => typeof value === "string",
|
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
|
-
"jsdom": "^27.
|
|
14
|
-
"oxfmt": "^0.
|
|
15
|
-
"oxlint": "^1.
|
|
13
|
+
"jsdom": "^27.4",
|
|
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",
|
|
@@ -27,9 +27,8 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"files": ["dist", "src", "types"],
|
|
30
|
-
"keywords": [],
|
|
30
|
+
"keywords": ["schema", "validation"],
|
|
31
31
|
"license": "MIT",
|
|
32
|
-
"main": "./dist/index.cjs",
|
|
33
32
|
"module": "./dist/index.js",
|
|
34
33
|
"name": "@oscarpalmer/jhunal",
|
|
35
34
|
"repository": {
|
|
@@ -45,6 +44,6 @@
|
|
|
45
44
|
"watch": "npx vite build --watch"
|
|
46
45
|
},
|
|
47
46
|
"type": "module",
|
|
48
|
-
"types": "./types/index.d.
|
|
49
|
-
"version": "0.
|
|
47
|
+
"types": "./types/index.d.ts",
|
|
48
|
+
"version": "0.7.0"
|
|
50
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,3 +1,4 @@
|
|
|
1
|
+
import {SCHEMATIC_NAME} from './constants';
|
|
1
2
|
import type {Schematic} from './schematic';
|
|
2
3
|
|
|
3
4
|
export function isDateLike(value: unknown): value is Date {
|
|
@@ -16,7 +17,7 @@ export function isSchematic(value: unknown): value is Schematic<never> {
|
|
|
16
17
|
return (
|
|
17
18
|
typeof value === 'object' &&
|
|
18
19
|
value !== null &&
|
|
19
|
-
|
|
20
|
-
value
|
|
20
|
+
SCHEMATIC_NAME in value &&
|
|
21
|
+
value[SCHEMATIC_NAME] === true
|
|
21
22
|
);
|
|
22
23
|
}
|