@briklab/malle 1.0.0 → 1.0.2
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/core/class.js +108 -28
- package/core/main.js +5 -0
- package/helpers/arrHelpers.js +9 -9
- package/helpers/typeHelpers.js +24 -6
- package/index.js +1 -2
- package/package.json +1 -1
package/core/class.js
CHANGED
|
@@ -23,18 +23,32 @@ const expector = {
|
|
|
23
23
|
for (const schema of schemasArr) {
|
|
24
24
|
if (
|
|
25
25
|
!checkFormat(
|
|
26
|
-
{ name: "string", type: "string", optional: "boolean" },
|
|
26
|
+
{ name: "string", type: "string", optional: "boolean", aliasNames: "array", spread: "boolean" },
|
|
27
27
|
schema,
|
|
28
28
|
)
|
|
29
29
|
) {
|
|
30
30
|
new cM.error(
|
|
31
|
-
"Invalid format in schema object. (expected {name, type, optional?})",
|
|
31
|
+
"Invalid format in schema object. (expected {name, type, optional?, aliasNames?, spread?})",
|
|
32
32
|
debug,
|
|
33
33
|
);
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
schema.optional = !!schema.optional;
|
|
38
|
+
schema.spread = !!schema.spread;
|
|
39
|
+
|
|
40
|
+
if (!isUndefined(schema.aliasNames)) {
|
|
41
|
+
if (!Array.isArray(schema.aliasNames) || !areAllTypes(schema.aliasNames, "string")) {
|
|
42
|
+
new cM.error("schema.aliasNames must be an array of strings", debug);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
schema.aliasNames = [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!schema.spread && typeof schema.type === "string" && /\[\]$/.test(schema.type)) {
|
|
50
|
+
schema.spread = true;
|
|
51
|
+
}
|
|
38
52
|
}
|
|
39
53
|
|
|
40
54
|
return schemasArr;
|
|
@@ -62,7 +76,6 @@ const guessor = {
|
|
|
62
76
|
looseModeGuessor(args, schemas, debug) {
|
|
63
77
|
const result = {};
|
|
64
78
|
const errors = [];
|
|
65
|
-
|
|
66
79
|
const consumed = new Set();
|
|
67
80
|
|
|
68
81
|
for (let s = 0; s < schemas.length; s++) {
|
|
@@ -71,11 +84,43 @@ const guessor = {
|
|
|
71
84
|
|
|
72
85
|
for (let a = 0; a < args.length; a++) {
|
|
73
86
|
if (consumed.has(a)) continue;
|
|
74
|
-
if (isType(args[a], "object")
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
if (isType(args[a], "object")) {
|
|
88
|
+
const keys = [schema.name].concat(schema.aliasNames || []);
|
|
89
|
+
for (const k of keys) {
|
|
90
|
+
if (!isUndefined(args[a][k])) {
|
|
91
|
+
result[schema.name] = args[a][k];
|
|
92
|
+
consumed.add(a);
|
|
93
|
+
found = true;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (found) break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!found && schema.spread) {
|
|
102
|
+
let elemType = null;
|
|
103
|
+
if (typeof schema.type === "string") {
|
|
104
|
+
if (/\[\]$/.test(schema.type)) elemType = schema.type.replace(/\[\]$/, "");
|
|
105
|
+
else elemType = schema.type;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const collected = [];
|
|
109
|
+
for (let a = 0; a < args.length; a++) {
|
|
110
|
+
if (consumed.has(a)) continue;
|
|
111
|
+
if (isType(args[a], elemType)) {
|
|
112
|
+
collected.push(args[a]);
|
|
113
|
+
consumed.add(a);
|
|
114
|
+
}
|
|
115
|
+
else if (isType(args[a], elemType + "[]")) {
|
|
116
|
+
collected.push(...args[a]);
|
|
117
|
+
consumed.add(a);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (collected.length > 0) {
|
|
122
|
+
result[schema.name] = collected;
|
|
77
123
|
found = true;
|
|
78
|
-
break;
|
|
79
124
|
}
|
|
80
125
|
}
|
|
81
126
|
|
|
@@ -117,37 +162,72 @@ const guessor = {
|
|
|
117
162
|
const schema = schemas[s];
|
|
118
163
|
let found = false;
|
|
119
164
|
|
|
165
|
+
// 1) try named object property or any alias
|
|
120
166
|
for (let a = 0; a < args.length; a++) {
|
|
121
167
|
if (isType(args[a], "object")) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
168
|
+
const keys = [schema.name].concat(schema.aliasNames || []);
|
|
169
|
+
for (const k of keys) {
|
|
170
|
+
if (!isUndefined(args[a][k])) {
|
|
171
|
+
result[schema.name] = args[a][k];
|
|
172
|
+
found = true;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
126
175
|
}
|
|
176
|
+
if (found) break;
|
|
127
177
|
}
|
|
128
178
|
}
|
|
129
179
|
|
|
130
180
|
if (!found) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
181
|
+
if (schema.spread) {
|
|
182
|
+
let elemType = null;
|
|
183
|
+
if (typeof schema.type === "string") {
|
|
184
|
+
if (/\[\]$/.test(schema.type)) elemType = schema.type.replace(/\[\]$/, "");
|
|
185
|
+
else elemType = schema.type;
|
|
136
186
|
}
|
|
137
|
-
}
|
|
138
187
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
188
|
+
const collected = [];
|
|
189
|
+
let i = onArgCounter;
|
|
190
|
+
for (; i < args.length; i++) {
|
|
191
|
+
if (isType(args[i], elemType)) {
|
|
192
|
+
collected.push(args[i]);
|
|
193
|
+
} else break;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (collected.length > 0) {
|
|
197
|
+
result[schema.name] = collected;
|
|
198
|
+
onArgCounter = i;
|
|
199
|
+
found = true;
|
|
200
|
+
} else if (isType(args[onArgCounter], elemType + "[]")) {
|
|
201
|
+
result[schema.name] = args[onArgCounter];
|
|
202
|
+
onArgCounter = onArgCounter + 1;
|
|
203
|
+
found = true;
|
|
204
|
+
}
|
|
145
205
|
} else {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
206
|
+
let consumedIndex = -1;
|
|
207
|
+
for (let i = onArgCounter; i < args.length; i++) {
|
|
208
|
+
if (isType(args[i], schema.type)) {
|
|
209
|
+
consumedIndex = i;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (consumedIndex !== -1) {
|
|
215
|
+
result[schema.name] = args[consumedIndex];
|
|
216
|
+
onArgCounter = consumedIndex + 1;
|
|
217
|
+
found = true;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!found) {
|
|
222
|
+
if (schema.optional) {
|
|
223
|
+
result[schema.name] = undefined;
|
|
224
|
+
} else {
|
|
225
|
+
errors.push({
|
|
226
|
+
name: schema.name,
|
|
227
|
+
expected: schema.type,
|
|
228
|
+
message: `missing required argument '${schema.name}' of type '${schema.type}'`,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
151
231
|
}
|
|
152
232
|
}
|
|
153
233
|
}
|
package/core/main.js
CHANGED
|
@@ -20,6 +20,11 @@ import MalleClass from "./class";
|
|
|
20
20
|
* - `__DidSucceed` (boolean) — true when all *required* schemas were satisfied
|
|
21
21
|
* - `__errors` (Array) — list of reported issues when parsing failed
|
|
22
22
|
*
|
|
23
|
+
* Schema notes:
|
|
24
|
+
* - `type` supports unions (`string|number`), generic arrays (`T[]`, e.g. `number[]`) and `any`.
|
|
25
|
+
* - `aliasNames` accepts an array of alternate object-property names.
|
|
26
|
+
* - `spread: true` collects multiple matching positional values into an array.
|
|
27
|
+
*
|
|
23
28
|
* @example
|
|
24
29
|
* import malle from "@briklab/malle";
|
|
25
30
|
*
|
package/helpers/arrHelpers.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import isType from "./typeHelpers";
|
|
2
2
|
|
|
3
|
-
export default function getArrFromSpread(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export default function getArrFromSpread(args) {
|
|
4
|
+
if (!Array.isArray(args)) return false;
|
|
5
|
+
if (args.length === 1 && Array.isArray(args[0])) return args[0];
|
|
6
|
+
return args;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export function areAllTypes(arr,type){
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
export function areAllTypes(arr, type) {
|
|
10
|
+
for (let i = 0; i < arr.length; i++) {
|
|
11
|
+
if (!isType(arr[i], type)) return false;
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
14
|
}
|
package/helpers/typeHelpers.js
CHANGED
|
@@ -13,19 +13,37 @@ const aliasMap = {
|
|
|
13
13
|
array: "array",
|
|
14
14
|
"[]": "array",
|
|
15
15
|
null: "null",
|
|
16
|
+
any: "any",
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
function _isPrimitiveMatch(stuff, norm) {
|
|
20
|
+
if (norm === "array") return Array.isArray(stuff);
|
|
21
|
+
if (norm === "null") return stuff === null;
|
|
22
|
+
if (norm === "any") return true;
|
|
23
|
+
if (typeofvalidtypes.includes(norm)) return typeof stuff === norm;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
export default function isType(stuff, type) {
|
|
19
28
|
if (!type) return false;
|
|
20
|
-
const raw = String(type);
|
|
21
|
-
const t = raw.toLowerCase();
|
|
29
|
+
const raw = String(type).trim();
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
// union (e.g. "string|number[]")
|
|
32
|
+
if (raw.includes("|")) {
|
|
33
|
+
return raw.split("|").map((p) => p.trim()).some((part) => isType(stuff, part));
|
|
34
|
+
}
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
36
|
+
// array-of-type syntax (T[])
|
|
37
|
+
if (raw.endsWith("[]")) {
|
|
38
|
+
const elemType = raw.slice(0, -2) || "any";
|
|
39
|
+
if (!Array.isArray(stuff)) return false;
|
|
40
|
+
return stuff.every((el) => isType(el, elemType));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const t = raw.toLowerCase();
|
|
44
|
+
const norm = aliasMap[t] || t;
|
|
28
45
|
|
|
46
|
+
if (_isPrimitiveMatch(stuff, norm)) return true;
|
|
29
47
|
if (raw === "Array") return Array.isArray(stuff);
|
|
30
48
|
|
|
31
49
|
return false;
|
package/index.js
CHANGED