@evolu/common 7.4.0 → 8.0.0-next.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 (251) hide show
  1. package/README.md +4 -61
  2. package/dist/src/Array.d.ts +343 -102
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +181 -85
  5. package/dist/src/Assert.d.ts +57 -11
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +47 -11
  8. package/dist/src/BigInt.d.ts +6 -1
  9. package/dist/src/BigInt.d.ts.map +1 -1
  10. package/dist/src/BigInt.js +5 -0
  11. package/dist/src/Brand.d.ts +40 -12
  12. package/dist/src/Brand.d.ts.map +1 -1
  13. package/dist/src/Brand.js +5 -0
  14. package/dist/src/Buffer.d.ts +6 -1
  15. package/dist/src/Buffer.d.ts.map +1 -1
  16. package/dist/src/Buffer.js +7 -2
  17. package/dist/src/Cache.d.ts +10 -2
  18. package/dist/src/Cache.d.ts.map +1 -1
  19. package/dist/src/Cache.js +6 -0
  20. package/dist/src/Callbacks.d.ts +10 -7
  21. package/dist/src/Callbacks.d.ts.map +1 -1
  22. package/dist/src/Callbacks.js +23 -13
  23. package/dist/src/Console.d.ts +361 -69
  24. package/dist/src/Console.d.ts.map +1 -1
  25. package/dist/src/Console.js +217 -117
  26. package/dist/src/Crypto.d.ts +70 -21
  27. package/dist/src/Crypto.d.ts.map +1 -1
  28. package/dist/src/Crypto.js +72 -27
  29. package/dist/src/Eq.d.ts +22 -2
  30. package/dist/src/Eq.d.ts.map +1 -1
  31. package/dist/src/Eq.js +21 -1
  32. package/dist/src/Error.d.ts +23 -12
  33. package/dist/src/Error.d.ts.map +1 -1
  34. package/dist/src/Error.js +27 -11
  35. package/dist/src/Function.d.ts +128 -56
  36. package/dist/src/Function.d.ts.map +1 -1
  37. package/dist/src/Function.js +118 -14
  38. package/dist/src/Identicon.d.ts +5 -0
  39. package/dist/src/Identicon.d.ts.map +1 -1
  40. package/dist/src/Identicon.js +6 -1
  41. package/dist/src/Lookup.d.ts +160 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +192 -0
  44. package/dist/src/Microtask.d.ts +21 -0
  45. package/dist/src/Microtask.d.ts.map +1 -0
  46. package/dist/src/Microtask.js +37 -0
  47. package/dist/src/Number.d.ts +18 -4
  48. package/dist/src/Number.d.ts.map +1 -1
  49. package/dist/src/Number.js +23 -1
  50. package/dist/src/Object.d.ts +110 -5
  51. package/dist/src/Object.d.ts.map +1 -1
  52. package/dist/src/Object.js +108 -5
  53. package/dist/src/Option.d.ts +58 -0
  54. package/dist/src/Option.d.ts.map +1 -0
  55. package/dist/src/Option.js +43 -0
  56. package/dist/src/Order.d.ts +5 -0
  57. package/dist/src/Order.d.ts.map +1 -1
  58. package/dist/src/Order.js +8 -3
  59. package/dist/src/Platform.d.ts +36 -2
  60. package/dist/src/Platform.d.ts.map +1 -1
  61. package/dist/src/Platform.js +10 -6
  62. package/dist/src/Polyfills.d.ts +27 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +299 -0
  65. package/dist/src/Random.d.ts +18 -7
  66. package/dist/src/Random.d.ts.map +1 -1
  67. package/dist/src/Random.js +9 -7
  68. package/dist/src/Redacted.d.ts +7 -4
  69. package/dist/src/Redacted.d.ts.map +1 -1
  70. package/dist/src/Redacted.js +5 -0
  71. package/dist/src/Ref.d.ts +31 -16
  72. package/dist/src/Ref.d.ts.map +1 -1
  73. package/dist/src/Ref.js +35 -7
  74. package/dist/src/RefCount.d.ts +62 -0
  75. package/dist/src/RefCount.d.ts.map +1 -0
  76. package/dist/src/RefCount.js +83 -0
  77. package/dist/src/Relation.d.ts +84 -29
  78. package/dist/src/Relation.d.ts.map +1 -1
  79. package/dist/src/Relation.js +83 -105
  80. package/dist/src/Resource.d.ts +263 -0
  81. package/dist/src/Resource.d.ts.map +1 -0
  82. package/dist/src/Resource.js +389 -0
  83. package/dist/src/Result.d.ts +390 -374
  84. package/dist/src/Result.d.ts.map +1 -1
  85. package/dist/src/Result.js +142 -70
  86. package/dist/src/Schedule.d.ts +953 -0
  87. package/dist/src/Schedule.d.ts.map +1 -0
  88. package/dist/src/Schedule.js +1199 -0
  89. package/dist/src/Set.d.ts +181 -0
  90. package/dist/src/Set.d.ts.map +1 -0
  91. package/dist/src/Set.js +137 -0
  92. package/dist/src/Skiplist.d.ts +6 -1
  93. package/dist/src/Skiplist.d.ts.map +1 -1
  94. package/dist/src/Skiplist.js +5 -0
  95. package/dist/src/Sqlite.d.ts +160 -60
  96. package/dist/src/Sqlite.d.ts.map +1 -1
  97. package/dist/src/Sqlite.js +290 -190
  98. package/dist/src/Store.d.ts +36 -28
  99. package/dist/src/Store.d.ts.map +1 -1
  100. package/dist/src/Store.js +55 -15
  101. package/dist/src/String.d.ts +5 -0
  102. package/dist/src/String.d.ts.map +1 -1
  103. package/dist/src/String.js +5 -0
  104. package/dist/src/Task.d.ts +2263 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1488 -348
  107. package/dist/src/Test.d.ts +122 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +66 -0
  110. package/dist/src/Time.d.ts +201 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +190 -86
  113. package/dist/src/Tracer.d.ts +48 -0
  114. package/dist/src/Tracer.d.ts.map +1 -0
  115. package/dist/src/Tracer.js +6 -0
  116. package/dist/src/Type.d.ts +582 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +362 -240
  119. package/dist/src/Types.d.ts +160 -6
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/Types.js +22 -2
  122. package/dist/src/WebSocket.d.ts +113 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +210 -118
  125. package/dist/src/Worker.d.ts +245 -60
  126. package/dist/src/Worker.d.ts.map +1 -1
  127. package/dist/src/Worker.js +220 -64
  128. package/dist/src/index.d.ts +25 -3
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +19 -3
  131. package/dist/src/local-first/Db.d.ts +24 -213
  132. package/dist/src/local-first/Db.d.ts.map +1 -1
  133. package/dist/src/local-first/Db.js +622 -293
  134. package/dist/src/local-first/Error.d.ts +12 -0
  135. package/dist/src/local-first/Error.d.ts.map +1 -0
  136. package/dist/src/local-first/Error.js +6 -0
  137. package/dist/src/local-first/Evolu.d.ts +330 -285
  138. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  139. package/dist/src/local-first/Evolu.js +472 -402
  140. package/dist/src/local-first/LocalAuth.d.ts +10 -10
  141. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  142. package/dist/src/local-first/LocalAuth.js +6 -1
  143. package/dist/src/local-first/Owner.d.ts +70 -27
  144. package/dist/src/local-first/Owner.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.js +27 -14
  146. package/dist/src/local-first/Protocol.d.ts +58 -50
  147. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  148. package/dist/src/local-first/Protocol.js +253 -155
  149. package/dist/src/local-first/Query.d.ts +133 -43
  150. package/dist/src/local-first/Query.d.ts.map +1 -1
  151. package/dist/src/local-first/Query.js +139 -90
  152. package/dist/src/local-first/Relay.d.ts +18 -38
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -171
  155. package/dist/src/local-first/Schema.d.ts +139 -144
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +78 -163
  158. package/dist/src/local-first/Shared.d.ts +125 -0
  159. package/dist/src/local-first/Shared.d.ts.map +1 -0
  160. package/dist/src/local-first/Shared.js +569 -0
  161. package/dist/src/local-first/Storage.d.ts +50 -53
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +121 -173
  164. package/dist/src/local-first/Timestamp.d.ts +28 -35
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +24 -25
  167. package/dist/src/local-first/index.d.ts +2 -15
  168. package/dist/src/local-first/index.d.ts.map +1 -1
  169. package/dist/src/local-first/index.js +2 -15
  170. package/package.json +36 -26
  171. package/src/Array.ts +467 -106
  172. package/src/Assert.ts +87 -12
  173. package/src/BigInt.ts +7 -1
  174. package/src/Brand.ts +41 -12
  175. package/src/Buffer.ts +10 -4
  176. package/src/Cache.ts +11 -2
  177. package/src/Callbacks.ts +33 -23
  178. package/src/Console.ts +567 -178
  179. package/src/Crypto.ts +111 -62
  180. package/src/Eq.ts +30 -3
  181. package/src/Error.ts +50 -34
  182. package/src/Function.ts +140 -68
  183. package/src/Identicon.ts +7 -1
  184. package/src/Lookup.ts +415 -0
  185. package/src/Microtask.ts +58 -0
  186. package/src/Number.ts +40 -7
  187. package/src/Object.ts +155 -16
  188. package/src/Option.ts +74 -0
  189. package/src/Order.ts +11 -3
  190. package/src/Platform.ts +43 -7
  191. package/src/Polyfills.ts +465 -0
  192. package/src/Random.ts +25 -11
  193. package/src/Redacted.ts +8 -4
  194. package/src/Ref.ts +72 -21
  195. package/src/RefCount.ts +170 -0
  196. package/src/Relation.ts +188 -136
  197. package/src/Resource.ts +864 -0
  198. package/src/Result.ts +548 -381
  199. package/src/Schedule.ts +1471 -0
  200. package/src/Set.ts +247 -0
  201. package/src/Skiplist.ts +7 -1
  202. package/src/Sqlite.ts +409 -304
  203. package/src/Store.ts +94 -48
  204. package/src/String.ts +6 -0
  205. package/src/Task.ts +3685 -740
  206. package/src/Test.ts +162 -0
  207. package/src/Time.ts +348 -145
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +1064 -630
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +311 -178
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +871 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -823
  217. package/src/local-first/LocalAuth.ts +10 -10
  218. package/src/local-first/Owner.ts +94 -42
  219. package/src/local-first/Protocol.ts +440 -303
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +91 -252
  222. package/src/local-first/Schema.ts +284 -394
  223. package/src/local-first/Shared.ts +932 -0
  224. package/src/local-first/Storage.ts +209 -282
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/LICENSE +0 -21
  228. package/dist/src/Instances.d.ts +0 -34
  229. package/dist/src/Instances.d.ts.map +0 -1
  230. package/dist/src/Instances.js +0 -44
  231. package/dist/src/Resources.d.ts +0 -118
  232. package/dist/src/Resources.d.ts.map +0 -1
  233. package/dist/src/Resources.js +0 -197
  234. package/dist/src/local-first/Platform.d.ts +0 -25
  235. package/dist/src/local-first/Platform.d.ts.map +0 -1
  236. package/dist/src/local-first/Platform.js +0 -1
  237. package/dist/src/local-first/Public.d.ts +0 -22
  238. package/dist/src/local-first/Public.d.ts.map +0 -1
  239. package/dist/src/local-first/Public.js +0 -15
  240. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  241. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  242. package/dist/src/local-first/PublicKysely.js +0 -184
  243. package/dist/src/local-first/Sync.d.ts +0 -112
  244. package/dist/src/local-first/Sync.d.ts.map +0 -1
  245. package/dist/src/local-first/Sync.js +0 -529
  246. package/src/Instances.ts +0 -90
  247. package/src/Resources.ts +0 -367
  248. package/src/local-first/Platform.ts +0 -27
  249. package/src/local-first/Public.ts +0 -42
  250. package/src/local-first/PublicKysely.ts +0 -239
  251. package/src/local-first/Sync.ts +0 -960
package/src/Array.ts CHANGED
@@ -1,8 +1,67 @@
1
1
  /**
2
- * Array types, type guards, operations, transformations, accessors, and (rare)
3
- * mutations
2
+ * Array helpers that help TypeScript help you.
4
3
  *
5
- * ### Example
4
+ * - **Non-empty arrays**: compile-time guarantee of at least one element
5
+ * - **Readonly arrays**: prevents accidental mutation
6
+ *
7
+ * Instead of checking array length at runtime, use {@link NonEmptyReadonlyArray}
8
+ * so TypeScript rejects empty arrays at compile time. Functions like
9
+ * {@link firstInArray} require a non-empty array — TypeScript won't let us pass
10
+ * an empty one. {@link mapArray} preserves non-emptiness (native `map` doesn't),
11
+ * while {@link appendToArray} and {@link prependToArray} guarantee the result is
12
+ * non-empty.
13
+ *
14
+ * All helpers return readonly arrays for safety. Consider how dangerous native
15
+ * `sort()` is — it mutates the original array and returns it, making bugs hard
16
+ * to track:
17
+ *
18
+ * ```ts
19
+ * const sortScores = (arr: number[]) => arr.sort((a, b) => a - b);
20
+ *
21
+ * const scores = [3, 1, 2];
22
+ * const leaderboard = sortScores(scores);
23
+ * leaderboard; // [1, 2, 3]
24
+ * scores; // [1, 2, 3] — original order lost!
25
+ * ```
26
+ *
27
+ * Imagine every method doing that.
28
+ *
29
+ * On a `ReadonlyArray`, `.sort()` doesn't even exist. Use {@link sortArray}
30
+ * instead:
31
+ *
32
+ * ```ts
33
+ * const sortScores = (arr: ReadonlyArray<number>) =>
34
+ * sortArray(arr, (a, b) => a - b);
35
+ *
36
+ * const scores: ReadonlyArray<number> = [3, 1, 2];
37
+ * const leaderboard = sortScores(scores);
38
+ * leaderboard; // [1, 2, 3]
39
+ * scores; // [3, 1, 2] — safe!
40
+ * ```
41
+ *
42
+ * Even better, require a {@link NonEmptyReadonlyArray} — there's nothing to sort
43
+ * if the array is empty anyway:
44
+ *
45
+ * ```ts
46
+ * const sortScores = (arr: NonEmptyReadonlyArray<number>) =>
47
+ * sortArray(arr, (a, b) => a - b);
48
+ * ```
49
+ *
50
+ * Sorting an empty array isn't expensive, but functions can have side effects
51
+ * like database queries or network requests. Using non-empty arrays whenever
52
+ * possible is a good convention.
53
+ *
54
+ * For performance-critical cases where mutation is needed, Evolu provides
55
+ * {@link shiftFromArray} and {@link popFromArray} — but only because they improve
56
+ * type safety by returning a guaranteed `T` rather than an optional value.
57
+ *
58
+ * ### When to use native methods
59
+ *
60
+ * These helpers only exist where they add type-level value. Native methods like
61
+ * `find`, `some`, `every`, `includes`, `indexOf`, and `findIndex` work well on
62
+ * readonly arrays without mutation — use them directly.
63
+ *
64
+ * ## Examples
6
65
  *
7
66
  * ```ts
8
67
  * // Types - compile-time guarantee of at least one element
@@ -12,15 +71,13 @@
12
71
  *
13
72
  * // Type guards
14
73
  * const arr: ReadonlyArray<number> = [1, 2, 3];
15
- * if (isNonEmptyReadonlyArray(arr)) {
74
+ * if (isNonEmptyArray(arr)) {
16
75
  * firstInArray(arr);
17
76
  * }
18
77
  *
19
- * // Operations
78
+ * // Transformations
20
79
  * const appended = appendToArray([1, 2, 3], 4); // [1, 2, 3, 4]
21
80
  * const prepended = prependToArray([2, 3], 1); // [1, 2, 3]
22
- *
23
- * // Transformations
24
81
  * const readonly: ReadonlyArray<number> = [1, 2, 3];
25
82
  * const mapped = mapArray(readonly, (x) => x * 2); // [2, 4, 6]
26
83
  * const filtered = filterArray(readonly, (x) => x > 1); // [2, 3]
@@ -28,7 +85,7 @@
28
85
  * const [evens, odds] = partitionArray(
29
86
  * [1, 2, 3, 4, 5],
30
87
  * (x) => x % 2 === 0,
31
- * ); // [[2, 4], [1, 3, 5]]
88
+ * );
32
89
  *
33
90
  * // Accessors
34
91
  * const first = firstInArray(["a", "b", "c"]); // "a"
@@ -36,121 +93,186 @@
36
93
  *
37
94
  * // Mutations
38
95
  * const mutable: NonEmptyArray<number> = [1, 2, 3];
39
- * shiftArray(mutable); // 1 (guaranteed to exist)
96
+ * shiftFromArray(mutable); // 1 (guaranteed to exist)
40
97
  * mutable; // [2, 3]
41
98
  * ```
42
99
  *
43
- * Functions are intentionally data-first to be prepared for the upcoming
44
- * JavaScript pipe operator.
100
+ * ## Composition
101
+ *
102
+ * All array helpers use a data-first style (the array is the first argument)
103
+ * because it's natural for single operations:
45
104
  *
46
105
  * ```ts
47
- * // Data-first is natural for single operations.
48
106
  * const timestamps = mapArray(messages, (m) => m.timestamp);
107
+ * ```
49
108
  *
50
- * // But data-first can be hard to read for nested calls.
51
- * const result = firstInArray(
52
- * mapArray(dedupeArray(appendToArray(value, 2)), (x) => x * 2),
53
- * );
109
+ * Data-first style also reads well for a few operations, often fitting on a
110
+ * line:
54
111
  *
55
- * // With the upcoming pipe operator, it's clear.
56
- * // const result = value
57
- * // |> appendToArray(%, 2)
58
- * // |> dedupeArray(%)
59
- * // |> mapArray(%, (x) => x * 2)
60
- * // |> firstInArray(%);
61
- *
62
- * // Until the pipe operator lands, use nested calls or name each step:
63
- * const appended = appendToArray(value, 2);
64
- * const deduped = dedupeArray(appended);
65
- * const mapped = mapArray(deduped, (x) => x * 2);
66
- * const result = firstInArray(mapped);
112
+ * ```ts
113
+ * const cheapest = firstInArray(sortArray(prices, orderNumber));
114
+ * const uniqueNames = dedupeArray(mapArray(users, (u) => u.name));
115
+ * const latestDone = lastInArray(filterArray(jobs, isCompletedJob));
67
116
  * ```
68
117
  *
69
- * ### Why data-first?
118
+ * For more operations, create a function like `getOldestActiveUser` or a
119
+ * generic helper.
70
120
  *
71
- * Evolu optimizes for consistent code style. We can't have both data-first
72
- * single operations and curried data-last helpers without sacrificing
73
- * consistency. We chose data-first because:
121
+ * Some libraries provide dual APIs with data-last for pipe-based composition.
122
+ * Evolu prefers simplicity (in Latin, simplex means "one") so we don't have to
123
+ * choose between seemingly equivalent options (Buridan's ass dilemma).
74
124
  *
75
- * - It's natural for single operations (for example `mapArray(messages, (m) =>
76
- * m.timestamp)`).
77
- * - It aligns with the upcoming JavaScript pipe operator.
78
- *
79
- * **Note**: Feel free to use Array instance methods (mutation) if you think
80
- * it's better (performance, local scope, etc.).
125
+ * Evolu doesn't provide `pipe` because few operations compose well without it,
126
+ * and for more operations, well-named functions communicate intent better.
81
127
  *
82
128
  * @module
83
129
  */
84
130
 
85
- import { PredicateWithIndex, RefinementWithIndex } from "./Types.js";
131
+ import { identity } from "./Function.js";
132
+ import type { PredicateWithIndex, RefinementWithIndex } from "./Types.js";
86
133
 
87
134
  /**
88
135
  * An array with at least one element.
89
136
  *
90
- * @category Types
137
+ * @group Types
91
138
  */
92
139
  export type NonEmptyArray<T> = [T, ...Array<T>];
93
140
 
94
141
  /**
95
142
  * A readonly array with at least one element.
96
143
  *
97
- * @category Types
144
+ * @group Types
98
145
  */
99
146
  export type NonEmptyReadonlyArray<T> = readonly [T, ...ReadonlyArray<T>];
100
147
 
101
148
  /**
102
- * Checks if an array is non-empty and narrows its type to {@link NonEmptyArray}.
149
+ * An empty readonly array.
150
+ *
151
+ * Use as a default or initial value to avoid allocating new empty arrays.
152
+ *
153
+ * @group Constants
154
+ */
155
+ export const emptyArray: ReadonlyArray<never> = [];
156
+
157
+ /**
158
+ * Better `Array.from`.
103
159
  *
104
- * Use `if (!isNonEmptyArray(arr))` for empty checks.
160
+ * - Returns readonly arrays
161
+ * - Accepts length directly: `arrayFrom(3, fn)` instead of `Array.from({ length:
162
+ * 3 }, fn)`
163
+ * - Skips copying if iterable is already an array (safe because readonly)
105
164
  *
106
165
  * ### Example
107
166
  *
108
167
  * ```ts
109
- * const arr: Array<number> = [1, 2, 3];
110
- * if (isNonEmptyArray(arr)) {
111
- * firstInArray(arr); // arr is NonEmptyArray<number>
112
- * }
168
+ * arrayFrom(new Set([1, 2, 3])); // ReadonlyArray<number>
169
+ * arrayFrom(3, (i) => i * 10); // [0, 10, 20]
170
+ * arrayFrom(iterableMaybeArray); // no unnecessary copy
113
171
  * ```
114
172
  *
115
- * @category Type Guards
173
+ * Unlike `Array.from`, there's no map parameter for iterables — use
174
+ * {@link mapArray} instead, or
175
+ * {@link https://web.dev/blog/baseline-iterator-helpers | iterator helpers}
176
+ * directly on iterables.
177
+ *
178
+ * @group Constructors
116
179
  */
117
- export const isNonEmptyArray = <T>(
118
- array: Array<T>,
119
- ): array is NonEmptyArray<T> => array.length > 0;
180
+ export function arrayFrom<T>(iterable: Iterable<T>): ReadonlyArray<T>;
181
+ /** From length and map function. */
182
+ export function arrayFrom<T>(
183
+ length: number,
184
+ map: (index: number) => T,
185
+ ): ReadonlyArray<T>;
186
+ export function arrayFrom<T>(
187
+ iterableOrLength: Iterable<T> | number,
188
+ map?: (index: number) => T,
189
+ ): ReadonlyArray<T> {
190
+ if (typeof iterableOrLength === "number") {
191
+ return Array.from({ length: iterableOrLength }, (_, i) =>
192
+ (map as (index: number) => T)(i),
193
+ );
194
+ }
195
+ return Array.isArray(iterableOrLength)
196
+ ? (iterableOrLength as ReadonlyArray<T>)
197
+ : [...iterableOrLength];
198
+ }
120
199
 
121
200
  /**
122
- * Checks if a readonly array is non-empty and narrows its type to
123
- * {@link NonEmptyReadonlyArray}.
201
+ * Better `Array.fromAsync`.
124
202
  *
125
- * Use `if (!isNonEmptyReadonlyArray(arr))` for empty checks.
203
+ * Returns a readonly array and awaits promised items from sync or async
204
+ * iterables.
126
205
  *
127
206
  * ### Example
128
207
  *
129
208
  * ```ts
130
- * const arr: ReadonlyArray<number> = [1, 2, 3];
131
- * if (isNonEmptyReadonlyArray(arr)) {
132
- * firstInArray(arr); // arr is NonEmptyReadonlyArray<number>
209
+ * await arrayFromAsync(new Set([1, 2, 3])); // ReadonlyArray<number>
210
+ * await arrayFromAsync(
211
+ * (async function* () {
212
+ * yield Promise.resolve(1);
213
+ * yield Promise.resolve(2);
214
+ * })(),
215
+ * ); // [1, 2]
216
+ * ```
217
+ *
218
+ * Unlike `Array.fromAsync`, there's no map parameter — map the result with
219
+ * {@link mapArray} or use
220
+ * {@link https://web.dev/blog/baseline-iterator-helpers | iterator helpers}
221
+ * directly on iterables.
222
+ *
223
+ * @group Constructors
224
+ */
225
+ export const arrayFromAsync = async <T>(
226
+ iterable: AsyncIterable<T> | Iterable<T | PromiseLike<T>>,
227
+ ): Promise<ReadonlyArray<T>> => Array.fromAsync(iterable);
228
+
229
+ /**
230
+ * Checks if an array is non-empty and narrows its type to {@link NonEmptyArray}
231
+ * or {@link NonEmptyReadonlyArray} based on the input.
232
+ *
233
+ * To check if an array is empty, use `if (!isNonEmptyArray(arr))` — using the
234
+ * negated guard is better than `.length === 0` for early returns because
235
+ * TypeScript narrows the type after the check.
236
+ *
237
+ * ### Example
238
+ *
239
+ * ```ts
240
+ * // Mutable array narrows to NonEmptyArray
241
+ * const arr: Array<number> = [1, 2, 3];
242
+ * if (isNonEmptyArray(arr)) {
243
+ * shiftFromArray(arr); // arr is NonEmptyArray<number>
244
+ * }
245
+ *
246
+ * // Readonly array narrows to NonEmptyReadonlyArray
247
+ * const readonly: ReadonlyArray<number> = [1, 2, 3];
248
+ * if (isNonEmptyArray(readonly)) {
249
+ * firstInArray(readonly); // readonly is NonEmptyReadonlyArray<number>
133
250
  * }
134
251
  * ```
135
252
  *
136
- * @category Type Guards
253
+ * @group Types
137
254
  */
138
- export const isNonEmptyReadonlyArray = <T>(
255
+ export function isNonEmptyArray<T>(array: Array<T>): array is NonEmptyArray<T>;
256
+ /** Readonly array overload. */
257
+ export function isNonEmptyArray<T>(
258
+ array: ReadonlyArray<T>,
259
+ ): array is NonEmptyReadonlyArray<T>;
260
+ export function isNonEmptyArray<T>(
139
261
  array: ReadonlyArray<T>,
140
- ): array is NonEmptyReadonlyArray<T> => array.length > 0;
262
+ ): array is NonEmptyReadonlyArray<T> {
263
+ return array.length > 0;
264
+ }
141
265
 
142
266
  /**
143
267
  * Appends an item to an array, returning a new non-empty readonly array.
144
268
  *
145
- * Accepts both mutable and readonly arrays. Does not mutate the original array.
146
- *
147
269
  * ### Example
148
270
  *
149
271
  * ```ts
150
272
  * appendToArray([1, 2, 3], 4); // [1, 2, 3, 4]
151
273
  * ```
152
274
  *
153
- * @category Operations
275
+ * @group Transformations
154
276
  */
155
277
  export const appendToArray = <T>(
156
278
  array: ReadonlyArray<T>,
@@ -161,15 +283,13 @@ export const appendToArray = <T>(
161
283
  /**
162
284
  * Prepends an item to an array, returning a new non-empty readonly array.
163
285
  *
164
- * Accepts both mutable and readonly arrays. Does not mutate the original array.
165
- *
166
286
  * ### Example
167
287
  *
168
288
  * ```ts
169
289
  * prependToArray([2, 3], 1); // [1, 2, 3]
170
290
  * ```
171
291
  *
172
- * @category Operations
292
+ * @group Transformations
173
293
  */
174
294
  export const prependToArray = <T>(
175
295
  array: ReadonlyArray<T>,
@@ -177,9 +297,9 @@ export const prependToArray = <T>(
177
297
  ): NonEmptyReadonlyArray<T> => [item, ...array] as NonEmptyReadonlyArray<T>;
178
298
 
179
299
  /**
180
- * Maps an array using a mapper function.
300
+ * Maps an array using a mapper function, returning a new readonly array.
181
301
  *
182
- * Accepts both mutable and readonly arrays. Preserves non-empty type.
302
+ * Preserves non-empty type.
183
303
  *
184
304
  * ### Example
185
305
  *
@@ -187,12 +307,13 @@ export const prependToArray = <T>(
187
307
  * mapArray([1, 2, 3], (x) => x * 2); // [2, 4, 6]
188
308
  * ```
189
309
  *
190
- * @category Transformations
310
+ * @group Transformations
191
311
  */
192
312
  export function mapArray<T, U>(
193
313
  array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
194
314
  mapper: (item: T, index: number) => U,
195
315
  ): NonEmptyReadonlyArray<U>;
316
+ /** Possibly empty array. */
196
317
  export function mapArray<T, U>(
197
318
  array: ReadonlyArray<T> | Array<T>,
198
319
  mapper: (item: T, index: number) => U,
@@ -201,27 +322,134 @@ export function mapArray<T, U>(
201
322
  array: ReadonlyArray<T> | Array<T>,
202
323
  mapper: (item: T, index: number) => U,
203
324
  ): ReadonlyArray<U> {
204
- return array.map(mapper) as ReadonlyArray<U>;
325
+ // For loop is faster than array.map.
326
+ const length = array.length;
327
+ const result = new Array<U>(length);
328
+ for (let i = 0; i < length; i++) result[i] = mapper(array[i], i);
329
+ return result as ReadonlyArray<U>;
330
+ }
331
+
332
+ /**
333
+ * Maps each element to an array and flattens the result.
334
+ *
335
+ * Preserves non-empty type when the input is non-empty and the mapper returns
336
+ * non-empty arrays. When called without a mapper, flattens nested arrays using
337
+ * {@link identity}.
338
+ *
339
+ * ### Example
340
+ *
341
+ * ```ts
342
+ * flatMapArray([
343
+ * [1, 2],
344
+ * [3, 4],
345
+ * ]); // [1, 2, 3, 4]
346
+ *
347
+ * flatMapArray([1, 2, 3], (x) => [x, x * 10]); // [1, 10, 2, 20, 3, 30]
348
+ * ```
349
+ *
350
+ * ### Filter and map in one pass
351
+ *
352
+ * Return `[]` to filter out, `[value]` to keep:
353
+ *
354
+ * ```ts
355
+ * const errors = flatMapArray(fields, (f) => {
356
+ * const result = validate(f);
357
+ * return result.ok ? [] : [result.error];
358
+ * });
359
+ * ```
360
+ *
361
+ * @group Transformations
362
+ */
363
+ export function flatMapArray<T>(
364
+ array:
365
+ | NonEmptyReadonlyArray<NonEmptyReadonlyArray<T> | NonEmptyArray<T>>
366
+ | NonEmptyArray<NonEmptyReadonlyArray<T> | NonEmptyArray<T>>,
367
+ ): NonEmptyReadonlyArray<T>;
368
+ /** Possibly empty nested arrays. */
369
+ export function flatMapArray<T>(
370
+ array:
371
+ | ReadonlyArray<ReadonlyArray<T> | Array<T>>
372
+ | Array<ReadonlyArray<T> | Array<T>>,
373
+ ): ReadonlyArray<T>;
374
+ /** Non-empty with mapper returning non-empty. */
375
+ export function flatMapArray<T, U>(
376
+ array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
377
+ mapper: (
378
+ item: T,
379
+ index: number,
380
+ ) => NonEmptyReadonlyArray<U> | NonEmptyArray<U>,
381
+ ): NonEmptyReadonlyArray<U>;
382
+ /** With mapper function. */
383
+ export function flatMapArray<T, U>(
384
+ array: ReadonlyArray<T> | Array<T>,
385
+ mapper: (item: T, index: number) => ReadonlyArray<U> | Array<U>,
386
+ ): ReadonlyArray<U>;
387
+ export function flatMapArray<T, U>(
388
+ array: ReadonlyArray<T> | Array<T>,
389
+ mapper: (
390
+ item: T,
391
+ index: number,
392
+ ) => ReadonlyArray<U> | Array<U> = identity as (
393
+ item: T,
394
+ index: number,
395
+ ) => ReadonlyArray<U> | Array<U>,
396
+ ): ReadonlyArray<U> {
397
+ return array.flatMap(mapper) as ReadonlyArray<U>;
398
+ }
399
+
400
+ /**
401
+ * Concatenates two arrays, returning a new readonly array.
402
+ *
403
+ * Returns a non-empty array when at least one input is non-empty.
404
+ *
405
+ * ### Example
406
+ *
407
+ * ```ts
408
+ * concatArrays([1, 2], [3, 4]); // [1, 2, 3, 4]
409
+ * concatArrays([], [1]); // [1] (non-empty)
410
+ * concatArrays([1], []); // [1] (non-empty)
411
+ * ```
412
+ *
413
+ * @group Transformations
414
+ */
415
+ export function concatArrays<T>(
416
+ first: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
417
+ second: ReadonlyArray<T> | Array<T>,
418
+ ): NonEmptyReadonlyArray<T>;
419
+ /** Second non-empty. */
420
+ export function concatArrays<T>(
421
+ first: ReadonlyArray<T> | Array<T>,
422
+ second: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
423
+ ): NonEmptyReadonlyArray<T>;
424
+ /** Both possibly empty. */
425
+ export function concatArrays<T>(
426
+ first: ReadonlyArray<T> | Array<T>,
427
+ second: ReadonlyArray<T> | Array<T>,
428
+ ): ReadonlyArray<T>;
429
+ export function concatArrays<T>(
430
+ first: ReadonlyArray<T> | Array<T>,
431
+ second: ReadonlyArray<T> | Array<T>,
432
+ ): ReadonlyArray<T> {
433
+ return [...first, ...second] as ReadonlyArray<T>;
205
434
  }
206
435
 
207
436
  /**
208
437
  * Filters an array using a predicate or refinement function, returning a new
209
438
  * readonly array.
210
439
  *
211
- * Accepts both mutable and readonly arrays. When used with a refinement
212
- * function (with `value is Type` syntax), TypeScript will narrow the result
213
- * type to the narrowed type, making it useful for filtering with Evolu Types
214
- * like `PositiveInt.is`.
440
+ * When used with a refinement function (with `value is Type` syntax),
441
+ * TypeScript will narrow the result type to the narrowed type, making it useful
442
+ * for filtering with Evolu Types like `PositiveInt.is`.
215
443
  *
216
- * ### Examples
444
+ * ### Example
217
445
  *
218
- * #### With predicate
446
+ * ### With predicate
219
447
  *
220
448
  * ```ts
221
449
  * filterArray([1, 2, 3, 4, 5], (x) => x % 2 === 0); // [2, 4]
222
450
  * ```
223
451
  *
224
- * #### With refinement
452
+ * ### With refinement
225
453
  *
226
454
  * ```ts
227
455
  * const mixed: ReadonlyArray<NonEmptyString | PositiveInt> = [
@@ -232,12 +460,13 @@ export function mapArray<T, U>(
232
460
  * // positiveInts: ReadonlyArray<PositiveInt> (narrowed type)
233
461
  * ```
234
462
  *
235
- * @category Transformations
463
+ * @group Transformations
236
464
  */
237
465
  export function filterArray<T, S extends T>(
238
466
  array: ReadonlyArray<T>,
239
467
  refinement: RefinementWithIndex<T, S>,
240
468
  ): ReadonlyArray<S>;
469
+ /** With predicate. */
241
470
  export function filterArray<T>(
242
471
  array: ReadonlyArray<T>,
243
472
  predicate: PredicateWithIndex<T>,
@@ -255,7 +484,6 @@ export function filterArray<T>(
255
484
  * are used directly. Dedupes by reference equality of values (or extracted keys
256
485
  * when `by` is used).
257
486
  *
258
- * Accepts both mutable and readonly arrays. Does not mutate the original array.
259
487
  * Preserves non-empty type.
260
488
  *
261
489
  * ### Example
@@ -275,12 +503,13 @@ export function filterArray<T>(
275
503
  * ); // [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
276
504
  * ```
277
505
  *
278
- * @category Transformations
506
+ * @group Transformations
279
507
  */
280
508
  export function dedupeArray<T>(
281
509
  array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
282
510
  by?: (item: T) => unknown,
283
511
  ): NonEmptyReadonlyArray<T>;
512
+ /** Possibly empty array. */
284
513
  export function dedupeArray<T>(
285
514
  array: ReadonlyArray<T> | Array<T>,
286
515
  by?: (item: T) => unknown,
@@ -303,20 +532,19 @@ export function dedupeArray<T>(
303
532
  }
304
533
 
305
534
  /**
306
- * Partitions an array into two arrays based on a predicate or refinement
307
- * function.
535
+ * Partitions an array into two readonly arrays based on a predicate or
536
+ * refinement function.
308
537
  *
309
538
  * Returns a tuple where the first array contains elements that satisfy the
310
- * predicate, and the second array contains elements that do not. Accepts both
311
- * mutable and readonly arrays.
539
+ * predicate, and the second array contains elements that do not.
312
540
  *
313
541
  * When used with a refinement function (with `value is Type` syntax),
314
542
  * TypeScript will narrow the first array to the narrowed type, making it useful
315
543
  * for filtering with Evolu Types like `PositiveInt.is`.
316
544
  *
317
- * ### Examples
545
+ * ### Example
318
546
  *
319
- * #### With predicate
547
+ * ### With predicate
320
548
  *
321
549
  * ```ts
322
550
  * const [evens, odds] = partitionArray(
@@ -327,7 +555,7 @@ export function dedupeArray<T>(
327
555
  * odds; // [1, 3, 5]
328
556
  * ```
329
557
  *
330
- * #### With refinement
558
+ * ### With refinement
331
559
  *
332
560
  * ```ts
333
561
  * const mixed: ReadonlyArray<NonEmptyString | PositiveInt> = [
@@ -339,12 +567,13 @@ export function dedupeArray<T>(
339
567
  * // strings: ReadonlyArray<NonEmptyString> (Exclude<T, PositiveInt>)
340
568
  * ```
341
569
  *
342
- * @category Transformations
570
+ * @group Transformations
343
571
  */
344
572
  export function partitionArray<T, S extends T>(
345
573
  array: ReadonlyArray<T>,
346
574
  refinement: RefinementWithIndex<T, S>,
347
575
  ): readonly [ReadonlyArray<S>, ReadonlyArray<Exclude<T, S>>];
576
+ /** With predicate. */
348
577
  export function partitionArray<T>(
349
578
  array: ReadonlyArray<T>,
350
579
  predicate: PredicateWithIndex<T>,
@@ -368,9 +597,137 @@ export function partitionArray<T>(
368
597
  }
369
598
 
370
599
  /**
371
- * Returns the first element of a non-empty array.
600
+ * Returns a new sorted readonly array.
601
+ *
602
+ * Wraps native `toSorted`. Preserves non-empty type.
603
+ *
604
+ * ### Example
605
+ *
606
+ * ```ts
607
+ * sortArray([3, 1, 2], (a, b) => a - b); // [1, 2, 3]
608
+ * ```
609
+ *
610
+ * @group Transformations
611
+ */
612
+ export function sortArray<T>(
613
+ array: NonEmptyReadonlyArray<T>,
614
+ compareFn?: (a: T, b: T) => number,
615
+ ): NonEmptyReadonlyArray<T>;
616
+ /** Possibly empty array. */
617
+ export function sortArray<T>(
618
+ array: ReadonlyArray<T>,
619
+ compareFn?: (a: T, b: T) => number,
620
+ ): ReadonlyArray<T>;
621
+ export function sortArray<T>(
622
+ array: ReadonlyArray<T>,
623
+ compareFn?: (a: T, b: T) => number,
624
+ ): ReadonlyArray<T> {
625
+ return array.toSorted(compareFn) as ReadonlyArray<T>;
626
+ }
627
+
628
+ /**
629
+ * Returns a new reversed readonly array.
630
+ *
631
+ * Wraps native `toReversed`. Preserves non-empty type.
632
+ *
633
+ * ### Example
372
634
  *
373
- * Accepts both mutable and readonly arrays. Does not mutate the original array.
635
+ * ```ts
636
+ * reverseArray([1, 2, 3]); // [3, 2, 1]
637
+ * ```
638
+ *
639
+ * @group Transformations
640
+ */
641
+ export function reverseArray<T>(
642
+ array: NonEmptyReadonlyArray<T>,
643
+ ): NonEmptyReadonlyArray<T>;
644
+ /** Possibly empty array. */
645
+ export function reverseArray<T>(array: ReadonlyArray<T>): ReadonlyArray<T>;
646
+ export function reverseArray<T>(array: ReadonlyArray<T>): ReadonlyArray<T> {
647
+ return array.toReversed() as ReadonlyArray<T>;
648
+ }
649
+
650
+ /**
651
+ * Returns a new readonly array with elements removed and/or replaced.
652
+ *
653
+ * Wraps native `toSpliced`.
654
+ *
655
+ * ### Example
656
+ *
657
+ * ```ts
658
+ * spliceArray([1, 2, 3, 4], 1, 2); // [1, 4]
659
+ * spliceArray([1, 2, 3], 1, 1, 10, 11); // [1, 10, 11, 3]
660
+ * ```
661
+ *
662
+ * @group Transformations
663
+ */
664
+ export const spliceArray = <T>(
665
+ array: ReadonlyArray<T>,
666
+ start: number,
667
+ deleteCount: number,
668
+ ...items: ReadonlyArray<T>
669
+ ): ReadonlyArray<T> => array.toSpliced(start, deleteCount, ...items);
670
+
671
+ /**
672
+ * Extracts element types from a tuple of arrays, producing a tuple type.
673
+ *
674
+ * @group Types
675
+ */
676
+ export type ZipArrayResult<T extends ReadonlyArray<ReadonlyArray<unknown>>> = {
677
+ [K in keyof T]: T[K] extends ReadonlyArray<infer U> ? U : never;
678
+ };
679
+
680
+ /**
681
+ * Combines multiple arrays into an array of tuples.
682
+ *
683
+ * Uses "shortest" mode — stops at the shortest input array. Preserves non-empty
684
+ * type when all input arrays are non-empty. See the
685
+ * {@link https://github.com/tc39/proposal-array-zip | TC39 Array.zip proposal}
686
+ * for the pattern this follows.
687
+ *
688
+ * ### Example
689
+ *
690
+ * ```ts
691
+ * zipArray([
692
+ * [1, 2, 3],
693
+ * ["a", "b", "c"],
694
+ * ]);
695
+ * // [[1, "a"], [2, "b"], [3, "c"]]
696
+ *
697
+ * zipArray([
698
+ * [1, 2],
699
+ * ["a", "b", "c"],
700
+ * [true, false],
701
+ * ]);
702
+ * // [[1, "a", true], [2, "b", false]]
703
+ * ```
704
+ *
705
+ * @group Transformations
706
+ */
707
+ export function zipArray<
708
+ T extends NonEmptyReadonlyArray<NonEmptyReadonlyArray<unknown>>,
709
+ >(arrays: T): NonEmptyReadonlyArray<Readonly<ZipArrayResult<T>>>;
710
+ /** Possibly empty arrays. */
711
+ export function zipArray<T extends ReadonlyArray<ReadonlyArray<unknown>>>(
712
+ arrays: T,
713
+ ): ReadonlyArray<Readonly<ZipArrayResult<T>>>;
714
+ export function zipArray<T extends ReadonlyArray<ReadonlyArray<unknown>>>(
715
+ arrays: T,
716
+ ): ReadonlyArray<Readonly<ZipArrayResult<T>>> {
717
+ if (arrays.length === 0) return emptyArray;
718
+
719
+ const minLength = Math.min(...mapArray(arrays, (a) => a.length));
720
+ const result = new Array<unknown>(minLength);
721
+
722
+ for (let i = 0; i < minLength; i++) {
723
+ result[i] = mapArray(arrays, (a) => a[i]);
724
+ }
725
+
726
+ return result as ReadonlyArray<Readonly<ZipArrayResult<T>>>;
727
+ }
728
+
729
+ /**
730
+ * Returns the first element of a non-empty array.
374
731
  *
375
732
  * ### Example
376
733
  *
@@ -378,56 +735,60 @@ export function partitionArray<T>(
378
735
  * firstInArray(["a", "b", "c"]); // "a"
379
736
  * ```
380
737
  *
381
- * @category Accessors
738
+ * @group Accessors
382
739
  */
383
740
  export const firstInArray = <T>(array: NonEmptyReadonlyArray<T>): T => array[0];
384
741
 
385
742
  /**
386
743
  * Returns the last element of a non-empty array.
387
744
  *
388
- * Accepts both mutable and readonly arrays. Does not mutate the original array.
389
- *
390
745
  * ### Example
391
746
  *
392
747
  * ```ts
393
748
  * lastInArray(["a", "b", "c"]); // "c"
394
749
  * ```
395
750
  *
396
- * @category Accessors
751
+ * @group Accessors
397
752
  */
398
753
  export const lastInArray = <T>(array: NonEmptyReadonlyArray<T>): T =>
399
754
  array[array.length - 1];
400
755
 
401
756
  /**
402
- * Shifts an item from a non-empty mutable array, guaranteed to return T.
757
+ * Shifts (removes and returns) the first element from a non-empty mutable
758
+ * array.
403
759
  *
404
760
  * **Mutates** the original array.
405
761
  *
406
762
  * ### Example
407
763
  *
408
764
  * ```ts
409
- * const arr: NonEmptyArray<number> = [1, 2, 3];
410
- * shiftArray(arr); // 1
411
- * arr; // [2, 3]
765
+ * // Process a queue of callbacks
766
+ * const waitingQueue: Array<() => void> = [callback1, callback2];
767
+ * if (isNonEmptyArray(waitingQueue)) {
768
+ * shiftFromArray(waitingQueue)(); // Remove and immediately invoke
769
+ * }
412
770
  * ```
413
771
  *
414
- * @category Mutations
772
+ * @group Mutations
415
773
  */
416
- export const shiftArray = <T>(array: NonEmptyArray<T>): T => array.shift() as T;
774
+ export const shiftFromArray = <T>(array: NonEmptyArray<T>): T =>
775
+ array.shift() as T;
417
776
 
418
777
  /**
419
- * Pops an item from a non-empty mutable array, guaranteed to return T.
778
+ * Pops (removes and returns) the last element from a non-empty mutable array.
420
779
  *
421
780
  * **Mutates** the original array.
422
781
  *
423
782
  * ### Example
424
783
  *
425
784
  * ```ts
426
- * const arr: NonEmptyArray<number> = [1, 2, 3];
427
- * popArray(arr); // 3
428
- * arr; // [1, 2]
785
+ * // Process a stack of callbacks (LIFO)
786
+ * const callbackStack: Array<() => void> = [callback1, callback2];
787
+ * if (isNonEmptyArray(callbackStack)) {
788
+ * popFromArray(callbackStack)(); // Remove and immediately invoke
789
+ * }
429
790
  * ```
430
791
  *
431
- * @category Mutations
792
+ * @group Mutations
432
793
  */
433
- export const popArray = <T>(array: NonEmptyArray<T>): T => array.pop() as T;
794
+ export const popFromArray = <T>(array: NonEmptyArray<T>): T => array.pop() as T;