@chainlink/external-adapter-framework 2.12.0 → 2.13.1

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 (173) hide show
  1. package/README.md +1 -1
  2. package/config/index.d.ts +9 -0
  3. package/config/index.js +12 -1
  4. package/config/index.js.map +1 -1
  5. package/generator-adapter/generators/app/templates/src/config/index.ts.ejs +3 -0
  6. package/generator-adapter/node_modules/.yarn-integrity +31 -35
  7. package/generator-adapter/node_modules/@inquirer/ansi/package.json +28 -26
  8. package/generator-adapter/node_modules/@inquirer/checkbox/dist/index.d.ts +6 -1
  9. package/generator-adapter/node_modules/@inquirer/checkbox/dist/index.js +30 -10
  10. package/generator-adapter/node_modules/@inquirer/checkbox/package.json +23 -21
  11. package/generator-adapter/node_modules/@inquirer/confirm/package.json +21 -19
  12. package/generator-adapter/node_modules/@inquirer/core/dist/index.d.ts +1 -1
  13. package/generator-adapter/node_modules/@inquirer/core/dist/index.js +1 -1
  14. package/generator-adapter/node_modules/@inquirer/core/dist/lib/create-prompt.js +51 -25
  15. package/generator-adapter/node_modules/@inquirer/core/dist/lib/key.d.ts +2 -0
  16. package/generator-adapter/node_modules/@inquirer/core/dist/lib/key.js +1 -0
  17. package/generator-adapter/node_modules/@inquirer/core/dist/lib/utils.js +1 -1
  18. package/generator-adapter/node_modules/@inquirer/core/package.json +22 -20
  19. package/generator-adapter/node_modules/@inquirer/editor/dist/index.d.ts +3 -0
  20. package/generator-adapter/node_modules/@inquirer/editor/dist/index.js +5 -5
  21. package/generator-adapter/node_modules/@inquirer/editor/package.json +22 -20
  22. package/generator-adapter/node_modules/@inquirer/expand/package.json +21 -19
  23. package/generator-adapter/node_modules/@inquirer/external-editor/package.json +33 -31
  24. package/generator-adapter/node_modules/@inquirer/figures/package.json +18 -16
  25. package/generator-adapter/node_modules/@inquirer/input/dist/index.js +5 -3
  26. package/generator-adapter/node_modules/@inquirer/input/package.json +21 -19
  27. package/generator-adapter/node_modules/@inquirer/number/package.json +21 -19
  28. package/generator-adapter/node_modules/@inquirer/password/dist/index.d.ts +6 -1
  29. package/generator-adapter/node_modules/@inquirer/password/dist/index.js +7 -2
  30. package/generator-adapter/node_modules/@inquirer/password/package.json +22 -20
  31. package/generator-adapter/node_modules/@inquirer/prompts/README.md +15 -0
  32. package/generator-adapter/node_modules/@inquirer/prompts/package.json +31 -29
  33. package/generator-adapter/node_modules/@inquirer/rawlist/README.md +4 -0
  34. package/generator-adapter/node_modules/@inquirer/rawlist/dist/index.d.ts +9 -2
  35. package/generator-adapter/node_modules/@inquirer/rawlist/dist/index.js +24 -7
  36. package/generator-adapter/node_modules/@inquirer/rawlist/package.json +21 -19
  37. package/generator-adapter/node_modules/@inquirer/search/README.md +2 -1
  38. package/generator-adapter/node_modules/@inquirer/search/dist/index.d.ts +1 -0
  39. package/generator-adapter/node_modules/@inquirer/search/dist/index.js +11 -4
  40. package/generator-adapter/node_modules/@inquirer/search/package.json +22 -20
  41. package/generator-adapter/node_modules/@inquirer/select/dist/index.d.ts +5 -2
  42. package/generator-adapter/node_modules/@inquirer/select/dist/index.js +28 -11
  43. package/generator-adapter/node_modules/@inquirer/select/package.json +23 -21
  44. package/generator-adapter/node_modules/@inquirer/type/package.json +18 -16
  45. package/generator-adapter/node_modules/@types/node/README.md +1 -1
  46. package/generator-adapter/node_modules/@types/node/http.d.ts +1 -1
  47. package/generator-adapter/node_modules/@types/node/package.json +2 -2
  48. package/generator-adapter/node_modules/@types/node/stream/web.d.ts +1 -1
  49. package/generator-adapter/node_modules/@types/node/test.d.ts +2 -1
  50. package/generator-adapter/node_modules/@types/node/web-globals/fetch.d.ts +12 -0
  51. package/generator-adapter/node_modules/@yeoman/adapter/dist/adapter.d.ts +0 -3
  52. package/generator-adapter/node_modules/@yeoman/adapter/dist/adapter.js +1 -0
  53. package/generator-adapter/node_modules/@yeoman/adapter/dist/adapter.js.map +1 -1
  54. package/generator-adapter/node_modules/@yeoman/adapter/dist/log.d.ts +0 -2
  55. package/generator-adapter/node_modules/@yeoman/adapter/dist/log.js.map +1 -1
  56. package/generator-adapter/node_modules/@yeoman/adapter/dist/queued-adapter.d.ts +0 -1
  57. package/generator-adapter/node_modules/@yeoman/adapter/dist/queued-adapter.js.map +1 -1
  58. package/generator-adapter/node_modules/@yeoman/adapter/dist/testing/test-adapter.d.ts +0 -1
  59. package/generator-adapter/node_modules/@yeoman/adapter/dist/testing/test-adapter.js.map +1 -1
  60. package/generator-adapter/node_modules/@yeoman/adapter/package.json +11 -11
  61. package/generator-adapter/node_modules/ansi-styles/index.d.ts +341 -232
  62. package/generator-adapter/node_modules/ansi-styles/index.js +130 -190
  63. package/generator-adapter/node_modules/ansi-styles/license +1 -1
  64. package/generator-adapter/node_modules/ansi-styles/package.json +10 -8
  65. package/generator-adapter/node_modules/ansi-styles/readme.md +32 -53
  66. package/generator-adapter/node_modules/chalk/package.json +3 -3
  67. package/generator-adapter/node_modules/chalk/readme.md +25 -53
  68. package/generator-adapter/node_modules/chalk/source/index.d.ts +6 -1
  69. package/generator-adapter/node_modules/chalk/source/vendor/supports-color/browser.js +6 -2
  70. package/generator-adapter/node_modules/chalk/source/vendor/supports-color/index.js +10 -2
  71. package/generator-adapter/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
  72. package/generator-adapter/node_modules/fast-string-truncated-width/dist/index.js +111 -0
  73. package/generator-adapter/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
  74. package/generator-adapter/node_modules/{p-queue/dist/options copy.js → fast-string-truncated-width/dist/types.js} +1 -0
  75. package/generator-adapter/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
  76. package/generator-adapter/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
  77. package/generator-adapter/node_modules/fast-string-truncated-width/license +21 -0
  78. package/generator-adapter/node_modules/fast-string-truncated-width/package.json +35 -0
  79. package/generator-adapter/node_modules/fast-string-truncated-width/readme.md +59 -0
  80. package/generator-adapter/node_modules/fast-string-width/dist/index.d.ts +4 -0
  81. package/generator-adapter/node_modules/fast-string-width/dist/index.js +14 -0
  82. package/generator-adapter/node_modules/fast-string-width/license +21 -0
  83. package/generator-adapter/node_modules/fast-string-width/package.json +34 -0
  84. package/generator-adapter/node_modules/fast-string-width/readme.md +42 -0
  85. package/generator-adapter/node_modules/fast-wrap-ansi/LICENSE +23 -0
  86. package/generator-adapter/node_modules/fast-wrap-ansi/README.md +26 -0
  87. package/generator-adapter/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
  88. package/generator-adapter/node_modules/fast-wrap-ansi/lib/main.js +219 -0
  89. package/generator-adapter/node_modules/fast-wrap-ansi/lib/main.js.map +1 -0
  90. package/generator-adapter/node_modules/fast-wrap-ansi/package.json +51 -0
  91. package/generator-adapter/node_modules/iconv-lite/lib/index.d.ts +114 -26
  92. package/generator-adapter/node_modules/iconv-lite/lib/index.js +39 -40
  93. package/generator-adapter/node_modules/iconv-lite/package.json +13 -2
  94. package/generator-adapter/node_modules/iconv-lite/types/encodings.d.ts +423 -0
  95. package/generator-adapter/node_modules/inquirer/dist/types.d.ts +12 -11
  96. package/generator-adapter/node_modules/inquirer/dist/ui/prompt.js +9 -1
  97. package/generator-adapter/node_modules/inquirer/dist/ui/skipped-renderer.d.ts +13 -0
  98. package/generator-adapter/node_modules/inquirer/dist/ui/skipped-renderer.js +57 -0
  99. package/generator-adapter/node_modules/inquirer/package.json +21 -19
  100. package/generator-adapter/node_modules/log-symbols/package.json +1 -1
  101. package/generator-adapter/node_modules/log-symbols/symbols.js +1 -1
  102. package/generator-adapter/node_modules/mem-fs/dist/index.d.ts +0 -2
  103. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/LICENSE +21 -0
  104. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/README.md +447 -0
  105. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/index.js +346 -0
  106. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/lib/inspect-stream.js +13 -0
  107. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/lib/is-stream.js +15 -0
  108. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/lib/normalize.js +9 -0
  109. package/generator-adapter/node_modules/mem-fs/node_modules/vinyl/package.json +59 -0
  110. package/generator-adapter/node_modules/mem-fs/package.json +9 -9
  111. package/generator-adapter/node_modules/ora/index.d.ts +3 -1
  112. package/generator-adapter/node_modules/ora/index.js +299 -109
  113. package/generator-adapter/node_modules/ora/package.json +3 -4
  114. package/generator-adapter/node_modules/ora/readme.md +24 -0
  115. package/generator-adapter/node_modules/p-queue/dist/index.js +145 -22
  116. package/generator-adapter/node_modules/p-queue/dist/options.d.ts +17 -1
  117. package/generator-adapter/node_modules/p-queue/package.json +1 -1
  118. package/generator-adapter/node_modules/p-queue/readme.md +32 -1
  119. package/generator-adapter/node_modules/stdin-discarder/index.js +50 -22
  120. package/generator-adapter/node_modules/stdin-discarder/package.json +1 -1
  121. package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/package.json +3 -3
  122. package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/readme.md +53 -25
  123. package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/index.d.ts +1 -6
  124. package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/browser.js +2 -6
  125. package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/index.js +2 -10
  126. package/generator-adapter/package.json +3 -3
  127. package/package.json +12 -12
  128. package/transports/websocket.d.ts +5 -0
  129. package/transports/websocket.js +25 -13
  130. package/transports/websocket.js.map +1 -1
  131. package/generator-adapter/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  132. package/generator-adapter/node_modules/emoji-regex/README.md +0 -107
  133. package/generator-adapter/node_modules/emoji-regex/index.d.ts +0 -3
  134. package/generator-adapter/node_modules/emoji-regex/index.js +0 -4
  135. package/generator-adapter/node_modules/emoji-regex/index.mjs +0 -4
  136. package/generator-adapter/node_modules/emoji-regex/package.json +0 -45
  137. package/generator-adapter/node_modules/iconv-lite/Changelog.md +0 -236
  138. package/generator-adapter/node_modules/jake/node_modules/ansi-styles/index.d.ts +0 -345
  139. package/generator-adapter/node_modules/jake/node_modules/ansi-styles/index.js +0 -163
  140. package/generator-adapter/node_modules/jake/node_modules/ansi-styles/license +0 -9
  141. package/generator-adapter/node_modules/jake/node_modules/ansi-styles/package.json +0 -56
  142. package/generator-adapter/node_modules/jake/node_modules/ansi-styles/readme.md +0 -152
  143. package/generator-adapter/node_modules/ora/node_modules/log-symbols/browser-symbols.js +0 -4
  144. package/generator-adapter/node_modules/ora/node_modules/log-symbols/browser.js +0 -1
  145. package/generator-adapter/node_modules/ora/node_modules/log-symbols/index.d.ts +0 -22
  146. package/generator-adapter/node_modules/ora/node_modules/log-symbols/index.js +0 -1
  147. package/generator-adapter/node_modules/ora/node_modules/log-symbols/license +0 -9
  148. package/generator-adapter/node_modules/ora/node_modules/log-symbols/package.json +0 -60
  149. package/generator-adapter/node_modules/ora/node_modules/log-symbols/readme.md +0 -39
  150. package/generator-adapter/node_modules/ora/node_modules/log-symbols/symbols.js +0 -14
  151. package/generator-adapter/node_modules/ora/node_modules/strip-ansi/index.d.ts +0 -15
  152. package/generator-adapter/node_modules/ora/node_modules/strip-ansi/index.js +0 -14
  153. package/generator-adapter/node_modules/ora/node_modules/strip-ansi/license +0 -9
  154. package/generator-adapter/node_modules/ora/node_modules/strip-ansi/package.json +0 -59
  155. package/generator-adapter/node_modules/ora/node_modules/strip-ansi/readme.md +0 -37
  156. package/generator-adapter/node_modules/p-queue/dist/options copy.d.ts +0 -121
  157. package/generator-adapter/node_modules/wrap-ansi/index.d.ts +0 -41
  158. package/generator-adapter/node_modules/wrap-ansi/index.js +0 -222
  159. package/generator-adapter/node_modules/wrap-ansi/license +0 -9
  160. package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/index.d.ts +0 -39
  161. package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/index.js +0 -82
  162. package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/license +0 -9
  163. package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/package.json +0 -64
  164. package/generator-adapter/node_modules/wrap-ansi/node_modules/string-width/readme.md +0 -66
  165. package/generator-adapter/node_modules/wrap-ansi/package.json +0 -69
  166. package/generator-adapter/node_modules/wrap-ansi/readme.md +0 -75
  167. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/license +0 -0
  168. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/index.js +0 -0
  169. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/utilities.js +0 -0
  170. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +0 -0
  171. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/ansi-styles/index.js +0 -0
  172. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/browser.d.ts +0 -0
  173. /package/generator-adapter/node_modules/{ora → yeoman-generator}/node_modules/chalk/source/vendor/supports-color/index.d.ts +0 -0
@@ -16,6 +16,10 @@ export default class PQueue extends EventEmitter {
16
16
  #lastExecutionTime = 0;
17
17
  #intervalId;
18
18
  #timeoutId;
19
+ #strict;
20
+ // Circular buffer implementation for better performance
21
+ #strictTicks = [];
22
+ #strictTicksStartIndex = 0;
19
23
  #queue;
20
24
  #queueClass;
21
25
  #pending = 0;
@@ -52,6 +56,7 @@ export default class PQueue extends EventEmitter {
52
56
  concurrency: Number.POSITIVE_INFINITY,
53
57
  autoStart: true,
54
58
  queueClass: PriorityQueue,
59
+ strict: false,
55
60
  ...options,
56
61
  };
57
62
  if (!(typeof options.intervalCap === 'number' && options.intervalCap >= 1)) {
@@ -60,12 +65,19 @@ export default class PQueue extends EventEmitter {
60
65
  if (options.interval === undefined || !(Number.isFinite(options.interval) && options.interval >= 0)) {
61
66
  throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${options.interval?.toString() ?? ''}\` (${typeof options.interval})`);
62
67
  }
68
+ if (options.strict && options.interval === 0) {
69
+ throw new TypeError('The `strict` option requires a non-zero `interval`');
70
+ }
71
+ if (options.strict && options.intervalCap === Number.POSITIVE_INFINITY) {
72
+ throw new TypeError('The `strict` option requires a finite `intervalCap`');
73
+ }
63
74
  // TODO: Remove this fallback in the next major version
64
75
  // eslint-disable-next-line @typescript-eslint/no-deprecated
65
76
  this.#carryoverIntervalCount = options.carryoverIntervalCount ?? options.carryoverConcurrencyCount ?? false;
66
77
  this.#isIntervalIgnored = options.intervalCap === Number.POSITIVE_INFINITY || options.interval === 0;
67
78
  this.#intervalCap = options.intervalCap;
68
79
  this.#interval = options.interval;
80
+ this.#strict = options.strict;
69
81
  this.#queue = new options.queueClass();
70
82
  this.#queueClass = options.queueClass;
71
83
  this.concurrency = options.concurrency;
@@ -76,8 +88,58 @@ export default class PQueue extends EventEmitter {
76
88
  this.#isPaused = options.autoStart === false;
77
89
  this.#setupRateLimitTracking();
78
90
  }
91
+ #cleanupStrictTicks(now) {
92
+ // Remove ticks outside the current interval window using circular buffer approach
93
+ while (this.#strictTicksStartIndex < this.#strictTicks.length) {
94
+ const oldestTick = this.#strictTicks[this.#strictTicksStartIndex];
95
+ if (oldestTick !== undefined && now - oldestTick >= this.#interval) {
96
+ this.#strictTicksStartIndex++;
97
+ }
98
+ else {
99
+ break;
100
+ }
101
+ }
102
+ // Compact the array when it becomes inefficient or fully consumed
103
+ // Compact when: (start index is large AND more than half wasted) OR all ticks expired
104
+ const shouldCompact = (this.#strictTicksStartIndex > 100 && this.#strictTicksStartIndex > this.#strictTicks.length / 2)
105
+ || this.#strictTicksStartIndex === this.#strictTicks.length;
106
+ if (shouldCompact) {
107
+ this.#strictTicks = this.#strictTicks.slice(this.#strictTicksStartIndex);
108
+ this.#strictTicksStartIndex = 0;
109
+ }
110
+ }
111
+ // Helper methods for interval consumption
112
+ #consumeIntervalSlot(now) {
113
+ if (this.#strict) {
114
+ this.#strictTicks.push(now);
115
+ }
116
+ else {
117
+ this.#intervalCount++;
118
+ }
119
+ }
120
+ #rollbackIntervalSlot() {
121
+ if (this.#strict) {
122
+ // Pop from the end of the actual data (not from start index)
123
+ if (this.#strictTicks.length > this.#strictTicksStartIndex) {
124
+ this.#strictTicks.pop();
125
+ }
126
+ }
127
+ else if (this.#intervalCount > 0) {
128
+ this.#intervalCount--;
129
+ }
130
+ }
131
+ #getActiveTicksCount() {
132
+ return this.#strictTicks.length - this.#strictTicksStartIndex;
133
+ }
79
134
  get #doesIntervalAllowAnother() {
80
- return this.#isIntervalIgnored || this.#intervalCount < this.#intervalCap;
135
+ if (this.#isIntervalIgnored) {
136
+ return true;
137
+ }
138
+ if (this.#strict) {
139
+ // Cleanup already done by #isIntervalPausedAt before this is called
140
+ return this.#getActiveTicksCount() < this.#intervalCap;
141
+ }
142
+ return this.#intervalCount < this.#intervalCap;
81
143
  }
82
144
  get #doesConcurrentAllowAnother() {
83
145
  return this.#pending < this.#concurrency;
@@ -91,12 +153,28 @@ export default class PQueue extends EventEmitter {
91
153
  this.emit('next');
92
154
  }
93
155
  #onResumeInterval() {
94
- this.#onInterval(); // Already schedules update
95
- this.#initializeIntervalIfNeeded();
156
+ // Clear timeout ID before processing to prevent race condition
157
+ // Must clear before #onInterval to allow new timeouts to be scheduled
96
158
  this.#timeoutId = undefined;
159
+ this.#onInterval();
160
+ this.#initializeIntervalIfNeeded();
97
161
  }
98
- get #isIntervalPaused() {
99
- const now = Date.now();
162
+ #isIntervalPausedAt(now) {
163
+ // Strict mode: check if we need to wait for oldest tick to age out
164
+ if (this.#strict) {
165
+ this.#cleanupStrictTicks(now);
166
+ // If at capacity, need to wait for oldest tick to age out
167
+ const activeTicksCount = this.#getActiveTicksCount();
168
+ if (activeTicksCount >= this.#intervalCap) {
169
+ const oldestTick = this.#strictTicks[this.#strictTicksStartIndex];
170
+ // After cleanup, remaining ticks are within interval, so delay is always > 0
171
+ const delay = this.#interval - (now - oldestTick);
172
+ this.#createIntervalTimeout(delay);
173
+ return true;
174
+ }
175
+ return false;
176
+ }
177
+ // Fixed window mode (original logic)
100
178
  if (this.#intervalId === undefined) {
101
179
  const delay = this.#intervalEnd - now;
102
180
  if (delay < 0) {
@@ -151,22 +229,26 @@ export default class PQueue extends EventEmitter {
151
229
  if (this.#pending === 0) {
152
230
  // Clear timeout as well when completely idle
153
231
  this.#clearTimeoutTimer();
232
+ // Compact strict ticks when idle to free memory
233
+ if (this.#strict && this.#strictTicksStartIndex > 0) {
234
+ const now = Date.now();
235
+ this.#cleanupStrictTicks(now);
236
+ }
154
237
  this.emit('idle');
155
238
  }
156
239
  return false;
157
240
  }
158
241
  let taskStarted = false;
159
242
  if (!this.#isPaused) {
160
- const canInitializeInterval = !this.#isIntervalPaused;
243
+ const now = Date.now();
244
+ const canInitializeInterval = !this.#isIntervalPausedAt(now);
161
245
  if (this.#doesIntervalAllowAnother && this.#doesConcurrentAllowAnother) {
162
246
  const job = this.#queue.dequeue();
163
- // Increment interval count immediately to prevent race conditions
164
247
  if (!this.#isIntervalIgnored) {
165
- this.#intervalCount++;
248
+ this.#consumeIntervalSlot(now);
166
249
  this.#scheduleRateLimitUpdate();
167
250
  }
168
251
  this.emit('active');
169
- this.#lastExecutionTime = Date.now();
170
252
  job();
171
253
  if (canInitializeInterval) {
172
254
  this.#initializeIntervalIfNeeded();
@@ -180,16 +262,23 @@ export default class PQueue extends EventEmitter {
180
262
  if (this.#isIntervalIgnored || this.#intervalId !== undefined) {
181
263
  return;
182
264
  }
265
+ // Strict mode uses timeouts instead of interval timers
266
+ if (this.#strict) {
267
+ return;
268
+ }
183
269
  this.#intervalId = setInterval(() => {
184
270
  this.#onInterval();
185
271
  }, this.#interval);
186
272
  this.#intervalEnd = Date.now() + this.#interval;
187
273
  }
188
274
  #onInterval() {
189
- if (this.#intervalCount === 0 && this.#pending === 0 && this.#intervalId) {
190
- this.#clearIntervalTimer();
275
+ // Non-strict mode uses interval timers and intervalCount
276
+ if (!this.#strict) {
277
+ if (this.#intervalCount === 0 && this.#pending === 0 && this.#intervalId) {
278
+ this.#clearIntervalTimer();
279
+ }
280
+ this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
191
281
  }
192
- this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
193
282
  this.#processQueue();
194
283
  this.#scheduleRateLimitUpdate();
195
284
  }
@@ -253,11 +342,12 @@ export default class PQueue extends EventEmitter {
253
342
  this.#queue.setPriority(id, priority);
254
343
  }
255
344
  async add(function_, options = {}) {
256
- // In case `id` is not defined.
257
- options.id ??= (this.#idAssigner++).toString();
345
+ // Create a copy to avoid mutating the original options object
258
346
  options = {
259
347
  timeout: this.timeout,
260
348
  ...options,
349
+ // Assign unique ID if not provided
350
+ id: options.id ?? (this.#idAssigner++).toString(),
261
351
  };
262
352
  return new Promise((resolve, reject) => {
263
353
  // Create a unique symbol for tracking this task
@@ -279,14 +369,12 @@ export default class PQueue extends EventEmitter {
279
369
  options.signal?.throwIfAborted();
280
370
  }
281
371
  catch (error) {
282
- // Decrement the counter that was already incremented
283
- if (!this.#isIntervalIgnored) {
284
- this.#intervalCount--;
285
- }
372
+ this.#rollbackIntervalConsumption();
286
373
  // Clean up tracking before throwing
287
374
  this.#runningTasks.delete(taskSymbol);
288
375
  throw error;
289
376
  }
377
+ this.#lastExecutionTime = Date.now();
290
378
  let operation = function_({ signal: options.signal });
291
379
  if (options.timeout) {
292
380
  operation = pTimeout(Promise.resolve(operation), {
@@ -353,10 +441,22 @@ export default class PQueue extends EventEmitter {
353
441
  */
354
442
  clear() {
355
443
  this.#queue = new this.#queueClass();
444
+ // Clear interval timer since queue is now empty (consistent with #tryToStartAnother)
445
+ this.#clearIntervalTimer();
446
+ // Note: We preserve strict mode rate-limiting state (ticks and timeout)
447
+ // because clear() only clears queued tasks, not rate limit history.
448
+ // This ensures that rate limits are still enforced after clearing the queue.
356
449
  // Note: We don't clear #runningTasks as those tasks are still running
357
450
  // They will be removed when they complete in the finally block
358
451
  // Force synchronous update since clear() should have immediate effect
359
452
  this.#updateRateLimitState();
453
+ // Emit events so waiters (onEmpty, onIdle, onSizeLessThan) can resolve
454
+ this.emit('empty');
455
+ if (this.#pending === 0) {
456
+ this.#clearTimeoutTimer();
457
+ this.emit('idle');
458
+ }
459
+ this.emit('next');
360
460
  }
361
461
  /**
362
462
  Can be called multiple times. Useful if you for example add additional items at a later time.
@@ -455,7 +555,7 @@ export default class PQueue extends EventEmitter {
455
555
  ```
456
556
  */
457
557
  // eslint-disable-next-line @typescript-eslint/promise-function-async
458
- async onError() {
558
+ onError() {
459
559
  return new Promise((_resolve, reject) => {
460
560
  const handleError = (error) => {
461
561
  this.off('error', handleError);
@@ -530,11 +630,34 @@ export default class PQueue extends EventEmitter {
530
630
  this.#updateRateLimitState();
531
631
  });
532
632
  }
633
+ #rollbackIntervalConsumption() {
634
+ if (this.#isIntervalIgnored) {
635
+ return;
636
+ }
637
+ this.#rollbackIntervalSlot();
638
+ this.#scheduleRateLimitUpdate();
639
+ }
533
640
  #updateRateLimitState() {
534
641
  const previous = this.#rateLimitedInInterval;
535
- const shouldBeRateLimited = !this.#isIntervalIgnored
536
- && this.#intervalCount >= this.#intervalCap
537
- && this.#queue.size > 0;
642
+ // Early exit if rate limiting is disabled or queue is empty
643
+ if (this.#isIntervalIgnored || this.#queue.size === 0) {
644
+ if (previous) {
645
+ this.#rateLimitedInInterval = false;
646
+ this.emit('rateLimitCleared');
647
+ }
648
+ return;
649
+ }
650
+ // Get the current count based on mode
651
+ let count;
652
+ if (this.#strict) {
653
+ const now = Date.now();
654
+ this.#cleanupStrictTicks(now);
655
+ count = this.#getActiveTicksCount();
656
+ }
657
+ else {
658
+ count = this.#intervalCount;
659
+ }
660
+ const shouldBeRateLimited = count >= this.#intervalCap;
538
661
  if (shouldBeRateLimited !== previous) {
539
662
  this.#rateLimitedInInterval = shouldBeRateLimited;
540
663
  this.emit(shouldBeRateLimited ? 'rateLimit' : 'rateLimitCleared');
@@ -67,6 +67,22 @@ export type Options<QueueType extends Queue<RunFunction, QueueOptions>, QueueOpt
67
67
  @deprecated Renamed to `carryoverIntervalCount`.
68
68
  */
69
69
  readonly carryoverConcurrencyCount?: boolean;
70
+ /**
71
+ Whether to use strict mode for rate limiting (sliding window algorithm).
72
+
73
+ When enabled, ensures that no more than `intervalCap` tasks execute in any rolling `interval` window, rather than resetting the count at fixed intervals. This provides more predictable and evenly distributed execution.
74
+
75
+ @default false
76
+
77
+ For example, with `intervalCap: 2` and `interval: 1000`:
78
+ - __Default mode (fixed window)__: Tasks can burst at window boundaries. You could execute 2 tasks at 999ms and 2 more at 1000ms, resulting in 4 tasks within 1ms.
79
+ - __Strict mode (sliding window)__: Enforces that no more than 2 tasks execute in any 1000ms rolling window, preventing bursts.
80
+
81
+ Strict mode is more resource-intensive as it tracks individual execution timestamps. Use it when you need guaranteed rate-limit compliance, such as when interacting with APIs that enforce strict rate limits.
82
+
83
+ The `carryoverIntervalCount` option has no effect when `strict` mode is enabled, as strict mode tracks actual execution timestamps rather than counting pending tasks.
84
+ */
85
+ readonly strict?: boolean;
70
86
  } & TimeoutOptions;
71
87
  export type QueueAddOptions = {
72
88
  /**
@@ -116,6 +132,6 @@ export type TaskOptions = {
116
132
  }
117
133
  ```
118
134
  */
119
- readonly signal?: AbortSignal;
135
+ readonly signal?: AbortSignal | undefined;
120
136
  };
121
137
  export {};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "p-queue",
3
- "version": "9.0.1",
3
+ "version": "9.1.0",
4
4
  "description": "Promise queue with concurrency control",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/p-queue",
@@ -140,6 +140,25 @@ Default: `false`
140
140
 
141
141
  If `true`, specifies that any [pending](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) Promises, should be carried over into the next interval and counted against the `intervalCap`. If `false`, any of those pending Promises will not count towards the next `intervalCap`.
142
142
 
143
+ ##### strict
144
+
145
+ Type: `boolean`\
146
+ Default: `false`
147
+
148
+ Whether to use strict mode for rate limiting (sliding window algorithm).
149
+
150
+ When enabled, ensures that no more than `intervalCap` tasks execute in any rolling `interval` window, rather than resetting the count at fixed intervals. This provides more predictable and evenly distributed execution.
151
+
152
+ For example, with `intervalCap: 2` and `interval: 1000`:
153
+ - **Default mode (fixed window)**: Tasks can burst at window boundaries. You could execute 2 tasks at 999ms and 2 more at 1000ms, resulting in 4 tasks within 1ms.
154
+ - **Strict mode (sliding window)**: Enforces that no more than 2 tasks execute in any 1000ms rolling window, preventing bursts.
155
+
156
+ > [!NOTE]
157
+ > Strict mode is more resource-intensive as it tracks individual execution timestamps. Use it when you need guaranteed rate-limit compliance, such as when interacting with APIs that enforce strict rate limits.
158
+
159
+ > [!NOTE]
160
+ > The `carryoverIntervalCount` option has no effect when `strict` mode is enabled, as strict mode tracks actual execution timestamps rather than counting pending tasks.
161
+
143
162
  ### queue
144
163
 
145
164
  `PQueue` instance.
@@ -555,7 +574,7 @@ Useful if you for example add additional items at a later time.
555
574
 
556
575
  #### idle
557
576
 
558
- Emitted every time the queue becomes empty and all promises have completed; `queue.size === 0 && queue.pending === 0`.
577
+ Emitted whenever the queue becomes idle: both empty and with zero running tasks (`size === 0 && pending === 0`). If no tasks are ever added, it never fires.
559
578
 
560
579
  The difference with `empty` is that `idle` guarantees that all work from the queue has finished. `empty` merely signals that the queue is empty, but it could mean that some promises haven't completed yet.
561
580
 
@@ -828,6 +847,18 @@ const queue = new PQueue({queueClass: QueueClass});
828
847
 
829
848
  They are just different constraints. The `concurrency` option limits how many things run at the same time. The `intervalCap` option limits how many things run in total during the interval (over time).
830
849
 
850
+ #### When should I use `strict` mode for rate limiting?
851
+
852
+ Use `strict: true` when:
853
+ - You're interacting with APIs that enforce strict rate limits and will throttle or block you if you exceed them, even briefly
854
+ - You've experienced issues with the default fixed window mode (such as [#126](https://github.com/sindresorhus/p-queue/issues/126))
855
+ - You need guaranteed compliance with rate limits for any rolling time window
856
+
857
+ Use the default fixed window mode when:
858
+ - You don't have strict rate limit requirements
859
+ - Performance is more important than perfect rate limit distribution
860
+ - You're rate limiting for backpressure management rather than external API constraints
861
+
831
862
  #### How do I implement backpressure?
832
863
 
833
864
  Use `.onSizeLessThan()` to prevent the queue from growing unbounded and causing memory issues when producers are faster than consumers:
@@ -1,59 +1,87 @@
1
1
  import process from 'node:process';
2
2
 
3
- const ASCII_ETX_CODE = 0x03; // Ctrl+C emits this code
3
+ const ASCII_ETX_CODE = 0x03; // Ctrl+C
4
4
 
5
5
  class StdinDiscarder {
6
6
  #activeCount = 0;
7
+ #stdin;
8
+ #stdinWasPaused = false;
9
+ #stdinWasRaw = false;
10
+ #handleInputBound = chunk => {
11
+ if (!chunk?.length) {
12
+ return;
13
+ }
14
+
15
+ const code = typeof chunk === 'string' ? chunk.codePointAt(0) : chunk[0];
16
+ if (code === ASCII_ETX_CODE) {
17
+ if (process.listenerCount('SIGINT') > 0) {
18
+ process.emit('SIGINT');
19
+ } else {
20
+ process.kill(process.pid, 'SIGINT');
21
+ }
22
+ }
23
+ };
7
24
 
8
25
  start() {
9
26
  this.#activeCount++;
10
-
11
27
  if (this.#activeCount === 1) {
12
28
  this.#realStart();
13
29
  }
14
30
  }
15
31
 
16
32
  stop() {
17
- if (this.#activeCount <= 0) {
18
- throw new Error('`stop` called more times than `start`');
33
+ if (this.#activeCount === 0) {
34
+ return;
19
35
  }
20
36
 
21
- this.#activeCount--;
22
-
23
- if (this.#activeCount === 0) {
37
+ if (--this.#activeCount === 0) {
24
38
  this.#realStop();
25
39
  }
26
40
  }
27
41
 
28
42
  #realStart() {
29
- // No known way to make it work reliably on Windows.
30
- if (process.platform === 'win32' || !process.stdin.isTTY) {
43
+ const {stdin} = process;
44
+
45
+ if (process.platform === 'win32' || !stdin?.isTTY || typeof stdin.setRawMode !== 'function') {
46
+ this.#stdin = undefined;
31
47
  return;
32
48
  }
33
49
 
34
- process.stdin.setRawMode(true);
35
- process.stdin.on('data', this.#handleInput);
36
- process.stdin.resume();
50
+ this.#stdin = stdin;
51
+ this.#stdinWasPaused = stdin.isPaused();
52
+ this.#stdinWasRaw = Boolean(stdin.isRaw);
53
+
54
+ stdin.setRawMode(true);
55
+ stdin.prependListener('data', this.#handleInputBound);
56
+
57
+ if (this.#stdinWasPaused) {
58
+ stdin.resume();
59
+ }
37
60
  }
38
61
 
39
62
  #realStop() {
40
- if (!process.stdin.isTTY) {
63
+ if (!this.#stdin) {
41
64
  return;
42
65
  }
43
66
 
44
- process.stdin.off('data', this.#handleInput);
45
- process.stdin.pause();
46
- process.stdin.setRawMode(false);
47
- }
67
+ const stdin = this.#stdin;
48
68
 
49
- #handleInput(chunk) {
50
- // Allow Ctrl+C to gracefully exit.
51
- if (chunk[0] === ASCII_ETX_CODE) {
52
- process.emit('SIGINT');
69
+ stdin.off('data', this.#handleInputBound);
70
+
71
+ if (stdin.isTTY) {
72
+ stdin.setRawMode?.(this.#stdinWasRaw);
73
+ }
74
+
75
+ if (this.#stdinWasPaused) {
76
+ stdin.pause();
53
77
  }
78
+
79
+ this.#stdin = undefined;
80
+ this.#stdinWasPaused = false;
81
+ this.#stdinWasRaw = false;
54
82
  }
55
83
  }
56
84
 
57
85
  const stdinDiscarder = new StdinDiscarder();
58
86
 
59
- export default stdinDiscarder;
87
+ export default Object.freeze(stdinDiscarder);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stdin-discarder",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Discard stdin input except for Ctrl+C",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/stdin-discarder",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chalk",
3
- "version": "5.6.2",
3
+ "version": "5.3.0",
4
4
  "description": "Terminal string styling done right",
5
5
  "license": "MIT",
6
6
  "repository": "chalk/chalk",
@@ -16,7 +16,6 @@
16
16
  }
17
17
  },
18
18
  "types": "./source/index.d.ts",
19
- "sideEffects": false,
20
19
  "engines": {
21
20
  "node": "^12.17.0 || ^14.13 || >=16.0.0"
22
21
  },
@@ -59,9 +58,10 @@
59
58
  "log-update": "^5.0.0",
60
59
  "matcha": "^0.7.0",
61
60
  "tsd": "^0.19.0",
62
- "xo": "^0.57.0",
61
+ "xo": "^0.53.0",
63
62
  "yoctodelay": "^2.0.0"
64
63
  },
64
+ "sideEffects": false,
65
65
  "xo": {
66
66
  "rules": {
67
67
  "unicorn/prefer-string-slice": "off",
@@ -12,13 +12,59 @@
12
12
  [![Coverage Status](https://codecov.io/gh/chalk/chalk/branch/main/graph/badge.svg)](https://codecov.io/gh/chalk/chalk)
13
13
  [![npm dependents](https://badgen.net/npm/dependents/chalk)](https://www.npmjs.com/package/chalk?activeTab=dependents)
14
14
  [![Downloads](https://badgen.net/npm/dt/chalk)](https://www.npmjs.com/package/chalk)
15
+ [![run on repl.it](https://img.shields.io/badge/Run_on_Replit-f26207?logo=replit&logoColor=white)](https://repl.it/github/chalk/chalk)
15
16
 
16
17
  ![](media/screenshot.png)
17
18
 
18
- ## Info
19
-
20
- - [Why not switch to a smaller coloring package?](https://github.com/chalk/chalk?tab=readme-ov-file#why-not-switch-to-a-smaller-coloring-package)
21
- - See [yoctocolors](https://github.com/sindresorhus/yoctocolors) for a smaller alternative
19
+ <br>
20
+
21
+ ---
22
+
23
+ <div align="center">
24
+ <p>
25
+ <p>
26
+ <sup>
27
+ Sindre Sorhus' open source work is supported by the community on <a href="https://github.com/sponsors/sindresorhus">GitHub Sponsors</a>
28
+ </sup>
29
+ </p>
30
+ <sup>Special thanks to:</sup>
31
+ <br>
32
+ <br>
33
+ <a href="https://standardresume.co/tech">
34
+ <img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="160">
35
+ </a>
36
+ <br>
37
+ <br>
38
+ <a href="https://retool.com/?utm_campaign=sindresorhus">
39
+ <img src="https://sindresorhus.com/assets/thanks/retool-logo.svg" width="230">
40
+ </a>
41
+ <br>
42
+ <br>
43
+ <a href="https://strapi.io/?ref=sindresorhus">
44
+ <div>
45
+ <img src="https://sindresorhus.com/assets/thanks/strapi-logo-white-bg.png" width="220" alt="Strapi">
46
+ </div>
47
+ <b>Strapi is the leading open-source headless CMS.</b>
48
+ <div>
49
+ <sup>It’s 100% JavaScript, fully customizable, and developer-first.</sup>
50
+ </div>
51
+ </a>
52
+ <br>
53
+ <br>
54
+ <a href="https://www.stackaid.us/?utm_campaign=sindre">
55
+ <div>
56
+ <img src="https://sindresorhus.com/assets/thanks/stackaid-logo.png" width="230" alt="StackAid">
57
+ </div>
58
+ <b>Fund your open source dependencies</b>
59
+ </a>
60
+ <br>
61
+ <br>
62
+ </p>
63
+ </div>
64
+
65
+ ---
66
+
67
+ <br>
22
68
 
23
69
  ## Highlights
24
70
 
@@ -31,7 +77,7 @@
31
77
  - Doesn't extend `String.prototype`
32
78
  - Clean and focused
33
79
  - Actively maintained
34
- - [Used by ~115,000 packages](https://www.npmjs.com/browse/depended/chalk) as of July 4, 2024
80
+ - [Used by ~86,000 packages](https://www.npmjs.com/browse/depended/chalk) as of October 4, 2022
35
81
 
36
82
  ## Install
37
83
 
@@ -251,25 +297,9 @@ Since Chrome 69, ANSI escape codes are natively supported in the developer conso
251
297
 
252
298
  If you're on Windows, do yourself a favor and use [Windows Terminal](https://github.com/microsoft/terminal) instead of `cmd.exe`.
253
299
 
254
- ## FAQ
255
-
256
- ### Why not switch to a smaller coloring package?
257
-
258
- Chalk may be larger, but there is a reason for that. It offers a more user-friendly API, well-documented types, supports millions of colors, and covers edge cases that smaller alternatives miss. Chalk is mature, reliable, and built to last.
259
-
260
- But beyond the technical aspects, there's something more critical: trust and long-term maintenance. I have been active in open source for over a decade, and I'm committed to keeping Chalk maintained. Smaller packages might seem appealing now, but there's no guarantee they will be around for the long term, or that they won't become malicious over time.
261
-
262
- Chalk is also likely already in your dependency tree (since 100K+ packages depend on it), so switching won’t save space—in fact, it might increase it. npm deduplicates dependencies, so multiple Chalk instances turn into one, but adding another package alongside it will increase your overall size.
300
+ ## Origin story
263
301
 
264
- If the goal is to clean up the ecosystem, switching away from Chalk won’t even make a dent. The real problem lies with packages that have very deep dependency trees (for example, those including a lot of polyfills). Chalk has no dependencies. It's better to focus on impactful changes rather than minor optimizations.
265
-
266
- If absolute package size is important to you, I also maintain [yoctocolors](https://github.com/sindresorhus/yoctocolors), one of the smallest color packages out there.
267
-
268
- *\- [Sindre](https://github.com/sindresorhus)*
269
-
270
- ### But the smaller coloring package has benchmarks showing it is faster
271
-
272
- [Micro-benchmarks are flawed](https://sindresorhus.com/blog/micro-benchmark-fallacy) because they measure performance in unrealistic, isolated scenarios, often giving a distorted view of real-world performance. Don't believe marketing fluff. All the coloring packages are more than fast enough.
302
+ [colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68) and the package is unmaintained. Although there are other packages, they either do too much or not enough. Chalk is a clean and focused alternative.
273
303
 
274
304
  ## Related
275
305
 
@@ -289,8 +319,6 @@ If absolute package size is important to you, I also maintain [yoctocolors](http
289
319
  - [chalk-pipe](https://github.com/LitoMore/chalk-pipe) - Create chalk style schemes with simpler style strings
290
320
  - [terminal-link](https://github.com/sindresorhus/terminal-link) - Create clickable links in the terminal
291
321
 
292
- *(Not accepting additional entries)*
293
-
294
322
  ## Maintainers
295
323
 
296
324
  - [Sindre Sorhus](https://github.com/sindresorhus)
@@ -1,12 +1,7 @@
1
1
  // TODO: Make it this when TS suports that.
2
2
  // import {ModifierName, ForegroundColor, BackgroundColor, ColorName} from '#ansi-styles';
3
3
  // import {ColorInfo, ColorSupportLevel} from '#supports-color';
4
- import {
5
- ModifierName,
6
- ForegroundColorName,
7
- BackgroundColorName,
8
- ColorName,
9
- } from './vendor/ansi-styles/index.js';
4
+ import {ModifierName, ForegroundColorName, BackgroundColorName, ColorName} from './vendor/ansi-styles/index.js';
10
5
  import {ColorInfo, ColorSupportLevel} from './vendor/supports-color/index.js';
11
6
 
12
7
  export interface Options {