atomic-fns 1.2.0 → 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 +397 -91
  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 -256
  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 -623
  27. package/dist/collections/index.js +14 -844
  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 -148
  55. package/dist/decimal/index.js +10 -410
  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 -53
  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 -224
  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 +99 -51
  171. package/dist/collections/BTree.d.ts +0 -230
  172. package/dist/collections/BTree.js +0 -1143
  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 -83
  176. package/dist/collections/LRUCache.js +0 -139
  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 -142
  183. package/dist/collections/SplayTree.d.ts +0 -107
  184. package/dist/collections/SplayTree.js +0 -351
  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 -33
  191. package/dist/functools/index.d.ts +0 -106
  192. package/dist/functools/index.js +0 -203
  193. package/dist/globals/index.d.ts +0 -268
  194. package/dist/globals/index.js +0 -447
  195. package/dist/i18n/currency.d.ts +0 -27
  196. package/dist/i18n/currency.js +0 -100
  197. package/dist/i18n/date/index.d.ts +0 -210
  198. package/dist/i18n/date/index.js +0 -552
  199. package/dist/i18n/date/utils.d.ts +0 -64
  200. package/dist/i18n/date/utils.js +0 -192
  201. package/dist/i18n/duration.d.ts +0 -113
  202. package/dist/i18n/duration.js +0 -398
  203. package/dist/i18n/format.d.ts +0 -34
  204. package/dist/i18n/format.js +0 -378
  205. package/dist/i18n/index.d.ts +0 -9
  206. package/dist/i18n/index.js +0 -8
  207. package/dist/itertools/index.d.ts +0 -251
  208. package/dist/itertools/index.js +0 -434
  209. package/dist/math/index.d.ts +0 -79
  210. package/dist/math/index.js +0 -81
  211. package/dist/mod.d.ts +0 -1
  212. package/dist/mod.js +0 -1
  213. package/dist/operators/index.d.ts +0 -148
  214. package/dist/operators/index.js +0 -325
  215. package/dist/types.d.ts +0 -19
  216. package/dist/types.js +0 -1
package/README.md CHANGED
@@ -1,139 +1,445 @@
1
1
  # atomic-fns
2
2
 
3
- [Read the Docs](https://jsr.io/@atomic-stack/atomic-fns/doc)
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']
10
83
 
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.
84
+ const roles = new FrozenSet(['admin', 'editor'])
85
+ roles.add('guest') // throws TypeError
86
+ ```
87
+
88
+ ### Bisect
89
+
90
+ ```ts
91
+ import { binarySearch, bisectLeft, bisectRight, insort } from 'atomic-fns/collections'
17
92
 
18
- ## Docs
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
+ ```
19
100
 
20
- 📖 Available at [atomic-fns](https://jsr.io/@atomic-stack/atomic-fns/doc)
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
+ ```
21
130
 
22
- ## Get Started
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 })`.
23
132
 
24
- This library is available as an [npm package](https://www.npmjs.com/package/atomic-fns).
133
+ #### Objects (`atomic-fns/object`)
25
134
 
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:
135
+ ```ts
136
+ import { cloneDeep, get, matches, merge, omit, pick, set } from 'atomic-fns/object'
27
137
 
28
- ```bash
29
- npm install atomic-fns
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' } }))
30
146
  ```
31
147
 
32
- It is also on [JSR](https://jsr.io/@atomic-stack/atomic-fns), for Deno and other runtimes that read TypeScript directly:
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.
33
151
 
34
- ```bash
35
- npx jsr add @atomic-stack/atomic-fns
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'
158
+
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
36
166
  ```
37
167
 
38
- ## How to use
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`)
173
+
174
+ ```ts
175
+ import { isEqual, isEqualWith } from 'atomic-fns/object'
39
176
 
40
- Just import any function or class you need.
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
+ }
41
186
 
42
- ```js
43
- import { sorted, times, uniqueId } from '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
194
+ ```
195
+
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`.
44
197
 
45
- [...times(5, (i) => i)]
46
- // -> [0, 1, 2, 3, 4]
198
+ #### `atomic-fns/hash`
47
199
 
48
- sorted([4, 3, 2, 1])
49
- // -> [1, 2, 3, 4]
200
+ ```ts
201
+ import { digest, hash, hashString, stableStringify } from 'atomic-fns/hash'
50
202
 
51
- uniqueId('user_')
52
- // -> 'user_101225005'
53
- ...
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...'
54
207
  ```
55
208
 
56
- See the full list of [modules](https://jsr.io/@atomic-stack/atomic-fns/doc).
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.
210
+
211
+ #### `atomic-fns/id`
57
212
 
58
- ## Data model
59
- 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.
213
+ ```ts
214
+ import { nanoid, uniqueId, uuid, uuidv7 } from 'atomic-fns/id'
60
215
 
61
- 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.
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
220
+ ```
62
221
 
63
- ## Special method names
64
- This library adds support for operators using object methods such as:
65
- - `obj.eq(other)` adds custom `obj == other`.
66
- - `obj.lt(other)` adds custom `obj < other`.
67
- - `obj.lte(other)` adds custom `obj <= other`.
68
- - `obj.gt(other)` adds custom `obj > other`.
69
- - `obj.gte(other)` adds custom `obj >= other`.
70
- - `obj.compare(other)` overloads sorting operations.
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.
71
223
 
72
- 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.
224
+ #### `atomic-fns/encoding`
73
225
 
74
- ## New Types
226
+ ```ts
227
+ import { fromBase64, fromHex, toBase64, toHex } from 'atomic-fns/encoding'
75
228
 
76
- [`Decimal`](https://jsr.io/@atomic-stack/atomic-fns/doc/decimal/~/Decimal) provides support for correct rounded floating point arithmetic, with arbitrary precision.
229
+ const bytes = new Uint8Array([251, 255])
77
230
 
78
- This fixes precision issues with float arithmetic, for example:
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]
79
236
  ```
80
- 0.1 + 0.1 + 0.1
81
- // 0.30000000000000004
82
237
 
83
- decimal('0.1').add('0.1').add('0.1').toNumber()
84
- // 0.3
238
+ #### `atomic-fns/is`
239
+
240
+ ```ts
241
+ import { isArrayLike, isError, isNil, isPlainObject } from 'atomic-fns/is'
242
+
243
+ isPlainObject(Object.create(null)) // true
244
+ isArrayLike('abc') // true
245
+ isError(new TypeError('x')) // true, from any realm
246
+ isNil(undefined) // true
85
247
  ```
86
248
 
87
- Some common base interfaces and typing patterns you may have seen like `Optional`, `Result`, `Comparable`, `Iterator`, and others.
249
+ ### Functions, async, strings, numbers and random
88
250
 
89
- ### Sorted Containers
90
- - [SortedSet](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/SortedSet)
91
- - [SortedMap](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/SortedMap)
92
- - [SortedTree](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/SortedTree)
251
+ #### Functions (`atomic-fns/function`)
93
252
 
94
- ### Other Containers
95
- - [Deque](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/Deque)
96
- - [FrozenSet](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/FrozenSet)
97
- - [LRUCache](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/LRUCache)
253
+ ```ts
254
+ import { debounce, flow, memoize, once, throttle } from 'atomic-fns/function'
98
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'
263
+ ```
264
+
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.
99
268
 
100
- ## Upgrading to 1.2
269
+ #### Async (`atomic-fns/async`)
101
270
 
102
- 1.2 removes the exports that only repeated a native function, along with the deprecated `id` and the experimental `cache` decorator.
271
+ ```ts
272
+ import { retry, sleep, timeout } from 'atomic-fns/async'
103
273
 
104
- | Removed | Use instead |
105
- | ------------------------------- | -------------------------------------- |
106
- | `isArray` | `Array.isArray` |
107
- | `isInteger` | `Number.isInteger` |
108
- | `keys`, `values` | `Object.keys`, `Object.values` |
109
- | `bind` | `Function.prototype.bind` |
110
- | `callAsync` | `new Promise` |
111
- | `floor`, `ceil`, `log2` | `Math.floor`, `Math.ceil`, `Math.log2` |
112
- | `cloneArray`, `cloneTypedArray` | `clone`, `structuredClone`, `slice` |
113
- | `IterableIterator` | `Iterator.from` |
114
- | `id` | `identity` |
115
- | `cache` | `memoize` |
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
+ ```
278
+
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
+ ```
116
302
 
117
- The rest of 1.2 fixes bugs. These fixes change results that code may have worked around:
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.
118
304
 
119
- - `eq`, `lt`, `lte`, `gt` and `gte` now ask an object's own method first and honor a `false` answer, so comparing two `Decimal` values gives the right answer. `eq(0, -0)` is `true`.
120
- - `deepEqual` and `shallowEqual` tell kinds apart, and compare Dates, RegExps and typed arrays by value.
121
- - `merge` copies plain objects from its sources and skips `__proto__`, so it no longer changes a source or `Object.prototype`.
122
- - `difference` removes the values of the other arrays, where it returned a symmetric difference.
123
- - `uniq` with an iteratee returns the original elements, not the computed keys, and `sortedUniq` sorts them by that key.
124
- - `memoize` caches an `undefined` result.
125
- - `divmod` follows Python, so the quotient and remainder add back up to the input.
126
- - `bisect` and `insort` find the right position in every case.
127
- - `Deque.set` throws `KeyError` past the end, where the value was lost.
128
- - A frozen `BTree` refuses `remove`, as it does `set`.
129
- - `IntlDate` adds months with a clamp to the last day of the month, keeps the wall clock across DST for calendar units, and `diff` counts calendar months, quarters and years. `exact` keeps the averaged result.
130
- - `addDuration` and `subDuration` read a date-only string as local time, as `IntlDate` does.
131
- - `format('Z')` prints the numeric offset, such as `-05:00`.
305
+ #### Numbers (`atomic-fns/number`)
132
306
 
133
- ## Contributing
134
- 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.
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
+ ```
135
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.
136
442
 
137
443
  ## License
138
444
 
139
- [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';