@tanstack/pacer 0.1.0 → 0.2.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 +60 -44
  2. package/dist/cjs/async-debouncer.cjs.map +1 -1
  3. package/dist/cjs/async-debouncer.d.cts +37 -24
  4. package/dist/cjs/async-queuer.cjs +149 -125
  5. package/dist/cjs/async-queuer.cjs.map +1 -1
  6. package/dist/cjs/async-queuer.d.cts +65 -48
  7. package/dist/cjs/async-rate-limiter.cjs +63 -46
  8. package/dist/cjs/async-rate-limiter.cjs.map +1 -1
  9. package/dist/cjs/async-rate-limiter.d.cts +39 -27
  10. package/dist/cjs/async-throttler.cjs +70 -47
  11. package/dist/cjs/async-throttler.cjs.map +1 -1
  12. package/dist/cjs/async-throttler.d.cts +43 -25
  13. package/dist/cjs/debouncer.cjs +46 -22
  14. package/dist/cjs/debouncer.cjs.map +1 -1
  15. package/dist/cjs/debouncer.d.cts +25 -11
  16. package/dist/cjs/index.cjs +2 -0
  17. package/dist/cjs/index.cjs.map +1 -1
  18. package/dist/cjs/index.d.cts +2 -0
  19. package/dist/cjs/queuer.cjs +114 -104
  20. package/dist/cjs/queuer.cjs.map +1 -1
  21. package/dist/cjs/queuer.d.cts +53 -40
  22. package/dist/cjs/rate-limiter.cjs +54 -42
  23. package/dist/cjs/rate-limiter.cjs.map +1 -1
  24. package/dist/cjs/rate-limiter.d.cts +37 -45
  25. package/dist/cjs/throttler.cjs +61 -41
  26. package/dist/cjs/throttler.cjs.map +1 -1
  27. package/dist/cjs/throttler.d.cts +35 -22
  28. package/dist/cjs/types.d.cts +12 -0
  29. package/dist/cjs/utils.cjs +13 -0
  30. package/dist/cjs/utils.cjs.map +1 -0
  31. package/dist/cjs/utils.d.cts +1 -0
  32. package/dist/esm/async-debouncer.d.ts +37 -24
  33. package/dist/esm/async-debouncer.js +60 -44
  34. package/dist/esm/async-debouncer.js.map +1 -1
  35. package/dist/esm/async-queuer.d.ts +65 -48
  36. package/dist/esm/async-queuer.js +149 -125
  37. package/dist/esm/async-queuer.js.map +1 -1
  38. package/dist/esm/async-rate-limiter.d.ts +39 -27
  39. package/dist/esm/async-rate-limiter.js +63 -46
  40. package/dist/esm/async-rate-limiter.js.map +1 -1
  41. package/dist/esm/async-throttler.d.ts +43 -25
  42. package/dist/esm/async-throttler.js +70 -47
  43. package/dist/esm/async-throttler.js.map +1 -1
  44. package/dist/esm/debouncer.d.ts +25 -11
  45. package/dist/esm/debouncer.js +46 -22
  46. package/dist/esm/debouncer.js.map +1 -1
  47. package/dist/esm/index.d.ts +2 -0
  48. package/dist/esm/index.js +2 -0
  49. package/dist/esm/index.js.map +1 -1
  50. package/dist/esm/queuer.d.ts +53 -40
  51. package/dist/esm/queuer.js +114 -104
  52. package/dist/esm/queuer.js.map +1 -1
  53. package/dist/esm/rate-limiter.d.ts +37 -45
  54. package/dist/esm/rate-limiter.js +54 -42
  55. package/dist/esm/rate-limiter.js.map +1 -1
  56. package/dist/esm/throttler.d.ts +35 -22
  57. package/dist/esm/throttler.js +61 -41
  58. package/dist/esm/throttler.js.map +1 -1
  59. package/dist/esm/types.d.ts +12 -0
  60. package/dist/esm/utils.d.ts +1 -0
  61. package/dist/esm/utils.js +13 -0
  62. package/dist/esm/utils.js.map +1 -0
  63. package/package.json +8 -1
  64. package/src/async-debouncer.ts +90 -62
  65. package/src/async-queuer.ts +178 -145
  66. package/src/async-rate-limiter.ts +93 -67
  67. package/src/async-throttler.ts +98 -63
  68. package/src/debouncer.ts +71 -35
  69. package/src/index.ts +2 -0
  70. package/src/queuer.ts +135 -118
  71. package/src/rate-limiter.ts +79 -81
  72. package/src/throttler.ts +87 -61
  73. package/src/types.ts +17 -0
  74. package/src/utils.ts +13 -0
@@ -2,14 +2,17 @@ const defaultOptions = {
2
2
  enabled: true,
3
3
  leading: false,
4
4
  trailing: true,
5
- wait: 0
5
+ wait: 0,
6
+ onExecute: () => {
7
+ }
6
8
  };
7
9
  class Debouncer {
8
10
  constructor(fn, initialOptions) {
9
11
  this.fn = fn;
10
- this.canLeadingExecute = true;
11
- this.executionCount = 0;
12
- this.options = {
12
+ this._canLeadingExecute = true;
13
+ this._isPending = false;
14
+ this._executionCount = 0;
15
+ this._options = {
13
16
  ...defaultOptions,
14
17
  ...initialOptions
15
18
  };
@@ -19,49 +22,70 @@ class Debouncer {
19
22
  * Returns the new options state
20
23
  */
21
24
  setOptions(newOptions) {
22
- this.options = {
23
- ...this.options,
25
+ this._options = {
26
+ ...this._options,
24
27
  ...newOptions
25
28
  };
26
- return this.options;
29
+ if (!this._options.enabled) {
30
+ this._isPending = false;
31
+ }
32
+ return this._options;
27
33
  }
28
34
  /**
29
- * Returns the number of times the function has been executed
35
+ * Returns the current debouncer options
30
36
  */
31
- getExecutionCount() {
32
- return this.executionCount;
37
+ getOptions() {
38
+ return this._options;
33
39
  }
34
40
  /**
35
41
  * Attempts to execute the debounced function
36
42
  * If a call is already in progress, it will be queued
37
43
  */
38
44
  maybeExecute(...args) {
39
- if (this.options.leading && this.canLeadingExecute) {
45
+ if (this._options.leading && this._canLeadingExecute) {
40
46
  this.executeFunction(...args);
41
- this.canLeadingExecute = false;
47
+ this._canLeadingExecute = false;
42
48
  }
43
- if (this.timeoutId) clearTimeout(this.timeoutId);
44
- this.timeoutId = setTimeout(() => {
45
- this.canLeadingExecute = true;
46
- if (this.options.trailing) {
49
+ if (this._options.leading || this._options.trailing) {
50
+ this._isPending = true;
51
+ }
52
+ if (this._timeoutId) clearTimeout(this._timeoutId);
53
+ this._timeoutId = setTimeout(() => {
54
+ this._canLeadingExecute = true;
55
+ this._isPending = false;
56
+ if (this._options.trailing) {
47
57
  this.executeFunction(...args);
48
58
  }
49
- }, this.options.wait);
59
+ }, this._options.wait);
50
60
  }
51
61
  executeFunction(...args) {
52
- if (!this.options.enabled) return;
53
- this.executionCount++;
62
+ if (!this._options.enabled) return;
54
63
  this.fn(...args);
64
+ this._executionCount++;
65
+ this._options.onExecute(this);
55
66
  }
56
67
  /**
57
68
  * Cancels any pending execution
58
69
  */
59
70
  cancel() {
60
- if (this.timeoutId) {
61
- clearTimeout(this.timeoutId);
62
- this.canLeadingExecute = true;
71
+ if (this._timeoutId) {
72
+ clearTimeout(this._timeoutId);
73
+ this._canLeadingExecute = true;
74
+ this._isPending = false;
63
75
  }
64
76
  }
77
+ /**
78
+ * Returns the number of times the function has been executed
79
+ */
80
+ getExecutionCount() {
81
+ return this._executionCount;
82
+ }
83
+ /**
84
+ * Returns `true` if debouncing
85
+ */
86
+ getIsPending() {
87
+ return this._options.enabled && this._isPending;
88
+ }
65
89
  }
66
90
  function debounce(fn, initialOptions) {
67
91
  const debouncer = new Debouncer(fn, initialOptions);
@@ -1 +1 @@
1
- {"version":3,"file":"debouncer.js","sources":["../../src/debouncer.ts"],"sourcesContent":["/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * Whether to execute on the leading edge of the timeout.\n * Defaults to false.\n */\n leading?: boolean\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 * Defaults to 0ms\n */\n wait: number\n}\n\nconst defaultOptions: Required<DebouncerOptions> = {\n enabled: true,\n leading: false,\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<\n TFn extends (...args: Array<any>) => any,\n TArgs extends Parameters<TFn>,\n> {\n private canLeadingExecute = true\n private executionCount = 0\n private options: Required<DebouncerOptions>\n private timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: DebouncerOptions,\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(\n newOptions: Partial<DebouncerOptions>,\n ): Required<DebouncerOptions> {\n this.options = {\n ...this.options,\n ...newOptions,\n }\n return this.options\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 * Attempts to execute the debounced function\n * If a call is already in progress, it will be queued\n */\n maybeExecute(...args: TArgs): void {\n // Handle leading execution\n if (this.options.leading && this.canLeadingExecute) {\n this.executeFunction(...args)\n this.canLeadingExecute = false\n }\n\n // Clear any existing timeout\n if (this.timeoutId) clearTimeout(this.timeoutId)\n\n // Set new timeout that will reset canLeadingExecute\n this.timeoutId = setTimeout(() => {\n this.canLeadingExecute = true\n // Execute trailing only if enabled\n if (this.options.trailing) {\n this.executeFunction(...args)\n }\n }, this.options.wait)\n }\n\n private executeFunction(...args: TArgs): void {\n if (!this.options.enabled) return\n this.executionCount++\n this.fn(...args)\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 }\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 (...args: Array<any>) => any>(\n fn: TFn,\n initialOptions: Omit<DebouncerOptions, 'enabled'>,\n) {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute.bind(debouncer)\n}\n"],"names":[],"mappings":"AA0BA,MAAM,iBAA6C;AAAA,EACjD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AACR;AAyBO,MAAM,UAGX;AAAA,EAMA,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AANV,SAAQ,oBAAoB;AAC5B,SAAQ,iBAAiB;AAQvB,SAAK,UAAU;AAAA,MACb,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WACE,YAC4B;AAC5B,SAAK,UAAU;AAAA,MACb,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACL;AACA,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,gBAAgB,MAAmB;AAEjC,QAAI,KAAK,QAAQ,WAAW,KAAK,mBAAmB;AAC7C,WAAA,gBAAgB,GAAG,IAAI;AAC5B,WAAK,oBAAoB;AAAA,IAAA;AAI3B,QAAI,KAAK,UAAwB,cAAA,KAAK,SAAS;AAG1C,SAAA,YAAY,WAAW,MAAM;AAChC,WAAK,oBAAoB;AAErB,UAAA,KAAK,QAAQ,UAAU;AACpB,aAAA,gBAAgB,GAAG,IAAI;AAAA,MAAA;AAAA,IAC9B,GACC,KAAK,QAAQ,IAAI;AAAA,EAAA;AAAA,EAGd,mBAAmB,MAAmB;AACxC,QAAA,CAAC,KAAK,QAAQ,QAAS;AACtB,SAAA;AACA,SAAA,GAAG,GAAG,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,SAAe;AACb,QAAI,KAAK,WAAW;AAClB,mBAAa,KAAK,SAAS;AAC3B,WAAK,oBAAoB;AAAA,IAAA;AAAA,EAC3B;AAEJ;AAsBgB,SAAA,SACd,IACA,gBACA;AACA,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;"}
1
+ {"version":3,"file":"debouncer.js","sources":["../../src/debouncer.ts"],"sourcesContent":["import type { AnyFunction } from './types'\n\n/**\n * Options for configuring a debounced function\n */\nexport interface DebouncerOptions<\n TFn extends AnyFunction,\n TArgs extends Parameters<TFn>,\n> {\n /**\n * Whether the debouncer is enabled. When disabled, maybeExecute will not trigger any executions.\n * Defaults to true.\n */\n enabled?: boolean\n /**\n * Whether to execute on the leading edge of the timeout.\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, TArgs>) => 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 * Defaults to 0ms\n */\n wait: number\n}\n\nconst defaultOptions: Required<DebouncerOptions<any, any>> = {\n enabled: true,\n leading: false,\n trailing: true,\n wait: 0,\n onExecute: () => {},\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, TArgs extends Parameters<TFn>> {\n private _canLeadingExecute = true\n private _isPending = false\n private _executionCount = 0\n private _options: Required<DebouncerOptions<TFn, TArgs>>\n private _timeoutId: NodeJS.Timeout | undefined\n\n constructor(\n private fn: TFn,\n initialOptions: DebouncerOptions<TFn, TArgs>,\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(\n newOptions: Partial<DebouncerOptions<TFn, TArgs>>,\n ): Required<DebouncerOptions<TFn, TArgs>> {\n this._options = {\n ...this._options,\n ...newOptions,\n }\n\n // End the pending state if the debouncer is disabled\n if (!this._options.enabled) {\n this._isPending = false\n }\n\n return this._options\n }\n\n /**\n * Returns the current debouncer options\n */\n getOptions(): Required<DebouncerOptions<TFn, TArgs>> {\n return this._options\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: TArgs): void {\n // Handle leading execution\n if (this._options.leading && this._canLeadingExecute) {\n this.executeFunction(...args)\n this._canLeadingExecute = false\n }\n\n // Start pending state\n if (this._options.leading || 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\n this._timeoutId = setTimeout(() => {\n this._canLeadingExecute = true\n this._isPending = false\n // Execute trailing only if enabled\n if (this._options.trailing) {\n this.executeFunction(...args)\n }\n }, this._options.wait)\n }\n\n private executeFunction(...args: TArgs): void {\n if (!this._options.enabled) return\n this.fn(...args) // EXECUTE!\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._options.enabled && 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: Omit<DebouncerOptions<TFn, Parameters<TFn>>, 'enabled'>,\n) {\n const debouncer = new Debouncer(fn, initialOptions)\n return debouncer.maybeExecute.bind(debouncer)\n}\n"],"names":[],"mappings":"AAmCA,MAAM,iBAAuD;AAAA,EAC3D,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,MAAM;AAAA,EACN,WAAW,MAAM;AAAA,EAAA;AACnB;AAyBO,MAAM,UAAkE;AAAA,EAO7E,YACU,IACR,gBACA;AAFQ,SAAA,KAAA;AAPV,SAAQ,qBAAqB;AAC7B,SAAQ,aAAa;AACrB,SAAQ,kBAAkB;AAQxB,SAAK,WAAW;AAAA,MACd,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WACE,YACwC;AACxC,SAAK,WAAW;AAAA,MACd,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACL;AAGI,QAAA,CAAC,KAAK,SAAS,SAAS;AAC1B,WAAK,aAAa;AAAA,IAAA;AAGpB,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,aAAqD;AACnD,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,gBAAgB,MAAmB;AAEjC,QAAI,KAAK,SAAS,WAAW,KAAK,oBAAoB;AAC/C,WAAA,gBAAgB,GAAG,IAAI;AAC5B,WAAK,qBAAqB;AAAA,IAAA;AAI5B,QAAI,KAAK,SAAS,WAAW,KAAK,SAAS,UAAU;AACnD,WAAK,aAAa;AAAA,IAAA;AAIpB,QAAI,KAAK,WAAyB,cAAA,KAAK,UAAU;AAG5C,SAAA,aAAa,WAAW,MAAM;AACjC,WAAK,qBAAqB;AAC1B,WAAK,aAAa;AAEd,UAAA,KAAK,SAAS,UAAU;AACrB,aAAA,gBAAgB,GAAG,IAAI;AAAA,MAAA;AAAA,IAC9B,GACC,KAAK,SAAS,IAAI;AAAA,EAAA;AAAA,EAGf,mBAAmB,MAAmB;AACxC,QAAA,CAAC,KAAK,SAAS,QAAS;AACvB,SAAA,GAAG,GAAG,IAAI;AACV,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,SAAS,WAAW,KAAK;AAAA,EAAA;AAEzC;AAsBgB,SAAA,SACd,IACA,gBACA;AACA,QAAM,YAAY,IAAI,UAAU,IAAI,cAAc;AAC3C,SAAA,UAAU,aAAa,KAAK,SAAS;AAC9C;"}
@@ -7,3 +7,5 @@ export * from './debouncer.js';
7
7
  export * from './queuer.js';
8
8
  export * from './rate-limiter.js';
9
9
  export * from './throttler.js';
10
+ export * from './types.js';
11
+ export * from './utils.js';
package/dist/esm/index.js CHANGED
@@ -7,6 +7,7 @@ import { Debouncer, debounce } from "./debouncer.js";
7
7
  import { Queuer, queue } from "./queuer.js";
8
8
  import { RateLimiter, rateLimit } from "./rate-limiter.js";
9
9
  import { Throttler, throttle } from "./throttler.js";
10
+ import { bindInstanceMethods } from "./utils.js";
10
11
  export {
11
12
  AsyncDebouncer,
12
13
  AsyncQueuer,
@@ -20,6 +21,7 @@ export {
20
21
  asyncQueue,
21
22
  asyncRateLimit,
22
23
  asyncThrottle,
24
+ bindInstanceMethods,
23
25
  debounce,
24
26
  isPlainArray,
25
27
  isPlainObject,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -29,10 +29,18 @@ export interface QueuerOptions<TValue> {
29
29
  * Callback fired whenever an item is removed from the queuer
30
30
  */
31
31
  onGetNextItem?: (item: TValue, queuer: Queuer<TValue>) => void;
32
+ /**
33
+ * Callback fired whenever the queuer's running state changes
34
+ */
35
+ onIsRunningChange?: (queuer: Queuer<TValue>) => void;
32
36
  /**
33
37
  * Callback fired whenever an item is added or removed from the queuer
34
38
  */
35
- onUpdate?: (queuer: Queuer<TValue>) => void;
39
+ onItemsChange?: (queuer: Queuer<TValue>) => void;
40
+ /**
41
+ * Callback fired whenever an item is rejected from being added to the queuer
42
+ */
43
+ onReject?: (item: TValue, queuer: Queuer<TValue>) => void;
36
44
  /**
37
45
  * Whether the queuer should start processing tasks immediately
38
46
  */
@@ -73,7 +81,7 @@ export type QueuePosition = 'front' | 'back';
73
81
  * - start(): begins processing items in the queuer
74
82
  * - stop(): pauses processing
75
83
  * - wait: configurable delay between processing items
76
- * - onUpdate/onGetNextItem: callbacks for monitoring queuer state
84
+ * - onItemsChange/onGetNextItem: callbacks for monitoring queuer state
77
85
  *
78
86
  * @example
79
87
  * ```ts
@@ -88,7 +96,7 @@ export type QueuePosition = 'front' | 'back';
88
96
  * getPriority: (n) => n, // Higher numbers have priority
89
97
  * started: true, // Begin processing immediately
90
98
  * wait: 1000, // Wait 1s between items
91
- * onGetNextItem: (item) => console.log(item)
99
+ * onGetNextItem: (item, queuer) => console.log(item)
92
100
  * });
93
101
  * priorityQueue.addItem(1); // [1]
94
102
  * priorityQueue.addItem(3); // [3, 1] - 3 processed first
@@ -96,22 +104,43 @@ export type QueuePosition = 'front' | 'back';
96
104
  * ```
97
105
  */
98
106
  export declare class Queuer<TValue> {
99
- protected options: Required<QueuerOptions<TValue>>;
100
- private items;
101
- private executionCount;
102
- private onUpdates;
103
- private running;
104
- private pendingTick;
107
+ private _options;
108
+ private _items;
109
+ private _executionCount;
110
+ private _rejectionCount;
111
+ private _onItemsChanges;
112
+ private _running;
113
+ private _pendingTick;
105
114
  constructor(initialOptions?: QueuerOptions<TValue>);
106
- /**
107
- * Processes items in the queuer
108
- */
109
- protected tick(): void;
110
115
  /**
111
116
  * Updates the queuer options
112
117
  * Returns the new options state
113
118
  */
114
119
  setOptions(newOptions: Partial<QueuerOptions<TValue>>): QueuerOptions<TValue>;
120
+ /**
121
+ * Returns the current queuer options
122
+ */
123
+ getOptions(): Required<QueuerOptions<TValue>>;
124
+ /**
125
+ * Processes items in the queuer
126
+ */
127
+ private tick;
128
+ /**
129
+ * Stops the queuer from processing items
130
+ */
131
+ stop(): void;
132
+ /**
133
+ * Starts the queuer and processes items
134
+ */
135
+ start(): void;
136
+ /**
137
+ * Removes all items from the queuer
138
+ */
139
+ clear(): void;
140
+ /**
141
+ * Resets the queuer to its initial state
142
+ */
143
+ reset(withInitialItems?: boolean): void;
115
144
  /**
116
145
  * Adds an item to the queuer and starts processing if not already running
117
146
  * @returns true if item was added, false if queuer is full
@@ -135,32 +164,24 @@ export declare class Queuer<TValue> {
135
164
  * @example
136
165
  * ```ts
137
166
  * // Look at next item to getNextItem
138
- * queuer.peek()
167
+ * queuer.getPeek()
139
168
  * // Look at last item (like stack top)
140
- * queuer.peek('back')
169
+ * queuer.getPeek('back')
141
170
  * ```
142
171
  */
143
- peek(position?: QueuePosition): TValue | undefined;
172
+ getPeek(position?: QueuePosition): TValue | undefined;
144
173
  /**
145
174
  * Returns true if the queuer is empty
146
175
  */
147
- isEmpty(): boolean;
176
+ getIsEmpty(): boolean;
148
177
  /**
149
178
  * Returns true if the queuer is full
150
179
  */
151
- isFull(): boolean;
180
+ getIsFull(): boolean;
152
181
  /**
153
182
  * Returns the current size of the queuer
154
183
  */
155
- size(): number;
156
- /**
157
- * Removes all items from the queuer
158
- */
159
- clear(): void;
160
- /**
161
- * Resets the queuer to its initial state
162
- */
163
- reset(withInitialItems?: boolean): void;
184
+ getSize(): number;
164
185
  /**
165
186
  * Returns a copy of all items in the queuer
166
187
  */
@@ -170,25 +191,17 @@ export declare class Queuer<TValue> {
170
191
  */
171
192
  getExecutionCount(): number;
172
193
  /**
173
- * Adds a callback to be called when an item is processed
174
- */
175
- onUpdate(cb: (item: TValue) => void): () => void;
176
- /**
177
- * Stops the queuer from processing items
194
+ * Returns the number of items that have been rejected from the queuer
178
195
  */
179
- stop(): void;
180
- /**
181
- * Starts the queuer and processes items
182
- */
183
- start(): void;
196
+ getRejectionCount(): number;
184
197
  /**
185
198
  * Returns true if the queuer is running
186
199
  */
187
- isRunning(): boolean;
200
+ getIsRunning(): boolean;
188
201
  /**
189
202
  * Returns true if the queuer is running but has no items to process
190
203
  */
191
- isIdle(): boolean;
204
+ getIsIdle(): boolean;
192
205
  }
193
206
  /**
194
207
  * Creates a queue that processes items in a queuer immediately upon addition.
@@ -204,7 +217,7 @@ export declare class Queuer<TValue> {
204
217
  * // Basic sequential processing
205
218
  * const processItems = queuer<number>({
206
219
  * wait: 1000,
207
- * onUpdate: (queuer) => console.log(queuer.getAllItems())
220
+ * onItemsChange: (queuer) => console.log(queuer.getAllItems())
208
221
  * })
209
222
  * processItems(1) // Logs: 1
210
223
  * processItems(2) // Logs: 2 after 1 completes
@@ -1,91 +1,141 @@
1
1
  const defaultOptions = {
2
2
  addItemsTo: "back",
3
3
  getItemsFrom: "front",
4
- getPriority: () => 0,
4
+ getPriority: (item) => (item == null ? void 0 : item.priority) ?? 0,
5
5
  initialItems: [],
6
6
  maxSize: Infinity,
7
7
  onGetNextItem: () => {
8
8
  },
9
- onUpdate: () => {
9
+ onIsRunningChange: () => {
10
+ },
11
+ onItemsChange: () => {
12
+ },
13
+ onReject: () => {
10
14
  },
11
15
  started: false,
12
16
  wait: 0
13
17
  };
14
18
  class Queuer {
15
19
  constructor(initialOptions = defaultOptions) {
16
- this.items = [];
17
- this.executionCount = 0;
18
- this.onUpdates = [];
19
- this.pendingTick = false;
20
- this.options = { ...defaultOptions, ...initialOptions };
21
- this.running = this.options.started;
22
- for (let i = 0; i < this.options.initialItems.length; i++) {
23
- const item = this.options.initialItems[i];
24
- const isLast = i === this.options.initialItems.length - 1;
25
- this.addItem(item, this.options.addItemsTo, isLast);
20
+ this._items = [];
21
+ this._executionCount = 0;
22
+ this._rejectionCount = 0;
23
+ this._onItemsChanges = [];
24
+ this._pendingTick = false;
25
+ this._options = { ...defaultOptions, ...initialOptions };
26
+ this._running = this._options.started;
27
+ for (let i = 0; i < this._options.initialItems.length; i++) {
28
+ const item = this._options.initialItems[i];
29
+ const isLast = i === this._options.initialItems.length - 1;
30
+ this.addItem(item, this._options.addItemsTo, isLast);
26
31
  }
27
32
  }
33
+ /**
34
+ * Updates the queuer options
35
+ * Returns the new options state
36
+ */
37
+ setOptions(newOptions) {
38
+ this._options = { ...this._options, ...newOptions };
39
+ return this._options;
40
+ }
41
+ /**
42
+ * Returns the current queuer options
43
+ */
44
+ getOptions() {
45
+ return this._options;
46
+ }
28
47
  /**
29
48
  * Processes items in the queuer
30
49
  */
31
50
  tick() {
32
- if (!this.running) {
33
- this.pendingTick = false;
51
+ if (!this._running) {
52
+ this._pendingTick = false;
34
53
  return;
35
54
  }
36
- while (!this.isEmpty()) {
37
- const nextItem = this.getNextItem(this.options.getItemsFrom);
55
+ while (!this.getIsEmpty()) {
56
+ const nextItem = this.getNextItem(this._options.getItemsFrom);
38
57
  if (nextItem === void 0) {
39
58
  break;
40
59
  }
41
- this.onUpdates.forEach((cb) => cb(nextItem));
42
- if (this.options.wait > 0) {
43
- setTimeout(() => this.tick(), this.options.wait);
60
+ this._onItemsChanges.forEach((cb) => cb(nextItem));
61
+ if (this._options.wait > 0) {
62
+ setTimeout(() => this.tick(), this._options.wait);
44
63
  return;
45
64
  }
46
65
  this.tick();
47
66
  }
48
- this.pendingTick = false;
67
+ this._pendingTick = false;
49
68
  }
50
69
  /**
51
- * Updates the queuer options
52
- * Returns the new options state
70
+ * Stops the queuer from processing items
53
71
  */
54
- setOptions(newOptions) {
55
- this.options = { ...this.options, ...newOptions };
56
- return this.options;
72
+ stop() {
73
+ this._running = false;
74
+ this._pendingTick = false;
75
+ this._options.onIsRunningChange(this);
76
+ }
77
+ /**
78
+ * Starts the queuer and processes items
79
+ */
80
+ start() {
81
+ this._running = true;
82
+ if (!this._pendingTick && !this.getIsEmpty()) {
83
+ this._pendingTick = true;
84
+ this.tick();
85
+ }
86
+ this._options.onIsRunningChange(this);
87
+ }
88
+ /**
89
+ * Removes all items from the queuer
90
+ */
91
+ clear() {
92
+ this._items = [];
93
+ this._options.onItemsChange(this);
94
+ }
95
+ /**
96
+ * Resets the queuer to its initial state
97
+ */
98
+ reset(withInitialItems) {
99
+ this.clear();
100
+ this._executionCount = 0;
101
+ if (withInitialItems) {
102
+ this._items = [...this._options.initialItems];
103
+ }
104
+ this._running = this._options.started;
57
105
  }
58
106
  /**
59
107
  * Adds an item to the queuer and starts processing if not already running
60
108
  * @returns true if item was added, false if queuer is full
61
109
  */
62
- addItem(item, position = this.options.addItemsTo, runOnUpdate = true) {
63
- if (this.isFull()) {
110
+ addItem(item, position = this._options.addItemsTo, runOnUpdate = true) {
111
+ if (this.getIsFull()) {
112
+ this._rejectionCount++;
113
+ this._options.onReject(item, this);
64
114
  return false;
65
115
  }
66
- if (this.options.getPriority !== defaultOptions.getPriority) {
67
- const priority = this.options.getPriority(item);
68
- const insertIndex = this.items.findIndex(
69
- (existing) => this.options.getPriority(existing) > priority
116
+ if (this._options.getPriority !== defaultOptions.getPriority) {
117
+ const priority = this._options.getPriority(item);
118
+ const insertIndex = this._items.findIndex(
119
+ (existing) => this._options.getPriority(existing) > priority
70
120
  );
71
121
  if (insertIndex === -1) {
72
- this.items.push(item);
122
+ this._items.push(item);
73
123
  } else {
74
- this.items.splice(insertIndex, 0, item);
124
+ this._items.splice(insertIndex, 0, item);
75
125
  }
76
126
  } else {
77
127
  if (position === "front") {
78
- this.items.unshift(item);
128
+ this._items.unshift(item);
79
129
  } else {
80
- this.items.push(item);
130
+ this._items.push(item);
81
131
  }
82
132
  }
83
- if (this.running && !this.pendingTick) {
84
- this.pendingTick = true;
133
+ if (this._running && !this._pendingTick) {
134
+ this._pendingTick = true;
85
135
  this.tick();
86
136
  }
87
137
  if (runOnUpdate) {
88
- this.options.onUpdate(this);
138
+ this._options.onItemsChange(this);
89
139
  }
90
140
  return true;
91
141
  }
@@ -100,17 +150,17 @@ class Queuer {
100
150
  * queuer.getNextItem('back')
101
151
  * ```
102
152
  */
103
- getNextItem(position = this.options.getItemsFrom) {
153
+ getNextItem(position = this._options.getItemsFrom) {
104
154
  let item;
105
155
  if (position === "front") {
106
- item = this.items.shift();
156
+ item = this._items.shift();
107
157
  } else {
108
- item = this.items.pop();
158
+ item = this._items.pop();
109
159
  }
110
160
  if (item !== void 0) {
111
- this.executionCount++;
112
- this.options.onUpdate(this);
113
- this.options.onGetNextItem(item, this);
161
+ this._executionCount++;
162
+ this._options.onItemsChange(this);
163
+ this._options.onGetNextItem(item, this);
114
164
  }
115
165
  return item;
116
166
  }
@@ -120,104 +170,64 @@ class Queuer {
120
170
  * @example
121
171
  * ```ts
122
172
  * // Look at next item to getNextItem
123
- * queuer.peek()
173
+ * queuer.getPeek()
124
174
  * // Look at last item (like stack top)
125
- * queuer.peek('back')
175
+ * queuer.getPeek('back')
126
176
  * ```
127
177
  */
128
- peek(position = this.options.getItemsFrom) {
178
+ getPeek(position = this._options.getItemsFrom) {
129
179
  if (position === "front") {
130
- return this.items[0];
180
+ return this._items[0];
131
181
  }
132
- return this.items[this.items.length - 1];
182
+ return this._items[this._items.length - 1];
133
183
  }
134
184
  /**
135
185
  * Returns true if the queuer is empty
136
186
  */
137
- isEmpty() {
138
- return this.items.length === 0;
187
+ getIsEmpty() {
188
+ return this._items.length === 0;
139
189
  }
140
190
  /**
141
191
  * Returns true if the queuer is full
142
192
  */
143
- isFull() {
144
- return this.items.length >= this.options.maxSize;
193
+ getIsFull() {
194
+ return this._items.length >= this._options.maxSize;
145
195
  }
146
196
  /**
147
197
  * Returns the current size of the queuer
148
198
  */
149
- size() {
150
- return this.items.length;
151
- }
152
- /**
153
- * Removes all items from the queuer
154
- */
155
- clear() {
156
- this.items = [];
157
- this.options.onUpdate(this);
158
- }
159
- /**
160
- * Resets the queuer to its initial state
161
- */
162
- reset(withInitialItems) {
163
- this.clear();
164
- this.executionCount = 0;
165
- if (withInitialItems) {
166
- this.items = [...this.options.initialItems];
167
- }
168
- this.running = this.options.started;
199
+ getSize() {
200
+ return this._items.length;
169
201
  }
170
202
  /**
171
203
  * Returns a copy of all items in the queuer
172
204
  */
173
205
  getAllItems() {
174
- return [...this.items];
206
+ return [...this._items];
175
207
  }
176
208
  /**
177
209
  * Returns the number of items that have been removed from the queuer
178
210
  */
179
211
  getExecutionCount() {
180
- return this.executionCount;
181
- }
182
- /**
183
- * Adds a callback to be called when an item is processed
184
- */
185
- onUpdate(cb) {
186
- this.onUpdates.push(cb);
187
- return () => {
188
- this.onUpdates = this.onUpdates.filter((d) => d !== cb);
189
- };
212
+ return this._executionCount;
190
213
  }
191
214
  /**
192
- * Stops the queuer from processing items
215
+ * Returns the number of items that have been rejected from the queuer
193
216
  */
194
- stop() {
195
- this.running = false;
196
- this.pendingTick = false;
197
- this.options.onUpdate(this);
198
- }
199
- /**
200
- * Starts the queuer and processes items
201
- */
202
- start() {
203
- this.running = true;
204
- if (!this.pendingTick && !this.isEmpty()) {
205
- this.pendingTick = true;
206
- this.tick();
207
- }
208
- this.options.onUpdate(this);
217
+ getRejectionCount() {
218
+ return this._rejectionCount;
209
219
  }
210
220
  /**
211
221
  * Returns true if the queuer is running
212
222
  */
213
- isRunning() {
214
- return this.running;
223
+ getIsRunning() {
224
+ return this._running;
215
225
  }
216
226
  /**
217
227
  * Returns true if the queuer is running but has no items to process
218
228
  */
219
- isIdle() {
220
- return this.running && this.isEmpty();
229
+ getIsIdle() {
230
+ return this._running && this.getIsEmpty();
221
231
  }
222
232
  }
223
233
  function queue(options = {}) {