@tanstack/pacer 0.21.1 → 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 (101) hide show
  1. package/README.md +21 -6
  2. package/dist/async-batcher.d.ts +11 -14
  3. package/dist/async-batcher.js +186 -132
  4. package/dist/async-debouncer.d.ts +10 -13
  5. package/dist/async-debouncer.js +187 -146
  6. package/dist/async-queuer.d.ts +12 -14
  7. package/dist/async-queuer.js +384 -259
  8. package/dist/async-rate-limiter.d.ts +9 -12
  9. package/dist/async-rate-limiter.js +198 -147
  10. package/dist/async-retryer.d.ts +10 -10
  11. package/dist/async-retryer.js +209 -188
  12. package/dist/async-throttler.d.ts +10 -13
  13. package/dist/async-throttler.js +215 -161
  14. package/dist/batcher.d.ts +5 -8
  15. package/dist/batcher.js +107 -87
  16. package/dist/debouncer.d.ts +6 -9
  17. package/dist/debouncer.js +99 -86
  18. package/dist/event-client.d.ts +8 -11
  19. package/dist/event-client.js +1 -2
  20. package/dist/index.js +1 -1
  21. package/dist/queuer.d.ts +8 -10
  22. package/dist/queuer.js +277 -207
  23. package/dist/rate-limiter.d.ts +6 -9
  24. package/dist/rate-limiter.js +127 -109
  25. package/dist/throttler.d.ts +6 -9
  26. package/dist/throttler.js +127 -90
  27. package/dist/types.d.ts +4 -6
  28. package/dist/utils.d.ts +3 -6
  29. package/dist/utils.js +1 -2
  30. package/package.json +23 -70
  31. package/dist/async-batcher.cjs +0 -337
  32. package/dist/async-batcher.cjs.map +0 -1
  33. package/dist/async-batcher.d.cts +0 -344
  34. package/dist/async-batcher.js.map +0 -1
  35. package/dist/async-debouncer.cjs +0 -330
  36. package/dist/async-debouncer.cjs.map +0 -1
  37. package/dist/async-debouncer.d.cts +0 -300
  38. package/dist/async-debouncer.js.map +0 -1
  39. package/dist/async-queuer.cjs +0 -484
  40. package/dist/async-queuer.cjs.map +0 -1
  41. package/dist/async-queuer.d.cts +0 -440
  42. package/dist/async-queuer.js.map +0 -1
  43. package/dist/async-rate-limiter.cjs +0 -373
  44. package/dist/async-rate-limiter.cjs.map +0 -1
  45. package/dist/async-rate-limiter.d.cts +0 -357
  46. package/dist/async-rate-limiter.js.map +0 -1
  47. package/dist/async-retryer.cjs +0 -374
  48. package/dist/async-retryer.cjs.map +0 -1
  49. package/dist/async-retryer.d.cts +0 -319
  50. package/dist/async-retryer.js.map +0 -1
  51. package/dist/async-throttler.cjs +0 -347
  52. package/dist/async-throttler.cjs.map +0 -1
  53. package/dist/async-throttler.d.cts +0 -320
  54. package/dist/async-throttler.js.map +0 -1
  55. package/dist/batcher.cjs +0 -200
  56. package/dist/batcher.cjs.map +0 -1
  57. package/dist/batcher.d.cts +0 -180
  58. package/dist/batcher.js.map +0 -1
  59. package/dist/debouncer.cjs +0 -203
  60. package/dist/debouncer.cjs.map +0 -1
  61. package/dist/debouncer.d.cts +0 -167
  62. package/dist/debouncer.js.map +0 -1
  63. package/dist/event-client.cjs +0 -64
  64. package/dist/event-client.cjs.map +0 -1
  65. package/dist/event-client.d.cts +0 -66
  66. package/dist/event-client.js.map +0 -1
  67. package/dist/index.cjs +0 -52
  68. package/dist/index.d.cts +0 -15
  69. package/dist/queuer.cjs +0 -401
  70. package/dist/queuer.cjs.map +0 -1
  71. package/dist/queuer.d.cts +0 -345
  72. package/dist/queuer.js.map +0 -1
  73. package/dist/rate-limiter.cjs +0 -263
  74. package/dist/rate-limiter.cjs.map +0 -1
  75. package/dist/rate-limiter.d.cts +0 -215
  76. package/dist/rate-limiter.js.map +0 -1
  77. package/dist/throttler.cjs +0 -215
  78. package/dist/throttler.cjs.map +0 -1
  79. package/dist/throttler.d.cts +0 -207
  80. package/dist/throttler.js.map +0 -1
  81. package/dist/types.cjs +0 -0
  82. package/dist/types.d.cts +0 -13
  83. package/dist/utils.cjs +0 -14
  84. package/dist/utils.cjs.map +0 -1
  85. package/dist/utils.d.cts +0 -8
  86. package/dist/utils.js.map +0 -1
  87. package/src/async-batcher.ts +0 -594
  88. package/src/async-debouncer.ts +0 -565
  89. package/src/async-queuer.ts +0 -925
  90. package/src/async-rate-limiter.ts +0 -647
  91. package/src/async-retryer.ts +0 -684
  92. package/src/async-throttler.ts +0 -633
  93. package/src/batcher.ts +0 -329
  94. package/src/debouncer.ts +0 -334
  95. package/src/event-client.ts +0 -129
  96. package/src/index.ts +0 -24
  97. package/src/queuer.ts +0 -740
  98. package/src/rate-limiter.ts +0 -429
  99. package/src/throttler.ts +0 -380
  100. package/src/types.ts +0 -12
  101. package/src/utils.ts +0 -12
@@ -71,105 +71,13 @@ const defaultOptions = {
71
71
  * ```
72
72
  */
73
73
  var RateLimiter = class {
74
- #timeoutIds;
74
+ fn;
75
+ store = new Store(getDefaultRateLimiterState());
76
+ key;
77
+ options;
78
+ #timeoutIds = /* @__PURE__ */ new Set();
75
79
  constructor(fn, initialOptions) {
76
80
  this.fn = fn;
77
- this.store = new Store(getDefaultRateLimiterState());
78
- this.#timeoutIds = /* @__PURE__ */ new Set();
79
- this.setOptions = (newOptions) => {
80
- this.options = {
81
- ...this.options,
82
- ...newOptions
83
- };
84
- };
85
- this.#setState = (newState) => {
86
- this.store.setState((state) => {
87
- const combinedState = {
88
- ...state,
89
- ...newState
90
- };
91
- const isExceeded = combinedState.executionTimes.length >= this.#getLimit();
92
- const status = !this.#getEnabled() ? "disabled" : isExceeded ? "exceeded" : "idle";
93
- return {
94
- ...combinedState,
95
- isExceeded,
96
- status
97
- };
98
- });
99
- emitChange("RateLimiter", this);
100
- };
101
- this.#getEnabled = () => {
102
- return !!parseFunctionOrValue(this.options.enabled, this);
103
- };
104
- this.#getLimit = () => {
105
- return parseFunctionOrValue(this.options.limit, this);
106
- };
107
- this.#getWindow = () => {
108
- return parseFunctionOrValue(this.options.window, this);
109
- };
110
- this.maybeExecute = (...args) => {
111
- this.#setState({ maybeExecuteCount: this.store.state.maybeExecuteCount + 1 });
112
- this.#cleanupOldExecutions();
113
- if (this.#getExecutionTimesInWindow().length < this.#getLimit()) {
114
- this.#execute(...args);
115
- return true;
116
- }
117
- this.#setState({ rejectionCount: this.store.state.rejectionCount + 1 });
118
- this.options.onReject?.(this);
119
- return false;
120
- };
121
- this.#execute = (...args) => {
122
- if (!this.#getEnabled()) return;
123
- const now = Date.now();
124
- this.fn(...args);
125
- this.store.state.executionTimes.push(now);
126
- this.#setCleanupTimeout(now);
127
- this.#setState({ executionCount: this.store.state.executionCount + 1 });
128
- this.options.onExecute?.(args, this);
129
- };
130
- this.#getExecutionTimesInWindow = () => {
131
- if (this.options.windowType === "sliding") return this.store.state.executionTimes.filter((time) => time > Date.now() - this.#getWindow());
132
- else {
133
- if (this.store.state.executionTimes.length === 0) return [];
134
- const windowStart = Math.min(...this.store.state.executionTimes);
135
- const windowEnd = windowStart + this.#getWindow();
136
- if (Date.now() > windowEnd) return [];
137
- return this.store.state.executionTimes.filter((time) => time >= windowStart && time <= windowEnd);
138
- }
139
- };
140
- this.#setCleanupTimeout = (executionTime) => {
141
- if (this.options.windowType === "sliding" || this.#timeoutIds.size === 0) {
142
- const timeUntilExpiration = executionTime - Date.now() + this.#getWindow() + 1;
143
- const timeoutId = setTimeout(() => {
144
- this.#cleanupOldExecutions();
145
- this.#clearTimeout(timeoutId);
146
- }, timeUntilExpiration);
147
- this.#timeoutIds.add(timeoutId);
148
- }
149
- };
150
- this.#clearTimeout = (timeoutId) => {
151
- clearTimeout(timeoutId);
152
- this.#timeoutIds.delete(timeoutId);
153
- };
154
- this.#clearTimeouts = () => {
155
- this.#timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
156
- this.#timeoutIds.clear();
157
- };
158
- this.#cleanupOldExecutions = () => {
159
- this.#setState({ executionTimes: this.#getExecutionTimesInWindow() });
160
- };
161
- this.getRemainingInWindow = () => {
162
- const relevantExecutionTimes = this.#getExecutionTimesInWindow();
163
- return Math.max(0, this.#getLimit() - relevantExecutionTimes.length);
164
- };
165
- this.getMsUntilNextWindow = () => {
166
- if (this.getRemainingInWindow() > 0) return 0;
167
- return (this.store.state.executionTimes[0] ?? Infinity) + this.#getWindow() - Date.now();
168
- };
169
- this.reset = () => {
170
- this.#setState(getDefaultRateLimiterState());
171
- this.#clearTimeouts();
172
- };
173
81
  this.key = initialOptions.key;
174
82
  this.options = {
175
83
  ...defaultOptions,
@@ -183,25 +91,136 @@ var RateLimiter = class {
183
91
  this.setOptions(event.payload.options);
184
92
  });
185
93
  }
186
- #setState;
94
+ /**
95
+ * Updates the rate limiter options
96
+ */
97
+ setOptions = (newOptions) => {
98
+ this.options = {
99
+ ...this.options,
100
+ ...newOptions
101
+ };
102
+ };
103
+ #setState = (newState) => {
104
+ this.store.setState((state) => {
105
+ const combinedState = {
106
+ ...state,
107
+ ...newState
108
+ };
109
+ const isExceeded = combinedState.executionTimes.length >= this.#getLimit();
110
+ const status = !this.#getEnabled() ? "disabled" : isExceeded ? "exceeded" : "idle";
111
+ return {
112
+ ...combinedState,
113
+ isExceeded,
114
+ status
115
+ };
116
+ });
117
+ emitChange("RateLimiter", this);
118
+ };
187
119
  /**
188
120
  * Returns the current enabled state of the rate limiter
189
121
  */
190
- #getEnabled;
122
+ #getEnabled = () => {
123
+ return !!parseFunctionOrValue(this.options.enabled, this);
124
+ };
191
125
  /**
192
126
  * Returns the current limit of executions allowed within the time window
193
127
  */
194
- #getLimit;
128
+ #getLimit = () => {
129
+ return parseFunctionOrValue(this.options.limit, this);
130
+ };
195
131
  /**
196
132
  * Returns the current time window in milliseconds
197
133
  */
198
- #getWindow;
199
- #execute;
200
- #getExecutionTimesInWindow;
201
- #setCleanupTimeout;
202
- #clearTimeout;
203
- #clearTimeouts;
204
- #cleanupOldExecutions;
134
+ #getWindow = () => {
135
+ return parseFunctionOrValue(this.options.window, this);
136
+ };
137
+ /**
138
+ * Attempts to execute the rate-limited function if within the configured limits.
139
+ * Will reject execution if the number of calls in the current window exceeds the limit.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const rateLimiter = new RateLimiter(fn, { limit: 5, window: 1000 });
144
+ *
145
+ * // First 5 calls will return true
146
+ * rateLimiter.maybeExecute('arg1', 'arg2'); // true
147
+ *
148
+ * // Additional calls within the window will return false
149
+ * rateLimiter.maybeExecute('arg1', 'arg2'); // false
150
+ * ```
151
+ */
152
+ maybeExecute = (...args) => {
153
+ this.#setState({ maybeExecuteCount: this.store.state.maybeExecuteCount + 1 });
154
+ this.#cleanupOldExecutions();
155
+ if (this.#getExecutionTimesInWindow().length < this.#getLimit()) {
156
+ this.#execute(...args);
157
+ return true;
158
+ }
159
+ this.#setState({ rejectionCount: this.store.state.rejectionCount + 1 });
160
+ this.options.onReject?.(this);
161
+ return false;
162
+ };
163
+ #execute = (...args) => {
164
+ if (!this.#getEnabled()) return;
165
+ const now = Date.now();
166
+ this.fn(...args);
167
+ this.store.state.executionTimes.push(now);
168
+ this.#setCleanupTimeout(now);
169
+ this.#setState({ executionCount: this.store.state.executionCount + 1 });
170
+ this.options.onExecute?.(args, this);
171
+ };
172
+ #getExecutionTimesInWindow = () => {
173
+ if (this.options.windowType === "sliding") return this.store.state.executionTimes.filter((time) => time > Date.now() - this.#getWindow());
174
+ else {
175
+ if (this.store.state.executionTimes.length === 0) return [];
176
+ const windowStart = Math.min(...this.store.state.executionTimes);
177
+ const windowEnd = windowStart + this.#getWindow();
178
+ if (Date.now() > windowEnd) return [];
179
+ return this.store.state.executionTimes.filter((time) => time >= windowStart && time <= windowEnd);
180
+ }
181
+ };
182
+ #setCleanupTimeout = (executionTime) => {
183
+ if (this.options.windowType === "sliding" || this.#timeoutIds.size === 0) {
184
+ const timeUntilExpiration = executionTime - Date.now() + this.#getWindow() + 1;
185
+ const timeoutId = setTimeout(() => {
186
+ this.#cleanupOldExecutions();
187
+ this.#clearTimeout(timeoutId);
188
+ }, timeUntilExpiration);
189
+ this.#timeoutIds.add(timeoutId);
190
+ }
191
+ };
192
+ #clearTimeout = (timeoutId) => {
193
+ clearTimeout(timeoutId);
194
+ this.#timeoutIds.delete(timeoutId);
195
+ };
196
+ #clearTimeouts = () => {
197
+ this.#timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
198
+ this.#timeoutIds.clear();
199
+ };
200
+ #cleanupOldExecutions = () => {
201
+ this.#setState({ executionTimes: this.#getExecutionTimesInWindow() });
202
+ };
203
+ /**
204
+ * Returns the number of remaining executions allowed in the current window
205
+ */
206
+ getRemainingInWindow = () => {
207
+ const relevantExecutionTimes = this.#getExecutionTimesInWindow();
208
+ return Math.max(0, this.#getLimit() - relevantExecutionTimes.length);
209
+ };
210
+ /**
211
+ * Returns the number of milliseconds until the next execution will be possible
212
+ */
213
+ getMsUntilNextWindow = () => {
214
+ if (this.getRemainingInWindow() > 0) return 0;
215
+ return (this.store.state.executionTimes[0] ?? Infinity) + this.#getWindow() - Date.now();
216
+ };
217
+ /**
218
+ * Resets the rate limiter state
219
+ */
220
+ reset = () => {
221
+ this.#setState(getDefaultRateLimiterState());
222
+ this.#clearTimeouts();
223
+ };
205
224
  };
206
225
  /**
207
226
  * Creates a rate-limited function that will execute the provided function up to a maximum number of times within a time window.
@@ -256,5 +275,4 @@ function rateLimit(fn, initialOptions) {
256
275
  }
257
276
 
258
277
  //#endregion
259
- export { RateLimiter, rateLimit, rateLimiterOptions };
260
- //# sourceMappingURL=rate-limiter.js.map
278
+ export { RateLimiter, rateLimit, rateLimiterOptions };
@@ -1,8 +1,7 @@
1
1
  import { AnyFunction } from "./types.js";
2
2
  import { Store } from "@tanstack/store";
3
-
4
3
  //#region src/throttler.d.ts
5
- interface ThrottlerState<TFn extends AnyFunction> {
4
+ export interface ThrottlerState<TFn extends AnyFunction> {
6
5
  /**
7
6
  * Number of function executions that have been completed
8
7
  */
@@ -35,7 +34,7 @@ interface ThrottlerState<TFn extends AnyFunction> {
35
34
  /**
36
35
  * Options for configuring a throttled function
37
36
  */
38
- interface ThrottlerOptions<TFn extends AnyFunction> {
37
+ export interface ThrottlerOptions<TFn extends AnyFunction> {
39
38
  /**
40
39
  * Whether the throttler is enabled. When disabled, maybeExecute will not trigger any executions.
41
40
  * Can be a boolean or a function that returns a boolean.
@@ -75,7 +74,7 @@ interface ThrottlerOptions<TFn extends AnyFunction> {
75
74
  /**
76
75
  * Utility function for sharing common `ThrottlerOptions` options between different `Throttler` instances.
77
76
  */
78
- declare function throttlerOptions<TFn extends AnyFunction = AnyFunction, TOptions extends Partial<ThrottlerOptions<TFn>> = Partial<ThrottlerOptions<TFn>>>(options: TOptions): TOptions;
77
+ export declare function throttlerOptions<TFn extends AnyFunction = AnyFunction, TOptions extends Partial<ThrottlerOptions<TFn>> = Partial<ThrottlerOptions<TFn>>>(options: TOptions): TOptions;
79
78
  /**
80
79
  * A class that creates a throttled function.
81
80
  *
@@ -112,7 +111,7 @@ declare function throttlerOptions<TFn extends AnyFunction = AnyFunction, TOption
112
111
  * throttler.maybeExecute('123'); // Throttled
113
112
  * ```
114
113
  */
115
- declare class Throttler<TFn extends AnyFunction> {
114
+ export declare class Throttler<TFn extends AnyFunction> {
116
115
  #private;
117
116
  fn: TFn;
118
117
  readonly store: Store<Readonly<ThrottlerState<TFn>>>;
@@ -201,7 +200,5 @@ declare class Throttler<TFn extends AnyFunction> {
201
200
  * });
202
201
  * ```
203
202
  */
204
- declare function throttle<TFn extends AnyFunction>(fn: TFn, initialOptions: ThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
205
- //#endregion
206
- export { Throttler, ThrottlerOptions, ThrottlerState, throttle, throttlerOptions };
207
- //# sourceMappingURL=throttler.d.ts.map
203
+ export declare function throttle<TFn extends AnyFunction>(fn: TFn, initialOptions: ThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
204
+ //#endregion
package/dist/throttler.js CHANGED
@@ -63,92 +63,13 @@ const defaultOptions = {
63
63
  * ```
64
64
  */
65
65
  var Throttler = class {
66
+ fn;
67
+ store = new Store(getDefaultThrottlerState());
68
+ key;
69
+ options;
66
70
  #timeoutId;
67
71
  constructor(fn, initialOptions) {
68
72
  this.fn = fn;
69
- this.store = new Store(getDefaultThrottlerState());
70
- this.setOptions = (newOptions) => {
71
- this.options = {
72
- ...this.options,
73
- ...newOptions
74
- };
75
- if (!this.#getEnabled()) this.cancel();
76
- };
77
- this.#setState = (newState) => {
78
- this.store.setState((state) => {
79
- const combinedState = {
80
- ...state,
81
- ...newState
82
- };
83
- const { isPending } = combinedState;
84
- return {
85
- ...combinedState,
86
- status: !this.#getEnabled() ? "disabled" : isPending ? "pending" : "idle"
87
- };
88
- });
89
- emitChange("Throttler", this);
90
- };
91
- this.#getEnabled = () => {
92
- return !!parseFunctionOrValue(this.options.enabled, this);
93
- };
94
- this.#getWait = () => {
95
- return parseFunctionOrValue(this.options.wait, this);
96
- };
97
- this.maybeExecute = (...args) => {
98
- this.#setState({ maybeExecuteCount: this.store.state.maybeExecuteCount + 1 });
99
- const now = Date.now();
100
- const timeSinceLastExecution = now - this.store.state.lastExecutionTime;
101
- const wait = this.#getWait();
102
- if (this.options.leading && timeSinceLastExecution >= wait) this.#execute(...args);
103
- else {
104
- this.#setState({ lastArgs: args });
105
- if (!this.#timeoutId && this.options.trailing) {
106
- const timeoutDuration = wait - (this.store.state.lastExecutionTime ? now - this.store.state.lastExecutionTime : 0);
107
- this.#setState({ isPending: true });
108
- this.#timeoutId = setTimeout(() => {
109
- const { lastArgs } = this.store.state;
110
- if (lastArgs !== void 0) this.#execute(...lastArgs);
111
- }, timeoutDuration);
112
- }
113
- }
114
- };
115
- this.#execute = (...args) => {
116
- if (!this.#getEnabled()) return;
117
- this.fn(...args);
118
- const lastExecutionTime = Date.now();
119
- const nextExecutionTime = lastExecutionTime + this.#getWait();
120
- this.#clearTimeout();
121
- this.#setState({
122
- executionCount: this.store.state.executionCount + 1,
123
- lastExecutionTime,
124
- nextExecutionTime,
125
- isPending: false,
126
- lastArgs: void 0
127
- });
128
- this.options.onExecute?.(args, this);
129
- setTimeout(() => {
130
- if (!this.store.state.isPending) this.#setState({ nextExecutionTime: void 0 });
131
- }, this.#getWait());
132
- };
133
- this.flush = () => {
134
- if (this.store.state.isPending && this.store.state.lastArgs) this.#execute(...this.store.state.lastArgs);
135
- };
136
- this.#clearTimeout = () => {
137
- if (this.#timeoutId) {
138
- clearTimeout(this.#timeoutId);
139
- this.#timeoutId = void 0;
140
- }
141
- };
142
- this.cancel = () => {
143
- this.#clearTimeout();
144
- this.#setState({
145
- lastArgs: void 0,
146
- isPending: false
147
- });
148
- };
149
- this.reset = () => {
150
- this.#setState(getDefaultThrottlerState());
151
- };
152
73
  this.key = initialOptions.key;
153
74
  this.options = {
154
75
  ...defaultOptions,
@@ -161,11 +82,128 @@ var Throttler = class {
161
82
  this.setOptions(event.payload.options);
162
83
  });
163
84
  }
164
- #setState;
165
- #getEnabled;
166
- #getWait;
167
- #execute;
168
- #clearTimeout;
85
+ /**
86
+ * Updates the throttler options
87
+ */
88
+ setOptions = (newOptions) => {
89
+ this.options = {
90
+ ...this.options,
91
+ ...newOptions
92
+ };
93
+ if (!this.#getEnabled()) this.cancel();
94
+ };
95
+ #setState = (newState) => {
96
+ this.store.setState((state) => {
97
+ const combinedState = {
98
+ ...state,
99
+ ...newState
100
+ };
101
+ const { isPending } = combinedState;
102
+ return {
103
+ ...combinedState,
104
+ status: !this.#getEnabled() ? "disabled" : isPending ? "pending" : "idle"
105
+ };
106
+ });
107
+ emitChange("Throttler", this);
108
+ };
109
+ #getEnabled = () => {
110
+ return !!parseFunctionOrValue(this.options.enabled, this);
111
+ };
112
+ #getWait = () => {
113
+ return parseFunctionOrValue(this.options.wait, this);
114
+ };
115
+ /**
116
+ * Attempts to execute the throttled function. The execution behavior depends on the throttler options:
117
+ *
118
+ * - If enough time has passed since the last execution (>= wait period):
119
+ * - With leading=true: Executes immediately
120
+ * - With leading=false: Waits for the next trailing execution
121
+ *
122
+ * - If within the wait period:
123
+ * - With trailing=true: Schedules execution for end of wait period
124
+ * - With trailing=false: Drops the execution
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const throttled = new Throttler(fn, { wait: 1000 });
129
+ *
130
+ * // First call executes immediately
131
+ * throttled.maybeExecute('a', 'b');
132
+ *
133
+ * // Call during wait period - gets throttled
134
+ * throttled.maybeExecute('c', 'd');
135
+ * ```
136
+ */
137
+ maybeExecute = (...args) => {
138
+ this.#setState({ maybeExecuteCount: this.store.state.maybeExecuteCount + 1 });
139
+ const now = Date.now();
140
+ const timeSinceLastExecution = now - this.store.state.lastExecutionTime;
141
+ const wait = this.#getWait();
142
+ if (this.options.leading && timeSinceLastExecution >= wait) this.#execute(...args);
143
+ else {
144
+ this.#setState({ lastArgs: args });
145
+ if (!this.#timeoutId && this.options.trailing) {
146
+ const timeoutDuration = wait - (this.store.state.lastExecutionTime ? now - this.store.state.lastExecutionTime : 0);
147
+ this.#setState({ isPending: true });
148
+ this.#timeoutId = setTimeout(() => {
149
+ const { lastArgs } = this.store.state;
150
+ if (lastArgs !== void 0) this.#execute(...lastArgs);
151
+ }, timeoutDuration);
152
+ }
153
+ }
154
+ };
155
+ #execute = (...args) => {
156
+ if (!this.#getEnabled()) return;
157
+ this.fn(...args);
158
+ const lastExecutionTime = Date.now();
159
+ const nextExecutionTime = lastExecutionTime + this.#getWait();
160
+ this.#clearTimeout();
161
+ this.#setState({
162
+ executionCount: this.store.state.executionCount + 1,
163
+ lastExecutionTime,
164
+ nextExecutionTime,
165
+ isPending: false,
166
+ lastArgs: void 0
167
+ });
168
+ this.options.onExecute?.(args, this);
169
+ setTimeout(() => {
170
+ if (!this.store.state.isPending) this.#setState({ nextExecutionTime: void 0 });
171
+ }, this.#getWait());
172
+ };
173
+ /**
174
+ * Processes the current pending execution immediately
175
+ */
176
+ flush = () => {
177
+ if (this.store.state.isPending && this.store.state.lastArgs) this.#execute(...this.store.state.lastArgs);
178
+ };
179
+ #clearTimeout = () => {
180
+ if (this.#timeoutId) {
181
+ clearTimeout(this.#timeoutId);
182
+ this.#timeoutId = void 0;
183
+ }
184
+ };
185
+ /**
186
+ * Cancels any pending trailing execution and clears internal state.
187
+ *
188
+ * If a trailing execution is scheduled (due to throttling with trailing=true),
189
+ * this will prevent that execution from occurring. The internal timeout and
190
+ * stored arguments will be cleared.
191
+ *
192
+ * Has no effect if there is no pending execution.
193
+ */
194
+ cancel = () => {
195
+ this.#clearTimeout();
196
+ this.#setState({
197
+ lastArgs: void 0,
198
+ isPending: false
199
+ });
200
+ };
201
+ /**
202
+ * Resets the throttler state to its default values
203
+ */
204
+ reset = () => {
205
+ this.#setState(getDefaultThrottlerState());
206
+ };
169
207
  };
170
208
  /**
171
209
  * Creates a throttled function that limits how often the provided function can execute.
@@ -208,5 +246,4 @@ function throttle(fn, initialOptions) {
208
246
  }
209
247
 
210
248
  //#endregion
211
- export { Throttler, throttle, throttlerOptions };
212
- //# sourceMappingURL=throttler.js.map
249
+ export { Throttler, throttle, throttlerOptions };
package/dist/types.d.ts CHANGED
@@ -2,12 +2,10 @@
2
2
  /**
3
3
  * Represents a function that can be called with any arguments and returns any value.
4
4
  */
5
- type AnyFunction = (...args: Array<any>) => any;
5
+ export type AnyFunction = (...args: Array<any>) => any;
6
6
  /**
7
7
  * Represents an asynchronous function that can be called with any arguments and returns a promise.
8
8
  */
9
- type AnyAsyncFunction = (...args: Array<any>) => Promise<any>;
10
- type OptionalKeys<T, TKey extends keyof T> = Omit<T, TKey> & Partial<Pick<T, TKey>>;
11
- //#endregion
12
- export { AnyAsyncFunction, AnyFunction, OptionalKeys };
13
- //# sourceMappingURL=types.d.ts.map
9
+ export type AnyAsyncFunction = (...args: Array<any>) => Promise<any>;
10
+ export type OptionalKeys<T, TKey extends keyof T> = Omit<T, TKey> & Partial<Pick<T, TKey>>;
11
+ //#endregion
package/dist/utils.d.ts CHANGED
@@ -1,8 +1,5 @@
1
1
  import { AnyFunction } from "./types.js";
2
-
3
2
  //#region src/utils.d.ts
4
- declare function isFunction<T extends AnyFunction>(value: any): value is T;
5
- declare function parseFunctionOrValue<T, TArgs extends Array<any>>(value: T | ((...args: TArgs) => T), ...args: TArgs): T;
6
- //#endregion
7
- export { isFunction, parseFunctionOrValue };
8
- //# sourceMappingURL=utils.d.ts.map
3
+ export declare function isFunction<T extends AnyFunction>(value: any): value is T;
4
+ export declare function parseFunctionOrValue<T, TArgs extends Array<any>>(value: T | ((...args: TArgs) => T), ...args: TArgs): T;
5
+ //#endregion
package/dist/utils.js CHANGED
@@ -7,5 +7,4 @@ function parseFunctionOrValue(value, ...args) {
7
7
  }
8
8
 
9
9
  //#endregion
10
- export { isFunction, parseFunctionOrValue };
11
- //# sourceMappingURL=utils.js.map
10
+ export { isFunction, parseFunctionOrValue };