@nlozgachev/pipelined 0.33.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # pipelined
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@nlozgachev/pipelined?style=for-the-badge&color=000&logo=npm&label&logoColor=fff)](https://www.npmjs.com/package/@nlozgachev/pipelined) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nlozgachev/pipelined/publish.yml?style=for-the-badge&color=000&logo=githubactions&label&logoColor=fff)](https://github.com/nlozgachev/pipelined/actions/workflows/publish.yml) [![Codecov](https://img.shields.io/codecov/c/github/nlozgachev/pipelined?style=for-the-badge&color=000&logo=codecov&label&logoColor=fff)](https://app.codecov.io/github/nlozgachev/pipelined)
3
+ [![npm](https://img.shields.io/npm/v/@nlozgachev/pipelined?style=for-the-badge&color=000&logo=npm&label&logoColor=fff)](https://www.npmjs.com/package/@nlozgachev/pipelined)
4
+ [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nlozgachev/pipelined/publish.yml?style=for-the-badge&color=000&logo=githubactions&label&logoColor=fff)](https://github.com/nlozgachev/pipelined/actions/workflows/publish.yml)
5
+ [![Codecov](https://img.shields.io/codecov/c/github/nlozgachev/pipelined?style=for-the-badge&color=000&logo=codecov&label&logoColor=fff)](https://app.codecov.io/github/nlozgachev/pipelined)
4
6
 
5
7
  Opinionated functional abstractions for TypeScript.
6
8
 
@@ -12,11 +14,11 @@ npm add @nlozgachev/pipelined
12
14
 
13
15
  ## Possibly maybe
14
16
 
15
- **pipelined** names every possible state and gives you operations that compose. `Maybe<A>` for values
16
- that may or may not be there. `Result<E, A>` for operations that succeed or fail with a typed error.
17
- `TaskResult<E, A>` for async operations that keep failures as typed values and propagate cancellation
18
- automatically. `Op<I, E, A>` for managing repeated async interactions — retry, timeout, and
19
- concurrency strategy in one place. And, of course, there is more than that.
17
+ **pipelined** names every possible state and gives you operations that compose. `Maybe<A>` for
18
+ values that may or may not be there. `Result<E, A>` for operations that succeed or fail with a typed
19
+ error. `TaskResult<E, A>` for async operations that keep failures as typed values and propagate
20
+ cancellation automatically. `Op<I, E, A>` for managing repeated async interactions — retry, timeout,
21
+ and concurrency strategy in one place. And, of course, there is more than that.
20
22
 
21
23
  ## Documentation
22
24
 
@@ -59,7 +61,7 @@ values — the error type is part of the signature, not a runtime surprise.
59
61
  import { pipe } from "@nlozgachev/pipelined/composition";
60
62
  import { Result, TaskResult } from "@nlozgachev/pipelined/core";
61
63
 
62
- type ApiError = { status: number; message: string; };
64
+ type ApiError = { status: number; message: string };
63
65
 
64
66
  const fetchUser = (id: string): TaskResult<ApiError, User> =>
65
67
  TaskResult.tryCatch(
@@ -73,7 +75,8 @@ const fetchUser = (id: string): TaskResult<ApiError, User> =>
73
75
 
74
76
  const fetchPosts = (userId: string): TaskResult<ApiError, Post[]> =>
75
77
  TaskResult.tryCatch(
76
- (signal) => fetch(`/users/${userId}/posts`, { signal }).then((r) => r.json()),
78
+ (signal) =>
79
+ fetch(`/users/${userId}/posts`, { signal }).then((r) => r.json()),
77
80
  (e) => e as ApiError,
78
81
  );
79
82
 
@@ -115,13 +118,17 @@ import { pipe } from "@nlozgachev/pipelined/composition";
115
118
  import { Maybe } from "@nlozgachev/pipelined/core";
116
119
  import { Arr, Num, Rec, Str } from "@nlozgachev/pipelined/utils";
117
120
 
118
- type RawItem = { name: string; price: string; category: string; };
119
- type Item = { name: string; price: number; category: string; };
121
+ type RawItem = { name: string; price: string; category: string };
122
+ type Item = { name: string; price: number; category: string };
120
123
 
121
124
  const normalise = (raw: RawItem): Maybe<Item> =>
122
125
  pipe(
123
126
  Num.parse(raw.price), // "9.99" → Some(9.99), "n/a" → None
124
- Maybe.map((price) => ({ name: Str.trim(raw.name), price, category: raw.category })),
127
+ Maybe.map((price) => ({
128
+ name: Str.trim(raw.name),
129
+ price,
130
+ category: raw.category,
131
+ })),
125
132
  );
126
133
 
127
134
  const cheapestByCategory = (items: RawItem[]) =>
@@ -135,8 +142,9 @@ const cheapestByCategory = (items: RawItem[]) =>
135
142
  ```
136
143
 
137
144
  `filterMap` applies a function that returns `Maybe` and collects only the `Some` results — one step
138
- replaces a `map` followed by a `filter`. `Arr.head` returns `Maybe<Item>` rather than `Item | undefined`,
139
- so the absence is explicit in the type and the rest of the pipeline handles it the same way.
145
+ replaces a `map` followed by a `filter`. `Arr.head` returns `Maybe<Item>` rather than
146
+ `Item | undefined`, so the absence is explicit in the type and the rest of the pipeline handles it
147
+ the same way.
140
148
 
141
149
  ## Example: retry, timeout, and cancellation
142
150
 
@@ -144,8 +152,8 @@ A careful, production-minded attempt at "fetch with retry, timeout, and cancella
144
152
 
145
153
  ```ts
146
154
  type UserResult =
147
- | { ok: true; user: User; }
148
- | { ok: false; error: "Timeout" | "NetworkError"; };
155
+ | { ok: true; user: User }
156
+ | { ok: false; error: "Timeout" | "NetworkError" };
149
157
 
150
158
  async function fetchUser(
151
159
  id: string,
@@ -177,13 +185,14 @@ async function fetchUser(
177
185
  ```
178
186
 
179
187
  The signal is forwarded by hand. The timeout needs its own controller. Timed-out aborts are
180
- distinguished from external cancellation by checking `signal?.aborted`. The retry is recursive
181
- to thread the attempt count.
188
+ distinguished from external cancellation by checking `signal?.aborted`. The retry is recursive to
189
+ thread the attempt count.
182
190
 
183
191
  With **pipelined**:
184
192
 
185
193
  ```ts
186
194
  import { Op } from "@nlozgachev/pipelined/core";
195
+ import { Duration } from "@nlozgachev/pipelined/types";
187
196
 
188
197
  const fetchUser = Op.interpret(
189
198
  Op.create(
@@ -193,8 +202,11 @@ const fetchUser = Op.interpret(
193
202
  ),
194
203
  {
195
204
  strategy: "restartable",
196
- retry: { attempts: 3, backoff: (n) => n * 1000 },
197
- timeout: { ms: 5000, onTimeout: () => new ApiError("request timed out") },
205
+ retry: { attempts: 3, backoff: (n) => Duration.seconds(n) },
206
+ timeout: {
207
+ duration: Duration.seconds(5),
208
+ onTimeout: () => new ApiError("request timed out"),
209
+ },
198
210
  },
199
211
  );
200
212
  ```
@@ -220,7 +232,7 @@ fetchUser.abort();
220
232
 
221
233
  Real UIs make the same call many times — a search input fires on every keystroke, a submit button
222
234
  gets clicked twice, a polling loop needs to stop when something newer starts. Each scenario has a
223
- different answer to the same question: _what happens to the previous call when a new one arrives?_
235
+ different answer to the same question: *what happens to the previous call when a new one arrives?*
224
236
 
225
237
  `Op` makes that question a one-word configuration choice.
226
238
 
@@ -228,23 +240,26 @@ different answer to the same question: _what happens to the previous call when a
228
240
 
229
241
  ```ts
230
242
  import { Op } from "@nlozgachev/pipelined/core";
243
+ import { Duration } from "@nlozgachev/pipelined/types";
231
244
 
232
245
  const searchOp = Op.create(
233
246
  (signal) => (query: string) =>
234
- fetch(`/search?q=${query}`, { signal }).then((r) => r.json() as Promise<SearchResult[]>),
247
+ fetch(`/search?q=${query}`, { signal }).then((r) =>
248
+ r.json() as Promise<SearchResult[]>
249
+ ),
235
250
  (e) => new SearchError(e),
236
251
  );
237
252
 
238
253
  const search = Op.interpret(searchOp, {
239
254
  strategy: "restartable", // new call cancels the previous one
240
- retry: { attempts: 2, backoff: 300 },
255
+ retry: { attempts: 2, backoff: Duration.milliseconds(300) },
241
256
  });
242
257
 
243
258
  search.subscribe((state) => {
244
259
  if (Op.isPending(state)) showSpinner();
245
260
  if (Op.isRetrying(state)) showSpinner(`retrying… attempt ${state.attempt}`);
246
261
  if (Op.isOk(state)) showResults(state.value);
247
- if (Op.isError(state)) showError(state.error);
262
+ if (Op.isErr(state)) showError(state.error);
248
263
  });
249
264
 
250
265
  input.addEventListener("input", (e) => search.run(e.currentTarget.value));
@@ -255,7 +270,9 @@ input.addEventListener("input", (e) => search.run(e.currentTarget.value));
255
270
  ```ts
256
271
  const submitOp = Op.create(
257
272
  (signal) => (data: FormData) =>
258
- fetch("/orders", { method: "POST", body: data, signal }).then((r) => r.json()),
273
+ fetch("/orders", { method: "POST", body: data, signal }).then((r) =>
274
+ r.json()
275
+ ),
259
276
  (e) => new ApiError(e),
260
277
  );
261
278
 
@@ -266,7 +283,7 @@ const submit = Op.interpret(submitOp, {
266
283
  submit.subscribe((state) => {
267
284
  submitButton.disabled = Op.isPending(state);
268
285
  if (Op.isOk(state)) showConfirmation(state.value);
269
- if (Op.isError(state)) showError(state.error);
286
+ if (Op.isErr(state)) showError(state.error);
270
287
  });
271
288
 
272
289
  form.addEventListener("submit", (e) => {
@@ -275,32 +292,48 @@ form.addEventListener("submit", (e) => {
275
292
  });
276
293
  ```
277
294
 
278
- `restartable`, `exclusive`, `debounced`, `throttled`, `queue`, `buffered`, `concurrent`, `keyed`, `once` — each strategy is a complete, tested answer to one concurrency scenario. Swap the word, keep the rest of the code.
295
+ `restartable`, `exclusive`, `debounced`, `throttled`, `queue`, `buffered`, `concurrent`, `keyed`,
296
+ `once` — each strategy is a complete, tested answer to one concurrency scenario. Swap the word, keep
297
+ the rest of the code.
279
298
 
280
299
  ## What's included?
281
300
 
282
- The library covers the states you encounter in real applications: values that may be absent, operations that accumulate multiple errors, data that moves through `NotAsked >> Loading >> ( Success | Failure )`, async interactions with concurrency policies, nested immutable updates, and computations that share a common environment. Every type follows the same conventions — `map`, `chain`, `match`, `getOrElse` — so moving between them feels familiar.
301
+ The library covers the states you encounter in real applications: values that may be absent,
302
+ operations that accumulate multiple errors, data that moves through
303
+ `NotAsked >> Loading >> ( Success | Failure )`, async interactions with concurrency policies, nested
304
+ immutable updates, and computations that share a common environment. Every type follows the same
305
+ conventions — `map`, `chain`, `match`, `getOrElse` — so moving between them feels familiar.
283
306
 
284
307
  ### pipelined/core
285
308
 
286
309
  - **`Maybe<A>`** — a value that may not exist; propagates absence without null checks.
287
310
  - **`Result<E, A>`** — an operation that succeeds or fails with a typed error.
288
- - **`Validation<E, A>`** — like `Result`, but accumulates every failure instead of stopping at the first.
311
+ - **`Validation<E, A>`** — like `Result`, but accumulates every failure instead of stopping at the
312
+ first.
289
313
  - **`Task<A>`** — a lazy, infallible async operation; nothing runs until called.
290
314
  - **`TaskResult<E, A>`** — a lazy async operation that can fail with a typed error.
291
315
  - **`TaskMaybe<A>`** — a lazy async operation that may produce nothing.
292
316
  - **`TaskValidation<E, A>`** — a lazy async operation that accumulates validation errors.
293
- - **`Op<I, E, A>`** — a managed async operation with a named concurrency strategy: `restartable`, `exclusive`, `debounced`, `throttled`, `queue`, `buffered`, `concurrent`, `keyed`, or `once`. Handles retry, timeout, cancellation, and state in one place.
294
- - **`RemoteData<E, A>`** the four states of a data fetch: `NotAsked`, `Loading`, `Failure`, `Success`.
317
+ - **`Op<I, E, A>`** — a managed async operation with a named concurrency strategy: `restartable`,
318
+ `exclusive`, `debounced`, `throttled`, `queue`, `buffered`, `concurrent`, `keyed`, or `once`.
319
+ Handles retry, timeout, cancellation, and state in one place.
320
+ - **`RemoteData<E, A>`** — the four states of a data fetch: `NotAsked`, `Loading`, `Failure`,
321
+ `Success`.
295
322
  - **`These<A, B>`** — an inclusive OR: holds a first value, a second, or both at once.
296
- - **`Lens<S, A>`** — focus on a required field in a nested structure. Read, set, and modify immutably.
323
+ - **`Lens<S, A>`** — focus on a required field in a nested structure. Read, set, and modify
324
+ immutably.
297
325
  - **`Optional<S, A>`** — like `Lens`, but the target may be absent (nullable fields, array indices).
298
- - **`Reader<R, A>`** — a computation that depends on an environment `R`, supplied once at the boundary.
299
- - **`State<S, A>`** — a computation that reads and updates a state value, threaded explicitly through the chain.
300
- - **`Logged<W, A>`** — a computation that accumulates a log alongside its value; no console output, just data.
326
+ - **`Reader<R, A>`** — a computation that depends on an environment `R`, supplied once at the
327
+ boundary.
328
+ - **`State<S, A>`** — a computation that reads and updates a state value, threaded explicitly
329
+ through the chain.
330
+ - **`Logged<W, A>`** — a computation that accumulates a log alongside its value; no console output,
331
+ just data.
301
332
  - **`Predicate<A>`** — a typed boolean function, composable with `and`, `or`, `not`, and `using`.
302
- - **`Refinement<A, B>`** — a type predicate that narrows `A` to `B` at runtime; composes with `Predicate`.
303
- - **`Resource<E, A>`** — an acquire/release pair for safe resource management in `TaskResult` pipelines.
333
+ - **`Refinement<A, B>`** — a type predicate that narrows `A` to `B` at runtime; composes with
334
+ `Predicate`.
335
+ - **`Resource<E, A>`** — an acquire/release pair for safe resource management in `TaskResult`
336
+ pipelines.
304
337
  - **`Deferred<A>`** — an infallible async value: a thenable that always resolves, never rejects.
305
338
  - **`Tuple<A, B>`** — a typed pair with `first`, `second`, `map`, `swap`, and `fold`.
306
339
 
@@ -311,14 +344,16 @@ Everyday utilities for built-in JS types.
311
344
  - **`Arr`** — array utilities, data-last, returning `Maybe` instead of `undefined`.
312
345
  - **`Rec`** — record/object utilities, data-last, with `Maybe`-returning key lookup.
313
346
  - **`Dict`** — `ReadonlyMap<K, V>` utilities: `lookup`, `groupBy`, `upsert`, set operations.
314
- - **`Uniq`** — `ReadonlySet<A>` utilities: `insert`, `remove`, `union`, `intersection`, `difference`.
347
+ - **`Uniq`** — `ReadonlySet<A>` utilities: `insert`, `remove`, `union`, `intersection`,
348
+ `difference`.
315
349
  - **`Num`** — number utilities: `range`, `clamp`, `between`, safe `parse`, and curried arithmetic.
316
- - **`Str`** — string utilities: `split`, `trim`, `words`, `lines`, and safe `parse.int` / `parse.float`.
350
+ - **`Str`** — string utilities: `split`, `trim`, `words`, `lines`, and safe `parse.int` /
351
+ `parse.float`.
317
352
 
318
353
  Every utility is benchmarked against its native equivalent. The data-last currying adds a function
319
354
  call; that is the expected cost of composability. Operations that exceeded a reasonable overhead
320
- have custom implementations that in several cases run faster than the native method they replace. See the
321
- [benchmarks page](https://pipelined.lozgachev.dev/appendix/benchmarks) for the methodology.
355
+ have custom implementations that in several cases run faster than the native method they replace.
356
+ See the [benchmarks page](https://pipelined.lozgachev.dev/appendix/benchmarks) for the methodology.
322
357
 
323
358
  ### pipelined/types
324
359
 
@@ -24,19 +24,19 @@ type WithLog<T> = {
24
24
  /** Retry policy for `Op.interpret`. */
25
25
  type RetryOptions<E> = {
26
26
  readonly attempts: number;
27
- readonly backoff?: number | ((attempt: number) => number);
27
+ readonly backoff?: Duration | ((attempt: number) => Duration);
28
28
  readonly when?: (error: E) => boolean;
29
29
  };
30
30
  /** Timeout policy for `Op.interpret`. Wraps the entire retry sequence. */
31
31
  type TimeoutOptions<E> = {
32
- readonly ms: number;
32
+ readonly duration: Duration;
33
33
  readonly onTimeout: () => E;
34
34
  };
35
35
  type WithTimeout<E> = {
36
36
  readonly timeout?: TimeoutOptions<E>;
37
37
  };
38
- type WithMs = {
39
- readonly ms: number;
38
+ type WithDuration = {
39
+ readonly duration: Duration;
40
40
  };
41
41
  type WithN = {
42
42
  readonly n: number;
@@ -48,22 +48,22 @@ type WithSize = {
48
48
  readonly size?: number;
49
49
  };
50
50
  type WithCooldown = {
51
- readonly cooldown?: number;
51
+ readonly cooldown?: Duration;
52
52
  };
53
53
  type WithMinInterval = {
54
- readonly minInterval?: number;
54
+ readonly minInterval?: Duration;
55
55
  };
56
56
 
57
57
  type Ok<A> = WithKind<"Ok"> & WithValue<A>;
58
- type Error<E> = WithKind<"Error"> & WithError<E>;
58
+ type Err<E> = WithKind<"Err"> & WithError<E>;
59
59
  /**
60
- * Result represents a value that can be one of two types: a success (Ok) or a failure (Error).
60
+ * Result represents a value that can be one of two types: a success (Ok) or a failure (Err).
61
61
  * Use Result when an operation can fail with a meaningful error value.
62
62
  *
63
63
  * @example
64
64
  * ```ts
65
65
  * const divide = (a: number, b: number): Result<string, number> =>
66
- * b === 0 ? Result.error("Division by zero") : Result.ok(a / b);
66
+ * b === 0 ? Result.err("Division by zero") : Result.ok(a / b);
67
67
  *
68
68
  * pipe(
69
69
  * divide(10, 2),
@@ -72,7 +72,7 @@ type Error<E> = WithKind<"Error"> & WithError<E>;
72
72
  * ); // 10
73
73
  * ```
74
74
  */
75
- type Result<E, A> = Ok<A> | Error<E>;
75
+ type Result<E, A> = Ok<A> | Err<E>;
76
76
  declare namespace Result {
77
77
  /**
78
78
  * Creates a successful Result with the given value.
@@ -81,17 +81,17 @@ declare namespace Result {
81
81
  /**
82
82
  * Creates a failed Result with the given error.
83
83
  */
84
- const error: <E>(e: E) => Error<E>;
84
+ const err: <E>(e: E) => Err<E>;
85
85
  /**
86
- * Type guard that checks if an Result is Ok.
86
+ * Type guard that checks if a Result is Ok.
87
87
  */
88
88
  const isOk: <E, A>(data: Result<E, A>) => data is Ok<A>;
89
89
  /**
90
- * Type guard that checks if an Result is Error.
90
+ * Type guard that checks if a Result is Err.
91
91
  */
92
- const isError: <E, A>(data: Result<E, A>) => data is Error<E>;
92
+ const isErr: <E, A>(data: Result<E, A>) => data is Err<E>;
93
93
  /**
94
- * Creates an Result from a function that may throw.
94
+ * Creates a Result from a function that may throw.
95
95
  * Catches any errors and transforms them using the onError function.
96
96
  *
97
97
  * @example
@@ -105,21 +105,21 @@ declare namespace Result {
105
105
  */
106
106
  const tryCatch: <E, A>(f: () => A, onError: (e: unknown) => E) => Result<E, A>;
107
107
  /**
108
- * Transforms the success value inside an Result.
108
+ * Transforms the success value inside a Result.
109
109
  *
110
110
  * @example
111
111
  * ```ts
112
112
  * pipe(Result.ok(5), Result.map(n => n * 2)); // Ok(10)
113
- * pipe(Result.error("error"), Result.map(n => n * 2)); // Error("error")
113
+ * pipe(Result.err("error"), Result.map(n => n * 2)); // Err("error")
114
114
  * ```
115
115
  */
116
116
  const map: <E, A, B>(f: (a: A) => B) => (data: Result<E, A>) => Result<E, B>;
117
117
  /**
118
- * Transforms the error value inside an Result.
118
+ * Transforms the error value inside a Result.
119
119
  *
120
120
  * @example
121
121
  * ```ts
122
- * pipe(Result.error("oops"), Result.mapError(e => e.toUpperCase())); // Error("OOPS")
122
+ * pipe(Result.err("oops"), Result.mapError(e => e.toUpperCase())); // Err("OOPS")
123
123
  * ```
124
124
  */
125
125
  const mapError: <E, F, A>(f: (e: E) => F) => (data: Result<E, A>) => Result<F, A>;
@@ -130,15 +130,15 @@ declare namespace Result {
130
130
  * @example
131
131
  * ```ts
132
132
  * const validatePositive = (n: number): Result<string, number> =>
133
- * n > 0 ? Result.ok(n) : Result.error("Must be positive");
133
+ * n > 0 ? Result.ok(n) : Result.err("Must be positive");
134
134
  *
135
135
  * pipe(Result.ok(5), Result.chain(validatePositive)); // Ok(5)
136
- * pipe(Result.ok(-1), Result.chain(validatePositive)); // Error("Must be positive")
136
+ * pipe(Result.ok(-1), Result.chain(validatePositive)); // Err("Must be positive")
137
137
  * ```
138
138
  */
139
139
  const chain: <E, A, B>(f: (a: A) => Result<E, B>) => (data: Result<E, A>) => Result<E, B>;
140
140
  /**
141
- * Extracts the value from an Result by providing handlers for both cases.
141
+ * Extracts the value from a Result by providing handlers for both cases.
142
142
  *
143
143
  * @example
144
144
  * ```ts
@@ -178,8 +178,8 @@ declare namespace Result {
178
178
  * @example
179
179
  * ```ts
180
180
  * pipe(Result.ok(5), Result.getOrElse(() => 0)); // 5
181
- * pipe(Result.error("error"), Result.getOrElse(() => 0)); // 0
182
- * pipe(Result.error("error"), Result.getOrElse(() => null)); // null — typed as number | null
181
+ * pipe(Result.err("error"), Result.getOrElse(() => 0)); // 0
182
+ * pipe(Result.err("error"), Result.getOrElse(() => null)); // null — typed as number | null
183
183
  * ```
184
184
  */
185
185
  const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Result<E, A>) => A | B;
@@ -204,7 +204,7 @@ declare namespace Result {
204
204
  * @example
205
205
  * ```ts
206
206
  * pipe(
207
- * Result.error("not found"),
207
+ * Result.err("not found"),
208
208
  * Result.tapError(e => console.error("validation failed:", e)),
209
209
  * Result.chain(save),
210
210
  * )
@@ -218,8 +218,8 @@ declare namespace Result {
218
218
  * @example
219
219
  * ```ts
220
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`)); // Error("-1 is not positive")
222
- * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Error("empty string")
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")
223
223
  * ```
224
224
  */
225
225
  const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Result<E, A>;
@@ -229,7 +229,7 @@ declare namespace Result {
229
229
  *
230
230
  * @example
231
231
  * ```ts
232
- * pipe(null, Result.fromNullable(() => "is null")); // Error("is null")
232
+ * pipe(null, Result.fromNullable(() => "is null")); // Err("is null")
233
233
  * pipe(42, Result.fromNullable(() => "is null")); // Ok(42)
234
234
  * ```
235
235
  */
@@ -240,7 +240,7 @@ declare namespace Result {
240
240
  *
241
241
  * @example
242
242
  * ```ts
243
- * pipe(Maybe.none(), Result.fromMaybe(() => "is none")); // Error("is none")
243
+ * pipe(Maybe.none(), Result.fromMaybe(() => "is none")); // Err("is none")
244
244
  * pipe(Maybe.some(42), Result.fromMaybe(() => "is none")); // Ok(42)
245
245
  * ```
246
246
  */
@@ -257,7 +257,7 @@ declare namespace Result {
257
257
  * );
258
258
  *
259
259
  * safeParse('{"a":1}'); // Ok({ a: 1 })
260
- * safeParse('invalid'); // Error(Error)
260
+ * safeParse('invalid'); // Err(Error)
261
261
  * ```
262
262
  */
263
263
  const fromThrowable: <Args extends readonly unknown[], A, E>(f: (...args: Args) => A, onError: (e: unknown) => E) => (...args: Args) => Result<E, A>;
@@ -273,25 +273,25 @@ declare namespace Result {
273
273
  * @example
274
274
  * ```ts
275
275
  * pipe(
276
- * Result.error(new Error("not found")),
276
+ * Result.err(new Error("not found")),
277
277
  * Result.recoverUnless(e => e.message === "fatal", () => Result.ok(0))
278
278
  * ); // Ok(0)
279
279
  * ```
280
280
  */
281
281
  const recoverUnless: <E, A, B>(isBlocked: (e: E) => boolean, fallback: () => Result<E, B>) => (data: Result<E, A>) => Result<E, A | B>;
282
282
  /**
283
- * Converts a Result to an Maybe.
283
+ * Converts a Result to a Maybe.
284
284
  * Ok becomes Some, Err becomes None (the error is discarded).
285
285
  *
286
286
  * @example
287
287
  * ```ts
288
288
  * Result.toMaybe(Result.ok(42)); // Some(42)
289
- * Result.toMaybe(Result.error("oops")); // None
289
+ * Result.toMaybe(Result.err("oops")); // None
290
290
  * ```
291
291
  */
292
292
  const toMaybe: <E, A>(data: Result<E, A>) => Maybe<A>;
293
293
  /**
294
- * Applies a function wrapped in an Result to a value wrapped in an Result.
294
+ * Applies a function wrapped in a Result to a value wrapped in a Result.
295
295
  *
296
296
  * @example
297
297
  * ```ts
@@ -314,14 +314,13 @@ type None = WithKind<"None">;
314
314
  *
315
315
  * @example
316
316
  * ```ts
317
- * const findUser = (id: string): Maybe<User> =>
318
- * users.has(id) ? Maybe.some(users.get(id)!) : Maybe.none();
317
+ * const user = { name: "Alice", email: Maybe.some("alice@example.com") };
319
318
  *
320
319
  * pipe(
321
- * findUser("123"),
322
- * Maybe.map(user => user.name),
323
- * Maybe.getOrElse(() => "Unknown")
324
- * );
320
+ * user.email,
321
+ * Maybe.map(email => email.toUpperCase()),
322
+ * Maybe.getOrElse(() => "NO EMAIL")
323
+ * ); // "ALICE@EXAMPLE.COM"
325
324
  * ```
326
325
  */
327
326
  type Maybe<T> = Some<T> | None;
@@ -376,7 +375,7 @@ declare namespace Maybe {
376
375
  */
377
376
  const fromPredicate: <A>(pred: (a: A) => boolean) => (a: A) => Maybe<A>;
378
377
  /**
379
- * Converts an Maybe to a Result.
378
+ * Converts a Maybe to a Result.
380
379
  * Some becomes Ok, None becomes Err with the provided error.
381
380
  *
382
381
  * @example
@@ -394,13 +393,13 @@ declare namespace Maybe {
394
393
  */
395
394
  const toResult: <E>(onNone: () => E) => <A>(data: Maybe<A>) => Result<E, A>;
396
395
  /**
397
- * Creates an Maybe from a Result.
396
+ * Creates a Maybe from a Result.
398
397
  * Ok becomes Some, Err becomes None (the error is discarded).
399
398
  *
400
399
  * @example
401
400
  * ```ts
402
401
  * Maybe.fromResult(Result.ok(42)); // Some(42)
403
- * Maybe.fromResult(Result.error("oops")); // None
402
+ * Maybe.fromResult(Result.err("oops")); // None
404
403
  * ```
405
404
  */
406
405
  const fromResult: <E, A>(data: Result<E, A>) => Maybe<A>;
@@ -464,7 +463,7 @@ declare namespace Maybe {
464
463
  some: (a: A) => B;
465
464
  }) => (data: Maybe<A>) => B;
466
465
  /**
467
- * Returns the value inside an Maybe, or a default value if None.
466
+ * Returns the value inside a Maybe, or a default value if None.
468
467
  * The default is a thunk `() => B` — evaluated only when the Maybe is None.
469
468
  * The default can be a different type, widening the result to `A | B`.
470
469
  *
@@ -882,7 +881,7 @@ declare namespace Task {
882
881
  */
883
882
  const all: <T extends readonly Task<unknown>[]>(tasks: T) => Task<{ [K in keyof T]: T[K] extends Task<infer A> ? A : never; }>;
884
883
  /**
885
- * Delays the execution of a Task by the specified milliseconds or duration.
884
+ * Delays the execution of a Task by the specified duration.
886
885
  * Useful for debouncing or rate limiting.
887
886
  *
888
887
  * @example
@@ -893,10 +892,10 @@ declare namespace Task {
893
892
  * )(); // Resolves after 1 second
894
893
  * ```
895
894
  */
896
- const delay: (ms: number | Duration) => <A>(data: Task<A>) => Task<A>;
895
+ const delay: (duration: Duration) => <A>(data: Task<A>) => Task<A>;
897
896
  /**
898
897
  * Runs a Task a fixed number of times sequentially, collecting all results into an array.
899
- * An optional delay (ms or Duration) can be inserted between runs.
898
+ * An optional delay duration can be inserted between runs.
900
899
  *
901
900
  * @example
902
901
  * ```ts
@@ -908,11 +907,11 @@ declare namespace Task {
908
907
  */
909
908
  const repeat: (options: {
910
909
  times: number;
911
- delay?: number | Duration;
910
+ delay?: Duration;
912
911
  }) => <A>(task: Task<A>) => Task<readonly A[]>;
913
912
  /**
914
913
  * Runs a Task repeatedly until the result satisfies a predicate, returning that result.
915
- * An optional delay (ms or Duration) can be inserted between runs.
914
+ * An optional delay duration can be inserted between runs.
916
915
  * An optional `maxAttempts` cap stops the loop after N calls — the last value is returned
917
916
  * regardless of whether the predicate was satisfied.
918
917
  *
@@ -926,7 +925,7 @@ declare namespace Task {
926
925
  */
927
926
  const repeatUntil: <A>(options: {
928
927
  when: (a: A) => boolean;
929
- delay?: number | Duration;
928
+ delay?: Duration;
930
929
  maxAttempts?: number;
931
930
  }) => (task: Task<A>) => Task<A>;
932
931
  /**
@@ -973,8 +972,8 @@ declare namespace Task {
973
972
  const sequential: <A>(tasks: ReadonlyArray<Task<A>>) => Task<ReadonlyArray<A>>;
974
973
  /**
975
974
  * Converts a `Task<A>` into a `Task<Result<E, A>>`, resolving to `Err` if the
976
- * Task does not complete within the given time. The inner Task receives an
977
- * `AbortSignal` that fires when the deadline passes, so operations like `fetch`
975
+ * Task does not complete within the given duration. The inner Task receives an
976
+ * `AbortSignal` that fires when the deadline passes, so asynchronous operations
978
977
  * that accept a signal are cancelled rather than left dangling.
979
978
  *
980
979
  * @example
@@ -986,7 +985,7 @@ declare namespace Task {
986
985
  * );
987
986
  * ```
988
987
  */
989
- const timeout: <E>(ms: number | Duration, onTimeout: () => E) => <A>(task: Task<A>) => Task<Result<E, A>>;
988
+ const timeout: <E>(duration: Duration, onTimeout: () => E) => <A>(task: Task<A>) => Task<Result<E, A>>;
990
989
  /**
991
990
  * Creates a Task paired with an `abort` handle. Calling `abort()` cancels the
992
991
  * current in-flight call immediately. Unlike a one-shot abort, calling `task()`
@@ -1018,7 +1017,7 @@ declare namespace Task {
1018
1017
  * @example
1019
1018
  * ```ts
1020
1019
  * const name = await pipe(
1021
- * fetchConfig,
1020
+ * loadConfig,
1022
1021
  * Task.map(config => config.name),
1023
1022
  * Task.run(),
1024
1023
  * );
@@ -1027,4 +1026,4 @@ declare namespace Task {
1027
1026
  const run: (signal?: AbortSignal) => <A>(task: Task<A>) => Promise<A>;
1028
1027
  }
1029
1028
 
1030
- 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 Error 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 WithMs as m, type WithN as n, type WithErrors as o, type WithFirst as p, type WithSecond as q };
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 };