@tanstack/pacer 0.4.0 → 0.6.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 (70) hide show
  1. package/dist/cjs/async-debouncer.cjs +39 -15
  2. package/dist/cjs/async-debouncer.cjs.map +1 -1
  3. package/dist/cjs/async-debouncer.d.cts +63 -11
  4. package/dist/cjs/async-queuer.cjs +102 -119
  5. package/dist/cjs/async-queuer.cjs.map +1 -1
  6. package/dist/cjs/async-queuer.d.cts +94 -52
  7. package/dist/cjs/async-rate-limiter.cjs +48 -16
  8. package/dist/cjs/async-rate-limiter.cjs.map +1 -1
  9. package/dist/cjs/async-rate-limiter.d.cts +74 -9
  10. package/dist/cjs/async-throttler.cjs +42 -17
  11. package/dist/cjs/async-throttler.cjs.map +1 -1
  12. package/dist/cjs/async-throttler.d.cts +62 -10
  13. package/dist/cjs/debouncer.cjs +16 -3
  14. package/dist/cjs/debouncer.cjs.map +1 -1
  15. package/dist/cjs/debouncer.d.cts +14 -4
  16. package/dist/cjs/index.cjs +2 -0
  17. package/dist/cjs/index.cjs.map +1 -1
  18. package/dist/cjs/queuer.cjs +13 -5
  19. package/dist/cjs/queuer.cjs.map +1 -1
  20. package/dist/cjs/queuer.d.cts +9 -3
  21. package/dist/cjs/rate-limiter.cjs +26 -7
  22. package/dist/cjs/rate-limiter.cjs.map +1 -1
  23. package/dist/cjs/rate-limiter.d.cts +20 -6
  24. package/dist/cjs/throttler.cjs +19 -5
  25. package/dist/cjs/throttler.cjs.map +1 -1
  26. package/dist/cjs/throttler.d.cts +15 -4
  27. package/dist/cjs/types.d.cts +1 -0
  28. package/dist/cjs/utils.cjs +18 -7
  29. package/dist/cjs/utils.cjs.map +1 -1
  30. package/dist/cjs/utils.d.cts +3 -0
  31. package/dist/esm/async-debouncer.d.ts +63 -11
  32. package/dist/esm/async-debouncer.js +39 -15
  33. package/dist/esm/async-debouncer.js.map +1 -1
  34. package/dist/esm/async-queuer.d.ts +94 -52
  35. package/dist/esm/async-queuer.js +102 -119
  36. package/dist/esm/async-queuer.js.map +1 -1
  37. package/dist/esm/async-rate-limiter.d.ts +74 -9
  38. package/dist/esm/async-rate-limiter.js +48 -16
  39. package/dist/esm/async-rate-limiter.js.map +1 -1
  40. package/dist/esm/async-throttler.d.ts +62 -10
  41. package/dist/esm/async-throttler.js +42 -17
  42. package/dist/esm/async-throttler.js.map +1 -1
  43. package/dist/esm/debouncer.d.ts +14 -4
  44. package/dist/esm/debouncer.js +16 -3
  45. package/dist/esm/debouncer.js.map +1 -1
  46. package/dist/esm/index.js +3 -1
  47. package/dist/esm/queuer.d.ts +9 -3
  48. package/dist/esm/queuer.js +13 -5
  49. package/dist/esm/queuer.js.map +1 -1
  50. package/dist/esm/rate-limiter.d.ts +20 -6
  51. package/dist/esm/rate-limiter.js +26 -7
  52. package/dist/esm/rate-limiter.js.map +1 -1
  53. package/dist/esm/throttler.d.ts +15 -4
  54. package/dist/esm/throttler.js +19 -5
  55. package/dist/esm/throttler.js.map +1 -1
  56. package/dist/esm/types.d.ts +1 -0
  57. package/dist/esm/utils.d.ts +3 -0
  58. package/dist/esm/utils.js +19 -8
  59. package/dist/esm/utils.js.map +1 -1
  60. package/package.json +9 -3
  61. package/src/async-debouncer.ts +89 -22
  62. package/src/async-queuer.ts +205 -175
  63. package/src/async-rate-limiter.ts +111 -25
  64. package/src/async-throttler.ts +92 -24
  65. package/src/debouncer.ts +24 -7
  66. package/src/queuer.ts +19 -7
  67. package/src/rate-limiter.ts +37 -13
  68. package/src/throttler.ts +28 -9
  69. package/src/types.ts +3 -0
  70. package/src/utils.ts +19 -5
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const utils = require("./utils.cjs");
3
4
  const defaultOptions = {
4
5
  enabled: true,
5
6
  limit: 1,
@@ -34,6 +35,24 @@ class RateLimiter {
34
35
  getOptions() {
35
36
  return this._options;
36
37
  }
38
+ /**
39
+ * Returns the current enabled state of the rate limiter
40
+ */
41
+ getEnabled() {
42
+ return utils.parseFunctionOrValue(this._options.enabled, this);
43
+ }
44
+ /**
45
+ * Returns the current limit of executions allowed within the time window
46
+ */
47
+ getLimit() {
48
+ return utils.parseFunctionOrValue(this._options.limit, this);
49
+ }
50
+ /**
51
+ * Returns the current time window in milliseconds
52
+ */
53
+ getWindow() {
54
+ return utils.parseFunctionOrValue(this._options.window, this);
55
+ }
37
56
  /**
38
57
  * Attempts to execute the rate-limited function if within the configured limits.
39
58
  * Will reject execution if the number of calls in the current window exceeds the limit.
@@ -52,15 +71,15 @@ class RateLimiter {
52
71
  maybeExecute(...args) {
53
72
  this.cleanupOldExecutions();
54
73
  if (this._options.windowType === "sliding") {
55
- if (this._executionTimes.length < this._options.limit) {
74
+ if (this._executionTimes.length < this.getLimit()) {
56
75
  this.executeFunction(...args);
57
76
  return true;
58
77
  }
59
78
  } else {
60
79
  const now = Date.now();
61
80
  const oldestExecution = Math.min(...this._executionTimes);
62
- const isNewWindow = oldestExecution + this._options.window <= now;
63
- if (isNewWindow || this._executionTimes.length < this._options.limit) {
81
+ const isNewWindow = oldestExecution + this.getWindow() <= now;
82
+ if (isNewWindow || this._executionTimes.length < this.getLimit()) {
64
83
  this.executeFunction(...args);
65
84
  return true;
66
85
  }
@@ -70,7 +89,7 @@ class RateLimiter {
70
89
  }
71
90
  executeFunction(...args) {
72
91
  var _a, _b;
73
- if (!this._options.enabled) return;
92
+ if (!this.getEnabled()) return;
74
93
  const now = Date.now();
75
94
  this._executionCount++;
76
95
  this._executionTimes.push(now);
@@ -85,7 +104,7 @@ class RateLimiter {
85
104
  }
86
105
  cleanupOldExecutions() {
87
106
  const now = Date.now();
88
- const windowStart = now - this._options.window;
107
+ const windowStart = now - this.getWindow();
89
108
  this._executionTimes = this._executionTimes.filter(
90
109
  (time) => time > windowStart
91
110
  );
@@ -107,7 +126,7 @@ class RateLimiter {
107
126
  */
108
127
  getRemainingInWindow() {
109
128
  this.cleanupOldExecutions();
110
- return Math.max(0, this._options.limit - this._executionTimes.length);
129
+ return Math.max(0, this.getLimit() - this._executionTimes.length);
111
130
  }
112
131
  /**
113
132
  * Returns the number of milliseconds until the next execution will be possible
@@ -117,7 +136,7 @@ class RateLimiter {
117
136
  return 0;
118
137
  }
119
138
  const oldestExecution = Math.min(...this._executionTimes);
120
- return oldestExecution + this._options.window - Date.now();
139
+ return oldestExecution + this.getWindow() - Date.now();
121
140
  }
122
141
  /**
123
142
  * Resets the rate limiter state
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limiter.cjs","sources":["../../src/rate-limiter.ts"],"sourcesContent":["import type { AnyFunction } from './types'\n\n/**\n * Options for configuring a rate-limited function\n */\nexport interface RateLimiterOptions<TFn extends AnyFunction> {\n /**\n * Whether the rate limiter is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * Maximum number of executions allowed within the time window\n */\n limit: number\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (rateLimiter: RateLimiter<TFn>) => void\n /**\n * Optional callback function that is called when an execution is rejected due to rate limiting\n */\n onReject?: (rateLimiter: RateLimiter<TFn>) => void\n /**\n * Time window in milliseconds within which the limit applies\n */\n window: number\n /**\n * Type of window to use for rate limiting\n * - 'fixed': Uses a fixed window that resets after the window period\n * - 'sliding': Uses a sliding window that allows executions as old ones expire\n * Defaults to 'fixed'\n */\n windowType?: 'fixed' | 'sliding'\n}\n\nconst defaultOptions: Required<RateLimiterOptions<any>> = {\n enabled: true,\n limit: 1,\n onExecute: () => {},\n onReject: () => {},\n window: 0,\n windowType: 'fixed',\n}\n\n/**\n * A class that creates a rate-limited function.\n *\n * Rate limiting is a simple approach that allows a function to execute up to a limit within a time window,\n * then blocks all subsequent calls until the window passes. This can lead to \"bursty\" behavior where\n * all executions happen immediately, followed by a complete block.\n *\n * The rate limiter supports two types of windows:\n * - 'fixed': A strict window that resets after the window period. All executions within the window count\n * towards the limit, and the window resets completely after the period.\n * - 'sliding': A rolling window that allows executions as old ones expire. This provides a more\n * consistent rate of execution over time.\n *\n * For smoother execution patterns, consider using:\n * - Throttling: Ensures consistent spacing between executions (e.g. max once per 200ms)\n * - Debouncing: Waits for a pause in calls before executing (e.g. after 500ms of no calls)\n *\n * Rate limiting is best used for hard API limits or resource constraints. For UI updates or\n * smoothing out frequent events, throttling or debouncing usually provide better user experience.\n *\n * @example\n * ```ts\n * const rateLimiter = new RateLimiter(\n * (id: string) => api.getData(id),\n * { limit: 5, window: 1000, windowType: 'sliding' } // 5 calls per second with sliding window\n * );\n *\n * // Will execute immediately until limit reached, then block\n * rateLimiter.maybeExecute('123');\n * ```\n */\nexport class RateLimiter<TFn extends AnyFunction> {\n private _executionCount = 0\n private _rejectionCount = 0\n private _executionTimes: Array<number> = []\n private _options: RateLimiterOptions<TFn>\n\n constructor(\n private fn: TFn,\n initialOptions: RateLimiterOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the rate limiter options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<RateLimiterOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n }\n\n /**\n * Returns the current rate limiter options\n */\n getOptions(): Required<RateLimiterOptions<TFn>> {\n return this._options as Required<RateLimiterOptions<TFn>>\n }\n\n /**\n * Attempts to execute the rate-limited function if within the configured limits.\n * Will reject execution if the number of calls in the current window exceeds the limit.\n *\n * @example\n * ```ts\n * const rateLimiter = new RateLimiter(fn, { limit: 5, window: 1000 });\n *\n * // First 5 calls will return true\n * rateLimiter.maybeExecute('arg1', 'arg2'); // true\n *\n * // Additional calls within the window will return false\n * rateLimiter.maybeExecute('arg1', 'arg2'); // false\n * ```\n */\n maybeExecute(...args: Parameters<TFn>): boolean {\n this.cleanupOldExecutions()\n\n if (this._options.windowType === 'sliding') {\n // For sliding window, we can execute if we have capacity in the current window\n if (this._executionTimes.length < this._options.limit) {\n this.executeFunction(...args)\n return true\n }\n } else {\n // For fixed window, we need to check if we're in a new window\n const now = Date.now()\n const oldestExecution = Math.min(...this._executionTimes)\n const isNewWindow = oldestExecution + this._options.window <= now\n\n if (isNewWindow || this._executionTimes.length < this._options.limit) {\n this.executeFunction(...args)\n return true\n }\n }\n\n this.rejectFunction()\n return false\n }\n\n private executeFunction(...args: Parameters<TFn>): void {\n if (!this._options.enabled) return\n const now = Date.now()\n this._executionCount++\n this._executionTimes.push(now)\n this.fn(...args) // execute the function\n this._options.onExecute?.(this)\n }\n\n private rejectFunction(): void {\n this._rejectionCount++\n if (this._options.onReject) {\n this._options.onReject(this)\n }\n }\n\n private cleanupOldExecutions(): void {\n const now = Date.now()\n const windowStart = now - this._options.window\n this._executionTimes = this._executionTimes.filter(\n (time) => time > windowStart,\n )\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns the number of times the function has been rejected\n */\n getRejectionCount(): number {\n return this._rejectionCount\n }\n\n /**\n * Returns the number of remaining executions allowed in the current window\n */\n getRemainingInWindow(): number {\n this.cleanupOldExecutions()\n return Math.max(0, this._options.limit - this._executionTimes.length)\n }\n\n /**\n * Returns the number of milliseconds until the next execution will be possible\n */\n getMsUntilNextWindow(): number {\n if (this.getRemainingInWindow() > 0) {\n return 0\n }\n const oldestExecution = Math.min(...this._executionTimes)\n return oldestExecution + this._options.window - Date.now()\n }\n\n /**\n * Resets the rate limiter state\n */\n reset(): void {\n this._executionTimes = []\n this._executionCount = 0\n this._rejectionCount = 0\n }\n}\n\n/**\n * Creates a rate-limited function that will execute the provided function up to a maximum number of times within a time window.\n *\n * Note that rate limiting is a simpler form of execution control compared to throttling or debouncing:\n * - A rate limiter will allow all executions until the limit is reached, then block all subsequent calls until the window resets\n * - A throttler ensures even spacing between executions, which can be better for consistent performance\n * - A debouncer collapses multiple calls into one, which is better for handling bursts of events\n *\n * The rate limiter supports two types of windows:\n * - 'fixed': A strict window that resets after the window period. All executions within the window count\n * towards the limit, and the window resets completely after the period.\n * - 'sliding': A rolling window that allows executions as old ones expire. This provides a more\n * consistent rate of execution over time.\n *\n * Consider using throttle() or debounce() if you need more intelligent execution control. Use rate limiting when you specifically\n * need to enforce a hard limit on the number of executions within a time period.\n *\n * @example\n * ```ts\n * // Rate limit to 5 calls per minute with a sliding window\n * const rateLimited = rateLimit(makeApiCall, {\n * limit: 5,\n * window: 60000,\n * windowType: 'sliding',\n * onReject: (rateLimiter) => {\n * console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`);\n * }\n * });\n *\n * // First 5 calls will execute immediately\n * // Additional calls will be rejected until the minute window resets\n * rateLimited();\n *\n * // For more even execution, consider using throttle instead:\n * const throttled = throttle(makeApiCall, { wait: 12000 }); // One call every 12 seconds\n * ```\n */\nexport function rateLimit<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: Omit<RateLimiterOptions<TFn>, 'enabled'>,\n) {\n const rateLimiter = new RateLimiter(fn, initialOptions)\n return rateLimiter.maybeExecute.bind(rateLimiter)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate-limiter.cjs","sources":["../../src/rate-limiter.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyFunction } from './types'\n\n/**\n * Options for configuring a rate-limited function\n */\nexport interface RateLimiterOptions<TFn extends AnyFunction> {\n /**\n * Whether the rate limiter is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean | ((rateLimiter: RateLimiter<TFn>) => boolean)\n /**\n * Maximum number of executions allowed within the time window.\n * Can be a number or a callback function that receives the rate limiter instance and returns a number.\n */\n limit: number | ((rateLimiter: RateLimiter<TFn>) => number)\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (rateLimiter: RateLimiter<TFn>) => void\n /**\n * Optional callback function that is called when an execution is rejected due to rate limiting\n */\n onReject?: (rateLimiter: RateLimiter<TFn>) => void\n /**\n * Time window in milliseconds within which the limit applies.\n * Can be a number or a callback function that receives the rate limiter instance and returns a number.\n */\n window: number | ((rateLimiter: RateLimiter<TFn>) => number)\n /**\n * Type of window to use for rate limiting\n * - 'fixed': Uses a fixed window that resets after the window period\n * - 'sliding': Uses a sliding window that allows executions as old ones expire\n * Defaults to 'fixed'\n */\n windowType?: 'fixed' | 'sliding'\n}\n\nconst defaultOptions: Required<RateLimiterOptions<any>> = {\n enabled: true,\n limit: 1,\n onExecute: () => {},\n onReject: () => {},\n window: 0,\n windowType: 'fixed',\n}\n\n/**\n * A class that creates a rate-limited function.\n *\n * Rate limiting is a simple approach that allows a function to execute up to a limit within a time window,\n * then blocks all subsequent calls until the window passes. This can lead to \"bursty\" behavior where\n * all executions happen immediately, followed by a complete block.\n *\n * The rate limiter supports two types of windows:\n * - 'fixed': A strict window that resets after the window period. All executions within the window count\n * towards the limit, and the window resets completely after the period.\n * - 'sliding': A rolling window that allows executions as old ones expire. This provides a more\n * consistent rate of execution over time.\n *\n * For smoother execution patterns, consider using:\n * - Throttling: Ensures consistent spacing between executions (e.g. max once per 200ms)\n * - Debouncing: Waits for a pause in calls before executing (e.g. after 500ms of no calls)\n *\n * Rate limiting is best used for hard API limits or resource constraints. For UI updates or\n * smoothing out frequent events, throttling or debouncing usually provide better user experience.\n *\n * @example\n * ```ts\n * const rateLimiter = new RateLimiter(\n * (id: string) => api.getData(id),\n * { limit: 5, window: 1000, windowType: 'sliding' } // 5 calls per second with sliding window\n * );\n *\n * // Will execute immediately until limit reached, then block\n * rateLimiter.maybeExecute('123');\n * ```\n */\nexport class RateLimiter<TFn extends AnyFunction> {\n private _executionCount = 0\n private _rejectionCount = 0\n private _executionTimes: Array<number> = []\n private _options: RateLimiterOptions<TFn>\n\n constructor(\n private fn: TFn,\n initialOptions: RateLimiterOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the rate limiter options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<RateLimiterOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n }\n\n /**\n * Returns the current rate limiter options\n */\n getOptions(): Required<RateLimiterOptions<TFn>> {\n return this._options as Required<RateLimiterOptions<TFn>>\n }\n\n /**\n * Returns the current enabled state of the rate limiter\n */\n getEnabled(): boolean {\n return parseFunctionOrValue(this._options.enabled, this)!\n }\n\n /**\n * Returns the current limit of executions allowed within the time window\n */\n getLimit(): number {\n return parseFunctionOrValue(this._options.limit, this)\n }\n\n /**\n * Returns the current time window in milliseconds\n */\n getWindow(): number {\n return parseFunctionOrValue(this._options.window, this)\n }\n\n /**\n * Attempts to execute the rate-limited function if within the configured limits.\n * Will reject execution if the number of calls in the current window exceeds the limit.\n *\n * @example\n * ```ts\n * const rateLimiter = new RateLimiter(fn, { limit: 5, window: 1000 });\n *\n * // First 5 calls will return true\n * rateLimiter.maybeExecute('arg1', 'arg2'); // true\n *\n * // Additional calls within the window will return false\n * rateLimiter.maybeExecute('arg1', 'arg2'); // false\n * ```\n */\n maybeExecute(...args: Parameters<TFn>): boolean {\n this.cleanupOldExecutions()\n\n if (this._options.windowType === 'sliding') {\n // For sliding window, we can execute if we have capacity in the current window\n if (this._executionTimes.length < this.getLimit()) {\n this.executeFunction(...args)\n return true\n }\n } else {\n // For fixed window, we need to check if we're in a new window\n const now = Date.now()\n const oldestExecution = Math.min(...this._executionTimes)\n const isNewWindow = oldestExecution + this.getWindow() <= now\n\n if (isNewWindow || this._executionTimes.length < this.getLimit()) {\n this.executeFunction(...args)\n return true\n }\n }\n\n this.rejectFunction()\n return false\n }\n\n private executeFunction(...args: Parameters<TFn>): void {\n if (!this.getEnabled()) return\n const now = Date.now()\n this._executionCount++\n this._executionTimes.push(now)\n this.fn(...args) // execute the function\n this._options.onExecute?.(this)\n }\n\n private rejectFunction(): void {\n this._rejectionCount++\n if (this._options.onReject) {\n this._options.onReject(this)\n }\n }\n\n private cleanupOldExecutions(): void {\n const now = Date.now()\n const windowStart = now - this.getWindow()\n this._executionTimes = this._executionTimes.filter(\n (time) => time > windowStart,\n )\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns the number of times the function has been rejected\n */\n getRejectionCount(): number {\n return this._rejectionCount\n }\n\n /**\n * Returns the number of remaining executions allowed in the current window\n */\n getRemainingInWindow(): number {\n this.cleanupOldExecutions()\n return Math.max(0, this.getLimit() - this._executionTimes.length)\n }\n\n /**\n * Returns the number of milliseconds until the next execution will be possible\n */\n getMsUntilNextWindow(): number {\n if (this.getRemainingInWindow() > 0) {\n return 0\n }\n const oldestExecution = Math.min(...this._executionTimes)\n return oldestExecution + this.getWindow() - Date.now()\n }\n\n /**\n * Resets the rate limiter state\n */\n reset(): void {\n this._executionTimes = []\n this._executionCount = 0\n this._rejectionCount = 0\n }\n}\n\n/**\n * Creates a rate-limited function that will execute the provided function up to a maximum number of times within a time window.\n *\n * Note that rate limiting is a simpler form of execution control compared to throttling or debouncing:\n * - A rate limiter will allow all executions until the limit is reached, then block all subsequent calls until the window resets\n * - A throttler ensures even spacing between executions, which can be better for consistent performance\n * - A debouncer collapses multiple calls into one, which is better for handling bursts of events\n *\n * The rate limiter supports two types of windows:\n * - 'fixed': A strict window that resets after the window period. All executions within the window count\n * towards the limit, and the window resets completely after the period.\n * - 'sliding': A rolling window that allows executions as old ones expire. This provides a more\n * consistent rate of execution over time.\n *\n * Consider using throttle() or debounce() if you need more intelligent execution control. Use rate limiting when you specifically\n * need to enforce a hard limit on the number of executions within a time period.\n *\n * @example\n * ```ts\n * // Rate limit to 5 calls per minute with a sliding window\n * const rateLimited = rateLimit(makeApiCall, {\n * limit: 5,\n * window: 60000,\n * windowType: 'sliding',\n * onReject: (rateLimiter) => {\n * console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`);\n * }\n * });\n *\n * // First 5 calls will execute immediately\n * // Additional calls will be rejected until the minute window resets\n * rateLimited();\n *\n * // For more even execution, consider using throttle instead:\n * const throttled = throttle(makeApiCall, { wait: 12000 }); // One call every 12 seconds\n * ```\n */\nexport function rateLimit<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: RateLimiterOptions<TFn>,\n) {\n const rateLimiter = new RateLimiter(fn, initialOptions)\n return rateLimiter.maybeExecute.bind(rateLimiter)\n}\n"],"names":["parseFunctionOrValue"],"mappings":";;;AAuCA,MAAM,iBAAoD;AAAA,EACxD,SAAS;AAAA,EACT,OAAO;AAAA,EACP,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,QAAQ;AAAA,EACR,YAAY;AACd;AAiCO,MAAM,YAAqC;AAAA,EAMhD,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AANV,SAAQ,kBAAkB;AAC1B,SAAQ,kBAAkB;AAC1B,SAAQ,kBAAiC,CAAC;AAOxC,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WAAW,YAAoD;AAC7D,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU,GAAG,WAAW;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMpD,aAAgD;AAC9C,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,aAAsB;AACpB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,WAAmB;AACjB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,OAAO,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,YAAoB;AAClB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,QAAQ,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBxD,gBAAgB,MAAgC;AAC9C,SAAK,qBAAqB;AAEtB,QAAA,KAAK,SAAS,eAAe,WAAW;AAE1C,UAAI,KAAK,gBAAgB,SAAS,KAAK,YAAY;AAC5C,aAAA,gBAAgB,GAAG,IAAI;AACrB,eAAA;AAAA,MAAA;AAAA,IACT,OACK;AAEC,YAAA,MAAM,KAAK,IAAI;AACrB,YAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,eAAe;AACxD,YAAM,cAAc,kBAAkB,KAAK,UAAe,KAAA;AAE1D,UAAI,eAAe,KAAK,gBAAgB,SAAS,KAAK,YAAY;AAC3D,aAAA,gBAAgB,GAAG,IAAI;AACrB,eAAA;AAAA,MAAA;AAAA,IACT;AAGF,SAAK,eAAe;AACb,WAAA;AAAA,EAAA;AAAA,EAGD,mBAAmB,MAA6B;;AAClD,QAAA,CAAC,KAAK,aAAc;AAClB,UAAA,MAAM,KAAK,IAAI;AAChB,SAAA;AACA,SAAA,gBAAgB,KAAK,GAAG;AACxB,SAAA,GAAG,GAAG,IAAI;AACV,qBAAA,UAAS,cAAT,4BAAqB;AAAA,EAAI;AAAA,EAGxB,iBAAuB;AACxB,SAAA;AACD,QAAA,KAAK,SAAS,UAAU;AACrB,WAAA,SAAS,SAAS,IAAI;AAAA,IAAA;AAAA,EAC7B;AAAA,EAGM,uBAA6B;AAC7B,UAAA,MAAM,KAAK,IAAI;AACf,UAAA,cAAc,MAAM,KAAK,UAAU;AACpC,SAAA,kBAAkB,KAAK,gBAAgB;AAAA,MAC1C,CAAC,SAAS,OAAO;AAAA,IACnB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,uBAA+B;AAC7B,SAAK,qBAAqB;AACnB,WAAA,KAAK,IAAI,GAAG,KAAK,aAAa,KAAK,gBAAgB,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMlE,uBAA+B;AACzB,QAAA,KAAK,qBAAqB,IAAI,GAAG;AAC5B,aAAA;AAAA,IAAA;AAET,UAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,eAAe;AACxD,WAAO,kBAAkB,KAAK,UAAU,IAAI,KAAK,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMvD,QAAc;AACZ,SAAK,kBAAkB,CAAC;AACxB,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AAAA,EAAA;AAE3B;AAuCgB,SAAA,UACd,IACA,gBACA;AACA,QAAM,cAAc,IAAI,YAAY,IAAI,cAAc;AAC/C,SAAA,YAAY,aAAa,KAAK,WAAW;AAClD;;;"}
@@ -7,11 +7,12 @@ export interface RateLimiterOptions<TFn extends AnyFunction> {
7
7
  * Whether the rate limiter is enabled. When disabled, maybeExecute will not trigger any executions.
8
8
  * Defaults to true.
9
9
  */
10
- enabled?: boolean;
10
+ enabled?: boolean | ((rateLimiter: RateLimiter<TFn>) => boolean);
11
11
  /**
12
- * Maximum number of executions allowed within the time window
12
+ * Maximum number of executions allowed within the time window.
13
+ * Can be a number or a callback function that receives the rate limiter instance and returns a number.
13
14
  */
14
- limit: number;
15
+ limit: number | ((rateLimiter: RateLimiter<TFn>) => number);
15
16
  /**
16
17
  * Callback function that is called after the function is executed
17
18
  */
@@ -21,9 +22,10 @@ export interface RateLimiterOptions<TFn extends AnyFunction> {
21
22
  */
22
23
  onReject?: (rateLimiter: RateLimiter<TFn>) => void;
23
24
  /**
24
- * Time window in milliseconds within which the limit applies
25
+ * Time window in milliseconds within which the limit applies.
26
+ * Can be a number or a callback function that receives the rate limiter instance and returns a number.
25
27
  */
26
- window: number;
28
+ window: number | ((rateLimiter: RateLimiter<TFn>) => number);
27
29
  /**
28
30
  * Type of window to use for rate limiting
29
31
  * - 'fixed': Uses a fixed window that resets after the window period
@@ -79,6 +81,18 @@ export declare class RateLimiter<TFn extends AnyFunction> {
79
81
  * Returns the current rate limiter options
80
82
  */
81
83
  getOptions(): Required<RateLimiterOptions<TFn>>;
84
+ /**
85
+ * Returns the current enabled state of the rate limiter
86
+ */
87
+ getEnabled(): boolean;
88
+ /**
89
+ * Returns the current limit of executions allowed within the time window
90
+ */
91
+ getLimit(): number;
92
+ /**
93
+ * Returns the current time window in milliseconds
94
+ */
95
+ getWindow(): number;
82
96
  /**
83
97
  * Attempts to execute the rate-limited function if within the configured limits.
84
98
  * Will reject execution if the number of calls in the current window exceeds the limit.
@@ -156,4 +170,4 @@ export declare class RateLimiter<TFn extends AnyFunction> {
156
170
  * const throttled = throttle(makeApiCall, { wait: 12000 }); // One call every 12 seconds
157
171
  * ```
158
172
  */
159
- export declare function rateLimit<TFn extends AnyFunction>(fn: TFn, initialOptions: Omit<RateLimiterOptions<TFn>, 'enabled'>): (...args: Parameters<TFn>) => boolean;
173
+ export declare function rateLimit<TFn extends AnyFunction>(fn: TFn, initialOptions: RateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const utils = require("./utils.cjs");
3
4
  const defaultOptions = {
4
5
  enabled: true,
5
6
  leading: true,
@@ -34,6 +35,18 @@ class Throttler {
34
35
  getOptions() {
35
36
  return this._options;
36
37
  }
38
+ /**
39
+ * Returns the current enabled state of the throttler
40
+ */
41
+ getEnabled() {
42
+ return utils.parseFunctionOrValue(this._options.enabled, this);
43
+ }
44
+ /**
45
+ * Returns the current wait time in milliseconds
46
+ */
47
+ getWait() {
48
+ return utils.parseFunctionOrValue(this._options.wait, this);
49
+ }
37
50
  /**
38
51
  * Attempts to execute the throttled function. The execution behavior depends on the throttler options:
39
52
  *
@@ -59,13 +72,14 @@ class Throttler {
59
72
  maybeExecute(...args) {
60
73
  const now = Date.now();
61
74
  const timeSinceLastExecution = now - this._lastExecutionTime;
62
- if (this._options.leading && timeSinceLastExecution >= this._options.wait) {
75
+ const wait = this.getWait();
76
+ if (this._options.leading && timeSinceLastExecution >= wait) {
63
77
  this.executeFunction(...args);
64
78
  } else {
65
79
  this._lastArgs = args;
66
80
  if (!this._timeoutId && this._options.trailing) {
67
81
  const _timeSinceLastExecution = this._lastExecutionTime ? now - this._lastExecutionTime : 0;
68
- const timeoutDuration = this._options.wait - _timeSinceLastExecution;
82
+ const timeoutDuration = wait - _timeSinceLastExecution;
69
83
  this._timeoutId = setTimeout(() => {
70
84
  if (this._lastArgs !== void 0) {
71
85
  this.executeFunction(...this._lastArgs);
@@ -75,7 +89,7 @@ class Throttler {
75
89
  }
76
90
  }
77
91
  executeFunction(...args) {
78
- if (!this._options.enabled) return;
92
+ if (!this.getEnabled()) return;
79
93
  this.fn(...args);
80
94
  this._executionCount++;
81
95
  this._lastExecutionTime = Date.now();
@@ -109,7 +123,7 @@ class Throttler {
109
123
  * Returns the next execution time
110
124
  */
111
125
  getNextExecutionTime() {
112
- return this._lastExecutionTime + this._options.wait;
126
+ return this._lastExecutionTime + this.getWait();
113
127
  }
114
128
  /**
115
129
  * Returns the number of times the function has been executed
@@ -121,7 +135,7 @@ class Throttler {
121
135
  * Returns `true` if there is a pending execution
122
136
  */
123
137
  getIsPending() {
124
- return this._options.enabled && !!this._timeoutId;
138
+ return this.getEnabled() && !!this._timeoutId;
125
139
  }
126
140
  }
127
141
  function throttle(fn, initialOptions) {
@@ -1 +1 @@
1
- {"version":3,"file":"throttler.cjs","sources":["../../src/throttler.ts"],"sourcesContent":["import type { AnyFunction } from './types'\n\n/**\n * Options for configuring a throttled function\n */\nexport interface ThrottlerOptions<TFn extends AnyFunction> {\n /**\n * Whether the throttler is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to true.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (throttler: Throttler<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Time window in milliseconds during which the function can only be executed once\n */\n wait: number\n}\n\nconst defaultOptions: Required<ThrottlerOptions<any>> = {\n enabled: true,\n leading: true,\n onExecute: () => {},\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates a throttled function.\n *\n * Throttling ensures a function is called at most once within a specified time window.\n * Unlike debouncing which waits for a pause in calls, throttling guarantees consistent\n * execution timing regardless of call frequency.\n *\n * Supports both leading and trailing edge execution:\n * - Leading: Execute immediately on first call (default: true)\n * - Trailing: Execute after wait period if called during throttle (default: true)\n *\n * For collapsing rapid-fire events where you only care about the last call, consider using Debouncer.\n *\n * @example\n * ```ts\n * const throttler = new Throttler(\n * (id: string) => api.getData(id),\n * { wait: 1000 } // Execute at most once per second\n * );\n *\n * // First call executes immediately\n * throttler.maybeExecute('123');\n *\n * // Subsequent calls within 1000ms are throttled\n * throttler.maybeExecute('123'); // Throttled\n * ```\n */\nexport class Throttler<TFn extends AnyFunction> {\n private _executionCount = 0\n private _lastArgs: Parameters<TFn> | undefined\n private _lastExecutionTime = 0\n private _options: Required<ThrottlerOptions<TFn>>\n private _timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: ThrottlerOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the throttler options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<ThrottlerOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this.cancel()\n }\n }\n\n /**\n * Returns the current throttler options\n */\n getOptions(): Required<ThrottlerOptions<TFn>> {\n return this._options\n }\n\n /**\n * Attempts to execute the throttled function. The execution behavior depends on the throttler options:\n *\n * - If enough time has passed since the last execution (>= wait period):\n * - With leading=true: Executes immediately\n * - With leading=false: Waits for the next trailing execution\n *\n * - If within the wait period:\n * - With trailing=true: Schedules execution for end of wait period\n * - With trailing=false: Drops the execution\n *\n * @example\n * ```ts\n * const throttled = new Throttler(fn, { wait: 1000 });\n *\n * // First call executes immediately\n * throttled.maybeExecute('a', 'b');\n *\n * // Call during wait period - gets throttled\n * throttled.maybeExecute('c', 'd');\n * ```\n */\n maybeExecute(...args: Parameters<TFn>): void {\n const now = Date.now()\n const timeSinceLastExecution = now - this._lastExecutionTime\n\n // Handle leading execution\n if (this._options.leading && timeSinceLastExecution >= this._options.wait) {\n this.executeFunction(...args)\n } else {\n // Store the most recent arguments for potential trailing execution\n this._lastArgs = args\n\n // Set up trailing execution if not already scheduled\n if (!this._timeoutId && this._options.trailing) {\n const _timeSinceLastExecution = this._lastExecutionTime\n ? now - this._lastExecutionTime\n : 0\n const timeoutDuration = this._options.wait - _timeSinceLastExecution\n this._timeoutId = setTimeout(() => {\n if (this._lastArgs !== undefined) {\n this.executeFunction(...this._lastArgs)\n }\n }, timeoutDuration)\n }\n }\n }\n\n private executeFunction(...args: Parameters<TFn>): void {\n if (!this._options.enabled) return\n this.fn(...args) // EXECUTE!\n this._executionCount++\n this._lastExecutionTime = Date.now()\n this._timeoutId = undefined\n this._lastArgs = undefined\n this._options.onExecute(this)\n }\n\n /**\n * Cancels any pending trailing execution and clears internal state.\n *\n * If a trailing execution is scheduled (due to throttling with trailing=true),\n * this will prevent that execution from occurring. The internal timeout and\n * stored arguments will be cleared.\n *\n * Has no effect if there is no pending execution.\n */\n cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._timeoutId = undefined\n this._lastArgs = undefined\n }\n }\n\n /**\n * Returns the last execution time\n */\n getLastExecutionTime(): number {\n return this._lastExecutionTime\n }\n\n /**\n * Returns the next execution time\n */\n getNextExecutionTime(): number {\n return this._lastExecutionTime + this._options.wait\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns `true` if there is a pending execution\n */\n getIsPending(): boolean {\n return this._options.enabled && !!this._timeoutId\n }\n}\n\n/**\n * Creates a throttled function that limits how often the provided function can execute.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * The throttled function can be configured to execute on the leading and/or trailing\n * edge of the throttle window via options.\n *\n * For handling bursts of events, consider using debounce() instead. For hard execution\n * limits, consider using rateLimit().\n *\n * @example\n * ```ts\n * // Basic throttling - max once per second\n * const throttled = throttle(updateUI, { wait: 1000 });\n *\n * // Configure leading/trailing execution\n * const throttled = throttle(saveData, {\n * wait: 2000,\n * leading: true, // Execute immediately on first call\n * trailing: true // Execute again after delay if called during wait\n * });\n * ```\n */\nexport function throttle<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: Omit<ThrottlerOptions<TFn>, 'enabled'>,\n) {\n const throttler = new Throttler(fn, initialOptions)\n return throttler.maybeExecute.bind(throttler)\n}\n"],"names":[],"mappings":";;AA+BA,MAAM,iBAAkD;AAAA,EACtD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,UAAU;AAAA,EACV,MAAM;AACR;AA6BO,MAAM,UAAmC;AAAA,EAO9C,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AAPV,SAAQ,kBAAkB;AAE1B,SAAQ,qBAAqB;AAQ3B,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WAAW,YAAkD;AAC3D,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU,GAAG,WAAW;AAG9C,QAAA,CAAC,KAAK,SAAS,SAAS;AAC1B,WAAK,OAAO;AAAA,IAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAMF,aAA8C;AAC5C,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBd,gBAAgB,MAA6B;AACrC,UAAA,MAAM,KAAK,IAAI;AACf,UAAA,yBAAyB,MAAM,KAAK;AAG1C,QAAI,KAAK,SAAS,WAAW,0BAA0B,KAAK,SAAS,MAAM;AACpE,WAAA,gBAAgB,GAAG,IAAI;AAAA,IAAA,OACvB;AAEL,WAAK,YAAY;AAGjB,UAAI,CAAC,KAAK,cAAc,KAAK,SAAS,UAAU;AAC9C,cAAM,0BAA0B,KAAK,qBACjC,MAAM,KAAK,qBACX;AACE,cAAA,kBAAkB,KAAK,SAAS,OAAO;AACxC,aAAA,aAAa,WAAW,MAAM;AAC7B,cAAA,KAAK,cAAc,QAAW;AAC3B,iBAAA,gBAAgB,GAAG,KAAK,SAAS;AAAA,UAAA;AAAA,WAEvC,eAAe;AAAA,MAAA;AAAA,IACpB;AAAA,EACF;AAAA,EAGM,mBAAmB,MAA6B;AAClD,QAAA,CAAC,KAAK,SAAS,QAAS;AACvB,SAAA,GAAG,GAAG,IAAI;AACV,SAAA;AACA,SAAA,qBAAqB,KAAK,IAAI;AACnC,SAAK,aAAa;AAClB,SAAK,YAAY;AACZ,SAAA,SAAS,UAAU,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY9B,SAAe;AACb,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAClB,WAAK,YAAY;AAAA,IAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAMF,uBAA+B;AAC7B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,uBAA+B;AACtB,WAAA,KAAK,qBAAqB,KAAK,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMjD,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,eAAwB;AACtB,WAAO,KAAK,SAAS,WAAW,CAAC,CAAC,KAAK;AAAA,EAAA;AAE3C;AA4BgB,SAAA,SACd,IACA,gBACA;AACA,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;;;"}
1
+ {"version":3,"file":"throttler.cjs","sources":["../../src/throttler.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyFunction } from './types'\n\n/**\n * Options for configuring a throttled function\n */\nexport interface ThrottlerOptions<TFn extends AnyFunction> {\n /**\n * Whether the throttler 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 | ((throttler: Throttler<TFn>) => boolean)\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to true.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (throttler: Throttler<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Time window in milliseconds during which the function can only be executed once.\n * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((throttler: Throttler<TFn>) => number)\n}\n\nconst defaultOptions: Required<ThrottlerOptions<any>> = {\n enabled: true,\n leading: true,\n onExecute: () => {},\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates a throttled function.\n *\n * Throttling ensures a function is called at most once within a specified time window.\n * Unlike debouncing which waits for a pause in calls, throttling guarantees consistent\n * execution timing regardless of call frequency.\n *\n * Supports both leading and trailing edge execution:\n * - Leading: Execute immediately on first call (default: true)\n * - Trailing: Execute after wait period if called during throttle (default: true)\n *\n * For collapsing rapid-fire events where you only care about the last call, consider using Debouncer.\n *\n * @example\n * ```ts\n * const throttler = new Throttler(\n * (id: string) => api.getData(id),\n * { wait: 1000 } // Execute at most once per second\n * );\n *\n * // First call executes immediately\n * throttler.maybeExecute('123');\n *\n * // Subsequent calls within 1000ms are throttled\n * throttler.maybeExecute('123'); // Throttled\n * ```\n */\nexport class Throttler<TFn extends AnyFunction> {\n private _executionCount = 0\n private _lastArgs: Parameters<TFn> | undefined\n private _lastExecutionTime = 0\n private _options: Required<ThrottlerOptions<TFn>>\n private _timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: ThrottlerOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the throttler options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<ThrottlerOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this.cancel()\n }\n }\n\n /**\n * Returns the current throttler options\n */\n getOptions(): Required<ThrottlerOptions<TFn>> {\n return this._options\n }\n\n /**\n * Returns the current enabled state of the throttler\n */\n getEnabled(): boolean {\n return parseFunctionOrValue(this._options.enabled, this)\n }\n\n /**\n * Returns the current wait time in milliseconds\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Attempts to execute the throttled function. The execution behavior depends on the throttler options:\n *\n * - If enough time has passed since the last execution (>= wait period):\n * - With leading=true: Executes immediately\n * - With leading=false: Waits for the next trailing execution\n *\n * - If within the wait period:\n * - With trailing=true: Schedules execution for end of wait period\n * - With trailing=false: Drops the execution\n *\n * @example\n * ```ts\n * const throttled = new Throttler(fn, { wait: 1000 });\n *\n * // First call executes immediately\n * throttled.maybeExecute('a', 'b');\n *\n * // Call during wait period - gets throttled\n * throttled.maybeExecute('c', 'd');\n * ```\n */\n maybeExecute(...args: Parameters<TFn>): void {\n const now = Date.now()\n const timeSinceLastExecution = now - this._lastExecutionTime\n const wait = this.getWait()\n\n // Handle leading execution\n if (this._options.leading && timeSinceLastExecution >= wait) {\n this.executeFunction(...args)\n } else {\n // Store the most recent arguments for potential trailing execution\n this._lastArgs = args\n\n // Set up trailing execution if not already scheduled\n if (!this._timeoutId && this._options.trailing) {\n const _timeSinceLastExecution = this._lastExecutionTime\n ? now - this._lastExecutionTime\n : 0\n const timeoutDuration = wait - _timeSinceLastExecution\n this._timeoutId = setTimeout(() => {\n if (this._lastArgs !== undefined) {\n this.executeFunction(...this._lastArgs)\n }\n }, timeoutDuration)\n }\n }\n }\n\n private executeFunction(...args: Parameters<TFn>): void {\n if (!this.getEnabled()) return\n this.fn(...args) // EXECUTE!\n this._executionCount++\n this._lastExecutionTime = Date.now()\n this._timeoutId = undefined\n this._lastArgs = undefined\n this._options.onExecute(this)\n }\n\n /**\n * Cancels any pending trailing execution and clears internal state.\n *\n * If a trailing execution is scheduled (due to throttling with trailing=true),\n * this will prevent that execution from occurring. The internal timeout and\n * stored arguments will be cleared.\n *\n * Has no effect if there is no pending execution.\n */\n cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._timeoutId = undefined\n this._lastArgs = undefined\n }\n }\n\n /**\n * Returns the last execution time\n */\n getLastExecutionTime(): number {\n return this._lastExecutionTime\n }\n\n /**\n * Returns the next execution time\n */\n getNextExecutionTime(): number {\n return this._lastExecutionTime + this.getWait()\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns `true` if there is a pending execution\n */\n getIsPending(): boolean {\n return this.getEnabled() && !!this._timeoutId\n }\n}\n\n/**\n * Creates a throttled function that limits how often the provided function can execute.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * The throttled function can be configured to execute on the leading and/or trailing\n * edge of the throttle window via options.\n *\n * For handling bursts of events, consider using debounce() instead. For hard execution\n * limits, consider using rateLimit().\n *\n * @example\n * ```ts\n * // Basic throttling - max once per second\n * const throttled = throttle(updateUI, { wait: 1000 });\n *\n * // Configure leading/trailing execution\n * const throttled = throttle(saveData, {\n * wait: 2000,\n * leading: true, // Execute immediately on first call\n * trailing: true // Execute again after delay if called during wait\n * });\n * ```\n */\nexport function throttle<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: ThrottlerOptions<TFn>,\n) {\n const throttler = new Throttler(fn, initialOptions)\n return throttler.maybeExecute.bind(throttler)\n}\n"],"names":["parseFunctionOrValue"],"mappings":";;;AAmCA,MAAM,iBAAkD;AAAA,EACtD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,UAAU;AAAA,EACV,MAAM;AACR;AA6BO,MAAM,UAAmC;AAAA,EAO9C,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AAPV,SAAQ,kBAAkB;AAE1B,SAAQ,qBAAqB;AAQ3B,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WAAW,YAAkD;AAC3D,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU,GAAG,WAAW;AAG9C,QAAA,CAAC,KAAK,SAAS,SAAS;AAC1B,WAAK,OAAO;AAAA,IAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAMF,aAA8C;AAC5C,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,aAAsB;AACpB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,UAAkB;AAChB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,MAAM,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBtD,gBAAgB,MAA6B;AACrC,UAAA,MAAM,KAAK,IAAI;AACf,UAAA,yBAAyB,MAAM,KAAK;AACpC,UAAA,OAAO,KAAK,QAAQ;AAG1B,QAAI,KAAK,SAAS,WAAW,0BAA0B,MAAM;AACtD,WAAA,gBAAgB,GAAG,IAAI;AAAA,IAAA,OACvB;AAEL,WAAK,YAAY;AAGjB,UAAI,CAAC,KAAK,cAAc,KAAK,SAAS,UAAU;AAC9C,cAAM,0BAA0B,KAAK,qBACjC,MAAM,KAAK,qBACX;AACJ,cAAM,kBAAkB,OAAO;AAC1B,aAAA,aAAa,WAAW,MAAM;AAC7B,cAAA,KAAK,cAAc,QAAW;AAC3B,iBAAA,gBAAgB,GAAG,KAAK,SAAS;AAAA,UAAA;AAAA,WAEvC,eAAe;AAAA,MAAA;AAAA,IACpB;AAAA,EACF;AAAA,EAGM,mBAAmB,MAA6B;AAClD,QAAA,CAAC,KAAK,aAAc;AACnB,SAAA,GAAG,GAAG,IAAI;AACV,SAAA;AACA,SAAA,qBAAqB,KAAK,IAAI;AACnC,SAAK,aAAa;AAClB,SAAK,YAAY;AACZ,SAAA,SAAS,UAAU,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY9B,SAAe;AACb,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAClB,WAAK,YAAY;AAAA,IAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAMF,uBAA+B;AAC7B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,uBAA+B;AACtB,WAAA,KAAK,qBAAqB,KAAK,QAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMhD,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,eAAwB;AACtB,WAAO,KAAK,WAAA,KAAgB,CAAC,CAAC,KAAK;AAAA,EAAA;AAEvC;AA4BgB,SAAA,SACd,IACA,gBACA;AACA,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;;;"}
@@ -5,9 +5,10 @@ import { AnyFunction } from './types.cjs';
5
5
  export interface ThrottlerOptions<TFn extends AnyFunction> {
6
6
  /**
7
7
  * Whether the throttler is enabled. When disabled, maybeExecute will not trigger any executions.
8
+ * Can be a boolean or a function that returns a boolean.
8
9
  * Defaults to true.
9
10
  */
10
- enabled?: boolean;
11
+ enabled?: boolean | ((throttler: Throttler<TFn>) => boolean);
11
12
  /**
12
13
  * Whether to execute on the leading edge of the timeout.
13
14
  * Defaults to true.
@@ -23,9 +24,11 @@ export interface ThrottlerOptions<TFn extends AnyFunction> {
23
24
  */
24
25
  trailing?: boolean;
25
26
  /**
26
- * Time window in milliseconds during which the function can only be executed once
27
+ * Time window in milliseconds during which the function can only be executed once.
28
+ * Can be a number or a function that returns a number.
29
+ * Defaults to 0ms
27
30
  */
28
- wait: number;
31
+ wait: number | ((throttler: Throttler<TFn>) => number);
29
32
  }
30
33
  /**
31
34
  * A class that creates a throttled function.
@@ -71,6 +74,14 @@ export declare class Throttler<TFn extends AnyFunction> {
71
74
  * Returns the current throttler options
72
75
  */
73
76
  getOptions(): Required<ThrottlerOptions<TFn>>;
77
+ /**
78
+ * Returns the current enabled state of the throttler
79
+ */
80
+ getEnabled(): boolean;
81
+ /**
82
+ * Returns the current wait time in milliseconds
83
+ */
84
+ getWait(): number;
74
85
  /**
75
86
  * Attempts to execute the throttled function. The execution behavior depends on the throttler options:
76
87
  *
@@ -148,4 +159,4 @@ export declare class Throttler<TFn extends AnyFunction> {
148
159
  * });
149
160
  * ```
150
161
  */
151
- export declare function throttle<TFn extends AnyFunction>(fn: TFn, initialOptions: Omit<ThrottlerOptions<TFn>, 'enabled'>): (...args: Parameters<TFn>) => void;
162
+ export declare function throttle<TFn extends AnyFunction>(fn: TFn, initialOptions: ThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
@@ -6,3 +6,4 @@ export type AnyFunction = (...args: Array<any>) => any;
6
6
  * Represents an asynchronous function that can be called with any arguments and returns a promise.
7
7
  */
8
8
  export type AnyAsyncFunction = (...args: Array<any>) => Promise<any>;
9
+ export type OptionalKeys<T, TKey extends keyof T> = Omit<T, TKey> & Partial<Pick<T, TKey>>;
@@ -1,13 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function isFunction(value) {
4
+ return typeof value === "function";
5
+ }
6
+ function parseFunctionOrValue(value, ...args) {
7
+ return isFunction(value) ? value(...args) : value;
8
+ }
3
9
  function bindInstanceMethods(instance) {
4
- return Object.getOwnPropertyNames(Object.getPrototypeOf(instance)).filter((key) => typeof instance[key] === "function").reduce((acc, key) => {
5
- const method = instance[key];
6
- if (typeof method === "function") {
7
- acc[key] = method.bind(instance);
8
- }
9
- return acc;
10
- }, {});
10
+ return Object.getOwnPropertyNames(Object.getPrototypeOf(instance)).reduce(
11
+ (acc, key) => {
12
+ const method = instance[key];
13
+ if (isFunction(method)) {
14
+ acc[key] = method.bind(instance);
15
+ }
16
+ return acc;
17
+ },
18
+ instance
19
+ );
11
20
  }
12
21
  exports.bindInstanceMethods = bindInstanceMethods;
22
+ exports.isFunction = isFunction;
23
+ exports.parseFunctionOrValue = parseFunctionOrValue;
13
24
  //# sourceMappingURL=utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["export function bindInstanceMethods<T extends Record<string, any>>(\n instance: T,\n): T {\n return Object.getOwnPropertyNames(Object.getPrototypeOf(instance))\n .filter((key) => typeof instance[key as keyof T] === 'function')\n .reduce((acc: any, key) => {\n const method = instance[key as keyof T]\n if (typeof method === 'function') {\n acc[key] = method.bind(instance)\n }\n return acc\n }, {} as T)\n}\n"],"names":[],"mappings":";;AAAO,SAAS,oBACd,UACG;AACH,SAAO,OAAO,oBAAoB,OAAO,eAAe,QAAQ,CAAC,EAC9D,OAAO,CAAC,QAAQ,OAAO,SAAS,GAAc,MAAM,UAAU,EAC9D,OAAO,CAAC,KAAU,QAAQ;AACnB,UAAA,SAAS,SAAS,GAAc;AAClC,QAAA,OAAO,WAAW,YAAY;AAChC,UAAI,GAAG,IAAI,OAAO,KAAK,QAAQ;AAAA,IAAA;AAE1B,WAAA;AAAA,EACT,GAAG,EAAO;AACd;;"}
1
+ {"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["import type { AnyFunction } from './types'\n\nexport function isFunction<T extends AnyFunction>(value: any): value is T {\n return typeof value === 'function'\n}\n\nexport function parseFunctionOrValue<T, TArgs extends Array<any>>(\n value: T | ((...args: TArgs) => T),\n ...args: TArgs\n): T {\n return isFunction(value) ? value(...args) : value\n}\n\nexport function bindInstanceMethods<T extends Record<string, any>>(\n instance: T,\n): T {\n return Object.getOwnPropertyNames(Object.getPrototypeOf(instance)).reduce(\n (acc: any, key) => {\n const method = instance[key as keyof T]\n if (isFunction(method)) {\n acc[key] = method.bind(instance)\n }\n return acc\n },\n instance,\n )\n}\n"],"names":[],"mappings":";;AAEO,SAAS,WAAkC,OAAwB;AACxE,SAAO,OAAO,UAAU;AAC1B;AAEgB,SAAA,qBACd,UACG,MACA;AACH,SAAO,WAAW,KAAK,IAAI,MAAM,GAAG,IAAI,IAAI;AAC9C;AAEO,SAAS,oBACd,UACG;AACH,SAAO,OAAO,oBAAoB,OAAO,eAAe,QAAQ,CAAC,EAAE;AAAA,IACjE,CAAC,KAAU,QAAQ;AACX,YAAA,SAAS,SAAS,GAAc;AAClC,UAAA,WAAW,MAAM,GAAG;AACtB,YAAI,GAAG,IAAI,OAAO,KAAK,QAAQ;AAAA,MAAA;AAE1B,aAAA;AAAA,IACT;AAAA,IACA;AAAA,EACF;AACF;;;;"}
@@ -1 +1,4 @@
1
+ import { AnyFunction } from './types.cjs';
2
+ export declare function isFunction<T extends AnyFunction>(value: any): value is T;
3
+ export declare function parseFunctionOrValue<T, TArgs extends Array<any>>(value: T | ((...args: TArgs) => T), ...args: TArgs): T;
1
4
  export declare function bindInstanceMethods<T extends Record<string, any>>(instance: T): T;
@@ -5,16 +5,19 @@ import { AnyAsyncFunction } from './types.js';
5
5
  export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
6
6
  /**
7
7
  * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.
8
+ * Can be a boolean or a function that returns a boolean.
8
9
  * Defaults to true.
9
10
  */
10
- enabled?: boolean;
11
+ enabled?: boolean | ((debouncer: AsyncDebouncer<TFn>) => boolean);
11
12
  /**
12
13
  * Whether to execute on the leading edge of the timeout.
13
14
  * Defaults to false.
14
15
  */
15
16
  leading?: boolean;
16
17
  /**
17
- * Optional error handler for when the debounced function throws
18
+ * Optional error handler for when the debounced function throws.
19
+ * If provided, the handler will be called with the error and debouncer instance.
20
+ * This can be used alongside throwOnError - the handler will be called before any error is thrown.
18
21
  */
19
22
  onError?: (error: unknown, debouncer: AsyncDebouncer<TFn>) => void;
20
23
  /**
@@ -25,16 +28,23 @@ export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
25
28
  * Optional callback to call when the debounced function is executed
26
29
  */
27
30
  onSuccess?: (result: ReturnType<TFn>, debouncer: AsyncDebouncer<TFn>) => void;
31
+ /**
32
+ * Whether to throw errors when they occur.
33
+ * Defaults to true if no onError handler is provided, false if an onError handler is provided.
34
+ * Can be explicitly set to override these defaults.
35
+ */
36
+ throwOnError?: boolean;
28
37
  /**
29
38
  * Whether to execute on the trailing edge of the timeout.
30
39
  * Defaults to true.
31
40
  */
32
41
  trailing?: boolean;
33
42
  /**
34
- * Delay in milliseconds to wait after the last call before executing
43
+ * Delay in milliseconds to wait after the last call before executing.
44
+ * Can be a number or a function that returns a number.
35
45
  * Defaults to 0ms
36
46
  */
37
- wait: number;
47
+ wait: number | ((debouncer: AsyncDebouncer<TFn>) => number);
38
48
  }
39
49
  /**
40
50
  * A class that creates an async debounced function.
@@ -50,12 +60,23 @@ export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
50
60
  * making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call
51
61
  * instead of setting the result on a state variable from within the debounced function.
52
62
  *
63
+ * Error Handling:
64
+ * - If an error occurs during execution and no `onError` handler is provided, the error will be thrown and propagate up to the caller.
65
+ * - If an `onError` handler is provided, errors will be caught and passed to the handler instead of being thrown.
66
+ * - The error count can be tracked using `getErrorCount()`.
67
+ * - The debouncer maintains its state and can continue to be used after an error occurs.
68
+ *
53
69
  * @example
54
70
  * ```ts
55
71
  * const asyncDebouncer = new AsyncDebouncer(async (value: string) => {
56
72
  * const results = await searchAPI(value);
57
73
  * return results; // Return value is preserved
58
- * }, { wait: 500 });
74
+ * }, {
75
+ * wait: 500,
76
+ * onError: (error) => {
77
+ * console.error('Search failed:', error);
78
+ * }
79
+ * });
59
80
  *
60
81
  * // Called on each keystroke but only executes after 500ms of no typing
61
82
  * // Returns the API response directly
@@ -64,6 +85,7 @@ export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
64
85
  */
65
86
  export declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
66
87
  private fn;
88
+ private _options;
67
89
  private _abortController;
68
90
  private _canLeadingExecute;
69
91
  private _errorCount;
@@ -71,7 +93,6 @@ export declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
71
93
  private _isPending;
72
94
  private _lastArgs;
73
95
  private _lastResult;
74
- private _options;
75
96
  private _settleCount;
76
97
  private _successCount;
77
98
  private _timeoutId;
@@ -84,10 +105,28 @@ export declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
84
105
  /**
85
106
  * Returns the current debouncer options
86
107
  */
87
- getOptions(): Required<AsyncDebouncerOptions<TFn>>;
108
+ getOptions(): AsyncDebouncerOptions<TFn>;
88
109
  /**
89
- * Attempts to execute the debounced function
90
- * If a call is already in progress, it will be queued
110
+ * Returns the current debouncer enabled state
111
+ */
112
+ getEnabled(): boolean;
113
+ /**
114
+ * Returns the current debouncer wait state
115
+ */
116
+ getWait(): number;
117
+ /**
118
+ * Attempts to execute the debounced function.
119
+ * If a call is already in progress, it will be queued.
120
+ *
121
+ * Error Handling:
122
+ * - If the debounced function throws and no `onError` handler is configured,
123
+ * the error will be thrown from this method.
124
+ * - If an `onError` handler is configured, errors will be caught and passed to the handler,
125
+ * and this method will return undefined.
126
+ * - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.
127
+ *
128
+ * @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError
129
+ * @throws The error from the debounced function if no onError handler is configured
91
130
  */
92
131
  maybeExecute(...args: Parameters<TFn>): Promise<ReturnType<TFn> | undefined>;
93
132
  private executeFunction;
@@ -133,16 +172,29 @@ export declare class AsyncDebouncer<TFn extends AnyAsyncFunction> {
133
172
  * making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call
134
173
  * instead of setting the result on a state variable from within the debounced function.
135
174
  *
175
+ * Error Handling:
176
+ * - If an `onError` handler is provided, it will be called with the error and debouncer instance
177
+ * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown
178
+ * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed
179
+ * - The error state can be checked using the underlying AsyncDebouncer instance
180
+ * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown
181
+ *
136
182
  * @example
137
183
  * ```ts
138
184
  * const debounced = asyncDebounce(async (value: string) => {
139
185
  * const result = await saveToAPI(value);
140
186
  * return result; // Return value is preserved
141
- * }, { wait: 1000 });
187
+ * }, {
188
+ * wait: 1000,
189
+ * onError: (error) => {
190
+ * console.error('API call failed:', error);
191
+ * },
192
+ * throwOnError: true // Will both log the error and throw it
193
+ * });
142
194
  *
143
195
  * // Will only execute once, 1 second after the last call
144
196
  * // Returns the API response directly
145
197
  * const result = await debounced("third");
146
198
  * ```
147
199
  */
148
- export declare function asyncDebounce<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions: Omit<AsyncDebouncerOptions<TFn>, 'enabled'>): (...args: Parameters<TFn>) => Promise<ReturnType<TFn> | undefined>;
200
+ export declare function asyncDebounce<TFn extends AnyAsyncFunction>(fn: TFn, initialOptions: AsyncDebouncerOptions<TFn>): (...args: Parameters<TFn>) => Promise<ReturnType<TFn> | undefined>;