@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.
- package/README.md +21 -6
- package/dist/async-batcher.d.ts +11 -14
- package/dist/async-batcher.js +186 -132
- package/dist/async-debouncer.d.ts +10 -13
- package/dist/async-debouncer.js +187 -146
- package/dist/async-queuer.d.ts +12 -14
- package/dist/async-queuer.js +384 -259
- package/dist/async-rate-limiter.d.ts +9 -12
- package/dist/async-rate-limiter.js +198 -147
- package/dist/async-retryer.d.ts +10 -10
- package/dist/async-retryer.js +209 -188
- package/dist/async-throttler.d.ts +10 -13
- package/dist/async-throttler.js +215 -161
- package/dist/batcher.d.ts +5 -8
- package/dist/batcher.js +107 -87
- package/dist/debouncer.d.ts +6 -9
- package/dist/debouncer.js +99 -86
- package/dist/event-client.d.ts +8 -11
- package/dist/event-client.js +1 -2
- package/dist/index.js +1 -1
- package/dist/queuer.d.ts +8 -10
- package/dist/queuer.js +277 -207
- package/dist/rate-limiter.d.ts +6 -9
- package/dist/rate-limiter.js +127 -109
- package/dist/throttler.d.ts +6 -9
- package/dist/throttler.js +127 -90
- package/dist/types.d.ts +4 -6
- package/dist/utils.d.ts +3 -6
- package/dist/utils.js +1 -2
- package/package.json +23 -70
- package/dist/async-batcher.cjs +0 -337
- package/dist/async-batcher.cjs.map +0 -1
- package/dist/async-batcher.d.cts +0 -344
- package/dist/async-batcher.js.map +0 -1
- package/dist/async-debouncer.cjs +0 -330
- package/dist/async-debouncer.cjs.map +0 -1
- package/dist/async-debouncer.d.cts +0 -300
- package/dist/async-debouncer.js.map +0 -1
- package/dist/async-queuer.cjs +0 -484
- package/dist/async-queuer.cjs.map +0 -1
- package/dist/async-queuer.d.cts +0 -440
- package/dist/async-queuer.js.map +0 -1
- package/dist/async-rate-limiter.cjs +0 -373
- package/dist/async-rate-limiter.cjs.map +0 -1
- package/dist/async-rate-limiter.d.cts +0 -357
- package/dist/async-rate-limiter.js.map +0 -1
- package/dist/async-retryer.cjs +0 -374
- package/dist/async-retryer.cjs.map +0 -1
- package/dist/async-retryer.d.cts +0 -319
- package/dist/async-retryer.js.map +0 -1
- package/dist/async-throttler.cjs +0 -347
- package/dist/async-throttler.cjs.map +0 -1
- package/dist/async-throttler.d.cts +0 -320
- package/dist/async-throttler.js.map +0 -1
- package/dist/batcher.cjs +0 -200
- package/dist/batcher.cjs.map +0 -1
- package/dist/batcher.d.cts +0 -180
- package/dist/batcher.js.map +0 -1
- package/dist/debouncer.cjs +0 -203
- package/dist/debouncer.cjs.map +0 -1
- package/dist/debouncer.d.cts +0 -167
- package/dist/debouncer.js.map +0 -1
- package/dist/event-client.cjs +0 -64
- package/dist/event-client.cjs.map +0 -1
- package/dist/event-client.d.cts +0 -66
- package/dist/event-client.js.map +0 -1
- package/dist/index.cjs +0 -52
- package/dist/index.d.cts +0 -15
- package/dist/queuer.cjs +0 -401
- package/dist/queuer.cjs.map +0 -1
- package/dist/queuer.d.cts +0 -345
- package/dist/queuer.js.map +0 -1
- package/dist/rate-limiter.cjs +0 -263
- package/dist/rate-limiter.cjs.map +0 -1
- package/dist/rate-limiter.d.cts +0 -215
- package/dist/rate-limiter.js.map +0 -1
- package/dist/throttler.cjs +0 -215
- package/dist/throttler.cjs.map +0 -1
- package/dist/throttler.d.cts +0 -207
- package/dist/throttler.js.map +0 -1
- package/dist/types.cjs +0 -0
- package/dist/types.d.cts +0 -13
- package/dist/utils.cjs +0 -14
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.d.cts +0 -8
- package/dist/utils.js.map +0 -1
- package/src/async-batcher.ts +0 -594
- package/src/async-debouncer.ts +0 -565
- package/src/async-queuer.ts +0 -925
- package/src/async-rate-limiter.ts +0 -647
- package/src/async-retryer.ts +0 -684
- package/src/async-throttler.ts +0 -633
- package/src/batcher.ts +0 -329
- package/src/debouncer.ts +0 -334
- package/src/event-client.ts +0 -129
- package/src/index.ts +0 -24
- package/src/queuer.ts +0 -740
- package/src/rate-limiter.ts +0 -429
- package/src/throttler.ts +0 -380
- package/src/types.ts +0 -12
- package/src/utils.ts +0 -12
package/dist/rate-limiter.js
CHANGED
|
@@ -71,105 +71,13 @@ const defaultOptions = {
|
|
|
71
71
|
* ```
|
|
72
72
|
*/
|
|
73
73
|
var RateLimiter = class {
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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 };
|
package/dist/throttler.d.ts
CHANGED
|
@@ -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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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