@briklab/malle 1.0.1 → 1.0.3

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/index.js CHANGED
@@ -8,12 +8,13 @@
8
8
  * @example
9
9
  * import malle from "@briklab/malle";
10
10
  * function calc(...args) {
11
- * const {Number1, Number2, Mode} = malle.expect(
11
+ * const {Number1, Number2, Mode} = malle({mode:"strict"}).expect(
12
12
  * {name:"Number1", type:"number"},
13
13
  * {name:"Number2", type:"number"},
14
14
  * {name:"Mode", type:"string"}
15
- * );
15
+ * ).guess(args);
16
16
  * return Mode === "add" ? Number1 + Number2 : Number1 - Number2;
17
17
  * }
18
18
  */
19
- export * from "./core/main.js";
19
+ export { default } from "./core/main.js";
20
+ export * from "./core/main.js";
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@briklab/malle",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A fast, flexible, developer-first argument parser for modern JavaScript and Node.js.",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
+ "exports": {
10
+ ".": "./index.js",
11
+ "./class": "./class/index.js"
12
+ },
9
13
  "private": false,
10
14
  "keywords": [],
11
15
  "author": "EkaanshPC",
12
16
  "license": "Apache 2.0",
13
17
  "files": [
14
- "class/**",
15
- "core/**",
16
- "helpers/**",
17
- "index.js",
18
- "index.cjs"
18
+ "index.js"
19
19
  ],
20
20
  "scripts": {}
21
21
  }
package/class/index.cjs DELETED
@@ -1,3 +0,0 @@
1
- const main = require("../core/class");
2
-
3
- module.exports = main
package/class/index.js DELETED
@@ -1,8 +0,0 @@
1
- /**
2
- * @packageDocumentation
3
- * exports the class instead of malle function.
4
- * @example
5
- * new MalleClass({mode:"strict"}).expect({name:"string",type:"string"}).guess("lol")
6
- */
7
- export { default } from "../core/class"
8
- export * from "../core/class"
package/core/class.js DELETED
@@ -1,294 +0,0 @@
1
- //-----------------------------------------------------------------------------------------------------------
2
- //#region Helpers
3
- import getArrFromSpread, { areAllTypes } from "../helpers/arrHelpers";
4
- import { cM } from "../helpers/consoleManager";
5
- import isUndefined from "../helpers/isUndefined";
6
- import loop from "../helpers/loop";
7
- import checkFormat from "../helpers/objHelpers";
8
- import isType from "../helpers/typeHelpers";
9
-
10
- const expector = {
11
- checkSchemas(schemas, debug = false) {
12
- const schemasArr = getArrFromSpread(schemas);
13
- if (!schemasArr) {
14
- new cM.error("Could not get array from spread in expector.", debug);
15
- return false;
16
- }
17
-
18
- if (!areAllTypes(schemasArr, "object")) {
19
- new cM.error("The schema array must consist of only objects.", debug);
20
- return false;
21
- }
22
-
23
- for (const schema of schemasArr) {
24
- if (
25
- !checkFormat(
26
- { name: "string", type: "string", optional: "boolean", aliasNames: "array", spread: "boolean" },
27
- schema,
28
- )
29
- ) {
30
- new cM.error(
31
- "Invalid format in schema object. (expected {name, type, optional?, aliasNames?, spread?})",
32
- debug,
33
- );
34
- return false;
35
- }
36
-
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
- }
52
- }
53
-
54
- return schemasArr;
55
- },
56
- };
57
-
58
- const guessor = {
59
- guessBy(args, schemas, mode, debug = false) {
60
- if (debug) {
61
- cM.log(`[Malle] guessing with mode: ${mode}`, true);
62
- cM.log(["args:", args], true);
63
- cM.log(["schemas:", schemas], true);
64
- }
65
-
66
- if (mode === "loose") return this.looseModeGuessor(args, schemas, debug);
67
- return this.strictModeGuessor(args, schemas, debug);
68
- },
69
- /**
70
- *
71
- * @param {Array} args
72
- * @param {Array} schemas
73
- * @param {"strict"|"loose"} mode
74
- * @param {Boolean} debug
75
- */
76
- looseModeGuessor(args, schemas, debug) {
77
- const result = {};
78
- const errors = [];
79
- const consumed = new Set();
80
-
81
- for (let s = 0; s < schemas.length; s++) {
82
- const schema = schemas[s];
83
- let found = false;
84
-
85
- for (let a = 0; a < args.length; a++) {
86
- if (consumed.has(a)) continue;
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;
123
- found = true;
124
- }
125
- }
126
-
127
- if (!found) {
128
- for (let a = 0; a < args.length; a++) {
129
- if (consumed.has(a)) continue;
130
- if (isType(args[a], schema.type)) {
131
- result[schema.name] = args[a];
132
- consumed.add(a);
133
- found = true;
134
- break;
135
- }
136
- }
137
- }
138
-
139
- if (!found) {
140
- if (schema.optional) {
141
- result[schema.name] = undefined;
142
- } else {
143
- errors.push({
144
- name: schema.name,
145
- expected: schema.type,
146
- message: `missing required argument '${schema.name}' of type '${schema.type}' (loose mode)`,
147
- });
148
- }
149
- }
150
- }
151
-
152
- const success = errors.length === 0;
153
- const report = Object.assign({}, result, { __DidSucceed: success, __errors: errors });
154
- if (!success && debug) cM.log(["malle: loose-mode guess errors", errors], true);
155
- return report;
156
- }, strictModeGuessor(args, schemas, debug) {
157
- const result = {};
158
- const errors = [];
159
- let onArgCounter = 0;
160
-
161
- for (let s = 0; s < schemas.length; s++) {
162
- const schema = schemas[s];
163
- let found = false;
164
-
165
- // 1) try named object property or any alias
166
- for (let a = 0; a < args.length; a++) {
167
- if (isType(args[a], "object")) {
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
- }
175
- }
176
- if (found) break;
177
- }
178
- }
179
-
180
- if (!found) {
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;
186
- }
187
-
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
- }
205
- } else {
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
- }
231
- }
232
- }
233
- }
234
-
235
- const success = errors.length === 0;
236
- const report = Object.assign({}, result, { __DidSucceed: success, __errors: errors });
237
- if (!success && debug) cM.log(["malle: guess errors", errors], true);
238
- return report;
239
- },
240
- };
241
- //#endregion Helpers
242
- //-----------------------------------------------------------------------------------------------------------
243
- //#region Main Class
244
- /**
245
- * Class representing a Malle argument parser instance.
246
- */
247
- export default class MalleClass {
248
- #modes = ["strict", "loose"];
249
- #mode = "strict";
250
- #debug = false;
251
-
252
- #isValidMode(mode) {
253
- return this.#modes.includes(mode);
254
- }
255
-
256
- constructor(config = {}) {
257
- const { mode, debug } = config;
258
-
259
- if (!isUndefined(mode) && this.#isValidMode(mode)) {
260
- this.#mode = mode;
261
- }
262
-
263
- if (!isUndefined(debug)) {
264
- this.#debug = !!debug;
265
- }
266
- }
267
-
268
- setDebug(value) {
269
- this.#debug = !!value;
270
- return this;
271
- }
272
-
273
- setMode(mode) {
274
- if (this.#isValidMode(mode)) {
275
- this.#mode = mode;
276
- }
277
- return this;
278
- }
279
-
280
- /**
281
- * Define expected argument schemas
282
- */
283
- expect(...schemas) {
284
- const arr = expector.checkSchemas(schemas, this.#debug);
285
- if (!arr) return false;
286
-
287
- return {
288
- guess: (...args) =>
289
- guessor.guessBy(getArrFromSpread(args), arr, this.#mode, this.#debug),
290
- };
291
- }
292
- }
293
- //#endregion Main Class
294
- //-----------------------------------------------------------------------------------------------------------
package/core/main.js DELETED
@@ -1,46 +0,0 @@
1
- import MalleClass from "./class";
2
-
3
- /**
4
- * The **Malle** argument parser.
5
- *
6
- * Allows functions to accept arguments in **any order**, infer intent,
7
- * and validate inputs according to schemas.
8
- *
9
- * @param {Object} [config] - Optional configuration object.
10
- * @param {"strict"|"loose"} [config.mode="strict"] - Parsing mode.
11
- * - `"strict"`: matches types and positional args conservatively.
12
- * - `"loose"`: more permissive (order-agnostic, type-aliases such as `arr`/`array` allowed).
13
- * @param {boolean} [config.debug=false] - If `true`, prints debug info to console.
14
- *
15
- * @returns {MalleClass}
16
- *
17
- * @remarks
18
- * The object returned by `expect(...).guess(...)` contains the matched keys
19
- * plus two meta-keys:
20
- * - `__DidSucceed` (boolean) — true when all *required* schemas were satisfied
21
- * - `__errors` (Array) — list of reported issues when parsing failed
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
- *
28
- * @example
29
- * import malle from "@briklab/malle";
30
- *
31
- * function calc(...args) {
32
- * const { Number1, Number2, Mode, __DidSucceed } = malle({mode: "strict"})
33
- * .expect(
34
- * {name:"Number1", type:"number"},
35
- * {name:"Number2", type:"number"},
36
- * {name:"Mode", type:"string"}
37
- * );
38
- *
39
- * if(!__DidSucceed) return;
40
- * return Mode === "add" ? Number1 + Number2 : Number1 - Number2;
41
- * }
42
- */
43
- export default function malle(config) {
44
- return new MalleClass(config)
45
- }
46
-
@@ -1,14 +0,0 @@
1
- import isType from "./typeHelpers";
2
-
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
- }
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;
14
- }
@@ -1,22 +0,0 @@
1
- export const cM = {
2
- __name: "Malle",
3
- error: class extends Error {
4
- constructor(message, debug = false) {
5
- super(message);
6
- this.name = cM.__name;
7
-
8
- if (debug) {
9
- console.log(
10
- "%c[Malle]: Error: %c",
11
- "color:red;font-weight:bold;",
12
- message,
13
- ""
14
- );
15
- }
16
- }
17
- },
18
- log(message,debug){
19
- if(!debug)return
20
- console.log("%c[Malle] Log: %c","color:lime;font-weight:bold;","",message)
21
- }
22
- };
@@ -1 +0,0 @@
1
- export default function isUndefined(a){return typeof a === "undefined"}
package/helpers/loop.js DELETED
@@ -1 +0,0 @@
1
- export default function loop(t,f,...a){for(let i=0;i<t;i++){f(...a)}}
@@ -1,8 +0,0 @@
1
- import isType from "./typeHelpers";
2
-
3
- export default function checkFormat(format,object){
4
- for (const [key,value] of Object.entries(format)) {
5
- if(!isType(object[key],value)&&!isType(object[key],"undefined"))return false
6
- }
7
- return true
8
- }
@@ -1,50 +0,0 @@
1
- const typeofvalidtypes = [
2
- "boolean",
3
- "number",
4
- "function",
5
- "object",
6
- "string",
7
- "symbol",
8
- "undefined",
9
- ];
10
-
11
- const aliasMap = {
12
- arr: "array",
13
- array: "array",
14
- "[]": "array",
15
- null: "null",
16
- any: "any",
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
-
27
- export default function isType(stuff, type) {
28
- if (!type) return false;
29
- const raw = String(type).trim();
30
-
31
- // union (e.g. "string|number[]")
32
- if (raw.includes("|")) {
33
- return raw.split("|").map((p) => p.trim()).some((part) => isType(stuff, part));
34
- }
35
-
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;
45
-
46
- if (_isPrimitiveMatch(stuff, norm)) return true;
47
- if (raw === "Array") return Array.isArray(stuff);
48
-
49
- return false;
50
- }
package/index.cjs DELETED
@@ -1,3 +0,0 @@
1
- const main = require("./core/main.js");
2
-
3
- module.exports = main