atomic-fns 1.1.7 → 2.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 +405 -60
- package/dist/array/index.d.ts +9 -0
- package/dist/array/index.js +9 -0
- package/dist/array/list.d.ts +99 -0
- package/dist/array/list.js +133 -0
- package/dist/array/sets.d.ts +130 -0
- package/dist/array/sets.js +213 -0
- package/dist/async/index.d.ts +6 -0
- package/dist/async/index.js +6 -0
- package/dist/async/retry.d.ts +38 -0
- package/dist/async/retry.js +56 -0
- package/dist/async/sleep.d.ts +42 -0
- package/dist/async/sleep.js +76 -0
- package/dist/collections/bisect.d.ts +103 -0
- package/dist/collections/bisect.js +157 -0
- package/dist/collections/deque.d.ts +67 -97
- package/dist/collections/deque.js +197 -249
- package/dist/collections/entries.d.ts +89 -0
- package/dist/collections/entries.js +131 -0
- package/dist/collections/frozen-set.d.ts +42 -0
- package/dist/collections/frozen-set.js +64 -0
- package/dist/collections/group.d.ts +141 -0
- package/dist/collections/group.js +241 -0
- package/dist/collections/heap.d.ts +64 -0
- package/dist/collections/heap.js +205 -0
- package/dist/collections/index.d.ts +15 -642
- package/dist/collections/index.js +14 -716
- package/dist/collections/internal/arrayKeys.d.ts +19 -0
- package/dist/collections/internal/arrayKeys.js +83 -0
- package/dist/collections/internal/invariants.d.ts +4 -0
- package/dist/collections/internal/invariants.js +12 -0
- package/dist/collections/internal/iterable.d.ts +25 -0
- package/dist/collections/internal/iterable.js +62 -0
- package/dist/collections/iterate.d.ts +162 -0
- package/dist/collections/iterate.js +188 -0
- package/dist/collections/lru-cache.d.ts +58 -0
- package/dist/collections/lru-cache.js +164 -0
- package/dist/collections/order.d.ts +19 -0
- package/dist/collections/order.js +38 -0
- package/dist/collections/shape.d.ts +32 -0
- package/dist/collections/shape.js +86 -0
- package/dist/collections/sorted-map.d.ts +171 -0
- package/dist/collections/sorted-map.js +845 -0
- package/dist/collections/sorted-set.d.ts +103 -0
- package/dist/collections/sorted-set.js +147 -0
- package/dist/collections/splay-tree.d.ts +82 -0
- package/dist/collections/splay-tree.js +354 -0
- package/dist/collections/trie.d.ts +58 -0
- package/dist/collections/trie.js +196 -0
- package/dist/decimal/currency.d.ts +211 -0
- package/dist/decimal/currency.js +321 -0
- package/dist/decimal/decimal.d.ts +299 -0
- package/dist/decimal/decimal.js +660 -0
- package/dist/decimal/index.d.ts +11 -145
- package/dist/decimal/index.js +10 -374
- package/dist/decimal/internal/bits.d.ts +2 -0
- package/dist/decimal/internal/bits.js +7 -0
- package/dist/decimal/internal/echo.d.ts +4 -0
- package/dist/decimal/internal/echo.js +6 -0
- package/dist/decimal/internal/isqrt.d.ts +9 -0
- package/dist/decimal/internal/isqrt.js +34 -0
- package/dist/decimal/internal/locale.d.ts +7 -0
- package/dist/decimal/internal/locale.js +24 -0
- package/dist/decimal/internal/minor-units.d.ts +2 -0
- package/dist/decimal/internal/minor-units.js +33 -0
- package/dist/decimal/internal/pow10.d.ts +24 -0
- package/dist/decimal/internal/pow10.js +96 -0
- package/dist/encoding/base64.d.ts +39 -0
- package/dist/encoding/base64.js +53 -0
- package/dist/encoding/hex.d.ts +23 -0
- package/dist/encoding/hex.js +34 -0
- package/dist/encoding/index.d.ts +6 -0
- package/dist/encoding/index.js +6 -0
- package/dist/function/debounce.d.ts +68 -0
- package/dist/function/debounce.js +203 -0
- package/dist/function/index.d.ts +6 -0
- package/dist/function/index.js +6 -0
- package/dist/function/shared.d.ts +2 -0
- package/dist/function/shared.js +5 -0
- package/dist/function/wrap.d.ts +130 -0
- package/dist/function/wrap.js +113 -0
- package/dist/hash/digest.d.ts +18 -0
- package/dist/hash/digest.js +26 -0
- package/dist/hash/hash.d.ts +22 -0
- package/dist/hash/hash.js +252 -0
- package/dist/hash/hashString.d.ts +13 -0
- package/dist/hash/hashString.js +53 -0
- package/dist/hash/index.d.ts +8 -0
- package/dist/hash/index.js +8 -0
- package/dist/hash/stableStringify.d.ts +19 -0
- package/dist/hash/stableStringify.js +102 -0
- package/dist/id/index.d.ts +7 -0
- package/dist/id/index.js +7 -0
- package/dist/id/nanoid.d.ts +16 -0
- package/dist/id/nanoid.js +65 -0
- package/dist/id/uniqueId.d.ts +14 -0
- package/dist/id/uniqueId.js +20 -0
- package/dist/id/uuid.d.ts +25 -0
- package/dist/id/uuid.js +64 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.js +15 -8
- package/dist/internal/bytes.d.ts +11 -0
- package/dist/internal/bytes.js +33 -0
- package/dist/internal/codec.d.ts +11 -0
- package/dist/internal/codec.js +84 -0
- package/dist/internal/compare.d.ts +10 -0
- package/dist/internal/compare.js +26 -0
- package/dist/internal/crypto.d.ts +5 -0
- package/dist/internal/crypto.js +11 -0
- package/dist/internal/keys.d.ts +12 -0
- package/dist/internal/keys.js +23 -0
- package/dist/internal/kind.d.ts +44 -0
- package/dist/internal/kind.js +139 -0
- package/dist/internal/sparse.d.ts +51 -0
- package/dist/internal/sparse.js +104 -0
- package/dist/internal/text.d.ts +7 -0
- package/dist/internal/text.js +15 -0
- package/dist/internal/timer.d.ts +10 -0
- package/dist/internal/timer.js +17 -0
- package/dist/internal/truncate.d.ts +10 -0
- package/dist/internal/truncate.js +22 -0
- package/dist/internal/walk.d.ts +47 -0
- package/dist/internal/walk.js +100 -0
- package/dist/is/index.d.ts +8 -0
- package/dist/is/index.js +8 -0
- package/dist/is/isArrayLike.d.ts +18 -0
- package/dist/is/isArrayLike.js +25 -0
- package/dist/is/isError.d.ts +13 -0
- package/dist/is/isError.js +18 -0
- package/dist/is/isNil.d.ts +12 -0
- package/dist/is/isNil.js +14 -0
- package/dist/is/isPlainObject.d.ts +15 -0
- package/dist/is/isPlainObject.js +34 -0
- package/dist/number/index.d.ts +7 -0
- package/dist/number/index.js +7 -0
- package/dist/number/round.d.ts +86 -0
- package/dist/number/round.js +130 -0
- package/dist/number/stats.d.ts +52 -0
- package/dist/number/stats.js +115 -0
- package/dist/object/cloneDeep.d.ts +28 -0
- package/dist/object/cloneDeep.js +200 -0
- package/dist/object/index.d.ts +14 -0
- package/dist/object/index.js +13 -0
- package/dist/object/isEqual.d.ts +64 -0
- package/dist/object/isEqual.js +289 -0
- package/dist/object/matches.d.ts +40 -0
- package/dist/object/matches.js +284 -0
- package/dist/object/merge.d.ts +66 -0
- package/dist/object/merge.js +272 -0
- package/dist/object/parsePath.d.ts +22 -0
- package/dist/object/parsePath.js +111 -0
- package/dist/object/path.d.ts +85 -0
- package/dist/object/path.js +208 -0
- package/dist/object/pick.d.ts +91 -0
- package/dist/object/pick.js +180 -0
- package/dist/object/reshape.d.ts +73 -0
- package/dist/object/reshape.js +144 -0
- package/dist/random/index.d.ts +96 -32
- package/dist/random/index.js +215 -50
- package/dist/string/case.d.ts +71 -0
- package/dist/string/case.js +152 -0
- package/dist/string/fold.d.ts +35 -0
- package/dist/string/fold.js +100 -0
- package/dist/string/index.d.ts +6 -187
- package/dist/string/index.js +6 -226
- package/dist/string/truncate.d.ts +25 -0
- package/dist/string/truncate.js +44 -0
- package/dist/string/words.d.ts +27 -0
- package/dist/string/words.js +241 -0
- package/package.json +101 -58
- package/dist/collections/BTree.d.ts +0 -229
- package/dist/collections/BTree.js +0 -1165
- package/dist/collections/Heap.d.ts +0 -69
- package/dist/collections/Heap.js +0 -181
- package/dist/collections/LRUCache.d.ts +0 -80
- package/dist/collections/LRUCache.js +0 -126
- package/dist/collections/SortedMap.d.ts +0 -92
- package/dist/collections/SortedMap.js +0 -131
- package/dist/collections/SortedSet.d.ts +0 -69
- package/dist/collections/SortedSet.js +0 -99
- package/dist/collections/SortedTree.d.ts +0 -103
- package/dist/collections/SortedTree.js +0 -141
- package/dist/collections/SplayTree.d.ts +0 -105
- package/dist/collections/SplayTree.js +0 -338
- package/dist/collections/Trie.d.ts +0 -42
- package/dist/collections/Trie.js +0 -104
- package/dist/collections/abc.d.ts +0 -152
- package/dist/collections/abc.js +0 -39
- package/dist/collections/frozenset.d.ts +0 -19
- package/dist/collections/frozenset.js +0 -30
- package/dist/functools/index.d.ts +0 -130
- package/dist/functools/index.js +0 -235
- package/dist/globals/index.d.ts +0 -285
- package/dist/globals/index.js +0 -463
- package/dist/i18n/currency.d.ts +0 -24
- package/dist/i18n/currency.js +0 -85
- package/dist/i18n/date/index.d.ts +0 -203
- package/dist/i18n/date/index.js +0 -531
- package/dist/i18n/date/utils.d.ts +0 -65
- package/dist/i18n/date/utils.js +0 -290
- package/dist/i18n/duration.d.ts +0 -112
- package/dist/i18n/duration.js +0 -392
- package/dist/i18n/format.d.ts +0 -31
- package/dist/i18n/format.js +0 -506
- package/dist/i18n/index.d.ts +0 -8
- package/dist/i18n/index.js +0 -8
- package/dist/itertools/index.d.ts +0 -255
- package/dist/itertools/index.js +0 -437
- package/dist/math/index.d.ts +0 -96
- package/dist/math/index.js +0 -98
- package/dist/mod.d.ts +0 -1
- package/dist/mod.js +0 -1
- package/dist/operators/index.d.ts +0 -152
- package/dist/operators/index.js +0 -305
- package/dist/types.d.ts +0 -19
- package/dist/types.js +0 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array building blocks: `chunk`, `zip`, `range`, `remove`, `dropWhile`, `takeWhile`.
|
|
3
|
+
*/
|
|
4
|
+
import { assertNotHugeSparse, walkArray } from '../internal/sparse.js';
|
|
5
|
+
/** The longest array the platform can build. */
|
|
6
|
+
const MAX_LENGTH = 2 ** 32 - 1;
|
|
7
|
+
/**
|
|
8
|
+
* `array` split into consecutive pieces of `size` elements; the last piece holds the rest.
|
|
9
|
+
* @param array The array to split.
|
|
10
|
+
* @param size The length of each piece, a positive integer.
|
|
11
|
+
* @returns A new array of new arrays.
|
|
12
|
+
* @throws {RangeError} when `size` is not a positive integer, or `array` is a huge sparse array.
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function chunk(array, size) {
|
|
19
|
+
if (!Number.isInteger(size) || size < 1) {
|
|
20
|
+
throw new RangeError(`chunk size must be a positive integer, got ${String(size)}`);
|
|
21
|
+
}
|
|
22
|
+
assertNotHugeSparse(array);
|
|
23
|
+
const out = [];
|
|
24
|
+
for (let i = 0; i < array.length; i += size)
|
|
25
|
+
out.push(array.slice(i, i + size));
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Rows built from the elements at each index of `arrays`: `zip(a, b)[i]` is `[a[i], b[i]]`. The
|
|
30
|
+
* result is as long as the longest input, and shorter inputs give `undefined`, as lodash does, so
|
|
31
|
+
* `zip(...zip(a, b))` restores `a` and `b` padded to one length.
|
|
32
|
+
* @param arrays The arrays to combine.
|
|
33
|
+
* @returns A new array of rows.
|
|
34
|
+
* @throws {RangeError} when an input is a huge sparse array.
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* zip(['a', 'b'], [1, 2]) // [['a', 1], ['b', 2]]
|
|
38
|
+
* zip(...[['a', 1], ['b', 2]]) // [['a', 'b'], [1, 2]]
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function zip(...arrays) {
|
|
42
|
+
let length = 0;
|
|
43
|
+
for (const arr of arrays) {
|
|
44
|
+
assertNotHugeSparse(arr);
|
|
45
|
+
length = Math.max(length, arr.length);
|
|
46
|
+
}
|
|
47
|
+
return Array.from({ length }, (_, i) => arrays.map((arr) => arr[i]));
|
|
48
|
+
}
|
|
49
|
+
export function range(first, last, by) {
|
|
50
|
+
const [start, stop, step] = last === undefined ? [0, first, 1] : [first, last, by ?? 1];
|
|
51
|
+
for (const n of [start, stop, step]) {
|
|
52
|
+
if (typeof n !== 'number')
|
|
53
|
+
throw new TypeError(`range bounds must be numbers, got ${typeof n}`);
|
|
54
|
+
if (!Number.isFinite(n))
|
|
55
|
+
throw new RangeError('range bounds and step must be finite');
|
|
56
|
+
}
|
|
57
|
+
if (step === 0)
|
|
58
|
+
throw new RangeError('range step must not be zero');
|
|
59
|
+
const count = Math.max(0, Math.ceil((stop - start) / step));
|
|
60
|
+
if (count > MAX_LENGTH)
|
|
61
|
+
throw new RangeError(`range of ${String(count)} numbers is too long`);
|
|
62
|
+
const out = [];
|
|
63
|
+
for (let i = 0; i < count; i++) {
|
|
64
|
+
const value = start + i * step;
|
|
65
|
+
if (step > 0 ? !(value < stop) : !(value > stop))
|
|
66
|
+
break;
|
|
67
|
+
out.push(value);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
export function remove(array, predicate) {
|
|
72
|
+
const removed = [];
|
|
73
|
+
const kept = [];
|
|
74
|
+
walkArray(array, (event) => {
|
|
75
|
+
if (event.kind !== 'value')
|
|
76
|
+
return;
|
|
77
|
+
const value = event.value;
|
|
78
|
+
if (predicate(value, event.index, array))
|
|
79
|
+
removed.push(value);
|
|
80
|
+
else
|
|
81
|
+
kept.push([event.index - removed.length, value]);
|
|
82
|
+
});
|
|
83
|
+
if (removed.length === 0)
|
|
84
|
+
return [];
|
|
85
|
+
const length = array.length - removed.length;
|
|
86
|
+
array.length = 0;
|
|
87
|
+
for (const [index, value] of kept)
|
|
88
|
+
array[index] = value;
|
|
89
|
+
array.length = length;
|
|
90
|
+
return removed;
|
|
91
|
+
}
|
|
92
|
+
function splitWhile(array, predicate, take) {
|
|
93
|
+
const out = [];
|
|
94
|
+
const length = array.length;
|
|
95
|
+
let boundary = length;
|
|
96
|
+
let matching = true;
|
|
97
|
+
walkArray(array, (event) => {
|
|
98
|
+
if (event.kind !== 'value')
|
|
99
|
+
return;
|
|
100
|
+
const value = event.value;
|
|
101
|
+
if (matching) {
|
|
102
|
+
if (predicate(value, event.index, array)) {
|
|
103
|
+
if (take)
|
|
104
|
+
out[event.index] = value;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
matching = false;
|
|
108
|
+
boundary = event.index;
|
|
109
|
+
}
|
|
110
|
+
if (take)
|
|
111
|
+
return false;
|
|
112
|
+
out[event.index - boundary] = value;
|
|
113
|
+
});
|
|
114
|
+
out.length = take ? boundary : length - boundary;
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* `array` without its leading elements for which `predicate` is truthy. Holes are skipped, as
|
|
119
|
+
* {@link remove} skips them, and keep their place in the result.
|
|
120
|
+
* @param array The array to read.
|
|
121
|
+
* @param predicate Tests one element present; gets `(value, index, array)`.
|
|
122
|
+
* @returns A new array from the first element that fails.
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* dropWhile([1, 2, 3, 1], (n) => n < 3) // [3, 1]
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function dropWhile(array, predicate) {
|
|
129
|
+
return splitWhile(array, predicate, false);
|
|
130
|
+
}
|
|
131
|
+
export function takeWhile(array, predicate) {
|
|
132
|
+
return splitWhile(array, predicate, true);
|
|
133
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set operations on arrays: values in order of first appearance, duplicates removed, compared
|
|
3
|
+
* with SameValueZero (as `Set` and `includes` do) unless a key or equality function is given.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `array` with the later elements that give the same key as an earlier one removed.
|
|
7
|
+
* `[...new Set(array)]` is the plain `uniq`.
|
|
8
|
+
* @param array The array to read.
|
|
9
|
+
* @param keyOf Returns the key of one element, compared with SameValueZero.
|
|
10
|
+
* @returns A new array of the first element for each key.
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* uniqBy([2.1, 1.2, 2.3], Math.floor) // [2.1, 1.2]
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function uniqBy<T>(array: Iterable<T>, keyOf: (value: T) => unknown): T[];
|
|
17
|
+
/**
|
|
18
|
+
* `array` with the later elements that `equals` matches to an earlier one removed. It costs one
|
|
19
|
+
* comparison per pair of kept elements.
|
|
20
|
+
* @param array The array to read.
|
|
21
|
+
* @param equals Returns truthy when two elements count as the same; it gets the element under test
|
|
22
|
+
* first and the kept element second.
|
|
23
|
+
* @returns A new array of the first element of each group.
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* uniqWith([{ x: 1 }, { x: 1 }], (a, b) => a.x === b.x) // [{ x: 1 }]
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function uniqWith<T>(array: Iterable<T>, equals: (a: T, b: T) => unknown): T[];
|
|
30
|
+
/**
|
|
31
|
+
* The distinct values of `array` that none of `others` holds.
|
|
32
|
+
* @param array The array to keep values from.
|
|
33
|
+
* @param others The arrays whose values to leave out.
|
|
34
|
+
* @returns A new array, in the order of `array`.
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* difference([2, 1, 2, 3], [2]) // [1, 3]
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function difference<T>(array: Iterable<T>, ...others: Iterable<T>[]): T[];
|
|
41
|
+
/**
|
|
42
|
+
* {@link difference} by the key `keyOf` gives each value; the key function comes last.
|
|
43
|
+
* @param array The array to keep values from.
|
|
44
|
+
* @param rest The arrays to leave out, then the key function.
|
|
45
|
+
* @returns A new array, in the order of `array`.
|
|
46
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* differenceBy([2.1, 1.2], [2.3], Math.floor) // [1.2]
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function differenceBy<T>(array: Iterable<T>, ...rest: [...others: Iterable<T>[], keyOf: (value: T) => unknown]): T[];
|
|
53
|
+
/**
|
|
54
|
+
* {@link difference} with `equals` deciding which values match; the function comes last.
|
|
55
|
+
* @param array The array to keep values from.
|
|
56
|
+
* @param rest The arrays to leave out, then the equality function, which gets the element under
|
|
57
|
+
* test first and the kept or excluded element second.
|
|
58
|
+
* @returns A new array, in the order of `array`.
|
|
59
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* differenceWith([{ x: 1 }, { x: 2 }], [{ x: 1 }], (a, b) => a.x === b.x) // [{ x: 2 }]
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function differenceWith<T>(array: Iterable<T>, ...rest: [...others: Iterable<T>[], equals: (a: T, b: T) => unknown]): T[];
|
|
66
|
+
/**
|
|
67
|
+
* The distinct values of the first array that every other array holds.
|
|
68
|
+
* @param arrays The arrays to intersect.
|
|
69
|
+
* @returns A new array, in the order of the first array; `[]` with no arrays.
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* intersection([2, 1, 2], [2, 3], [4, 2]) // [2]
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function intersection<T>(...arrays: Iterable<T>[]): T[];
|
|
76
|
+
/**
|
|
77
|
+
* {@link intersection} by the key `keyOf` gives each value; the key function comes last.
|
|
78
|
+
* @param rest The arrays to intersect, then the key function.
|
|
79
|
+
* @returns A new array, in the order of the first array.
|
|
80
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* intersectionBy([2.1, 1.2], [2.3], Math.floor) // [2.1]
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function intersectionBy<T>(...rest: [...arrays: Iterable<T>[], keyOf: (value: T) => unknown]): T[];
|
|
87
|
+
/**
|
|
88
|
+
* The distinct values of all `arrays`, in order of first appearance.
|
|
89
|
+
* @param arrays The arrays to join.
|
|
90
|
+
* @returns A new array.
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* union([2], [1, 2], [3]) // [2, 1, 3]
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function union<T>(...arrays: Iterable<T>[]): T[];
|
|
97
|
+
/**
|
|
98
|
+
* {@link union} by the key `keyOf` gives each value; the key function comes last.
|
|
99
|
+
* @param rest The arrays to join, then the key function.
|
|
100
|
+
* @returns A new array of the first value for each key.
|
|
101
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* unionBy([2.1], [1.2, 2.3], Math.floor) // [2.1, 1.2]
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function unionBy<T>(...rest: [...arrays: Iterable<T>[], keyOf: (value: T) => unknown]): T[];
|
|
108
|
+
/**
|
|
109
|
+
* The distinct values that exactly one of `arrays` holds (for two arrays, the symmetric
|
|
110
|
+
* difference), in order of first appearance.
|
|
111
|
+
* @param arrays The arrays to compare.
|
|
112
|
+
* @returns A new array.
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* xor([2, 1], [2, 3]) // [1, 3]
|
|
116
|
+
* xor([1], [1], [1]) // []
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function xor<T>(...arrays: Iterable<T>[]): T[];
|
|
120
|
+
/**
|
|
121
|
+
* {@link xor} by the key `keyOf` gives each value; the key function comes last.
|
|
122
|
+
* @param rest The arrays to compare, then the key function.
|
|
123
|
+
* @returns A new array of the first value for each key only one array holds.
|
|
124
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* xorBy([2.1, 1.2], [2.3], Math.floor) // [1.2]
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare function xorBy<T>(...rest: [...arrays: Iterable<T>[], keyOf: (value: T) => unknown]): T[];
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set operations on arrays: values in order of first appearance, duplicates removed, compared
|
|
3
|
+
* with SameValueZero (as `Set` and `includes` do) unless a key or equality function is given.
|
|
4
|
+
*/
|
|
5
|
+
const same = (value) => value;
|
|
6
|
+
function splitLast(rest, what) {
|
|
7
|
+
const fn = rest[rest.length - 1];
|
|
8
|
+
if (typeof fn !== 'function') {
|
|
9
|
+
throw new TypeError(`the last argument must be ${what}, got ${typeof fn}`);
|
|
10
|
+
}
|
|
11
|
+
// TypeScript cannot split a tuple whose rest element comes first, so the halves are cast.
|
|
12
|
+
return [rest.slice(0, -1), fn];
|
|
13
|
+
}
|
|
14
|
+
function uniqueBy(lists, keyOf, keep) {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const list of lists) {
|
|
18
|
+
for (const value of list) {
|
|
19
|
+
const key = keyOf(value);
|
|
20
|
+
if (seen.has(key))
|
|
21
|
+
continue;
|
|
22
|
+
seen.add(key);
|
|
23
|
+
if (keep(key))
|
|
24
|
+
out.push(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
function keySet(list, keyOf) {
|
|
30
|
+
const keys = new Set();
|
|
31
|
+
for (const value of list)
|
|
32
|
+
keys.add(keyOf(value));
|
|
33
|
+
return keys;
|
|
34
|
+
}
|
|
35
|
+
function uniqueWith(lists, equals, keep) {
|
|
36
|
+
const out = [];
|
|
37
|
+
for (const list of lists) {
|
|
38
|
+
for (const value of list) {
|
|
39
|
+
if (out.some((kept) => equals(value, kept)) || !keep(value))
|
|
40
|
+
continue;
|
|
41
|
+
out.push(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `array` with the later elements that give the same key as an earlier one removed.
|
|
48
|
+
* `[...new Set(array)]` is the plain `uniq`.
|
|
49
|
+
* @param array The array to read.
|
|
50
|
+
* @param keyOf Returns the key of one element, compared with SameValueZero.
|
|
51
|
+
* @returns A new array of the first element for each key.
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* uniqBy([2.1, 1.2, 2.3], Math.floor) // [2.1, 1.2]
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function uniqBy(array, keyOf) {
|
|
58
|
+
return uniqueBy([array], keyOf, () => true);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `array` with the later elements that `equals` matches to an earlier one removed. It costs one
|
|
62
|
+
* comparison per pair of kept elements.
|
|
63
|
+
* @param array The array to read.
|
|
64
|
+
* @param equals Returns truthy when two elements count as the same; it gets the element under test
|
|
65
|
+
* first and the kept element second.
|
|
66
|
+
* @returns A new array of the first element of each group.
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* uniqWith([{ x: 1 }, { x: 1 }], (a, b) => a.x === b.x) // [{ x: 1 }]
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function uniqWith(array, equals) {
|
|
73
|
+
return uniqueWith([array], equals, () => true);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The distinct values of `array` that none of `others` holds.
|
|
77
|
+
* @param array The array to keep values from.
|
|
78
|
+
* @param others The arrays whose values to leave out.
|
|
79
|
+
* @returns A new array, in the order of `array`.
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* difference([2, 1, 2, 3], [2]) // [1, 3]
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function difference(array, ...others) {
|
|
86
|
+
return differenceBy(array, ...others, same);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* {@link difference} by the key `keyOf` gives each value; the key function comes last.
|
|
90
|
+
* @param array The array to keep values from.
|
|
91
|
+
* @param rest The arrays to leave out, then the key function.
|
|
92
|
+
* @returns A new array, in the order of `array`.
|
|
93
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* differenceBy([2.1, 1.2], [2.3], Math.floor) // [1.2]
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function differenceBy(array, ...rest) {
|
|
100
|
+
const [others, keyOf] = splitLast(rest, 'a key function');
|
|
101
|
+
const excluded = new Set();
|
|
102
|
+
for (const other of others)
|
|
103
|
+
for (const value of other)
|
|
104
|
+
excluded.add(keyOf(value));
|
|
105
|
+
return uniqueBy([array], keyOf, (key) => !excluded.has(key));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* {@link difference} with `equals` deciding which values match; the function comes last.
|
|
109
|
+
* @param array The array to keep values from.
|
|
110
|
+
* @param rest The arrays to leave out, then the equality function, which gets the element under
|
|
111
|
+
* test first and the kept or excluded element second.
|
|
112
|
+
* @returns A new array, in the order of `array`.
|
|
113
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* differenceWith([{ x: 1 }, { x: 2 }], [{ x: 1 }], (a, b) => a.x === b.x) // [{ x: 2 }]
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export function differenceWith(array, ...rest) {
|
|
120
|
+
const [others, equals] = splitLast(rest, 'an equality function');
|
|
121
|
+
const excluded = others.flatMap((other) => [...other]);
|
|
122
|
+
return uniqueWith([array], equals, (value) => !excluded.some((other) => equals(value, other)));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The distinct values of the first array that every other array holds.
|
|
126
|
+
* @param arrays The arrays to intersect.
|
|
127
|
+
* @returns A new array, in the order of the first array; `[]` with no arrays.
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* intersection([2, 1, 2], [2, 3], [4, 2]) // [2]
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export function intersection(...arrays) {
|
|
134
|
+
return intersectionBy(...arrays, same);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* {@link intersection} by the key `keyOf` gives each value; the key function comes last.
|
|
138
|
+
* @param rest The arrays to intersect, then the key function.
|
|
139
|
+
* @returns A new array, in the order of the first array.
|
|
140
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* intersectionBy([2.1, 1.2], [2.3], Math.floor) // [2.1]
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function intersectionBy(...rest) {
|
|
147
|
+
const [arrays, keyOf] = splitLast(rest, 'a key function');
|
|
148
|
+
if (arrays.length === 0)
|
|
149
|
+
return [];
|
|
150
|
+
const others = arrays.slice(1).map((other) => keySet(other, keyOf));
|
|
151
|
+
return uniqueBy([arrays[0]], keyOf, (key) => others.every((keys) => keys.has(key)));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The distinct values of all `arrays`, in order of first appearance.
|
|
155
|
+
* @param arrays The arrays to join.
|
|
156
|
+
* @returns A new array.
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* union([2], [1, 2], [3]) // [2, 1, 3]
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export function union(...arrays) {
|
|
163
|
+
return uniqueBy(arrays, same, () => true);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* {@link union} by the key `keyOf` gives each value; the key function comes last.
|
|
167
|
+
* @param rest The arrays to join, then the key function.
|
|
168
|
+
* @returns A new array of the first value for each key.
|
|
169
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
170
|
+
* @example
|
|
171
|
+
* ```ts
|
|
172
|
+
* unionBy([2.1], [1.2, 2.3], Math.floor) // [2.1, 1.2]
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
export function unionBy(...rest) {
|
|
176
|
+
const [arrays, keyOf] = splitLast(rest, 'a key function');
|
|
177
|
+
return uniqueBy(arrays, keyOf, () => true);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The distinct values that exactly one of `arrays` holds (for two arrays, the symmetric
|
|
181
|
+
* difference), in order of first appearance.
|
|
182
|
+
* @param arrays The arrays to compare.
|
|
183
|
+
* @returns A new array.
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* xor([2, 1], [2, 3]) // [1, 3]
|
|
187
|
+
* xor([1], [1], [1]) // []
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export function xor(...arrays) {
|
|
191
|
+
return xorBy(...arrays, same);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* {@link xor} by the key `keyOf` gives each value; the key function comes last.
|
|
195
|
+
* @param rest The arrays to compare, then the key function.
|
|
196
|
+
* @returns A new array of the first value for each key only one array holds.
|
|
197
|
+
* @throws {TypeError} when the last argument is not a function.
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* xorBy([2.1, 1.2], [2.3], Math.floor) // [1.2]
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export function xorBy(...rest) {
|
|
204
|
+
const [arrays, keyOf] = splitLast(rest, 'a key function');
|
|
205
|
+
const lists = arrays.map((array) => Array.from(array, (value) => [value, keyOf(value)]));
|
|
206
|
+
const keyOfPair = ([, key]) => key;
|
|
207
|
+
const holders = new Map();
|
|
208
|
+
for (const list of lists) {
|
|
209
|
+
for (const key of keySet(list, keyOfPair))
|
|
210
|
+
holders.set(key, (holders.get(key) ?? 0) + 1);
|
|
211
|
+
}
|
|
212
|
+
return uniqueBy(lists, keyOfPair, (key) => holders.get(key) === 1).map(([value]) => value);
|
|
213
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Options for {@link retry}. */
|
|
2
|
+
export interface RetryOptions {
|
|
3
|
+
/** Retries after the first attempt (default 3); `Infinity` retries until success or abort. */
|
|
4
|
+
retries?: number;
|
|
5
|
+
/** Delay before the first retry, in milliseconds (default 100). */
|
|
6
|
+
minDelay?: number;
|
|
7
|
+
/** Longest delay between attempts, in milliseconds (default 10000, or `minDelay` when larger). */
|
|
8
|
+
maxDelay?: number;
|
|
9
|
+
/** Growth of the delay per retry (default 2). */
|
|
10
|
+
factor?: number;
|
|
11
|
+
/** Scale each delay by a random factor in [0.5, 1) so callers spread out (default `true`). */
|
|
12
|
+
jitter?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Stops retrying and rejects with the signal's `reason` when it aborts. A running attempt is
|
|
15
|
+
* not interrupted, so pass the same signal to the work in `fn` to stop it too.
|
|
16
|
+
*/
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
/** Decides whether an error is worth another attempt (default: every error is). */
|
|
19
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean | PromiseLike<boolean>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Calls `fn` until it succeeds, waiting between attempts with exponential backoff:
|
|
23
|
+
* `min(maxDelay, minDelay * factor ** (retry - 1))`, scaled into [0.5, 1) of that when `jitter`
|
|
24
|
+
* is on. `fn` receives the attempt number, from 1. After the last retry, or when `shouldRetry`
|
|
25
|
+
* returns `false`, the promise rejects with the error `fn` threw. An aborted `signal` rejects with
|
|
26
|
+
* its `reason` once the running attempt settles, before `shouldRetry` and the next attempt, or
|
|
27
|
+
* during a wait. Delays are checked as in {@link sleep}: negative ones clamp to 0.
|
|
28
|
+
* @param fn The work to try; may return a value or a promise.
|
|
29
|
+
* @param options Backoff, `signal` and `shouldRetry`.
|
|
30
|
+
* @returns A promise for the first successful result.
|
|
31
|
+
* @throws {TypeError} when `fn` is not a function, or a delay is not a number.
|
|
32
|
+
* @throws {RangeError} when an option is out of range, or `maxDelay` is below `minDelay`.
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const data = await retry(() => fetch('/api').then((r) => r.json()), { retries: 5 })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function retry<T>(fn: (attempt: number) => T | PromiseLike<T>, options?: RetryOptions): Promise<T>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { assertFunction } from '../function/shared.js';
|
|
2
|
+
import { timerWait } from '../internal/timer.js';
|
|
3
|
+
import { sleep } from './sleep.js';
|
|
4
|
+
/**
|
|
5
|
+
* Calls `fn` until it succeeds, waiting between attempts with exponential backoff:
|
|
6
|
+
* `min(maxDelay, minDelay * factor ** (retry - 1))`, scaled into [0.5, 1) of that when `jitter`
|
|
7
|
+
* is on. `fn` receives the attempt number, from 1. After the last retry, or when `shouldRetry`
|
|
8
|
+
* returns `false`, the promise rejects with the error `fn` threw. An aborted `signal` rejects with
|
|
9
|
+
* its `reason` once the running attempt settles, before `shouldRetry` and the next attempt, or
|
|
10
|
+
* during a wait. Delays are checked as in {@link sleep}: negative ones clamp to 0.
|
|
11
|
+
* @param fn The work to try; may return a value or a promise.
|
|
12
|
+
* @param options Backoff, `signal` and `shouldRetry`.
|
|
13
|
+
* @returns A promise for the first successful result.
|
|
14
|
+
* @throws {TypeError} when `fn` is not a function, or a delay is not a number.
|
|
15
|
+
* @throws {RangeError} when an option is out of range, or `maxDelay` is below `minDelay`.
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const data = await retry(() => fetch('/api').then((r) => r.json()), { retries: 5 })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function retry(fn, options = {}) {
|
|
22
|
+
assertFunction(fn, 'fn');
|
|
23
|
+
const retries = options.retries ?? 3;
|
|
24
|
+
if (!(Number.isInteger(retries) || retries === Number.POSITIVE_INFINITY) || retries < 0) {
|
|
25
|
+
throw new RangeError('retries must be a non-negative integer or Infinity');
|
|
26
|
+
}
|
|
27
|
+
const minDelay = timerWait(options.minDelay ?? 100, 'minDelay');
|
|
28
|
+
const maxDelay = options.maxDelay == null ? Math.max(10_000, minDelay) : timerWait(options.maxDelay, 'maxDelay');
|
|
29
|
+
if (maxDelay < minDelay)
|
|
30
|
+
throw new RangeError('maxDelay must be >= minDelay');
|
|
31
|
+
const factor = options.factor ?? 2;
|
|
32
|
+
if (typeof factor !== 'number' || !(factor >= 1 && Number.isFinite(factor))) {
|
|
33
|
+
throw new RangeError('factor must be a finite number >= 1');
|
|
34
|
+
}
|
|
35
|
+
const jitter = options.jitter ?? true;
|
|
36
|
+
const { signal, shouldRetry } = options;
|
|
37
|
+
return (async () => {
|
|
38
|
+
let backoff = minDelay;
|
|
39
|
+
for (let attempt = 1;; attempt++) {
|
|
40
|
+
signal?.throwIfAborted();
|
|
41
|
+
try {
|
|
42
|
+
return await fn(attempt);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
signal?.throwIfAborted();
|
|
46
|
+
if (attempt > retries)
|
|
47
|
+
throw error;
|
|
48
|
+
if (shouldRetry && !(await shouldRetry(error, attempt)))
|
|
49
|
+
throw error;
|
|
50
|
+
await sleep(jitter ? backoff * (0.5 + Math.random() / 2) : backoff, { signal });
|
|
51
|
+
// Grown one step at a time, so it stays capped instead of overflowing to Infinity or NaN.
|
|
52
|
+
backoff = Math.min(maxDelay, backoff * factor);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Options for {@link sleep}. */
|
|
2
|
+
export interface SleepOptions {
|
|
3
|
+
/** Rejects the wait with the signal's `reason` when it aborts. */
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
}
|
|
6
|
+
/** Options for {@link timeout}. */
|
|
7
|
+
export interface TimeoutOptions {
|
|
8
|
+
/** Rejects with the signal's `reason` when it aborts before the promise settles. */
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolves after `ms` milliseconds. An aborted `signal` rejects with its `reason`, at once when it
|
|
13
|
+
* is already aborted. Negative waits clamp to 0; NaN, infinite or past-2147483647 waits reject with
|
|
14
|
+
* `RangeError`.
|
|
15
|
+
* @param ms Milliseconds to wait.
|
|
16
|
+
* @param options `signal` to cancel the wait.
|
|
17
|
+
* @returns A promise that resolves to `undefined`.
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* await sleep(100)
|
|
21
|
+
* const controller = new AbortController()
|
|
22
|
+
* const nap = sleep(1000, { signal: controller.signal })
|
|
23
|
+
* controller.abort()
|
|
24
|
+
* await nap.catch((reason) => console.log(reason)) // the abort reason
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function sleep(ms: number, options?: SleepOptions): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Settles as `promise` does, or rejects with a `DOMException` named `TimeoutError` when `ms`
|
|
30
|
+
* milliseconds pass first, as `AbortSignal.timeout` does. An aborted `signal` rejects with its
|
|
31
|
+
* `reason`. The timer and the abort listener are removed once the result is known; `promise`
|
|
32
|
+
* itself keeps running, so pass the same `signal` to the work to stop it too.
|
|
33
|
+
* @param promise The promise to wait for.
|
|
34
|
+
* @param ms Milliseconds before it times out; checked as in {@link sleep}.
|
|
35
|
+
* @param options `signal` to cancel the wait.
|
|
36
|
+
* @returns A promise that settles with `promise`, or rejects on timeout or abort.
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const response = await timeout(fetch('/api'), 5000)
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function timeout<T>(promise: PromiseLike<T> | T, ms: number, options?: TimeoutOptions): Promise<T>;
|