@tanstack/pacer 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/cjs/async-debouncer.cjs +32 -17
  2. package/dist/cjs/async-debouncer.cjs.map +1 -1
  3. package/dist/cjs/async-debouncer.d.cts +51 -9
  4. package/dist/cjs/async-queuer.cjs +189 -191
  5. package/dist/cjs/async-queuer.cjs.map +1 -1
  6. package/dist/cjs/async-queuer.d.cts +151 -94
  7. package/dist/cjs/async-rate-limiter.cjs +23 -13
  8. package/dist/cjs/async-rate-limiter.cjs.map +1 -1
  9. package/dist/cjs/async-rate-limiter.d.cts +54 -5
  10. package/dist/cjs/async-throttler.cjs +38 -17
  11. package/dist/cjs/async-throttler.cjs.map +1 -1
  12. package/dist/cjs/async-throttler.d.cts +51 -8
  13. package/dist/cjs/batcher.cjs +138 -0
  14. package/dist/cjs/batcher.cjs.map +1 -0
  15. package/dist/cjs/batcher.d.cts +149 -0
  16. package/dist/cjs/debouncer.cjs +3 -4
  17. package/dist/cjs/debouncer.cjs.map +1 -1
  18. package/dist/cjs/debouncer.d.cts +1 -2
  19. package/dist/cjs/index.cjs +3 -0
  20. package/dist/cjs/index.cjs.map +1 -1
  21. package/dist/cjs/index.d.cts +1 -0
  22. package/dist/cjs/queuer.cjs +68 -41
  23. package/dist/cjs/queuer.cjs.map +1 -1
  24. package/dist/cjs/queuer.d.cts +123 -92
  25. package/dist/cjs/rate-limiter.cjs +3 -4
  26. package/dist/cjs/rate-limiter.cjs.map +1 -1
  27. package/dist/cjs/rate-limiter.d.cts +1 -2
  28. package/dist/cjs/throttler.cjs +3 -4
  29. package/dist/cjs/throttler.cjs.map +1 -1
  30. package/dist/cjs/throttler.d.cts +1 -2
  31. package/dist/cjs/types.d.cts +1 -0
  32. package/dist/esm/async-debouncer.d.ts +51 -9
  33. package/dist/esm/async-debouncer.js +32 -17
  34. package/dist/esm/async-debouncer.js.map +1 -1
  35. package/dist/esm/async-queuer.d.ts +151 -94
  36. package/dist/esm/async-queuer.js +189 -191
  37. package/dist/esm/async-queuer.js.map +1 -1
  38. package/dist/esm/async-rate-limiter.d.ts +54 -5
  39. package/dist/esm/async-rate-limiter.js +23 -13
  40. package/dist/esm/async-rate-limiter.js.map +1 -1
  41. package/dist/esm/async-throttler.d.ts +51 -8
  42. package/dist/esm/async-throttler.js +38 -17
  43. package/dist/esm/async-throttler.js.map +1 -1
  44. package/dist/esm/batcher.d.ts +149 -0
  45. package/dist/esm/batcher.js +138 -0
  46. package/dist/esm/batcher.js.map +1 -0
  47. package/dist/esm/debouncer.d.ts +1 -2
  48. package/dist/esm/debouncer.js +3 -4
  49. package/dist/esm/debouncer.js.map +1 -1
  50. package/dist/esm/index.d.ts +1 -0
  51. package/dist/esm/index.js +3 -0
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/queuer.d.ts +123 -92
  54. package/dist/esm/queuer.js +68 -41
  55. package/dist/esm/queuer.js.map +1 -1
  56. package/dist/esm/rate-limiter.d.ts +1 -2
  57. package/dist/esm/rate-limiter.js +3 -4
  58. package/dist/esm/rate-limiter.js.map +1 -1
  59. package/dist/esm/throttler.d.ts +1 -2
  60. package/dist/esm/throttler.js +3 -4
  61. package/dist/esm/throttler.js.map +1 -1
  62. package/dist/esm/types.d.ts +1 -0
  63. package/package.json +11 -1
  64. package/src/async-debouncer.ts +76 -20
  65. package/src/async-queuer.ts +309 -275
  66. package/src/async-rate-limiter.ts +73 -16
  67. package/src/async-throttler.ts +84 -20
  68. package/src/batcher.ts +253 -0
  69. package/src/debouncer.ts +3 -4
  70. package/src/index.ts +1 -0
  71. package/src/queuer.ts +142 -98
  72. package/src/rate-limiter.ts +3 -4
  73. package/src/throttler.ts +3 -4
  74. package/src/types.ts +3 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Options for configuring a Batcher instance
3
+ */
4
+ export interface BatcherOptions<TValue> {
5
+ /**
6
+ * Custom function to determine if a batch should be processed
7
+ * Return true to process the batch immediately
8
+ */
9
+ getShouldExecute?: (items: Array<TValue>, batcher: Batcher<TValue>) => boolean;
10
+ /**
11
+ * Maximum number of items in a batch
12
+ * @default Infinity
13
+ */
14
+ maxSize?: number;
15
+ /**
16
+ * Callback fired after a batch is processed
17
+ */
18
+ onExecute?: (batcher: Batcher<TValue>) => void;
19
+ /**
20
+ * Callback fired when the batcher's running state changes
21
+ */
22
+ onIsRunningChange?: (batcher: Batcher<TValue>) => void;
23
+ /**
24
+ * Callback fired after items are added to the batcher
25
+ */
26
+ onItemsChange?: (batcher: Batcher<TValue>) => void;
27
+ /**
28
+ * Whether the batcher should start processing immediately
29
+ * @default true
30
+ */
31
+ started?: boolean;
32
+ /**
33
+ * Maximum time in milliseconds to wait before processing a batch.
34
+ * If the wait duration has elapsed, the batch will be processed.
35
+ * If not provided, the batch will not be triggered by a timeout.
36
+ * @default Infinity
37
+ */
38
+ wait?: number;
39
+ }
40
+ /**
41
+ * A class that collects items and processes them in batches.
42
+ *
43
+ * Batching is a technique for grouping multiple operations together to be processed as a single unit.
44
+ *
45
+ * The Batcher provides a flexible way to implement batching with configurable:
46
+ * - Maximum batch size (number of items per batch)
47
+ * - Time-based batching (process after X milliseconds)
48
+ * - Custom batch processing logic via getShouldExecute
49
+ * - Event callbacks for monitoring batch operations
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const batcher = new Batcher<number>(
54
+ * (items) => console.log('Processing batch:', items),
55
+ * {
56
+ * maxSize: 5,
57
+ * wait: 2000,
58
+ * onExecuteBatch: (items) => console.log('Batch executed:', items)
59
+ * }
60
+ * );
61
+ *
62
+ * batcher.addItem(1);
63
+ * batcher.addItem(2);
64
+ * // After 2 seconds or when 5 items are added, whichever comes first,
65
+ * // the batch will be processed
66
+ * // batcher.execute() // manually trigger a batch
67
+ * ```
68
+ */
69
+ export declare class Batcher<TValue> {
70
+ private fn;
71
+ private _options;
72
+ private _batchExecutionCount;
73
+ private _itemExecutionCount;
74
+ private _items;
75
+ private _running;
76
+ private _timeoutId;
77
+ constructor(fn: (items: Array<TValue>) => void, initialOptions: BatcherOptions<TValue>);
78
+ /**
79
+ * Updates the batcher options
80
+ */
81
+ setOptions(newOptions: Partial<BatcherOptions<TValue>>): void;
82
+ /**
83
+ * Returns the current batcher options
84
+ */
85
+ getOptions(): BatcherOptions<TValue>;
86
+ /**
87
+ * Adds an item to the batcher
88
+ * If the batch size is reached, timeout occurs, or shouldProcess returns true, the batch will be processed
89
+ */
90
+ addItem(item: TValue): void;
91
+ /**
92
+ * Processes the current batch of items.
93
+ * This method will automatically be triggered if the batcher is running and any of these conditions are met:
94
+ * - The number of items reaches batchSize
95
+ * - The wait duration has elapsed
96
+ * - The getShouldExecute function returns true upon adding an item
97
+ *
98
+ * You can also call this method manually to process the current batch at any time.
99
+ */
100
+ execute(): void;
101
+ /**
102
+ * Stops the batcher from processing batches
103
+ */
104
+ stop(): void;
105
+ /**
106
+ * Starts the batcher and processes any pending items
107
+ */
108
+ start(): void;
109
+ /**
110
+ * Returns the current number of items in the batcher
111
+ */
112
+ getSize(): number;
113
+ /**
114
+ * Returns true if the batcher is empty
115
+ */
116
+ getIsEmpty(): boolean;
117
+ /**
118
+ * Returns true if the batcher is running
119
+ */
120
+ getIsRunning(): boolean;
121
+ /**
122
+ * Returns a copy of all items currently in the batcher
123
+ */
124
+ getAllItems(): Array<TValue>;
125
+ /**
126
+ * Returns the number of times batches have been processed
127
+ */
128
+ getBatchExecutionCount(): number;
129
+ /**
130
+ * Returns the total number of individual items that have been processed
131
+ */
132
+ getItemExecutionCount(): number;
133
+ }
134
+ /**
135
+ * Creates a batcher that processes items in batches
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * const batchItems = batch<number>({
140
+ * batchSize: 3,
141
+ * processBatch: (items) => console.log('Processing:', items)
142
+ * });
143
+ *
144
+ * batchItems(1);
145
+ * batchItems(2);
146
+ * batchItems(3); // Triggers batch processing
147
+ * ```
148
+ */
149
+ export declare function batch<TValue>(fn: (items: Array<TValue>) => void, options: BatcherOptions<TValue>): (item: TValue) => void;
@@ -22,7 +22,6 @@ class Debouncer {
22
22
  }
23
23
  /**
24
24
  * Updates the debouncer options
25
- * Returns the new options state
26
25
  */
27
26
  setOptions(newOptions) {
28
27
  this._options = { ...this._options, ...newOptions };
@@ -57,7 +56,7 @@ class Debouncer {
57
56
  if (this._options.leading && this._canLeadingExecute) {
58
57
  this._canLeadingExecute = false;
59
58
  _didLeadingExecute = true;
60
- this.executeFunction(...args);
59
+ this.execute(...args);
61
60
  }
62
61
  if (this._options.trailing) {
63
62
  this._isPending = true;
@@ -66,11 +65,11 @@ class Debouncer {
66
65
  this._timeoutId = setTimeout(() => {
67
66
  this._canLeadingExecute = true;
68
67
  if (this._options.trailing && !_didLeadingExecute) {
69
- this.executeFunction(...args);
68
+ this.execute(...args);
70
69
  }
71
70
  }, this.getWait());
72
71
  }
73
- executeFunction(...args) {
72
+ execute(...args) {
74
73
  if (!this.getEnabled()) return void 0;
75
74
  this.fn(...args);
76
75
  this._isPending = false;
@@ -1 +1 @@
1
- {"version":3,"file":"debouncer.cjs","sources":["../../src/debouncer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyFunction } from './types'\n\n/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions<TFn extends AnyFunction> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: Debouncer<TFn>) => boolean)\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?: (debouncer: Debouncer<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 * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: Debouncer<TFn>) => number)\n}\n\nconst defaultOptions: Required<DebouncerOptions<any>> = {\n enabled: true,\n leading: false,\n onExecute: () => {},\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates a debounced function.\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 * @example\n * ```ts\n * const debouncer = new Debouncer((value: string) => {\n * saveToDatabase(value);\n * }, { wait: 500 });\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 Debouncer<TFn extends AnyFunction> {\n private _canLeadingExecute = true\n private _executionCount = 0\n private _isPending = false\n private _options: Required<DebouncerOptions<TFn>>\n private _timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the debouncer options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<DebouncerOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this._isPending = false\n }\n }\n\n /**\n * Returns the current debouncer options\n */\n getOptions(): Required<DebouncerOptions<TFn>> {\n return this._options\n }\n\n /**\n * Returns the current enabled state of the debouncer\n */\n getEnabled(): boolean {\n return parseFunctionOrValue(this._options.enabled, this)\n }\n\n /**\n * Returns the current wait time in milliseconds\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function\n * If a call is already in progress, it will be queued\n */\n maybeExecute(...args: Parameters<TFn>): void {\n let _didLeadingExecute = false\n\n // Handle leading execution\n if (this._options.leading && this._canLeadingExecute) {\n this._canLeadingExecute = false\n _didLeadingExecute = true\n this.executeFunction(...args)\n }\n\n // Start pending state to indicate that the debouncer is waiting for the trailing edge\n if (this._options.trailing) {\n this._isPending = true\n }\n\n // Clear any existing timeout\n if (this._timeoutId) clearTimeout(this._timeoutId)\n\n // Set new timeout that will reset canLeadingExecute and execute trailing only if enabled and did not execute leading\n this._timeoutId = setTimeout(() => {\n this._canLeadingExecute = true\n if (this._options.trailing && !_didLeadingExecute) {\n this.executeFunction(...args)\n }\n }, this.getWait())\n }\n\n private executeFunction(...args: Parameters<TFn>): void {\n if (!this.getEnabled()) return undefined\n this.fn(...args) // EXECUTE!\n this._isPending = false\n this._executionCount++\n this._options.onExecute(this)\n }\n\n /**\n * Cancels any pending execution\n */\n cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._canLeadingExecute = true\n this._isPending = false\n }\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns `true` if debouncing\n */\n getIsPending(): boolean {\n return this.getEnabled() && this._isPending\n }\n}\n\n/**\n * Creates a 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 the the simple function wrapper implementation pulled from the Debouncer class. If you need\n * more control over the debouncing behavior, use the Debouncer class directly.\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 debounced = debounce(() => {\n * saveChanges();\n * }, { wait: 1000 });\n *\n * // Called repeatedly but executes at most once per second\n * inputElement.addEventListener('input', debounced);\n * ```\n */\nexport function debounce<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute.bind(debouncer)\n}\n"],"names":["parseFunctionOrValue"],"mappings":";;;AAoCA,MAAM,iBAAkD;AAAA,EACtD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,UAAU;AAAA,EACV,MAAM;AACR;AAyBO,MAAM,UAAmC;AAAA,EAO9C,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AAPV,SAAQ,qBAAqB;AAC7B,SAAQ,kBAAkB;AAC1B,SAAQ,aAAa;AAQnB,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WAAW,YAAkD;AAC3D,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU,GAAG,WAAW;AAG9C,QAAA,CAAC,KAAK,SAAS,SAAS;AAC1B,WAAK,aAAa;AAAA,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAMF,aAA8C;AAC5C,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,aAAsB;AACpB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,UAAkB;AAChB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,MAAM,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtD,gBAAgB,MAA6B;AAC3C,QAAI,qBAAqB;AAGzB,QAAI,KAAK,SAAS,WAAW,KAAK,oBAAoB;AACpD,WAAK,qBAAqB;AACL,2BAAA;AAChB,WAAA,gBAAgB,GAAG,IAAI;AAAA,IAAA;AAI1B,QAAA,KAAK,SAAS,UAAU;AAC1B,WAAK,aAAa;AAAA,IAAA;AAIpB,QAAI,KAAK,WAAyB,cAAA,KAAK,UAAU;AAG5C,SAAA,aAAa,WAAW,MAAM;AACjC,WAAK,qBAAqB;AAC1B,UAAI,KAAK,SAAS,YAAY,CAAC,oBAAoB;AAC5C,aAAA,gBAAgB,GAAG,IAAI;AAAA,MAAA;AAAA,IAC9B,GACC,KAAK,SAAS;AAAA,EAAA;AAAA,EAGX,mBAAmB,MAA6B;AACtD,QAAI,CAAC,KAAK,WAAW,EAAU,QAAA;AAC1B,SAAA,GAAG,GAAG,IAAI;AACf,SAAK,aAAa;AACb,SAAA;AACA,SAAA,SAAS,UAAU,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,SAAe;AACb,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,qBAAqB;AAC1B,WAAK,aAAa;AAAA,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAMF,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,eAAwB;AACf,WAAA,KAAK,gBAAgB,KAAK;AAAA,EAAA;AAErC;AAsBgB,SAAA,SACd,IACA,gBACoC;AACpC,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;;;"}
1
+ {"version":3,"file":"debouncer.cjs","sources":["../../src/debouncer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyFunction } from './types'\n\n/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions<TFn extends AnyFunction> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Can be a boolean or a function that returns a boolean.\n * Defaults to true.\n */\n enabled?: boolean | ((debouncer: Debouncer<TFn>) => boolean)\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?: (debouncer: Debouncer<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 * Can be a number or a function that returns a number.\n * Defaults to 0ms\n */\n wait: number | ((debouncer: Debouncer<TFn>) => number)\n}\n\nconst defaultOptions: Required<DebouncerOptions<any>> = {\n enabled: true,\n leading: false,\n onExecute: () => {},\n trailing: true,\n wait: 0,\n}\n\n/**\n * A class that creates a debounced function.\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 * @example\n * ```ts\n * const debouncer = new Debouncer((value: string) => {\n * saveToDatabase(value);\n * }, { wait: 500 });\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 Debouncer<TFn extends AnyFunction> {\n private _canLeadingExecute = true\n private _executionCount = 0\n private _isPending = false\n private _options: Required<DebouncerOptions<TFn>>\n private _timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n }\n }\n\n /**\n * Updates the debouncer options\n */\n setOptions(newOptions: Partial<DebouncerOptions<TFn>>): void {\n this._options = { ...this._options, ...newOptions }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this._isPending = false\n }\n }\n\n /**\n * Returns the current debouncer options\n */\n getOptions(): Required<DebouncerOptions<TFn>> {\n return this._options\n }\n\n /**\n * Returns the current enabled state of the debouncer\n */\n getEnabled(): boolean {\n return parseFunctionOrValue(this._options.enabled, this)\n }\n\n /**\n * Returns the current wait time in milliseconds\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Attempts to execute the debounced function\n * If a call is already in progress, it will be queued\n */\n maybeExecute(...args: Parameters<TFn>): void {\n let _didLeadingExecute = false\n\n // Handle leading execution\n if (this._options.leading && this._canLeadingExecute) {\n this._canLeadingExecute = false\n _didLeadingExecute = true\n this.execute(...args)\n }\n\n // Start pending state to indicate that the debouncer is waiting for the trailing edge\n if (this._options.trailing) {\n this._isPending = true\n }\n\n // Clear any existing timeout\n if (this._timeoutId) clearTimeout(this._timeoutId)\n\n // Set new timeout that will reset canLeadingExecute and execute trailing only if enabled and did not execute leading\n this._timeoutId = setTimeout(() => {\n this._canLeadingExecute = true\n if (this._options.trailing && !_didLeadingExecute) {\n this.execute(...args)\n }\n }, this.getWait())\n }\n\n private execute(...args: Parameters<TFn>): void {\n if (!this.getEnabled()) return undefined\n this.fn(...args) // EXECUTE!\n this._isPending = false\n this._executionCount++\n this._options.onExecute(this)\n }\n\n /**\n * Cancels any pending execution\n */\n cancel(): void {\n if (this._timeoutId) {\n clearTimeout(this._timeoutId)\n this._canLeadingExecute = true\n this._isPending = false\n }\n }\n\n /**\n * Returns the number of times the function has been executed\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns `true` if debouncing\n */\n getIsPending(): boolean {\n return this.getEnabled() && this._isPending\n }\n}\n\n/**\n * Creates a 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 the the simple function wrapper implementation pulled from the Debouncer class. If you need\n * more control over the debouncing behavior, use the Debouncer class directly.\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 debounced = debounce(() => {\n * saveChanges();\n * }, { wait: 1000 });\n *\n * // Called repeatedly but executes at most once per second\n * inputElement.addEventListener('input', debounced);\n * ```\n */\nexport function debounce<TFn extends AnyFunction>(\n fn: TFn,\n initialOptions: DebouncerOptions<TFn>,\n): (...args: Parameters<TFn>) => void {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute.bind(debouncer)\n}\n"],"names":["parseFunctionOrValue"],"mappings":";;;AAoCA,MAAM,iBAAkD;AAAA,EACtD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,UAAU;AAAA,EACV,MAAM;AACR;AAyBO,MAAM,UAAmC;AAAA,EAO9C,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AAPV,SAAQ,qBAAqB;AAC7B,SAAQ,kBAAkB;AAC1B,SAAQ,aAAa;AAQnB,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,WAAW,YAAkD;AAC3D,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU,GAAG,WAAW;AAG9C,QAAA,CAAC,KAAK,SAAS,SAAS;AAC1B,WAAK,aAAa;AAAA,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAMF,aAA8C;AAC5C,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,aAAsB;AACpB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,UAAkB;AAChB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,MAAM,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtD,gBAAgB,MAA6B;AAC3C,QAAI,qBAAqB;AAGzB,QAAI,KAAK,SAAS,WAAW,KAAK,oBAAoB;AACpD,WAAK,qBAAqB;AACL,2BAAA;AAChB,WAAA,QAAQ,GAAG,IAAI;AAAA,IAAA;AAIlB,QAAA,KAAK,SAAS,UAAU;AAC1B,WAAK,aAAa;AAAA,IAAA;AAIpB,QAAI,KAAK,WAAyB,cAAA,KAAK,UAAU;AAG5C,SAAA,aAAa,WAAW,MAAM;AACjC,WAAK,qBAAqB;AAC1B,UAAI,KAAK,SAAS,YAAY,CAAC,oBAAoB;AAC5C,aAAA,QAAQ,GAAG,IAAI;AAAA,MAAA;AAAA,IACtB,GACC,KAAK,SAAS;AAAA,EAAA;AAAA,EAGX,WAAW,MAA6B;AAC9C,QAAI,CAAC,KAAK,WAAW,EAAU,QAAA;AAC1B,SAAA,GAAG,GAAG,IAAI;AACf,SAAK,aAAa;AACb,SAAA;AACA,SAAA,SAAS,UAAU,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,SAAe;AACb,QAAI,KAAK,YAAY;AACnB,mBAAa,KAAK,UAAU;AAC5B,WAAK,qBAAqB;AAC1B,WAAK,aAAa;AAAA,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAMF,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,eAAwB;AACf,WAAA,KAAK,gBAAgB,KAAK;AAAA,EAAA;AAErC;AAsBgB,SAAA,SACd,IACA,gBACoC;AACpC,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;;;"}
@@ -64,7 +64,6 @@ export declare class Debouncer<TFn extends AnyFunction> {
64
64
  constructor(fn: TFn, initialOptions: DebouncerOptions<TFn>);
65
65
  /**
66
66
  * Updates the debouncer options
67
- * Returns the new options state
68
67
  */
69
68
  setOptions(newOptions: Partial<DebouncerOptions<TFn>>): void;
70
69
  /**
@@ -84,7 +83,7 @@ export declare class Debouncer<TFn extends AnyFunction> {
84
83
  * If a call is already in progress, it will be queued
85
84
  */
86
85
  maybeExecute(...args: Parameters<TFn>): void;
87
- private executeFunction;
86
+ private execute;
88
87
  /**
89
88
  * Cancels any pending execution
90
89
  */
@@ -4,6 +4,7 @@ const asyncDebouncer = require("./async-debouncer.cjs");
4
4
  const asyncQueuer = require("./async-queuer.cjs");
5
5
  const asyncRateLimiter = require("./async-rate-limiter.cjs");
6
6
  const asyncThrottler = require("./async-throttler.cjs");
7
+ const batcher = require("./batcher.cjs");
7
8
  const compare = require("./compare.cjs");
8
9
  const debouncer = require("./debouncer.cjs");
9
10
  const queuer = require("./queuer.cjs");
@@ -18,6 +19,8 @@ exports.AsyncRateLimiter = asyncRateLimiter.AsyncRateLimiter;
18
19
  exports.asyncRateLimit = asyncRateLimiter.asyncRateLimit;
19
20
  exports.AsyncThrottler = asyncThrottler.AsyncThrottler;
20
21
  exports.asyncThrottle = asyncThrottler.asyncThrottle;
22
+ exports.Batcher = batcher.Batcher;
23
+ exports.batch = batcher.batch;
21
24
  exports.isPlainArray = compare.isPlainArray;
22
25
  exports.isPlainObject = compare.isPlainObject;
23
26
  exports.replaceEqualDeep = compare.replaceEqualDeep;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2,6 +2,7 @@ export * from './async-debouncer.cjs';
2
2
  export * from './async-queuer.cjs';
3
3
  export * from './async-rate-limiter.cjs';
4
4
  export * from './async-throttler.cjs';
5
+ export * from './batcher.cjs';
5
6
  export * from './compare.cjs';
6
7
  export * from './debouncer.cjs';
7
8
  export * from './queuer.cjs';
@@ -9,7 +9,7 @@ const defaultOptions = {
9
9
  expirationDuration: Infinity,
10
10
  initialItems: [],
11
11
  maxSize: Infinity,
12
- onGetNextItem: () => {
12
+ onExecute: () => {
13
13
  },
14
14
  onIsRunningChange: () => {
15
15
  },
@@ -23,7 +23,8 @@ const defaultOptions = {
23
23
  wait: 0
24
24
  };
25
25
  class Queuer {
26
- constructor(initialOptions = defaultOptions) {
26
+ constructor(fn, initialOptions = {}) {
27
+ this.fn = fn;
27
28
  this._items = [];
28
29
  this._itemTimestamps = [];
29
30
  this._executionCount = 0;
@@ -40,26 +41,26 @@ class Queuer {
40
41
  }
41
42
  }
42
43
  /**
43
- * Updates the queuer options
44
- * Returns the new options state
44
+ * Updates the queuer options. New options are merged with existing options.
45
45
  */
46
46
  setOptions(newOptions) {
47
47
  this._options = { ...this._options, ...newOptions };
48
48
  }
49
49
  /**
50
- * Returns the current queuer options
50
+ * Returns the current queuer options, including defaults and any overrides.
51
51
  */
52
52
  getOptions() {
53
53
  return this._options;
54
54
  }
55
55
  /**
56
- * Returns the current wait time in milliseconds
56
+ * Returns the current wait time (in milliseconds) between processing items.
57
+ * If a function is provided, it is called with the queuer instance.
57
58
  */
58
59
  getWait() {
59
60
  return utils.parseFunctionOrValue(this._options.wait, this);
60
61
  }
61
62
  /**
62
- * Processes items in the queuer
63
+ * Processes items in the queue up to the wait interval. Internal use only.
63
64
  */
64
65
  tick() {
65
66
  if (!this._running) {
@@ -68,7 +69,7 @@ class Queuer {
68
69
  }
69
70
  this.checkExpiredItems();
70
71
  while (!this.getIsEmpty()) {
71
- const nextItem = this.getNextItem(this._options.getItemsFrom);
72
+ const nextItem = this.execute(this._options.getItemsFrom);
72
73
  if (nextItem === void 0) {
73
74
  break;
74
75
  }
@@ -83,7 +84,8 @@ class Queuer {
83
84
  this._pendingTick = false;
84
85
  }
85
86
  /**
86
- * Checks for and removes expired items from the queuer
87
+ * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.
88
+ * Internal use only.
87
89
  */
88
90
  checkExpiredItems() {
89
91
  if (this._options.expirationDuration === Infinity && this._options.getIsExpired === defaultOptions.getIsExpired)
@@ -115,7 +117,7 @@ class Queuer {
115
117
  }
116
118
  }
117
119
  /**
118
- * Stops the queuer from processing items
120
+ * Stops processing items in the queue. Does not clear the queue.
119
121
  */
120
122
  stop() {
121
123
  this._running = false;
@@ -123,7 +125,7 @@ class Queuer {
123
125
  this._options.onIsRunningChange(this);
124
126
  }
125
127
  /**
126
- * Starts the queuer and processes items
128
+ * Starts processing items in the queue. If already running, does nothing.
127
129
  */
128
130
  start() {
129
131
  this._running = true;
@@ -134,14 +136,15 @@ class Queuer {
134
136
  this._options.onIsRunningChange(this);
135
137
  }
136
138
  /**
137
- * Removes all items from the queuer
139
+ * Removes all pending items from the queue. Does not affect items being processed.
138
140
  */
139
141
  clear() {
140
142
  this._items = [];
141
143
  this._options.onItemsChange(this);
142
144
  }
143
145
  /**
144
- * Resets the queuer to its initial state
146
+ * Resets the queuer to its initial state. Optionally repopulates with initial items.
147
+ * Does not affect callbacks or options.
145
148
  */
146
149
  reset(withInitialItems) {
147
150
  this.clear();
@@ -152,8 +155,16 @@ class Queuer {
152
155
  this._running = this._options.started;
153
156
  }
154
157
  /**
155
- * Adds an item to the queuer and starts processing if not already running
156
- * @returns true if item was added, false if queuer is full
158
+ * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.
159
+ * Items can be inserted based on priority or at the front/back depending on configuration.
160
+ *
161
+ * Returns true if the item was added, false if the queue is full.
162
+ *
163
+ * Example usage:
164
+ * ```ts
165
+ * queuer.addItem('task');
166
+ * queuer.addItem('task2', 'front');
167
+ * ```
157
168
  */
158
169
  addItem(item, position = this._options.addItemsTo, runOnUpdate = true) {
159
170
  if (this.getIsFull()) {
@@ -164,7 +175,7 @@ class Queuer {
164
175
  if (this._options.getPriority !== defaultOptions.getPriority) {
165
176
  const priority = this._options.getPriority(item);
166
177
  const insertIndex = this._items.findIndex(
167
- (existing) => this._options.getPriority(existing) > priority
178
+ (existing) => this._options.getPriority(existing) < priority
168
179
  );
169
180
  if (insertIndex === -1) {
170
181
  this._items.push(item);
@@ -192,14 +203,15 @@ class Queuer {
192
203
  return true;
193
204
  }
194
205
  /**
195
- * Removes and returns an item from the queuer using shift (default) or pop
206
+ * Removes and returns the next item from the queue without executing the function.
207
+ * Use for manual queue management. Normally, use execute() to process items.
196
208
  *
197
- * @example
209
+ * Example usage:
198
210
  * ```ts
199
- * // Standard FIFO queuer
200
- * queuer.getNextItem()
201
- * // Stack-like behavior (LIFO)
202
- * queuer.getNextItem('back')
211
+ * // FIFO
212
+ * queuer.getNextItem();
213
+ * // LIFO
214
+ * queuer.getNextItem('back');
203
215
  * ```
204
216
  */
205
217
  getNextItem(position = this._options.getItemsFrom) {
@@ -212,21 +224,36 @@ class Queuer {
212
224
  this._itemTimestamps.pop();
213
225
  }
214
226
  if (item !== void 0) {
215
- this._executionCount++;
216
227
  this._options.onItemsChange(this);
217
- this._options.onGetNextItem(item, this);
218
228
  }
219
229
  return item;
220
230
  }
221
231
  /**
222
- * Returns an item without removing it
232
+ * Removes and returns the next item from the queue and processes it using the provided function.
233
+ *
234
+ * Example usage:
235
+ * ```ts
236
+ * queuer.execute();
237
+ * // LIFO
238
+ * queuer.execute('back');
239
+ * ```
240
+ */
241
+ execute(position) {
242
+ const item = this.getNextItem(position);
243
+ if (item !== void 0) {
244
+ this.fn(item);
245
+ this._executionCount++;
246
+ this._options.onExecute(item, this);
247
+ }
248
+ return item;
249
+ }
250
+ /**
251
+ * Returns the next item in the queue without removing it.
223
252
  *
224
- * @example
253
+ * Example usage:
225
254
  * ```ts
226
- * // Look at next item to getNextItem
227
- * queuer.getPeek()
228
- * // Look at last item (like stack top)
229
- * queuer.getPeek('back')
255
+ * queuer.getPeek(); // front
256
+ * queuer.getPeek('back'); // back
230
257
  * ```
231
258
  */
232
259
  getPeek(position = this._options.getItemsFrom) {
@@ -236,62 +263,62 @@ class Queuer {
236
263
  return this._items[this._items.length - 1];
237
264
  }
238
265
  /**
239
- * Returns true if the queuer is empty
266
+ * Returns true if the queue is empty (no pending items).
240
267
  */
241
268
  getIsEmpty() {
242
269
  return this._items.length === 0;
243
270
  }
244
271
  /**
245
- * Returns true if the queuer is full
272
+ * Returns true if the queue is full (reached maxSize).
246
273
  */
247
274
  getIsFull() {
248
275
  return this._items.length >= this._options.maxSize;
249
276
  }
250
277
  /**
251
- * Returns the current size of the queuer
278
+ * Returns the number of pending items in the queue.
252
279
  */
253
280
  getSize() {
254
281
  return this._items.length;
255
282
  }
256
283
  /**
257
- * Returns a copy of all items in the queuer
284
+ * Returns a copy of all items in the queue.
258
285
  */
259
286
  getAllItems() {
260
287
  return [...this._items];
261
288
  }
262
289
  /**
263
- * Returns the number of items that have been removed from the queuer
290
+ * Returns the number of items that have been processed and removed from the queue.
264
291
  */
265
292
  getExecutionCount() {
266
293
  return this._executionCount;
267
294
  }
268
295
  /**
269
- * Returns the number of items that have been rejected from the queuer
296
+ * Returns the number of items that have been rejected from being added to the queue.
270
297
  */
271
298
  getRejectionCount() {
272
299
  return this._rejectionCount;
273
300
  }
274
301
  /**
275
- * Returns the number of items that have expired from the queuer
302
+ * Returns the number of items that have expired and been removed from the queue.
276
303
  */
277
304
  getExpirationCount() {
278
305
  return this._expirationCount;
279
306
  }
280
307
  /**
281
- * Returns true if the queuer is running
308
+ * Returns true if the queuer is currently running (processing items).
282
309
  */
283
310
  getIsRunning() {
284
311
  return this._running;
285
312
  }
286
313
  /**
287
- * Returns true if the queuer is running but has no items to process
314
+ * Returns true if the queuer is running but has no items to process.
288
315
  */
289
316
  getIsIdle() {
290
317
  return this._running && this.getIsEmpty();
291
318
  }
292
319
  }
293
- function queue(options) {
294
- const queuer = new Queuer(options);
320
+ function queue(fn, options) {
321
+ const queuer = new Queuer(fn, options);
295
322
  return queuer.addItem.bind(queuer);
296
323
  }
297
324
  exports.Queuer = Queuer;
@@ -1 +1 @@
1
- {"version":3,"file":"queuer.cjs","sources":["../../src/queuer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\n\n/**\n * Options for configuring a Queuer instance\n */\nexport interface QueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\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 queuer\n * Higher priority items will be processed first\n */\n getPriority?: (item: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onGetNextItem?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever the queuer's running state changes\n */\n onIsRunningChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: Queuer<TValue>) => number)\n}\n\nconst defaultOptions: Required<QueuerOptions<any>> = {\n addItemsTo: 'back',\n getItemsFrom: 'front',\n getPriority: (item) => item?.priority ?? 0,\n getIsExpired: () => false,\n expirationDuration: Infinity,\n initialItems: [],\n maxSize: Infinity,\n onGetNextItem: () => {},\n onIsRunningChange: () => {},\n onItemsChange: () => {},\n onReject: () => {},\n onExpire: () => {},\n started: true,\n wait: 0,\n}\n\n/**\n * Position type for addItem and getNextItem operations\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * A flexible queue data structure that defaults to FIFO (First In First Out) behavior\n * with optional position overrides for stack-like or double-ended operations.\n *\n * The queuer can automatically process items as they are added, with configurable\n * wait times between processing each item. Processing can be started/stopped\n * and the queuer will maintain its state.\n *\n * Supports priority-based ordering when a getPriority function is provided.\n * Items with higher priority values will be processed first.\n *\n * Default queue behavior:\n * - addItem(item): adds to back of queuer\n * - getNextItem(): removes and returns from front of queuer\n *\n * Stack (LIFO) behavior:\n * - addItem(item, 'back'): adds to back\n * - getNextItem('back'): removes and returns from back\n *\n * Double-ended queuer behavior:\n * - addItem(item, position): adds to specified position ('front' or 'back')\n * - getNextItem(position): removes and returns from specified position\n *\n * Processing behavior:\n * - start(): begins processing items in the queuer\n * - stop(): pauses processing\n * - wait: configurable delay between processing items\n * - onItemsChange/onGetNextItem: callbacks for monitoring queuer state\n *\n * Supports item expiration to clear stale items from the queuer\n * - expirationDuration: maximum time in milliseconds that an item can stay in the queue\n * - getIsExpired: function to override default expiration behavior\n * - onExpire: callback for when an item expires\n *\n * @example\n * ```ts\n * // FIFO queuer\n * const queuer = new Queuer<number>();\n * queuer.addItem(1); // [1]\n * queuer.addItem(2); // [1, 2]\n * queuer.getNextItem(); // returns 1, queuer is [2]\n *\n * // Priority queuer with processing\n * const priorityQueue = new Queuer<number>({\n * getPriority: (n) => n, // Higher numbers have priority\n * started: true, // Begin processing immediately\n * wait: 1000, // Wait 1s between items\n * onGetNextItem: (item, queuer) => console.log(item)\n * });\n * priorityQueue.addItem(1); // [1]\n * priorityQueue.addItem(3); // [3, 1] - 3 processed first\n * priorityQueue.addItem(2); // [3, 2, 1]\n * ```\n */\nexport class Queuer<TValue> {\n private _options: Required<QueuerOptions<TValue>>\n private _items: Array<TValue> = []\n private _itemTimestamps: Array<number> = []\n private _executionCount = 0\n private _rejectionCount = 0\n private _expirationCount = 0\n private _onItemsChanges: Array<(item: TValue) => void> = []\n private _running: boolean\n private _pendingTick = false\n\n constructor(initialOptions: QueuerOptions<TValue> = defaultOptions) {\n this._options = { ...defaultOptions, ...initialOptions }\n this._running = this._options.started\n\n for (let i = 0; i < this._options.initialItems.length; i++) {\n const item = this._options.initialItems[i]!\n const isLast = i === this._options.initialItems.length - 1\n this.addItem(item, this._options.addItemsTo, isLast)\n }\n }\n\n /**\n * Updates the queuer options\n * Returns the new options state\n */\n setOptions(newOptions: Partial<QueuerOptions<TValue>>): void {\n this._options = { ...this._options, ...newOptions }\n }\n\n /**\n * Returns the current queuer options\n */\n getOptions(): Required<QueuerOptions<TValue>> {\n return this._options\n }\n\n /**\n * Returns the current wait time in milliseconds\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Processes items in the queuer\n */\n private tick() {\n if (!this._running) {\n this._pendingTick = false\n return\n }\n\n // Check for expired items\n this.checkExpiredItems()\n\n while (!this.getIsEmpty()) {\n const nextItem = this.getNextItem(this._options.getItemsFrom)\n if (nextItem === undefined) {\n break\n }\n this._onItemsChanges.forEach((cb) => cb(nextItem))\n\n const wait = this.getWait()\n if (wait > 0) {\n // Use setTimeout to wait before processing next item\n setTimeout(() => this.tick(), wait)\n return\n }\n\n this.tick()\n }\n this._pendingTick = false\n }\n\n /**\n * Checks for and removes expired items from the queuer\n */\n private checkExpiredItems() {\n if (\n this._options.expirationDuration === Infinity &&\n this._options.getIsExpired === defaultOptions.getIsExpired\n )\n return\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this._items.length; i++) {\n const timestamp = this._itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this._items[i]\n if (item === undefined) continue\n\n const isExpired =\n this._options.getIsExpired !== defaultOptions.getIsExpired\n ? this._options.getIsExpired(item, timestamp)\n : now - timestamp > this._options.expirationDuration\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this._items[index]\n if (expiredItem === undefined) continue\n\n this._items.splice(index, 1)\n this._itemTimestamps.splice(index, 1)\n this._expirationCount++\n this._options.onExpire(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this._options.onItemsChange(this)\n }\n }\n\n /**\n * Stops the queuer from processing items\n */\n stop() {\n this._running = false\n this._pendingTick = false\n this._options.onIsRunningChange(this)\n }\n\n /**\n * Starts the queuer and processes items\n */\n start() {\n this._running = true\n if (!this._pendingTick && !this.getIsEmpty()) {\n this._pendingTick = true\n this.tick()\n }\n this._options.onIsRunningChange(this)\n }\n\n /**\n * Removes all items from the queuer\n */\n clear(): void {\n this._items = []\n this._options.onItemsChange(this)\n }\n\n /**\n * Resets the queuer to its initial state\n */\n reset(withInitialItems?: boolean): void {\n this.clear()\n this._executionCount = 0\n if (withInitialItems) {\n this._items = [...this._options.initialItems]\n }\n this._running = this._options.started\n }\n\n /**\n * Adds an item to the queuer and starts processing if not already running\n * @returns true if item was added, false if queuer is full\n */\n addItem(\n item: TValue,\n position: QueuePosition = this._options.addItemsTo,\n runOnUpdate: boolean = true,\n ): boolean {\n if (this.getIsFull()) {\n this._rejectionCount++\n this._options.onReject(item, this)\n return false\n }\n\n if (this._options.getPriority !== defaultOptions.getPriority) {\n // If custom priority function is provided, insert based on priority\n const priority = this._options.getPriority(item)\n const insertIndex = this._items.findIndex(\n (existing) => this._options.getPriority(existing) > priority,\n )\n\n if (insertIndex === -1) {\n this._items.push(item)\n this._itemTimestamps.push(Date.now())\n } else {\n this._items.splice(insertIndex, 0, item)\n this._itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n // Default FIFO/LIFO behavior\n if (position === 'front') {\n this._items.unshift(item)\n this._itemTimestamps.unshift(Date.now())\n } else {\n this._items.push(item)\n this._itemTimestamps.push(Date.now())\n }\n }\n\n if (this._running && !this._pendingTick) {\n this._pendingTick = true\n this.tick()\n }\n if (runOnUpdate) {\n this._options.onItemsChange(this)\n }\n return true\n }\n\n /**\n * Removes and returns an item from the queuer using shift (default) or pop\n *\n * @example\n * ```ts\n * // Standard FIFO queuer\n * queuer.getNextItem()\n * // Stack-like behavior (LIFO)\n * queuer.getNextItem('back')\n * ```\n */\n getNextItem(\n position: QueuePosition = this._options.getItemsFrom,\n ): TValue | undefined {\n let item: TValue | undefined\n\n if (position === 'front') {\n item = this._items.shift()\n this._itemTimestamps.shift()\n } else {\n item = this._items.pop()\n this._itemTimestamps.pop()\n }\n\n if (item !== undefined) {\n this._executionCount++\n this._options.onItemsChange(this)\n this._options.onGetNextItem(item, this)\n }\n return item\n }\n\n /**\n * Returns an item without removing it\n *\n * @example\n * ```ts\n * // Look at next item to getNextItem\n * queuer.getPeek()\n * // Look at last item (like stack top)\n * queuer.getPeek('back')\n * ```\n */\n getPeek(\n position: QueuePosition = this._options.getItemsFrom,\n ): TValue | undefined {\n if (position === 'front') {\n return this._items[0]\n }\n return this._items[this._items.length - 1]\n }\n\n /**\n * Returns true if the queuer is empty\n */\n getIsEmpty(): boolean {\n return this._items.length === 0\n }\n\n /**\n * Returns true if the queuer is full\n */\n getIsFull(): boolean {\n return this._items.length >= this._options.maxSize\n }\n\n /**\n * Returns the current size of the queuer\n */\n getSize(): number {\n return this._items.length\n }\n\n /**\n * Returns a copy of all items in the queuer\n */\n getAllItems(): Array<TValue> {\n return [...this._items]\n }\n\n /**\n * Returns the number of items that have been removed from the queuer\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns the number of items that have been rejected from the queuer\n */\n getRejectionCount(): number {\n return this._rejectionCount\n }\n\n /**\n * Returns the number of items that have expired from the queuer\n */\n getExpirationCount(): number {\n return this._expirationCount\n }\n\n /**\n * Returns true if the queuer is running\n */\n getIsRunning() {\n return this._running\n }\n\n /**\n * Returns true if the queuer is running but has no items to process\n */\n getIsIdle() {\n return this._running && this.getIsEmpty()\n }\n}\n\n/**\n * Creates a queue that processes items in a queuer immediately upon addition.\n * Items are processed sequentially in FIFO order by default.\n *\n * This is a simplified wrapper around the Queuer class that only exposes the\n * `addItem` method. This queue is always running and will process items as they are added.\n * For more control over queuer processing, use the Queuer class\n * directly which provides methods like `start`, `stop`, `reset`, and more.\n *\n * @example\n * ```ts\n * // Basic sequential processing\n * const processItems = queuer<number>({\n * wait: 1000,\n * onItemsChange: (queuer) => console.log(queuer.getAllItems())\n * })\n * processItems(1) // Logs: 1\n * processItems(2) // Logs: 2 after 1 completes\n *\n * // Priority queuer\n * const processPriority = queuer<number>({\n * process: async (n) => console.log(n),\n * getPriority: n => n // Higher numbers processed first\n * })\n * processPriority(1)\n * processPriority(3) // Processed before 1\n * ```\n */\nexport function queue<TValue>(options: QueuerOptions<TValue>) {\n const queuer = new Queuer<TValue>(options)\n return queuer.addItem.bind(queuer)\n}\n"],"names":["parseFunctionOrValue"],"mappings":"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"}
1
+ {"version":3,"file":"queuer.cjs","sources":["../../src/queuer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\n\n/**\n * Options for configuring a Queuer instance.\n *\n * These options control queue behavior, item expiration, callbacks, and more.\n */\nexport interface QueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\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 queuer\n * Higher priority items will be processed first\n */\n getPriority?: (item: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onExecute?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever the queuer's running state changes\n */\n onIsRunningChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: Queuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: TValue, queuer: Queuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: Queuer<TValue>) => number)\n}\n\nconst defaultOptions: Required<QueuerOptions<any>> = {\n addItemsTo: 'back',\n getItemsFrom: 'front',\n getPriority: (item) => item?.priority ?? 0,\n getIsExpired: () => false,\n expirationDuration: Infinity,\n initialItems: [],\n maxSize: Infinity,\n onExecute: () => {},\n onIsRunningChange: () => {},\n onItemsChange: () => {},\n onReject: () => {},\n onExpire: () => {},\n started: true,\n wait: 0,\n}\n\n/**\n * Position type for addItem and getNextItem operations.\n *\n * - 'front': Operate on the front of the queue (FIFO)\n * - 'back': Operate on the back of the queue (LIFO)\n */\nexport type QueuePosition = 'front' | 'back'\n\n/**\n * A flexible queue that processes items with configurable wait times, expiration, and priority.\n *\n * Features:\n * - Automatic or manual processing of items\n * - FIFO (First In First Out), LIFO (Last In First Out), or double-ended queue behavior\n * - Priority-based ordering when getPriority is provided\n * - Item expiration and removal of stale items\n * - Callbacks for queue state changes, execution, rejection, and expiration\n *\n * Running behavior:\n * - `start()`: Begins automatically processing items in the queue (defaults to running)\n * - `stop()`: Pauses processing but maintains queue state\n * - `wait`: Configurable delay between processing items\n * - `onItemsChange`/`onExecute`: Callbacks for monitoring queue state\n *\n * Manual processing is also supported when automatic processing is disabled:\n * - `execute()`: Processes the next item using the provided function\n * - `getNextItem()`: Removes and returns the next item without processing\n *\n * Queue behavior defaults to FIFO:\n * - `addItem(item)`: Adds to the back of the queue\n * - Items processed from the front of the queue\n *\n * Priority queue:\n * - Provide a `getPriority` function; higher values are processed first\n *\n * Stack (LIFO):\n * - `addItem(item, 'back')`: Adds to the back\n * - `getNextItem('back')`: Removes from the back\n *\n * Double-ended queue:\n * - `addItem(item, position)`: Adds to specified position ('front'/'back')\n * - `getNextItem(position)`: Removes from specified position\n *\n * Item expiration:\n * - `expirationDuration`: Maximum time items can stay in the queue\n * - `getIsExpired`: Function to override default expiration\n * - `onExpire`: Callback for expired items\n *\n * Example usage:\n * ```ts\n * // Auto-processing queue with wait time\n * const autoQueue = new Queuer<number>((n) => console.log(n), {\n * started: true, // Begin processing immediately\n * wait: 1000, // Wait 1s between items\n * onExecute: (item) => console.log(`Processed ${item}`)\n * });\n * autoQueue.addItem(1); // Will process after 1s\n * autoQueue.addItem(2); // Will process 1s after first item\n *\n * // Manual processing queue\n * const manualQueue = new Queuer<number>((n) => console.log(n), {\n * started: false\n * });\n * manualQueue.addItem(1); // [1]\n * manualQueue.addItem(2); // [1, 2]\n * manualQueue.execute(); // logs 1, queue is [2]\n * manualQueue.getNextItem(); // returns 2, queue is empty\n * ```\n */\nexport class Queuer<TValue> {\n private _options: Required<QueuerOptions<TValue>>\n private _items: Array<TValue> = []\n private _itemTimestamps: Array<number> = []\n private _executionCount = 0\n private _rejectionCount = 0\n private _expirationCount = 0\n private _onItemsChanges: Array<(item: TValue) => void> = []\n private _running: boolean\n private _pendingTick = false\n\n constructor(\n private fn: (item: TValue) => void,\n initialOptions: QueuerOptions<TValue> = {},\n ) {\n this._options = { ...defaultOptions, ...initialOptions }\n this._running = this._options.started\n\n for (let i = 0; i < this._options.initialItems.length; i++) {\n const item = this._options.initialItems[i]!\n const isLast = i === this._options.initialItems.length - 1\n this.addItem(item, this._options.addItemsTo, isLast)\n }\n }\n\n /**\n * Updates the queuer options. New options are merged with existing options.\n */\n setOptions(newOptions: Partial<QueuerOptions<TValue>>): void {\n this._options = { ...this._options, ...newOptions }\n }\n\n /**\n * Returns the current queuer options, including defaults and any overrides.\n */\n getOptions(): Required<QueuerOptions<TValue>> {\n return this._options\n }\n\n /**\n * Returns the current wait time (in milliseconds) between processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Processes items in the queue up to the wait interval. Internal use only.\n */\n private tick() {\n if (!this._running) {\n this._pendingTick = false\n return\n }\n\n // Check for expired items\n this.checkExpiredItems()\n\n while (!this.getIsEmpty()) {\n const nextItem = this.execute(this._options.getItemsFrom)\n if (nextItem === undefined) {\n break\n }\n this._onItemsChanges.forEach((cb) => cb(nextItem))\n\n const wait = this.getWait()\n if (wait > 0) {\n // Use setTimeout to wait before processing next item\n setTimeout(() => this.tick(), wait)\n return\n }\n\n this.tick()\n }\n this._pendingTick = false\n }\n\n /**\n * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.\n * Internal use only.\n */\n private checkExpiredItems() {\n if (\n this._options.expirationDuration === Infinity &&\n this._options.getIsExpired === defaultOptions.getIsExpired\n )\n return\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this._items.length; i++) {\n const timestamp = this._itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this._items[i]\n if (item === undefined) continue\n\n const isExpired =\n this._options.getIsExpired !== defaultOptions.getIsExpired\n ? this._options.getIsExpired(item, timestamp)\n : now - timestamp > this._options.expirationDuration\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this._items[index]\n if (expiredItem === undefined) continue\n\n this._items.splice(index, 1)\n this._itemTimestamps.splice(index, 1)\n this._expirationCount++\n this._options.onExpire(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this._options.onItemsChange(this)\n }\n }\n\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop() {\n this._running = false\n this._pendingTick = false\n this._options.onIsRunningChange(this)\n }\n\n /**\n * Starts processing items in the queue. If already running, does nothing.\n */\n start() {\n this._running = true\n if (!this._pendingTick && !this.getIsEmpty()) {\n this._pendingTick = true\n this.tick()\n }\n this._options.onIsRunningChange(this)\n }\n\n /**\n * Removes all pending items from the queue. Does not affect items being processed.\n */\n clear(): void {\n this._items = []\n this._options.onItemsChange(this)\n }\n\n /**\n * Resets the queuer to its initial state. Optionally repopulates with initial items.\n * Does not affect callbacks or options.\n */\n reset(withInitialItems?: boolean): void {\n this.clear()\n this._executionCount = 0\n if (withInitialItems) {\n this._items = [...this._options.initialItems]\n }\n this._running = this._options.started\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.\n * Items can be inserted based on priority or at the front/back depending on configuration.\n *\n * Returns true if the item was added, false if the queue is full.\n *\n * Example usage:\n * ```ts\n * queuer.addItem('task');\n * queuer.addItem('task2', 'front');\n * ```\n */\n addItem(\n item: TValue,\n position: QueuePosition = this._options.addItemsTo,\n runOnUpdate: boolean = true,\n ): boolean {\n if (this.getIsFull()) {\n this._rejectionCount++\n this._options.onReject(item, this)\n return false\n }\n\n if (this._options.getPriority !== defaultOptions.getPriority) {\n // If custom priority function is provided, insert based on priority\n const priority = this._options.getPriority(item)\n const insertIndex = this._items.findIndex(\n (existing) => this._options.getPriority(existing) < priority,\n )\n\n if (insertIndex === -1) {\n this._items.push(item)\n this._itemTimestamps.push(Date.now())\n } else {\n this._items.splice(insertIndex, 0, item)\n this._itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n // Default FIFO/LIFO behavior\n if (position === 'front') {\n this._items.unshift(item)\n this._itemTimestamps.unshift(Date.now())\n } else {\n this._items.push(item)\n this._itemTimestamps.push(Date.now())\n }\n }\n\n if (this._running && !this._pendingTick) {\n this._pendingTick = true\n this.tick()\n }\n if (runOnUpdate) {\n this._options.onItemsChange(this)\n }\n return true\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 usage:\n * ```ts\n * // FIFO\n * queuer.getNextItem();\n * // LIFO\n * queuer.getNextItem('back');\n * ```\n */\n getNextItem(\n position: QueuePosition = this._options.getItemsFrom,\n ): TValue | undefined {\n let item: TValue | undefined\n\n if (position === 'front') {\n item = this._items.shift()\n this._itemTimestamps.shift()\n } else {\n item = this._items.pop()\n this._itemTimestamps.pop()\n }\n\n if (item !== undefined) {\n this._options.onItemsChange(this)\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 usage:\n * ```ts\n * queuer.execute();\n * // LIFO\n * queuer.execute('back');\n * ```\n */\n execute(position?: QueuePosition): TValue | undefined {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n this.fn(item)\n this._executionCount++\n this._options.onExecute(item, this)\n }\n return item\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * Example usage:\n * ```ts\n * queuer.getPeek(); // front\n * queuer.getPeek('back'); // back\n * ```\n */\n getPeek(\n position: QueuePosition = this._options.getItemsFrom,\n ): TValue | undefined {\n if (position === 'front') {\n return this._items[0]\n }\n return this._items[this._items.length - 1]\n }\n\n /**\n * Returns true if the queue is empty (no pending items).\n */\n getIsEmpty(): boolean {\n return this._items.length === 0\n }\n\n /**\n * Returns true if the queue is full (reached maxSize).\n */\n getIsFull(): boolean {\n return this._items.length >= this._options.maxSize\n }\n\n /**\n * Returns the number of pending items in the queue.\n */\n getSize(): number {\n return this._items.length\n }\n\n /**\n * Returns a copy of all items in the queue.\n */\n getAllItems(): Array<TValue> {\n return [...this._items]\n }\n\n /**\n * Returns the number of items that have been processed and removed from the queue.\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns the number of items that have been rejected from being added to the queue.\n */\n getRejectionCount(): number {\n return this._rejectionCount\n }\n\n /**\n * Returns the number of items that have expired and been removed from the queue.\n */\n getExpirationCount(): number {\n return this._expirationCount\n }\n\n /**\n * Returns true if the queuer is currently running (processing items).\n */\n getIsRunning() {\n return this._running\n }\n\n /**\n * Returns true if the queuer is running but has no items to process.\n */\n getIsIdle() {\n return this._running && this.getIsEmpty()\n }\n}\n\n/**\n * Creates a queue that processes items immediately upon addition.\n * Items are processed sequentially in FIFO order by default.\n *\n * This is a simplified wrapper around the Queuer class that only exposes the\n * `addItem` method. The queue is always running and will process items as they are added.\n * For more control over queue processing, use the Queuer class directly.\n *\n * Example usage:\n * ```ts\n * // Basic sequential processing\n * const processItems = queue<number>((n) => console.log(n), {\n * wait: 1000,\n * onItemsChange: (queuer) => console.log(queuer.getAllItems())\n * });\n * processItems(1); // Logs: 1\n * processItems(2); // Logs: 2 after 1 completes\n *\n * // Priority queue\n * const processPriority = queue<number>((n) => console.log(n), {\n * getPriority: n => n // Higher numbers processed first\n * });\n * processPriority(1);\n * processPriority(3); // Processed before 1\n * ```\n */\nexport function queue<TValue>(\n fn: (item: TValue) => void,\n options: QueuerOptions<TValue>,\n) {\n const queuer = new Queuer<TValue>(fn, options)\n return queuer.addItem.bind(queuer)\n}\n"],"names":["parseFunctionOrValue"],"mappings":";;;AAyEA,MAAM,iBAA+C;AAAA,EACnD,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,aAAa,CAAC,UAAS,6BAAM,aAAY;AAAA,EACzC,cAAc,MAAM;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc,CAAC;AAAA,EACf,SAAS;AAAA,EACT,WAAW,MAAM;AAAA,EAAC;AAAA,EAClB,mBAAmB,MAAM;AAAA,EAAC;AAAA,EAC1B,eAAe,MAAM;AAAA,EAAC;AAAA,EACtB,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,SAAS;AAAA,EACT,MAAM;AACR;AAuEO,MAAM,OAAe;AAAA,EAW1B,YACU,IACR,iBAAwC,IACxC;AAFQ,SAAA,KAAA;AAVV,SAAQ,SAAwB,CAAC;AACjC,SAAQ,kBAAiC,CAAC;AAC1C,SAAQ,kBAAkB;AAC1B,SAAQ,kBAAkB;AAC1B,SAAQ,mBAAmB;AAC3B,SAAQ,kBAAiD,CAAC;AAE1D,SAAQ,eAAe;AAMrB,SAAK,WAAW,EAAE,GAAG,gBAAgB,GAAG,eAAe;AAClD,SAAA,WAAW,KAAK,SAAS;AAE9B,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,aAAa,QAAQ,KAAK;AAC1D,YAAM,OAAO,KAAK,SAAS,aAAa,CAAC;AACzC,YAAM,SAAS,MAAM,KAAK,SAAS,aAAa,SAAS;AACzD,WAAK,QAAQ,MAAM,KAAK,SAAS,YAAY,MAAM;AAAA,IAAA;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAMF,WAAW,YAAkD;AAC3D,SAAK,WAAW,EAAE,GAAG,KAAK,UAAU,GAAG,WAAW;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMpD,aAA8C;AAC5C,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,UAAkB;AAChB,WAAOA,MAAqB,qBAAA,KAAK,SAAS,MAAM,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM9C,OAAO;AACT,QAAA,CAAC,KAAK,UAAU;AAClB,WAAK,eAAe;AACpB;AAAA,IAAA;AAIF,SAAK,kBAAkB;AAEhB,WAAA,CAAC,KAAK,cAAc;AACzB,YAAM,WAAW,KAAK,QAAQ,KAAK,SAAS,YAAY;AACxD,UAAI,aAAa,QAAW;AAC1B;AAAA,MAAA;AAEF,WAAK,gBAAgB,QAAQ,CAAC,OAAO,GAAG,QAAQ,CAAC;AAE3C,YAAA,OAAO,KAAK,QAAQ;AAC1B,UAAI,OAAO,GAAG;AAEZ,mBAAW,MAAM,KAAK,KAAK,GAAG,IAAI;AAClC;AAAA,MAAA;AAGF,WAAK,KAAK;AAAA,IAAA;AAEZ,SAAK,eAAe;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,oBAAoB;AAC1B,QACE,KAAK,SAAS,uBAAuB,YACrC,KAAK,SAAS,iBAAiB,eAAe;AAE9C;AAEI,UAAA,MAAM,KAAK,IAAI;AACrB,UAAM,iBAAgC,CAAC;AAGvC,aAAS,IAAI,GAAG,IAAI,KAAK,OAAO,QAAQ,KAAK;AACrC,YAAA,YAAY,KAAK,gBAAgB,CAAC;AACxC,UAAI,cAAc,OAAW;AAEvB,YAAA,OAAO,KAAK,OAAO,CAAC;AAC1B,UAAI,SAAS,OAAW;AAExB,YAAM,YACJ,KAAK,SAAS,iBAAiB,eAAe,eAC1C,KAAK,SAAS,aAAa,MAAM,SAAS,IAC1C,MAAM,YAAY,KAAK,SAAS;AAEtC,UAAI,WAAW;AACb,uBAAe,KAAK,CAAC;AAAA,MAAA;AAAA,IACvB;AAIF,aAAS,IAAI,eAAe,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,YAAA,QAAQ,eAAe,CAAC;AAC9B,UAAI,UAAU,OAAW;AAEnB,YAAA,cAAc,KAAK,OAAO,KAAK;AACrC,UAAI,gBAAgB,OAAW;AAE1B,WAAA,OAAO,OAAO,OAAO,CAAC;AACtB,WAAA,gBAAgB,OAAO,OAAO,CAAC;AAC/B,WAAA;AACA,WAAA,SAAS,SAAS,aAAa,IAAI;AAAA,IAAA;AAGtC,QAAA,eAAe,SAAS,GAAG;AACxB,WAAA,SAAS,cAAc,IAAI;AAAA,IAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAMF,OAAO;AACL,SAAK,WAAW;AAChB,SAAK,eAAe;AACf,SAAA,SAAS,kBAAkB,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,QAAQ;AACN,SAAK,WAAW;AAChB,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,cAAc;AAC5C,WAAK,eAAe;AACpB,WAAK,KAAK;AAAA,IAAA;AAEP,SAAA,SAAS,kBAAkB,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,QAAc;AACZ,SAAK,SAAS,CAAC;AACV,SAAA,SAAS,cAAc,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,MAAM,kBAAkC;AACtC,SAAK,MAAM;AACX,SAAK,kBAAkB;AACvB,QAAI,kBAAkB;AACpB,WAAK,SAAS,CAAC,GAAG,KAAK,SAAS,YAAY;AAAA,IAAA;AAEzC,SAAA,WAAW,KAAK,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAehC,QACE,MACA,WAA0B,KAAK,SAAS,YACxC,cAAuB,MACd;AACL,QAAA,KAAK,aAAa;AACf,WAAA;AACA,WAAA,SAAS,SAAS,MAAM,IAAI;AAC1B,aAAA;AAAA,IAAA;AAGT,QAAI,KAAK,SAAS,gBAAgB,eAAe,aAAa;AAE5D,YAAM,WAAW,KAAK,SAAS,YAAY,IAAI;AACzC,YAAA,cAAc,KAAK,OAAO;AAAA,QAC9B,CAAC,aAAa,KAAK,SAAS,YAAY,QAAQ,IAAI;AAAA,MACtD;AAEA,UAAI,gBAAgB,IAAI;AACjB,aAAA,OAAO,KAAK,IAAI;AACrB,aAAK,gBAAgB,KAAK,KAAK,IAAA,CAAK;AAAA,MAAA,OAC/B;AACL,aAAK,OAAO,OAAO,aAAa,GAAG,IAAI;AACvC,aAAK,gBAAgB,OAAO,aAAa,GAAG,KAAK,KAAK;AAAA,MAAA;AAAA,IACxD,OACK;AAEL,UAAI,aAAa,SAAS;AACnB,aAAA,OAAO,QAAQ,IAAI;AACxB,aAAK,gBAAgB,QAAQ,KAAK,IAAA,CAAK;AAAA,MAAA,OAClC;AACA,aAAA,OAAO,KAAK,IAAI;AACrB,aAAK,gBAAgB,KAAK,KAAK,IAAA,CAAK;AAAA,MAAA;AAAA,IACtC;AAGF,QAAI,KAAK,YAAY,CAAC,KAAK,cAAc;AACvC,WAAK,eAAe;AACpB,WAAK,KAAK;AAAA,IAAA;AAEZ,QAAI,aAAa;AACV,WAAA,SAAS,cAAc,IAAI;AAAA,IAAA;AAE3B,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeT,YACE,WAA0B,KAAK,SAAS,cACpB;AAChB,QAAA;AAEJ,QAAI,aAAa,SAAS;AACjB,aAAA,KAAK,OAAO,MAAM;AACzB,WAAK,gBAAgB,MAAM;AAAA,IAAA,OACtB;AACE,aAAA,KAAK,OAAO,IAAI;AACvB,WAAK,gBAAgB,IAAI;AAAA,IAAA;AAG3B,QAAI,SAAS,QAAW;AACjB,WAAA,SAAS,cAAc,IAAI;AAAA,IAAA;AAG3B,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaT,QAAQ,UAA8C;AAC9C,UAAA,OAAO,KAAK,YAAY,QAAQ;AACtC,QAAI,SAAS,QAAW;AACtB,WAAK,GAAG,IAAI;AACP,WAAA;AACA,WAAA,SAAS,UAAU,MAAM,IAAI;AAAA,IAAA;AAE7B,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYT,QACE,WAA0B,KAAK,SAAS,cACpB;AACpB,QAAI,aAAa,SAAS;AACjB,aAAA,KAAK,OAAO,CAAC;AAAA,IAAA;AAEtB,WAAO,KAAK,OAAO,KAAK,OAAO,SAAS,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM3C,aAAsB;AACb,WAAA,KAAK,OAAO,WAAW;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMhC,YAAqB;AACnB,WAAO,KAAK,OAAO,UAAU,KAAK,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM7C,UAAkB;AAChB,WAAO,KAAK,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMrB,cAA6B;AACpB,WAAA,CAAC,GAAG,KAAK,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMxB,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,qBAA6B;AAC3B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,eAAe;AACb,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,YAAY;AACH,WAAA,KAAK,YAAY,KAAK,WAAW;AAAA,EAAA;AAE5C;AA4BgB,SAAA,MACd,IACA,SACA;AACA,QAAM,SAAS,IAAI,OAAe,IAAI,OAAO;AACtC,SAAA,OAAO,QAAQ,KAAK,MAAM;AACnC;;;"}