@nlozgachev/pipelined 0.36.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.
@@ -1,311 +1,198 @@
1
1
  import { NonEmptyList, Duration } from './types.mjs';
2
2
 
3
- type WithKind<K extends string> = {
4
- readonly kind: K;
5
- };
6
- type WithValue<T> = {
7
- readonly value: T;
8
- };
9
- type WithError<T> = {
10
- readonly error: T;
11
- };
12
- type WithErrors<T> = {
13
- readonly errors: NonEmptyList<T>;
14
- };
15
- type WithFirst<T> = {
16
- readonly first: T;
17
- };
18
- type WithSecond<T> = {
19
- readonly second: T;
20
- };
21
- type WithLog<T> = {
22
- readonly log: ReadonlyArray<T>;
23
- };
24
- /** Retry policy for `Op.interpret`. */
25
- type RetryOptions<E> = {
26
- readonly attempts: number;
27
- readonly backoff?: Duration | ((attempt: number) => Duration);
28
- readonly when?: (error: E) => boolean;
29
- };
30
- /** Timeout policy for `Op.interpret`. Wraps the entire retry sequence. */
31
- type TimeoutOptions<E> = {
32
- readonly duration: Duration;
33
- readonly onTimeout: () => E;
34
- };
35
- type WithTimeout<E> = {
36
- readonly timeout?: TimeoutOptions<E>;
37
- };
38
- type WithDuration = {
39
- readonly duration: Duration;
40
- };
41
- type WithN = {
42
- readonly n: number;
43
- };
44
- type WithConcurrency = {
45
- readonly concurrency?: number;
46
- };
47
- type WithSize = {
48
- readonly size?: number;
49
- };
50
- type WithCooldown = {
51
- readonly cooldown?: Duration;
52
- };
53
- type WithMinInterval = {
54
- readonly minInterval?: Duration;
55
- };
56
-
57
- type Ok<A> = WithKind<"Ok"> & WithValue<A>;
58
- type Err<E> = WithKind<"Err"> & WithError<E>;
3
+ declare const _deferred: unique symbol;
59
4
  /**
60
- * Result represents a value that can be one of two types: a success (Ok) or a failure (Err).
61
- * Use Result when an operation can fail with a meaningful error value.
5
+ * A nominally typed, one-shot async value that supports `await` but enforces infallibility.
6
+ *
7
+ * Two design choices work together to make the guarantee structural rather than documentary:
8
+ *
9
+ * - The phantom `[_deferred]` symbol makes the type **nominal**: only values produced by
10
+ * `Deferred.fromPromise` satisfy it. A plain object `{ then: ... }` does not.
11
+ * - The single-parameter `.then()` **excludes rejection handlers** by construction. There is
12
+ * no second argument to pass, so chaining and `.catch()` are impossible.
13
+ *
14
+ * This makes `Deferred<A>` the natural return type for `Task<A>`, which is guaranteed to
15
+ * never reject.
62
16
  *
63
17
  * @example
64
18
  * ```ts
65
- * const divide = (a: number, b: number): Result<string, number> =>
66
- * b === 0 ? Result.err("Division by zero") : Result.ok(a / b);
67
- *
68
- * pipe(
69
- * divide(10, 2),
70
- * Result.map(n => n * 2),
71
- * Result.getOrElse(() => 0)
72
- * ); // 10
19
+ * const value = await Deferred.fromPromise(Promise.resolve(42));
20
+ * // value === 42
73
21
  * ```
74
22
  */
75
- type Result<E, A> = Ok<A> | Err<E>;
76
- declare namespace Result {
77
- /**
78
- * Creates a successful Result with the given value.
79
- */
80
- const ok: <A>(value: A) => Ok<A>;
81
- /**
82
- * Creates a failed Result with the given error.
83
- */
84
- const err: <E>(e: E) => Err<E>;
85
- /**
86
- * Type guard that checks if a Result is Ok.
87
- */
88
- const isOk: <E, A>(data: Result<E, A>) => data is Ok<A>;
89
- /**
90
- * Type guard that checks if a Result is Err.
91
- */
92
- const isErr: <E, A>(data: Result<E, A>) => data is Err<E>;
93
- /**
94
- * Creates a Result from a function that may throw.
95
- * Catches any errors and transforms them using the onError function.
96
- *
97
- * @example
98
- * ```ts
99
- * const parseJson = (s: string): Result<string, unknown> =>
100
- * Result.tryCatch(
101
- * () => JSON.parse(s),
102
- * (e) => `Parse error: ${e}`
103
- * );
104
- * ```
105
- */
106
- const tryCatch: <E, A>(f: () => A, onError: (e: unknown) => E) => Result<E, A>;
107
- /**
108
- * Transforms the success value inside a Result.
109
- *
110
- * @example
111
- * ```ts
112
- * pipe(Result.ok(5), Result.map(n => n * 2)); // Ok(10)
113
- * pipe(Result.err("error"), Result.map(n => n * 2)); // Err("error")
114
- * ```
115
- */
116
- const map: <E, A, B>(f: (a: A) => B) => (data: Result<E, A>) => Result<E, B>;
117
- /**
118
- * Transforms the error value inside a Result.
119
- *
120
- * @example
121
- * ```ts
122
- * pipe(Result.err("oops"), Result.mapError(e => e.toUpperCase())); // Err("OOPS")
123
- * ```
124
- */
125
- const mapError: <E, F, A>(f: (e: E) => F) => (data: Result<E, A>) => Result<F, A>;
126
- /**
127
- * Chains Result computations. If the first is Ok, passes the value to f.
128
- * If the first is Err, propagates the error.
129
- *
130
- * @example
131
- * ```ts
132
- * const validatePositive = (n: number): Result<string, number> =>
133
- * n > 0 ? Result.ok(n) : Result.err("Must be positive");
134
- *
135
- * pipe(Result.ok(5), Result.chain(validatePositive)); // Ok(5)
136
- * pipe(Result.ok(-1), Result.chain(validatePositive)); // Err("Must be positive")
137
- * ```
138
- */
139
- const chain: <E, A, B>(f: (a: A) => Result<E, B>) => (data: Result<E, A>) => Result<E, B>;
140
- /**
141
- * Extracts the value from a Result by providing handlers for both cases.
142
- *
143
- * @example
144
- * ```ts
145
- * pipe(
146
- * Result.ok(5),
147
- * Result.fold(
148
- * e => `Error: ${e}`,
149
- * n => `Value: ${n}`
150
- * )
151
- * ); // "Value: 5"
152
- * ```
153
- */
154
- const fold: <E, A, B>(onErr: (e: E) => B, onOk: (a: A) => B) => (data: Result<E, A>) => B;
155
- /**
156
- * Pattern matches on a Result, returning the result of the matching case.
157
- *
158
- * @example
159
- * ```ts
160
- * pipe(
161
- * result,
162
- * Result.match({
163
- * ok: value => `Got ${value}`,
164
- * err: error => `Failed: ${error}`
165
- * })
166
- * );
167
- * ```
168
- */
169
- const match: <E, A, B>(cases: {
170
- ok: (a: A) => B;
171
- err: (e: E) => B;
172
- }) => (data: Result<E, A>) => B;
23
+ type Deferred<A> = {
24
+ readonly [_deferred]: A;
25
+ readonly then: (onfulfilled: (value: A) => unknown) => void;
26
+ };
27
+ declare namespace Deferred {
173
28
  /**
174
- * Returns the success value or a default value if the Result is an error.
175
- * The default is a thunk `() => B` — evaluated only when the Result is Err.
176
- * The default can be a different type, widening the result to `A | B`.
29
+ * Wraps a `Promise` into a `Deferred`, structurally excluding rejection handlers,
30
+ * `.catch()`, `.finally()`, and chainable `.then()`.
177
31
  *
178
- * @example
179
- * ```ts
180
- * pipe(Result.ok(5), Result.getOrElse(() => 0)); // 5
181
- * pipe(Result.err("error"), Result.getOrElse(() => 0)); // 0
182
- * pipe(Result.err("error"), Result.getOrElse(() => null)); // null — typed as number | null
183
- * ```
184
- */
185
- const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Result<E, A>) => A | B;
186
- /**
187
- * Executes a side effect on the success value without changing the Result.
188
- * Useful for logging or debugging.
32
+ * **Precondition**: `p` must never reject. If `p` rejects, the returned `Deferred` will
33
+ * never resolve — `await`-ing it will hang indefinitely. Use `TaskResult.tryCatch` to
34
+ * handle operations that may fail before converting to a `Deferred`.
189
35
  *
190
36
  * @example
191
37
  * ```ts
192
- * pipe(
193
- * Result.ok(5),
194
- * Result.tap(n => console.log("Value:", n)),
195
- * Result.map(n => n * 2)
196
- * );
38
+ * const d = Deferred.fromPromise(Promise.resolve("hello"));
39
+ * const value = await d; // "hello"
197
40
  * ```
198
41
  */
199
- const tap: <E, A>(f: (a: A) => void) => (data: Result<E, A>) => Result<E, A>;
42
+ const fromPromise: <A>(p: Promise<A>) => Deferred<A>;
200
43
  /**
201
- * Executes a side effect on the error value without changing the Result.
202
- * Useful for logging or reporting errors.
44
+ * Converts a `Deferred` back into a `Promise`.
203
45
  *
204
46
  * @example
205
47
  * ```ts
206
- * pipe(
207
- * Result.err("not found"),
208
- * Result.tapError(e => console.error("validation failed:", e)),
209
- * Result.chain(save),
210
- * )
48
+ * const p = Deferred.toPromise(Deferred.fromPromise(Promise.resolve(42)));
49
+ * // p is Promise<42>
211
50
  * ```
212
51
  */
213
- const tapError: <E, A>(f: (e: E) => void) => (data: Result<E, A>) => Result<E, A>;
52
+ const toPromise: <A>(d: Deferred<A>) => Promise<A>;
53
+ }
54
+
55
+ /**
56
+ * A function that checks whether two values of type `A` are equal.
57
+ * Use built-in instances (`Equality.string`, `Equality.number`, etc.) as starting points,
58
+ * then adapt them with `Equality.by` and combine them with `Equality.and`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * type User = { id: string; name: string };
63
+ * const byId = pipe(Equality.string, Equality.by((u: User) => u.id));
64
+ *
65
+ * pipe(users, Arr.uniqWith(byId));
66
+ * ```
67
+ */
68
+ type Equality<A> = (a: A, b: A) => boolean;
69
+ declare namespace Equality {
214
70
  /**
215
- * Creates a Result from a predicate applied to a value.
216
- * Returns Ok if the predicate passes, Err from onFalse otherwise.
71
+ * Equality for strings. Case-sensitive.
217
72
  *
218
73
  * @example
219
74
  * ```ts
220
- * pipe(5, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Ok(5)
221
- * pipe(-1, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Err("-1 is not positive")
222
- * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Err("empty string")
75
+ * Equality.string("hello", "hello"); // true
76
+ * Equality.string("hello", "Hello"); // false
223
77
  * ```
224
78
  */
225
- const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Result<E, A>;
79
+ const string: Equality<string>;
226
80
  /**
227
- * Creates a Result from a nullable value.
228
- * Returns Ok if the value is not null or undefined, error from onNull otherwise.
81
+ * Equality for numbers. Uses strict equality.
229
82
  *
230
83
  * @example
231
84
  * ```ts
232
- * pipe(null, Result.fromNullable(() => "is null")); // Err("is null")
233
- * pipe(42, Result.fromNullable(() => "is null")); // Ok(42)
85
+ * Equality.number(42, 42); // true
234
86
  * ```
235
87
  */
236
- const fromNullable: <E>(onNull: () => E) => <A>(value: A | null | undefined) => Result<E, A>;
88
+ const number: Equality<number>;
237
89
  /**
238
- * Creates a Result from a Maybe.
239
- * Some becomes Ok, None becomes error from onNone.
90
+ * Equality for booleans.
240
91
  *
241
92
  * @example
242
93
  * ```ts
243
- * pipe(Maybe.none(), Result.fromMaybe(() => "is none")); // Err("is none")
244
- * pipe(Maybe.some(42), Result.fromMaybe(() => "is none")); // Ok(42)
94
+ * Equality.boolean(true, true); // true
245
95
  * ```
246
96
  */
247
- const fromMaybe: <E>(onNone: () => E) => <A>(maybe: Maybe<A>) => Result<E, A>;
97
+ const boolean: Equality<boolean>;
248
98
  /**
249
- * Wraps a throwing function of any arguments, returning a new function
250
- * that catches errors and returns a Result.
99
+ * Equality for `Date` values. Compares by numeric time value.
251
100
  *
252
101
  * @example
253
102
  * ```ts
254
- * const safeParse = Result.fromThrowable(
255
- * (s: string) => JSON.parse(s),
256
- * (e) => new Error(`Parse error: ${e}`)
257
- * );
258
- *
259
- * safeParse('{"a":1}'); // Ok({ a: 1 })
260
- * safeParse('invalid'); // Err(Error)
103
+ * Equality.date(new Date("2024-01-01"), new Date("2024-01-01")); // true
261
104
  * ```
262
105
  */
263
- const fromThrowable: <Args extends readonly unknown[], A, E>(f: (...args: Args) => A, onError: (e: unknown) => E) => (...args: Args) => Result<E, A>;
264
- /**
265
- * Recovers from an error by providing a fallback Result.
266
- * The fallback can produce a different success type, widening the result to `Result<E, A | B>`.
267
- */
268
- const recover: <E, A, B>(fallback: (e: E) => Result<E, B>) => (data: Result<E, A>) => Result<E, A | B>;
106
+ const date: Equality<Date>;
269
107
  /**
270
- * Recovers from an error unless the predicate `isBlocked` returns true for that error.
271
- * The fallback can produce a different success type, widening the result to `Result<E, A | B>`.
108
+ * Lifts an element equality into an array equality. Two arrays are equal if they have the
109
+ * same length and every element pair is equal under `eq`.
272
110
  *
273
111
  * @example
274
112
  * ```ts
275
- * pipe(
276
- * Result.err(new Error("not found")),
277
- * Result.recoverUnless(e => e.message === "fatal", () => Result.ok(0))
278
- * ); // Ok(0)
113
+ * Equality.array(Equality.number)([1, 2, 3], [1, 2, 3]); // true
279
114
  * ```
280
115
  */
281
- const recoverUnless: <E, A, B>(isBlocked: (e: E) => boolean, fallback: () => Result<E, B>) => (data: Result<E, A>) => Result<E, A | B>;
116
+ const array: <A>(eq: Equality<A>) => Equality<readonly A[]>;
282
117
  /**
283
- * Converts a Result to a Maybe.
284
- * Ok becomes Some, Err becomes None (the error is discarded).
118
+ * Adapts an equality for type `A` into an equality for type `B` by extracting a field.
119
+ * Read as "equality by this field": `pipe(Equality.string, Equality.by(u => u.name))`.
285
120
  *
286
121
  * @example
287
122
  * ```ts
288
- * Result.toMaybe(Result.ok(42)); // Some(42)
289
- * Result.toMaybe(Result.err("oops")); // None
123
+ * type Product = { id: string; price: number };
124
+ * const byId = pipe(Equality.string, Equality.by((p: Product) => p.id));
125
+ * byId({ id: "p1", price: 9 }, { id: "p1", price: 12 }); // true
290
126
  * ```
291
127
  */
292
- const toMaybe: <E, A>(data: Result<E, A>) => Maybe<A>;
128
+ const by: <A, B>(f: (b: B) => A) => (eq: Equality<A>) => Equality<B>;
293
129
  /**
294
- * Applies a function wrapped in a Result to a value wrapped in a Result.
130
+ * Combines two equalities with logical AND. Both must pass for two values to be considered equal.
131
+ * Data-last: the first equality is the data being piped.
295
132
  *
296
133
  * @example
297
134
  * ```ts
298
- * const add = (a: number) => (b: number) => a + b;
299
- * pipe(
300
- * Result.ok(add),
301
- * Result.ap(Result.ok(5)),
302
- * Result.ap(Result.ok(3))
303
- * ); // Ok(8)
135
+ * const exact = pipe(byName, Equality.and(byRole));
136
+ * exact(userA, userB); // true only if name AND role match
304
137
  * ```
305
138
  */
306
- const ap: <E, A>(arg: Result<E, A>) => <B>(data: Result<E, (a: A) => B>) => Result<E, B>;
139
+ const and: <A>(eq2: Equality<A>) => (eq1: Equality<A>) => Equality<A>;
307
140
  }
308
141
 
142
+ type WithKind<K extends string> = {
143
+ readonly kind: K;
144
+ };
145
+ type WithValue<T> = {
146
+ readonly value: T;
147
+ };
148
+ type WithError<T> = {
149
+ readonly error: T;
150
+ };
151
+ type WithErrors<T> = {
152
+ readonly errors: NonEmptyList<T>;
153
+ };
154
+ type WithFirst<T> = {
155
+ readonly first: T;
156
+ };
157
+ type WithSecond<T> = {
158
+ readonly second: T;
159
+ };
160
+ type WithLog<T> = {
161
+ readonly log: ReadonlyArray<T>;
162
+ };
163
+ /** Retry policy for `Op.interpret`. */
164
+ type RetryOptions<E> = {
165
+ readonly attempts: number;
166
+ readonly backoff?: Duration | ((attempt: number) => Duration);
167
+ readonly when?: (error: E) => boolean;
168
+ };
169
+ /** Timeout policy for `Op.interpret`. Wraps the entire retry sequence. */
170
+ type TimeoutOptions<E> = {
171
+ readonly duration: Duration;
172
+ readonly onTimeout: () => E;
173
+ };
174
+ type WithTimeout<E> = {
175
+ readonly timeout?: TimeoutOptions<E>;
176
+ };
177
+ type WithDuration = {
178
+ readonly duration: Duration;
179
+ };
180
+ type WithN = {
181
+ readonly n: number;
182
+ };
183
+ type WithConcurrency = {
184
+ readonly concurrency?: number;
185
+ };
186
+ type WithSize = {
187
+ readonly size?: number;
188
+ };
189
+ type WithCooldown = {
190
+ readonly cooldown?: Duration;
191
+ };
192
+ type WithMinInterval = {
193
+ readonly minInterval?: Duration;
194
+ };
195
+
309
196
  type Some<A> = WithKind<"Some"> & WithValue<A>;
310
197
  type None = WithKind<"None">;
311
198
  /**
@@ -504,238 +391,421 @@ declare namespace Maybe {
504
391
  * Recovers from a None by providing a fallback Maybe.
505
392
  * The fallback can produce a different type, widening the result to `Maybe<A | B>`.
506
393
  */
507
- const recover: <A, B>(fallback: () => Maybe<B>) => (data: Maybe<A>) => Maybe<A | B>;
394
+ const recover: <A, B>(fallback: () => Maybe<B>) => (data: Maybe<A>) => Maybe<A | B>;
395
+ /**
396
+ * Applies a function wrapped in a Maybe to a value wrapped in a Maybe.
397
+ *
398
+ * @example
399
+ * ```ts
400
+ * const add = (a: number) => (b: number) => a + b;
401
+ * pipe(
402
+ * Maybe.some(add),
403
+ * Maybe.ap(Maybe.some(5)),
404
+ * Maybe.ap(Maybe.some(3))
405
+ * ); // Some(8)
406
+ * ```
407
+ */
408
+ const ap: <A>(arg: Maybe<A>) => <B>(data: Maybe<(a: A) => B>) => Maybe<B>;
409
+ /**
410
+ * Converts a Maybe value into an object containing a single property.
411
+ * Initiates the pipeline accumulator record.
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * pipe(Maybe.some(42), Maybe.bindTo("value")); // Some({ value: 42 })
416
+ * ```
417
+ */
418
+ const bindTo: <K extends string>(key: K) => <A>(data: Maybe<A>) => Maybe<{ [P in K]: A; }>;
419
+ /**
420
+ * Evaluates a new Maybe using the current accumulator and attaches the output to a new key.
421
+ *
422
+ * @example
423
+ * ```ts
424
+ * pipe(
425
+ * Maybe.some({ a: 1 }),
426
+ * Maybe.bind("b", ({ a }) => Maybe.some(a + 1))
427
+ * ); // Some({ a: 1, b: 2 })
428
+ * ```
429
+ */
430
+ const bind: <K extends string, A, B>(key: K, f: (a: A) => Maybe<B>) => (data: Maybe<A>) => Maybe<A & { [P in K]: B; }>;
431
+ /**
432
+ * Combines a record of Maybes into a single Maybe of a record.
433
+ * Evaluates fields in key order and short-circuits on the first None.
434
+ *
435
+ * @example
436
+ * ```ts
437
+ * Maybe.struct({
438
+ * name: Maybe.some("Alice"),
439
+ * age: Maybe.some(30)
440
+ * }); // Some({ name: "Alice", age: 30 })
441
+ * ```
442
+ */
443
+ const struct: <R extends Record<string, any>>(fields: { [K in keyof R]: Maybe<R[K]>; }) => Maybe<R>;
444
+ }
445
+
446
+ /**
447
+ * A function that orders two values of type `A`. Returns a negative number when `a` comes before
448
+ * `b`, a positive number when `a` comes after `b`, and `0` when they are equal.
449
+ *
450
+ * Compatible with `Array.prototype.sort` and `Arr.sortWith`.
451
+ *
452
+ * @example
453
+ * ```ts
454
+ * type Employee = { name: string; salary: number };
455
+ *
456
+ * const byName = pipe(Ordering.string, Ordering.by((e: Employee) => e.name));
457
+ * const bySalary = pipe(Ordering.number, Ordering.by((e: Employee) => e.salary));
458
+ *
459
+ * pipe(employees, Arr.sortWith(pipe(byName, Ordering.thenBy(bySalary))));
460
+ * ```
461
+ */
462
+ type Ordering<A> = (a: A, b: A) => number;
463
+ declare namespace Ordering {
464
+ /**
465
+ * Alphabetical ordering for strings.
466
+ *
467
+ * @example
468
+ * ```ts
469
+ * Ordering.string("apple", "banana"); // negative
470
+ * ```
471
+ */
472
+ const string: Ordering<string>;
473
+ /**
474
+ * Numeric ordering. Equivalent to `(a, b) => a - b`.
475
+ *
476
+ * @example
477
+ * ```ts
478
+ * pipe([3, 1, 2], Arr.sortWith(Ordering.number)); // [1, 2, 3]
479
+ * ```
480
+ */
481
+ const number: Ordering<number>;
482
+ /**
483
+ * Ordering for `Date` values by numeric time value.
484
+ *
485
+ * @example
486
+ * ```ts
487
+ * pipe(dates, Arr.sortWith(Ordering.date)); // earliest first
488
+ * ```
489
+ */
490
+ const date: Ordering<Date>;
491
+ /**
492
+ * Flips the direction of an ordering.
493
+ *
494
+ * @example
495
+ * ```ts
496
+ * pipe([3, 1, 2], Arr.sortWith(Ordering.reverse(Ordering.number))); // [3, 2, 1]
497
+ * ```
498
+ */
499
+ const reverse: <A>(ord: Ordering<A>) => Ordering<A>;
500
+ /**
501
+ * Chains two orderings: the second is used only when the first returns `0`.
502
+ * Data-last: the first ordering is the data being piped.
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * const byDeptThenSalary = pipe(byDept, Ordering.thenBy(bySalary));
507
+ * ```
508
+ */
509
+ const thenBy: <A>(ord2: Ordering<A>) => (ord1: Ordering<A>) => Ordering<A>;
510
+ /**
511
+ * Adapts an ordering for type `A` into an ordering for type `B` by extracting a field.
512
+ * Read as "ordering by this field": `pipe(Ordering.number, Ordering.by(p => p.price))`.
513
+ *
514
+ * @example
515
+ * ```ts
516
+ * type Product = { name: string; price: number };
517
+ * const byPrice = pipe(Ordering.number, Ordering.by((p: Product) => p.price));
518
+ * pipe(products, Arr.sortWith(byPrice));
519
+ * ```
520
+ */
521
+ const by: <A, B>(f: (b: B) => A) => (ord: Ordering<A>) => Ordering<B>;
522
+ }
523
+
524
+ type Ok<A> = WithKind<"Ok"> & WithValue<A>;
525
+ type Err<E> = WithKind<"Err"> & WithError<E>;
526
+ /**
527
+ * Result represents a value that can be one of two types: a success (Ok) or a failure (Err).
528
+ * Use Result when an operation can fail with a meaningful error value.
529
+ *
530
+ * @example
531
+ * ```ts
532
+ * const divide = (a: number, b: number): Result<string, number> =>
533
+ * b === 0 ? Result.err("Division by zero") : Result.ok(a / b);
534
+ *
535
+ * pipe(
536
+ * divide(10, 2),
537
+ * Result.map(n => n * 2),
538
+ * Result.getOrElse(() => 0)
539
+ * ); // 10
540
+ * ```
541
+ */
542
+ type Result<E, A> = Ok<A> | Err<E>;
543
+ declare namespace Result {
544
+ /**
545
+ * Creates a successful Result with the given value.
546
+ */
547
+ const ok: <A>(value: A) => Ok<A>;
548
+ /**
549
+ * Creates a failed Result with the given error.
550
+ */
551
+ const err: <E>(e: E) => Err<E>;
552
+ /**
553
+ * Type guard that checks if a Result is Ok.
554
+ */
555
+ const isOk: <E, A>(data: Result<E, A>) => data is Ok<A>;
556
+ /**
557
+ * Type guard that checks if a Result is Err.
558
+ */
559
+ const isErr: <E, A>(data: Result<E, A>) => data is Err<E>;
560
+ /**
561
+ * Creates a Result from a function that may throw.
562
+ * Catches any errors and transforms them using the onError function.
563
+ *
564
+ * @example
565
+ * ```ts
566
+ * const parseJson = (s: string): Result<string, unknown> =>
567
+ * Result.tryCatch(
568
+ * () => JSON.parse(s),
569
+ * (e) => `Parse error: ${e}`
570
+ * );
571
+ * ```
572
+ */
573
+ const tryCatch: <E, A>(f: () => A, onError: (e: unknown) => E) => Result<E, A>;
574
+ /**
575
+ * Transforms the success value inside a Result.
576
+ *
577
+ * @example
578
+ * ```ts
579
+ * pipe(Result.ok(5), Result.map(n => n * 2)); // Ok(10)
580
+ * pipe(Result.err("error"), Result.map(n => n * 2)); // Err("error")
581
+ * ```
582
+ */
583
+ const map: <E, A, B>(f: (a: A) => B) => (data: Result<E, A>) => Result<E, B>;
584
+ /**
585
+ * Transforms the error value inside a Result.
586
+ *
587
+ * @example
588
+ * ```ts
589
+ * pipe(Result.err("oops"), Result.mapError(e => e.toUpperCase())); // Err("OOPS")
590
+ * ```
591
+ */
592
+ const mapError: <E, F, A>(f: (e: E) => F) => (data: Result<E, A>) => Result<F, A>;
508
593
  /**
509
- * Applies a function wrapped in a Maybe to a value wrapped in a Maybe.
594
+ * Chains Result computations. If the first is Ok, passes the value to f.
595
+ * If the first is Err, propagates the error.
510
596
  *
511
597
  * @example
512
598
  * ```ts
513
- * const add = (a: number) => (b: number) => a + b;
514
- * pipe(
515
- * Maybe.some(add),
516
- * Maybe.ap(Maybe.some(5)),
517
- * Maybe.ap(Maybe.some(3))
518
- * ); // Some(8)
599
+ * const validatePositive = (n: number): Result<string, number> =>
600
+ * n > 0 ? Result.ok(n) : Result.err("Must be positive");
601
+ *
602
+ * pipe(Result.ok(5), Result.chain(validatePositive)); // Ok(5)
603
+ * pipe(Result.ok(-1), Result.chain(validatePositive)); // Err("Must be positive")
519
604
  * ```
520
605
  */
521
- const ap: <A>(arg: Maybe<A>) => <B>(data: Maybe<(a: A) => B>) => Maybe<B>;
522
- }
523
-
524
- declare const _deferred: unique symbol;
525
- /**
526
- * A nominally typed, one-shot async value that supports `await` but enforces infallibility.
527
- *
528
- * Two design choices work together to make the guarantee structural rather than documentary:
529
- *
530
- * - The phantom `[_deferred]` symbol makes the type **nominal**: only values produced by
531
- * `Deferred.fromPromise` satisfy it. A plain object `{ then: ... }` does not.
532
- * - The single-parameter `.then()` **excludes rejection handlers** by construction. There is
533
- * no second argument to pass, so chaining and `.catch()` are impossible.
534
- *
535
- * This makes `Deferred<A>` the natural return type for `Task<A>`, which is guaranteed to
536
- * never reject.
537
- *
538
- * @example
539
- * ```ts
540
- * const value = await Deferred.fromPromise(Promise.resolve(42));
541
- * // value === 42
542
- * ```
543
- */
544
- type Deferred<A> = {
545
- readonly [_deferred]: A;
546
- readonly then: (onfulfilled: (value: A) => unknown) => void;
547
- };
548
- declare namespace Deferred {
606
+ const chain: <E, A, B>(f: (a: A) => Result<E, B>) => (data: Result<E, A>) => Result<E, B>;
549
607
  /**
550
- * Wraps a `Promise` into a `Deferred`, structurally excluding rejection handlers,
551
- * `.catch()`, `.finally()`, and chainable `.then()`.
552
- *
553
- * **Precondition**: `p` must never reject. If `p` rejects, the returned `Deferred` will
554
- * never resolve — `await`-ing it will hang indefinitely. Use `TaskResult.tryCatch` to
555
- * handle operations that may fail before converting to a `Deferred`.
608
+ * Extracts the value from a Result by providing handlers for both cases.
556
609
  *
557
610
  * @example
558
611
  * ```ts
559
- * const d = Deferred.fromPromise(Promise.resolve("hello"));
560
- * const value = await d; // "hello"
612
+ * pipe(
613
+ * Result.ok(5),
614
+ * Result.fold(
615
+ * e => `Error: ${e}`,
616
+ * n => `Value: ${n}`
617
+ * )
618
+ * ); // "Value: 5"
561
619
  * ```
562
620
  */
563
- const fromPromise: <A>(p: Promise<A>) => Deferred<A>;
621
+ const fold: <E, A, B>(onErr: (e: E) => B, onOk: (a: A) => B) => (data: Result<E, A>) => B;
564
622
  /**
565
- * Converts a `Deferred` back into a `Promise`.
623
+ * Pattern matches on a Result, returning the result of the matching case.
566
624
  *
567
625
  * @example
568
626
  * ```ts
569
- * const p = Deferred.toPromise(Deferred.fromPromise(Promise.resolve(42)));
570
- * // p is Promise<42>
627
+ * pipe(
628
+ * result,
629
+ * Result.match({
630
+ * ok: value => `Got ${value}`,
631
+ * err: error => `Failed: ${error}`
632
+ * })
633
+ * );
571
634
  * ```
572
635
  */
573
- const toPromise: <A>(d: Deferred<A>) => Promise<A>;
574
- }
575
-
576
- /**
577
- * A function that checks whether two values of type `A` are equal.
578
- * Use built-in instances (`Equality.string`, `Equality.number`, etc.) as starting points,
579
- * then adapt them with `Equality.by` and combine them with `Equality.and`.
580
- *
581
- * @example
582
- * ```ts
583
- * type User = { id: string; name: string };
584
- * const byId = pipe(Equality.string, Equality.by((u: User) => u.id));
585
- *
586
- * pipe(users, Arr.uniqWith(byId));
587
- * ```
588
- */
589
- type Equality<A> = (a: A, b: A) => boolean;
590
- declare namespace Equality {
636
+ const match: <E, A, B>(cases: {
637
+ ok: (a: A) => B;
638
+ err: (e: E) => B;
639
+ }) => (data: Result<E, A>) => B;
591
640
  /**
592
- * Equality for strings. Case-sensitive.
641
+ * Returns the success value or a default value if the Result is an error.
642
+ * The default is a thunk `() => B` — evaluated only when the Result is Err.
643
+ * The default can be a different type, widening the result to `A | B`.
593
644
  *
594
645
  * @example
595
646
  * ```ts
596
- * Equality.string("hello", "hello"); // true
597
- * Equality.string("hello", "Hello"); // false
647
+ * pipe(Result.ok(5), Result.getOrElse(() => 0)); // 5
648
+ * pipe(Result.err("error"), Result.getOrElse(() => 0)); // 0
649
+ * pipe(Result.err("error"), Result.getOrElse(() => null)); // null — typed as number | null
598
650
  * ```
599
651
  */
600
- const string: Equality<string>;
652
+ const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Result<E, A>) => A | B;
601
653
  /**
602
- * Equality for numbers. Uses strict equality.
654
+ * Executes a side effect on the success value without changing the Result.
655
+ * Useful for logging or debugging.
603
656
  *
604
657
  * @example
605
658
  * ```ts
606
- * Equality.number(42, 42); // true
659
+ * pipe(
660
+ * Result.ok(5),
661
+ * Result.tap(n => console.log("Value:", n)),
662
+ * Result.map(n => n * 2)
663
+ * );
607
664
  * ```
608
665
  */
609
- const number: Equality<number>;
666
+ const tap: <E, A>(f: (a: A) => void) => (data: Result<E, A>) => Result<E, A>;
610
667
  /**
611
- * Equality for booleans.
668
+ * Executes a side effect on the error value without changing the Result.
669
+ * Useful for logging or reporting errors.
612
670
  *
613
671
  * @example
614
672
  * ```ts
615
- * Equality.boolean(true, true); // true
673
+ * pipe(
674
+ * Result.err("not found"),
675
+ * Result.tapError(e => console.error("validation failed:", e)),
676
+ * Result.chain(save),
677
+ * )
616
678
  * ```
617
679
  */
618
- const boolean: Equality<boolean>;
680
+ const tapError: <E, A>(f: (e: E) => void) => (data: Result<E, A>) => Result<E, A>;
619
681
  /**
620
- * Equality for `Date` values. Compares by numeric time value.
682
+ * Creates a Result from a predicate applied to a value.
683
+ * Returns Ok if the predicate passes, Err from onFalse otherwise.
621
684
  *
622
685
  * @example
623
686
  * ```ts
624
- * Equality.date(new Date("2024-01-01"), new Date("2024-01-01")); // true
687
+ * pipe(5, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Ok(5)
688
+ * pipe(-1, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Err("-1 is not positive")
689
+ * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Err("empty string")
625
690
  * ```
626
691
  */
627
- const date: Equality<Date>;
692
+ const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Result<E, A>;
628
693
  /**
629
- * Lifts an element equality into an array equality. Two arrays are equal if they have the
630
- * same length and every element pair is equal under `eq`.
694
+ * Creates a Result from a nullable value.
695
+ * Returns Ok if the value is not null or undefined, error from onNull otherwise.
631
696
  *
632
697
  * @example
633
698
  * ```ts
634
- * Equality.array(Equality.number)([1, 2, 3], [1, 2, 3]); // true
699
+ * pipe(null, Result.fromNullable(() => "is null")); // Err("is null")
700
+ * pipe(42, Result.fromNullable(() => "is null")); // Ok(42)
635
701
  * ```
636
702
  */
637
- const array: <A>(eq: Equality<A>) => Equality<readonly A[]>;
703
+ const fromNullable: <E>(onNull: () => E) => <A>(value: A | null | undefined) => Result<E, A>;
638
704
  /**
639
- * Adapts an equality for type `A` into an equality for type `B` by extracting a field.
640
- * Read as "equality by this field": `pipe(Equality.string, Equality.by(u => u.name))`.
705
+ * Creates a Result from a Maybe.
706
+ * Some becomes Ok, None becomes error from onNone.
641
707
  *
642
708
  * @example
643
709
  * ```ts
644
- * type Product = { id: string; price: number };
645
- * const byId = pipe(Equality.string, Equality.by((p: Product) => p.id));
646
- * byId({ id: "p1", price: 9 }, { id: "p1", price: 12 }); // true
710
+ * pipe(Maybe.none(), Result.fromMaybe(() => "is none")); // Err("is none")
711
+ * pipe(Maybe.some(42), Result.fromMaybe(() => "is none")); // Ok(42)
647
712
  * ```
648
713
  */
649
- const by: <A, B>(f: (b: B) => A) => (eq: Equality<A>) => Equality<B>;
714
+ const fromMaybe: <E>(onNone: () => E) => <A>(maybe: Maybe<A>) => Result<E, A>;
650
715
  /**
651
- * Combines two equalities with logical AND. Both must pass for two values to be considered equal.
652
- * Data-last: the first equality is the data being piped.
716
+ * Wraps a throwing function of any arguments, returning a new function
717
+ * that catches errors and returns a Result.
653
718
  *
654
719
  * @example
655
720
  * ```ts
656
- * const exact = pipe(byName, Equality.and(byRole));
657
- * exact(userA, userB); // true only if name AND role match
721
+ * const safeParse = Result.fromThrowable(
722
+ * (s: string) => JSON.parse(s),
723
+ * (e) => new Error(`Parse error: ${e}`)
724
+ * );
725
+ *
726
+ * safeParse('{"a":1}'); // Ok({ a: 1 })
727
+ * safeParse('invalid'); // Err(Error)
658
728
  * ```
659
729
  */
660
- const and: <A>(eq2: Equality<A>) => (eq1: Equality<A>) => Equality<A>;
661
- }
662
-
663
- /**
664
- * A function that orders two values of type `A`. Returns a negative number when `a` comes before
665
- * `b`, a positive number when `a` comes after `b`, and `0` when they are equal.
666
- *
667
- * Compatible with `Array.prototype.sort` and `Arr.sortWith`.
668
- *
669
- * @example
670
- * ```ts
671
- * type Employee = { name: string; salary: number };
672
- *
673
- * const byName = pipe(Ordering.string, Ordering.by((e: Employee) => e.name));
674
- * const bySalary = pipe(Ordering.number, Ordering.by((e: Employee) => e.salary));
675
- *
676
- * pipe(employees, Arr.sortWith(pipe(byName, Ordering.thenBy(bySalary))));
677
- * ```
678
- */
679
- type Ordering<A> = (a: A, b: A) => number;
680
- declare namespace Ordering {
730
+ const fromThrowable: <Args extends readonly unknown[], A, E>(f: (...args: Args) => A, onError: (e: unknown) => E) => (...args: Args) => Result<E, A>;
681
731
  /**
682
- * Alphabetical ordering for strings.
732
+ * Recovers from an error by providing a fallback Result.
733
+ * The fallback can produce a different success type, widening the result to `Result<E, A | B>`.
734
+ */
735
+ const recover: <E, A, B>(fallback: (e: E) => Result<E, B>) => (data: Result<E, A>) => Result<E, A | B>;
736
+ /**
737
+ * Recovers from an error unless the predicate `isBlocked` returns true for that error.
738
+ * The fallback can produce a different success type, widening the result to `Result<E, A | B>`.
683
739
  *
684
740
  * @example
685
741
  * ```ts
686
- * Ordering.string("apple", "banana"); // negative
742
+ * pipe(
743
+ * Result.err(new Error("not found")),
744
+ * Result.recoverUnless(e => e.message === "fatal", () => Result.ok(0))
745
+ * ); // Ok(0)
687
746
  * ```
688
747
  */
689
- const string: Ordering<string>;
748
+ const recoverUnless: <E, A, B>(isBlocked: (e: E) => boolean, fallback: () => Result<E, B>) => (data: Result<E, A>) => Result<E, A | B>;
690
749
  /**
691
- * Numeric ordering. Equivalent to `(a, b) => a - b`.
750
+ * Converts a Result to a Maybe.
751
+ * Ok becomes Some, Err becomes None (the error is discarded).
692
752
  *
693
753
  * @example
694
754
  * ```ts
695
- * pipe([3, 1, 2], Arr.sortWith(Ordering.number)); // [1, 2, 3]
755
+ * Result.toMaybe(Result.ok(42)); // Some(42)
756
+ * Result.toMaybe(Result.err("oops")); // None
696
757
  * ```
697
758
  */
698
- const number: Ordering<number>;
759
+ const toMaybe: <E, A>(data: Result<E, A>) => Maybe<A>;
699
760
  /**
700
- * Ordering for `Date` values by numeric time value.
761
+ * Applies a function wrapped in a Result to a value wrapped in a Result.
701
762
  *
702
763
  * @example
703
764
  * ```ts
704
- * pipe(dates, Arr.sortWith(Ordering.date)); // earliest first
765
+ * const add = (a: number) => (b: number) => a + b;
766
+ * pipe(
767
+ * Result.ok(add),
768
+ * Result.ap(Result.ok(5)),
769
+ * Result.ap(Result.ok(3))
770
+ * ); // Ok(8)
705
771
  * ```
706
772
  */
707
- const date: Ordering<Date>;
773
+ const ap: <E, A>(arg: Result<E, A>) => <B>(data: Result<E, (a: A) => B>) => Result<E, B>;
708
774
  /**
709
- * Flips the direction of an ordering.
775
+ * Converts a Result value into an object containing a single property.
776
+ * Initiates the pipeline accumulator record.
710
777
  *
711
778
  * @example
712
779
  * ```ts
713
- * pipe([3, 1, 2], Arr.sortWith(Ordering.reverse(Ordering.number))); // [3, 2, 1]
780
+ * pipe(Result.ok(42), Result.bindTo("value")); // Ok({ value: 42 })
714
781
  * ```
715
782
  */
716
- const reverse: <A>(ord: Ordering<A>) => Ordering<A>;
783
+ const bindTo: <K extends string>(key: K) => <E, A>(data: Result<E, A>) => Result<E, { [P in K]: A; }>;
717
784
  /**
718
- * Chains two orderings: the second is used only when the first returns `0`.
719
- * Data-last: the first ordering is the data being piped.
785
+ * Evaluates a new Result using the current accumulator and attaches the output to a new key.
720
786
  *
721
787
  * @example
722
788
  * ```ts
723
- * const byDeptThenSalary = pipe(byDept, Ordering.thenBy(bySalary));
789
+ * pipe(
790
+ * Result.ok({ a: 1 }),
791
+ * Result.bind("b", ({ a }) => Result.ok(a + 1))
792
+ * ); // Ok({ a: 1, b: 2 })
724
793
  * ```
725
794
  */
726
- const thenBy: <A>(ord2: Ordering<A>) => (ord1: Ordering<A>) => Ordering<A>;
795
+ const bind: <K extends string, E, A, B>(key: K, f: (a: A) => Result<E, B>) => (data: Result<E, A>) => Result<E, A & { [P in K]: B; }>;
727
796
  /**
728
- * Adapts an ordering for type `A` into an ordering for type `B` by extracting a field.
729
- * Read as "ordering by this field": `pipe(Ordering.number, Ordering.by(p => p.price))`.
797
+ * Combines a record of Results into a single Result of a record.
798
+ * Evaluates fields in key order and short-circuits on the first failure.
730
799
  *
731
800
  * @example
732
801
  * ```ts
733
- * type Product = { name: string; price: number };
734
- * const byPrice = pipe(Ordering.number, Ordering.by((p: Product) => p.price));
735
- * pipe(products, Arr.sortWith(byPrice));
802
+ * Result.struct({
803
+ * name: Result.ok("Alice"),
804
+ * age: Result.ok(30)
805
+ * }); // Ok({ name: "Alice", age: 30 })
736
806
  * ```
737
807
  */
738
- const by: <A, B>(f: (b: B) => A) => (ord: Ordering<A>) => Ordering<B>;
808
+ const struct: <E, R extends Record<string, any>>(fields: { [K in keyof R]: Result<E, R[K]>; }) => Result<E, R>;
739
809
  }
740
810
 
741
811
  /**
@@ -1024,6 +1094,28 @@ declare namespace Task {
1024
1094
  * ```
1025
1095
  */
1026
1096
  const run: (signal?: AbortSignal) => <A>(task: Task<A>) => Promise<A>;
1097
+ /**
1098
+ * Converts a Task value into an object containing a single property.
1099
+ * Initiates the pipeline accumulator record.
1100
+ *
1101
+ * @example
1102
+ * ```ts
1103
+ * pipe(Task.resolve(42), Task.bindTo("value")); // Task({ value: 42 })
1104
+ * ```
1105
+ */
1106
+ const bindTo: <K extends string>(key: K) => <A>(data: Task<A>) => Task<{ [P in K]: A; }>;
1107
+ /**
1108
+ * Evaluates a new Task using the current accumulator and attaches the output to a new key.
1109
+ *
1110
+ * @example
1111
+ * ```ts
1112
+ * pipe(
1113
+ * Task.resolve({ a: 1 }),
1114
+ * Task.bind("b", ({ a }) => Task.resolve(a + 1))
1115
+ * ); // Task({ a: 1, b: 2 })
1116
+ * ```
1117
+ */
1118
+ const bind: <K extends string, A, B>(key: K, f: (a: A) => Task<B>) => (data: Task<A>) => Task<A & { [P in K]: B; }>;
1027
1119
  }
1028
1120
 
1029
- export { Deferred as D, Equality as E, Maybe as M, type None as N, type Ok as O, Result as R, type Some as S, Task as T, type WithValue as W, type Err as a, Ordering as b, type WithLog as c, type WithKind as d, type WithError as e, type RetryOptions as f, type TimeoutOptions as g, type WithTimeout as h, type WithMinInterval as i, type WithCooldown as j, type WithConcurrency as k, type WithSize as l, type WithDuration as m, type WithN as n, type WithErrors as o, type WithFirst as p, type WithSecond as q };
1121
+ export { Deferred as D, Equality as E, Maybe as M, type None as N, type Ok as O, Result as R, type Some as S, Task as T, type WithConcurrency as W, type Err as a, Ordering as b, type RetryOptions as c, type TimeoutOptions as d, type WithCooldown as e, type WithDuration as f, type WithError as g, type WithErrors as h, type WithFirst as i, type WithKind as j, type WithLog as k, type WithMinInterval as l, type WithN as m, type WithSecond as n, type WithSize as o, type WithTimeout as p, type WithValue as q };