@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
@@ -86,141 +86,15 @@ const defaultOptions = {
86
86
  * ```
87
87
  */
88
88
  var AsyncDebouncer = class {
89
- #timeoutId;
90
- #resolvePreviousPromise;
89
+ fn;
90
+ store = new Store(getDefaultAsyncDebouncerState());
91
+ key;
92
+ options;
93
+ asyncRetryers = /* @__PURE__ */ new Map();
94
+ #timeoutId = null;
95
+ #resolvePreviousPromise = null;
91
96
  constructor(fn, initialOptions) {
92
97
  this.fn = fn;
93
- this.store = new Store(getDefaultAsyncDebouncerState());
94
- this.asyncRetryers = /* @__PURE__ */ new Map();
95
- this.#timeoutId = null;
96
- this.#resolvePreviousPromise = null;
97
- this.setOptions = (newOptions) => {
98
- this.options = {
99
- ...this.options,
100
- ...newOptions
101
- };
102
- if (!this.#getEnabled()) this.cancel();
103
- };
104
- this.#setState = (newState) => {
105
- this.store.setState((state) => {
106
- const combinedState = {
107
- ...state,
108
- ...newState
109
- };
110
- const { isPending, isExecuting, settleCount } = combinedState;
111
- return {
112
- ...combinedState,
113
- status: !this.#getEnabled() ? "disabled" : isPending ? "pending" : isExecuting ? "executing" : settleCount > 0 ? "settled" : "idle"
114
- };
115
- });
116
- emitChange("AsyncDebouncer", this);
117
- };
118
- this.#getEnabled = () => {
119
- return !!parseFunctionOrValue(this.options.enabled, this);
120
- };
121
- this.#getWait = () => {
122
- return parseFunctionOrValue(this.options.wait, this);
123
- };
124
- this.maybeExecute = async (...args) => {
125
- if (!this.#getEnabled()) return void 0;
126
- this.#cancelPendingExecution();
127
- this.#setState({
128
- lastArgs: args,
129
- maybeExecuteCount: this.store.state.maybeExecuteCount + 1
130
- });
131
- if (this.options.leading && this.store.state.canLeadingExecute) {
132
- this.#setState({ canLeadingExecute: false });
133
- await this.#execute(...args);
134
- return this.store.state.lastResult;
135
- }
136
- if (this.options.trailing && this.#getEnabled()) this.#setState({ isPending: true });
137
- return new Promise((resolve, reject) => {
138
- this.#resolvePreviousPromise = resolve;
139
- this.#timeoutId = setTimeout(async () => {
140
- if (this.options.trailing && this.store.state.lastArgs) try {
141
- await this.#execute(...this.store.state.lastArgs);
142
- } catch (error) {
143
- reject(error);
144
- }
145
- this.#setState({ canLeadingExecute: true });
146
- this.#resolvePreviousPromise = null;
147
- resolve(this.store.state.lastResult);
148
- }, this.#getWait());
149
- });
150
- };
151
- this.#execute = async (...args) => {
152
- if (!this.#getEnabled()) return void 0;
153
- const currentMaybeExecuteCount = this.store.state.maybeExecuteCount + 1;
154
- try {
155
- this.#setState({ isExecuting: true });
156
- const currentAsyncRetryer = new AsyncRetryer(this.fn, this.options.asyncRetryerOptions);
157
- this.asyncRetryers.set(currentMaybeExecuteCount, currentAsyncRetryer);
158
- const result = await currentAsyncRetryer.execute(...args);
159
- this.#setState({
160
- lastResult: result,
161
- successCount: this.store.state.successCount + 1
162
- });
163
- this.options.onSuccess?.(result, args, this);
164
- } catch (error) {
165
- this.#setState({ errorCount: this.store.state.errorCount + 1 });
166
- this.options.onError?.(error, args, this);
167
- if (this.options.throwOnError) throw error;
168
- } finally {
169
- this.asyncRetryers.delete(currentMaybeExecuteCount);
170
- this.#setState({
171
- isExecuting: false,
172
- isPending: false,
173
- lastArgs: void 0,
174
- settleCount: this.store.state.settleCount + 1
175
- });
176
- this.options.onSettled?.(args, this);
177
- }
178
- return this.store.state.lastResult;
179
- };
180
- this.flush = async () => {
181
- if (this.store.state.isPending && this.store.state.lastArgs) {
182
- const { lastArgs } = this.store.state;
183
- this.#cancelPendingExecution();
184
- return await this.#execute(...lastArgs);
185
- }
186
- };
187
- this.#resolvePreviousPromiseInternal = () => {
188
- if (this.#resolvePreviousPromise) {
189
- this.#resolvePreviousPromise(this.store.state.lastResult);
190
- this.#resolvePreviousPromise = null;
191
- }
192
- };
193
- this.#clearTimeout = () => {
194
- if (this.#timeoutId) {
195
- clearTimeout(this.#timeoutId);
196
- this.#timeoutId = null;
197
- }
198
- };
199
- this.#cancelPendingExecution = () => {
200
- this.#clearTimeout();
201
- this.#resolvePreviousPromiseInternal();
202
- this.#setState({
203
- isPending: false,
204
- lastArgs: void 0
205
- });
206
- };
207
- this.getAbortSignal = (maybeExecuteCount) => {
208
- const count = maybeExecuteCount ?? this.store.state.maybeExecuteCount;
209
- return this.asyncRetryers.get(count)?.getAbortSignal() ?? null;
210
- };
211
- this.abort = () => {
212
- this.asyncRetryers.forEach((retryer) => retryer.abort());
213
- this.asyncRetryers.clear();
214
- this.#setState({ isExecuting: false });
215
- };
216
- this.cancel = () => {
217
- this.#cancelPendingExecution();
218
- this.#setState({ canLeadingExecute: true });
219
- };
220
- this.reset = () => {
221
- this.#setState(getDefaultAsyncDebouncerState());
222
- this.asyncRetryers.forEach((retryer) => retryer.reset());
223
- };
224
98
  this.key = initialOptions.key;
225
99
  this.options = {
226
100
  ...defaultOptions,
@@ -234,22 +108,193 @@ var AsyncDebouncer = class {
234
108
  this.setOptions(event.payload.options);
235
109
  });
236
110
  }
237
- #setState;
111
+ /**
112
+ * Updates the async debouncer options
113
+ */
114
+ setOptions = (newOptions) => {
115
+ this.options = {
116
+ ...this.options,
117
+ ...newOptions
118
+ };
119
+ if (!this.#getEnabled()) this.cancel();
120
+ };
121
+ #setState = (newState) => {
122
+ this.store.setState((state) => {
123
+ const combinedState = {
124
+ ...state,
125
+ ...newState
126
+ };
127
+ const { isPending, isExecuting, settleCount } = combinedState;
128
+ return {
129
+ ...combinedState,
130
+ status: !this.#getEnabled() ? "disabled" : isPending ? "pending" : isExecuting ? "executing" : settleCount > 0 ? "settled" : "idle"
131
+ };
132
+ });
133
+ emitChange("AsyncDebouncer", this);
134
+ };
238
135
  /**
239
136
  * Returns the current debouncer enabled state
240
137
  */
241
- #getEnabled;
138
+ #getEnabled = () => {
139
+ return !!parseFunctionOrValue(this.options.enabled, this);
140
+ };
242
141
  /**
243
142
  * Returns the current debouncer wait state
244
143
  */
245
- #getWait;
246
- #execute;
247
- #resolvePreviousPromiseInternal;
248
- #clearTimeout;
144
+ #getWait = () => {
145
+ return parseFunctionOrValue(this.options.wait, this);
146
+ };
147
+ /**
148
+ * Attempts to execute the debounced function.
149
+ * If a call is already in progress, it will be queued.
150
+ *
151
+ * Error Handling:
152
+ * - If the debounced function throws and no `onError` handler is configured,
153
+ * the error will be thrown from this method.
154
+ * - If an `onError` handler is configured, errors will be caught and passed to the handler,
155
+ * and this method will return undefined.
156
+ * - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.
157
+ *
158
+ * @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError
159
+ * @throws The error from the debounced function if no onError handler is configured
160
+ */
161
+ maybeExecute = async (...args) => {
162
+ if (!this.#getEnabled()) return void 0;
163
+ this.#cancelPendingExecution();
164
+ this.#setState({
165
+ lastArgs: args,
166
+ maybeExecuteCount: this.store.state.maybeExecuteCount + 1
167
+ });
168
+ if (this.options.leading && this.store.state.canLeadingExecute) {
169
+ this.#setState({ canLeadingExecute: false });
170
+ await this.#execute(...args);
171
+ return this.store.state.lastResult;
172
+ }
173
+ if (this.options.trailing && this.#getEnabled()) this.#setState({ isPending: true });
174
+ return new Promise((resolve, reject) => {
175
+ this.#resolvePreviousPromise = resolve;
176
+ this.#timeoutId = setTimeout(async () => {
177
+ if (this.options.trailing && this.store.state.lastArgs) try {
178
+ await this.#execute(...this.store.state.lastArgs);
179
+ } catch (error) {
180
+ reject(error);
181
+ }
182
+ this.#setState({ canLeadingExecute: true });
183
+ this.#resolvePreviousPromise = null;
184
+ resolve(this.store.state.lastResult);
185
+ }, this.#getWait());
186
+ });
187
+ };
188
+ #execute = async (...args) => {
189
+ if (!this.#getEnabled()) return void 0;
190
+ const currentMaybeExecuteCount = this.store.state.maybeExecuteCount + 1;
191
+ try {
192
+ this.#setState({ isExecuting: true });
193
+ const currentAsyncRetryer = new AsyncRetryer(this.fn, this.options.asyncRetryerOptions);
194
+ this.asyncRetryers.set(currentMaybeExecuteCount, currentAsyncRetryer);
195
+ const result = await currentAsyncRetryer.execute(...args);
196
+ this.#setState({
197
+ lastResult: result,
198
+ successCount: this.store.state.successCount + 1
199
+ });
200
+ this.options.onSuccess?.(result, args, this);
201
+ } catch (error) {
202
+ this.#setState({ errorCount: this.store.state.errorCount + 1 });
203
+ this.options.onError?.(error, args, this);
204
+ if (this.options.throwOnError) throw error;
205
+ } finally {
206
+ this.asyncRetryers.delete(currentMaybeExecuteCount);
207
+ this.#setState({
208
+ isExecuting: false,
209
+ isPending: false,
210
+ lastArgs: void 0,
211
+ settleCount: this.store.state.settleCount + 1
212
+ });
213
+ this.options.onSettled?.(args, this);
214
+ }
215
+ return this.store.state.lastResult;
216
+ };
217
+ /**
218
+ * Processes the current pending execution immediately
219
+ */
220
+ flush = async () => {
221
+ if (this.store.state.isPending && this.store.state.lastArgs) {
222
+ const { lastArgs } = this.store.state;
223
+ this.#cancelPendingExecution();
224
+ return await this.#execute(...lastArgs);
225
+ }
226
+ };
227
+ #resolvePreviousPromiseInternal = () => {
228
+ if (this.#resolvePreviousPromise) {
229
+ this.#resolvePreviousPromise(this.store.state.lastResult);
230
+ this.#resolvePreviousPromise = null;
231
+ }
232
+ };
233
+ #clearTimeout = () => {
234
+ if (this.#timeoutId) {
235
+ clearTimeout(this.#timeoutId);
236
+ this.#timeoutId = null;
237
+ }
238
+ };
249
239
  /**
250
240
  * Internal cancel without resetting the leading execute state
251
241
  */
252
- #cancelPendingExecution;
242
+ #cancelPendingExecution = () => {
243
+ this.#clearTimeout();
244
+ this.#resolvePreviousPromiseInternal();
245
+ this.#setState({
246
+ isPending: false,
247
+ lastArgs: void 0
248
+ });
249
+ };
250
+ /**
251
+ * Returns the AbortSignal for a specific execution.
252
+ * If no maybeExecuteCount is provided, returns the signal for the most recent execution.
253
+ * Returns null if no execution is found or not currently executing.
254
+ *
255
+ * @param maybeExecuteCount - Optional specific execution to get signal for
256
+ * @example
257
+ * ```typescript
258
+ * const debouncer = new AsyncDebouncer(
259
+ * async (searchTerm: string) => {
260
+ * const signal = debouncer.getAbortSignal()
261
+ * if (signal) {
262
+ * const response = await fetch(`/api/search?q=${searchTerm}`, { signal })
263
+ * return response.json()
264
+ * }
265
+ * },
266
+ * { wait: 300 }
267
+ * )
268
+ * ```
269
+ */
270
+ getAbortSignal = (maybeExecuteCount) => {
271
+ const count = maybeExecuteCount ?? this.store.state.maybeExecuteCount;
272
+ return this.asyncRetryers.get(count)?.getAbortSignal() ?? null;
273
+ };
274
+ /**
275
+ * Aborts all ongoing executions with the internal abort controllers.
276
+ * Does NOT cancel any pending execution that have not started yet.
277
+ */
278
+ abort = () => {
279
+ this.asyncRetryers.forEach((retryer) => retryer.abort());
280
+ this.asyncRetryers.clear();
281
+ this.#setState({ isExecuting: false });
282
+ };
283
+ /**
284
+ * Cancels any pending execution that have not started yet.
285
+ * Does NOT abort any execution already in progress.
286
+ */
287
+ cancel = () => {
288
+ this.#cancelPendingExecution();
289
+ this.#setState({ canLeadingExecute: true });
290
+ };
291
+ /**
292
+ * Resets the debouncer state to its default values
293
+ */
294
+ reset = () => {
295
+ this.#setState(getDefaultAsyncDebouncerState());
296
+ this.asyncRetryers.forEach((retryer) => retryer.reset());
297
+ };
253
298
  };
254
299
  /**
255
300
  * Creates an async debounced function that delays execution until after a specified wait time.
@@ -320,5 +365,4 @@ function asyncDebounce(fn, initialOptions) {
320
365
  }
321
366
 
322
367
  //#endregion
323
- export { AsyncDebouncer, asyncDebounce, asyncDebouncerOptions };
324
- //# sourceMappingURL=async-debouncer.js.map
368
+ export { AsyncDebouncer, asyncDebounce, asyncDebouncerOptions };
@@ -2,7 +2,7 @@ import { AsyncRetryer, AsyncRetryerOptions } from "./async-retryer.js";
2
2
  import { QueuePosition } from "./queuer.js";
3
3
  import { Store } from "@tanstack/store";
4
4
  //#region src/async-queuer.d.ts
5
- interface AsyncQueuerState<TValue> {
5
+ export interface AsyncQueuerState<TValue> {
6
6
  /**
7
7
  * Items currently being processed by the queuer
8
8
  */
@@ -18,7 +18,7 @@ interface AsyncQueuerState<TValue> {
18
18
  /**
19
19
  * Number of times execute has been called
20
20
  */
21
- executeCount: number;
21
+ executionCount: number;
22
22
  /**
23
23
  * Number of items that have been removed from the queue due to expiration
24
24
  */
@@ -66,7 +66,7 @@ interface AsyncQueuerState<TValue> {
66
66
  /**
67
67
  * Number of task executions that have completed (either successfully or with errors)
68
68
  */
69
- settledCount: number;
69
+ settleCount: number;
70
70
  /**
71
71
  * Number of items currently in the queue
72
72
  */
@@ -80,7 +80,7 @@ interface AsyncQueuerState<TValue> {
80
80
  */
81
81
  successCount: number;
82
82
  }
83
- interface AsyncQueuerOptions<TValue> {
83
+ export interface AsyncQueuerOptions<TValue> {
84
84
  /**
85
85
  * Options for configuring the underlying async retryer
86
86
  */
@@ -180,7 +180,7 @@ interface AsyncQueuerOptions<TValue> {
180
180
  /**
181
181
  * Utility function for sharing common `AsyncQueuerOptions` options between different `AsyncQueuer` instances.
182
182
  */
183
- declare function asyncQueuerOptions<TValue = any, TOptions extends Partial<AsyncQueuerOptions<TValue>> = Partial<AsyncQueuerOptions<TValue>>>(options: TOptions): TOptions;
183
+ export declare function asyncQueuerOptions<TValue = any, TOptions extends Partial<AsyncQueuerOptions<TValue>> = Partial<AsyncQueuerOptions<TValue>>>(options: TOptions): TOptions;
184
184
  /**
185
185
  * A flexible asynchronous queue for processing tasks with configurable concurrency, priority, and expiration.
186
186
  *
@@ -245,7 +245,7 @@ declare function asyncQueuerOptions<TValue = any, TOptions extends Partial<Async
245
245
  * asyncQueuer.start();
246
246
  * ```
247
247
  */
248
- declare class AsyncQueuer<TValue> {
248
+ export declare class AsyncQueuer<TValue> {
249
249
  #private;
250
250
  fn: (item: TValue) => Promise<any>;
251
251
  readonly store: Store<Readonly<AsyncQueuerState<TValue>>>;
@@ -340,10 +340,10 @@ declare class AsyncQueuer<TValue> {
340
340
  clear: () => void;
341
341
  /**
342
342
  * Returns the AbortSignal for a specific execution.
343
- * If no executeCount is provided, returns the signal for the most recent execution.
343
+ * If no executionCount is provided, returns the signal for the most recent execution.
344
344
  * Returns null if no execution is found or not currently executing.
345
345
  *
346
- * @param executeCount - Optional specific execution to get signal for
346
+ * @param executionCount - Optional specific execution to get signal for
347
347
  * @example
348
348
  * ```typescript
349
349
  * const queuer = new AsyncQueuer(
@@ -358,7 +358,7 @@ declare class AsyncQueuer<TValue> {
358
358
  * )
359
359
  * ```
360
360
  */
361
- getAbortSignal: (executeCount?: number) => AbortSignal | null;
361
+ getAbortSignal: (executionCount?: number) => AbortSignal | null;
362
362
  /**
363
363
  * Aborts all ongoing executions with the internal abort controllers.
364
364
  * Does NOT clear out the items.
@@ -434,7 +434,5 @@ declare class AsyncQueuer<TValue> {
434
434
  * enqueue('hello');
435
435
  * ```
436
436
  */
437
- declare function asyncQueue<TValue>(fn: (value: TValue) => Promise<any>, initialOptions: AsyncQueuerOptions<TValue>): (item: TValue, position?: QueuePosition, runOnItemsChange?: boolean) => boolean;
438
- //#endregion
439
- export { AsyncQueuer, AsyncQueuerOptions, AsyncQueuerState, asyncQueue, asyncQueuerOptions };
440
- //# sourceMappingURL=async-queuer.d.ts.map
437
+ export declare function asyncQueue<TValue>(fn: (value: TValue) => Promise<any>, initialOptions: AsyncQueuerOptions<TValue>): (item: TValue, position?: QueuePosition, runOnItemsChange?: boolean) => boolean;
438
+ //#endregion