@nunofyobiz/effect-extras 2.0.0 → 2.1.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 (127) hide show
  1. package/README.md +36 -2
  2. package/dist/ArrayX.d.ts +415 -0
  3. package/dist/ArrayX.d.ts.map +1 -0
  4. package/dist/ArrayX.js +547 -0
  5. package/dist/ArrayX.js.map +1 -0
  6. package/dist/BigIntX.d.ts +24 -0
  7. package/dist/BigIntX.d.ts.map +1 -0
  8. package/dist/BigIntX.js +30 -0
  9. package/dist/BigIntX.js.map +1 -0
  10. package/dist/BooleanX.d.ts +25 -0
  11. package/dist/BooleanX.d.ts.map +1 -0
  12. package/dist/BooleanX.js +25 -0
  13. package/dist/BooleanX.js.map +1 -0
  14. package/dist/DurationX.d.ts +73 -0
  15. package/dist/DurationX.d.ts.map +1 -0
  16. package/dist/DurationX.js +91 -0
  17. package/dist/DurationX.js.map +1 -0
  18. package/dist/EffectX.d.ts +120 -0
  19. package/dist/EffectX.d.ts.map +1 -0
  20. package/dist/EffectX.js +140 -0
  21. package/dist/EffectX.js.map +1 -0
  22. package/dist/FormDataX.d.ts +49 -0
  23. package/dist/FormDataX.d.ts.map +1 -0
  24. package/dist/FormDataX.js +42 -0
  25. package/dist/FormDataX.js.map +1 -0
  26. package/dist/MapX.d.ts +32 -0
  27. package/dist/MapX.d.ts.map +1 -0
  28. package/dist/MapX.js +49 -0
  29. package/dist/MapX.js.map +1 -0
  30. package/dist/NonNullableX.d.ts +174 -0
  31. package/dist/NonNullableX.d.ts.map +1 -0
  32. package/dist/NonNullableX.js +212 -0
  33. package/dist/NonNullableX.js.map +1 -0
  34. package/dist/NumberX.d.ts +178 -0
  35. package/dist/NumberX.d.ts.map +1 -0
  36. package/dist/NumberX.js +214 -0
  37. package/dist/NumberX.js.map +1 -0
  38. package/dist/OptionX.d.ts +181 -0
  39. package/dist/OptionX.d.ts.map +1 -0
  40. package/dist/OptionX.js +195 -0
  41. package/dist/OptionX.js.map +1 -0
  42. package/dist/OrderX.d.ts +32 -0
  43. package/dist/OrderX.d.ts.map +1 -0
  44. package/dist/OrderX.js +32 -0
  45. package/dist/OrderX.js.map +1 -0
  46. package/dist/PredicateX.d.ts +76 -0
  47. package/dist/PredicateX.d.ts.map +1 -0
  48. package/dist/PredicateX.js +73 -0
  49. package/dist/PredicateX.js.map +1 -0
  50. package/dist/PromiseX.d.ts +32 -0
  51. package/dist/PromiseX.d.ts.map +1 -0
  52. package/dist/PromiseX.js +32 -0
  53. package/dist/PromiseX.js.map +1 -0
  54. package/dist/RecordX.d.ts +323 -0
  55. package/dist/RecordX.d.ts.map +1 -0
  56. package/dist/RecordX.js +326 -0
  57. package/dist/RecordX.js.map +1 -0
  58. package/dist/ResultX.d.ts +50 -0
  59. package/dist/ResultX.d.ts.map +1 -0
  60. package/dist/ResultX.js +50 -0
  61. package/dist/ResultX.js.map +1 -0
  62. package/dist/SchemaX.d.ts +249 -0
  63. package/dist/SchemaX.d.ts.map +1 -0
  64. package/dist/SchemaX.js +243 -0
  65. package/dist/SchemaX.js.map +1 -0
  66. package/dist/SetX.d.ts +121 -0
  67. package/dist/SetX.d.ts.map +1 -0
  68. package/dist/SetX.js +137 -0
  69. package/dist/SetX.js.map +1 -0
  70. package/dist/StringX.d.ts +70 -0
  71. package/dist/StringX.d.ts.map +1 -0
  72. package/dist/StringX.js +81 -0
  73. package/dist/StringX.js.map +1 -0
  74. package/dist/StructX.d.ts +219 -0
  75. package/dist/StructX.d.ts.map +1 -0
  76. package/dist/StructX.js +173 -0
  77. package/dist/StructX.js.map +1 -0
  78. package/dist/WarnResult.d.ts +1146 -0
  79. package/dist/WarnResult.d.ts.map +1 -0
  80. package/dist/WarnResult.js +1060 -0
  81. package/dist/WarnResult.js.map +1 -0
  82. package/dist/index.d.ts +22 -3772
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +21 -1011
  85. package/dist/index.js.map +1 -1
  86. package/package.json +18 -5
  87. package/src/{ArrayX/ArrayX.ts → ArrayX.ts} +3 -3
  88. package/src/{DurationX/DurationX.ts → DurationX.ts} +1 -1
  89. package/src/{RecordX/RecordX.ts → RecordX.ts} +1 -1
  90. package/src/index.ts +21 -20
  91. package/src/ArrayX/index.ts +0 -1
  92. package/src/BigIntX/index.ts +0 -1
  93. package/src/BooleanX/index.ts +0 -1
  94. package/src/DurationX/index.ts +0 -1
  95. package/src/EffectX/index.ts +0 -1
  96. package/src/FormDataX/index.ts +0 -1
  97. package/src/MapX/index.ts +0 -1
  98. package/src/NonNullableX/index.ts +0 -2
  99. package/src/NumberX/index.ts +0 -1
  100. package/src/OptionX/index.ts +0 -1
  101. package/src/OrderX/index.ts +0 -1
  102. package/src/PredicateX/index.ts +0 -1
  103. package/src/PromiseX/index.ts +0 -1
  104. package/src/RecordX/index.ts +0 -1
  105. package/src/ResultX/index.ts +0 -1
  106. package/src/SchemaX/index.ts +0 -1
  107. package/src/SetX/index.ts +0 -1
  108. package/src/StringX/index.ts +0 -1
  109. package/src/StructX/index.ts +0 -1
  110. package/src/WarnResult/index.ts +0 -1
  111. /package/src/{BigIntX/BigIntX.ts → BigIntX.ts} +0 -0
  112. /package/src/{BooleanX/BooleanX.ts → BooleanX.ts} +0 -0
  113. /package/src/{EffectX/EffectX.ts → EffectX.ts} +0 -0
  114. /package/src/{FormDataX/FormDataX.ts → FormDataX.ts} +0 -0
  115. /package/src/{MapX/MapX.ts → MapX.ts} +0 -0
  116. /package/src/{NonNullableX/NonNullableX.ts → NonNullableX.ts} +0 -0
  117. /package/src/{NumberX/NumberX.ts → NumberX.ts} +0 -0
  118. /package/src/{OptionX/OptionX.ts → OptionX.ts} +0 -0
  119. /package/src/{OrderX/OrderX.ts → OrderX.ts} +0 -0
  120. /package/src/{PredicateX/PredicateX.ts → PredicateX.ts} +0 -0
  121. /package/src/{PromiseX/PromiseX.ts → PromiseX.ts} +0 -0
  122. /package/src/{ResultX/ResultX.ts → ResultX.ts} +0 -0
  123. /package/src/{SchemaX/SchemaX.ts → SchemaX.ts} +0 -0
  124. /package/src/{SetX/SetX.ts → SetX.ts} +0 -0
  125. /package/src/{StringX/StringX.ts → StringX.ts} +0 -0
  126. /package/src/{StructX/StructX.ts → StructX.ts} +0 -0
  127. /package/src/{WarnResult/WarnResult.ts → WarnResult.ts} +0 -0
package/README.md CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@nunofyobiz/effect-extras)](https://www.npmjs.com/package/@nunofyobiz/effect-extras)
4
4
  [![CI](https://github.com/nunofyobiz/effect-extras/actions/workflows/ci.yml/badge.svg)](https://github.com/nunofyobiz/effect-extras/actions/workflows/ci.yml)
5
+ [![Docs](https://img.shields.io/badge/docs-API%20reference-blue)](https://nunofyobiz.github.io/effect-extras/)
5
6
  [![License](https://img.shields.io/npm/l/@nunofyobiz/effect-extras)](./LICENSE)
6
7
 
8
+ 📖 **[API reference & docs →](https://nunofyobiz.github.io/effect-extras/)**
9
+
7
10
  Generic, framework-agnostic extensions of the [Effect](https://effect.website)
8
11
  standard library. These are the `*X` utility modules — `ArrayX`, `OptionX`,
9
12
  `RecordX`, `StructX`, and friends — that extend Effect's own modules with small,
@@ -35,6 +38,33 @@ pnpm add @nunofyobiz/effect-extras
35
38
  compatible version of `effect`. This package extends Effect; it does not bundle
36
39
  it.
37
40
 
41
+ ## Tree-shaking
42
+
43
+ The package is **side-effect free** (`"sideEffects": false`) and is built — the
44
+ same way [Effect](https://effect.website) is — with `tsc` (one ESM file per
45
+ module, no bundling) plus Babel's
46
+ [`annotate-pure-calls`](https://github.com/Andarist/babel-plugin-annotate-pure-calls)
47
+ pass, which stamps every helper `/*#__PURE__*/`. A bundler therefore keeps only
48
+ what you actually use. Three import styles, finest-grained first:
49
+
50
+ ```ts
51
+ // Subpath, named — only this function (and its real deps) reach your bundle.
52
+ import { compactNullable } from "@nunofyobiz/effect-extras/ArrayX";
53
+
54
+ // Subpath, namespace — `ArrayX.*`, tree-shaken per function (Effect-style).
55
+ import * as ArrayX from "@nunofyobiz/effect-extras/ArrayX";
56
+
57
+ // Root barrel — convenient; unused *modules* are shaken away, but a module you
58
+ // touch is kept whole (per-module granularity).
59
+ import { ArrayX } from "@nunofyobiz/effect-extras";
60
+ ```
61
+
62
+ Measured (minified + brotli, `effect` externalized): a single function lands at
63
+ **~40 B**, a whole module at **~1 kB**, the entire library at **~4.4 kB** — so a
64
+ subpath import pays for what it uses, not the library. Budgets are enforced in CI
65
+ via [size-limit](https://github.com/ai/size-limit); packaging correctness via
66
+ [publint](https://publint.dev).
67
+
38
68
  ## What belongs here
39
69
 
40
70
  This package has exactly one job: hold the generic `*X` helpers that extend
@@ -93,7 +123,9 @@ shape — not before.
93
123
 
94
124
  ## Modules
95
125
 
96
- Each module is exported as a namespace from the package root:
126
+ Each module is exported as a namespace from the package root and from a
127
+ matching subpath (`@nunofyobiz/effect-extras/ArrayX`); see
128
+ [Tree-shaking](#tree-shaking):
97
129
 
98
130
  | Module | Extends / purpose |
99
131
  | -------------- | -------------------------------------------------------------------------- |
@@ -125,7 +157,9 @@ pnpm install
125
157
  pnpm tc # typecheck (src + tests)
126
158
  pnpm lint # ESLint (formatting included via eslint-plugin-prettier)
127
159
  pnpm test # vitest run
128
- pnpm build # emit dist/ (ESM + bundled .d.ts) via tsup
160
+ pnpm build # emit dist/ (one ESM file + .d.ts per module): tsc + babel
161
+ pnpm publint # validate packaging (exports map, types, ESM) — needs a build
162
+ pnpm treeshake # enforce per-function tree-shaking budgets (size-limit) — needs a build
129
163
  pnpm knip # unused code / deps
130
164
  pnpm check-all # all of the above, in CI order
131
165
  ```
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Generic, framework-agnostic extensions to Effect's `Array` module.
3
+ *
4
+ * @since 0.0.0
5
+ */
6
+ import { Array, Equivalence, Option, Order, Predicate } from "effect";
7
+ import * as WarnResult from "./WarnResult.js";
8
+ /**
9
+ * Returns a shallow copy of `array` between `start` (inclusive) and `end`
10
+ * (exclusive), as a pipeable, dual-form alias for `Array.prototype.slice`.
11
+ *
12
+ * `Array.prototype.slice` is already non-mutating (it returns a shallow copy),
13
+ * but it isn't pipeable. This helper makes it composable inside `pipe(...)`
14
+ * chains alongside the rest of the codebase's Effect-style utilities.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { pipe } from "effect"
19
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
20
+ *
21
+ * // data-first
22
+ * assert.deepStrictEqual(ArrayX.slice([1, 2, 3, 4], 1, 3), [2, 3])
23
+ *
24
+ * // data-last (pipeable)
25
+ * assert.deepStrictEqual(pipe([1, 2, 3, 4], ArrayX.slice(1, 3)), [2, 3])
26
+ * ```
27
+ *
28
+ * @category getters
29
+ * @since 0.0.0
30
+ */
31
+ export declare const slice: (<A>(start: number, end: number) => (array: readonly A[]) => A[]) & (<A>(array: readonly A[], start: number, end: number) => A[]);
32
+ /**
33
+ * Zips two arrays into one, calling `f` with a `WarnResult` for each index so
34
+ * that length mismatches are handled explicitly rather than truncated.
35
+ *
36
+ * Unlike `Array.zipWith` (which stops at the shorter array), this walks to the
37
+ * length of the *longer* array. The first array's element fills the `warnings`
38
+ * side and the second array's element fills the `success` side, so at each index
39
+ * `f` receives a `WarnResult.WarnResult<A, B>`: `SuccessWithWarnings` when both
40
+ * arrays have an element, `WarningsOnly` when only the first does, and
41
+ * `SuccessOnly` when only the second does. Use it when the "extra" tail of either
42
+ * array still carries meaning.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import { ArrayX, WarnResult } from "@nunofyobiz/effect-extras"
47
+ *
48
+ * const describe = WarnResult.match({
49
+ * WarningsOnly: ({ warnings }) => `warnings ${warnings}`,
50
+ * SuccessOnly: ({ success }) => `success ${success}`,
51
+ * SuccessWithWarnings: ({ warnings, success }) => `both ${warnings}/${success}`,
52
+ * })
53
+ *
54
+ * assert.deepStrictEqual(ArrayX.zipWithWarnings([1, 2, 3], [10, 20], describe), [
55
+ * "both 1/10",
56
+ * "both 2/20",
57
+ * "warnings 3",
58
+ * ])
59
+ * ```
60
+ *
61
+ * @category combinators
62
+ * @since 0.0.0
63
+ */
64
+ export declare const zipWithWarnings: (<A, B, C>(f: (ab: WarnResult.WarnResult<A, B>) => C) => (array1: readonly A[], array2: readonly B[]) => C[]) & (<A, B, C>(array1: readonly A[], array2: readonly B[], f: (ab: WarnResult.WarnResult<A, B>) => C) => C[]);
65
+ /**
66
+ * Inserts or moves a unique item in an array at a specified position.
67
+ *
68
+ * **Assumption**: Items should be unique in the array based on standard equality.
69
+ *
70
+ * **Happy case**: If the item doesn't exist in the array and the destination reference item is found:
71
+ * The new item is inserted before the destination reference item
72
+ *
73
+ * **Item not found in array**:
74
+ * - If destination reference item is found: The new item is inserted before the destination reference item
75
+ * - If destination reference item is not found: The new item is inserted at the end of the array
76
+ *
77
+ * **Item found but duplicated**:
78
+ * - If destination reference item is found: All existing copies are removed, then a single copy is inserted before the destination reference item
79
+ * - If destination reference item is not found: All existing copies are removed, then a single copy is inserted at the end of the array
80
+ *
81
+ * @param array - The input array to modify
82
+ * @param config - Configuration object containing:
83
+ * - `item`: The item to insert or update (must be a string or number)
84
+ * - `insertToBeLeftOf`: The item to position the new/updated item before,
85
+ * or null to insert at the end
86
+ *
87
+ * @returns A new array with the item inserted or moved to the specified position
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
92
+ *
93
+ * // Move an existing item to sit just before "c"
94
+ * assert.deepStrictEqual(
95
+ * ArrayX.insertUniq(["a", "b", "c", "d"], { item: "a", insertToBeLeftOf: "c" }),
96
+ * ["b", "a", "c", "d"],
97
+ * )
98
+ *
99
+ * // Insert a brand-new item; unknown destination falls through to the end
100
+ * assert.deepStrictEqual(
101
+ * ArrayX.insertUniq(["a", "b"], { item: "new", insertToBeLeftOf: null }),
102
+ * ["a", "b", "new"],
103
+ * )
104
+ * ```
105
+ *
106
+ * @category combinators
107
+ * @since 0.0.0
108
+ */
109
+ export declare const insertUniq: (<A extends string | number>(config: {
110
+ item: A;
111
+ insertToBeLeftOf: A | null;
112
+ }) => (array: readonly A[] | A[]) => A[]) & (<A extends string | number>(array: readonly A[] | A[], config: {
113
+ item: A;
114
+ insertToBeLeftOf: A | null;
115
+ }) => A[]);
116
+ /**
117
+ * Maps over `array` while threading an accumulator, iterating from right to
118
+ * left instead of left to right.
119
+ *
120
+ * Identical to `Array.mapAccum`, except the traversal order is reversed: `f` is
121
+ * called on the last element first, and the resulting array is returned in the
122
+ * original (left-to-right) order. Use it when each element's mapped value
123
+ * depends on state accumulated from the elements that follow it.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
128
+ *
129
+ * // Running suffix-sum: each slot holds the sum of itself and everything after it
130
+ * assert.deepStrictEqual(
131
+ * ArrayX.mapRightAccum([1, 2, 3], 0, (total, n) => [total + n, total + n]),
132
+ * [6, [6, 5, 3]],
133
+ * )
134
+ * ```
135
+ *
136
+ * @category folding
137
+ * @since 0.0.0
138
+ */
139
+ export declare const mapRightAccum: (<A, B, C>(initialAccumulator: C, f: (accumulator: C, a: A, index: number) => [C, B]) => (array: A[]) => [C, B[]]) & (<A, B, C>(array: A[], initialAccumulator: C, f: (accumulator: C, a: A, index: number) => [C, B]) => [C, B[]]);
140
+ /**
141
+ * Returns the maximum element of `array` according to `order`, wrapped in an
142
+ * `Option` so that empty arrays are handled safely.
143
+ *
144
+ * Effect's `Array.max` throws on an empty array; this returns `Option.none()`
145
+ * instead, and `Option.some(max)` otherwise. Reach for it whenever the input
146
+ * array might be empty.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * import { Option, Order, pipe } from "effect"
151
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
152
+ *
153
+ * assert.deepStrictEqual(
154
+ * pipe([3, 7, 2], ArrayX.maxOption(Order.Number)),
155
+ * Option.some(7),
156
+ * )
157
+ * assert.deepStrictEqual(
158
+ * pipe([], ArrayX.maxOption(Order.Number)),
159
+ * Option.none(),
160
+ * )
161
+ * ```
162
+ *
163
+ * @category getters
164
+ * @since 0.0.0
165
+ */
166
+ export declare const maxOption: (<A>(order: Order.Order<A>) => (array: A[]) => Option.Option<A>) & (<A>(array: A[], order: Order.Order<A>) => Option.Option<A>);
167
+ /**
168
+ * Returns the smallest element of `array` (per `order`) that matches
169
+ * `predicate`, narrowed to the refined type `B`, or `Option.none()` if none
170
+ * match.
171
+ *
172
+ * Combines a refinement filter with `Array.min`: only elements satisfying
173
+ * `predicate` are considered, and the minimum of those (by `order`) is
174
+ * returned. The refinement narrows the element type, so the resulting `Option`
175
+ * carries the more specific `B`.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * import { Option, Order, pipe } from "effect"
180
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
181
+ *
182
+ * const isEven = (n: number): n is number => n % 2 === 0
183
+ *
184
+ * assert.deepStrictEqual(
185
+ * pipe([3, 4, 1, 2, 5], ArrayX.takeFirstWhere(isEven, Order.Number)),
186
+ * Option.some(2),
187
+ * )
188
+ * assert.deepStrictEqual(
189
+ * pipe([1, 3, 5], ArrayX.takeFirstWhere(isEven, Order.Number)),
190
+ * Option.none(),
191
+ * )
192
+ * ```
193
+ *
194
+ * @category getters
195
+ * @since 0.0.0
196
+ */
197
+ export declare const takeFirstWhere: (<A, B extends A>(predicate: Predicate.Refinement<A, B>, order: Order.Order<B>) => (array: A[]) => Option.Option<B>) & (<A, B extends A>(array: A[], predicate: Predicate.Refinement<A, B>, order: Order.Order<B>) => Option.Option<B>);
198
+ /**
199
+ * Returns the largest element of `array` (per `order`) that matches
200
+ * `predicate`, narrowed to the refined type `B`, or `Option.none()` if none
201
+ * match.
202
+ *
203
+ * The mirror of {@link takeFirstWhere}: only elements satisfying `predicate`
204
+ * are considered, and the maximum of those (by `order`) is returned. The
205
+ * refinement narrows the element type, so the resulting `Option` carries the
206
+ * more specific `B`.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * import { Option, Order, pipe } from "effect"
211
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
212
+ *
213
+ * const isEven = (n: number): n is number => n % 2 === 0
214
+ *
215
+ * assert.deepStrictEqual(
216
+ * pipe([3, 4, 1, 2, 5], ArrayX.takeLastWhere(isEven, Order.Number)),
217
+ * Option.some(4),
218
+ * )
219
+ * assert.deepStrictEqual(
220
+ * pipe([1, 3, 5], ArrayX.takeLastWhere(isEven, Order.Number)),
221
+ * Option.none(),
222
+ * )
223
+ * ```
224
+ *
225
+ * @category getters
226
+ * @since 0.0.0
227
+ */
228
+ export declare const takeLastWhere: (<A, B extends A>(predicate: Predicate.Refinement<A, B>, order: Order.Order<B>) => (array: A[]) => Option.Option<B>) & (<A, B extends A>(array: A[], predicate: Predicate.Refinement<A, B>, order: Order.Order<B>) => Option.Option<B>);
229
+ /**
230
+ * Groups `items` into a partial record keyed by the category each item maps to
231
+ * via `categorize`.
232
+ *
233
+ * Each item is appended to the array under its category, preserving input
234
+ * order. The result is `Partial<Record<C, A[]>>` because not every possible
235
+ * category `C` is guaranteed to appear — only categories that received at least
236
+ * one item are present.
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
241
+ *
242
+ * const parity = (n: number) => (n % 2 === 0 ? "even" : "odd")
243
+ *
244
+ * assert.deepStrictEqual(ArrayX.categorize([1, 2, 3, 4], parity), {
245
+ * odd: [1, 3],
246
+ * even: [2, 4],
247
+ * })
248
+ * ```
249
+ *
250
+ * @category folding
251
+ * @since 0.0.0
252
+ */
253
+ export declare const categorize: <A, C extends string>(items: Iterable<A>, categorize: (a: A) => C) => Partial<Record<C, A[]>>;
254
+ /**
255
+ * Removes all `null` and `undefined` elements from `array`, narrowing the
256
+ * element type to `NonNullable<A>`.
257
+ *
258
+ * Falsy-but-present values such as `0` and `""` are kept — only nullish values
259
+ * are dropped. Use it to clean up an array of optionals into a dense array of
260
+ * known-present values.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
265
+ *
266
+ * assert.deepStrictEqual(
267
+ * ArrayX.compactNullable([1, null, 2, undefined, 0, ""]),
268
+ * [1, 2, 0, ""],
269
+ * )
270
+ * ```
271
+ *
272
+ * @category filtering
273
+ * @since 0.0.0
274
+ */
275
+ export declare const compactNullable: <A>(array: A[]) => NonNullable<A>[];
276
+ /**
277
+ * Drops the leading elements of `array` until `predicate` first holds, keeping
278
+ * everything from the first match onward.
279
+ *
280
+ * The first matching element and all subsequent elements are retained
281
+ * regardless of whether they match — only the prefix *before* the first match
282
+ * is trimmed. If nothing matches, returns an empty array.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * import { Predicate } from "effect"
287
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
288
+ *
289
+ * // Trims the leading strings, then keeps everything (including the trailing "b")
290
+ * assert.deepStrictEqual(
291
+ * ArrayX.filterHead(["a", 1, 2, "b"], Predicate.isNumber),
292
+ * [1, 2, "b"],
293
+ * )
294
+ * ```
295
+ *
296
+ * @category filtering
297
+ * @since 0.0.0
298
+ */
299
+ export declare const filterHead: (<A>(predicate: Predicate.Predicate<A>) => (array: A[]) => A[]) & (<A>(array: A[], predicate: Predicate.Predicate<A>) => A[]);
300
+ /**
301
+ * Drops the trailing elements of `array` after `predicate` last holds, keeping
302
+ * everything up to and including the last match.
303
+ *
304
+ * The mirror of {@link filterHead}: the last matching element and all preceding
305
+ * elements are retained regardless of whether they match — only the suffix
306
+ * *after* the last match is trimmed. If nothing matches, returns an empty
307
+ * array.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * import { Predicate } from "effect"
312
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
313
+ *
314
+ * // Keeps the leading "a" and trims the trailing strings after the last number
315
+ * assert.deepStrictEqual(
316
+ * ArrayX.filterTail(["a", 1, 2, "b"], Predicate.isNumber),
317
+ * ["a", 1, 2],
318
+ * )
319
+ * ```
320
+ *
321
+ * @category filtering
322
+ * @since 0.0.0
323
+ */
324
+ export declare const filterTail: (<A>(predicate: Predicate.Predicate<A>) => (array: A[]) => A[]) & (<A>(array: A[], predicate: Predicate.Predicate<A>) => A[]);
325
+ /**
326
+ * Maps `f` over `array` and drops every result that is `null` or `undefined`,
327
+ * narrowing the element type to `NonNullable<B>`.
328
+ *
329
+ * A nullable-friendly `Array.filterMap`: where `filterMap` expects `f` to
330
+ * return an `Option`, this accepts a function returning `B | null` (or
331
+ * `undefined`) and treats nullish results as "skip this element". Falsy-but-
332
+ * present values such as `0` and `""` are kept.
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
337
+ *
338
+ * // Keep only the even numbers, mapped to their halves
339
+ * assert.deepStrictEqual(
340
+ * ArrayX.filterMapNullable([1, 2, 3, 4], (n) => (n % 2 === 0 ? n / 2 : null)),
341
+ * [1, 2],
342
+ * )
343
+ * ```
344
+ *
345
+ * @category filtering
346
+ * @since 0.0.0
347
+ */
348
+ export declare const filterMapNullable: (<A, B>(f: (a: A) => B | null) => (array: A[]) => NonNullable<B>[]) & (<A, B>(array: A[], f: (a: A) => B | null) => NonNullable<B>[]);
349
+ /**
350
+ * Finds the first element of a 2-dimensional array (row-major order) matching
351
+ * `predicate`, returning it alongside its row and column indices.
352
+ *
353
+ * Scans rows top-to-bottom and, within each row, left-to-right. On a match
354
+ * returns `Option.some([value, rowIndex, columnIndex])`; if no element matches
355
+ * (or the grid is empty), returns `Option.none()`.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * import { Option } from "effect"
360
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
361
+ *
362
+ * const grid = [
363
+ * ["A", "B", "C"],
364
+ * ["D", "E", "F"],
365
+ * ]
366
+ *
367
+ * assert.deepStrictEqual(
368
+ * ArrayX.findFirstWithIndex2d(grid, (cell) => cell === "E"),
369
+ * Option.some(["E", 1, 1]),
370
+ * )
371
+ * ```
372
+ *
373
+ * @category getters
374
+ * @since 0.0.0
375
+ */
376
+ export declare const findFirstWithIndex2d: (<A>(predicate: Predicate.Predicate<A>) => (array: A[][]) => Option.Option<[A, number, number]>) & (<A>(array: A[][], predicate: Predicate.Predicate<A>) => Option.Option<[A, number, number]>);
377
+ /**
378
+ * Splits `array` into runs of consecutive elements that share the same group
379
+ * value, where the group is derived by `chunk` and compared with the provided
380
+ * `Equivalence`.
381
+ *
382
+ * Only *adjacent* elements are grouped: a new run starts every time the group
383
+ * value changes from the previous element. Each entry in the result carries the
384
+ * `group` value and the non-empty array of `values` that produced it, preserving
385
+ * input order. An empty input yields an empty array. Use it for run-length-style
386
+ * segmentation; reach for `Array.groupBy` instead when you want all elements
387
+ * with the same key collapsed regardless of position.
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * import { Equivalence } from "effect"
392
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
393
+ *
394
+ * // Group adjacent numbers by parity
395
+ * assert.deepStrictEqual(
396
+ * ArrayX.chunkBy([2, 4, 1, 3, 6], (n) => n % 2 === 0, Equivalence.Boolean),
397
+ * [
398
+ * { group: true, values: [2, 4] },
399
+ * { group: false, values: [1, 3] },
400
+ * { group: true, values: [6] },
401
+ * ],
402
+ * )
403
+ * ```
404
+ *
405
+ * @category folding
406
+ * @since 0.0.0
407
+ */
408
+ export declare const chunkBy: (<A, B>(chunk: (a: A) => B, GroupEquivalence: Equivalence.Equivalence<B>) => (array: A[]) => {
409
+ group: B;
410
+ values: Array.NonEmptyArray<A>;
411
+ }[]) & (<A, B>(array: A[], chunk: (a: A) => B, GroupEquivalence: Equivalence.Equivalence<B>) => {
412
+ group: B;
413
+ values: Array.NonEmptyArray<A>;
414
+ }[]);
415
+ //# sourceMappingURL=ArrayX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ArrayX.d.ts","sourceRoot":"","sources":["../src/ArrayX.ts"],"names":[],"mappings":"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"}