@nunofyobiz/effect-extras 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +38 -3
  2. package/dist/ArrayX.d.ts +381 -0
  3. package/dist/ArrayX.d.ts.map +1 -0
  4. package/dist/ArrayX.js +493 -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/InclusiveOr.d.ts +1123 -0
  27. package/dist/InclusiveOr.d.ts.map +1 -0
  28. package/dist/InclusiveOr.js +1074 -0
  29. package/dist/InclusiveOr.js.map +1 -0
  30. package/dist/MapX.d.ts +32 -0
  31. package/dist/MapX.d.ts.map +1 -0
  32. package/dist/MapX.js +49 -0
  33. package/dist/MapX.js.map +1 -0
  34. package/dist/NonNullableX.d.ts +174 -0
  35. package/dist/NonNullableX.d.ts.map +1 -0
  36. package/dist/NonNullableX.js +217 -0
  37. package/dist/NonNullableX.js.map +1 -0
  38. package/dist/NumberX.d.ts +178 -0
  39. package/dist/NumberX.d.ts.map +1 -0
  40. package/dist/NumberX.js +214 -0
  41. package/dist/NumberX.js.map +1 -0
  42. package/dist/OptionX.d.ts +187 -0
  43. package/dist/OptionX.d.ts.map +1 -0
  44. package/dist/OptionX.js +201 -0
  45. package/dist/OptionX.js.map +1 -0
  46. package/dist/OrderX.d.ts +32 -0
  47. package/dist/OrderX.d.ts.map +1 -0
  48. package/dist/OrderX.js +32 -0
  49. package/dist/OrderX.js.map +1 -0
  50. package/dist/PredicateX.d.ts +108 -0
  51. package/dist/PredicateX.d.ts.map +1 -0
  52. package/dist/PredicateX.js +111 -0
  53. package/dist/PredicateX.js.map +1 -0
  54. package/dist/PromiseX.d.ts +32 -0
  55. package/dist/PromiseX.d.ts.map +1 -0
  56. package/dist/PromiseX.js +32 -0
  57. package/dist/PromiseX.js.map +1 -0
  58. package/dist/RecordX.d.ts +450 -0
  59. package/dist/RecordX.d.ts.map +1 -0
  60. package/dist/RecordX.js +487 -0
  61. package/dist/RecordX.js.map +1 -0
  62. package/dist/ResultX.d.ts +50 -0
  63. package/dist/ResultX.d.ts.map +1 -0
  64. package/dist/ResultX.js +50 -0
  65. package/dist/ResultX.js.map +1 -0
  66. package/dist/SchemaX.d.ts +249 -0
  67. package/dist/SchemaX.d.ts.map +1 -0
  68. package/dist/SchemaX.js +243 -0
  69. package/dist/SchemaX.js.map +1 -0
  70. package/dist/SetX.d.ts +121 -0
  71. package/dist/SetX.d.ts.map +1 -0
  72. package/dist/SetX.js +137 -0
  73. package/dist/SetX.js.map +1 -0
  74. package/dist/StringX.d.ts +131 -0
  75. package/dist/StringX.d.ts.map +1 -0
  76. package/dist/StringX.js +149 -0
  77. package/dist/StringX.js.map +1 -0
  78. package/dist/StructX.d.ts +219 -0
  79. package/dist/StructX.d.ts.map +1 -0
  80. package/dist/StructX.js +173 -0
  81. package/dist/StructX.js.map +1 -0
  82. package/dist/WarnResult.d.ts +1191 -0
  83. package/dist/WarnResult.d.ts.map +1 -0
  84. package/dist/WarnResult.js +991 -0
  85. package/dist/WarnResult.js.map +1 -0
  86. package/dist/index.d.ts +23 -3772
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +22 -1011
  89. package/dist/index.js.map +1 -1
  90. package/package.json +18 -5
  91. package/src/{ArrayX/ArrayX.ts → ArrayX.ts} +6 -88
  92. package/src/{DurationX/DurationX.ts → DurationX.ts} +1 -1
  93. package/src/InclusiveOr.ts +1255 -0
  94. package/src/{NonNullableX/NonNullableX.ts → NonNullableX.ts} +5 -0
  95. package/src/{OptionX/OptionX.ts → OptionX.ts} +8 -2
  96. package/src/{PredicateX/PredicateX.ts → PredicateX.ts} +41 -0
  97. package/src/{RecordX/RecordX.ts → RecordX.ts} +184 -2
  98. package/src/StringX.ts +210 -0
  99. package/src/{WarnResult/WarnResult.ts → WarnResult.ts} +297 -227
  100. package/src/index.ts +22 -20
  101. package/src/ArrayX/index.ts +0 -1
  102. package/src/BigIntX/index.ts +0 -1
  103. package/src/BooleanX/index.ts +0 -1
  104. package/src/DurationX/index.ts +0 -1
  105. package/src/EffectX/index.ts +0 -1
  106. package/src/FormDataX/index.ts +0 -1
  107. package/src/MapX/index.ts +0 -1
  108. package/src/NonNullableX/index.ts +0 -2
  109. package/src/NumberX/index.ts +0 -1
  110. package/src/OptionX/index.ts +0 -1
  111. package/src/OrderX/index.ts +0 -1
  112. package/src/PredicateX/index.ts +0 -1
  113. package/src/PromiseX/index.ts +0 -1
  114. package/src/RecordX/index.ts +0 -1
  115. package/src/ResultX/index.ts +0 -1
  116. package/src/SchemaX/index.ts +0 -1
  117. package/src/SetX/index.ts +0 -1
  118. package/src/StringX/StringX.ts +0 -97
  119. package/src/StringX/index.ts +0 -1
  120. package/src/StructX/index.ts +0 -1
  121. package/src/WarnResult/index.ts +0 -1
  122. /package/src/{BigIntX/BigIntX.ts → BigIntX.ts} +0 -0
  123. /package/src/{BooleanX/BooleanX.ts → BooleanX.ts} +0 -0
  124. /package/src/{EffectX/EffectX.ts → EffectX.ts} +0 -0
  125. /package/src/{FormDataX/FormDataX.ts → FormDataX.ts} +0 -0
  126. /package/src/{MapX/MapX.ts → MapX.ts} +0 -0
  127. /package/src/{NumberX/NumberX.ts → NumberX.ts} +0 -0
  128. /package/src/{OrderX/OrderX.ts → OrderX.ts} +0 -0
  129. /package/src/{PromiseX/PromiseX.ts → PromiseX.ts} +0 -0
  130. /package/src/{ResultX/ResultX.ts → ResultX.ts} +0 -0
  131. /package/src/{SchemaX/SchemaX.ts → SchemaX.ts} +0 -0
  132. /package/src/{SetX/SetX.ts → SetX.ts} +0 -0
  133. /package/src/{StructX/StructX.ts → StructX.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,16 +123,19 @@ 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
  | -------------- | -------------------------------------------------------------------------- |
100
- | `ArrayX` | Array helpers (grouping, ordered insertion, `WarnResult`-zip) |
132
+ | `ArrayX` | Array helpers (grouping, ordered insertion) |
101
133
  | `BigIntX` | BigInt helpers (`toNumberOrThrow`) |
102
134
  | `BooleanX` | Boolean helpers |
103
135
  | `DurationX` | Duration / DateTime diff helpers |
104
136
  | `EffectX` | Effect bridges (`flattenOption`, `fromOptionOrElse`, `tryUntil`) |
105
137
  | `FormDataX` | Schema-based `FormData` parsing |
138
+ | `InclusiveOr` | Inclusive-or of a `left` and/or `right` — terminology-free `WarnResult` |
106
139
  | `MapX` | Native `Map` helpers |
107
140
  | `NonNullableX` | Non-nullable assertions (`fromNullableOrThrow`, exported as `nn`) |
108
141
  | `NumberX` | Number helpers |
@@ -125,7 +158,9 @@ pnpm install
125
158
  pnpm tc # typecheck (src + tests)
126
159
  pnpm lint # ESLint (formatting included via eslint-plugin-prettier)
127
160
  pnpm test # vitest run
128
- pnpm build # emit dist/ (ESM + bundled .d.ts) via tsup
161
+ pnpm build # emit dist/ (one ESM file + .d.ts per module): tsc + babel
162
+ pnpm publint # validate packaging (exports map, types, ESM) — needs a build
163
+ pnpm treeshake # enforce per-function tree-shaking budgets (size-limit) — needs a build
129
164
  pnpm knip # unused code / deps
130
165
  pnpm check-all # all of the above, in CI order
131
166
  ```
@@ -0,0 +1,381 @@
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
+ /**
8
+ * Returns a shallow copy of `array` between `start` (inclusive) and `end`
9
+ * (exclusive), as a pipeable, dual-form alias for `Array.prototype.slice`.
10
+ *
11
+ * `Array.prototype.slice` is already non-mutating (it returns a shallow copy),
12
+ * but it isn't pipeable. This helper makes it composable inside `pipe(...)`
13
+ * chains alongside the rest of the codebase's Effect-style utilities.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { pipe } from "effect"
18
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
19
+ *
20
+ * // data-first
21
+ * assert.deepStrictEqual(ArrayX.slice([1, 2, 3, 4], 1, 3), [2, 3])
22
+ *
23
+ * // data-last (pipeable)
24
+ * assert.deepStrictEqual(pipe([1, 2, 3, 4], ArrayX.slice(1, 3)), [2, 3])
25
+ * ```
26
+ *
27
+ * @category getters
28
+ * @since 0.0.0
29
+ */
30
+ export declare const slice: (<A>(start: number, end: number) => (array: readonly A[]) => A[]) & (<A>(array: readonly A[], start: number, end: number) => A[]);
31
+ /**
32
+ * Inserts or moves a unique item in an array at a specified position.
33
+ *
34
+ * **Assumption**: Items should be unique in the array based on standard equality.
35
+ *
36
+ * **Happy case**: If the item doesn't exist in the array and the destination reference item is found:
37
+ * The new item is inserted before the destination reference item
38
+ *
39
+ * **Item not found in array**:
40
+ * - If destination reference item is found: The new item is inserted before the destination reference item
41
+ * - If destination reference item is not found: The new item is inserted at the end of the array
42
+ *
43
+ * **Item found but duplicated**:
44
+ * - If destination reference item is found: All existing copies are removed, then a single copy is inserted before the destination reference item
45
+ * - If destination reference item is not found: All existing copies are removed, then a single copy is inserted at the end of the array
46
+ *
47
+ * @param array - The input array to modify
48
+ * @param config - Configuration object containing:
49
+ * - `item`: The item to insert or update (must be a string or number)
50
+ * - `insertToBeLeftOf`: The item to position the new/updated item before,
51
+ * or null to insert at the end
52
+ *
53
+ * @returns A new array with the item inserted or moved to the specified position
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
58
+ *
59
+ * // Move an existing item to sit just before "c"
60
+ * assert.deepStrictEqual(
61
+ * ArrayX.insertUniq(["a", "b", "c", "d"], { item: "a", insertToBeLeftOf: "c" }),
62
+ * ["b", "a", "c", "d"],
63
+ * )
64
+ *
65
+ * // Insert a brand-new item; unknown destination falls through to the end
66
+ * assert.deepStrictEqual(
67
+ * ArrayX.insertUniq(["a", "b"], { item: "new", insertToBeLeftOf: null }),
68
+ * ["a", "b", "new"],
69
+ * )
70
+ * ```
71
+ *
72
+ * @category combinators
73
+ * @since 0.0.0
74
+ */
75
+ export declare const insertUniq: (<A extends string | number>(config: {
76
+ item: A;
77
+ insertToBeLeftOf: A | null;
78
+ }) => (array: readonly A[] | A[]) => A[]) & (<A extends string | number>(array: readonly A[] | A[], config: {
79
+ item: A;
80
+ insertToBeLeftOf: A | null;
81
+ }) => A[]);
82
+ /**
83
+ * Maps over `array` while threading an accumulator, iterating from right to
84
+ * left instead of left to right.
85
+ *
86
+ * Identical to `Array.mapAccum`, except the traversal order is reversed: `f` is
87
+ * called on the last element first, and the resulting array is returned in the
88
+ * original (left-to-right) order. Use it when each element's mapped value
89
+ * depends on state accumulated from the elements that follow it.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
94
+ *
95
+ * // Running suffix-sum: each slot holds the sum of itself and everything after it
96
+ * assert.deepStrictEqual(
97
+ * ArrayX.mapRightAccum([1, 2, 3], 0, (total, n) => [total + n, total + n]),
98
+ * [6, [6, 5, 3]],
99
+ * )
100
+ * ```
101
+ *
102
+ * @category folding
103
+ * @since 0.0.0
104
+ */
105
+ 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[]]);
106
+ /**
107
+ * Returns the maximum element of `array` according to `order`, wrapped in an
108
+ * `Option` so that empty arrays are handled safely.
109
+ *
110
+ * Effect's `Array.max` throws on an empty array; this returns `Option.none()`
111
+ * instead, and `Option.some(max)` otherwise. Reach for it whenever the input
112
+ * array might be empty.
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * import { Option, Order, pipe } from "effect"
117
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
118
+ *
119
+ * assert.deepStrictEqual(
120
+ * pipe([3, 7, 2], ArrayX.maxOption(Order.Number)),
121
+ * Option.some(7),
122
+ * )
123
+ * assert.deepStrictEqual(
124
+ * pipe([], ArrayX.maxOption(Order.Number)),
125
+ * Option.none(),
126
+ * )
127
+ * ```
128
+ *
129
+ * @category getters
130
+ * @since 0.0.0
131
+ */
132
+ export declare const maxOption: (<A>(order: Order.Order<A>) => (array: A[]) => Option.Option<A>) & (<A>(array: A[], order: Order.Order<A>) => Option.Option<A>);
133
+ /**
134
+ * Returns the smallest element of `array` (per `order`) that matches
135
+ * `predicate`, narrowed to the refined type `B`, or `Option.none()` if none
136
+ * match.
137
+ *
138
+ * Combines a refinement filter with `Array.min`: only elements satisfying
139
+ * `predicate` are considered, and the minimum of those (by `order`) is
140
+ * returned. The refinement narrows the element type, so the resulting `Option`
141
+ * carries the more specific `B`.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * import { Option, Order, pipe } from "effect"
146
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
147
+ *
148
+ * const isEven = (n: number): n is number => n % 2 === 0
149
+ *
150
+ * assert.deepStrictEqual(
151
+ * pipe([3, 4, 1, 2, 5], ArrayX.takeFirstWhere(isEven, Order.Number)),
152
+ * Option.some(2),
153
+ * )
154
+ * assert.deepStrictEqual(
155
+ * pipe([1, 3, 5], ArrayX.takeFirstWhere(isEven, Order.Number)),
156
+ * Option.none(),
157
+ * )
158
+ * ```
159
+ *
160
+ * @category getters
161
+ * @since 0.0.0
162
+ */
163
+ 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>);
164
+ /**
165
+ * Returns the largest element of `array` (per `order`) that matches
166
+ * `predicate`, narrowed to the refined type `B`, or `Option.none()` if none
167
+ * match.
168
+ *
169
+ * The mirror of {@link takeFirstWhere}: only elements satisfying `predicate`
170
+ * are considered, and the maximum of those (by `order`) is returned. The
171
+ * refinement narrows the element type, so the resulting `Option` carries the
172
+ * more specific `B`.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * import { Option, Order, pipe } from "effect"
177
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
178
+ *
179
+ * const isEven = (n: number): n is number => n % 2 === 0
180
+ *
181
+ * assert.deepStrictEqual(
182
+ * pipe([3, 4, 1, 2, 5], ArrayX.takeLastWhere(isEven, Order.Number)),
183
+ * Option.some(4),
184
+ * )
185
+ * assert.deepStrictEqual(
186
+ * pipe([1, 3, 5], ArrayX.takeLastWhere(isEven, Order.Number)),
187
+ * Option.none(),
188
+ * )
189
+ * ```
190
+ *
191
+ * @category getters
192
+ * @since 0.0.0
193
+ */
194
+ 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>);
195
+ /**
196
+ * Groups `items` into a partial record keyed by the category each item maps to
197
+ * via `categorize`.
198
+ *
199
+ * Each item is appended to the array under its category, preserving input
200
+ * order. The result is `Partial<Record<C, A[]>>` because not every possible
201
+ * category `C` is guaranteed to appear — only categories that received at least
202
+ * one item are present.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
207
+ *
208
+ * const parity = (n: number) => (n % 2 === 0 ? "even" : "odd")
209
+ *
210
+ * assert.deepStrictEqual(ArrayX.categorize([1, 2, 3, 4], parity), {
211
+ * odd: [1, 3],
212
+ * even: [2, 4],
213
+ * })
214
+ * ```
215
+ *
216
+ * @category folding
217
+ * @since 0.0.0
218
+ */
219
+ export declare const categorize: <A, C extends string>(items: Iterable<A>, categorize: (a: A) => C) => Partial<Record<C, A[]>>;
220
+ /**
221
+ * Removes all `null` and `undefined` elements from `array`, narrowing the
222
+ * element type to `NonNullable<A>`.
223
+ *
224
+ * Falsy-but-present values such as `0` and `""` are kept — only nullish values
225
+ * are dropped. Use it to clean up an array of optionals into a dense array of
226
+ * known-present values.
227
+ *
228
+ * @example
229
+ * ```ts
230
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
231
+ *
232
+ * assert.deepStrictEqual(
233
+ * ArrayX.compactNullable([1, null, 2, undefined, 0, ""]),
234
+ * [1, 2, 0, ""],
235
+ * )
236
+ * ```
237
+ *
238
+ * @category filtering
239
+ * @since 0.0.0
240
+ */
241
+ export declare const compactNullable: <A>(array: A[]) => NonNullable<A>[];
242
+ /**
243
+ * Drops the leading elements of `array` until `predicate` first holds, keeping
244
+ * everything from the first match onward.
245
+ *
246
+ * The first matching element and all subsequent elements are retained
247
+ * regardless of whether they match — only the prefix *before* the first match
248
+ * is trimmed. If nothing matches, returns an empty array.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * import { Predicate } from "effect"
253
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
254
+ *
255
+ * // Trims the leading strings, then keeps everything (including the trailing "b")
256
+ * assert.deepStrictEqual(
257
+ * ArrayX.filterHead(["a", 1, 2, "b"], Predicate.isNumber),
258
+ * [1, 2, "b"],
259
+ * )
260
+ * ```
261
+ *
262
+ * @category filtering
263
+ * @since 0.0.0
264
+ */
265
+ export declare const filterHead: (<A>(predicate: Predicate.Predicate<A>) => (array: A[]) => A[]) & (<A>(array: A[], predicate: Predicate.Predicate<A>) => A[]);
266
+ /**
267
+ * Drops the trailing elements of `array` after `predicate` last holds, keeping
268
+ * everything up to and including the last match.
269
+ *
270
+ * The mirror of {@link filterHead}: the last matching element and all preceding
271
+ * elements are retained regardless of whether they match — only the suffix
272
+ * *after* the last match is trimmed. If nothing matches, returns an empty
273
+ * array.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * import { Predicate } from "effect"
278
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
279
+ *
280
+ * // Keeps the leading "a" and trims the trailing strings after the last number
281
+ * assert.deepStrictEqual(
282
+ * ArrayX.filterTail(["a", 1, 2, "b"], Predicate.isNumber),
283
+ * ["a", 1, 2],
284
+ * )
285
+ * ```
286
+ *
287
+ * @category filtering
288
+ * @since 0.0.0
289
+ */
290
+ export declare const filterTail: (<A>(predicate: Predicate.Predicate<A>) => (array: A[]) => A[]) & (<A>(array: A[], predicate: Predicate.Predicate<A>) => A[]);
291
+ /**
292
+ * Maps `f` over `array` and drops every result that is `null` or `undefined`,
293
+ * narrowing the element type to `NonNullable<B>`.
294
+ *
295
+ * A nullable-friendly `Array.filterMap`: where `filterMap` expects `f` to
296
+ * return an `Option`, this accepts a function returning `B | null` (or
297
+ * `undefined`) and treats nullish results as "skip this element". Falsy-but-
298
+ * present values such as `0` and `""` are kept.
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
303
+ *
304
+ * // Keep only the even numbers, mapped to their halves
305
+ * assert.deepStrictEqual(
306
+ * ArrayX.filterMapNullable([1, 2, 3, 4], (n) => (n % 2 === 0 ? n / 2 : null)),
307
+ * [1, 2],
308
+ * )
309
+ * ```
310
+ *
311
+ * @category filtering
312
+ * @since 0.0.0
313
+ */
314
+ 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>[]);
315
+ /**
316
+ * Finds the first element of a 2-dimensional array (row-major order) matching
317
+ * `predicate`, returning it alongside its row and column indices.
318
+ *
319
+ * Scans rows top-to-bottom and, within each row, left-to-right. On a match
320
+ * returns `Option.some([value, rowIndex, columnIndex])`; if no element matches
321
+ * (or the grid is empty), returns `Option.none()`.
322
+ *
323
+ * @example
324
+ * ```ts
325
+ * import { Option } from "effect"
326
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
327
+ *
328
+ * const grid = [
329
+ * ["A", "B", "C"],
330
+ * ["D", "E", "F"],
331
+ * ]
332
+ *
333
+ * assert.deepStrictEqual(
334
+ * ArrayX.findFirstWithIndex2d(grid, (cell) => cell === "E"),
335
+ * Option.some(["E", 1, 1]),
336
+ * )
337
+ * ```
338
+ *
339
+ * @category getters
340
+ * @since 0.0.0
341
+ */
342
+ 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]>);
343
+ /**
344
+ * Splits `array` into runs of consecutive elements that share the same group
345
+ * value, where the group is derived by `chunk` and compared with the provided
346
+ * `Equivalence`.
347
+ *
348
+ * Only *adjacent* elements are grouped: a new run starts every time the group
349
+ * value changes from the previous element. Each entry in the result carries the
350
+ * `group` value and the non-empty array of `values` that produced it, preserving
351
+ * input order. An empty input yields an empty array. Use it for run-length-style
352
+ * segmentation; reach for `Array.groupBy` instead when you want all elements
353
+ * with the same key collapsed regardless of position.
354
+ *
355
+ * @example
356
+ * ```ts
357
+ * import { Equivalence } from "effect"
358
+ * import { ArrayX } from "@nunofyobiz/effect-extras"
359
+ *
360
+ * // Group adjacent numbers by parity
361
+ * assert.deepStrictEqual(
362
+ * ArrayX.chunkBy([2, 4, 1, 3, 6], (n) => n % 2 === 0, Equivalence.Boolean),
363
+ * [
364
+ * { group: true, values: [2, 4] },
365
+ * { group: false, values: [1, 3] },
366
+ * { group: true, values: [6] },
367
+ * ],
368
+ * )
369
+ * ```
370
+ *
371
+ * @category folding
372
+ * @since 0.0.0
373
+ */
374
+ export declare const chunkBy: (<A, B>(chunk: (a: A) => B, GroupEquivalence: Equivalence.Equivalence<B>) => (array: A[]) => {
375
+ group: B;
376
+ values: Array.NonEmptyArray<A>;
377
+ }[]) & (<A, B>(array: A[], chunk: (a: A) => B, GroupEquivalence: Equivalence.Equivalence<B>) => {
378
+ group: B;
379
+ values: Array.NonEmptyArray<A>;
380
+ }[]);
381
+ //# 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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"}