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.
- package/README.md +397 -91
- 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 -256
- 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 -623
- package/dist/collections/index.js +14 -844
- 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 -148
- package/dist/decimal/index.js +10 -410
- 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 -53
- 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 -224
- 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 +99 -51
- package/dist/collections/BTree.d.ts +0 -230
- package/dist/collections/BTree.js +0 -1143
- package/dist/collections/Heap.d.ts +0 -69
- package/dist/collections/Heap.js +0 -181
- package/dist/collections/LRUCache.d.ts +0 -83
- package/dist/collections/LRUCache.js +0 -139
- 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 -142
- package/dist/collections/SplayTree.d.ts +0 -107
- package/dist/collections/SplayTree.js +0 -351
- 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 -33
- package/dist/functools/index.d.ts +0 -106
- package/dist/functools/index.js +0 -203
- package/dist/globals/index.d.ts +0 -268
- package/dist/globals/index.js +0 -447
- package/dist/i18n/currency.d.ts +0 -27
- package/dist/i18n/currency.js +0 -100
- package/dist/i18n/date/index.d.ts +0 -210
- package/dist/i18n/date/index.js +0 -552
- package/dist/i18n/date/utils.d.ts +0 -64
- package/dist/i18n/date/utils.js +0 -192
- package/dist/i18n/duration.d.ts +0 -113
- package/dist/i18n/duration.js +0 -398
- package/dist/i18n/format.d.ts +0 -34
- package/dist/i18n/format.js +0 -378
- package/dist/i18n/index.d.ts +0 -9
- package/dist/i18n/index.js +0 -8
- package/dist/itertools/index.d.ts +0 -251
- package/dist/itertools/index.js +0 -434
- package/dist/math/index.d.ts +0 -79
- package/dist/math/index.js +0 -81
- package/dist/mod.d.ts +0 -1
- package/dist/mod.js +0 -1
- package/dist/operators/index.d.ts +0 -148
- package/dist/operators/index.js +0 -325
- package/dist/types.d.ts +0 -19
- package/dist/types.js +0 -1
package/README.md
CHANGED
|
@@ -1,139 +1,445 @@
|
|
|
1
1
|
# atomic-fns
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
5
|
+
Install from npm with `npm install atomic-fns`, or from JSR with `deno add jsr:@atomic-stack/atomic-fns`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Import
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
133
|
+
#### Objects (`atomic-fns/object`)
|
|
25
134
|
|
|
26
|
-
|
|
135
|
+
```ts
|
|
136
|
+
import { cloneDeep, get, matches, merge, omit, pick, set } from 'atomic-fns/object'
|
|
27
137
|
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
// -> [0, 1, 2, 3, 4]
|
|
198
|
+
#### `atomic-fns/hash`
|
|
47
199
|
|
|
48
|
-
|
|
49
|
-
|
|
200
|
+
```ts
|
|
201
|
+
import { digest, hash, hashString, stableStringify } from 'atomic-fns/hash'
|
|
50
202
|
|
|
51
|
-
|
|
52
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
213
|
+
```ts
|
|
214
|
+
import { nanoid, uniqueId, uuid, uuidv7 } from 'atomic-fns/id'
|
|
60
215
|
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
224
|
+
#### `atomic-fns/encoding`
|
|
73
225
|
|
|
74
|
-
|
|
226
|
+
```ts
|
|
227
|
+
import { fromBase64, fromHex, toBase64, toHex } from 'atomic-fns/encoding'
|
|
75
228
|
|
|
76
|
-
|
|
229
|
+
const bytes = new Uint8Array([251, 255])
|
|
77
230
|
|
|
78
|
-
|
|
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
|
-
|
|
84
|
-
|
|
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
|
-
|
|
249
|
+
### Functions, async, strings, numbers and random
|
|
88
250
|
|
|
89
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
269
|
+
#### Async (`atomic-fns/async`)
|
|
101
270
|
|
|
102
|
-
|
|
271
|
+
```ts
|
|
272
|
+
import { retry, sleep, timeout } from 'atomic-fns/async'
|
|
103
273
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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](
|
|
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';
|