@tanstack/pacer 0.22.0 → 0.23.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.
Files changed (100) hide show
  1. package/README.md +4 -4
  2. package/dist/async-batcher.d.ts +11 -13
  3. package/dist/async-batcher.js +185 -131
  4. package/dist/async-debouncer.d.ts +6 -8
  5. package/dist/async-debouncer.js +186 -142
  6. package/dist/async-queuer.d.ts +11 -13
  7. package/dist/async-queuer.js +383 -290
  8. package/dist/async-rate-limiter.d.ts +6 -8
  9. package/dist/async-rate-limiter.js +197 -143
  10. package/dist/async-retryer.d.ts +6 -8
  11. package/dist/async-retryer.js +209 -179
  12. package/dist/async-throttler.d.ts +6 -8
  13. package/dist/async-throttler.js +214 -157
  14. package/dist/batcher.d.ts +5 -7
  15. package/dist/batcher.js +107 -87
  16. package/dist/debouncer.d.ts +6 -8
  17. package/dist/debouncer.js +99 -86
  18. package/dist/event-client.d.ts +8 -10
  19. package/dist/event-client.js +1 -2
  20. package/dist/queuer.d.ts +7 -9
  21. package/dist/queuer.js +277 -212
  22. package/dist/rate-limiter.d.ts +6 -8
  23. package/dist/rate-limiter.js +127 -109
  24. package/dist/throttler.d.ts +6 -8
  25. package/dist/throttler.js +127 -90
  26. package/dist/types.d.ts +4 -6
  27. package/dist/utils.d.ts +3 -5
  28. package/dist/utils.js +1 -2
  29. package/package.json +21 -68
  30. package/dist/async-batcher.cjs +0 -337
  31. package/dist/async-batcher.cjs.map +0 -1
  32. package/dist/async-batcher.d.cts +0 -343
  33. package/dist/async-batcher.js.map +0 -1
  34. package/dist/async-debouncer.cjs +0 -327
  35. package/dist/async-debouncer.cjs.map +0 -1
  36. package/dist/async-debouncer.d.cts +0 -299
  37. package/dist/async-debouncer.js.map +0 -1
  38. package/dist/async-queuer.cjs +0 -516
  39. package/dist/async-queuer.cjs.map +0 -1
  40. package/dist/async-queuer.d.cts +0 -440
  41. package/dist/async-queuer.js.map +0 -1
  42. package/dist/async-rate-limiter.cjs +0 -370
  43. package/dist/async-rate-limiter.cjs.map +0 -1
  44. package/dist/async-rate-limiter.d.cts +0 -356
  45. package/dist/async-rate-limiter.js.map +0 -1
  46. package/dist/async-retryer.cjs +0 -365
  47. package/dist/async-retryer.cjs.map +0 -1
  48. package/dist/async-retryer.d.cts +0 -321
  49. package/dist/async-retryer.js.map +0 -1
  50. package/dist/async-throttler.cjs +0 -344
  51. package/dist/async-throttler.cjs.map +0 -1
  52. package/dist/async-throttler.d.cts +0 -319
  53. package/dist/async-throttler.js.map +0 -1
  54. package/dist/batcher.cjs +0 -200
  55. package/dist/batcher.cjs.map +0 -1
  56. package/dist/batcher.d.cts +0 -179
  57. package/dist/batcher.js.map +0 -1
  58. package/dist/debouncer.cjs +0 -203
  59. package/dist/debouncer.cjs.map +0 -1
  60. package/dist/debouncer.d.cts +0 -166
  61. package/dist/debouncer.js.map +0 -1
  62. package/dist/event-client.cjs +0 -64
  63. package/dist/event-client.cjs.map +0 -1
  64. package/dist/event-client.d.cts +0 -65
  65. package/dist/event-client.js.map +0 -1
  66. package/dist/index.cjs +0 -52
  67. package/dist/index.d.cts +0 -15
  68. package/dist/queuer.cjs +0 -406
  69. package/dist/queuer.cjs.map +0 -1
  70. package/dist/queuer.d.cts +0 -345
  71. package/dist/queuer.js.map +0 -1
  72. package/dist/rate-limiter.cjs +0 -263
  73. package/dist/rate-limiter.cjs.map +0 -1
  74. package/dist/rate-limiter.d.cts +0 -214
  75. package/dist/rate-limiter.js.map +0 -1
  76. package/dist/throttler.cjs +0 -215
  77. package/dist/throttler.cjs.map +0 -1
  78. package/dist/throttler.d.cts +0 -206
  79. package/dist/throttler.js.map +0 -1
  80. package/dist/types.cjs +0 -0
  81. package/dist/types.d.cts +0 -13
  82. package/dist/utils.cjs +0 -13
  83. package/dist/utils.cjs.map +0 -1
  84. package/dist/utils.d.cts +0 -7
  85. package/dist/utils.js.map +0 -1
  86. package/src/async-batcher.ts +0 -594
  87. package/src/async-debouncer.ts +0 -566
  88. package/src/async-queuer.ts +0 -988
  89. package/src/async-rate-limiter.ts +0 -648
  90. package/src/async-retryer.ts +0 -673
  91. package/src/async-throttler.ts +0 -634
  92. package/src/batcher.ts +0 -329
  93. package/src/debouncer.ts +0 -334
  94. package/src/event-client.ts +0 -129
  95. package/src/index.ts +0 -24
  96. package/src/queuer.ts +0 -751
  97. package/src/rate-limiter.ts +0 -429
  98. package/src/throttler.ts +0 -380
  99. package/src/types.ts +0 -12
  100. package/src/utils.ts +0 -12
@@ -1,327 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_utils = require('./utils.cjs');
3
- const require_async_retryer = require('./async-retryer.cjs');
4
- const require_event_client = require('./event-client.cjs');
5
- let _tanstack_store = require("@tanstack/store");
6
-
7
- //#region src/async-debouncer.ts
8
- function getDefaultAsyncDebouncerState() {
9
- return {
10
- canLeadingExecute: true,
11
- errorCount: 0,
12
- isExecuting: false,
13
- isPending: false,
14
- lastArgs: void 0,
15
- lastResult: void 0,
16
- maybeExecuteCount: 0,
17
- settleCount: 0,
18
- status: "idle",
19
- successCount: 0
20
- };
21
- }
22
- /**
23
- * Utility function for sharing common `AsyncDebouncerOptions` options between different `AsyncDebouncer` instances.
24
- */
25
- function asyncDebouncerOptions(options) {
26
- return options;
27
- }
28
- const defaultOptions = {
29
- asyncRetryerOptions: { maxAttempts: 1 },
30
- enabled: true,
31
- leading: false,
32
- trailing: true,
33
- wait: 0
34
- };
35
- /**
36
- * A class that creates an async debounced function.
37
- *
38
- * Async vs Sync Versions:
39
- * The async version provides advanced features over the sync Debouncer:
40
- * - Returns promises that can be awaited for debounced function results
41
- * - Built-in retry support via AsyncRetryer integration
42
- * - Abort support to cancel in-flight executions
43
- * - Cancel support to prevent pending executions from starting
44
- * - Comprehensive error handling with onError callbacks and throwOnError control
45
- * - Detailed execution tracking (success/error/settle counts)
46
- *
47
- * The sync Debouncer is lighter weight and simpler when you don't need async features,
48
- * return values, or execution control.
49
- *
50
- * What is Debouncing?
51
- * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.
52
- * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing
53
- * or input changes where you only want to execute the handler after the events have stopped occurring.
54
- *
55
- * Unlike throttling which allows execution at regular intervals, debouncing prevents any execution until
56
- * the function stops being called for the specified delay period.
57
- *
58
- * Error Handling:
59
- * - If an `onError` handler is provided, it will be called with the error and debouncer instance
60
- * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown
61
- * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed
62
- * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown
63
- * - The error state can be checked using the underlying store
64
- *
65
- * State Management:
66
- * - The debouncer uses a reactive store for state management
67
- * - Use `initialState` to provide initial state values when creating the async debouncer
68
- * - The state includes canLeadingExecute, error count, execution status, and success/settle counts
69
- * - State can be accessed via the `store` property and its `state` getter
70
- * - The store is reactive and will notify subscribers of state changes
71
- *
72
- * @example
73
- * ```ts
74
- * const asyncDebouncer = new AsyncDebouncer(async (value: string) => {
75
- * const results = await searchAPI(value);
76
- * return results; // Return value is preserved
77
- * }, {
78
- * wait: 500,
79
- * onError: (error) => {
80
- * console.error('Search failed:', error);
81
- * }
82
- * });
83
- *
84
- * // Called on each keystroke but only executes after 500ms of no typing
85
- * // Returns the API response directly
86
- * const results = await asyncDebouncer.maybeExecute(inputElement.value);
87
- * ```
88
- */
89
- var AsyncDebouncer = class {
90
- #timeoutId;
91
- #resolvePreviousPromise;
92
- constructor(fn, initialOptions) {
93
- this.fn = fn;
94
- this.store = new _tanstack_store.Store(getDefaultAsyncDebouncerState());
95
- this.asyncRetryers = /* @__PURE__ */ new Map();
96
- this.#timeoutId = null;
97
- this.#resolvePreviousPromise = null;
98
- this.setOptions = (newOptions) => {
99
- this.options = {
100
- ...this.options,
101
- ...newOptions
102
- };
103
- if (!this.#getEnabled()) this.cancel();
104
- };
105
- this.#setState = (newState) => {
106
- this.store.setState((state) => {
107
- const combinedState = {
108
- ...state,
109
- ...newState
110
- };
111
- const { isPending, isExecuting, settleCount } = combinedState;
112
- return {
113
- ...combinedState,
114
- status: !this.#getEnabled() ? "disabled" : isPending ? "pending" : isExecuting ? "executing" : settleCount > 0 ? "settled" : "idle"
115
- };
116
- });
117
- require_event_client.emitChange("AsyncDebouncer", this);
118
- };
119
- this.#getEnabled = () => {
120
- return !!require_utils.parseFunctionOrValue(this.options.enabled, this);
121
- };
122
- this.#getWait = () => {
123
- return require_utils.parseFunctionOrValue(this.options.wait, this);
124
- };
125
- this.maybeExecute = async (...args) => {
126
- if (!this.#getEnabled()) return void 0;
127
- this.#cancelPendingExecution();
128
- this.#setState({
129
- lastArgs: args,
130
- maybeExecuteCount: this.store.state.maybeExecuteCount + 1
131
- });
132
- if (this.options.leading && this.store.state.canLeadingExecute) {
133
- this.#setState({ canLeadingExecute: false });
134
- await this.#execute(...args);
135
- return this.store.state.lastResult;
136
- }
137
- if (this.options.trailing && this.#getEnabled()) this.#setState({ isPending: true });
138
- return new Promise((resolve, reject) => {
139
- this.#resolvePreviousPromise = resolve;
140
- this.#timeoutId = setTimeout(async () => {
141
- if (this.options.trailing && this.store.state.lastArgs) try {
142
- await this.#execute(...this.store.state.lastArgs);
143
- } catch (error) {
144
- reject(error);
145
- }
146
- this.#setState({ canLeadingExecute: true });
147
- this.#resolvePreviousPromise = null;
148
- resolve(this.store.state.lastResult);
149
- }, this.#getWait());
150
- });
151
- };
152
- this.#execute = async (...args) => {
153
- if (!this.#getEnabled()) return void 0;
154
- const currentMaybeExecuteCount = this.store.state.maybeExecuteCount + 1;
155
- try {
156
- this.#setState({ isExecuting: true });
157
- const currentAsyncRetryer = new require_async_retryer.AsyncRetryer(this.fn, this.options.asyncRetryerOptions);
158
- this.asyncRetryers.set(currentMaybeExecuteCount, currentAsyncRetryer);
159
- const result = await currentAsyncRetryer.execute(...args);
160
- this.#setState({
161
- lastResult: result,
162
- successCount: this.store.state.successCount + 1
163
- });
164
- this.options.onSuccess?.(result, args, this);
165
- } catch (error) {
166
- this.#setState({ errorCount: this.store.state.errorCount + 1 });
167
- this.options.onError?.(error, args, this);
168
- if (this.options.throwOnError) throw error;
169
- } finally {
170
- this.asyncRetryers.delete(currentMaybeExecuteCount);
171
- this.#setState({
172
- isExecuting: false,
173
- isPending: false,
174
- lastArgs: void 0,
175
- settleCount: this.store.state.settleCount + 1
176
- });
177
- this.options.onSettled?.(args, this);
178
- }
179
- return this.store.state.lastResult;
180
- };
181
- this.flush = async () => {
182
- if (this.store.state.isPending && this.store.state.lastArgs) {
183
- const { lastArgs } = this.store.state;
184
- this.#cancelPendingExecution();
185
- return await this.#execute(...lastArgs);
186
- }
187
- };
188
- this.#resolvePreviousPromiseInternal = () => {
189
- if (this.#resolvePreviousPromise) {
190
- this.#resolvePreviousPromise(this.store.state.lastResult);
191
- this.#resolvePreviousPromise = null;
192
- }
193
- };
194
- this.#clearTimeout = () => {
195
- if (this.#timeoutId) {
196
- clearTimeout(this.#timeoutId);
197
- this.#timeoutId = null;
198
- }
199
- };
200
- this.#cancelPendingExecution = () => {
201
- this.#clearTimeout();
202
- this.#resolvePreviousPromiseInternal();
203
- this.#setState({
204
- isPending: false,
205
- lastArgs: void 0
206
- });
207
- };
208
- this.getAbortSignal = (maybeExecuteCount) => {
209
- const count = maybeExecuteCount ?? this.store.state.maybeExecuteCount;
210
- return this.asyncRetryers.get(count)?.getAbortSignal() ?? null;
211
- };
212
- this.abort = () => {
213
- this.asyncRetryers.forEach((retryer) => retryer.abort());
214
- this.asyncRetryers.clear();
215
- this.#setState({ isExecuting: false });
216
- };
217
- this.cancel = () => {
218
- this.#cancelPendingExecution();
219
- this.#setState({ canLeadingExecute: true });
220
- };
221
- this.reset = () => {
222
- this.#setState(getDefaultAsyncDebouncerState());
223
- this.asyncRetryers.forEach((retryer) => retryer.reset());
224
- };
225
- this.key = initialOptions.key;
226
- this.options = {
227
- ...defaultOptions,
228
- ...initialOptions,
229
- throwOnError: initialOptions.throwOnError ?? !initialOptions.onError
230
- };
231
- this.#setState(this.options.initialState ?? {});
232
- if (this.key) require_event_client.pacerEventClient.on("d-AsyncDebouncer", (event) => {
233
- if (event.payload.key !== this.key) return;
234
- this.#setState(event.payload.store.state);
235
- this.setOptions(event.payload.options);
236
- });
237
- }
238
- #setState;
239
- /**
240
- * Returns the current debouncer enabled state
241
- */
242
- #getEnabled;
243
- /**
244
- * Returns the current debouncer wait state
245
- */
246
- #getWait;
247
- #execute;
248
- #resolvePreviousPromiseInternal;
249
- #clearTimeout;
250
- /**
251
- * Internal cancel without resetting the leading execute state
252
- */
253
- #cancelPendingExecution;
254
- };
255
- /**
256
- * Creates an async debounced function that delays execution until after a specified wait time.
257
- * The debounced function will only execute once the wait period has elapsed without any new calls.
258
- * If called again during the wait period, the timer resets and a new wait period begins.
259
- *
260
- * Async vs Sync Versions:
261
- * The async version provides advanced features over the sync debounce function:
262
- * - Returns promises that can be awaited for debounced function results
263
- * - Built-in retry support via AsyncRetryer integration
264
- * - Abort support to cancel in-flight executions
265
- * - Cancel support to prevent pending executions from starting
266
- * - Comprehensive error handling with onError callbacks and throwOnError control
267
- * - Detailed execution tracking (success/error/settle counts)
268
- *
269
- * The sync debounce function is lighter weight and simpler when you don't need async features,
270
- * return values, or execution control.
271
- *
272
- * What is Debouncing?
273
- * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.
274
- * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing
275
- * or input changes where you only want to execute the handler after the events have stopped occurring.
276
- *
277
- * Configuration Options:
278
- * - `wait`: Delay in milliseconds to wait after the last call (required)
279
- * - `leading`: Execute on the leading edge of the timeout (default: false)
280
- * - `trailing`: Execute on the trailing edge of the timeout (default: true)
281
- * - `enabled`: Whether the debouncer is enabled (default: true)
282
- * - `asyncRetryerOptions`: Configure retry behavior for executions
283
- *
284
- * Error Handling:
285
- * - If an `onError` handler is provided, it will be called with the error and debouncer instance
286
- * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown
287
- * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed
288
- * - The error state can be checked using the underlying AsyncDebouncer instance
289
- * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown
290
- *
291
- * State Management:
292
- * - Uses TanStack Store for reactive state management
293
- * - Use `initialState` to provide initial state values when creating the async debouncer
294
- * - Use `onSuccess` callback to react to successful function execution and implement custom logic
295
- * - Use `onError` callback to react to function execution errors and implement custom error handling
296
- * - Use `onSettled` callback to react to function execution completion (success or error) and implement custom logic
297
- * - The state includes canLeadingExecute, error count, execution status, and success/settle counts
298
- * - State can be accessed via `asyncDebouncer.store.state` when using the class directly
299
- * - When using framework adapters (React/Solid), state is accessed from `asyncDebouncer.state`
300
- *
301
- * @example
302
- * ```ts
303
- * const debounced = asyncDebounce(async (value: string) => {
304
- * const result = await saveToAPI(value);
305
- * return result; // Return value is preserved
306
- * }, {
307
- * wait: 1000,
308
- * onError: (error) => {
309
- * console.error('API call failed:', error);
310
- * },
311
- * throwOnError: true // Will both log the error and throw it
312
- * });
313
- *
314
- * // Will only execute once, 1 second after the last call
315
- * // Returns the API response directly
316
- * const result = await debounced("third");
317
- * ```
318
- */
319
- function asyncDebounce(fn, initialOptions) {
320
- return new AsyncDebouncer(fn, initialOptions).maybeExecute;
321
- }
322
-
323
- //#endregion
324
- exports.AsyncDebouncer = AsyncDebouncer;
325
- exports.asyncDebounce = asyncDebounce;
326
- exports.asyncDebouncerOptions = asyncDebouncerOptions;
327
- //# sourceMappingURL=async-debouncer.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"async-debouncer.cjs","names":["Store","#getEnabled","parseFunctionOrValue","#cancelPendingExecution","#setState","#execute","#resolvePreviousPromise","#timeoutId","#getWait","AsyncRetryer","#clearTimeout","#resolvePreviousPromiseInternal"],"sources":["../src/async-debouncer.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { AsyncRetryer } from './async-retryer'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\nimport type { AsyncRetryerOptions } from './async-retryer'\nimport type { AnyAsyncFunction, OptionalKeys } from './types'\n\nexport interface AsyncDebouncerState<TFn extends AnyAsyncFunction> {\n /**\n * Whether the debouncer can execute on the leading edge of the timeout\n */\n canLeadingExecute: boolean\n /**\n * Number of function executions that have resulted in errors\n */\n errorCount: number\n /**\n * Whether the debounced function is currently executing asynchronously\n */\n isExecuting: boolean\n /**\n * Whether the debouncer is waiting for the timeout to trigger execution\n */\n isPending: boolean\n /**\n * The arguments from the most recent call to maybeExecute\n */\n lastArgs: Parameters<TFn> | undefined\n /**\n * The result from the most recent successful function execution\n */\n lastResult: Awaited<ReturnType<TFn>> | undefined\n /**\n * Number of times maybeExecute has been called (for reduction calculations)\n */\n maybeExecuteCount: number\n /**\n * Number of function executions that have completed (either successfully or with errors)\n */\n settleCount: number\n /**\n * Current execution status - 'idle' when not active, 'pending' when waiting, 'executing' when running, 'settled' when completed\n */\n status: 'disabled' | 'idle' | 'pending' | 'executing' | 'settled'\n /**\n * Number of function executions that have completed successfully\n */\n successCount: number\n}\n\nfunction getDefaultAsyncDebouncerState<\n TFn extends AnyAsyncFunction,\n>(): AsyncDebouncerState<TFn> {\n return {\n canLeadingExecute: true,\n errorCount: 0,\n isExecuting: false,\n isPending: false,\n lastArgs: undefined,\n lastResult: undefined,\n maybeExecuteCount: 0,\n settleCount: 0,\n status: 'idle',\n successCount: 0,\n }\n}\n\n/**\n * Options for configuring an async debounced function\n */\nexport interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {\n /**\n * Options for configuring the underlying async retryer\n */\n asyncRetryerOptions?: AsyncRetryerOptions<TFn>\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: AsyncDebouncer<TFn>) => boolean)\n /**\n * Initial state for the async debouncer\n */\n initialState?: Partial<AsyncDebouncerState<TFn>>\n /**\n * Optional key to identify this async debouncer instance.\n * If provided, the async debouncer will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Optional error handler for when the debounced function throws.\n * If provided, the handler will be called with the error and debouncer instance.\n * This can be used alongside throwOnError - the handler will be called before any error is thrown.\n */\n onError?: (\n error: Error,\n args: Parameters<TFn>,\n debouncer: AsyncDebouncer<TFn>,\n ) => void\n /**\n * Optional callback to call when the debounced function is executed\n */\n onSettled?: (args: Parameters<TFn>, debouncer: AsyncDebouncer<TFn>) => void\n /**\n * Optional callback to call when the debounced function is executed\n */\n onSuccess?: (\n result: Awaited<ReturnType<TFn>>,\n args: Parameters<TFn>,\n debouncer: AsyncDebouncer<TFn>,\n ) => void\n /**\n * Whether to throw errors when they occur.\n * Defaults to true if no onError handler is provided, false if an onError handler is provided.\n * Can be explicitly set to override these defaults.\n */\n throwOnError?: boolean\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Delay in milliseconds to wait after the last call before executing.\n * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: AsyncDebouncer<TFn>) => number)\n}\n\n/**\n * Utility function for sharing common `AsyncDebouncerOptions` options between different `AsyncDebouncer` instances.\n */\nexport function asyncDebouncerOptions<\n TFn extends AnyAsyncFunction = AnyAsyncFunction,\n TOptions extends Partial<AsyncDebouncerOptions<TFn>> = Partial<\n AsyncDebouncerOptions<TFn>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\ntype AsyncDebouncerOptionsWithOptionalCallbacks = OptionalKeys<\n AsyncDebouncerOptions<any>,\n 'initialState' | 'onError' | 'onSettled' | 'onSuccess' | 'key'\n>\n\nconst defaultOptions: AsyncDebouncerOptionsWithOptionalCallbacks = {\n asyncRetryerOptions: {\n maxAttempts: 1,\n },\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates an async debounced function.\n *\n * Async vs Sync Versions:\n * The async version provides advanced features over the sync Debouncer:\n * - Returns promises that can be awaited for debounced function results\n * - Built-in retry support via AsyncRetryer integration\n * - Abort support to cancel in-flight executions\n * - Cancel support to prevent pending executions from starting\n * - Comprehensive error handling with onError callbacks and throwOnError control\n * - Detailed execution tracking (success/error/settle counts)\n *\n * The sync Debouncer is lighter weight and simpler when you don't need async features,\n * return values, or execution control.\n *\n * What is Debouncing?\n * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.\n * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing\n * or input changes where you only want to execute the handler after the events have stopped occurring.\n *\n * Unlike throttling which allows execution at regular intervals, debouncing prevents any execution until\n * the function stops being called for the specified delay period.\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and debouncer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown\n * - The error state can be checked using the underlying store\n *\n * State Management:\n * - The debouncer uses a reactive store for state management\n * - Use `initialState` to provide initial state values when creating the async debouncer\n * - The state includes canLeadingExecute, error count, execution status, and success/settle counts\n * - State can be accessed via the `store` property and its `state` getter\n * - The store is reactive and will notify subscribers of state changes\n *\n * @example\n * ```ts\n * const asyncDebouncer = new AsyncDebouncer(async (value: string) => {\n * const results = await searchAPI(value);\n * return results; // Return value is preserved\n * }, {\n * wait: 500,\n * onError: (error) => {\n * console.error('Search failed:', error);\n * }\n * });\n *\n * // Called on each keystroke but only executes after 500ms of no typing\n * // Returns the API response directly\n * const results = await asyncDebouncer.maybeExecute(inputElement.value);\n * ```\n */\nexport class AsyncDebouncer<TFn extends AnyAsyncFunction> {\n readonly store: Store<Readonly<AsyncDebouncerState<TFn>>> = new Store<\n AsyncDebouncerState<TFn>\n >(getDefaultAsyncDebouncerState<TFn>())\n key: string | undefined\n options: AsyncDebouncerOptions<TFn>\n asyncRetryers = new Map<number, AsyncRetryer<TFn>>()\n #timeoutId: ReturnType<typeof setTimeout> | null = null\n #resolvePreviousPromise:\n ((value?: Awaited<ReturnType<TFn>> | undefined) => void) | null = null\n\n constructor(\n public fn: TFn,\n initialOptions: AsyncDebouncerOptions<TFn>,\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n throwOnError: initialOptions.throwOnError ?? !initialOptions.onError,\n }\n this.#setState(this.options.initialState ?? {})\n\n if (this.key) {\n pacerEventClient.on('d-AsyncDebouncer', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(\n event.payload.store.state as Partial<AsyncDebouncerState<TFn>>,\n )\n this.setOptions(\n event.payload.options as Partial<AsyncDebouncerOptions<TFn>>,\n )\n })\n }\n }\n\n /**\n * Updates the async debouncer options\n */\n setOptions = (newOptions: Partial<AsyncDebouncerOptions<TFn>>): void => {\n this.options = { ...this.options, ...newOptions }\n\n // Cancel pending execution if the debouncer is disabled\n if (!this.#getEnabled()) {\n this.cancel()\n }\n }\n\n #setState = (newState: Partial<AsyncDebouncerState<TFn>>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n const { isPending, isExecuting, settleCount } = combinedState\n return {\n ...combinedState,\n status: !this.#getEnabled()\n ? 'disabled'\n : isPending\n ? 'pending'\n : isExecuting\n ? 'executing'\n : settleCount > 0\n ? 'settled'\n : 'idle',\n }\n })\n emitChange('AsyncDebouncer', this)\n }\n\n /**\n * Returns the current debouncer enabled state\n */\n #getEnabled = (): boolean => {\n return !!parseFunctionOrValue(this.options.enabled, this)\n }\n\n /**\n * Returns the current debouncer wait state\n */\n #getWait = (): number => {\n return parseFunctionOrValue(this.options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function.\n * If a call is already in progress, it will be queued.\n *\n * Error Handling:\n * - If the debounced function throws and no `onError` handler is configured,\n * the error will be thrown from this method.\n * - If an `onError` handler is configured, errors will be caught and passed to the handler,\n * and this method will return undefined.\n * - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.\n *\n * @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError\n * @throws The error from the debounced function if no onError handler is configured\n */\n maybeExecute = async (\n ...args: Parameters<TFn>\n ): Promise<Awaited<ReturnType<TFn>> | undefined> => {\n if (!this.#getEnabled()) return undefined\n this.#cancelPendingExecution()\n this.#setState({\n lastArgs: args,\n maybeExecuteCount: this.store.state.maybeExecuteCount + 1,\n })\n\n // Handle leading execution\n if (this.options.leading && this.store.state.canLeadingExecute) {\n this.#setState({ canLeadingExecute: false })\n await this.#execute(...args)\n return this.store.state.lastResult\n }\n\n // Handle trailing execution\n if (this.options.trailing && this.#getEnabled()) {\n this.#setState({ isPending: true })\n }\n\n return new Promise<Awaited<ReturnType<TFn>> | undefined>(\n (resolve, reject) => {\n this.#resolvePreviousPromise = resolve\n // this.#rejectPreviousPromise = reject\n this.#timeoutId = setTimeout(async () => {\n // Execute trailing if enabled\n if (this.options.trailing && this.store.state.lastArgs) {\n try {\n await this.#execute(...this.store.state.lastArgs)\n } catch (error) {\n reject(error)\n }\n }\n\n // Reset state and resolve\n this.#setState({ canLeadingExecute: true })\n this.#resolvePreviousPromise = null\n resolve(this.store.state.lastResult)\n }, this.#getWait())\n },\n )\n }\n\n #execute = async (\n ...args: Parameters<TFn>\n ): Promise<Awaited<ReturnType<TFn>> | undefined> => {\n if (!this.#getEnabled()) return undefined\n const currentMaybeExecuteCount = this.store.state.maybeExecuteCount + 1\n\n try {\n this.#setState({ isExecuting: true })\n const currentAsyncRetryer = new AsyncRetryer(\n this.fn,\n this.options.asyncRetryerOptions,\n )\n this.asyncRetryers.set(currentMaybeExecuteCount, currentAsyncRetryer)\n const result = await currentAsyncRetryer.execute(...args) // EXECUTE!\n this.#setState({\n lastResult: result,\n successCount: this.store.state.successCount + 1,\n })\n this.options.onSuccess?.(result as Awaited<ReturnType<TFn>>, args, this)\n } catch (error) {\n this.#setState({\n errorCount: this.store.state.errorCount + 1,\n })\n this.options.onError?.(error as Error, args, this)\n if (this.options.throwOnError) {\n throw error\n }\n } finally {\n this.asyncRetryers.delete(currentMaybeExecuteCount) // dispose retryer\n this.#setState({\n isExecuting: false,\n isPending: false,\n lastArgs: undefined,\n settleCount: this.store.state.settleCount + 1,\n })\n this.options.onSettled?.(args, this)\n }\n return this.store.state.lastResult\n }\n\n /**\n * Processes the current pending execution immediately\n */\n flush = async (): Promise<Awaited<ReturnType<TFn>> | undefined> => {\n if (this.store.state.isPending && this.store.state.lastArgs) {\n const { lastArgs } = this.store.state\n this.#cancelPendingExecution()\n return await this.#execute(...lastArgs)\n }\n return undefined\n }\n\n #resolvePreviousPromiseInternal = (): void => {\n if (this.#resolvePreviousPromise) {\n this.#resolvePreviousPromise(this.store.state.lastResult)\n this.#resolvePreviousPromise = null\n }\n }\n\n #clearTimeout = (): void => {\n if (this.#timeoutId) {\n clearTimeout(this.#timeoutId)\n this.#timeoutId = null\n }\n }\n\n /**\n * Internal cancel without resetting the leading execute state\n */\n #cancelPendingExecution = (): void => {\n this.#clearTimeout()\n this.#resolvePreviousPromiseInternal()\n this.#setState({\n isPending: false,\n lastArgs: undefined,\n })\n }\n\n /**\n * Returns the AbortSignal for a specific execution.\n * If no maybeExecuteCount is provided, returns the signal for the most recent execution.\n * Returns null if no execution is found or not currently executing.\n *\n * @param maybeExecuteCount - Optional specific execution to get signal for\n * @example\n * ```typescript\n * const debouncer = new AsyncDebouncer(\n * async (searchTerm: string) => {\n * const signal = debouncer.getAbortSignal()\n * if (signal) {\n * const response = await fetch(`/api/search?q=${searchTerm}`, { signal })\n * return response.json()\n * }\n * },\n * { wait: 300 }\n * )\n * ```\n */\n getAbortSignal = (maybeExecuteCount?: number): AbortSignal | null => {\n const count = maybeExecuteCount ?? this.store.state.maybeExecuteCount\n const retryer = this.asyncRetryers.get(count)\n return retryer?.getAbortSignal() ?? null\n }\n\n /**\n * Aborts all ongoing executions with the internal abort controllers.\n * Does NOT cancel any pending execution that have not started yet.\n */\n abort = (): void => {\n this.asyncRetryers.forEach((retryer) => retryer.abort())\n this.asyncRetryers.clear()\n this.#setState({\n isExecuting: false,\n })\n }\n\n /**\n * Cancels any pending execution that have not started yet.\n * Does NOT abort any execution already in progress.\n */\n cancel = (): void => {\n this.#cancelPendingExecution()\n this.#setState({ canLeadingExecute: true })\n }\n\n /**\n * Resets the debouncer state to its default values\n */\n reset = (): void => {\n this.#setState(getDefaultAsyncDebouncerState<TFn>())\n this.asyncRetryers.forEach((retryer) => retryer.reset())\n }\n}\n\n/**\n * Creates an async debounced function that delays execution until after a specified wait time.\n * The debounced function will only execute once the wait period has elapsed without any new calls.\n * If called again during the wait period, the timer resets and a new wait period begins.\n *\n * Async vs Sync Versions:\n * The async version provides advanced features over the sync debounce function:\n * - Returns promises that can be awaited for debounced function results\n * - Built-in retry support via AsyncRetryer integration\n * - Abort support to cancel in-flight executions\n * - Cancel support to prevent pending executions from starting\n * - Comprehensive error handling with onError callbacks and throwOnError control\n * - Detailed execution tracking (success/error/settle counts)\n *\n * The sync debounce function is lighter weight and simpler when you don't need async features,\n * return values, or execution control.\n *\n * What is Debouncing?\n * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.\n * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing\n * or input changes where you only want to execute the handler after the events have stopped occurring.\n *\n * Configuration Options:\n * - `wait`: Delay in milliseconds to wait after the last call (required)\n * - `leading`: Execute on the leading edge of the timeout (default: false)\n * - `trailing`: Execute on the trailing edge of the timeout (default: true)\n * - `enabled`: Whether the debouncer is enabled (default: true)\n * - `asyncRetryerOptions`: Configure retry behavior for executions\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and debouncer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - The error state can be checked using the underlying AsyncDebouncer instance\n * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown\n *\n * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the async debouncer\n * - Use `onSuccess` callback to react to successful function execution and implement custom logic\n * - Use `onError` callback to react to function execution errors and implement custom error handling\n * - Use `onSettled` callback to react to function execution completion (success or error) and implement custom logic\n * - The state includes canLeadingExecute, error count, execution status, and success/settle counts\n * - State can be accessed via `asyncDebouncer.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `asyncDebouncer.state`\n *\n * @example\n * ```ts\n * const debounced = asyncDebounce(async (value: string) => {\n * const result = await saveToAPI(value);\n * return result; // Return value is preserved\n * }, {\n * wait: 1000,\n * onError: (error) => {\n * console.error('API call failed:', error);\n * },\n * throwOnError: true // Will both log the error and throw it\n * });\n *\n * // Will only execute once, 1 second after the last call\n * // Returns the API response directly\n * const result = await debounced(\"third\");\n * ```\n */\nexport function asyncDebounce<TFn extends AnyAsyncFunction>(\n fn: TFn,\n initialOptions: AsyncDebouncerOptions<TFn>,\n) {\n const asyncDebouncer = new AsyncDebouncer(fn, initialOptions)\n return asyncDebouncer.maybeExecute\n}\n"],"mappings":"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"}
@@ -1,299 +0,0 @@
1
- import { AnyAsyncFunction } from "./types.cjs";
2
- import { AsyncRetryer, AsyncRetryerOptions } from "./async-retryer.cjs";
3
- import { Store } from "@tanstack/store";
4
- //#region src/async-debouncer.d.ts
5
- interface AsyncDebouncerState<TFn extends AnyAsyncFunction> {
6
- /**
7
- * Whether the debouncer can execute on the leading edge of the timeout
8
- */
9
- canLeadingExecute: boolean;
10
- /**
11
- * Number of function executions that have resulted in errors
12
- */
13
- errorCount: number;
14
- /**
15
- * Whether the debounced function is currently executing asynchronously
16
- */
17
- isExecuting: boolean;
18
- /**
19
- * Whether the debouncer is waiting for the timeout to trigger execution
20
- */
21
- isPending: boolean;
22
- /**
23
- * The arguments from the most recent call to maybeExecute
24
- */
25
- lastArgs: Parameters<TFn> | undefined;
26
- /**
27
- * The result from the most recent successful function execution
28
- */
29
- lastResult: Awaited<ReturnType<TFn>> | undefined;
30
- /**
31
- * Number of times maybeExecute has been called (for reduction calculations)
32
- */
33
- maybeExecuteCount: number;
34
- /**
35
- * Number of function executions that have completed (either successfully or with errors)
36
- */
37
- settleCount: number;
38
- /**
39
- * Current execution status - 'idle' when not active, 'pending' when waiting, 'executing' when running, 'settled' when completed
40
- */
41
- status: 'disabled' | 'idle' | 'pending' | 'executing' | 'settled';
42
- /**
43
- * Number of function executions that have completed successfully
44
- */
45
- successCount: number;
46
- }
47
- /**
48
- * Options for configuring an async debounced function
49
- */
50
- interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
51
- /**
52
- * Options for configuring the underlying async retryer
53
- */
54
- asyncRetryerOptions?: AsyncRetryerOptions<TFn>;
55
- /**
56
- * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.
57
- * Can be a boolean or a function that returns a boolean.
58
- * Defaults to true.
59
- */
60
- enabled?: boolean | ((debouncer: AsyncDebouncer<TFn>) => boolean);
61
- /**
62
- * Initial state for the async debouncer
63
- */
64
- initialState?: Partial<AsyncDebouncerState<TFn>>;
65
- /**
66
- * Optional key to identify this async debouncer instance.
67
- * If provided, the async debouncer will be identified by this key in the devtools and PacerProvider if applicable.
68
- */
69
- key?: string;
70
- /**
71
- * Whether to execute on the leading edge of the timeout.
72
- * Defaults to false.
73
- */
74
- leading?: boolean;
75
- /**
76
- * Optional error handler for when the debounced function throws.
77
- * If provided, the handler will be called with the error and debouncer instance.
78
- * This can be used alongside throwOnError - the handler will be called before any error is thrown.
79
- */
80
- onError?: (error: Error, args: Parameters<TFn>, debouncer: AsyncDebouncer<TFn>) => void;
81
- /**
82
- * Optional callback to call when the debounced function is executed
83
- */
84
- onSettled?: (args: Parameters<TFn>, debouncer: AsyncDebouncer<TFn>) => void;
85
- /**
86
- * Optional callback to call when the debounced function is executed
87
- */
88
- onSuccess?: (result: Awaited<ReturnType<TFn>>, args: Parameters<TFn>, debouncer: AsyncDebouncer<TFn>) => void;
89
- /**
90
- * Whether to throw errors when they occur.
91
- * Defaults to true if no onError handler is provided, false if an onError handler is provided.
92
- * Can be explicitly set to override these defaults.
93
- */
94
- throwOnError?: boolean;
95
- /**
96
- * Whether to execute on the trailing edge of the timeout.
97
- * Defaults to true.
98
- */
99
- trailing?: boolean;
100
- /**
101
- * Delay in milliseconds to wait after the last call before executing.
102
- * Can be a number or a function that returns a number.
103
- * Defaults to 0ms
104
- */
105
- wait: number | ((debouncer: AsyncDebouncer<TFn>) => number);
106
- }
107
- /**
108
- * Utility function for sharing common `AsyncDebouncerOptions` options between different `AsyncDebouncer` instances.
109
- */
110
- declare function asyncDebouncerOptions<TFn extends AnyAsyncFunction = AnyAsyncFunction, TOptions extends Partial<AsyncDebouncerOptions<TFn>> = Partial<AsyncDebouncerOptions<TFn>>>(options: TOptions): TOptions;
111
- /**
112
- * A class that creates an async debounced function.
113
- *
114
- * Async vs Sync Versions:
115
- * The async version provides advanced features over the sync Debouncer:
116
- * - Returns promises that can be awaited for debounced function results
117
- * - Built-in retry support via AsyncRetryer integration
118
- * - Abort support to cancel in-flight executions
119
- * - Cancel support to prevent pending executions from starting
120
- * - Comprehensive error handling with onError callbacks and throwOnError control
121
- * - Detailed execution tracking (success/error/settle counts)
122
- *
123
- * The sync Debouncer is lighter weight and simpler when you don't need async features,
124
- * return values, or execution control.
125
- *
126
- * What is Debouncing?
127
- * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.
128
- * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing
129
- * or input changes where you only want to execute the handler after the events have stopped occurring.
130
- *
131
- * Unlike throttling which allows execution at regular intervals, debouncing prevents any execution until
132
- * the function stops being called for the specified delay period.
133
- *
134
- * Error Handling:
135
- * - If an `onError` handler is provided, it will be called with the error and debouncer instance
136
- * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown
137
- * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed
138
- * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown
139
- * - The error state can be checked using the underlying store
140
- *
141
- * State Management:
142
- * - The debouncer uses a reactive store for state management
143
- * - Use `initialState` to provide initial state values when creating the async debouncer
144
- * - The state includes canLeadingExecute, error count, execution status, and success/settle counts
145
- * - State can be accessed via the `store` property and its `state` getter
146
- * - The store is reactive and will notify subscribers of state changes
147
- *
148
- * @example
149
- * ```ts
150
- * const asyncDebouncer = new AsyncDebouncer(async (value: string) => {
151
- * const results = await searchAPI(value);
152
- * return results; // Return value is preserved
153
- * }, {
154
- * wait: 500,
155
- * onError: (error) => {
156
- * console.error('Search failed:', error);
157
- * }
158
- * });
159
- *
160
- * // Called on each keystroke but only executes after 500ms of no typing
161
- * // Returns the API response directly
162
- * const results = await asyncDebouncer.maybeExecute(inputElement.value);
163
- * ```
164
- */
165
- declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
166
- #private;
167
- fn: TFn;
168
- readonly store: Store<Readonly<AsyncDebouncerState<TFn>>>;
169
- key: string | undefined;
170
- options: AsyncDebouncerOptions<TFn>;
171
- asyncRetryers: Map<number, AsyncRetryer<TFn>>;
172
- constructor(fn: TFn, initialOptions: AsyncDebouncerOptions<TFn>);
173
- /**
174
- * Updates the async debouncer options
175
- */
176
- setOptions: (newOptions: Partial<AsyncDebouncerOptions<TFn>>) => void;
177
- /**
178
- * Attempts to execute the debounced function.
179
- * If a call is already in progress, it will be queued.
180
- *
181
- * Error Handling:
182
- * - If the debounced function throws and no `onError` handler is configured,
183
- * the error will be thrown from this method.
184
- * - If an `onError` handler is configured, errors will be caught and passed to the handler,
185
- * and this method will return undefined.
186
- * - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.
187
- *
188
- * @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError
189
- * @throws The error from the debounced function if no onError handler is configured
190
- */
191
- maybeExecute: (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
192
- /**
193
- * Processes the current pending execution immediately
194
- */
195
- flush: () => Promise<Awaited<ReturnType<TFn>> | undefined>;
196
- /**
197
- * Returns the AbortSignal for a specific execution.
198
- * If no maybeExecuteCount is provided, returns the signal for the most recent execution.
199
- * Returns null if no execution is found or not currently executing.
200
- *
201
- * @param maybeExecuteCount - Optional specific execution to get signal for
202
- * @example
203
- * ```typescript
204
- * const debouncer = new AsyncDebouncer(
205
- * async (searchTerm: string) => {
206
- * const signal = debouncer.getAbortSignal()
207
- * if (signal) {
208
- * const response = await fetch(`/api/search?q=${searchTerm}`, { signal })
209
- * return response.json()
210
- * }
211
- * },
212
- * { wait: 300 }
213
- * )
214
- * ```
215
- */
216
- getAbortSignal: (maybeExecuteCount?: number) => AbortSignal | null;
217
- /**
218
- * Aborts all ongoing executions with the internal abort controllers.
219
- * Does NOT cancel any pending execution that have not started yet.
220
- */
221
- abort: () => void;
222
- /**
223
- * Cancels any pending execution that have not started yet.
224
- * Does NOT abort any execution already in progress.
225
- */
226
- cancel: () => void;
227
- /**
228
- * Resets the debouncer state to its default values
229
- */
230
- reset: () => void;
231
- }
232
- /**
233
- * Creates an async debounced function that delays execution until after a specified wait time.
234
- * The debounced function will only execute once the wait period has elapsed without any new calls.
235
- * If called again during the wait period, the timer resets and a new wait period begins.
236
- *
237
- * Async vs Sync Versions:
238
- * The async version provides advanced features over the sync debounce function:
239
- * - Returns promises that can be awaited for debounced function results
240
- * - Built-in retry support via AsyncRetryer integration
241
- * - Abort support to cancel in-flight executions
242
- * - Cancel support to prevent pending executions from starting
243
- * - Comprehensive error handling with onError callbacks and throwOnError control
244
- * - Detailed execution tracking (success/error/settle counts)
245
- *
246
- * The sync debounce function is lighter weight and simpler when you don't need async features,
247
- * return values, or execution control.
248
- *
249
- * What is Debouncing?
250
- * Debouncing ensures that a function is only executed after a specified delay has passed since its last invocation.
251
- * Each new invocation resets the delay timer. This is useful for handling frequent events like window resizing
252
- * or input changes where you only want to execute the handler after the events have stopped occurring.
253
- *
254
- * Configuration Options:
255
- * - `wait`: Delay in milliseconds to wait after the last call (required)
256
- * - `leading`: Execute on the leading edge of the timeout (default: false)
257
- * - `trailing`: Execute on the trailing edge of the timeout (default: true)
258
- * - `enabled`: Whether the debouncer is enabled (default: true)
259
- * - `asyncRetryerOptions`: Configure retry behavior for executions
260
- *
261
- * Error Handling:
262
- * - If an `onError` handler is provided, it will be called with the error and debouncer instance
263
- * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown
264
- * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed
265
- * - The error state can be checked using the underlying AsyncDebouncer instance
266
- * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown
267
- *
268
- * State Management:
269
- * - Uses TanStack Store for reactive state management
270
- * - Use `initialState` to provide initial state values when creating the async debouncer
271
- * - Use `onSuccess` callback to react to successful function execution and implement custom logic
272
- * - Use `onError` callback to react to function execution errors and implement custom error handling
273
- * - Use `onSettled` callback to react to function execution completion (success or error) and implement custom logic
274
- * - The state includes canLeadingExecute, error count, execution status, and success/settle counts
275
- * - State can be accessed via `asyncDebouncer.store.state` when using the class directly
276
- * - When using framework adapters (React/Solid), state is accessed from `asyncDebouncer.state`
277
- *
278
- * @example
279
- * ```ts
280
- * const debounced = asyncDebounce(async (value: string) => {
281
- * const result = await saveToAPI(value);
282
- * return result; // Return value is preserved
283
- * }, {
284
- * wait: 1000,
285
- * onError: (error) => {
286
- * console.error('API call failed:', error);
287
- * },
288
- * throwOnError: true // Will both log the error and throw it
289
- * });
290
- *
291
- * // Will only execute once, 1 second after the last call
292
- * // Returns the API response directly
293
- * const result = await debounced("third");
294
- * ```
295
- */
296
- declare function asyncDebounce<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions: AsyncDebouncerOptions<TFn>): (...args: Parameters<TFn>) => Promise<Awaited<ReturnType<TFn>> | undefined>;
297
- //#endregion
298
- export { AsyncDebouncer, AsyncDebouncerOptions, AsyncDebouncerState, asyncDebounce, asyncDebouncerOptions };
299
- //# sourceMappingURL=async-debouncer.d.cts.map