@nlozgachev/pipelined 0.28.0 → 0.29.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
@@ -34,18 +34,18 @@ import { Maybe } from "@nlozgachev/pipelined/core";
34
34
  import { Num, Str } from "@nlozgachev/pipelined/utils";
35
35
 
36
36
  const parseDiscount = (raw: string): string =>
37
- pipe(
38
- raw,
39
- Str.trim,
40
- Num.parse, // "10" → Some(10), "abc" → None
41
- Maybe.filter((n) => n >= 0 && n <= 100), // out of range → None
42
- Maybe.map((n) => `${n}% off`),
43
- Maybe.getOrElse(() => "No discount"),
44
- );
37
+ pipe(
38
+ raw,
39
+ Str.trim,
40
+ Num.parse, // "10" → Some(10), "abc" → None
41
+ Maybe.filter((n) => n >= 0 && n <= 100), // out of range → None
42
+ Maybe.map((n) => `${n}% off`),
43
+ Maybe.getOrElse(() => "No discount"),
44
+ );
45
45
 
46
46
  parseDiscount(" 15 "); // "15% off"
47
- parseDiscount("150"); // "No discount"
48
- parseDiscount("abc"); // "No discount"
47
+ parseDiscount("150"); // "No discount"
48
+ parseDiscount("abc"); // "No discount"
49
49
  ```
50
50
 
51
51
  Every step that sees `None` is skipped. The fallback runs once, at the end.
@@ -59,35 +59,35 @@ values — the error type is part of the signature, not a runtime surprise.
59
59
  import { pipe } from "@nlozgachev/pipelined/composition";
60
60
  import { Result, TaskResult } from "@nlozgachev/pipelined/core";
61
61
 
62
- type ApiError = { status: number; message: string };
62
+ type ApiError = { status: number; message: string; };
63
63
 
64
64
  const fetchUser = (id: string): TaskResult<ApiError, User> =>
65
- TaskResult.tryCatch(
66
- (signal) =>
67
- fetch(`/users/${id}`, { signal }).then((r) => {
68
- if (!r.ok) throw { status: r.status, message: r.statusText };
69
- return r.json() as Promise<User>;
70
- }),
71
- (e) => e as ApiError,
72
- );
65
+ TaskResult.tryCatch(
66
+ (signal) =>
67
+ fetch(`/users/${id}`, { signal }).then((r) => {
68
+ if (!r.ok) throw { status: r.status, message: r.statusText };
69
+ return r.json() as Promise<User>;
70
+ }),
71
+ (e) => e as ApiError,
72
+ );
73
73
 
74
74
  const fetchPosts = (userId: string): TaskResult<ApiError, Post[]> =>
75
- TaskResult.tryCatch(
76
- (signal) => fetch(`/users/${userId}/posts`, { signal }).then((r) => r.json()),
77
- (e) => e as ApiError,
78
- );
75
+ TaskResult.tryCatch(
76
+ (signal) => fetch(`/users/${userId}/posts`, { signal }).then((r) => r.json()),
77
+ (e) => e as ApiError,
78
+ );
79
79
 
80
80
  // Chain two requests — the AbortSignal propagates to both automatically
81
81
  const userWithPosts = (id: string) =>
82
- pipe(
83
- fetchUser(id),
84
- TaskResult.chain((user) =>
85
- pipe(
86
- fetchPosts(user.id),
87
- TaskResult.map((posts) => ({ ...user, posts })),
88
- )
89
- ),
90
- );
82
+ pipe(
83
+ fetchUser(id),
84
+ TaskResult.chain((user) =>
85
+ pipe(
86
+ fetchPosts(user.id),
87
+ TaskResult.map((posts) => ({ ...user, posts })),
88
+ )
89
+ ),
90
+ );
91
91
  ```
92
92
 
93
93
  `userWithPosts` is a lazy function — nothing runs until called. The `AbortSignal` threads through
@@ -95,12 +95,13 @@ both requests: abort at any point and whichever request is in flight is cancelle
95
95
 
96
96
  ```ts
97
97
  const controller = new AbortController();
98
- const result = await userWithPosts("42")(controller.signal);
98
+ const fetchUserWithPosts = userWithPosts("42"); // build the lazy task
99
+ const result = await fetchUserWithPosts(controller.signal); // run it — signal controls cancellation
99
100
 
100
101
  if (Result.isOk(result)) {
101
- render(result.value); // { ...User, posts: Post[] }
102
+ render(result.value); // { ...User, posts: Post[] }
102
103
  } else {
103
- showError(result.error); // ApiError — typed, not unknown
104
+ showError(result.error); // ApiError — typed, not unknown
104
105
  }
105
106
  ```
106
107
 
@@ -114,23 +115,23 @@ import { pipe } from "@nlozgachev/pipelined/composition";
114
115
  import { Maybe } from "@nlozgachev/pipelined/core";
115
116
  import { Arr, Num, Rec, Str } from "@nlozgachev/pipelined/utils";
116
117
 
117
- type RawItem = { name: string; price: string; category: string };
118
- type Item = { name: string; price: number; category: string };
118
+ type RawItem = { name: string; price: string; category: string; };
119
+ type Item = { name: string; price: number; category: string; };
119
120
 
120
121
  const normalise = (raw: RawItem): Maybe<Item> =>
121
- pipe(
122
- Num.parse(raw.price), // "9.99" → Some(9.99), "n/a" → None
123
- Maybe.map((price) => ({ name: Str.trim(raw.name), price, category: raw.category })),
124
- );
122
+ pipe(
123
+ 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 })),
125
+ );
125
126
 
126
127
  const cheapestByCategory = (items: RawItem[]) =>
127
- pipe(
128
- items,
129
- Arr.filterMap(normalise), // parse + drop unparseable prices in one pass
130
- Arr.sortBy((a, b) => a.price - b.price), // ascending price
131
- Arr.groupBy((item) => item.category), // Record<string, NonEmptyList<Item>>
132
- Rec.map((group) => Arr.head(group)), // cheapest per category — Maybe<Item>
133
- );
128
+ pipe(
129
+ items,
130
+ Arr.filterMap(normalise), // parse + drop unparseable prices in one pass
131
+ Arr.sortBy((a, b) => a.price - b.price), // ascending price
132
+ Arr.groupBy((item) => item.category), // Record<string, NonEmptyList<Item>>
133
+ Rec.map((group) => Arr.head(group)), // cheapest per category — Maybe<Item>
134
+ );
134
135
  ```
135
136
 
136
137
  `filterMap` applies a function that returns `Maybe` and collects only the `Some` results — one step
@@ -143,35 +144,35 @@ A careful, production-minded attempt at "fetch with retry, timeout, and cancella
143
144
 
144
145
  ```ts
145
146
  type UserResult =
146
- | { ok: true; user: User; }
147
- | { ok: false; error: "Timeout" | "NetworkError"; };
147
+ | { ok: true; user: User; }
148
+ | { ok: false; error: "Timeout" | "NetworkError"; };
148
149
 
149
150
  async function fetchUser(
150
- id: string,
151
- signal?: AbortSignal,
151
+ id: string,
152
+ signal?: AbortSignal,
152
153
  ): Promise<UserResult> {
153
- async function attempt(n: number): Promise<UserResult> {
154
- const controller = new AbortController();
155
- const timerId = setTimeout(() => controller.abort(), 5000);
156
- signal?.addEventListener("abort", () => controller.abort(), { once: true });
157
-
158
- try {
159
- const res = await fetch(`/users/${id}`, { signal: controller.signal });
160
- clearTimeout(timerId);
161
- return { ok: true, user: await res.json() };
162
- } catch (e) {
163
- clearTimeout(timerId);
164
- if ((e as Error).name === "AbortError" && !signal?.aborted) {
165
- return { ok: false, error: "Timeout" };
166
- }
167
- if (n < 3) {
168
- await new Promise((r) => setTimeout(r, n * 1000));
169
- return attempt(n + 1);
170
- }
171
- return { ok: false, error: "NetworkError" };
172
- }
173
- }
174
- return attempt(1);
154
+ async function attempt(n: number): Promise<UserResult> {
155
+ const controller = new AbortController();
156
+ const timerId = setTimeout(() => controller.abort(), 5000);
157
+ signal?.addEventListener("abort", () => controller.abort(), { once: true });
158
+
159
+ try {
160
+ const res = await fetch(`/users/${id}`, { signal: controller.signal });
161
+ clearTimeout(timerId);
162
+ return { ok: true, user: await res.json() };
163
+ } catch (e) {
164
+ clearTimeout(timerId);
165
+ if ((e as Error).name === "AbortError" && !signal?.aborted) {
166
+ return { ok: false, error: "Timeout" };
167
+ }
168
+ if (n < 3) {
169
+ await new Promise((r) => setTimeout(r, n * 1000));
170
+ return attempt(n + 1);
171
+ }
172
+ return { ok: false, error: "NetworkError" };
173
+ }
174
+ }
175
+ return attempt(1);
175
176
  }
176
177
  ```
177
178
 
@@ -185,15 +186,16 @@ With **pipelined**:
185
186
  import { Op } from "@nlozgachev/pipelined/core";
186
187
 
187
188
  const fetchUser = Op.interpret(
188
- Op.create(
189
- (signal) => (id: string) => fetch(`/users/${id}`, { signal }).then((r) => r.json() as Promise<User>),
190
- (e) => new ApiError(e),
191
- ),
192
- {
193
- strategy: "restartable",
194
- retry: { attempts: 3, backoff: (n) => n * 1000 },
195
- timeout: { ms: 5000, onTimeout: () => new ApiError("request timed out") },
196
- },
189
+ Op.create(
190
+ (signal) => (id: string) =>
191
+ fetch(`/users/${id}`, { signal }).then((r) => r.json() as Promise<User>),
192
+ (e) => new ApiError(e),
193
+ ),
194
+ {
195
+ strategy: "restartable",
196
+ retry: { attempts: 3, backoff: (n) => n * 1000 },
197
+ timeout: { ms: 5000, onTimeout: () => new ApiError("request timed out") },
198
+ },
197
199
  );
198
200
  ```
199
201
 
@@ -205,9 +207,9 @@ propagation, and timeout wiring are handled automatically. The outcome type is t
205
207
  const outcome = await fetchUser.run("42");
206
208
 
207
209
  if (Op.isOk(outcome)) {
208
- render(outcome.value); // User
210
+ render(outcome.value); // User
209
211
  } else if (Op.isErr(outcome)) {
210
- showError(outcome.error); // ApiError, not unknown
212
+ showError(outcome.error); // ApiError, not unknown
211
213
  }
212
214
 
213
215
  // explicit cancellation — in-flight request is aborted immediately
@@ -228,21 +230,21 @@ different answer to the same question: _what happens to the previous call when a
228
230
  import { Op } from "@nlozgachev/pipelined/core";
229
231
 
230
232
  const searchOp = Op.create(
231
- (signal) => (query: string) =>
232
- fetch(`/search?q=${query}`, { signal }).then((r) => r.json() as Promise<SearchResult[]>),
233
- (e) => new SearchError(e),
233
+ (signal) => (query: string) =>
234
+ fetch(`/search?q=${query}`, { signal }).then((r) => r.json() as Promise<SearchResult[]>),
235
+ (e) => new SearchError(e),
234
236
  );
235
237
 
236
238
  const search = Op.interpret(searchOp, {
237
- strategy: "restartable", // new call cancels the previous one
238
- retry: { attempts: 2, backoff: 300 },
239
+ strategy: "restartable", // new call cancels the previous one
240
+ retry: { attempts: 2, backoff: 300 },
239
241
  });
240
242
 
241
243
  search.subscribe((state) => {
242
- if (state.kind === "Pending") showSpinner();
243
- if (state.kind === "Retrying") showSpinner(`retrying… attempt ${state.attempt}`);
244
- if (state.kind === "Ok") showResults(state.value);
245
- if (state.kind === "Err") showError(state.error);
244
+ if (Op.isPending(state)) showSpinner();
245
+ if (Op.isRetrying(state)) showSpinner(`retrying… attempt ${state.attempt}`);
246
+ if (Op.isOk(state)) showResults(state.value);
247
+ if (Op.isError(state)) showError(state.error);
246
248
  });
247
249
 
248
250
  input.addEventListener("input", (e) => search.run(e.currentTarget.value));
@@ -252,23 +254,24 @@ input.addEventListener("input", (e) => search.run(e.currentTarget.value));
252
254
 
253
255
  ```ts
254
256
  const submitOp = Op.create(
255
- (signal) => (data: FormData) => fetch("/orders", { method: "POST", body: data, signal }).then((r) => r.json()),
256
- (e) => new ApiError(e),
257
+ (signal) => (data: FormData) =>
258
+ fetch("/orders", { method: "POST", body: data, signal }).then((r) => r.json()),
259
+ (e) => new ApiError(e),
257
260
  );
258
261
 
259
262
  const submit = Op.interpret(submitOp, {
260
- strategy: "exclusive", // in-flight? new calls are dropped immediately
263
+ strategy: "exclusive", // in-flight? new calls are dropped immediately
261
264
  });
262
265
 
263
266
  submit.subscribe((state) => {
264
- submitButton.disabled = state.kind === "Pending";
265
- if (state.kind === "Ok") showConfirmation(state.value);
266
- if (state.kind === "Err") showError(state.error);
267
+ submitButton.disabled = Op.isPending(state);
268
+ if (Op.isOk(state)) showConfirmation(state.value);
269
+ if (Op.isError(state)) showError(state.error);
267
270
  });
268
271
 
269
272
  form.addEventListener("submit", (e) => {
270
- e.preventDefault();
271
- submit.run(new FormData(form)); // double-clicks and rage-clicks are ignored
273
+ e.preventDefault();
274
+ submit.run(new FormData(form)); // double-clicks and rage-clicks are ignored
272
275
  });
273
276
  ```
274
277
 
@@ -107,15 +107,15 @@ type WithMinInterval = {
107
107
  };
108
108
 
109
109
  type Ok<A> = WithKind<"Ok"> & WithValue<A>;
110
- type Err<E> = WithKind<"Error"> & WithError<E>;
110
+ type Error<E> = WithKind<"Error"> & WithError<E>;
111
111
  /**
112
- * Result represents a value that can be one of two types: a success (Ok) or a failure (Err).
112
+ * Result represents a value that can be one of two types: a success (Ok) or a failure (Error).
113
113
  * Use Result when an operation can fail with a meaningful error value.
114
114
  *
115
115
  * @example
116
116
  * ```ts
117
117
  * const divide = (a: number, b: number): Result<string, number> =>
118
- * b === 0 ? Result.err("Division by zero") : Result.ok(a / b);
118
+ * b === 0 ? Result.error("Division by zero") : Result.ok(a / b);
119
119
  *
120
120
  * pipe(
121
121
  * divide(10, 2),
@@ -124,7 +124,7 @@ type Err<E> = WithKind<"Error"> & WithError<E>;
124
124
  * ); // 10
125
125
  * ```
126
126
  */
127
- type Result<E, A> = Ok<A> | Err<E>;
127
+ type Result<E, A> = Ok<A> | Error<E>;
128
128
  declare namespace Result {
129
129
  /**
130
130
  * Creates a successful Result with the given value.
@@ -133,15 +133,15 @@ declare namespace Result {
133
133
  /**
134
134
  * Creates a failed Result with the given error.
135
135
  */
136
- const err: <E>(error: E) => Err<E>;
136
+ const error: <E>(e: E) => Error<E>;
137
137
  /**
138
138
  * Type guard that checks if an Result is Ok.
139
139
  */
140
140
  const isOk: <E, A>(data: Result<E, A>) => data is Ok<A>;
141
141
  /**
142
- * Type guard that checks if an Result is Err.
142
+ * Type guard that checks if an Result is Error.
143
143
  */
144
- const isErr: <E, A>(data: Result<E, A>) => data is Err<E>;
144
+ const isError: <E, A>(data: Result<E, A>) => data is Error<E>;
145
145
  /**
146
146
  * Creates an Result from a function that may throw.
147
147
  * Catches any errors and transforms them using the onError function.
@@ -162,7 +162,7 @@ declare namespace Result {
162
162
  * @example
163
163
  * ```ts
164
164
  * pipe(Result.ok(5), Result.map(n => n * 2)); // Ok(10)
165
- * pipe(Result.err("error"), Result.map(n => n * 2)); // Err("error")
165
+ * pipe(Result.error("error"), Result.map(n => n * 2)); // Error("error")
166
166
  * ```
167
167
  */
168
168
  const map: <E, A, B>(f: (a: A) => B) => (data: Result<E, A>) => Result<E, B>;
@@ -171,7 +171,7 @@ declare namespace Result {
171
171
  *
172
172
  * @example
173
173
  * ```ts
174
- * pipe(Result.err("oops"), Result.mapError(e => e.toUpperCase())); // Err("OOPS")
174
+ * pipe(Result.error("oops"), Result.mapError(e => e.toUpperCase())); // Error("OOPS")
175
175
  * ```
176
176
  */
177
177
  const mapError: <E, F, A>(f: (e: E) => F) => (data: Result<E, A>) => Result<F, A>;
@@ -182,10 +182,10 @@ declare namespace Result {
182
182
  * @example
183
183
  * ```ts
184
184
  * const validatePositive = (n: number): Result<string, number> =>
185
- * n > 0 ? Result.ok(n) : Result.err("Must be positive");
185
+ * n > 0 ? Result.ok(n) : Result.error("Must be positive");
186
186
  *
187
187
  * pipe(Result.ok(5), Result.chain(validatePositive)); // Ok(5)
188
- * pipe(Result.ok(-1), Result.chain(validatePositive)); // Err("Must be positive")
188
+ * pipe(Result.ok(-1), Result.chain(validatePositive)); // Error("Must be positive")
189
189
  * ```
190
190
  */
191
191
  const chain: <E, A, B>(f: (a: A) => Result<E, B>) => (data: Result<E, A>) => Result<E, B>;
@@ -230,8 +230,8 @@ declare namespace Result {
230
230
  * @example
231
231
  * ```ts
232
232
  * pipe(Result.ok(5), Result.getOrElse(() => 0)); // 5
233
- * pipe(Result.err("error"), Result.getOrElse(() => 0)); // 0
234
- * pipe(Result.err("error"), Result.getOrElse(() => null)); // null — typed as number | null
233
+ * pipe(Result.error("error"), Result.getOrElse(() => 0)); // 0
234
+ * pipe(Result.error("error"), Result.getOrElse(() => null)); // null — typed as number | null
235
235
  * ```
236
236
  */
237
237
  const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Result<E, A>) => A | B;
@@ -256,7 +256,7 @@ declare namespace Result {
256
256
  * @example
257
257
  * ```ts
258
258
  * pipe(
259
- * Result.err("not found"),
259
+ * Result.error("not found"),
260
260
  * Result.tapError(e => console.error("validation failed:", e)),
261
261
  * Result.chain(save),
262
262
  * )
@@ -270,8 +270,8 @@ declare namespace Result {
270
270
  * @example
271
271
  * ```ts
272
272
  * pipe(5, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Ok(5)
273
- * pipe(-1, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Err("-1 is not positive")
274
- * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Err("empty string")
273
+ * pipe(-1, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Error("-1 is not positive")
274
+ * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Error("empty string")
275
275
  * ```
276
276
  */
277
277
  const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Result<E, A>;
@@ -287,7 +287,7 @@ declare namespace Result {
287
287
  * @example
288
288
  * ```ts
289
289
  * pipe(
290
- * Result.err(new Error("not found")),
290
+ * Result.error(new Error("not found")),
291
291
  * Result.recoverUnless(e => e.message === "fatal", () => Result.ok(0))
292
292
  * ); // Ok(0)
293
293
  * ```
@@ -300,7 +300,7 @@ declare namespace Result {
300
300
  * @example
301
301
  * ```ts
302
302
  * Result.toMaybe(Result.ok(42)); // Some(42)
303
- * Result.toMaybe(Result.err("oops")); // None
303
+ * Result.toMaybe(Result.error("oops")); // None
304
304
  * ```
305
305
  */
306
306
  const toMaybe: <E, A>(data: Result<E, A>) => Maybe<A>;
@@ -414,7 +414,7 @@ declare namespace Maybe {
414
414
  * @example
415
415
  * ```ts
416
416
  * Maybe.fromResult(Result.ok(42)); // Some(42)
417
- * Maybe.fromResult(Result.err("oops")); // None
417
+ * Maybe.fromResult(Result.error("oops")); // None
418
418
  * ```
419
419
  */
420
420
  const fromResult: <E, A>(data: Result<E, A>) => Maybe<A>;
@@ -797,6 +797,19 @@ declare namespace Task {
797
797
  task: Task<A>;
798
798
  abort: () => void;
799
799
  };
800
+ /**
801
+ * Executes a task with an optional signal. Use as a terminal step in a `pipe` chain.
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * const name = await pipe(
806
+ * fetchConfig,
807
+ * Task.map(config => config.name),
808
+ * Task.run(),
809
+ * );
810
+ * ```
811
+ */
812
+ const run: (signal?: AbortSignal) => <A>(task: Task<A>) => Promise<A>;
800
813
  }
801
814
 
802
- export { Deferred as D, type Err 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 WithLog as a, type WithKind as b, type WithError as c, type RetryOptions as d, type TimeoutOptions as e, type WithTimeout as f, type WithMinInterval as g, type WithCooldown as h, type WithConcurrency as i, type WithSize as j, type WithMs as k, type WithN as l, type WithErrors as m, type WithFirst as n, type WithSecond as o };
815
+ export { Deferred as D, type Error 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 WithLog as a, type WithKind as b, type WithError as c, type RetryOptions as d, type TimeoutOptions as e, type WithTimeout as f, type WithMinInterval as g, type WithCooldown as h, type WithConcurrency as i, type WithSize as j, type WithMs as k, type WithN as l, type WithErrors as m, type WithFirst as n, type WithSecond as o };
@@ -107,15 +107,15 @@ type WithMinInterval = {
107
107
  };
108
108
 
109
109
  type Ok<A> = WithKind<"Ok"> & WithValue<A>;
110
- type Err<E> = WithKind<"Error"> & WithError<E>;
110
+ type Error<E> = WithKind<"Error"> & WithError<E>;
111
111
  /**
112
- * Result represents a value that can be one of two types: a success (Ok) or a failure (Err).
112
+ * Result represents a value that can be one of two types: a success (Ok) or a failure (Error).
113
113
  * Use Result when an operation can fail with a meaningful error value.
114
114
  *
115
115
  * @example
116
116
  * ```ts
117
117
  * const divide = (a: number, b: number): Result<string, number> =>
118
- * b === 0 ? Result.err("Division by zero") : Result.ok(a / b);
118
+ * b === 0 ? Result.error("Division by zero") : Result.ok(a / b);
119
119
  *
120
120
  * pipe(
121
121
  * divide(10, 2),
@@ -124,7 +124,7 @@ type Err<E> = WithKind<"Error"> & WithError<E>;
124
124
  * ); // 10
125
125
  * ```
126
126
  */
127
- type Result<E, A> = Ok<A> | Err<E>;
127
+ type Result<E, A> = Ok<A> | Error<E>;
128
128
  declare namespace Result {
129
129
  /**
130
130
  * Creates a successful Result with the given value.
@@ -133,15 +133,15 @@ declare namespace Result {
133
133
  /**
134
134
  * Creates a failed Result with the given error.
135
135
  */
136
- const err: <E>(error: E) => Err<E>;
136
+ const error: <E>(e: E) => Error<E>;
137
137
  /**
138
138
  * Type guard that checks if an Result is Ok.
139
139
  */
140
140
  const isOk: <E, A>(data: Result<E, A>) => data is Ok<A>;
141
141
  /**
142
- * Type guard that checks if an Result is Err.
142
+ * Type guard that checks if an Result is Error.
143
143
  */
144
- const isErr: <E, A>(data: Result<E, A>) => data is Err<E>;
144
+ const isError: <E, A>(data: Result<E, A>) => data is Error<E>;
145
145
  /**
146
146
  * Creates an Result from a function that may throw.
147
147
  * Catches any errors and transforms them using the onError function.
@@ -162,7 +162,7 @@ declare namespace Result {
162
162
  * @example
163
163
  * ```ts
164
164
  * pipe(Result.ok(5), Result.map(n => n * 2)); // Ok(10)
165
- * pipe(Result.err("error"), Result.map(n => n * 2)); // Err("error")
165
+ * pipe(Result.error("error"), Result.map(n => n * 2)); // Error("error")
166
166
  * ```
167
167
  */
168
168
  const map: <E, A, B>(f: (a: A) => B) => (data: Result<E, A>) => Result<E, B>;
@@ -171,7 +171,7 @@ declare namespace Result {
171
171
  *
172
172
  * @example
173
173
  * ```ts
174
- * pipe(Result.err("oops"), Result.mapError(e => e.toUpperCase())); // Err("OOPS")
174
+ * pipe(Result.error("oops"), Result.mapError(e => e.toUpperCase())); // Error("OOPS")
175
175
  * ```
176
176
  */
177
177
  const mapError: <E, F, A>(f: (e: E) => F) => (data: Result<E, A>) => Result<F, A>;
@@ -182,10 +182,10 @@ declare namespace Result {
182
182
  * @example
183
183
  * ```ts
184
184
  * const validatePositive = (n: number): Result<string, number> =>
185
- * n > 0 ? Result.ok(n) : Result.err("Must be positive");
185
+ * n > 0 ? Result.ok(n) : Result.error("Must be positive");
186
186
  *
187
187
  * pipe(Result.ok(5), Result.chain(validatePositive)); // Ok(5)
188
- * pipe(Result.ok(-1), Result.chain(validatePositive)); // Err("Must be positive")
188
+ * pipe(Result.ok(-1), Result.chain(validatePositive)); // Error("Must be positive")
189
189
  * ```
190
190
  */
191
191
  const chain: <E, A, B>(f: (a: A) => Result<E, B>) => (data: Result<E, A>) => Result<E, B>;
@@ -230,8 +230,8 @@ declare namespace Result {
230
230
  * @example
231
231
  * ```ts
232
232
  * pipe(Result.ok(5), Result.getOrElse(() => 0)); // 5
233
- * pipe(Result.err("error"), Result.getOrElse(() => 0)); // 0
234
- * pipe(Result.err("error"), Result.getOrElse(() => null)); // null — typed as number | null
233
+ * pipe(Result.error("error"), Result.getOrElse(() => 0)); // 0
234
+ * pipe(Result.error("error"), Result.getOrElse(() => null)); // null — typed as number | null
235
235
  * ```
236
236
  */
237
237
  const getOrElse: <E, A, B>(defaultValue: () => B) => (data: Result<E, A>) => A | B;
@@ -256,7 +256,7 @@ declare namespace Result {
256
256
  * @example
257
257
  * ```ts
258
258
  * pipe(
259
- * Result.err("not found"),
259
+ * Result.error("not found"),
260
260
  * Result.tapError(e => console.error("validation failed:", e)),
261
261
  * Result.chain(save),
262
262
  * )
@@ -270,8 +270,8 @@ declare namespace Result {
270
270
  * @example
271
271
  * ```ts
272
272
  * pipe(5, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Ok(5)
273
- * pipe(-1, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Err("-1 is not positive")
274
- * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Err("empty string")
273
+ * pipe(-1, Result.fromPredicate(n => n > 0, n => `${n} is not positive`)); // Error("-1 is not positive")
274
+ * pipe("", Result.fromPredicate(s => s.length > 0, () => "empty string")); // Error("empty string")
275
275
  * ```
276
276
  */
277
277
  const fromPredicate: <E, A>(pred: (a: A) => boolean, onFalse: (a: A) => E) => (a: A) => Result<E, A>;
@@ -287,7 +287,7 @@ declare namespace Result {
287
287
  * @example
288
288
  * ```ts
289
289
  * pipe(
290
- * Result.err(new Error("not found")),
290
+ * Result.error(new Error("not found")),
291
291
  * Result.recoverUnless(e => e.message === "fatal", () => Result.ok(0))
292
292
  * ); // Ok(0)
293
293
  * ```
@@ -300,7 +300,7 @@ declare namespace Result {
300
300
  * @example
301
301
  * ```ts
302
302
  * Result.toMaybe(Result.ok(42)); // Some(42)
303
- * Result.toMaybe(Result.err("oops")); // None
303
+ * Result.toMaybe(Result.error("oops")); // None
304
304
  * ```
305
305
  */
306
306
  const toMaybe: <E, A>(data: Result<E, A>) => Maybe<A>;
@@ -414,7 +414,7 @@ declare namespace Maybe {
414
414
  * @example
415
415
  * ```ts
416
416
  * Maybe.fromResult(Result.ok(42)); // Some(42)
417
- * Maybe.fromResult(Result.err("oops")); // None
417
+ * Maybe.fromResult(Result.error("oops")); // None
418
418
  * ```
419
419
  */
420
420
  const fromResult: <E, A>(data: Result<E, A>) => Maybe<A>;
@@ -797,6 +797,19 @@ declare namespace Task {
797
797
  task: Task<A>;
798
798
  abort: () => void;
799
799
  };
800
+ /**
801
+ * Executes a task with an optional signal. Use as a terminal step in a `pipe` chain.
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * const name = await pipe(
806
+ * fetchConfig,
807
+ * Task.map(config => config.name),
808
+ * Task.run(),
809
+ * );
810
+ * ```
811
+ */
812
+ const run: (signal?: AbortSignal) => <A>(task: Task<A>) => Promise<A>;
800
813
  }
801
814
 
802
- export { Deferred as D, type Err 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 WithLog as a, type WithKind as b, type WithError as c, type RetryOptions as d, type TimeoutOptions as e, type WithTimeout as f, type WithMinInterval as g, type WithCooldown as h, type WithConcurrency as i, type WithSize as j, type WithMs as k, type WithN as l, type WithErrors as m, type WithFirst as n, type WithSecond as o };
815
+ export { Deferred as D, type Error 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 WithLog as a, type WithKind as b, type WithError as c, type RetryOptions as d, type TimeoutOptions as e, type WithTimeout as f, type WithMinInterval as g, type WithCooldown as h, type WithConcurrency as i, type WithSize as j, type WithMs as k, type WithN as l, type WithErrors as m, type WithFirst as n, type WithSecond as o };
@@ -3,7 +3,7 @@ import {
3
3
  Maybe,
4
4
  Result,
5
5
  Task
6
- } from "./chunk-7JF44HJH.mjs";
6
+ } from "./chunk-SDGDJ7CU.mjs";
7
7
  import {
8
8
  isNonEmptyList
9
9
  } from "./chunk-DBIC62UV.mjs";
@@ -166,7 +166,7 @@ var Arr;
166
166
  const result = [];
167
167
  for (const a of data) {
168
168
  const r = await Deferred.toPromise(f(a)());
169
- if (Result.isErr(r)) return r;
169
+ if (Result.isError(r)) return r;
170
170
  result.push(r.value);
171
171
  }
172
172
  return Result.ok(result);
@@ -307,6 +307,14 @@ var Dict;
307
307
  }
308
308
  return result;
309
309
  };
310
+ Dict2.filterMap = (f) => (m) => {
311
+ const result = new globalThis.Map();
312
+ for (const [key, value] of m) {
313
+ const mapped = f(value);
314
+ if (mapped.kind === "Some") result.set(key, mapped.value);
315
+ }
316
+ return result;
317
+ };
310
318
  Dict2.union = (other) => (m) => {
311
319
  const result = new globalThis.Map(m);
312
320
  for (const [k, v] of other) {