@briklab/malle 1.0.0 → 1.0.1

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 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") && !isUndefined(args[a][schema.name])) {
75
- result[schema.name] = args[a][schema.name];
76
- consumed.add(a);
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
- if (!isUndefined(args[a][schema.name])) {
123
- result[schema.name] = args[a][schema.name];
124
- found = true;
125
- break;
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
- let consumedIndex = -1;
132
- for (let i = onArgCounter; i < args.length; i++) {
133
- if (isType(args[i], schema.type)) {
134
- consumedIndex = i;
135
- break;
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
- if (consumedIndex !== -1) {
140
- result[schema.name] = args[consumedIndex];
141
- onArgCounter = consumedIndex + 1;
142
- found = true;
143
- } else if (schema.optional) {
144
- result[schema.name] = undefined;
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
- errors.push({
147
- name: schema.name,
148
- expected: schema.type,
149
- message: `missing required argument '${schema.name}' of type '${schema.type}'`,
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
  *
@@ -1,14 +1,14 @@
1
1
  import isType from "./typeHelpers";
2
2
 
3
- export default function getArrFromSpread(arr){
4
- if(Array.isArray(arr))return arr;
5
- if(Array.isArray(arr[0]))return arr;
6
- return false
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
- for(let i=0;i<arr.length;i++){
11
- if(!isType(arr[i],type))return false
12
- }
13
- return true
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
  }
@@ -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
- const norm = aliasMap[t] || t;
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
- if (norm === "array") return Array.isArray(stuff);
26
- if (norm === "null") return stuff === null;
27
- if (typeofvalidtypes.includes(norm)) return typeof stuff === norm;
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
@@ -16,5 +16,4 @@
16
16
  * return Mode === "add" ? Number1 + Number2 : Number1 - Number2;
17
17
  * }
18
18
  */
19
- export { default } from "./core/main.js";
20
- export * from "./core/main.js";
19
+ export * from "./core/main.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@briklab/malle",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A fast, flexible, developer-first argument parser for modern JavaScript and Node.js.",
5
5
  "main": "index.js",
6
6
  "publishConfig": {