@tanstack/pacer-lite
Advanced tools
| 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;"} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
266414
8.16%3619
18.73%6
-14.29%43
-4.44%1
Infinity%