Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@tanstack/pacer-lite

Package Overview
Dependencies
Maintainers
2
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tanstack/pacer-lite - npm Package Compare versions

Comparing version
0.1.0
to
0.1.1
+16
dist/index.cjs
const require_lite_debouncer = require('./lite-debouncer.cjs');
const require_lite_throttler = require('./lite-throttler.cjs');
const require_lite_rate_limiter = require('./lite-rate-limiter.cjs');
const require_lite_queuer = require('./lite-queuer.cjs');
const require_lite_batcher = require('./lite-batcher.cjs');
exports.LiteBatcher = require_lite_batcher.LiteBatcher;
exports.LiteDebouncer = require_lite_debouncer.LiteDebouncer;
exports.LiteQueuer = require_lite_queuer.LiteQueuer;
exports.LiteRateLimiter = require_lite_rate_limiter.LiteRateLimiter;
exports.LiteThrottler = require_lite_throttler.LiteThrottler;
exports.liteBatch = require_lite_batcher.liteBatch;
exports.liteDebounce = require_lite_debouncer.liteDebounce;
exports.liteQueue = require_lite_queuer.liteQueue;
exports.liteRateLimit = require_lite_rate_limiter.liteRateLimit;
exports.liteThrottle = require_lite_throttler.liteThrottle;
import { LiteDebouncer, LiteDebouncerOptions, liteDebounce } from "./lite-debouncer.cjs";
import { LiteThrottler, LiteThrottlerOptions, liteThrottle } from "./lite-throttler.cjs";
import { LiteRateLimiter, LiteRateLimiterOptions, liteRateLimit } from "./lite-rate-limiter.cjs";
import { LiteQueuer, LiteQueuerOptions, QueuePosition, liteQueue } from "./lite-queuer.cjs";
import { LiteBatcher, LiteBatcherOptions, liteBatch } from "./lite-batcher.cjs";
export { LiteBatcher, LiteBatcherOptions, LiteDebouncer, LiteDebouncerOptions, LiteQueuer, LiteQueuerOptions, LiteRateLimiter, LiteRateLimiterOptions, LiteThrottler, LiteThrottlerOptions, QueuePosition, liteBatch, liteDebounce, liteQueue, liteRateLimit, liteThrottle };
import { LiteDebouncer, LiteDebouncerOptions, liteDebounce } from "./lite-debouncer.js";
import { LiteThrottler, LiteThrottlerOptions, liteThrottle } from "./lite-throttler.js";
import { LiteRateLimiter, LiteRateLimiterOptions, liteRateLimit } from "./lite-rate-limiter.js";
import { LiteQueuer, LiteQueuerOptions, QueuePosition, liteQueue } from "./lite-queuer.js";
import { LiteBatcher, LiteBatcherOptions, liteBatch } from "./lite-batcher.js";
export { LiteBatcher, LiteBatcherOptions, LiteDebouncer, LiteDebouncerOptions, LiteQueuer, LiteQueuerOptions, LiteRateLimiter, LiteRateLimiterOptions, LiteThrottler, LiteThrottlerOptions, QueuePosition, liteBatch, liteDebounce, liteQueue, liteRateLimit, liteThrottle };
import { LiteDebouncer, liteDebounce } from "./lite-debouncer.js";
import { LiteThrottler, liteThrottle } from "./lite-throttler.js";
import { LiteRateLimiter, liteRateLimit } from "./lite-rate-limiter.js";
import { LiteQueuer, liteQueue } from "./lite-queuer.js";
import { LiteBatcher, liteBatch } from "./lite-batcher.js";
export { LiteBatcher, LiteDebouncer, LiteQueuer, LiteRateLimiter, LiteThrottler, liteBatch, liteDebounce, liteQueue, liteRateLimit, liteThrottle };
//#region src/lite-batcher.ts
/**
* A lightweight class that collects items and processes them in batches.
*
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential batching functionality.
*
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
* This synchronous version is lighter weight and often all you need.
*
* The Batcher provides a flexible way to implement batching with configurable:
* - Maximum batch size (number of items per batch)
* - Time-based batching (process after X milliseconds)
* - Custom batch processing logic via getShouldExecute
*
* Features included:
* - Core batching functionality (addItem, flush, clear, cancel)
* - Size-based batching (maxSize)
* - Time-based batching (wait timeout)
* - Custom condition batching (getShouldExecute)
* - Manual processing controls
* - Public mutable options
* - Callback support for monitoring batch execution and state changes
*
* Features NOT included (compared to core Batcher):
* - No TanStack Store state management
* - No devtools integration
* - No complex state tracking (execution counts, etc.)
* - No reactive state management
*
* @example
* ```ts
* // Basic batching
* const batcher = new LiteBatcher<number>(
* (items) => console.log('Processing batch:', items),
* {
* maxSize: 5,
* wait: 2000,
* onExecute: (batch, batcher) => {
* console.log('Batch executed with', batch.length, 'items');
* },
* onItemsChange: (batcher) => {
* console.log('Batch size changed to:', batcher.size);
* }
* }
* );
*
* batcher.addItem(1);
* batcher.addItem(2);
* // After 2 seconds or when 5 items are added, whichever comes first,
* // the batch will be processed
* ```
*
* @example
* ```ts
* // Custom condition batching
* const batcher = new LiteBatcher<Task>(
* (items) => processTasks(items),
* {
* getShouldExecute: (items) => items.some(task => task.urgent),
* maxSize: 10,
* }
* );
*
* batcher.addItem({ name: 'normal', urgent: false });
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
* ```
*/
var LiteBatcher = class {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this._isPending = false;
this.addItem = (item) => {
this.items.push(item);
this._isPending = this.options.wait !== Infinity;
this.options.onItemsChange?.(this);
if (this.items.length >= this.options.maxSize || this.options.getShouldExecute(this.items, this)) this.execute();
else if (this.options.wait !== Infinity) {
this.clearTimeout();
this.timeoutId = setTimeout(() => this.execute(), this.getWait());
}
};
this.execute = () => {
if (this.items.length === 0) return;
const batch = this.peekAllItems();
this.clear();
this.fn(batch);
this.options.onExecute?.(batch, this);
};
this.flush = () => {
this.clearTimeout();
this.execute();
};
this.peekAllItems = () => {
return [...this.items];
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.clear = () => {
const hadItems = this.items.length > 0;
this.items = [];
this._isPending = false;
if (hadItems) this.options.onItemsChange?.(this);
};
this.cancel = () => {
this.clearTimeout();
this._isPending = false;
};
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? Infinity;
this.options.getShouldExecute = this.options.getShouldExecute ?? (() => false);
}
/**
* Number of items currently in the batch
*/
get size() {
return this.items.length;
}
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending() {
return this._isPending;
}
getWait() {
if (typeof this.options.wait === "function") return this.options.wait(this);
return this.options.wait;
}
};
/**
* Creates a batcher that processes items in batches.
*
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const batchItems = liteBatch<number>(
* (items) => console.log('Processing:', items),
* {
* maxSize: 3,
* }
* );
*
* batchItems(1);
* batchItems(2);
* batchItems(3); // Triggers batch processing
* ```
*/
function liteBatch(fn, options = {}) {
return new LiteBatcher(fn, options).addItem;
}
//#endregion
exports.LiteBatcher = LiteBatcher;
exports.liteBatch = liteBatch;
//# sourceMappingURL=lite-batcher.cjs.map
{"version":3,"file":"lite-batcher.cjs","names":["fn: (items: Array<TValue>) => void","options: LiteBatcherOptions<TValue>"],"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,AAAOA,IACP,AAAOC,UAAsC,EAAE,EAC/C;EAFO;EACA;eANsB,EAAE;mBACU;oBACtB;kBA8CV,SAAuB;AAChC,QAAK,MAAM,KAAK,KAAK;AACrB,QAAK,aAAa,KAAK,QAAQ,SAAS;AACxC,QAAK,QAAQ,gBAAgB,KAAK;AAMlC,OAHE,KAAK,MAAM,UAAU,KAAK,QAAQ,WAClC,KAAK,QAAQ,iBAAkB,KAAK,OAAO,KAAK,CAGhD,MAAK,SAAS;YACL,KAAK,QAAQ,SAAS,UAAU;AACzC,SAAK,cAAc;AACnB,SAAK,YAAY,iBAAiB,KAAK,SAAS,EAAE,KAAK,SAAS,CAAC;;;uBAavC;AAC5B,OAAI,KAAK,MAAM,WAAW,EACxB;GAGF,MAAM,QAAQ,KAAK,cAAc;AACjC,QAAK,OAAO;AAEZ,QAAK,GAAG,MAAM;AACd,QAAK,QAAQ,YAAY,OAAO,KAAK;;qBAMnB;AAClB,QAAK,cAAc;AACnB,QAAK,SAAS;;4BAMoB;AAClC,UAAO,CAAC,GAAG,KAAK,MAAM;;4BAGW;AACjC,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;;qBAOD;GAClB,MAAM,WAAW,KAAK,MAAM,SAAS;AACrC,QAAK,QAAQ,EAAE;AACf,QAAK,aAAa;AAClB,OAAI,SACF,MAAK,QAAQ,gBAAgB,KAAK;;sBAQjB;AACnB,QAAK,cAAc;AACnB,QAAK,aAAa;;AArHlB,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AACzC,OAAK,QAAQ,mBACX,KAAK,QAAQ,2BAA2B;;;;;CAM5C,IAAI,OAAe;AACjB,SAAO,KAAK,MAAM;;;;;CAMpB,IAAI,UAAmB;AACrB,SAAO,KAAK,MAAM,WAAW;;;;;CAM/B,IAAI,YAAqB;AACvB,SAAO,KAAK;;CAGd,AAAQ,UAAkB;AACxB,MAAI,OAAO,KAAK,QAAQ,SAAS,WAC/B,QAAO,KAAK,QAAQ,KAAK,KAAK;AAEhC,SAAO,KAAK,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;AA8GxB,SAAgB,UACd,IACA,UAAsC,EAAE,EAChB;AAExB,QADgB,IAAI,YAAoB,IAAI,QAAQ,CACrC"}
//#region src/lite-batcher.d.ts
/**
* Options for configuring a lite batcher instance
*/
interface LiteBatcherOptions<TValue> {
/**
* Custom function to determine if a batch should be processed
* Return true to process the batch immediately
*/
getShouldExecute?: (items: Array<TValue>, batcher: LiteBatcher<TValue>) => boolean;
/**
* Maximum number of items in a batch
* @default Infinity
*/
maxSize?: number;
/**
* Callback fired after a batch is processed
*/
onExecute?: (batch: Array<TValue>, batcher: LiteBatcher<TValue>) => void;
/**
* Callback fired after items are added to the batcher
*/
onItemsChange?: (batcher: LiteBatcher<TValue>) => void;
/**
* Whether the batcher should start processing immediately
* @default true
*/
started?: boolean;
/**
* Maximum time in milliseconds to wait before processing a batch.
* If the wait duration has elapsed, the batch will be processed.
* If not provided, the batch will not be triggered by a timeout.
* @default Infinity
*/
wait?: number | ((batcher: LiteBatcher<TValue>) => number);
}
/**
* A lightweight class that collects items and processes them in batches.
*
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential batching functionality.
*
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
* This synchronous version is lighter weight and often all you need.
*
* The Batcher provides a flexible way to implement batching with configurable:
* - Maximum batch size (number of items per batch)
* - Time-based batching (process after X milliseconds)
* - Custom batch processing logic via getShouldExecute
*
* Features included:
* - Core batching functionality (addItem, flush, clear, cancel)
* - Size-based batching (maxSize)
* - Time-based batching (wait timeout)
* - Custom condition batching (getShouldExecute)
* - Manual processing controls
* - Public mutable options
* - Callback support for monitoring batch execution and state changes
*
* Features NOT included (compared to core Batcher):
* - No TanStack Store state management
* - No devtools integration
* - No complex state tracking (execution counts, etc.)
* - No reactive state management
*
* @example
* ```ts
* // Basic batching
* const batcher = new LiteBatcher<number>(
* (items) => console.log('Processing batch:', items),
* {
* maxSize: 5,
* wait: 2000,
* onExecute: (batch, batcher) => {
* console.log('Batch executed with', batch.length, 'items');
* },
* onItemsChange: (batcher) => {
* console.log('Batch size changed to:', batcher.size);
* }
* }
* );
*
* batcher.addItem(1);
* batcher.addItem(2);
* // After 2 seconds or when 5 items are added, whichever comes first,
* // the batch will be processed
* ```
*
* @example
* ```ts
* // Custom condition batching
* const batcher = new LiteBatcher<Task>(
* (items) => processTasks(items),
* {
* getShouldExecute: (items) => items.some(task => task.urgent),
* maxSize: 10,
* }
* );
*
* batcher.addItem({ name: 'normal', urgent: false });
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
* ```
*/
declare class LiteBatcher<TValue> {
fn: (items: Array<TValue>) => void;
options: LiteBatcherOptions<TValue>;
private items;
private timeoutId;
private _isPending;
constructor(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>);
/**
* Number of items currently in the batch
*/
get size(): number;
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty(): boolean;
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending(): boolean;
private getWait;
/**
* Adds an item to the batcher
* If the batch size is reached, timeout occurs, or getShouldExecute returns true, the batch will be processed
*/
addItem: (item: TValue) => void;
/**
* Processes the current batch of items.
* This method will automatically be triggered if the batcher is running and any of these conditions are met:
* - The number of items reaches maxSize
* - The wait duration has elapsed
* - The getShouldExecute function returns true upon adding an item
*
* You can also call this method manually to process the current batch at any time.
*/
private execute;
/**
* Processes the current batch of items immediately
*/
flush: () => void;
/**
* Returns a copy of all items in the batcher
*/
peekAllItems: () => Array<TValue>;
private clearTimeout;
/**
* Removes all items from the batcher
*/
clear: () => void;
/**
* Cancels any pending execution that was scheduled.
* Does NOT clear out the items.
*/
cancel: () => void;
}
/**
* Creates a batcher that processes items in batches.
*
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const batchItems = liteBatch<number>(
* (items) => console.log('Processing:', items),
* {
* maxSize: 3,
* }
* );
*
* batchItems(1);
* batchItems(2);
* batchItems(3); // Triggers batch processing
* ```
*/
declare function liteBatch<TValue>(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>): (item: TValue) => void;
//#endregion
export { LiteBatcher, LiteBatcherOptions, liteBatch };
//# sourceMappingURL=lite-batcher.d.cts.map
//#region src/lite-batcher.d.ts
/**
* Options for configuring a lite batcher instance
*/
interface LiteBatcherOptions<TValue> {
/**
* Custom function to determine if a batch should be processed
* Return true to process the batch immediately
*/
getShouldExecute?: (items: Array<TValue>, batcher: LiteBatcher<TValue>) => boolean;
/**
* Maximum number of items in a batch
* @default Infinity
*/
maxSize?: number;
/**
* Callback fired after a batch is processed
*/
onExecute?: (batch: Array<TValue>, batcher: LiteBatcher<TValue>) => void;
/**
* Callback fired after items are added to the batcher
*/
onItemsChange?: (batcher: LiteBatcher<TValue>) => void;
/**
* Whether the batcher should start processing immediately
* @default true
*/
started?: boolean;
/**
* Maximum time in milliseconds to wait before processing a batch.
* If the wait duration has elapsed, the batch will be processed.
* If not provided, the batch will not be triggered by a timeout.
* @default Infinity
*/
wait?: number | ((batcher: LiteBatcher<TValue>) => number);
}
/**
* A lightweight class that collects items and processes them in batches.
*
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential batching functionality.
*
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
* This synchronous version is lighter weight and often all you need.
*
* The Batcher provides a flexible way to implement batching with configurable:
* - Maximum batch size (number of items per batch)
* - Time-based batching (process after X milliseconds)
* - Custom batch processing logic via getShouldExecute
*
* Features included:
* - Core batching functionality (addItem, flush, clear, cancel)
* - Size-based batching (maxSize)
* - Time-based batching (wait timeout)
* - Custom condition batching (getShouldExecute)
* - Manual processing controls
* - Public mutable options
* - Callback support for monitoring batch execution and state changes
*
* Features NOT included (compared to core Batcher):
* - No TanStack Store state management
* - No devtools integration
* - No complex state tracking (execution counts, etc.)
* - No reactive state management
*
* @example
* ```ts
* // Basic batching
* const batcher = new LiteBatcher<number>(
* (items) => console.log('Processing batch:', items),
* {
* maxSize: 5,
* wait: 2000,
* onExecute: (batch, batcher) => {
* console.log('Batch executed with', batch.length, 'items');
* },
* onItemsChange: (batcher) => {
* console.log('Batch size changed to:', batcher.size);
* }
* }
* );
*
* batcher.addItem(1);
* batcher.addItem(2);
* // After 2 seconds or when 5 items are added, whichever comes first,
* // the batch will be processed
* ```
*
* @example
* ```ts
* // Custom condition batching
* const batcher = new LiteBatcher<Task>(
* (items) => processTasks(items),
* {
* getShouldExecute: (items) => items.some(task => task.urgent),
* maxSize: 10,
* }
* );
*
* batcher.addItem({ name: 'normal', urgent: false });
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
* ```
*/
declare class LiteBatcher<TValue> {
fn: (items: Array<TValue>) => void;
options: LiteBatcherOptions<TValue>;
private items;
private timeoutId;
private _isPending;
constructor(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>);
/**
* Number of items currently in the batch
*/
get size(): number;
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty(): boolean;
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending(): boolean;
private getWait;
/**
* Adds an item to the batcher
* If the batch size is reached, timeout occurs, or getShouldExecute returns true, the batch will be processed
*/
addItem: (item: TValue) => void;
/**
* Processes the current batch of items.
* This method will automatically be triggered if the batcher is running and any of these conditions are met:
* - The number of items reaches maxSize
* - The wait duration has elapsed
* - The getShouldExecute function returns true upon adding an item
*
* You can also call this method manually to process the current batch at any time.
*/
private execute;
/**
* Processes the current batch of items immediately
*/
flush: () => void;
/**
* Returns a copy of all items in the batcher
*/
peekAllItems: () => Array<TValue>;
private clearTimeout;
/**
* Removes all items from the batcher
*/
clear: () => void;
/**
* Cancels any pending execution that was scheduled.
* Does NOT clear out the items.
*/
cancel: () => void;
}
/**
* Creates a batcher that processes items in batches.
*
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const batchItems = liteBatch<number>(
* (items) => console.log('Processing:', items),
* {
* maxSize: 3,
* }
* );
*
* batchItems(1);
* batchItems(2);
* batchItems(3); // Triggers batch processing
* ```
*/
declare function liteBatch<TValue>(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>): (item: TValue) => void;
//#endregion
export { LiteBatcher, LiteBatcherOptions, liteBatch };
//# sourceMappingURL=lite-batcher.d.ts.map
//#region src/lite-batcher.ts
/**
* A lightweight class that collects items and processes them in batches.
*
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential batching functionality.
*
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
* This synchronous version is lighter weight and often all you need.
*
* The Batcher provides a flexible way to implement batching with configurable:
* - Maximum batch size (number of items per batch)
* - Time-based batching (process after X milliseconds)
* - Custom batch processing logic via getShouldExecute
*
* Features included:
* - Core batching functionality (addItem, flush, clear, cancel)
* - Size-based batching (maxSize)
* - Time-based batching (wait timeout)
* - Custom condition batching (getShouldExecute)
* - Manual processing controls
* - Public mutable options
* - Callback support for monitoring batch execution and state changes
*
* Features NOT included (compared to core Batcher):
* - No TanStack Store state management
* - No devtools integration
* - No complex state tracking (execution counts, etc.)
* - No reactive state management
*
* @example
* ```ts
* // Basic batching
* const batcher = new LiteBatcher<number>(
* (items) => console.log('Processing batch:', items),
* {
* maxSize: 5,
* wait: 2000,
* onExecute: (batch, batcher) => {
* console.log('Batch executed with', batch.length, 'items');
* },
* onItemsChange: (batcher) => {
* console.log('Batch size changed to:', batcher.size);
* }
* }
* );
*
* batcher.addItem(1);
* batcher.addItem(2);
* // After 2 seconds or when 5 items are added, whichever comes first,
* // the batch will be processed
* ```
*
* @example
* ```ts
* // Custom condition batching
* const batcher = new LiteBatcher<Task>(
* (items) => processTasks(items),
* {
* getShouldExecute: (items) => items.some(task => task.urgent),
* maxSize: 10,
* }
* );
*
* batcher.addItem({ name: 'normal', urgent: false });
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
* ```
*/
var LiteBatcher = class {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this._isPending = false;
this.addItem = (item) => {
this.items.push(item);
this._isPending = this.options.wait !== Infinity;
this.options.onItemsChange?.(this);
if (this.items.length >= this.options.maxSize || this.options.getShouldExecute(this.items, this)) this.execute();
else if (this.options.wait !== Infinity) {
this.clearTimeout();
this.timeoutId = setTimeout(() => this.execute(), this.getWait());
}
};
this.execute = () => {
if (this.items.length === 0) return;
const batch = this.peekAllItems();
this.clear();
this.fn(batch);
this.options.onExecute?.(batch, this);
};
this.flush = () => {
this.clearTimeout();
this.execute();
};
this.peekAllItems = () => {
return [...this.items];
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.clear = () => {
const hadItems = this.items.length > 0;
this.items = [];
this._isPending = false;
if (hadItems) this.options.onItemsChange?.(this);
};
this.cancel = () => {
this.clearTimeout();
this._isPending = false;
};
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? Infinity;
this.options.getShouldExecute = this.options.getShouldExecute ?? (() => false);
}
/**
* Number of items currently in the batch
*/
get size() {
return this.items.length;
}
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending() {
return this._isPending;
}
getWait() {
if (typeof this.options.wait === "function") return this.options.wait(this);
return this.options.wait;
}
};
/**
* Creates a batcher that processes items in batches.
*
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const batchItems = liteBatch<number>(
* (items) => console.log('Processing:', items),
* {
* maxSize: 3,
* }
* );
*
* batchItems(1);
* batchItems(2);
* batchItems(3); // Triggers batch processing
* ```
*/
function liteBatch(fn, options = {}) {
return new LiteBatcher(fn, options).addItem;
}
//#endregion
export { LiteBatcher, liteBatch };
//# sourceMappingURL=lite-batcher.js.map
{"version":3,"file":"lite-batcher.js","names":["fn: (items: Array<TValue>) => void","options: LiteBatcherOptions<TValue>"],"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,AAAOA,IACP,AAAOC,UAAsC,EAAE,EAC/C;EAFO;EACA;eANsB,EAAE;mBACU;oBACtB;kBA8CV,SAAuB;AAChC,QAAK,MAAM,KAAK,KAAK;AACrB,QAAK,aAAa,KAAK,QAAQ,SAAS;AACxC,QAAK,QAAQ,gBAAgB,KAAK;AAMlC,OAHE,KAAK,MAAM,UAAU,KAAK,QAAQ,WAClC,KAAK,QAAQ,iBAAkB,KAAK,OAAO,KAAK,CAGhD,MAAK,SAAS;YACL,KAAK,QAAQ,SAAS,UAAU;AACzC,SAAK,cAAc;AACnB,SAAK,YAAY,iBAAiB,KAAK,SAAS,EAAE,KAAK,SAAS,CAAC;;;uBAavC;AAC5B,OAAI,KAAK,MAAM,WAAW,EACxB;GAGF,MAAM,QAAQ,KAAK,cAAc;AACjC,QAAK,OAAO;AAEZ,QAAK,GAAG,MAAM;AACd,QAAK,QAAQ,YAAY,OAAO,KAAK;;qBAMnB;AAClB,QAAK,cAAc;AACnB,QAAK,SAAS;;4BAMoB;AAClC,UAAO,CAAC,GAAG,KAAK,MAAM;;4BAGW;AACjC,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;;qBAOD;GAClB,MAAM,WAAW,KAAK,MAAM,SAAS;AACrC,QAAK,QAAQ,EAAE;AACf,QAAK,aAAa;AAClB,OAAI,SACF,MAAK,QAAQ,gBAAgB,KAAK;;sBAQjB;AACnB,QAAK,cAAc;AACnB,QAAK,aAAa;;AArHlB,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AACzC,OAAK,QAAQ,mBACX,KAAK,QAAQ,2BAA2B;;;;;CAM5C,IAAI,OAAe;AACjB,SAAO,KAAK,MAAM;;;;;CAMpB,IAAI,UAAmB;AACrB,SAAO,KAAK,MAAM,WAAW;;;;;CAM/B,IAAI,YAAqB;AACvB,SAAO,KAAK;;CAGd,AAAQ,UAAkB;AACxB,MAAI,OAAO,KAAK,QAAQ,SAAS,WAC/B,QAAO,KAAK,QAAQ,KAAK,KAAK;AAEhC,SAAO,KAAK,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;AA8GxB,SAAgB,UACd,IACA,UAAsC,EAAE,EAChB;AAExB,QADgB,IAAI,YAAoB,IAAI,QAAQ,CACrC"}
//#region src/lite-debouncer.ts
/**
* A lightweight class that creates a debounced function.
*
* This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential debouncing functionality.
*
* Debouncing ensures that a function is only executed after a certain amount of time has passed
* since its last invocation. This is useful for handling frequent events like window resizing,
* scroll events, or input changes where you want to limit the rate of execution.
*
* The debounced function can be configured to execute either at the start of the delay period
* (leading edge) or at the end (trailing edge, default). Each new call during the wait period
* will reset the timer.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const debouncer = new LiteDebouncer((value: string) => {
* saveToDatabase(value);
* }, {
* wait: 500,
* onExecute: (args, debouncer) => {
* console.log('Saved value:', args[0]);
* }
* });
*
* // Will only save after 500ms of no new input
* inputElement.addEventListener('input', () => {
* debouncer.maybeExecute(inputElement.value);
* });
* ```
*/
var LiteDebouncer = class {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.canLeadingExecute = true;
this.maybeExecute = (...args) => {
let didLeadingExecute = false;
if (this.options.leading && this.canLeadingExecute) {
this.canLeadingExecute = false;
didLeadingExecute = true;
this.fn(...args);
this.options.onExecute?.(args, this);
}
this.lastArgs = args;
if (this.timeoutId) clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
this.canLeadingExecute = true;
if (this.options.trailing && !didLeadingExecute && this.lastArgs) {
this.fn(...this.lastArgs);
this.options.onExecute?.(this.lastArgs, this);
}
this.lastArgs = void 0;
}, this.options.wait);
};
this.flush = () => {
if (this.timeoutId && this.lastArgs) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
const args = this.lastArgs;
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastArgs = void 0;
this.canLeadingExecute = true;
}
};
this.cancel = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
this.lastArgs = void 0;
this.canLeadingExecute = true;
};
if (this.options.leading === void 0 && this.options.trailing === void 0) this.options.trailing = true;
}
};
/**
* Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.
* Multiple calls during the wait period will cancel previous pending invocations and reset the timer.
*
* This is an alternative to the debounce function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a debouncer with no external dependencies, devtools integration, or reactive state.
*
* If leading option is true, the function will execute immediately on the first call, then wait the delay
* before allowing another execution.
*
* @example
* ```ts
* const debouncedSave = liteDebounce(() => {
* saveChanges();
* }, { wait: 1000 });
*
* // Called repeatedly but executes at most once per second
* inputElement.addEventListener('input', debouncedSave);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then waits
* const debouncedSearch = liteDebounce((query: string) => {
* performSearch(query);
* }, { wait: 300, leading: true });
* ```
*/
function liteDebounce(fn, options) {
return new LiteDebouncer(fn, options).maybeExecute;
}
//#endregion
exports.LiteDebouncer = LiteDebouncer;
exports.liteDebounce = liteDebounce;
//# sourceMappingURL=lite-debouncer.cjs.map
{"version":3,"file":"lite-debouncer.cjs","names":["fn: TFn","options: LiteDebouncerOptions<TFn>"],"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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,IAAa,gBAAb,MAAoD;CAKlD,YACE,AAAOA,IACP,AAAOC,SACP;EAFO;EACA;2BAJmB;uBAqBZ,GAAG,SAAgC;GACjD,IAAI,oBAAoB;AAExB,OAAI,KAAK,QAAQ,WAAW,KAAK,mBAAmB;AAClD,SAAK,oBAAoB;AACzB,wBAAoB;AACpB,SAAK,GAAG,GAAG,KAAK;AAChB,SAAK,QAAQ,YAAY,MAAM,KAAK;;AAGtC,QAAK,WAAW;AAEhB,OAAI,KAAK,UACP,cAAa,KAAK,UAAU;AAG9B,QAAK,YAAY,iBAAiB;AAChC,SAAK,oBAAoB;AACzB,QAAI,KAAK,QAAQ,YAAY,CAAC,qBAAqB,KAAK,UAAU;AAChE,UAAK,GAAG,GAAG,KAAK,SAAS;AACzB,UAAK,QAAQ,YAAY,KAAK,UAAU,KAAK;;AAE/C,SAAK,WAAW;MACf,KAAK,QAAQ,KAAK;;qBAQH;AAClB,OAAI,KAAK,aAAa,KAAK,UAAU;AACnC,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;IACjB,MAAM,OAAO,KAAK;AAClB,SAAK,GAAG,GAAG,KAAK;AAChB,SAAK,QAAQ,YAAY,MAAM,KAAK;AACpC,SAAK,WAAW;AAChB,SAAK,oBAAoB;;;sBAQR;AACnB,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;AAEnB,QAAK,WAAW;AAChB,QAAK,oBAAoB;;AAnEzB,MACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,OAE1B,MAAK,QAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgG9B,SAAgB,aACd,IACA,SACoC;AAEpC,QADkB,IAAI,cAAc,IAAI,QAAQ,CAC/B"}
import { AnyFunction } from "@tanstack/pacer/types";
//#region src/lite-debouncer.d.ts
/**
* Options for configuring a lite debounced function
*/
interface LiteDebouncerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* The first call will execute immediately and the rest will wait the delay.
* Defaults to false.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, debouncer: LiteDebouncer<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Delay in milliseconds before executing the function.
*/
wait: number;
}
/**
* A lightweight class that creates a debounced function.
*
* This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential debouncing functionality.
*
* Debouncing ensures that a function is only executed after a certain amount of time has passed
* since its last invocation. This is useful for handling frequent events like window resizing,
* scroll events, or input changes where you want to limit the rate of execution.
*
* The debounced function can be configured to execute either at the start of the delay period
* (leading edge) or at the end (trailing edge, default). Each new call during the wait period
* will reset the timer.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const debouncer = new LiteDebouncer((value: string) => {
* saveToDatabase(value);
* }, {
* wait: 500,
* onExecute: (args, debouncer) => {
* console.log('Saved value:', args[0]);
* }
* });
*
* // Will only save after 500ms of no new input
* inputElement.addEventListener('input', () => {
* debouncer.maybeExecute(inputElement.value);
* });
* ```
*/
declare class LiteDebouncer<TFn extends AnyFunction> {
fn: TFn;
options: LiteDebouncerOptions<TFn>;
private timeoutId;
private lastArgs;
private canLeadingExecute;
constructor(fn: TFn, options: LiteDebouncerOptions<TFn>);
/**
* Attempts to execute the debounced function.
* If leading is true and this is the first call, executes immediately.
* Otherwise, queues the execution for after the wait time.
* Each new call resets the timer.
*/
maybeExecute: (...args: Parameters<TFn>) => void;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending execution.
* Clears the timeout and resets the internal state.
*/
cancel: () => void;
}
/**
* Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.
* Multiple calls during the wait period will cancel previous pending invocations and reset the timer.
*
* This is an alternative to the debounce function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a debouncer with no external dependencies, devtools integration, or reactive state.
*
* If leading option is true, the function will execute immediately on the first call, then wait the delay
* before allowing another execution.
*
* @example
* ```ts
* const debouncedSave = liteDebounce(() => {
* saveChanges();
* }, { wait: 1000 });
*
* // Called repeatedly but executes at most once per second
* inputElement.addEventListener('input', debouncedSave);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then waits
* const debouncedSearch = liteDebounce((query: string) => {
* performSearch(query);
* }, { wait: 300, leading: true });
* ```
*/
declare function liteDebounce<TFn extends AnyFunction>(fn: TFn, options: LiteDebouncerOptions<TFn>): (...args: Parameters<TFn>) => void;
//#endregion
export { LiteDebouncer, LiteDebouncerOptions, liteDebounce };
//# sourceMappingURL=lite-debouncer.d.cts.map
import { AnyFunction } from "@tanstack/pacer/types";
//#region src/lite-debouncer.d.ts
/**
* Options for configuring a lite debounced function
*/
interface LiteDebouncerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* The first call will execute immediately and the rest will wait the delay.
* Defaults to false.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, debouncer: LiteDebouncer<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Delay in milliseconds before executing the function.
*/
wait: number;
}
/**
* A lightweight class that creates a debounced function.
*
* This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential debouncing functionality.
*
* Debouncing ensures that a function is only executed after a certain amount of time has passed
* since its last invocation. This is useful for handling frequent events like window resizing,
* scroll events, or input changes where you want to limit the rate of execution.
*
* The debounced function can be configured to execute either at the start of the delay period
* (leading edge) or at the end (trailing edge, default). Each new call during the wait period
* will reset the timer.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const debouncer = new LiteDebouncer((value: string) => {
* saveToDatabase(value);
* }, {
* wait: 500,
* onExecute: (args, debouncer) => {
* console.log('Saved value:', args[0]);
* }
* });
*
* // Will only save after 500ms of no new input
* inputElement.addEventListener('input', () => {
* debouncer.maybeExecute(inputElement.value);
* });
* ```
*/
declare class LiteDebouncer<TFn extends AnyFunction> {
fn: TFn;
options: LiteDebouncerOptions<TFn>;
private timeoutId;
private lastArgs;
private canLeadingExecute;
constructor(fn: TFn, options: LiteDebouncerOptions<TFn>);
/**
* Attempts to execute the debounced function.
* If leading is true and this is the first call, executes immediately.
* Otherwise, queues the execution for after the wait time.
* Each new call resets the timer.
*/
maybeExecute: (...args: Parameters<TFn>) => void;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending execution.
* Clears the timeout and resets the internal state.
*/
cancel: () => void;
}
/**
* Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.
* Multiple calls during the wait period will cancel previous pending invocations and reset the timer.
*
* This is an alternative to the debounce function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a debouncer with no external dependencies, devtools integration, or reactive state.
*
* If leading option is true, the function will execute immediately on the first call, then wait the delay
* before allowing another execution.
*
* @example
* ```ts
* const debouncedSave = liteDebounce(() => {
* saveChanges();
* }, { wait: 1000 });
*
* // Called repeatedly but executes at most once per second
* inputElement.addEventListener('input', debouncedSave);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then waits
* const debouncedSearch = liteDebounce((query: string) => {
* performSearch(query);
* }, { wait: 300, leading: true });
* ```
*/
declare function liteDebounce<TFn extends AnyFunction>(fn: TFn, options: LiteDebouncerOptions<TFn>): (...args: Parameters<TFn>) => void;
//#endregion
export { LiteDebouncer, LiteDebouncerOptions, liteDebounce };
//# sourceMappingURL=lite-debouncer.d.ts.map
//#region src/lite-debouncer.ts
/**
* A lightweight class that creates a debounced function.
*
* This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential debouncing functionality.
*
* Debouncing ensures that a function is only executed after a certain amount of time has passed
* since its last invocation. This is useful for handling frequent events like window resizing,
* scroll events, or input changes where you want to limit the rate of execution.
*
* The debounced function can be configured to execute either at the start of the delay period
* (leading edge) or at the end (trailing edge, default). Each new call during the wait period
* will reset the timer.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const debouncer = new LiteDebouncer((value: string) => {
* saveToDatabase(value);
* }, {
* wait: 500,
* onExecute: (args, debouncer) => {
* console.log('Saved value:', args[0]);
* }
* });
*
* // Will only save after 500ms of no new input
* inputElement.addEventListener('input', () => {
* debouncer.maybeExecute(inputElement.value);
* });
* ```
*/
var LiteDebouncer = class {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.canLeadingExecute = true;
this.maybeExecute = (...args) => {
let didLeadingExecute = false;
if (this.options.leading && this.canLeadingExecute) {
this.canLeadingExecute = false;
didLeadingExecute = true;
this.fn(...args);
this.options.onExecute?.(args, this);
}
this.lastArgs = args;
if (this.timeoutId) clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
this.canLeadingExecute = true;
if (this.options.trailing && !didLeadingExecute && this.lastArgs) {
this.fn(...this.lastArgs);
this.options.onExecute?.(this.lastArgs, this);
}
this.lastArgs = void 0;
}, this.options.wait);
};
this.flush = () => {
if (this.timeoutId && this.lastArgs) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
const args = this.lastArgs;
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastArgs = void 0;
this.canLeadingExecute = true;
}
};
this.cancel = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
this.lastArgs = void 0;
this.canLeadingExecute = true;
};
if (this.options.leading === void 0 && this.options.trailing === void 0) this.options.trailing = true;
}
};
/**
* Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.
* Multiple calls during the wait period will cancel previous pending invocations and reset the timer.
*
* This is an alternative to the debounce function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a debouncer with no external dependencies, devtools integration, or reactive state.
*
* If leading option is true, the function will execute immediately on the first call, then wait the delay
* before allowing another execution.
*
* @example
* ```ts
* const debouncedSave = liteDebounce(() => {
* saveChanges();
* }, { wait: 1000 });
*
* // Called repeatedly but executes at most once per second
* inputElement.addEventListener('input', debouncedSave);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then waits
* const debouncedSearch = liteDebounce((query: string) => {
* performSearch(query);
* }, { wait: 300, leading: true });
* ```
*/
function liteDebounce(fn, options) {
return new LiteDebouncer(fn, options).maybeExecute;
}
//#endregion
export { LiteDebouncer, liteDebounce };
//# sourceMappingURL=lite-debouncer.js.map
{"version":3,"file":"lite-debouncer.js","names":["fn: TFn","options: LiteDebouncerOptions<TFn>"],"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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,IAAa,gBAAb,MAAoD;CAKlD,YACE,AAAOA,IACP,AAAOC,SACP;EAFO;EACA;2BAJmB;uBAqBZ,GAAG,SAAgC;GACjD,IAAI,oBAAoB;AAExB,OAAI,KAAK,QAAQ,WAAW,KAAK,mBAAmB;AAClD,SAAK,oBAAoB;AACzB,wBAAoB;AACpB,SAAK,GAAG,GAAG,KAAK;AAChB,SAAK,QAAQ,YAAY,MAAM,KAAK;;AAGtC,QAAK,WAAW;AAEhB,OAAI,KAAK,UACP,cAAa,KAAK,UAAU;AAG9B,QAAK,YAAY,iBAAiB;AAChC,SAAK,oBAAoB;AACzB,QAAI,KAAK,QAAQ,YAAY,CAAC,qBAAqB,KAAK,UAAU;AAChE,UAAK,GAAG,GAAG,KAAK,SAAS;AACzB,UAAK,QAAQ,YAAY,KAAK,UAAU,KAAK;;AAE/C,SAAK,WAAW;MACf,KAAK,QAAQ,KAAK;;qBAQH;AAClB,OAAI,KAAK,aAAa,KAAK,UAAU;AACnC,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;IACjB,MAAM,OAAO,KAAK;AAClB,SAAK,GAAG,GAAG,KAAK;AAChB,SAAK,QAAQ,YAAY,MAAM,KAAK;AACpC,SAAK,WAAW;AAChB,SAAK,oBAAoB;;;sBAQR;AACnB,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;AAEnB,QAAK,WAAW;AAChB,QAAK,oBAAoB;;AAnEzB,MACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,OAE1B,MAAK,QAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgG9B,SAAgB,aACd,IACA,SACoC;AAEpC,QADkB,IAAI,cAAc,IAAI,QAAQ,CAC/B"}
//#region src/lite-queuer.ts
/**
* A lightweight class that creates a queue for processing items.
*
* This is an alternative to the Queuer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential queueing functionality.
*
* The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based
* processing of items. Items can be processed automatically with configurable wait times
* between executions, or processed manually using the execute methods.
*
* Features included:
* - Automatic or manual processing of items
* - FIFO, LIFO, and priority-based ordering
* - Queue size limits with item rejection
* - Configurable wait times between processing
* - Batch processing capabilities
* - Start/stop processing control
* - Callback support for monitoring execution, rejection, and state change events
*
* Features NOT included (compared to core Queuer):
* - No TanStack Store state management
* - No devtools integration
* - No item expiration functionality (no onExpire callback)
* - No dynamic options updates (setOptions)
* - No detailed state tracking (execution counts, etc.)
*
* Queue behavior:
* - Default: FIFO (add to back, process from front)
* - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'
* - Priority: Provide getPriority function; higher values processed first
*
* @example
* ```ts
* // Basic FIFO queue
* const queue = new LiteQueuer((item: string) => {
* console.log('Processing:', item);
* }, { wait: 100 });
*
* queue.addItem('task1');
* queue.addItem('task2');
* // Processes: task1, then task2 after 100ms delay
* ```
*
* @example
* ```ts
* // Priority queue
* const priorityQueue = new LiteQueuer((item: Task) => {
* processTask(item);
* }, {
* getPriority: task => task.priority,
* wait: 500
* });
*
* priorityQueue.addItem({ name: 'low', priority: 1 });
* priorityQueue.addItem({ name: 'high', priority: 10 });
* // Processes high priority task first
* ```
*/
var LiteQueuer = class {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this.isRunning = true;
this.pendingTick = false;
this.addItem = (item, position = this.options.addItemsTo, startProcessing = true) => {
if (this.items.length >= this.options.maxSize) return false;
if (this.options.getPriority) {
const priority = this.options.getPriority(item);
if (priority !== void 0) {
const insertIndex = this.items.findIndex((existing) => {
return (this.options.getPriority(existing) ?? -Infinity) < priority;
});
if (insertIndex === -1) this.items.push(item);
else this.items.splice(insertIndex, 0, item);
} else this.insertAtPosition(item, position);
} else this.insertAtPosition(item, position);
if (startProcessing && this.isRunning && !this.pendingTick) this.tick();
return true;
};
this.insertAtPosition = (item, position) => {
if (position === "front") this.items.unshift(item);
else this.items.push(item);
};
this.getNextItem = (position = this.options.getItemsFrom) => {
if (this.items.length === 0) return;
let item;
if (this.options.getPriority || position === "front") item = this.items.shift();
else item = this.items.pop();
return item;
};
this.execute = (position) => {
const item = this.getNextItem(position);
if (item !== void 0) this.fn(item);
return item;
};
this.tick = () => {
if (!this.isRunning) {
this.pendingTick = false;
return;
}
this.pendingTick = true;
while (this.items.length > 0) {
if (this.execute(this.options.getItemsFrom) === void 0) break;
const wait = this.options.wait;
if (wait > 0) {
this.timeoutId = setTimeout(() => this.tick(), wait);
return;
}
}
this.pendingTick = false;
};
this.start = () => {
this.isRunning = true;
if (!this.pendingTick && this.items.length > 0) this.tick();
};
this.stop = () => {
this.clearTimeout();
this.isRunning = false;
this.pendingTick = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.peekNextItem = (position = "front") => {
if (this.items.length === 0) return;
if (this.options.getPriority || position === "front") return this.items[0];
else return this.items[this.items.length - 1];
};
this.peekAllItems = () => {
return [...this.items];
};
this.flush = (numberOfItems = this.items.length, position) => {
this.clearTimeout();
for (let i = 0; i < numberOfItems && this.items.length > 0; i++) this.execute(position);
if (this.isRunning && this.items.length > 0 && !this.pendingTick) this.tick();
};
this.flushAsBatch = (batchFunction) => {
const items = this.peekAllItems();
this.clear();
batchFunction(items);
};
this.clear = () => {
this.items = [];
};
this.options.addItemsTo = this.options.addItemsTo ?? "back";
this.options.getItemsFrom = this.options.getItemsFrom ?? "front";
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? 0;
this.isRunning = this.options.started;
if (this.options.initialItems) for (const item of this.options.initialItems) this.addItem(item, this.options.addItemsTo, false);
if (this.isRunning && this.items.length > 0) this.tick();
}
/**
* Number of items currently in the queue
*/
get size() {
return this.items.length;
}
/**
* Whether the queue is empty
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning() {
return this.isRunning;
}
};
/**
* Creates a lightweight queue that processes items using the provided function.
*
* This is an alternative to the queue function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a queuer with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const processItem = liteQueue((item: string) => {
* console.log('Processing:', item);
* }, { wait: 1000 });
*
* processItem('task1');
* processItem('task2');
* // Processes each item with 1 second delay between them
* ```
*/
function liteQueue(fn, options = {}) {
const queuer = new LiteQueuer(fn, options);
return (item) => queuer.addItem(item);
}
//#endregion
exports.LiteQueuer = LiteQueuer;
exports.liteQueue = liteQueue;
//# sourceMappingURL=lite-queuer.cjs.map
{"version":3,"file":"lite-queuer.cjs","names":["fn: (item: TValue) => void","options: LiteQueuerOptions<TValue>","item: TValue | undefined"],"sources":["../src/lite-queuer.ts"],"sourcesContent":["/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO for getNextItem)\n * - 'back': Operate on the back of the queue (LIFO for getNextItem)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * Options for configuring a lite queuer instance\n */\nexport interface LiteQueuerOptions<TValue> {\n /**\n * Default position to add items to the queue\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queue\n * Higher priority items will be processed first\n * Return undefined for items that should use positional ordering\n */\n getPriority?: (item: TValue) => number | undefined\n /**\n * Initial items to populate the queue with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queue\n */\n maxSize?: number\n /**\n * Whether the queuer should start processing items immediately\n * @default true\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items\n * @default 0\n */\n wait?: number\n}\n\n/**\n * A lightweight class that creates a queue for processing items.\n *\n * This is an alternative to the Queuer in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * no callbacks, and provides only essential queueing functionality.\n *\n * The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based\n * processing of items. Items can be processed automatically with configurable wait times\n * between executions, or processed manually using the execute methods.\n *\n * Features included:\n * - Automatic or manual processing of items\n * - FIFO, LIFO, and priority-based ordering\n * - Queue size limits with item rejection\n * - Configurable wait times between processing\n * - Batch processing capabilities\n * - Start/stop processing control\n * - Callback support for monitoring execution, rejection, and state change events\n *\n * Features NOT included (compared to core Queuer):\n * - No TanStack Store state management\n * - No devtools integration\n * - No item expiration functionality (no onExpire callback)\n * - No dynamic options updates (setOptions)\n * - No detailed state tracking (execution counts, etc.)\n *\n * Queue behavior:\n * - Default: FIFO (add to back, process from front)\n * - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'\n * - Priority: Provide getPriority function; higher values processed first\n *\n * @example\n * ```ts\n * // Basic FIFO queue\n * const queue = new LiteQueuer((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 100 });\n *\n * queue.addItem('task1');\n * queue.addItem('task2');\n * // Processes: task1, then task2 after 100ms delay\n * ```\n *\n * @example\n * ```ts\n * // Priority queue\n * const priorityQueue = new LiteQueuer((item: Task) => {\n * processTask(item);\n * }, {\n * getPriority: task => task.priority,\n * wait: 500\n * });\n *\n * priorityQueue.addItem({ name: 'low', priority: 1 });\n * priorityQueue.addItem({ name: 'high', priority: 10 });\n * // Processes high priority task first\n * ```\n */\nexport class LiteQueuer<TValue> {\n private items: Array<TValue> = []\n private timeoutId: NodeJS.Timeout | null = null\n private isRunning = true\n private pendingTick = false\n\n constructor(\n public fn: (item: TValue) => void,\n public options: LiteQueuerOptions<TValue> = {},\n ) {\n // Set defaults\n this.options.addItemsTo = this.options.addItemsTo ?? 'back'\n this.options.getItemsFrom = this.options.getItemsFrom ?? 'front'\n this.options.maxSize = this.options.maxSize ?? Infinity\n this.options.started = this.options.started ?? true\n this.options.wait = this.options.wait ?? 0\n\n this.isRunning = this.options.started\n\n // Add initial items if provided\n if (this.options.initialItems) {\n for (const item of this.options.initialItems) {\n this.addItem(item, this.options.addItemsTo, false)\n }\n }\n\n // Start processing if enabled and has items\n if (this.isRunning && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Number of items currently in the queue\n */\n get size(): number {\n return this.items.length\n }\n\n /**\n * Whether the queue is empty\n */\n get isEmpty(): boolean {\n return this.items.length === 0\n }\n\n /**\n * Whether the queue is currently running (auto-processing items)\n */\n get isQueueRunning(): boolean {\n return this.isRunning\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected.\n * Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * @example\n * ```ts\n * queue.addItem('task1'); // Add to default position (back)\n * queue.addItem('task2', 'front'); // Add to front\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo!,\n startProcessing: boolean = true,\n ): boolean => {\n // Check size limit\n if (this.items.length >= this.options.maxSize!) {\n return false\n }\n\n // Handle priority insertion\n if (this.options.getPriority) {\n const priority = this.options.getPriority(item)\n if (priority !== undefined) {\n // Find insertion point for priority\n const insertIndex = this.items.findIndex((existing) => {\n const existingPriority = this.options.getPriority!(existing)\n // Treat undefined priority as negative infinity for comparison\n const effectivePriority = existingPriority ?? -Infinity\n return effectivePriority < priority\n })\n\n if (insertIndex === -1) {\n this.items.push(item)\n } else {\n this.items.splice(insertIndex, 0, item)\n }\n } else {\n // No priority, use position\n this.insertAtPosition(item, position)\n }\n } else {\n // No priority function, use position\n this.insertAtPosition(item, position)\n }\n\n // Start processing if running and not already processing\n if (startProcessing && this.isRunning && !this.pendingTick) {\n this.tick()\n }\n\n return true\n }\n\n private insertAtPosition = (item: TValue, position: QueuePosition): void => {\n if (position === 'front') {\n this.items.unshift(item)\n } else {\n this.items.push(item)\n }\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * @example\n * ```ts\n * const nextItem = queue.getNextItem(); // Get from default position (front)\n * const lastItem = queue.getNextItem('back'); // Get from back (LIFO)\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom!,\n ): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n if (this.options.getPriority || position === 'front') {\n item = this.items.shift()\n } else {\n item = this.items.pop()\n }\n\n return item\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * @example\n * ```ts\n * queue.execute(); // Execute from default position\n * queue.execute('back'); // Execute from back (LIFO)\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n }\n return item\n }\n\n /**\n * Internal method that processes items in the queue with wait intervals\n */\n private tick = (): void => {\n if (!this.isRunning) {\n this.pendingTick = false\n return\n }\n\n this.pendingTick = true\n\n // Process items while queue is not empty\n while (this.items.length > 0) {\n const item = this.execute(this.options.getItemsFrom)\n if (item === undefined) {\n break\n }\n\n const wait = this.options.wait!\n if (wait > 0) {\n // Schedule next processing after wait time\n this.timeoutId = setTimeout(() => this.tick(), wait)\n return\n }\n\n // No wait time, continue processing immediately\n }\n\n this.pendingTick = false\n }\n\n /**\n * Starts processing items in the queue. If already running, does nothing.\n */\n start = (): void => {\n this.isRunning = true\n if (!this.pendingTick && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = (): void => {\n this.clearTimeout()\n this.isRunning = false\n this.pendingTick = false\n }\n\n /**\n * Clears any pending timeout\n */\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = null\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * @example\n * ```ts\n * const next = queue.peekNextItem(); // Peek at front\n * const last = queue.peekNextItem('back'); // Peek at back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n if (this.options.getPriority || position === 'front') {\n return this.items[0]\n } else {\n return this.items[this.items.length - 1]\n }\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.items]\n }\n\n /**\n * Processes a specified number of items immediately with no wait time.\n * If no numberOfItems is provided, all items will be processed.\n *\n * @example\n * ```ts\n * queue.flush(); // Process all items immediately\n * queue.flush(3); // Process next 3 items immediately\n * ```\n */\n flush = (\n numberOfItems: number = this.items.length,\n position?: QueuePosition,\n ): void => {\n this.clearTimeout() // Clear any pending timeout\n for (let i = 0; i < numberOfItems && this.items.length > 0; i++) {\n this.execute(position)\n }\n // Restart normal processing if still running and has items\n if (this.isRunning && this.items.length > 0 && !this.pendingTick) {\n this.tick()\n }\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function.\n * The queue is cleared after processing.\n *\n * @example\n * ```ts\n * queue.flushAsBatch((items) => {\n * console.log('Processing batch:', items);\n * // Process all items together\n * });\n * ```\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.peekAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Removes all items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.items = []\n }\n}\n\n/**\n * Creates a lightweight queue that processes items using the provided function.\n *\n * This is an alternative to the queue 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 queuer with no external dependencies, devtools integration, or reactive state.\n *\n * @example\n * ```ts\n * const processItem = liteQueue((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 1000 });\n *\n * processItem('task1');\n * processItem('task2');\n * // Processes each item with 1 second delay between them\n * ```\n */\nexport function liteQueue<TValue>(\n fn: (item: TValue) => void,\n options: LiteQueuerOptions<TValue> = {},\n): (item: TValue) => boolean {\n const queuer = new LiteQueuer(fn, options)\n return (item: TValue) => queuer.addItem(item)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GA,IAAa,aAAb,MAAgC;CAM9B,YACE,AAAOA,IACP,AAAOC,UAAqC,EAAE,EAC9C;EAFO;EACA;eAPsB,EAAE;mBACU;mBACvB;qBACE;kBA8DpB,MACA,WAA0B,KAAK,QAAQ,YACvC,kBAA2B,SACf;AAEZ,OAAI,KAAK,MAAM,UAAU,KAAK,QAAQ,QACpC,QAAO;AAIT,OAAI,KAAK,QAAQ,aAAa;IAC5B,MAAM,WAAW,KAAK,QAAQ,YAAY,KAAK;AAC/C,QAAI,aAAa,QAAW;KAE1B,MAAM,cAAc,KAAK,MAAM,WAAW,aAAa;AAIrD,cAHyB,KAAK,QAAQ,YAAa,SAAS,IAEd,aACnB;OAC3B;AAEF,SAAI,gBAAgB,GAClB,MAAK,MAAM,KAAK,KAAK;SAErB,MAAK,MAAM,OAAO,aAAa,GAAG,KAAK;UAIzC,MAAK,iBAAiB,MAAM,SAAS;SAIvC,MAAK,iBAAiB,MAAM,SAAS;AAIvC,OAAI,mBAAmB,KAAK,aAAa,CAAC,KAAK,YAC7C,MAAK,MAAM;AAGb,UAAO;;2BAGmB,MAAc,aAAkC;AAC1E,OAAI,aAAa,QACf,MAAK,MAAM,QAAQ,KAAK;OAExB,MAAK,MAAM,KAAK,KAAK;;sBAevB,WAA0B,KAAK,QAAQ,iBAChB;AACvB,OAAI,KAAK,MAAM,WAAW,EACxB;GAGF,IAAIC;AAGJ,OAAI,KAAK,QAAQ,eAAe,aAAa,QAC3C,QAAO,KAAK,MAAM,OAAO;OAEzB,QAAO,KAAK,MAAM,KAAK;AAGzB,UAAO;;kBAYE,aAAiD;GAC1D,MAAM,OAAO,KAAK,YAAY,SAAS;AACvC,OAAI,SAAS,OACX,MAAK,GAAG,KAAK;AAEf,UAAO;;oBAMkB;AACzB,OAAI,CAAC,KAAK,WAAW;AACnB,SAAK,cAAc;AACnB;;AAGF,QAAK,cAAc;AAGnB,UAAO,KAAK,MAAM,SAAS,GAAG;AAE5B,QADa,KAAK,QAAQ,KAAK,QAAQ,aAAa,KACvC,OACX;IAGF,MAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,OAAO,GAAG;AAEZ,UAAK,YAAY,iBAAiB,KAAK,MAAM,EAAE,KAAK;AACpD;;;AAMJ,QAAK,cAAc;;qBAMD;AAClB,QAAK,YAAY;AACjB,OAAI,CAAC,KAAK,eAAe,KAAK,MAAM,SAAS,EAC3C,MAAK,MAAM;;oBAOI;AACjB,QAAK,cAAc;AACnB,QAAK,YAAY;AACjB,QAAK,cAAc;;4BAMc;AACjC,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;;uBAaL,WAA0B,YAAgC;AACxE,OAAI,KAAK,MAAM,WAAW,EACxB;AAGF,OAAI,KAAK,QAAQ,eAAe,aAAa,QAC3C,QAAO,KAAK,MAAM;OAElB,QAAO,KAAK,MAAM,KAAK,MAAM,SAAS;;4BAON;AAClC,UAAO,CAAC,GAAG,KAAK,MAAM;;gBActB,gBAAwB,KAAK,MAAM,QACnC,aACS;AACT,QAAK,cAAc;AACnB,QAAK,IAAI,IAAI,GAAG,IAAI,iBAAiB,KAAK,MAAM,SAAS,GAAG,IAC1D,MAAK,QAAQ,SAAS;AAGxB,OAAI,KAAK,aAAa,KAAK,MAAM,SAAS,KAAK,CAAC,KAAK,YACnD,MAAK,MAAM;;uBAgBC,kBAAwD;GACtE,MAAM,QAAQ,KAAK,cAAc;AACjC,QAAK,OAAO;AACZ,iBAAc,MAAM;;qBAMF;AAClB,QAAK,QAAQ,EAAE;;AA9Rf,OAAK,QAAQ,aAAa,KAAK,QAAQ,cAAc;AACrD,OAAK,QAAQ,eAAe,KAAK,QAAQ,gBAAgB;AACzD,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAEzC,OAAK,YAAY,KAAK,QAAQ;AAG9B,MAAI,KAAK,QAAQ,aACf,MAAK,MAAM,QAAQ,KAAK,QAAQ,aAC9B,MAAK,QAAQ,MAAM,KAAK,QAAQ,YAAY,MAAM;AAKtD,MAAI,KAAK,aAAa,KAAK,MAAM,SAAS,EACxC,MAAK,MAAM;;;;;CAOf,IAAI,OAAe;AACjB,SAAO,KAAK,MAAM;;;;;CAMpB,IAAI,UAAmB;AACrB,SAAO,KAAK,MAAM,WAAW;;;;;CAM/B,IAAI,iBAA0B;AAC5B,SAAO,KAAK;;;;;;;;;;;;;;;;;;;;;AA6QhB,SAAgB,UACd,IACA,UAAqC,EAAE,EACZ;CAC3B,MAAM,SAAS,IAAI,WAAW,IAAI,QAAQ;AAC1C,SAAQ,SAAiB,OAAO,QAAQ,KAAK"}
//#region src/lite-queuer.d.ts
/**
* Position type for addItem and getNextItem operations.
*
* - 'front': Operate on the front of the queue (FIFO for getNextItem)
* - 'back': Operate on the back of the queue (LIFO for getNextItem)
*/
type QueuePosition = 'front' | 'back';
/**
* Options for configuring a lite queuer instance
*/
interface LiteQueuerOptions<TValue> {
/**
* Default position to add items to the queue
* @default 'back'
*/
addItemsTo?: QueuePosition;
/**
* Default position to get items from during processing
* @default 'front'
*/
getItemsFrom?: QueuePosition;
/**
* Function to determine priority of items in the queue
* Higher priority items will be processed first
* Return undefined for items that should use positional ordering
*/
getPriority?: (item: TValue) => number | undefined;
/**
* Initial items to populate the queue with
*/
initialItems?: Array<TValue>;
/**
* Maximum number of items allowed in the queue
*/
maxSize?: number;
/**
* Whether the queuer should start processing items immediately
* @default true
*/
started?: boolean;
/**
* Time in milliseconds to wait between processing items
* @default 0
*/
wait?: number;
}
/**
* A lightweight class that creates a queue for processing items.
*
* This is an alternative to the Queuer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential queueing functionality.
*
* The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based
* processing of items. Items can be processed automatically with configurable wait times
* between executions, or processed manually using the execute methods.
*
* Features included:
* - Automatic or manual processing of items
* - FIFO, LIFO, and priority-based ordering
* - Queue size limits with item rejection
* - Configurable wait times between processing
* - Batch processing capabilities
* - Start/stop processing control
* - Callback support for monitoring execution, rejection, and state change events
*
* Features NOT included (compared to core Queuer):
* - No TanStack Store state management
* - No devtools integration
* - No item expiration functionality (no onExpire callback)
* - No dynamic options updates (setOptions)
* - No detailed state tracking (execution counts, etc.)
*
* Queue behavior:
* - Default: FIFO (add to back, process from front)
* - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'
* - Priority: Provide getPriority function; higher values processed first
*
* @example
* ```ts
* // Basic FIFO queue
* const queue = new LiteQueuer((item: string) => {
* console.log('Processing:', item);
* }, { wait: 100 });
*
* queue.addItem('task1');
* queue.addItem('task2');
* // Processes: task1, then task2 after 100ms delay
* ```
*
* @example
* ```ts
* // Priority queue
* const priorityQueue = new LiteQueuer((item: Task) => {
* processTask(item);
* }, {
* getPriority: task => task.priority,
* wait: 500
* });
*
* priorityQueue.addItem({ name: 'low', priority: 1 });
* priorityQueue.addItem({ name: 'high', priority: 10 });
* // Processes high priority task first
* ```
*/
declare class LiteQueuer<TValue> {
fn: (item: TValue) => void;
options: LiteQueuerOptions<TValue>;
private items;
private timeoutId;
private isRunning;
private pendingTick;
constructor(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>);
/**
* Number of items currently in the queue
*/
get size(): number;
/**
* Whether the queue is empty
*/
get isEmpty(): boolean;
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning(): boolean;
/**
* Adds an item to the queue. If the queue is full, the item is rejected.
* Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.
*
* Returns true if the item was added, false if the queue is full.
*
* @example
* ```ts
* queue.addItem('task1'); // Add to default position (back)
* queue.addItem('task2', 'front'); // Add to front
* ```
*/
addItem: (item: TValue, position?: QueuePosition, startProcessing?: boolean) => boolean;
private insertAtPosition;
/**
* Removes and returns the next item from the queue without executing the function.
* Use for manual queue management. Normally, use execute() to process items.
*
* @example
* ```ts
* const nextItem = queue.getNextItem(); // Get from default position (front)
* const lastItem = queue.getNextItem('back'); // Get from back (LIFO)
* ```
*/
getNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Removes and returns the next item from the queue and processes it using the provided function.
*
* @example
* ```ts
* queue.execute(); // Execute from default position
* queue.execute('back'); // Execute from back (LIFO)
* ```
*/
execute: (position?: QueuePosition) => TValue | undefined;
/**
* Internal method that processes items in the queue with wait intervals
*/
private tick;
/**
* Starts processing items in the queue. If already running, does nothing.
*/
start: () => void;
/**
* Stops processing items in the queue. Does not clear the queue.
*/
stop: () => void;
/**
* Clears any pending timeout
*/
private clearTimeout;
/**
* Returns the next item in the queue without removing it.
*
* @example
* ```ts
* const next = queue.peekNextItem(); // Peek at front
* const last = queue.peekNextItem('back'); // Peek at back
* ```
*/
peekNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Returns a copy of all items in the queue.
*/
peekAllItems: () => Array<TValue>;
/**
* Processes a specified number of items immediately with no wait time.
* If no numberOfItems is provided, all items will be processed.
*
* @example
* ```ts
* queue.flush(); // Process all items immediately
* queue.flush(3); // Process next 3 items immediately
* ```
*/
flush: (numberOfItems?: number, position?: QueuePosition) => void;
/**
* Processes all items in the queue as a batch using the provided function.
* The queue is cleared after processing.
*
* @example
* ```ts
* queue.flushAsBatch((items) => {
* console.log('Processing batch:', items);
* // Process all items together
* });
* ```
*/
flushAsBatch: (batchFunction: (items: Array<TValue>) => void) => void;
/**
* Removes all items from the queue. Does not affect items being processed.
*/
clear: () => void;
}
/**
* Creates a lightweight queue that processes items using the provided function.
*
* This is an alternative to the queue function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a queuer with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const processItem = liteQueue((item: string) => {
* console.log('Processing:', item);
* }, { wait: 1000 });
*
* processItem('task1');
* processItem('task2');
* // Processes each item with 1 second delay between them
* ```
*/
declare function liteQueue<TValue>(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>): (item: TValue) => boolean;
//#endregion
export { LiteQueuer, LiteQueuerOptions, QueuePosition, liteQueue };
//# sourceMappingURL=lite-queuer.d.cts.map
//#region src/lite-queuer.d.ts
/**
* Position type for addItem and getNextItem operations.
*
* - 'front': Operate on the front of the queue (FIFO for getNextItem)
* - 'back': Operate on the back of the queue (LIFO for getNextItem)
*/
type QueuePosition = 'front' | 'back';
/**
* Options for configuring a lite queuer instance
*/
interface LiteQueuerOptions<TValue> {
/**
* Default position to add items to the queue
* @default 'back'
*/
addItemsTo?: QueuePosition;
/**
* Default position to get items from during processing
* @default 'front'
*/
getItemsFrom?: QueuePosition;
/**
* Function to determine priority of items in the queue
* Higher priority items will be processed first
* Return undefined for items that should use positional ordering
*/
getPriority?: (item: TValue) => number | undefined;
/**
* Initial items to populate the queue with
*/
initialItems?: Array<TValue>;
/**
* Maximum number of items allowed in the queue
*/
maxSize?: number;
/**
* Whether the queuer should start processing items immediately
* @default true
*/
started?: boolean;
/**
* Time in milliseconds to wait between processing items
* @default 0
*/
wait?: number;
}
/**
* A lightweight class that creates a queue for processing items.
*
* This is an alternative to the Queuer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential queueing functionality.
*
* The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based
* processing of items. Items can be processed automatically with configurable wait times
* between executions, or processed manually using the execute methods.
*
* Features included:
* - Automatic or manual processing of items
* - FIFO, LIFO, and priority-based ordering
* - Queue size limits with item rejection
* - Configurable wait times between processing
* - Batch processing capabilities
* - Start/stop processing control
* - Callback support for monitoring execution, rejection, and state change events
*
* Features NOT included (compared to core Queuer):
* - No TanStack Store state management
* - No devtools integration
* - No item expiration functionality (no onExpire callback)
* - No dynamic options updates (setOptions)
* - No detailed state tracking (execution counts, etc.)
*
* Queue behavior:
* - Default: FIFO (add to back, process from front)
* - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'
* - Priority: Provide getPriority function; higher values processed first
*
* @example
* ```ts
* // Basic FIFO queue
* const queue = new LiteQueuer((item: string) => {
* console.log('Processing:', item);
* }, { wait: 100 });
*
* queue.addItem('task1');
* queue.addItem('task2');
* // Processes: task1, then task2 after 100ms delay
* ```
*
* @example
* ```ts
* // Priority queue
* const priorityQueue = new LiteQueuer((item: Task) => {
* processTask(item);
* }, {
* getPriority: task => task.priority,
* wait: 500
* });
*
* priorityQueue.addItem({ name: 'low', priority: 1 });
* priorityQueue.addItem({ name: 'high', priority: 10 });
* // Processes high priority task first
* ```
*/
declare class LiteQueuer<TValue> {
fn: (item: TValue) => void;
options: LiteQueuerOptions<TValue>;
private items;
private timeoutId;
private isRunning;
private pendingTick;
constructor(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>);
/**
* Number of items currently in the queue
*/
get size(): number;
/**
* Whether the queue is empty
*/
get isEmpty(): boolean;
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning(): boolean;
/**
* Adds an item to the queue. If the queue is full, the item is rejected.
* Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.
*
* Returns true if the item was added, false if the queue is full.
*
* @example
* ```ts
* queue.addItem('task1'); // Add to default position (back)
* queue.addItem('task2', 'front'); // Add to front
* ```
*/
addItem: (item: TValue, position?: QueuePosition, startProcessing?: boolean) => boolean;
private insertAtPosition;
/**
* Removes and returns the next item from the queue without executing the function.
* Use for manual queue management. Normally, use execute() to process items.
*
* @example
* ```ts
* const nextItem = queue.getNextItem(); // Get from default position (front)
* const lastItem = queue.getNextItem('back'); // Get from back (LIFO)
* ```
*/
getNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Removes and returns the next item from the queue and processes it using the provided function.
*
* @example
* ```ts
* queue.execute(); // Execute from default position
* queue.execute('back'); // Execute from back (LIFO)
* ```
*/
execute: (position?: QueuePosition) => TValue | undefined;
/**
* Internal method that processes items in the queue with wait intervals
*/
private tick;
/**
* Starts processing items in the queue. If already running, does nothing.
*/
start: () => void;
/**
* Stops processing items in the queue. Does not clear the queue.
*/
stop: () => void;
/**
* Clears any pending timeout
*/
private clearTimeout;
/**
* Returns the next item in the queue without removing it.
*
* @example
* ```ts
* const next = queue.peekNextItem(); // Peek at front
* const last = queue.peekNextItem('back'); // Peek at back
* ```
*/
peekNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Returns a copy of all items in the queue.
*/
peekAllItems: () => Array<TValue>;
/**
* Processes a specified number of items immediately with no wait time.
* If no numberOfItems is provided, all items will be processed.
*
* @example
* ```ts
* queue.flush(); // Process all items immediately
* queue.flush(3); // Process next 3 items immediately
* ```
*/
flush: (numberOfItems?: number, position?: QueuePosition) => void;
/**
* Processes all items in the queue as a batch using the provided function.
* The queue is cleared after processing.
*
* @example
* ```ts
* queue.flushAsBatch((items) => {
* console.log('Processing batch:', items);
* // Process all items together
* });
* ```
*/
flushAsBatch: (batchFunction: (items: Array<TValue>) => void) => void;
/**
* Removes all items from the queue. Does not affect items being processed.
*/
clear: () => void;
}
/**
* Creates a lightweight queue that processes items using the provided function.
*
* This is an alternative to the queue function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a queuer with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const processItem = liteQueue((item: string) => {
* console.log('Processing:', item);
* }, { wait: 1000 });
*
* processItem('task1');
* processItem('task2');
* // Processes each item with 1 second delay between them
* ```
*/
declare function liteQueue<TValue>(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>): (item: TValue) => boolean;
//#endregion
export { LiteQueuer, LiteQueuerOptions, QueuePosition, liteQueue };
//# sourceMappingURL=lite-queuer.d.ts.map
//#region src/lite-queuer.ts
/**
* A lightweight class that creates a queue for processing items.
*
* This is an alternative to the Queuer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential queueing functionality.
*
* The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based
* processing of items. Items can be processed automatically with configurable wait times
* between executions, or processed manually using the execute methods.
*
* Features included:
* - Automatic or manual processing of items
* - FIFO, LIFO, and priority-based ordering
* - Queue size limits with item rejection
* - Configurable wait times between processing
* - Batch processing capabilities
* - Start/stop processing control
* - Callback support for monitoring execution, rejection, and state change events
*
* Features NOT included (compared to core Queuer):
* - No TanStack Store state management
* - No devtools integration
* - No item expiration functionality (no onExpire callback)
* - No dynamic options updates (setOptions)
* - No detailed state tracking (execution counts, etc.)
*
* Queue behavior:
* - Default: FIFO (add to back, process from front)
* - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'
* - Priority: Provide getPriority function; higher values processed first
*
* @example
* ```ts
* // Basic FIFO queue
* const queue = new LiteQueuer((item: string) => {
* console.log('Processing:', item);
* }, { wait: 100 });
*
* queue.addItem('task1');
* queue.addItem('task2');
* // Processes: task1, then task2 after 100ms delay
* ```
*
* @example
* ```ts
* // Priority queue
* const priorityQueue = new LiteQueuer((item: Task) => {
* processTask(item);
* }, {
* getPriority: task => task.priority,
* wait: 500
* });
*
* priorityQueue.addItem({ name: 'low', priority: 1 });
* priorityQueue.addItem({ name: 'high', priority: 10 });
* // Processes high priority task first
* ```
*/
var LiteQueuer = class {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this.isRunning = true;
this.pendingTick = false;
this.addItem = (item, position = this.options.addItemsTo, startProcessing = true) => {
if (this.items.length >= this.options.maxSize) return false;
if (this.options.getPriority) {
const priority = this.options.getPriority(item);
if (priority !== void 0) {
const insertIndex = this.items.findIndex((existing) => {
return (this.options.getPriority(existing) ?? -Infinity) < priority;
});
if (insertIndex === -1) this.items.push(item);
else this.items.splice(insertIndex, 0, item);
} else this.insertAtPosition(item, position);
} else this.insertAtPosition(item, position);
if (startProcessing && this.isRunning && !this.pendingTick) this.tick();
return true;
};
this.insertAtPosition = (item, position) => {
if (position === "front") this.items.unshift(item);
else this.items.push(item);
};
this.getNextItem = (position = this.options.getItemsFrom) => {
if (this.items.length === 0) return;
let item;
if (this.options.getPriority || position === "front") item = this.items.shift();
else item = this.items.pop();
return item;
};
this.execute = (position) => {
const item = this.getNextItem(position);
if (item !== void 0) this.fn(item);
return item;
};
this.tick = () => {
if (!this.isRunning) {
this.pendingTick = false;
return;
}
this.pendingTick = true;
while (this.items.length > 0) {
if (this.execute(this.options.getItemsFrom) === void 0) break;
const wait = this.options.wait;
if (wait > 0) {
this.timeoutId = setTimeout(() => this.tick(), wait);
return;
}
}
this.pendingTick = false;
};
this.start = () => {
this.isRunning = true;
if (!this.pendingTick && this.items.length > 0) this.tick();
};
this.stop = () => {
this.clearTimeout();
this.isRunning = false;
this.pendingTick = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.peekNextItem = (position = "front") => {
if (this.items.length === 0) return;
if (this.options.getPriority || position === "front") return this.items[0];
else return this.items[this.items.length - 1];
};
this.peekAllItems = () => {
return [...this.items];
};
this.flush = (numberOfItems = this.items.length, position) => {
this.clearTimeout();
for (let i = 0; i < numberOfItems && this.items.length > 0; i++) this.execute(position);
if (this.isRunning && this.items.length > 0 && !this.pendingTick) this.tick();
};
this.flushAsBatch = (batchFunction) => {
const items = this.peekAllItems();
this.clear();
batchFunction(items);
};
this.clear = () => {
this.items = [];
};
this.options.addItemsTo = this.options.addItemsTo ?? "back";
this.options.getItemsFrom = this.options.getItemsFrom ?? "front";
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? 0;
this.isRunning = this.options.started;
if (this.options.initialItems) for (const item of this.options.initialItems) this.addItem(item, this.options.addItemsTo, false);
if (this.isRunning && this.items.length > 0) this.tick();
}
/**
* Number of items currently in the queue
*/
get size() {
return this.items.length;
}
/**
* Whether the queue is empty
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning() {
return this.isRunning;
}
};
/**
* Creates a lightweight queue that processes items using the provided function.
*
* This is an alternative to the queue function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a queuer with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const processItem = liteQueue((item: string) => {
* console.log('Processing:', item);
* }, { wait: 1000 });
*
* processItem('task1');
* processItem('task2');
* // Processes each item with 1 second delay between them
* ```
*/
function liteQueue(fn, options = {}) {
const queuer = new LiteQueuer(fn, options);
return (item) => queuer.addItem(item);
}
//#endregion
export { LiteQueuer, liteQueue };
//# sourceMappingURL=lite-queuer.js.map
{"version":3,"file":"lite-queuer.js","names":["fn: (item: TValue) => void","options: LiteQueuerOptions<TValue>","item: TValue | undefined"],"sources":["../src/lite-queuer.ts"],"sourcesContent":["/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO for getNextItem)\n * - 'back': Operate on the back of the queue (LIFO for getNextItem)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * Options for configuring a lite queuer instance\n */\nexport interface LiteQueuerOptions<TValue> {\n /**\n * Default position to add items to the queue\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queue\n * Higher priority items will be processed first\n * Return undefined for items that should use positional ordering\n */\n getPriority?: (item: TValue) => number | undefined\n /**\n * Initial items to populate the queue with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queue\n */\n maxSize?: number\n /**\n * Whether the queuer should start processing items immediately\n * @default true\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items\n * @default 0\n */\n wait?: number\n}\n\n/**\n * A lightweight class that creates a queue for processing items.\n *\n * This is an alternative to the Queuer in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * no callbacks, and provides only essential queueing functionality.\n *\n * The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based\n * processing of items. Items can be processed automatically with configurable wait times\n * between executions, or processed manually using the execute methods.\n *\n * Features included:\n * - Automatic or manual processing of items\n * - FIFO, LIFO, and priority-based ordering\n * - Queue size limits with item rejection\n * - Configurable wait times between processing\n * - Batch processing capabilities\n * - Start/stop processing control\n * - Callback support for monitoring execution, rejection, and state change events\n *\n * Features NOT included (compared to core Queuer):\n * - No TanStack Store state management\n * - No devtools integration\n * - No item expiration functionality (no onExpire callback)\n * - No dynamic options updates (setOptions)\n * - No detailed state tracking (execution counts, etc.)\n *\n * Queue behavior:\n * - Default: FIFO (add to back, process from front)\n * - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'\n * - Priority: Provide getPriority function; higher values processed first\n *\n * @example\n * ```ts\n * // Basic FIFO queue\n * const queue = new LiteQueuer((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 100 });\n *\n * queue.addItem('task1');\n * queue.addItem('task2');\n * // Processes: task1, then task2 after 100ms delay\n * ```\n *\n * @example\n * ```ts\n * // Priority queue\n * const priorityQueue = new LiteQueuer((item: Task) => {\n * processTask(item);\n * }, {\n * getPriority: task => task.priority,\n * wait: 500\n * });\n *\n * priorityQueue.addItem({ name: 'low', priority: 1 });\n * priorityQueue.addItem({ name: 'high', priority: 10 });\n * // Processes high priority task first\n * ```\n */\nexport class LiteQueuer<TValue> {\n private items: Array<TValue> = []\n private timeoutId: NodeJS.Timeout | null = null\n private isRunning = true\n private pendingTick = false\n\n constructor(\n public fn: (item: TValue) => void,\n public options: LiteQueuerOptions<TValue> = {},\n ) {\n // Set defaults\n this.options.addItemsTo = this.options.addItemsTo ?? 'back'\n this.options.getItemsFrom = this.options.getItemsFrom ?? 'front'\n this.options.maxSize = this.options.maxSize ?? Infinity\n this.options.started = this.options.started ?? true\n this.options.wait = this.options.wait ?? 0\n\n this.isRunning = this.options.started\n\n // Add initial items if provided\n if (this.options.initialItems) {\n for (const item of this.options.initialItems) {\n this.addItem(item, this.options.addItemsTo, false)\n }\n }\n\n // Start processing if enabled and has items\n if (this.isRunning && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Number of items currently in the queue\n */\n get size(): number {\n return this.items.length\n }\n\n /**\n * Whether the queue is empty\n */\n get isEmpty(): boolean {\n return this.items.length === 0\n }\n\n /**\n * Whether the queue is currently running (auto-processing items)\n */\n get isQueueRunning(): boolean {\n return this.isRunning\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected.\n * Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * @example\n * ```ts\n * queue.addItem('task1'); // Add to default position (back)\n * queue.addItem('task2', 'front'); // Add to front\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo!,\n startProcessing: boolean = true,\n ): boolean => {\n // Check size limit\n if (this.items.length >= this.options.maxSize!) {\n return false\n }\n\n // Handle priority insertion\n if (this.options.getPriority) {\n const priority = this.options.getPriority(item)\n if (priority !== undefined) {\n // Find insertion point for priority\n const insertIndex = this.items.findIndex((existing) => {\n const existingPriority = this.options.getPriority!(existing)\n // Treat undefined priority as negative infinity for comparison\n const effectivePriority = existingPriority ?? -Infinity\n return effectivePriority < priority\n })\n\n if (insertIndex === -1) {\n this.items.push(item)\n } else {\n this.items.splice(insertIndex, 0, item)\n }\n } else {\n // No priority, use position\n this.insertAtPosition(item, position)\n }\n } else {\n // No priority function, use position\n this.insertAtPosition(item, position)\n }\n\n // Start processing if running and not already processing\n if (startProcessing && this.isRunning && !this.pendingTick) {\n this.tick()\n }\n\n return true\n }\n\n private insertAtPosition = (item: TValue, position: QueuePosition): void => {\n if (position === 'front') {\n this.items.unshift(item)\n } else {\n this.items.push(item)\n }\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * @example\n * ```ts\n * const nextItem = queue.getNextItem(); // Get from default position (front)\n * const lastItem = queue.getNextItem('back'); // Get from back (LIFO)\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom!,\n ): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n if (this.options.getPriority || position === 'front') {\n item = this.items.shift()\n } else {\n item = this.items.pop()\n }\n\n return item\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * @example\n * ```ts\n * queue.execute(); // Execute from default position\n * queue.execute('back'); // Execute from back (LIFO)\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n }\n return item\n }\n\n /**\n * Internal method that processes items in the queue with wait intervals\n */\n private tick = (): void => {\n if (!this.isRunning) {\n this.pendingTick = false\n return\n }\n\n this.pendingTick = true\n\n // Process items while queue is not empty\n while (this.items.length > 0) {\n const item = this.execute(this.options.getItemsFrom)\n if (item === undefined) {\n break\n }\n\n const wait = this.options.wait!\n if (wait > 0) {\n // Schedule next processing after wait time\n this.timeoutId = setTimeout(() => this.tick(), wait)\n return\n }\n\n // No wait time, continue processing immediately\n }\n\n this.pendingTick = false\n }\n\n /**\n * Starts processing items in the queue. If already running, does nothing.\n */\n start = (): void => {\n this.isRunning = true\n if (!this.pendingTick && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = (): void => {\n this.clearTimeout()\n this.isRunning = false\n this.pendingTick = false\n }\n\n /**\n * Clears any pending timeout\n */\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = null\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * @example\n * ```ts\n * const next = queue.peekNextItem(); // Peek at front\n * const last = queue.peekNextItem('back'); // Peek at back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n if (this.options.getPriority || position === 'front') {\n return this.items[0]\n } else {\n return this.items[this.items.length - 1]\n }\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.items]\n }\n\n /**\n * Processes a specified number of items immediately with no wait time.\n * If no numberOfItems is provided, all items will be processed.\n *\n * @example\n * ```ts\n * queue.flush(); // Process all items immediately\n * queue.flush(3); // Process next 3 items immediately\n * ```\n */\n flush = (\n numberOfItems: number = this.items.length,\n position?: QueuePosition,\n ): void => {\n this.clearTimeout() // Clear any pending timeout\n for (let i = 0; i < numberOfItems && this.items.length > 0; i++) {\n this.execute(position)\n }\n // Restart normal processing if still running and has items\n if (this.isRunning && this.items.length > 0 && !this.pendingTick) {\n this.tick()\n }\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function.\n * The queue is cleared after processing.\n *\n * @example\n * ```ts\n * queue.flushAsBatch((items) => {\n * console.log('Processing batch:', items);\n * // Process all items together\n * });\n * ```\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.peekAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Removes all items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.items = []\n }\n}\n\n/**\n * Creates a lightweight queue that processes items using the provided function.\n *\n * This is an alternative to the queue 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 queuer with no external dependencies, devtools integration, or reactive state.\n *\n * @example\n * ```ts\n * const processItem = liteQueue((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 1000 });\n *\n * processItem('task1');\n * processItem('task2');\n * // Processes each item with 1 second delay between them\n * ```\n */\nexport function liteQueue<TValue>(\n fn: (item: TValue) => void,\n options: LiteQueuerOptions<TValue> = {},\n): (item: TValue) => boolean {\n const queuer = new LiteQueuer(fn, options)\n return (item: TValue) => queuer.addItem(item)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GA,IAAa,aAAb,MAAgC;CAM9B,YACE,AAAOA,IACP,AAAOC,UAAqC,EAAE,EAC9C;EAFO;EACA;eAPsB,EAAE;mBACU;mBACvB;qBACE;kBA8DpB,MACA,WAA0B,KAAK,QAAQ,YACvC,kBAA2B,SACf;AAEZ,OAAI,KAAK,MAAM,UAAU,KAAK,QAAQ,QACpC,QAAO;AAIT,OAAI,KAAK,QAAQ,aAAa;IAC5B,MAAM,WAAW,KAAK,QAAQ,YAAY,KAAK;AAC/C,QAAI,aAAa,QAAW;KAE1B,MAAM,cAAc,KAAK,MAAM,WAAW,aAAa;AAIrD,cAHyB,KAAK,QAAQ,YAAa,SAAS,IAEd,aACnB;OAC3B;AAEF,SAAI,gBAAgB,GAClB,MAAK,MAAM,KAAK,KAAK;SAErB,MAAK,MAAM,OAAO,aAAa,GAAG,KAAK;UAIzC,MAAK,iBAAiB,MAAM,SAAS;SAIvC,MAAK,iBAAiB,MAAM,SAAS;AAIvC,OAAI,mBAAmB,KAAK,aAAa,CAAC,KAAK,YAC7C,MAAK,MAAM;AAGb,UAAO;;2BAGmB,MAAc,aAAkC;AAC1E,OAAI,aAAa,QACf,MAAK,MAAM,QAAQ,KAAK;OAExB,MAAK,MAAM,KAAK,KAAK;;sBAevB,WAA0B,KAAK,QAAQ,iBAChB;AACvB,OAAI,KAAK,MAAM,WAAW,EACxB;GAGF,IAAIC;AAGJ,OAAI,KAAK,QAAQ,eAAe,aAAa,QAC3C,QAAO,KAAK,MAAM,OAAO;OAEzB,QAAO,KAAK,MAAM,KAAK;AAGzB,UAAO;;kBAYE,aAAiD;GAC1D,MAAM,OAAO,KAAK,YAAY,SAAS;AACvC,OAAI,SAAS,OACX,MAAK,GAAG,KAAK;AAEf,UAAO;;oBAMkB;AACzB,OAAI,CAAC,KAAK,WAAW;AACnB,SAAK,cAAc;AACnB;;AAGF,QAAK,cAAc;AAGnB,UAAO,KAAK,MAAM,SAAS,GAAG;AAE5B,QADa,KAAK,QAAQ,KAAK,QAAQ,aAAa,KACvC,OACX;IAGF,MAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,OAAO,GAAG;AAEZ,UAAK,YAAY,iBAAiB,KAAK,MAAM,EAAE,KAAK;AACpD;;;AAMJ,QAAK,cAAc;;qBAMD;AAClB,QAAK,YAAY;AACjB,OAAI,CAAC,KAAK,eAAe,KAAK,MAAM,SAAS,EAC3C,MAAK,MAAM;;oBAOI;AACjB,QAAK,cAAc;AACnB,QAAK,YAAY;AACjB,QAAK,cAAc;;4BAMc;AACjC,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;;uBAaL,WAA0B,YAAgC;AACxE,OAAI,KAAK,MAAM,WAAW,EACxB;AAGF,OAAI,KAAK,QAAQ,eAAe,aAAa,QAC3C,QAAO,KAAK,MAAM;OAElB,QAAO,KAAK,MAAM,KAAK,MAAM,SAAS;;4BAON;AAClC,UAAO,CAAC,GAAG,KAAK,MAAM;;gBActB,gBAAwB,KAAK,MAAM,QACnC,aACS;AACT,QAAK,cAAc;AACnB,QAAK,IAAI,IAAI,GAAG,IAAI,iBAAiB,KAAK,MAAM,SAAS,GAAG,IAC1D,MAAK,QAAQ,SAAS;AAGxB,OAAI,KAAK,aAAa,KAAK,MAAM,SAAS,KAAK,CAAC,KAAK,YACnD,MAAK,MAAM;;uBAgBC,kBAAwD;GACtE,MAAM,QAAQ,KAAK,cAAc;AACjC,QAAK,OAAO;AACZ,iBAAc,MAAM;;qBAMF;AAClB,QAAK,QAAQ,EAAE;;AA9Rf,OAAK,QAAQ,aAAa,KAAK,QAAQ,cAAc;AACrD,OAAK,QAAQ,eAAe,KAAK,QAAQ,gBAAgB;AACzD,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,OAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAEzC,OAAK,YAAY,KAAK,QAAQ;AAG9B,MAAI,KAAK,QAAQ,aACf,MAAK,MAAM,QAAQ,KAAK,QAAQ,aAC9B,MAAK,QAAQ,MAAM,KAAK,QAAQ,YAAY,MAAM;AAKtD,MAAI,KAAK,aAAa,KAAK,MAAM,SAAS,EACxC,MAAK,MAAM;;;;;CAOf,IAAI,OAAe;AACjB,SAAO,KAAK,MAAM;;;;;CAMpB,IAAI,UAAmB;AACrB,SAAO,KAAK,MAAM,WAAW;;;;;CAM/B,IAAI,iBAA0B;AAC5B,SAAO,KAAK;;;;;;;;;;;;;;;;;;;;;AA6QhB,SAAgB,UACd,IACA,UAAqC,EAAE,EACZ;CAC3B,MAAM,SAAS,IAAI,WAAW,IAAI,QAAQ;AAC1C,SAAQ,SAAiB,OAAO,QAAQ,KAAK"}
//#region src/lite-rate-limiter.ts
/**
* A lightweight class that creates a rate-limited function.
*
* This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential rate limiting functionality.
*
* Rate limiting allows a function to execute up to a limit within a time window,
* then blocks all subsequent calls until the window passes. This can lead to "bursty" behavior where
* all executions happen immediately, followed by a complete block.
*
* The rate limiter supports two types of windows:
* - 'fixed': A strict window that resets after the window period. All executions within the window count
* towards the limit, and the window resets completely after the period.
* - 'sliding': A rolling window that allows executions as old ones expire. This provides a more
* consistent rate of execution over time.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)
* - Simple state management - uses basic private properties instead of reactive stores
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter((id: string) => {
* api.getData(id);
* }, { limit: 5, window: 1000 });
*
* // First 5 calls will execute, then block until window resets
* if (rateLimiter.maybeExecute('123')) {
* console.log('API call made');
* } else {
* console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');
* }
* ```
*/
var LiteRateLimiter = class {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.executionTimes = [];
this.timeoutIds = /* @__PURE__ */ new Set();
this.maybeExecute = (...args) => {
this.cleanupOldExecutions();
if (this.getExecutionTimesInWindow().length < this.options.limit) {
this.execute(...args);
return true;
}
this.options.onReject?.(this);
return false;
};
this.execute = (...args) => {
const now = Date.now();
this.fn(...args);
this.options.onExecute?.(args, this);
this.executionTimes.push(now);
this.setCleanupTimeout(now);
};
this.getExecutionTimesInWindow = () => {
if (this.options.windowType === "sliding") return this.executionTimes.filter((time) => time > Date.now() - this.options.window);
else {
if (this.executionTimes.length === 0) return [];
const windowStart = Math.min(...this.executionTimes);
const windowEnd = windowStart + this.options.window;
if (Date.now() > windowEnd) return [];
return this.executionTimes.filter((time) => time >= windowStart && time <= windowEnd);
}
};
this.setCleanupTimeout = (executionTime) => {
if (this.options.windowType === "sliding" || this.timeoutIds.size === 0) {
const timeUntilExpiration = executionTime - Date.now() + this.options.window + 1;
const timeoutId = setTimeout(() => {
this.cleanupOldExecutions();
this.clearTimeout(timeoutId);
}, timeUntilExpiration);
this.timeoutIds.add(timeoutId);
}
};
this.clearTimeout = (timeoutId) => {
clearTimeout(timeoutId);
this.timeoutIds.delete(timeoutId);
};
this.clearTimeouts = () => {
this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
this.timeoutIds.clear();
};
this.cleanupOldExecutions = () => {
this.executionTimes = this.getExecutionTimesInWindow();
};
this.getRemainingInWindow = () => {
const relevantExecutionTimes = this.getExecutionTimesInWindow();
return Math.max(0, this.options.limit - relevantExecutionTimes.length);
};
this.getMsUntilNextWindow = () => {
if (this.getRemainingInWindow() > 0) return 0;
return (this.executionTimes[0] ?? Infinity) + this.options.window - Date.now();
};
this.reset = () => {
this.executionTimes = [];
this.clearTimeouts();
};
if (this.options.windowType === void 0) this.options.windowType = "fixed";
}
};
/**
* Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
*
* This is an alternative to the rateLimit function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a rate limiter with no external dependencies, devtools integration, or reactive state.
*
* Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.
* This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).
*
* @example
* ```ts
* const rateLimitedApi = liteRateLimit(makeApiCall, {
* limit: 5,
* window: 60000, // 1 minute
* windowType: 'sliding'
* });
*
* // First 5 calls execute immediately
* // Additional calls are rejected until window allows
* rateLimitedApi();
* ```
*
* @example
* ```ts
* // Fixed window - all 10 calls happen in first second, then 10 second wait
* const rateLimitedFixed = liteRateLimit(logEvent, {
* limit: 10,
* window: 10000,
* windowType: 'fixed'
* });
* ```
*/
function liteRateLimit(fn, options) {
return new LiteRateLimiter(fn, options).maybeExecute;
}
//#endregion
exports.LiteRateLimiter = LiteRateLimiter;
exports.liteRateLimit = liteRateLimit;
//# sourceMappingURL=lite-rate-limiter.cjs.map
{"version":3,"file":"lite-rate-limiter.cjs","names":["fn: TFn","options: LiteRateLimiterOptions<TFn>"],"sources":["../src/lite-rate-limiter.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite rate-limited function\n */\nexport interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Maximum number of executions allowed within the time window.\n */\n limit: number\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Optional callback function that is called when an execution is rejected due to rate limiting\n */\n onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Time window in milliseconds within which the limit applies.\n */\n window: 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 * A lightweight class that creates a rate-limited function.\n *\n * This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential rate limiting functionality.\n *\n * Rate limiting 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 *\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 * Features:\n * - Zero dependencies - no external libraries required\n * - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)\n * - Simple state management - uses basic private properties instead of reactive stores\n * - Lightweight - designed for use in npm packages where bundle size matters\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter((id: string) => {\n * api.getData(id);\n * }, { limit: 5, window: 1000 });\n *\n * // First 5 calls will execute, then block until window resets\n * if (rateLimiter.maybeExecute('123')) {\n * console.log('API call made');\n * } else {\n * console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');\n * }\n * ```\n */\nexport class LiteRateLimiter<TFn extends AnyFunction> {\n private executionTimes: Array<number> = []\n private timeoutIds: Set<NodeJS.Timeout> = new Set()\n\n constructor(\n public fn: TFn,\n public options: LiteRateLimiterOptions<TFn>,\n ) {\n // Default windowType to 'fixed' if not specified\n if (this.options.windowType === undefined) {\n this.options.windowType = 'fixed'\n }\n }\n\n /**\n * Attempts to execute the rate-limited function if within the configured limits.\n * Returns true if executed, false if rejected due to rate limiting.\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });\n *\n * // First 5 calls return true\n * rateLimiter.maybeExecute('arg1', 'arg2'); // true\n *\n * // Additional calls within the window return false\n * rateLimiter.maybeExecute('arg1', 'arg2'); // false\n * ```\n */\n maybeExecute = (...args: Parameters<TFn>): boolean => {\n this.cleanupOldExecutions()\n\n const relevantExecutionTimes = this.getExecutionTimesInWindow()\n\n if (relevantExecutionTimes.length < this.options.limit) {\n this.execute(...args)\n return true\n }\n\n this.options.onReject?.(this)\n return false\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.executionTimes.push(now)\n this.setCleanupTimeout(now)\n }\n\n private getExecutionTimesInWindow = (): Array<number> => {\n if (this.options.windowType === 'sliding') {\n // For sliding window, return all executions within the current window\n return this.executionTimes.filter(\n (time) => time > Date.now() - this.options.window,\n )\n } else {\n // For fixed window, return all executions in the current window\n if (this.executionTimes.length === 0) {\n return []\n }\n const oldestExecution = Math.min(...this.executionTimes)\n const windowStart = oldestExecution\n const windowEnd = windowStart + this.options.window\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.executionTimes.filter(\n (time) => time >= windowStart && time <= windowEnd,\n )\n }\n }\n\n private 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.options.window + 1\n const timeoutId = setTimeout(() => {\n this.cleanupOldExecutions()\n this.clearTimeout(timeoutId)\n }, timeUntilExpiration)\n this.timeoutIds.add(timeoutId)\n }\n }\n\n private clearTimeout = (timeoutId: NodeJS.Timeout): void => {\n clearTimeout(timeoutId)\n this.timeoutIds.delete(timeoutId)\n }\n\n private clearTimeouts = (): void => {\n this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId))\n this.timeoutIds.clear()\n }\n\n private cleanupOldExecutions = (): void => {\n this.executionTimes = this.getExecutionTimesInWindow()\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.options.limit - relevantExecutionTimes.length)\n }\n\n /**\n * Returns the number of milliseconds until the next execution will be possible.\n * Returns 0 if executions are currently allowed.\n */\n getMsUntilNextWindow = (): number => {\n if (this.getRemainingInWindow() > 0) {\n return 0\n }\n const oldestExecution = this.executionTimes[0] ?? Infinity\n return oldestExecution + this.options.window - Date.now()\n }\n\n /**\n * Resets the rate limiter state, clearing all execution history.\n */\n reset = (): void => {\n this.executionTimes = []\n this.clearTimeouts()\n }\n}\n\n/**\n * Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.\n *\n * This is an alternative to the rateLimit 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 rate limiter with no external dependencies, devtools integration, or reactive state.\n *\n * Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.\n * This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).\n *\n * @example\n * ```ts\n * const rateLimitedApi = liteRateLimit(makeApiCall, {\n * limit: 5,\n * window: 60000, // 1 minute\n * windowType: 'sliding'\n * });\n *\n * // First 5 calls execute immediately\n * // Additional calls are rejected until window allows\n * rateLimitedApi();\n * ```\n *\n * @example\n * ```ts\n * // Fixed window - all 10 calls happen in first second, then 10 second wait\n * const rateLimitedFixed = liteRateLimit(logEvent, {\n * limit: 10,\n * window: 10000,\n * windowType: 'fixed'\n * });\n * ```\n */\nexport function liteRateLimit<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteRateLimiterOptions<TFn>,\n): (...args: Parameters<TFn>) => boolean {\n const rateLimiter = new LiteRateLimiter(fn, options)\n return rateLimiter.maybeExecute\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,IAAa,kBAAb,MAAsD;CAIpD,YACE,AAAOA,IACP,AAAOC,SACP;EAFO;EACA;wBAL+B,EAAE;oCACA,IAAI,KAAK;uBA2BnC,GAAG,SAAmC;AACpD,QAAK,sBAAsB;AAI3B,OAF+B,KAAK,2BAA2B,CAEpC,SAAS,KAAK,QAAQ,OAAO;AACtD,SAAK,QAAQ,GAAG,KAAK;AACrB,WAAO;;AAGT,QAAK,QAAQ,WAAW,KAAK;AAC7B,UAAO;;kBAGU,GAAG,SAAgC;GACpD,MAAM,MAAM,KAAK,KAAK;AACtB,QAAK,GAAG,GAAG,KAAK;AAChB,QAAK,QAAQ,YAAY,MAAM,KAAK;AACpC,QAAK,eAAe,KAAK,IAAI;AAC7B,QAAK,kBAAkB,IAAI;;yCAG4B;AACvD,OAAI,KAAK,QAAQ,eAAe,UAE9B,QAAO,KAAK,eAAe,QACxB,SAAS,OAAO,KAAK,KAAK,GAAG,KAAK,QAAQ,OAC5C;QACI;AAEL,QAAI,KAAK,eAAe,WAAW,EACjC,QAAO,EAAE;IAGX,MAAM,cADkB,KAAK,IAAI,GAAG,KAAK,eAAe;IAExD,MAAM,YAAY,cAAc,KAAK,QAAQ;AAI7C,QAHY,KAAK,KAAK,GAGZ,UACR,QAAO,EAAE;AAIX,WAAO,KAAK,eAAe,QACxB,SAAS,QAAQ,eAAe,QAAQ,UAC1C;;;4BAIwB,kBAAgC;AAC3D,OACE,KAAK,QAAQ,eAAe,aAC5B,KAAK,WAAW,SAAS,GACzB;IAEA,MAAM,sBAAsB,gBADhB,KAAK,KAAK,GAC4B,KAAK,QAAQ,SAAS;IACxE,MAAM,YAAY,iBAAiB;AACjC,UAAK,sBAAsB;AAC3B,UAAK,aAAa,UAAU;OAC3B,oBAAoB;AACvB,SAAK,WAAW,IAAI,UAAU;;;uBAIV,cAAoC;AAC1D,gBAAa,UAAU;AACvB,QAAK,WAAW,OAAO,UAAU;;6BAGC;AAClC,QAAK,WAAW,SAAS,cAAc,aAAa,UAAU,CAAC;AAC/D,QAAK,WAAW,OAAO;;oCAGkB;AACzC,QAAK,iBAAiB,KAAK,2BAA2B;;oCAMnB;GACnC,MAAM,yBAAyB,KAAK,2BAA2B;AAC/D,UAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,QAAQ,uBAAuB,OAAO;;oCAOnC;AACnC,OAAI,KAAK,sBAAsB,GAAG,EAChC,QAAO;AAGT,WADwB,KAAK,eAAe,MAAM,YACzB,KAAK,QAAQ,SAAS,KAAK,KAAK;;qBAMvC;AAClB,QAAK,iBAAiB,EAAE;AACxB,QAAK,eAAe;;AA5HpB,MAAI,KAAK,QAAQ,eAAe,OAC9B,MAAK,QAAQ,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgKhC,SAAgB,cACd,IACA,SACuC;AAEvC,QADoB,IAAI,gBAAgB,IAAI,QAAQ,CACjC"}
import { AnyFunction } from "@tanstack/pacer/types";
//#region src/lite-rate-limiter.d.ts
/**
* Options for configuring a lite rate-limited function
*/
interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Maximum number of executions allowed within the time window.
*/
limit: number;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Optional callback function that is called when an execution is rejected due to rate limiting
*/
onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Time window in milliseconds within which the limit applies.
*/
window: number;
/**
* Type of window to use for rate limiting
* - 'fixed': Uses a fixed window that resets after the window period
* - 'sliding': Uses a sliding window that allows executions as old ones expire
* Defaults to 'fixed'
*/
windowType?: 'fixed' | 'sliding';
}
/**
* A lightweight class that creates a rate-limited function.
*
* This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential rate limiting functionality.
*
* Rate limiting allows a function to execute up to a limit within a time window,
* then blocks all subsequent calls until the window passes. This can lead to "bursty" behavior where
* all executions happen immediately, followed by a complete block.
*
* The rate limiter supports two types of windows:
* - 'fixed': A strict window that resets after the window period. All executions within the window count
* towards the limit, and the window resets completely after the period.
* - 'sliding': A rolling window that allows executions as old ones expire. This provides a more
* consistent rate of execution over time.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)
* - Simple state management - uses basic private properties instead of reactive stores
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter((id: string) => {
* api.getData(id);
* }, { limit: 5, window: 1000 });
*
* // First 5 calls will execute, then block until window resets
* if (rateLimiter.maybeExecute('123')) {
* console.log('API call made');
* } else {
* console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');
* }
* ```
*/
declare class LiteRateLimiter<TFn extends AnyFunction> {
fn: TFn;
options: LiteRateLimiterOptions<TFn>;
private executionTimes;
private timeoutIds;
constructor(fn: TFn, options: LiteRateLimiterOptions<TFn>);
/**
* Attempts to execute the rate-limited function if within the configured limits.
* Returns true if executed, false if rejected due to rate limiting.
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });
*
* // First 5 calls return true
* rateLimiter.maybeExecute('arg1', 'arg2'); // true
*
* // Additional calls within the window return false
* rateLimiter.maybeExecute('arg1', 'arg2'); // false
* ```
*/
maybeExecute: (...args: Parameters<TFn>) => boolean;
private execute;
private getExecutionTimesInWindow;
private setCleanupTimeout;
private clearTimeout;
private clearTimeouts;
private cleanupOldExecutions;
/**
* Returns the number of remaining executions allowed in the current window.
*/
getRemainingInWindow: () => number;
/**
* Returns the number of milliseconds until the next execution will be possible.
* Returns 0 if executions are currently allowed.
*/
getMsUntilNextWindow: () => number;
/**
* Resets the rate limiter state, clearing all execution history.
*/
reset: () => void;
}
/**
* Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
*
* This is an alternative to the rateLimit function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a rate limiter with no external dependencies, devtools integration, or reactive state.
*
* Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.
* This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).
*
* @example
* ```ts
* const rateLimitedApi = liteRateLimit(makeApiCall, {
* limit: 5,
* window: 60000, // 1 minute
* windowType: 'sliding'
* });
*
* // First 5 calls execute immediately
* // Additional calls are rejected until window allows
* rateLimitedApi();
* ```
*
* @example
* ```ts
* // Fixed window - all 10 calls happen in first second, then 10 second wait
* const rateLimitedFixed = liteRateLimit(logEvent, {
* limit: 10,
* window: 10000,
* windowType: 'fixed'
* });
* ```
*/
declare function liteRateLimit<TFn extends AnyFunction>(fn: TFn, options: LiteRateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
//#endregion
export { LiteRateLimiter, LiteRateLimiterOptions, liteRateLimit };
//# sourceMappingURL=lite-rate-limiter.d.cts.map
import { AnyFunction } from "@tanstack/pacer/types";
//#region src/lite-rate-limiter.d.ts
/**
* Options for configuring a lite rate-limited function
*/
interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Maximum number of executions allowed within the time window.
*/
limit: number;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Optional callback function that is called when an execution is rejected due to rate limiting
*/
onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Time window in milliseconds within which the limit applies.
*/
window: number;
/**
* Type of window to use for rate limiting
* - 'fixed': Uses a fixed window that resets after the window period
* - 'sliding': Uses a sliding window that allows executions as old ones expire
* Defaults to 'fixed'
*/
windowType?: 'fixed' | 'sliding';
}
/**
* A lightweight class that creates a rate-limited function.
*
* This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential rate limiting functionality.
*
* Rate limiting allows a function to execute up to a limit within a time window,
* then blocks all subsequent calls until the window passes. This can lead to "bursty" behavior where
* all executions happen immediately, followed by a complete block.
*
* The rate limiter supports two types of windows:
* - 'fixed': A strict window that resets after the window period. All executions within the window count
* towards the limit, and the window resets completely after the period.
* - 'sliding': A rolling window that allows executions as old ones expire. This provides a more
* consistent rate of execution over time.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)
* - Simple state management - uses basic private properties instead of reactive stores
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter((id: string) => {
* api.getData(id);
* }, { limit: 5, window: 1000 });
*
* // First 5 calls will execute, then block until window resets
* if (rateLimiter.maybeExecute('123')) {
* console.log('API call made');
* } else {
* console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');
* }
* ```
*/
declare class LiteRateLimiter<TFn extends AnyFunction> {
fn: TFn;
options: LiteRateLimiterOptions<TFn>;
private executionTimes;
private timeoutIds;
constructor(fn: TFn, options: LiteRateLimiterOptions<TFn>);
/**
* Attempts to execute the rate-limited function if within the configured limits.
* Returns true if executed, false if rejected due to rate limiting.
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });
*
* // First 5 calls return true
* rateLimiter.maybeExecute('arg1', 'arg2'); // true
*
* // Additional calls within the window return false
* rateLimiter.maybeExecute('arg1', 'arg2'); // false
* ```
*/
maybeExecute: (...args: Parameters<TFn>) => boolean;
private execute;
private getExecutionTimesInWindow;
private setCleanupTimeout;
private clearTimeout;
private clearTimeouts;
private cleanupOldExecutions;
/**
* Returns the number of remaining executions allowed in the current window.
*/
getRemainingInWindow: () => number;
/**
* Returns the number of milliseconds until the next execution will be possible.
* Returns 0 if executions are currently allowed.
*/
getMsUntilNextWindow: () => number;
/**
* Resets the rate limiter state, clearing all execution history.
*/
reset: () => void;
}
/**
* Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
*
* This is an alternative to the rateLimit function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a rate limiter with no external dependencies, devtools integration, or reactive state.
*
* Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.
* This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).
*
* @example
* ```ts
* const rateLimitedApi = liteRateLimit(makeApiCall, {
* limit: 5,
* window: 60000, // 1 minute
* windowType: 'sliding'
* });
*
* // First 5 calls execute immediately
* // Additional calls are rejected until window allows
* rateLimitedApi();
* ```
*
* @example
* ```ts
* // Fixed window - all 10 calls happen in first second, then 10 second wait
* const rateLimitedFixed = liteRateLimit(logEvent, {
* limit: 10,
* window: 10000,
* windowType: 'fixed'
* });
* ```
*/
declare function liteRateLimit<TFn extends AnyFunction>(fn: TFn, options: LiteRateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
//#endregion
export { LiteRateLimiter, LiteRateLimiterOptions, liteRateLimit };
//# sourceMappingURL=lite-rate-limiter.d.ts.map
//#region src/lite-rate-limiter.ts
/**
* A lightweight class that creates a rate-limited function.
*
* This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential rate limiting functionality.
*
* Rate limiting allows a function to execute up to a limit within a time window,
* then blocks all subsequent calls until the window passes. This can lead to "bursty" behavior where
* all executions happen immediately, followed by a complete block.
*
* The rate limiter supports two types of windows:
* - 'fixed': A strict window that resets after the window period. All executions within the window count
* towards the limit, and the window resets completely after the period.
* - 'sliding': A rolling window that allows executions as old ones expire. This provides a more
* consistent rate of execution over time.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)
* - Simple state management - uses basic private properties instead of reactive stores
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter((id: string) => {
* api.getData(id);
* }, { limit: 5, window: 1000 });
*
* // First 5 calls will execute, then block until window resets
* if (rateLimiter.maybeExecute('123')) {
* console.log('API call made');
* } else {
* console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');
* }
* ```
*/
var LiteRateLimiter = class {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.executionTimes = [];
this.timeoutIds = /* @__PURE__ */ new Set();
this.maybeExecute = (...args) => {
this.cleanupOldExecutions();
if (this.getExecutionTimesInWindow().length < this.options.limit) {
this.execute(...args);
return true;
}
this.options.onReject?.(this);
return false;
};
this.execute = (...args) => {
const now = Date.now();
this.fn(...args);
this.options.onExecute?.(args, this);
this.executionTimes.push(now);
this.setCleanupTimeout(now);
};
this.getExecutionTimesInWindow = () => {
if (this.options.windowType === "sliding") return this.executionTimes.filter((time) => time > Date.now() - this.options.window);
else {
if (this.executionTimes.length === 0) return [];
const windowStart = Math.min(...this.executionTimes);
const windowEnd = windowStart + this.options.window;
if (Date.now() > windowEnd) return [];
return this.executionTimes.filter((time) => time >= windowStart && time <= windowEnd);
}
};
this.setCleanupTimeout = (executionTime) => {
if (this.options.windowType === "sliding" || this.timeoutIds.size === 0) {
const timeUntilExpiration = executionTime - Date.now() + this.options.window + 1;
const timeoutId = setTimeout(() => {
this.cleanupOldExecutions();
this.clearTimeout(timeoutId);
}, timeUntilExpiration);
this.timeoutIds.add(timeoutId);
}
};
this.clearTimeout = (timeoutId) => {
clearTimeout(timeoutId);
this.timeoutIds.delete(timeoutId);
};
this.clearTimeouts = () => {
this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
this.timeoutIds.clear();
};
this.cleanupOldExecutions = () => {
this.executionTimes = this.getExecutionTimesInWindow();
};
this.getRemainingInWindow = () => {
const relevantExecutionTimes = this.getExecutionTimesInWindow();
return Math.max(0, this.options.limit - relevantExecutionTimes.length);
};
this.getMsUntilNextWindow = () => {
if (this.getRemainingInWindow() > 0) return 0;
return (this.executionTimes[0] ?? Infinity) + this.options.window - Date.now();
};
this.reset = () => {
this.executionTimes = [];
this.clearTimeouts();
};
if (this.options.windowType === void 0) this.options.windowType = "fixed";
}
};
/**
* Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
*
* This is an alternative to the rateLimit function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a rate limiter with no external dependencies, devtools integration, or reactive state.
*
* Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.
* This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).
*
* @example
* ```ts
* const rateLimitedApi = liteRateLimit(makeApiCall, {
* limit: 5,
* window: 60000, // 1 minute
* windowType: 'sliding'
* });
*
* // First 5 calls execute immediately
* // Additional calls are rejected until window allows
* rateLimitedApi();
* ```
*
* @example
* ```ts
* // Fixed window - all 10 calls happen in first second, then 10 second wait
* const rateLimitedFixed = liteRateLimit(logEvent, {
* limit: 10,
* window: 10000,
* windowType: 'fixed'
* });
* ```
*/
function liteRateLimit(fn, options) {
return new LiteRateLimiter(fn, options).maybeExecute;
}
//#endregion
export { LiteRateLimiter, liteRateLimit };
//# sourceMappingURL=lite-rate-limiter.js.map
{"version":3,"file":"lite-rate-limiter.js","names":["fn: TFn","options: LiteRateLimiterOptions<TFn>"],"sources":["../src/lite-rate-limiter.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite rate-limited function\n */\nexport interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Maximum number of executions allowed within the time window.\n */\n limit: number\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Optional callback function that is called when an execution is rejected due to rate limiting\n */\n onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Time window in milliseconds within which the limit applies.\n */\n window: 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 * A lightweight class that creates a rate-limited function.\n *\n * This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential rate limiting functionality.\n *\n * Rate limiting 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 *\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 * Features:\n * - Zero dependencies - no external libraries required\n * - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)\n * - Simple state management - uses basic private properties instead of reactive stores\n * - Lightweight - designed for use in npm packages where bundle size matters\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter((id: string) => {\n * api.getData(id);\n * }, { limit: 5, window: 1000 });\n *\n * // First 5 calls will execute, then block until window resets\n * if (rateLimiter.maybeExecute('123')) {\n * console.log('API call made');\n * } else {\n * console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');\n * }\n * ```\n */\nexport class LiteRateLimiter<TFn extends AnyFunction> {\n private executionTimes: Array<number> = []\n private timeoutIds: Set<NodeJS.Timeout> = new Set()\n\n constructor(\n public fn: TFn,\n public options: LiteRateLimiterOptions<TFn>,\n ) {\n // Default windowType to 'fixed' if not specified\n if (this.options.windowType === undefined) {\n this.options.windowType = 'fixed'\n }\n }\n\n /**\n * Attempts to execute the rate-limited function if within the configured limits.\n * Returns true if executed, false if rejected due to rate limiting.\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });\n *\n * // First 5 calls return true\n * rateLimiter.maybeExecute('arg1', 'arg2'); // true\n *\n * // Additional calls within the window return false\n * rateLimiter.maybeExecute('arg1', 'arg2'); // false\n * ```\n */\n maybeExecute = (...args: Parameters<TFn>): boolean => {\n this.cleanupOldExecutions()\n\n const relevantExecutionTimes = this.getExecutionTimesInWindow()\n\n if (relevantExecutionTimes.length < this.options.limit) {\n this.execute(...args)\n return true\n }\n\n this.options.onReject?.(this)\n return false\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.executionTimes.push(now)\n this.setCleanupTimeout(now)\n }\n\n private getExecutionTimesInWindow = (): Array<number> => {\n if (this.options.windowType === 'sliding') {\n // For sliding window, return all executions within the current window\n return this.executionTimes.filter(\n (time) => time > Date.now() - this.options.window,\n )\n } else {\n // For fixed window, return all executions in the current window\n if (this.executionTimes.length === 0) {\n return []\n }\n const oldestExecution = Math.min(...this.executionTimes)\n const windowStart = oldestExecution\n const windowEnd = windowStart + this.options.window\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.executionTimes.filter(\n (time) => time >= windowStart && time <= windowEnd,\n )\n }\n }\n\n private 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.options.window + 1\n const timeoutId = setTimeout(() => {\n this.cleanupOldExecutions()\n this.clearTimeout(timeoutId)\n }, timeUntilExpiration)\n this.timeoutIds.add(timeoutId)\n }\n }\n\n private clearTimeout = (timeoutId: NodeJS.Timeout): void => {\n clearTimeout(timeoutId)\n this.timeoutIds.delete(timeoutId)\n }\n\n private clearTimeouts = (): void => {\n this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId))\n this.timeoutIds.clear()\n }\n\n private cleanupOldExecutions = (): void => {\n this.executionTimes = this.getExecutionTimesInWindow()\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.options.limit - relevantExecutionTimes.length)\n }\n\n /**\n * Returns the number of milliseconds until the next execution will be possible.\n * Returns 0 if executions are currently allowed.\n */\n getMsUntilNextWindow = (): number => {\n if (this.getRemainingInWindow() > 0) {\n return 0\n }\n const oldestExecution = this.executionTimes[0] ?? Infinity\n return oldestExecution + this.options.window - Date.now()\n }\n\n /**\n * Resets the rate limiter state, clearing all execution history.\n */\n reset = (): void => {\n this.executionTimes = []\n this.clearTimeouts()\n }\n}\n\n/**\n * Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.\n *\n * This is an alternative to the rateLimit 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 rate limiter with no external dependencies, devtools integration, or reactive state.\n *\n * Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.\n * This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).\n *\n * @example\n * ```ts\n * const rateLimitedApi = liteRateLimit(makeApiCall, {\n * limit: 5,\n * window: 60000, // 1 minute\n * windowType: 'sliding'\n * });\n *\n * // First 5 calls execute immediately\n * // Additional calls are rejected until window allows\n * rateLimitedApi();\n * ```\n *\n * @example\n * ```ts\n * // Fixed window - all 10 calls happen in first second, then 10 second wait\n * const rateLimitedFixed = liteRateLimit(logEvent, {\n * limit: 10,\n * window: 10000,\n * windowType: 'fixed'\n * });\n * ```\n */\nexport function liteRateLimit<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteRateLimiterOptions<TFn>,\n): (...args: Parameters<TFn>) => boolean {\n const rateLimiter = new LiteRateLimiter(fn, options)\n return rateLimiter.maybeExecute\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,IAAa,kBAAb,MAAsD;CAIpD,YACE,AAAOA,IACP,AAAOC,SACP;EAFO;EACA;wBAL+B,EAAE;oCACA,IAAI,KAAK;uBA2BnC,GAAG,SAAmC;AACpD,QAAK,sBAAsB;AAI3B,OAF+B,KAAK,2BAA2B,CAEpC,SAAS,KAAK,QAAQ,OAAO;AACtD,SAAK,QAAQ,GAAG,KAAK;AACrB,WAAO;;AAGT,QAAK,QAAQ,WAAW,KAAK;AAC7B,UAAO;;kBAGU,GAAG,SAAgC;GACpD,MAAM,MAAM,KAAK,KAAK;AACtB,QAAK,GAAG,GAAG,KAAK;AAChB,QAAK,QAAQ,YAAY,MAAM,KAAK;AACpC,QAAK,eAAe,KAAK,IAAI;AAC7B,QAAK,kBAAkB,IAAI;;yCAG4B;AACvD,OAAI,KAAK,QAAQ,eAAe,UAE9B,QAAO,KAAK,eAAe,QACxB,SAAS,OAAO,KAAK,KAAK,GAAG,KAAK,QAAQ,OAC5C;QACI;AAEL,QAAI,KAAK,eAAe,WAAW,EACjC,QAAO,EAAE;IAGX,MAAM,cADkB,KAAK,IAAI,GAAG,KAAK,eAAe;IAExD,MAAM,YAAY,cAAc,KAAK,QAAQ;AAI7C,QAHY,KAAK,KAAK,GAGZ,UACR,QAAO,EAAE;AAIX,WAAO,KAAK,eAAe,QACxB,SAAS,QAAQ,eAAe,QAAQ,UAC1C;;;4BAIwB,kBAAgC;AAC3D,OACE,KAAK,QAAQ,eAAe,aAC5B,KAAK,WAAW,SAAS,GACzB;IAEA,MAAM,sBAAsB,gBADhB,KAAK,KAAK,GAC4B,KAAK,QAAQ,SAAS;IACxE,MAAM,YAAY,iBAAiB;AACjC,UAAK,sBAAsB;AAC3B,UAAK,aAAa,UAAU;OAC3B,oBAAoB;AACvB,SAAK,WAAW,IAAI,UAAU;;;uBAIV,cAAoC;AAC1D,gBAAa,UAAU;AACvB,QAAK,WAAW,OAAO,UAAU;;6BAGC;AAClC,QAAK,WAAW,SAAS,cAAc,aAAa,UAAU,CAAC;AAC/D,QAAK,WAAW,OAAO;;oCAGkB;AACzC,QAAK,iBAAiB,KAAK,2BAA2B;;oCAMnB;GACnC,MAAM,yBAAyB,KAAK,2BAA2B;AAC/D,UAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,QAAQ,uBAAuB,OAAO;;oCAOnC;AACnC,OAAI,KAAK,sBAAsB,GAAG,EAChC,QAAO;AAGT,WADwB,KAAK,eAAe,MAAM,YACzB,KAAK,QAAQ,SAAS,KAAK,KAAK;;qBAMvC;AAClB,QAAK,iBAAiB,EAAE;AACxB,QAAK,eAAe;;AA5HpB,MAAI,KAAK,QAAQ,eAAe,OAC9B,MAAK,QAAQ,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgKhC,SAAgB,cACd,IACA,SACuC;AAEvC,QADoB,IAAI,gBAAgB,IAAI,QAAQ,CACjC"}
//#region src/lite-throttler.ts
/**
* A lightweight class that creates a throttled function.
*
* This is an alternative to the Throttler in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential throttling functionality.
*
* Throttling ensures a function is called at most once within a specified time window.
* Unlike debouncing which waits for a pause in calls, throttling guarantees consistent
* execution timing regardless of call frequency.
*
* Supports both leading and trailing edge execution:
* - Leading: Execute immediately on first call (default: true)
* - Trailing: Execute after wait period if called during throttle (default: true)
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const throttler = new LiteThrottler((scrollY: number) => {
* updateScrollPosition(scrollY);
* }, {
* wait: 100,
* onExecute: (args, throttler) => {
* console.log('Updated scroll position:', args[0]);
* }
* });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', () => {
* throttler.maybeExecute(window.scrollY);
* });
* ```
*/
var LiteThrottler = class {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.lastExecutionTime = 0;
this.isPending = false;
this.maybeExecute = (...args) => {
const timeSinceLastExecution = Date.now() - this.lastExecutionTime;
if (this.options.leading && timeSinceLastExecution >= this.options.wait) this.execute(...args);
else {
this.lastArgs = args;
if (!this.timeoutId && this.options.trailing) {
const timeoutDuration = this.options.wait - timeSinceLastExecution;
this.isPending = true;
this.timeoutId = setTimeout(() => {
if (this.lastArgs !== void 0) this.execute(...this.lastArgs);
}, timeoutDuration);
}
}
};
this.execute = (...args) => {
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastExecutionTime = Date.now();
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.flush = () => {
if (this.isPending && this.lastArgs) this.execute(...this.lastArgs);
};
this.cancel = () => {
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
};
if (this.options.leading === void 0 && this.options.trailing === void 0) {
this.options.leading = true;
this.options.trailing = true;
}
}
};
/**
* Creates a lightweight throttled function that limits how often the provided function can execute.
*
* This is an alternative to the throttle function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a throttler with no external dependencies, devtools integration, or reactive state.
*
* Throttling ensures a function executes at most once within a specified time window,
* regardless of how many times it is called. This is useful for rate-limiting
* expensive operations or UI updates.
*
* @example
* ```ts
* const throttledScroll = liteThrottle(() => {
* updateScrollIndicator();
* }, { wait: 100 });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', throttledScroll);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then throttles
* const throttledResize = liteThrottle(() => {
* recalculateLayout();
* }, { wait: 250, leading: true, trailing: false });
* ```
*/
function liteThrottle(fn, options) {
return new LiteThrottler(fn, options).maybeExecute;
}
//#endregion
exports.LiteThrottler = LiteThrottler;
exports.liteThrottle = liteThrottle;
//# sourceMappingURL=lite-throttler.cjs.map
{"version":3,"file":"lite-throttler.cjs","names":["fn: TFn","options: LiteThrottlerOptions<TFn>"],"sources":["../src/lite-throttler.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite throttled function\n */\nexport interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to true.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Time window in milliseconds during which the function can only be executed once.\n */\n wait: number\n}\n\n/**\n * A lightweight class that creates a throttled function.\n *\n * This is an alternative to the Throttler in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential throttling functionality.\n *\n * Throttling ensures a function is called at most once within a specified time window.\n * Unlike debouncing which waits for a pause in calls, throttling guarantees consistent\n * execution timing regardless of call frequency.\n *\n * Supports both leading and trailing edge execution:\n * - Leading: Execute immediately on first call (default: true)\n * - Trailing: Execute after wait period if called during throttle (default: true)\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 throttler = new LiteThrottler((scrollY: number) => {\n * updateScrollPosition(scrollY);\n * }, {\n * wait: 100,\n * onExecute: (args, throttler) => {\n * console.log('Updated scroll position:', args[0]);\n * }\n * });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', () => {\n * throttler.maybeExecute(window.scrollY);\n * });\n * ```\n */\nexport class LiteThrottler<TFn extends AnyFunction> {\n private timeoutId: NodeJS.Timeout | undefined\n private lastArgs: Parameters<TFn> | undefined\n private lastExecutionTime = 0\n private isPending = false\n\n constructor(\n public fn: TFn,\n public options: LiteThrottlerOptions<TFn>,\n ) {\n // Default both leading and trailing to true if neither is specified\n if (\n this.options.leading === undefined &&\n this.options.trailing === undefined\n ) {\n this.options.leading = true\n this.options.trailing = true\n }\n }\n\n /**\n * Attempts to execute the throttled function. The execution behavior depends on the throttler options:\n *\n * - If enough time has passed since the last execution (>= wait period):\n * - With leading=true: Executes immediately\n * - With leading=false: Waits for the next trailing execution\n *\n * - If within the wait period:\n * - With trailing=true: Schedules execution for end of wait period\n * - With trailing=false: Drops the execution\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n const timeSinceLastExecution = now - this.lastExecutionTime\n\n // Handle leading execution\n if (this.options.leading && timeSinceLastExecution >= this.options.wait) {\n this.execute(...args)\n } else {\n // Store the most recent arguments for potential trailing execution\n this.lastArgs = args\n\n // Set up trailing execution if not already scheduled\n if (!this.timeoutId && this.options.trailing) {\n const timeoutDuration = this.options.wait - timeSinceLastExecution\n this.isPending = true\n this.timeoutId = setTimeout(() => {\n if (this.lastArgs !== undefined) {\n this.execute(...this.lastArgs)\n }\n }, timeoutDuration)\n }\n }\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.lastExecutionTime = Date.now()\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\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.isPending && this.lastArgs) {\n this.execute(...this.lastArgs)\n }\n }\n\n /**\n * Cancels any pending trailing execution and clears internal state.\n * If a trailing execution is scheduled, this will prevent that execution from occurring.\n */\n cancel = (): void => {\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\n }\n\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = undefined\n }\n }\n}\n\n/**\n * Creates a lightweight throttled function that limits how often the provided function can execute.\n *\n * This is an alternative to the throttle 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 throttler with no external dependencies, devtools integration, or reactive state.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * @example\n * ```ts\n * const throttledScroll = liteThrottle(() => {\n * updateScrollIndicator();\n * }, { wait: 100 });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', throttledScroll);\n * ```\n *\n * @example\n * ```ts\n * // Leading edge execution - fires immediately then throttles\n * const throttledResize = liteThrottle(() => {\n * recalculateLayout();\n * }, { wait: 250, leading: true, trailing: false });\n * ```\n */\nexport function liteThrottle<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteThrottlerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const throttler = new LiteThrottler(fn, options)\n return throttler.maybeExecute\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,IAAa,gBAAb,MAAoD;CAMlD,YACE,AAAOA,IACP,AAAOC,SACP;EAFO;EACA;2BALmB;mBACR;uBA2BJ,GAAG,SAAgC;GAEjD,MAAM,yBADM,KAAK,KAAK,GACe,KAAK;AAG1C,OAAI,KAAK,QAAQ,WAAW,0BAA0B,KAAK,QAAQ,KACjE,MAAK,QAAQ,GAAG,KAAK;QAChB;AAEL,SAAK,WAAW;AAGhB,QAAI,CAAC,KAAK,aAAa,KAAK,QAAQ,UAAU;KAC5C,MAAM,kBAAkB,KAAK,QAAQ,OAAO;AAC5C,UAAK,YAAY;AACjB,UAAK,YAAY,iBAAiB;AAChC,UAAI,KAAK,aAAa,OACpB,MAAK,QAAQ,GAAG,KAAK,SAAS;QAE/B,gBAAgB;;;;kBAKN,GAAG,SAAgC;AACpD,QAAK,GAAG,GAAG,KAAK;AAChB,QAAK,QAAQ,YAAY,MAAM,KAAK;AACpC,QAAK,oBAAoB,KAAK,KAAK;AACnC,QAAK,cAAc;AACnB,QAAK,WAAW;AAChB,QAAK,YAAY;;qBAQC;AAClB,OAAI,KAAK,aAAa,KAAK,SACzB,MAAK,QAAQ,GAAG,KAAK,SAAS;;sBAQb;AACnB,QAAK,cAAc;AACnB,QAAK,WAAW;AAChB,QAAK,YAAY;;4BAGgB;AACjC,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;;AA7EnB,MACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,QAC1B;AACA,QAAK,QAAQ,UAAU;AACvB,QAAK,QAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0G9B,SAAgB,aACd,IACA,SACoC;AAEpC,QADkB,IAAI,cAAc,IAAI,QAAQ,CAC/B"}
import { AnyFunction } from "@tanstack/pacer/types";
//#region src/lite-throttler.d.ts
/**
* Options for configuring a lite throttled function
*/
interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* Defaults to true.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Time window in milliseconds during which the function can only be executed once.
*/
wait: number;
}
/**
* A lightweight class that creates a throttled function.
*
* This is an alternative to the Throttler in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential throttling functionality.
*
* Throttling ensures a function is called at most once within a specified time window.
* Unlike debouncing which waits for a pause in calls, throttling guarantees consistent
* execution timing regardless of call frequency.
*
* Supports both leading and trailing edge execution:
* - Leading: Execute immediately on first call (default: true)
* - Trailing: Execute after wait period if called during throttle (default: true)
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const throttler = new LiteThrottler((scrollY: number) => {
* updateScrollPosition(scrollY);
* }, {
* wait: 100,
* onExecute: (args, throttler) => {
* console.log('Updated scroll position:', args[0]);
* }
* });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', () => {
* throttler.maybeExecute(window.scrollY);
* });
* ```
*/
declare class LiteThrottler<TFn extends AnyFunction> {
fn: TFn;
options: LiteThrottlerOptions<TFn>;
private timeoutId;
private lastArgs;
private lastExecutionTime;
private isPending;
constructor(fn: TFn, options: LiteThrottlerOptions<TFn>);
/**
* Attempts to execute the throttled function. The execution behavior depends on the throttler options:
*
* - If enough time has passed since the last execution (>= wait period):
* - With leading=true: Executes immediately
* - With leading=false: Waits for the next trailing execution
*
* - If within the wait period:
* - With trailing=true: Schedules execution for end of wait period
* - With trailing=false: Drops the execution
*/
maybeExecute: (...args: Parameters<TFn>) => void;
private execute;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending trailing execution and clears internal state.
* If a trailing execution is scheduled, this will prevent that execution from occurring.
*/
cancel: () => void;
private clearTimeout;
}
/**
* Creates a lightweight throttled function that limits how often the provided function can execute.
*
* This is an alternative to the throttle function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a throttler with no external dependencies, devtools integration, or reactive state.
*
* Throttling ensures a function executes at most once within a specified time window,
* regardless of how many times it is called. This is useful for rate-limiting
* expensive operations or UI updates.
*
* @example
* ```ts
* const throttledScroll = liteThrottle(() => {
* updateScrollIndicator();
* }, { wait: 100 });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', throttledScroll);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then throttles
* const throttledResize = liteThrottle(() => {
* recalculateLayout();
* }, { wait: 250, leading: true, trailing: false });
* ```
*/
declare function liteThrottle<TFn extends AnyFunction>(fn: TFn, options: LiteThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
//#endregion
export { LiteThrottler, LiteThrottlerOptions, liteThrottle };
//# sourceMappingURL=lite-throttler.d.cts.map
import { AnyFunction } from "@tanstack/pacer/types";
//#region src/lite-throttler.d.ts
/**
* Options for configuring a lite throttled function
*/
interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* Defaults to true.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Time window in milliseconds during which the function can only be executed once.
*/
wait: number;
}
/**
* A lightweight class that creates a throttled function.
*
* This is an alternative to the Throttler in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential throttling functionality.
*
* Throttling ensures a function is called at most once within a specified time window.
* Unlike debouncing which waits for a pause in calls, throttling guarantees consistent
* execution timing regardless of call frequency.
*
* Supports both leading and trailing edge execution:
* - Leading: Execute immediately on first call (default: true)
* - Trailing: Execute after wait period if called during throttle (default: true)
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const throttler = new LiteThrottler((scrollY: number) => {
* updateScrollPosition(scrollY);
* }, {
* wait: 100,
* onExecute: (args, throttler) => {
* console.log('Updated scroll position:', args[0]);
* }
* });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', () => {
* throttler.maybeExecute(window.scrollY);
* });
* ```
*/
declare class LiteThrottler<TFn extends AnyFunction> {
fn: TFn;
options: LiteThrottlerOptions<TFn>;
private timeoutId;
private lastArgs;
private lastExecutionTime;
private isPending;
constructor(fn: TFn, options: LiteThrottlerOptions<TFn>);
/**
* Attempts to execute the throttled function. The execution behavior depends on the throttler options:
*
* - If enough time has passed since the last execution (>= wait period):
* - With leading=true: Executes immediately
* - With leading=false: Waits for the next trailing execution
*
* - If within the wait period:
* - With trailing=true: Schedules execution for end of wait period
* - With trailing=false: Drops the execution
*/
maybeExecute: (...args: Parameters<TFn>) => void;
private execute;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending trailing execution and clears internal state.
* If a trailing execution is scheduled, this will prevent that execution from occurring.
*/
cancel: () => void;
private clearTimeout;
}
/**
* Creates a lightweight throttled function that limits how often the provided function can execute.
*
* This is an alternative to the throttle function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a throttler with no external dependencies, devtools integration, or reactive state.
*
* Throttling ensures a function executes at most once within a specified time window,
* regardless of how many times it is called. This is useful for rate-limiting
* expensive operations or UI updates.
*
* @example
* ```ts
* const throttledScroll = liteThrottle(() => {
* updateScrollIndicator();
* }, { wait: 100 });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', throttledScroll);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then throttles
* const throttledResize = liteThrottle(() => {
* recalculateLayout();
* }, { wait: 250, leading: true, trailing: false });
* ```
*/
declare function liteThrottle<TFn extends AnyFunction>(fn: TFn, options: LiteThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
//#endregion
export { LiteThrottler, LiteThrottlerOptions, liteThrottle };
//# sourceMappingURL=lite-throttler.d.ts.map
//#region src/lite-throttler.ts
/**
* A lightweight class that creates a throttled function.
*
* This is an alternative to the Throttler in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential throttling functionality.
*
* Throttling ensures a function is called at most once within a specified time window.
* Unlike debouncing which waits for a pause in calls, throttling guarantees consistent
* execution timing regardless of call frequency.
*
* Supports both leading and trailing edge execution:
* - Leading: Execute immediately on first call (default: true)
* - Trailing: Execute after wait period if called during throttle (default: true)
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const throttler = new LiteThrottler((scrollY: number) => {
* updateScrollPosition(scrollY);
* }, {
* wait: 100,
* onExecute: (args, throttler) => {
* console.log('Updated scroll position:', args[0]);
* }
* });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', () => {
* throttler.maybeExecute(window.scrollY);
* });
* ```
*/
var LiteThrottler = class {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.lastExecutionTime = 0;
this.isPending = false;
this.maybeExecute = (...args) => {
const timeSinceLastExecution = Date.now() - this.lastExecutionTime;
if (this.options.leading && timeSinceLastExecution >= this.options.wait) this.execute(...args);
else {
this.lastArgs = args;
if (!this.timeoutId && this.options.trailing) {
const timeoutDuration = this.options.wait - timeSinceLastExecution;
this.isPending = true;
this.timeoutId = setTimeout(() => {
if (this.lastArgs !== void 0) this.execute(...this.lastArgs);
}, timeoutDuration);
}
}
};
this.execute = (...args) => {
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastExecutionTime = Date.now();
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.flush = () => {
if (this.isPending && this.lastArgs) this.execute(...this.lastArgs);
};
this.cancel = () => {
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
};
if (this.options.leading === void 0 && this.options.trailing === void 0) {
this.options.leading = true;
this.options.trailing = true;
}
}
};
/**
* Creates a lightweight throttled function that limits how often the provided function can execute.
*
* This is an alternative to the throttle function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a throttler with no external dependencies, devtools integration, or reactive state.
*
* Throttling ensures a function executes at most once within a specified time window,
* regardless of how many times it is called. This is useful for rate-limiting
* expensive operations or UI updates.
*
* @example
* ```ts
* const throttledScroll = liteThrottle(() => {
* updateScrollIndicator();
* }, { wait: 100 });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', throttledScroll);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then throttles
* const throttledResize = liteThrottle(() => {
* recalculateLayout();
* }, { wait: 250, leading: true, trailing: false });
* ```
*/
function liteThrottle(fn, options) {
return new LiteThrottler(fn, options).maybeExecute;
}
//#endregion
export { LiteThrottler, liteThrottle };
//# sourceMappingURL=lite-throttler.js.map
{"version":3,"file":"lite-throttler.js","names":["fn: TFn","options: LiteThrottlerOptions<TFn>"],"sources":["../src/lite-throttler.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite throttled function\n */\nexport interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to true.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Time window in milliseconds during which the function can only be executed once.\n */\n wait: number\n}\n\n/**\n * A lightweight class that creates a throttled function.\n *\n * This is an alternative to the Throttler in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential throttling functionality.\n *\n * Throttling ensures a function is called at most once within a specified time window.\n * Unlike debouncing which waits for a pause in calls, throttling guarantees consistent\n * execution timing regardless of call frequency.\n *\n * Supports both leading and trailing edge execution:\n * - Leading: Execute immediately on first call (default: true)\n * - Trailing: Execute after wait period if called during throttle (default: true)\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 throttler = new LiteThrottler((scrollY: number) => {\n * updateScrollPosition(scrollY);\n * }, {\n * wait: 100,\n * onExecute: (args, throttler) => {\n * console.log('Updated scroll position:', args[0]);\n * }\n * });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', () => {\n * throttler.maybeExecute(window.scrollY);\n * });\n * ```\n */\nexport class LiteThrottler<TFn extends AnyFunction> {\n private timeoutId: NodeJS.Timeout | undefined\n private lastArgs: Parameters<TFn> | undefined\n private lastExecutionTime = 0\n private isPending = false\n\n constructor(\n public fn: TFn,\n public options: LiteThrottlerOptions<TFn>,\n ) {\n // Default both leading and trailing to true if neither is specified\n if (\n this.options.leading === undefined &&\n this.options.trailing === undefined\n ) {\n this.options.leading = true\n this.options.trailing = true\n }\n }\n\n /**\n * Attempts to execute the throttled function. The execution behavior depends on the throttler options:\n *\n * - If enough time has passed since the last execution (>= wait period):\n * - With leading=true: Executes immediately\n * - With leading=false: Waits for the next trailing execution\n *\n * - If within the wait period:\n * - With trailing=true: Schedules execution for end of wait period\n * - With trailing=false: Drops the execution\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n const timeSinceLastExecution = now - this.lastExecutionTime\n\n // Handle leading execution\n if (this.options.leading && timeSinceLastExecution >= this.options.wait) {\n this.execute(...args)\n } else {\n // Store the most recent arguments for potential trailing execution\n this.lastArgs = args\n\n // Set up trailing execution if not already scheduled\n if (!this.timeoutId && this.options.trailing) {\n const timeoutDuration = this.options.wait - timeSinceLastExecution\n this.isPending = true\n this.timeoutId = setTimeout(() => {\n if (this.lastArgs !== undefined) {\n this.execute(...this.lastArgs)\n }\n }, timeoutDuration)\n }\n }\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.lastExecutionTime = Date.now()\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\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.isPending && this.lastArgs) {\n this.execute(...this.lastArgs)\n }\n }\n\n /**\n * Cancels any pending trailing execution and clears internal state.\n * If a trailing execution is scheduled, this will prevent that execution from occurring.\n */\n cancel = (): void => {\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\n }\n\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = undefined\n }\n }\n}\n\n/**\n * Creates a lightweight throttled function that limits how often the provided function can execute.\n *\n * This is an alternative to the throttle 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 throttler with no external dependencies, devtools integration, or reactive state.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * @example\n * ```ts\n * const throttledScroll = liteThrottle(() => {\n * updateScrollIndicator();\n * }, { wait: 100 });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', throttledScroll);\n * ```\n *\n * @example\n * ```ts\n * // Leading edge execution - fires immediately then throttles\n * const throttledResize = liteThrottle(() => {\n * recalculateLayout();\n * }, { wait: 250, leading: true, trailing: false });\n * ```\n */\nexport function liteThrottle<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteThrottlerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const throttler = new LiteThrottler(fn, options)\n return throttler.maybeExecute\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,IAAa,gBAAb,MAAoD;CAMlD,YACE,AAAOA,IACP,AAAOC,SACP;EAFO;EACA;2BALmB;mBACR;uBA2BJ,GAAG,SAAgC;GAEjD,MAAM,yBADM,KAAK,KAAK,GACe,KAAK;AAG1C,OAAI,KAAK,QAAQ,WAAW,0BAA0B,KAAK,QAAQ,KACjE,MAAK,QAAQ,GAAG,KAAK;QAChB;AAEL,SAAK,WAAW;AAGhB,QAAI,CAAC,KAAK,aAAa,KAAK,QAAQ,UAAU;KAC5C,MAAM,kBAAkB,KAAK,QAAQ,OAAO;AAC5C,UAAK,YAAY;AACjB,UAAK,YAAY,iBAAiB;AAChC,UAAI,KAAK,aAAa,OACpB,MAAK,QAAQ,GAAG,KAAK,SAAS;QAE/B,gBAAgB;;;;kBAKN,GAAG,SAAgC;AACpD,QAAK,GAAG,GAAG,KAAK;AAChB,QAAK,QAAQ,YAAY,MAAM,KAAK;AACpC,QAAK,oBAAoB,KAAK,KAAK;AACnC,QAAK,cAAc;AACnB,QAAK,WAAW;AAChB,QAAK,YAAY;;qBAQC;AAClB,OAAI,KAAK,aAAa,KAAK,SACzB,MAAK,QAAQ,GAAG,KAAK,SAAS;;sBAQb;AACnB,QAAK,cAAc;AACnB,QAAK,WAAW;AAChB,QAAK,YAAY;;4BAGgB;AACjC,OAAI,KAAK,WAAW;AAClB,iBAAa,KAAK,UAAU;AAC5B,SAAK,YAAY;;;AA7EnB,MACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,QAC1B;AACA,QAAK,QAAQ,UAAU;AACvB,QAAK,QAAQ,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0G9B,SAAgB,aACd,IACA,SACoC;AAEpC,QADkB,IAAI,cAAc,IAAI,QAAQ,CAC/B"}
+22
-59
{
"name": "@tanstack/pacer-lite",
"version": "0.1.0",
"version": "0.1.1",
"description": "Lightweight utilities for debouncing, throttling, and more - designed for npm packages.",

@@ -26,66 +26,30 @@ "author": "Tanner Linsley",

"type": "module",
"types": "dist/esm/index.d.ts",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.js",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.cts",
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
"require": "./dist/index.cjs",
"import": "./dist/index.js"
},
"./lite-batcher": {
"require": "./dist/lite-batcher.cjs",
"import": "./dist/lite-batcher.js"
},
"./lite-debouncer": {
"import": {
"types": "./dist/esm/lite-debouncer.d.ts",
"default": "./dist/esm/lite-debouncer.js"
},
"require": {
"types": "./dist/cjs/lite-debouncer.d.cts",
"default": "./dist/cjs/lite-debouncer.cjs"
}
"require": "./dist/lite-debouncer.cjs",
"import": "./dist/lite-debouncer.js"
},
"./lite-throttler": {
"import": {
"types": "./dist/esm/lite-throttler.d.ts",
"default": "./dist/esm/lite-throttler.js"
},
"require": {
"types": "./dist/cjs/lite-throttler.d.cts",
"default": "./dist/cjs/lite-throttler.cjs"
}
"./lite-queuer": {
"require": "./dist/lite-queuer.cjs",
"import": "./dist/lite-queuer.js"
},
"./lite-rate-limiter": {
"import": {
"types": "./dist/esm/lite-rate-limiter.d.ts",
"default": "./dist/esm/lite-rate-limiter.js"
},
"require": {
"types": "./dist/cjs/lite-rate-limiter.d.cts",
"default": "./dist/cjs/lite-rate-limiter.cjs"
}
"require": "./dist/lite-rate-limiter.cjs",
"import": "./dist/lite-rate-limiter.js"
},
"./lite-queuer": {
"import": {
"types": "./dist/esm/lite-queuer.d.ts",
"default": "./dist/esm/lite-queuer.js"
},
"require": {
"types": "./dist/cjs/lite-queuer.d.cts",
"default": "./dist/cjs/lite-queuer.cjs"
}
"./lite-throttler": {
"require": "./dist/lite-throttler.cjs",
"import": "./dist/lite-throttler.js"
},
"./lite-batcher": {
"import": {
"types": "./dist/esm/lite-batcher.d.ts",
"default": "./dist/esm/lite-batcher.js"
},
"require": {
"types": "./dist/cjs/lite-batcher.d.cts",
"default": "./dist/cjs/lite-batcher.cjs"
}
},
"./package.json": "./package.json"

@@ -102,3 +66,3 @@ },

"devDependencies": {
"@tanstack/pacer": "0.16.3"
"@tanstack/pacer": "0.16.4"
},

@@ -112,5 +76,4 @@ "scripts": {

"test:types": "tsc",
"test:build": "publint --strict",
"build": "vite build"
"build": "tsdown"
}
}
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const liteDebouncer = require("./lite-debouncer.cjs");
const liteThrottler = require("./lite-throttler.cjs");
const liteRateLimiter = require("./lite-rate-limiter.cjs");
const liteQueuer = require("./lite-queuer.cjs");
const liteBatcher = require("./lite-batcher.cjs");
exports.LiteDebouncer = liteDebouncer.LiteDebouncer;
exports.liteDebounce = liteDebouncer.liteDebounce;
exports.LiteThrottler = liteThrottler.LiteThrottler;
exports.liteThrottle = liteThrottler.liteThrottle;
exports.LiteRateLimiter = liteRateLimiter.LiteRateLimiter;
exports.liteRateLimit = liteRateLimiter.liteRateLimit;
exports.LiteQueuer = liteQueuer.LiteQueuer;
exports.liteQueue = liteQueuer.liteQueue;
exports.LiteBatcher = liteBatcher.LiteBatcher;
exports.liteBatch = liteBatcher.liteBatch;
//# sourceMappingURL=index.cjs.map
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
export * from './lite-debouncer.cjs';
export * from './lite-throttler.cjs';
export * from './lite-rate-limiter.cjs';
export * from './lite-queuer.cjs';
export * from './lite-batcher.cjs';
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
class LiteBatcher {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this._isPending = false;
this.addItem = (item) => {
this.items.push(item);
this._isPending = this.options.wait !== Infinity;
this.options.onItemsChange?.(this);
const shouldProcess = this.items.length >= this.options.maxSize || this.options.getShouldExecute(this.items, this);
if (shouldProcess) {
this.execute();
} else if (this.options.wait !== Infinity) {
this.clearTimeout();
this.timeoutId = setTimeout(() => this.execute(), this.getWait());
}
};
this.execute = () => {
if (this.items.length === 0) {
return;
}
const batch = this.peekAllItems();
this.clear();
this.fn(batch);
this.options.onExecute?.(batch, this);
};
this.flush = () => {
this.clearTimeout();
this.execute();
};
this.peekAllItems = () => {
return [...this.items];
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.clear = () => {
const hadItems = this.items.length > 0;
this.items = [];
this._isPending = false;
if (hadItems) {
this.options.onItemsChange?.(this);
}
};
this.cancel = () => {
this.clearTimeout();
this._isPending = false;
};
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? Infinity;
this.options.getShouldExecute = this.options.getShouldExecute ?? (() => false);
}
/**
* Number of items currently in the batch
*/
get size() {
return this.items.length;
}
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending() {
return this._isPending;
}
getWait() {
if (typeof this.options.wait === "function") {
return this.options.wait(this);
}
return this.options.wait;
}
}
function liteBatch(fn, options = {}) {
const batcher = new LiteBatcher(fn, options);
return batcher.addItem;
}
exports.LiteBatcher = LiteBatcher;
exports.liteBatch = liteBatch;
//# sourceMappingURL=lite-batcher.cjs.map
{"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;;;"}
/**
* Options for configuring a lite batcher instance
*/
export interface LiteBatcherOptions<TValue> {
/**
* Custom function to determine if a batch should be processed
* Return true to process the batch immediately
*/
getShouldExecute?: (items: Array<TValue>, batcher: LiteBatcher<TValue>) => boolean;
/**
* Maximum number of items in a batch
* @default Infinity
*/
maxSize?: number;
/**
* Callback fired after a batch is processed
*/
onExecute?: (batch: Array<TValue>, batcher: LiteBatcher<TValue>) => void;
/**
* Callback fired after items are added to the batcher
*/
onItemsChange?: (batcher: LiteBatcher<TValue>) => void;
/**
* Whether the batcher should start processing immediately
* @default true
*/
started?: boolean;
/**
* Maximum time in milliseconds to wait before processing a batch.
* If the wait duration has elapsed, the batch will be processed.
* If not provided, the batch will not be triggered by a timeout.
* @default Infinity
*/
wait?: number | ((batcher: LiteBatcher<TValue>) => number);
}
/**
* A lightweight class that collects items and processes them in batches.
*
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential batching functionality.
*
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
* This synchronous version is lighter weight and often all you need.
*
* The Batcher provides a flexible way to implement batching with configurable:
* - Maximum batch size (number of items per batch)
* - Time-based batching (process after X milliseconds)
* - Custom batch processing logic via getShouldExecute
*
* Features included:
* - Core batching functionality (addItem, flush, clear, cancel)
* - Size-based batching (maxSize)
* - Time-based batching (wait timeout)
* - Custom condition batching (getShouldExecute)
* - Manual processing controls
* - Public mutable options
* - Callback support for monitoring batch execution and state changes
*
* Features NOT included (compared to core Batcher):
* - No TanStack Store state management
* - No devtools integration
* - No complex state tracking (execution counts, etc.)
* - No reactive state management
*
* @example
* ```ts
* // Basic batching
* const batcher = new LiteBatcher<number>(
* (items) => console.log('Processing batch:', items),
* {
* maxSize: 5,
* wait: 2000,
* onExecute: (batch, batcher) => {
* console.log('Batch executed with', batch.length, 'items');
* },
* onItemsChange: (batcher) => {
* console.log('Batch size changed to:', batcher.size);
* }
* }
* );
*
* batcher.addItem(1);
* batcher.addItem(2);
* // After 2 seconds or when 5 items are added, whichever comes first,
* // the batch will be processed
* ```
*
* @example
* ```ts
* // Custom condition batching
* const batcher = new LiteBatcher<Task>(
* (items) => processTasks(items),
* {
* getShouldExecute: (items) => items.some(task => task.urgent),
* maxSize: 10,
* }
* );
*
* batcher.addItem({ name: 'normal', urgent: false });
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
* ```
*/
export declare class LiteBatcher<TValue> {
fn: (items: Array<TValue>) => void;
options: LiteBatcherOptions<TValue>;
private items;
private timeoutId;
private _isPending;
constructor(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>);
/**
* Number of items currently in the batch
*/
get size(): number;
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty(): boolean;
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending(): boolean;
private getWait;
/**
* Adds an item to the batcher
* If the batch size is reached, timeout occurs, or getShouldExecute returns true, the batch will be processed
*/
addItem: (item: TValue) => void;
/**
* Processes the current batch of items.
* This method will automatically be triggered if the batcher is running and any of these conditions are met:
* - The number of items reaches maxSize
* - The wait duration has elapsed
* - The getShouldExecute function returns true upon adding an item
*
* You can also call this method manually to process the current batch at any time.
*/
private execute;
/**
* Processes the current batch of items immediately
*/
flush: () => void;
/**
* Returns a copy of all items in the batcher
*/
peekAllItems: () => Array<TValue>;
private clearTimeout;
/**
* Removes all items from the batcher
*/
clear: () => void;
/**
* Cancels any pending execution that was scheduled.
* Does NOT clear out the items.
*/
cancel: () => void;
}
/**
* Creates a batcher that processes items in batches.
*
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const batchItems = liteBatch<number>(
* (items) => console.log('Processing:', items),
* {
* maxSize: 3,
* }
* );
*
* batchItems(1);
* batchItems(2);
* batchItems(3); // Triggers batch processing
* ```
*/
export declare function liteBatch<TValue>(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>): (item: TValue) => void;
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
class LiteDebouncer {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.canLeadingExecute = true;
this.maybeExecute = (...args) => {
let didLeadingExecute = false;
if (this.options.leading && this.canLeadingExecute) {
this.canLeadingExecute = false;
didLeadingExecute = true;
this.fn(...args);
this.options.onExecute?.(args, this);
}
this.lastArgs = args;
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
this.timeoutId = setTimeout(() => {
this.canLeadingExecute = true;
if (this.options.trailing && !didLeadingExecute && this.lastArgs) {
this.fn(...this.lastArgs);
this.options.onExecute?.(this.lastArgs, this);
}
this.lastArgs = void 0;
}, this.options.wait);
};
this.flush = () => {
if (this.timeoutId && this.lastArgs) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
const args = this.lastArgs;
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastArgs = void 0;
this.canLeadingExecute = true;
}
};
this.cancel = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
this.lastArgs = void 0;
this.canLeadingExecute = true;
};
if (this.options.leading === void 0 && this.options.trailing === void 0) {
this.options.trailing = true;
}
}
}
function liteDebounce(fn, options) {
const debouncer = new LiteDebouncer(fn, options);
return debouncer.maybeExecute;
}
exports.LiteDebouncer = LiteDebouncer;
exports.liteDebounce = liteDebounce;
//# sourceMappingURL=lite-debouncer.cjs.map
{"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;;;"}
import { AnyFunction } from '@tanstack/pacer/types';
/**
* Options for configuring a lite debounced function
*/
export interface LiteDebouncerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* The first call will execute immediately and the rest will wait the delay.
* Defaults to false.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, debouncer: LiteDebouncer<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Delay in milliseconds before executing the function.
*/
wait: number;
}
/**
* A lightweight class that creates a debounced function.
*
* This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential debouncing functionality.
*
* Debouncing ensures that a function is only executed after a certain amount of time has passed
* since its last invocation. This is useful for handling frequent events like window resizing,
* scroll events, or input changes where you want to limit the rate of execution.
*
* The debounced function can be configured to execute either at the start of the delay period
* (leading edge) or at the end (trailing edge, default). Each new call during the wait period
* will reset the timer.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const debouncer = new LiteDebouncer((value: string) => {
* saveToDatabase(value);
* }, {
* wait: 500,
* onExecute: (args, debouncer) => {
* console.log('Saved value:', args[0]);
* }
* });
*
* // Will only save after 500ms of no new input
* inputElement.addEventListener('input', () => {
* debouncer.maybeExecute(inputElement.value);
* });
* ```
*/
export declare class LiteDebouncer<TFn extends AnyFunction> {
fn: TFn;
options: LiteDebouncerOptions<TFn>;
private timeoutId;
private lastArgs;
private canLeadingExecute;
constructor(fn: TFn, options: LiteDebouncerOptions<TFn>);
/**
* Attempts to execute the debounced function.
* If leading is true and this is the first call, executes immediately.
* Otherwise, queues the execution for after the wait time.
* Each new call resets the timer.
*/
maybeExecute: (...args: Parameters<TFn>) => void;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending execution.
* Clears the timeout and resets the internal state.
*/
cancel: () => void;
}
/**
* Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.
* Multiple calls during the wait period will cancel previous pending invocations and reset the timer.
*
* This is an alternative to the debounce function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a debouncer with no external dependencies, devtools integration, or reactive state.
*
* If leading option is true, the function will execute immediately on the first call, then wait the delay
* before allowing another execution.
*
* @example
* ```ts
* const debouncedSave = liteDebounce(() => {
* saveChanges();
* }, { wait: 1000 });
*
* // Called repeatedly but executes at most once per second
* inputElement.addEventListener('input', debouncedSave);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then waits
* const debouncedSearch = liteDebounce((query: string) => {
* performSearch(query);
* }, { wait: 300, leading: true });
* ```
*/
export declare function liteDebounce<TFn extends AnyFunction>(fn: TFn, options: LiteDebouncerOptions<TFn>): (...args: Parameters<TFn>) => void;
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
class LiteQueuer {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this.isRunning = true;
this.pendingTick = false;
this.addItem = (item, position = this.options.addItemsTo, startProcessing = true) => {
if (this.items.length >= this.options.maxSize) {
return false;
}
if (this.options.getPriority) {
const priority = this.options.getPriority(item);
if (priority !== void 0) {
const insertIndex = this.items.findIndex((existing) => {
const existingPriority = this.options.getPriority(existing);
const effectivePriority = existingPriority ?? -Infinity;
return effectivePriority < priority;
});
if (insertIndex === -1) {
this.items.push(item);
} else {
this.items.splice(insertIndex, 0, item);
}
} else {
this.insertAtPosition(item, position);
}
} else {
this.insertAtPosition(item, position);
}
if (startProcessing && this.isRunning && !this.pendingTick) {
this.tick();
}
return true;
};
this.insertAtPosition = (item, position) => {
if (position === "front") {
this.items.unshift(item);
} else {
this.items.push(item);
}
};
this.getNextItem = (position = this.options.getItemsFrom) => {
if (this.items.length === 0) {
return void 0;
}
let item;
if (this.options.getPriority || position === "front") {
item = this.items.shift();
} else {
item = this.items.pop();
}
return item;
};
this.execute = (position) => {
const item = this.getNextItem(position);
if (item !== void 0) {
this.fn(item);
}
return item;
};
this.tick = () => {
if (!this.isRunning) {
this.pendingTick = false;
return;
}
this.pendingTick = true;
while (this.items.length > 0) {
const item = this.execute(this.options.getItemsFrom);
if (item === void 0) {
break;
}
const wait = this.options.wait;
if (wait > 0) {
this.timeoutId = setTimeout(() => this.tick(), wait);
return;
}
}
this.pendingTick = false;
};
this.start = () => {
this.isRunning = true;
if (!this.pendingTick && this.items.length > 0) {
this.tick();
}
};
this.stop = () => {
this.clearTimeout();
this.isRunning = false;
this.pendingTick = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.peekNextItem = (position = "front") => {
if (this.items.length === 0) {
return void 0;
}
if (this.options.getPriority || position === "front") {
return this.items[0];
} else {
return this.items[this.items.length - 1];
}
};
this.peekAllItems = () => {
return [...this.items];
};
this.flush = (numberOfItems = this.items.length, position) => {
this.clearTimeout();
for (let i = 0; i < numberOfItems && this.items.length > 0; i++) {
this.execute(position);
}
if (this.isRunning && this.items.length > 0 && !this.pendingTick) {
this.tick();
}
};
this.flushAsBatch = (batchFunction) => {
const items = this.peekAllItems();
this.clear();
batchFunction(items);
};
this.clear = () => {
this.items = [];
};
this.options.addItemsTo = this.options.addItemsTo ?? "back";
this.options.getItemsFrom = this.options.getItemsFrom ?? "front";
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? 0;
this.isRunning = this.options.started;
if (this.options.initialItems) {
for (const item of this.options.initialItems) {
this.addItem(item, this.options.addItemsTo, false);
}
}
if (this.isRunning && this.items.length > 0) {
this.tick();
}
}
/**
* Number of items currently in the queue
*/
get size() {
return this.items.length;
}
/**
* Whether the queue is empty
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning() {
return this.isRunning;
}
}
function liteQueue(fn, options = {}) {
const queuer = new LiteQueuer(fn, options);
return (item) => queuer.addItem(item);
}
exports.LiteQueuer = LiteQueuer;
exports.liteQueue = liteQueue;
//# sourceMappingURL=lite-queuer.cjs.map
{"version":3,"file":"lite-queuer.cjs","sources":["../../src/lite-queuer.ts"],"sourcesContent":["/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO for getNextItem)\n * - 'back': Operate on the back of the queue (LIFO for getNextItem)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * Options for configuring a lite queuer instance\n */\nexport interface LiteQueuerOptions<TValue> {\n /**\n * Default position to add items to the queue\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queue\n * Higher priority items will be processed first\n * Return undefined for items that should use positional ordering\n */\n getPriority?: (item: TValue) => number | undefined\n /**\n * Initial items to populate the queue with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queue\n */\n maxSize?: number\n /**\n * Whether the queuer should start processing items immediately\n * @default true\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items\n * @default 0\n */\n wait?: number\n}\n\n/**\n * A lightweight class that creates a queue for processing items.\n *\n * This is an alternative to the Queuer in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * no callbacks, and provides only essential queueing functionality.\n *\n * The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based\n * processing of items. Items can be processed automatically with configurable wait times\n * between executions, or processed manually using the execute methods.\n *\n * Features included:\n * - Automatic or manual processing of items\n * - FIFO, LIFO, and priority-based ordering\n * - Queue size limits with item rejection\n * - Configurable wait times between processing\n * - Batch processing capabilities\n * - Start/stop processing control\n * - Callback support for monitoring execution, rejection, and state change events\n *\n * Features NOT included (compared to core Queuer):\n * - No TanStack Store state management\n * - No devtools integration\n * - No item expiration functionality (no onExpire callback)\n * - No dynamic options updates (setOptions)\n * - No detailed state tracking (execution counts, etc.)\n *\n * Queue behavior:\n * - Default: FIFO (add to back, process from front)\n * - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'\n * - Priority: Provide getPriority function; higher values processed first\n *\n * @example\n * ```ts\n * // Basic FIFO queue\n * const queue = new LiteQueuer((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 100 });\n *\n * queue.addItem('task1');\n * queue.addItem('task2');\n * // Processes: task1, then task2 after 100ms delay\n * ```\n *\n * @example\n * ```ts\n * // Priority queue\n * const priorityQueue = new LiteQueuer((item: Task) => {\n * processTask(item);\n * }, {\n * getPriority: task => task.priority,\n * wait: 500\n * });\n *\n * priorityQueue.addItem({ name: 'low', priority: 1 });\n * priorityQueue.addItem({ name: 'high', priority: 10 });\n * // Processes high priority task first\n * ```\n */\nexport class LiteQueuer<TValue> {\n private items: Array<TValue> = []\n private timeoutId: NodeJS.Timeout | null = null\n private isRunning = true\n private pendingTick = false\n\n constructor(\n public fn: (item: TValue) => void,\n public options: LiteQueuerOptions<TValue> = {},\n ) {\n // Set defaults\n this.options.addItemsTo = this.options.addItemsTo ?? 'back'\n this.options.getItemsFrom = this.options.getItemsFrom ?? 'front'\n this.options.maxSize = this.options.maxSize ?? Infinity\n this.options.started = this.options.started ?? true\n this.options.wait = this.options.wait ?? 0\n\n this.isRunning = this.options.started\n\n // Add initial items if provided\n if (this.options.initialItems) {\n for (const item of this.options.initialItems) {\n this.addItem(item, this.options.addItemsTo, false)\n }\n }\n\n // Start processing if enabled and has items\n if (this.isRunning && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Number of items currently in the queue\n */\n get size(): number {\n return this.items.length\n }\n\n /**\n * Whether the queue is empty\n */\n get isEmpty(): boolean {\n return this.items.length === 0\n }\n\n /**\n * Whether the queue is currently running (auto-processing items)\n */\n get isQueueRunning(): boolean {\n return this.isRunning\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected.\n * Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * @example\n * ```ts\n * queue.addItem('task1'); // Add to default position (back)\n * queue.addItem('task2', 'front'); // Add to front\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo!,\n startProcessing: boolean = true,\n ): boolean => {\n // Check size limit\n if (this.items.length >= this.options.maxSize!) {\n return false\n }\n\n // Handle priority insertion\n if (this.options.getPriority) {\n const priority = this.options.getPriority(item)\n if (priority !== undefined) {\n // Find insertion point for priority\n const insertIndex = this.items.findIndex((existing) => {\n const existingPriority = this.options.getPriority!(existing)\n // Treat undefined priority as negative infinity for comparison\n const effectivePriority = existingPriority ?? -Infinity\n return effectivePriority < priority\n })\n\n if (insertIndex === -1) {\n this.items.push(item)\n } else {\n this.items.splice(insertIndex, 0, item)\n }\n } else {\n // No priority, use position\n this.insertAtPosition(item, position)\n }\n } else {\n // No priority function, use position\n this.insertAtPosition(item, position)\n }\n\n // Start processing if running and not already processing\n if (startProcessing && this.isRunning && !this.pendingTick) {\n this.tick()\n }\n\n return true\n }\n\n private insertAtPosition = (item: TValue, position: QueuePosition): void => {\n if (position === 'front') {\n this.items.unshift(item)\n } else {\n this.items.push(item)\n }\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * @example\n * ```ts\n * const nextItem = queue.getNextItem(); // Get from default position (front)\n * const lastItem = queue.getNextItem('back'); // Get from back (LIFO)\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom!,\n ): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n if (this.options.getPriority || position === 'front') {\n item = this.items.shift()\n } else {\n item = this.items.pop()\n }\n\n return item\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * @example\n * ```ts\n * queue.execute(); // Execute from default position\n * queue.execute('back'); // Execute from back (LIFO)\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n }\n return item\n }\n\n /**\n * Internal method that processes items in the queue with wait intervals\n */\n private tick = (): void => {\n if (!this.isRunning) {\n this.pendingTick = false\n return\n }\n\n this.pendingTick = true\n\n // Process items while queue is not empty\n while (this.items.length > 0) {\n const item = this.execute(this.options.getItemsFrom)\n if (item === undefined) {\n break\n }\n\n const wait = this.options.wait!\n if (wait > 0) {\n // Schedule next processing after wait time\n this.timeoutId = setTimeout(() => this.tick(), wait)\n return\n }\n\n // No wait time, continue processing immediately\n }\n\n this.pendingTick = false\n }\n\n /**\n * Starts processing items in the queue. If already running, does nothing.\n */\n start = (): void => {\n this.isRunning = true\n if (!this.pendingTick && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = (): void => {\n this.clearTimeout()\n this.isRunning = false\n this.pendingTick = false\n }\n\n /**\n * Clears any pending timeout\n */\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = null\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * @example\n * ```ts\n * const next = queue.peekNextItem(); // Peek at front\n * const last = queue.peekNextItem('back'); // Peek at back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n if (this.options.getPriority || position === 'front') {\n return this.items[0]\n } else {\n return this.items[this.items.length - 1]\n }\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.items]\n }\n\n /**\n * Processes a specified number of items immediately with no wait time.\n * If no numberOfItems is provided, all items will be processed.\n *\n * @example\n * ```ts\n * queue.flush(); // Process all items immediately\n * queue.flush(3); // Process next 3 items immediately\n * ```\n */\n flush = (\n numberOfItems: number = this.items.length,\n position?: QueuePosition,\n ): void => {\n this.clearTimeout() // Clear any pending timeout\n for (let i = 0; i < numberOfItems && this.items.length > 0; i++) {\n this.execute(position)\n }\n // Restart normal processing if still running and has items\n if (this.isRunning && this.items.length > 0 && !this.pendingTick) {\n this.tick()\n }\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function.\n * The queue is cleared after processing.\n *\n * @example\n * ```ts\n * queue.flushAsBatch((items) => {\n * console.log('Processing batch:', items);\n * // Process all items together\n * });\n * ```\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.peekAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Removes all items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.items = []\n }\n}\n\n/**\n * Creates a lightweight queue that processes items using the provided function.\n *\n * This is an alternative to the queue 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 queuer with no external dependencies, devtools integration, or reactive state.\n *\n * @example\n * ```ts\n * const processItem = liteQueue((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 1000 });\n *\n * processItem('task1');\n * processItem('task2');\n * // Processes each item with 1 second delay between them\n * ```\n */\nexport function liteQueue<TValue>(\n fn: (item: TValue) => void,\n options: LiteQueuerOptions<TValue> = {},\n): (item: TValue) => boolean {\n const queuer = new LiteQueuer(fn, options)\n return (item: TValue) => queuer.addItem(item)\n}\n"],"names":[],"mappings":";;AA4GO,MAAM,WAAmB;AAAA,EAM9B,YACS,IACA,UAAqC,IAC5C;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AAPT,SAAQ,QAAuB,CAAA;AAC/B,SAAQ,YAAmC;AAC3C,SAAQ,YAAY;AACpB,SAAQ,cAAc;AA6DtB,SAAA,UAAU,CACR,MACA,WAA0B,KAAK,QAAQ,YACvC,kBAA2B,SACf;AAEZ,UAAI,KAAK,MAAM,UAAU,KAAK,QAAQ,SAAU;AAC9C,eAAO;AAAA,MACT;AAGA,UAAI,KAAK,QAAQ,aAAa;AAC5B,cAAM,WAAW,KAAK,QAAQ,YAAY,IAAI;AAC9C,YAAI,aAAa,QAAW;AAE1B,gBAAM,cAAc,KAAK,MAAM,UAAU,CAAC,aAAa;AACrD,kBAAM,mBAAmB,KAAK,QAAQ,YAAa,QAAQ;AAE3D,kBAAM,oBAAoB,oBAAoB;AAC9C,mBAAO,oBAAoB;AAAA,UAC7B,CAAC;AAED,cAAI,gBAAgB,IAAI;AACtB,iBAAK,MAAM,KAAK,IAAI;AAAA,UACtB,OAAO;AACL,iBAAK,MAAM,OAAO,aAAa,GAAG,IAAI;AAAA,UACxC;AAAA,QACF,OAAO;AAEL,eAAK,iBAAiB,MAAM,QAAQ;AAAA,QACtC;AAAA,MACF,OAAO;AAEL,aAAK,iBAAiB,MAAM,QAAQ;AAAA,MACtC;AAGA,UAAI,mBAAmB,KAAK,aAAa,CAAC,KAAK,aAAa;AAC1D,aAAK,KAAA;AAAA,MACP;AAEA,aAAO;AAAA,IACT;AAEA,SAAQ,mBAAmB,CAAC,MAAc,aAAkC;AAC1E,UAAI,aAAa,SAAS;AACxB,aAAK,MAAM,QAAQ,IAAI;AAAA,MACzB,OAAO;AACL,aAAK,MAAM,KAAK,IAAI;AAAA,MACtB;AAAA,IACF;AAYA,SAAA,cAAc,CACZ,WAA0B,KAAK,QAAQ,iBAChB;AACvB,UAAI,KAAK,MAAM,WAAW,GAAG;AAC3B,eAAO;AAAA,MACT;AAEA,UAAI;AAGJ,UAAI,KAAK,QAAQ,eAAe,aAAa,SAAS;AACpD,eAAO,KAAK,MAAM,MAAA;AAAA,MACpB,OAAO;AACL,eAAO,KAAK,MAAM,IAAA;AAAA,MACpB;AAEA,aAAO;AAAA,IACT;AAWA,SAAA,UAAU,CAAC,aAAiD;AAC1D,YAAM,OAAO,KAAK,YAAY,QAAQ;AACtC,UAAI,SAAS,QAAW;AACtB,aAAK,GAAG,IAAI;AAAA,MACd;AACA,aAAO;AAAA,IACT;AAKA,SAAQ,OAAO,MAAY;AACzB,UAAI,CAAC,KAAK,WAAW;AACnB,aAAK,cAAc;AACnB;AAAA,MACF;AAEA,WAAK,cAAc;AAGnB,aAAO,KAAK,MAAM,SAAS,GAAG;AAC5B,cAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,YAAY;AACnD,YAAI,SAAS,QAAW;AACtB;AAAA,QACF;AAEA,cAAM,OAAO,KAAK,QAAQ;AAC1B,YAAI,OAAO,GAAG;AAEZ,eAAK,YAAY,WAAW,MAAM,KAAK,KAAA,GAAQ,IAAI;AACnD;AAAA,QACF;AAAA,MAGF;AAEA,WAAK,cAAc;AAAA,IACrB;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,YAAY;AACjB,UAAI,CAAC,KAAK,eAAe,KAAK,MAAM,SAAS,GAAG;AAC9C,aAAK,KAAA;AAAA,MACP;AAAA,IACF;AAKA,SAAA,OAAO,MAAY;AACjB,WAAK,aAAA;AACL,WAAK,YAAY;AACjB,WAAK,cAAc;AAAA,IACrB;AAKA,SAAQ,eAAe,MAAY;AACjC,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AAWA,SAAA,eAAe,CAAC,WAA0B,YAAgC;AACxE,UAAI,KAAK,MAAM,WAAW,GAAG;AAC3B,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,QAAQ,eAAe,aAAa,SAAS;AACpD,eAAO,KAAK,MAAM,CAAC;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAAA,MACzC;AAAA,IACF;AAKA,SAAA,eAAe,MAAqB;AAClC,aAAO,CAAC,GAAG,KAAK,KAAK;AAAA,IACvB;AAYA,SAAA,QAAQ,CACN,gBAAwB,KAAK,MAAM,QACnC,aACS;AACT,WAAK,aAAA;AACL,eAAS,IAAI,GAAG,IAAI,iBAAiB,KAAK,MAAM,SAAS,GAAG,KAAK;AAC/D,aAAK,QAAQ,QAAQ;AAAA,MACvB;AAEA,UAAI,KAAK,aAAa,KAAK,MAAM,SAAS,KAAK,CAAC,KAAK,aAAa;AAChE,aAAK,KAAA;AAAA,MACP;AAAA,IACF;AAcA,SAAA,eAAe,CAAC,kBAAwD;AACtE,YAAM,QAAQ,KAAK,aAAA;AACnB,WAAK,MAAA;AACL,oBAAc,KAAK;AAAA,IACrB;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,QAAQ,CAAA;AAAA,IACf;AA/RE,SAAK,QAAQ,aAAa,KAAK,QAAQ,cAAc;AACrD,SAAK,QAAQ,eAAe,KAAK,QAAQ,gBAAgB;AACzD,SAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,SAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,SAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAEzC,SAAK,YAAY,KAAK,QAAQ;AAG9B,QAAI,KAAK,QAAQ,cAAc;AAC7B,iBAAW,QAAQ,KAAK,QAAQ,cAAc;AAC5C,aAAK,QAAQ,MAAM,KAAK,QAAQ,YAAY,KAAK;AAAA,MACnD;AAAA,IACF;AAGA,QAAI,KAAK,aAAa,KAAK,MAAM,SAAS,GAAG;AAC3C,WAAK,KAAA;AAAA,IACP;AAAA,EACF;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,iBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAwPF;AAoBO,SAAS,UACd,IACA,UAAqC,IACV;AAC3B,QAAM,SAAS,IAAI,WAAW,IAAI,OAAO;AACzC,SAAO,CAAC,SAAiB,OAAO,QAAQ,IAAI;AAC9C;;;"}
/**
* Position type for addItem and getNextItem operations.
*
* - 'front': Operate on the front of the queue (FIFO for getNextItem)
* - 'back': Operate on the back of the queue (LIFO for getNextItem)
*/
export type QueuePosition = 'front' | 'back';
/**
* Options for configuring a lite queuer instance
*/
export interface LiteQueuerOptions<TValue> {
/**
* Default position to add items to the queue
* @default 'back'
*/
addItemsTo?: QueuePosition;
/**
* Default position to get items from during processing
* @default 'front'
*/
getItemsFrom?: QueuePosition;
/**
* Function to determine priority of items in the queue
* Higher priority items will be processed first
* Return undefined for items that should use positional ordering
*/
getPriority?: (item: TValue) => number | undefined;
/**
* Initial items to populate the queue with
*/
initialItems?: Array<TValue>;
/**
* Maximum number of items allowed in the queue
*/
maxSize?: number;
/**
* Whether the queuer should start processing items immediately
* @default true
*/
started?: boolean;
/**
* Time in milliseconds to wait between processing items
* @default 0
*/
wait?: number;
}
/**
* A lightweight class that creates a queue for processing items.
*
* This is an alternative to the Queuer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential queueing functionality.
*
* The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based
* processing of items. Items can be processed automatically with configurable wait times
* between executions, or processed manually using the execute methods.
*
* Features included:
* - Automatic or manual processing of items
* - FIFO, LIFO, and priority-based ordering
* - Queue size limits with item rejection
* - Configurable wait times between processing
* - Batch processing capabilities
* - Start/stop processing control
* - Callback support for monitoring execution, rejection, and state change events
*
* Features NOT included (compared to core Queuer):
* - No TanStack Store state management
* - No devtools integration
* - No item expiration functionality (no onExpire callback)
* - No dynamic options updates (setOptions)
* - No detailed state tracking (execution counts, etc.)
*
* Queue behavior:
* - Default: FIFO (add to back, process from front)
* - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'
* - Priority: Provide getPriority function; higher values processed first
*
* @example
* ```ts
* // Basic FIFO queue
* const queue = new LiteQueuer((item: string) => {
* console.log('Processing:', item);
* }, { wait: 100 });
*
* queue.addItem('task1');
* queue.addItem('task2');
* // Processes: task1, then task2 after 100ms delay
* ```
*
* @example
* ```ts
* // Priority queue
* const priorityQueue = new LiteQueuer((item: Task) => {
* processTask(item);
* }, {
* getPriority: task => task.priority,
* wait: 500
* });
*
* priorityQueue.addItem({ name: 'low', priority: 1 });
* priorityQueue.addItem({ name: 'high', priority: 10 });
* // Processes high priority task first
* ```
*/
export declare class LiteQueuer<TValue> {
fn: (item: TValue) => void;
options: LiteQueuerOptions<TValue>;
private items;
private timeoutId;
private isRunning;
private pendingTick;
constructor(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>);
/**
* Number of items currently in the queue
*/
get size(): number;
/**
* Whether the queue is empty
*/
get isEmpty(): boolean;
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning(): boolean;
/**
* Adds an item to the queue. If the queue is full, the item is rejected.
* Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.
*
* Returns true if the item was added, false if the queue is full.
*
* @example
* ```ts
* queue.addItem('task1'); // Add to default position (back)
* queue.addItem('task2', 'front'); // Add to front
* ```
*/
addItem: (item: TValue, position?: QueuePosition, startProcessing?: boolean) => boolean;
private insertAtPosition;
/**
* Removes and returns the next item from the queue without executing the function.
* Use for manual queue management. Normally, use execute() to process items.
*
* @example
* ```ts
* const nextItem = queue.getNextItem(); // Get from default position (front)
* const lastItem = queue.getNextItem('back'); // Get from back (LIFO)
* ```
*/
getNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Removes and returns the next item from the queue and processes it using the provided function.
*
* @example
* ```ts
* queue.execute(); // Execute from default position
* queue.execute('back'); // Execute from back (LIFO)
* ```
*/
execute: (position?: QueuePosition) => TValue | undefined;
/**
* Internal method that processes items in the queue with wait intervals
*/
private tick;
/**
* Starts processing items in the queue. If already running, does nothing.
*/
start: () => void;
/**
* Stops processing items in the queue. Does not clear the queue.
*/
stop: () => void;
/**
* Clears any pending timeout
*/
private clearTimeout;
/**
* Returns the next item in the queue without removing it.
*
* @example
* ```ts
* const next = queue.peekNextItem(); // Peek at front
* const last = queue.peekNextItem('back'); // Peek at back
* ```
*/
peekNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Returns a copy of all items in the queue.
*/
peekAllItems: () => Array<TValue>;
/**
* Processes a specified number of items immediately with no wait time.
* If no numberOfItems is provided, all items will be processed.
*
* @example
* ```ts
* queue.flush(); // Process all items immediately
* queue.flush(3); // Process next 3 items immediately
* ```
*/
flush: (numberOfItems?: number, position?: QueuePosition) => void;
/**
* Processes all items in the queue as a batch using the provided function.
* The queue is cleared after processing.
*
* @example
* ```ts
* queue.flushAsBatch((items) => {
* console.log('Processing batch:', items);
* // Process all items together
* });
* ```
*/
flushAsBatch: (batchFunction: (items: Array<TValue>) => void) => void;
/**
* Removes all items from the queue. Does not affect items being processed.
*/
clear: () => void;
}
/**
* Creates a lightweight queue that processes items using the provided function.
*
* This is an alternative to the queue function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a queuer with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const processItem = liteQueue((item: string) => {
* console.log('Processing:', item);
* }, { wait: 1000 });
*
* processItem('task1');
* processItem('task2');
* // Processes each item with 1 second delay between them
* ```
*/
export declare function liteQueue<TValue>(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>): (item: TValue) => boolean;
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
class LiteRateLimiter {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.executionTimes = [];
this.timeoutIds = /* @__PURE__ */ new Set();
this.maybeExecute = (...args) => {
this.cleanupOldExecutions();
const relevantExecutionTimes = this.getExecutionTimesInWindow();
if (relevantExecutionTimes.length < this.options.limit) {
this.execute(...args);
return true;
}
this.options.onReject?.(this);
return false;
};
this.execute = (...args) => {
const now = Date.now();
this.fn(...args);
this.options.onExecute?.(args, this);
this.executionTimes.push(now);
this.setCleanupTimeout(now);
};
this.getExecutionTimesInWindow = () => {
if (this.options.windowType === "sliding") {
return this.executionTimes.filter(
(time) => time > Date.now() - this.options.window
);
} else {
if (this.executionTimes.length === 0) {
return [];
}
const oldestExecution = Math.min(...this.executionTimes);
const windowStart = oldestExecution;
const windowEnd = windowStart + this.options.window;
const now = Date.now();
if (now > windowEnd) {
return [];
}
return this.executionTimes.filter(
(time) => time >= windowStart && time <= windowEnd
);
}
};
this.setCleanupTimeout = (executionTime) => {
if (this.options.windowType === "sliding" || this.timeoutIds.size === 0) {
const now = Date.now();
const timeUntilExpiration = executionTime - now + this.options.window + 1;
const timeoutId = setTimeout(() => {
this.cleanupOldExecutions();
this.clearTimeout(timeoutId);
}, timeUntilExpiration);
this.timeoutIds.add(timeoutId);
}
};
this.clearTimeout = (timeoutId) => {
clearTimeout(timeoutId);
this.timeoutIds.delete(timeoutId);
};
this.clearTimeouts = () => {
this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
this.timeoutIds.clear();
};
this.cleanupOldExecutions = () => {
this.executionTimes = this.getExecutionTimesInWindow();
};
this.getRemainingInWindow = () => {
const relevantExecutionTimes = this.getExecutionTimesInWindow();
return Math.max(0, this.options.limit - relevantExecutionTimes.length);
};
this.getMsUntilNextWindow = () => {
if (this.getRemainingInWindow() > 0) {
return 0;
}
const oldestExecution = this.executionTimes[0] ?? Infinity;
return oldestExecution + this.options.window - Date.now();
};
this.reset = () => {
this.executionTimes = [];
this.clearTimeouts();
};
if (this.options.windowType === void 0) {
this.options.windowType = "fixed";
}
}
}
function liteRateLimit(fn, options) {
const rateLimiter = new LiteRateLimiter(fn, options);
return rateLimiter.maybeExecute;
}
exports.LiteRateLimiter = LiteRateLimiter;
exports.liteRateLimit = liteRateLimit;
//# sourceMappingURL=lite-rate-limiter.cjs.map
{"version":3,"file":"lite-rate-limiter.cjs","sources":["../../src/lite-rate-limiter.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite rate-limited function\n */\nexport interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Maximum number of executions allowed within the time window.\n */\n limit: number\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Optional callback function that is called when an execution is rejected due to rate limiting\n */\n onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Time window in milliseconds within which the limit applies.\n */\n window: 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 * A lightweight class that creates a rate-limited function.\n *\n * This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential rate limiting functionality.\n *\n * Rate limiting 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 *\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 * Features:\n * - Zero dependencies - no external libraries required\n * - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)\n * - Simple state management - uses basic private properties instead of reactive stores\n * - Lightweight - designed for use in npm packages where bundle size matters\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter((id: string) => {\n * api.getData(id);\n * }, { limit: 5, window: 1000 });\n *\n * // First 5 calls will execute, then block until window resets\n * if (rateLimiter.maybeExecute('123')) {\n * console.log('API call made');\n * } else {\n * console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');\n * }\n * ```\n */\nexport class LiteRateLimiter<TFn extends AnyFunction> {\n private executionTimes: Array<number> = []\n private timeoutIds: Set<NodeJS.Timeout> = new Set()\n\n constructor(\n public fn: TFn,\n public options: LiteRateLimiterOptions<TFn>,\n ) {\n // Default windowType to 'fixed' if not specified\n if (this.options.windowType === undefined) {\n this.options.windowType = 'fixed'\n }\n }\n\n /**\n * Attempts to execute the rate-limited function if within the configured limits.\n * Returns true if executed, false if rejected due to rate limiting.\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });\n *\n * // First 5 calls return true\n * rateLimiter.maybeExecute('arg1', 'arg2'); // true\n *\n * // Additional calls within the window return false\n * rateLimiter.maybeExecute('arg1', 'arg2'); // false\n * ```\n */\n maybeExecute = (...args: Parameters<TFn>): boolean => {\n this.cleanupOldExecutions()\n\n const relevantExecutionTimes = this.getExecutionTimesInWindow()\n\n if (relevantExecutionTimes.length < this.options.limit) {\n this.execute(...args)\n return true\n }\n\n this.options.onReject?.(this)\n return false\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.executionTimes.push(now)\n this.setCleanupTimeout(now)\n }\n\n private getExecutionTimesInWindow = (): Array<number> => {\n if (this.options.windowType === 'sliding') {\n // For sliding window, return all executions within the current window\n return this.executionTimes.filter(\n (time) => time > Date.now() - this.options.window,\n )\n } else {\n // For fixed window, return all executions in the current window\n if (this.executionTimes.length === 0) {\n return []\n }\n const oldestExecution = Math.min(...this.executionTimes)\n const windowStart = oldestExecution\n const windowEnd = windowStart + this.options.window\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.executionTimes.filter(\n (time) => time >= windowStart && time <= windowEnd,\n )\n }\n }\n\n private 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.options.window + 1\n const timeoutId = setTimeout(() => {\n this.cleanupOldExecutions()\n this.clearTimeout(timeoutId)\n }, timeUntilExpiration)\n this.timeoutIds.add(timeoutId)\n }\n }\n\n private clearTimeout = (timeoutId: NodeJS.Timeout): void => {\n clearTimeout(timeoutId)\n this.timeoutIds.delete(timeoutId)\n }\n\n private clearTimeouts = (): void => {\n this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId))\n this.timeoutIds.clear()\n }\n\n private cleanupOldExecutions = (): void => {\n this.executionTimes = this.getExecutionTimesInWindow()\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.options.limit - relevantExecutionTimes.length)\n }\n\n /**\n * Returns the number of milliseconds until the next execution will be possible.\n * Returns 0 if executions are currently allowed.\n */\n getMsUntilNextWindow = (): number => {\n if (this.getRemainingInWindow() > 0) {\n return 0\n }\n const oldestExecution = this.executionTimes[0] ?? Infinity\n return oldestExecution + this.options.window - Date.now()\n }\n\n /**\n * Resets the rate limiter state, clearing all execution history.\n */\n reset = (): void => {\n this.executionTimes = []\n this.clearTimeouts()\n }\n}\n\n/**\n * Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.\n *\n * This is an alternative to the rateLimit 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 rate limiter with no external dependencies, devtools integration, or reactive state.\n *\n * Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.\n * This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).\n *\n * @example\n * ```ts\n * const rateLimitedApi = liteRateLimit(makeApiCall, {\n * limit: 5,\n * window: 60000, // 1 minute\n * windowType: 'sliding'\n * });\n *\n * // First 5 calls execute immediately\n * // Additional calls are rejected until window allows\n * rateLimitedApi();\n * ```\n *\n * @example\n * ```ts\n * // Fixed window - all 10 calls happen in first second, then 10 second wait\n * const rateLimitedFixed = liteRateLimit(logEvent, {\n * limit: 10,\n * window: 10000,\n * windowType: 'fixed'\n * });\n * ```\n */\nexport function liteRateLimit<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteRateLimiterOptions<TFn>,\n): (...args: Parameters<TFn>) => boolean {\n const rateLimiter = new LiteRateLimiter(fn, options)\n return rateLimiter.maybeExecute\n}\n"],"names":[],"mappings":";;AAqEO,MAAM,gBAAyC;AAAA,EAIpD,YACS,IACA,SACP;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AALT,SAAQ,iBAAgC,CAAA;AACxC,SAAQ,iCAAsC,IAAA;AA2B9C,SAAA,eAAe,IAAI,SAAmC;AACpD,WAAK,qBAAA;AAEL,YAAM,yBAAyB,KAAK,0BAAA;AAEpC,UAAI,uBAAuB,SAAS,KAAK,QAAQ,OAAO;AACtD,aAAK,QAAQ,GAAG,IAAI;AACpB,eAAO;AAAA,MACT;AAEA,WAAK,QAAQ,WAAW,IAAI;AAC5B,aAAO;AAAA,IACT;AAEA,SAAQ,UAAU,IAAI,SAAgC;AACpD,YAAM,MAAM,KAAK,IAAA;AACjB,WAAK,GAAG,GAAG,IAAI;AACf,WAAK,QAAQ,YAAY,MAAM,IAAI;AACnC,WAAK,eAAe,KAAK,GAAG;AAC5B,WAAK,kBAAkB,GAAG;AAAA,IAC5B;AAEA,SAAQ,4BAA4B,MAAqB;AACvD,UAAI,KAAK,QAAQ,eAAe,WAAW;AAEzC,eAAO,KAAK,eAAe;AAAA,UACzB,CAAC,SAAS,OAAO,KAAK,IAAA,IAAQ,KAAK,QAAQ;AAAA,QAAA;AAAA,MAE/C,OAAO;AAEL,YAAI,KAAK,eAAe,WAAW,GAAG;AACpC,iBAAO,CAAA;AAAA,QACT;AACA,cAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,cAAc;AACvD,cAAM,cAAc;AACpB,cAAM,YAAY,cAAc,KAAK,QAAQ;AAC7C,cAAM,MAAM,KAAK,IAAA;AAGjB,YAAI,MAAM,WAAW;AACnB,iBAAO,CAAA;AAAA,QACT;AAGA,eAAO,KAAK,eAAe;AAAA,UACzB,CAAC,SAAS,QAAQ,eAAe,QAAQ;AAAA,QAAA;AAAA,MAE7C;AAAA,IACF;AAEA,SAAQ,oBAAoB,CAAC,kBAAgC;AAC3D,UACE,KAAK,QAAQ,eAAe,aAC5B,KAAK,WAAW,SAAS,GACzB;AACA,cAAM,MAAM,KAAK,IAAA;AACjB,cAAM,sBAAsB,gBAAgB,MAAM,KAAK,QAAQ,SAAS;AACxE,cAAM,YAAY,WAAW,MAAM;AACjC,eAAK,qBAAA;AACL,eAAK,aAAa,SAAS;AAAA,QAC7B,GAAG,mBAAmB;AACtB,aAAK,WAAW,IAAI,SAAS;AAAA,MAC/B;AAAA,IACF;AAEA,SAAQ,eAAe,CAAC,cAAoC;AAC1D,mBAAa,SAAS;AACtB,WAAK,WAAW,OAAO,SAAS;AAAA,IAClC;AAEA,SAAQ,gBAAgB,MAAY;AAClC,WAAK,WAAW,QAAQ,CAAC,cAAc,aAAa,SAAS,CAAC;AAC9D,WAAK,WAAW,MAAA;AAAA,IAClB;AAEA,SAAQ,uBAAuB,MAAY;AACzC,WAAK,iBAAiB,KAAK,0BAAA;AAAA,IAC7B;AAKA,SAAA,uBAAuB,MAAc;AACnC,YAAM,yBAAyB,KAAK,0BAAA;AACpC,aAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,QAAQ,uBAAuB,MAAM;AAAA,IACvE;AAMA,SAAA,uBAAuB,MAAc;AACnC,UAAI,KAAK,qBAAA,IAAyB,GAAG;AACnC,eAAO;AAAA,MACT;AACA,YAAM,kBAAkB,KAAK,eAAe,CAAC,KAAK;AAClD,aAAO,kBAAkB,KAAK,QAAQ,SAAS,KAAK,IAAA;AAAA,IACtD;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,iBAAiB,CAAA;AACtB,WAAK,cAAA;AAAA,IACP;AA7HE,QAAI,KAAK,QAAQ,eAAe,QAAW;AACzC,WAAK,QAAQ,aAAa;AAAA,IAC5B;AAAA,EACF;AA2HF;AAmCO,SAAS,cACd,IACA,SACuC;AACvC,QAAM,cAAc,IAAI,gBAAgB,IAAI,OAAO;AACnD,SAAO,YAAY;AACrB;;;"}
import { AnyFunction } from '@tanstack/pacer/types';
/**
* Options for configuring a lite rate-limited function
*/
export interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Maximum number of executions allowed within the time window.
*/
limit: number;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Optional callback function that is called when an execution is rejected due to rate limiting
*/
onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Time window in milliseconds within which the limit applies.
*/
window: number;
/**
* Type of window to use for rate limiting
* - 'fixed': Uses a fixed window that resets after the window period
* - 'sliding': Uses a sliding window that allows executions as old ones expire
* Defaults to 'fixed'
*/
windowType?: 'fixed' | 'sliding';
}
/**
* A lightweight class that creates a rate-limited function.
*
* This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential rate limiting functionality.
*
* Rate limiting allows a function to execute up to a limit within a time window,
* then blocks all subsequent calls until the window passes. This can lead to "bursty" behavior where
* all executions happen immediately, followed by a complete block.
*
* The rate limiter supports two types of windows:
* - 'fixed': A strict window that resets after the window period. All executions within the window count
* towards the limit, and the window resets completely after the period.
* - 'sliding': A rolling window that allows executions as old ones expire. This provides a more
* consistent rate of execution over time.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)
* - Simple state management - uses basic private properties instead of reactive stores
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter((id: string) => {
* api.getData(id);
* }, { limit: 5, window: 1000 });
*
* // First 5 calls will execute, then block until window resets
* if (rateLimiter.maybeExecute('123')) {
* console.log('API call made');
* } else {
* console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');
* }
* ```
*/
export declare class LiteRateLimiter<TFn extends AnyFunction> {
fn: TFn;
options: LiteRateLimiterOptions<TFn>;
private executionTimes;
private timeoutIds;
constructor(fn: TFn, options: LiteRateLimiterOptions<TFn>);
/**
* Attempts to execute the rate-limited function if within the configured limits.
* Returns true if executed, false if rejected due to rate limiting.
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });
*
* // First 5 calls return true
* rateLimiter.maybeExecute('arg1', 'arg2'); // true
*
* // Additional calls within the window return false
* rateLimiter.maybeExecute('arg1', 'arg2'); // false
* ```
*/
maybeExecute: (...args: Parameters<TFn>) => boolean;
private execute;
private getExecutionTimesInWindow;
private setCleanupTimeout;
private clearTimeout;
private clearTimeouts;
private cleanupOldExecutions;
/**
* Returns the number of remaining executions allowed in the current window.
*/
getRemainingInWindow: () => number;
/**
* Returns the number of milliseconds until the next execution will be possible.
* Returns 0 if executions are currently allowed.
*/
getMsUntilNextWindow: () => number;
/**
* Resets the rate limiter state, clearing all execution history.
*/
reset: () => void;
}
/**
* Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
*
* This is an alternative to the rateLimit function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a rate limiter with no external dependencies, devtools integration, or reactive state.
*
* Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.
* This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).
*
* @example
* ```ts
* const rateLimitedApi = liteRateLimit(makeApiCall, {
* limit: 5,
* window: 60000, // 1 minute
* windowType: 'sliding'
* });
*
* // First 5 calls execute immediately
* // Additional calls are rejected until window allows
* rateLimitedApi();
* ```
*
* @example
* ```ts
* // Fixed window - all 10 calls happen in first second, then 10 second wait
* const rateLimitedFixed = liteRateLimit(logEvent, {
* limit: 10,
* window: 10000,
* windowType: 'fixed'
* });
* ```
*/
export declare function liteRateLimit<TFn extends AnyFunction>(fn: TFn, options: LiteRateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
class LiteThrottler {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.lastExecutionTime = 0;
this.isPending = false;
this.maybeExecute = (...args) => {
const now = Date.now();
const timeSinceLastExecution = now - this.lastExecutionTime;
if (this.options.leading && timeSinceLastExecution >= this.options.wait) {
this.execute(...args);
} else {
this.lastArgs = args;
if (!this.timeoutId && this.options.trailing) {
const timeoutDuration = this.options.wait - timeSinceLastExecution;
this.isPending = true;
this.timeoutId = setTimeout(() => {
if (this.lastArgs !== void 0) {
this.execute(...this.lastArgs);
}
}, timeoutDuration);
}
}
};
this.execute = (...args) => {
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastExecutionTime = Date.now();
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.flush = () => {
if (this.isPending && this.lastArgs) {
this.execute(...this.lastArgs);
}
};
this.cancel = () => {
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
};
if (this.options.leading === void 0 && this.options.trailing === void 0) {
this.options.leading = true;
this.options.trailing = true;
}
}
}
function liteThrottle(fn, options) {
const throttler = new LiteThrottler(fn, options);
return throttler.maybeExecute;
}
exports.LiteThrottler = LiteThrottler;
exports.liteThrottle = liteThrottle;
//# sourceMappingURL=lite-throttler.cjs.map
{"version":3,"file":"lite-throttler.cjs","sources":["../../src/lite-throttler.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite throttled function\n */\nexport interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to true.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Time window in milliseconds during which the function can only be executed once.\n */\n wait: number\n}\n\n/**\n * A lightweight class that creates a throttled function.\n *\n * This is an alternative to the Throttler in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential throttling functionality.\n *\n * Throttling ensures a function is called at most once within a specified time window.\n * Unlike debouncing which waits for a pause in calls, throttling guarantees consistent\n * execution timing regardless of call frequency.\n *\n * Supports both leading and trailing edge execution:\n * - Leading: Execute immediately on first call (default: true)\n * - Trailing: Execute after wait period if called during throttle (default: true)\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 throttler = new LiteThrottler((scrollY: number) => {\n * updateScrollPosition(scrollY);\n * }, {\n * wait: 100,\n * onExecute: (args, throttler) => {\n * console.log('Updated scroll position:', args[0]);\n * }\n * });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', () => {\n * throttler.maybeExecute(window.scrollY);\n * });\n * ```\n */\nexport class LiteThrottler<TFn extends AnyFunction> {\n private timeoutId: NodeJS.Timeout | undefined\n private lastArgs: Parameters<TFn> | undefined\n private lastExecutionTime = 0\n private isPending = false\n\n constructor(\n public fn: TFn,\n public options: LiteThrottlerOptions<TFn>,\n ) {\n // Default both leading and trailing to true if neither is specified\n if (\n this.options.leading === undefined &&\n this.options.trailing === undefined\n ) {\n this.options.leading = true\n this.options.trailing = true\n }\n }\n\n /**\n * Attempts to execute the throttled function. The execution behavior depends on the throttler options:\n *\n * - If enough time has passed since the last execution (>= wait period):\n * - With leading=true: Executes immediately\n * - With leading=false: Waits for the next trailing execution\n *\n * - If within the wait period:\n * - With trailing=true: Schedules execution for end of wait period\n * - With trailing=false: Drops the execution\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n const timeSinceLastExecution = now - this.lastExecutionTime\n\n // Handle leading execution\n if (this.options.leading && timeSinceLastExecution >= this.options.wait) {\n this.execute(...args)\n } else {\n // Store the most recent arguments for potential trailing execution\n this.lastArgs = args\n\n // Set up trailing execution if not already scheduled\n if (!this.timeoutId && this.options.trailing) {\n const timeoutDuration = this.options.wait - timeSinceLastExecution\n this.isPending = true\n this.timeoutId = setTimeout(() => {\n if (this.lastArgs !== undefined) {\n this.execute(...this.lastArgs)\n }\n }, timeoutDuration)\n }\n }\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.lastExecutionTime = Date.now()\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\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.isPending && this.lastArgs) {\n this.execute(...this.lastArgs)\n }\n }\n\n /**\n * Cancels any pending trailing execution and clears internal state.\n * If a trailing execution is scheduled, this will prevent that execution from occurring.\n */\n cancel = (): void => {\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\n }\n\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = undefined\n }\n }\n}\n\n/**\n * Creates a lightweight throttled function that limits how often the provided function can execute.\n *\n * This is an alternative to the throttle 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 throttler with no external dependencies, devtools integration, or reactive state.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * @example\n * ```ts\n * const throttledScroll = liteThrottle(() => {\n * updateScrollIndicator();\n * }, { wait: 100 });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', throttledScroll);\n * ```\n *\n * @example\n * ```ts\n * // Leading edge execution - fires immediately then throttles\n * const throttledResize = liteThrottle(() => {\n * recalculateLayout();\n * }, { wait: 250, leading: true, trailing: false });\n * ```\n */\nexport function liteThrottle<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteThrottlerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const throttler = new LiteThrottler(fn, options)\n return throttler.maybeExecute\n}\n"],"names":[],"mappings":";;AAkEO,MAAM,cAAuC;AAAA,EAMlD,YACS,IACA,SACP;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AALT,SAAQ,oBAAoB;AAC5B,SAAQ,YAAY;AA2BpB,SAAA,eAAe,IAAI,SAAgC;AACjD,YAAM,MAAM,KAAK,IAAA;AACjB,YAAM,yBAAyB,MAAM,KAAK;AAG1C,UAAI,KAAK,QAAQ,WAAW,0BAA0B,KAAK,QAAQ,MAAM;AACvE,aAAK,QAAQ,GAAG,IAAI;AAAA,MACtB,OAAO;AAEL,aAAK,WAAW;AAGhB,YAAI,CAAC,KAAK,aAAa,KAAK,QAAQ,UAAU;AAC5C,gBAAM,kBAAkB,KAAK,QAAQ,OAAO;AAC5C,eAAK,YAAY;AACjB,eAAK,YAAY,WAAW,MAAM;AAChC,gBAAI,KAAK,aAAa,QAAW;AAC/B,mBAAK,QAAQ,GAAG,KAAK,QAAQ;AAAA,YAC/B;AAAA,UACF,GAAG,eAAe;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAEA,SAAQ,UAAU,IAAI,SAAgC;AACpD,WAAK,GAAG,GAAG,IAAI;AACf,WAAK,QAAQ,YAAY,MAAM,IAAI;AACnC,WAAK,oBAAoB,KAAK,IAAA;AAC9B,WAAK,aAAA;AACL,WAAK,WAAW;AAChB,WAAK,YAAY;AAAA,IACnB;AAOA,SAAA,QAAQ,MAAY;AAClB,UAAI,KAAK,aAAa,KAAK,UAAU;AACnC,aAAK,QAAQ,GAAG,KAAK,QAAQ;AAAA,MAC/B;AAAA,IACF;AAMA,SAAA,SAAS,MAAY;AACnB,WAAK,aAAA;AACL,WAAK,WAAW;AAChB,WAAK,YAAY;AAAA,IACnB;AAEA,SAAQ,eAAe,MAAY;AACjC,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AA/EE,QACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,QAC1B;AACA,WAAK,QAAQ,UAAU;AACvB,WAAK,QAAQ,WAAW;AAAA,IAC1B;AAAA,EACF;AAyEF;AA+BO,SAAS,aACd,IACA,SACoC;AACpC,QAAM,YAAY,IAAI,cAAc,IAAI,OAAO;AAC/C,SAAO,UAAU;AACnB;;;"}
import { AnyFunction } from '@tanstack/pacer/types';
/**
* Options for configuring a lite throttled function
*/
export interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* Defaults to true.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Time window in milliseconds during which the function can only be executed once.
*/
wait: number;
}
/**
* A lightweight class that creates a throttled function.
*
* This is an alternative to the Throttler in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential throttling functionality.
*
* Throttling ensures a function is called at most once within a specified time window.
* Unlike debouncing which waits for a pause in calls, throttling guarantees consistent
* execution timing regardless of call frequency.
*
* Supports both leading and trailing edge execution:
* - Leading: Execute immediately on first call (default: true)
* - Trailing: Execute after wait period if called during throttle (default: true)
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const throttler = new LiteThrottler((scrollY: number) => {
* updateScrollPosition(scrollY);
* }, {
* wait: 100,
* onExecute: (args, throttler) => {
* console.log('Updated scroll position:', args[0]);
* }
* });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', () => {
* throttler.maybeExecute(window.scrollY);
* });
* ```
*/
export declare class LiteThrottler<TFn extends AnyFunction> {
fn: TFn;
options: LiteThrottlerOptions<TFn>;
private timeoutId;
private lastArgs;
private lastExecutionTime;
private isPending;
constructor(fn: TFn, options: LiteThrottlerOptions<TFn>);
/**
* Attempts to execute the throttled function. The execution behavior depends on the throttler options:
*
* - If enough time has passed since the last execution (>= wait period):
* - With leading=true: Executes immediately
* - With leading=false: Waits for the next trailing execution
*
* - If within the wait period:
* - With trailing=true: Schedules execution for end of wait period
* - With trailing=false: Drops the execution
*/
maybeExecute: (...args: Parameters<TFn>) => void;
private execute;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending trailing execution and clears internal state.
* If a trailing execution is scheduled, this will prevent that execution from occurring.
*/
cancel: () => void;
private clearTimeout;
}
/**
* Creates a lightweight throttled function that limits how often the provided function can execute.
*
* This is an alternative to the throttle function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a throttler with no external dependencies, devtools integration, or reactive state.
*
* Throttling ensures a function executes at most once within a specified time window,
* regardless of how many times it is called. This is useful for rate-limiting
* expensive operations or UI updates.
*
* @example
* ```ts
* const throttledScroll = liteThrottle(() => {
* updateScrollIndicator();
* }, { wait: 100 });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', throttledScroll);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then throttles
* const throttledResize = liteThrottle(() => {
* recalculateLayout();
* }, { wait: 250, leading: true, trailing: false });
* ```
*/
export declare function liteThrottle<TFn extends AnyFunction>(fn: TFn, options: LiteThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
export * from './lite-debouncer.js';
export * from './lite-throttler.js';
export * from './lite-rate-limiter.js';
export * from './lite-queuer.js';
export * from './lite-batcher.js';
import { LiteDebouncer, liteDebounce } from "./lite-debouncer.js";
import { LiteThrottler, liteThrottle } from "./lite-throttler.js";
import { LiteRateLimiter, liteRateLimit } from "./lite-rate-limiter.js";
import { LiteQueuer, liteQueue } from "./lite-queuer.js";
import { LiteBatcher, liteBatch } from "./lite-batcher.js";
export {
LiteBatcher,
LiteDebouncer,
LiteQueuer,
LiteRateLimiter,
LiteThrottler,
liteBatch,
liteDebounce,
liteQueue,
liteRateLimit,
liteThrottle
};
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
/**
* Options for configuring a lite batcher instance
*/
export interface LiteBatcherOptions<TValue> {
/**
* Custom function to determine if a batch should be processed
* Return true to process the batch immediately
*/
getShouldExecute?: (items: Array<TValue>, batcher: LiteBatcher<TValue>) => boolean;
/**
* Maximum number of items in a batch
* @default Infinity
*/
maxSize?: number;
/**
* Callback fired after a batch is processed
*/
onExecute?: (batch: Array<TValue>, batcher: LiteBatcher<TValue>) => void;
/**
* Callback fired after items are added to the batcher
*/
onItemsChange?: (batcher: LiteBatcher<TValue>) => void;
/**
* Whether the batcher should start processing immediately
* @default true
*/
started?: boolean;
/**
* Maximum time in milliseconds to wait before processing a batch.
* If the wait duration has elapsed, the batch will be processed.
* If not provided, the batch will not be triggered by a timeout.
* @default Infinity
*/
wait?: number | ((batcher: LiteBatcher<TValue>) => number);
}
/**
* A lightweight class that collects items and processes them in batches.
*
* This is an alternative to the Batcher in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Batcher,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential batching functionality.
*
* Batching is a technique for grouping multiple operations together to be processed as a single unit.
* This synchronous version is lighter weight and often all you need.
*
* The Batcher provides a flexible way to implement batching with configurable:
* - Maximum batch size (number of items per batch)
* - Time-based batching (process after X milliseconds)
* - Custom batch processing logic via getShouldExecute
*
* Features included:
* - Core batching functionality (addItem, flush, clear, cancel)
* - Size-based batching (maxSize)
* - Time-based batching (wait timeout)
* - Custom condition batching (getShouldExecute)
* - Manual processing controls
* - Public mutable options
* - Callback support for monitoring batch execution and state changes
*
* Features NOT included (compared to core Batcher):
* - No TanStack Store state management
* - No devtools integration
* - No complex state tracking (execution counts, etc.)
* - No reactive state management
*
* @example
* ```ts
* // Basic batching
* const batcher = new LiteBatcher<number>(
* (items) => console.log('Processing batch:', items),
* {
* maxSize: 5,
* wait: 2000,
* onExecute: (batch, batcher) => {
* console.log('Batch executed with', batch.length, 'items');
* },
* onItemsChange: (batcher) => {
* console.log('Batch size changed to:', batcher.size);
* }
* }
* );
*
* batcher.addItem(1);
* batcher.addItem(2);
* // After 2 seconds or when 5 items are added, whichever comes first,
* // the batch will be processed
* ```
*
* @example
* ```ts
* // Custom condition batching
* const batcher = new LiteBatcher<Task>(
* (items) => processTasks(items),
* {
* getShouldExecute: (items) => items.some(task => task.urgent),
* maxSize: 10,
* }
* );
*
* batcher.addItem({ name: 'normal', urgent: false });
* batcher.addItem({ name: 'urgent', urgent: true }); // Triggers immediate processing
* ```
*/
export declare class LiteBatcher<TValue> {
fn: (items: Array<TValue>) => void;
options: LiteBatcherOptions<TValue>;
private items;
private timeoutId;
private _isPending;
constructor(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>);
/**
* Number of items currently in the batch
*/
get size(): number;
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty(): boolean;
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending(): boolean;
private getWait;
/**
* Adds an item to the batcher
* If the batch size is reached, timeout occurs, or getShouldExecute returns true, the batch will be processed
*/
addItem: (item: TValue) => void;
/**
* Processes the current batch of items.
* This method will automatically be triggered if the batcher is running and any of these conditions are met:
* - The number of items reaches maxSize
* - The wait duration has elapsed
* - The getShouldExecute function returns true upon adding an item
*
* You can also call this method manually to process the current batch at any time.
*/
private execute;
/**
* Processes the current batch of items immediately
*/
flush: () => void;
/**
* Returns a copy of all items in the batcher
*/
peekAllItems: () => Array<TValue>;
private clearTimeout;
/**
* Removes all items from the batcher
*/
clear: () => void;
/**
* Cancels any pending execution that was scheduled.
* Does NOT clear out the items.
*/
cancel: () => void;
}
/**
* Creates a batcher that processes items in batches.
*
* This is an alternative to the batch function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a batcher with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const batchItems = liteBatch<number>(
* (items) => console.log('Processing:', items),
* {
* maxSize: 3,
* }
* );
*
* batchItems(1);
* batchItems(2);
* batchItems(3); // Triggers batch processing
* ```
*/
export declare function liteBatch<TValue>(fn: (items: Array<TValue>) => void, options?: LiteBatcherOptions<TValue>): (item: TValue) => void;
class LiteBatcher {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this._isPending = false;
this.addItem = (item) => {
this.items.push(item);
this._isPending = this.options.wait !== Infinity;
this.options.onItemsChange?.(this);
const shouldProcess = this.items.length >= this.options.maxSize || this.options.getShouldExecute(this.items, this);
if (shouldProcess) {
this.execute();
} else if (this.options.wait !== Infinity) {
this.clearTimeout();
this.timeoutId = setTimeout(() => this.execute(), this.getWait());
}
};
this.execute = () => {
if (this.items.length === 0) {
return;
}
const batch = this.peekAllItems();
this.clear();
this.fn(batch);
this.options.onExecute?.(batch, this);
};
this.flush = () => {
this.clearTimeout();
this.execute();
};
this.peekAllItems = () => {
return [...this.items];
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.clear = () => {
const hadItems = this.items.length > 0;
this.items = [];
this._isPending = false;
if (hadItems) {
this.options.onItemsChange?.(this);
}
};
this.cancel = () => {
this.clearTimeout();
this._isPending = false;
};
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? Infinity;
this.options.getShouldExecute = this.options.getShouldExecute ?? (() => false);
}
/**
* Number of items currently in the batch
*/
get size() {
return this.items.length;
}
/**
* Whether the batch has no items to process (items array is empty)
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the batcher is waiting for the timeout to trigger batch processing
*/
get isPending() {
return this._isPending;
}
getWait() {
if (typeof this.options.wait === "function") {
return this.options.wait(this);
}
return this.options.wait;
}
}
function liteBatch(fn, options = {}) {
const batcher = new LiteBatcher(fn, options);
return batcher.addItem;
}
export {
LiteBatcher,
liteBatch
};
//# sourceMappingURL=lite-batcher.js.map
{"version":3,"file":"lite-batcher.js","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;"}
import { AnyFunction } from '@tanstack/pacer/types';
/**
* Options for configuring a lite debounced function
*/
export interface LiteDebouncerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* The first call will execute immediately and the rest will wait the delay.
* Defaults to false.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, debouncer: LiteDebouncer<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Delay in milliseconds before executing the function.
*/
wait: number;
}
/**
* A lightweight class that creates a debounced function.
*
* This is an alternative to the Debouncer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Debouncer,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential debouncing functionality.
*
* Debouncing ensures that a function is only executed after a certain amount of time has passed
* since its last invocation. This is useful for handling frequent events like window resizing,
* scroll events, or input changes where you want to limit the rate of execution.
*
* The debounced function can be configured to execute either at the start of the delay period
* (leading edge) or at the end (trailing edge, default). Each new call during the wait period
* will reset the timer.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const debouncer = new LiteDebouncer((value: string) => {
* saveToDatabase(value);
* }, {
* wait: 500,
* onExecute: (args, debouncer) => {
* console.log('Saved value:', args[0]);
* }
* });
*
* // Will only save after 500ms of no new input
* inputElement.addEventListener('input', () => {
* debouncer.maybeExecute(inputElement.value);
* });
* ```
*/
export declare class LiteDebouncer<TFn extends AnyFunction> {
fn: TFn;
options: LiteDebouncerOptions<TFn>;
private timeoutId;
private lastArgs;
private canLeadingExecute;
constructor(fn: TFn, options: LiteDebouncerOptions<TFn>);
/**
* Attempts to execute the debounced function.
* If leading is true and this is the first call, executes immediately.
* Otherwise, queues the execution for after the wait time.
* Each new call resets the timer.
*/
maybeExecute: (...args: Parameters<TFn>) => void;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending execution.
* Clears the timeout and resets the internal state.
*/
cancel: () => void;
}
/**
* Creates a lightweight debounced function that delays invoking the provided function until after a specified wait time.
* Multiple calls during the wait period will cancel previous pending invocations and reset the timer.
*
* This is an alternative to the debounce function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a debouncer with no external dependencies, devtools integration, or reactive state.
*
* If leading option is true, the function will execute immediately on the first call, then wait the delay
* before allowing another execution.
*
* @example
* ```ts
* const debouncedSave = liteDebounce(() => {
* saveChanges();
* }, { wait: 1000 });
*
* // Called repeatedly but executes at most once per second
* inputElement.addEventListener('input', debouncedSave);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then waits
* const debouncedSearch = liteDebounce((query: string) => {
* performSearch(query);
* }, { wait: 300, leading: true });
* ```
*/
export declare function liteDebounce<TFn extends AnyFunction>(fn: TFn, options: LiteDebouncerOptions<TFn>): (...args: Parameters<TFn>) => void;
class LiteDebouncer {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.canLeadingExecute = true;
this.maybeExecute = (...args) => {
let didLeadingExecute = false;
if (this.options.leading && this.canLeadingExecute) {
this.canLeadingExecute = false;
didLeadingExecute = true;
this.fn(...args);
this.options.onExecute?.(args, this);
}
this.lastArgs = args;
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
this.timeoutId = setTimeout(() => {
this.canLeadingExecute = true;
if (this.options.trailing && !didLeadingExecute && this.lastArgs) {
this.fn(...this.lastArgs);
this.options.onExecute?.(this.lastArgs, this);
}
this.lastArgs = void 0;
}, this.options.wait);
};
this.flush = () => {
if (this.timeoutId && this.lastArgs) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
const args = this.lastArgs;
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastArgs = void 0;
this.canLeadingExecute = true;
}
};
this.cancel = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
this.lastArgs = void 0;
this.canLeadingExecute = true;
};
if (this.options.leading === void 0 && this.options.trailing === void 0) {
this.options.trailing = true;
}
}
}
function liteDebounce(fn, options) {
const debouncer = new LiteDebouncer(fn, options);
return debouncer.maybeExecute;
}
export {
LiteDebouncer,
liteDebounce
};
//# sourceMappingURL=lite-debouncer.js.map
{"version":3,"file":"lite-debouncer.js","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;"}
/**
* Position type for addItem and getNextItem operations.
*
* - 'front': Operate on the front of the queue (FIFO for getNextItem)
* - 'back': Operate on the back of the queue (LIFO for getNextItem)
*/
export type QueuePosition = 'front' | 'back';
/**
* Options for configuring a lite queuer instance
*/
export interface LiteQueuerOptions<TValue> {
/**
* Default position to add items to the queue
* @default 'back'
*/
addItemsTo?: QueuePosition;
/**
* Default position to get items from during processing
* @default 'front'
*/
getItemsFrom?: QueuePosition;
/**
* Function to determine priority of items in the queue
* Higher priority items will be processed first
* Return undefined for items that should use positional ordering
*/
getPriority?: (item: TValue) => number | undefined;
/**
* Initial items to populate the queue with
*/
initialItems?: Array<TValue>;
/**
* Maximum number of items allowed in the queue
*/
maxSize?: number;
/**
* Whether the queuer should start processing items immediately
* @default true
*/
started?: boolean;
/**
* Time in milliseconds to wait between processing items
* @default 0
*/
wait?: number;
}
/**
* A lightweight class that creates a queue for processing items.
*
* This is an alternative to the Queuer in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,
* this version does not use TanStack Store for state management, has no devtools integration,
* no callbacks, and provides only essential queueing functionality.
*
* The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based
* processing of items. Items can be processed automatically with configurable wait times
* between executions, or processed manually using the execute methods.
*
* Features included:
* - Automatic or manual processing of items
* - FIFO, LIFO, and priority-based ordering
* - Queue size limits with item rejection
* - Configurable wait times between processing
* - Batch processing capabilities
* - Start/stop processing control
* - Callback support for monitoring execution, rejection, and state change events
*
* Features NOT included (compared to core Queuer):
* - No TanStack Store state management
* - No devtools integration
* - No item expiration functionality (no onExpire callback)
* - No dynamic options updates (setOptions)
* - No detailed state tracking (execution counts, etc.)
*
* Queue behavior:
* - Default: FIFO (add to back, process from front)
* - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'
* - Priority: Provide getPriority function; higher values processed first
*
* @example
* ```ts
* // Basic FIFO queue
* const queue = new LiteQueuer((item: string) => {
* console.log('Processing:', item);
* }, { wait: 100 });
*
* queue.addItem('task1');
* queue.addItem('task2');
* // Processes: task1, then task2 after 100ms delay
* ```
*
* @example
* ```ts
* // Priority queue
* const priorityQueue = new LiteQueuer((item: Task) => {
* processTask(item);
* }, {
* getPriority: task => task.priority,
* wait: 500
* });
*
* priorityQueue.addItem({ name: 'low', priority: 1 });
* priorityQueue.addItem({ name: 'high', priority: 10 });
* // Processes high priority task first
* ```
*/
export declare class LiteQueuer<TValue> {
fn: (item: TValue) => void;
options: LiteQueuerOptions<TValue>;
private items;
private timeoutId;
private isRunning;
private pendingTick;
constructor(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>);
/**
* Number of items currently in the queue
*/
get size(): number;
/**
* Whether the queue is empty
*/
get isEmpty(): boolean;
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning(): boolean;
/**
* Adds an item to the queue. If the queue is full, the item is rejected.
* Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.
*
* Returns true if the item was added, false if the queue is full.
*
* @example
* ```ts
* queue.addItem('task1'); // Add to default position (back)
* queue.addItem('task2', 'front'); // Add to front
* ```
*/
addItem: (item: TValue, position?: QueuePosition, startProcessing?: boolean) => boolean;
private insertAtPosition;
/**
* Removes and returns the next item from the queue without executing the function.
* Use for manual queue management. Normally, use execute() to process items.
*
* @example
* ```ts
* const nextItem = queue.getNextItem(); // Get from default position (front)
* const lastItem = queue.getNextItem('back'); // Get from back (LIFO)
* ```
*/
getNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Removes and returns the next item from the queue and processes it using the provided function.
*
* @example
* ```ts
* queue.execute(); // Execute from default position
* queue.execute('back'); // Execute from back (LIFO)
* ```
*/
execute: (position?: QueuePosition) => TValue | undefined;
/**
* Internal method that processes items in the queue with wait intervals
*/
private tick;
/**
* Starts processing items in the queue. If already running, does nothing.
*/
start: () => void;
/**
* Stops processing items in the queue. Does not clear the queue.
*/
stop: () => void;
/**
* Clears any pending timeout
*/
private clearTimeout;
/**
* Returns the next item in the queue without removing it.
*
* @example
* ```ts
* const next = queue.peekNextItem(); // Peek at front
* const last = queue.peekNextItem('back'); // Peek at back
* ```
*/
peekNextItem: (position?: QueuePosition) => TValue | undefined;
/**
* Returns a copy of all items in the queue.
*/
peekAllItems: () => Array<TValue>;
/**
* Processes a specified number of items immediately with no wait time.
* If no numberOfItems is provided, all items will be processed.
*
* @example
* ```ts
* queue.flush(); // Process all items immediately
* queue.flush(3); // Process next 3 items immediately
* ```
*/
flush: (numberOfItems?: number, position?: QueuePosition) => void;
/**
* Processes all items in the queue as a batch using the provided function.
* The queue is cleared after processing.
*
* @example
* ```ts
* queue.flushAsBatch((items) => {
* console.log('Processing batch:', items);
* // Process all items together
* });
* ```
*/
flushAsBatch: (batchFunction: (items: Array<TValue>) => void) => void;
/**
* Removes all items from the queue. Does not affect items being processed.
*/
clear: () => void;
}
/**
* Creates a lightweight queue that processes items using the provided function.
*
* This is an alternative to the queue function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a queuer with no external dependencies, devtools integration, or reactive state.
*
* @example
* ```ts
* const processItem = liteQueue((item: string) => {
* console.log('Processing:', item);
* }, { wait: 1000 });
*
* processItem('task1');
* processItem('task2');
* // Processes each item with 1 second delay between them
* ```
*/
export declare function liteQueue<TValue>(fn: (item: TValue) => void, options?: LiteQueuerOptions<TValue>): (item: TValue) => boolean;
class LiteQueuer {
constructor(fn, options = {}) {
this.fn = fn;
this.options = options;
this.items = [];
this.timeoutId = null;
this.isRunning = true;
this.pendingTick = false;
this.addItem = (item, position = this.options.addItemsTo, startProcessing = true) => {
if (this.items.length >= this.options.maxSize) {
return false;
}
if (this.options.getPriority) {
const priority = this.options.getPriority(item);
if (priority !== void 0) {
const insertIndex = this.items.findIndex((existing) => {
const existingPriority = this.options.getPriority(existing);
const effectivePriority = existingPriority ?? -Infinity;
return effectivePriority < priority;
});
if (insertIndex === -1) {
this.items.push(item);
} else {
this.items.splice(insertIndex, 0, item);
}
} else {
this.insertAtPosition(item, position);
}
} else {
this.insertAtPosition(item, position);
}
if (startProcessing && this.isRunning && !this.pendingTick) {
this.tick();
}
return true;
};
this.insertAtPosition = (item, position) => {
if (position === "front") {
this.items.unshift(item);
} else {
this.items.push(item);
}
};
this.getNextItem = (position = this.options.getItemsFrom) => {
if (this.items.length === 0) {
return void 0;
}
let item;
if (this.options.getPriority || position === "front") {
item = this.items.shift();
} else {
item = this.items.pop();
}
return item;
};
this.execute = (position) => {
const item = this.getNextItem(position);
if (item !== void 0) {
this.fn(item);
}
return item;
};
this.tick = () => {
if (!this.isRunning) {
this.pendingTick = false;
return;
}
this.pendingTick = true;
while (this.items.length > 0) {
const item = this.execute(this.options.getItemsFrom);
if (item === void 0) {
break;
}
const wait = this.options.wait;
if (wait > 0) {
this.timeoutId = setTimeout(() => this.tick(), wait);
return;
}
}
this.pendingTick = false;
};
this.start = () => {
this.isRunning = true;
if (!this.pendingTick && this.items.length > 0) {
this.tick();
}
};
this.stop = () => {
this.clearTimeout();
this.isRunning = false;
this.pendingTick = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
};
this.peekNextItem = (position = "front") => {
if (this.items.length === 0) {
return void 0;
}
if (this.options.getPriority || position === "front") {
return this.items[0];
} else {
return this.items[this.items.length - 1];
}
};
this.peekAllItems = () => {
return [...this.items];
};
this.flush = (numberOfItems = this.items.length, position) => {
this.clearTimeout();
for (let i = 0; i < numberOfItems && this.items.length > 0; i++) {
this.execute(position);
}
if (this.isRunning && this.items.length > 0 && !this.pendingTick) {
this.tick();
}
};
this.flushAsBatch = (batchFunction) => {
const items = this.peekAllItems();
this.clear();
batchFunction(items);
};
this.clear = () => {
this.items = [];
};
this.options.addItemsTo = this.options.addItemsTo ?? "back";
this.options.getItemsFrom = this.options.getItemsFrom ?? "front";
this.options.maxSize = this.options.maxSize ?? Infinity;
this.options.started = this.options.started ?? true;
this.options.wait = this.options.wait ?? 0;
this.isRunning = this.options.started;
if (this.options.initialItems) {
for (const item of this.options.initialItems) {
this.addItem(item, this.options.addItemsTo, false);
}
}
if (this.isRunning && this.items.length > 0) {
this.tick();
}
}
/**
* Number of items currently in the queue
*/
get size() {
return this.items.length;
}
/**
* Whether the queue is empty
*/
get isEmpty() {
return this.items.length === 0;
}
/**
* Whether the queue is currently running (auto-processing items)
*/
get isQueueRunning() {
return this.isRunning;
}
}
function liteQueue(fn, options = {}) {
const queuer = new LiteQueuer(fn, options);
return (item) => queuer.addItem(item);
}
export {
LiteQueuer,
liteQueue
};
//# sourceMappingURL=lite-queuer.js.map
{"version":3,"file":"lite-queuer.js","sources":["../../src/lite-queuer.ts"],"sourcesContent":["/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO for getNextItem)\n * - 'back': Operate on the back of the queue (LIFO for getNextItem)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * Options for configuring a lite queuer instance\n */\nexport interface LiteQueuerOptions<TValue> {\n /**\n * Default position to add items to the queue\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queue\n * Higher priority items will be processed first\n * Return undefined for items that should use positional ordering\n */\n getPriority?: (item: TValue) => number | undefined\n /**\n * Initial items to populate the queue with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queue\n */\n maxSize?: number\n /**\n * Whether the queuer should start processing items immediately\n * @default true\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items\n * @default 0\n */\n wait?: number\n}\n\n/**\n * A lightweight class that creates a queue for processing items.\n *\n * This is an alternative to the Queuer in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Queuer,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * no callbacks, and provides only essential queueing functionality.\n *\n * The queuer supports FIFO (First In First Out), LIFO (Last In First Out), and priority-based\n * processing of items. Items can be processed automatically with configurable wait times\n * between executions, or processed manually using the execute methods.\n *\n * Features included:\n * - Automatic or manual processing of items\n * - FIFO, LIFO, and priority-based ordering\n * - Queue size limits with item rejection\n * - Configurable wait times between processing\n * - Batch processing capabilities\n * - Start/stop processing control\n * - Callback support for monitoring execution, rejection, and state change events\n *\n * Features NOT included (compared to core Queuer):\n * - No TanStack Store state management\n * - No devtools integration\n * - No item expiration functionality (no onExpire callback)\n * - No dynamic options updates (setOptions)\n * - No detailed state tracking (execution counts, etc.)\n *\n * Queue behavior:\n * - Default: FIFO (add to back, process from front)\n * - LIFO: Configure addItemsTo: 'back', getItemsFrom: 'back'\n * - Priority: Provide getPriority function; higher values processed first\n *\n * @example\n * ```ts\n * // Basic FIFO queue\n * const queue = new LiteQueuer((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 100 });\n *\n * queue.addItem('task1');\n * queue.addItem('task2');\n * // Processes: task1, then task2 after 100ms delay\n * ```\n *\n * @example\n * ```ts\n * // Priority queue\n * const priorityQueue = new LiteQueuer((item: Task) => {\n * processTask(item);\n * }, {\n * getPriority: task => task.priority,\n * wait: 500\n * });\n *\n * priorityQueue.addItem({ name: 'low', priority: 1 });\n * priorityQueue.addItem({ name: 'high', priority: 10 });\n * // Processes high priority task first\n * ```\n */\nexport class LiteQueuer<TValue> {\n private items: Array<TValue> = []\n private timeoutId: NodeJS.Timeout | null = null\n private isRunning = true\n private pendingTick = false\n\n constructor(\n public fn: (item: TValue) => void,\n public options: LiteQueuerOptions<TValue> = {},\n ) {\n // Set defaults\n this.options.addItemsTo = this.options.addItemsTo ?? 'back'\n this.options.getItemsFrom = this.options.getItemsFrom ?? 'front'\n this.options.maxSize = this.options.maxSize ?? Infinity\n this.options.started = this.options.started ?? true\n this.options.wait = this.options.wait ?? 0\n\n this.isRunning = this.options.started\n\n // Add initial items if provided\n if (this.options.initialItems) {\n for (const item of this.options.initialItems) {\n this.addItem(item, this.options.addItemsTo, false)\n }\n }\n\n // Start processing if enabled and has items\n if (this.isRunning && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Number of items currently in the queue\n */\n get size(): number {\n return this.items.length\n }\n\n /**\n * Whether the queue is empty\n */\n get isEmpty(): boolean {\n return this.items.length === 0\n }\n\n /**\n * Whether the queue is currently running (auto-processing items)\n */\n get isQueueRunning(): boolean {\n return this.isRunning\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected.\n * Items can be inserted at the front or back, and priority ordering is applied if getPriority is configured.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * @example\n * ```ts\n * queue.addItem('task1'); // Add to default position (back)\n * queue.addItem('task2', 'front'); // Add to front\n * ```\n */\n addItem = (\n item: TValue,\n position: QueuePosition = this.options.addItemsTo!,\n startProcessing: boolean = true,\n ): boolean => {\n // Check size limit\n if (this.items.length >= this.options.maxSize!) {\n return false\n }\n\n // Handle priority insertion\n if (this.options.getPriority) {\n const priority = this.options.getPriority(item)\n if (priority !== undefined) {\n // Find insertion point for priority\n const insertIndex = this.items.findIndex((existing) => {\n const existingPriority = this.options.getPriority!(existing)\n // Treat undefined priority as negative infinity for comparison\n const effectivePriority = existingPriority ?? -Infinity\n return effectivePriority < priority\n })\n\n if (insertIndex === -1) {\n this.items.push(item)\n } else {\n this.items.splice(insertIndex, 0, item)\n }\n } else {\n // No priority, use position\n this.insertAtPosition(item, position)\n }\n } else {\n // No priority function, use position\n this.insertAtPosition(item, position)\n }\n\n // Start processing if running and not already processing\n if (startProcessing && this.isRunning && !this.pendingTick) {\n this.tick()\n }\n\n return true\n }\n\n private insertAtPosition = (item: TValue, position: QueuePosition): void => {\n if (position === 'front') {\n this.items.unshift(item)\n } else {\n this.items.push(item)\n }\n }\n\n /**\n * Removes and returns the next item from the queue without executing the function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * @example\n * ```ts\n * const nextItem = queue.getNextItem(); // Get from default position (front)\n * const lastItem = queue.getNextItem('back'); // Get from back (LIFO)\n * ```\n */\n getNextItem = (\n position: QueuePosition = this.options.getItemsFrom!,\n ): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n let item: TValue | undefined\n\n // When priority function is provided, always get from front (highest priority)\n if (this.options.getPriority || position === 'front') {\n item = this.items.shift()\n } else {\n item = this.items.pop()\n }\n\n return item\n }\n\n /**\n * Removes and returns the next item from the queue and processes it using the provided function.\n *\n * @example\n * ```ts\n * queue.execute(); // Execute from default position\n * queue.execute('back'); // Execute from back (LIFO)\n * ```\n */\n execute = (position?: QueuePosition): TValue | undefined => {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n }\n return item\n }\n\n /**\n * Internal method that processes items in the queue with wait intervals\n */\n private tick = (): void => {\n if (!this.isRunning) {\n this.pendingTick = false\n return\n }\n\n this.pendingTick = true\n\n // Process items while queue is not empty\n while (this.items.length > 0) {\n const item = this.execute(this.options.getItemsFrom)\n if (item === undefined) {\n break\n }\n\n const wait = this.options.wait!\n if (wait > 0) {\n // Schedule next processing after wait time\n this.timeoutId = setTimeout(() => this.tick(), wait)\n return\n }\n\n // No wait time, continue processing immediately\n }\n\n this.pendingTick = false\n }\n\n /**\n * Starts processing items in the queue. If already running, does nothing.\n */\n start = (): void => {\n this.isRunning = true\n if (!this.pendingTick && this.items.length > 0) {\n this.tick()\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop = (): void => {\n this.clearTimeout()\n this.isRunning = false\n this.pendingTick = false\n }\n\n /**\n * Clears any pending timeout\n */\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = null\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * @example\n * ```ts\n * const next = queue.peekNextItem(); // Peek at front\n * const last = queue.peekNextItem('back'); // Peek at back\n * ```\n */\n peekNextItem = (position: QueuePosition = 'front'): TValue | undefined => {\n if (this.items.length === 0) {\n return undefined\n }\n\n if (this.options.getPriority || position === 'front') {\n return this.items[0]\n } else {\n return this.items[this.items.length - 1]\n }\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n peekAllItems = (): Array<TValue> => {\n return [...this.items]\n }\n\n /**\n * Processes a specified number of items immediately with no wait time.\n * If no numberOfItems is provided, all items will be processed.\n *\n * @example\n * ```ts\n * queue.flush(); // Process all items immediately\n * queue.flush(3); // Process next 3 items immediately\n * ```\n */\n flush = (\n numberOfItems: number = this.items.length,\n position?: QueuePosition,\n ): void => {\n this.clearTimeout() // Clear any pending timeout\n for (let i = 0; i < numberOfItems && this.items.length > 0; i++) {\n this.execute(position)\n }\n // Restart normal processing if still running and has items\n if (this.isRunning && this.items.length > 0 && !this.pendingTick) {\n this.tick()\n }\n }\n\n /**\n * Processes all items in the queue as a batch using the provided function.\n * The queue is cleared after processing.\n *\n * @example\n * ```ts\n * queue.flushAsBatch((items) => {\n * console.log('Processing batch:', items);\n * // Process all items together\n * });\n * ```\n */\n flushAsBatch = (batchFunction: (items: Array<TValue>) => void): void => {\n const items = this.peekAllItems()\n this.clear()\n batchFunction(items)\n }\n\n /**\n * Removes all items from the queue. Does not affect items being processed.\n */\n clear = (): void => {\n this.items = []\n }\n}\n\n/**\n * Creates a lightweight queue that processes items using the provided function.\n *\n * This is an alternative to the queue 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 queuer with no external dependencies, devtools integration, or reactive state.\n *\n * @example\n * ```ts\n * const processItem = liteQueue((item: string) => {\n * console.log('Processing:', item);\n * }, { wait: 1000 });\n *\n * processItem('task1');\n * processItem('task2');\n * // Processes each item with 1 second delay between them\n * ```\n */\nexport function liteQueue<TValue>(\n fn: (item: TValue) => void,\n options: LiteQueuerOptions<TValue> = {},\n): (item: TValue) => boolean {\n const queuer = new LiteQueuer(fn, options)\n return (item: TValue) => queuer.addItem(item)\n}\n"],"names":[],"mappings":"AA4GO,MAAM,WAAmB;AAAA,EAM9B,YACS,IACA,UAAqC,IAC5C;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AAPT,SAAQ,QAAuB,CAAA;AAC/B,SAAQ,YAAmC;AAC3C,SAAQ,YAAY;AACpB,SAAQ,cAAc;AA6DtB,SAAA,UAAU,CACR,MACA,WAA0B,KAAK,QAAQ,YACvC,kBAA2B,SACf;AAEZ,UAAI,KAAK,MAAM,UAAU,KAAK,QAAQ,SAAU;AAC9C,eAAO;AAAA,MACT;AAGA,UAAI,KAAK,QAAQ,aAAa;AAC5B,cAAM,WAAW,KAAK,QAAQ,YAAY,IAAI;AAC9C,YAAI,aAAa,QAAW;AAE1B,gBAAM,cAAc,KAAK,MAAM,UAAU,CAAC,aAAa;AACrD,kBAAM,mBAAmB,KAAK,QAAQ,YAAa,QAAQ;AAE3D,kBAAM,oBAAoB,oBAAoB;AAC9C,mBAAO,oBAAoB;AAAA,UAC7B,CAAC;AAED,cAAI,gBAAgB,IAAI;AACtB,iBAAK,MAAM,KAAK,IAAI;AAAA,UACtB,OAAO;AACL,iBAAK,MAAM,OAAO,aAAa,GAAG,IAAI;AAAA,UACxC;AAAA,QACF,OAAO;AAEL,eAAK,iBAAiB,MAAM,QAAQ;AAAA,QACtC;AAAA,MACF,OAAO;AAEL,aAAK,iBAAiB,MAAM,QAAQ;AAAA,MACtC;AAGA,UAAI,mBAAmB,KAAK,aAAa,CAAC,KAAK,aAAa;AAC1D,aAAK,KAAA;AAAA,MACP;AAEA,aAAO;AAAA,IACT;AAEA,SAAQ,mBAAmB,CAAC,MAAc,aAAkC;AAC1E,UAAI,aAAa,SAAS;AACxB,aAAK,MAAM,QAAQ,IAAI;AAAA,MACzB,OAAO;AACL,aAAK,MAAM,KAAK,IAAI;AAAA,MACtB;AAAA,IACF;AAYA,SAAA,cAAc,CACZ,WAA0B,KAAK,QAAQ,iBAChB;AACvB,UAAI,KAAK,MAAM,WAAW,GAAG;AAC3B,eAAO;AAAA,MACT;AAEA,UAAI;AAGJ,UAAI,KAAK,QAAQ,eAAe,aAAa,SAAS;AACpD,eAAO,KAAK,MAAM,MAAA;AAAA,MACpB,OAAO;AACL,eAAO,KAAK,MAAM,IAAA;AAAA,MACpB;AAEA,aAAO;AAAA,IACT;AAWA,SAAA,UAAU,CAAC,aAAiD;AAC1D,YAAM,OAAO,KAAK,YAAY,QAAQ;AACtC,UAAI,SAAS,QAAW;AACtB,aAAK,GAAG,IAAI;AAAA,MACd;AACA,aAAO;AAAA,IACT;AAKA,SAAQ,OAAO,MAAY;AACzB,UAAI,CAAC,KAAK,WAAW;AACnB,aAAK,cAAc;AACnB;AAAA,MACF;AAEA,WAAK,cAAc;AAGnB,aAAO,KAAK,MAAM,SAAS,GAAG;AAC5B,cAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,YAAY;AACnD,YAAI,SAAS,QAAW;AACtB;AAAA,QACF;AAEA,cAAM,OAAO,KAAK,QAAQ;AAC1B,YAAI,OAAO,GAAG;AAEZ,eAAK,YAAY,WAAW,MAAM,KAAK,KAAA,GAAQ,IAAI;AACnD;AAAA,QACF;AAAA,MAGF;AAEA,WAAK,cAAc;AAAA,IACrB;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,YAAY;AACjB,UAAI,CAAC,KAAK,eAAe,KAAK,MAAM,SAAS,GAAG;AAC9C,aAAK,KAAA;AAAA,MACP;AAAA,IACF;AAKA,SAAA,OAAO,MAAY;AACjB,WAAK,aAAA;AACL,WAAK,YAAY;AACjB,WAAK,cAAc;AAAA,IACrB;AAKA,SAAQ,eAAe,MAAY;AACjC,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AAWA,SAAA,eAAe,CAAC,WAA0B,YAAgC;AACxE,UAAI,KAAK,MAAM,WAAW,GAAG;AAC3B,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,QAAQ,eAAe,aAAa,SAAS;AACpD,eAAO,KAAK,MAAM,CAAC;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAAA,MACzC;AAAA,IACF;AAKA,SAAA,eAAe,MAAqB;AAClC,aAAO,CAAC,GAAG,KAAK,KAAK;AAAA,IACvB;AAYA,SAAA,QAAQ,CACN,gBAAwB,KAAK,MAAM,QACnC,aACS;AACT,WAAK,aAAA;AACL,eAAS,IAAI,GAAG,IAAI,iBAAiB,KAAK,MAAM,SAAS,GAAG,KAAK;AAC/D,aAAK,QAAQ,QAAQ;AAAA,MACvB;AAEA,UAAI,KAAK,aAAa,KAAK,MAAM,SAAS,KAAK,CAAC,KAAK,aAAa;AAChE,aAAK,KAAA;AAAA,MACP;AAAA,IACF;AAcA,SAAA,eAAe,CAAC,kBAAwD;AACtE,YAAM,QAAQ,KAAK,aAAA;AACnB,WAAK,MAAA;AACL,oBAAc,KAAK;AAAA,IACrB;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,QAAQ,CAAA;AAAA,IACf;AA/RE,SAAK,QAAQ,aAAa,KAAK,QAAQ,cAAc;AACrD,SAAK,QAAQ,eAAe,KAAK,QAAQ,gBAAgB;AACzD,SAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,SAAK,QAAQ,UAAU,KAAK,QAAQ,WAAW;AAC/C,SAAK,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAEzC,SAAK,YAAY,KAAK,QAAQ;AAG9B,QAAI,KAAK,QAAQ,cAAc;AAC7B,iBAAW,QAAQ,KAAK,QAAQ,cAAc;AAC5C,aAAK,QAAQ,MAAM,KAAK,QAAQ,YAAY,KAAK;AAAA,MACnD;AAAA,IACF;AAGA,QAAI,KAAK,aAAa,KAAK,MAAM,SAAS,GAAG;AAC3C,WAAK,KAAA;AAAA,IACP;AAAA,EACF;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,iBAA0B;AAC5B,WAAO,KAAK;AAAA,EACd;AAwPF;AAoBO,SAAS,UACd,IACA,UAAqC,IACV;AAC3B,QAAM,SAAS,IAAI,WAAW,IAAI,OAAO;AACzC,SAAO,CAAC,SAAiB,OAAO,QAAQ,IAAI;AAC9C;"}
import { AnyFunction } from '@tanstack/pacer/types';
/**
* Options for configuring a lite rate-limited function
*/
export interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Maximum number of executions allowed within the time window.
*/
limit: number;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Optional callback function that is called when an execution is rejected due to rate limiting
*/
onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void;
/**
* Time window in milliseconds within which the limit applies.
*/
window: number;
/**
* Type of window to use for rate limiting
* - 'fixed': Uses a fixed window that resets after the window period
* - 'sliding': Uses a sliding window that allows executions as old ones expire
* Defaults to 'fixed'
*/
windowType?: 'fixed' | 'sliding';
}
/**
* A lightweight class that creates a rate-limited function.
*
* This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential rate limiting functionality.
*
* Rate limiting allows a function to execute up to a limit within a time window,
* then blocks all subsequent calls until the window passes. This can lead to "bursty" behavior where
* all executions happen immediately, followed by a complete block.
*
* The rate limiter supports two types of windows:
* - 'fixed': A strict window that resets after the window period. All executions within the window count
* towards the limit, and the window resets completely after the period.
* - 'sliding': A rolling window that allows executions as old ones expire. This provides a more
* consistent rate of execution over time.
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)
* - Simple state management - uses basic private properties instead of reactive stores
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter((id: string) => {
* api.getData(id);
* }, { limit: 5, window: 1000 });
*
* // First 5 calls will execute, then block until window resets
* if (rateLimiter.maybeExecute('123')) {
* console.log('API call made');
* } else {
* console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');
* }
* ```
*/
export declare class LiteRateLimiter<TFn extends AnyFunction> {
fn: TFn;
options: LiteRateLimiterOptions<TFn>;
private executionTimes;
private timeoutIds;
constructor(fn: TFn, options: LiteRateLimiterOptions<TFn>);
/**
* Attempts to execute the rate-limited function if within the configured limits.
* Returns true if executed, false if rejected due to rate limiting.
*
* @example
* ```ts
* const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });
*
* // First 5 calls return true
* rateLimiter.maybeExecute('arg1', 'arg2'); // true
*
* // Additional calls within the window return false
* rateLimiter.maybeExecute('arg1', 'arg2'); // false
* ```
*/
maybeExecute: (...args: Parameters<TFn>) => boolean;
private execute;
private getExecutionTimesInWindow;
private setCleanupTimeout;
private clearTimeout;
private clearTimeouts;
private cleanupOldExecutions;
/**
* Returns the number of remaining executions allowed in the current window.
*/
getRemainingInWindow: () => number;
/**
* Returns the number of milliseconds until the next execution will be possible.
* Returns 0 if executions are currently allowed.
*/
getMsUntilNextWindow: () => number;
/**
* Resets the rate limiter state, clearing all execution history.
*/
reset: () => void;
}
/**
* Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.
*
* This is an alternative to the rateLimit function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a rate limiter with no external dependencies, devtools integration, or reactive state.
*
* Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.
* This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).
*
* @example
* ```ts
* const rateLimitedApi = liteRateLimit(makeApiCall, {
* limit: 5,
* window: 60000, // 1 minute
* windowType: 'sliding'
* });
*
* // First 5 calls execute immediately
* // Additional calls are rejected until window allows
* rateLimitedApi();
* ```
*
* @example
* ```ts
* // Fixed window - all 10 calls happen in first second, then 10 second wait
* const rateLimitedFixed = liteRateLimit(logEvent, {
* limit: 10,
* window: 10000,
* windowType: 'fixed'
* });
* ```
*/
export declare function liteRateLimit<TFn extends AnyFunction>(fn: TFn, options: LiteRateLimiterOptions<TFn>): (...args: Parameters<TFn>) => boolean;
class LiteRateLimiter {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.executionTimes = [];
this.timeoutIds = /* @__PURE__ */ new Set();
this.maybeExecute = (...args) => {
this.cleanupOldExecutions();
const relevantExecutionTimes = this.getExecutionTimesInWindow();
if (relevantExecutionTimes.length < this.options.limit) {
this.execute(...args);
return true;
}
this.options.onReject?.(this);
return false;
};
this.execute = (...args) => {
const now = Date.now();
this.fn(...args);
this.options.onExecute?.(args, this);
this.executionTimes.push(now);
this.setCleanupTimeout(now);
};
this.getExecutionTimesInWindow = () => {
if (this.options.windowType === "sliding") {
return this.executionTimes.filter(
(time) => time > Date.now() - this.options.window
);
} else {
if (this.executionTimes.length === 0) {
return [];
}
const oldestExecution = Math.min(...this.executionTimes);
const windowStart = oldestExecution;
const windowEnd = windowStart + this.options.window;
const now = Date.now();
if (now > windowEnd) {
return [];
}
return this.executionTimes.filter(
(time) => time >= windowStart && time <= windowEnd
);
}
};
this.setCleanupTimeout = (executionTime) => {
if (this.options.windowType === "sliding" || this.timeoutIds.size === 0) {
const now = Date.now();
const timeUntilExpiration = executionTime - now + this.options.window + 1;
const timeoutId = setTimeout(() => {
this.cleanupOldExecutions();
this.clearTimeout(timeoutId);
}, timeUntilExpiration);
this.timeoutIds.add(timeoutId);
}
};
this.clearTimeout = (timeoutId) => {
clearTimeout(timeoutId);
this.timeoutIds.delete(timeoutId);
};
this.clearTimeouts = () => {
this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId));
this.timeoutIds.clear();
};
this.cleanupOldExecutions = () => {
this.executionTimes = this.getExecutionTimesInWindow();
};
this.getRemainingInWindow = () => {
const relevantExecutionTimes = this.getExecutionTimesInWindow();
return Math.max(0, this.options.limit - relevantExecutionTimes.length);
};
this.getMsUntilNextWindow = () => {
if (this.getRemainingInWindow() > 0) {
return 0;
}
const oldestExecution = this.executionTimes[0] ?? Infinity;
return oldestExecution + this.options.window - Date.now();
};
this.reset = () => {
this.executionTimes = [];
this.clearTimeouts();
};
if (this.options.windowType === void 0) {
this.options.windowType = "fixed";
}
}
}
function liteRateLimit(fn, options) {
const rateLimiter = new LiteRateLimiter(fn, options);
return rateLimiter.maybeExecute;
}
export {
LiteRateLimiter,
liteRateLimit
};
//# sourceMappingURL=lite-rate-limiter.js.map
{"version":3,"file":"lite-rate-limiter.js","sources":["../../src/lite-rate-limiter.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite rate-limited function\n */\nexport interface LiteRateLimiterOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Maximum number of executions allowed within the time window.\n */\n limit: number\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Optional callback function that is called when an execution is rejected due to rate limiting\n */\n onReject?: (rateLimiter: LiteRateLimiter<TFn>) => void\n /**\n * Time window in milliseconds within which the limit applies.\n */\n window: 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 * A lightweight class that creates a rate-limited function.\n *\n * This is an alternative to the RateLimiter in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core RateLimiter,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential rate limiting functionality.\n *\n * Rate limiting 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 *\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 * Features:\n * - Zero dependencies - no external libraries required\n * - Minimal API surface - only essential methods (maybeExecute, getRemainingInWindow, getMsUntilNextWindow, reset)\n * - Simple state management - uses basic private properties instead of reactive stores\n * - Lightweight - designed for use in npm packages where bundle size matters\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter((id: string) => {\n * api.getData(id);\n * }, { limit: 5, window: 1000 });\n *\n * // First 5 calls will execute, then block until window resets\n * if (rateLimiter.maybeExecute('123')) {\n * console.log('API call made');\n * } else {\n * console.log('Rate limited - try again in', rateLimiter.getMsUntilNextWindow(), 'ms');\n * }\n * ```\n */\nexport class LiteRateLimiter<TFn extends AnyFunction> {\n private executionTimes: Array<number> = []\n private timeoutIds: Set<NodeJS.Timeout> = new Set()\n\n constructor(\n public fn: TFn,\n public options: LiteRateLimiterOptions<TFn>,\n ) {\n // Default windowType to 'fixed' if not specified\n if (this.options.windowType === undefined) {\n this.options.windowType = 'fixed'\n }\n }\n\n /**\n * Attempts to execute the rate-limited function if within the configured limits.\n * Returns true if executed, false if rejected due to rate limiting.\n *\n * @example\n * ```ts\n * const rateLimiter = new LiteRateLimiter(fn, { limit: 5, window: 1000 });\n *\n * // First 5 calls return true\n * rateLimiter.maybeExecute('arg1', 'arg2'); // true\n *\n * // Additional calls within the window return false\n * rateLimiter.maybeExecute('arg1', 'arg2'); // false\n * ```\n */\n maybeExecute = (...args: Parameters<TFn>): boolean => {\n this.cleanupOldExecutions()\n\n const relevantExecutionTimes = this.getExecutionTimesInWindow()\n\n if (relevantExecutionTimes.length < this.options.limit) {\n this.execute(...args)\n return true\n }\n\n this.options.onReject?.(this)\n return false\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.executionTimes.push(now)\n this.setCleanupTimeout(now)\n }\n\n private getExecutionTimesInWindow = (): Array<number> => {\n if (this.options.windowType === 'sliding') {\n // For sliding window, return all executions within the current window\n return this.executionTimes.filter(\n (time) => time > Date.now() - this.options.window,\n )\n } else {\n // For fixed window, return all executions in the current window\n if (this.executionTimes.length === 0) {\n return []\n }\n const oldestExecution = Math.min(...this.executionTimes)\n const windowStart = oldestExecution\n const windowEnd = windowStart + this.options.window\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.executionTimes.filter(\n (time) => time >= windowStart && time <= windowEnd,\n )\n }\n }\n\n private 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.options.window + 1\n const timeoutId = setTimeout(() => {\n this.cleanupOldExecutions()\n this.clearTimeout(timeoutId)\n }, timeUntilExpiration)\n this.timeoutIds.add(timeoutId)\n }\n }\n\n private clearTimeout = (timeoutId: NodeJS.Timeout): void => {\n clearTimeout(timeoutId)\n this.timeoutIds.delete(timeoutId)\n }\n\n private clearTimeouts = (): void => {\n this.timeoutIds.forEach((timeoutId) => clearTimeout(timeoutId))\n this.timeoutIds.clear()\n }\n\n private cleanupOldExecutions = (): void => {\n this.executionTimes = this.getExecutionTimesInWindow()\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.options.limit - relevantExecutionTimes.length)\n }\n\n /**\n * Returns the number of milliseconds until the next execution will be possible.\n * Returns 0 if executions are currently allowed.\n */\n getMsUntilNextWindow = (): number => {\n if (this.getRemainingInWindow() > 0) {\n return 0\n }\n const oldestExecution = this.executionTimes[0] ?? Infinity\n return oldestExecution + this.options.window - Date.now()\n }\n\n /**\n * Resets the rate limiter state, clearing all execution history.\n */\n reset = (): void => {\n this.executionTimes = []\n this.clearTimeouts()\n }\n}\n\n/**\n * Creates a lightweight rate-limited function that will execute the provided function up to a maximum number of times within a time window.\n *\n * This is an alternative to the rateLimit 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 rate limiter with no external dependencies, devtools integration, or reactive state.\n *\n * Rate limiting allows all executions until the limit is reached, then blocks all subsequent calls until the window resets.\n * This differs from throttling (which ensures even spacing) and debouncing (which waits for pauses).\n *\n * @example\n * ```ts\n * const rateLimitedApi = liteRateLimit(makeApiCall, {\n * limit: 5,\n * window: 60000, // 1 minute\n * windowType: 'sliding'\n * });\n *\n * // First 5 calls execute immediately\n * // Additional calls are rejected until window allows\n * rateLimitedApi();\n * ```\n *\n * @example\n * ```ts\n * // Fixed window - all 10 calls happen in first second, then 10 second wait\n * const rateLimitedFixed = liteRateLimit(logEvent, {\n * limit: 10,\n * window: 10000,\n * windowType: 'fixed'\n * });\n * ```\n */\nexport function liteRateLimit<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteRateLimiterOptions<TFn>,\n): (...args: Parameters<TFn>) => boolean {\n const rateLimiter = new LiteRateLimiter(fn, options)\n return rateLimiter.maybeExecute\n}\n"],"names":[],"mappings":"AAqEO,MAAM,gBAAyC;AAAA,EAIpD,YACS,IACA,SACP;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AALT,SAAQ,iBAAgC,CAAA;AACxC,SAAQ,iCAAsC,IAAA;AA2B9C,SAAA,eAAe,IAAI,SAAmC;AACpD,WAAK,qBAAA;AAEL,YAAM,yBAAyB,KAAK,0BAAA;AAEpC,UAAI,uBAAuB,SAAS,KAAK,QAAQ,OAAO;AACtD,aAAK,QAAQ,GAAG,IAAI;AACpB,eAAO;AAAA,MACT;AAEA,WAAK,QAAQ,WAAW,IAAI;AAC5B,aAAO;AAAA,IACT;AAEA,SAAQ,UAAU,IAAI,SAAgC;AACpD,YAAM,MAAM,KAAK,IAAA;AACjB,WAAK,GAAG,GAAG,IAAI;AACf,WAAK,QAAQ,YAAY,MAAM,IAAI;AACnC,WAAK,eAAe,KAAK,GAAG;AAC5B,WAAK,kBAAkB,GAAG;AAAA,IAC5B;AAEA,SAAQ,4BAA4B,MAAqB;AACvD,UAAI,KAAK,QAAQ,eAAe,WAAW;AAEzC,eAAO,KAAK,eAAe;AAAA,UACzB,CAAC,SAAS,OAAO,KAAK,IAAA,IAAQ,KAAK,QAAQ;AAAA,QAAA;AAAA,MAE/C,OAAO;AAEL,YAAI,KAAK,eAAe,WAAW,GAAG;AACpC,iBAAO,CAAA;AAAA,QACT;AACA,cAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,cAAc;AACvD,cAAM,cAAc;AACpB,cAAM,YAAY,cAAc,KAAK,QAAQ;AAC7C,cAAM,MAAM,KAAK,IAAA;AAGjB,YAAI,MAAM,WAAW;AACnB,iBAAO,CAAA;AAAA,QACT;AAGA,eAAO,KAAK,eAAe;AAAA,UACzB,CAAC,SAAS,QAAQ,eAAe,QAAQ;AAAA,QAAA;AAAA,MAE7C;AAAA,IACF;AAEA,SAAQ,oBAAoB,CAAC,kBAAgC;AAC3D,UACE,KAAK,QAAQ,eAAe,aAC5B,KAAK,WAAW,SAAS,GACzB;AACA,cAAM,MAAM,KAAK,IAAA;AACjB,cAAM,sBAAsB,gBAAgB,MAAM,KAAK,QAAQ,SAAS;AACxE,cAAM,YAAY,WAAW,MAAM;AACjC,eAAK,qBAAA;AACL,eAAK,aAAa,SAAS;AAAA,QAC7B,GAAG,mBAAmB;AACtB,aAAK,WAAW,IAAI,SAAS;AAAA,MAC/B;AAAA,IACF;AAEA,SAAQ,eAAe,CAAC,cAAoC;AAC1D,mBAAa,SAAS;AACtB,WAAK,WAAW,OAAO,SAAS;AAAA,IAClC;AAEA,SAAQ,gBAAgB,MAAY;AAClC,WAAK,WAAW,QAAQ,CAAC,cAAc,aAAa,SAAS,CAAC;AAC9D,WAAK,WAAW,MAAA;AAAA,IAClB;AAEA,SAAQ,uBAAuB,MAAY;AACzC,WAAK,iBAAiB,KAAK,0BAAA;AAAA,IAC7B;AAKA,SAAA,uBAAuB,MAAc;AACnC,YAAM,yBAAyB,KAAK,0BAAA;AACpC,aAAO,KAAK,IAAI,GAAG,KAAK,QAAQ,QAAQ,uBAAuB,MAAM;AAAA,IACvE;AAMA,SAAA,uBAAuB,MAAc;AACnC,UAAI,KAAK,qBAAA,IAAyB,GAAG;AACnC,eAAO;AAAA,MACT;AACA,YAAM,kBAAkB,KAAK,eAAe,CAAC,KAAK;AAClD,aAAO,kBAAkB,KAAK,QAAQ,SAAS,KAAK,IAAA;AAAA,IACtD;AAKA,SAAA,QAAQ,MAAY;AAClB,WAAK,iBAAiB,CAAA;AACtB,WAAK,cAAA;AAAA,IACP;AA7HE,QAAI,KAAK,QAAQ,eAAe,QAAW;AACzC,WAAK,QAAQ,aAAa;AAAA,IAC5B;AAAA,EACF;AA2HF;AAmCO,SAAS,cACd,IACA,SACuC;AACvC,QAAM,cAAc,IAAI,gBAAgB,IAAI,OAAO;AACnD,SAAO,YAAY;AACrB;"}
import { AnyFunction } from '@tanstack/pacer/types';
/**
* Options for configuring a lite throttled function
*/
export interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {
/**
* Whether to execute on the leading edge of the timeout.
* Defaults to true.
*/
leading?: boolean;
/**
* Callback function that is called after the function is executed
*/
onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void;
/**
* Whether to execute on the trailing edge of the timeout.
* Defaults to true.
*/
trailing?: boolean;
/**
* Time window in milliseconds during which the function can only be executed once.
*/
wait: number;
}
/**
* A lightweight class that creates a throttled function.
*
* This is an alternative to the Throttler in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,
* this version does not use TanStack Store for state management, has no devtools integration,
* and provides only essential throttling functionality.
*
* Throttling ensures a function is called at most once within a specified time window.
* Unlike debouncing which waits for a pause in calls, throttling guarantees consistent
* execution timing regardless of call frequency.
*
* Supports both leading and trailing edge execution:
* - Leading: Execute immediately on first call (default: true)
* - Trailing: Execute after wait period if called during throttle (default: true)
*
* Features:
* - Zero dependencies - no external libraries required
* - Minimal API surface - only essential methods (maybeExecute, flush, cancel)
* - Simple state management - uses basic private properties instead of reactive stores
* - Callback support for monitoring execution events
* - Lightweight - designed for use in npm packages where bundle size matters
*
* @example
* ```ts
* const throttler = new LiteThrottler((scrollY: number) => {
* updateScrollPosition(scrollY);
* }, {
* wait: 100,
* onExecute: (args, throttler) => {
* console.log('Updated scroll position:', args[0]);
* }
* });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', () => {
* throttler.maybeExecute(window.scrollY);
* });
* ```
*/
export declare class LiteThrottler<TFn extends AnyFunction> {
fn: TFn;
options: LiteThrottlerOptions<TFn>;
private timeoutId;
private lastArgs;
private lastExecutionTime;
private isPending;
constructor(fn: TFn, options: LiteThrottlerOptions<TFn>);
/**
* Attempts to execute the throttled function. The execution behavior depends on the throttler options:
*
* - If enough time has passed since the last execution (>= wait period):
* - With leading=true: Executes immediately
* - With leading=false: Waits for the next trailing execution
*
* - If within the wait period:
* - With trailing=true: Schedules execution for end of wait period
* - With trailing=false: Drops the execution
*/
maybeExecute: (...args: Parameters<TFn>) => void;
private execute;
/**
* Processes the current pending execution immediately.
* If there's a pending execution, it will be executed right away
* and the timeout will be cleared.
*/
flush: () => void;
/**
* Cancels any pending trailing execution and clears internal state.
* If a trailing execution is scheduled, this will prevent that execution from occurring.
*/
cancel: () => void;
private clearTimeout;
}
/**
* Creates a lightweight throttled function that limits how often the provided function can execute.
*
* This is an alternative to the throttle function in the core @tanstack/pacer package, but is more
* suitable for libraries and npm packages that need minimal overhead. Unlike the core version,
* this function creates a throttler with no external dependencies, devtools integration, or reactive state.
*
* Throttling ensures a function executes at most once within a specified time window,
* regardless of how many times it is called. This is useful for rate-limiting
* expensive operations or UI updates.
*
* @example
* ```ts
* const throttledScroll = liteThrottle(() => {
* updateScrollIndicator();
* }, { wait: 100 });
*
* // Will execute at most once per 100ms
* window.addEventListener('scroll', throttledScroll);
* ```
*
* @example
* ```ts
* // Leading edge execution - fires immediately then throttles
* const throttledResize = liteThrottle(() => {
* recalculateLayout();
* }, { wait: 250, leading: true, trailing: false });
* ```
*/
export declare function liteThrottle<TFn extends AnyFunction>(fn: TFn, options: LiteThrottlerOptions<TFn>): (...args: Parameters<TFn>) => void;
class LiteThrottler {
constructor(fn, options) {
this.fn = fn;
this.options = options;
this.lastExecutionTime = 0;
this.isPending = false;
this.maybeExecute = (...args) => {
const now = Date.now();
const timeSinceLastExecution = now - this.lastExecutionTime;
if (this.options.leading && timeSinceLastExecution >= this.options.wait) {
this.execute(...args);
} else {
this.lastArgs = args;
if (!this.timeoutId && this.options.trailing) {
const timeoutDuration = this.options.wait - timeSinceLastExecution;
this.isPending = true;
this.timeoutId = setTimeout(() => {
if (this.lastArgs !== void 0) {
this.execute(...this.lastArgs);
}
}, timeoutDuration);
}
}
};
this.execute = (...args) => {
this.fn(...args);
this.options.onExecute?.(args, this);
this.lastExecutionTime = Date.now();
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.flush = () => {
if (this.isPending && this.lastArgs) {
this.execute(...this.lastArgs);
}
};
this.cancel = () => {
this.clearTimeout();
this.lastArgs = void 0;
this.isPending = false;
};
this.clearTimeout = () => {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = void 0;
}
};
if (this.options.leading === void 0 && this.options.trailing === void 0) {
this.options.leading = true;
this.options.trailing = true;
}
}
}
function liteThrottle(fn, options) {
const throttler = new LiteThrottler(fn, options);
return throttler.maybeExecute;
}
export {
LiteThrottler,
liteThrottle
};
//# sourceMappingURL=lite-throttler.js.map
{"version":3,"file":"lite-throttler.js","sources":["../../src/lite-throttler.ts"],"sourcesContent":["import type { AnyFunction } from '@tanstack/pacer/types'\n\n/**\n * Options for configuring a lite throttled function\n */\nexport interface LiteThrottlerOptions<TFn extends AnyFunction = AnyFunction> {\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to true.\n */\n leading?: boolean\n /**\n * Callback function that is called after the function is executed\n */\n onExecute?: (args: Parameters<TFn>, throttler: LiteThrottler<TFn>) => void\n /**\n * Whether to execute on the trailing edge of the timeout.\n * Defaults to true.\n */\n trailing?: boolean\n /**\n * Time window in milliseconds during which the function can only be executed once.\n */\n wait: number\n}\n\n/**\n * A lightweight class that creates a throttled function.\n *\n * This is an alternative to the Throttler in the core @tanstack/pacer package, but is more\n * suitable for libraries and npm packages that need minimal overhead. Unlike the core Throttler,\n * this version does not use TanStack Store for state management, has no devtools integration,\n * and provides only essential throttling functionality.\n *\n * Throttling ensures a function is called at most once within a specified time window.\n * Unlike debouncing which waits for a pause in calls, throttling guarantees consistent\n * execution timing regardless of call frequency.\n *\n * Supports both leading and trailing edge execution:\n * - Leading: Execute immediately on first call (default: true)\n * - Trailing: Execute after wait period if called during throttle (default: true)\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 throttler = new LiteThrottler((scrollY: number) => {\n * updateScrollPosition(scrollY);\n * }, {\n * wait: 100,\n * onExecute: (args, throttler) => {\n * console.log('Updated scroll position:', args[0]);\n * }\n * });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', () => {\n * throttler.maybeExecute(window.scrollY);\n * });\n * ```\n */\nexport class LiteThrottler<TFn extends AnyFunction> {\n private timeoutId: NodeJS.Timeout | undefined\n private lastArgs: Parameters<TFn> | undefined\n private lastExecutionTime = 0\n private isPending = false\n\n constructor(\n public fn: TFn,\n public options: LiteThrottlerOptions<TFn>,\n ) {\n // Default both leading and trailing to true if neither is specified\n if (\n this.options.leading === undefined &&\n this.options.trailing === undefined\n ) {\n this.options.leading = true\n this.options.trailing = true\n }\n }\n\n /**\n * Attempts to execute the throttled function. The execution behavior depends on the throttler options:\n *\n * - If enough time has passed since the last execution (>= wait period):\n * - With leading=true: Executes immediately\n * - With leading=false: Waits for the next trailing execution\n *\n * - If within the wait period:\n * - With trailing=true: Schedules execution for end of wait period\n * - With trailing=false: Drops the execution\n */\n maybeExecute = (...args: Parameters<TFn>): void => {\n const now = Date.now()\n const timeSinceLastExecution = now - this.lastExecutionTime\n\n // Handle leading execution\n if (this.options.leading && timeSinceLastExecution >= this.options.wait) {\n this.execute(...args)\n } else {\n // Store the most recent arguments for potential trailing execution\n this.lastArgs = args\n\n // Set up trailing execution if not already scheduled\n if (!this.timeoutId && this.options.trailing) {\n const timeoutDuration = this.options.wait - timeSinceLastExecution\n this.isPending = true\n this.timeoutId = setTimeout(() => {\n if (this.lastArgs !== undefined) {\n this.execute(...this.lastArgs)\n }\n }, timeoutDuration)\n }\n }\n }\n\n private execute = (...args: Parameters<TFn>): void => {\n this.fn(...args)\n this.options.onExecute?.(args, this)\n this.lastExecutionTime = Date.now()\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\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.isPending && this.lastArgs) {\n this.execute(...this.lastArgs)\n }\n }\n\n /**\n * Cancels any pending trailing execution and clears internal state.\n * If a trailing execution is scheduled, this will prevent that execution from occurring.\n */\n cancel = (): void => {\n this.clearTimeout()\n this.lastArgs = undefined\n this.isPending = false\n }\n\n private clearTimeout = (): void => {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId)\n this.timeoutId = undefined\n }\n }\n}\n\n/**\n * Creates a lightweight throttled function that limits how often the provided function can execute.\n *\n * This is an alternative to the throttle 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 throttler with no external dependencies, devtools integration, or reactive state.\n *\n * Throttling ensures a function executes at most once within a specified time window,\n * regardless of how many times it is called. This is useful for rate-limiting\n * expensive operations or UI updates.\n *\n * @example\n * ```ts\n * const throttledScroll = liteThrottle(() => {\n * updateScrollIndicator();\n * }, { wait: 100 });\n *\n * // Will execute at most once per 100ms\n * window.addEventListener('scroll', throttledScroll);\n * ```\n *\n * @example\n * ```ts\n * // Leading edge execution - fires immediately then throttles\n * const throttledResize = liteThrottle(() => {\n * recalculateLayout();\n * }, { wait: 250, leading: true, trailing: false });\n * ```\n */\nexport function liteThrottle<TFn extends AnyFunction>(\n fn: TFn,\n options: LiteThrottlerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const throttler = new LiteThrottler(fn, options)\n return throttler.maybeExecute\n}\n"],"names":[],"mappings":"AAkEO,MAAM,cAAuC;AAAA,EAMlD,YACS,IACA,SACP;AAFO,SAAA,KAAA;AACA,SAAA,UAAA;AALT,SAAQ,oBAAoB;AAC5B,SAAQ,YAAY;AA2BpB,SAAA,eAAe,IAAI,SAAgC;AACjD,YAAM,MAAM,KAAK,IAAA;AACjB,YAAM,yBAAyB,MAAM,KAAK;AAG1C,UAAI,KAAK,QAAQ,WAAW,0BAA0B,KAAK,QAAQ,MAAM;AACvE,aAAK,QAAQ,GAAG,IAAI;AAAA,MACtB,OAAO;AAEL,aAAK,WAAW;AAGhB,YAAI,CAAC,KAAK,aAAa,KAAK,QAAQ,UAAU;AAC5C,gBAAM,kBAAkB,KAAK,QAAQ,OAAO;AAC5C,eAAK,YAAY;AACjB,eAAK,YAAY,WAAW,MAAM;AAChC,gBAAI,KAAK,aAAa,QAAW;AAC/B,mBAAK,QAAQ,GAAG,KAAK,QAAQ;AAAA,YAC/B;AAAA,UACF,GAAG,eAAe;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAEA,SAAQ,UAAU,IAAI,SAAgC;AACpD,WAAK,GAAG,GAAG,IAAI;AACf,WAAK,QAAQ,YAAY,MAAM,IAAI;AACnC,WAAK,oBAAoB,KAAK,IAAA;AAC9B,WAAK,aAAA;AACL,WAAK,WAAW;AAChB,WAAK,YAAY;AAAA,IACnB;AAOA,SAAA,QAAQ,MAAY;AAClB,UAAI,KAAK,aAAa,KAAK,UAAU;AACnC,aAAK,QAAQ,GAAG,KAAK,QAAQ;AAAA,MAC/B;AAAA,IACF;AAMA,SAAA,SAAS,MAAY;AACnB,WAAK,aAAA;AACL,WAAK,WAAW;AAChB,WAAK,YAAY;AAAA,IACnB;AAEA,SAAQ,eAAe,MAAY;AACjC,UAAI,KAAK,WAAW;AAClB,qBAAa,KAAK,SAAS;AAC3B,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AA/EE,QACE,KAAK,QAAQ,YAAY,UACzB,KAAK,QAAQ,aAAa,QAC1B;AACA,WAAK,QAAQ,UAAU;AACvB,WAAK,QAAQ,WAAW;AAAA,IAC1B;AAAA,EACF;AAyEF;AA+BO,SAAS,aACd,IACA,SACoC;AACpC,QAAM,YAAY,IAAI,cAAc,IAAI,OAAO;AAC/C,SAAO,UAAU;AACnB;"}