@tanstack/pacer 0.16.3 → 0.16.4

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.
Files changed (169) hide show
  1. package/README.md +165 -0
  2. package/dist/async-batcher.cjs +355 -0
  3. package/dist/async-batcher.cjs.map +1 -0
  4. package/dist/async-batcher.d.cts +344 -0
  5. package/dist/async-batcher.d.ts +344 -0
  6. package/dist/async-batcher.js +353 -0
  7. package/dist/async-batcher.js.map +1 -0
  8. package/dist/async-debouncer.cjs +340 -0
  9. package/dist/async-debouncer.cjs.map +1 -0
  10. package/dist/async-debouncer.d.cts +300 -0
  11. package/dist/async-debouncer.d.ts +300 -0
  12. package/dist/async-debouncer.js +338 -0
  13. package/dist/async-debouncer.js.map +1 -0
  14. package/dist/async-queuer.cjs +496 -0
  15. package/dist/async-queuer.cjs.map +1 -0
  16. package/dist/async-queuer.d.cts +440 -0
  17. package/dist/async-queuer.d.ts +440 -0
  18. package/dist/async-queuer.js +494 -0
  19. package/dist/async-queuer.js.map +1 -0
  20. package/dist/async-rate-limiter.cjs +381 -0
  21. package/dist/async-rate-limiter.cjs.map +1 -0
  22. package/dist/{cjs/async-rate-limiter.d.cts → async-rate-limiter.d.cts} +190 -185
  23. package/dist/{esm/async-rate-limiter.d.ts → async-rate-limiter.d.ts} +190 -185
  24. package/dist/async-rate-limiter.js +379 -0
  25. package/dist/async-rate-limiter.js.map +1 -0
  26. package/dist/async-retryer.cjs +378 -0
  27. package/dist/async-retryer.cjs.map +1 -0
  28. package/dist/async-retryer.d.cts +312 -0
  29. package/dist/async-retryer.d.ts +312 -0
  30. package/dist/async-retryer.js +376 -0
  31. package/dist/async-retryer.js.map +1 -0
  32. package/dist/async-throttler.cjs +362 -0
  33. package/dist/async-throttler.cjs.map +1 -0
  34. package/dist/async-throttler.d.cts +320 -0
  35. package/dist/async-throttler.d.ts +320 -0
  36. package/dist/async-throttler.js +360 -0
  37. package/dist/async-throttler.js.map +1 -0
  38. package/dist/batcher.cjs +194 -0
  39. package/dist/batcher.cjs.map +1 -0
  40. package/dist/batcher.d.cts +180 -0
  41. package/dist/batcher.d.ts +180 -0
  42. package/dist/batcher.js +193 -0
  43. package/dist/batcher.js.map +1 -0
  44. package/dist/debouncer.cjs +197 -0
  45. package/dist/debouncer.cjs.map +1 -0
  46. package/dist/debouncer.d.cts +167 -0
  47. package/dist/debouncer.d.ts +167 -0
  48. package/dist/debouncer.js +195 -0
  49. package/dist/debouncer.js.map +1 -0
  50. package/dist/event-client.cjs +20 -0
  51. package/dist/event-client.cjs.map +1 -0
  52. package/dist/event-client.d.cts +49 -0
  53. package/dist/event-client.d.ts +49 -0
  54. package/dist/event-client.js +19 -0
  55. package/dist/event-client.js.map +1 -0
  56. package/dist/index.cjs +49 -0
  57. package/dist/index.d.cts +15 -0
  58. package/dist/index.d.ts +15 -0
  59. package/dist/{esm/index.js → index.js} +5 -41
  60. package/dist/queuer.cjs +393 -0
  61. package/dist/queuer.cjs.map +1 -0
  62. package/dist/queuer.d.cts +345 -0
  63. package/dist/queuer.d.ts +345 -0
  64. package/dist/queuer.js +391 -0
  65. package/dist/queuer.js.map +1 -0
  66. package/dist/rate-limiter.cjs +251 -0
  67. package/dist/rate-limiter.cjs.map +1 -0
  68. package/dist/{cjs/rate-limiter.d.cts → rate-limiter.d.cts} +113 -108
  69. package/dist/{esm/rate-limiter.d.ts → rate-limiter.d.ts} +113 -108
  70. package/dist/rate-limiter.js +249 -0
  71. package/dist/rate-limiter.js.map +1 -0
  72. package/dist/throttler.cjs +209 -0
  73. package/dist/throttler.cjs.map +1 -0
  74. package/dist/throttler.d.cts +207 -0
  75. package/dist/throttler.d.ts +207 -0
  76. package/dist/throttler.js +207 -0
  77. package/dist/throttler.js.map +1 -0
  78. package/dist/types.cjs +0 -0
  79. package/dist/types.d.cts +13 -0
  80. package/dist/types.d.ts +13 -0
  81. package/dist/types.js +1 -0
  82. package/dist/utils.cjs +13 -0
  83. package/dist/utils.cjs.map +1 -0
  84. package/dist/utils.d.cts +8 -0
  85. package/dist/utils.d.ts +8 -0
  86. package/dist/utils.js +11 -0
  87. package/dist/utils.js.map +1 -0
  88. package/package.json +38 -122
  89. package/dist/cjs/async-batcher.cjs +0 -222
  90. package/dist/cjs/async-batcher.cjs.map +0 -1
  91. package/dist/cjs/async-batcher.d.cts +0 -340
  92. package/dist/cjs/async-debouncer.cjs +0 -233
  93. package/dist/cjs/async-debouncer.cjs.map +0 -1
  94. package/dist/cjs/async-debouncer.d.cts +0 -295
  95. package/dist/cjs/async-queuer.cjs +0 -399
  96. package/dist/cjs/async-queuer.cjs.map +0 -1
  97. package/dist/cjs/async-queuer.d.cts +0 -435
  98. package/dist/cjs/async-rate-limiter.cjs +0 -254
  99. package/dist/cjs/async-rate-limiter.cjs.map +0 -1
  100. package/dist/cjs/async-retryer.cjs +0 -287
  101. package/dist/cjs/async-retryer.cjs.map +0 -1
  102. package/dist/cjs/async-retryer.d.cts +0 -308
  103. package/dist/cjs/async-throttler.cjs +0 -263
  104. package/dist/cjs/async-throttler.cjs.map +0 -1
  105. package/dist/cjs/async-throttler.d.cts +0 -315
  106. package/dist/cjs/batcher.cjs +0 -132
  107. package/dist/cjs/batcher.cjs.map +0 -1
  108. package/dist/cjs/batcher.d.cts +0 -176
  109. package/dist/cjs/debouncer.cjs +0 -139
  110. package/dist/cjs/debouncer.cjs.map +0 -1
  111. package/dist/cjs/debouncer.d.cts +0 -162
  112. package/dist/cjs/event-client.cjs +0 -20
  113. package/dist/cjs/event-client.cjs.map +0 -1
  114. package/dist/cjs/event-client.d.cts +0 -45
  115. package/dist/cjs/index.cjs +0 -51
  116. package/dist/cjs/index.cjs.map +0 -1
  117. package/dist/cjs/index.d.cts +0 -15
  118. package/dist/cjs/queuer.cjs +0 -308
  119. package/dist/cjs/queuer.cjs.map +0 -1
  120. package/dist/cjs/queuer.d.cts +0 -340
  121. package/dist/cjs/rate-limiter.cjs +0 -181
  122. package/dist/cjs/rate-limiter.cjs.map +0 -1
  123. package/dist/cjs/throttler.cjs +0 -153
  124. package/dist/cjs/throttler.cjs.map +0 -1
  125. package/dist/cjs/throttler.d.cts +0 -202
  126. package/dist/cjs/types.d.cts +0 -9
  127. package/dist/cjs/utils.cjs +0 -11
  128. package/dist/cjs/utils.cjs.map +0 -1
  129. package/dist/cjs/utils.d.cts +0 -3
  130. package/dist/esm/async-batcher.d.ts +0 -340
  131. package/dist/esm/async-batcher.js +0 -222
  132. package/dist/esm/async-batcher.js.map +0 -1
  133. package/dist/esm/async-debouncer.d.ts +0 -295
  134. package/dist/esm/async-debouncer.js +0 -233
  135. package/dist/esm/async-debouncer.js.map +0 -1
  136. package/dist/esm/async-queuer.d.ts +0 -435
  137. package/dist/esm/async-queuer.js +0 -399
  138. package/dist/esm/async-queuer.js.map +0 -1
  139. package/dist/esm/async-rate-limiter.js +0 -254
  140. package/dist/esm/async-rate-limiter.js.map +0 -1
  141. package/dist/esm/async-retryer.d.ts +0 -308
  142. package/dist/esm/async-retryer.js +0 -287
  143. package/dist/esm/async-retryer.js.map +0 -1
  144. package/dist/esm/async-throttler.d.ts +0 -315
  145. package/dist/esm/async-throttler.js +0 -263
  146. package/dist/esm/async-throttler.js.map +0 -1
  147. package/dist/esm/batcher.d.ts +0 -176
  148. package/dist/esm/batcher.js +0 -132
  149. package/dist/esm/batcher.js.map +0 -1
  150. package/dist/esm/debouncer.d.ts +0 -162
  151. package/dist/esm/debouncer.js +0 -139
  152. package/dist/esm/debouncer.js.map +0 -1
  153. package/dist/esm/event-client.d.ts +0 -45
  154. package/dist/esm/event-client.js +0 -20
  155. package/dist/esm/event-client.js.map +0 -1
  156. package/dist/esm/index.d.ts +0 -15
  157. package/dist/esm/index.js.map +0 -1
  158. package/dist/esm/queuer.d.ts +0 -340
  159. package/dist/esm/queuer.js +0 -308
  160. package/dist/esm/queuer.js.map +0 -1
  161. package/dist/esm/rate-limiter.js +0 -181
  162. package/dist/esm/rate-limiter.js.map +0 -1
  163. package/dist/esm/throttler.d.ts +0 -202
  164. package/dist/esm/throttler.js +0 -153
  165. package/dist/esm/throttler.js.map +0 -1
  166. package/dist/esm/types.d.ts +0 -9
  167. package/dist/esm/utils.d.ts +0 -3
  168. package/dist/esm/utils.js +0 -11
  169. package/dist/esm/utils.js.map +0 -1
@@ -0,0 +1,312 @@
1
+ import { AnyAsyncFunction } from "./types.cjs";
2
+ import { Store } from "@tanstack/store";
3
+
4
+ //#region src/async-retryer.d.ts
5
+ interface AsyncRetryerState<TFn extends AnyAsyncFunction> {
6
+ /**
7
+ * The current retry attempt number (0 when not executing)
8
+ */
9
+ currentAttempt: number;
10
+ /**
11
+ * Total number of completed executions (successful or failed)
12
+ */
13
+ executionCount: number;
14
+ /**
15
+ * Whether the retryer is currently executing the function
16
+ */
17
+ isExecuting: boolean;
18
+ /**
19
+ * The most recent error encountered during execution
20
+ */
21
+ lastError: Error | undefined;
22
+ /**
23
+ * Timestamp of the last execution completion in milliseconds
24
+ */
25
+ lastExecutionTime: number;
26
+ /**
27
+ * The result from the most recent successful execution
28
+ */
29
+ lastResult: Awaited<ReturnType<TFn>> | undefined;
30
+ /**
31
+ * Current execution status - 'disabled' when not enabled, 'idle' when ready, 'executing' when running
32
+ */
33
+ status: 'disabled' | 'idle' | 'executing' | 'retrying';
34
+ /**
35
+ * Total time spent executing (including retries) in milliseconds
36
+ */
37
+ totalExecutionTime: number;
38
+ }
39
+ interface AsyncRetryerOptions<TFn extends AnyAsyncFunction> {
40
+ /**
41
+ * The backoff strategy for retry delays:
42
+ * - 'exponential': Wait time doubles with each attempt (1s, 2s, 4s, ...)
43
+ * - 'linear': Wait time increases linearly (1s, 2s, 3s, ...)
44
+ * - 'fixed': Same wait time for all attempts
45
+ * @default 'exponential'
46
+ */
47
+ backoff?: 'linear' | 'exponential' | 'fixed';
48
+ /**
49
+ * Base wait time in milliseconds between retries, or a function that returns the wait time
50
+ * @default 1000
51
+ */
52
+ baseWait?: number | ((retryer: AsyncRetryer<TFn>) => number);
53
+ /**
54
+ * Whether the retryer is enabled, or a function that determines if it's enabled
55
+ * @default true
56
+ */
57
+ enabled?: boolean | ((retryer: AsyncRetryer<TFn>) => boolean);
58
+ /**
59
+ * Initial state to merge with the default state
60
+ */
61
+ initialState?: Partial<AsyncRetryerState<TFn>>;
62
+ /**
63
+ * Jitter percentage to add to retry delays (0-1). Adds randomness to prevent thundering herd.
64
+ * @default 0
65
+ */
66
+ jitter?: number;
67
+ /**
68
+ * Optional key to identify this async retryer instance.
69
+ * If provided, the async retryer will be identified by this key in the devtools and PacerProvider if applicable.
70
+ */
71
+ key?: string;
72
+ /**
73
+ * Maximum number of retry attempts, or a function that returns the max attempts
74
+ * @default 3
75
+ */
76
+ maxAttempts?: number | ((retryer: AsyncRetryer<TFn>) => number);
77
+ /**
78
+ * Maximum execution time in milliseconds for a single function call before aborting
79
+ * @default Infinity
80
+ */
81
+ maxExecutionTime?: number;
82
+ /**
83
+ * Maximum total execution time in milliseconds for the entire retry operation before aborting
84
+ * @default Infinity
85
+ */
86
+ maxTotalExecutionTime?: number;
87
+ /**
88
+ * Callback invoked when the execution is aborted (manually or due to timeouts)
89
+ */
90
+ onAbort?: (reason: 'manual' | 'execution-timeout' | 'total-timeout' | 'new-execution', retryer: AsyncRetryer<TFn>) => void;
91
+ /**
92
+ * Callback invoked when any error occurs during execution (including retries)
93
+ */
94
+ onError?: (error: Error, args: Parameters<TFn>, retryer: AsyncRetryer<TFn>) => void;
95
+ /**
96
+ * Callback invoked when the final error occurs after all retries are exhausted
97
+ */
98
+ onLastError?: (error: Error, retryer: AsyncRetryer<TFn>) => void;
99
+ /**
100
+ * Callback invoked before each retry attempt
101
+ */
102
+ onRetry?: (attempt: number, error: Error, retryer: AsyncRetryer<TFn>) => void;
103
+ /**
104
+ * Callback invoked after execution completes (success or failure) of each attempt
105
+ */
106
+ onSettled?: (args: Parameters<TFn>, retryer: AsyncRetryer<TFn>) => void;
107
+ /**
108
+ * Callback invoked when execution succeeds
109
+ */
110
+ onSuccess?: (result: Awaited<ReturnType<TFn>>, args: Parameters<TFn>, retryer: AsyncRetryer<TFn>) => void;
111
+ /**
112
+ * Callback invoked when a single execution attempt times out (maxExecutionTime exceeded)
113
+ */
114
+ onExecutionTimeout?: (retryer: AsyncRetryer<TFn>) => void;
115
+ /**
116
+ * Callback invoked when the total execution time times out (maxTotalExecutionTime exceeded)
117
+ */
118
+ onTotalExecutionTimeout?: (retryer: AsyncRetryer<TFn>) => void;
119
+ /**
120
+ * Controls when errors are thrown:
121
+ * - 'last': Only throw the final error after all retries are exhausted
122
+ * - true: Throw every error immediately (disables retrying)
123
+ * - false: Never throw errors, return undefined instead
124
+ * @default 'last'
125
+ */
126
+ throwOnError?: boolean | 'last';
127
+ }
128
+ /**
129
+ * Utility function for sharing common `AsyncRetryerOptions` options between different `AsyncRetryer` instances.
130
+ */
131
+ declare function asyncRetryerOptions<TFn extends AnyAsyncFunction = AnyAsyncFunction, TOptions extends Partial<AsyncRetryerOptions<TFn>> = Partial<AsyncRetryerOptions<TFn>>>(options: TOptions): TOptions;
132
+ declare const defaultOptions: Omit<Required<AsyncRetryerOptions<any>>, 'initialState' | 'key' | 'onAbort' | 'onError' | 'onLastError' | 'onRetry' | 'onSettled' | 'onSuccess' | 'onExecutionTimeout' | 'onTotalExecutionTimeout'>;
133
+ /**
134
+ * Provides robust retry functionality for asynchronous functions, supporting configurable backoff strategies,
135
+ * attempt limits, timeout controls, and detailed state management. The AsyncRetryer class is designed to help you reliably
136
+ * execute async operations that may fail intermittently, such as network requests or database operations,
137
+ * by automatically retrying them according to your chosen policy.
138
+ *
139
+ * ## Retrying Concepts
140
+ *
141
+ * - **Retrying**: Automatically re-executes a failed async function up to a specified number of attempts.
142
+ * Useful for handling transient errors (e.g., network flakiness, rate limits, temporary server issues).
143
+ * - **Backoff Strategies**: Controls the delay between retry attempts (default: `'exponential'`):
144
+ * - `'exponential'`: Wait time doubles with each attempt (1s, 2s, 4s, ...) - **DEFAULT**
145
+ * - `'linear'`: Wait time increases linearly (1s, 2s, 3s, ...)
146
+ * - `'fixed'`: Waits a constant amount of time (`baseWait`) between each attempt
147
+ * - **Jitter**: Adds randomness to retry delays to prevent thundering herd problems (default: `0`).
148
+ * Set to a value between 0-1 to apply that percentage of random variation to each delay.
149
+ * - **Timeout Controls**: Set limits on execution time to prevent hanging operations:
150
+ * - `maxExecutionTime`: Maximum time for a single function call (default: `Infinity`)
151
+ * - `maxTotalExecutionTime`: Maximum time for the entire retry operation (default: `Infinity`)
152
+ * - **Abort & Cancellation**: Supports cancellation via an internal `AbortController`. Call `abort()` to stop retries.
153
+ * Use `getAbortSignal()` to make your async function actually cancellable (e.g., with fetch requests).
154
+ *
155
+ * ## State Management
156
+ *
157
+ * Uses TanStack Store for fine-grained reactivity. State can be accessed via the `store.state` property.
158
+ *
159
+ * Available state properties:
160
+ * - `currentAttempt`: The current retry attempt number (0 when not executing)
161
+ * - `executionCount`: Total number of completed executions (successful or failed)
162
+ * - `isExecuting`: Whether the retryer is currently executing the function
163
+ * - `lastError`: The most recent error encountered during execution
164
+ * - `lastExecutionTime`: Timestamp of the last execution completion in milliseconds
165
+ * - `lastResult`: The result from the most recent successful execution
166
+ * - `status`: Current execution status ('disabled' | 'idle' | 'executing' | 'retrying')
167
+ * - `totalExecutionTime`: Total time spent executing (including retries) in milliseconds
168
+ *
169
+ * ## Error Handling
170
+ *
171
+ * The `throwOnError` option controls when errors are thrown (default: `'last'`):
172
+ * - `'last'`: Only throws the final error after all retries are exhausted - **DEFAULT**
173
+ * - `true`: Throws every error immediately (disables retrying)
174
+ * - `false`: Never throws errors, returns `undefined` instead
175
+ *
176
+ * Callbacks for lifecycle management:
177
+ * - `onAbort`: Called when execution is aborted (manually or due to timeouts)
178
+ * - `onError`: Called for every error (including during retries)
179
+ * - `onLastError`: Called only for the final error after all retries fail
180
+ * - `onRetry`: Called before each retry attempt
181
+ * - `onSettled`: Called after execution completes (success or failure) of each attempt
182
+ * - `onSuccess`: Called when execution succeeds
183
+ * - `onExecutionTimeout`: Called when a single execution attempt times out
184
+ * - `onTotalExecutionTimeout`: Called when the total execution time times out
185
+ *
186
+ * ## Usage
187
+ *
188
+ * - Use for async operations that may fail transiently and benefit from retrying.
189
+ * - Configure `maxAttempts`, `backoff`, `baseWait`, and `jitter` to control retry behavior.
190
+ * - Set `maxExecutionTime` and `maxTotalExecutionTime` to prevent hanging operations.
191
+ * - Use `onAbort`, `onError`, `onLastError`, `onRetry`, `onSettled`, `onSuccess`, `onExecutionTimeout`, and `onTotalExecutionTimeout` for custom side effects.
192
+ * - Call `abort()` to cancel ongoing execution and pending retries.
193
+ * - Call `reset()` to reset state and cancel execution.
194
+ * - Use `getAbortSignal()` to make your async function cancellable.
195
+ * - Use dynamic options (functions) for `maxAttempts`, `baseWait`, and `enabled` based on retryer state.
196
+ *
197
+ * **Important:** This class is designed for single-use execution. Calling `execute()` multiple times
198
+ * on the same instance will abort previous executions. For multiple calls, create a new instance
199
+ * each time.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * // Retry a fetch operation up to 5 times with exponential backoff, jitter, and timeouts
204
+ * const retryer = new AsyncRetryer(async (url: string) => {
205
+ * const signal = retryer.getAbortSignal()
206
+ * return await fetch(url, { signal })
207
+ * }, {
208
+ * maxAttempts: 5,
209
+ * backoff: 'exponential',
210
+ * baseWait: 1000,
211
+ * jitter: 0.1, // Add 10% random variation to prevent thundering herd
212
+ * maxExecutionTime: 5000, // Abort individual calls after 5 seconds
213
+ * maxTotalExecutionTime: 30000, // Abort entire operation after 30 seconds
214
+ * onRetry: (attempt, error) => console.log(`Retry attempt ${attempt} after error:`, error),
215
+ * onSuccess: (result) => console.log('Success:', result),
216
+ * onError: (error) => console.error('Error:', error),
217
+ * onLastError: (error) => console.error('All retries failed:', error),
218
+ * })
219
+ *
220
+ * const result = await retryer.execute('/api/data')
221
+ * ```
222
+ *
223
+ * @template TFn The async function type to be retried.
224
+ */
225
+ declare class AsyncRetryer<TFn extends AnyAsyncFunction> {
226
+ #private;
227
+ fn: TFn;
228
+ readonly store: Store<Readonly<AsyncRetryerState<TFn>>>;
229
+ key: string | undefined;
230
+ options: AsyncRetryerOptions<TFn> & typeof defaultOptions;
231
+ /**
232
+ * Creates a new AsyncRetryer instance
233
+ * @param fn The async function to retry
234
+ * @param initialOptions Configuration options for the retryer
235
+ */
236
+ constructor(fn: TFn, initialOptions?: AsyncRetryerOptions<TFn>);
237
+ /**
238
+ * Updates the retryer options
239
+ * @param newOptions Partial options to merge with existing options
240
+ */
241
+ setOptions: (newOptions: Partial<AsyncRetryerOptions<TFn>>) => void;
242
+ /**
243
+ * Executes the function with retry logic
244
+ * @param args Arguments to pass to the function
245
+ * @returns The function result, or undefined if disabled or all retries failed (when throwOnError is false)
246
+ * @throws The last error if throwOnError is true and all retries fail
247
+ */
248
+ execute: (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
249
+ /**
250
+ * Returns the current AbortSignal for the executing operation.
251
+ * Use this signal in your async function to make it cancellable.
252
+ * Returns null when not currently executing.
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * const retryer = new AsyncRetryer(async (userId: string) => {
257
+ * const signal = retryer.getAbortSignal()
258
+ * if (signal) {
259
+ * return fetch(`/api/users/${userId}`, { signal })
260
+ * }
261
+ * return fetch(`/api/users/${userId}`)
262
+ * })
263
+ *
264
+ * // Abort will now actually cancel the fetch
265
+ * retryer.abort()
266
+ * ```
267
+ */
268
+ getAbortSignal(): AbortSignal | null;
269
+ /**
270
+ * Cancels the current execution and any pending retries
271
+ * @param reason The reason for the abort (defaults to 'manual')
272
+ */
273
+ abort: (reason?: "manual" | "execution-timeout" | "total-timeout" | "new-execution") => void;
274
+ /**
275
+ * Resets the retryer to its initial state
276
+ */
277
+ reset: () => void;
278
+ }
279
+ /**
280
+ * Creates a retry-enabled version of an async function. This is a convenience wrapper
281
+ * around the AsyncRetryer class that returns the execute method.
282
+ *
283
+ * @param fn The async function to add retry functionality to
284
+ * @param initialOptions Configuration options for the retry behavior
285
+ * @returns A new function that executes the original with retry logic
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * // Define your async function normally
290
+ * async function fetchData(url: string) {
291
+ * const response = await fetch(url)
292
+ * if (!response.ok) throw new Error('Request failed')
293
+ * return response.json()
294
+ * }
295
+ *
296
+ * // Create retry-enabled function
297
+ * const fetchWithRetry = asyncRetry(fetchData, {
298
+ * maxAttempts: 3,
299
+ * backoff: 'exponential',
300
+ * baseWait: 1000,
301
+ * jitter: 0.1
302
+ * })
303
+ *
304
+ * // Call it multiple times
305
+ * const data1 = await fetchWithRetry('/api/data1')
306
+ * const data2 = await fetchWithRetry('/api/data2')
307
+ * ```
308
+ */
309
+ declare function asyncRetry<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions?: AsyncRetryerOptions<TFn>): (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
310
+ //#endregion
311
+ export { AsyncRetryer, AsyncRetryerOptions, AsyncRetryerState, asyncRetry, asyncRetryerOptions };
312
+ //# sourceMappingURL=async-retryer.d.cts.map
@@ -0,0 +1,312 @@
1
+ import { AnyAsyncFunction } from "./types.js";
2
+ import { Store } from "@tanstack/store";
3
+
4
+ //#region src/async-retryer.d.ts
5
+ interface AsyncRetryerState<TFn extends AnyAsyncFunction> {
6
+ /**
7
+ * The current retry attempt number (0 when not executing)
8
+ */
9
+ currentAttempt: number;
10
+ /**
11
+ * Total number of completed executions (successful or failed)
12
+ */
13
+ executionCount: number;
14
+ /**
15
+ * Whether the retryer is currently executing the function
16
+ */
17
+ isExecuting: boolean;
18
+ /**
19
+ * The most recent error encountered during execution
20
+ */
21
+ lastError: Error | undefined;
22
+ /**
23
+ * Timestamp of the last execution completion in milliseconds
24
+ */
25
+ lastExecutionTime: number;
26
+ /**
27
+ * The result from the most recent successful execution
28
+ */
29
+ lastResult: Awaited<ReturnType<TFn>> | undefined;
30
+ /**
31
+ * Current execution status - 'disabled' when not enabled, 'idle' when ready, 'executing' when running
32
+ */
33
+ status: 'disabled' | 'idle' | 'executing' | 'retrying';
34
+ /**
35
+ * Total time spent executing (including retries) in milliseconds
36
+ */
37
+ totalExecutionTime: number;
38
+ }
39
+ interface AsyncRetryerOptions<TFn extends AnyAsyncFunction> {
40
+ /**
41
+ * The backoff strategy for retry delays:
42
+ * - 'exponential': Wait time doubles with each attempt (1s, 2s, 4s, ...)
43
+ * - 'linear': Wait time increases linearly (1s, 2s, 3s, ...)
44
+ * - 'fixed': Same wait time for all attempts
45
+ * @default 'exponential'
46
+ */
47
+ backoff?: 'linear' | 'exponential' | 'fixed';
48
+ /**
49
+ * Base wait time in milliseconds between retries, or a function that returns the wait time
50
+ * @default 1000
51
+ */
52
+ baseWait?: number | ((retryer: AsyncRetryer<TFn>) => number);
53
+ /**
54
+ * Whether the retryer is enabled, or a function that determines if it's enabled
55
+ * @default true
56
+ */
57
+ enabled?: boolean | ((retryer: AsyncRetryer<TFn>) => boolean);
58
+ /**
59
+ * Initial state to merge with the default state
60
+ */
61
+ initialState?: Partial<AsyncRetryerState<TFn>>;
62
+ /**
63
+ * Jitter percentage to add to retry delays (0-1). Adds randomness to prevent thundering herd.
64
+ * @default 0
65
+ */
66
+ jitter?: number;
67
+ /**
68
+ * Optional key to identify this async retryer instance.
69
+ * If provided, the async retryer will be identified by this key in the devtools and PacerProvider if applicable.
70
+ */
71
+ key?: string;
72
+ /**
73
+ * Maximum number of retry attempts, or a function that returns the max attempts
74
+ * @default 3
75
+ */
76
+ maxAttempts?: number | ((retryer: AsyncRetryer<TFn>) => number);
77
+ /**
78
+ * Maximum execution time in milliseconds for a single function call before aborting
79
+ * @default Infinity
80
+ */
81
+ maxExecutionTime?: number;
82
+ /**
83
+ * Maximum total execution time in milliseconds for the entire retry operation before aborting
84
+ * @default Infinity
85
+ */
86
+ maxTotalExecutionTime?: number;
87
+ /**
88
+ * Callback invoked when the execution is aborted (manually or due to timeouts)
89
+ */
90
+ onAbort?: (reason: 'manual' | 'execution-timeout' | 'total-timeout' | 'new-execution', retryer: AsyncRetryer<TFn>) => void;
91
+ /**
92
+ * Callback invoked when any error occurs during execution (including retries)
93
+ */
94
+ onError?: (error: Error, args: Parameters<TFn>, retryer: AsyncRetryer<TFn>) => void;
95
+ /**
96
+ * Callback invoked when the final error occurs after all retries are exhausted
97
+ */
98
+ onLastError?: (error: Error, retryer: AsyncRetryer<TFn>) => void;
99
+ /**
100
+ * Callback invoked before each retry attempt
101
+ */
102
+ onRetry?: (attempt: number, error: Error, retryer: AsyncRetryer<TFn>) => void;
103
+ /**
104
+ * Callback invoked after execution completes (success or failure) of each attempt
105
+ */
106
+ onSettled?: (args: Parameters<TFn>, retryer: AsyncRetryer<TFn>) => void;
107
+ /**
108
+ * Callback invoked when execution succeeds
109
+ */
110
+ onSuccess?: (result: Awaited<ReturnType<TFn>>, args: Parameters<TFn>, retryer: AsyncRetryer<TFn>) => void;
111
+ /**
112
+ * Callback invoked when a single execution attempt times out (maxExecutionTime exceeded)
113
+ */
114
+ onExecutionTimeout?: (retryer: AsyncRetryer<TFn>) => void;
115
+ /**
116
+ * Callback invoked when the total execution time times out (maxTotalExecutionTime exceeded)
117
+ */
118
+ onTotalExecutionTimeout?: (retryer: AsyncRetryer<TFn>) => void;
119
+ /**
120
+ * Controls when errors are thrown:
121
+ * - 'last': Only throw the final error after all retries are exhausted
122
+ * - true: Throw every error immediately (disables retrying)
123
+ * - false: Never throw errors, return undefined instead
124
+ * @default 'last'
125
+ */
126
+ throwOnError?: boolean | 'last';
127
+ }
128
+ /**
129
+ * Utility function for sharing common `AsyncRetryerOptions` options between different `AsyncRetryer` instances.
130
+ */
131
+ declare function asyncRetryerOptions<TFn extends AnyAsyncFunction = AnyAsyncFunction, TOptions extends Partial<AsyncRetryerOptions<TFn>> = Partial<AsyncRetryerOptions<TFn>>>(options: TOptions): TOptions;
132
+ declare const defaultOptions: Omit<Required<AsyncRetryerOptions<any>>, 'initialState' | 'key' | 'onAbort' | 'onError' | 'onLastError' | 'onRetry' | 'onSettled' | 'onSuccess' | 'onExecutionTimeout' | 'onTotalExecutionTimeout'>;
133
+ /**
134
+ * Provides robust retry functionality for asynchronous functions, supporting configurable backoff strategies,
135
+ * attempt limits, timeout controls, and detailed state management. The AsyncRetryer class is designed to help you reliably
136
+ * execute async operations that may fail intermittently, such as network requests or database operations,
137
+ * by automatically retrying them according to your chosen policy.
138
+ *
139
+ * ## Retrying Concepts
140
+ *
141
+ * - **Retrying**: Automatically re-executes a failed async function up to a specified number of attempts.
142
+ * Useful for handling transient errors (e.g., network flakiness, rate limits, temporary server issues).
143
+ * - **Backoff Strategies**: Controls the delay between retry attempts (default: `'exponential'`):
144
+ * - `'exponential'`: Wait time doubles with each attempt (1s, 2s, 4s, ...) - **DEFAULT**
145
+ * - `'linear'`: Wait time increases linearly (1s, 2s, 3s, ...)
146
+ * - `'fixed'`: Waits a constant amount of time (`baseWait`) between each attempt
147
+ * - **Jitter**: Adds randomness to retry delays to prevent thundering herd problems (default: `0`).
148
+ * Set to a value between 0-1 to apply that percentage of random variation to each delay.
149
+ * - **Timeout Controls**: Set limits on execution time to prevent hanging operations:
150
+ * - `maxExecutionTime`: Maximum time for a single function call (default: `Infinity`)
151
+ * - `maxTotalExecutionTime`: Maximum time for the entire retry operation (default: `Infinity`)
152
+ * - **Abort & Cancellation**: Supports cancellation via an internal `AbortController`. Call `abort()` to stop retries.
153
+ * Use `getAbortSignal()` to make your async function actually cancellable (e.g., with fetch requests).
154
+ *
155
+ * ## State Management
156
+ *
157
+ * Uses TanStack Store for fine-grained reactivity. State can be accessed via the `store.state` property.
158
+ *
159
+ * Available state properties:
160
+ * - `currentAttempt`: The current retry attempt number (0 when not executing)
161
+ * - `executionCount`: Total number of completed executions (successful or failed)
162
+ * - `isExecuting`: Whether the retryer is currently executing the function
163
+ * - `lastError`: The most recent error encountered during execution
164
+ * - `lastExecutionTime`: Timestamp of the last execution completion in milliseconds
165
+ * - `lastResult`: The result from the most recent successful execution
166
+ * - `status`: Current execution status ('disabled' | 'idle' | 'executing' | 'retrying')
167
+ * - `totalExecutionTime`: Total time spent executing (including retries) in milliseconds
168
+ *
169
+ * ## Error Handling
170
+ *
171
+ * The `throwOnError` option controls when errors are thrown (default: `'last'`):
172
+ * - `'last'`: Only throws the final error after all retries are exhausted - **DEFAULT**
173
+ * - `true`: Throws every error immediately (disables retrying)
174
+ * - `false`: Never throws errors, returns `undefined` instead
175
+ *
176
+ * Callbacks for lifecycle management:
177
+ * - `onAbort`: Called when execution is aborted (manually or due to timeouts)
178
+ * - `onError`: Called for every error (including during retries)
179
+ * - `onLastError`: Called only for the final error after all retries fail
180
+ * - `onRetry`: Called before each retry attempt
181
+ * - `onSettled`: Called after execution completes (success or failure) of each attempt
182
+ * - `onSuccess`: Called when execution succeeds
183
+ * - `onExecutionTimeout`: Called when a single execution attempt times out
184
+ * - `onTotalExecutionTimeout`: Called when the total execution time times out
185
+ *
186
+ * ## Usage
187
+ *
188
+ * - Use for async operations that may fail transiently and benefit from retrying.
189
+ * - Configure `maxAttempts`, `backoff`, `baseWait`, and `jitter` to control retry behavior.
190
+ * - Set `maxExecutionTime` and `maxTotalExecutionTime` to prevent hanging operations.
191
+ * - Use `onAbort`, `onError`, `onLastError`, `onRetry`, `onSettled`, `onSuccess`, `onExecutionTimeout`, and `onTotalExecutionTimeout` for custom side effects.
192
+ * - Call `abort()` to cancel ongoing execution and pending retries.
193
+ * - Call `reset()` to reset state and cancel execution.
194
+ * - Use `getAbortSignal()` to make your async function cancellable.
195
+ * - Use dynamic options (functions) for `maxAttempts`, `baseWait`, and `enabled` based on retryer state.
196
+ *
197
+ * **Important:** This class is designed for single-use execution. Calling `execute()` multiple times
198
+ * on the same instance will abort previous executions. For multiple calls, create a new instance
199
+ * each time.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * // Retry a fetch operation up to 5 times with exponential backoff, jitter, and timeouts
204
+ * const retryer = new AsyncRetryer(async (url: string) => {
205
+ * const signal = retryer.getAbortSignal()
206
+ * return await fetch(url, { signal })
207
+ * }, {
208
+ * maxAttempts: 5,
209
+ * backoff: 'exponential',
210
+ * baseWait: 1000,
211
+ * jitter: 0.1, // Add 10% random variation to prevent thundering herd
212
+ * maxExecutionTime: 5000, // Abort individual calls after 5 seconds
213
+ * maxTotalExecutionTime: 30000, // Abort entire operation after 30 seconds
214
+ * onRetry: (attempt, error) => console.log(`Retry attempt ${attempt} after error:`, error),
215
+ * onSuccess: (result) => console.log('Success:', result),
216
+ * onError: (error) => console.error('Error:', error),
217
+ * onLastError: (error) => console.error('All retries failed:', error),
218
+ * })
219
+ *
220
+ * const result = await retryer.execute('/api/data')
221
+ * ```
222
+ *
223
+ * @template TFn The async function type to be retried.
224
+ */
225
+ declare class AsyncRetryer<TFn extends AnyAsyncFunction> {
226
+ #private;
227
+ fn: TFn;
228
+ readonly store: Store<Readonly<AsyncRetryerState<TFn>>>;
229
+ key: string | undefined;
230
+ options: AsyncRetryerOptions<TFn> & typeof defaultOptions;
231
+ /**
232
+ * Creates a new AsyncRetryer instance
233
+ * @param fn The async function to retry
234
+ * @param initialOptions Configuration options for the retryer
235
+ */
236
+ constructor(fn: TFn, initialOptions?: AsyncRetryerOptions<TFn>);
237
+ /**
238
+ * Updates the retryer options
239
+ * @param newOptions Partial options to merge with existing options
240
+ */
241
+ setOptions: (newOptions: Partial<AsyncRetryerOptions<TFn>>) => void;
242
+ /**
243
+ * Executes the function with retry logic
244
+ * @param args Arguments to pass to the function
245
+ * @returns The function result, or undefined if disabled or all retries failed (when throwOnError is false)
246
+ * @throws The last error if throwOnError is true and all retries fail
247
+ */
248
+ execute: (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
249
+ /**
250
+ * Returns the current AbortSignal for the executing operation.
251
+ * Use this signal in your async function to make it cancellable.
252
+ * Returns null when not currently executing.
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * const retryer = new AsyncRetryer(async (userId: string) => {
257
+ * const signal = retryer.getAbortSignal()
258
+ * if (signal) {
259
+ * return fetch(`/api/users/${userId}`, { signal })
260
+ * }
261
+ * return fetch(`/api/users/${userId}`)
262
+ * })
263
+ *
264
+ * // Abort will now actually cancel the fetch
265
+ * retryer.abort()
266
+ * ```
267
+ */
268
+ getAbortSignal(): AbortSignal | null;
269
+ /**
270
+ * Cancels the current execution and any pending retries
271
+ * @param reason The reason for the abort (defaults to 'manual')
272
+ */
273
+ abort: (reason?: "manual" | "execution-timeout" | "total-timeout" | "new-execution") => void;
274
+ /**
275
+ * Resets the retryer to its initial state
276
+ */
277
+ reset: () => void;
278
+ }
279
+ /**
280
+ * Creates a retry-enabled version of an async function. This is a convenience wrapper
281
+ * around the AsyncRetryer class that returns the execute method.
282
+ *
283
+ * @param fn The async function to add retry functionality to
284
+ * @param initialOptions Configuration options for the retry behavior
285
+ * @returns A new function that executes the original with retry logic
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * // Define your async function normally
290
+ * async function fetchData(url: string) {
291
+ * const response = await fetch(url)
292
+ * if (!response.ok) throw new Error('Request failed')
293
+ * return response.json()
294
+ * }
295
+ *
296
+ * // Create retry-enabled function
297
+ * const fetchWithRetry = asyncRetry(fetchData, {
298
+ * maxAttempts: 3,
299
+ * backoff: 'exponential',
300
+ * baseWait: 1000,
301
+ * jitter: 0.1
302
+ * })
303
+ *
304
+ * // Call it multiple times
305
+ * const data1 = await fetchWithRetry('/api/data1')
306
+ * const data2 = await fetchWithRetry('/api/data2')
307
+ * ```
308
+ */
309
+ declare function asyncRetry<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions?: AsyncRetryerOptions<TFn>): (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
310
+ //#endregion
311
+ export { AsyncRetryer, AsyncRetryerOptions, AsyncRetryerState, asyncRetry, asyncRetryerOptions };
312
+ //# sourceMappingURL=async-retryer.d.ts.map