moderndash 0.5.0 → 0.6.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/dist/index.d.ts CHANGED
@@ -431,6 +431,125 @@ declare function omit<TObj extends object, Key extends keyof TObj>(object: TObj,
431
431
  */
432
432
  declare function pick<TInput, Key extends keyof TInput>(object: TInput, keysToPick: Key[]): Pick<TInput, Key>;
433
433
 
434
+ /**
435
+ * A class for managing a queue of async functions that runs a set number concurrently.
436
+ * If for example you have 10 async functions and you want to run 3 at a time, you can use this class.
437
+ *
438
+ * If the queue is paused, the queue will not run any more async functions until it is resumed.
439
+ *
440
+ * ---
441
+ *
442
+ * **Methods:**
443
+ * - {@link Queue.add} - adds a async function or array of functions to the queue.
444
+ * Returns a promise that resolves when the added function(s) finish.
445
+ * - {@link Queue.clear} - clears the queue.
446
+ * - {@link Queue.pause} - pauses the queue.
447
+ * - {@link Queue.resume} - resumes the queue.
448
+ * - {@link Queue.getQueue} - returns the queue.
449
+ * - {@link Queue.isPaused} - returns whether the queue is paused.
450
+ *
451
+ * @example
452
+ * // Create a queue that can run 3 tasks concurrently
453
+ * const queue = new Queue(3);
454
+ *
455
+ * queue.add(() => fetch('https://example.com'));
456
+ *
457
+ * queue.add(async () => {
458
+ * const response = await fetch('https://example.com');
459
+ * return response.json();
460
+ * });
461
+ *
462
+ * // Add an array of tasks to the queue and wait for them to resolve
463
+ * await queue.add([
464
+ * () => fetch('https://apple.com'),
465
+ * () => fetch('https://microsoft.com')
466
+ * ]);
467
+ * // => [Response, Response]
468
+ */
469
+ declare class Queue {
470
+ private running;
471
+ private maxConcurrent;
472
+ private paused;
473
+ private queue;
474
+ /**
475
+ * @constructor
476
+ * @param maxConcurrent - The maximum number of async functions to run concurrently.
477
+ */
478
+ constructor(maxConcurrent: number);
479
+ /**
480
+ * Add aync functions or an array of async functions to the queue.
481
+ *
482
+ * @param asyncFn - The aync function(s) to add to the queue.
483
+ * @returns A promise that resolves when the added function(s) finishes.
484
+ */
485
+ add<TProm, TAsyncFn extends () => Promise<TProm>>(asyncFn: TAsyncFn): Promise<TProm>;
486
+ add<TProm, TAsyncFn extends () => Promise<TProm>>(asyncFn: TAsyncFn[]): Promise<TProm[]>;
487
+ private buildWaitingPromise;
488
+ private run;
489
+ /** Removes all the tasks from the queue */
490
+ clear(): void;
491
+ /** Pauses the execution of the functions in the queue */
492
+ pause(): void;
493
+ /** Resumes the execution of the tasks in the queue */
494
+ resume(): void;
495
+ /** Returns the queue */
496
+ getQueue(): (() => Promise<any>)[];
497
+ /** Returns whether the queue is paused */
498
+ isPaused(): boolean;
499
+ }
500
+
501
+ /**
502
+ * Similar to [Promise.race](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race?retiredLocale=de)
503
+ * but allows to specify how many promises to wait for.
504
+ *
505
+ * @example
506
+ * const promise1 = Promise.resolve(1);
507
+ * const promise2 = Promise.resolve(2);
508
+ * const promise2 = Promise.resolve(3);
509
+ *
510
+ * const firstThree = await races(3, promise1, promise2, promise3);
511
+ * // => [1, 2, 3]
512
+ * @template TRes - The type of the result of the promises.
513
+ * @param waitFor - The number of promises to wait for.
514
+ * @param promises - The promises to wait for.
515
+ * @returns A promise that resolves an array of the results of the first n promises.
516
+ */
517
+ declare function races<TRes>(waitFor: number, ...promises: Promise<TRes>[]): Promise<TRes[]>;
518
+
519
+ /**
520
+ * Retry a function until it succeeds or the maximum number of retries is reached.
521
+ *
522
+ * Default maxRetries: `5`.
523
+ * Default backoff: `2^retries * 100ms` (100, 200, 400, 800, 1600, 3200, ...)
524
+ *
525
+ * @example
526
+ * await retry(() => fetch('https://example.com'));
527
+ *
528
+ * // ---- Advanced example ----
529
+ * const fetchSite = async (url: string) => {
530
+ * const response = await fetch(url);
531
+ * response.ok || throw new Error('Failed to fetch');
532
+ * }
533
+ *
534
+ * const logger = (error: unknown, retry?: number) => console.log("Retrying", retry, error);
535
+ *
536
+ * await retry(() => fetchSite('https://example.com'), { maxRetries: 3, backoff: retries => retries * 1000, onRetry: logger }));
537
+ * // => Will retry 3 times with a 1 second delay between each retry. Will log the error and retry number.
538
+ *
539
+ * @param func The function to retry.
540
+ * @param options The options for the retry.
541
+ * @param options.maxRetries The maximum number of retries. Defaults to `5`.
542
+ * @param options.backoff The backoff function to use. Defaults to `2^retries * 100`.
543
+ * @param options.onRetry The function to call when a retry is attempted.
544
+ * @template TRes The type of the result of the function.
545
+ * @returns A promise that resolves when the function succeeds.
546
+ */
547
+ declare function retry<TRes>(func: () => Promise<TRes>, options?: {
548
+ maxRetries?: number;
549
+ backoff?: ((retries: number) => number);
550
+ onRetry: (error?: unknown, retry?: number) => void;
551
+ }): Promise<TRes>;
552
+
434
553
  /**
435
554
  * Sleeps for the given amount of time.
436
555
  *
@@ -442,6 +561,24 @@ declare function pick<TInput, Key extends keyof TInput>(object: TInput, keysToPi
442
561
  */
443
562
  declare function sleep(ms: number): Promise<unknown>;
444
563
 
564
+ /**
565
+ * Returns a new promise that will reject with an error after a specified timeout.
566
+ *
567
+ * @example
568
+ * try {
569
+ * await timeout(fetch('https://example.com'), 1000);
570
+ * } catch (error) {
571
+ * console.log(error.message);
572
+ * // => 'Promise timed out after 1000ms'
573
+ * }
574
+ * @template TRes - The type of the resolved value.
575
+ * @param promise - The promise to wrap.
576
+ * @param timeout - The timeout in milliseconds.
577
+ *
578
+ * @returns A new promise that will reject with an error after the specified timeout.
579
+ */
580
+ declare function timeout<TRes>(promise: Promise<TRes>, timeout: number): Promise<TRes>;
581
+
445
582
  /**
446
583
  * Converts `string` to camelCase.
447
584
  *
@@ -470,6 +607,22 @@ declare function camelCase(str: string): string;
470
607
  */
471
608
  declare function capitalize(str: string): string;
472
609
 
610
+ /**
611
+ * Converts a string to dash-case.
612
+ *
613
+ * @example
614
+ * dashCase('Foo Bar')
615
+ * // => 'foo-bar'
616
+ * dashCase('fooBar')
617
+ * // => 'foo-bar'
618
+ * dashCase('__FOO_BAR__')
619
+ * // => 'foo-bar'
620
+ * @category String
621
+ * @param str - The string to convert.
622
+ * @returns Returns the dash cased string.
623
+ */
624
+ declare function dashCase(str: string): string;
625
+
473
626
  /**
474
627
  * Deburrs a string by converting
475
628
  * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
@@ -581,7 +734,7 @@ declare function startCase(str: string): string;
581
734
  * Removes all special characters from a string.
582
735
  *
583
736
  * @example
584
- * stripSpecialChars('Héllo! World #$%&*!')
737
+ * stripSpecial('Héllo! World #$%&*!')
585
738
  * // => 'Hello World'
586
739
  * @category String
587
740
  * @param str - The string to remove special characters from.
@@ -602,81 +755,6 @@ declare function stripSpecial(str: string): string;
602
755
  */
603
756
  declare function unescapeHtml(str: string): string;
604
757
 
605
- /**
606
- * A `number` that is an integer.
607
- * You can't pass a `bigint` as they are already guaranteed to be integers.
608
- *
609
- * Use-case: Validating and documenting parameters. Other usage is limited.
610
- *
611
- * *Note: Check this [writeup](https://github.com/sindresorhus/type-fest/issues/334#issuecomment-987787840) for more details.*
612
- * @example
613
- * function setYear<T extends number>(x: Integer<T>){};
614
- *
615
- * setYear(1); // OK
616
- * setYear(1.1); // Error
617
- * @category type
618
- */
619
- type Int<T extends number> = `${T}` extends `${bigint}` ? T : never;
620
-
621
- /**
622
- * A `number` that is not an integer.
623
- * You can't pass a `bigint` as they are already guaranteed to be integers.
624
- *
625
- * Use-case: Validating and documenting parameters. Other usage is limited.
626
- *
627
- * *Note: Check this [writeup](https://github.com/sindresorhus/type-fest/issues/334#issuecomment-987787840) for more details.*
628
- * @example
629
- * function setPercentage<T extends number>(x: Float<T>) {};
630
- *
631
- * setPercentage(1.1); // OK
632
- * setPercentage(1); // Error
633
- * @category type
634
- */
635
- type Float<T extends number> = T extends Int<T> ? never : T;
636
-
637
- /**
638
- * A negative `number`/`bigint` (`-∞ < x < 0`)
639
- *
640
- * Use-case: Validating and documenting parameters. Other usage is limited.
641
- *
642
- * *Note: Check this [writeup](https://github.com/sindresorhus/type-fest/issues/334#issuecomment-987787840) for more details.*
643
- * @example
644
- * function setNegative<T extends number>(x: Negative<T>) {};
645
- *
646
- * setNegative(-1.2); // OK
647
- * setNegative(1); // Error
648
- *
649
- * function setNegInt<T extends number>(x: Negative<Int<T>>) {};
650
- *
651
- * setNegInt(-1); // OK
652
- * setNegInt(1); // Error
653
- * setNegInt(-1.1); // Error
654
- * @category type
655
- */
656
- type Negative<T extends number | bigint> = T extends 0 | 0n ? never : `${T}` extends `-${string}` ? T : never;
657
-
658
- /**
659
- * A positive `number`/`bigint` (`0 < x < ∞`).
660
- *
661
- * Use-case: Validating and documenting parameters. Other usage is limited.
662
- *
663
- * *Note: Check this [writeup](https://github.com/sindresorhus/type-fest/issues/334#issuecomment-987787840) for more details.*
664
- * @example
665
- * function setPositive<T extends number>(x: Positive<T>) {};
666
- *
667
- * setPositive(1.2); // OK
668
- * setPositive(-1); // Error
669
- *
670
- * function setPosInt<T extends number>(x: Positive<Int<T>>) {};
671
- *
672
- * setPosInt(1); // OK
673
- * setPosInt(-1); // Error
674
- * setPosInt(1.1); // Error
675
- *
676
- * @category type
677
- */
678
- type Positive<T extends number | bigint> = T extends 0 | 0n ? never : Negative<T> extends never ? T : never;
679
-
680
758
  /**
681
759
  * Checks if `value` is an empty object, collection, map, or set.
682
760
  *
@@ -730,11 +808,11 @@ declare function isEmpty(value: string | object | null | undefined): boolean;
730
808
  * object === other;
731
809
  * // => false
732
810
  * @category Validate
733
- * @param value1 - The value to compare.
734
- * @param value2 - The other value to compare.
811
+ * @param a - The value to compare.
812
+ * @param b - The other value to compare.
735
813
  * @returns Returns `true` if the values are equivalent, else `false`.
736
814
  */
737
- declare function isEqual(value1: unknown, value2: unknown): boolean;
815
+ declare function isEqual(a: unknown, b: unknown): boolean;
738
816
 
739
817
  declare function isPlainObject(value: unknown): value is object;
740
818
 
@@ -751,4 +829,4 @@ declare function isPlainObject(value: unknown): value is object;
751
829
  */
752
830
  declare function isUrl(str: string): boolean;
753
831
 
754
- export { Float, Int, Negative, Positive, after, before, camelCase, capitalize, chunk, count, debounce, deburr, difference, dropRightWhile, dropWhile, escapeHtml, escapeRegExp, group, intersection, isEmpty, isEqual, isPlainObject, isUrl, kebabCase, memoize, omit, once, pascalCase, pick, sample, shuffle, sleep, snakeCase, sort, startCase, stripSpecial, takeRightWhile, takeWhile, throttle, times, unescapeHtml, unique };
832
+ export { Queue, after, before, camelCase, capitalize, chunk, count, dashCase, debounce, deburr, difference, dropRightWhile, dropWhile, escapeHtml, escapeRegExp, group, intersection, isEmpty, isEqual, isPlainObject, isUrl, kebabCase, memoize, omit, once, pascalCase, pick, races, retry, sample, shuffle, sleep, snakeCase, sort, startCase, stripSpecial, takeRightWhile, takeWhile, throttle, timeout, times, unescapeHtml, unique };
package/dist/index.js CHANGED
@@ -28,17 +28,20 @@ function count(array, iteratee) {
28
28
  }
29
29
 
30
30
  // src/validate/isEqual.ts
31
- function isEqual(value1, value2) {
32
- if (value1 === value2)
31
+ function isEqual(a, b) {
32
+ if (Object.is(a, b))
33
33
  return true;
34
- if (Array.isArray(value1) && Array.isArray(value2)) {
35
- return isSameArray(value1, value2);
34
+ if (a instanceof Date && b instanceof Date) {
35
+ return a.getTime() === b.getTime();
36
36
  }
37
- if (value1 instanceof RegExp && value2 instanceof RegExp) {
38
- return value1.toString() === value2.toString();
37
+ if (Array.isArray(a) && Array.isArray(b)) {
38
+ return isSameArray(a, b);
39
39
  }
40
- if (isObject(value1) && isObject(value2)) {
41
- return isSameObject(value1, value2);
40
+ if (a instanceof RegExp && b instanceof RegExp) {
41
+ return a.toString() === b.toString();
42
+ }
43
+ if (isObject(a) && isObject(b)) {
44
+ return isSameObject(a, b);
42
45
  }
43
46
  return false;
44
47
  }
@@ -373,11 +376,134 @@ function omit(object, keysToOmit) {
373
376
  return pick(object, filteredKeys);
374
377
  }
375
378
 
379
+ // src/promise/queue.ts
380
+ var Queue = class {
381
+ running = 0;
382
+ maxConcurrent;
383
+ paused = false;
384
+ queue = [];
385
+ constructor(maxConcurrent) {
386
+ this.maxConcurrent = maxConcurrent;
387
+ }
388
+ add(asyncFn) {
389
+ if (Array.isArray(asyncFn)) {
390
+ const promises = asyncFn.map((fn) => this.buildWaitingPromise(fn));
391
+ return Promise.all(promises);
392
+ } else {
393
+ return this.buildWaitingPromise(asyncFn);
394
+ }
395
+ }
396
+ buildWaitingPromise(asyncFn) {
397
+ return new Promise((resolve, reject) => {
398
+ this.queue.push({ asyncFn, resolve, reject });
399
+ this.run();
400
+ });
401
+ }
402
+ run() {
403
+ while (this.queue.length > 0 && this.running < this.maxConcurrent && !this.paused) {
404
+ this.running++;
405
+ const queueElement = this.queue.shift();
406
+ void queueElement.asyncFn().then((result) => {
407
+ queueElement.resolve(result);
408
+ }).catch((error) => {
409
+ queueElement.reject(error);
410
+ }).finally(() => {
411
+ this.running--;
412
+ this.run();
413
+ });
414
+ }
415
+ }
416
+ clear() {
417
+ for (const queueElement of this.queue) {
418
+ queueElement.reject(new Error("Queue cleared"));
419
+ }
420
+ this.queue = [];
421
+ }
422
+ pause() {
423
+ this.paused = true;
424
+ }
425
+ resume() {
426
+ this.paused = false;
427
+ this.run();
428
+ }
429
+ getQueue() {
430
+ return this.queue.map((queueElement) => queueElement.asyncFn);
431
+ }
432
+ isPaused() {
433
+ return this.paused;
434
+ }
435
+ };
436
+
437
+ // src/promise/races.ts
438
+ function races(waitFor, ...promises) {
439
+ return new Promise((resolve, reject) => {
440
+ if (promises.length < waitFor)
441
+ waitFor = promises.length;
442
+ const results = [];
443
+ let resolved = 0;
444
+ for (const promise of promises) {
445
+ promise.then((value) => {
446
+ results.push(value);
447
+ resolved++;
448
+ if (resolved >= waitFor) {
449
+ resolve(results);
450
+ }
451
+ }).catch((error) => {
452
+ reject(error);
453
+ });
454
+ }
455
+ });
456
+ }
457
+
376
458
  // src/promise/sleep.ts
377
459
  function sleep(ms) {
378
460
  return new Promise((resolve) => setTimeout(resolve, ms));
379
461
  }
380
462
 
463
+ // src/promise/retry.ts
464
+ async function retry(func, options) {
465
+ const backOffFn = options?.backoff ?? ((retries2) => 2 ** retries2 * 100);
466
+ const maxRetries = options?.maxRetries ?? 5;
467
+ const onRetry = options?.onRetry ?? (() => {
468
+ });
469
+ let retries = 0;
470
+ let lastError;
471
+ while (retries <= maxRetries) {
472
+ try {
473
+ if (retries > 0)
474
+ onRetry(lastError, retries);
475
+ return await func();
476
+ } catch (error) {
477
+ lastError = error;
478
+ retries++;
479
+ if (retries > maxRetries) {
480
+ throw error;
481
+ }
482
+ await sleep(backOffFn(retries));
483
+ }
484
+ }
485
+ throw new Error("Retry terminated without success, this should never happen");
486
+ }
487
+
488
+ // src/promise/timeout.ts
489
+ function timeout(promise, timeout2) {
490
+ return new Promise((resolve, reject) => {
491
+ const timeoutId = setTimeout(() => {
492
+ reject(new Error(`Promise timed out after ${timeout2}ms`));
493
+ }, timeout2);
494
+ promise.then(
495
+ (result) => {
496
+ clearTimeout(timeoutId);
497
+ resolve(result);
498
+ },
499
+ (error) => {
500
+ clearTimeout(timeoutId);
501
+ reject(error);
502
+ }
503
+ );
504
+ });
505
+ }
506
+
381
507
  // src/string/deburr.ts
382
508
  function deburr(str) {
383
509
  return str.replace(/[^\u0000-\u007E]/g, (chr) => chr.normalize("NFD").replace(/[\u0300-\u036F]/g, ""));
@@ -409,6 +535,16 @@ function capitalize(str) {
409
535
  return str.charAt(0).toUpperCase() + str.slice(1);
410
536
  }
411
537
 
538
+ // src/string/dashCase.ts
539
+ function dashCase(str) {
540
+ const words = splitWords(str);
541
+ let dashCase2 = "";
542
+ for (const word of words) {
543
+ dashCase2 += word.toLowerCase() + "-";
544
+ }
545
+ return dashCase2.slice(0, -1);
546
+ }
547
+
412
548
  // src/string/escapeHtml.ts
413
549
  function escapeHtml(str) {
414
550
  const escapeChars = {
@@ -519,12 +655,14 @@ function isUrl(str) {
519
655
  }
520
656
  }
521
657
  export {
658
+ Queue,
522
659
  after,
523
660
  before,
524
661
  camelCase,
525
662
  capitalize,
526
663
  chunk,
527
664
  count,
665
+ dashCase,
528
666
  debounce,
529
667
  deburr,
530
668
  difference,
@@ -544,6 +682,8 @@ export {
544
682
  once,
545
683
  pascalCase,
546
684
  pick,
685
+ races,
686
+ retry,
547
687
  sample,
548
688
  shuffle,
549
689
  sleep,
@@ -554,6 +694,7 @@ export {
554
694
  takeRightWhile,
555
695
  takeWhile,
556
696
  throttle,
697
+ timeout,
557
698
  times,
558
699
  unescapeHtml,
559
700
  unique