jsfunx 1.0.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.
Files changed (5) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +256 -0
  3. package/jsfunx.cjs +1641 -0
  4. package/jsfunx.mjs +1646 -0
  5. package/package.json +32 -0
package/jsfunx.mjs ADDED
@@ -0,0 +1,1646 @@
1
+ /*
2
+
3
+ Copyright (c) 2025 Khiat Mohammed Abderrezzak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ Author: Khiat Mohammed Abderrezzak <khiat.dev@gmail.com>
24
+
25
+ */
26
+
27
+ // JavaScript utility functions for cleaner, more readable code
28
+
29
+ import { argv } from "process";
30
+
31
+ import { basename } from "path";
32
+
33
+ import { fileURLToPath } from "url";
34
+
35
+ /** @type {string} */
36
+
37
+ export const and = "and";
38
+
39
+ /** @type {string} */
40
+
41
+ export const or = "or";
42
+
43
+ /** @type {string} */
44
+
45
+ export const bigint = "bigint";
46
+
47
+ /** @type {string} */
48
+
49
+ export const func = "function";
50
+
51
+ /** @type {string} */
52
+
53
+ export const number = "number";
54
+
55
+ /** @type {string} */
56
+
57
+ export const object = "object";
58
+
59
+ /** @type {string} */
60
+
61
+ export const string = "string";
62
+
63
+ /** @type {string} */
64
+
65
+ export const symbol = "symbol";
66
+
67
+ /** @type {string} */
68
+
69
+ export const boolean = "boolean";
70
+
71
+ /** @type {string} */
72
+
73
+ export const undef = "undefined";
74
+
75
+ /**
76
+ * Compares a value or a list of values against a given type (or list of types),
77
+ * similar to using `typeof x === y`, but in a more flexible and readable way.
78
+ *
79
+ * Supports single values or arrays, and can combine results using logical
80
+ * operators like `"and"` or `"or"`.
81
+ *
82
+ * @param {* | Array<*>} data - The value or array of values to check.
83
+ * @param {* | Array<*>} dataType - The expected type or array of expected types.
84
+ * @param {"and" | "or"} logic - Logic mode: `"and"` requires all matches, `"or"` allows any match.
85
+ * @param {boolean} match - Whether to compare type names strictly or loosely.
86
+ * @param {boolean} strict - If `true`, enforces strict comparison rules.
87
+ * @returns {boolean} `true` if the data matches the expected type(s) according to the given logic, otherwise `false`.
88
+ */
89
+
90
+ function checkType(data, dataType, logic, match, strict) {
91
+ // if data or dataType is Array must be nested Array
92
+
93
+ if (!(dataType instanceof Array)) {
94
+ if (!(data instanceof Array)) {
95
+ return typeof data === dataType;
96
+ }
97
+
98
+ if (logic === and) {
99
+ for (let dt of data) {
100
+ // 1 step directly in all cases
101
+
102
+ if (!(typeof dt === dataType)) {
103
+ return false;
104
+ }
105
+
106
+ // or using recursion (one to one)
107
+ // 2 steps
108
+
109
+ // if (!checkType(dt, dataType, logic, match, strict)) {
110
+ // return false;
111
+ // }
112
+ }
113
+
114
+ return true;
115
+ }
116
+
117
+ if (logic === or) {
118
+ for (let dt of data) {
119
+ // 1 step directly in all cases
120
+
121
+ if (typeof dt === dataType) {
122
+ return true;
123
+ }
124
+
125
+ // or using recursion (one to one)
126
+ // 2 steps
127
+
128
+ // if (checkType(dt, dataType, logic, match, strict)) {
129
+ // return true;
130
+ // }
131
+ }
132
+
133
+ return false;
134
+ }
135
+
136
+ throw new TypeError("logic type undefined !");
137
+ }
138
+
139
+ if (!(data instanceof Array)) {
140
+ for (let dtype of dataType) {
141
+ // 1 step directly in all cases
142
+
143
+ if (typeof data === dtype) {
144
+ return true;
145
+ }
146
+
147
+ // or using recursion if (one to one) enabled
148
+ // 2 steps
149
+
150
+ // if (checkType(data, dtype, logic, match, strict)) {
151
+ // return true;
152
+ // }
153
+ }
154
+
155
+ return false;
156
+ }
157
+
158
+ if (!match) {
159
+ if (logic === and) {
160
+ outerLoop: for (let dt of data) {
161
+ for (let dtype of dataType) {
162
+ if (typeof dt === dtype) {
163
+ continue outerLoop;
164
+ }
165
+ }
166
+
167
+ return false;
168
+ }
169
+
170
+ return true;
171
+
172
+ // or like this handling each case individually using recursion
173
+
174
+ // checking relationship type in this case (one to many)
175
+
176
+ // if (data.length < 2) {
177
+ // // [x], [y, z] => x, [y, z] (one to many)
178
+
179
+ // // we can call isType(data[0], dataType) in place of
180
+ // // checkType(data[0], dataType, logic, match, strict) but take
181
+ // // one step plus
182
+
183
+ // // 2 steps (one to one)
184
+ // if (!checkType(data[0], dataType, logic, match, strict)) {
185
+ // return false;
186
+ // }
187
+
188
+ // return true;
189
+
190
+ // // 1 step directly using (one to one)
191
+
192
+ // // for (let dtype of dataType) {
193
+ // // if (checkType(data[0], dtype, logic, match, strict)) {
194
+ // // return true;
195
+ // // }
196
+
197
+ // // // the same as this
198
+
199
+ // // // if (typeof data[0] === dtype) {
200
+ // // // return true;
201
+ // // // }
202
+ // // }
203
+
204
+ // // return false;
205
+ // }
206
+
207
+ // checking relationship type in this case (many to one)
208
+
209
+ // if (dataType.length < 2) {
210
+ // // [x, y], [z] => [x, y], z (many to one)
211
+
212
+ // // we can call isType(data, dataType[0]) in place of
213
+ // // checkType(data, dataType[0], logic, match, strict) but take
214
+ // // one step plus
215
+
216
+ // // 2 steps (one to one)
217
+
218
+ // if (!checkType(data, dataType[0], logic, match, strict)) {
219
+ // return false;
220
+ // }
221
+
222
+ // // 1 step directly (one to one)
223
+
224
+ // // for (let dt of data) {
225
+ // // if (!checkType(dt, dataType[0], logic, match, strict)) {
226
+ // // return false;
227
+ // // }
228
+
229
+ // // // the same as this
230
+
231
+ // // // if (!(typeof dt === dataType[0])) {
232
+ // // // return false;
233
+ // // // }
234
+ // // }
235
+
236
+ // // shared return
237
+
238
+ // return true;
239
+ // }
240
+
241
+ // checking relationship type in this case (many to many)
242
+
243
+ // we can embeds this in recursive (one to many) with the condition
244
+ // if (data.length < 2 || (data.length > 1 && dataType.length > 1)){
245
+ // with adding a for loop for any number of data at least one
246
+ // and replacing data[0] with dt from the loop and remove the next
247
+ // condition if (dataType.length < 2) {} because it's the last one no
248
+ // more conditions after it run the nested condition directly
249
+ // and set just one global true in the last of the two logics
250
+
251
+ // for (let dt of data) {
252
+ // // [w, x], [y, z] => w, [y, z]
253
+ // // [w, x], [y, z] => x, [y, z]
254
+ // // (one to many) 1 step directly
255
+
256
+ // // we can call isType(dt, dataType) in place of
257
+ // // checkType(dt, dataType, logic, match, strict) but take
258
+ // // one step plus
259
+
260
+ // if (!checkType(dt, dataType, logic, match, strict)) {
261
+ // return false;
262
+ // }
263
+ // }
264
+
265
+ // return true;
266
+
267
+ // checking relationship type in this case (many to many)
268
+
269
+ // for (let dt of data) {
270
+ // // [w, x], [y, z] => [w], [y, z]
271
+ // // [w, x], [y, z] => [x], [y, z]
272
+ // // (one to many) 2 steps
273
+
274
+ // // we can call isType([dt], dataType, match) in place of
275
+ // // checkType([dt], dataType, logic, match, strict) but take
276
+ // // one step plus
277
+
278
+ // if (!checkType([dt], dataType, logic, match, strict)) {
279
+ // return false;
280
+ // }
281
+ // }
282
+
283
+ // return true;
284
+ }
285
+
286
+ if (logic === or) {
287
+ for (let dt of data) {
288
+ for (let dtype of dataType) {
289
+ if (typeof dt === dtype) {
290
+ return true;
291
+ }
292
+ }
293
+ }
294
+ return false;
295
+
296
+ // or like this handling each case individually using recursion
297
+
298
+ // checking relationship type in this case (one to many)
299
+
300
+ // if (data.length < 2) {
301
+ // // [x], [y, z] => x, [y, z] (one to many)
302
+
303
+ // // we can call isType(data[0], dataType, logic) in place of
304
+ // // checkType(data[0], dataType, logic, match, strict) but take
305
+ // // one step plus
306
+
307
+ // // 2 steps (one to one)
308
+
309
+ // if (checkType(data[0], dataType, logic, match, strict)) {
310
+ // return true;
311
+ // }
312
+
313
+ // // 1 step directly using (one to one)
314
+
315
+ // // for (let dtype of dataType) {
316
+ // // if (checkType(data[0], dtype, logic, match, strict)) {
317
+ // // return true;
318
+ // // }
319
+
320
+ // // // the same as this
321
+
322
+ // // // if (typeof data[0] === dtype) {
323
+ // // // return true;
324
+ // // // }
325
+ // // }
326
+
327
+ // // shared return
328
+
329
+ // return false;
330
+ // }
331
+
332
+ // checking relationship type in this case (many to one)
333
+
334
+ // if (dataType.length < 2) {
335
+ // // [x, y], [z] => [x, y], z (many to one)
336
+
337
+ // // we can call isType(data, dataType[0], logic) in place of
338
+ // // checkType(data, dataType[0], logic, match, strict) but take
339
+ // // one step plus
340
+
341
+ // // 2 steps (one to one)
342
+
343
+ // if (checkType(data, dataType[0], logic, match, strict)) {
344
+ // return true;
345
+ // }
346
+
347
+ // // 1 step directly using (one to one)
348
+
349
+ // // for (let dt of data) {
350
+ // // if (checkType(dt, dataType[0], logic, match, strict)) {
351
+ // // return true;
352
+ // // }
353
+
354
+ // // // the same as this
355
+
356
+ // // if (typeof dt === dataType[0]) {
357
+ // // return true;
358
+ // // }
359
+ // // }
360
+
361
+ // // shared return
362
+
363
+ // return false;
364
+ // }
365
+
366
+ // checking relationship type in this case (many to many)
367
+
368
+ // we can embeds this in recursive (one to many) with the condition
369
+ // if (data.length < 2 || (data.length > 1 && dataType.length > 1)){
370
+ // with adding a for loop for any number of data at least one
371
+ // and replacing data[0] with dt from the loop and remove the next
372
+ // condition if (dataType.length < 2) {} because it's the last one no
373
+ // more conditions after it run the nested condition directly
374
+ // and set just one global true in the last of the two logics
375
+
376
+ // for (let dt of data) {
377
+ // // [w, x], [y, z] => w, [y, z]
378
+ // // [w, x], [y, z] => x, [y, z]
379
+ // // (one to many) 1 step directly & 2 steps if (one to one)
380
+
381
+ // // we can call isType(dt, dataType, logic) in place of
382
+ // // checkType(dt, dataType, logic, match, strict) but take
383
+ // // one step plus
384
+
385
+ // if (checkType(dt, dataType, logic, match, strict)) {
386
+ // return true;
387
+ // }
388
+ // }
389
+
390
+ // return false;
391
+
392
+ // checking relationship type in this case (many to many)
393
+
394
+ // for (let dtype of dataType) {
395
+ // // [w, x], [y, z] => [w, x], y
396
+ // // [w, x], [y, z] => [w, x], z
397
+ // // (many to one) 1 step directly & 2 steps (one to one)
398
+
399
+ // // we can call isType(data, dtype, logic) in place of
400
+ // // checkType(data, dtype, logic, match, strict) but take
401
+ // // one step plus
402
+
403
+ // if (checkType(data, dtype, logic, match, strict)) {
404
+ // return true;
405
+ // }
406
+ // }
407
+
408
+ // return false;
409
+
410
+ // checking relationship type in this case (many to many)
411
+
412
+ // for (let dt of data) {
413
+ // // [w, x], [y, z] => [w], [y, z]
414
+ // // [w, x], [y, z] => [x], [y, z]
415
+ // // (one to many) 2 steps & 3 steps (one to one)
416
+
417
+ // // we can call isType([dt], dataType, logic, match) in place of
418
+ // // checkType([dt], dataType, logic, match, strict) but take
419
+ // // one step plus
420
+
421
+ // if (checkType([dt], dataType, logic, match, strict)) {
422
+ // return true;
423
+ // }
424
+ // }
425
+
426
+ // return false;
427
+
428
+ // checking relationship type in this case (many to many)
429
+
430
+ // for (let dtype of dataType) {
431
+ // // [w, x], [y, z] => [w, x], [y]
432
+ // // [w, x], [y, z] => [w, x], [z]
433
+ // // (many to one) 2 steps & 3 steps (one to one)
434
+
435
+ // // we can call isType(data, [dtype], logic, match) in place of
436
+ // // checkType(data, [dtype], logic, match, strict) but take
437
+ // // one step plus
438
+
439
+ // if (checkType(data, [dtype], logic, match, strict)) {
440
+ // return true;
441
+ // }
442
+ // }
443
+
444
+ // return false;
445
+ }
446
+
447
+ throw new TypeError("logic type undefined !");
448
+ }
449
+
450
+ if (data.length !== dataType.length) {
451
+ if (strict) {
452
+ throw new TypeError(
453
+ "length of array of dataTypes must match number of data"
454
+ );
455
+ }
456
+
457
+ /** @type {number} */
458
+
459
+ let min;
460
+
461
+ if (data.length < dataType.length) {
462
+ min = data.length;
463
+ } else {
464
+ min = dataType.length;
465
+ }
466
+
467
+ if (logic === and) {
468
+ for (let i = 0; i < min; ++i) {
469
+ // 1 step directly in all cases
470
+
471
+ if (!(typeof data[i] === dataType[i])) {
472
+ return false;
473
+ }
474
+
475
+ // or using recursion (one to one)
476
+ // 2 steps
477
+
478
+ // if (!checkType(data[i], dataType[i], logic, match, strict)) {
479
+ // return false;
480
+ // }
481
+ }
482
+
483
+ // shared return
484
+
485
+ return true;
486
+ }
487
+
488
+ if (logic === or) {
489
+ for (let i = 0; i < min; ++i) {
490
+ // 1 step directly in all cases
491
+
492
+ if (typeof data[i] === dataType[i]) {
493
+ return true;
494
+ }
495
+
496
+ // or using recursion (one to one)
497
+ // 2 steps
498
+
499
+ // if (checkType(data[i], dataType[i], logic, match, strict)) {
500
+ // return true;
501
+ // }
502
+ }
503
+
504
+ // shared return
505
+
506
+ return false;
507
+ }
508
+
509
+ throw new TypeError("logic type undefined !");
510
+ }
511
+
512
+ if (logic === and) {
513
+ for (let i = 0; i < data.length; ++i) {
514
+ // 1 step directly in all cases
515
+
516
+ if (!(typeof data[i] === dataType[i])) {
517
+ return false;
518
+ }
519
+
520
+ // or using recursion (one to one)
521
+ // 2 steps
522
+
523
+ // if (!checkType(data[i], dataType[i], logic, match, strict)) {
524
+ // return false;
525
+ // }
526
+ }
527
+
528
+ // shared return
529
+
530
+ return true;
531
+ }
532
+
533
+ if (logic === or) {
534
+ for (let i = 0; i < data.length; ++i) {
535
+ // 1 step directly in all cases
536
+
537
+ if (typeof data[i] === dataType[i]) {
538
+ return true;
539
+ }
540
+
541
+ // or using recursion (one to one)
542
+ // 2 steps
543
+
544
+ // if (checkType(data[i], dataType[i], logic, match, strict)) {
545
+ // return true;
546
+ // }
547
+ }
548
+
549
+ // shared return
550
+
551
+ return false;
552
+ }
553
+
554
+ throw new TypeError("logic type undefined !");
555
+ }
556
+
557
+ /**
558
+ * Checks whether one or more objects are instances of a given class (or classes),
559
+ * similar to using the `instanceof` operator — but extended to handle arrays
560
+ * and multiple constructors with logical control.
561
+ *
562
+ * Works like a plural form of `instanceof`, allowing you to check several objects
563
+ * against one or more classes, using `"and"` or `"or"` logic.
564
+ *
565
+ * @template T - The instance type returned by the constructor(s).
566
+ * @param {* | Array<*>} objs - The object or array of objects to test.
567
+ * @param {(new (...args: any[]) => T) | Array<new (...args: any[]) => T>} type - The constructor or list of constructors to test against.
568
+ * @param {"and" | "or"} logic - Logic mode: `"and"` requires all matches, `"or"` allows any match.
569
+ * @param {boolean} match - Whether to compare classes names strictly or loosely.
570
+ * @param {boolean} strict - If `true`, enables strict comparison behavior.
571
+ * @returns {boolean} `true` if the provided object(s) match the given type(s) according to the chosen logic, otherwise `false`.
572
+ */
573
+
574
+ function isinstances(objs, type, logic, match, strict) {
575
+ if (!(type instanceof Array)) {
576
+ // uncomment (return objs instanceof type)
577
+ // to enable the 1/4 of (one to one)
578
+
579
+ if (!(objs instanceof Array)) {
580
+ // return objs instanceof type;
581
+
582
+ throw new TypeError(
583
+ `instancesof expected at least : 2 objects as first argument 'objs' or 2 types as second argument 'type', got 1`
584
+ );
585
+ }
586
+
587
+ // remove this (if) condition to enable the 1/4 of (one to one)
588
+
589
+ if (objs.length < 2) {
590
+ throw new TypeError(
591
+ `instancesof expected at least 2 objects as first argument 'objs', got 1`
592
+ );
593
+ }
594
+
595
+ if (logic === and) {
596
+ for (let obj of objs) {
597
+ // 1 step directly in all cases
598
+
599
+ try {
600
+ if (!(obj instanceof type)) {
601
+ return false;
602
+ }
603
+ } catch (error) {
604
+ throw new TypeError(
605
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
606
+ );
607
+ }
608
+
609
+ // or using recursion if (one to one) enabled
610
+ // 2 steps
611
+
612
+ // if (!isinstances(obj, type, logic, match, strict)) {
613
+ // return false;
614
+ // }
615
+ }
616
+
617
+ return true;
618
+ }
619
+
620
+ if (logic === or) {
621
+ for (let obj of objs) {
622
+ // 1 step directly in all cases
623
+
624
+ try {
625
+ if (obj instanceof type) {
626
+ return true;
627
+ }
628
+ } catch (error) {
629
+ throw new TypeError(
630
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
631
+ );
632
+ }
633
+
634
+ // or using recursion if (one to one) enabled
635
+ // 2 steps
636
+
637
+ // if (isinstances(obj, type, logic, match, strict)) {
638
+ // return true;
639
+ // }
640
+ }
641
+
642
+ return false;
643
+ }
644
+
645
+ throw new TypeError("logic type undefined !");
646
+ }
647
+
648
+ if (!(objs instanceof Array)) {
649
+ // remove this (if) condition to enable the 1/4 of (one to one)
650
+
651
+ if (type.length < 2) {
652
+ throw new TypeError(
653
+ `instancesof expected at least 2 types as second argument 'type', got 1`
654
+ );
655
+ }
656
+
657
+ for (let dt of type) {
658
+ // 1 step directly in all cases
659
+
660
+ try {
661
+ if (objs instanceof dt) {
662
+ return true;
663
+ }
664
+ } catch (error) {
665
+ throw new TypeError(
666
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
667
+ );
668
+ }
669
+
670
+ // or using recursion if (one to one) enabled
671
+ // 2 steps
672
+
673
+ // if (isinstances(objs, dt, logic, match, strict)) {
674
+ // return true;
675
+ // }
676
+ }
677
+
678
+ return false;
679
+ }
680
+
681
+ // remove this (if) to enable 1/4 of (one to one)
682
+
683
+ if (objs.length < 2 && type.length < 2) {
684
+ throw new TypeError(
685
+ `instancesof expected at least : 2 objects as first argument 'objs' or 2 types as second argument 'type', got 1`
686
+ );
687
+ }
688
+
689
+ if (!match) {
690
+ if (logic === and) {
691
+ outerLoop: for (let obj of objs) {
692
+ for (let dt of type) {
693
+ try {
694
+ if (obj instanceof dt) {
695
+ continue outerLoop;
696
+ }
697
+ } catch (error) {
698
+ throw new TypeError(
699
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
700
+ );
701
+ }
702
+ }
703
+
704
+ return false;
705
+ }
706
+
707
+ return true;
708
+
709
+ // or like this handling each case individually using recursion
710
+
711
+ // checking relationship type in this case (one to many)
712
+
713
+ // if (objs.length < 2) {
714
+ // // [x], [y, z] => x, [y, z] (one to many)
715
+
716
+ // // we can call instancesof(objs[0], type) in place of
717
+ // // isinstances(objs[0], type, logic, match, strict) but take
718
+ // // one step plus
719
+
720
+ // // 2 steps if (one to one) enabled
721
+ // if (!isinstances(objs[0], type, logic, match, strict)) {
722
+ // return false;
723
+ // }
724
+
725
+ // return true;
726
+
727
+ // // 1 step directly using (one to one) if enabled
728
+
729
+ // // for (let dt of type) {
730
+ // // if (isinstances(objs[0], dt, logic, match, strict)) {
731
+ // // return true;
732
+ // // }
733
+
734
+ // // // the same as this
735
+
736
+ // // // if (objs[0] instanceof dt) {
737
+ // // // return true;
738
+ // // // }
739
+ // // }
740
+
741
+ // // return false;
742
+ // }
743
+
744
+ // checking relationship type in this case (many to one)
745
+
746
+ // if (type.length < 2) {
747
+ // // [x, y], [z] => [x, y], z (many to one)
748
+
749
+ // // we can call instancesof(objs, type[0]) in place of
750
+ // // isinstances(objs, type[0], logic, match, strict) but take
751
+ // // one step plus
752
+
753
+ // // 2 steps if (one to one) enabled
754
+
755
+ // if (!isinstances(objs, type[0], logic, match, strict)) {
756
+ // return false;
757
+ // }
758
+
759
+ // // 1 step directly using (one to one) if enabled
760
+
761
+ // // for (let obj of objs) {
762
+ // // if (!isinstances(obj, type[0], logic, match, strict)) {
763
+ // // return false;
764
+ // // }
765
+
766
+ // // // the same as this
767
+
768
+ // // // if (!(obj instanceof type[0])) {
769
+ // // // return false;
770
+ // // // }
771
+ // // }
772
+
773
+ // // shared return
774
+
775
+ // return true;
776
+ // }
777
+
778
+ // checking relationship type in this case (many to many)
779
+
780
+ // we can embeds this in recursive (one to many) with the condition
781
+ // if (objs.length < 2 || (objs.length > 1 && type.length > 1)){
782
+ // with adding a for loop for any number of objs at least one
783
+ // and replacing objs[0] with obj from the loop and remove the next
784
+ // condition if (type.length < 2) {} because it's the last one no
785
+ // more conditions after it run the nested condition directly
786
+ // and set just one global true in the last of the two logics
787
+
788
+ // for (let obj of objs) {
789
+ // // [w, x], [y, z] => w, [y, z]
790
+ // // [w, x], [y, z] => x, [y, z]
791
+ // // (one to many) (1 step directly)
792
+
793
+ // // we can call instancesof(obj, type) in place of
794
+ // // isinstances(obj, type, logic, match, strict) but take
795
+ // // one step plus
796
+
797
+ // if (!isinstances(obj, type, logic, match, strict)) {
798
+ // return false;
799
+ // }
800
+ // }
801
+
802
+ // return true;
803
+
804
+ // checking relationship type in this case (many to many)
805
+
806
+ // for (let obj of objs) {
807
+ // // [w, x], [y, z] => [w], [y, z]
808
+ // // [w, x], [y, z] => [x], [y, z]
809
+ // // (one to many) (2 steps)
810
+
811
+ // // we can call instancesof([obj], type, match) in place of
812
+ // // isinstances([obj], type, logic, match, strict) but take
813
+ // // one step plus
814
+
815
+ // if (!isinstances([obj], type, logic, match, strict)) {
816
+ // return false;
817
+ // }
818
+ // }
819
+
820
+ // return true;
821
+ }
822
+
823
+ if (logic === or) {
824
+ for (let obj of objs) {
825
+ for (let dt of type) {
826
+ try {
827
+ if (obj instanceof dt) {
828
+ return true;
829
+ }
830
+ } catch (error) {
831
+ throw new TypeError(
832
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
833
+ );
834
+ }
835
+ }
836
+ }
837
+ return false;
838
+
839
+ // or like this handling each case individually using recursion
840
+
841
+ // checking relationship type in this case (one to many)
842
+
843
+ // if (objs.length < 2) {
844
+ // // [x], [y, z] => x, [y, z] (one to many)
845
+
846
+ // // we can call instancesof(objs[0], type, logic) in place of
847
+ // // isinstances(objs[0], type, logic, match, strict) but take
848
+ // // one step plus
849
+
850
+ // // 2 steps if (one to one) enabled
851
+
852
+ // if (isinstances(objs[0], type, logic, match, strict)) {
853
+ // return true;
854
+ // }
855
+
856
+ // // // 1 step directly using (one to one) if enabled
857
+
858
+ // // for (let dt of type) {
859
+ // // if (isinstances(objs[0], dt, logic, match, strict)) {
860
+ // // return true;
861
+ // // }
862
+
863
+ // // // the same as this
864
+
865
+ // // // if (objs[0] instanceof dt) {
866
+ // // // return true;
867
+ // // // }
868
+ // // }
869
+
870
+ // // shared return
871
+
872
+ // return false;
873
+ // }
874
+
875
+ // checking relationship type in this case (many to one)
876
+
877
+ // if (type.length < 2) {
878
+ // // [x, y], [z] => [x, y], z (many to one)
879
+
880
+ // // we can call instancesof(objs, type[0], logic) in place of
881
+ // // isinstances(objs, type[0], logic, match, strict) but take
882
+ // // one step plus
883
+
884
+ // // 2 steps if (one to one) enabled
885
+
886
+ // if (isinstances(objs, type[0], logic, match, strict)) {
887
+ // return true;
888
+ // }
889
+
890
+ // // // 1 step directly using (one to one) if enabled
891
+
892
+ // // for (let obj of objs) {
893
+ // // if (isinstances(obj, type[0], logic, match, strict)) {
894
+ // // return true;
895
+ // // }
896
+
897
+ // // // the same as this
898
+
899
+ // // // if (obj instanceof type[0]) {
900
+ // // // return true;
901
+ // // // }
902
+ // // }
903
+
904
+ // // shared return
905
+
906
+ // return false;
907
+ // }
908
+
909
+ // checking relationship type in this case (many to many)
910
+
911
+ // we can embeds this in recursive (one to many) with the condition
912
+ // if (objs.length < 2 || (objs.length > 1 && type.length > 1)){
913
+ // with adding a for loop for any number of objs at least one
914
+ // and replacing objs[0] with obj from the loop and remove the next
915
+ // condition if (type.length < 2) {} because it's the last one no
916
+ // more conditions after it run the nested condition directly
917
+ // and set just one global true in the last of the two logics
918
+
919
+ // for (let obj of objs) {
920
+ // // [w, x], [y, z] => w, [y, z]
921
+ // // [w, x], [y, z] => x, [y, z]
922
+ // // (one to many) (1 step directly) & (2 steps if (one to one) enabled)
923
+
924
+ // // we can call instancesof(obj, type, logic) in place of
925
+ // // isinstances(obj, type, logic, match, strict) but take
926
+ // // one step plus
927
+
928
+ // if (isinstances(obj, type, logic, match, strict)) {
929
+ // return true;
930
+ // }
931
+ // }
932
+
933
+ // return false;
934
+
935
+ // checking relationship type in this case (many to many)
936
+
937
+ // for (let dt of type) {
938
+ // // [w, x], [y, z] => [w, x], y
939
+ // // [w, x], [y, z] => [w, x], z
940
+ // // (many to one) (1 step directly) & (2 steps if (one to one) enabled)
941
+
942
+ // // we can call instancesof(objs, dt, logic) in place of
943
+ // // isinstances(objs, dt, logic, match, strict) but take
944
+ // // one step plus
945
+
946
+ // if (isinstances(objs, dt, logic, match, strict)) {
947
+ // return true;
948
+ // }
949
+ // }
950
+
951
+ // return false;
952
+
953
+ // checking relationship type in this case (many to many)
954
+
955
+ // for (let obj of objs) {
956
+ // // [w, x], [y, z] => [w], [y, z]
957
+ // // [w, x], [y, z] => [x], [y, z]
958
+ // // (one to many) (2 steps) & (3 steps if (one to one) enabled)
959
+
960
+ // // we can call instancesof([obj], type, logic, match) in place of
961
+ // // isinstances([obj], type, logic, match, strict) but take
962
+ // // one step plus
963
+
964
+ // if (isinstances([obj], type, logic, match, strict)) {
965
+ // return true;
966
+ // }
967
+ // }
968
+
969
+ // return false;
970
+
971
+ // checking relationship type in this case (many to many)
972
+
973
+ // for (let dt of type) {
974
+ // // [w, x], [y, z] => [w, x], [y]
975
+ // // [w, x], [y, z] => [w, x], [z]
976
+ // // (many to one) (2 steps) & (3 steps if (one to one) enabled)
977
+
978
+ // // we can call instancesof(objs, [dt], logic, match) in place of
979
+ // // isinstances(objs, [dt], logic, match, strict) but take
980
+ // // one step plus
981
+
982
+ // if (isinstances(objs, [dt], logic, match, strict)) {
983
+ // return true;
984
+ // }
985
+ // }
986
+
987
+ // return false;
988
+ }
989
+
990
+ throw new TypeError("logic type undefined !");
991
+ }
992
+
993
+ if (objs.length !== type.length) {
994
+ if (strict) {
995
+ throw new TypeError(
996
+ "length of array of types must match number of objects"
997
+ );
998
+ }
999
+
1000
+ /** @type {number} */
1001
+
1002
+ let min;
1003
+
1004
+ if (objs.length < type.length) {
1005
+ min = objs.length;
1006
+ } else {
1007
+ min = type.length;
1008
+ }
1009
+
1010
+ if (logic === and) {
1011
+ for (let i = 0; i < min; ++i) {
1012
+ // 1 step directly in all cases
1013
+
1014
+ try {
1015
+ if (!(objs[i] instanceof type[i])) {
1016
+ return false;
1017
+ }
1018
+ } catch (error) {
1019
+ throw new TypeError(
1020
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
1021
+ );
1022
+ }
1023
+
1024
+ // or using recursion if (one to one) enabled
1025
+ // 2 steps
1026
+
1027
+ // if (!isinstances(objs[i], type[i], logic, match, strict)) {
1028
+ // return false;
1029
+ // }
1030
+ }
1031
+
1032
+ // shared return
1033
+
1034
+ return true;
1035
+ }
1036
+
1037
+ if (logic === or) {
1038
+ for (let i = 0; i < min; ++i) {
1039
+ // 1 step directly in all cases
1040
+
1041
+ try {
1042
+ if (objs[i] instanceof type[i]) {
1043
+ return true;
1044
+ }
1045
+ } catch (error) {
1046
+ throw new TypeError(
1047
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
1048
+ );
1049
+ }
1050
+
1051
+ // or using recursion if (one to one) enabled
1052
+ // 2 steps
1053
+
1054
+ // if (isinstances(objs[i], type[i], logic, match, strict)) {
1055
+ // return true;
1056
+ // }
1057
+ }
1058
+
1059
+ // shared return
1060
+
1061
+ return false;
1062
+ }
1063
+
1064
+ throw new TypeError("logic type undefined !");
1065
+ }
1066
+
1067
+ if (logic === and) {
1068
+ for (let i = 0; i < objs.length; ++i) {
1069
+ // 1 step directly in all cases
1070
+
1071
+ try {
1072
+ if (!(objs[i] instanceof type[i])) {
1073
+ return false;
1074
+ }
1075
+ } catch (error) {
1076
+ throw new TypeError(
1077
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
1078
+ );
1079
+ }
1080
+
1081
+ // or using recursion if (one to one) enabled
1082
+ // 2 steps
1083
+
1084
+ // if (!isinstances(objs[i], type[i], logic, match, strict)) {
1085
+ // return false;
1086
+ // }
1087
+ }
1088
+
1089
+ // shared return
1090
+
1091
+ return true;
1092
+ }
1093
+
1094
+ if (logic === or) {
1095
+ for (let i = 0; i < objs.length; ++i) {
1096
+ // 1 step directly in all cases
1097
+
1098
+ try {
1099
+ if (objs[i] instanceof type[i]) {
1100
+ return true;
1101
+ }
1102
+ } catch (error) {
1103
+ throw new TypeError(
1104
+ "second argument of 'instancesof' is not a type/class or array of types/classes"
1105
+ );
1106
+ }
1107
+
1108
+ // or using recursion if (one to one) enabled
1109
+ // 2 steps
1110
+
1111
+ // if (isinstances(objs[i], type[i], logic, match, strict)) {
1112
+ // return true;
1113
+ // }
1114
+ }
1115
+
1116
+ // shared return
1117
+
1118
+ return false;
1119
+ }
1120
+
1121
+ throw new TypeError("logic type undefined !");
1122
+ }
1123
+
1124
+ /**
1125
+ * Checks whether one or more objects are instances of a given class (or classes),
1126
+ * similar to using the `instanceof` operator — but extended to handle arrays
1127
+ * and multiple constructors with logical control.
1128
+ *
1129
+ * Works like a plural form of `instanceof`, allowing you to check several objects
1130
+ * against one or more classes, using `"and"` or `"or"` logic.
1131
+ *
1132
+ * @template T - The instance type returned by the constructor(s).
1133
+ * @param {*|Array<*>} objs - The object or array of objects to test.
1134
+ * @param {(new (...args: any[]) => T) | Array<new (...args: any[]) => T>} type - The constructor or list of constructors to test against.
1135
+ * @param {"and" | "or" | boolean} logic_or_match - Logic mode: `"and"` requires all matches, `"or"` allows any match.
1136
+ * @param {boolean} match_or_strict - Whether to compare classes names strictly or loosely.
1137
+ * @param {boolean} strict - If `true`, enables strict comparison behavior.
1138
+ * @returns {boolean} `true` if the provided object(s) match the given type(s) according to the chosen logic, otherwise `false`.
1139
+ */
1140
+
1141
+ export function instancesof(
1142
+ objs,
1143
+ type,
1144
+ logic_or_match = and,
1145
+ match_or_strict = true,
1146
+ strict = true
1147
+ ) {
1148
+ /** @type {number} */
1149
+
1150
+ let len = arguments.length;
1151
+
1152
+ if (len < 2) {
1153
+ throw new TypeError(
1154
+ len == 1
1155
+ ? "instancesof() missing 1 required arguments: 'type'"
1156
+ : "instancesof() missing 2 required arguments: 'objs' and 'type'"
1157
+ );
1158
+ }
1159
+
1160
+ if (len > 5) {
1161
+ throw new TypeError(
1162
+ `instancesof() takes 5 positional arguments but ${len} were given`
1163
+ );
1164
+ }
1165
+
1166
+ if (len === 3) {
1167
+ switch (arguments[2]) {
1168
+ case true:
1169
+ case false:
1170
+ return isinstances(objs, type, and, logic_or_match, strict);
1171
+ }
1172
+ }
1173
+
1174
+ if (len === 4) {
1175
+ switch (arguments[3]) {
1176
+ case true:
1177
+ case false:
1178
+ break;
1179
+ default:
1180
+ throw new TypeError("forth argument must be true or false");
1181
+ }
1182
+
1183
+ switch (arguments[2]) {
1184
+ case true:
1185
+ case false:
1186
+ return isinstances(objs, type, and, logic_or_match, match_or_strict);
1187
+ }
1188
+ }
1189
+
1190
+ return isinstances(objs, type, logic_or_match, match_or_strict, strict);
1191
+ }
1192
+
1193
+ /**
1194
+ * Compares a value or a list of values against a given type (or list of types),
1195
+ * similar to using `typeof x === y`, but in a more flexible and readable way.
1196
+ *
1197
+ * Supports single values or arrays, and can combine results using logical
1198
+ * operators like `"and"` or `"or"`.
1199
+ *
1200
+ * @param {* | Array<*>} data - The value or array of values to check.
1201
+ * @param {* | Array<*>} dataType - The expected type or array of expected types.
1202
+ * @param {"and" | "or" | boolean} logic_or_match - Logic mode: `"and"` requires all matches, `"or"` allows any match.
1203
+ * @param {boolean} match_or_strict - Whether to compare type names strictly or loosely.
1204
+ * @param {boolean} strict - If `true`, enforces strict comparison rules.
1205
+ * @returns {boolean} `true` if the data matches the expected type(s) according to the given logic, otherwise `false`.
1206
+ */
1207
+
1208
+ export function isType(
1209
+ data,
1210
+ dataType,
1211
+ logic_or_match = and,
1212
+ match_or_strict = true,
1213
+ strict = true
1214
+ ) {
1215
+ /** @type {number} */
1216
+
1217
+ let len = arguments.length;
1218
+
1219
+ if (len < 2) {
1220
+ throw new TypeError(
1221
+ len == 1
1222
+ ? "isType() missing 1 required arguments: 'dataType'"
1223
+ : "isType() missing 2 required arguments: 'data' and 'dataType'"
1224
+ );
1225
+ }
1226
+
1227
+ if (len > 5) {
1228
+ throw new TypeError(
1229
+ `isType() takes 5 positional arguments but ${len} were given`
1230
+ );
1231
+ }
1232
+
1233
+ if (len === 3) {
1234
+ switch (arguments[2]) {
1235
+ case true:
1236
+ case false:
1237
+ return checkType(data, dataType, and, logic_or_match, strict);
1238
+ }
1239
+ }
1240
+
1241
+ if (len === 4) {
1242
+ switch (arguments[3]) {
1243
+ case true:
1244
+ case false:
1245
+ break;
1246
+ default:
1247
+ throw new TypeError("forth argument must be true or false");
1248
+ }
1249
+
1250
+ switch (arguments[2]) {
1251
+ case true:
1252
+ case false:
1253
+ return checkType(data, dataType, and, logic_or_match, match_or_strict);
1254
+ }
1255
+ }
1256
+
1257
+ return checkType(data, dataType, logic_or_match, match_or_strict, strict);
1258
+ }
1259
+
1260
+ /**
1261
+ * Checks whether all provided values have the same length.
1262
+ * Each value must have a numeric `length` property (e.g., arrays, strings, typed arrays, etc.).
1263
+ *
1264
+ * If all lengths are equal, returns a tuple containing that length and `true`.
1265
+ * Otherwise, returns a tuple containing an array of all detected lengths and `false`.
1266
+ *
1267
+ * @param {...{length: number}} objs - The values to compare by their length property.
1268
+ * @returns {[number | number[], boolean]} A tuple:
1269
+ * - `[length, true]` if all values have the same length.
1270
+ * - `[lengths, false]` if the lengths differ.
1271
+ */
1272
+
1273
+ export function lengths(...objs) {
1274
+ // use not(objs) to enable all cases including one argument only
1275
+
1276
+ if (objs.length < 2) {
1277
+ throw new TypeError(
1278
+ `lengths() takes at least two arguments (${objs.length} given)`
1279
+ );
1280
+ }
1281
+
1282
+ /** @type {Array<number>} */
1283
+
1284
+ let lens = [objs[0].length];
1285
+
1286
+ /** @type {boolean} */
1287
+
1288
+ let equal = true;
1289
+
1290
+ /** @type {number} */
1291
+
1292
+ let len;
1293
+
1294
+ for (let i = 1; i < objs.length; ++i) {
1295
+ len = objs[i].length;
1296
+
1297
+ lens.push(len);
1298
+
1299
+ if (!equal) {
1300
+ continue;
1301
+ }
1302
+
1303
+ if (len !== lens[i - 1]) {
1304
+ equal = false;
1305
+ }
1306
+ }
1307
+
1308
+ if (equal) {
1309
+ return [lens[0], equal];
1310
+ }
1311
+
1312
+ return [lens, equal];
1313
+ }
1314
+
1315
+ /**
1316
+ * Evaluates the truthiness of a given value.
1317
+ * Works like the logical NOT operator (`!`), returning `true` if the value is falsy and `false` if it is truthy.
1318
+ *
1319
+ * @param {*} obj - The value to evaluate.
1320
+ * @returns {boolean} `true` if the value is falsy, otherwise `false`.
1321
+ */
1322
+
1323
+ export function not(obj) {
1324
+ if (arguments.length === 0) {
1325
+ throw new TypeError("not() takes exactly one argument (0 given)");
1326
+ }
1327
+
1328
+ if (obj instanceof Boolean) {
1329
+ return obj == false;
1330
+ }
1331
+
1332
+ return obj?.length === 0 || !obj; // handling the object case
1333
+ }
1334
+
1335
+ /**
1336
+ * Checks whether all provided values are of the same type.
1337
+ * Similar to the `typeof` operator but supports multiple inputs.
1338
+ *
1339
+ * If all values share the same type, returns a tuple containing that type as a string and `true`.
1340
+ * Otherwise, returns a tuple containing an array of the detected types and `false`.
1341
+ *
1342
+ * @param {...*} objs - The values to check.
1343
+ * @returns {[string | string[], boolean]} A tuple:
1344
+ * - `[type, true]` if all values are of the same type.
1345
+ * - `[types, false]` if values have mixed types.
1346
+ */
1347
+
1348
+ export function typesof(...objs) {
1349
+ // use not(objs) to enable all cases including one argument only
1350
+
1351
+ if (objs.length < 2) {
1352
+ throw new TypeError(
1353
+ `typesof() takes at least two arguments (${objs.length} given)`
1354
+ );
1355
+ }
1356
+
1357
+ /** @type {Array<string>} */
1358
+
1359
+ let types = [typeof objs[0]];
1360
+
1361
+ /** @type {boolean} */
1362
+
1363
+ let equal = true;
1364
+
1365
+ /** @type {string} */
1366
+
1367
+ let type;
1368
+
1369
+ for (let i = 1; i < objs.length; ++i) {
1370
+ type = typeof objs[i];
1371
+
1372
+ types.push(type);
1373
+
1374
+ if (!equal) {
1375
+ continue;
1376
+ }
1377
+
1378
+ if (type !== types[i - 1]) {
1379
+ equal = false;
1380
+ }
1381
+ }
1382
+
1383
+ if (equal) {
1384
+ return [types[0], equal];
1385
+ }
1386
+
1387
+ return [types, equal];
1388
+ }
1389
+
1390
+ /**
1391
+ * Evaluates the truthiness of a given value.
1392
+ * Works like the logical NOT operator (`!`), returning `true` if the value is falsy and `false` if it is truthy.
1393
+ *
1394
+ * @param {*} obj - The value to evaluate.
1395
+ * @returns {boolean} `true` if the value is falsy, otherwise `false`.
1396
+ */
1397
+
1398
+ export function $(obj) {
1399
+ return not(obj);
1400
+ }
1401
+
1402
+ function main() {
1403
+ console.log("----------instancesof----------");
1404
+
1405
+ /** @type {Number} */
1406
+
1407
+ let a = new Number(3);
1408
+
1409
+ /** @type {Number} */
1410
+
1411
+ let b = new Number(7);
1412
+
1413
+ /** @type {String} */
1414
+
1415
+ let c = new String("test");
1416
+
1417
+ // one to one (disabled)
1418
+
1419
+ // console.log(instancesof(a, Number)); // true
1420
+ // console.log(instancesof(a, [Number])); // true
1421
+ // console.log(instancesof([a], Number)); // true
1422
+
1423
+ // match=true and strict=true (default)
1424
+ // console.log(instancesof([a], [Number])); // true
1425
+
1426
+ // match=false and strict=true
1427
+ // but when match=false strict don't matter True or False
1428
+ // console.log(instancesof([a], [Number], false)); // true
1429
+
1430
+ // one to many
1431
+
1432
+ console.log(instancesof(a, [Number, String], false)); // true
1433
+ console.log(instancesof([a], [Number, String], false)); // true
1434
+
1435
+ // match=true and strict=false
1436
+ // in this case take just Number with x the second type ignored
1437
+ console.log(instancesof(a, [Number, String], true, false)); // true
1438
+
1439
+ console.log(instancesof([a], [Number, String], true, false)); // true
1440
+
1441
+ // many to one
1442
+
1443
+ // AND
1444
+
1445
+ console.log(instancesof([a, b], Number)); // true
1446
+ console.log(instancesof([a, b], [Number], false)); // true
1447
+
1448
+ // OR
1449
+
1450
+ console.log(instancesof([b, c], Number, or)); // true
1451
+ console.log(instancesof([b, c], [Number], or, false)); // true
1452
+
1453
+ // many to many
1454
+
1455
+ // AND
1456
+
1457
+ console.log(instancesof([a, b], [Number, String], false)); // true
1458
+
1459
+ // OR
1460
+
1461
+ console.log(instancesof([b, c], [Number, Boolean], or, false)); // true
1462
+
1463
+ // pattern matching
1464
+
1465
+ // AND
1466
+
1467
+ console.log(instancesof([a, b], [Number, Number])); // true
1468
+
1469
+ console.log(instancesof([a, b], [Number, Number, String], true, false)); // true
1470
+
1471
+ // OR
1472
+
1473
+ console.log(instancesof([b, c], [Number, Boolean], or)); // true
1474
+
1475
+ console.log(instancesof([b, c], [Number, Boolean, String], or, true, false)); // true
1476
+
1477
+ console.log("------------isType------------");
1478
+
1479
+ /** @type {number} */
1480
+
1481
+ let x = 3; // or Number(3)
1482
+
1483
+ /** @type {number} */
1484
+
1485
+ let y = 7; // or Number(7)
1486
+
1487
+ /** @type {number} */
1488
+
1489
+ let z = "test"; // or String("test")
1490
+
1491
+ // one to one
1492
+
1493
+ console.log(isType(x, number)); // true
1494
+ console.log(isType(x, [number])); // true
1495
+ console.log(isType([x], number)); // true
1496
+
1497
+ // match=true and strict=true (default)
1498
+ console.log(isType([x], [number])); // true
1499
+
1500
+ // match=false and strict=true
1501
+ // but when match=false strict don't matter True or False
1502
+ console.log(isType([x], [number], false)); // true
1503
+
1504
+ // one to many
1505
+
1506
+ console.log(isType(x, [number, string], false)); // true
1507
+ console.log(isType([x], [number, string], false)); // true
1508
+
1509
+ // match=true and strict=false
1510
+ // in this case take just Number with x the second type ignored
1511
+ console.log(isType(x, [number, string], true, false)); // true
1512
+
1513
+ console.log(isType([x], [number, string], true, false)); // true
1514
+
1515
+ // many to one
1516
+
1517
+ // AND
1518
+
1519
+ console.log(isType([x, y], number)); // true
1520
+ console.log(isType([x, y], [number], false)); // true
1521
+
1522
+ // OR
1523
+
1524
+ console.log(isType([y, z], number, or)); // true
1525
+ console.log(isType([y, z], [number], or, false)); // true
1526
+
1527
+ // many to many
1528
+
1529
+ // AND
1530
+
1531
+ console.log(isType([x, y], [number, string], false)); // true
1532
+
1533
+ // OR
1534
+
1535
+ console.log(isType([y, z], [number, boolean], or, false)); // true
1536
+
1537
+ // pattern matching
1538
+
1539
+ // AND
1540
+
1541
+ console.log(isType([x, y], [number, number])); // true
1542
+
1543
+ console.log(isType([x, y], [number, number, string], true, false)); // true
1544
+
1545
+ // OR
1546
+
1547
+ console.log(isType([y, z], [number, boolean], or)); // true
1548
+
1549
+ console.log(isType([y, z], [number, boolean, string], or, true, false)); // true
1550
+
1551
+ console.log("-----------lengths-----------");
1552
+
1553
+ // // use .length directly in this case is the best practice
1554
+ // // one arg (disabled)
1555
+
1556
+ // console.log(lengths([1, 2, 3])); // [ 3, true ]
1557
+
1558
+ // two args +
1559
+
1560
+ console.log(lengths([1, 2, 3], [1, 2, 3])); // [ 3, true ]
1561
+ console.log(lengths([1, 2, 3], "hello")); // [ [3, 5], false ]
1562
+
1563
+ console.log("--------------!--------------");
1564
+
1565
+ console.log(!"test"); // false
1566
+ console.log(!1); // false
1567
+ console.log(!true); // false
1568
+ console.log(!""); // true
1569
+ console.log(!0); // true
1570
+ console.log(!false); // true
1571
+ console.log(!null); // true
1572
+ console.log(!undefined); // true
1573
+
1574
+ // using (!) these is false because objects in JS always true
1575
+ console.log(![]); // new Array()
1576
+ console.log(!new Boolean(""));
1577
+ console.log(!new Boolean(0));
1578
+ console.log(!new Boolean(false));
1579
+ console.log(!new Boolean(null));
1580
+ console.log(!new Boolean(undefined));
1581
+
1582
+ console.log("-------------not-------------");
1583
+
1584
+ console.log(not("test")); // false
1585
+ console.log(not(1)); // false
1586
+ console.log(not(true)); // false
1587
+ console.log(not("")); // true
1588
+ console.log(not(0)); // true
1589
+ console.log(not(false)); // true
1590
+ console.log(not(null)); // true
1591
+ console.log(not(undefined)); // true
1592
+
1593
+ // using (not) these is true because is empty/falsy values
1594
+ console.log(not([])); // new Array()
1595
+ console.log(not(new Boolean("")));
1596
+ console.log(not(new Boolean(0)));
1597
+ console.log(not(new Boolean(false)));
1598
+ console.log(not(new Boolean(null)));
1599
+ console.log(not(new Boolean(undefined)));
1600
+
1601
+ // you can use $() in place of not() (syntactic sugar)
1602
+
1603
+ console.log("-----------typesof-----------");
1604
+
1605
+ // // use typeof directly in this case is the best practice
1606
+ // // one arg (disabled)
1607
+
1608
+ // console.log(typesof(1)); // [ number, true ]
1609
+
1610
+ // two args +
1611
+
1612
+ console.log(typesof(1, 2)); // [ number, true ]
1613
+ console.log(typesof(1, "hi")); // [ [number, string], false ]
1614
+
1615
+ console.log("------------------------------");
1616
+
1617
+ console.log("51 tests passed");
1618
+
1619
+ console.log("7 tests are commented");
1620
+
1621
+ console.log("total tests : 58");
1622
+
1623
+ console.log("============ jsfunx ============");
1624
+ }
1625
+
1626
+ /**
1627
+ * The name of the main script file that was executed.
1628
+ * Derived from the second CLI argument (`process.argv[1]`).
1629
+ *
1630
+ * @type {string}
1631
+ */
1632
+
1633
+ const mainFile = basename(argv[1]);
1634
+
1635
+ /**
1636
+ * The name of the currently running module file.
1637
+ * Computed from the current module URL using `fileURLToPath(import.meta.url)`.
1638
+ *
1639
+ * @type {string}
1640
+ */
1641
+
1642
+ const currentFile = basename(fileURLToPath(import.meta.url));
1643
+
1644
+ if (currentFile === mainFile) {
1645
+ main(); // testing
1646
+ }