@nlozgachev/pipelined 0.37.0 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as Maybe, q as WithValue, k as WithLog, D as Deferred, R as Result, j as WithKind, g as WithError, c as RetryOptions, d as TimeoutOptions, p as WithTimeout, l as WithMinInterval, e as WithCooldown, W as WithConcurrency, o as WithSize, f as WithDuration, m as WithN, T as Task, h as WithErrors, i as WithFirst, n as WithSecond } from './Task-DIgwvoIb.js';
2
- export { E as Equality, a as Err, N as None, O as Ok, b as Ordering, S as Some } from './Task-DIgwvoIb.js';
1
+ import { M as Maybe, q as WithValue, k as WithLog, D as Deferred, R as Result, j as WithKind, g as WithError, c as RetryOptions, d as TimeoutOptions, p as WithTimeout, l as WithMinInterval, e as WithCooldown, W as WithConcurrency, o as WithSize, f as WithDuration, m as WithN, T as Task, i as WithFirst, n as WithSecond, h as WithErrors } from './Task-uupX7xd9.js';
2
+ export { E as Equality, a as Err, N as None, O as Ok, b as Ordering, S as Some } from './Task-uupX7xd9.js';
3
3
  import { Duration, NonEmptyList } from './types.js';
4
4
 
5
5
  /**
@@ -166,161 +166,6 @@ declare namespace Lazy {
166
166
  const tap: <A>(f: (a: A) => void) => (lazy: Lazy<A>) => Lazy<A>;
167
167
  }
168
168
 
169
- /** Keys of T for which undefined is assignable (i.e. optional fields). */
170
- type OptionalKeys<T> = {
171
- [K in keyof T]-?: undefined extends T[K] ? K : never;
172
- }[keyof T];
173
- /**
174
- * Optional<S, A> focuses on a value A inside a structure S that may or may
175
- * not be present. Like a Lens, but get returns Maybe<A>.
176
- *
177
- * Compose with other Optionals via `andThen`, or with a Lens via `andThenLens`.
178
- * Convert a Lens to an Optional with `Lens.toOptional`.
179
- *
180
- * @example
181
- * ```ts
182
- * type Profile = { username: string; bio?: string };
183
- *
184
- * const bioOpt = Optional.prop<Profile>()("bio");
185
- *
186
- * pipe(profile, Optional.get(bioOpt)); // Some("hello") or None
187
- * pipe(profile, Optional.set(bioOpt)("hello")); // new Profile with bio set
188
- * pipe(profile, Optional.modify(bioOpt)(s => s + "!")); // appends if present
189
- * ```
190
- */
191
- type Optional<S, A> = {
192
- readonly get: (s: S) => Maybe<A>;
193
- readonly set: (a: A) => (s: S) => S;
194
- };
195
- declare namespace Optional {
196
- /**
197
- * Constructs an Optional from a getter (returning Maybe<A>) and a setter.
198
- *
199
- * @example
200
- * ```ts
201
- * const firstChar = Optional.make(
202
- * (s: string) => s.length > 0 ? Maybe.some(s[0]) : Maybe.none(),
203
- * (c) => (s) => s.length > 0 ? c + s.slice(1) : s,
204
- * );
205
- * ```
206
- */
207
- const make: <S, A>(get: (s: S) => Maybe<A>, set: (a: A) => (s: S) => S) => Optional<S, A>;
208
- /**
209
- * Creates an Optional that focuses on an optional property of an object.
210
- * Only keys whose type includes undefined (i.e. `field?: T`) are accepted.
211
- * Call with the structure type first, then the key.
212
- *
213
- * @example
214
- * ```ts
215
- * type Profile = { username: string; bio?: string };
216
- * const bioOpt = Optional.prop<Profile>()("bio");
217
- * ```
218
- */
219
- const prop: <S>() => <K extends OptionalKeys<S>>(key: K) => Optional<S, NonNullable<S[K]>>;
220
- /**
221
- * Creates an Optional that focuses on an element at a given index in an array.
222
- * Returns None when the index is out of bounds; set is a no-op when out of bounds.
223
- *
224
- * @example
225
- * ```ts
226
- * const firstItem = Optional.index<string>(0);
227
- *
228
- * pipe(["a", "b"], Optional.get(firstItem)); // Some("a")
229
- * pipe([], Optional.get(firstItem)); // None
230
- * ```
231
- */
232
- const index: <A>(i: number) => Optional<A[], A>;
233
- /**
234
- * Reads the focused value from a structure, returning Maybe<A>.
235
- *
236
- * @example
237
- * ```ts
238
- * pipe(profile, Optional.get(bioOpt)); // Some("...") or None
239
- * ```
240
- */
241
- const get: <S, A>(opt: Optional<S, A>) => (s: S) => Maybe<A>;
242
- /**
243
- * Replaces the focused value within a structure.
244
- * For indexed focuses, this is a no-op when the index is out of bounds.
245
- *
246
- * @example
247
- * ```ts
248
- * pipe(profile, Optional.set(bioOpt)("hello"));
249
- * ```
250
- */
251
- const set: <S, A>(opt: Optional<S, A>) => (a: A) => (s: S) => S;
252
- /**
253
- * Applies a function to the focused value if it is present; returns the
254
- * structure unchanged if the focus is absent.
255
- *
256
- * @example
257
- * ```ts
258
- * pipe(profile, Optional.modify(bioOpt)(s => s.toUpperCase()));
259
- * ```
260
- */
261
- const modify: <S, A>(opt: Optional<S, A>) => (f: (a: A) => A) => (s: S) => S;
262
- /**
263
- * Returns the focused value or a default when the focus is absent.
264
- *
265
- * @example
266
- * ```ts
267
- * pipe(profile, Optional.getOrElse(bioOpt)(() => "no bio"));
268
- * ```
269
- */
270
- const getOrElse: <S, A>(opt: Optional<S, A>) => (defaultValue: () => A) => (s: S) => A;
271
- /**
272
- * Extracts a value from an Optional focus using handlers for the present
273
- * and absent cases.
274
- *
275
- * @example
276
- * ```ts
277
- * pipe(profile, Optional.fold(bioOpt)(() => "no bio", (bio) => bio.toUpperCase()));
278
- * ```
279
- */
280
- const fold: <S, A>(opt: Optional<S, A>) => <B>(onNone: () => B, onSome: (a: A) => B) => (s: S) => B;
281
- /**
282
- * Pattern matches on an Optional focus using a named-case object.
283
- *
284
- * @example
285
- * ```ts
286
- * pipe(
287
- * profile,
288
- * Optional.match(bioOpt)({ none: () => "no bio", some: (bio) => bio }),
289
- * );
290
- * ```
291
- */
292
- const match: <S, A>(opt: Optional<S, A>) => <B>(cases: {
293
- none: () => B;
294
- some: (a: A) => B;
295
- }) => (s: S) => B;
296
- /**
297
- * Composes two Optionals: focuses through the outer, then through the inner.
298
- * Returns None if either focus is absent.
299
- *
300
- * @example
301
- * ```ts
302
- * const deepOpt = pipe(
303
- * Optional.prop<User>()("address"),
304
- * Optional.andThen(Optional.prop<Address>()("landmark")),
305
- * );
306
- * ```
307
- */
308
- const andThen: <A, B>(inner: Optional<A, B>) => <S>(outer: Optional<S, A>) => Optional<S, B>;
309
- /**
310
- * Composes an Optional with a Lens, producing an Optional.
311
- * The Lens focuses within the value found by the Optional.
312
- *
313
- * @example
314
- * ```ts
315
- * const cityOpt = pipe(
316
- * Optional.prop<User>()("address"),
317
- * Optional.andThenLens(Lens.prop<Address>()("city")),
318
- * );
319
- * ```
320
- */
321
- const andThenLens: <A, B>(inner: Lens<A, B>) => <S>(outer: Optional<S, A>) => Optional<S, B>;
322
- }
323
-
324
169
  /**
325
170
  * Lens<S, A> focuses on a single value A inside a structure S, providing
326
171
  * a composable way to read and immutably update nested data.
@@ -1286,140 +1131,159 @@ declare namespace Op {
1286
1131
  function interpret<I, E, A, O extends AllInterpretOptions<I, E>>(op: Op<I, E, A>, options: O): InterpretResult<I, E, A, O>;
1287
1132
  }
1288
1133
 
1134
+ /** Keys of T for which undefined is assignable (i.e. optional fields). */
1135
+ type OptionalKeys<T> = {
1136
+ [K in keyof T]-?: undefined extends T[K] ? K : never;
1137
+ }[keyof T];
1289
1138
  /**
1290
- * A function from `A` to `A is B` — a type predicate paired with a runtime check.
1139
+ * Optional<S, A> focuses on a value A inside a structure S that may or may
1140
+ * not be present. Like a Lens, but get returns Maybe<A>.
1291
1141
  *
1292
- * A `Refinement<A, B>` proves at compile time that a value of type `A` is actually
1293
- * the narrower type `B extends A`, backed by a runtime boolean test. Use it to
1294
- * express domain invariants (non-empty strings, positive numbers, valid emails) as
1295
- * first-class, composable values rather than one-off type guards scattered across
1296
- * the codebase.
1142
+ * Compose with other Optionals via `andThen`, or with a Lens via `andThenLens`.
1143
+ * Convert a Lens to an Optional with `Lens.toOptional`.
1297
1144
  *
1298
1145
  * @example
1299
1146
  * ```ts
1300
- * type NonEmptyString = string & { readonly _tag: "NonEmptyString" };
1147
+ * type Profile = { username: string; bio?: string };
1301
1148
  *
1302
- * const isNonEmpty: Refinement<string, NonEmptyString> =
1303
- * Refinement.make(s => s.length > 0);
1149
+ * const bioOpt = Optional.prop<Profile>()("bio");
1304
1150
  *
1305
- * pipe(
1306
- * "hello",
1307
- * Refinement.toFilter(isNonEmpty)
1308
- * ); // Some("hello")
1151
+ * pipe(profile, Optional.get(bioOpt)); // Some("hello") or None
1152
+ * pipe(profile, Optional.set(bioOpt)("hello")); // new Profile with bio set
1153
+ * pipe(profile, Optional.modify(bioOpt)(s => s + "!")); // appends if present
1309
1154
  * ```
1310
1155
  */
1311
- type Refinement<A, B extends A> = (a: A) => a is B;
1312
- declare namespace Refinement {
1156
+ type Optional<S, A> = {
1157
+ readonly get: (s: S) => Maybe<A>;
1158
+ readonly set: (a: A) => (s: S) => S;
1159
+ };
1160
+ declare namespace Optional {
1313
1161
  /**
1314
- * Creates a `Refinement<A, B>` from a plain boolean predicate.
1315
- *
1316
- * This is an unsafe cast — the caller is responsible for ensuring that the
1317
- * predicate truly characterises values of type `B`. Use this only when
1318
- * bootstrapping a new refinement; prefer `compose`, `and`, or `or` to build
1319
- * derived refinements from existing ones.
1162
+ * Constructs an Optional from a getter (returning Maybe<A>) and a setter.
1320
1163
  *
1321
1164
  * @example
1322
1165
  * ```ts
1323
- * type PositiveNumber = number & { readonly _tag: "PositiveNumber" };
1324
- *
1325
- * const isPositive: Refinement<number, PositiveNumber> =
1326
- * Refinement.make(n => n > 0);
1166
+ * const firstChar = Optional.make(
1167
+ * (s: string) => s.length > 0 ? Maybe.some(s[0]) : Maybe.none(),
1168
+ * (c) => (s) => s.length > 0 ? c + s.slice(1) : s,
1169
+ * );
1327
1170
  * ```
1328
1171
  */
1329
- const make: <A, B extends A>(f: (a: A) => boolean) => Refinement<A, B>;
1172
+ const make: <S, A>(get: (s: S) => Maybe<A>, set: (a: A) => (s: S) => S) => Optional<S, A>;
1330
1173
  /**
1331
- * Chains two refinements: if `ab` narrows `A` to `B` and `bc` narrows `B` to `C`,
1332
- * the result narrows `A` directly to `C`.
1333
- *
1334
- * Data-last — the first refinement `ab` is the data being piped.
1174
+ * Creates an Optional that focuses on an optional property of an object.
1175
+ * Only keys whose type includes undefined (i.e. `field?: T`) are accepted.
1176
+ * Call with the structure type first, then the key.
1335
1177
  *
1336
1178
  * @example
1337
1179
  * ```ts
1338
- * type NonEmptyString = string & { readonly _tag: "NonEmpty" };
1339
- * type TrimmedString = NonEmptyString & { readonly _tag: "Trimmed" };
1340
- *
1341
- * const isNonEmpty: Refinement<string, NonEmptyString> =
1342
- * Refinement.make(s => s.length > 0);
1343
- * const isTrimmed: Refinement<NonEmptyString, TrimmedString> =
1344
- * Refinement.make(s => s === s.trim());
1345
- *
1346
- * const isNonEmptyTrimmed: Refinement<string, TrimmedString> = pipe(
1347
- * isNonEmpty,
1348
- * Refinement.compose(isTrimmed)
1349
- * );
1180
+ * type Profile = { username: string; bio?: string };
1181
+ * const bioOpt = Optional.prop<Profile>()("bio");
1350
1182
  * ```
1351
1183
  */
1352
- const compose: <A, B extends A, C extends B>(bc: Refinement<B, C>) => (ab: Refinement<A, B>) => Refinement<A, C>;
1184
+ const prop: <S>() => <K extends OptionalKeys<S>>(key: K) => Optional<S, NonNullable<S[K]>>;
1353
1185
  /**
1354
- * Intersects two refinements: the result narrows `A` to `B & C`, passing only
1355
- * when both refinements hold simultaneously.
1356
- *
1357
- * Data-last — the first refinement is the data being piped.
1186
+ * Creates an Optional that focuses on an element at a given index in an array.
1187
+ * Returns None when the index is out of bounds; set is a no-op when out of bounds.
1358
1188
  *
1359
1189
  * @example
1360
1190
  * ```ts
1361
- * const isString: Refinement<unknown, string> = Refinement.make(x => typeof x === "string");
1362
- * const isNonEmpty: Refinement<unknown, { length: number }> =
1363
- * Refinement.make(x => (x as any).length > 0);
1191
+ * const firstItem = Optional.index<string>(0);
1364
1192
  *
1365
- * const isNonEmptyString = pipe(isString, Refinement.and(isNonEmpty));
1366
- * isNonEmptyString("hi"); // true
1367
- * isNonEmptyString(""); // false
1193
+ * pipe(["a", "b"], Optional.get(firstItem)); // Some("a")
1194
+ * pipe([], Optional.get(firstItem)); // None
1368
1195
  * ```
1369
1196
  */
1370
- const and: <A, C extends A>(second: Refinement<A, C>) => <B extends A>(first: Refinement<A, B>) => Refinement<A, B & C>;
1197
+ const index: <A>(i: number) => Optional<A[], A>;
1371
1198
  /**
1372
- * Unions two refinements: the result narrows `A` to `B | C`, passing when either
1373
- * refinement holds.
1374
- *
1375
- * Data-last — the first refinement is the data being piped.
1199
+ * Reads the focused value from a structure, returning Maybe<A>.
1376
1200
  *
1377
1201
  * @example
1378
1202
  * ```ts
1379
- * const isString: Refinement<unknown, string> = Refinement.make(x => typeof x === "string");
1380
- * const isNumber: Refinement<unknown, number> = Refinement.make(x => typeof x === "number");
1381
- *
1382
- * const isStringOrNumber = pipe(isString, Refinement.or(isNumber));
1383
- * isStringOrNumber("hi"); // true
1384
- * isStringOrNumber(42); // true
1385
- * isStringOrNumber(true); // false
1203
+ * pipe(profile, Optional.get(bioOpt)); // Some("...") or None
1386
1204
  * ```
1387
1205
  */
1388
- const or: <A, C extends A>(second: Refinement<A, C>) => <B extends A>(first: Refinement<A, B>) => Refinement<A, B | C>;
1206
+ const get: <S, A>(opt: Optional<S, A>) => (s: S) => Maybe<A>;
1389
1207
  /**
1390
- * Converts a `Refinement<A, B>` into a function `(a: A) => Maybe<B>`.
1208
+ * Replaces the focused value within a structure.
1209
+ * For indexed focuses, this is a no-op when the index is out of bounds.
1391
1210
  *
1392
- * Returns `Some(a)` when the refinement holds, `None` otherwise. Useful for
1393
- * integrating runtime validation into a `Maybe`-based pipeline.
1211
+ * @example
1212
+ * ```ts
1213
+ * pipe(profile, Optional.set(bioOpt)("hello"));
1214
+ * ```
1215
+ */
1216
+ const set: <S, A>(opt: Optional<S, A>) => (a: A) => (s: S) => S;
1217
+ /**
1218
+ * Applies a function to the focused value if it is present; returns the
1219
+ * structure unchanged if the focus is absent.
1394
1220
  *
1395
1221
  * @example
1396
1222
  * ```ts
1397
- * type PositiveNumber = number & { readonly _tag: "Positive" };
1398
- * const isPositive: Refinement<number, PositiveNumber> =
1399
- * Refinement.make(n => n > 0);
1223
+ * pipe(profile, Optional.modify(bioOpt)(s => s.toUpperCase()));
1224
+ * ```
1225
+ */
1226
+ const modify: <S, A>(opt: Optional<S, A>) => (f: (a: A) => A) => (s: S) => S;
1227
+ /**
1228
+ * Returns the focused value or a default when the focus is absent.
1400
1229
  *
1401
- * pipe(-1, Refinement.toFilter(isPositive)); // None
1402
- * pipe(42, Refinement.toFilter(isPositive)); // Some(42)
1230
+ * @example
1231
+ * ```ts
1232
+ * pipe(profile, Optional.getOrElse(bioOpt)(() => "no bio"));
1403
1233
  * ```
1404
1234
  */
1405
- const toFilter: <A, B extends A>(r: Refinement<A, B>) => (a: A) => Maybe<B>;
1235
+ const getOrElse: <S, A>(opt: Optional<S, A>) => (defaultValue: () => A) => (s: S) => A;
1406
1236
  /**
1407
- * Converts a `Refinement<A, B>` into a function `(a: A) => Result<E, B>`.
1237
+ * Extracts a value from an Optional focus using handlers for the present
1238
+ * and absent cases.
1408
1239
  *
1409
- * Returns `Ok(a)` when the refinement holds, `Err(onFail(a))` otherwise. Use
1410
- * this to surface validation failures as typed errors inside a `Result` pipeline.
1240
+ * @example
1241
+ * ```ts
1242
+ * pipe(profile, Optional.fold(bioOpt)(() => "no bio", (bio) => bio.toUpperCase()));
1243
+ * ```
1244
+ */
1245
+ const fold: <S, A>(opt: Optional<S, A>) => <B>(onNone: () => B, onSome: (a: A) => B) => (s: S) => B;
1246
+ /**
1247
+ * Pattern matches on an Optional focus using a named-case object.
1411
1248
  *
1412
1249
  * @example
1413
1250
  * ```ts
1414
- * type NonEmptyString = string & { readonly _tag: "NonEmpty" };
1415
- * const isNonEmpty: Refinement<string, NonEmptyString> =
1416
- * Refinement.make(s => s.length > 0);
1251
+ * pipe(
1252
+ * profile,
1253
+ * Optional.match(bioOpt)({ none: () => "no bio", some: (bio) => bio }),
1254
+ * );
1255
+ * ```
1256
+ */
1257
+ const match: <S, A>(opt: Optional<S, A>) => <B>(cases: {
1258
+ none: () => B;
1259
+ some: (a: A) => B;
1260
+ }) => (s: S) => B;
1261
+ /**
1262
+ * Composes two Optionals: focuses through the outer, then through the inner.
1263
+ * Returns None if either focus is absent.
1417
1264
  *
1418
- * pipe("", Refinement.toResult(isNonEmpty, () => "must not be empty")); // Err(...)
1419
- * pipe("hi", Refinement.toResult(isNonEmpty, () => "must not be empty")); // Ok("hi")
1265
+ * @example
1266
+ * ```ts
1267
+ * const deepOpt = pipe(
1268
+ * Optional.prop<User>()("address"),
1269
+ * Optional.andThen(Optional.prop<Address>()("landmark")),
1270
+ * );
1420
1271
  * ```
1421
1272
  */
1422
- const toResult: <A, B extends A, E>(r: Refinement<A, B>, onFail: (a: A) => E) => (a: A) => Result<E, B>;
1273
+ const andThen: <A, B>(inner: Optional<A, B>) => <S>(outer: Optional<S, A>) => Optional<S, B>;
1274
+ /**
1275
+ * Composes an Optional with a Lens, producing an Optional.
1276
+ * The Lens focuses within the value found by the Optional.
1277
+ *
1278
+ * @example
1279
+ * ```ts
1280
+ * const cityOpt = pipe(
1281
+ * Optional.prop<User>()("address"),
1282
+ * Optional.andThenLens(Lens.prop<Address>()("city")),
1283
+ * );
1284
+ * ```
1285
+ */
1286
+ const andThenLens: <A, B>(inner: Lens<A, B>) => <S>(outer: Optional<S, A>) => Optional<S, B>;
1423
1287
  }
1424
1288
 
1425
1289
  /**
@@ -1759,6 +1623,142 @@ declare namespace Reader {
1759
1623
  const bind: <K extends string, R, A, B>(key: K, f: (a: A) => Reader<R, B>) => (data: Reader<R, A>) => Reader<R, A & { [P in K]: B; }>;
1760
1624
  }
1761
1625
 
1626
+ /**
1627
+ * A function from `A` to `A is B` — a type predicate paired with a runtime check.
1628
+ *
1629
+ * A `Refinement<A, B>` proves at compile time that a value of type `A` is actually
1630
+ * the narrower type `B extends A`, backed by a runtime boolean test. Use it to
1631
+ * express domain invariants (non-empty strings, positive numbers, valid emails) as
1632
+ * first-class, composable values rather than one-off type guards scattered across
1633
+ * the codebase.
1634
+ *
1635
+ * @example
1636
+ * ```ts
1637
+ * type NonEmptyString = string & { readonly _tag: "NonEmptyString" };
1638
+ *
1639
+ * const isNonEmpty: Refinement<string, NonEmptyString> =
1640
+ * Refinement.make(s => s.length > 0);
1641
+ *
1642
+ * pipe(
1643
+ * "hello",
1644
+ * Refinement.toFilter(isNonEmpty)
1645
+ * ); // Some("hello")
1646
+ * ```
1647
+ */
1648
+ type Refinement<A, B extends A> = (a: A) => a is B;
1649
+ declare namespace Refinement {
1650
+ /**
1651
+ * Creates a `Refinement<A, B>` from a plain boolean predicate.
1652
+ *
1653
+ * This is an unsafe cast — the caller is responsible for ensuring that the
1654
+ * predicate truly characterises values of type `B`. Use this only when
1655
+ * bootstrapping a new refinement; prefer `compose`, `and`, or `or` to build
1656
+ * derived refinements from existing ones.
1657
+ *
1658
+ * @example
1659
+ * ```ts
1660
+ * type PositiveNumber = number & { readonly _tag: "PositiveNumber" };
1661
+ *
1662
+ * const isPositive: Refinement<number, PositiveNumber> =
1663
+ * Refinement.make(n => n > 0);
1664
+ * ```
1665
+ */
1666
+ const make: <A, B extends A>(f: (a: A) => boolean) => Refinement<A, B>;
1667
+ /**
1668
+ * Chains two refinements: if `ab` narrows `A` to `B` and `bc` narrows `B` to `C`,
1669
+ * the result narrows `A` directly to `C`.
1670
+ *
1671
+ * Data-last — the first refinement `ab` is the data being piped.
1672
+ *
1673
+ * @example
1674
+ * ```ts
1675
+ * type NonEmptyString = string & { readonly _tag: "NonEmpty" };
1676
+ * type TrimmedString = NonEmptyString & { readonly _tag: "Trimmed" };
1677
+ *
1678
+ * const isNonEmpty: Refinement<string, NonEmptyString> =
1679
+ * Refinement.make(s => s.length > 0);
1680
+ * const isTrimmed: Refinement<NonEmptyString, TrimmedString> =
1681
+ * Refinement.make(s => s === s.trim());
1682
+ *
1683
+ * const isNonEmptyTrimmed: Refinement<string, TrimmedString> = pipe(
1684
+ * isNonEmpty,
1685
+ * Refinement.compose(isTrimmed)
1686
+ * );
1687
+ * ```
1688
+ */
1689
+ const compose: <A, B extends A, C extends B>(bc: Refinement<B, C>) => (ab: Refinement<A, B>) => Refinement<A, C>;
1690
+ /**
1691
+ * Intersects two refinements: the result narrows `A` to `B & C`, passing only
1692
+ * when both refinements hold simultaneously.
1693
+ *
1694
+ * Data-last — the first refinement is the data being piped.
1695
+ *
1696
+ * @example
1697
+ * ```ts
1698
+ * const isString: Refinement<unknown, string> = Refinement.make(x => typeof x === "string");
1699
+ * const isNonEmpty: Refinement<unknown, { length: number }> =
1700
+ * Refinement.make(x => (x as any).length > 0);
1701
+ *
1702
+ * const isNonEmptyString = pipe(isString, Refinement.and(isNonEmpty));
1703
+ * isNonEmptyString("hi"); // true
1704
+ * isNonEmptyString(""); // false
1705
+ * ```
1706
+ */
1707
+ const and: <A, C extends A>(second: Refinement<A, C>) => <B extends A>(first: Refinement<A, B>) => Refinement<A, B & C>;
1708
+ /**
1709
+ * Unions two refinements: the result narrows `A` to `B | C`, passing when either
1710
+ * refinement holds.
1711
+ *
1712
+ * Data-last — the first refinement is the data being piped.
1713
+ *
1714
+ * @example
1715
+ * ```ts
1716
+ * const isString: Refinement<unknown, string> = Refinement.make(x => typeof x === "string");
1717
+ * const isNumber: Refinement<unknown, number> = Refinement.make(x => typeof x === "number");
1718
+ *
1719
+ * const isStringOrNumber = pipe(isString, Refinement.or(isNumber));
1720
+ * isStringOrNumber("hi"); // true
1721
+ * isStringOrNumber(42); // true
1722
+ * isStringOrNumber(true); // false
1723
+ * ```
1724
+ */
1725
+ const or: <A, C extends A>(second: Refinement<A, C>) => <B extends A>(first: Refinement<A, B>) => Refinement<A, B | C>;
1726
+ /**
1727
+ * Converts a `Refinement<A, B>` into a function `(a: A) => Maybe<B>`.
1728
+ *
1729
+ * Returns `Some(a)` when the refinement holds, `None` otherwise. Useful for
1730
+ * integrating runtime validation into a `Maybe`-based pipeline.
1731
+ *
1732
+ * @example
1733
+ * ```ts
1734
+ * type PositiveNumber = number & { readonly _tag: "Positive" };
1735
+ * const isPositive: Refinement<number, PositiveNumber> =
1736
+ * Refinement.make(n => n > 0);
1737
+ *
1738
+ * pipe(-1, Refinement.toFilter(isPositive)); // None
1739
+ * pipe(42, Refinement.toFilter(isPositive)); // Some(42)
1740
+ * ```
1741
+ */
1742
+ const toFilter: <A, B extends A>(r: Refinement<A, B>) => (a: A) => Maybe<B>;
1743
+ /**
1744
+ * Converts a `Refinement<A, B>` into a function `(a: A) => Result<E, B>`.
1745
+ *
1746
+ * Returns `Ok(a)` when the refinement holds, `Err(onFail(a))` otherwise. Use
1747
+ * this to surface validation failures as typed errors inside a `Result` pipeline.
1748
+ *
1749
+ * @example
1750
+ * ```ts
1751
+ * type NonEmptyString = string & { readonly _tag: "NonEmpty" };
1752
+ * const isNonEmpty: Refinement<string, NonEmptyString> =
1753
+ * Refinement.make(s => s.length > 0);
1754
+ *
1755
+ * pipe("", Refinement.toResult(isNonEmpty, () => "must not be empty")); // Err(...)
1756
+ * pipe("hi", Refinement.toResult(isNonEmpty, () => "must not be empty")); // Ok("hi")
1757
+ * ```
1758
+ */
1759
+ const toResult: <A, B extends A, E>(r: Refinement<A, B>, onFail: (a: A) => E) => (a: A) => Result<E, B>;
1760
+ }
1761
+
1762
1762
  type NotAsked = WithKind<"NotAsked">;
1763
1763
  type Loading = WithKind<"Loading">;
1764
1764
  type Failure<E> = WithKind<"Failure"> & WithError<E>;
@@ -2004,186 +2004,31 @@ declare namespace RemoteData {
2004
2004
  }
2005
2005
 
2006
2006
  /**
2007
- * A Task that can fail with an error of type E or succeed with a value of type A.
2008
- * Combines async operations with typed error handling.
2007
+ * A Resource pairs an async acquisition step with a guaranteed cleanup step.
2008
+ *
2009
+ * Use it whenever something must be explicitly closed, released, or torn down
2010
+ * after you are done with it — database connections, file handles, locks,
2011
+ * temporary directories, or any object with a lifecycle.
2012
+ *
2013
+ * The key guarantee: `release` always runs after `Resource.use`, even when
2014
+ * the work function returns an error. If `acquire` itself fails, `release` is
2015
+ * skipped — there is nothing to clean up.
2016
+ *
2017
+ * Build a Resource with `Resource.make` or `Resource.fromTask`, then run it
2018
+ * with `Resource.use`.
2009
2019
  *
2010
2020
  * @example
2011
2021
  * ```ts
2012
- * const fetchUser = (id: string): TaskResult<Error, User> =>
2013
- * TaskResult.tryCatch(
2014
- * (signal) => fetch(`/users/${id}`, { signal }).then(r => r.json()),
2015
- * (e) => new Error(`Failed to fetch user: ${e}`)
2016
- * );
2017
- * ```
2018
- */
2019
- type TaskResult<E, A> = Task<Result<E, A>>;
2020
- declare namespace TaskResult {
2021
- /**
2022
- * Wraps a value in a successful TaskResult.
2023
- */
2024
- const ok: <E, A>(value: A) => TaskResult<E, A>;
2025
- /**
2026
- * Creates a failed TaskResult with the given error.
2027
- */
2028
- const err: <E, A>(error: E) => TaskResult<E, A>;
2029
- /**
2030
- * Creates a TaskResult from a nullable value.
2031
- * Returns Ok if the value is not null or undefined, err from onNull otherwise.
2032
- */
2033
- const fromNullable: <E>(onNull: () => E) => <A>(value: A | null | undefined) => TaskResult<E, A>;
2034
- /**
2035
- * Creates a TaskResult from a Maybe.
2036
- * Some becomes Ok, None becomes err from onNone.
2037
- */
2038
- const fromMaybe: <E>(onNone: () => E) => <A>(maybe: Maybe<A>) => TaskResult<E, A>;
2039
- /**
2040
- * Lifts a Result into a TaskResult.
2041
- */
2042
- const fromResult: <E, A>(result: Result<E, A>) => TaskResult<E, A>;
2043
- /**
2044
- * Wraps a Promise-returning function of any arguments, returning a new function
2045
- * that catches rejections and returns a TaskResult.
2046
- */
2047
- const fromThrowable: <Args extends readonly unknown[], A, E>(f: (...args: Args) => Promise<A>, onError: (e: unknown) => E) => (...args: Args) => TaskResult<E, A>;
2048
- /**
2049
- * Creates a TaskResult from a function that may throw.
2050
- * Catches any errors and transforms them using the onError function.
2051
- * The factory optionally receives an `AbortSignal` forwarded from the call site.
2052
- *
2053
- * @example
2054
- * ```ts
2055
- * const fetchUser = (id: string): TaskResult<string, User> =>
2056
- * TaskResult.tryCatch(
2057
- * (signal) => fetch(`/users/${id}`, { signal }).then(r => r.json()),
2058
- * String
2059
- * );
2060
- * ```
2061
- */
2062
- const tryCatch: <E, A>(f: (signal?: AbortSignal) => Promise<A>, onError: (e: unknown) => E) => TaskResult<E, A>;
2063
- /**
2064
- * Transforms the success value inside a TaskResult.
2065
- */
2066
- const map: <E, A, B>(f: (a: A) => B) => (data: TaskResult<E, A>) => TaskResult<E, B>;
2067
- /**
2068
- * Transforms the error value inside a TaskResult.
2069
- */
2070
- const mapError: <E, F, A>(f: (e: E) => F) => (data: TaskResult<E, A>) => TaskResult<F, A>;
2071
- /**
2072
- * Chains TaskResult computations. If the first succeeds, passes the value to f.
2073
- * If the first fails, propagates the error.
2074
- */
2075
- const chain: <E, A, B>(f: (a: A) => TaskResult<E, B>) => (data: TaskResult<E, A>) => TaskResult<E, B>;
2076
- /**
2077
- * Extracts the value from a TaskResult by providing handlers for both cases.
2078
- */
2079
- const fold: <E, A, B>(onErr: (e: E) => B, onOk: (a: A) => B) => (data: TaskResult<E, A>) => Task<B>;
2080
- /**
2081
- * Pattern matches on a TaskResult, returning a Task of the result.
2082
- */
2083
- const match: <E, A, B>(cases: {
2084
- err: (e: E) => B;
2085
- ok: (a: A) => B;
2086
- }) => (data: TaskResult<E, A>) => Task<B>;
2087
- /**
2088
- * Recovers from an error by providing a fallback TaskResult.
2089
- * The fallback can produce a different success type, widening the result to `TaskResult<E, A | B>`.
2090
- */
2091
- const recover: <E, A, B>(fallback: (e: E) => TaskResult<E, B>) => (data: TaskResult<E, A>) => TaskResult<E, A | B>;
2092
- /**
2093
- * Returns the success value or a default value if the TaskResult is an error.
2094
- * The default can be a different type, widening the result to `Task<A | B>`.
2095
- */
2096
- const getOrElse: <E, A, B>(defaultValue: () => B) => (data: TaskResult<E, A>) => Task<A | B>;
2097
- /**
2098
- * Executes a side effect on the success value without changing the TaskResult.
2099
- * Useful for logging or debugging.
2100
- */
2101
- const tap: <E, A>(f: (a: A) => void) => (data: TaskResult<E, A>) => TaskResult<E, A>;
2102
- /**
2103
- * Executes a side effect on the error value without changing the TaskResult.
2104
- * Useful for logging or reporting async errors.
2105
- *
2106
- * @example
2107
- * ```ts
2108
- * pipe(
2109
- * fetchUser(id),
2110
- * TaskResult.tapError(e => console.error("fetch failed:", e)),
2111
- * TaskResult.chain(saveToCache),
2112
- * )
2113
- * ```
2114
- */
2115
- const tapError: <E, A>(f: (e: E) => void) => (data: TaskResult<E, A>) => TaskResult<E, A>;
2116
- /**
2117
- * Applies a function wrapped in a TaskResult to a value wrapped in a TaskResult.
2118
- * Both Tasks run in parallel.
2119
- */
2120
- const ap: <E, A>(arg: TaskResult<E, A>) => <B>(data: TaskResult<E, (a: A) => B>) => TaskResult<E, B>;
2121
- /**
2122
- * Executes a `TaskResult` with an optional signal, returning `Promise<Result<E, A>>`.
2123
- * Use as a terminal step in a `pipe` chain.
2124
- *
2125
- * @example
2126
- * ```ts
2127
- * const controller = new AbortController();
2128
- * const result = await pipe(
2129
- * fetchUser("42"),
2130
- * TaskResult.chain(user => fetchPosts(user.id)),
2131
- * TaskResult.run(controller.signal),
2132
- * );
2133
- * if (Result.isOk(result)) render(result.value);
2134
- * ```
2135
- */
2136
- const run: (signal?: AbortSignal) => <E, A>(task: TaskResult<E, A>) => Promise<Result<E, A>>;
2137
- /**
2138
- * Converts a TaskResult value into an object containing a single property.
2139
- * Initiates the pipeline accumulator record.
2140
- *
2141
- * @example
2142
- * ```ts
2143
- * pipe(TaskResult.ok(42), TaskResult.bindTo("value")); // TaskResult({ value: 42 })
2144
- * ```
2145
- */
2146
- const bindTo: <K extends string>(key: K) => <E, A>(data: TaskResult<E, A>) => TaskResult<E, { [P in K]: A; }>;
2147
- /**
2148
- * Evaluates a new TaskResult using the current accumulator and attaches the output to a new key.
2149
- *
2150
- * @example
2151
- * ```ts
2152
- * pipe(
2153
- * TaskResult.ok({ a: 1 }),
2154
- * TaskResult.bind("b", ({ a }) => TaskResult.ok(a + 1))
2155
- * ); // TaskResult({ a: 1, b: 2 })
2156
- * ```
2157
- */
2158
- const bind: <K extends string, E, A, B>(key: K, f: (a: A) => TaskResult<E, B>) => (data: TaskResult<E, A>) => TaskResult<E, A & { [P in K]: B; }>;
2159
- }
2160
-
2161
- /**
2162
- * A Resource pairs an async acquisition step with a guaranteed cleanup step.
2163
- *
2164
- * Use it whenever something must be explicitly closed, released, or torn down
2165
- * after you are done with it — database connections, file handles, locks,
2166
- * temporary directories, or any object with a lifecycle.
2167
- *
2168
- * The key guarantee: `release` always runs after `Resource.use`, even when
2169
- * the work function returns an error. If `acquire` itself fails, `release` is
2170
- * skipped — there is nothing to clean up.
2171
- *
2172
- * Build a Resource with `Resource.make` or `Resource.fromTask`, then run it
2173
- * with `Resource.use`.
2174
- *
2175
- * @example
2176
- * ```ts
2177
- * const dbResource = Resource.make(
2178
- * TaskResult.tryCatch(() => openConnection(config), (e) => new DbError(e)),
2179
- * (conn) => Task.from(() => conn.close())
2180
- * );
2181
- *
2182
- * const result = await pipe(
2183
- * dbResource,
2184
- * Resource.use((conn) => queryUser(conn, userId))
2185
- * )();
2186
- * // conn.close() is called whether queryUser succeeds or fails
2022
+ * const dbResource = Resource.make(
2023
+ * TaskResult.tryCatch(() => openConnection(config), (e) => new DbError(e)),
2024
+ * (conn) => Task.from(() => conn.close())
2025
+ * );
2026
+ *
2027
+ * const result = await pipe(
2028
+ * dbResource,
2029
+ * Resource.use((conn) => queryUser(conn, userId))
2030
+ * )();
2031
+ * // conn.close() is called whether queryUser succeeds or fails
2187
2032
  * ```
2188
2033
  */
2189
2034
  type Resource<E, A> = {
@@ -2616,311 +2461,173 @@ declare namespace TaskMaybe {
2616
2461
  const bind: <K extends string, A, B>(key: K, f: (a: A) => TaskMaybe<B>) => (data: TaskMaybe<A>) => TaskMaybe<A & { [P in K]: B; }>;
2617
2462
  }
2618
2463
 
2619
- type Passed<A> = WithKind<"Passed"> & WithValue<A>;
2620
- type Failed<E> = WithKind<"Failed"> & WithErrors<E>;
2621
2464
  /**
2622
- * Validation represents a value that is either passed with a success value,
2623
- * or failed with accumulated errors.
2624
- * Unlike Result, Validation can accumulate multiple errors instead of short-circuiting.
2625
- *
2626
- * Use Validation when you need to collect all errors (e.g., form validation).
2627
- * Use Result when you want to fail fast on the first error.
2465
+ * A Task that can fail with an error of type E or succeed with a value of type A.
2466
+ * Combines async operations with typed error handling.
2628
2467
  *
2629
2468
  * @example
2630
2469
  * ```ts
2631
- * const validateName = (name: string): Validation<string, string> =>
2632
- * name.length > 0 ? Validation.passed(name) : Validation.failed("Name is required");
2633
- *
2634
- * const validateAge = (age: number): Validation<string, number> =>
2635
- * age >= 0 ? Validation.passed(age) : Validation.failed("Age must be positive");
2636
- *
2637
- * // Accumulates all errors using ap
2638
- * pipe(
2639
- * Validation.passed((name: string) => (age: number) => ({ name, age })),
2640
- * Validation.ap(validateName("")),
2641
- * Validation.ap(validateAge(-1))
2642
- * );
2643
- * // Failed(["Name is required", "Age must be positive"])
2470
+ * const fetchUser = (id: string): TaskResult<Error, User> =>
2471
+ * TaskResult.tryCatch(
2472
+ * (signal) => fetch(`/users/${id}`, { signal }).then(r => r.json()),
2473
+ * (e) => new Error(`Failed to fetch user: ${e}`)
2474
+ * );
2644
2475
  * ```
2645
2476
  */
2646
- type Validation<E, A> = Passed<A> | Failed<E>;
2647
- declare namespace Validation {
2477
+ type TaskResult<E, A> = Task<Result<E, A>>;
2478
+ declare namespace TaskResult {
2648
2479
  /**
2649
- * Wraps a value in a passed Validation.
2650
- *
2651
- * @example
2652
- * ```ts
2653
- * Validation.passed(42); // Passed(42)
2654
- * ```
2480
+ * Wraps a value in a successful TaskResult.
2655
2481
  */
2656
- const passed: <E, A>(value: A) => Validation<E, A>;
2482
+ const ok: <E, A>(value: A) => TaskResult<E, A>;
2657
2483
  /**
2658
- * Creates a failed Validation from a single error.
2659
- *
2660
- * @example
2661
- * ```ts
2662
- * Validation.failed("Invalid input");
2663
- * ```
2484
+ * Creates a failed TaskResult with the given error.
2664
2485
  */
2665
- const failed: <E>(error: E) => Failed<E>;
2486
+ const err: <E, A>(error: E) => TaskResult<E, A>;
2666
2487
  /**
2667
- * Creates a failed Validation from multiple errors.
2668
- *
2669
- * @example
2670
- * ```ts
2671
- * Validation.failedAll(["Invalid input"]);
2672
- * ```
2488
+ * Creates a TaskResult from a nullable value.
2489
+ * Returns Ok if the value is not null or undefined, err from onNull otherwise.
2673
2490
  */
2674
- const failedAll: <E>(errors: NonEmptyList<E>) => Failed<E>;
2491
+ const fromNullable: <E>(onNull: () => E) => <A>(value: A | null | undefined) => TaskResult<E, A>;
2675
2492
  /**
2676
- * Type guard that checks if a Validation is passed.
2493
+ * Creates a TaskResult from a Maybe.
2494
+ * Some becomes Ok, None becomes err from onNone.
2677
2495
  */
2678
- const isPassed: <E, A>(data: Validation<E, A>) => data is Passed<A>;
2496
+ const fromMaybe: <E>(onNone: () => E) => <A>(maybe: Maybe<A>) => TaskResult<E, A>;
2679
2497
  /**
2680
- * Type guard that checks if a Validation is failed.
2498
+ * Lifts a Result into a TaskResult.
2681
2499
  */
2682
- const isFailed: <E, A>(data: Validation<E, A>) => data is Failed<E>;
2500
+ const fromResult: <E, A>(result: Result<E, A>) => TaskResult<E, A>;
2683
2501
  /**
2684
- * Creates a Validation from a predicate applied to a value.
2685
- * Returns Passed if the predicate passes, Failed from `onFalse` otherwise.
2502
+ * Wraps a Promise-returning function of any arguments, returning a new function
2503
+ * that catches rejections and returns a TaskResult.
2504
+ */
2505
+ const fromThrowable: <Args extends readonly unknown[], A, E>(f: (...args: Args) => Promise<A>, onError: (e: unknown) => E) => (...args: Args) => TaskResult<E, A>;
2506
+ /**
2507
+ * Creates a TaskResult from a function that may throw.
2508
+ * Catches any errors and transforms them using the onError function.
2509
+ * The factory optionally receives an `AbortSignal` forwarded from the call site.
2686
2510
  *
2687
2511
  * @example
2688
2512
  * ```ts
2689
- * const validateName = Validation.fromPredicate(
2690
- * (s: string) => s.length > 0,
2691
- * () => "Name is required"
2692
- * );
2693
- *
2694
- * validateName("Alice"); // Passed("Alice")
2695
- * validateName(""); // Failed(["Name is required"])
2513
+ * const fetchUser = (id: string): TaskResult<string, User> =>
2514
+ * TaskResult.tryCatch(
2515
+ * (signal) => fetch(`/users/${id}`, { signal }).then(r => r.json()),
2516
+ * String
2517
+ * );
2696
2518
  * ```
2697
2519
  */
2698
- const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Validation<E, A>;
2699
- /**
2700
- * Creates a Validation from a nullable value.
2701
- * If the value is null or undefined, returns Failed with the error from onNull.
2702
- * Otherwise, returns Passed.
2703
- *
2704
- * @example
2705
- * ```ts
2706
- * pipe(null, Validation.fromNullable(() => "is null")); // Failed(["is null"])
2707
- * pipe(42, Validation.fromNullable(() => "is null")); // Passed(42)
2708
- * ```
2709
- */
2710
- const fromNullable: <E>(onNull: () => E) => <A>(value: A | null | undefined) => Validation<E, A>;
2711
- /**
2712
- * Creates a Validation from a Maybe.
2713
- * If the Maybe is None, returns Failed with the error from onNone.
2714
- * Otherwise, returns Passed.
2715
- *
2716
- * @example
2717
- * ```ts
2718
- * pipe(Maybe.none(), Validation.fromMaybe(() => "is none")); // Failed(["is none"])
2719
- * pipe(Maybe.some(42), Validation.fromMaybe(() => "is none")); // Passed(42)
2720
- * ```
2721
- */
2722
- const fromMaybe: <E>(onNone: () => E) => <A>(maybe: Maybe<A>) => Validation<E, A>;
2520
+ const tryCatch: <E, A>(f: (signal?: AbortSignal) => Promise<A>, onError: (e: unknown) => E) => TaskResult<E, A>;
2723
2521
  /**
2724
- * Transforms the success value inside a Validation.
2725
- *
2726
- * @example
2727
- * ```ts
2728
- * pipe(Validation.passed(5), Validation.map(n => n * 2)); // Passed(10)
2729
- * pipe(Validation.failed("oops"), Validation.map(n => n * 2)); // Failed(["oops"])
2730
- * ```
2522
+ * Transforms the success value inside a TaskResult.
2731
2523
  */
2732
- const map: <A, B>(f: (a: A) => B) => <E>(data: Validation<E, A>) => Validation<E, B>;
2524
+ const map: <E, A, B>(f: (a: A) => B) => (data: TaskResult<E, A>) => TaskResult<E, B>;
2733
2525
  /**
2734
- * Transforms the error list inside a Validation.
2735
- *
2736
- * @example
2737
- * ```ts
2738
- * pipe(Validation.failed("oops"), Validation.mapError(e => e.toUpperCase())); // Failed(["OOPS"])
2739
- * ```
2526
+ * Transforms the error value inside a TaskResult.
2740
2527
  */
2741
- const mapError: <E, F, A>(f: (e: E) => F) => (data: Validation<E, A>) => Validation<F, A>;
2528
+ const mapError: <E, F, A>(f: (e: E) => F) => (data: TaskResult<E, A>) => TaskResult<F, A>;
2742
2529
  /**
2743
- * Applies a function wrapped in a Validation to a value wrapped in a Validation.
2744
- * Accumulates errors from both sides.
2745
- *
2746
- * @example
2747
- * ```ts
2748
- * const add = (a: number) => (b: number) => a + b;
2749
- * pipe(
2750
- * Validation.passed(add),
2751
- * Validation.ap(Validation.passed(5)),
2752
- * Validation.ap(Validation.passed(3))
2753
- * ); // Passed(8)
2754
- *
2755
- * pipe(
2756
- * Validation.passed(add),
2757
- * Validation.ap(Validation.failed<string, number>("bad a")),
2758
- * Validation.ap(Validation.failed<string, number>("bad b"))
2759
- * ); // Failed(["bad a", "bad b"])
2760
- * ```
2530
+ * Chains TaskResult computations. If the first succeeds, passes the value to f.
2531
+ * If the first fails, propagates the error.
2761
2532
  */
2762
- const ap: <E, A>(arg: Validation<E, A>) => <B>(data: Validation<E, (a: A) => B>) => Validation<E, B>;
2533
+ const chain: <E, A, B>(f: (a: A) => TaskResult<E, B>) => (data: TaskResult<E, A>) => TaskResult<E, B>;
2763
2534
  /**
2764
- * Extracts the value from a Validation by providing handlers for both cases.
2765
- *
2766
- * @example
2767
- * ```ts
2768
- * pipe(
2769
- * Validation.passed(42),
2770
- * Validation.fold(
2771
- * errors => `Errors: ${errors.join(", ")}`,
2772
- * value => `Value: ${value}`
2773
- * )
2774
- * );
2775
- * ```
2535
+ * Extracts the value from a TaskResult by providing handlers for both cases.
2776
2536
  */
2777
- const fold: <E, A, B>(onFailed: (errors: NonEmptyList<E>) => B, onPassed: (a: A) => B) => (data: Validation<E, A>) => B;
2537
+ const fold: <E, A, B>(onErr: (e: E) => B, onOk: (a: A) => B) => (data: TaskResult<E, A>) => Task<B>;
2778
2538
  /**
2779
- * Pattern matches on a Validation, returning the result of the matching case.
2780
- *
2781
- * @example
2782
- * ```ts
2783
- * pipe(
2784
- * validation,
2785
- * Validation.match({
2786
- * passed: value => `Got ${value}`,
2787
- * failed: errors => `Failed: ${errors.join(", ")}`
2788
- * })
2789
- * );
2790
- * ```
2539
+ * Pattern matches on a TaskResult, returning a Task of the result.
2791
2540
  */
2792
2541
  const match: <E, A, B>(cases: {
2793
- passed: (a: A) => B;
2794
- failed: (errors: NonEmptyList<E>) => B;
2795
- }) => (data: Validation<E, A>) => B;
2796
- /**
2797
- * Returns the success value or a default value if the Validation is failed.
2798
- * The default can be a different type, widening the result to `A | B`.
2799
- *
2800
- * @example
2801
- * ```ts
2802
- * pipe(Validation.passed(5), Validation.getOrElse(() => 0)); // 5
2803
- * pipe(Validation.failed("oops"), Validation.getOrElse(() => 0)); // 0
2804
- * pipe(Validation.failed("oops"), Validation.getOrElse(() => null)); // null — typed as number | null
2805
- * ```
2806
- */
2807
- const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Validation<E, A>) => A | B;
2542
+ err: (e: E) => B;
2543
+ ok: (a: A) => B;
2544
+ }) => (data: TaskResult<E, A>) => Task<B>;
2808
2545
  /**
2809
- * Executes a side effect on the success value without changing the Validation.
2810
- *
2811
- * @example
2812
- * ```ts
2813
- * pipe(
2814
- * Validation.passed(5),
2815
- * Validation.tap(n => console.log("Value:", n)),
2816
- * Validation.map(n => n * 2)
2817
- * );
2818
- * ```
2546
+ * Recovers from an error by providing a fallback TaskResult.
2547
+ * The fallback can produce a different success type, widening the result to `TaskResult<E, A | B>`.
2819
2548
  */
2820
- const tap: <E, A>(f: (a: A) => void) => (data: Validation<E, A>) => Validation<E, A>;
2549
+ const recover: <E, A, B>(fallback: (e: E) => TaskResult<E, B>) => (data: TaskResult<E, A>) => TaskResult<E, A | B>;
2821
2550
  /**
2822
- * Executes a side effect on the accumulated errors without changing the Validation.
2823
- * Useful for logging or reporting validation failures.
2824
- *
2825
- * @example
2826
- * ```ts
2827
- * pipe(
2828
- * Validation.failed("Name required"),
2829
- * Validation.tapError(errors => console.error("validation failed:", errors)),
2830
- * Validation.map(toUser)
2831
- * );
2832
- * ```
2551
+ * Returns the success value or a default value if the TaskResult is an error.
2552
+ * The default can be a different type, widening the result to `Task<A | B>`.
2833
2553
  */
2834
- const tapError: <E, A>(f: (errors: NonEmptyList<E>) => void) => (data: Validation<E, A>) => Validation<E, A>;
2554
+ const getOrElse: <E, A, B>(defaultValue: () => B) => (data: TaskResult<E, A>) => Task<A | B>;
2835
2555
  /**
2836
- * Recovers from a Failed state by providing a fallback Validation.
2837
- * The fallback receives the accumulated error list so callers can inspect which errors occurred.
2838
- * The fallback can produce a different success type, widening the result to `Validation<E, A | B>`.
2556
+ * Executes a side effect on the success value without changing the TaskResult.
2557
+ * Useful for logging or debugging.
2839
2558
  */
2840
- const recover: <E, A, B>(fallback: (errors: NonEmptyList<E>) => Validation<E, B>) => (data: Validation<E, A>) => Validation<E, A | B>;
2559
+ const tap: <E, A>(f: (a: A) => void) => (data: TaskResult<E, A>) => TaskResult<E, A>;
2841
2560
  /**
2842
- * Recovers from a Failed state unless `isBlocked` returns true for any of the accumulated errors.
2843
- * The fallback can produce a different success type, widening the result to `Validation<E, A | B>`.
2561
+ * Executes a side effect on the error value without changing the TaskResult.
2562
+ * Useful for logging or reporting async errors.
2844
2563
  *
2845
2564
  * @example
2846
2565
  * ```ts
2847
2566
  * pipe(
2848
- * Validation.failed("field-error"),
2849
- * Validation.recoverUnless(e => e === "fatal", () => Validation.passed(0))
2850
- * ); // Passed(0)
2567
+ * fetchUser(id),
2568
+ * TaskResult.tapError(e => console.error("fetch failed:", e)),
2569
+ * TaskResult.chain(saveToCache),
2570
+ * )
2851
2571
  * ```
2852
2572
  */
2853
- const recoverUnless: <E, A, B>(isBlocked: (e: E) => boolean, fallback: () => Validation<E, B>) => (data: Validation<E, A>) => Validation<E, A | B>;
2573
+ const tapError: <E, A>(f: (e: E) => void) => (data: TaskResult<E, A>) => TaskResult<E, A>;
2854
2574
  /**
2855
- * Converts a Validation to a Result.
2856
- * Passed becomes Ok, Failed becomes Err with the accumulated error list.
2857
- *
2858
- * @example
2859
- * ```ts
2860
- * Validation.toResult(Validation.passed(42)); // Ok(42)
2861
- * Validation.toResult(Validation.failed("oops")); // Err(["oops"])
2862
- * ```
2575
+ * Applies a function wrapped in a TaskResult to a value wrapped in a TaskResult.
2576
+ * Both Tasks run in parallel.
2863
2577
  */
2864
- const toResult: <E, A>(data: Validation<E, A>) => Result<NonEmptyList<E>, A>;
2578
+ const ap: <E, A>(arg: TaskResult<E, A>) => <B>(data: TaskResult<E, (a: A) => B>) => TaskResult<E, B>;
2865
2579
  /**
2866
- * Converts a Validation to a Maybe. `Passed` becomes `Some`; `Failed` becomes `None`
2867
- * (errors are discarded).
2580
+ * Executes a `TaskResult` with an optional signal, returning `Promise<Result<E, A>>`.
2581
+ * Use as a terminal step in a `pipe` chain.
2868
2582
  *
2869
2583
  * @example
2870
2584
  * ```ts
2871
- * Validation.toMaybe(Validation.passed(42)); // Some(42)
2872
- * Validation.toMaybe(Validation.failed("bad")); // None
2585
+ * const controller = new AbortController();
2586
+ * const result = await pipe(
2587
+ * fetchUser("42"),
2588
+ * TaskResult.chain(user => fetchPosts(user.id)),
2589
+ * TaskResult.run(controller.signal),
2590
+ * );
2591
+ * if (Result.isOk(result)) render(result.value);
2873
2592
  * ```
2874
2593
  */
2875
- const toMaybe: <E, A>(data: Validation<E, A>) => Maybe<A>;
2594
+ const run: (signal?: AbortSignal) => <E, A>(task: TaskResult<E, A>) => Promise<Result<E, A>>;
2876
2595
  /**
2877
- * Converts a `Result` to a `Validation`. `Ok` becomes `Passed`; `Err(e)` becomes `Failed([e])`.
2878
- *
2879
- * Useful when bridging from error-short-circuiting `Result` pipelines into
2880
- * error-accumulating `Validation` pipelines.
2596
+ * Converts a TaskResult value into an object containing a single property.
2597
+ * Initiates the pipeline accumulator record.
2881
2598
  *
2882
2599
  * @example
2883
2600
  * ```ts
2884
- * Validation.fromResult(Result.ok(42)); // Passed(42)
2885
- * Validation.fromResult(Result.err("bad")); // Failed(["bad"])
2601
+ * pipe(TaskResult.ok(42), TaskResult.bindTo("value")); // TaskResult({ value: 42 })
2886
2602
  * ```
2887
2603
  */
2888
- const fromResult: <E, A>(data: Result<E, A>) => Validation<E, A>;
2604
+ const bindTo: <K extends string>(key: K) => <E, A>(data: TaskResult<E, A>) => TaskResult<E, { [P in K]: A; }>;
2889
2605
  /**
2890
- * Combines two independent Validation instances into a tuple.
2891
- * If both are Passed, returns Passed with both values as a tuple.
2892
- * If either is Failed, accumulates errors from both sides.
2606
+ * Evaluates a new TaskResult using the current accumulator and attaches the output to a new key.
2893
2607
  *
2894
2608
  * @example
2895
2609
  * ```ts
2896
- * Validation.product(
2897
- * Validation.passed("alice"),
2898
- * Validation.passed(30)
2899
- * ); // Passed(["alice", 30])
2900
- *
2901
- * Validation.product(
2902
- * Validation.failed("Name required"),
2903
- * Validation.failed("Age must be >= 0")
2904
- * ); // Failed(["Name required", "Age must be >= 0"])
2610
+ * pipe(
2611
+ * TaskResult.ok({ a: 1 }),
2612
+ * TaskResult.bind("b", ({ a }) => TaskResult.ok(a + 1))
2613
+ * ); // TaskResult({ a: 1, b: 2 })
2905
2614
  * ```
2906
2615
  */
2907
- const product: <E, A, B>(first: Validation<E, A>, second: Validation<E, B>) => Validation<E, readonly [A, B]>;
2616
+ const bind: <K extends string, E, A, B>(key: K, f: (a: A) => TaskResult<E, B>) => (data: TaskResult<E, A>) => TaskResult<E, A & { [P in K]: B; }>;
2908
2617
  /**
2909
- * Combines a non-empty list of Validation instances, accumulating all errors.
2910
- * If all are Passed, returns Passed with all values collected into an array.
2911
- * If any are Failed, returns Failed with all accumulated errors.
2618
+ * Combines a record of TaskResults into a single TaskResult of a record.
2619
+ * Evaluates all tasks in parallel, forwarding the AbortSignal down to each sub-task.
2620
+ * Returns the first Err encountered in key order.
2912
2621
  *
2913
2622
  * @example
2914
2623
  * ```ts
2915
- * Validation.productAll([
2916
- * validateName(name),
2917
- * validateEmail(email),
2918
- * validateAge(age)
2919
- * ]);
2920
- * // Passed([name, email, age]) or Failed([...all errors])
2624
+ * TaskResult.struct({
2625
+ * name: TaskResult.ok("Alice"),
2626
+ * age: TaskResult.ok(30)
2627
+ * }); // TaskResult({ name: "Alice", age: 30 })
2921
2628
  * ```
2922
2629
  */
2923
- const productAll: <E, A>(data: NonEmptyList<Validation<E, A>>) => Validation<E, readonly A[]>;
2630
+ const struct: <E, R extends Record<string, any>>(fields: { [K in keyof R]: TaskResult<E, R[K]>; }) => TaskResult<E, R>;
2924
2631
  }
2925
2632
 
2926
2633
  /**
@@ -3434,4 +3141,329 @@ declare namespace Tuple {
3434
3141
  const tap: <A, B>(f: (a: A, b: B) => void) => (tuple: Tuple<A, B>) => Tuple<A, B>;
3435
3142
  }
3436
3143
 
3144
+ type Passed<A> = WithKind<"Passed"> & WithValue<A>;
3145
+ type Failed<E> = WithKind<"Failed"> & WithErrors<E>;
3146
+ /**
3147
+ * Validation represents a value that is either passed with a success value,
3148
+ * or failed with accumulated errors.
3149
+ * Unlike Result, Validation can accumulate multiple errors instead of short-circuiting.
3150
+ *
3151
+ * Use Validation when you need to collect all errors (e.g., form validation).
3152
+ * Use Result when you want to fail fast on the first error.
3153
+ *
3154
+ * @example
3155
+ * ```ts
3156
+ * const validateName = (name: string): Validation<string, string> =>
3157
+ * name.length > 0 ? Validation.passed(name) : Validation.failed("Name is required");
3158
+ *
3159
+ * const validateAge = (age: number): Validation<string, number> =>
3160
+ * age >= 0 ? Validation.passed(age) : Validation.failed("Age must be positive");
3161
+ *
3162
+ * // Accumulates all errors using ap
3163
+ * pipe(
3164
+ * Validation.passed((name: string) => (age: number) => ({ name, age })),
3165
+ * Validation.ap(validateName("")),
3166
+ * Validation.ap(validateAge(-1))
3167
+ * );
3168
+ * // Failed(["Name is required", "Age must be positive"])
3169
+ * ```
3170
+ */
3171
+ type Validation<E, A> = Passed<A> | Failed<E>;
3172
+ declare namespace Validation {
3173
+ /**
3174
+ * Wraps a value in a passed Validation.
3175
+ *
3176
+ * @example
3177
+ * ```ts
3178
+ * Validation.passed(42); // Passed(42)
3179
+ * ```
3180
+ */
3181
+ const passed: <E, A>(value: A) => Validation<E, A>;
3182
+ /**
3183
+ * Creates a failed Validation from a single error.
3184
+ *
3185
+ * @example
3186
+ * ```ts
3187
+ * Validation.failed("Invalid input");
3188
+ * ```
3189
+ */
3190
+ const failed: <E>(error: E) => Failed<E>;
3191
+ /**
3192
+ * Creates a failed Validation from multiple errors.
3193
+ *
3194
+ * @example
3195
+ * ```ts
3196
+ * Validation.failedAll(["Invalid input"]);
3197
+ * ```
3198
+ */
3199
+ const failedAll: <E>(errors: NonEmptyList<E>) => Failed<E>;
3200
+ /**
3201
+ * Type guard that checks if a Validation is passed.
3202
+ */
3203
+ const isPassed: <E, A>(data: Validation<E, A>) => data is Passed<A>;
3204
+ /**
3205
+ * Type guard that checks if a Validation is failed.
3206
+ */
3207
+ const isFailed: <E, A>(data: Validation<E, A>) => data is Failed<E>;
3208
+ /**
3209
+ * Creates a Validation from a predicate applied to a value.
3210
+ * Returns Passed if the predicate passes, Failed from `onFalse` otherwise.
3211
+ *
3212
+ * @example
3213
+ * ```ts
3214
+ * const validateName = Validation.fromPredicate(
3215
+ * (s: string) => s.length > 0,
3216
+ * () => "Name is required"
3217
+ * );
3218
+ *
3219
+ * validateName("Alice"); // Passed("Alice")
3220
+ * validateName(""); // Failed(["Name is required"])
3221
+ * ```
3222
+ */
3223
+ const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Validation<E, A>;
3224
+ /**
3225
+ * Creates a Validation from a nullable value.
3226
+ * If the value is null or undefined, returns Failed with the error from onNull.
3227
+ * Otherwise, returns Passed.
3228
+ *
3229
+ * @example
3230
+ * ```ts
3231
+ * pipe(null, Validation.fromNullable(() => "is null")); // Failed(["is null"])
3232
+ * pipe(42, Validation.fromNullable(() => "is null")); // Passed(42)
3233
+ * ```
3234
+ */
3235
+ const fromNullable: <E>(onNull: () => E) => <A>(value: A | null | undefined) => Validation<E, A>;
3236
+ /**
3237
+ * Creates a Validation from a Maybe.
3238
+ * If the Maybe is None, returns Failed with the error from onNone.
3239
+ * Otherwise, returns Passed.
3240
+ *
3241
+ * @example
3242
+ * ```ts
3243
+ * pipe(Maybe.none(), Validation.fromMaybe(() => "is none")); // Failed(["is none"])
3244
+ * pipe(Maybe.some(42), Validation.fromMaybe(() => "is none")); // Passed(42)
3245
+ * ```
3246
+ */
3247
+ const fromMaybe: <E>(onNone: () => E) => <A>(maybe: Maybe<A>) => Validation<E, A>;
3248
+ /**
3249
+ * Transforms the success value inside a Validation.
3250
+ *
3251
+ * @example
3252
+ * ```ts
3253
+ * pipe(Validation.passed(5), Validation.map(n => n * 2)); // Passed(10)
3254
+ * pipe(Validation.failed("oops"), Validation.map(n => n * 2)); // Failed(["oops"])
3255
+ * ```
3256
+ */
3257
+ const map: <A, B>(f: (a: A) => B) => <E>(data: Validation<E, A>) => Validation<E, B>;
3258
+ /**
3259
+ * Transforms the error list inside a Validation.
3260
+ *
3261
+ * @example
3262
+ * ```ts
3263
+ * pipe(Validation.failed("oops"), Validation.mapError(e => e.toUpperCase())); // Failed(["OOPS"])
3264
+ * ```
3265
+ */
3266
+ const mapError: <E, F, A>(f: (e: E) => F) => (data: Validation<E, A>) => Validation<F, A>;
3267
+ /**
3268
+ * Applies a function wrapped in a Validation to a value wrapped in a Validation.
3269
+ * Accumulates errors from both sides.
3270
+ *
3271
+ * @example
3272
+ * ```ts
3273
+ * const add = (a: number) => (b: number) => a + b;
3274
+ * pipe(
3275
+ * Validation.passed(add),
3276
+ * Validation.ap(Validation.passed(5)),
3277
+ * Validation.ap(Validation.passed(3))
3278
+ * ); // Passed(8)
3279
+ *
3280
+ * pipe(
3281
+ * Validation.passed(add),
3282
+ * Validation.ap(Validation.failed<string, number>("bad a")),
3283
+ * Validation.ap(Validation.failed<string, number>("bad b"))
3284
+ * ); // Failed(["bad a", "bad b"])
3285
+ * ```
3286
+ */
3287
+ const ap: <E, A>(arg: Validation<E, A>) => <B>(data: Validation<E, (a: A) => B>) => Validation<E, B>;
3288
+ /**
3289
+ * Extracts the value from a Validation by providing handlers for both cases.
3290
+ *
3291
+ * @example
3292
+ * ```ts
3293
+ * pipe(
3294
+ * Validation.passed(42),
3295
+ * Validation.fold(
3296
+ * errors => `Errors: ${errors.join(", ")}`,
3297
+ * value => `Value: ${value}`
3298
+ * )
3299
+ * );
3300
+ * ```
3301
+ */
3302
+ const fold: <E, A, B>(onFailed: (errors: NonEmptyList<E>) => B, onPassed: (a: A) => B) => (data: Validation<E, A>) => B;
3303
+ /**
3304
+ * Pattern matches on a Validation, returning the result of the matching case.
3305
+ *
3306
+ * @example
3307
+ * ```ts
3308
+ * pipe(
3309
+ * validation,
3310
+ * Validation.match({
3311
+ * passed: value => `Got ${value}`,
3312
+ * failed: errors => `Failed: ${errors.join(", ")}`
3313
+ * })
3314
+ * );
3315
+ * ```
3316
+ */
3317
+ const match: <E, A, B>(cases: {
3318
+ passed: (a: A) => B;
3319
+ failed: (errors: NonEmptyList<E>) => B;
3320
+ }) => (data: Validation<E, A>) => B;
3321
+ /**
3322
+ * Returns the success value or a default value if the Validation is failed.
3323
+ * The default can be a different type, widening the result to `A | B`.
3324
+ *
3325
+ * @example
3326
+ * ```ts
3327
+ * pipe(Validation.passed(5), Validation.getOrElse(() => 0)); // 5
3328
+ * pipe(Validation.failed("oops"), Validation.getOrElse(() => 0)); // 0
3329
+ * pipe(Validation.failed("oops"), Validation.getOrElse(() => null)); // null — typed as number | null
3330
+ * ```
3331
+ */
3332
+ const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Validation<E, A>) => A | B;
3333
+ /**
3334
+ * Executes a side effect on the success value without changing the Validation.
3335
+ *
3336
+ * @example
3337
+ * ```ts
3338
+ * pipe(
3339
+ * Validation.passed(5),
3340
+ * Validation.tap(n => console.log("Value:", n)),
3341
+ * Validation.map(n => n * 2)
3342
+ * );
3343
+ * ```
3344
+ */
3345
+ const tap: <E, A>(f: (a: A) => void) => (data: Validation<E, A>) => Validation<E, A>;
3346
+ /**
3347
+ * Executes a side effect on the accumulated errors without changing the Validation.
3348
+ * Useful for logging or reporting validation failures.
3349
+ *
3350
+ * @example
3351
+ * ```ts
3352
+ * pipe(
3353
+ * Validation.failed("Name required"),
3354
+ * Validation.tapError(errors => console.error("validation failed:", errors)),
3355
+ * Validation.map(toUser)
3356
+ * );
3357
+ * ```
3358
+ */
3359
+ const tapError: <E, A>(f: (errors: NonEmptyList<E>) => void) => (data: Validation<E, A>) => Validation<E, A>;
3360
+ /**
3361
+ * Recovers from a Failed state by providing a fallback Validation.
3362
+ * The fallback receives the accumulated error list so callers can inspect which errors occurred.
3363
+ * The fallback can produce a different success type, widening the result to `Validation<E, A | B>`.
3364
+ */
3365
+ const recover: <E, A, B>(fallback: (errors: NonEmptyList<E>) => Validation<E, B>) => (data: Validation<E, A>) => Validation<E, A | B>;
3366
+ /**
3367
+ * Recovers from a Failed state unless `isBlocked` returns true for any of the accumulated errors.
3368
+ * The fallback can produce a different success type, widening the result to `Validation<E, A | B>`.
3369
+ *
3370
+ * @example
3371
+ * ```ts
3372
+ * pipe(
3373
+ * Validation.failed("field-error"),
3374
+ * Validation.recoverUnless(e => e === "fatal", () => Validation.passed(0))
3375
+ * ); // Passed(0)
3376
+ * ```
3377
+ */
3378
+ const recoverUnless: <E, A, B>(isBlocked: (e: E) => boolean, fallback: () => Validation<E, B>) => (data: Validation<E, A>) => Validation<E, A | B>;
3379
+ /**
3380
+ * Converts a Validation to a Result.
3381
+ * Passed becomes Ok, Failed becomes Err with the accumulated error list.
3382
+ *
3383
+ * @example
3384
+ * ```ts
3385
+ * Validation.toResult(Validation.passed(42)); // Ok(42)
3386
+ * Validation.toResult(Validation.failed("oops")); // Err(["oops"])
3387
+ * ```
3388
+ */
3389
+ const toResult: <E, A>(data: Validation<E, A>) => Result<NonEmptyList<E>, A>;
3390
+ /**
3391
+ * Converts a Validation to a Maybe. `Passed` becomes `Some`; `Failed` becomes `None`
3392
+ * (errors are discarded).
3393
+ *
3394
+ * @example
3395
+ * ```ts
3396
+ * Validation.toMaybe(Validation.passed(42)); // Some(42)
3397
+ * Validation.toMaybe(Validation.failed("bad")); // None
3398
+ * ```
3399
+ */
3400
+ const toMaybe: <E, A>(data: Validation<E, A>) => Maybe<A>;
3401
+ /**
3402
+ * Converts a `Result` to a `Validation`. `Ok` becomes `Passed`; `Err(e)` becomes `Failed([e])`.
3403
+ *
3404
+ * Useful when bridging from error-short-circuiting `Result` pipelines into
3405
+ * error-accumulating `Validation` pipelines.
3406
+ *
3407
+ * @example
3408
+ * ```ts
3409
+ * Validation.fromResult(Result.ok(42)); // Passed(42)
3410
+ * Validation.fromResult(Result.err("bad")); // Failed(["bad"])
3411
+ * ```
3412
+ */
3413
+ const fromResult: <E, A>(data: Result<E, A>) => Validation<E, A>;
3414
+ /**
3415
+ * Combines two independent Validation instances into a tuple.
3416
+ * If both are Passed, returns Passed with both values as a tuple.
3417
+ * If either is Failed, accumulates errors from both sides.
3418
+ *
3419
+ * @example
3420
+ * ```ts
3421
+ * Validation.product(
3422
+ * Validation.passed("alice"),
3423
+ * Validation.passed(30)
3424
+ * ); // Passed(["alice", 30])
3425
+ *
3426
+ * Validation.product(
3427
+ * Validation.failed("Name required"),
3428
+ * Validation.failed("Age must be >= 0")
3429
+ * ); // Failed(["Name required", "Age must be >= 0"])
3430
+ * ```
3431
+ */
3432
+ const product: <E, A, B>(first: Validation<E, A>, second: Validation<E, B>) => Validation<E, readonly [A, B]>;
3433
+ /**
3434
+ * Combines a non-empty list of Validation instances, accumulating all errors.
3435
+ * If all are Passed, returns Passed with all values collected into an array.
3436
+ * If any are Failed, returns Failed with all accumulated errors.
3437
+ *
3438
+ * @example
3439
+ * ```ts
3440
+ * Validation.productAll([
3441
+ * validateName(name),
3442
+ * validateEmail(email),
3443
+ * validateAge(age)
3444
+ * ]);
3445
+ * // Passed([name, email, age]) or Failed([...all errors])
3446
+ * ```
3447
+ */
3448
+ const productAll: <E, A>(data: NonEmptyList<Validation<E, A>>) => Validation<E, readonly A[]>;
3449
+ /**
3450
+ * Combines a record of Validations into a single Validation of a record.
3451
+ * Accumulates all failed branches' errors.
3452
+ *
3453
+ * @example
3454
+ * ```ts
3455
+ * Validation.struct({
3456
+ * name: Validation.passed("Alice"),
3457
+ * age: Validation.passed(30)
3458
+ * }); // Passed({ name: "Alice", age: 30 })
3459
+ *
3460
+ * Validation.struct({
3461
+ * name: Validation.failed("Name required"),
3462
+ * age: Validation.failed("Age must be >= 0")
3463
+ * }); // Failed(["Name required", "Age must be >= 0"])
3464
+ * ```
3465
+ */
3466
+ const struct: <E, R extends Record<string, any>>(fields: { [K in keyof R]: Validation<E, R[K]>; }) => Validation<E, R>;
3467
+ }
3468
+
3437
3469
  export { Combinable, Deferred, type Failed, type Failure, Lazy, Lens, type Loading, Logged, Maybe, type NotAsked, Op, Optional, type Passed, Predicate, Reader, Refinement, RemoteData, Resource, Result, State, type Success, Task, TaskMaybe, TaskResult, TaskValidation, These, type TheseBoth, type TheseFirst, type TheseSecond, Tuple, Validation };