@ndriadev/futurable 2.3.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,69 +1,187 @@
1
- export interface FuturableLike<T> {
1
+ /**
2
+ * Result type for safe operations that may succeed or fail.
3
+ * Provides a discriminated union for type-safe error handling without try-catch.
4
+ *
5
+ * @template T - The type of the success value
6
+ * @template E - The type of the error (defaults to Error)
7
+ */
8
+ type SafeResult<T, E = Error> = {
9
+ success: true;
10
+ data: T;
11
+ error: null;
12
+ } | {
13
+ success: false;
14
+ data: null;
15
+ error: E;
16
+ };
17
+ /**
18
+ * A thenable-like interface that represents a value that may be available now, in the future, or never.
19
+ * Compatible with both Promises and Futurables, allowing for flexible composition.
20
+ *
21
+ * @template T - The type of the value that will be resolved
22
+ */
23
+ interface FuturableLike<T> {
2
24
  /**
3
- * Attaches callbacks for the resolution and/or rejection of the Futurable.
4
- * @param onfulfilled The callback to execute when the Futurable is resolved.
5
- * @param onrejected The callback to execute when the Futurable is rejected.
6
- * @returns A Futurable for the completion of which ever callback is executed.
7
- */
25
+ * Attaches callbacks for the resolution and/or rejection of the Futurable.
26
+ *
27
+ * @template TResult1 - The type returned by the fulfillment callback
28
+ * @template TResult2 - The type returned by the rejection callback
29
+ * @param onfulfilled - The callback to execute when the Futurable is resolved
30
+ * @param onrejected - The callback to execute when the Futurable is rejected
31
+ * @returns A new Futurable for the completion of whichever callback is executed
32
+ */
8
33
  then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null): FuturableLike<TResult1 | TResult2>;
9
34
  }
10
- export interface FuturableResolve<T> {
35
+ /**
36
+ * Function signature for resolving a Futurable with a value.
37
+ * Accepts a direct value, a Promise, or another Futurable.
38
+ *
39
+ * @template T - The type of the value to resolve with
40
+ * @param value - The value, Promise, or Futurable to resolve with
41
+ */
42
+ interface FuturableResolve<T> {
11
43
  (value: T | FuturableLike<T> | PromiseLike<T>): void;
12
44
  }
13
- export interface FuturableReject {
45
+ /**
46
+ * Function signature for rejecting a Futurable with a reason.
47
+ *
48
+ * @param reason - The reason for rejection (typically an Error)
49
+ */
50
+ interface FuturableReject {
14
51
  (reason?: any): void;
15
52
  }
16
- export interface FuturableUtils<T> {
53
+ /**
54
+ * Utility methods and properties available within a Futurable executor.
55
+ * Provides advanced features like cancellation, delays, HTTP fetching, and signal management.
56
+ *
57
+ * @template T - The type of value the Futurable will resolve to
58
+ */
59
+ interface FuturableUtils<T> {
17
60
  /**
18
- * Internal futurable signal
19
- */
61
+ * Internal AbortSignal for cancellation support.
62
+ * This signal is aborted when the Futurable is cancelled.
63
+ */
20
64
  signal: AbortSignal;
21
65
  /**
22
- * Cancel the futurable if it is to be executed or if it is still executing.
23
- */
66
+ * Cancels the Futurable if it is pending or currently executing.
67
+ * Triggers the abort signal and executes any registered onCancel callbacks.
68
+ */
24
69
  cancel: () => void;
25
70
  /**
26
- * Executes the callback passed as a parameter when the futurable is cancelled.
27
- * @param cb: callback
28
- */
71
+ * Registers a callback to be executed when the Futurable is cancelled.
72
+ * Multiple callbacks can be registered and will be executed in order.
73
+ *
74
+ * @param cb - The callback function to execute on cancellation
75
+ */
29
76
  onCancel: (cb: () => void) => void;
30
77
  /**
31
- * Waits for timer, then executes callback with the futurable value and returns the result obtained from the invocation.
32
- * @param cb: callback executed after timer
33
- * @param timer: timer to wait (in milliseconds)
34
- */
35
- delay: <TResult = T, TResult2 = never>(cb: () => TResult, timer: number) => FuturableLike<TResult | TResult2>;
78
+ * Waits for a specified duration, then executes a callback and returns the result.
79
+ * The delay is cancellable via the Futurable's signal.
80
+ *
81
+ * @template TResult - The type returned by the callback
82
+ * @template TResult2 - The type in case of rejection (defaults to never)
83
+ * @param cb - The callback to execute after the timer expires
84
+ * @param timer - The delay duration in milliseconds
85
+ * @returns A new Futurable that resolves with the callback's result
86
+ */
87
+ delay: <TResult = T, TResult2 = never>(cb: () => TResult, timer: number) => Futurable<TResult | TResult2>;
36
88
  /**
37
- * Waits for timer parameter (in milliseconds) before returning the value.
38
- * @param timer: timer to wait (in milliseconds)
39
- */
40
- sleep: (timer: number) => FuturableLike<void>;
89
+ * Pauses execution for a specified duration.
90
+ * Equivalent to delay with an empty callback.
91
+ *
92
+ * @param timer - The duration to wait in milliseconds
93
+ * @returns A Futurable that resolves after the timer expires
94
+ */
95
+ sleep: (timer: number) => Futurable<void>;
41
96
  /**
42
- * Extension of the fetch API with cancellation support. Url parameter can be a string or a function with receive value from futurable chaining as paremeter.
43
- * @param url: url to fetch
44
- * @param opts: fetch options
45
- */
97
+ * Extension of the native Fetch API with automatic cancellation support.
98
+ * The request is automatically cancelled if the Futurable is cancelled.
99
+ *
100
+ * @param url - The URL to fetch
101
+ * @param opts - Optional Fetch API options (signal will be automatically provided)
102
+ * @returns A Futurable that resolves with the Response object
103
+ */
46
104
  fetch: (url: string, opts?: RequestInit) => Futurable<Response>;
47
105
  /**
48
- * Takes a promise and transforms it into a futurizable. Promise can be also a function that receives value from futurable chaining as parameter.
49
- * @param promise: Promise to futurize
50
- */
106
+ * Converts a standard Promise into a Futurable with cancellation support.
107
+ * The original Promise cannot be cancelled, but the Futurable wrapper can be.
108
+ *
109
+ * @template TResult - The type the Promise resolves to
110
+ * @param promise - The Promise to convert
111
+ * @returns A Futurable wrapping the Promise
112
+ */
51
113
  futurizable: <TResult = any>(promise: Promise<TResult>) => Futurable<TResult>;
52
114
  }
53
- export type FuturableExecutor<T> = (resolve: FuturableResolve<T>, reject: FuturableReject,
54
115
  /**
55
- * Object containing implemented functionalities.
56
- */
57
- utils: FuturableUtils<T>) => void;
58
- export type FuturableIterable<T = any> = Iterable<FuturableLike<T> | PromiseLike<T> | T>;
116
+ * Executor function signature for creating a new Futurable.
117
+ * Similar to the Promise executor, but with additional utilities for cancellation and async operations.
118
+ *
119
+ * @template T - The type of value the Futurable will resolve to
120
+ * @param resolve - Function to resolve the Futurable with a value
121
+ * @param reject - Function to reject the Futurable with a reason
122
+ * @param utils - Utility object containing cancellation and async helpers
123
+ */
124
+ type FuturableExecutor<T> = (resolve: FuturableResolve<T>, reject: FuturableReject, utils: FuturableUtils<T>) => void;
125
+ /**
126
+ * An iterable collection of values that can be Futurables, Promises, or plain values.
127
+ * Used by static methods like Futurable.all(), Futurable.race(), etc.
128
+ *
129
+ * @template T - The type of values in the iterable
130
+ */
131
+ type FuturableIterable<T = any> = Iterable<FuturableLike<T> | PromiseLike<T> | T>;
132
+ /**
133
+ * Return type of Futurable.withResolvers() static method.
134
+ * Provides direct access to the Futurable and its control functions.
135
+ *
136
+ * @template T - The type of value the Futurable will resolve to
137
+ */
59
138
  interface FuturableWithResolvers<T> {
139
+ /** The created Futurable or Promise instance */
60
140
  promise: Futurable<T> | Promise<T>;
141
+ /** Function to resolve the Futurable with a value */
61
142
  resolve: (value: T | PromiseLike<T> | FuturableLike<T>) => void;
143
+ /** Function to reject the Futurable with a reason */
62
144
  reject: (reason?: any) => void;
145
+ /** Function to cancel the Futurable */
63
146
  cancel: () => void;
147
+ /** Utility object with advanced Futurable features */
64
148
  utils: FuturableUtils<T>;
65
149
  }
66
- export declare class Futurable<T> extends Promise<T> {
150
+ /**
151
+ * Return type of Futurable.polling() static method.
152
+ * Provides controls for a polling operation.
153
+ */
154
+ interface FuturablePollingController {
155
+ /** Stops the polling and cancels any pending operations */
156
+ cancel: () => void;
157
+ /** Registers an error handler for polling operations */
158
+ catch: (onrejected: (reason: unknown) => void) => void;
159
+ }
160
+ /**
161
+ * A cancellable Promise implementation with extended async utilities.
162
+ *
163
+ * Futurable extends the native Promise API with:
164
+ * - Built-in cancellation via AbortSignal
165
+ * - Chainable delay and sleep operations
166
+ * - Integrated fetch with automatic cancellation
167
+ * - Polling capabilities
168
+ * - Promise-to-Futurable conversion
169
+ *
170
+ * @template T - The type of value this Futurable will resolve to
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * // Basic usage with cancellation
175
+ * const futurable = new Futurable((resolve, reject, { signal }) => {
176
+ * const timeoutId = setTimeout(() => resolve('done'), 5000);
177
+ * signal.addEventListener('abort', () => clearTimeout(timeoutId));
178
+ * });
179
+ *
180
+ * // Cancel after 1 second
181
+ * setTimeout(() => futurable.cancel(), 1000);
182
+ * ```
183
+ */
184
+ declare class Futurable<T> extends Promise<T> {
67
185
  private controller;
68
186
  private internalSignal;
69
187
  private idsTimeout;
@@ -71,184 +189,2839 @@ export declare class Futurable<T> extends Promise<T> {
71
189
  static get [Symbol.species](): typeof Futurable;
72
190
  get [Symbol.toStringTag](): string;
73
191
  /**
74
- * Return internal futurable signal
75
- * @returns {AbortSignal}
76
- */
192
+ * Returns the internal AbortSignal used for cancellation.
193
+ * This signal is aborted when cancel() is called.
194
+ *
195
+ * @returns The internal AbortSignal
196
+ */
77
197
  get signal(): AbortSignal;
78
198
  private clearTimeout;
79
199
  /**
80
- * Attaches callbacks for the resolution and/or rejection of the Futurable.
81
- * @param {((value: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>) | undefined | null} onfulfilled
82
- * @param {((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null} onrejected
83
- * @returns {Futurable<TResult1 | TResult2>}
84
- */
200
+ * Attaches callbacks for the resolution and/or rejection of the Futurable.
201
+ * Chainable method that returns a new Futurable.
202
+ *
203
+ * @template TResult1 - Type returned by the fulfillment callback
204
+ * @template TResult2 - Type returned by the rejection callback
205
+ * @param onfulfilled - Callback executed when the Futurable is resolved
206
+ * @param onrejected - Callback executed when the Futurable is rejected
207
+ * @returns A new Futurable for the completion of the callback
208
+ */
85
209
  then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null): Futurable<TResult1 | TResult2>;
86
210
  /**
87
- * Attaches a callback for only the rejection of the Futurable.
88
- * @param {((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null} onRejected
89
- * @returns {Futurable<T | TResult2>}
90
- */
211
+ * Attaches a callback for only the rejection of the Futurable.
212
+ *
213
+ * @template TResult2 - Type returned by the rejection callback
214
+ * @param onRejected - Callback executed when the Futurable is rejected
215
+ * @returns A new Futurable
216
+ */
91
217
  catch<TResult2 = never>(onRejected: ((reason: any) => TResult2 | PromiseLike<TResult2> | FuturableLike<TResult2>) | undefined | null): Futurable<T | TResult2>;
92
218
  /**
93
- * Attaches a callback that is invoked when the Futurable is settled (fulfilled or rejected).
94
- * The resolved value cannot be modified from the callback.
95
- * @param {() => void | undefined | null} onfinally
96
- * @returns {Futurable<T>}
97
- */
219
+ * Attaches a callback that is invoked when the Futurable is settled (fulfilled or rejected).
220
+ * The resolved value cannot be modified from the callback.
221
+ *
222
+ * @param onfinally - Callback executed when the Futurable settles
223
+ * @returns A new Futurable with the same value
224
+ */
98
225
  finally(onfinally: () => void | undefined | null): Futurable<T>;
99
226
  /**
100
- * Cancel the futurable if it is to be executed or if it is still executing.
101
- */
227
+ * Cancels the Futurable if it is pending or currently executing.
228
+ * Aborts the internal signal and triggers all registered onCancel callbacks.
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * const futurable = new Futurable((resolve) => {
233
+ * setTimeout(() => resolve('done'), 5000);
234
+ * });
235
+ * futurable.cancel(); // Cancels the operation
236
+ * ```
237
+ */
102
238
  cancel(): void;
103
239
  /**
104
- * Waits for timer, then executes callback with the futurable value and returns the result obtained from the invocation.
105
- * @param {(val: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>} cb - callback executed after timer with futurable chain value as parameter
106
- * @param {number} timer - timer to wait (in milliseconds)
107
- */
240
+ * Waits for a specified duration, then executes a callback with the Futurable's value.
241
+ * The delay is cancellable via the Futurable's signal.
242
+ *
243
+ * @template TResult1 - Type returned by the callback
244
+ * @template TResult2 - Type in case of rejection
245
+ * @param cb - Callback executed after the delay, receives the resolved value
246
+ * @param timer - Delay duration in milliseconds
247
+ * @returns A new Futurable that resolves with the callback's result
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * Futurable.resolve(5)
252
+ * .delay(val => val * 2, 1000) // Wait 1s, then multiply by 2
253
+ * .then(result => console.log(result)); // Logs: 10
254
+ * ```
255
+ */
108
256
  delay<TResult1 = T, TResult2 = never>(cb: (val: T) => TResult1 | PromiseLike<TResult1> | FuturableLike<TResult1>, timer: number): Futurable<TResult1 | TResult2>;
109
257
  /**
110
- * Waits for timer parameter (in milliseconds) before returning the value.
111
- * @param {number} timer - timer to wait (in milliseconds)
112
- * @returns {Futurable<T>}
113
- */
258
+ * Pauses execution for a specified duration before passing through the value.
259
+ * Equivalent to delay with an identity callback.
260
+ *
261
+ * @param timer - Duration to wait in milliseconds
262
+ * @returns A new Futurable that resolves with the same value after the delay
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * Futurable.resolve('hello')
267
+ * .sleep(2000) // Wait 2 seconds
268
+ * .then(val => console.log(val)); // Logs: "hello" after 2s
269
+ * ```
270
+ */
114
271
  sleep(timer: number): Futurable<T>;
115
272
  /**
116
- * Extension of the fetch API with cancellation support. Url parameter can be a string or a function with receive value from futurable chaining as paremeter.
117
- * @param {string| ((val?:T)=>string)} url - url to fetch or function with futurable chaining value that returns url to fetch
118
- * @param {object | RequestInit | ((val?: T) => RequestInit)} opts - fetch options or function with futurable chaining value that return fetch options
119
- * @returns {Futurable<Response>}
120
- */
121
- fetch(url: string | ((val?: T) => string), opts?: object | RequestInit | ((val?: T) => RequestInit)): Futurable<Response>;
273
+ * Performs an HTTP fetch operation with automatic cancellation support.
274
+ * The request is automatically cancelled if the Futurable is cancelled.
275
+ *
276
+ * @param url - URL to fetch, or a function that receives the Futurable's value and returns a URL
277
+ * @param opts - Fetch options, or a function that receives the Futurable's value and returns fetch options
278
+ * @returns A new Futurable that resolves with the Response object
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * Futurable.resolve('users')
283
+ * .fetch(endpoint => `https://api.example.com/${endpoint}`)
284
+ * .then(response => response.json())
285
+ * .then(data => console.log(data));
286
+ * ```
287
+ */
288
+ fetch(url: string | ((val: T) => string), opts?: object | RequestInit | ((val: T) => RequestInit)): Futurable<Response>;
122
289
  /**
123
- * Executes the callback passed as a parameter when the futurable is cancelled.
124
- * @param {()=>void} cb
125
- * @returns {Futurable<TResult1 | TResult2>}
126
- */
290
+ * Registers a callback to be executed when the Futurable is cancelled.
291
+ * Useful for cleanup operations or aborting dependent async tasks.
292
+ *
293
+ * @template TResult1 - Type in case of resolution
294
+ * @template TResult2 - Type in case of rejection
295
+ * @param cb - Callback executed on cancellation
296
+ * @returns A new Futurable
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * const futurable = new Futurable((resolve) => {
301
+ * const task = startLongTask();
302
+ * setTimeout(() => resolve('done'), 5000);
303
+ * }).onCancel(() => {
304
+ * console.log('Operation cancelled, cleaning up...');
305
+ * });
306
+ * ```
307
+ */
127
308
  onCancel<TResult1 = void, TResult2 = never>(cb: () => void): Futurable<TResult1 | TResult2>;
128
309
  /**
129
- * Takes a promise and transforms it into a futurizable. Promise can be also a function that receives value from futurable chaining as parameter.
130
- * @param {Promise<TResult1> | ((val?: T) => Promise<TResult1>)} promise - Promise to futurize or function that return promise with futurable chaining value as parameter
131
- * @returns {Futurable<TResult1 | TResult2>}
132
- */
310
+ * Converts a Promise into a Futurable with cancellation support.
311
+ * The Promise can be provided directly or via a function that receives the current value.
312
+ *
313
+ * @template TResult1 - Type the Promise resolves to
314
+ * @template TResult2 - Type in case of rejection
315
+ * @param promise - Promise to convert, or a function that receives the Futurable's value and returns a Promise
316
+ * @returns A new Futurable wrapping the Promise
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * Futurable.resolve(123)
321
+ * .futurizable(val => fetch(`/api/${val}`).then(r => r.json()))
322
+ * .then(data => console.log(data));
323
+ * ```
324
+ */
133
325
  futurizable<TResult1 = T, TResult2 = never>(promise: Promise<TResult1> | ((val?: T) => Promise<TResult1>)): Futurable<TResult1 | TResult2>;
134
326
  /**
135
- * Creates a new resolved futurable. Creates a new resolved futurable for the provided value.
136
- * @returns {Futurable<void>}
327
+ * Wraps the Futurable in a safe execution context that never throws.
328
+ * Returns a result object containing either the resolved value or the error,
329
+ * eliminating the need for try-catch blocks or .catch() handlers.
330
+ *
331
+ * This method is particularly useful in async/await contexts where you want
332
+ * to handle errors explicitly without wrapping code in try-catch blocks.
333
+ *
334
+ * @template TError - The type of the error (defaults to unknown)
335
+ * @returns A Futurable that resolves to a SafeResult containing either data or error
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * // Instead of try-catch:
340
+ * const result = await Futurable.fetch('/api/data')
341
+ * .then(r => r.json())
342
+ * .safe();
343
+ *
344
+ * if (result.success) {
345
+ * console.log('Data:', result.data);
346
+ * } else {
347
+ * console.error('Error:', result.error);
348
+ * }
349
+ * ```
350
+ *
351
+ * @example
352
+ * ```typescript
353
+ * // Chaining multiple operations safely:
354
+ * const result = await Futurable.resolve(5)
355
+ * .delay(val => val * 2, 1000)
356
+ * .fetch(val => `/api/item/${val}`)
357
+ * .futurizable(r => r.json())
358
+ * .safe();
359
+ *
360
+ * if (result.success) {
361
+ * // TypeScript knows result.data is the JSON response
362
+ * processData(result.data);
363
+ * } else {
364
+ * // TypeScript knows result.error exists
365
+ * logError(result.error);
366
+ * }
367
+ * ```
137
368
  */
369
+ safe<TError = unknown>(): Futurable<SafeResult<T, TError>>;
370
+ /**
371
+ * Creates a new resolved Futurable without a value (resolves to void).
372
+ *
373
+ * @returns A Futurable that immediately resolves to void
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * Futurable.resolve()
378
+ * .delay(() => 'hello', 1000)
379
+ * .then(val => console.log(val)); // Logs: "hello" after 1s
380
+ * ```
381
+ */
138
382
  static resolve(): Futurable<void>;
139
383
  /**
140
- * @param {T | PromiseLike<T> | FuturableLike<T>} value
141
- * @param {AbortSignal} [signal]
142
- * @returns {Futurable<T>}
143
- */
384
+ * Creates a new resolved Futurable for the provided value.
385
+ *
386
+ * @template T - Type of the value to resolve with
387
+ * @param value - The value, Promise, or Futurable to resolve with
388
+ * @param signal - Optional AbortSignal for cancellation coordination
389
+ * @returns A Futurable that immediately resolves with the value
390
+ *
391
+ * @example
392
+ * ```typescript
393
+ * Futurable.resolve(42)
394
+ * .then(val => console.log(val)); // Logs: 42
395
+ * ```
396
+ */
144
397
  static resolve<T = any>(value: T | PromiseLike<T> | FuturableLike<T>, signal?: AbortSignal): Futurable<T>;
145
398
  /**
146
- * Creates a new rejected futurable for the provided reason.
147
- * @param {any} [reason]
148
- * @param {AbortSignal} [signal]
149
- * @returns {Futurable<T>}
150
- */
399
+ * Creates a new rejected Futurable for the provided reason.
400
+ *
401
+ * @template T - Type of value (never used for rejected Futurables)
402
+ * @param reason - The reason for rejection (typically an Error)
403
+ * @param signal - Optional AbortSignal for cancellation coordination
404
+ * @returns A Futurable that immediately rejects with the reason
405
+ *
406
+ * @example
407
+ * ```typescript
408
+ * Futurable.reject(new Error('Failed'))
409
+ * .catch(err => console.error(err)); // Logs the error
410
+ * ```
411
+ */
151
412
  static reject<T = never>(reason?: any, signal?: AbortSignal): Futurable<T>;
152
413
  /**
153
- * OnCancel static method. It accepts a callback or a object with cb property and an optional signal.
154
- * @param {{cb: () => T, signal: AbortSignal|undefined}} options
155
- * @returns {Futurable<T>}
156
- */
414
+ * Creates a Futurable that resolves when cancelled.
415
+ * Useful for creating cancellation-aware cleanup logic.
416
+ *
417
+ * @template T - Type returned by the callback
418
+ * @param options - Configuration object
419
+ * @param options.cb - Callback executed on cancellation
420
+ * @param options.signal - Optional external AbortSignal
421
+ * @returns A Futurable that resolves with the callback's result when cancelled
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * const cleanup = Futurable.onCancel({
426
+ * cb: () => console.log('Cleanup performed')
427
+ * });
428
+ * cleanup.cancel(); // Triggers the callback
429
+ * ```
430
+ */
157
431
  static onCancel<T = void>({ cb, signal }: {
158
432
  cb: () => T;
159
433
  signal?: AbortSignal;
160
434
  }): Futurable<T>;
161
435
  /**
162
- * Delay static method. It accepts a object with timer and cb properties and an optional signal property.
163
- * @param {{cb: () => T, timer: number, signal: AbortSignal|undefined}} options
164
- * @returns {Futurable<T|TResult2>}
165
- */
436
+ * Creates a Futurable that executes a callback after a specified delay.
437
+ *
438
+ * @template T - Type returned by the callback
439
+ * @template TResult2 - Type in case of rejection
440
+ * @param options - Configuration object
441
+ * @param options.cb - Callback to execute after the delay
442
+ * @param options.timer - Delay duration in milliseconds
443
+ * @param options.signal - Optional AbortSignal for cancellation
444
+ * @returns A Futurable that resolves with the callback's result after the delay
445
+ *
446
+ * @example
447
+ * ```typescript
448
+ * Futurable.delay({
449
+ * cb: () => 'Hello after delay',
450
+ * timer: 2000
451
+ * }).then(msg => console.log(msg)); // Logs after 2s
452
+ * ```
453
+ */
166
454
  static delay<T = any, TResult2 = never>({ cb, timer, signal }: {
167
455
  cb: () => T;
168
456
  timer: number;
169
457
  signal?: AbortSignal;
170
458
  }): Futurable<T | TResult2>;
171
459
  /**
172
- * Sleep static method. It accepts a timer or a object with timer property and an optional signal.
173
- * @param {{timer: number, signal: AbortSignal|undefined}} options
174
- * @returns {Futurable<void>}
175
- */
460
+ * Creates a Futurable that resolves after a specified delay.
461
+ * Equivalent to delay with an empty callback.
462
+ *
463
+ * @param options - Configuration object
464
+ * @param options.timer - Duration to wait in milliseconds
465
+ * @param options.signal - Optional AbortSignal for cancellation
466
+ * @returns A Futurable that resolves after the delay
467
+ *
468
+ * @example
469
+ * ```typescript
470
+ * Futurable.sleep({ timer: 3000 })
471
+ * .then(() => console.log('3 seconds passed'));
472
+ * ```
473
+ */
176
474
  static sleep({ timer, signal }: {
177
475
  timer: number;
178
476
  signal?: AbortSignal;
179
477
  }): Futurable<void>;
180
478
  /**
181
- * Fetch static method.
182
- * @param {string} url
183
- * @param {RequestInit} [opts]
184
- * @returns {Futurable<Response>}
185
- */
479
+ * Performs an HTTP fetch operation with cancellation support.
480
+ *
481
+ * @param url - The URL to fetch
482
+ * @param opts - Optional Fetch API options (if signal is provided, it overrides the internal one)
483
+ * @returns A Futurable that resolves with the Response object
484
+ *
485
+ * @example
486
+ * ```typescript
487
+ * Futurable.fetch('https://api.example.com/data')
488
+ * .then(response => response.json())
489
+ * .then(data => console.log(data));
490
+ * ```
491
+ */
186
492
  static fetch(url: string, opts?: RequestInit): Futurable<Response>;
187
493
  /**
188
- * Futurizable static method.
189
- * @param {{promise: Promise<TResult1>, signal: AbortSignal|undefined}} options
190
- * @returns {Futurable<TResult1 | TResult2>}
191
- */
494
+ * Converts a Promise into a Futurable with cancellation support.
495
+ * Note: The original Promise cannot be cancelled, but the Futurable wrapper can be.
496
+ *
497
+ * @template TResult1 - Type the Promise resolves to
498
+ * @template TResult2 - Type in case of rejection
499
+ * @param options - Configuration object
500
+ * @param options.promise - The Promise to convert
501
+ * @param options.signal - Optional AbortSignal for cancellation
502
+ * @returns A Futurable wrapping the Promise
503
+ *
504
+ * @example
505
+ * ```typescript
506
+ * const promise = fetch('/api/data').then(r => r.json());
507
+ * Futurable.futurizable({ promise })
508
+ * .then(data => console.log(data));
509
+ * ```
510
+ */
192
511
  static futurizable<TResult1 = any, TResult2 = never>({ promise, signal }: {
193
512
  promise: Promise<TResult1>;
194
513
  signal?: AbortSignal;
195
514
  }): Futurable<TResult1 | TResult2>;
196
515
  private static handleValues;
197
516
  /**
198
- * Creates a Futurable with cancellation support that is resolved with an array of results when all of the provided Futurables resolve, or rejected when any Futurable is rejected.
199
- * @param {T} values
200
- * @param {AbortSignal} [signal]
201
- * @returns {Futurable<{ -readonly [P in keyof T]: Awaited<T[P]>; }>}
202
- */
517
+ * Creates a Futurable that resolves when all provided Futurables/Promises resolve,
518
+ * or rejects when any of them rejects. Supports cancellation of all pending operations.
519
+ *
520
+ * @template T - Tuple type of input values
521
+ * @param values - Array of Futurables, Promises, or plain values
522
+ * @param signal - Optional AbortSignal that cancels all operations when aborted
523
+ * @returns A Futurable that resolves with an array of all resolved values
524
+ *
525
+ * @example
526
+ * ```typescript
527
+ * const all = Futurable.all([
528
+ * Futurable.delay({ cb: () => 1, timer: 100 }),
529
+ * Futurable.delay({ cb: () => 2, timer: 200 }),
530
+ * Promise.resolve(3)
531
+ * ]);
532
+ * all.then(results => console.log(results)); // [1, 2, 3]
533
+ * ```
534
+ */
203
535
  static all<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<{
204
536
  -readonly [P in keyof T]: Awaited<T[P]>;
205
537
  }>;
206
538
  /**
207
- * Creates a Futurable with cancellation support that is resolved with an array of results when all of the provided Futurables resolve or reject.
208
- * @param {T} values
209
- * @param {AbortSignal} [signal]
210
- * @returns {Futurable<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }>}
211
- */
539
+ * Creates a Futurable that resolves when all provided Futurables/Promises settle
540
+ * (either resolve or reject). Returns an array of result objects indicating the outcome.
541
+ *
542
+ * @template T - Tuple type of input values
543
+ * @param values - Array of Futurables, Promises, or plain values
544
+ * @param signal - Optional AbortSignal for cancellation
545
+ * @returns A Futurable that resolves with an array of PromiseSettledResult objects
546
+ *
547
+ * @example
548
+ * ```typescript
549
+ * Futurable.allSettled([
550
+ * Futurable.resolve(1),
551
+ * Futurable.reject('error'),
552
+ * Promise.resolve(3)
553
+ * ]).then(results => {
554
+ * // results[0]: { status: 'fulfilled', value: 1 }
555
+ * // results[1]: { status: 'rejected', reason: 'error' }
556
+ * // results[2]: { status: 'fulfilled', value: 3 }
557
+ * });
558
+ * ```
559
+ */
212
560
  static allSettled<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<{
213
561
  -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>>;
214
562
  }>;
215
563
  /**
216
- * Creates a Futurable with cancellation support that is resolved or rejected when any of the provided Futurables are resolved or rejected.
217
- * @param {T} values
218
- * @param {AbortSignal} [signal]
219
- * @returns {Futurable<Awaited<T[number]>>}
220
- */
564
+ * Creates a Futurable that resolves or rejects as soon as any of the provided
565
+ * Futurables/Promises resolves or rejects. Cancels all other pending operations.
566
+ *
567
+ * @template T - Tuple type of input values
568
+ * @param values - Array of Futurables, Promises, or plain values
569
+ * @param signal - Optional AbortSignal for cancellation
570
+ * @returns A Futurable that settles with the first settled value/reason
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * Futurable.race([
575
+ * Futurable.delay({ cb: () => 'slow', timer: 2000 }),
576
+ * Futurable.delay({ cb: () => 'fast', timer: 100 })
577
+ * ]).then(result => console.log(result)); // 'fast'
578
+ * ```
579
+ */
221
580
  static race<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<Awaited<T[number]>>;
222
581
  /**
223
- * The any function returns a futurable with cancellation support that is fulfilled by the first given futurable to be fulfilled,
224
- * or rejected with an AggregateError containing an array of rejection reasons if all of the
225
- * given futurables are rejected. It resolves all elements of the passed iterable to futurables as
226
- * it runs this algorithm.
227
- * @param {T} values
228
- * @param {AbortSignal} [signal]
229
- * @returns {Futurable<Awaited<T[number]>>}
230
- */
582
+ * Creates a Futurable that resolves as soon as any of the provided Futurables/Promises
583
+ * successfully resolves. Rejects with an AggregateError if all of them reject.
584
+ *
585
+ * @template T - Tuple type of input values
586
+ * @param values - Array of Futurables, Promises, or plain values
587
+ * @param signal - Optional AbortSignal for cancellation
588
+ * @returns A Futurable that resolves with the first successful value
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * Futurable.any([
593
+ * Futurable.reject('error1'),
594
+ * Futurable.delay({ cb: () => 'success', timer: 100 }),
595
+ * Futurable.reject('error2')
596
+ * ]).then(result => console.log(result)); // 'success'
597
+ * ```
598
+ */
231
599
  static any<T extends readonly unknown[] | []>(values: T, signal?: AbortSignal): Futurable<Awaited<T[number]>>;
232
600
  /**
233
- * Creates a polling service with cancellation support and possibility to handle error. An optional param __immediate__ can be set _true_ if __fun__ must to be invoke immediatly.
234
- * @param {()=> Futurable<T>} fun
235
- * @param {{interval: number, signal?: AbortSignal, immediate?: boolean}} options
236
- * @returns {{cancel: () => void, catch: (onrejected:(reason: unknown)=>void)=>void }}
237
- */
601
+ * Creates a polling service that repeatedly executes a function at regular intervals.
602
+ * Supports cancellation and error handling.
603
+ *
604
+ * @template T - Type returned by the polling function
605
+ * @param fun - Function to execute on each poll (can return a Futurable, Promise, or plain value)
606
+ * @param options - Configuration object
607
+ * @param options.interval - Interval between polls in milliseconds
608
+ * @param options.signal - Optional AbortSignal to stop polling
609
+ * @param options.immediate - If true, executes the function immediately before starting the interval
610
+ * @returns A controller object with cancel() and catch() methods
611
+ *
612
+ * @example
613
+ * ```typescript
614
+ * const polling = Futurable.polling(
615
+ * () => fetch('/api/status').then(r => r.json()),
616
+ * { interval: 5000, immediate: true }
617
+ * );
618
+ *
619
+ * polling.catch(err => console.error('Polling error:', err));
620
+ *
621
+ * // Stop polling after 30 seconds
622
+ * setTimeout(() => polling.cancel(), 30000);
623
+ * ```
624
+ */
238
625
  static polling<T>(fun: () => Futurable<T> | Promise<T> | T, { interval, signal, immediate }: {
239
626
  interval: number;
240
627
  signal?: AbortSignal;
241
628
  immediate?: boolean;
242
- }): {
243
- cancel: () => void;
244
- catch: (onrejected: (reason: unknown) => void) => void;
245
- };
629
+ }): FuturablePollingController;
246
630
  /**
247
- * Extension of _Promise.withResolvers_ static method. Creates a new Futurable and returns it in an object, along with its resolve, reject and cancel functions and utils object.
248
- * @param {AbortSignal} [signal]
249
- * @returns {{ resolve: null | FuturableResolve<T>, reject: null | FuturableReject, utils: null | FuturableUtils<T>, futurable: Futurable<T>, cancel: null | (() => void) }}
250
- */
631
+ * Creates a new Futurable and returns it along with its control functions.
632
+ * Extension of the Promise.withResolvers() static method with cancellation support.
633
+ *
634
+ * @template T - Type of value the Futurable will resolve to
635
+ * @param signal - Optional AbortSignal for cancellation
636
+ * @returns An object containing the Futurable and its control functions
637
+ *
638
+ * @example
639
+ * ```typescript
640
+ * const { promise, resolve, reject, cancel } = Futurable.withResolvers<number>();
641
+ *
642
+ * // Resolve from elsewhere
643
+ * setTimeout(() => resolve(42), 1000);
644
+ *
645
+ * // Or cancel
646
+ * cancel();
647
+ *
648
+ * promise.then(val => console.log(val));
649
+ * ```
650
+ */
251
651
  static withResolvers<T>(signal?: AbortSignal): FuturableWithResolvers<T>;
652
+ /**
653
+ * Creates a Futurable that wraps an executor in a safe execution context.
654
+ * The resulting Futurable never rejects - instead, it resolves with a result
655
+ * object containing either the success value or the error.
656
+ *
657
+ * This is useful when you want to create a Futurable that handles its own errors
658
+ * internally and always resolves with a discriminated result type.
659
+ *
660
+ * @template T - The type of the success value
661
+ * @template E - The type of the error (defaults to unknown)
662
+ * @param executor - The Futurable executor function
663
+ * @param signal - Optional AbortSignal for cancellation coordination
664
+ * @returns A Futurable that always resolves to a SafeResult
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * const result = await Futurable.safe<number>(
669
+ * (resolve, reject, { fetch }) => {
670
+ * fetch('/api/data')
671
+ * .then(r => r.json())
672
+ * .then(data => resolve(data.value))
673
+ * .catch(reject);
674
+ * }
675
+ * );
676
+ *
677
+ * if (result.success) {
678
+ * console.log('Value:', result.data);
679
+ * } else {
680
+ * console.error('Failed:', result.error);
681
+ * }
682
+ * ```
683
+ *
684
+ * @example
685
+ * ```typescript
686
+ * // With custom error type:
687
+ * interface ApiError {
688
+ * code: string;
689
+ * message: string;
690
+ * }
691
+ *
692
+ * const result = await Futurable.safe<User, ApiError>(
693
+ * (resolve, reject, { fetch }) => {
694
+ * fetch('/api/user')
695
+ * .then(r => r.ok ? r.json() : reject({ code: 'HTTP_ERROR', message: r.statusText }))
696
+ * .then(resolve)
697
+ * .catch(err => reject({ code: 'NETWORK_ERROR', message: err.message }));
698
+ * }
699
+ * );
700
+ *
701
+ * if (!result.success) {
702
+ * switch (result.error.code) {
703
+ * case 'HTTP_ERROR':
704
+ * // Handle HTTP errors
705
+ * break;
706
+ * case 'NETWORK_ERROR':
707
+ * // Handle network errors
708
+ * break;
709
+ * }
710
+ * }
711
+ * ```
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * // Combining with cancellation:
716
+ * const controller = new AbortController();
717
+ *
718
+ * const result = await Futurable.safe<string>(
719
+ * (resolve, reject, { signal, fetch }) => {
720
+ * fetch('/api/slow-endpoint')
721
+ * .then(r => r.text())
722
+ * .then(resolve)
723
+ * .catch(reject);
724
+ * },
725
+ * controller.signal
726
+ * );
727
+ *
728
+ * // Cancel after 5 seconds
729
+ * setTimeout(() => controller.abort(), 5000);
730
+ * ```
731
+ */
732
+ static safe<T, E = unknown>(executor: FuturableExecutor<T>, signal?: AbortSignal): Futurable<SafeResult<T, E>>;
733
+ }
734
+
735
+ /**
736
+ * Configuration options for memoization behavior.
737
+ *
738
+ * @template T - The type of value being memoized
739
+ *
740
+ * @property enabled - Whether memoization is active
741
+ * @property catchErrors - If true, caches the result even when the execution rejects
742
+ * @property instance - The cached Futurable instance (if memoization is active)
743
+ */
744
+ type MemoizeOptions<T> = {
745
+ enabled: boolean;
746
+ catchErrors?: boolean;
747
+ instance?: Futurable<T>;
748
+ };
749
+ /**
750
+ * Event hooks for monitoring task limiter lifecycle.
751
+ * All hooks are optional and provide insight into task execution flow.
752
+ *
753
+ * @property onActive - Called when a task starts executing
754
+ * @property onCompleted - Called when a task completes successfully with its result
755
+ * @property onError - Called when a task fails with an error
756
+ * @property onIdle - Called when all tasks have completed and the queue is empty
757
+ *
758
+ * @example
759
+ * ```typescript
760
+ * const events: LimiterEvents = {
761
+ * onActive: (task) => console.log('Task started:', task),
762
+ * onCompleted: (result) => console.log('Task completed:', result),
763
+ * onError: (error) => console.error('Task failed:', error),
764
+ * onIdle: () => console.log('All tasks finished')
765
+ * };
766
+ * ```
767
+ */
768
+ interface LimiterEvents {
769
+ onActive?: (task: any) => void;
770
+ onCompleted?: (result: any) => void;
771
+ onError?: (error: any) => void;
772
+ onIdle?: () => void;
773
+ }
774
+ /**
775
+ * A higher-order function that wraps tasks with concurrency limiting.
776
+ *
777
+ * Acts as both a function and an object with readonly properties.
778
+ * The function takes a task and returns a new task that respects the concurrency limit.
779
+ *
780
+ * @property activeCount - Number of tasks currently executing
781
+ * @property pendingCount - Number of tasks waiting in the queue
782
+ * @property concurrency - Maximum number of concurrent tasks allowed
783
+ *
784
+ * @example
785
+ * ```typescript
786
+ * const limiter = FuturableTask.createLimiter(2);
787
+ *
788
+ * console.log(limiter.concurrency); // 2
789
+ * console.log(limiter.activeCount); // 0
790
+ * console.log(limiter.pendingCount); // 0
791
+ *
792
+ * const limitedTask = limiter(myTask);
793
+ * ```
794
+ */
795
+ type FuturableTaskLimiter = (<T>(task: FuturableTask<T>) => FuturableTask<T>) & {
796
+ readonly activeCount: number;
797
+ readonly pendingCount: number;
798
+ readonly concurrency: number;
799
+ };
800
+ /**
801
+ * Lazy computation wrapper for deferred execution.
802
+ *
803
+ * Unlike Futurable (which extends Promise and is eager), FuturableTask is lazy:
804
+ * - Creation doesn't trigger execution
805
+ * - Can be composed without side effects
806
+ * - Execution happens only when run() is called
807
+ * - Can be run multiple times (each run is independent)
808
+ * - Provides functional composition methods
809
+ * - Supports cancellation at both task and execution level
810
+ *
811
+ * @template T - The type of value this task will eventually produce
812
+ *
813
+ * @example
814
+ * ```typescript
815
+ * // Creating a task doesn't execute it
816
+ * const task = FuturableTask.of(() => {
817
+ * console.log('Executing!');
818
+ * return fetch('/api/data');
819
+ * });
820
+ * // Nothing logged yet
821
+ *
822
+ * const result1 = await task.run(); // Logs: "Executing!" and fetches
823
+ * const result2 = await task.run(); // Logs: "Executing!" again (independent execution)
824
+ * ```
825
+ *
826
+ * @example
827
+ * ```typescript
828
+ * // Functional composition without execution
829
+ * const pipeline = FuturableTask
830
+ * .of(() => fetch('/users'))
831
+ * .map(res => res.json())
832
+ * .map(users => users.filter(u => u.active))
833
+ * .retry(3)
834
+ * .timeout(5000);
835
+ *
836
+ * // Only now does execution happen
837
+ * const activeUsers = await pipeline.run();
838
+ * ```
839
+ *
840
+ * @example
841
+ * ```typescript
842
+ * // Cancellation support
843
+ * const task = FuturableTask.of(() => longOperation())
844
+ * .onCancel(() => console.log('Cleanup'));
845
+ *
846
+ * const run = task.run();
847
+ * task.cancel(); // Logs: "Cleanup", cancels the operation
848
+ * ```
849
+ */
850
+ declare class FuturableTask<T> {
851
+ private readonly executor;
852
+ /**
853
+ * Internal AbortController that manages task cancellation.
854
+ * Created in the constructor and used to abort all executions of this task.
855
+ *
856
+ * @private
857
+ * @readonly
858
+ */
859
+ private readonly controller;
860
+ /**
861
+ * Array of callbacks to execute when the task is cancelled.
862
+ * These callbacks are executed eagerly (even if the task was never run).
863
+ *
864
+ * @private
865
+ * @readonly
866
+ */
867
+ private readonly cancelCallbacks;
868
+ /**
869
+ * Configuration object for memoization behavior.
870
+ * When enabled, caches the first execution result and reuses it.
871
+ *
872
+ * @private
873
+ * @readonly
874
+ */
875
+ private readonly memoizeOptions;
876
+ /**
877
+ * Reference to the source task (if this task is debounced).
878
+ * When calling debounce() on an already debounced task, this points to the original source.
879
+ *
880
+ * @private
881
+ */
882
+ private sourceTask?;
883
+ /**
884
+ * Creates a new FuturableTask.
885
+ *
886
+ * The executor function is NOT invoked until run() is called.
887
+ * If an external signal is provided, aborting it will also cancel this task.
888
+ *
889
+ * @param executor - The executor function that defines the computation.
890
+ * Receives resolve, reject, and utils (with signal, onCancel, delay, sleep, fetch, etc.)
891
+ * @param externalSignal - Optional AbortSignal that will cancel this task when aborted.
892
+ * Useful for coordinating cancellation with parent operations.
893
+ *
894
+ * @example
895
+ * ```typescript
896
+ * // Basic task
897
+ * const task = new FuturableTask((resolve, reject, utils) => {
898
+ * setTimeout(() => resolve('done'), 1000);
899
+ * });
900
+ * ```
901
+ *
902
+ * @example
903
+ * ```typescript
904
+ * // With cancellation support
905
+ * const task = new FuturableTask((resolve, reject, utils) => {
906
+ * const timeoutId = setTimeout(() => resolve('done'), 5000);
907
+ *
908
+ * utils.onCancel(() => {
909
+ * console.log('Cancelled!');
910
+ * clearTimeout(timeoutId);
911
+ * });
912
+ * });
913
+ * ```
914
+ *
915
+ * @example
916
+ * ```typescript
917
+ * // With external signal
918
+ * const controller = new AbortController();
919
+ * const task = new FuturableTask((res) => {
920
+ * res('value');
921
+ * }, controller.signal);
922
+ *
923
+ * controller.abort(); // Cancels the task
924
+ * ```
925
+ */
926
+ constructor(executor: FuturableExecutor<T>, externalSignal?: AbortSignal);
927
+ /**
928
+ * Returns the internal AbortSignal for this task.
929
+ *
930
+ * This signal is aborted when cancel() is called on the task.
931
+ * All executions created by run() will listen to this signal.
932
+ *
933
+ * @returns The internal AbortSignal
934
+ *
935
+ * @example
936
+ * ```typescript
937
+ * const task = FuturableTask.of(() => fetch('/api/data'));
938
+ *
939
+ * console.log(task.signal.aborted); // false
940
+ * task.cancel();
941
+ * console.log(task.signal.aborted); // true
942
+ * ```
943
+ */
944
+ get signal(): AbortSignal;
945
+ /**
946
+ * Cancels all running and future executions of this task.
947
+ *
948
+ * This will:
949
+ * 1. Abort the internal signal
950
+ * 2. Execute all registered task-level onCancel callbacks
951
+ * 3. Cancel all Futurables created by run() that haven't completed yet
952
+ * 4. Prevent new executions from starting (they will be pending)
953
+ *
954
+ * Note: This is idempotent - calling it multiple times has no additional effect.
955
+ *
956
+ * @example
957
+ * ```typescript
958
+ * const task = FuturableTask.of(() => longRunningOperation());
959
+ * const run1 = task.run();
960
+ * const run2 = task.run();
961
+ *
962
+ * task.cancel(); // Cancels both run1 and run2
963
+ *
964
+ * const run3 = task.run(); // This will be pending (never resolves)
965
+ * ```
966
+ *
967
+ * @example
968
+ * ```typescript
969
+ * const task = FuturableTask.of(() => fetch('/api'))
970
+ * .onCancel(() => console.log('Task cancelled'));
971
+ *
972
+ * task.cancel(); // Logs: "Task cancelled"
973
+ * task.cancel(); // Does nothing (already cancelled)
974
+ * ```
975
+ */
976
+ cancel(): void;
977
+ /**
978
+ * Registers a callback to be executed when the task is cancelled.
979
+ *
980
+ * IMPORTANT: This is an eager callback - it executes when task.cancel() is called,
981
+ * even if the task has never been run. This is different from registering callbacks
982
+ * inside the executor with utils.onCancel, which are only called if the task was
983
+ * actively running when cancelled.
984
+ *
985
+ * Multiple callbacks can be registered and will execute in order.
986
+ *
987
+ * @param cb - Callback to execute on cancellation
988
+ * @returns The same FuturableTask instance for chaining
989
+ *
990
+ * @example
991
+ * ```typescript
992
+ * const task = FuturableTask.of(() => fetch('/api/data'))
993
+ * .onCancel(() => console.log('Task cancelled'))
994
+ * .onCancel(() => console.log('Cleanup complete'));
995
+ *
996
+ * task.cancel();
997
+ * // Logs: "Task cancelled"
998
+ * // Logs: "Cleanup complete"
999
+ * // (even without calling run())
1000
+ * ```
1001
+ *
1002
+ * @example
1003
+ * ```typescript
1004
+ * // Cleanup external resources
1005
+ * const ws = new WebSocket('ws://...');
1006
+ *
1007
+ * const task = FuturableTask.of(() => fetchFromWebSocket(ws))
1008
+ * .onCancel(() => {
1009
+ * console.log('Closing WebSocket');
1010
+ * ws.close();
1011
+ * });
1012
+ *
1013
+ * task.cancel(); // Closes WebSocket even if never run
1014
+ * ```
1015
+ */
1016
+ onCancel(cb: () => void): this;
1017
+ /**
1018
+ * Executes the task and returns a Futurable.
1019
+ *
1020
+ * Each call to run() creates a new independent execution. The returned Futurable
1021
+ * can be cancelled in two ways:
1022
+ * 1. By calling task.cancel() - cancels all running executions
1023
+ * 2. By calling futurable.cancel() - cancels only this specific execution
1024
+ * 3. By aborting the overrideSignal - cancels only this specific execution
1025
+ *
1026
+ * The execution uses a composite signal that listens to:
1027
+ * - The task's internal signal (from task.cancel())
1028
+ * - The overrideSignal (if provided)
1029
+ *
1030
+ * If the task is already cancelled, the returned Futurable will be pending (never resolves).
1031
+ *
1032
+ * @param overrideSignal - Optional signal to override/supplement the task's default signal.
1033
+ * Useful for adding execution-specific cancellation.
1034
+ * @returns A Futurable representing this execution
1035
+ *
1036
+ * @example
1037
+ * ```typescript
1038
+ * const task = FuturableTask.of(() => fetch('/data'));
1039
+ *
1040
+ * const run1 = task.run(); // Can be cancelled by task.cancel() or run1.cancel()
1041
+ * const run2 = task.run(); // Independent execution
1042
+ *
1043
+ * task.cancel(); // Cancels both run1 and run2
1044
+ * ```
1045
+ *
1046
+ * @example
1047
+ * ```typescript
1048
+ * // With override signal
1049
+ * const task = FuturableTask.of(() => fetch('/data'));
1050
+ * const controller = new AbortController();
1051
+ *
1052
+ * const run = task.run(controller.signal);
1053
+ *
1054
+ * controller.abort(); // Cancels only this execution
1055
+ * // task itself is NOT cancelled, can still run() again
1056
+ * ```
1057
+ *
1058
+ * @example
1059
+ * ```typescript
1060
+ * // Cancelled task produces pending Futurables
1061
+ * const task = FuturableTask.of(() => fetch('/data'));
1062
+ * task.cancel();
1063
+ *
1064
+ * const run = task.run(); // Never resolves or rejects (pending)
1065
+ * ```
1066
+ */
1067
+ run(overrideSignal?: AbortSignal): Futurable<T>;
1068
+ /**
1069
+ * Executes the task and returns a SafeResult instead of throwing errors.
1070
+ *
1071
+ * This method wraps the task execution in a try-catch pattern that returns
1072
+ * a discriminated union type, making error handling explicit and type-safe.
1073
+ *
1074
+ * The returned Futurable resolves to a SafeResult object that contains either:
1075
+ * - `{ success: true, data: T, error: null }` on success
1076
+ * - `{ success: false, data: null, error: E }` on failure
1077
+ *
1078
+ * This pattern eliminates the need for try-catch blocks and makes error
1079
+ * handling explicit at the type level, similar to Rust's Result type.
1080
+ *
1081
+ * @template E - The type of error (defaults to unknown)
1082
+ * @param overrideSignal - Optional signal to override/supplement the task's default signal
1083
+ * @returns A Futurable that always resolves with a SafeResult (never rejects)
1084
+ *
1085
+ * @example
1086
+ * ```typescript
1087
+ * const task = FuturableTask.of(() => riskyOperation());
1088
+ * const result = await task.runSafe();
1089
+ *
1090
+ * if (result.success) {
1091
+ * console.log('Success:', result.data);
1092
+ * // TypeScript knows result.data is T
1093
+ * } else {
1094
+ * console.error('Error:', result.error);
1095
+ * // TypeScript knows result.error is E
1096
+ * }
1097
+ * ```
1098
+ *
1099
+ * @example
1100
+ * ```typescript
1101
+ * // Chaining multiple safe operations
1102
+ * const result1 = await task1.runSafe();
1103
+ * if (!result1.success) return result1.error;
1104
+ *
1105
+ * const result2 = await task2.runSafe();
1106
+ * if (!result2.success) return result2.error;
1107
+ *
1108
+ * return { data1: result1.data, data2: result2.data };
1109
+ * ```
1110
+ *
1111
+ * @example
1112
+ * ```typescript
1113
+ * // With explicit error type
1114
+ * const task = FuturableTask.of(() => fetchUser(id));
1115
+ * const result = await task.runSafe<ApiError>();
1116
+ *
1117
+ * if (!result.success) {
1118
+ * // result.error is typed as ApiError
1119
+ * console.error('API Error:', result.error.statusCode);
1120
+ * }
1121
+ * ```
1122
+ *
1123
+ * @example
1124
+ * ```typescript
1125
+ * // Functional error handling without exceptions
1126
+ * const results = await Promise.all([
1127
+ * task1.runSafe(),
1128
+ * task2.runSafe(),
1129
+ * task3.runSafe()
1130
+ * ]);
1131
+ *
1132
+ * const errors = results.filter(r => !r.success);
1133
+ * const successes = results.filter(r => r.success);
1134
+ *
1135
+ * console.log(`${successes.length} succeeded, ${errors.length} failed`);
1136
+ * ```
1137
+ *
1138
+ * @example
1139
+ * ```typescript
1140
+ * // With cancellation support
1141
+ * const controller = new AbortController();
1142
+ * const result = await task.runSafe(controller.signal);
1143
+ *
1144
+ * // Can be cancelled externally
1145
+ * controller.abort();
1146
+ * ```
1147
+ */
1148
+ runSafe<E = unknown>(overrideSignal?: AbortSignal): Futurable<SafeResult<T, E>>;
1149
+ /**
1150
+ * Caches the result of the first execution and reuses it for subsequent runs.
1151
+ *
1152
+ * All calls to run() after the first will return the same cached Futurable.
1153
+ * This is useful for expensive computations that should only run once.
1154
+ *
1155
+ * IMPORTANT: The cached result is shared across all calls. If you need independent
1156
+ * executions, don't use memoize() or create a new memoized task for each use case.
1157
+ *
1158
+ * Returns a NEW FuturableTask (does not mutate the original).
1159
+ *
1160
+ * @param catchErrors - If true, caches the result even when the execution rejects.
1161
+ * If false (default), a rejection clears the cache and the next
1162
+ * run() will retry the operation.
1163
+ * @returns A new FuturableTask that caches its result
1164
+ *
1165
+ * @example
1166
+ * ```typescript
1167
+ * const expensiveTask = FuturableTask.of(() => {
1168
+ * console.log('Computing...');
1169
+ * return complexCalculation();
1170
+ * }).memoize();
1171
+ *
1172
+ * await expensiveTask.run(); // Logs "Computing..." and calculates
1173
+ * await expensiveTask.run(); // Returns cached result immediately (no log)
1174
+ * await expensiveTask.run(); // Returns cached result immediately (no log)
1175
+ * ```
1176
+ *
1177
+ * @example
1178
+ * ```typescript
1179
+ * // Without catchErrors (default) - retries on failure
1180
+ * const task = FuturableTask.of(() => riskyOperation()).memoize();
1181
+ *
1182
+ * try {
1183
+ * await task.run(); // Fails
1184
+ * } catch (err) {}
1185
+ *
1186
+ * await task.run(); // Retries (not cached because it failed)
1187
+ * ```
1188
+ *
1189
+ * @example
1190
+ * ```typescript
1191
+ * // With catchErrors - caches failures too
1192
+ * const task = FuturableTask.of(() => riskyOperation()).memoize(true);
1193
+ *
1194
+ * try {
1195
+ * await task.run(); // Fails
1196
+ * } catch (err) {}
1197
+ *
1198
+ * try {
1199
+ * await task.run(); // Returns cached error (doesn't retry)
1200
+ * } catch (err) {}
1201
+ * ```
1202
+ */
1203
+ memoize(catchErrors?: boolean): FuturableTask<T>;
1204
+ /**
1205
+ * Transforms the task's result value using a mapping function.
1206
+ *
1207
+ * The transformation is lazy and won't execute until run() is called.
1208
+ * The mapping function receives the resolved value and optionally the abort signal
1209
+ * to check if the operation was cancelled.
1210
+ *
1211
+ * This is the basic building block for transforming task results.
1212
+ *
1213
+ * @template U - The type of the transformed value
1214
+ * @param fn - Function to transform the value. Can be sync or async.
1215
+ * Receives the value and optionally the signal.
1216
+ * @returns A new FuturableTask with the transformed value
1217
+ *
1218
+ * @example
1219
+ * ```typescript
1220
+ * const doubleTask = FuturableTask.resolve(5)
1221
+ * .map(x => x * 2);
1222
+ *
1223
+ * await doubleTask.run(); // 10
1224
+ * ```
1225
+ *
1226
+ * @example
1227
+ * ```typescript
1228
+ * // Async transformation
1229
+ * const task = FuturableTask.of(() => fetch('/users'))
1230
+ * .map(async res => await res.json())
1231
+ * .map(users => users.filter(u => u.active));
1232
+ *
1233
+ * const activeUsers = await task.run();
1234
+ * ```
1235
+ *
1236
+ * @example
1237
+ * ```typescript
1238
+ * // Using the signal parameter
1239
+ * const task = FuturableTask.of(() => fetchData())
1240
+ * .map((data, signal) => {
1241
+ * if (signal?.aborted) {
1242
+ * console.log('Mapping cancelled');
1243
+ * return null;
1244
+ * }
1245
+ * return expensiveTransformation(data);
1246
+ * });
1247
+ * ```
1248
+ */
1249
+ map<U>(fn: (data: T, signal?: AbortSignal) => U | Promise<U>): FuturableTask<U>;
1250
+ /**
1251
+ * Chains this task with another task, creating a sequential composition.
1252
+ *
1253
+ * Also known as "bind" or "chain" in functional programming.
1254
+ * The function receives the resolved value and must return a new FuturableTask.
1255
+ *
1256
+ * This allows you to create dependent computations where the next task
1257
+ * depends on the result of the previous one.
1258
+ *
1259
+ * @template U - The type of value the chained task produces
1260
+ * @param fn - Function that receives the value and returns a new FuturableTask
1261
+ * @returns A new FuturableTask representing the chained computation
1262
+ *
1263
+ * @example
1264
+ * ```typescript
1265
+ * const getUserTask = FuturableTask.of(() => fetch('/user/1').then(r => r.json()));
1266
+ * const getPostsTask = (user) => FuturableTask.of(() =>
1267
+ * fetch(`/users/${user.id}/posts`).then(r => r.json())
1268
+ * );
1269
+ *
1270
+ * const userPosts = getUserTask.flatMap(getPostsTask);
1271
+ * const posts = await userPosts.run();
1272
+ * ```
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * // Chaining multiple dependent operations
1277
+ * const result = await FuturableTask.of(() => readConfig())
1278
+ * .flatMap(config => FuturableTask.of(() => connectToDb(config)))
1279
+ * .flatMap(db => FuturableTask.of(() => db.query('SELECT * FROM users')))
1280
+ * .run();
1281
+ * ```
1282
+ *
1283
+ * @example
1284
+ * ```typescript
1285
+ * // Error handling in chains
1286
+ * const result = await FuturableTask.of(() => mayFail())
1287
+ * .flatMap(val => {
1288
+ * if (val < 0) {
1289
+ * return FuturableTask.reject(new Error('Negative value'));
1290
+ * }
1291
+ * return FuturableTask.resolve(val * 2);
1292
+ * })
1293
+ * .run();
1294
+ * ```
1295
+ */
1296
+ flatMap<U>(fn: (data: T) => FuturableTask<U>): FuturableTask<U>;
1297
+ /**
1298
+ * Sequences this task with another task, discarding the first result.
1299
+ *
1300
+ * Executes the current task, waits for it to complete, then executes the next task.
1301
+ * The result of the current task is ignored - only the next task's result is returned.
1302
+ *
1303
+ * Useful for sequencing side effects or setup operations before the main computation.
1304
+ *
1305
+ * @template U - The type of value the next task produces
1306
+ * @param nextTask - The task to execute after this one
1307
+ * @returns A new FuturableTask that produces the next task's result
1308
+ *
1309
+ * @example
1310
+ * ```typescript
1311
+ * const setupTask = FuturableTask.of(() => initializeApp());
1312
+ * const mainTask = FuturableTask.of(() => fetchData());
1313
+ *
1314
+ * const result = await setupTask.andThen(mainTask).run();
1315
+ * // setupTask runs first, then mainTask, result is from mainTask
1316
+ * ```
1317
+ *
1318
+ * @example
1319
+ * ```typescript
1320
+ * // Chaining multiple sequential tasks
1321
+ * await FuturableTask.of(() => clearCache())
1322
+ * .andThen(FuturableTask.of(() => warmupCache()))
1323
+ * .andThen(FuturableTask.of(() => startServer()))
1324
+ * .run();
1325
+ * ```
1326
+ */
1327
+ andThen<U>(nextTask: FuturableTask<U>): FuturableTask<U>;
1328
+ /**
1329
+ * Executes a side-effect function with the task's value without modifying it.
1330
+ *
1331
+ * The value passes through unchanged. Useful for logging, debugging, or
1332
+ * triggering actions based on the value without affecting the result.
1333
+ *
1334
+ * If the side-effect function throws or rejects, the error is propagated.
1335
+ *
1336
+ * @param fn - Side-effect function that receives the value. Can be sync or async.
1337
+ * @returns A new FuturableTask that passes through the original value
1338
+ *
1339
+ * @example
1340
+ * ```typescript
1341
+ * const task = FuturableTask.of(() => fetchUser())
1342
+ * .tap(user => console.log('Fetched user:', user.id))
1343
+ * .map(user => user.name);
1344
+ *
1345
+ * const name = await task.run(); // Logs, then returns name
1346
+ * ```
1347
+ *
1348
+ * @example
1349
+ * ```typescript
1350
+ * // Multiple taps for debugging
1351
+ * const result = await FuturableTask.of(() => calculateResult())
1352
+ * .tap(val => console.log('Initial:', val))
1353
+ * .map(val => val * 2)
1354
+ * .tap(val => console.log('After doubling:', val))
1355
+ * .map(val => val + 10)
1356
+ * .tap(val => console.log('Final:', val))
1357
+ * .run();
1358
+ * ```
1359
+ *
1360
+ * @example
1361
+ * ```typescript
1362
+ * // Side effects with external systems
1363
+ * await FuturableTask.of(() => processData())
1364
+ * .tap(async data => {
1365
+ * await logToAnalytics('data_processed', data);
1366
+ * })
1367
+ * .run();
1368
+ * ```
1369
+ */
1370
+ tap(fn: (data: T) => any): FuturableTask<T>;
1371
+ /**
1372
+ * Executes a side effect only if this task fails.
1373
+ *
1374
+ * The error is still propagated after the side effect executes.
1375
+ * Useful for error logging or monitoring without handling the error.
1376
+ *
1377
+ * If the side-effect function itself throws, that error is logged to console
1378
+ * but the original error is still propagated.
1379
+ *
1380
+ * @param fn - Function to execute on error. Can be sync or async.
1381
+ * @returns A new FuturableTask that passes through the original error
1382
+ *
1383
+ * @example
1384
+ * ```typescript
1385
+ * await apiTask
1386
+ * .tapError(error => logger.error('API failed', error))
1387
+ * .tapError(error => sendToSentry(error))
1388
+ * .catch(handleError);
1389
+ * ```
1390
+ *
1391
+ * @example
1392
+ * ```typescript
1393
+ * // Log errors without stopping propagation
1394
+ * try {
1395
+ * await FuturableTask.of(() => riskyOperation())
1396
+ * .tapError(err => console.error('Operation failed:', err))
1397
+ * .run();
1398
+ * } catch (err) {
1399
+ * // err is the original error
1400
+ * console.log('Caught:', err);
1401
+ * }
1402
+ * ```
1403
+ *
1404
+ * @example
1405
+ * ```typescript
1406
+ * const result = await FuturableTask.of(() => mayFail())
1407
+ * .tapError(err => analytics.trackError(err))
1408
+ * .orElse(err => FuturableTask.resolve(defaultValue))
1409
+ * .run();
1410
+ * ```
1411
+ */
1412
+ tapError(fn: (error: any) => any): FuturableTask<T>;
1413
+ /**
1414
+ * Handles errors from the task execution by providing a fallback task.
1415
+ *
1416
+ * If the task succeeds, the result passes through unchanged.
1417
+ * If the task fails, the fallback function is called with the error
1418
+ * and must return a new FuturableTask to execute instead.
1419
+ *
1420
+ * The fallback task can return a different type, allowing for type transformations
1421
+ * during error fallbackToy.
1422
+ *
1423
+ * @template U - The type of the fallbackToy value
1424
+ * @param fallbackTask - Function that receives the error and returns a FuturableTask
1425
+ * @returns A new FuturableTask with error handling
1426
+ *
1427
+ * @example
1428
+ * ```typescript
1429
+ * const task = FuturableTask.of(() => fetchFromPrimary())
1430
+ * .catchError(err => {
1431
+ * console.error('Primary failed:', err);
1432
+ * return FuturableTask.of(() => fetchFromBackup());
1433
+ * });
1434
+ *
1435
+ * const data = await task.run(); // Falls back to backup on error
1436
+ * ```
1437
+ *
1438
+ * @example
1439
+ * ```typescript
1440
+ * // Retry with exponential backoff
1441
+ * const task = FuturableTask.of(() => unstableOperation())
1442
+ * .catchError(err =>
1443
+ * FuturableTask.delay(1000)
1444
+ * .flatMap(() => FuturableTask.of(() => unstableOperation()))
1445
+ * );
1446
+ * ```
1447
+ *
1448
+ * @example
1449
+ * ```typescript
1450
+ * // Type transformation on error
1451
+ * const task: FuturableTask<User> = fetchUser()
1452
+ * .catchError((err): FuturableTask<User | null> => {
1453
+ * if (err.status === 404) {
1454
+ * return FuturableTask.resolve(null);
1455
+ * }
1456
+ * return FuturableTask.reject(err);
1457
+ * });
1458
+ * ```
1459
+ */
1460
+ catchError<U>(fallbackTask: (err: any) => FuturableTask<U>): FuturableTask<T | U>;
1461
+ /**
1462
+ * Provides an alternative FuturableTask to execute if the current one fails.
1463
+ *
1464
+ * Similar to catchError, but ensures the result type remains consistent (type T).
1465
+ * This is used for fallback logic like using cache if API fails.
1466
+ *
1467
+ * If the task succeeds, the result passes through unchanged.
1468
+ * If the task fails, the fallback function is called with the error.
1469
+ *
1470
+ * @param fallbackTask - A function that receives the error and returns a fallback FuturableTask
1471
+ * @returns A new FuturableTask that will attempt the alternative on failure
1472
+ *
1473
+ * @example
1474
+ * ```typescript
1475
+ * const task = FuturableTask.of(() => readLocalConfig())
1476
+ * .orElse(err => {
1477
+ * console.warn('Local config not found, loading default...');
1478
+ * return FuturableTask.of(() => readDefaultConfig());
1479
+ * });
1480
+ *
1481
+ * const config = await task.run();
1482
+ * ```
1483
+ *
1484
+ * @example
1485
+ * ```typescript
1486
+ * // Cache fallback
1487
+ * const task = FuturableTask.of(() => fetch('/api/data'))
1488
+ * .orElse(() => FuturableTask.of(() => loadFromCache()));
1489
+ * ```
1490
+ *
1491
+ * @example
1492
+ * ```typescript
1493
+ * // Multiple fallbacks
1494
+ * const task = FuturableTask.of(() => fetchFromPrimary())
1495
+ * .orElse(() => FuturableTask.of(() => fetchFromSecondary()))
1496
+ * .orElse(() => FuturableTask.of(() => fetchFromTertiary()));
1497
+ * ```
1498
+ */
1499
+ orElse<U>(fallbackTask: (err: any) => FuturableTask<T | U>): FuturableTask<T | U>;
1500
+ /**
1501
+ * Provides a default value if the task fails.
1502
+ *
1503
+ * Simpler alternative to orElse when you just want to return a static value on failure.
1504
+ * The fallback value is wrapped in a resolved task automatically.
1505
+ *
1506
+ * @param fallback - The default value to use if the task fails
1507
+ * @returns A new FuturableTask that won't fail (returns fallback instead)
1508
+ *
1509
+ * @example
1510
+ * ```typescript
1511
+ * const data = await FuturableTask.of(() => fetchData())
1512
+ * .fallbackTo([])
1513
+ * .run();
1514
+ * // Always succeeds, returns [] if fetch fails
1515
+ * ```
1516
+ *
1517
+ * @example
1518
+ * ```typescript
1519
+ * // Configuration with defaults
1520
+ * const config = await FuturableTask.of(() => loadUserConfig())
1521
+ * .fallbackTo(DEFAULT_CONFIG)
1522
+ * .run();
1523
+ * ```
1524
+ *
1525
+ * @example
1526
+ * ```typescript
1527
+ * // Nullish values
1528
+ * const user = await FuturableTask.of(() => fetchUser(id))
1529
+ * .fallbackTo(null)
1530
+ * .run();
1531
+ * // Returns null instead of throwing on error
1532
+ * ```
1533
+ */
1534
+ fallbackTo<U>(fallback: U): FuturableTask<T | U>;
1535
+ /**
1536
+ * Conditional branching based on the task's result.
1537
+ *
1538
+ * Evaluates a condition with the resolved value and executes one of two alternative
1539
+ * tasks based on the result. Both branches must return tasks of the same type.
1540
+ *
1541
+ * The condition can be async, allowing for asynchronous decision-making.
1542
+ *
1543
+ * @template U - The type returned by both branches
1544
+ * @param condition - Predicate function to evaluate (can be async)
1545
+ * @param onTrue - Task to execute if condition is true
1546
+ * @param onFalse - Task to execute if condition is false
1547
+ * @returns A new FuturableTask with conditional execution
1548
+ *
1549
+ * @example
1550
+ * ```typescript
1551
+ * const result = await userTask.ifElse(
1552
+ * user => user.isPremium,
1553
+ * user => FuturableTask.of(() => loadPremiumContent(user)),
1554
+ * user => FuturableTask.of(() => loadBasicContent(user))
1555
+ * ).run();
1556
+ * ```
1557
+ *
1558
+ * @example
1559
+ * ```typescript
1560
+ * // Async condition
1561
+ * const task = FuturableTask.of(() => getUser())
1562
+ * .ifElse(
1563
+ * async user => await hasPermission(user, 'admin'),
1564
+ * user => FuturableTask.of(() => adminDashboard(user)),
1565
+ * user => FuturableTask.of(() => userDashboard(user))
1566
+ * );
1567
+ * ```
1568
+ *
1569
+ * @example
1570
+ * ```typescript
1571
+ * // Validation with branching
1572
+ * const processed = await FuturableTask.of(() => parseInput(data))
1573
+ * .ifElse(
1574
+ * parsed => parsed.isValid,
1575
+ * parsed => FuturableTask.of(() => processValidData(parsed)),
1576
+ * parsed => FuturableTask.reject(new Error('Invalid data'))
1577
+ * )
1578
+ * .run();
1579
+ * ```
1580
+ */
1581
+ ifElse<U>(condition: (value: T) => boolean | Promise<boolean>, onTrue: (value: T) => FuturableTask<U>, onFalse: (value: T) => FuturableTask<U>): FuturableTask<U>;
1582
+ /**
1583
+ * Folds the result of the task execution into a single value by applying
1584
+ * the appropriate transformation function.
1585
+ *
1586
+ * This is a catamorphism - it handles both success and failure cases uniformly,
1587
+ * mapping them both to the same result type. Both transformation functions must
1588
+ * return tasks of the same type.
1589
+ *
1590
+ * Useful when you want to treat success and failure symmetrically.
1591
+ *
1592
+ * @template U - The return type produced by the fold transformation
1593
+ * @param onFailure - Transformation function applied if the promise rejects
1594
+ * @param onSuccess - Transformation function applied if the promise resolves
1595
+ * @returns A new FuturableTask that resolves to the transformation result
1596
+ *
1597
+ * @example
1598
+ * ```typescript
1599
+ * const message = await FuturableTask.of(() => fetchData())
1600
+ * .fold(
1601
+ * error => FuturableTask.resolve(`Error: ${error.message}`),
1602
+ * data => FuturableTask.resolve(`Success: ${data.length} items`)
1603
+ * )
1604
+ * .run();
1605
+ * ```
1606
+ *
1607
+ * @example
1608
+ * ```typescript
1609
+ * // Result type for API responses
1610
+ * type Result<T> = { success: true; data: T } | { success: false; error: string };
1611
+ *
1612
+ * const result: Result<User> = await fetchUser()
1613
+ * .fold(
1614
+ * error => FuturableTask.resolve({ success: false, error: error.message }),
1615
+ * user => FuturableTask.resolve({ success: true, data: user })
1616
+ * )
1617
+ * .run();
1618
+ * ```
1619
+ *
1620
+ * @example
1621
+ * ```typescript
1622
+ * // Logging both outcomes
1623
+ * await task.fold(
1624
+ * err => FuturableTask.of(() => logError(err)).map(() => 0),
1625
+ * val => FuturableTask.of(() => logSuccess(val)).map(() => val)
1626
+ * ).run();
1627
+ * ```
1628
+ */
1629
+ fold<U>(onFailure: (err: any) => FuturableTask<U>, onSuccess: (value: T) => FuturableTask<U>): FuturableTask<U>;
1630
+ /**
1631
+ * Registers a callback that runs when the task completes (success or failure).
1632
+ *
1633
+ * Similar to Promise.finally(). The callback cannot modify the result or error,
1634
+ * but is useful for cleanup operations like closing connections or hiding spinners.
1635
+ *
1636
+ * If the callback throws or rejects, that error is propagated.
1637
+ *
1638
+ * @param callback - Function to execute on completion (can be async)
1639
+ * @returns A new FuturableTask with the finally handler
1640
+ *
1641
+ * @example
1642
+ * ```typescript
1643
+ * const task = FuturableTask.of(() => fetchData())
1644
+ * .finally(() => {
1645
+ * console.log('Fetch completed');
1646
+ * hideSpinner();
1647
+ * });
1648
+ *
1649
+ * await task.run(); // Always hides spinner, even on error
1650
+ * ```
1651
+ *
1652
+ * @example
1653
+ * ```typescript
1654
+ * // Cleanup resources
1655
+ * const result = await FuturableTask.of(() => {
1656
+ * const connection = openConnection();
1657
+ * return processData(connection);
1658
+ * })
1659
+ * .finally(() => connection.close())
1660
+ * .run();
1661
+ * ```
1662
+ *
1663
+ * @example
1664
+ * ```typescript
1665
+ * // Multiple cleanup operations
1666
+ * await task
1667
+ * .finally(() => releaseResource1())
1668
+ * .finally(() => releaseResource2())
1669
+ * .finally(() => console.log('All cleanup done'))
1670
+ * .run();
1671
+ * ```
1672
+ */
1673
+ finally(callback: () => any): FuturableTask<T>;
1674
+ /**
1675
+ * Adds a timeout to the task execution.
1676
+ *
1677
+ * If the task doesn't complete within the specified time, it's cancelled
1678
+ * and the task rejects with the provided reason.
1679
+ *
1680
+ * The timeout only applies when the task is run, not when it's created.
1681
+ *
1682
+ * @param ms - Timeout duration in milliseconds
1683
+ * @param reason - Rejection reason (default: "TimeoutExceeded")
1684
+ * @returns A new FuturableTask with timeout enforcement
1685
+ *
1686
+ * @example
1687
+ * ```typescript
1688
+ * const task = FuturableTask.of(() => fetch('/slow-api'))
1689
+ * .timeout(5000, new Error('Request timed out after 5s'));
1690
+ *
1691
+ * try {
1692
+ * await task.run();
1693
+ * } catch (err) {
1694
+ * console.error(err); // "Request timed out after 5s"
1695
+ * }
1696
+ * ```
1697
+ *
1698
+ * @example
1699
+ * ```typescript
1700
+ * // Timeout with default reason
1701
+ * await FuturableTask.of(() => longOperation())
1702
+ * .timeout(3000)
1703
+ * .run(); // Rejects with "TimeoutExceeded" after 3s
1704
+ * ```
1705
+ *
1706
+ * @example
1707
+ * ```typescript
1708
+ * // Chaining with retry
1709
+ * const result = await FuturableTask.of(() => fetchData())
1710
+ * .timeout(5000)
1711
+ * .retry(3)
1712
+ * .run();
1713
+ * // Each attempt has a 5s timeout
1714
+ * ```
1715
+ */
1716
+ timeout(ms: number, reason?: any): FuturableTask<T>;
1717
+ /**
1718
+ * Delays the execution of the task by a specified duration.
1719
+ *
1720
+ * The timer starts when run() is called, not when delay() is called.
1721
+ * The task waits for the specified duration before executing.
1722
+ *
1723
+ * Useful for rate limiting, debouncing, or implementing backoff strategies.
1724
+ *
1725
+ * @param ms - Delay duration in milliseconds
1726
+ * @returns A new FuturableTask with the delay
1727
+ *
1728
+ * @example
1729
+ * ```typescript
1730
+ * const delayedTask = FuturableTask.of(() => sendEmail())
1731
+ * .delay(5000); // Wait 5 seconds before sending
1732
+ *
1733
+ * await delayedTask.run(); // Starts after 5s
1734
+ * ```
1735
+ *
1736
+ * @example
1737
+ * ```typescript
1738
+ * // Progressive delays
1739
+ * await FuturableTask.of(() => step1())
1740
+ * .andThen(FuturableTask.of(() => step2()).delay(1000))
1741
+ * .andThen(FuturableTask.of(() => step3()).delay(2000))
1742
+ * .run();
1743
+ * // step1 immediately, step2 after 1s, step3 after 2s
1744
+ * ```
1745
+ *
1746
+ * @example
1747
+ * ```typescript
1748
+ * // Cancellable delay
1749
+ * const task = FuturableTask.of(() => operation()).delay(10000);
1750
+ * const run = task.run();
1751
+ * task.cancel(); // Cancels before the delay completes
1752
+ * ```
1753
+ */
1754
+ delay(ms: number): FuturableTask<T>;
1755
+ /**
1756
+ * Retries the task up to n times on failure.
1757
+ *
1758
+ * Each retry is an independent execution of the original task.
1759
+ * If all attempts fail, the last error is propagated.
1760
+ *
1761
+ * An optional delay can be added between retries for exponential backoff patterns.
1762
+ *
1763
+ * @param retries - Maximum number of retry attempts (0 means 1 total attempt)
1764
+ * @param delayMs - Optional delay between retries in milliseconds (default: 0)
1765
+ * @returns A new FuturableTask with retry logic
1766
+ *
1767
+ * @example
1768
+ * ```typescript
1769
+ * const unreliableTask = FuturableTask.of(() => fetch('/flaky-api'))
1770
+ * .retry(3); // Will try up to 4 times total (initial + 3 retries)
1771
+ *
1772
+ * const data = await unreliableTask.run();
1773
+ * ```
1774
+ *
1775
+ * @example
1776
+ * ```typescript
1777
+ * // With delay between retries
1778
+ * await FuturableTask.of(() => connectToService())
1779
+ * .retry(5, 1000) // Retry 5 times with 1s between attempts
1780
+ * .run();
1781
+ * ```
1782
+ *
1783
+ * @example
1784
+ * ```typescript
1785
+ * // Exponential backoff (manual)
1786
+ * let attempt = 0;
1787
+ * const task = FuturableTask.of(() => fetch('/api'))
1788
+ * .tapError(() => attempt++)
1789
+ * .retry(3, Math.pow(2, attempt) * 1000);
1790
+ * ```
1791
+ */
1792
+ retry(retries: number, delayMs?: number): FuturableTask<T>;
1793
+ /**
1794
+ * Creates a new Task that delays the execution of the original task with debounce logic.
1795
+ *
1796
+ * This method implements "smart debounce":
1797
+ * - If called on a regular Task, it wraps it with a delay
1798
+ * - If called on an already debounced Task (e.g., `.debounce(200).debounce(300)`),
1799
+ * it overrides the previous delay instead of nesting, ensuring only the
1800
+ * latest delay is applied to the source task
1801
+ *
1802
+ * Multiple calls to run() within the debounce window will cancel previous
1803
+ * pending executions and restart the timer.
1804
+ *
1805
+ * Perfect for scenarios like search-as-you-type where you want to wait for
1806
+ * user input to stabilize before executing.
1807
+ *
1808
+ * @param ms - The debounce delay in milliseconds
1809
+ * @returns A new Task instance that manages the debounced execution
1810
+ *
1811
+ * @example
1812
+ * ```typescript
1813
+ * const searchTask = FuturableTask.of(() => searchAPI(query))
1814
+ * .debounce(300);
1815
+ *
1816
+ * // Rapidly calling run() multiple times:
1817
+ * searchTask.run(); // Cancelled
1818
+ * searchTask.run(); // Cancelled
1819
+ * searchTask.run(); // This one executes after 300ms
1820
+ * ```
1821
+ *
1822
+ * @example
1823
+ * ```typescript
1824
+ * // Smart debounce - latest delay wins
1825
+ * const task = FuturableTask.of(() => operation())
1826
+ * .debounce(200)
1827
+ * .debounce(500); // Uses 500ms, not 200ms + 500ms
1828
+ * ```
1829
+ *
1830
+ * @example
1831
+ * ```typescript
1832
+ * // Cancelling a debounced task
1833
+ * const task = FuturableTask.of(() => saveData()).debounce(1000);
1834
+ * task.run();
1835
+ * task.cancel(); // Cancels the pending execution
1836
+ * ```
1837
+ */
1838
+ debounce(ms: number): FuturableTask<T>;
1839
+ /**
1840
+ * Creates a new Task that limits the execution rate to once every 'ms' milliseconds.
1841
+ *
1842
+ * Unlike debounce, throttle ensures the task runs at a steady maximum rate.
1843
+ * The first call executes immediately, and subsequent calls within the throttle
1844
+ * window will return the result of the previous execution.
1845
+ *
1846
+ * Perfect for performance-heavy operations that need to run consistently
1847
+ * during continuous events like scrolling, resizing, or mouse movement.
1848
+ *
1849
+ * @param ms - The throttle interval in milliseconds
1850
+ * @returns A new Task instance with throttling logic
1851
+ *
1852
+ * @example
1853
+ * ```typescript
1854
+ * const scrollTask = FuturableTask.of(() => updateScrollPosition())
1855
+ * .throttle(100);
1856
+ *
1857
+ * window.addEventListener('scroll', () => {
1858
+ * scrollTask.run(); // Runs at most once per 100ms
1859
+ * });
1860
+ * ```
1861
+ *
1862
+ * @example
1863
+ * ```typescript
1864
+ * // API rate limiting
1865
+ * const apiTask = FuturableTask.of(() => fetch('/api/data'))
1866
+ * .throttle(1000);
1867
+ *
1868
+ * // Rapid clicks will reuse the same result within 1s window
1869
+ * button.addEventListener('click', () => {
1870
+ * apiTask.run().then(data => updateUI(data));
1871
+ * });
1872
+ * ```
1873
+ *
1874
+ * @example
1875
+ * ```typescript
1876
+ * // Performance monitoring
1877
+ * const monitor = FuturableTask.of(() => collectMetrics())
1878
+ * .throttle(5000);
1879
+ *
1880
+ * setInterval(() => monitor.run(), 100); // Collects every 5s despite 100ms interval
1881
+ * ```
1882
+ */
1883
+ throttle(ms: number): FuturableTask<T>;
1884
+ /**
1885
+ * Combines this task with another task into a tuple of both results.
1886
+ *
1887
+ * Both tasks execute in parallel and the result is a tuple [T, U].
1888
+ * If either task fails, the combined task fails.
1889
+ *
1890
+ * This is useful for combining independent operations that you need to perform together.
1891
+ *
1892
+ * @template U - The type of value the other task produces
1893
+ * @param other - The task to combine with this one
1894
+ * @returns A new FuturableTask that resolves with a tuple of both results
1895
+ *
1896
+ * @example
1897
+ * ```typescript
1898
+ * const userTask = FuturableTask.of(() => fetchUser());
1899
+ * const postsTask = FuturableTask.of(() => fetchPosts());
1900
+ *
1901
+ * const [user, posts] = await userTask.zip(postsTask).run();
1902
+ * ```
1903
+ *
1904
+ * @example
1905
+ * ```typescript
1906
+ * // Combining multiple independent operations
1907
+ * const combined = taskA
1908
+ * .zip(taskB)
1909
+ * .zip(taskC)
1910
+ * .run(); // [[A, B], C]
1911
+ * ```
1912
+ *
1913
+ * @example
1914
+ * ```typescript
1915
+ * // Error handling - any failure cancels all
1916
+ * try {
1917
+ * const [result1, result2] = await task1.zip(task2).run();
1918
+ * } catch (err) {
1919
+ * // Either task1 or task2 failed
1920
+ * }
1921
+ * ```
1922
+ */
1923
+ zip<U>(other: FuturableTask<U>): FuturableTask<[T, U]>;
1924
+ /**
1925
+ * Combines this task with another task and applies a combining function.
1926
+ *
1927
+ * Similar to zip, but instead of returning a tuple, it applies a function
1928
+ * to both results to produce a single combined value.
1929
+ *
1930
+ * Both tasks execute in parallel.
1931
+ *
1932
+ * @template U - The type of value the other task produces
1933
+ * @template R - The type of the combined result
1934
+ * @param other - The task to combine with this one
1935
+ * @param fn - Function to combine both results
1936
+ * @returns A new FuturableTask with the combined result
1937
+ *
1938
+ * @example
1939
+ * ```typescript
1940
+ * const sum = await taskA.zipWith(taskB, (a, b) => a + b).run();
1941
+ * ```
1942
+ *
1943
+ * @example
1944
+ * ```typescript
1945
+ * // Combining user data
1946
+ * const fullProfile = await basicInfoTask.zipWith(
1947
+ * preferencesTask,
1948
+ * (basic, prefs) => ({ ...basic, preferences: prefs })
1949
+ * ).run();
1950
+ * ```
1951
+ *
1952
+ * @example
1953
+ * ```typescript
1954
+ * // Computing derived values
1955
+ * const average = await minTask.zipWith(
1956
+ * maxTask,
1957
+ * (min, max) => (min + max) / 2
1958
+ * ).run();
1959
+ * ```
1960
+ */
1961
+ zipWith<U, R>(other: FuturableTask<U>, fn: (a: T, b: U) => R): FuturableTask<R>;
1962
+ /**
1963
+ * Maps both success and error outcomes to new values.
1964
+ *
1965
+ * Applies different transformation functions depending on whether the task
1966
+ * succeeds or fails. Unlike fold, the transformations are synchronous and
1967
+ * don't return tasks.
1968
+ *
1969
+ * Useful for normalizing success and error cases into a consistent format.
1970
+ *
1971
+ * @template U - The type of the transformed success value
1972
+ * @template V - The type of the transformed error value
1973
+ * @param onSuccess - Function to transform the success value
1974
+ * @param onError - Function to transform the error
1975
+ * @returns A new FuturableTask with transformed success/error
1976
+ *
1977
+ * @example
1978
+ * ```typescript
1979
+ * const task = FuturableTask.of(() => riskyOperation())
1980
+ * .bimap(
1981
+ * result => `Success: ${result}`,
1982
+ * error => new CustomError(error.message)
1983
+ * );
1984
+ * ```
1985
+ *
1986
+ * @example
1987
+ * ```typescript
1988
+ * // Normalizing API responses
1989
+ * const normalized = await fetchData()
1990
+ * .bimap(
1991
+ * data => ({ status: 'ok', data }),
1992
+ * err => ({ status: 'error', message: err.message })
1993
+ * )
1994
+ * .run();
1995
+ * ```
1996
+ *
1997
+ * @example
1998
+ * ```typescript
1999
+ * // Adding context to errors
2000
+ * await task.bimap(
2001
+ * val => val,
2002
+ * err => new Error(`Operation failed: ${err.message}`)
2003
+ * ).run();
2004
+ * ```
2005
+ */
2006
+ bimap<U, V>(onSuccess: (value: T) => U, onError: (error: any) => V): FuturableTask<U>;
2007
+ /**
2008
+ * Repeats this task n times, collecting all results.
2009
+ *
2010
+ * Each execution is independent. If any execution fails, the entire
2011
+ * repeat operation fails.
2012
+ *
2013
+ * Executes sequentially, not in parallel.
2014
+ *
2015
+ * @param n - Number of times to repeat (must be >= 0)
2016
+ * @returns A new FuturableTask that resolves with an array of all results
2017
+ *
2018
+ * @example
2019
+ * ```typescript
2020
+ * const results = await task.repeat(3).run(); // [result1, result2, result3]
2021
+ * ```
2022
+ *
2023
+ * @example
2024
+ * ```typescript
2025
+ * // Collecting multiple samples
2026
+ * const measurements = await FuturableTask.of(() => takeMeasurement())
2027
+ * .repeat(10)
2028
+ * .map(samples => average(samples))
2029
+ * .run();
2030
+ * ```
2031
+ *
2032
+ * @example
2033
+ * ```typescript
2034
+ * // Batch operations
2035
+ * const created = await FuturableTask.of(() => createRecord())
2036
+ * .repeat(5)
2037
+ * .run();
2038
+ * ```
2039
+ */
2040
+ repeat(n: number): FuturableTask<T[]>;
2041
+ /**
2042
+ * Composes multiple transformation functions in sequence.
2043
+ *
2044
+ * Applies transformations from left to right, passing the result of each
2045
+ * transformation to the next. Type-safe for up to 9 transformations.
2046
+ *
2047
+ * This is a powerful way to build complex task pipelines in a readable way.
2048
+ *
2049
+ * @param fns - Transformation functions to apply in order
2050
+ * @returns The result of applying all transformations
2051
+ *
2052
+ * @example
2053
+ * ```typescript
2054
+ * const addRetry = (task) => task.retry(3);
2055
+ * const addTimeout = (task) => task.timeout(5000);
2056
+ * const addLogging = (task) => task.tap(x => console.log(x));
2057
+ *
2058
+ * const enhanced = baseTask.pipe(addRetry, addTimeout, addLogging);
2059
+ * ```
2060
+ *
2061
+ * @example
2062
+ * ```typescript
2063
+ * // Building a processing pipeline
2064
+ * const processData = (task: FuturableTask<string>) => task
2065
+ * .pipe(
2066
+ * t => t.map(s => s.trim()),
2067
+ * t => t.map(s => s.toLowerCase()),
2068
+ * t => t.map(s => s.split(',')),
2069
+ * t => t.map(arr => arr.map(s => s.trim()))
2070
+ * );
2071
+ * ```
2072
+ *
2073
+ * @example
2074
+ * ```typescript
2075
+ * // Reusable middleware
2076
+ * const withErrorHandling = (task) => task.tapError(logError);
2077
+ * const withRetry = (task) => task.retry(3, 1000);
2078
+ * const withTimeout = (task) => task.timeout(10000);
2079
+ *
2080
+ * const robustTask = apiTask.pipe(
2081
+ * withErrorHandling,
2082
+ * withRetry,
2083
+ * withTimeout
2084
+ * );
2085
+ * ```
2086
+ */
2087
+ pipe<A>(f1: (t: FuturableTask<T>) => A): A;
2088
+ pipe<A, B>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B): B;
2089
+ pipe<A, B, C>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C): C;
2090
+ pipe<A, B, C, D>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C, f4: (c: C) => D): D;
2091
+ pipe<A, B, C, D, E>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C, f4: (c: C) => D, f5: (d: D) => E): E;
2092
+ pipe<A, B, C, D, E, F>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C, f4: (c: C) => D, f5: (d: D) => E, f6: (e: E) => F): F;
2093
+ pipe<A, B, C, D, E, F, G>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C, f4: (c: C) => D, f5: (d: D) => E, f6: (e: E) => F, f7: (f: F) => G): G;
2094
+ pipe<A, B, C, D, E, F, G, H>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C, f4: (c: C) => D, f5: (d: D) => E, f6: (e: E) => F, f7: (f: F) => G, f8: (g: G) => H): H;
2095
+ pipe<A, B, C, D, E, F, G, H, I>(f1: (t: FuturableTask<T>) => A, f2: (a: A) => B, f3: (b: B) => C, f4: (c: C) => D, f5: (d: D) => E, f6: (e: E) => F, f7: (f: F) => G, f8: (g: G) => H, f9: (h: H) => I): I;
2096
+ /**
2097
+ * Creates a new FuturableTask that performs an HTTP fetch when executed.
2098
+ *
2099
+ * @param url - URL to fetch, or a function receiving the task's value
2100
+ * @param opts - Fetch options, or a function receiving the task's value
2101
+ * @returns A new FuturableTask that resolves with the Response
2102
+ *
2103
+ * @example
2104
+ * ```typescript
2105
+ * const fetchTask = FuturableTask.resolve('users')
2106
+ * .fetch(endpoint => `https://api.example.com/${endpoint}`);
2107
+ *
2108
+ * const response = await fetchTask.run();
2109
+ * const data = await response.json();
2110
+ * ```
2111
+ */
2112
+ fetch(url: string | ((val: T) => string), opts?: RequestInit | ((val: T) => RequestInit)): FuturableTask<Response>;
2113
+ /**
2114
+ * Creates a FuturableTask from various input types.
2115
+ *
2116
+ * Accepts:
2117
+ * - Plain values: Wraps in a task that resolves immediately
2118
+ * - Functions: Creates a task that executes the function
2119
+ *
2120
+ * Functions receive FuturableUtils with signal, onCancel, delay, sleep, fetch, etc.
2121
+ *
2122
+ * @template U - Type of the value
2123
+ * @param input - A value or a function that returns a value/Promise
2124
+ * @param signal - Optional AbortSignal for the task
2125
+ * @returns A new FuturableTask
2126
+ *
2127
+ * @example
2128
+ * ```typescript
2129
+ * // From a value
2130
+ * const task1 = FuturableTask.of(42);
2131
+ * await task1.run(); // 42
2132
+ * ```
2133
+ *
2134
+ * @example
2135
+ * ```typescript
2136
+ * // From a function
2137
+ * const task2 = FuturableTask.of(() => fetch('/api').then(r => r.json()));
2138
+ * ```
2139
+ *
2140
+ * @example
2141
+ * ```typescript
2142
+ * // With utils
2143
+ * const task3 = FuturableTask.of(async (utils) => {
2144
+ * const response = await utils.fetch('/api/data');
2145
+ * return response.json();
2146
+ * });
2147
+ * ```
2148
+ *
2149
+ * @example
2150
+ * ```typescript
2151
+ * // With external signal
2152
+ * const controller = new AbortController();
2153
+ * const task = FuturableTask.of(() => operation(), controller.signal);
2154
+ * ```
2155
+ */
2156
+ static of<U>(input: U | ((utils: FuturableUtils<U>) => Promise<U>), signal?: AbortSignal): FuturableTask<U>;
2157
+ /**
2158
+ * Creates a FuturableTask that immediately resolves with the given value.
2159
+ *
2160
+ * Alias for FuturableTask.of() when passing a plain value.
2161
+ * Useful for lifting values into the Task context.
2162
+ *
2163
+ * @template U - Type of the value
2164
+ * @param v - The value to resolve with
2165
+ * @param signal - Optional AbortSignal for the task
2166
+ * @returns A FuturableTask that resolves to the value
2167
+ *
2168
+ * @example
2169
+ * ```typescript
2170
+ * const task = FuturableTask.resolve(42)
2171
+ * .map(x => x * 2);
2172
+ *
2173
+ * await task.run(); // 84
2174
+ * ```
2175
+ *
2176
+ * @example
2177
+ * ```typescript
2178
+ * // Creating task chains
2179
+ * const pipeline = FuturableTask.resolve(data)
2180
+ * .map(d => transform(d))
2181
+ * .flatMap(t => FuturableTask.of(() => save(t)));
2182
+ * ```
2183
+ *
2184
+ * @example
2185
+ * ```typescript
2186
+ * // Default values
2187
+ * const task = condition
2188
+ * ? FuturableTask.of(() => fetchData())
2189
+ * : FuturableTask.resolve(defaultData);
2190
+ * ```
2191
+ */
2192
+ static resolve<U = any>(v: U, signal?: AbortSignal): FuturableTask<U>;
2193
+ /**
2194
+ * Creates a FuturableTask that immediately rejects with the given reason.
2195
+ *
2196
+ * Useful for creating tasks that represent known failures,
2197
+ * or for testing error handling logic.
2198
+ *
2199
+ * @param reason - The rejection reason (typically an Error)
2200
+ * @param signal - Optional AbortSignal for the task
2201
+ * @returns A FuturableTask that rejects with the reason
2202
+ *
2203
+ * @example
2204
+ * ```typescript
2205
+ * const task = FuturableTask.reject(new Error('Not implemented'))
2206
+ * .orElse(() => FuturableTask.of(() => fallbackImplementation()));
2207
+ * ```
2208
+ *
2209
+ * @example
2210
+ * ```typescript
2211
+ * // Conditional errors
2212
+ * const task = value < 0
2213
+ * ? FuturableTask.reject(new Error('Value must be positive'))
2214
+ * : FuturableTask.of(() => process(value));
2215
+ * ```
2216
+ *
2217
+ * @example
2218
+ * ```typescript
2219
+ * // Testing error handlers
2220
+ * const testTask = FuturableTask.reject(new Error('Test error'))
2221
+ * .tapError(err => assert(err.message === 'Test error'));
2222
+ * ```
2223
+ */
2224
+ static reject<U = never>(reason: any, signal?: AbortSignal): FuturableTask<U>;
2225
+ /**
2226
+ * Executes all tasks in parallel and resolves when all complete.
2227
+ *
2228
+ * All tasks run concurrently. If any task fails, the combined task
2229
+ * fails immediately and all running tasks are cancelled.
2230
+ *
2231
+ * Returns an array of results in the same order as the input tasks.
2232
+ *
2233
+ * @template T - Type of values the tasks produce
2234
+ * @param tasks - Array of FuturableTasks to execute in parallel
2235
+ * @param signal - Optional AbortSignal for the combined task
2236
+ * @returns A FuturableTask that resolves with an array of all results
2237
+ *
2238
+ * @example
2239
+ * ```typescript
2240
+ * const tasks = [
2241
+ * FuturableTask.of(() => fetch('/api/users')),
2242
+ * FuturableTask.of(() => fetch('/api/posts')),
2243
+ * FuturableTask.of(() => fetch('/api/comments'))
2244
+ * ];
2245
+ *
2246
+ * const [users, posts, comments] = await FuturableTask.all(tasks).run();
2247
+ * ```
2248
+ *
2249
+ * @example
2250
+ * ```typescript
2251
+ * // With error handling
2252
+ * try {
2253
+ * const results = await FuturableTask.all([task1, task2, task3]).run();
2254
+ * } catch (err) {
2255
+ * // One of the tasks failed, others were cancelled
2256
+ * }
2257
+ * ```
2258
+ *
2259
+ * @example
2260
+ * ```typescript
2261
+ * // Dynamic task lists
2262
+ * const userIds = [1, 2, 3, 4, 5];
2263
+ * const tasks = userIds.map(id =>
2264
+ * FuturableTask.of(() => fetchUser(id))
2265
+ * );
2266
+ * const users = await FuturableTask.all(tasks).run();
2267
+ * ```
2268
+ */
2269
+ static all<T>(tasks: FuturableTask<T>[], signal?: AbortSignal): FuturableTask<T[]>;
2270
+ /**
2271
+ * Executes all tasks in parallel and waits for all to settle (resolve or reject).
2272
+ *
2273
+ * Never rejects. Returns an array of result objects indicating the outcome
2274
+ * of each task. Each result has a status ('fulfilled' or 'rejected') and
2275
+ * either a value or reason.
2276
+ *
2277
+ * Useful when you want to attempt multiple operations and handle failures individually.
2278
+ *
2279
+ * @template T - Type of values the tasks produce
2280
+ * @param tasks - Array of FuturableTasks to execute
2281
+ * @param signal - Optional AbortSignal for the combined task
2282
+ * @returns A FuturableTask that resolves with an array of PromiseSettledResult objects
2283
+ *
2284
+ * @example
2285
+ * ```typescript
2286
+ * const results = await FuturableTask.allSettled([
2287
+ * FuturableTask.resolve(1),
2288
+ * FuturableTask.reject('error'),
2289
+ * FuturableTask.resolve(3)
2290
+ * ]).run();
2291
+ *
2292
+ * results.forEach(result => {
2293
+ * if (result.status === 'fulfilled') {
2294
+ * console.log('Success:', result.value);
2295
+ * } else {
2296
+ * console.log('Failed:', result.reason);
2297
+ * }
2298
+ * });
2299
+ * // Success: 1
2300
+ * // Failed: error
2301
+ * // Success: 3
2302
+ * ```
2303
+ *
2304
+ * @example
2305
+ * ```typescript
2306
+ * // Batch operations with individual error handling
2307
+ * const results = await FuturableTask.allSettled(
2308
+ * userIds.map(id => FuturableTask.of(() => deleteUser(id)))
2309
+ * ).run();
2310
+ *
2311
+ * const succeeded = results.filter(r => r.status === 'fulfilled').length;
2312
+ * const failed = results.filter(r => r.status === 'rejected').length;
2313
+ * console.log(`Deleted ${succeeded}, failed ${failed}`);
2314
+ * ```
2315
+ */
2316
+ static allSettled<T>(tasks: FuturableTask<T>[], signal?: AbortSignal): FuturableTask<PromiseSettledResult<T>[]>;
2317
+ /**
2318
+ * Executes all tasks in parallel and resolves/rejects with the first to settle.
2319
+ *
2320
+ * Once one task settles (successfully or with error), all other tasks are cancelled.
2321
+ * The result matches the first settled task - success or failure.
2322
+ *
2323
+ * Useful for timeout patterns or racing multiple data sources.
2324
+ *
2325
+ * @template T - Type of values the tasks produce
2326
+ * @param tasks - Array of FuturableTasks to race
2327
+ * @param signal - Optional AbortSignal for the combined task
2328
+ * @returns A FuturableTask that settles with the first task's result
2329
+ *
2330
+ * @example
2331
+ * ```typescript
2332
+ * const fastest = await FuturableTask.race([
2333
+ * FuturableTask.of(() => fetchFromCDN1()),
2334
+ * FuturableTask.of(() => fetchFromCDN2()),
2335
+ * FuturableTask.of(() => fetchFromCDN3())
2336
+ * ]).run();
2337
+ *
2338
+ * console.log('Fastest CDN responded with:', fastest);
2339
+ * ```
2340
+ *
2341
+ * @example
2342
+ * ```typescript
2343
+ * // Timeout pattern
2344
+ * const result = await FuturableTask.race([
2345
+ * FuturableTask.of(() => slowOperation()),
2346
+ * FuturableTask.delay(5000).flatMap(() =>
2347
+ * FuturableTask.reject(new Error('Timeout'))
2348
+ * )
2349
+ * ]).run();
2350
+ * ```
2351
+ *
2352
+ * @example
2353
+ * ```typescript
2354
+ * // First successful response wins
2355
+ * const data = await FuturableTask.race([
2356
+ * cacheTask,
2357
+ * apiTask
2358
+ * ]).run(); // Uses cache if available, API otherwise
2359
+ * ```
2360
+ */
2361
+ static race<T>(tasks: FuturableTask<T>[], signal?: AbortSignal): FuturableTask<T>;
2362
+ /**
2363
+ * Executes all tasks in parallel and resolves with the first successful result.
2364
+ *
2365
+ * Ignores failures and only resolves when at least one task succeeds.
2366
+ * Only rejects if ALL tasks fail (with an AggregateError containing all errors).
2367
+ *
2368
+ * Perfect for fallback scenarios with multiple alternatives.
2369
+ *
2370
+ * @template T - Type of values the tasks produce
2371
+ * @param tasks - Array of FuturableTasks to execute
2372
+ * @param signal - Optional AbortSignal for the combined task
2373
+ * @returns A FuturableTask that resolves with the first success
2374
+ *
2375
+ * @example
2376
+ * ```typescript
2377
+ * const data = await FuturableTask.any([
2378
+ * FuturableTask.of(() => fetchFromPrimaryServer()),
2379
+ * FuturableTask.of(() => fetchFromBackupServer1()),
2380
+ * FuturableTask.of(() => fetchFromBackupServer2())
2381
+ * ]).run();
2382
+ *
2383
+ * // Returns data from whichever server responds successfully first
2384
+ * ```
2385
+ *
2386
+ * @example
2387
+ * ```typescript
2388
+ * // Multiple fallback sources
2389
+ * const config = await FuturableTask.any([
2390
+ * FuturableTask.of(() => loadFromEnv()),
2391
+ * FuturableTask.of(() => loadFromFile()),
2392
+ * FuturableTask.of(() => loadDefaults())
2393
+ * ]).run();
2394
+ * ```
2395
+ *
2396
+ * @example
2397
+ * ```typescript
2398
+ * // All fail case
2399
+ * try {
2400
+ * await FuturableTask.any([
2401
+ * FuturableTask.reject('error1'),
2402
+ * FuturableTask.reject('error2')
2403
+ * ]).run();
2404
+ * } catch (err) {
2405
+ * console.log(err.errors); // ['error1', 'error2']
2406
+ * }
2407
+ * ```
2408
+ */
2409
+ static any<T>(tasks: FuturableTask<T>[], signal?: AbortSignal): FuturableTask<T>;
2410
+ /**
2411
+ * Creates a FuturableTask that resolves after a specified delay.
2412
+ *
2413
+ * The delay is lazy - it only starts when run() is called.
2414
+ * The task resolves with void (no value).
2415
+ *
2416
+ * Useful for adding delays in task chains or implementing backoff strategies.
2417
+ *
2418
+ * @param ms - Delay duration in milliseconds
2419
+ * @param signal - Optional AbortSignal for the task
2420
+ * @returns A FuturableTask that resolves after the delay
2421
+ *
2422
+ * @example
2423
+ * ```typescript
2424
+ * await FuturableTask.delay(2000).run(); // Waits 2 seconds
2425
+ * ```
2426
+ *
2427
+ * @example
2428
+ * ```typescript
2429
+ * // Chaining with delays
2430
+ * await FuturableTask.resolve('Starting...')
2431
+ * .tap(console.log)
2432
+ * .andThen(FuturableTask.delay(1000))
2433
+ * .andThen(FuturableTask.resolve('Done!'))
2434
+ * .tap(console.log)
2435
+ * .run();
2436
+ * ```
2437
+ *
2438
+ * @example
2439
+ * ```typescript
2440
+ * // Cancellable delay
2441
+ * const delayTask = FuturableTask.delay(5000);
2442
+ * delayTask.run();
2443
+ * delayTask.cancel(); // Cancels the delay
2444
+ * ```
2445
+ */
2446
+ static delay(ms: number, signal?: AbortSignal): FuturableTask<void>;
2447
+ /**
2448
+ * Creates a FuturableTask from an event listener on a given target.
2449
+ *
2450
+ * The listener is only attached when run() is called, and automatically
2451
+ * unsubscribes after the first occurrence to maintain single-value semantics.
2452
+ *
2453
+ * If the task is cancelled before the event fires, the listener is properly
2454
+ * removed to prevent memory leaks.
2455
+ *
2456
+ * @template E - The type of the Event object
2457
+ * @param target - The DOM element, Window, or event target
2458
+ * @param name - The name of the event to listen for (e.g., 'click', 'message')
2459
+ * @param opts - Standard listener options like capture, passive, or once
2460
+ * @param signal - Optional AbortSignal for the task
2461
+ * @returns A new FuturableTask that resolves with the Event object
2462
+ *
2463
+ * @example
2464
+ * ```typescript
2465
+ * // Listen for a one-time click
2466
+ * const clickTask = FuturableTask.fromEvent(
2467
+ * document.getElementById('myBtn'),
2468
+ * 'click',
2469
+ * { passive: true }
2470
+ * );
2471
+ *
2472
+ * const event = await clickTask.run();
2473
+ * console.log('Button clicked at:', event.timeStamp);
2474
+ * ```
2475
+ *
2476
+ * @example
2477
+ * ```typescript
2478
+ * // With timeout
2479
+ * try {
2480
+ * const event = await FuturableTask
2481
+ * .fromEvent(button, 'click')
2482
+ * .timeout(5000, new Error('No click within 5 seconds'))
2483
+ * .run();
2484
+ * } catch (err) {
2485
+ * console.log('Timed out waiting for click');
2486
+ * }
2487
+ * ```
2488
+ *
2489
+ * @example
2490
+ * ```typescript
2491
+ * // Cancellable event listener
2492
+ * const task = FuturableTask.fromEvent(window, 'resize');
2493
+ * task.run();
2494
+ * // ... later
2495
+ * task.cancel(); // Removes the listener
2496
+ * ```
2497
+ */
2498
+ static fromEvent<E extends Event>(target: EventTarget, name: string, opts?: AddEventListenerOptions, signal?: AbortSignal): FuturableTask<E>;
2499
+ /**
2500
+ * Executes an array of tasks sequentially, one after another.
2501
+ *
2502
+ * Each task waits for the previous one to complete before starting.
2503
+ * If any task fails, the sequence stops and the error is propagated.
2504
+ *
2505
+ * Returns an array of all results in order.
2506
+ *
2507
+ * @template T - Type of values the tasks produce
2508
+ * @param tasks - Array of FuturableTasks to execute in sequence
2509
+ * @param signal - Optional AbortSignal for the combined task
2510
+ * @returns A FuturableTask that resolves with an array of all results
2511
+ *
2512
+ * @example
2513
+ * ```typescript
2514
+ * const tasks = [
2515
+ * FuturableTask.of(() => step1()),
2516
+ * FuturableTask.of(() => step2()),
2517
+ * FuturableTask.of(() => step3())
2518
+ * ];
2519
+ *
2520
+ * const results = await FuturableTask.sequence(tasks).run();
2521
+ * // step1 completes, then step2, then step3
2522
+ * ```
2523
+ *
2524
+ * @example
2525
+ * ```typescript
2526
+ * // Database migrations in order
2527
+ * const migrations = [
2528
+ * FuturableTask.of(() => createUsersTable()),
2529
+ * FuturableTask.of(() => createPostsTable()),
2530
+ * FuturableTask.of(() => addForeignKeys())
2531
+ * ];
2532
+ * await FuturableTask.sequence(migrations).run();
2533
+ * ```
2534
+ *
2535
+ * @example
2536
+ * ```typescript
2537
+ * // With delays between steps
2538
+ * const steps = [
2539
+ * FuturableTask.of(() => init()),
2540
+ * FuturableTask.delay(1000).andThen(FuturableTask.of(() => warmup())),
2541
+ * FuturableTask.delay(2000).andThen(FuturableTask.of(() => start()))
2542
+ * ];
2543
+ * await FuturableTask.sequence(steps).run();
2544
+ * ```
2545
+ */
2546
+ static sequence<T>(tasks: FuturableTask<T>[], signal?: AbortSignal): FuturableTask<T[]>;
2547
+ /**
2548
+ * Executes tasks in parallel with a concurrency limit.
2549
+ *
2550
+ * Limits the number of tasks running simultaneously. When a task completes,
2551
+ * the next queued task starts. If any task fails, all running tasks are
2552
+ * cancelled and the error is propagated.
2553
+ *
2554
+ * Returns results in the same order as input tasks.
2555
+ *
2556
+ * @template T - Type of value the tasks produce
2557
+ * @param tasks - Array of FuturableTasks
2558
+ * @param limit - Maximum number of concurrent executions (default: 5)
2559
+ * @param signal - Optional AbortSignal for the combined task
2560
+ * @returns A FuturableTask that resolves with an array of all results
2561
+ *
2562
+ * @example
2563
+ * ```typescript
2564
+ * // Process 100 items with max 5 concurrent operations
2565
+ * const tasks = items.map(item =>
2566
+ * FuturableTask.of(() => processItem(item))
2567
+ * );
2568
+ *
2569
+ * const results = await FuturableTask.parallel(tasks, 5).run();
2570
+ * ```
2571
+ *
2572
+ * @example
2573
+ * ```typescript
2574
+ * // API rate limiting - max 3 concurrent requests
2575
+ * const userTasks = userIds.map(id =>
2576
+ * FuturableTask.of(() => fetch(`/api/users/${id}`))
2577
+ * );
2578
+ *
2579
+ * const users = await FuturableTask.parallel(userTasks, 3).run();
2580
+ * ```
2581
+ *
2582
+ * @example
2583
+ * ```typescript
2584
+ * // With error handling
2585
+ * try {
2586
+ * const results = await FuturableTask.parallel(tasks, 10).run();
2587
+ * } catch (err) {
2588
+ * // One task failed, all running tasks were cancelled
2589
+ * }
2590
+ * ```
2591
+ */
2592
+ static parallel<T>(tasks: FuturableTask<T>[], limit?: number, signal?: AbortSignal): FuturableTask<T[]>;
2593
+ /**
2594
+ * Creates a higher-order function (limiter) to control the maximum number
2595
+ * of concurrent executions for a set of Tasks.
2596
+ *
2597
+ * Tasks do not enter the queue or increment the running counter until
2598
+ * their .run() method is explicitly called. Tasks are processed FIFO.
2599
+ *
2600
+ * Optional event hooks allow tracking active tasks, completions, errors, and idle states.
2601
+ *
2602
+ * The returned limiter function wraps tasks and includes readonly properties
2603
+ * for monitoring (activeCount, pendingCount, concurrency).
2604
+ *
2605
+ * @param concurrency - Maximum number of tasks allowed to run simultaneously (must be >= 1)
2606
+ * @param events - Optional lifecycle hooks for monitoring
2607
+ * @param signal - Optional AbortSignal for the limiter
2608
+ * @returns A decorator function that limits task concurrency
2609
+ *
2610
+ * @example
2611
+ * ```typescript
2612
+ * const limiter = FuturableTask.createLimiter(2, {
2613
+ * onActive: (task) => console.log('Task started'),
2614
+ * onCompleted: (result) => console.log('Task completed:', result),
2615
+ * onIdle: () => console.log('All tasks finished!')
2616
+ * });
2617
+ *
2618
+ * // Wrap your original tasks
2619
+ * const tasks = [1,2,3,4,5].map(i => limiter(
2620
+ * FuturableTask.of(async () => {
2621
+ * await delay(1000);
2622
+ * return i;
2623
+ * })
2624
+ * ));
2625
+ *
2626
+ * // Execution starts here, respects limit of 2
2627
+ * const results = await FuturableTask.all(tasks).run();
2628
+ * console.log(results); // [1, 2, 3, 4, 5]
2629
+ * ```
2630
+ *
2631
+ * @example
2632
+ * ```typescript
2633
+ * // Monitoring limiter state
2634
+ * const limiter = FuturableTask.createLimiter(3);
2635
+ *
2636
+ * console.log(limiter.concurrency); // 3
2637
+ * console.log(limiter.activeCount); // 0
2638
+ * console.log(limiter.pendingCount); // 0
2639
+ *
2640
+ * const task = limiter(FuturableTask.of(() => operation()));
2641
+ * task.run();
2642
+ *
2643
+ * console.log(limiter.activeCount); // 1
2644
+ * ```
2645
+ *
2646
+ * @example
2647
+ * ```typescript
2648
+ * // API rate limiting
2649
+ * const apiLimiter = FuturableTask.createLimiter(5, {
2650
+ * onError: (err) => logger.error('API call failed', err)
2651
+ * });
2652
+ *
2653
+ * const fetchUser = (id) => apiLimiter(
2654
+ * FuturableTask.of(() => fetch(`/api/users/${id}`))
2655
+ * );
2656
+ *
2657
+ * const users = await FuturableTask.all(
2658
+ * userIds.map(fetchUser)
2659
+ * ).run();
2660
+ * ```
2661
+ */
2662
+ static createLimiter(concurrency: number, events?: LimiterEvents, signal?: AbortSignal): FuturableTaskLimiter;
2663
+ /**
2664
+ * Composes a FuturableTask through a sequence of transformation operators.
2665
+ *
2666
+ * Applies operators from left to right, similar to pipe but starting with
2667
+ * an initial task. Type-safe composition ensures each operator's output
2668
+ * matches the next operator's input.
2669
+ *
2670
+ * Useful for building reusable task pipelines.
2671
+ *
2672
+ * @template T - The type of the value produced by the initial task
2673
+ * @template R - The type of the value produced by the final task
2674
+ * @param initial - The starting FuturableTask to transform
2675
+ * @param operators - Variadic list of transformation functions
2676
+ * @returns A new FuturableTask representing the composed operations
2677
+ *
2678
+ * @example
2679
+ * ```typescript
2680
+ * // Linear composition
2681
+ * const finalTask = FuturableTask.compose(
2682
+ * FuturableTask.of(() => fetchUser(1)),
2683
+ * (t) => t.retry(3),
2684
+ * (t) => t.timeout(5000),
2685
+ * (t) => t.map(user => user.name)
2686
+ * );
2687
+ *
2688
+ * const name = await finalTask.run();
2689
+ * ```
2690
+ *
2691
+ * @example
2692
+ * ```typescript
2693
+ * // Building reusable pipelines
2694
+ * const robustAPI = <T>(task: FuturableTask<T>) =>
2695
+ * FuturableTask.compose(
2696
+ * task,
2697
+ * t => t.retry(3, 1000),
2698
+ * t => t.timeout(10000),
2699
+ * t => t.tapError(err => logger.error(err))
2700
+ * );
2701
+ *
2702
+ * const userData = await robustAPI(
2703
+ * FuturableTask.of(() => fetch('/api/user'))
2704
+ * ).run();
2705
+ * ```
2706
+ *
2707
+ * @example
2708
+ * ```typescript
2709
+ * // Data processing pipeline
2710
+ * const processData = FuturableTask.compose(
2711
+ * FuturableTask.of(() => loadRawData()),
2712
+ * t => t.map(data => validate(data)),
2713
+ * t => t.map(data => transform(data)),
2714
+ * t => t.flatMap(data => FuturableTask.of(() => save(data)))
2715
+ * );
2716
+ * ```
2717
+ */
2718
+ static compose<T, R>(initial: FuturableTask<T>, ...operators: Array<(t: FuturableTask<any>) => FuturableTask<any>>): FuturableTask<R>;
2719
+ /**
2720
+ * Filters an array of tasks based on a predicate.
2721
+ *
2722
+ * Executes all tasks sequentially, then applies the predicate to each result.
2723
+ * Only values that pass the predicate are included in the final array.
2724
+ *
2725
+ * The predicate can be async, allowing for asynchronous filtering logic.
2726
+ *
2727
+ * @template T - Type of values the tasks produce
2728
+ * @param tasks - Array of tasks to filter
2729
+ * @param predicate - Function to test each value (can be async)
2730
+ * @param signal - Optional AbortSignal for the combined task
2731
+ * @returns A FuturableTask that resolves with filtered values
2732
+ *
2733
+ * @example
2734
+ * ```typescript
2735
+ * const tasks = [
2736
+ * FuturableTask.resolve(1),
2737
+ * FuturableTask.resolve(2),
2738
+ * FuturableTask.resolve(3),
2739
+ * FuturableTask.resolve(4)
2740
+ * ];
2741
+ *
2742
+ * const evenNumbers = await FuturableTask
2743
+ * .filter(tasks, n => n % 2 === 0)
2744
+ * .run(); // [2, 4]
2745
+ * ```
2746
+ *
2747
+ * @example
2748
+ * ```typescript
2749
+ * // Async filtering
2750
+ * const userTasks = ids.map(id => FuturableTask.of(() => fetchUser(id)));
2751
+ *
2752
+ * const activeUsers = await FuturableTask.filter(
2753
+ * userTasks,
2754
+ * async user => await isActive(user.id)
2755
+ * ).run();
2756
+ * ```
2757
+ *
2758
+ * @example
2759
+ * ```typescript
2760
+ * // Filtering with validation
2761
+ * const results = await FuturableTask.filter(
2762
+ * dataTasks,
2763
+ * data => data.isValid && data.score > 0.5
2764
+ * ).run();
2765
+ * ```
2766
+ */
2767
+ static filter<T>(tasks: FuturableTask<T>[], predicate: (value: T) => boolean | Promise<boolean>, signal?: AbortSignal): FuturableTask<T[]>;
2768
+ /**
2769
+ * Reduces an array of tasks to a single value.
2770
+ *
2771
+ * Executes tasks sequentially, accumulating a result by applying the reducer
2772
+ * function to each task's value. The reducer receives the accumulator, current
2773
+ * value, and index.
2774
+ *
2775
+ * The reducer can be async.
2776
+ *
2777
+ * @template T - Type of values the tasks produce
2778
+ * @template U - Type of the accumulated result
2779
+ * @param tasks - Array of tasks to reduce
2780
+ * @param reducer - Function to accumulate results (can be async)
2781
+ * @param initialValue - Initial value for the accumulator
2782
+ * @param signal - Optional AbortSignal for the combined task
2783
+ * @returns A FuturableTask that resolves with the final accumulated value
2784
+ *
2785
+ * @example
2786
+ * ```typescript
2787
+ * const tasks = [
2788
+ * FuturableTask.resolve(1),
2789
+ * FuturableTask.resolve(2),
2790
+ * FuturableTask.resolve(3)
2791
+ * ];
2792
+ *
2793
+ * const sum = await FuturableTask
2794
+ * .reduce(tasks, (acc, n) => acc + n, 0)
2795
+ * .run(); // 6
2796
+ * ```
2797
+ *
2798
+ * @example
2799
+ * ```typescript
2800
+ * // Building an object from tasks
2801
+ * const userTasks = ids.map(id => FuturableTask.of(() => fetchUser(id)));
2802
+ *
2803
+ * const userMap = await FuturableTask.reduce(
2804
+ * userTasks,
2805
+ * (map, user) => ({ ...map, [user.id]: user }),
2806
+ * {}
2807
+ * ).run();
2808
+ * ```
2809
+ *
2810
+ * @example
2811
+ * ```typescript
2812
+ * // Async reducer
2813
+ * const total = await FuturableTask.reduce(
2814
+ * tasks,
2815
+ * async (acc, value) => {
2816
+ * const processed = await processValue(value);
2817
+ * return acc + processed;
2818
+ * },
2819
+ * 0
2820
+ * ).run();
2821
+ * ```
2822
+ */
2823
+ static reduce<T, U>(tasks: FuturableTask<T>[], reducer: (acc: U, value: T, index: number) => U | Promise<U>, initialValue: U, signal?: AbortSignal): FuturableTask<U>;
2824
+ /**
2825
+ * Repeatedly executes a task while a condition is true.
2826
+ *
2827
+ * Evaluates the condition before each execution. Stops when the condition
2828
+ * returns false or the task fails. Returns an array of all results.
2829
+ *
2830
+ * The condition can be async.
2831
+ *
2832
+ * @template T - Type of value the task produces
2833
+ * @param condition - Predicate evaluated before each execution (can be async)
2834
+ * @param task - The task to execute repeatedly
2835
+ * @param signal - Optional AbortSignal for the combined task
2836
+ * @returns A FuturableTask that resolves with an array of all results
2837
+ *
2838
+ * @example
2839
+ * ```typescript
2840
+ * let counter = 0;
2841
+ * const incrementTask = FuturableTask.of(() => ++counter);
2842
+ *
2843
+ * const results = await FuturableTask
2844
+ * .whilst(() => counter < 5, incrementTask)
2845
+ * .run(); // [1, 2, 3, 4, 5]
2846
+ * ```
2847
+ *
2848
+ * @example
2849
+ * ```typescript
2850
+ * // Async condition
2851
+ * const results = await FuturableTask.whilst(
2852
+ * async () => await hasMoreData(),
2853
+ * FuturableTask.of(() => fetchNextBatch())
2854
+ * ).run();
2855
+ * ```
2856
+ *
2857
+ * @example
2858
+ * ```typescript
2859
+ * // Paginated fetching
2860
+ * let page = 1;
2861
+ * let hasMore = true;
2862
+ *
2863
+ * const allData = await FuturableTask.whilst(
2864
+ * () => hasMore,
2865
+ * FuturableTask.of(async () => {
2866
+ * const response = await fetchPage(page++);
2867
+ * hasMore = response.hasMore;
2868
+ * return response.data;
2869
+ * })
2870
+ * ).run();
2871
+ * ```
2872
+ */
2873
+ static whilst<T>(condition: () => boolean | Promise<boolean>, task: FuturableTask<T>, signal?: AbortSignal): FuturableTask<T[]>;
2874
+ /**
2875
+ * Repeatedly executes a task until a condition becomes true.
2876
+ *
2877
+ * Opposite of whilst - executes while the condition is false.
2878
+ * Evaluates the condition before each execution.
2879
+ *
2880
+ * The condition can be async.
2881
+ *
2882
+ * @template T - Type of value the task produces
2883
+ * @param condition - Predicate that stops execution when true (can be async)
2884
+ * @param task - The task to execute repeatedly
2885
+ * @param signal - Optional AbortSignal for the combined task
2886
+ * @returns A FuturableTask that resolves with an array of all results
2887
+ *
2888
+ * @example
2889
+ * ```typescript
2890
+ * let counter = 0;
2891
+ * const incrementTask = FuturableTask.of(() => ++counter);
2892
+ *
2893
+ * const results = await FuturableTask
2894
+ * .until(() => counter >= 5, incrementTask)
2895
+ * .run(); // [1, 2, 3, 4, 5]
2896
+ * ```
2897
+ *
2898
+ * @example
2899
+ * ```typescript
2900
+ * // Polling until success
2901
+ * const results = await FuturableTask.until(
2902
+ * async () => await isReady(),
2903
+ * FuturableTask.of(() => checkStatus()).delay(1000)
2904
+ * ).run();
2905
+ * ```
2906
+ *
2907
+ * @example
2908
+ * ```typescript
2909
+ * // Retry until success or limit reached
2910
+ * let attempts = 0;
2911
+ * const MAX_ATTEMPTS = 10;
2912
+ *
2913
+ * await FuturableTask.until(
2914
+ * () => attempts >= MAX_ATTEMPTS,
2915
+ * FuturableTask.of(async () => {
2916
+ * attempts++;
2917
+ * return await tryOperation();
2918
+ * }).orElse(() => FuturableTask.resolve(null))
2919
+ * ).run();
2920
+ * ```
2921
+ */
2922
+ static until<T>(condition: () => boolean | Promise<boolean>, task: FuturableTask<T>, signal?: AbortSignal): FuturableTask<T[]>;
2923
+ /**
2924
+ * Executes a task factory n times, collecting all results.
2925
+ *
2926
+ * The factory receives the current index (0 to n-1) and returns a task.
2927
+ * Executes sequentially. If any execution fails, the entire operation fails.
2928
+ *
2929
+ * @template T - Type of value the tasks produce
2930
+ * @param n - Number of times to execute (must be >= 0)
2931
+ * @param taskFactory - Function that creates a task for each iteration
2932
+ * @param signal - Optional AbortSignal for the combined task
2933
+ * @returns A FuturableTask that resolves with an array of all results
2934
+ *
2935
+ * @example
2936
+ * ```typescript
2937
+ * const results = await FuturableTask
2938
+ * .times(5, i => FuturableTask.resolve(i * 2))
2939
+ * .run(); // [0, 2, 4, 6, 8]
2940
+ * ```
2941
+ *
2942
+ * @example
2943
+ * ```typescript
2944
+ * // Fetch multiple pages
2945
+ * const pages = await FuturableTask.times(
2946
+ * 10,
2947
+ * i => FuturableTask.of(() => fetch(`/api/data?page=${i}`))
2948
+ * ).run();
2949
+ * ```
2950
+ *
2951
+ * @example
2952
+ * ```typescript
2953
+ * // Create test data
2954
+ * const users = await FuturableTask.times(
2955
+ * 100,
2956
+ * i => FuturableTask.of(() => createUser({
2957
+ * name: `User ${i}`,
2958
+ * email: `user${i}@example.com`
2959
+ * }))
2960
+ * ).run();
2961
+ * ```
2962
+ */
2963
+ static times<T>(n: number, taskFactory: (index: number) => FuturableTask<T>, signal?: AbortSignal): FuturableTask<T[]>;
2964
+ /**
2965
+ * Maps an array of values to tasks and executes them in sequence.
2966
+ *
2967
+ * Combines map and sequence - applies a function to each value to create a task,
2968
+ * then executes all tasks sequentially.
2969
+ *
2970
+ * The mapping function receives both the value and its index.
2971
+ *
2972
+ * @template T - Type of input values
2973
+ * @template U - Type of values the tasks produce
2974
+ * @param values - Array of values to map
2975
+ * @param fn - Function that maps each value to a task
2976
+ * @param signal - Optional AbortSignal for the combined task
2977
+ * @returns A FuturableTask that resolves with an array of all results
2978
+ *
2979
+ * @example
2980
+ * ```typescript
2981
+ * const userIds = [1, 2, 3, 4, 5];
2982
+ * const users = await FuturableTask
2983
+ * .traverse(userIds, id => FuturableTask.of(() => fetchUser(id)))
2984
+ * .run();
2985
+ * ```
2986
+ *
2987
+ * @example
2988
+ * ```typescript
2989
+ * // With index
2990
+ * const results = await FuturableTask.traverse(
2991
+ * ['a', 'b', 'c'],
2992
+ * (letter, index) => FuturableTask.of(() => `${index}: ${letter}`)
2993
+ * ).run(); // ['0: a', '1: b', '2: c']
2994
+ * ```
2995
+ *
2996
+ * @example
2997
+ * ```typescript
2998
+ * // Processing files sequentially
2999
+ * const processed = await FuturableTask.traverse(
3000
+ * filePaths,
3001
+ * path => FuturableTask.of(() => processFile(path))
3002
+ * ).run();
3003
+ * ```
3004
+ */
3005
+ static traverse<T, U>(values: T[], fn: (value: T, index: number) => FuturableTask<U>, signal?: AbortSignal): FuturableTask<U[]>;
3006
+ /**
3007
+ * Static method to create a fetch task directly.
3008
+ *
3009
+ * @param url - The URL to fetch
3010
+ * @param opts - Optional Fetch API options
3011
+ * @param signal - Optional AbortSignal for the task
3012
+ * @returns A FuturableTask that resolves with the Response
3013
+ *
3014
+ * @example
3015
+ * ```typescript
3016
+ * const task = FuturableTask.fetch('https://api.example.com/data')
3017
+ * .map(res => res.json())
3018
+ * .retry(3);
3019
+ *
3020
+ * const data = await task.run();
3021
+ * ```
3022
+ */
3023
+ static fetch(url: string, opts?: RequestInit, signal?: AbortSignal): FuturableTask<Response>;
252
3024
  }
253
- export {};
254
- //# sourceMappingURL=index.d.ts.map
3025
+
3026
+ export { Futurable, FuturableTask };
3027
+ export type { FuturableExecutor, FuturableIterable, FuturableLike, FuturablePollingController, FuturableReject, FuturableResolve, FuturableTaskLimiter, FuturableUtils, FuturableWithResolvers, LimiterEvents, MemoizeOptions, SafeResult };