@tanstack/pacer-lite 0.1.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/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/cjs/index.cjs +18 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +5 -0
- package/dist/cjs/lite-batcher.cjs +92 -0
- package/dist/cjs/lite-batcher.cjs.map +1 -0
- package/dist/cjs/lite-batcher.d.cts +180 -0
- package/dist/cjs/lite-debouncer.cjs +59 -0
- package/dist/cjs/lite-debouncer.cjs.map +1 -0
- package/dist/cjs/lite-debouncer.d.cts +121 -0
- package/dist/cjs/lite-queuer.cjs +171 -0
- package/dist/cjs/lite-queuer.cjs.map +1 -0
- package/dist/cjs/lite-queuer.d.cts +239 -0
- package/dist/cjs/lite-rate-limiter.cjs +95 -0
- package/dist/cjs/lite-rate-limiter.cjs.map +1 -0
- package/dist/cjs/lite-rate-limiter.d.cts +143 -0
- package/dist/cjs/lite-throttler.cjs +63 -0
- package/dist/cjs/lite-throttler.cjs.map +1 -0
- package/dist/cjs/lite-throttler.d.cts +128 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/lite-batcher.d.ts +180 -0
- package/dist/esm/lite-batcher.js +92 -0
- package/dist/esm/lite-batcher.js.map +1 -0
- package/dist/esm/lite-debouncer.d.ts +121 -0
- package/dist/esm/lite-debouncer.js +59 -0
- package/dist/esm/lite-debouncer.js.map +1 -0
- package/dist/esm/lite-queuer.d.ts +239 -0
- package/dist/esm/lite-queuer.js +171 -0
- package/dist/esm/lite-queuer.js.map +1 -0
- package/dist/esm/lite-rate-limiter.d.ts +143 -0
- package/dist/esm/lite-rate-limiter.js +95 -0
- package/dist/esm/lite-rate-limiter.js.map +1 -0
- package/dist/esm/lite-throttler.d.ts +128 -0
- package/dist/esm/lite-throttler.js +63 -0
- package/dist/esm/lite-throttler.js.map +1 -0
- package/package.json +113 -0
- package/src/index.ts +5 -0
- package/src/lite-batcher.ts +267 -0
- package/src/lite-debouncer.ts +184 -0
- package/src/lite-queuer.ts +434 -0
- package/src/lite-rate-limiter.ts +246 -0
- package/src/lite-throttler.ts +195 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tanner Linsley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./media/header_pacer.png" >
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<br />
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@tanstack/pacer" target="\_parent">
|
|
9
|
+
<img alt="" src="https://img.shields.io/npm/dm/@tanstack/pacer.svg" alt="npm downloads" />
|
|
10
|
+
</a>
|
|
11
|
+
- <a href="https://github.com/TanStack/pacer" target="\_parent">
|
|
12
|
+
<img alt="" src="https://img.shields.io/github/stars/TanStack/pacer.svg?style=social&label=Star" alt="GitHub stars" />
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://bundlephobia.com/result?p=@tanstack/react-pacer@latest" target="\_parent">
|
|
15
|
+
<img alt="" src="https://badgen.net/bundlephobia/minzip/@tanstack/react-pacer@latest" alt="Bundle size" />
|
|
16
|
+
</a>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div align="center">
|
|
20
|
+
<a href="#badge">
|
|
21
|
+
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
|
22
|
+
</a>
|
|
23
|
+
<a href="#badge">
|
|
24
|
+
<img src="https://img.shields.io/github/v/release/tanstack/pacer" alt="Release"/>
|
|
25
|
+
</a>
|
|
26
|
+
<a href="https://twitter.com/tan_stack">
|
|
27
|
+
<img src="https://img.shields.io/twitter/follow/tan_stack.svg?style=social" alt="Follow @TanStack"/>
|
|
28
|
+
</a>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
|
|
33
|
+
### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
# TanStack Pacer
|
|
37
|
+
|
|
38
|
+
A lightweight timing and scheduling library for debouncing, throttling, rate limiting, queuing, and batching.
|
|
39
|
+
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> TanStack Pacer is currently mostly a client-side only library, but it is being designed to be able to potentially be used on the server-side as well.
|
|
42
|
+
|
|
43
|
+
- **Debouncing**
|
|
44
|
+
- Delay execution until after a period of inactivity for when you only care about the last execution in a sequence.
|
|
45
|
+
- Synchronous or Asynchronous Debounce utilities with promise support and error handling
|
|
46
|
+
- Control of leading, trailing, and enabled options
|
|
47
|
+
- **Throttling**
|
|
48
|
+
- Smoothly limit the rate at which a function can fire
|
|
49
|
+
- Synchronous or Asynchronous Throttle utilities with promise support and error handling
|
|
50
|
+
- Control of leading, trailing, and enabled options.
|
|
51
|
+
- **Rate Limiting**
|
|
52
|
+
- Limit the rate at which a function can fire over a period of time
|
|
53
|
+
- Synchronous or Asynchronous Rate Limiting utilities with promise support and error handling
|
|
54
|
+
- Fixed or Sliding Window variations of Rate Limiting
|
|
55
|
+
- **Queuing**
|
|
56
|
+
- Queue functions to be executed in a specific order
|
|
57
|
+
- Choose from FIFO, LIFO, and Priority queue implementations
|
|
58
|
+
- Control processing speed with configurable wait times or concurrency limits
|
|
59
|
+
- Manage queue execution with start/stop capabilities
|
|
60
|
+
- Expire items from the queue after a configurable duration
|
|
61
|
+
- **Batching**
|
|
62
|
+
- Chunk up multiple operations into larger batches to reduce total back-and-forth operations
|
|
63
|
+
- Batch by time period, batch size, whichever comes first, or a custom condition to trigger batch executions
|
|
64
|
+
- **Async or Sync Variations**
|
|
65
|
+
- Choose between synchronous and asynchronous versions of each utility
|
|
66
|
+
- Optional error, success, and settled handling for async variations
|
|
67
|
+
- Retry and Abort support for async variations
|
|
68
|
+
- **State Management**
|
|
69
|
+
- Uses TanStack Store under the hood for state management with fine-grained reactivity
|
|
70
|
+
- Easily integrate with your own state management library of choice
|
|
71
|
+
- Persist state to local or session storage for some utilities like rate limiting and queuing
|
|
72
|
+
- **Convenient Hooks**
|
|
73
|
+
- Reduce boilerplate code with pre-built hooks like `useDebouncedCallback`, `useThrottledValue`, and `useQueuedState`, and more.
|
|
74
|
+
- Multiple layers of abstraction to choose from depending on your use case.
|
|
75
|
+
- Works with each framework's default state management solutions, or with whatever custom state management library that you prefer.
|
|
76
|
+
- **Type Safety**
|
|
77
|
+
- Full type safety with TypeScript that makes sure that your functions will always be called with the correct arguments
|
|
78
|
+
- Generics for flexible and reusable utilities
|
|
79
|
+
- **Framework Adapters**
|
|
80
|
+
- React, Solid, and more
|
|
81
|
+
- **Tree Shaking**
|
|
82
|
+
- We, of course, get tree-shaking right for your applications by default, but we also provide extra deep imports for each utility, making it easier to embed these utilities into your libraries without increasing the bundle-phobia reports of your library.
|
|
83
|
+
|
|
84
|
+
### <a href="https://tanstack.com/pacer">Read the docs →</b></a>
|
|
85
|
+
|
|
86
|
+
<br />
|
|
87
|
+
|
|
88
|
+
> [!NOTE]
|
|
89
|
+
> You may know **TanSack Pacer** by our adapter names, too!
|
|
90
|
+
>
|
|
91
|
+
> - [**React Pacer**](https://tanstack.com/pacer/latest/docs/framework/react/react-pacer)
|
|
92
|
+
> - [**Solid Pacer**](https://tanstack.com/pacer/latest/docs/framework/solid/solid-pacer)
|
|
93
|
+
> - Angular Pacer - needs a contributor!
|
|
94
|
+
> - Preact Pacer - Coming soon! (After React Pacer is more fleshed out)
|
|
95
|
+
> - Svelte Pacer - needs a contributor!
|
|
96
|
+
> - Vue Pacer - needs a contributor!
|
|
97
|
+
|
|
98
|
+
## Get Involved
|
|
99
|
+
|
|
100
|
+
- We welcome issues and pull requests!
|
|
101
|
+
- Participate in [GitHub discussions](https://github.com/TanStack/pacer/discussions)
|
|
102
|
+
- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)
|
|
103
|
+
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions
|
|
104
|
+
|
|
105
|
+
## Partners
|
|
106
|
+
|
|
107
|
+
<table align="center">
|
|
108
|
+
<tr>
|
|
109
|
+
<td>
|
|
110
|
+
<a href="https://www.coderabbit.ai/?via=tanstack&dub_id=aCcEEdAOqqutX6OS" >
|
|
111
|
+
<picture>
|
|
112
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/coderabbit-dark-CMcuvjEy.svg" height="40" />
|
|
113
|
+
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/coderabbit-light-DVMJ2jHi.svg" height="40" />
|
|
114
|
+
<img src="https://tanstack.com/assets/coderabbit-light-DVMJ2jHi.svg" height="40" alt="CodeRabbit" />
|
|
115
|
+
</picture>
|
|
116
|
+
</a>
|
|
117
|
+
</td>
|
|
118
|
+
<td>
|
|
119
|
+
<a href="https://www.cloudflare.com?utm_source=tanstack">
|
|
120
|
+
<picture>
|
|
121
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://tanstack.com/assets/cloudflare-white-DQDB7UaL.svg" height="60" />
|
|
122
|
+
<source media="(prefers-color-scheme: light)" srcset="https://tanstack.com/assets/cloudflare-black-CPufaW0B.svg" height="60" />
|
|
123
|
+
<img src="https://tanstack.com/assets/cloudflare-black-CPufaW0B.svg" height="60" alt="Cloudflare" />
|
|
124
|
+
</picture>
|
|
125
|
+
</a>
|
|
126
|
+
</td>
|
|
127
|
+
<td>
|
|
128
|
+
<a href="https://www.unkey.com/?utm_source=tanstack">
|
|
129
|
+
<picture>
|
|
130
|
+
<source media="(prefers-color-scheme: dark)" srcset="./media/unkey_dark.svg" height="60" />
|
|
131
|
+
<source media="(prefers-color-scheme: light)" srcset="./media/unkey_logo.svg" height="60" />
|
|
132
|
+
<img src="./media/unkey_logo.svg" height="60" alt="Unkey"/>
|
|
133
|
+
</picture>
|
|
134
|
+
</a>
|
|
135
|
+
</td>
|
|
136
|
+
</tr>
|
|
137
|
+
</table>
|
|
138
|
+
|
|
139
|
+
<div align="center">
|
|
140
|
+
<img src="./media/partner_logo.svg" alt="Pacer & you?" height="65">
|
|
141
|
+
<p>
|
|
142
|
+
We're looking for TanStack Pacer Partners to join our mission! Partner with us to push the boundaries of TanStack Pacer and build amazing things together.
|
|
143
|
+
</p>
|
|
144
|
+
<a href="mailto:partners@tanstack.com?subject=TanStack Pacer Partnership"><b>LET'S CHAT</b></a>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
## Explore the TanStack Ecosystem
|
|
150
|
+
|
|
151
|
+
- <a href="https://github.com/tanstack/config"><b>TanStack Config</b></a> – Tooling for JS/TS packages
|
|
152
|
+
- <a href="https://github.com/tanstack/db"><b>TanStack DB</b></a> – Reactive sync client store
|
|
153
|
+
- <a href="https://github.com/tanstack/devtools"><b>TanStack DevTools</b></a> – Unified devtools panel
|
|
154
|
+
- <a href="https://github.com/tanstack/form"><b>TanStack Form</b></a> – Type‑safe form state
|
|
155
|
+
- <a href="https://github.com/tanstack/query"><b>TanStack Query</b></a> – Async state & caching
|
|
156
|
+
- <a href="https://github.com/tanstack/ranger"><b>TanStack Ranger</b></a> – Range & slider primitives
|
|
157
|
+
- <a href="https://github.com/tanstack/router"><b>TanStack Router</b></a> – Type‑safe routing, caching & URL state
|
|
158
|
+
- <a href="https://github.com/tanstack/router"><b>TanStack Start</b></a> – Full‑stack SSR & streaming
|
|
159
|
+
- <a href="https://github.com/tanstack/store"><b>TanStack Store</b></a> – Reactive data store
|
|
160
|
+
- <a href="https://github.com/tanstack/table"><b>TanStack Table</b></a> – Headless datagrids
|
|
161
|
+
- <a href="https://github.com/tanstack/virtual"><b>TanStack Virtual</b></a> – Virtualized rendering
|
|
162
|
+
|
|
163
|
+
… and more at <a href="https://tanstack.com"><b>TanStack.com »</b></a>
|
|
164
|
+
|
|
165
|
+
<!-- USE THE FORCE LUKE -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const liteDebouncer = require("./lite-debouncer.cjs");
|
|
4
|
+
const liteThrottler = require("./lite-throttler.cjs");
|
|
5
|
+
const liteRateLimiter = require("./lite-rate-limiter.cjs");
|
|
6
|
+
const liteQueuer = require("./lite-queuer.cjs");
|
|
7
|
+
const liteBatcher = require("./lite-batcher.cjs");
|
|
8
|
+
exports.LiteDebouncer = liteDebouncer.LiteDebouncer;
|
|
9
|
+
exports.liteDebounce = liteDebouncer.liteDebounce;
|
|
10
|
+
exports.LiteThrottler = liteThrottler.LiteThrottler;
|
|
11
|
+
exports.liteThrottle = liteThrottler.liteThrottle;
|
|
12
|
+
exports.LiteRateLimiter = liteRateLimiter.LiteRateLimiter;
|
|
13
|
+
exports.liteRateLimit = liteRateLimiter.liteRateLimit;
|
|
14
|
+
exports.LiteQueuer = liteQueuer.LiteQueuer;
|
|
15
|
+
exports.liteQueue = liteQueuer.liteQueue;
|
|
16
|
+
exports.LiteBatcher = liteBatcher.LiteBatcher;
|
|
17
|
+
exports.liteBatch = liteBatcher.liteBatch;
|
|
18
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
class LiteBatcher {
|
|
4
|
+
constructor(fn, options = {}) {
|
|
5
|
+
this.fn = fn;
|
|
6
|
+
this.options = options;
|
|
7
|
+
this.items = [];
|
|
8
|
+
this.timeoutId = null;
|
|
9
|
+
this._isPending = false;
|
|
10
|
+
this.addItem = (item) => {
|
|
11
|
+
this.items.push(item);
|
|
12
|
+
this._isPending = this.options.wait !== Infinity;
|
|
13
|
+
this.options.onItemsChange?.(this);
|
|
14
|
+
const shouldProcess = this.items.length >= this.options.maxSize || this.options.getShouldExecute(this.items, this);
|
|
15
|
+
if (shouldProcess) {
|
|
16
|
+
this.execute();
|
|
17
|
+
} else if (this.options.wait !== Infinity) {
|
|
18
|
+
this.clearTimeout();
|
|
19
|
+
this.timeoutId = setTimeout(() => this.execute(), this.getWait());
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
this.execute = () => {
|
|
23
|
+
if (this.items.length === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const batch = this.peekAllItems();
|
|
27
|
+
this.clear();
|
|
28
|
+
this.fn(batch);
|
|
29
|
+
this.options.onExecute?.(batch, this);
|
|
30
|
+
};
|
|
31
|
+
this.flush = () => {
|
|
32
|
+
this.clearTimeout();
|
|
33
|
+
this.execute();
|
|
34
|
+
};
|
|
35
|
+
this.peekAllItems = () => {
|
|
36
|
+
return [...this.items];
|
|
37
|
+
};
|
|
38
|
+
this.clearTimeout = () => {
|
|
39
|
+
if (this.timeoutId) {
|
|
40
|
+
clearTimeout(this.timeoutId);
|
|
41
|
+
this.timeoutId = null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
this.clear = () => {
|
|
45
|
+
const hadItems = this.items.length > 0;
|
|
46
|
+
this.items = [];
|
|
47
|
+
this._isPending = false;
|
|
48
|
+
if (hadItems) {
|
|
49
|
+
this.options.onItemsChange?.(this);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
this.cancel = () => {
|
|
53
|
+
this.clearTimeout();
|
|
54
|
+
this._isPending = false;
|
|
55
|
+
};
|
|
56
|
+
this.options.maxSize = this.options.maxSize ?? Infinity;
|
|
57
|
+
this.options.started = this.options.started ?? true;
|
|
58
|
+
this.options.wait = this.options.wait ?? Infinity;
|
|
59
|
+
this.options.getShouldExecute = this.options.getShouldExecute ?? (() => false);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Number of items currently in the batch
|
|
63
|
+
*/
|
|
64
|
+
get size() {
|
|
65
|
+
return this.items.length;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether the batch has no items to process (items array is empty)
|
|
69
|
+
*/
|
|
70
|
+
get isEmpty() {
|
|
71
|
+
return this.items.length === 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Whether the batcher is waiting for the timeout to trigger batch processing
|
|
75
|
+
*/
|
|
76
|
+
get isPending() {
|
|
77
|
+
return this._isPending;
|
|
78
|
+
}
|
|
79
|
+
getWait() {
|
|
80
|
+
if (typeof this.options.wait === "function") {
|
|
81
|
+
return this.options.wait(this);
|
|
82
|
+
}
|
|
83
|
+
return this.options.wait;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function liteBatch(fn, options = {}) {
|
|
87
|
+
const batcher = new LiteBatcher(fn, options);
|
|
88
|
+
return batcher.addItem;
|
|
89
|
+
}
|
|
90
|
+
exports.LiteBatcher = LiteBatcher;
|
|
91
|
+
exports.liteBatch = liteBatch;
|
|
92
|
+
//# sourceMappingURL=lite-batcher.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lite-batcher.cjs","sources":["../../src/lite-batcher.ts"],"sourcesContent":["/**\n * Options for configuring a lite batcher instance\n */\nexport interface LiteBatcherOptions<TValue> {\n /**\n * Custom function to determine if a batch should be processed\n * Return true to process the batch immediately\n */\n getShouldExecute?: (\n items: Array<TValue>,\n batcher: LiteBatcher<TValue>,\n ) => boolean\n /**\n * Maximum number of items in a batch\n * @default Infinity\n */\n maxSize?: number\n /**\n * Callback fired after a batch is processed\n */\n onExecute?: (batch: Array<TValue>, batcher: LiteBatcher<TValue>) => void\n /**\n * Callback fired after items are added to the batcher\n */\n onItemsChange?: (batcher: LiteBatcher<TValue>) => void\n /**\n * Whether the batcher should start processing immediately\n * @default true\n */\n started?: boolean\n /**\n * Maximum time in milliseconds to wait before processing a batch.\n * If the wait duration has elapsed, the batch will be processed.\n * If not provided, the batch will not be triggered by a timeout.\n * @default Infinity\n */\n wait?: number | ((batcher: LiteBatcher<TValue>) => number)\n}\n\n/**\n * A lightweight class that collects items and processes them in batches.\n *\n * This is an alternative to the Batcher in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * no callbacks, and provides only essential batching functionality.\n *\n * Batching is a technique for grouping multiple operations together to be processed as a single unit.\n * This synchronous version is lighter weight and often all you need.\n *\n * The Batcher provides a flexible way to implement batching with configurable:\n * - Maximum batch size (number of items per batch)\n * - Time-based batching (process after X milliseconds)\n * - Custom batch processing logic via getShouldExecute\n *\n * Features included:\n * - Core batching functionality (addItem, flush, clear, cancel)\n * - Size-based batching (maxSize)\n * - Time-based batching (wait timeout)\n * - Custom condition batching (getShouldExecute)\n * - Manual processing controls\n * - Public mutable options\n * - Callback support for monitoring batch execution and state changes\n *\n * Features NOT included (compared to core Batcher):\n * - No TanStack Store state management\n * - No devtools integration\n * - No complex state tracking (execution counts, etc.)\n * - No reactive state management\n *\n * @example\n * ```ts\n * // Basic batching\n * const batcher = new LiteBatcher<number>(\n * (items) => console.log('Processing batch:', items),\n * {\n * maxSize: 5,\n * wait: 2000,\n * onExecute: (batch, batcher) => {\n * console.log('Batch executed with', batch.length, 'items');\n * },\n * onItemsChange: (batcher) => {\n * console.log('Batch size changed to:', batcher.size);\n * }\n * }\n * );\n *\n * batcher.addItem(1);\n * batcher.addItem(2);\n * // After 2 seconds or when 5 items are added, whichever comes first,\n * // the batch will be processed\n * ```\n *\n * @example\n * ```ts\n * // Custom condition batching\n * const batcher = new LiteBatcher<Task>(\n * (items) => processTasks(items),\n * {\n * getShouldExecute: (items) => items.some(task => task.urgent),\n * maxSize: 10,\n * }\n * );\n *\n * batcher.addItem({ name: 'normal', urgent: false });\n * batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing\n * ```\n */\nexport class LiteBatcher<TValue> {\n private items: Array<TValue> = []\n private timeoutId: NodeJS.Timeout | null = null\n private _isPending = false\n\n constructor(\n public fn: (items: Array<TValue>) => void,\n public options: LiteBatcherOptions<TValue> = {},\n ) {\n // Set defaults\n this.options.maxSize = this.options.maxSize ?? Infinity\n this.options.started = this.options.started ?? true\n this.options.wait = this.options.wait ?? Infinity\n this.options.getShouldExecute =\n this.options.getShouldExecute ?? (() => false)\n }\n\n /**\n * Number of items currently in the batch\n */\n get size(): number {\n return this.items.length\n }\n\n /**\n * Whether the batch has no items to process (items array is empty)\n */\n get isEmpty(): boolean {\n return this.items.length === 0\n }\n\n /**\n * Whether the batcher is waiting for the timeout to trigger batch processing\n */\n get isPending(): boolean {\n return this._isPending\n }\n\n private getWait(): number {\n if (typeof this.options.wait === 'function') {\n return this.options.wait(this)\n }\n return this.options.wait!\n }\n\n /**\n * Adds an item to the batcher\n * If the batch size is reached, timeout occurs, or getShouldExecute returns true, the batch will be processed\n */\n addItem = (item: TValue): void => {\n this.items.push(item)\n this._isPending = this.options.wait !== Infinity\n this.options.onItemsChange?.(this)\n\n const shouldProcess =\n this.items.length >= this.options.maxSize! ||\n this.options.getShouldExecute!(this.items, this)\n\n if (shouldProcess) {\n this.execute()\n } else if (this.options.wait !== Infinity) {\n this.clearTimeout() // clear any pending timeout to replace it with a new one\n this.timeoutId = setTimeout(() => this.execute(), this.getWait())\n }\n }\n\n /**\n * Processes the current batch of items.\n * This method will automatically be triggered if the batcher is running and any of these conditions are met:\n * - The number of items reaches maxSize\n * - The wait duration has elapsed\n * - The getShouldExecute function returns true upon adding an item\n *\n * You can also call this method manually to process the current batch at any time.\n */\n private execute = (): void => {\n if (this.items.length === 0) {\n return\n }\n\n const batch = this.peekAllItems() // copy of the items to be processed (to prevent race conditions)\n this.clear() // Clear items before processing to prevent race conditions\n\n this.fn(batch) // EXECUTE\n this.options.onExecute?.(batch, this)\n }\n\n /**\n * Processes the current batch of items immediately\n */\n flush = (): void => {\n this.clearTimeout() // clear any pending timeout\n this.execute() // execute immediately\n }\n\n /**\n * Returns a copy of all items in the batcher\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.items]\n }\n\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = null\n }\n }\n\n /**\n * Removes all items from the batcher\n */\n clear = (): void => {\n const hadItems = this.items.length > 0\n this.items = []\n this._isPending = false\n if (hadItems) {\n this.options.onItemsChange?.(this)\n }\n }\n\n /**\n * Cancels any pending execution that was scheduled.\n * Does NOT clear out the items.\n */\n cancel = (): void => {\n this.clearTimeout()\n this._isPending = false\n }\n}\n\n/**\n * Creates a batcher that processes items in batches.\n *\n * This is an alternative to the batch function in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core version,\n * this function creates a batcher with no external dependencies, devtools integration, or reactive state.\n *\n * @example\n * ```ts\n * const batchItems = liteBatch<number>(\n * (items) => console.log('Processing:', items),\n * {\n * maxSize: 3,\n * }\n * );\n *\n * batchItems(1);\n * batchItems(2);\n * batchItems(3); // Triggers batch processing\n * ```\n */\nexport function liteBatch<TValue>(\n fn: (items: Array<TValue>) => void,\n options: LiteBatcherOptions<TValue> = {},\n): (item: TValue) => void {\n const batcher = new LiteBatcher<TValue>(fn, options)\n return batcher.addItem\n}\n"],"names":[],"mappings":";;AA4GO,MAAM,YAAoB;AAAA,EAK/B,YACS,IACA,UAAsC,IAC7C;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AANT,SAAQ,QAAuB,CAAA;AAC/B,SAAQ,YAAmC;AAC3C,SAAQ,aAAa;AA8CrB,SAAA,UAAU,CAAC,SAAuB;AAChC,WAAK,MAAM,KAAK,IAAI;AACpB,WAAK,aAAa,KAAK,QAAQ,SAAS;AACxC,WAAK,QAAQ,gBAAgB,IAAI;AAEjC,YAAM,gBACJ,KAAK,MAAM,UAAU,KAAK,QAAQ,WAClC,KAAK,QAAQ,iBAAkB,KAAK,OAAO,IAAI;AAEjD,UAAI,eAAe;AACjB,aAAK,QAAA;AAAA,MACP,WAAW,KAAK,QAAQ,SAAS,UAAU;AACzC,aAAK,aAAA;AACL,aAAK,YAAY,WAAW,MAAM,KAAK,WAAW,KAAK,SAAS;AAAA,MAClE;AAAA,IACF;AAWA,SAAQ,UAAU,MAAY;AAC5B,UAAI,KAAK,MAAM,WAAW,GAAG;AAC3B;AAAA,MACF;AAEA,YAAM,QAAQ,KAAK,aAAA;AACnB,WAAK,MAAA;AAEL,WAAK,GAAG,KAAK;AACb,WAAK,QAAQ,YAAY,OAAO,IAAI;AAAA,IACtC;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,aAAA;AACL,WAAK,QAAA;AAAA,IACP;AAKA,SAAA,eAAe,MAAqB;AAClC,aAAO,CAAC,GAAG,KAAK,KAAK;AAAA,IACvB;AAEA,SAAQ,eAAe,MAAY;AACjC,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AAKA,SAAA,QAAQ,MAAY;AAClB,YAAM,WAAW,KAAK,MAAM,SAAS;AACrC,WAAK,QAAQ,CAAA;AACb,WAAK,aAAa;AAClB,UAAI,UAAU;AACZ,aAAK,QAAQ,gBAAgB,IAAI;AAAA,MACnC;AAAA,IACF;AAMA,SAAA,SAAS,MAAY;AACnB,WAAK,aAAA;AACL,WAAK,aAAa;AAAA,IACpB;AAtHE,SAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,SAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,SAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AACzC,SAAK,QAAQ,mBACX,KAAK,QAAQ,qBAAqB,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAmB;AACrB,WAAO,KAAK,MAAM,WAAW;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,UAAkB;AACxB,QAAI,OAAO,KAAK,QAAQ,SAAS,YAAY;AAC3C,aAAO,KAAK,QAAQ,KAAK,IAAI;AAAA,IAC/B;AACA,WAAO,KAAK,QAAQ;AAAA,EACtB;AAsFF;AAuBO,SAAS,UACd,IACA,UAAsC,IACd;AACxB,QAAM,UAAU,IAAI,YAAoB,IAAI,OAAO;AACnD,SAAO,QAAQ;AACjB;;;"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for configuring a lite batcher instance
|
|
3
|
+
*/
|
|
4
|
+
export interface LiteBatcherOptions<TValue> {
|
|
5
|
+
/**
|
|
6
|
+
* Custom function to determine if a batch should be processed
|
|
7
|
+
* Return true to process the batch immediately
|
|
8
|
+
*/
|
|
9
|
+
getShouldExecute?: (items: Array<TValue>, batcher: LiteBatcher<TValue>) => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Maximum number of items in a batch
|
|
12
|
+
* @default Infinity
|
|
13
|
+
*/
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Callback fired after a batch is processed
|
|
17
|
+
*/
|
|
18
|
+
onExecute?: (batch: Array<TValue>, batcher: LiteBatcher<TValue>) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired after items are added to the batcher
|
|
21
|
+
*/
|
|
22
|
+
onItemsChange?: (batcher: LiteBatcher<TValue>) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the batcher should start processing immediately
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
started?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Maximum time in milliseconds to wait before processing a batch.
|
|
30
|
+
* If the wait duration has elapsed, the batch will be processed.
|
|
31
|
+
* If not provided, the batch will not be triggered by a timeout.
|
|
32
|
+
* @default Infinity
|
|
33
|
+
*/
|
|
34
|
+
wait?: number | ((batcher: LiteBatcher<TValue>) => number);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A lightweight class that collects items and processes them in batches.
|
|
38
|
+
*
|
|
39
|
+
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
|
|
40
|
+
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
|
|
41
|
+
* this version does not use TanStack Store for state management, has no devtools integration,
|
|
42
|
+
* no callbacks, and provides only essential batching functionality.
|
|
43
|
+
*
|
|
44
|
+
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
|
|
45
|
+
* This synchronous version is lighter weight and often all you need.
|
|
46
|
+
*
|
|
47
|
+
* The Batcher provides a flexible way to implement batching with configurable:
|
|
48
|
+
* - Maximum batch size (number of items per batch)
|
|
49
|
+
* - Time-based batching (process after X milliseconds)
|
|
50
|
+
* - Custom batch processing logic via getShouldExecute
|
|
51
|
+
*
|
|
52
|
+
* Features included:
|
|
53
|
+
* - Core batching functionality (addItem, flush, clear, cancel)
|
|
54
|
+
* - Size-based batching (maxSize)
|
|
55
|
+
* - Time-based batching (wait timeout)
|
|
56
|
+
* - Custom condition batching (getShouldExecute)
|
|
57
|
+
* - Manual processing controls
|
|
58
|
+
* - Public mutable options
|
|
59
|
+
* - Callback support for monitoring batch execution and state changes
|
|
60
|
+
*
|
|
61
|
+
* Features NOT included (compared to core Batcher):
|
|
62
|
+
* - No TanStack Store state management
|
|
63
|
+
* - No devtools integration
|
|
64
|
+
* - No complex state tracking (execution counts, etc.)
|
|
65
|
+
* - No reactive state management
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* // Basic batching
|
|
70
|
+
* const batcher = new LiteBatcher<number>(
|
|
71
|
+
* (items) => console.log('Processing batch:', items),
|
|
72
|
+
* {
|
|
73
|
+
* maxSize: 5,
|
|
74
|
+
* wait: 2000,
|
|
75
|
+
* onExecute: (batch, batcher) => {
|
|
76
|
+
* console.log('Batch executed with', batch.length, 'items');
|
|
77
|
+
* },
|
|
78
|
+
* onItemsChange: (batcher) => {
|
|
79
|
+
* console.log('Batch size changed to:', batcher.size);
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* );
|
|
83
|
+
*
|
|
84
|
+
* batcher.addItem(1);
|
|
85
|
+
* batcher.addItem(2);
|
|
86
|
+
* // After 2 seconds or when 5 items are added, whichever comes first,
|
|
87
|
+
* // the batch will be processed
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* // Custom condition batching
|
|
93
|
+
* const batcher = new LiteBatcher<Task>(
|
|
94
|
+
* (items) => processTasks(items),
|
|
95
|
+
* {
|
|
96
|
+
* getShouldExecute: (items) => items.some(task => task.urgent),
|
|
97
|
+
* maxSize: 10,
|
|
98
|
+
* }
|
|
99
|
+
* );
|
|
100
|
+
*
|
|
101
|
+
* batcher.addItem({ name: 'normal', urgent: false });
|
|
102
|
+
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare class LiteBatcher<TValue> {
|
|
106
|
+
fn: (items: Array<TValue>) => void;
|
|
107
|
+
options: LiteBatcherOptions<TValue>;
|
|
108
|
+
private items;
|
|
109
|
+
private timeoutId;
|
|
110
|
+
private _isPending;
|
|
111
|
+
constructor(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>);
|
|
112
|
+
/**
|
|
113
|
+
* Number of items currently in the batch
|
|
114
|
+
*/
|
|
115
|
+
get size(): number;
|
|
116
|
+
/**
|
|
117
|
+
* Whether the batch has no items to process (items array is empty)
|
|
118
|
+
*/
|
|
119
|
+
get isEmpty(): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Whether the batcher is waiting for the timeout to trigger batch processing
|
|
122
|
+
*/
|
|
123
|
+
get isPending(): boolean;
|
|
124
|
+
private getWait;
|
|
125
|
+
/**
|
|
126
|
+
* Adds an item to the batcher
|
|
127
|
+
* If the batch size is reached, timeout occurs, or getShouldExecute returns true, the batch will be processed
|
|
128
|
+
*/
|
|
129
|
+
addItem: (item: TValue) => void;
|
|
130
|
+
/**
|
|
131
|
+
* Processes the current batch of items.
|
|
132
|
+
* This method will automatically be triggered if the batcher is running and any of these conditions are met:
|
|
133
|
+
* - The number of items reaches maxSize
|
|
134
|
+
* - The wait duration has elapsed
|
|
135
|
+
* - The getShouldExecute function returns true upon adding an item
|
|
136
|
+
*
|
|
137
|
+
* You can also call this method manually to process the current batch at any time.
|
|
138
|
+
*/
|
|
139
|
+
private execute;
|
|
140
|
+
/**
|
|
141
|
+
* Processes the current batch of items immediately
|
|
142
|
+
*/
|
|
143
|
+
flush: () => void;
|
|
144
|
+
/**
|
|
145
|
+
* Returns a copy of all items in the batcher
|
|
146
|
+
*/
|
|
147
|
+
peekAllItems: () => Array<TValue>;
|
|
148
|
+
private clearTimeout;
|
|
149
|
+
/**
|
|
150
|
+
* Removes all items from the batcher
|
|
151
|
+
*/
|
|
152
|
+
clear: () => void;
|
|
153
|
+
/**
|
|
154
|
+
* Cancels any pending execution that was scheduled.
|
|
155
|
+
* Does NOT clear out the items.
|
|
156
|
+
*/
|
|
157
|
+
cancel: () => void;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Creates a batcher that processes items in batches.
|
|
161
|
+
*
|
|
162
|
+
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
|
|
163
|
+
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
|
|
164
|
+
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* const batchItems = liteBatch<number>(
|
|
169
|
+
* (items) => console.log('Processing:', items),
|
|
170
|
+
* {
|
|
171
|
+
* maxSize: 3,
|
|
172
|
+
* }
|
|
173
|
+
* );
|
|
174
|
+
*
|
|
175
|
+
* batchItems(1);
|
|
176
|
+
* batchItems(2);
|
|
177
|
+
* batchItems(3); // Triggers batch processing
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
export declare function liteBatch<TValue>(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>): (item: TValue) => void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
class LiteDebouncer {
|
|
4
|
+
constructor(fn, options) {
|
|
5
|
+
this.fn = fn;
|
|
6
|
+
this.options = options;
|
|
7
|
+
this.canLeadingExecute = true;
|
|
8
|
+
this.maybeExecute = (...args) => {
|
|
9
|
+
let didLeadingExecute = false;
|
|
10
|
+
if (this.options.leading && this.canLeadingExecute) {
|
|
11
|
+
this.canLeadingExecute = false;
|
|
12
|
+
didLeadingExecute = true;
|
|
13
|
+
this.fn(...args);
|
|
14
|
+
this.options.onExecute?.(args, this);
|
|
15
|
+
}
|
|
16
|
+
this.lastArgs = args;
|
|
17
|
+
if (this.timeoutId) {
|
|
18
|
+
clearTimeout(this.timeoutId);
|
|
19
|
+
}
|
|
20
|
+
this.timeoutId = setTimeout(() => {
|
|
21
|
+
this.canLeadingExecute = true;
|
|
22
|
+
if (this.options.trailing && !didLeadingExecute && this.lastArgs) {
|
|
23
|
+
this.fn(...this.lastArgs);
|
|
24
|
+
this.options.onExecute?.(this.lastArgs, this);
|
|
25
|
+
}
|
|
26
|
+
this.lastArgs = void 0;
|
|
27
|
+
}, this.options.wait);
|
|
28
|
+
};
|
|
29
|
+
this.flush = () => {
|
|
30
|
+
if (this.timeoutId && this.lastArgs) {
|
|
31
|
+
clearTimeout(this.timeoutId);
|
|
32
|
+
this.timeoutId = void 0;
|
|
33
|
+
const args = this.lastArgs;
|
|
34
|
+
this.fn(...args);
|
|
35
|
+
this.options.onExecute?.(args, this);
|
|
36
|
+
this.lastArgs = void 0;
|
|
37
|
+
this.canLeadingExecute = true;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
this.cancel = () => {
|
|
41
|
+
if (this.timeoutId) {
|
|
42
|
+
clearTimeout(this.timeoutId);
|
|
43
|
+
this.timeoutId = void 0;
|
|
44
|
+
}
|
|
45
|
+
this.lastArgs = void 0;
|
|
46
|
+
this.canLeadingExecute = true;
|
|
47
|
+
};
|
|
48
|
+
if (this.options.leading === void 0 && this.options.trailing === void 0) {
|
|
49
|
+
this.options.trailing = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function liteDebounce(fn, options) {
|
|
54
|
+
const debouncer = new LiteDebouncer(fn, options);
|
|
55
|
+
return debouncer.maybeExecute;
|
|
56
|
+
}
|
|
57
|
+
exports.LiteDebouncer = LiteDebouncer;
|
|
58
|
+
exports.liteDebounce = liteDebounce;
|
|
59
|
+
//# sourceMappingURL=lite-debouncer.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lite-debouncer.cjs","sources":["../../src/lite-debouncer.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite debounced function\n */\nexport interface LiteDebouncerOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Whether to execute on the leading edge of the timeout.\n * The first call will execute immediately and the rest will wait the delay.\n * Defaults to false.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, debouncer: LiteDebouncer<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Delay in milliseconds before executing the function.\n */\n wait: number\n}\n\n/**\n * A lightweight class that creates a debounced function.\n *\n * This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential debouncing functionality.\n *\n * Debouncing ensures that a function is only executed after a certain amount of time has passed\n * since its last invocation. This is useful for handling frequent events like window resizing,\n * scroll events, or input changes where you want to limit the rate of execution.\n *\n * The debounced function can be configured to execute either at the start of the delay period\n * (leading edge) or at the end (trailing edge, default). Each new call during the wait period\n * will reset the timer.\n *\n * Features:\n * - Zero dependencies - no external libraries required\n * - Minimal API surface - only essential methods (maybeExecute, flush, cancel)\n * - Simple state management - uses basic private properties instead of reactive stores\n * - Callback support for monitoring execution events\n * - Lightweight - designed for use in npm packages where bundle size matters\n *\n * @example\n * ```ts\n * const debouncer = new LiteDebouncer((value: string) => {\n * saveToDatabase(value);\n * }, {\n * wait: 500,\n * onExecute: (args, debouncer) => {\n * console.log('Saved value:', args[0]);\n * }\n * });\n *\n * // Will only save after 500ms of no new input\n * inputElement.addEventListener('input', () => {\n * debouncer.maybeExecute(inputElement.value);\n * });\n * ```\n */\nexport class LiteDebouncer<TFn extends AnyFunction> {\n private timeoutId: NodeJS.Timeout | undefined\n private lastArgs: Parameters<TFn> | undefined\n private canLeadingExecute = true\n\n constructor(\n public fn: TFn,\n public options: LiteDebouncerOptions<TFn>,\n ) {\n // Default trailing to true if neither leading nor trailing is specified\n if (\n this.options.leading === undefined &&\n this.options.trailing === undefined\n ) {\n this.options.trailing = true\n }\n }\n\n /**\n * Attempts to execute the debounced function.\n * If leading is true and this is the first call, executes immediately.\n * Otherwise, queues the execution for after the wait time.\n * Each new call resets the timer.\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n let didLeadingExecute = false\n\n if (this.options.leading && this.canLeadingExecute) {\n this.canLeadingExecute = false\n didLeadingExecute = true\n this.fn(...args)\n this.options.onExecute?.(args, this)\n }\n\n this.lastArgs = args\n\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n }\n\n this.timeoutId = setTimeout(() => {\n this.canLeadingExecute = true\n if (this.options.trailing && !didLeadingExecute && this.lastArgs) {\n this.fn(...this.lastArgs)\n this.options.onExecute?.(this.lastArgs, this)\n }\n this.lastArgs = undefined\n }, this.options.wait)\n }\n\n /**\n * Processes the current pending execution immediately.\n * If there's a pending execution, it will be executed right away\n * and the timeout will be cleared.\n */\n flush = (): void => {\n if (this.timeoutId && this.lastArgs) {\n clearTimeout(this.timeoutId)\n this.timeoutId = undefined\n const args = this.lastArgs\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.lastArgs = undefined\n this.canLeadingExecute = true\n }\n }\n\n /**\n * Cancels any pending execution.\n * Clears the timeout and resets the internal state.\n */\n cancel = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = undefined\n }\n this.lastArgs = undefined\n this.canLeadingExecute = true\n }\n}\n\n/**\n * Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.\n * Multiple calls during the wait period will cancel previous pending invocations and reset the timer.\n *\n * This is an alternative to the debounce function in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core version,\n * this function creates a debouncer with no external dependencies, devtools integration, or reactive state.\n *\n * If leading option is true, the function will execute immediately on the first call, then wait the delay\n * before allowing another execution.\n *\n * @example\n * ```ts\n * const debouncedSave = liteDebounce(() => {\n * saveChanges();\n * }, { wait: 1000 });\n *\n * // Called repeatedly but executes at most once per second\n * inputElement.addEventListener('input', debouncedSave);\n * ```\n *\n * @example\n * ```ts\n * // Leading edge execution - fires immediately then waits\n * const debouncedSearch = liteDebounce((query: string) => {\n * performSearch(query);\n * }, { wait: 300, leading: true });\n * ```\n */\nexport function liteDebounce<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteDebouncerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const debouncer = new LiteDebouncer(fn, options)\n return debouncer.maybeExecute\n}\n"],"names":[],"mappings":";;AAmEO,MAAM,cAAuC;AAAA,EAKlD,YACS,IACA,SACP;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AAJT,SAAQ,oBAAoB;AAqB5B,SAAA,eAAe,IAAI,SAAgC;AACjD,UAAI,oBAAoB;AAExB,UAAI,KAAK,QAAQ,WAAW,KAAK,mBAAmB;AAClD,aAAK,oBAAoB;AACzB,4BAAoB;AACpB,aAAK,GAAG,GAAG,IAAI;AACf,aAAK,QAAQ,YAAY,MAAM,IAAI;AAAA,MACrC;AAEA,WAAK,WAAW;AAEhB,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAAA,MAC7B;AAEA,WAAK,YAAY,WAAW,MAAM;AAChC,aAAK,oBAAoB;AACzB,YAAI,KAAK,QAAQ,YAAY,CAAC,qBAAqB,KAAK,UAAU;AAChE,eAAK,GAAG,GAAG,KAAK,QAAQ;AACxB,eAAK,QAAQ,YAAY,KAAK,UAAU,IAAI;AAAA,QAC9C;AACA,aAAK,WAAW;AAAA,MAClB,GAAG,KAAK,QAAQ,IAAI;AAAA,IACtB;AAOA,SAAA,QAAQ,MAAY;AAClB,UAAI,KAAK,aAAa,KAAK,UAAU;AACnC,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AACjB,cAAM,OAAO,KAAK;AAClB,aAAK,GAAG,GAAG,IAAI;AACf,aAAK,QAAQ,YAAY,MAAM,IAAI;AACnC,aAAK,WAAW;AAChB,aAAK,oBAAoB;AAAA,MAC3B;AAAA,IACF;AAMA,SAAA,SAAS,MAAY;AACnB,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AAAA,MACnB;AACA,WAAK,WAAW;AAChB,WAAK,oBAAoB;AAAA,IAC3B;AApEE,QACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,QAC1B;AACA,WAAK,QAAQ,WAAW;AAAA,IAC1B;AAAA,EACF;AA+DF;AA+BO,SAAS,aACd,IACA,SACoC;AACpC,QAAM,YAAY,IAAI,cAAc,IAAI,OAAO;AAC/C,SAAO,UAAU;AACnB;;;"}
|