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.
Files changed (216) hide show
  1. package/README.md +405 -60
  2. package/dist/array/index.d.ts +9 -0
  3. package/dist/array/index.js +9 -0
  4. package/dist/array/list.d.ts +99 -0
  5. package/dist/array/list.js +133 -0
  6. package/dist/array/sets.d.ts +130 -0
  7. package/dist/array/sets.js +213 -0
  8. package/dist/async/index.d.ts +6 -0
  9. package/dist/async/index.js +6 -0
  10. package/dist/async/retry.d.ts +38 -0
  11. package/dist/async/retry.js +56 -0
  12. package/dist/async/sleep.d.ts +42 -0
  13. package/dist/async/sleep.js +76 -0
  14. package/dist/collections/bisect.d.ts +103 -0
  15. package/dist/collections/bisect.js +157 -0
  16. package/dist/collections/deque.d.ts +67 -97
  17. package/dist/collections/deque.js +197 -249
  18. package/dist/collections/entries.d.ts +89 -0
  19. package/dist/collections/entries.js +131 -0
  20. package/dist/collections/frozen-set.d.ts +42 -0
  21. package/dist/collections/frozen-set.js +64 -0
  22. package/dist/collections/group.d.ts +141 -0
  23. package/dist/collections/group.js +241 -0
  24. package/dist/collections/heap.d.ts +64 -0
  25. package/dist/collections/heap.js +205 -0
  26. package/dist/collections/index.d.ts +15 -642
  27. package/dist/collections/index.js +14 -716
  28. package/dist/collections/internal/arrayKeys.d.ts +19 -0
  29. package/dist/collections/internal/arrayKeys.js +83 -0
  30. package/dist/collections/internal/invariants.d.ts +4 -0
  31. package/dist/collections/internal/invariants.js +12 -0
  32. package/dist/collections/internal/iterable.d.ts +25 -0
  33. package/dist/collections/internal/iterable.js +62 -0
  34. package/dist/collections/iterate.d.ts +162 -0
  35. package/dist/collections/iterate.js +188 -0
  36. package/dist/collections/lru-cache.d.ts +58 -0
  37. package/dist/collections/lru-cache.js +164 -0
  38. package/dist/collections/order.d.ts +19 -0
  39. package/dist/collections/order.js +38 -0
  40. package/dist/collections/shape.d.ts +32 -0
  41. package/dist/collections/shape.js +86 -0
  42. package/dist/collections/sorted-map.d.ts +171 -0
  43. package/dist/collections/sorted-map.js +845 -0
  44. package/dist/collections/sorted-set.d.ts +103 -0
  45. package/dist/collections/sorted-set.js +147 -0
  46. package/dist/collections/splay-tree.d.ts +82 -0
  47. package/dist/collections/splay-tree.js +354 -0
  48. package/dist/collections/trie.d.ts +58 -0
  49. package/dist/collections/trie.js +196 -0
  50. package/dist/decimal/currency.d.ts +211 -0
  51. package/dist/decimal/currency.js +321 -0
  52. package/dist/decimal/decimal.d.ts +299 -0
  53. package/dist/decimal/decimal.js +660 -0
  54. package/dist/decimal/index.d.ts +11 -145
  55. package/dist/decimal/index.js +10 -374
  56. package/dist/decimal/internal/bits.d.ts +2 -0
  57. package/dist/decimal/internal/bits.js +7 -0
  58. package/dist/decimal/internal/echo.d.ts +4 -0
  59. package/dist/decimal/internal/echo.js +6 -0
  60. package/dist/decimal/internal/isqrt.d.ts +9 -0
  61. package/dist/decimal/internal/isqrt.js +34 -0
  62. package/dist/decimal/internal/locale.d.ts +7 -0
  63. package/dist/decimal/internal/locale.js +24 -0
  64. package/dist/decimal/internal/minor-units.d.ts +2 -0
  65. package/dist/decimal/internal/minor-units.js +33 -0
  66. package/dist/decimal/internal/pow10.d.ts +24 -0
  67. package/dist/decimal/internal/pow10.js +96 -0
  68. package/dist/encoding/base64.d.ts +39 -0
  69. package/dist/encoding/base64.js +53 -0
  70. package/dist/encoding/hex.d.ts +23 -0
  71. package/dist/encoding/hex.js +34 -0
  72. package/dist/encoding/index.d.ts +6 -0
  73. package/dist/encoding/index.js +6 -0
  74. package/dist/function/debounce.d.ts +68 -0
  75. package/dist/function/debounce.js +203 -0
  76. package/dist/function/index.d.ts +6 -0
  77. package/dist/function/index.js +6 -0
  78. package/dist/function/shared.d.ts +2 -0
  79. package/dist/function/shared.js +5 -0
  80. package/dist/function/wrap.d.ts +130 -0
  81. package/dist/function/wrap.js +113 -0
  82. package/dist/hash/digest.d.ts +18 -0
  83. package/dist/hash/digest.js +26 -0
  84. package/dist/hash/hash.d.ts +22 -0
  85. package/dist/hash/hash.js +252 -0
  86. package/dist/hash/hashString.d.ts +13 -0
  87. package/dist/hash/hashString.js +53 -0
  88. package/dist/hash/index.d.ts +8 -0
  89. package/dist/hash/index.js +8 -0
  90. package/dist/hash/stableStringify.d.ts +19 -0
  91. package/dist/hash/stableStringify.js +102 -0
  92. package/dist/id/index.d.ts +7 -0
  93. package/dist/id/index.js +7 -0
  94. package/dist/id/nanoid.d.ts +16 -0
  95. package/dist/id/nanoid.js +65 -0
  96. package/dist/id/uniqueId.d.ts +14 -0
  97. package/dist/id/uniqueId.js +20 -0
  98. package/dist/id/uuid.d.ts +25 -0
  99. package/dist/id/uuid.js +64 -0
  100. package/dist/index.d.ts +15 -8
  101. package/dist/index.js +15 -8
  102. package/dist/internal/bytes.d.ts +11 -0
  103. package/dist/internal/bytes.js +33 -0
  104. package/dist/internal/codec.d.ts +11 -0
  105. package/dist/internal/codec.js +84 -0
  106. package/dist/internal/compare.d.ts +10 -0
  107. package/dist/internal/compare.js +26 -0
  108. package/dist/internal/crypto.d.ts +5 -0
  109. package/dist/internal/crypto.js +11 -0
  110. package/dist/internal/keys.d.ts +12 -0
  111. package/dist/internal/keys.js +23 -0
  112. package/dist/internal/kind.d.ts +44 -0
  113. package/dist/internal/kind.js +139 -0
  114. package/dist/internal/sparse.d.ts +51 -0
  115. package/dist/internal/sparse.js +104 -0
  116. package/dist/internal/text.d.ts +7 -0
  117. package/dist/internal/text.js +15 -0
  118. package/dist/internal/timer.d.ts +10 -0
  119. package/dist/internal/timer.js +17 -0
  120. package/dist/internal/truncate.d.ts +10 -0
  121. package/dist/internal/truncate.js +22 -0
  122. package/dist/internal/walk.d.ts +47 -0
  123. package/dist/internal/walk.js +100 -0
  124. package/dist/is/index.d.ts +8 -0
  125. package/dist/is/index.js +8 -0
  126. package/dist/is/isArrayLike.d.ts +18 -0
  127. package/dist/is/isArrayLike.js +25 -0
  128. package/dist/is/isError.d.ts +13 -0
  129. package/dist/is/isError.js +18 -0
  130. package/dist/is/isNil.d.ts +12 -0
  131. package/dist/is/isNil.js +14 -0
  132. package/dist/is/isPlainObject.d.ts +15 -0
  133. package/dist/is/isPlainObject.js +34 -0
  134. package/dist/number/index.d.ts +7 -0
  135. package/dist/number/index.js +7 -0
  136. package/dist/number/round.d.ts +86 -0
  137. package/dist/number/round.js +130 -0
  138. package/dist/number/stats.d.ts +52 -0
  139. package/dist/number/stats.js +115 -0
  140. package/dist/object/cloneDeep.d.ts +28 -0
  141. package/dist/object/cloneDeep.js +200 -0
  142. package/dist/object/index.d.ts +14 -0
  143. package/dist/object/index.js +13 -0
  144. package/dist/object/isEqual.d.ts +64 -0
  145. package/dist/object/isEqual.js +289 -0
  146. package/dist/object/matches.d.ts +40 -0
  147. package/dist/object/matches.js +284 -0
  148. package/dist/object/merge.d.ts +66 -0
  149. package/dist/object/merge.js +272 -0
  150. package/dist/object/parsePath.d.ts +22 -0
  151. package/dist/object/parsePath.js +111 -0
  152. package/dist/object/path.d.ts +85 -0
  153. package/dist/object/path.js +208 -0
  154. package/dist/object/pick.d.ts +91 -0
  155. package/dist/object/pick.js +180 -0
  156. package/dist/object/reshape.d.ts +73 -0
  157. package/dist/object/reshape.js +144 -0
  158. package/dist/random/index.d.ts +96 -32
  159. package/dist/random/index.js +215 -50
  160. package/dist/string/case.d.ts +71 -0
  161. package/dist/string/case.js +152 -0
  162. package/dist/string/fold.d.ts +35 -0
  163. package/dist/string/fold.js +100 -0
  164. package/dist/string/index.d.ts +6 -187
  165. package/dist/string/index.js +6 -226
  166. package/dist/string/truncate.d.ts +25 -0
  167. package/dist/string/truncate.js +44 -0
  168. package/dist/string/words.d.ts +27 -0
  169. package/dist/string/words.js +241 -0
  170. package/package.json +101 -58
  171. package/dist/collections/BTree.d.ts +0 -229
  172. package/dist/collections/BTree.js +0 -1165
  173. package/dist/collections/Heap.d.ts +0 -69
  174. package/dist/collections/Heap.js +0 -181
  175. package/dist/collections/LRUCache.d.ts +0 -80
  176. package/dist/collections/LRUCache.js +0 -126
  177. package/dist/collections/SortedMap.d.ts +0 -92
  178. package/dist/collections/SortedMap.js +0 -131
  179. package/dist/collections/SortedSet.d.ts +0 -69
  180. package/dist/collections/SortedSet.js +0 -99
  181. package/dist/collections/SortedTree.d.ts +0 -103
  182. package/dist/collections/SortedTree.js +0 -141
  183. package/dist/collections/SplayTree.d.ts +0 -105
  184. package/dist/collections/SplayTree.js +0 -338
  185. package/dist/collections/Trie.d.ts +0 -42
  186. package/dist/collections/Trie.js +0 -104
  187. package/dist/collections/abc.d.ts +0 -152
  188. package/dist/collections/abc.js +0 -39
  189. package/dist/collections/frozenset.d.ts +0 -19
  190. package/dist/collections/frozenset.js +0 -30
  191. package/dist/functools/index.d.ts +0 -130
  192. package/dist/functools/index.js +0 -235
  193. package/dist/globals/index.d.ts +0 -285
  194. package/dist/globals/index.js +0 -463
  195. package/dist/i18n/currency.d.ts +0 -24
  196. package/dist/i18n/currency.js +0 -85
  197. package/dist/i18n/date/index.d.ts +0 -203
  198. package/dist/i18n/date/index.js +0 -531
  199. package/dist/i18n/date/utils.d.ts +0 -65
  200. package/dist/i18n/date/utils.js +0 -290
  201. package/dist/i18n/duration.d.ts +0 -112
  202. package/dist/i18n/duration.js +0 -392
  203. package/dist/i18n/format.d.ts +0 -31
  204. package/dist/i18n/format.js +0 -506
  205. package/dist/i18n/index.d.ts +0 -8
  206. package/dist/i18n/index.js +0 -8
  207. package/dist/itertools/index.d.ts +0 -255
  208. package/dist/itertools/index.js +0 -437
  209. package/dist/math/index.d.ts +0 -96
  210. package/dist/math/index.js +0 -98
  211. package/dist/mod.d.ts +0 -1
  212. package/dist/mod.js +0 -1
  213. package/dist/operators/index.d.ts +0 -152
  214. package/dist/operators/index.js +0 -305
  215. package/dist/types.d.ts +0 -19
  216. package/dist/types.js +0 -1
package/README.md CHANGED
@@ -1,100 +1,445 @@
1
1
  # atomic-fns
2
2
 
3
- [Read the Docs](https://atomic-stack.github.io/atomic-fns/)
3
+ A utility library with no runtime dependencies that fills the gaps the platform leaves: collection helpers and data structures, object paths, deep equality and cloning, exact decimal arithmetic, array operations, function and async helpers, strings, numbers, ids, hashing and encoding.
4
4
 
5
- ## Like Lodash, but in TypeScript and for ESNext.
5
+ Install from npm with `npm install atomic-fns`, or from JSR with `deno add jsr:@atomic-stack/atomic-fns`.
6
6
 
7
- Stop shipping code built for browsers from 2015.
7
+ ## Import
8
8
 
9
- You may prefer this library because:
9
+ ```ts
10
+ import { Decimal, keys, Heap } from 'atomic-fns'
11
+ import { SortedMap } from 'atomic-fns/collections'
12
+ ```
13
+
14
+ Every export is available from the root and its area subpath. [docs/design.md](https://github.com/adrian2x/atomic-fns/blob/main/docs/design.md) describes the API and its rules.
15
+
16
+ Supports Node `^24.22.3 || >=26.13.1`, Deno, Bun, browsers at Baseline and workers. Published types support TypeScript 6.0 and later.
17
+
18
+ The 1.x line lives on the [`1.x` branch](https://github.com/adrian2x/atomic-fns/tree/1.x).
19
+
20
+ ## Collections
21
+
22
+ Sorted maps and sets, trees, heap, deque, LRU cache, trie, frozen set and bisect: the data structures the platform does not ship. No dependencies, no Node APIs.
23
+
24
+ ### Conventions
25
+
26
+ - Classes use `Map` and `Set` method names where the concept matches: `get`, `set`, `has`, `delete`, `clear`, `size`, `keys`, `values`, `entries`, `forEach(callback(value, key, collection))` and `[Symbol.iterator]`. `Deque` follows Array's names instead: `push`, `pop`, `shift`, `unshift`, `at`, `set(index, value)` and `includes`.
27
+ - Sorted structures and the bisect functions take an optional comparator `(a, b) => number`. The default orders numbers and bigints by value (compared exactly with each other) and strings by UTF-16 code units, as `<` does. `NaN` throws `RangeError`; mixed strings and numbers, `null`, `undefined` and every other value throw `TypeError` asking for a comparator. Keys the comparator calls equal are the same key.
28
+ - `Heap`, `Deque` and `FrozenSet` membership is SameValueZero, as in `Set` and `includes`.
29
+ - Errors are native `TypeError` and `RangeError`, never custom classes.
30
+
31
+ ### Ordered maps and sets
32
+
33
+ ```ts
34
+ import { SortedMap, SortedSet, SplayTree } from 'atomic-fns/collections'
35
+
36
+ const scores = new SortedMap([
37
+ ['carol', 7],
38
+ ['alice', 9]
39
+ ])
40
+ scores.set('bob', 8)
41
+ ;[...scores.keys()] // ['alice', 'bob', 'carol']
42
+ scores.at(-1) // ['carol', 7]
43
+ scores.rank('bob') // 1
44
+ scores.ceiling('b') // ['bob', 8]
45
+ scores.entriesBetween('a', 'c') // [['alice', 9], ['bob', 8]]
46
+ scores.removeRange('a', 'b') // 1 (alice)
47
+ const snapshot = scores.clone() // O(1), copy-on-write
48
+
49
+ const ids = new SortedSet([30, 10, 20])
50
+ ids.higher(10) // 20
51
+
52
+ const byDate = new SortedMap<Date, string>(undefined, (a, b) => a.getTime() - b.getTime())
53
+ ```
54
+
55
+ `SortedMap` is a copy-on-write B+ tree with subtree counts: lookups, `at` and `rank` take O(log n), range queries O(log n + k), and `updateRange` and `removeRange` are atomic, so a throwing callback or comparator leaves the map unchanged. A comparator that returns a non-finite number, or does not return 0 for a key against itself, is rejected before anything changes. `SortedSet` has the same ordered queries over values (`first`, `last`, `lower`, `higher`, `floor`, `ceiling`, `at`, `rank`, `valuesBetween`). `SplayTree` is an ordered map on a randomized splay tree, for skewed access patterns: `get` and `has` move the key they find to the root with probability 0.5, and `firstKey`, `lastKey`, `lower` and `higher` always do.
56
+
57
+ ### Heap, Deque, LRUCache
58
+
59
+ ```ts
60
+ import { Deque, Heap, LRUCache } from 'atomic-fns/collections'
61
+
62
+ const tasks = new Heap([5, 1, 3]) // pass (a, b) => b - a for a max-heap
63
+ tasks.pop() // 1
64
+
65
+ const recent = new Deque<number>([], { maxlen: 3 })
66
+ recent.push(1, 2, 3, 4) // 3, the new size
67
+ ;[...recent] // [2, 3, 4]
68
+ recent.unshift(0) // 3
69
+ ;[...recent] // [0, 2, 3]
70
+
71
+ const cache = new LRUCache<string, string>(100)
72
+ cache.set('/a', '<html>')
73
+ cache.get('/a') // refreshes recency; has() does not
74
+ ```
75
+
76
+ ### Trie and FrozenSet
77
+
78
+ ```ts
79
+ import { FrozenSet, Trie } from 'atomic-fns/collections'
80
+
81
+ const words = new Trie(['car', 'cart', 'cat'])
82
+ ;[...words.withPrefix('car')] // ['car', 'cart']
83
+
84
+ const roles = new FrozenSet(['admin', 'editor'])
85
+ roles.add('guest') // throws TypeError
86
+ ```
10
87
 
11
- * **TypeScript & docs**: Includes type declaration files and js with JSDocs for best in class experience.
12
- * **Zero deps**: Built from scratch with no runtime dependencies or polyfills.
13
- * **ESNext**: Uses modern idiomatic syntax, data structures, and control flow techniques.
14
- * **Modular**: Pick what you need. Supports tree-shaking, no side effects, and works with all bundlers.
15
- * **Lazy**: Leverage generators, iterators, and functional composition.
16
- * **Tests**: All modules have comprehensive test suites and 100% test coverage.
88
+ ### Bisect
17
89
 
18
- ## Docs
90
+ ```ts
91
+ import { binarySearch, bisectLeft, bisectRight, insort } from 'atomic-fns/collections'
19
92
 
20
- 📖 Available at [atomic-fns](https://atomic-stack.github.io/atomic-fns/)
93
+ bisectLeft([1, 2, 2, 3], 2) // 1
94
+ bisectRight([1, 2, 2, 3], 2) // 3
95
+ insort([1, 3], 2) // [1, 2, 3]
96
+ binarySearch([1, 3, 5], 4) // -3: absent, belongs at index 2
97
+ const rows = [{ id: 1 }, { id: 4 }]
98
+ bisectLeft(rows, { id: 3 }, (a, b) => a.id - b.id) // 1; lo and hi follow the comparator
99
+ ```
100
+
101
+ ## Objects, arrays and helpers
102
+
103
+ Lodash's intent on today's platform: object paths, deep equality and cloning, iteration over every shape, array set operations, function and async helpers, strings, numbers, ids, hashing and encoding.
104
+
105
+ ### Collections, objects and arrays
106
+
107
+ #### Iteration over every shape (`atomic-fns/collections`)
108
+
109
+ ```ts
110
+ import {
111
+ countBy,
112
+ entries,
113
+ filter,
114
+ groupBy,
115
+ includes,
116
+ map,
117
+ size,
118
+ sortBy
119
+ } from 'atomic-fns/collections'
120
+
121
+ entries({ a: 1 }) // [['a', 1]]
122
+ countBy(['one', 'two', 'three'], (s) => s.length) // { 3: 2, 5: 1 }, null prototype
123
+ map(new Map([['x', 1]]), (value, key) => `${key}=${value}`) // ['x=1']
124
+ size('👍🏽') // 2 code points
125
+ includes(new Set([NaN]), NaN) // true: SameValueZero
126
+ filter({ a: 1, b: 2 }, (value) => value > 1) // [2]
127
+ groupBy([6.1, 4.2, 6.3], Math.floor) // { 4: [4.2], 6: [6.1, 6.3] }, null prototype
128
+ sortBy(users, [(u) => u.name, (u) => u.age]) // stable; null and undefined keys last
129
+ ```
130
+
131
+ One rule per shape: arrays and typed arrays give their indices (holes skipped), strings their code points, a `Map` its keys, a `Set` its members, any other iterable its positions, and any other object its own enumerable string keys; `null`, `undefined` and other primitives are empty. Callbacks receive `(value, key, collection)`. `forEach` visits every entry, as the native method does; `some`, `every` and `find` stop early. Key helpers (`sortBy`, `orderBy`, `minBy`, `maxBy`) order numbers, bigints and strings; `null` and `undefined` keys sort last, and `minBy` and `maxBy` skip them. Comparing a `NaN` key throws `RangeError`, and comparing a key of any other type, or a string against a number, throws `TypeError`. Every callback is a function: lodash's `'name'` and `{ active: true }` shorthands are `(u) => u.name` and `matches({ active: true })`.
132
+
133
+ #### Objects (`atomic-fns/object`)
134
+
135
+ ```ts
136
+ import { cloneDeep, get, matches, merge, omit, pick, set } from 'atomic-fns/object'
137
+
138
+ get({ a: [{ b: 1 }] }, 'a[0].b') // 1
139
+ get({ 'a.b': 1 }, "['a.b']") // 1
140
+ set({}, 'a.b', 1) // { a: { b: 1 } }
141
+ pick({ a: { b: 1, c: 2 } }, 'a.b') // { a: { b: 1 } }
142
+ omit({ user: { name: 'a', password: 'p' } }, 'user.password') // { user: { name: 'a' } }
143
+ merge({ a: [{ b: 2 }] }, { a: [{ c: 3 }] }) // { a: [{ b: 2, c: 3 }] }
144
+ cloneDeep({ when: new Date(0), tags: new Set(['a']) })
145
+ users.filter(matches({ address: { city: 'Oslo' } }))
146
+ ```
147
+
148
+ A `__proto__`, `constructor` or `prototype` path segment that is not own data of a non-function object reads as missing. `set` throws `TypeError` and `unset` returns `false` for these segments unless they are own data of a plain object or array, and for inherited intermediate steps. `set` writes a terminal `constructor` or `prototype` as own data, refusing an existing own accessor or non-writable property. `merge` copies these three keys as own data and refuses to replace an own `constructor` function with an object. `defaults` and `defaultsDeep` fill `constructor` and `prototype` as own data but skip `__proto__` unless the target has a null prototype; `defaultsDeep` never descends into inherited values. `cloneDeep` copies plain objects, arrays, `Map`, `Set`, `Date`, `RegExp`, typed arrays, `DataView`, buffers and boxed primitives with their prototype and symbol keys, and keeps class instances, functions, errors and promises by reference. `cloneDeep` keeps cycles and shared references; `merge` and `defaultsDeep` keep cycles but give each key its own copy of a shared source plain object or array. Deep walks stop past 1000 levels of nesting with a `RangeError`.
149
+
150
+ `omit` copies own enumerable string and symbol keys of objects, and elements and own enumerable symbol keys of arrays; `defaults` and `defaultsDeep` consider own enumerable string and symbol keys of each source.
151
+
152
+ For non-null objects other than arrays and without string, numeric or symbol index signatures, literal tuples of individual top-level keys without `.`, `[` or `]` give `pick` and `omit` exact `Pick` and `Omit` result types; union keys and non-tuple lists of those keys give partial results, and other calls return `Partial<T>`.
153
+
154
+ #### Arrays (`atomic-fns/array`)
155
+
156
+ ```ts
157
+ import { chunk, difference, range, remove, uniqBy, xor, zip } from 'atomic-fns/array'
21
158
 
22
- ## Get Started
159
+ range(1, 10, 3) // [1, 4, 7]
160
+ chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
161
+ zip(['a', 'b'], [1, 2]) // [['a', 1], ['b', 2]]
162
+ difference([2, 1, 2, 3], [2]) // [1, 3]
163
+ uniqBy([2.1, 1.2, 2.3], Math.floor) // [2.1, 1.2]
164
+ xor([2, 1], [2, 3]) // [1, 3]
165
+ remove(xs, (n) => n % 2 === 0) // removes in place, returns the removed
166
+ ```
167
+
168
+ Set operations keep the order of first appearance, drop duplicates and compare with SameValueZero unless a key or equality function is given. Where one native call does the job, use it: `[...new Set(xs)]` for `uniq`, `xs.filter(Boolean)` for `compact`, `xs.filter((x) => x !== y)` for `without`, `xs.flat()` for `flatten`, `xs.toSpliced(i, 0, x)` for `insert`, `Array.from({ length: n }, (_, i) => fn(i))` for `times`, `zip(...rows)` for `unzip`, and `xs.toSorted((a, b) => a - b)` for `sort` and `sorted` on numbers (without a comparator, `toSorted` orders by string).
169
+
170
+ ### Equality, hashing, ids, encoding and type checks
171
+
172
+ #### Deep equality (`atomic-fns/object`)
23
173
 
24
- This library is available as an [npm package](https://www.npmjs.com/package/atomic-fns).
174
+ ```ts
175
+ import { isEqual, isEqualWith } from 'atomic-fns/object'
25
176
 
26
- To install the package you need to have [npm](https://www.npmjs.com/get-npm) (or [yarn](https://yarnpkg.com/getting-started/install)) installed and then run:
177
+ class Point {
178
+ constructor(
179
+ public x: number,
180
+ public y: number
181
+ ) {}
182
+ equals(other: Point): boolean {
183
+ return this.x === other.x && this.y === other.y
184
+ }
185
+ }
27
186
 
28
- ```bash
29
- npm install atomic-fns
187
+ isEqual({ a: [1, { b: NaN }] }, { a: [1, { b: NaN }] }) // true: SameValueZero at the leaves
188
+ isEqual(new Map([[1, 'x']]), new Map([[1, 'x']])) // true
189
+ isEqual(new Set([{}]), new Set([{}])) // false: members are matched by SameValueZero
190
+ isEqual(new Point(1, 2), new Point(1, 2)) // false: class instances compare by identity
191
+ isEqualWith([new Point(1, 2)], [new Point(1, 2)], (x, y) =>
192
+ x instanceof Point && y instanceof Point ? x.equals(y) : undefined
193
+ ) // true
30
194
  ```
31
195
 
32
- ## How to use
196
+ Plain objects, arrays, `arguments`, Dates, RegExps (source, flags and `lastIndex`, with SameValueZero for `lastIndex`), typed arrays, `DataView` and buffers, boxed primitives and errors (name, message, own `cause` and `errors`, and own enumerable properties, not `stack`) compare by content, from any realm. `Map` compares by key membership and then value, `Set` by membership; both match by SameValueZero, so object keys and members match by identity. Other class instances compare by identity. Cycles are compared once, and nesting past 1000 levels throws `RangeError`.
33
197
 
34
- Just import any function or class you need.
198
+ #### `atomic-fns/hash`
199
+
200
+ ```ts
201
+ import { digest, hash, hashString, stableStringify } from 'atomic-fns/hash'
202
+
203
+ hash({ a: 1, b: 2 }) === hash({ b: 2, a: 1 }) // true: isEqual(a, b) implies hash(a) === hash(b)
204
+ hashString('a') // 3826002220, FNV-1a over UTF-8
205
+ stableStringify({ b: 1, a: 2 }) // '{"a":2,"b":1}'
206
+ await digest('SHA-256', 'abc') // 'ba7816bf...'
207
+ ```
35
208
 
36
- ```js
37
- import { sorted, times, uniqueId } from 'atomic-fns'
209
+ `hash` reads at most 8 levels deep, so it never throws on deep, cyclic or huge sparse input (no 1000-level `RangeError`). `stableStringify` calls `toJSON`, as `JSON.stringify` does.
38
210
 
39
- times(5, (i) => i)
40
- // -> [0, 1, 2, 3, 4]
211
+ #### `atomic-fns/id`
41
212
 
42
- sorted([4, 3, 2, 1])
43
- // -> [1, 2, 3, 4]
213
+ ```ts
214
+ import { nanoid, uniqueId, uuid, uuidv7 } from 'atomic-fns/id'
44
215
 
45
- uniqueId('user_')
46
- // -> 'user_101225005'
47
- ...
216
+ `user_${uniqueId()}` // 'user_1'
217
+ uuid() // random v4
218
+ uuidv7() // time-ordered: sorts by creation time to the millisecond, and in creation order within one process
219
+ nanoid() // 21 URL-safe characters
48
220
  ```
49
221
 
50
- See the full list of [modules](https://atomic-stack.github.io/atomic-fns/modules.html).
222
+ `uniqueId()` takes no prefix and returns the next id from a realm-wide counter as a decimal string; like `crypto.randomUUID`, it ignores arguments passed at runtime, so add a prefix with a template string.
51
223
 
52
- ## Data model
53
- This library features a data model similar to [Python](https://docs.python.org/3/reference/datamodel.html#special-method-names) but not always. Only the missing parts have been implemented, while respecting the JS standards.
224
+ #### `atomic-fns/encoding`
54
225
 
55
- For example, in Python collections, `size()` is a method, while in JS it's just a property. So this library uses `size` property instead of method. Similarly, the `len` function implementation checks for both `length` and `size` since many JS objects have a `length` property.
226
+ ```ts
227
+ import { fromBase64, fromHex, toBase64, toHex } from 'atomic-fns/encoding'
56
228
 
57
- ## Special method names
58
- This library adds support for operators using object methods such as:
59
- - `obj.eq(other)` adds custom `obj == other`.
60
- - `obj.lt(other)` adds custom `obj < other`.
61
- - `obj.lte(other)` adds custom `obj <= other`.
62
- - `obj.gt(other)` adds custom `obj > other`.
63
- - `obj.gte(other)` adds custom `obj >= other`.
64
- - `obj.compare(other)` overloads sorting operations.
229
+ const bytes = new Uint8Array([251, 255])
65
230
 
66
- When working with objects, functions in this library use these custom operators for equality and sorting. You can also apply operators to values like `eq(x, y)`, `lte(x, y)`, `compare(x, y)`, etc.
231
+ toBase64('hi') // 'aGk='
232
+ toBase64(bytes, { alphabet: 'base64url', omitPadding: true }) // '-_8'
233
+ fromBase64('aGk=') // Uint8Array [104, 105]
234
+ toHex(new Uint8Array([0, 255])) // '00ff'
235
+ fromHex('00ff') // Uint8Array [0, 255]
236
+ ```
67
237
 
68
- ## New Types
238
+ #### `atomic-fns/is`
69
239
 
70
- [`Decimal`](https://atomic-stack.github.io/atomic-fns/classes/decimal.Decimal.html) provides support for correct rounded floating point arithmetic, with arbitrary precision.
240
+ ```ts
241
+ import { isArrayLike, isError, isNil, isPlainObject } from 'atomic-fns/is'
71
242
 
72
- This fixes precision issues with float arithmetic, for example:
243
+ isPlainObject(Object.create(null)) // true
244
+ isArrayLike('abc') // true
245
+ isError(new TypeError('x')) // true, from any realm
246
+ isNil(undefined) // true
73
247
  ```
74
- 0.1 + 0.1 + 0.1
75
- // 0.30000000000000004
76
248
 
77
- decimal('0.1').add('0.1').add('0.1').toNumber()
78
- // 0.3
249
+ ### Functions, async, strings, numbers and random
250
+
251
+ #### Functions (`atomic-fns/function`)
252
+
253
+ ```ts
254
+ import { debounce, flow, memoize, once, throttle } from 'atomic-fns/function'
255
+
256
+ const save = debounce(persist, 300, { maxWait: 2000 }) // 300 ms after the last call, 2 s after the first at most
257
+ const onScroll = throttle(update, 100) // once per 100 ms at most, on the leading and trailing edge
258
+ save()
259
+ save.flush() // runs the pending call now
260
+ const square = memoize((n: number) => n * n) // square(4) is 16, then read from square.cache
261
+ const init = once(setup) // calls setup the first time, then returns its result
262
+ flow(Math.abs, String)(-3) // '3'
79
263
  ```
80
264
 
81
- Some common base interfaces and typing patterns you may have seen like `Optional`, `Result`, `Comparable`, `Iterator`, and others.
265
+ `debounce` takes `leading`, `trailing` and `maxWait`; `throttle` takes `leading` and `trailing`. Both return a function with `cancel`, `flush` and `pending`. `memoize` keys on the first argument, or on what its `resolver` returns, and never evicts: `cache` is a `Map` you can clear or replace with any store that has the `Map` interface. `curry` and `compose` complete the module.
266
+
267
+ `debounce` and `throttle` measure idle windows with `performance.now()` and hold no timer for them when `wait > 0`; held trailing calls use timers, and `wait === 0` keeps a timer until the next macrotask.
82
268
 
83
- ### Sorted Containers
84
- - [SortedSet](https://atomic-stack.github.io/atomic-fns/classes/collections.SortedSet.html)
85
- - [SortedMap](https://atomic-stack.github.io/atomic-fns/classes/collections.SortedMap.html)
86
- - [SortedTree](https://atomic-stack.github.io/atomic-fns/classes/collections.SortedTree.html)
269
+ #### Async (`atomic-fns/async`)
87
270
 
88
- ### Other Containers
89
- - [Deque](https://atomic-stack.github.io/atomic-fns/classes/collections.Deque.html)
90
- - [FrozenSet](https://atomic-stack.github.io/atomic-fns/classes/collections.FrozenSet.html)
91
- - [LRUCache](https://atomic-stack.github.io/atomic-fns/classes/collections.LRUCache.html)
271
+ ```ts
272
+ import { retry, sleep, timeout } from 'atomic-fns/async'
92
273
 
274
+ await sleep(100, { signal }) // resolves after 100 ms, rejects with signal.reason on abort
275
+ await timeout(job, 5000) // settles as job does, or rejects with a TimeoutError DOMException
276
+ await retry(() => fetch('/api', { signal: AbortSignal.timeout(5000) }), { retries: 5 }) // 6 attempts at most
277
+ ```
93
278
 
94
- ## Contributing
95
- If you want to contribute to the project and make it better, your help is very welcome. Contributing is also a great way to learn more about social coding on Github, new technologies and and their ecosystems and how to make constructive, helpful bug reports, feature requests and the noblest of all contributions: a good, clean pull request. You will be listed as a **Champion** on the official site as well.
279
+ `retry` waits `minDelay` (100 ms) before the first retry and multiplies the delay by `factor` (2) up to `maxDelay` (10 s), with `jitter` on by default. `shouldRetry(error, attempt)` decides which errors earn another attempt, and `signal` stops the loop. `timeout` stops the wait, not the work: give the work its own signal, as `fetch` gets above, so a timed-out attempt is aborted before the next one starts.
280
+
281
+ #### Strings (`atomic-fns/string`)
282
+
283
+ ```ts
284
+ import {
285
+ camelCase,
286
+ capitalize,
287
+ kebabCase,
288
+ slugify,
289
+ startCase,
290
+ truncate,
291
+ words
292
+ } from 'atomic-fns/string'
293
+
294
+ words('fooBar HTTPServer 11th IDs') // ['foo', 'Bar', 'HTTP', 'Server', '11th', 'IDs']
295
+ camelCase('HTTPServer error') // 'httpServerError'
296
+ kebabCase('fooBar baz') // 'foo-bar-baz'
297
+ startCase('--foo-bar--') // 'Foo Bar'
298
+ capitalize('FRED') // 'Fred'
299
+ slugify('Antoine de Saint-Exupéry') // 'antoine-de-saint-exupery'
300
+ truncate('hi-diddly-ho there, neighborino', { length: 24 }) // 'hi-diddly-ho there, n...'
301
+ ```
302
+
303
+ `words` splits on `Intl.Segmenter` with identifier rules, and `camelCase`, `pascalCase`, `snakeCase`, `kebabCase` and `startCase` build on it. `capitalize` title-cases the first code point and lowercases the rest, so separators stay. `deburr` folds Latin accents, `slugify` makes URL slugs, and `truncate` cuts on grapheme boundaries.
304
+
305
+ #### Numbers (`atomic-fns/number`)
306
+
307
+ ```ts
308
+ import { clamp, formatBytes, mean, round } from 'atomic-fns/number'
309
+
310
+ round(1.005, 2) // 1.01
311
+ clamp(15, 0, 10) // 10
312
+ mean([1, 2, 3]) // 2
313
+ formatBytes(1536) // '1.54 kB'
314
+ formatBytes(1536, { binary: true }) // '1.5 KiB'
315
+ ```
316
+
317
+ `round`, `floor` and `ceil` work on the printed decimal, so `1.005` rounds as written. `inRange` and `sum` complete the module.
318
+
319
+ #### Random (`atomic-fns/random`)
320
+
321
+ ```ts
322
+ import { randomInt, sample, sampleSize, shuffle } from 'atomic-fns/random'
323
+
324
+ randomInt(1, 6) // 1 to 6, both included
325
+ sample({ a: 1, b: 2 }) // 1 or 2
326
+ sampleSize([1, 2, 3, 4, 5], 2) // two values at distinct positions, such as [4, 1]
327
+ shuffle(new Set(['a', 'b', 'c'])) // a new array, such as ['c', 'a', 'b']
328
+ randomInt(1, 6, { random: () => 0 }) // 1
329
+ ```
330
+
331
+ `random` and `randomInt` (uniform over any span of safe integers) draw numbers; `sample`, `sampleSize` and `shuffle` draw from every collection shape. Each takes `{ random }` to replace `Math.random`. `Math.random` is not cryptographically secure, so never use these for secrets, tokens or one-time codes: use `atomic-fns/id` or `crypto.getRandomValues`.
332
+
333
+ `sample` and `sampleSize` draw from arrays and typed arrays by reading only selected positions unless an array hole triggers a full scan, but `sampleSize` copies all values for full samples and when `n >= 5 && n >= length / 2`; sparse arrays may consume extra draws, so seeded results can differ from sampling their collected values, and `sampleSize(xs, 0)` reads no values.
334
+
335
+ ## Decimal
336
+
337
+ Exact decimal arithmetic shaped after the TC39 Decimal draft, with Currency.
338
+
339
+ ```ts
340
+ import { Currency, Decimal } from 'atomic-fns/decimal'
341
+ ```
342
+
343
+ No runtime dependencies, no Node APIs: it runs on the platforms listed above.
344
+
345
+ ### Decimal
346
+
347
+ A `Decimal` is an immutable exact decimal number. Methods replace operators, so nothing rounds through a binary `Number`.
348
+
349
+ ```ts
350
+ const a = new Decimal('0.1')
351
+ a.add('0.2').toString() // '0.3'
352
+ a.add('0.2').equals('0.3') // true
353
+ new Decimal('19.99').multiply(3).toString() // '59.97'
354
+ new Decimal('7.5').remainder(2).toString() // '1.5'
355
+ ```
356
+
357
+ The constructor and every operand take a `Decimal`, a decimal string (`'-1.5'`, `'.5'`, `'2e-3'`), a bigint, or a finite number, which enters through its shortest round-trip string (`new Decimal(0.1)` is exactly `0.1`). A malformed string or a non-finite number throws `RangeError`; any other type throws `TypeError`.
358
+
359
+ #### Rounding
360
+
361
+ `divide` and `sqrt` round to `precision` significant digits (default 34, as the TC39 draft does), while `round` and `toFixed` take fractional `digits`. Each takes one of the `Intl.NumberFormat` rounding modes: `ceil`, `floor`, `expand`, `trunc`, `halfCeil`, `halfFloor`, `halfExpand`, `halfTrunc` and `halfEven` (the default, except for `toFixed`, which defaults to `halfExpand`).
362
+
363
+ ```ts
364
+ new Decimal(1).divide(3).toString() // '0.3333333333333333333333333333333333'
365
+ new Decimal('1e-30').divide(3, { precision: 3 }).toString() // '0.000000000000000000000000000000333'
366
+ new Decimal(2).divide(3, { precision: 2, roundingMode: 'trunc' }).toString() // '0.66'
367
+ new Decimal('2.5').round().toString() // '2'
368
+ new Decimal('2.5').round(0, 'halfExpand').toString() // '3'
369
+ new Decimal('1234').round(-2).toString() // '1200'
370
+ new Decimal(2).sqrt({ precision: 5 }).toString() // '1.4142'
371
+ ```
372
+
373
+ Dividing by zero throws `RangeError`, and so does a quotient too small for the exponent bound.
374
+
375
+ #### Comparing
376
+
377
+ `compare` returns -1, 0 or 1, `equals` ignores the written form, and `lessThan`, `lessThanOrEqual`, `greaterThan` and `greaterThanOrEqual` answer the rest. `Decimal.compare` sorts:
378
+
379
+ ```ts
380
+ ;['10', '9', '9.5']
381
+ .map((s) => new Decimal(s))
382
+ .toSorted(Decimal.compare)
383
+ .map(String)
384
+ // ['9', '9.5', '10']
385
+ ```
386
+
387
+ `valueOf` throws, so `<`, `+` and `==` cannot silently compare or add a Decimal as a `Number`. Between two Decimals, `==` and `===` compare identity, so use `equals`. Template literals and `String()` still work. In Node, Deno and Bun, `console.log` shows `Decimal { 1.5 }`, through the runtime's `Symbol.for('nodejs.util.inspect.custom')` hook.
388
+
389
+ #### Converting
390
+
391
+ ```ts
392
+ new Decimal('1.50').toString() // '1.5'
393
+ new Decimal('1e100').toString() // '1e+100'
394
+ new Decimal('1e5').toPlainString() // '100000'
395
+ new Decimal('1.005').toFixed(2) // '1.01'
396
+ new Decimal('1234.5678').toLocaleString('de-DE') // '1.234,568'
397
+ new Decimal('0.1').toNumber() // 0.1
398
+ new Decimal('1.2e3').toBigInt() // 1200n
399
+ JSON.stringify({ price: new Decimal('9.90') }) // '{"price":"9.9"}'
400
+ ```
401
+
402
+ `toString` and `toJSON` switch to scientific form once plain notation would pad more than 64 zeros; `toPlainString` always expands. A short input such as `'1e10000'` becomes 10,001 characters, so prefer `toString` for untrusted values. `toFixed` rounds ties half away from zero, as `Number.prototype.toFixed` does, unless given a rounding mode.
403
+
404
+ #### Bounds
405
+
406
+ A Decimal holds at most `Decimal.MAX_DIGITS` (10,000) significant digits, and the exponent of its integer coefficient lies within `Decimal.MAX_EXPONENT` (10,000) either way: `'1.5e-10000'`, stored as 15e-10001, is out of range. `divide` and `sqrt` take a `precision` up to `Decimal.MAX_DIGITS`, and `round` and `toFixed` take `digits` up to `Decimal.MAX_PRECISION` (10,000). Inputs and results past a bound throw `RangeError`, and so do strings longer than 30,032 characters, so a short untrusted string such as `'1e1000000000'` cannot allocate without limit and parse cost is bounded. Each operation still costs time that grows with its operands' digits, so size-limit untrusted lists before summing them.
407
+
408
+ ### Currency
409
+
410
+ A `Currency` is an exact `amount` (a `Decimal`) and a `currency` code. The code must be three ASCII letters, as ISO 4217 codes are, but the constructor does not check it against the ISO 4217 list.
411
+
412
+ ```ts
413
+ const price = new Currency('19.99', 'usd')
414
+ price.currency // 'USD'
415
+ price.multiply(3).format('en-US') // '$59.97'
416
+ price.add(new Currency('0.01', 'USD')).toString() // '20 USD'
417
+ new Currency(10, 'USD').divide(3).round().toString() // '3.33 USD'
418
+ new Currency('1234.5', 'JPY').round().toString() // '1234 JPY'
419
+ ```
420
+
421
+ `add`, `subtract`, `compare` and the relational methods take another `Currency` and throw `RangeError` when the codes differ; `equals` answers false. `multiply` and `divide` take a plain factor. `round()` rounds to the currency's ISO 4217 minor unit, half to even, not to the digits `Intl` displays, and throws `RangeError` for a code that ISO 4217 gives no minor unit, such as `XAU`, or does not list; `round(digits)` takes any code.
422
+
423
+ `format(locale, options)`, also `toLocaleString`, formats with `Intl.NumberFormat` in currency style. Unless the options set `maximumFractionDigits`, a significant-digit option, `roundingIncrement` or a non-standard `notation`, the amount keeps its own fraction digits, up to 20:
424
+
425
+ ```ts
426
+ new Currency('0.125', 'USD').format('en-US') // '$0.125'
427
+ new Currency('0.125', 'USD').format('en-US', { maximumFractionDigits: 2 }) // '$0.13'
428
+ new Currency(-5, 'USD').format('en-US', { currencySign: 'accounting' }) // '($5.00)'
429
+ new Currency(5, 'USD').format('en-US', { currencyDisplay: 'name' }) // '5.00 US dollars'
430
+ ```
431
+
432
+ `toString` and `toJSON` give `'12.5 USD'`, and in Node, Deno and Bun `console.log` shows `Currency { 12.5 USD }`. Like `Decimal`, `valueOf` throws.
433
+
434
+ ## Development
435
+
436
+ ```sh
437
+ npm install
438
+ npm run check
439
+ ```
96
440
 
441
+ `check` runs Biome, the type check, tests with coverage thresholds (90% lines, statements and functions; 85% branches), the build, dist export checks, packed consumer runtime and type checks, publint, Are the Types Wrong? (`attw`), `deno doc --lint`, and a JSR dry run.
97
442
 
98
443
  ## License
99
444
 
100
- [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)
445
+ [Apache License 2.0](https://github.com/adrian2x/atomic-fns/blob/main/LICENSE)
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Array building blocks (`chunk`, `zip`, `range`, `remove`, `dropWhile`, `takeWhile`) and set
3
+ * operations (`uniqBy`, `uniqWith` and the `difference`, `intersection`, `union` and `xor`
4
+ * families), which keep the order of first appearance and compare with SameValueZero unless a key
5
+ * or equality function is given.
6
+ * @module array
7
+ */
8
+ export { chunk, dropWhile, range, remove, takeWhile, zip } from './list.js';
9
+ export { difference, differenceBy, differenceWith, intersection, intersectionBy, union, unionBy, uniqBy, uniqWith, xor, xorBy } from './sets.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Array building blocks (`chunk`, `zip`, `range`, `remove`, `dropWhile`, `takeWhile`) and set
3
+ * operations (`uniqBy`, `uniqWith` and the `difference`, `intersection`, `union` and `xor`
4
+ * families), which keep the order of first appearance and compare with SameValueZero unless a key
5
+ * or equality function is given.
6
+ * @module array
7
+ */
8
+ export { chunk, dropWhile, range, remove, takeWhile, zip } from './list.js';
9
+ export { difference, differenceBy, differenceWith, intersection, intersectionBy, union, unionBy, uniqBy, uniqWith, xor, xorBy } from './sets.js';
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Array building blocks: `chunk`, `zip`, `range`, `remove`, `dropWhile`, `takeWhile`.
3
+ */
4
+ /**
5
+ * `array` split into consecutive pieces of `size` elements; the last piece holds the rest.
6
+ * @param array The array to split.
7
+ * @param size The length of each piece, a positive integer.
8
+ * @returns A new array of new arrays.
9
+ * @throws {RangeError} when `size` is not a positive integer, or `array` is a huge sparse array.
10
+ * @example
11
+ * ```ts
12
+ * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
13
+ * ```
14
+ */
15
+ export declare function chunk<T>(array: readonly T[], size: number): T[][];
16
+ /**
17
+ * Rows built from the elements at each index of `arrays`: `zip(a, b)[i]` is `[a[i], b[i]]`. The
18
+ * result is as long as the longest input, and shorter inputs give `undefined`, as lodash does, so
19
+ * `zip(...zip(a, b))` restores `a` and `b` padded to one length.
20
+ * @param arrays The arrays to combine.
21
+ * @returns A new array of rows.
22
+ * @throws {RangeError} when an input is a huge sparse array.
23
+ * @example
24
+ * ```ts
25
+ * zip(['a', 'b'], [1, 2]) // [['a', 1], ['b', 2]]
26
+ * zip(...[['a', 1], ['b', 2]]) // [['a', 'b'], [1, 2]]
27
+ * ```
28
+ */
29
+ export declare function zip<A extends readonly (readonly unknown[])[]>(...arrays: A): {
30
+ [K in keyof A]: A[K][number] | undefined;
31
+ }[];
32
+ /**
33
+ * The numbers from `start` up to, not including, `stop`, `step` apart, with Python's bounds:
34
+ * `range(stop)` starts at 0, and a negative `step` counts down. Each element is
35
+ * `start + i * step`, so fractional steps do not drift. Numbers are doubles: past 2^53, or with a
36
+ * `step` smaller than the gap between doubles near the bounds, neighbouring numbers round to the
37
+ * same value. The length cap is the longest array the platform allows, not a memory budget, so
38
+ * bound `stop` yourself when it comes from untrusted input.
39
+ * @param start The first number, or `stop` when it is the only argument.
40
+ * @param stop The bound the numbers stop before.
41
+ * @param step The distance between numbers; defaults to 1.
42
+ * @returns A new array, empty when `start` is already past `stop`.
43
+ * @throws {TypeError} when a bound or `step` is not a number.
44
+ * @throws {RangeError} for a `NaN` or infinite bound, a zero or infinite `step`, or more than
45
+ * 2^32 - 1 numbers.
46
+ * @example
47
+ * ```ts
48
+ * range(4) // [0, 1, 2, 3]
49
+ * range(1, 10, 3) // [1, 4, 7]
50
+ * range(3, 0, -1) // [3, 2, 1]
51
+ * ```
52
+ */
53
+ export declare function range(stop: number): number[];
54
+ /** {@link range} from `start`, with an optional `step`. */
55
+ export declare function range(start: number, stop: number, step?: number): number[];
56
+ /**
57
+ * Remove from `array`, in place, every element `predicate` returns truthy for, and return them,
58
+ * as lodash does. The predicate gets `(value, index, array)` for the elements present, with the
59
+ * original indices, before `array` changes; holes are skipped and keep their place next to the
60
+ * elements around them.
61
+ * @param array The array to change.
62
+ * @param predicate Tests one element.
63
+ * @returns A new array of the removed elements, in order.
64
+ * @example
65
+ * ```ts
66
+ * const xs = [1, 2, 3, 4]
67
+ * remove(xs, (n) => n % 2 === 0) // [2, 4]
68
+ * xs // [1, 3]
69
+ * ```
70
+ */
71
+ export declare function remove<T, S extends T>(array: T[], predicate: (value: T, index: number, array: T[]) => value is S): S[];
72
+ /** {@link remove} with a predicate that is not a type guard. */
73
+ export declare function remove<T>(array: T[], predicate: (value: T, index: number, array: T[]) => unknown): T[];
74
+ /**
75
+ * `array` without its leading elements for which `predicate` is truthy. Holes are skipped, as
76
+ * {@link remove} skips them, and keep their place in the result.
77
+ * @param array The array to read.
78
+ * @param predicate Tests one element present; gets `(value, index, array)`.
79
+ * @returns A new array from the first element that fails.
80
+ * @example
81
+ * ```ts
82
+ * dropWhile([1, 2, 3, 1], (n) => n < 3) // [3, 1]
83
+ * ```
84
+ */
85
+ export declare function dropWhile<T>(array: readonly T[], predicate: (value: T, index: number, array: readonly T[]) => unknown): T[];
86
+ /**
87
+ * The leading elements of `array` for which `predicate` is truthy. Holes are skipped, as
88
+ * {@link remove} skips them, and keep their place in the result.
89
+ * @param array The array to read.
90
+ * @param predicate Tests one element present; gets `(value, index, array)`.
91
+ * @returns A new array up to the first element that fails.
92
+ * @example
93
+ * ```ts
94
+ * takeWhile([1, 2, 3, 1], (n) => n < 3) // [1, 2]
95
+ * ```
96
+ */
97
+ export declare function takeWhile<T, S extends T>(array: readonly T[], predicate: (value: T, index: number, array: readonly T[]) => value is S): S[];
98
+ /** {@link takeWhile} with a predicate that is not a type guard. */
99
+ export declare function takeWhile<T>(array: readonly T[], predicate: (value: T, index: number, array: readonly T[]) => unknown): T[];