@tanstack/pacer 0.16.3 → 0.16.4

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 (169) hide show
  1. package/README.md +165 -0
  2. package/dist/async-batcher.cjs +355 -0
  3. package/dist/async-batcher.cjs.map +1 -0
  4. package/dist/async-batcher.d.cts +344 -0
  5. package/dist/async-batcher.d.ts +344 -0
  6. package/dist/async-batcher.js +353 -0
  7. package/dist/async-batcher.js.map +1 -0
  8. package/dist/async-debouncer.cjs +340 -0
  9. package/dist/async-debouncer.cjs.map +1 -0
  10. package/dist/async-debouncer.d.cts +300 -0
  11. package/dist/async-debouncer.d.ts +300 -0
  12. package/dist/async-debouncer.js +338 -0
  13. package/dist/async-debouncer.js.map +1 -0
  14. package/dist/async-queuer.cjs +496 -0
  15. package/dist/async-queuer.cjs.map +1 -0
  16. package/dist/async-queuer.d.cts +440 -0
  17. package/dist/async-queuer.d.ts +440 -0
  18. package/dist/async-queuer.js +494 -0
  19. package/dist/async-queuer.js.map +1 -0
  20. package/dist/async-rate-limiter.cjs +381 -0
  21. package/dist/async-rate-limiter.cjs.map +1 -0
  22. package/dist/{cjs/async-rate-limiter.d.cts → async-rate-limiter.d.cts} +190 -185
  23. package/dist/{esm/async-rate-limiter.d.ts → async-rate-limiter.d.ts} +190 -185
  24. package/dist/async-rate-limiter.js +379 -0
  25. package/dist/async-rate-limiter.js.map +1 -0
  26. package/dist/async-retryer.cjs +378 -0
  27. package/dist/async-retryer.cjs.map +1 -0
  28. package/dist/async-retryer.d.cts +312 -0
  29. package/dist/async-retryer.d.ts +312 -0
  30. package/dist/async-retryer.js +376 -0
  31. package/dist/async-retryer.js.map +1 -0
  32. package/dist/async-throttler.cjs +362 -0
  33. package/dist/async-throttler.cjs.map +1 -0
  34. package/dist/async-throttler.d.cts +320 -0
  35. package/dist/async-throttler.d.ts +320 -0
  36. package/dist/async-throttler.js +360 -0
  37. package/dist/async-throttler.js.map +1 -0
  38. package/dist/batcher.cjs +194 -0
  39. package/dist/batcher.cjs.map +1 -0
  40. package/dist/batcher.d.cts +180 -0
  41. package/dist/batcher.d.ts +180 -0
  42. package/dist/batcher.js +193 -0
  43. package/dist/batcher.js.map +1 -0
  44. package/dist/debouncer.cjs +197 -0
  45. package/dist/debouncer.cjs.map +1 -0
  46. package/dist/debouncer.d.cts +167 -0
  47. package/dist/debouncer.d.ts +167 -0
  48. package/dist/debouncer.js +195 -0
  49. package/dist/debouncer.js.map +1 -0
  50. package/dist/event-client.cjs +20 -0
  51. package/dist/event-client.cjs.map +1 -0
  52. package/dist/event-client.d.cts +49 -0
  53. package/dist/event-client.d.ts +49 -0
  54. package/dist/event-client.js +19 -0
  55. package/dist/event-client.js.map +1 -0
  56. package/dist/index.cjs +49 -0
  57. package/dist/index.d.cts +15 -0
  58. package/dist/index.d.ts +15 -0
  59. package/dist/{esm/index.js → index.js} +5 -41
  60. package/dist/queuer.cjs +393 -0
  61. package/dist/queuer.cjs.map +1 -0
  62. package/dist/queuer.d.cts +345 -0
  63. package/dist/queuer.d.ts +345 -0
  64. package/dist/queuer.js +391 -0
  65. package/dist/queuer.js.map +1 -0
  66. package/dist/rate-limiter.cjs +251 -0
  67. package/dist/rate-limiter.cjs.map +1 -0
  68. package/dist/{cjs/rate-limiter.d.cts → rate-limiter.d.cts} +113 -108
  69. package/dist/{esm/rate-limiter.d.ts → rate-limiter.d.ts} +113 -108
  70. package/dist/rate-limiter.js +249 -0
  71. package/dist/rate-limiter.js.map +1 -0
  72. package/dist/throttler.cjs +209 -0
  73. package/dist/throttler.cjs.map +1 -0
  74. package/dist/throttler.d.cts +207 -0
  75. package/dist/throttler.d.ts +207 -0
  76. package/dist/throttler.js +207 -0
  77. package/dist/throttler.js.map +1 -0
  78. package/dist/types.cjs +0 -0
  79. package/dist/types.d.cts +13 -0
  80. package/dist/types.d.ts +13 -0
  81. package/dist/types.js +1 -0
  82. package/dist/utils.cjs +13 -0
  83. package/dist/utils.cjs.map +1 -0
  84. package/dist/utils.d.cts +8 -0
  85. package/dist/utils.d.ts +8 -0
  86. package/dist/utils.js +11 -0
  87. package/dist/utils.js.map +1 -0
  88. package/package.json +38 -122
  89. package/dist/cjs/async-batcher.cjs +0 -222
  90. package/dist/cjs/async-batcher.cjs.map +0 -1
  91. package/dist/cjs/async-batcher.d.cts +0 -340
  92. package/dist/cjs/async-debouncer.cjs +0 -233
  93. package/dist/cjs/async-debouncer.cjs.map +0 -1
  94. package/dist/cjs/async-debouncer.d.cts +0 -295
  95. package/dist/cjs/async-queuer.cjs +0 -399
  96. package/dist/cjs/async-queuer.cjs.map +0 -1
  97. package/dist/cjs/async-queuer.d.cts +0 -435
  98. package/dist/cjs/async-rate-limiter.cjs +0 -254
  99. package/dist/cjs/async-rate-limiter.cjs.map +0 -1
  100. package/dist/cjs/async-retryer.cjs +0 -287
  101. package/dist/cjs/async-retryer.cjs.map +0 -1
  102. package/dist/cjs/async-retryer.d.cts +0 -308
  103. package/dist/cjs/async-throttler.cjs +0 -263
  104. package/dist/cjs/async-throttler.cjs.map +0 -1
  105. package/dist/cjs/async-throttler.d.cts +0 -315
  106. package/dist/cjs/batcher.cjs +0 -132
  107. package/dist/cjs/batcher.cjs.map +0 -1
  108. package/dist/cjs/batcher.d.cts +0 -176
  109. package/dist/cjs/debouncer.cjs +0 -139
  110. package/dist/cjs/debouncer.cjs.map +0 -1
  111. package/dist/cjs/debouncer.d.cts +0 -162
  112. package/dist/cjs/event-client.cjs +0 -20
  113. package/dist/cjs/event-client.cjs.map +0 -1
  114. package/dist/cjs/event-client.d.cts +0 -45
  115. package/dist/cjs/index.cjs +0 -51
  116. package/dist/cjs/index.cjs.map +0 -1
  117. package/dist/cjs/index.d.cts +0 -15
  118. package/dist/cjs/queuer.cjs +0 -308
  119. package/dist/cjs/queuer.cjs.map +0 -1
  120. package/dist/cjs/queuer.d.cts +0 -340
  121. package/dist/cjs/rate-limiter.cjs +0 -181
  122. package/dist/cjs/rate-limiter.cjs.map +0 -1
  123. package/dist/cjs/throttler.cjs +0 -153
  124. package/dist/cjs/throttler.cjs.map +0 -1
  125. package/dist/cjs/throttler.d.cts +0 -202
  126. package/dist/cjs/types.d.cts +0 -9
  127. package/dist/cjs/utils.cjs +0 -11
  128. package/dist/cjs/utils.cjs.map +0 -1
  129. package/dist/cjs/utils.d.cts +0 -3
  130. package/dist/esm/async-batcher.d.ts +0 -340
  131. package/dist/esm/async-batcher.js +0 -222
  132. package/dist/esm/async-batcher.js.map +0 -1
  133. package/dist/esm/async-debouncer.d.ts +0 -295
  134. package/dist/esm/async-debouncer.js +0 -233
  135. package/dist/esm/async-debouncer.js.map +0 -1
  136. package/dist/esm/async-queuer.d.ts +0 -435
  137. package/dist/esm/async-queuer.js +0 -399
  138. package/dist/esm/async-queuer.js.map +0 -1
  139. package/dist/esm/async-rate-limiter.js +0 -254
  140. package/dist/esm/async-rate-limiter.js.map +0 -1
  141. package/dist/esm/async-retryer.d.ts +0 -308
  142. package/dist/esm/async-retryer.js +0 -287
  143. package/dist/esm/async-retryer.js.map +0 -1
  144. package/dist/esm/async-throttler.d.ts +0 -315
  145. package/dist/esm/async-throttler.js +0 -263
  146. package/dist/esm/async-throttler.js.map +0 -1
  147. package/dist/esm/batcher.d.ts +0 -176
  148. package/dist/esm/batcher.js +0 -132
  149. package/dist/esm/batcher.js.map +0 -1
  150. package/dist/esm/debouncer.d.ts +0 -162
  151. package/dist/esm/debouncer.js +0 -139
  152. package/dist/esm/debouncer.js.map +0 -1
  153. package/dist/esm/event-client.d.ts +0 -45
  154. package/dist/esm/event-client.js +0 -20
  155. package/dist/esm/event-client.js.map +0 -1
  156. package/dist/esm/index.d.ts +0 -15
  157. package/dist/esm/index.js.map +0 -1
  158. package/dist/esm/queuer.d.ts +0 -340
  159. package/dist/esm/queuer.js +0 -308
  160. package/dist/esm/queuer.js.map +0 -1
  161. package/dist/esm/rate-limiter.js +0 -181
  162. package/dist/esm/rate-limiter.js.map +0 -1
  163. package/dist/esm/throttler.d.ts +0 -202
  164. package/dist/esm/throttler.js +0 -153
  165. package/dist/esm/throttler.js.map +0 -1
  166. package/dist/esm/types.d.ts +0 -9
  167. package/dist/esm/utils.d.ts +0 -3
  168. package/dist/esm/utils.js +0 -11
  169. package/dist/esm/utils.js.map +0 -1
@@ -1,340 +0,0 @@
1
- import { Store } from '@tanstack/store';
2
- export interface QueuerState<TValue> {
3
- /**
4
- * Number of times addItem has been called (for reduction calculations)
5
- */
6
- addItemCount: number;
7
- /**
8
- * Number of items that have been processed by the queuer
9
- */
10
- executionCount: number;
11
- /**
12
- * Number of items that have been removed from the queue due to expiration
13
- */
14
- expirationCount: number;
15
- /**
16
- * Whether the queuer has no items to process (items array is empty)
17
- */
18
- isEmpty: boolean;
19
- /**
20
- * Whether the queuer has reached its maximum capacity
21
- */
22
- isFull: boolean;
23
- /**
24
- * Whether the queuer is not currently processing any items
25
- */
26
- isIdle: boolean;
27
- /**
28
- * Whether the queuer is active and will process items automatically
29
- */
30
- isRunning: boolean;
31
- /**
32
- * Array of items currently waiting to be processed
33
- */
34
- items: Array<TValue>;
35
- /**
36
- * Timestamps when items were added to the queue for expiration tracking
37
- */
38
- itemTimestamps: Array<number>;
39
- /**
40
- * Whether the queuer has a pending timeout for processing the next item
41
- */
42
- pendingTick: boolean;
43
- /**
44
- * Number of items that have been rejected from being added to the queue
45
- */
46
- rejectionCount: number;
47
- /**
48
- * Number of items currently in the queue
49
- */
50
- size: number;
51
- /**
52
- * Current processing status - 'idle' when not processing, 'running' when active, 'stopped' when paused
53
- */
54
- status: 'idle' | 'running' | 'stopped';
55
- }
56
- /**
57
- * Options for configuring a Queuer instance.
58
- *
59
- * These options control queue behavior, item expiration, callbacks, and more.
60
- */
61
- export interface QueuerOptions<TValue> {
62
- /**
63
- * Default position to add items to the queuer
64
- * @default 'back'
65
- */
66
- addItemsTo?: QueuePosition;
67
- /**
68
- * Maximum time in milliseconds that an item can stay in the queue
69
- * If not provided, items will never expire
70
- */
71
- expirationDuration?: number;
72
- /**
73
- * Function to determine if an item has expired
74
- * If provided, this overrides the expirationDuration behavior
75
- */
76
- getIsExpired?: (item: TValue, addedAt: number) => boolean;
77
- /**
78
- * Default position to get items from during processing
79
- * @default 'front'
80
- */
81
- getItemsFrom?: QueuePosition;
82
- /**
83
- * Function to determine priority of items in the queuer
84
- * Higher priority items will be processed first
85
- */
86
- getPriority?: (item: TValue) => number;
87
- /**
88
- * Initial items to populate the queuer with
89
- */
90
- initialItems?: Array<TValue>;
91
- /**
92
- * Initial state for the queuer
93
- */
94
- initialState?: Partial<QueuerState<TValue>>;
95
- /**
96
- * Optional key to identify this queuer instance.
97
- * If provided, the queuer will be identified by this key in the devtools and PacerProvider if applicable.
98
- */
99
- key?: string;
100
- /**
101
- * Maximum number of items allowed in the queuer
102
- */
103
- maxSize?: number;
104
- /**
105
- * Callback fired whenever an item is removed from the queuer
106
- */
107
- onExecute?: (item: TValue, queuer: Queuer<TValue>) => void;
108
- /**
109
- * Callback fired whenever an item expires in the queuer
110
- */
111
- onExpire?: (item: TValue, queuer: Queuer<TValue>) => void;
112
- /**
113
- * Callback fired whenever an item is added or removed from the queuer
114
- */
115
- onItemsChange?: (queuer: Queuer<TValue>) => void;
116
- /**
117
- * Callback fired whenever an item is rejected from being added to the queuer
118
- */
119
- onReject?: (item: TValue, queuer: Queuer<TValue>) => void;
120
- /**
121
- * Whether the queuer should start processing tasks immediately
122
- */
123
- started?: boolean;
124
- /**
125
- * Time in milliseconds to wait between processing items.
126
- * Can be a number or a function that returns a number.
127
- * @default 0
128
- */
129
- wait?: number | ((queuer: Queuer<TValue>) => number);
130
- }
131
- /**
132
- * Utility function for sharing common `QueuerOptions` options between different `Queuer` instances.
133
- */
134
- export declare function queuerOptions<TValue = any, TOptions extends Partial<QueuerOptions<TValue>> = Partial<QueuerOptions<TValue>>>(options: TOptions): TOptions;
135
- /**
136
- * Position type for addItem and getNextItem operations.
137
- *
138
- * - 'front': Operate on the front of the queue (FIFO)
139
- * - 'back': Operate on the back of the queue (LIFO)
140
- */
141
- export type QueuePosition = 'front' | 'back';
142
- /**
143
- * A flexible queue that processes items with configurable wait times, expiration, and priority.
144
- *
145
- * This synchronous version is lighter weight and often all you need - upgrade to AsyncQueuer when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.
146
- *
147
- * Features:
148
- * - Automatic or manual processing of items
149
- * - FIFO (First In First Out), LIFO (Last In First Out), or double-ended queue behavior
150
- * - Priority-based ordering when getPriority is provided
151
- * - Item expiration and removal of stale items
152
- * - Callbacks for queue state changes, execution, rejection, and expiration
153
- *
154
- * Running behavior:
155
- * - `start()`: Begins automatically processing items in the queue (defaults to isRunning)
156
- * - `stop()`: Pauses processing but maintains queue state
157
- * - `wait`: Configurable delay between processing items
158
- * - `onItemsChange`/`onExecute`: Callbacks for monitoring queue state
159
- *
160
- * Manual processing is also supported when automatic processing is disabled:
161
- * - `execute()`: Processes the next item using the provided function
162
- * - `getNextItem()`: Removes and returns the next item without processing
163
- *
164
- * Queue behavior defaults to FIFO:
165
- * - `addItem(item)`: Adds to the back of the queue
166
- * - Items processed from the front of the queue
167
- *
168
- * Priority queue:
169
- * - Provide a `getPriority` function; higher values are processed first
170
- *
171
- * Stack (LIFO):
172
- * - `addItem(item, 'back')`: Adds to the back
173
- * - `getNextItem('back')`: Removes from the back
174
- *
175
- * Double-ended queue:
176
- * - `addItem(item, position)`: Adds to specified position ('front'/'back')
177
- * - `getNextItem(position)`: Removes from specified position
178
- *
179
- * Item expiration:
180
- * - `expirationDuration`: Maximum time items can stay in the queue
181
- * - `getIsExpired`: Function to override default expiration
182
- * - `onExpire`: Callback for expired items
183
- *
184
- * State Management:
185
- * - Uses TanStack Store for reactive state management
186
- * - Use `initialState` to provide initial state values when creating the queuer
187
- * - Use `onExecute` callback to react to item execution and implement custom logic
188
- * - Use `onItemsChange` callback to react to items being added or removed from the queue
189
- * - Use `onExpire` callback to react to items expiring and implement custom logic
190
- * - Use `onReject` callback to react to items being rejected when the queue is full
191
- * - The state includes execution count, expiration count, rejection count, and isRunning status
192
- * - State can be accessed via `queuer.store.state` when using the class directly
193
- * - When using framework adapters (React/Solid), state is accessed from `queuer.state`
194
- *
195
- * Example usage:
196
- * ```ts
197
- * // Auto-processing queue with wait time
198
- * const autoQueue = new Queuer<number>((n) => console.log(n), {
199
- * started: true, // Begin processing immediately
200
- * wait: 1000, // Wait 1s between items
201
- * onExecute: (item, queuer) => console.log(`Processed ${item}`)
202
- * });
203
- * autoQueue.addItem(1); // Will process after 1s
204
- * autoQueue.addItem(2); // Will process 1s after first item
205
- *
206
- * // Manual processing queue
207
- * const manualQueue = new Queuer<number>((n) => console.log(n), {
208
- * started: false
209
- * });
210
- * manualQueue.addItem(1); // [1]
211
- * manualQueue.addItem(2); // [1, 2]
212
- * manualQueue.execute(); // logs 1, queue is [2]
213
- * manualQueue.getNextItem(); // returns 2, queue is empty
214
- * ```
215
- */
216
- export declare class Queuer<TValue> {
217
- #private;
218
- fn: (item: TValue) => void;
219
- readonly store: Store<Readonly<QueuerState<TValue>>>;
220
- key: string | undefined;
221
- options: QueuerOptions<TValue>;
222
- constructor(fn: (item: TValue) => void, initialOptions?: QueuerOptions<TValue>);
223
- /**
224
- * Updates the queuer options. New options are merged with existing options.
225
- */
226
- setOptions: (newOptions: Partial<QueuerOptions<TValue>>) => void;
227
- /**
228
- * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.
229
- * Items can be inserted based on priority or at the front/back depending on configuration.
230
- *
231
- * Returns true if the item was added, false if the queue is full.
232
- *
233
- * Example usage:
234
- * ```ts
235
- * queuer.addItem('task');
236
- * queuer.addItem('task2', 'front');
237
- * ```
238
- */
239
- addItem: (item: TValue, position?: QueuePosition, runOnItemsChange?: boolean) => boolean;
240
- /**
241
- * Removes and returns the next item from the queue without executing the function.
242
- * Use for manual queue management. Normally, use execute() to process items.
243
- *
244
- * Example usage:
245
- * ```ts
246
- * // FIFO
247
- * queuer.getNextItem();
248
- * // LIFO
249
- * queuer.getNextItem('back');
250
- * ```
251
- */
252
- getNextItem: (position?: QueuePosition) => TValue | undefined;
253
- /**
254
- * Removes and returns the next item from the queue and processes it using the provided function.
255
- *
256
- * Example usage:
257
- * ```ts
258
- * queuer.execute();
259
- * // LIFO
260
- * queuer.execute('back');
261
- * ```
262
- */
263
- execute: (position?: QueuePosition) => TValue | undefined;
264
- /**
265
- * Processes a specified number of items to execute immediately with no wait time
266
- * If no numberOfItems is provided, all items will be processed
267
- */
268
- flush: (numberOfItems?: number, position?: QueuePosition) => void;
269
- /**
270
- * Processes all items in the queue as a batch using the provided function as an argument
271
- * The queue is cleared after processing
272
- */
273
- flushAsBatch: (batchFunction: (items: Array<TValue>) => void) => void;
274
- /**
275
- * Returns the next item in the queue without removing it.
276
- *
277
- * Example usage:
278
- * ```ts
279
- * queuer.peekNextItem(); // front
280
- * queuer.peekNextItem('back'); // back
281
- * ```
282
- */
283
- peekNextItem: (position?: QueuePosition) => TValue | undefined;
284
- /**
285
- * Returns a copy of all items in the queue.
286
- */
287
- peekAllItems: () => Array<TValue>;
288
- /**
289
- * Starts processing items in the queue. If already isRunning, does nothing.
290
- */
291
- start: () => void;
292
- /**
293
- * Stops processing items in the queue. Does not clear the queue.
294
- */
295
- stop: () => void;
296
- /**
297
- * Removes all pending items from the queue. Does not affect items being processed.
298
- */
299
- clear: () => void;
300
- /**
301
- * Resets the queuer state to its default values
302
- */
303
- reset: () => void;
304
- }
305
- /**
306
- * Creates a queue that processes items immediately upon addition.
307
- * Items are processed sequentially in FIFO order by default.
308
- *
309
- * This synchronous version is lighter weight and often all you need - upgrade to asyncQueue when you need promises, retry support, abort capabilities, concurrent execution, or advanced error handling.
310
- *
311
- * State Management:
312
- * - Uses TanStack Store for reactive state management
313
- * - Use `initialState` to provide initial state values when creating the queuer
314
- * - Use `onExecute` callback to react to item execution and implement custom logic
315
- * - Use `onItemsChange` callback to react to items being added or removed from the queue
316
- * - Use `onExpire` callback to react to items expiring and implement custom logic
317
- * - Use `onReject` callback to react to items being rejected when the queue is full
318
- * - The state includes execution count, expiration count, rejection count, and isRunning status
319
- * - State can be accessed via the underlying Queuer instance's `store.state` property
320
- * - When using framework adapters (React/Solid), state is accessed from the hook's state property
321
- *
322
- * Example usage:
323
- * ```ts
324
- * // Basic sequential processing
325
- * const processItems = queue<number>((n) => console.log(n), {
326
- * wait: 1000,
327
- * onItemsChange: (queuer) => console.log(queuer.peekAllItems())
328
- * });
329
- * processItems(1); // Logs: 1
330
- * processItems(2); // Logs: 2 after 1 completes
331
- *
332
- * // Priority queue
333
- * const processPriority = queue<number>((n) => console.log(n), {
334
- * getPriority: n => n // Higher numbers processed first
335
- * });
336
- * processPriority(1);
337
- * processPriority(3); // Processed before 1
338
- * ```
339
- */
340
- export declare function queue<TValue>(fn: (item: TValue) => void, initialOptions: QueuerOptions<TValue>): (item: TValue, position?: QueuePosition, runOnItemsChange?: boolean) => boolean;
@@ -1,181 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const store = require("@tanstack/store");
4
- const utils = require("./utils.cjs");
5
- const eventClient = require("./event-client.cjs");
6
- function getDefaultRateLimiterState() {
7
- return {
8
- executionCount: 0,
9
- executionTimes: [],
10
- isExceeded: false,
11
- rejectionCount: 0,
12
- status: "idle",
13
- maybeExecuteCount: 0
14
- };
15
- }
16
- function rateLimiterOptions(options) {
17
- return options;
18
- }
19
- const defaultOptions = {
20
- enabled: true,
21
- limit: 1,
22
- window: 0,
23
- windowType: "fixed"
24
- };
25
- class RateLimiter {
26
- constructor(fn, initialOptions) {
27
- this.fn = fn;
28
- this.store = new store.Store(getDefaultRateLimiterState());
29
- this.#timeoutIds = /* @__PURE__ */ new Set();
30
- this.setOptions = (newOptions) => {
31
- this.options = { ...this.options, ...newOptions };
32
- };
33
- this.#setState = (newState) => {
34
- this.store.setState((state) => {
35
- const combinedState = {
36
- ...state,
37
- ...newState
38
- };
39
- const isExceeded = combinedState.executionTimes.length >= this.#getLimit();
40
- const status = !this.#getEnabled() ? "disabled" : isExceeded ? "exceeded" : "idle";
41
- return {
42
- ...combinedState,
43
- isExceeded,
44
- status
45
- };
46
- });
47
- eventClient.emitChange("RateLimiter", this);
48
- };
49
- this.#getEnabled = () => {
50
- return !!utils.parseFunctionOrValue(this.options.enabled, this);
51
- };
52
- this.#getLimit = () => {
53
- return utils.parseFunctionOrValue(this.options.limit, this);
54
- };
55
- this.#getWindow = () => {
56
- return utils.parseFunctionOrValue(this.options.window, this);
57
- };
58
- this.maybeExecute = (...args) => {
59
- this.#setState({
60
- maybeExecuteCount: this.store.state.maybeExecuteCount + 1
61
- });
62
- this.#cleanupOldExecutions();
63
- const relevantExecutionTimes = this.#getExecutionTimesInWindow();
64
- if (relevantExecutionTimes.length < this.#getLimit()) {
65
- this.#execute(...args);
66
- return true;
67
- }
68
- this.#setState({
69
- rejectionCount: this.store.state.rejectionCount + 1
70
- });
71
- this.options.onReject?.(this);
72
- return false;
73
- };
74
- this.#execute = (...args) => {
75
- if (!this.#getEnabled()) return;
76
- const now = Date.now();
77
- this.fn(...args);
78
- this.store.state.executionTimes.push(now);
79
- this.#setCleanupTimeout(now);
80
- this.#setState({
81
- executionCount: this.store.state.executionCount + 1
82
- });
83
- this.options.onExecute?.(args, this);
84
- };
85
- this.#getExecutionTimesInWindow = () => {
86
- if (this.options.windowType === "sliding") {
87
- return this.store.state.executionTimes.filter(
88
- (time) => time > Date.now() - this.#getWindow()
89
- );
90
- } else {
91
- if (this.store.state.executionTimes.length === 0) {
92
- return [];
93
- }
94
- const oldestExecution = Math.min(...this.store.state.executionTimes);
95
- const windowStart = oldestExecution;
96
- const windowEnd = windowStart + this.#getWindow();
97
- const now = Date.now();
98
- if (now > windowEnd) {
99
- return [];
100
- }
101
- return this.store.state.executionTimes.filter(
102
- (time) => time >= windowStart && time <= windowEnd
103
- );
104
- }
105
- };
106
- this.#setCleanupTimeout = (executionTime) => {
107
- if (this.options.windowType === "sliding" || this.#timeoutIds.size === 0) {
108
- const now = Date.now();
109
- const timeUntilExpiration = executionTime - now + this.#getWindow() + 1;
110
- const timeoutId = setTimeout(() => {
111
- this.#cleanupOldExecutions();
112
- this.#clearTimeout(timeoutId);
113
- }, timeUntilExpiration);
114
- this.#timeoutIds.add(timeoutId);
115
- }
116
- };
117
- this.#clearTimeout = (timeoutId) => {
118
- clearTimeout(timeoutId);
119
- this.#timeoutIds.delete(timeoutId);
120
- };
121
- this.#clearTimeouts = () => {
122
- this.#timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
123
- this.#timeoutIds.clear();
124
- };
125
- this.#cleanupOldExecutions = () => {
126
- this.#setState({
127
- executionTimes: this.#getExecutionTimesInWindow()
128
- });
129
- };
130
- this.getRemainingInWindow = () => {
131
- const relevantExecutionTimes = this.#getExecutionTimesInWindow();
132
- return Math.max(0, this.#getLimit() - relevantExecutionTimes.length);
133
- };
134
- this.getMsUntilNextWindow = () => {
135
- if (this.getRemainingInWindow() > 0) {
136
- return 0;
137
- }
138
- const oldestExecution = this.store.state.executionTimes[0] ?? Infinity;
139
- return oldestExecution + this.#getWindow() - Date.now();
140
- };
141
- this.reset = () => {
142
- this.#setState(getDefaultRateLimiterState());
143
- this.#clearTimeouts();
144
- };
145
- this.key = initialOptions.key;
146
- this.options = {
147
- ...defaultOptions,
148
- ...initialOptions
149
- };
150
- this.#setState(this.options.initialState ?? {});
151
- for (const executionTime of this.#getExecutionTimesInWindow()) {
152
- this.#setCleanupTimeout(executionTime);
153
- }
154
- if (this.key) {
155
- eventClient.pacerEventClient.on("d-RateLimiter", (event) => {
156
- if (event.payload.key !== this.key) return;
157
- this.#setState(event.payload.store.state);
158
- this.setOptions(event.payload.options);
159
- });
160
- }
161
- }
162
- #timeoutIds;
163
- #setState;
164
- #getEnabled;
165
- #getLimit;
166
- #getWindow;
167
- #execute;
168
- #getExecutionTimesInWindow;
169
- #setCleanupTimeout;
170
- #clearTimeout;
171
- #clearTimeouts;
172
- #cleanupOldExecutions;
173
- }
174
- function rateLimit(fn, initialOptions) {
175
- const rateLimiter = new RateLimiter(fn, initialOptions);
176
- return rateLimiter.maybeExecute;
177
- }
178
- exports.RateLimiter = RateLimiter;
179
- exports.rateLimit = rateLimit;
180
- exports.rateLimiterOptions = rateLimiterOptions;
181
- //# sourceMappingURL=rate-limiter.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.cjs","sources":["../../src/rate-limiter.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport { parseFunctionOrValue } from './utils'\nimport { emitChange, pacerEventClient } from './event-client'\nimport type { AnyFunction } from './types'\n\nexport interface RateLimiterState {\n /**\n * Number of function executions that have been completed\n */\n executionCount: number\n /**\n * Array of timestamps when executions occurred for rate limiting calculations\n */\n executionTimes: Array<number>\n /**\n * Whether the rate limiter has exceeded the limit\n */\n isExceeded: boolean\n /**\n * Number of times maybeExecute has been called (for reduction calculations)\n */\n maybeExecuteCount: number\n /**\n * Number of function executions that have been rejected due to rate limiting\n */\n rejectionCount: number\n /**\n * Current execution status - 'disabled' when not active, 'executing' when executing, 'idle' when not executing, 'exceeded' when rate limit is exceeded\n */\n status: 'disabled' | 'exceeded' | 'idle'\n}\n\nfunction getDefaultRateLimiterState(): RateLimiterState {\n return {\n executionCount: 0,\n executionTimes: [],\n isExceeded: false,\n rejectionCount: 0,\n status: 'idle',\n maybeExecuteCount: 0,\n }\n}\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 * Initial state for the rate limiter\n */\n initialState?: Partial<RateLimiterState>\n /**\n * Optional key to identify this rate limiter instance.\n * If provided, the rate limiter will be identified by this key in the devtools and PacerProvider if applicable.\n */\n key?: string\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?: (args: Parameters<TFn>, 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\n/**\n * Utility function for sharing common `RateLimiterOptions` options between different `RateLimiter` instances.\n */\nexport function rateLimiterOptions<\n TFn extends AnyFunction = AnyFunction,\n TOptions extends Partial<RateLimiterOptions<TFn>> = Partial<\n RateLimiterOptions<TFn>\n >,\n>(options: TOptions): TOptions {\n return options\n}\n\nconst defaultOptions: Omit<\n Required<RateLimiterOptions<any>>,\n 'initialState' | 'onExecute' | 'onReject' | 'key'\n> = {\n enabled: true,\n limit: 1,\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 * This synchronous version is lighter weight and often all you need - upgrade to AsyncRateLimiter when you need promises, retry support, abort capabilities, or advanced error handling.\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 * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the rate limiter\n * - Use `onExecute` callback to react to function execution and implement custom logic\n * - Use `onReject` callback to react to executions being rejected when rate limit is exceeded\n * - The state includes execution count, execution times, and rejection count\n * - State can be accessed via `rateLimiter.store.state` when using the class directly\n * - When using framework adapters (React/Solid), state is accessed from `rateLimiter.state`\n *\n * @example\n * ```ts\n * const rateLimiter = new RateLimiter(\n * (id: string) => api.getData(id),\n * {\n * limit: 5,\n * window: 1000,\n * windowType: 'sliding',\n * }\n * );\n *\n * // Will execute immediately until limit reached, then block\n * rateLimiter.maybeExecute('123');\n * ```\n */\nexport class RateLimiter<TFn extends AnyFunction> {\n readonly store: Store<Readonly<RateLimiterState>> =\n new Store<RateLimiterState>(getDefaultRateLimiterState())\n key: string | undefined\n options: RateLimiterOptions<TFn>\n #timeoutIds: Set<NodeJS.Timeout> = new Set()\n\n constructor(\n public fn: TFn,\n initialOptions: RateLimiterOptions<TFn>,\n ) {\n this.key = initialOptions.key\n this.options = {\n ...defaultOptions,\n ...initialOptions,\n }\n this.#setState(this.options.initialState ?? {})\n for (const executionTime of this.#getExecutionTimesInWindow()) {\n this.#setCleanupTimeout(executionTime)\n }\n\n if (this.key) {\n pacerEventClient.on('d-RateLimiter', (event) => {\n if (event.payload.key !== this.key) return\n this.#setState(event.payload.store.state)\n this.setOptions(event.payload.options)\n })\n }\n }\n\n /**\n * Updates the rate limiter options\n */\n setOptions = (newOptions: Partial<RateLimiterOptions<TFn>>): void => {\n this.options = { ...this.options, ...newOptions }\n }\n\n #setState = (newState: Partial<RateLimiterState>): void => {\n this.store.setState((state) => {\n const combinedState = {\n ...state,\n ...newState,\n }\n const isExceeded = combinedState.executionTimes.length >= this.#getLimit()\n const status = !this.#getEnabled()\n ? 'disabled'\n : isExceeded\n ? 'exceeded'\n : 'idle'\n return {\n ...combinedState,\n isExceeded,\n status,\n }\n })\n emitChange('RateLimiter', this)\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.#setState({\n maybeExecuteCount: this.store.state.maybeExecuteCount + 1,\n })\n\n this.#cleanupOldExecutions()\n\n const relevantExecutionTimes = this.#getExecutionTimesInWindow()\n\n if (relevantExecutionTimes.length < this.#getLimit()) {\n this.#execute(...args)\n return true\n }\n\n this.#setState({\n rejectionCount: this.store.state.rejectionCount + 1,\n })\n this.options.onReject?.(this)\n return false\n }\n\n #execute = (...args: Parameters<TFn>): void => {\n if (!this.#getEnabled()) return\n const now = Date.now()\n this.fn(...args) // EXECUTE!\n this.store.state.executionTimes.push(now) // mutate state directly for performance\n\n this.#setCleanupTimeout(now)\n\n this.#setState({\n executionCount: this.store.state.executionCount + 1,\n })\n this.options.onExecute?.(args, this)\n }\n\n #getExecutionTimesInWindow = (): Array<number> => {\n if (this.options.windowType === 'sliding') {\n // For sliding window, return all executions within the current window\n return this.store.state.executionTimes.filter(\n (time) => time > Date.now() - this.#getWindow(),\n )\n } else {\n // For fixed window, return all executions in the current window\n // The window starts from the oldest execution time\n if (this.store.state.executionTimes.length === 0) {\n return []\n }\n const oldestExecution = Math.min(...this.store.state.executionTimes)\n const windowStart = oldestExecution\n const windowEnd = windowStart + this.#getWindow()\n const now = Date.now()\n\n // If the window has expired, return empty array\n if (now > windowEnd) {\n return []\n }\n\n // Otherwise, return all executions in the current window\n return this.store.state.executionTimes.filter(\n (time) => time >= windowStart && time <= windowEnd,\n )\n }\n }\n\n #setCleanupTimeout = (executionTime: number): void => {\n if (\n this.options.windowType === 'sliding' ||\n this.#timeoutIds.size === 0 // new fixed window\n ) {\n const now = Date.now()\n const timeUntilExpiration = executionTime - now + this.#getWindow() + 1\n const timeoutId = setTimeout(() => {\n this.#cleanupOldExecutions()\n this.#clearTimeout(timeoutId)\n }, timeUntilExpiration)\n this.#timeoutIds.add(timeoutId)\n }\n }\n\n #clearTimeout = (timeoutId: NodeJS.Timeout): void => {\n clearTimeout(timeoutId)\n this.#timeoutIds.delete(timeoutId)\n }\n\n #clearTimeouts = (): void => {\n this.#timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId))\n this.#timeoutIds.clear()\n }\n\n #cleanupOldExecutions = (): void => {\n this.#setState({\n executionTimes: this.#getExecutionTimesInWindow(),\n })\n }\n\n /**\n * Returns the number of remaining executions allowed in the current window\n */\n getRemainingInWindow = (): number => {\n const relevantExecutionTimes = this.#getExecutionTimesInWindow()\n return Math.max(0, this.#getLimit() - relevantExecutionTimes.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 = this.store.state.executionTimes[0] ?? Infinity\n return oldestExecution + this.#getWindow() - Date.now()\n }\n\n /**\n * Resets the rate limiter state\n */\n reset = (): void => {\n this.#setState(getDefaultRateLimiterState())\n this.#clearTimeouts()\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 * This synchronous version is lighter weight and often all you need - upgrade to asyncRateLimit when you need promises, retry support, abort capabilities, or advanced error handling.\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 * State Management:\n * - Uses TanStack Store for reactive state management\n * - Use `initialState` to provide initial state values when creating the rate limiter\n * - Use `onExecute` callback to react to function execution and implement custom logic\n * - Use `onReject` callback to react to executions being rejected when rate limit is exceeded\n * - The state includes execution count, execution times, and rejection count\n * - State can be accessed via the underlying RateLimiter instance's `store.state` property\n * - When using framework adapters (React/Solid), state is accessed from the hook's state property\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\n}\n"],"names":["Store","emitChange","parseFunctionOrValue","pacerEventClient"],"mappings":"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"}