@ethanblaisalarms/utils 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.
package/README.md ADDED
@@ -0,0 +1,721 @@
1
+ Lightweight helpers, primarily focused on array and object manipulation and improved stringification.
2
+
3
+
4
+ # Installation and setup example
5
+ ```
6
+ npm install @ethanblaisalarms/utils
7
+ ```
8
+
9
+ ```ts
10
+ // ESM
11
+ import {merge} from "@ethanblaisalarms/utils";
12
+
13
+ // CommonJS
14
+ const {merge} = require("@ethanblaisalarms/utils");
15
+ ```
16
+
17
+
18
+ # TypeScript types
19
+ ## Record shorthand types
20
+ ```ts
21
+ type TRecord<T = unknown> = Record<string, T>
22
+ ```
23
+ | TypeScript | Shorthand for string records.
24
+ |------------|-
25
+ | JavaScript | Equal to `object`.
26
+
27
+
28
+ ```ts
29
+ type TNumRecord<T = unknown> = Record<number, T>
30
+ ```
31
+ | TypeScript | Shorthand for numeric records.
32
+ |------------|-
33
+ | JavaScript | Equal to `object` with numeric keys.
34
+
35
+
36
+ ## Await until condition
37
+ ```ts
38
+ type TCondition = (i?:number)=>boolean;
39
+ ```
40
+ | TypeScript | A function with an optional iteration parameter, which returns a boolean.
41
+ |------------|-
42
+ | JavaScript | Equal to `function`.
43
+
44
+
45
+ # Object utilities
46
+ ## Merge objects
47
+ ```ts
48
+ function merge<T extends object>(target:T, ...sources:object[]):T
49
+ ```
50
+
51
+ This function merges two or more objects together. Merging starts from the first object, and moves down the line. This means that in objects which share properties, the last object has priority.
52
+
53
+ **This function will mutate `target` and may overwrite properties.**
54
+
55
+ This function will:
56
+ * NOT merge any function
57
+ * NOT merge any of the following keys: `__proto__`, `prototype`, or `constructor`
58
+ * NOT merge any symbol keys
59
+ * NOT recursively merge non-plain objects
60
+ * Preserve excluded keys or functions in `target`
61
+ * Concatenate arrays
62
+
63
+ ### Arguments:
64
+ | Name | Type | Description
65
+ |-----------|--------------|------------
66
+ | `target` | object | **required, mutated:** The object to merge into.
67
+ | `sources` | ...object\[] | Array of objects to merge into `target`. If empty, `target` is unchanged.
68
+ | @returns | object | A reference to `target`.
69
+
70
+ ### Example:
71
+ ```ts
72
+ const x = {
73
+ A: 1,
74
+ B: 2,
75
+ C: 3,
76
+ };
77
+ const y = {
78
+ A: 10,
79
+ D: 40,
80
+ E: 50,
81
+ };
82
+ const z = {
83
+ A: 100,
84
+ E: 500,
85
+ F: 600,
86
+ };
87
+
88
+ merge(x, y, z);
89
+
90
+ // Expected value of x:
91
+ {
92
+ A: 100,
93
+ B: 2,
94
+ C: 3,
95
+ D: 40,
96
+ E: 500,
97
+ F: 600,
98
+ };
99
+ ```
100
+
101
+ ### Common Use Case:
102
+ ```ts
103
+ import userConfig from "./Config.js";
104
+
105
+ const CONFIG = {
106
+ debugMode: false,
107
+ };
108
+
109
+ merge(CONFIG, userConfig);
110
+ ```
111
+
112
+
113
+ ## De-comment JSON
114
+ ```ts
115
+ function decommentJSON(data:string):string
116
+ ```
117
+
118
+ This function removes comments from JSON strings. This makes it easy to parse JSON files which may contain comments.
119
+
120
+ ### Arguments:
121
+ | Name | Type | Description
122
+ |-----------|--------|------------
123
+ | `data` | string | **required:** The JSON string to remove comments from.
124
+ | @returns | string | `data`, but with JSON comments removed.
125
+
126
+ ### Example:
127
+ ```ts
128
+ const x = `{
129
+ // "A" holds the number of apples.
130
+ "A": 10,
131
+ /* "B" holds the number of bananas.
132
+ Must be greater than 0. */
133
+ "B": 20,
134
+ /*
135
+ "C" holds the number of carrots.
136
+ Must be:
137
+ - An even number
138
+ - Divisible by 3
139
+ - Not divisible by 60
140
+ - The digits must no add up to 6
141
+ */
142
+ "C": 30,
143
+ "D": "//This is not a JSON comment"
144
+ }`;
145
+
146
+ const y = decommentJSON(x);
147
+
148
+ // Expected value of y:
149
+ `{
150
+ "A": 10,
151
+ "B": 20,
152
+ "C": 30,
153
+ "D": "//This is not a JSON comment"
154
+ }`;
155
+ ```
156
+
157
+ ### Common Use Case:
158
+ ```ts
159
+ const rawConfig = fs.readFileSync("config.json");
160
+ const CONFIG = JSON.parse(decommentJSON(rawConfig));
161
+ ```
162
+
163
+
164
+ ## Check for plain objects
165
+ ```ts
166
+ function checkPlain(data:unknown):data is TRecord
167
+ ```
168
+
169
+ This function checks if a passed value is a plain object. If so, TypeScript will recognize the result as a TRecord.
170
+
171
+ ### Arguments:
172
+ | Name | Type | Description
173
+ |----------|---------|------------
174
+ | `data` | unknown | **required:** The value to check.
175
+ | @returns | boolean | Whether `data` is a plain object.
176
+
177
+ ### Example:
178
+ ```ts
179
+ const x = new Date();
180
+ const y = {};
181
+ const z = [
182
+ checkPlain(x),
183
+ checkPlain(y),
184
+ ];
185
+
186
+ // Expected value of z:
187
+ [false, true];
188
+ ```
189
+
190
+
191
+ # Array utilities
192
+ ## Pull from array
193
+ ```ts
194
+ function pull<T>(target:T[], ...elements:T[]):T[]
195
+ ```
196
+
197
+ This function removes the first instance of each of the specified elements from the array.
198
+
199
+ **This function will mutate `target` and may remove elements.**
200
+
201
+ ### Arguments:
202
+ | Name | Type | Description
203
+ |------------|---------------|------------
204
+ | `target` | unknown\[] | **required, mutated:** The array to remove elements from.
205
+ | `elements` | ...unknown\[] | Array of elements to remove from `target`. If no objects are specified, `target` is unchanged.
206
+ | @returns | unknown\[] | A reference to `target`.
207
+
208
+ ### Example:
209
+ ```ts
210
+ const x = [10, 20, 30, 40, 50, 20];
211
+ pull(x, 20, 50);
212
+
213
+ // Expected value of x:
214
+ [10, 30, 40, 20];
215
+ ```
216
+
217
+
218
+ ## Pull all
219
+ ```ts
220
+ function pullAll<T>(target:T[], ...elements:T[]):T[]
221
+ ```
222
+
223
+ This function removes all instances of each of the specified elements from the array.
224
+
225
+ **This function will mutate `target` and may remove elements.**
226
+
227
+ ### Arguments:
228
+ | Name | Type | Description
229
+ |------------|---------------|------------
230
+ | `target` | unknown\[] | **required, mutated:** The array to remove elements from.
231
+ | `elements` | ...unknown\[] | Array of elements to remove from `target`. If no objects are specified, `target` is unchanged.
232
+ | @returns | unknown\[] | A reference to `target`.
233
+
234
+ ### Example:
235
+ ```ts
236
+ const x = [10, 20, 30, 40, 50, 20];
237
+ pullAll(x, 20, 50);
238
+
239
+ // Expected value of x:
240
+ [10, 30, 40];
241
+ ```
242
+
243
+
244
+ ## Pull and replace
245
+ ```ts
246
+ function replace<T>(target:T[], element:T, ...replace:T[]):T[]
247
+ ```
248
+
249
+ This function removes the first instance of a specified element from the array and replaces it with the specified replacements.
250
+
251
+ **This function will mutate `target` and may add or remove elements.**
252
+
253
+ ### Arguments:
254
+ | Name | Type | Description
255
+ |------------|---------------|------------
256
+ | `target` | unknown\[] | **required, mutated:** The array to remove elements from.
257
+ | `element` | unknown | **required:** The element to remove from `target`.
258
+ | `replace` | ...unknown\[] | The elements to replace removed elements. If none are specified, behavior matches `pull()`.
259
+ | @returns | unknown\[] | A reference to `target`.
260
+
261
+ ### Example:
262
+ ```ts
263
+ const x = [10, 20, 30, 40, 50, 20];
264
+ replace(x, 20, 60, 70);
265
+
266
+ // Expected value of x:
267
+ [10, 60, 70, 30, 40, 50, 20];
268
+ ```
269
+
270
+
271
+ ## Pull and replace all
272
+ ```ts
273
+ function replaceAll<T>(target:T[], element:T, ...replace:T[]):T[]
274
+ ```
275
+
276
+ This function removes all instances of a specified element from the array and replaces it with the specified replacements.
277
+
278
+ **This function will mutate `target` and may add or remove elements.**
279
+
280
+ ### Arguments:
281
+ | Name | Type | Description
282
+ |------------|---------------|------------
283
+ | `target` | unknown\[] | **required, mutated:** The array to remove elements from.
284
+ | `element` | unknown | **required:** The element to remove from `target`.
285
+ | `replace` | ...unknown\[] | The elements to replace removed elements. If none are specified, behavior matches `pullAll()`.
286
+ | @returns | unknown\[] | A reference to `target`.
287
+
288
+ ### Example:
289
+ ```ts
290
+ const x = [10, 20, 30, 40, 50, 20];
291
+ replaceAll(x, 20, 60, 70);
292
+
293
+ // Expected value of x:
294
+ [10, 60, 70, 30, 40, 50, 60, 70];
295
+ ```
296
+
297
+
298
+ ## Objectify arrays
299
+ ```ts
300
+ function objectify<T>(source:T[]):TNumRecord<T>
301
+ ```
302
+
303
+ This function converts an array to a plain object with numeric keys. The original array is not mutated.
304
+
305
+ ### Arguments:
306
+ | Name | Type | Description
307
+ |----------|------------|------------
308
+ | `source` | unknown\[] | **required:** The array to convert to a plain object.
309
+ | @returns | TNumRecord | The new object created from `source`.
310
+
311
+ ### Example:
312
+ ```ts
313
+ const x = ["zero", "one", "two", "three"];
314
+ const y = objectify(x);
315
+
316
+ // Expected value of y:
317
+ {
318
+ 0: "zero",
319
+ 1: "one",
320
+ 2: "two",
321
+ 3: "three",
322
+ };
323
+ ```
324
+
325
+
326
+ # Stringify values
327
+ ## General stringify
328
+ ```ts
329
+ function stringify(data:unknown):string
330
+ ```
331
+
332
+ This function converts any value to a readable string.
333
+
334
+ The process looks like this:
335
+
336
+ * If the value is a bigint, `return data + "n";`
337
+ * If the value is not an object, `return String(data);`
338
+ * If the value is null, `return "null";`
339
+ * If the value is a Map or a Set, convert to an array and `stringify()` it
340
+ * If the value is an array, `return stringifyJSON(data);`
341
+ * If the value has a non-default toString method, call it and return the value
342
+ * Otherwise, `return stringifyJSON({...data});`
343
+
344
+ ### Arguments:
345
+ | Name | Type | Description
346
+ |----------|---------|------------
347
+ | `data` | unknown | **required:** The value to convert to a string.
348
+ | @returns | string | A string representation of `data`.
349
+
350
+ ### Example:
351
+ ```ts
352
+ const x = 500n;
353
+ const y = null;
354
+ const z = {};
355
+
356
+ const res = [
357
+ stringify(x),
358
+ stringify(y),
359
+ stringify(z),
360
+ ];
361
+
362
+ // Expected value of res:
363
+ ["500n", "null", "{}"];
364
+ ```
365
+
366
+
367
+ ## Stringify JSON
368
+ ```ts
369
+ function stringifyJSON(
370
+ data:object,
371
+ separator:string = " ",
372
+ ignore:string[] = [],
373
+ ):string
374
+ ```
375
+
376
+ This function converts an object into a string. This function adds additional support to `JSON.stringify()`.
377
+
378
+ Unlike `JSON.stringify()` alone, this function can:
379
+
380
+ * Handle bigint values
381
+ * Handle instances of Map and Set
382
+ * Handle circular object references
383
+ * Optionally ignore specified key paths
384
+
385
+ ### Arguments:
386
+ | Name | Type | Description
387
+ |-------------|----------|------------
388
+ | `data` | object | **required:** The object to convert to a string.
389
+ | `separator` | string | The separator to pass into `JSON.stringify()`.
390
+ | `ignore` | string[] | An array of dot-notation key paths to ignore.
391
+ | @returns | string | A string representation of `data`.
392
+
393
+ ### Example:
394
+ ```ts
395
+ const x = {
396
+ A: 1,
397
+ B: 2,
398
+ };
399
+ const y = {
400
+ A: 1,
401
+ B: 2,
402
+ C: {
403
+ A: 100n,
404
+ B: 2,
405
+ },
406
+ };
407
+ const z = {
408
+ X: x,
409
+ C: x,
410
+ };
411
+ z.Z = z;
412
+
413
+ const res = {
414
+ x: stringifyJSON(x),
415
+ y: stringifyJSON(y, " ", ["A", "C.B"]),
416
+ z: stringifyJSON(z),
417
+ };
418
+
419
+ // Expected value of res:
420
+ {
421
+ x: `{
422
+ "A": 1,
423
+ "B": 2
424
+ }`,
425
+ y: `{
426
+ "B": 2,
427
+ "C": {
428
+ "A": "100n"
429
+ }
430
+ }`,
431
+ z: `{
432
+ "X": {
433
+ "A": 1,
434
+ "B": 2
435
+ },
436
+ "C": "[Circular: X]",
437
+ "Z": "[Circular: ~]"
438
+ }`,
439
+ };
440
+ ```
441
+
442
+
443
+ ## Stringify array elements
444
+ ```ts
445
+ function stringifyArray(data:unknown[]):string[]
446
+ ```
447
+
448
+ Shorthand for `data.map(stringify)`. Converts all elements of an array into strings, and returns the new array without mutating the existing array.
449
+
450
+ ### Arguments:
451
+ | Name | Type | Description
452
+ |----------|------------|------------
453
+ | `data` | unknown\[] | **required:** The array to stringify the elements of.
454
+ | @returns | string\[] | `data`, but after all elements are converted to strings.
455
+
456
+ ### Example:
457
+ ```ts
458
+ const x = [null, 20, {}];
459
+ const y = stringifyArray(x);
460
+
461
+ // Expected value of y:
462
+ ["null", "20", "{}"];
463
+ ```
464
+
465
+
466
+ # Randomization
467
+ ## Random number
468
+ ```ts
469
+ function rand():number
470
+ ```
471
+
472
+ Shorthand for `Math.random()`. Generates a random number between 0 and 1.
473
+
474
+ ### Arguments:
475
+ | Name | Type | Description
476
+ |----------|--------|------------
477
+ | @returns | number | The result of `Math.random()`.
478
+
479
+ ### Example:
480
+ ```ts
481
+ const x = [
482
+ rand(),
483
+ rand(),
484
+ rand(),
485
+ rand(),
486
+ rand(),
487
+ ]
488
+
489
+ // Expected value of x:
490
+ [
491
+ 0.8966649491265986,
492
+ 0.18814033044643852,
493
+ 0.6812450432610444,
494
+ 0.30752727234410016,
495
+ 0.6411181322989353,
496
+ ];
497
+ ```
498
+
499
+
500
+ ## Random number up to
501
+ ```ts
502
+ function rand(base:number):number
503
+ ```
504
+
505
+ Generates a random integer between 0 and `base` (inclusive).
506
+
507
+ ### Arguments:
508
+ | Name | Type | Description
509
+ |----------|--------|------------
510
+ | `base` | number | **required:** The maximum number to generate.
511
+ | @returns | number | A random number between 0 and `base`.
512
+
513
+ ### Example:
514
+ ```ts
515
+ const x = [
516
+ rand(5),
517
+ rand(5),
518
+ rand(5),
519
+ rand(5),
520
+ rand(5),
521
+ ]
522
+
523
+ // Expected value of x:
524
+ [0, 4, 1, 0, 5];
525
+ ```
526
+
527
+
528
+ ## Random number between
529
+ ```ts
530
+ function rand(base:number, max:number):number
531
+ ```
532
+
533
+ Generates a random integer between `base` and `max` (inclusive).
534
+
535
+ ### Arguments:
536
+ | Name | Type | Description
537
+ |----------|--------|------------
538
+ | `base` | number | **required:** The minimum number to generate.
539
+ | `max` | number | **required:** The maximum number to generate.
540
+ | @returns | number | A random number between `base` and `max`.
541
+
542
+ ### Example:
543
+ ```ts
544
+ const x = [
545
+ rand(-5, 5),
546
+ rand(-5, 5),
547
+ rand(-5, 5),
548
+ rand(-5, 5),
549
+ rand(-5, 5),
550
+ ]
551
+
552
+ // Expected value of x:
553
+ [4, 1, -1, -1, -5];
554
+ ```
555
+
556
+
557
+ ## Random array element
558
+ ```ts
559
+ function rand<T>(base:T[]):T|null
560
+ ```
561
+
562
+ Returns a random element from the `base` array.
563
+
564
+ ### Arguments:
565
+ | Name | Type | Description
566
+ |----------|------------|------------
567
+ | `base` | unknown\[] | **required:** The array to search.
568
+ | @returns | unknown | A random element from `base`.
569
+ | @returns | null | If the array is empty.
570
+
571
+ ### Example:
572
+ ```ts
573
+ const x = "ABCDE".split("");
574
+ const y = [
575
+ rand(x),
576
+ rand(x),
577
+ rand(x),
578
+ rand(x),
579
+ rand(x),
580
+ ];
581
+
582
+ // Expected value of y:
583
+ ["C", "B", "B", "A", "E"];
584
+ ```
585
+
586
+
587
+ ## Random object key
588
+ ```ts
589
+ function rand(base:TRecord):string|null
590
+ ```
591
+
592
+ Returns a random key from the `base` object.
593
+
594
+ ### Arguments:
595
+ | Name | Type | Description
596
+ |----------|--------|------------
597
+ | `base` | object | **required:** The object to search.
598
+ | @returns | string | A random key from `base`.
599
+ | @returns | null | If the object is empty.
600
+
601
+ ### Example:
602
+ ```ts
603
+ const x = {
604
+ A: 10,
605
+ B: 20,
606
+ C: 30,
607
+ D: 40,
608
+ E: 50,
609
+ };
610
+ const y = [
611
+ rand(x),
612
+ rand(x),
613
+ rand(x),
614
+ rand(x),
615
+ rand(x),
616
+ ];
617
+
618
+ // Expected value of y:
619
+ ["E", "C", "A", "A", "E"];
620
+ ```
621
+
622
+
623
+ ## Random string
624
+ ```ts
625
+ function randString(
626
+ len:number = 16,
627
+ charset:string|string[] = "0123456789ABCDEF",
628
+ ):string
629
+ ```
630
+
631
+ Returns a randomly generated string of the specified length, using the specified charset.
632
+
633
+ ### Arguments:
634
+ | Name | Type | Description
635
+ |-----------|-----------|------------
636
+ | `len` | number | The length of the string.
637
+ | `charset` | string | The charset to use.
638
+ | `charset` | string\[] | The array of strings to use.
639
+ | @returns | string | A random string.
640
+ | @returns | "" | If `charset` did not include at least one item.
641
+
642
+ ### Example:
643
+ ```ts
644
+ const x = [
645
+ randString(4),
646
+ randString(8, "!@$^"),
647
+ randString(4, ["Hey", "Hi", "Hello"]),
648
+ ];
649
+
650
+ // Expected value of x:
651
+ [
652
+ "9D39",
653
+ "@$!!^@$$",
654
+ "HeyHiHiHello",
655
+ ];
656
+ ```
657
+
658
+
659
+ # Async Utilities
660
+ ## Sleep
661
+ ```ts
662
+ async function sleep(ms:number):Promise<void>
663
+ ```
664
+
665
+ When awaited, waits (sleeps) for the specified milliseconds.
666
+
667
+ ### Arguments:
668
+ | Name | Type | Description
669
+ |----------|---------|------------
670
+ | `ms` | number | **required:** The duration to sleep for.
671
+ | @returns | Promise | Resolves after `ms` milliseconds.
672
+
673
+ ### Example:
674
+ ```ts
675
+ console.log("Start!");
676
+ await sleep(5000);
677
+ console.log("Stop!");
678
+
679
+ // Expected delay between Start and Stop: 5 seconds.
680
+ ```
681
+
682
+
683
+ ## Await Until
684
+ ```ts
685
+ async function until(
686
+ condition:TCondition,
687
+ interval:number = 100,
688
+ attempts:number|null = null,
689
+ ):Promise<number>
690
+ ```
691
+
692
+ When awaited, waits until the specified condition returns true.
693
+
694
+ ### Arguments:
695
+ | Name | Type | Description
696
+ |-------------|----------|------------
697
+ | `condition` | function | The function condition.
698
+ | `interval` | number | The number of milliseconds between `condition` calls.
699
+ | `attempts` | number | The maximum number of attempts.
700
+ | `attempts` | null | Disables the maximum number of attempts.
701
+ | @returns | Promise | Resolves once `condition` returns true. Rejects when reaching the maximum attempts.
702
+
703
+ ### Example:
704
+ ```ts
705
+ const x = {
706
+ value: false,
707
+ };
708
+ async function setTrue() {
709
+ await sleep(5000);
710
+ x.value = true;
711
+ }
712
+
713
+ console.log("Start!");
714
+ setTrue();
715
+ await until(() => {
716
+ return x.value;
717
+ });
718
+ console.log("Stop!");
719
+
720
+ // Expected delay between Start and Stop: 5 seconds.
721
+ ```