@tanstack/pacer-lite 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +21 -6
  2. package/dist/lite-batcher.d.ts +4 -6
  3. package/dist/lite-batcher.js +72 -45
  4. package/dist/lite-debouncer.d.ts +4 -7
  5. package/dist/lite-debouncer.js +58 -40
  6. package/dist/lite-queuer.d.ts +5 -7
  7. package/dist/lite-queuer.js +170 -89
  8. package/dist/lite-rate-limiter.d.ts +4 -7
  9. package/dist/lite-rate-limiter.js +89 -63
  10. package/dist/lite-throttler.d.ts +4 -7
  11. package/dist/lite-throttler.js +63 -40
  12. package/dist/pacer/dist/types.d.ts +2 -7
  13. package/package.json +13 -33
  14. package/dist/index.cjs +0 -17
  15. package/dist/index.d.cts +0 -6
  16. package/dist/lite-batcher.cjs +0 -176
  17. package/dist/lite-batcher.cjs.map +0 -1
  18. package/dist/lite-batcher.d.cts +0 -184
  19. package/dist/lite-batcher.js.map +0 -1
  20. package/dist/lite-debouncer.cjs +0 -126
  21. package/dist/lite-debouncer.cjs.map +0 -1
  22. package/dist/lite-debouncer.d.cts +0 -126
  23. package/dist/lite-debouncer.js.map +0 -1
  24. package/dist/lite-queuer.cjs +0 -209
  25. package/dist/lite-queuer.cjs.map +0 -1
  26. package/dist/lite-queuer.d.cts +0 -243
  27. package/dist/lite-queuer.js.map +0 -1
  28. package/dist/lite-rate-limiter.cjs +0 -150
  29. package/dist/lite-rate-limiter.cjs.map +0 -1
  30. package/dist/lite-rate-limiter.d.cts +0 -148
  31. package/dist/lite-rate-limiter.js.map +0 -1
  32. package/dist/lite-throttler.cjs +0 -128
  33. package/dist/lite-throttler.cjs.map +0 -1
  34. package/dist/lite-throttler.d.cts +0 -133
  35. package/dist/lite-throttler.js.map +0 -1
  36. package/dist/pacer/dist/types.d.cts +0 -12
  37. package/src/index.ts +0 -5
  38. package/src/lite-batcher.ts +0 -267
  39. package/src/lite-debouncer.ts +0 -184
  40. package/src/lite-queuer.ts +0 -434
  41. package/src/lite-rate-limiter.ts +0 -246
  42. package/src/lite-throttler.ts +0 -195
@@ -1,10 +1,9 @@
1
1
  import { AnyFunction } from "./pacer/dist/types.js";
2
-
3
2
  //#region src/lite-rate-limiter.d.ts
4
3
  /**
5
4
  * Options for configuring a lite rate-limited function
6
5
  */
7
- interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
6
+ export interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
8
7
  /**
9
8
  * Maximum number of executions allowed within the time window.
10
9
  */
@@ -67,7 +66,7 @@ interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
67
66
  * }
68
67
  * ```
69
68
  */
70
- declare class LiteRateLimiter<TFn extends AnyFunction> {
69
+ export declare class LiteRateLimiter<TFn extends AnyFunction> {
71
70
  fn: TFn;
72
71
  options: LiteRateLimiterOptions<TFn>;
73
72
  private executionTimes;
@@ -142,7 +141,5 @@ declare class LiteRateLimiter<TFn extends AnyFunction> {
142
141
  * });
143
142
  * ```
144
143
  */
145
- declare function liteRateLimit<TFn extends AnyFunction>(fn: TFn, options: LiteRateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
146
- //#endregion
147
- export { LiteRateLimiter, LiteRateLimiterOptions, liteRateLimit };
148
- //# sourceMappingURL=lite-rate-limiter.d.ts.map
144
+ export declare function liteRateLimit<TFn extends AnyFunction>(fn: TFn, options: LiteRateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
145
+ //#endregion
@@ -38,72 +38,99 @@
38
38
  * ```
39
39
  */
40
40
  var LiteRateLimiter = class {
41
+ fn;
42
+ options;
43
+ executionTimes = [];
44
+ timeoutIds = /* @__PURE__ */ new Set();
41
45
  constructor(fn, options) {
42
46
  this.fn = fn;
43
47
  this.options = options;
44
- this.executionTimes = [];
45
- this.timeoutIds = /* @__PURE__ */ new Set();
46
- this.maybeExecute = (...args) => {
47
- this.cleanupOldExecutions();
48
- if (this.getExecutionTimesInWindow().length < this.options.limit) {
49
- this.execute(...args);
50
- return true;
51
- }
52
- this.options.onReject?.(this);
53
- return false;
54
- };
55
- this.execute = (...args) => {
56
- const now = Date.now();
57
- this.fn(...args);
58
- this.options.onExecute?.(args, this);
59
- this.executionTimes.push(now);
60
- this.setCleanupTimeout(now);
61
- };
62
- this.getExecutionTimesInWindow = () => {
63
- if (this.options.windowType === "sliding") return this.executionTimes.filter((time) => time > Date.now() - this.options.window);
64
- else {
65
- if (this.executionTimes.length === 0) return [];
66
- const windowStart = Math.min(...this.executionTimes);
67
- const windowEnd = windowStart + this.options.window;
68
- if (Date.now() > windowEnd) return [];
69
- return this.executionTimes.filter((time) => time >= windowStart && time <= windowEnd);
70
- }
71
- };
72
- this.setCleanupTimeout = (executionTime) => {
73
- if (this.options.windowType === "sliding" || this.timeoutIds.size === 0) {
74
- const timeUntilExpiration = executionTime - Date.now() + this.options.window + 1;
75
- const timeoutId = setTimeout(() => {
76
- this.cleanupOldExecutions();
77
- this.clearTimeout(timeoutId);
78
- }, timeUntilExpiration);
79
- this.timeoutIds.add(timeoutId);
80
- }
81
- };
82
- this.clearTimeout = (timeoutId) => {
83
- clearTimeout(timeoutId);
84
- this.timeoutIds.delete(timeoutId);
85
- };
86
- this.clearTimeouts = () => {
87
- this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
88
- this.timeoutIds.clear();
89
- };
90
- this.cleanupOldExecutions = () => {
91
- this.executionTimes = this.getExecutionTimesInWindow();
92
- };
93
- this.getRemainingInWindow = () => {
94
- const relevantExecutionTimes = this.getExecutionTimesInWindow();
95
- return Math.max(0, this.options.limit - relevantExecutionTimes.length);
96
- };
97
- this.getMsUntilNextWindow = () => {
98
- if (this.getRemainingInWindow() > 0) return 0;
99
- return (this.executionTimes[0] ?? Infinity) + this.options.window - Date.now();
100
- };
101
- this.reset = () => {
102
- this.executionTimes = [];
103
- this.clearTimeouts();
104
- };
105
48
  if (this.options.windowType === void 0) this.options.windowType = "fixed";
106
49
  }
50
+ /**
51
+ * Attempts to execute the rate-limited function if within the configured limits.
52
+ * Returns true if executed, false if rejected due to rate limiting.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });
57
+ *
58
+ * // First 5 calls return true
59
+ * rateLimiter.maybeExecute('arg1', 'arg2'); // true
60
+ *
61
+ * // Additional calls within the window return false
62
+ * rateLimiter.maybeExecute('arg1', 'arg2'); // false
63
+ * ```
64
+ */
65
+ maybeExecute = (...args) => {
66
+ this.cleanupOldExecutions();
67
+ if (this.getExecutionTimesInWindow().length < this.options.limit) {
68
+ this.execute(...args);
69
+ return true;
70
+ }
71
+ this.options.onReject?.(this);
72
+ return false;
73
+ };
74
+ execute = (...args) => {
75
+ const now = Date.now();
76
+ this.fn(...args);
77
+ this.options.onExecute?.(args, this);
78
+ this.executionTimes.push(now);
79
+ this.setCleanupTimeout(now);
80
+ };
81
+ getExecutionTimesInWindow = () => {
82
+ if (this.options.windowType === "sliding") return this.executionTimes.filter((time) => time > Date.now() - this.options.window);
83
+ else {
84
+ if (this.executionTimes.length === 0) return [];
85
+ const windowStart = Math.min(...this.executionTimes);
86
+ const windowEnd = windowStart + this.options.window;
87
+ if (Date.now() > windowEnd) return [];
88
+ return this.executionTimes.filter((time) => time >= windowStart && time <= windowEnd);
89
+ }
90
+ };
91
+ setCleanupTimeout = (executionTime) => {
92
+ if (this.options.windowType === "sliding" || this.timeoutIds.size === 0) {
93
+ const timeUntilExpiration = executionTime - Date.now() + this.options.window + 1;
94
+ const timeoutId = setTimeout(() => {
95
+ this.cleanupOldExecutions();
96
+ this.clearTimeout(timeoutId);
97
+ }, timeUntilExpiration);
98
+ this.timeoutIds.add(timeoutId);
99
+ }
100
+ };
101
+ clearTimeout = (timeoutId) => {
102
+ clearTimeout(timeoutId);
103
+ this.timeoutIds.delete(timeoutId);
104
+ };
105
+ clearTimeouts = () => {
106
+ this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
107
+ this.timeoutIds.clear();
108
+ };
109
+ cleanupOldExecutions = () => {
110
+ this.executionTimes = this.getExecutionTimesInWindow();
111
+ };
112
+ /**
113
+ * Returns the number of remaining executions allowed in the current window.
114
+ */
115
+ getRemainingInWindow = () => {
116
+ const relevantExecutionTimes = this.getExecutionTimesInWindow();
117
+ return Math.max(0, this.options.limit - relevantExecutionTimes.length);
118
+ };
119
+ /**
120
+ * Returns the number of milliseconds until the next execution will be possible.
121
+ * Returns 0 if executions are currently allowed.
122
+ */
123
+ getMsUntilNextWindow = () => {
124
+ if (this.getRemainingInWindow() > 0) return 0;
125
+ return (this.executionTimes[0] ?? Infinity) + this.options.window - Date.now();
126
+ };
127
+ /**
128
+ * Resets the rate limiter state, clearing all execution history.
129
+ */
130
+ reset = () => {
131
+ this.executionTimes = [];
132
+ this.clearTimeouts();
133
+ };
107
134
  };
108
135
  /**
109
136
  * Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
@@ -143,5 +170,4 @@ function liteRateLimit(fn, options) {
143
170
  }
144
171
 
145
172
  //#endregion
146
- export { LiteRateLimiter, liteRateLimit };
147
- //# sourceMappingURL=lite-rate-limiter.js.map
173
+ export { LiteRateLimiter, liteRateLimit };
@@ -1,10 +1,9 @@
1
1
  import { AnyFunction } from "./pacer/dist/types.js";
2
-
3
2
  //#region src/lite-throttler.d.ts
4
3
  /**
5
4
  * Options for configuring a lite throttled function
6
5
  */
7
- interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
6
+ export interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
8
7
  /**
9
8
  * Whether to execute on the leading edge of the timeout.
10
9
  * Defaults to true.
@@ -64,7 +63,7 @@ interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
64
63
  * });
65
64
  * ```
66
65
  */
67
- declare class LiteThrottler<TFn extends AnyFunction> {
66
+ export declare class LiteThrottler<TFn extends AnyFunction> {
68
67
  fn: TFn;
69
68
  options: LiteThrottlerOptions<TFn>;
70
69
  private timeoutId;
@@ -127,7 +126,5 @@ declare class LiteThrottler<TFn extends AnyFunction> {
127
126
  * }, { wait: 250, leading: true, trailing: false });
128
127
  * ```
129
128
  */
130
- declare function liteThrottle<TFn extends AnyFunction>(fn: TFn, options: LiteThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
131
- //#endregion
132
- export { LiteThrottler, LiteThrottlerOptions, liteThrottle };
133
- //# sourceMappingURL=lite-throttler.d.ts.map
129
+ export declare function liteThrottle<TFn extends AnyFunction>(fn: TFn, options: LiteThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
130
+ //#endregion
@@ -40,52 +40,76 @@
40
40
  * ```
41
41
  */
42
42
  var LiteThrottler = class {
43
+ fn;
44
+ options;
45
+ timeoutId;
46
+ lastArgs;
47
+ lastExecutionTime = 0;
48
+ isPending = false;
43
49
  constructor(fn, options) {
44
50
  this.fn = fn;
45
51
  this.options = options;
46
- this.lastExecutionTime = 0;
47
- this.isPending = false;
48
- this.maybeExecute = (...args) => {
49
- const timeSinceLastExecution = Date.now() - this.lastExecutionTime;
50
- if (this.options.leading && timeSinceLastExecution >= this.options.wait) this.execute(...args);
51
- else {
52
- this.lastArgs = args;
53
- if (!this.timeoutId && this.options.trailing) {
54
- const timeoutDuration = this.options.wait - timeSinceLastExecution;
55
- this.isPending = true;
56
- this.timeoutId = setTimeout(() => {
57
- if (this.lastArgs !== void 0) this.execute(...this.lastArgs);
58
- }, timeoutDuration);
59
- }
60
- }
61
- };
62
- this.execute = (...args) => {
63
- this.fn(...args);
64
- this.options.onExecute?.(args, this);
65
- this.lastExecutionTime = Date.now();
66
- this.clearTimeout();
67
- this.lastArgs = void 0;
68
- this.isPending = false;
69
- };
70
- this.flush = () => {
71
- if (this.isPending && this.lastArgs) this.execute(...this.lastArgs);
72
- };
73
- this.cancel = () => {
74
- this.clearTimeout();
75
- this.lastArgs = void 0;
76
- this.isPending = false;
77
- };
78
- this.clearTimeout = () => {
79
- if (this.timeoutId) {
80
- clearTimeout(this.timeoutId);
81
- this.timeoutId = void 0;
82
- }
83
- };
84
52
  if (this.options.leading === void 0 && this.options.trailing === void 0) {
85
53
  this.options.leading = true;
86
54
  this.options.trailing = true;
87
55
  }
88
56
  }
57
+ /**
58
+ * Attempts to execute the throttled function. The execution behavior depends on the throttler options:
59
+ *
60
+ * - If enough time has passed since the last execution (>= wait period):
61
+ * - With leading=true: Executes immediately
62
+ * - With leading=false: Waits for the next trailing execution
63
+ *
64
+ * - If within the wait period:
65
+ * - With trailing=true: Schedules execution for end of wait period
66
+ * - With trailing=false: Drops the execution
67
+ */
68
+ maybeExecute = (...args) => {
69
+ const timeSinceLastExecution = Date.now() - this.lastExecutionTime;
70
+ if (this.options.leading && timeSinceLastExecution >= this.options.wait) this.execute(...args);
71
+ else {
72
+ this.lastArgs = args;
73
+ if (!this.timeoutId && this.options.trailing) {
74
+ const timeoutDuration = this.options.wait - timeSinceLastExecution;
75
+ this.isPending = true;
76
+ this.timeoutId = setTimeout(() => {
77
+ if (this.lastArgs !== void 0) this.execute(...this.lastArgs);
78
+ }, timeoutDuration);
79
+ }
80
+ }
81
+ };
82
+ execute = (...args) => {
83
+ this.fn(...args);
84
+ this.options.onExecute?.(args, this);
85
+ this.lastExecutionTime = Date.now();
86
+ this.clearTimeout();
87
+ this.lastArgs = void 0;
88
+ this.isPending = false;
89
+ };
90
+ /**
91
+ * Processes the current pending execution immediately.
92
+ * If there's a pending execution, it will be executed right away
93
+ * and the timeout will be cleared.
94
+ */
95
+ flush = () => {
96
+ if (this.isPending && this.lastArgs) this.execute(...this.lastArgs);
97
+ };
98
+ /**
99
+ * Cancels any pending trailing execution and clears internal state.
100
+ * If a trailing execution is scheduled, this will prevent that execution from occurring.
101
+ */
102
+ cancel = () => {
103
+ this.clearTimeout();
104
+ this.lastArgs = void 0;
105
+ this.isPending = false;
106
+ };
107
+ clearTimeout = () => {
108
+ if (this.timeoutId) {
109
+ clearTimeout(this.timeoutId);
110
+ this.timeoutId = void 0;
111
+ }
112
+ };
89
113
  };
90
114
  /**
91
115
  * Creates a lightweight throttled function that limits how often the provided function can execute.
@@ -121,5 +145,4 @@ function liteThrottle(fn, options) {
121
145
  }
122
146
 
123
147
  //#endregion
124
- export { LiteThrottler, liteThrottle };
125
- //# sourceMappingURL=lite-throttler.js.map
148
+ export { LiteThrottler, liteThrottle };
@@ -3,10 +3,5 @@
3
3
  /**
4
4
  * Represents a function that can be called with any arguments and returns any value.
5
5
  */
6
- type AnyFunction = (...args: Array<any>) => any;
7
- /**
8
- * Represents an asynchronous function that can be called with any arguments and returns a promise.
9
- */
10
- //#endregion
11
- export { AnyFunction };
12
- //# sourceMappingURL=types.d.ts.map
6
+ export type AnyFunction = (...args: Array<any>) => any;
7
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/pacer-lite",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Lightweight utilities for debouncing, throttling, and more - designed for npm packages.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -23,46 +23,25 @@
23
23
  "minimal"
24
24
  ],
25
25
  "type": "module",
26
- "main": "./dist/index.cjs",
27
- "module": "./dist/index.js",
28
- "types": "./dist/index.d.cts",
26
+ "types": "./dist/index.d.ts",
29
27
  "exports": {
30
- ".": {
31
- "import": "./dist/index.js",
32
- "require": "./dist/index.cjs"
33
- },
34
- "./lite-batcher": {
35
- "import": "./dist/lite-batcher.js",
36
- "require": "./dist/lite-batcher.cjs"
37
- },
38
- "./lite-debouncer": {
39
- "import": "./dist/lite-debouncer.js",
40
- "require": "./dist/lite-debouncer.cjs"
41
- },
42
- "./lite-queuer": {
43
- "import": "./dist/lite-queuer.js",
44
- "require": "./dist/lite-queuer.cjs"
45
- },
46
- "./lite-rate-limiter": {
47
- "import": "./dist/lite-rate-limiter.js",
48
- "require": "./dist/lite-rate-limiter.cjs"
49
- },
50
- "./lite-throttler": {
51
- "import": "./dist/lite-throttler.js",
52
- "require": "./dist/lite-throttler.cjs"
53
- },
28
+ ".": "./dist/index.js",
29
+ "./lite-batcher": "./dist/lite-batcher.js",
30
+ "./lite-debouncer": "./dist/lite-debouncer.js",
31
+ "./lite-queuer": "./dist/lite-queuer.js",
32
+ "./lite-rate-limiter": "./dist/lite-rate-limiter.js",
33
+ "./lite-throttler": "./dist/lite-throttler.js",
54
34
  "./package.json": "./package.json"
55
35
  },
56
36
  "sideEffects": false,
57
37
  "engines": {
58
- "node": ">=18"
38
+ "node": ">=20"
59
39
  },
60
40
  "files": [
61
- "dist/",
62
- "src"
41
+ "dist"
63
42
  ],
64
43
  "devDependencies": {
65
- "@tanstack/pacer": "0.21.1"
44
+ "@tanstack/pacer": "0.23.0"
66
45
  },
67
46
  "scripts": {
68
47
  "clean": "premove ./build ./dist",
@@ -71,6 +50,7 @@
71
50
  "test:lib": "vitest",
72
51
  "test:lib:dev": "pnpm test:lib --watch",
73
52
  "test:types": "tsc",
74
- "build": "tsdown"
53
+ "build": "tsdown",
54
+ "test:build": "publint --strict && node ../../scripts/verify-package.ts"
75
55
  }
76
56
  }
package/dist/index.cjs DELETED
@@ -1,17 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_lite_debouncer = require('./lite-debouncer.cjs');
3
- const require_lite_throttler = require('./lite-throttler.cjs');
4
- const require_lite_rate_limiter = require('./lite-rate-limiter.cjs');
5
- const require_lite_queuer = require('./lite-queuer.cjs');
6
- const require_lite_batcher = require('./lite-batcher.cjs');
7
-
8
- exports.LiteBatcher = require_lite_batcher.LiteBatcher;
9
- exports.LiteDebouncer = require_lite_debouncer.LiteDebouncer;
10
- exports.LiteQueuer = require_lite_queuer.LiteQueuer;
11
- exports.LiteRateLimiter = require_lite_rate_limiter.LiteRateLimiter;
12
- exports.LiteThrottler = require_lite_throttler.LiteThrottler;
13
- exports.liteBatch = require_lite_batcher.liteBatch;
14
- exports.liteDebounce = require_lite_debouncer.liteDebounce;
15
- exports.liteQueue = require_lite_queuer.liteQueue;
16
- exports.liteRateLimit = require_lite_rate_limiter.liteRateLimit;
17
- exports.liteThrottle = require_lite_throttler.liteThrottle;
package/dist/index.d.cts DELETED
@@ -1,6 +0,0 @@
1
- import { LiteDebouncer, LiteDebouncerOptions, liteDebounce } from "./lite-debouncer.cjs";
2
- import { LiteThrottler, LiteThrottlerOptions, liteThrottle } from "./lite-throttler.cjs";
3
- import { LiteRateLimiter, LiteRateLimiterOptions, liteRateLimit } from "./lite-rate-limiter.cjs";
4
- import { LiteQueuer, LiteQueuerOptions, QueuePosition, liteQueue } from "./lite-queuer.cjs";
5
- import { LiteBatcher, LiteBatcherOptions, liteBatch } from "./lite-batcher.cjs";
6
- export { LiteBatcher, LiteBatcherOptions, LiteDebouncer, LiteDebouncerOptions, LiteQueuer, LiteQueuerOptions, LiteRateLimiter, LiteRateLimiterOptions, LiteThrottler, LiteThrottlerOptions, QueuePosition, liteBatch, liteDebounce, liteQueue, liteRateLimit, liteThrottle };
@@ -1,176 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
-
3
- //#region src/lite-batcher.ts
4
- /**
5
- * A lightweight class that collects items and processes them in batches.
6
- *
7
- * This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
8
- * suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
9
- * this version does not use TanStack Store for state management, has no devtools integration,
10
- * no callbacks, and provides only essential batching functionality.
11
- *
12
- * Batching is a technique for grouping multiple operations together to be processed as a single unit.
13
- * This synchronous version is lighter weight and often all you need.
14
- *
15
- * The Batcher provides a flexible way to implement batching with configurable:
16
- * - Maximum batch size (number of items per batch)
17
- * - Time-based batching (process after X milliseconds)
18
- * - Custom batch processing logic via getShouldExecute
19
- *
20
- * Features included:
21
- * - Core batching functionality (addItem, flush, clear, cancel)
22
- * - Size-based batching (maxSize)
23
- * - Time-based batching (wait timeout)
24
- * - Custom condition batching (getShouldExecute)
25
- * - Manual processing controls
26
- * - Public mutable options
27
- * - Callback support for monitoring batch execution and state changes
28
- *
29
- * Features NOT included (compared to core Batcher):
30
- * - No TanStack Store state management
31
- * - No devtools integration
32
- * - No complex state tracking (execution counts, etc.)
33
- * - No reactive state management
34
- *
35
- * @example
36
- * ```ts
37
- * // Basic batching
38
- * const batcher = new LiteBatcher<number>(
39
- * (items) => console.log('Processing batch:', items),
40
- * {
41
- * maxSize: 5,
42
- * wait: 2000,
43
- * onExecute: (batch, batcher) => {
44
- * console.log('Batch executed with', batch.length, 'items');
45
- * },
46
- * onItemsChange: (batcher) => {
47
- * console.log('Batch size changed to:', batcher.size);
48
- * }
49
- * }
50
- * );
51
- *
52
- * batcher.addItem(1);
53
- * batcher.addItem(2);
54
- * // After 2 seconds or when 5 items are added, whichever comes first,
55
- * // the batch will be processed
56
- * ```
57
- *
58
- * @example
59
- * ```ts
60
- * // Custom condition batching
61
- * const batcher = new LiteBatcher<Task>(
62
- * (items) => processTasks(items),
63
- * {
64
- * getShouldExecute: (items) => items.some(task => task.urgent),
65
- * maxSize: 10,
66
- * }
67
- * );
68
- *
69
- * batcher.addItem({ name: 'normal', urgent: false });
70
- * batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
71
- * ```
72
- */
73
- var LiteBatcher = class {
74
- constructor(fn, options = {}) {
75
- this.fn = fn;
76
- this.options = options;
77
- this.items = [];
78
- this.timeoutId = null;
79
- this._isPending = false;
80
- this.addItem = (item) => {
81
- this.items.push(item);
82
- this._isPending = this.options.wait !== Infinity;
83
- this.options.onItemsChange?.(this);
84
- if (this.items.length >= this.options.maxSize || this.options.getShouldExecute(this.items, this)) this.execute();
85
- else if (this.options.wait !== Infinity) {
86
- this.clearTimeout();
87
- this.timeoutId = setTimeout(() => this.execute(), this.getWait());
88
- }
89
- };
90
- this.execute = () => {
91
- if (this.items.length === 0) return;
92
- const batch = this.peekAllItems();
93
- this.clear();
94
- this.fn(batch);
95
- this.options.onExecute?.(batch, this);
96
- };
97
- this.flush = () => {
98
- this.clearTimeout();
99
- this.execute();
100
- };
101
- this.peekAllItems = () => {
102
- return [...this.items];
103
- };
104
- this.clearTimeout = () => {
105
- if (this.timeoutId) {
106
- clearTimeout(this.timeoutId);
107
- this.timeoutId = null;
108
- }
109
- };
110
- this.clear = () => {
111
- const hadItems = this.items.length > 0;
112
- this.items = [];
113
- this._isPending = false;
114
- if (hadItems) this.options.onItemsChange?.(this);
115
- };
116
- this.cancel = () => {
117
- this.clearTimeout();
118
- this._isPending = false;
119
- };
120
- this.options.maxSize = this.options.maxSize ?? Infinity;
121
- this.options.started = this.options.started ?? true;
122
- this.options.wait = this.options.wait ?? Infinity;
123
- this.options.getShouldExecute = this.options.getShouldExecute ?? (() => false);
124
- }
125
- /**
126
- * Number of items currently in the batch
127
- */
128
- get size() {
129
- return this.items.length;
130
- }
131
- /**
132
- * Whether the batch has no items to process (items array is empty)
133
- */
134
- get isEmpty() {
135
- return this.items.length === 0;
136
- }
137
- /**
138
- * Whether the batcher is waiting for the timeout to trigger batch processing
139
- */
140
- get isPending() {
141
- return this._isPending;
142
- }
143
- getWait() {
144
- if (typeof this.options.wait === "function") return this.options.wait(this);
145
- return this.options.wait;
146
- }
147
- };
148
- /**
149
- * Creates a batcher that processes items in batches.
150
- *
151
- * This is an alternative to the batch function in the core @tanstack/pacer package, but is more
152
- * suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
153
- * this function creates a batcher with no external dependencies, devtools integration, or reactive state.
154
- *
155
- * @example
156
- * ```ts
157
- * const batchItems = liteBatch<number>(
158
- * (items) => console.log('Processing:', items),
159
- * {
160
- * maxSize: 3,
161
- * }
162
- * );
163
- *
164
- * batchItems(1);
165
- * batchItems(2);
166
- * batchItems(3); // Triggers batch processing
167
- * ```
168
- */
169
- function liteBatch(fn, options = {}) {
170
- return new LiteBatcher(fn, options).addItem;
171
- }
172
-
173
- //#endregion
174
- exports.LiteBatcher = LiteBatcher;
175
- exports.liteBatch = liteBatch;
176
- //# sourceMappingURL=lite-batcher.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lite-batcher.cjs","names":[],"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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GA,IAAa,cAAb,MAAiC;CAK/B,YACE,AAAO,IACP,AAAO,UAAsC,EAAE,EAC/C;EAFO;EACA;eANsB,EAAE;mBACU;oBACtB;kBA8CV,SAAuB;GAChC,KAAK,MAAM,KAAK,KAAK;GACrB,KAAK,aAAa,KAAK,QAAQ,SAAS;GACxC,KAAK,QAAQ,gBAAgB,KAAK;GAMlC,IAHE,KAAK,MAAM,UAAU,KAAK,QAAQ,WAClC,KAAK,QAAQ,iBAAkB,KAAK,OAAO,KAAK,EAGhD,KAAK,SAAS;QACT,IAAI,KAAK,QAAQ,SAAS,UAAU;IACzC,KAAK,cAAc;IACnB,KAAK,YAAY,iBAAiB,KAAK,SAAS,EAAE,KAAK,SAAS,CAAC;;;uBAavC;GAC5B,IAAI,KAAK,MAAM,WAAW,GACxB;GAGF,MAAM,QAAQ,KAAK,cAAc;GACjC,KAAK,OAAO;GAEZ,KAAK,GAAG,MAAM;GACd,KAAK,QAAQ,YAAY,OAAO,KAAK;;qBAMnB;GAClB,KAAK,cAAc;GACnB,KAAK,SAAS;;4BAMoB;GAClC,OAAO,CAAC,GAAG,KAAK,MAAM;;4BAGW;GACjC,IAAI,KAAK,WAAW;IAClB,aAAa,KAAK,UAAU;IAC5B,KAAK,YAAY;;;qBAOD;GAClB,MAAM,WAAW,KAAK,MAAM,SAAS;GACrC,KAAK,QAAQ,EAAE;GACf,KAAK,aAAa;GAClB,IAAI,UACF,KAAK,QAAQ,gBAAgB,KAAK;;sBAQjB;GACnB,KAAK,cAAc;GACnB,KAAK,aAAa;;EArHlB,KAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;EAC/C,KAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;EAC/C,KAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;EACzC,KAAK,QAAQ,mBACX,KAAK,QAAQ,2BAA2B;;;;;CAM5C,IAAI,OAAe;EACjB,OAAO,KAAK,MAAM;;;;;CAMpB,IAAI,UAAmB;EACrB,OAAO,KAAK,MAAM,WAAW;;;;;CAM/B,IAAI,YAAqB;EACvB,OAAO,KAAK;;CAGd,AAAQ,UAAkB;EACxB,IAAI,OAAO,KAAK,QAAQ,SAAS,YAC/B,OAAO,KAAK,QAAQ,KAAK,KAAK;EAEhC,OAAO,KAAK,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;AA8GxB,SAAgB,UACd,IACA,UAAsC,EAAE,EAChB;CAExB,OAAO,IADa,YAAoB,IAAI,QAC9B,CAAC"}