@develia/commons 0.4.23 → 0.4.25

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 (354) hide show
  1. package/dist/array.d.ts.map +1 -0
  2. package/dist/array.js.map +1 -0
  3. package/dist/async-pool-executor.d.ts.map +1 -0
  4. package/dist/async-pool-executor.js.map +1 -0
  5. package/dist/browser/form-utilities.d.ts.map +1 -0
  6. package/dist/browser/form-utilities.js.map +1 -0
  7. package/dist/browser/geo-location.d.ts.map +1 -0
  8. package/dist/browser/geo-location.js.map +1 -0
  9. package/dist/browser/index.d.ts.map +1 -0
  10. package/dist/browser/index.js.map +1 -0
  11. package/dist/browser/native-bridge.d.ts.map +1 -0
  12. package/dist/browser/native-bridge.js.map +1 -0
  13. package/dist/cancellable-promise.d.ts.map +1 -0
  14. package/dist/cancellable-promise.js.map +1 -0
  15. package/dist/clean-url-search-params.d.ts.map +1 -0
  16. package/dist/clean-url-search-params.js.map +1 -0
  17. package/dist/collections/cache-dictionary.d.ts.map +1 -0
  18. package/dist/collections/cache-dictionary.js.map +1 -0
  19. package/dist/collections/default-map.d.ts.map +1 -0
  20. package/dist/collections/default-map.js.map +1 -0
  21. package/dist/collections/default-weak-map.d.ts.map +1 -0
  22. package/dist/collections/default-weak-map.js.map +1 -0
  23. package/dist/collections/from.d.ts.map +1 -0
  24. package/dist/collections/from.js.map +1 -0
  25. package/dist/collections/index.d.ts.map +1 -0
  26. package/dist/collections/index.js.map +1 -0
  27. package/dist/debouncer.d.ts +60 -0
  28. package/dist/debouncer.d.ts.map +1 -0
  29. package/dist/debouncer.js +118 -0
  30. package/dist/debouncer.js.map +1 -0
  31. package/dist/draft.d.ts.map +1 -0
  32. package/dist/draft.js.map +1 -0
  33. package/dist/event-bus.d.ts.map +1 -0
  34. package/dist/event-bus.js.map +1 -0
  35. package/dist/{types/functions.d.ts → functions.d.ts} +5 -35
  36. package/dist/functions.d.ts.map +1 -0
  37. package/dist/{esm/functions.js → functions.js} +0 -85
  38. package/dist/functions.js.map +1 -0
  39. package/dist/{types/index.d.ts → index.d.ts} +4 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/{web/index.js → index.js} +2 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/key-value-pair.d.ts.map +1 -0
  44. package/dist/key-value-pair.js.map +1 -0
  45. package/dist/keyed-debouncer.d.ts +56 -0
  46. package/dist/keyed-debouncer.d.ts.map +1 -0
  47. package/dist/keyed-debouncer.js +109 -0
  48. package/dist/keyed-debouncer.js.map +1 -0
  49. package/dist/lazy.d.ts.map +1 -0
  50. package/dist/lazy.js.map +1 -0
  51. package/dist/math.d.ts.map +1 -0
  52. package/dist/math.js.map +1 -0
  53. package/dist/order.d.ts.map +1 -0
  54. package/dist/order.js.map +1 -0
  55. package/dist/parse.d.ts.map +1 -0
  56. package/dist/parse.js.map +1 -0
  57. package/dist/patch.d.ts.map +1 -0
  58. package/dist/patch.js.map +1 -0
  59. package/dist/range.d.ts.map +1 -0
  60. package/dist/range.js.map +1 -0
  61. package/dist/result.d.ts.map +1 -0
  62. package/dist/result.js.map +1 -0
  63. package/dist/stopwatch.d.ts.map +1 -0
  64. package/dist/stopwatch.js.map +1 -0
  65. package/dist/string.d.ts.map +1 -0
  66. package/dist/string.js.map +1 -0
  67. package/dist/timer.d.ts.map +1 -0
  68. package/dist/timer.js.map +1 -0
  69. package/dist/timespan.d.ts.map +1 -0
  70. package/dist/timespan.js.map +1 -0
  71. package/dist/triggers.d.ts.map +1 -0
  72. package/dist/triggers.js.map +1 -0
  73. package/dist/type-code.d.ts.map +1 -0
  74. package/dist/type-code.js.map +1 -0
  75. package/dist/type.d.ts.map +1 -0
  76. package/dist/type.js.map +1 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js.map +1 -0
  79. package/dist/{types/utilities.d.ts → utilities.d.ts} +8 -3
  80. package/dist/utilities.d.ts.map +1 -0
  81. package/dist/{web/utilities.js → utilities.js} +27 -4
  82. package/dist/utilities.js.map +1 -0
  83. package/package.json +13 -62
  84. package/src/debouncer.ts +135 -0
  85. package/src/functions.ts +5 -114
  86. package/src/index.ts +4 -0
  87. package/src/keyed-debouncer.ts +121 -0
  88. package/src/utilities.ts +30 -4
  89. package/dist/cjs/array.js +0 -106
  90. package/dist/cjs/array.js.map +0 -1
  91. package/dist/cjs/async-pool-executor.js +0 -79
  92. package/dist/cjs/async-pool-executor.js.map +0 -1
  93. package/dist/cjs/browser/form-utilities.js +0 -162
  94. package/dist/cjs/browser/form-utilities.js.map +0 -1
  95. package/dist/cjs/browser/geo-location.js +0 -78
  96. package/dist/cjs/browser/geo-location.js.map +0 -1
  97. package/dist/cjs/browser/index.js +0 -99
  98. package/dist/cjs/browser/index.js.map +0 -1
  99. package/dist/cjs/browser/native-bridge.js +0 -81
  100. package/dist/cjs/browser/native-bridge.js.map +0 -1
  101. package/dist/cjs/cancellable-promise.js +0 -81
  102. package/dist/cjs/cancellable-promise.js.map +0 -1
  103. package/dist/cjs/clean-url-search-params.js +0 -34
  104. package/dist/cjs/clean-url-search-params.js.map +0 -1
  105. package/dist/cjs/collections/cache-dictionary.js +0 -102
  106. package/dist/cjs/collections/cache-dictionary.js.map +0 -1
  107. package/dist/cjs/collections/default-map.js +0 -45
  108. package/dist/cjs/collections/default-map.js.map +0 -1
  109. package/dist/cjs/collections/default-weak-map.js +0 -46
  110. package/dist/cjs/collections/default-weak-map.js.map +0 -1
  111. package/dist/cjs/collections/from.js +0 -778
  112. package/dist/cjs/collections/from.js.map +0 -1
  113. package/dist/cjs/collections/index.js +0 -15
  114. package/dist/cjs/collections/index.js.map +0 -1
  115. package/dist/cjs/draft.js +0 -225
  116. package/dist/cjs/draft.js.map +0 -1
  117. package/dist/cjs/event-bus.js +0 -71
  118. package/dist/cjs/event-bus.js.map +0 -1
  119. package/dist/cjs/functions.js +0 -348
  120. package/dist/cjs/functions.js.map +0 -1
  121. package/dist/cjs/index.js +0 -80
  122. package/dist/cjs/index.js.map +0 -1
  123. package/dist/cjs/key-value-pair.js +0 -22
  124. package/dist/cjs/key-value-pair.js.map +0 -1
  125. package/dist/cjs/lazy.js +0 -29
  126. package/dist/cjs/lazy.js.map +0 -1
  127. package/dist/cjs/math.js +0 -37
  128. package/dist/cjs/math.js.map +0 -1
  129. package/dist/cjs/order.js +0 -9
  130. package/dist/cjs/order.js.map +0 -1
  131. package/dist/cjs/parse.js +0 -174
  132. package/dist/cjs/parse.js.map +0 -1
  133. package/dist/cjs/patch.js +0 -52
  134. package/dist/cjs/patch.js.map +0 -1
  135. package/dist/cjs/range.js +0 -154
  136. package/dist/cjs/range.js.map +0 -1
  137. package/dist/cjs/result.js +0 -3
  138. package/dist/cjs/result.js.map +0 -1
  139. package/dist/cjs/stopwatch.js +0 -46
  140. package/dist/cjs/stopwatch.js.map +0 -1
  141. package/dist/cjs/string.js +0 -154
  142. package/dist/cjs/string.js.map +0 -1
  143. package/dist/cjs/timer.js +0 -57
  144. package/dist/cjs/timer.js.map +0 -1
  145. package/dist/cjs/timespan.js +0 -367
  146. package/dist/cjs/timespan.js.map +0 -1
  147. package/dist/cjs/triggers.js +0 -103
  148. package/dist/cjs/triggers.js.map +0 -1
  149. package/dist/cjs/type-code.js +0 -18
  150. package/dist/cjs/type-code.js.map +0 -1
  151. package/dist/cjs/type.js +0 -296
  152. package/dist/cjs/type.js.map +0 -1
  153. package/dist/cjs/types.js +0 -3
  154. package/dist/cjs/types.js.map +0 -1
  155. package/dist/cjs/utilities.js +0 -598
  156. package/dist/cjs/utilities.js.map +0 -1
  157. package/dist/esm/array.js.map +0 -1
  158. package/dist/esm/async-pool-executor.js.map +0 -1
  159. package/dist/esm/browser/form-utilities.js.map +0 -1
  160. package/dist/esm/browser/geo-location.js.map +0 -1
  161. package/dist/esm/browser/index.js.map +0 -1
  162. package/dist/esm/browser/native-bridge.js.map +0 -1
  163. package/dist/esm/cancellable-promise.js.map +0 -1
  164. package/dist/esm/clean-url-search-params.js.map +0 -1
  165. package/dist/esm/collections/cache-dictionary.js.map +0 -1
  166. package/dist/esm/collections/default-map.js.map +0 -1
  167. package/dist/esm/collections/default-weak-map.js.map +0 -1
  168. package/dist/esm/collections/from.js.map +0 -1
  169. package/dist/esm/collections/index.js.map +0 -1
  170. package/dist/esm/draft.js.map +0 -1
  171. package/dist/esm/event-bus.js.map +0 -1
  172. package/dist/esm/functions.js.map +0 -1
  173. package/dist/esm/index.js +0 -38
  174. package/dist/esm/index.js.map +0 -1
  175. package/dist/esm/key-value-pair.js.map +0 -1
  176. package/dist/esm/lazy.js.map +0 -1
  177. package/dist/esm/math.js.map +0 -1
  178. package/dist/esm/order.js.map +0 -1
  179. package/dist/esm/parse.js.map +0 -1
  180. package/dist/esm/patch.js.map +0 -1
  181. package/dist/esm/range.js.map +0 -1
  182. package/dist/esm/result.js.map +0 -1
  183. package/dist/esm/stopwatch.js.map +0 -1
  184. package/dist/esm/string.js.map +0 -1
  185. package/dist/esm/timer.js.map +0 -1
  186. package/dist/esm/timespan.js.map +0 -1
  187. package/dist/esm/triggers.js.map +0 -1
  188. package/dist/esm/type-code.js.map +0 -1
  189. package/dist/esm/type.js.map +0 -1
  190. package/dist/esm/types.js.map +0 -1
  191. package/dist/esm/utilities.js +0 -567
  192. package/dist/esm/utilities.js.map +0 -1
  193. package/dist/types/array.d.ts.map +0 -1
  194. package/dist/types/async-pool-executor.d.ts.map +0 -1
  195. package/dist/types/browser/form-utilities.d.ts.map +0 -1
  196. package/dist/types/browser/geo-location.d.ts.map +0 -1
  197. package/dist/types/browser/index.d.ts.map +0 -1
  198. package/dist/types/browser/native-bridge.d.ts.map +0 -1
  199. package/dist/types/cancellable-promise.d.ts.map +0 -1
  200. package/dist/types/clean-url-search-params.d.ts.map +0 -1
  201. package/dist/types/collections/cache-dictionary.d.ts.map +0 -1
  202. package/dist/types/collections/default-map.d.ts.map +0 -1
  203. package/dist/types/collections/default-weak-map.d.ts.map +0 -1
  204. package/dist/types/collections/from.d.ts.map +0 -1
  205. package/dist/types/collections/index.d.ts.map +0 -1
  206. package/dist/types/draft.d.ts.map +0 -1
  207. package/dist/types/event-bus.d.ts.map +0 -1
  208. package/dist/types/functions.d.ts.map +0 -1
  209. package/dist/types/index.d.ts.map +0 -1
  210. package/dist/types/key-value-pair.d.ts.map +0 -1
  211. package/dist/types/lazy.d.ts.map +0 -1
  212. package/dist/types/math.d.ts.map +0 -1
  213. package/dist/types/order.d.ts.map +0 -1
  214. package/dist/types/parse.d.ts.map +0 -1
  215. package/dist/types/patch.d.ts.map +0 -1
  216. package/dist/types/range.d.ts.map +0 -1
  217. package/dist/types/result.d.ts.map +0 -1
  218. package/dist/types/stopwatch.d.ts.map +0 -1
  219. package/dist/types/string.d.ts.map +0 -1
  220. package/dist/types/timer.d.ts.map +0 -1
  221. package/dist/types/timespan.d.ts.map +0 -1
  222. package/dist/types/triggers.d.ts.map +0 -1
  223. package/dist/types/type-code.d.ts.map +0 -1
  224. package/dist/types/type.d.ts.map +0 -1
  225. package/dist/types/types.d.ts.map +0 -1
  226. package/dist/types/utilities.d.ts.map +0 -1
  227. package/dist/web/array.js +0 -98
  228. package/dist/web/array.js.map +0 -1
  229. package/dist/web/async-pool-executor.js +0 -76
  230. package/dist/web/async-pool-executor.js.map +0 -1
  231. package/dist/web/browser/form-utilities.js +0 -155
  232. package/dist/web/browser/form-utilities.js.map +0 -1
  233. package/dist/web/browser/geo-location.js +0 -75
  234. package/dist/web/browser/geo-location.js.map +0 -1
  235. package/dist/web/browser/index.js +0 -75
  236. package/dist/web/browser/index.js.map +0 -1
  237. package/dist/web/browser/native-bridge.js +0 -78
  238. package/dist/web/browser/native-bridge.js.map +0 -1
  239. package/dist/web/cancellable-promise.js +0 -78
  240. package/dist/web/cancellable-promise.js.map +0 -1
  241. package/dist/web/clean-url-search-params.js +0 -31
  242. package/dist/web/clean-url-search-params.js.map +0 -1
  243. package/dist/web/collections/cache-dictionary.js +0 -99
  244. package/dist/web/collections/cache-dictionary.js.map +0 -1
  245. package/dist/web/collections/default-map.js +0 -42
  246. package/dist/web/collections/default-map.js.map +0 -1
  247. package/dist/web/collections/default-weak-map.js +0 -43
  248. package/dist/web/collections/default-weak-map.js.map +0 -1
  249. package/dist/web/collections/from.js +0 -770
  250. package/dist/web/collections/from.js.map +0 -1
  251. package/dist/web/collections/index.js +0 -5
  252. package/dist/web/collections/index.js.map +0 -1
  253. package/dist/web/draft.js +0 -222
  254. package/dist/web/draft.js.map +0 -1
  255. package/dist/web/event-bus.js +0 -66
  256. package/dist/web/event-bus.js.map +0 -1
  257. package/dist/web/functions.js +0 -336
  258. package/dist/web/functions.js.map +0 -1
  259. package/dist/web/index.js.map +0 -1
  260. package/dist/web/key-value-pair.js +0 -19
  261. package/dist/web/key-value-pair.js.map +0 -1
  262. package/dist/web/lazy.js +0 -26
  263. package/dist/web/lazy.js.map +0 -1
  264. package/dist/web/math.js +0 -33
  265. package/dist/web/math.js.map +0 -1
  266. package/dist/web/order.js +0 -7
  267. package/dist/web/order.js.map +0 -1
  268. package/dist/web/parse.js +0 -169
  269. package/dist/web/parse.js.map +0 -1
  270. package/dist/web/patch.js +0 -48
  271. package/dist/web/patch.js.map +0 -1
  272. package/dist/web/range.js +0 -151
  273. package/dist/web/range.js.map +0 -1
  274. package/dist/web/result.js +0 -2
  275. package/dist/web/result.js.map +0 -1
  276. package/dist/web/stopwatch.js +0 -40
  277. package/dist/web/stopwatch.js.map +0 -1
  278. package/dist/web/string.js +0 -142
  279. package/dist/web/string.js.map +0 -1
  280. package/dist/web/timer.js +0 -54
  281. package/dist/web/timer.js.map +0 -1
  282. package/dist/web/timespan.js +0 -365
  283. package/dist/web/timespan.js.map +0 -1
  284. package/dist/web/triggers.js +0 -99
  285. package/dist/web/triggers.js.map +0 -1
  286. package/dist/web/type-code.js +0 -16
  287. package/dist/web/type-code.js.map +0 -1
  288. package/dist/web/type.js +0 -292
  289. package/dist/web/type.js.map +0 -1
  290. package/dist/web/types.js +0 -2
  291. package/dist/web/types.js.map +0 -1
  292. package/dist/web/utilities.js.map +0 -1
  293. /package/dist/{types/array.d.ts → array.d.ts} +0 -0
  294. /package/dist/{esm/array.js → array.js} +0 -0
  295. /package/dist/{types/async-pool-executor.d.ts → async-pool-executor.d.ts} +0 -0
  296. /package/dist/{esm/async-pool-executor.js → async-pool-executor.js} +0 -0
  297. /package/dist/{types/browser → browser}/form-utilities.d.ts +0 -0
  298. /package/dist/{esm/browser → browser}/form-utilities.js +0 -0
  299. /package/dist/{types/browser → browser}/geo-location.d.ts +0 -0
  300. /package/dist/{esm/browser → browser}/geo-location.js +0 -0
  301. /package/dist/{types/browser → browser}/index.d.ts +0 -0
  302. /package/dist/{esm/browser → browser}/index.js +0 -0
  303. /package/dist/{types/browser → browser}/native-bridge.d.ts +0 -0
  304. /package/dist/{esm/browser → browser}/native-bridge.js +0 -0
  305. /package/dist/{types/cancellable-promise.d.ts → cancellable-promise.d.ts} +0 -0
  306. /package/dist/{esm/cancellable-promise.js → cancellable-promise.js} +0 -0
  307. /package/dist/{types/clean-url-search-params.d.ts → clean-url-search-params.d.ts} +0 -0
  308. /package/dist/{esm/clean-url-search-params.js → clean-url-search-params.js} +0 -0
  309. /package/dist/{types/collections → collections}/cache-dictionary.d.ts +0 -0
  310. /package/dist/{esm/collections → collections}/cache-dictionary.js +0 -0
  311. /package/dist/{types/collections → collections}/default-map.d.ts +0 -0
  312. /package/dist/{esm/collections → collections}/default-map.js +0 -0
  313. /package/dist/{types/collections → collections}/default-weak-map.d.ts +0 -0
  314. /package/dist/{esm/collections → collections}/default-weak-map.js +0 -0
  315. /package/dist/{types/collections → collections}/from.d.ts +0 -0
  316. /package/dist/{esm/collections → collections}/from.js +0 -0
  317. /package/dist/{types/collections → collections}/index.d.ts +0 -0
  318. /package/dist/{esm/collections → collections}/index.js +0 -0
  319. /package/dist/{types/draft.d.ts → draft.d.ts} +0 -0
  320. /package/dist/{esm/draft.js → draft.js} +0 -0
  321. /package/dist/{types/event-bus.d.ts → event-bus.d.ts} +0 -0
  322. /package/dist/{esm/event-bus.js → event-bus.js} +0 -0
  323. /package/dist/{types/key-value-pair.d.ts → key-value-pair.d.ts} +0 -0
  324. /package/dist/{esm/key-value-pair.js → key-value-pair.js} +0 -0
  325. /package/dist/{types/lazy.d.ts → lazy.d.ts} +0 -0
  326. /package/dist/{esm/lazy.js → lazy.js} +0 -0
  327. /package/dist/{types/math.d.ts → math.d.ts} +0 -0
  328. /package/dist/{esm/math.js → math.js} +0 -0
  329. /package/dist/{types/order.d.ts → order.d.ts} +0 -0
  330. /package/dist/{esm/order.js → order.js} +0 -0
  331. /package/dist/{types/parse.d.ts → parse.d.ts} +0 -0
  332. /package/dist/{esm/parse.js → parse.js} +0 -0
  333. /package/dist/{types/patch.d.ts → patch.d.ts} +0 -0
  334. /package/dist/{esm/patch.js → patch.js} +0 -0
  335. /package/dist/{types/range.d.ts → range.d.ts} +0 -0
  336. /package/dist/{esm/range.js → range.js} +0 -0
  337. /package/dist/{types/result.d.ts → result.d.ts} +0 -0
  338. /package/dist/{esm/result.js → result.js} +0 -0
  339. /package/dist/{types/stopwatch.d.ts → stopwatch.d.ts} +0 -0
  340. /package/dist/{esm/stopwatch.js → stopwatch.js} +0 -0
  341. /package/dist/{types/string.d.ts → string.d.ts} +0 -0
  342. /package/dist/{esm/string.js → string.js} +0 -0
  343. /package/dist/{types/timer.d.ts → timer.d.ts} +0 -0
  344. /package/dist/{esm/timer.js → timer.js} +0 -0
  345. /package/dist/{types/timespan.d.ts → timespan.d.ts} +0 -0
  346. /package/dist/{esm/timespan.js → timespan.js} +0 -0
  347. /package/dist/{types/triggers.d.ts → triggers.d.ts} +0 -0
  348. /package/dist/{esm/triggers.js → triggers.js} +0 -0
  349. /package/dist/{types/type-code.d.ts → type-code.d.ts} +0 -0
  350. /package/dist/{esm/type-code.js → type-code.js} +0 -0
  351. /package/dist/{types/type.d.ts → type.d.ts} +0 -0
  352. /package/dist/{esm/type.js → type.js} +0 -0
  353. /package/dist/{types/types.d.ts → types.d.ts} +0 -0
  354. /package/dist/{esm/types.js → types.js} +0 -0
@@ -0,0 +1,135 @@
1
+ import {delay} from "./utilities.js";
2
+ import {noop} from "./functions.js";
3
+
4
+ /**
5
+ * Cancellable task. It must honour `signal`: pass it to fetch/SDKs and check
6
+ * `signal.aborted` before producing irreversible side effects.
7
+ */
8
+ export type DebouncedTask = (signal: AbortSignal) => Promise<void>;
9
+
10
+ /** Options accepted by {@link Debouncer}. */
11
+ export interface DebouncerOptions {
12
+ /** Default wait, in milliseconds, before a task is executed. */
13
+ delayMs: number;
14
+ /** Called every time a task is scheduled, after any previous task has been cancelled. */
15
+ onSchedule?: () => void;
16
+ /** Called when the wait elapses and the task is about to run. */
17
+ onStart?: () => void;
18
+ /** Called when a task completes without throwing and without being cancelled. */
19
+ onSuccess?: () => void;
20
+ /** Called when a live task is cancelled, either by {@link Debouncer.cancel} or by being replaced. */
21
+ onCancel?: () => void;
22
+ /** Receives task errors. Cancellations never reach this callback. Defaults to `console.error`. */
23
+ onError?: (error: unknown) => void;
24
+ /** Called every time the debouncer goes from busy to idle. */
25
+ onIdle?: () => void;
26
+ }
27
+
28
+ /**
29
+ * Keeps at most one live task.
30
+ * Scheduling a new task cancels the previous one, whether it was still
31
+ * waiting or already running.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const debouncer = new Debouncer({delayMs: 1500});
36
+ *
37
+ * debouncer.schedule(async (signal) => {
38
+ * const reply = await callAI(messages, {signal});
39
+ * if (signal.aborted) return;
40
+ * await sendReply(reply);
41
+ * });
42
+ * ```
43
+ */
44
+ export default class Debouncer {
45
+ #controller: AbortController | undefined;
46
+ readonly #delayMs: number;
47
+ readonly #onSchedule: () => void;
48
+ readonly #onStart: () => void;
49
+ readonly #onSuccess: () => void;
50
+ readonly #onCancel: () => void;
51
+ readonly #onError: (error: unknown) => void;
52
+ readonly #onIdle: () => void;
53
+
54
+ constructor(options: DebouncerOptions) {
55
+ assertValidDelay(options.delayMs);
56
+ this.#delayMs = options.delayMs;
57
+ this.#onSchedule = options.onSchedule ?? noop;
58
+ this.#onStart = options.onStart ?? noop;
59
+ this.#onSuccess = options.onSuccess ?? noop;
60
+ this.#onCancel = options.onCancel ?? noop;
61
+ this.#onError = options.onError ?? console.error;
62
+ this.#onIdle = options.onIdle ?? noop;
63
+ }
64
+
65
+ /** Whether a task is waiting or running. */
66
+ get isPending(): boolean {
67
+ return this.#controller !== undefined;
68
+ }
69
+
70
+ /**
71
+ * Schedule `task`, cancelling any previous task.
72
+ *
73
+ * @param task - The task to run once the delay elapses.
74
+ * @param delayMs - Wait for this task only. Defaults to the configured `delayMs`.
75
+ */
76
+ schedule(task: DebouncedTask, delayMs: number = this.#delayMs): void {
77
+ assertValidDelay(delayMs);
78
+ if (this.#controller !== undefined) {
79
+ this.#controller.abort();
80
+ this.#onCancel();
81
+ }
82
+
83
+ const controller = new AbortController();
84
+ this.#controller = controller;
85
+ this.#onSchedule();
86
+ void this.#run(task, delayMs, controller);
87
+ }
88
+
89
+ /** Cancel the current task, if any. */
90
+ cancel(): void {
91
+ if (this.#controller === undefined) {
92
+ return;
93
+ }
94
+ this.#controller.abort();
95
+ this.#onCancel();
96
+ this.#becomeIdle();
97
+ }
98
+
99
+ async #run(task: DebouncedTask, delayMs: number, controller: AbortController): Promise<void> {
100
+ const signal = controller.signal;
101
+ try {
102
+ await delay(delayMs, signal);
103
+ this.#onStart();
104
+ await task(signal);
105
+ if (!signal.aborted) {
106
+ this.#onSuccess();
107
+ }
108
+ } catch (error) {
109
+ if (!signal.aborted) {
110
+ this.#onError(error);
111
+ }
112
+ } finally {
113
+ // A replaced or cancelled task must not touch the current state.
114
+ if (this.#controller === controller) {
115
+ this.#becomeIdle();
116
+ }
117
+ }
118
+ }
119
+
120
+ #becomeIdle(): void {
121
+ this.#controller = undefined;
122
+ this.#onIdle();
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Throw a `RangeError` unless `delayMs` is a finite number >= 0.
128
+ *
129
+ * @internal Shared with `KeyedDebouncer`; not part of the public API.
130
+ */
131
+ export function assertValidDelay(delayMs: number): void {
132
+ if (!Number.isFinite(delayMs) || delayMs < 0) {
133
+ throw new RangeError(`delayMs must be a number >= 0 (received: ${delayMs})`);
134
+ }
135
+ }
package/src/functions.ts CHANGED
@@ -1,14 +1,6 @@
1
1
  type AnyFunction = (this: any, ...args: any[]) => any;
2
2
  type TimerHandle = ReturnType<typeof setTimeout>;
3
3
 
4
- /** Options that control when a debounced function is executed. */
5
- export interface DebounceOptions {
6
- /** Execute on the first call in a debounce window. Defaults to false. */
7
- leading?: boolean;
8
- /** Execute after the debounce window using the latest arguments. Defaults to true. */
9
- trailing?: boolean;
10
- }
11
-
12
4
  /** Options that control when a throttled function is executed. */
13
5
  export interface ThrottleOptions {
14
6
  /** Execute immediately on the first call in a throttle window. Defaults to true. */
@@ -18,15 +10,15 @@ export interface ThrottleOptions {
18
10
  }
19
11
 
20
12
  /**
21
- * A delayed function returned by {@link debounce} or {@link throttle}.
13
+ * A delayed function returned by {@link throttle}.
22
14
  *
23
15
  * @example
24
16
  * ```ts
25
- * const saveLater = debounce(save, 250);
26
- * saveLater();
17
+ * const refresh = throttle(loadData, 250);
18
+ * refresh();
27
19
  *
28
- * if (saveLater.pending()) {
29
- * saveLater.flush();
20
+ * if (refresh.pending()) {
21
+ * refresh.flush();
30
22
  * }
31
23
  * ```
32
24
  */
@@ -199,107 +191,6 @@ export function memoize<F extends AnyFunction, K = Parameters<F>[0]>(
199
191
  return memoized;
200
192
  }
201
193
 
202
- /**
203
- * Delay a function until no calls have been made for the configured wait time.
204
- * The latest arguments and this-context are used for the trailing execution.
205
- *
206
- * @example
207
- * ```ts
208
- * const search = debounce((query: string) => {
209
- * console.log(query);
210
- * }, 300);
211
- *
212
- * search("com");
213
- * search("commons"); // only this value is printed after 300 ms
214
- * ```
215
- *
216
- * @example
217
- * ```ts
218
- * const track = debounce(sendAnalytics, 1000, {leading: true, trailing: false});
219
- *
220
- * track("open"); // executed immediately
221
- * track.cancel(); // cancel the pending debounce window
222
- * ```
223
- */
224
- export function debounce<F extends AnyFunction>(
225
- fn: F,
226
- wait: number,
227
- options: DebounceOptions = {}
228
- ): DeferredFunction<F> {
229
- const delay = normalizeDelay(wait);
230
- const leading = options.leading ?? false;
231
- const trailing = options.trailing ?? true;
232
-
233
- let timer: TimerHandle | undefined;
234
- let lastArgs: Parameters<F> | undefined;
235
- let lastThis: ThisParameterType<F> | undefined;
236
- let result: ReturnType<F> | undefined;
237
-
238
- const invoke = (): ReturnType<F> => {
239
- const args = lastArgs!;
240
- const context = lastThis!;
241
- lastArgs = undefined;
242
- lastThis = undefined;
243
- result = fn.apply(context, args);
244
- return result as ReturnType<F>;
245
- };
246
-
247
- const expire = (): void => {
248
- timer = undefined;
249
- if (trailing && lastArgs) {
250
- invoke();
251
- } else {
252
- lastArgs = undefined;
253
- lastThis = undefined;
254
- }
255
- };
256
-
257
- const start = (): void => {
258
- timer = setTimeout(expire, delay);
259
- };
260
-
261
- const debounced = function (this: ThisParameterType<F>, ...args: Parameters<F>): ReturnType<F> | undefined {
262
- const invokeLeading = leading && timer === undefined;
263
- lastArgs = args;
264
- lastThis = this;
265
-
266
- if (timer !== undefined) {
267
- clearTimeout(timer);
268
- }
269
- start();
270
-
271
- if (invokeLeading) {
272
- return invoke();
273
- }
274
-
275
- return result;
276
- } as DeferredFunction<F>;
277
-
278
- debounced.cancel = (): void => {
279
- if (timer !== undefined) {
280
- clearTimeout(timer);
281
- }
282
- timer = undefined;
283
- lastArgs = undefined;
284
- lastThis = undefined;
285
- };
286
-
287
- debounced.flush = (): ReturnType<F> | undefined => {
288
- if (timer !== undefined) {
289
- clearTimeout(timer);
290
- timer = undefined;
291
- if (lastArgs && trailing) {
292
- return invoke();
293
- }
294
- }
295
- return result;
296
- };
297
-
298
- debounced.pending = (): boolean => timer !== undefined;
299
-
300
- return debounced;
301
- }
302
-
303
194
  /**
304
195
  * Limit how often a function can run during a time window.
305
196
  * Calls made while the function is throttled can optionally be replayed once at the end with the latest arguments.
package/src/index.ts CHANGED
@@ -24,6 +24,10 @@ export {default as Stopwatch} from './stopwatch.js';
24
24
  export type {Result} from './result.js';
25
25
  export {TimeTrigger, CountTrigger} from './triggers.js';
26
26
  export {default as AsyncPoolExecutor} from './async-pool-executor.js';
27
+ export {default as Debouncer} from './debouncer.js';
28
+ export type {DebouncedTask, DebouncerOptions} from './debouncer.js';
29
+ export {default as KeyedDebouncer} from './keyed-debouncer.js';
30
+ export type {KeyedDebouncerOptions} from './keyed-debouncer.js';
27
31
 
28
32
  // ─── Patterns ────────────────────────────────────────
29
33
  export {default as Lazy} from './lazy.js';
@@ -0,0 +1,121 @@
1
+ import Debouncer, {assertValidDelay, type DebouncedTask} from "./debouncer.js";
2
+ import {noop} from "./functions.js";
3
+
4
+ /** Options accepted by {@link KeyedDebouncer}. Every callback receives the affected key. */
5
+ export interface KeyedDebouncerOptions<K> {
6
+ /** Default wait, in milliseconds, before a task is executed. */
7
+ delayMs: number;
8
+ /** Called every time a task is scheduled for `key`, after any previous task of that key has been cancelled. */
9
+ onSchedule?: (key: K) => void;
10
+ /** Called when the wait elapses and the task of `key` is about to run. */
11
+ onStart?: (key: K) => void;
12
+ /** Called when the task of `key` completes without throwing and without being cancelled. */
13
+ onSuccess?: (key: K) => void;
14
+ /** Called when a live task of `key` is cancelled, either explicitly or by being replaced. */
15
+ onCancel?: (key: K) => void;
16
+ /** Receives task errors. Cancellations never reach this callback. Defaults to `console.error`. */
17
+ onError?: (error: unknown, key: K) => void;
18
+ /** Called when `key` has no more pending work, after it has been removed from the debouncer. */
19
+ onIdle?: (key: K) => void;
20
+ }
21
+
22
+ /**
23
+ * An independent {@link Debouncer} per key.
24
+ * Debouncers only exist for keys with pending work: each one is created when
25
+ * its first task is scheduled and removed as soon as it becomes idle.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const debouncer = new KeyedDebouncer<string>({delayMs: 1500});
30
+ *
31
+ * debouncer.schedule(chatId, async (signal) => {
32
+ * const pending = await store.getPending(chatId);
33
+ * const reply = await callAI(pending, {signal});
34
+ * if (signal.aborted) return;
35
+ * await sendReply(chatId, reply);
36
+ * });
37
+ * ```
38
+ */
39
+ export default class KeyedDebouncer<K = string> {
40
+ readonly #debouncers = new Map<K, Debouncer>();
41
+ readonly #delayMs: number;
42
+ readonly #onSchedule: (key: K) => void;
43
+ readonly #onStart: (key: K) => void;
44
+ readonly #onSuccess: (key: K) => void;
45
+ readonly #onCancel: (key: K) => void;
46
+ readonly #onError: (error: unknown, key: K) => void;
47
+ readonly #onIdle: (key: K) => void;
48
+
49
+ constructor(options: KeyedDebouncerOptions<K>) {
50
+ assertValidDelay(options.delayMs);
51
+ this.#delayMs = options.delayMs;
52
+ this.#onSchedule = options.onSchedule ?? noop;
53
+ this.#onStart = options.onStart ?? noop;
54
+ this.#onSuccess = options.onSuccess ?? noop;
55
+ this.#onCancel = options.onCancel ?? noop;
56
+ this.#onError = options.onError ?? console.error;
57
+ this.#onIdle = options.onIdle ?? noop;
58
+ }
59
+
60
+ /** Number of keys with a task waiting or running. */
61
+ get size(): number {
62
+ return this.#debouncers.size;
63
+ }
64
+
65
+ /**
66
+ * Schedule `task` for `key`, cancelling any previous task of that key.
67
+ *
68
+ * @param key - The key whose task is replaced.
69
+ * @param task - The task to run once the delay elapses.
70
+ * @param delayMs - Wait for this task only. Defaults to the configured `delayMs`.
71
+ */
72
+ schedule(key: K, task: DebouncedTask, delayMs?: number): void {
73
+ const existing = this.#debouncers.get(key);
74
+ if (existing !== undefined) {
75
+ existing.schedule(task, delayMs);
76
+ return;
77
+ }
78
+
79
+ // Validate first so an invalid delay never registers the key.
80
+ assertValidDelay(delayMs ?? this.#delayMs);
81
+ const debouncer = this.#create(key);
82
+ this.#debouncers.set(key, debouncer);
83
+ debouncer.schedule(task, delayMs);
84
+ }
85
+
86
+ /** Cancel the task of `key`, if any. */
87
+ cancel(key: K): void {
88
+ this.#debouncers.get(key)?.cancel();
89
+ }
90
+
91
+ /** Cancel every task. Useful when shutting the process down. */
92
+ cancelAll(): void {
93
+ // Copy first: cancelling removes the debouncer from the map.
94
+ for (const debouncer of [...this.#debouncers.values()]) {
95
+ debouncer.cancel();
96
+ }
97
+ }
98
+
99
+ /** Whether a task is waiting or running for `key`. */
100
+ isPending(key: K): boolean {
101
+ return this.#debouncers.has(key);
102
+ }
103
+
104
+ #create(key: K): Debouncer {
105
+ const debouncer: Debouncer = new Debouncer({
106
+ delayMs: this.#delayMs,
107
+ onSchedule: () => this.#onSchedule(key),
108
+ onStart: () => this.#onStart(key),
109
+ onSuccess: () => this.#onSuccess(key),
110
+ onCancel: () => this.#onCancel(key),
111
+ onError: (error) => this.#onError(error, key),
112
+ onIdle: () => {
113
+ if (this.#debouncers.get(key) === debouncer) {
114
+ this.#debouncers.delete(key);
115
+ this.#onIdle(key);
116
+ }
117
+ },
118
+ });
119
+ return debouncer;
120
+ }
121
+ }
package/src/utilities.ts CHANGED
@@ -357,11 +357,37 @@ export function shallowEqual(a: any, b: any): boolean {
357
357
  /**
358
358
  * Pauses the execution of an asynchronous function for a specified duration.
359
359
  *
360
- * @param {number} ms - The delay duration in milliseconds.
361
- * @return {Promise<void>} A promise that resolves after the specified delay.
360
+ * When `signal` is provided, the returned promise rejects with `signal.reason`
361
+ * as soon as the signal is aborted (or immediately if it is already aborted),
362
+ * and the pending timer is cleared.
363
+ *
364
+ * @param ms - The delay duration in milliseconds.
365
+ * @param signal - Optional signal that cancels the wait.
366
+ * @return A promise that resolves after the specified delay.
362
367
  */
363
- export async function delay(ms: number): Promise<void> {
364
- await new Promise(resolve => setTimeout(resolve, ms));
368
+ export function delay(ms: number, signal?: AbortSignal): Promise<void> {
369
+ return new Promise<void>((resolve, reject) => {
370
+ if (signal === undefined) {
371
+ setTimeout(resolve, ms);
372
+ return;
373
+ }
374
+
375
+ if (signal.aborted) {
376
+ reject(signal.reason);
377
+ return;
378
+ }
379
+
380
+ const onAbort = (): void => {
381
+ clearTimeout(timer);
382
+ reject(signal.reason);
383
+ };
384
+ const timer = setTimeout(() => {
385
+ signal.removeEventListener("abort", onAbort);
386
+ resolve();
387
+ }, ms);
388
+
389
+ signal.addEventListener("abort", onAbort, {once: true});
390
+ });
365
391
  }
366
392
 
367
393
  /**
package/dist/cjs/array.js DELETED
@@ -1,106 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pull = pull;
4
- exports.chunk = chunk;
5
- exports.clear = clear;
6
- exports.remove = remove;
7
- exports.pushIfAbsent = pushIfAbsent;
8
- exports.patchArray = patchArray;
9
- const utilities_js_1 = require("./utilities.js");
10
- /**
11
- * Removes all given values from the provided array and returns the number of removed elements.
12
- * The operation mutates the original array.
13
- * @template T
14
- * @param {Array<T>} array - The array to modify by removing specified items.
15
- * @param {...T} items - The values to remove from the array.
16
- * @return {number} The number of elements removed from the array.
17
- */
18
- function pull(array, ...items) {
19
- let output = 0;
20
- for (const item of items) {
21
- let index;
22
- while ((index = array.findIndex(val => Object.is(val, item))) !== -1) {
23
- array.splice(index, 1);
24
- output++;
25
- }
26
- }
27
- return output;
28
- }
29
- /**
30
- * Splits an array into chunks of a given size.
31
- * @template T
32
- * @param {Array<T>} array - The array to split.
33
- * @param {number} size - The size of each chunk.
34
- * @return {Array<Array<T>>} The array of chunks.
35
- */
36
- function chunk(array, size) {
37
- if (size <= 0)
38
- throw new Error("Size must be greater than 0");
39
- const result = [];
40
- for (let i = 0; i < array.length; i += size) {
41
- result.push(array.slice(i, i + size));
42
- }
43
- return result;
44
- }
45
- /**
46
- * Removes all elements from the array, mutating it.
47
- * @template T
48
- * @param {Array<T>} array - The array to clear.
49
- */
50
- function clear(array) {
51
- array.length = 0;
52
- }
53
- /**
54
- * Removes all elements from the array that satisfy the predicate.
55
- * The operation mutates the original array.
56
- * @template T
57
- * @return {Array<T>} An array of removed elements.
58
- */
59
- function remove(array, predicate) {
60
- const removed = [];
61
- let i = 0;
62
- while (i < array.length) {
63
- if (predicate(array[i])) {
64
- removed.push(array[i]);
65
- array.splice(i, 1); // muta el array
66
- }
67
- else {
68
- i++;
69
- }
70
- }
71
- return removed;
72
- }
73
- /**
74
- * Adds an element to the array if it's not already present.
75
- * The operation mutates the original array.
76
- * @template T
77
- * @param {Array<T>} array - The array to modify.
78
- * @param {T} element - The element to add if absent.
79
- * @return {boolean} True if the element was added, false otherwise.
80
- */
81
- function pushIfAbsent(array, element) {
82
- if (!array.includes(element)) {
83
- array.push(element);
84
- return true;
85
- }
86
- return false;
87
- }
88
- /**
89
- * Patches the Array prototype with the methods defined in this module:
90
- * - pull
91
- * - chunk
92
- * - clear
93
- * - remove
94
- * - pushIfAbsent
95
- *
96
- * Each method is added only if it doesn't already exist on Array.prototype.
97
- */
98
- function patchArray() {
99
- const target = Array;
100
- (0, utilities_js_1.monkeyPatch)(target, pull);
101
- (0, utilities_js_1.monkeyPatch)(target, chunk);
102
- (0, utilities_js_1.monkeyPatch)(target, clear);
103
- (0, utilities_js_1.monkeyPatch)(target, remove);
104
- (0, utilities_js_1.monkeyPatch)(target, pushIfAbsent);
105
- }
106
- //# sourceMappingURL=array.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"array.js","sourceRoot":"","sources":["../../src/array.ts"],"names":[],"mappings":";;AAsBA,oBAWC;AASD,sBAOC;AAOD,sBAEC;AAQD,wBAcC;AAUD,oCAMC;AAYD,gCAQC;AAnHD,iDAA2C;AAa3C;;;;;;;GAOG;AACH,SAAgB,IAAI,CAAI,KAAe,EAAE,GAAG,KAAe;IAEvD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACnE,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACvB,MAAM,EAAE,CAAC;QACb,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,KAAK,CAAI,KAAe,EAAE,IAAY;IAClD,IAAI,IAAI,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAgB,KAAK,CAAI,KAAe;IACpC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,MAAM,CAAI,KAAe,EAAC,SAAuB;IAC7D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;IAEV,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACtB,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACvB,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,gBAAgB;QACxC,CAAC;aAAM,CAAC;YACJ,CAAC,EAAE,CAAC;QACR,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,YAAY,CAAI,KAAe,EAAE,OAAU;IACvD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,UAAU;IACtB,MAAM,MAAM,GAAG,KAA2C,CAAC;IAE3D,IAAA,0BAAW,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1B,IAAA,0BAAW,EAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3B,IAAA,0BAAW,EAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3B,IAAA,0BAAW,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAA,0BAAW,EAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACtC,CAAC","sourcesContent":["import type {Constructor, Predicate} from \"./types.js\";\nimport {monkeyPatch} from \"./utilities.js\";\n\ndeclare global {\n interface Array<T> {\n pull(...items: Array<T>): number;\n chunk(size: number): Array<Array<T>>;\n clear(): void;\n remove(predicate: Predicate<T>): Array<T>;\n pushIfAbsent(element: T): boolean;\n }\n}\n\n\n/**\n * Removes all given values from the provided array and returns the number of removed elements.\n * The operation mutates the original array.\n * @template T\n * @param {Array<T>} array - The array to modify by removing specified items.\n * @param {...T} items - The values to remove from the array.\n * @return {number} The number of elements removed from the array.\n */\nexport function pull<T>(array: Array<T>, ...items: Array<T>): number {\n\n let output = 0;\n for (const item of items) {\n let index;\n while ((index = array.findIndex(val => Object.is(val, item))) !== -1) {\n array.splice(index, 1);\n output++;\n }\n }\n return output;\n}\n\n/**\n * Splits an array into chunks of a given size.\n * @template T\n * @param {Array<T>} array - The array to split.\n * @param {number} size - The size of each chunk.\n * @return {Array<Array<T>>} The array of chunks.\n */\nexport function chunk<T>(array: Array<T>, size: number): Array<Array<T>> {\n if (size <= 0) throw new Error(\"Size must be greater than 0\");\n const result: Array<Array<T>> = [];\n for (let i = 0; i < array.length; i += size) {\n result.push(array.slice(i, i + size));\n }\n return result;\n}\n\n/**\n * Removes all elements from the array, mutating it.\n * @template T\n * @param {Array<T>} array - The array to clear.\n */\nexport function clear<T>(array: Array<T>): void {\n array.length = 0;\n}\n\n/**\n * Removes all elements from the array that satisfy the predicate.\n * The operation mutates the original array.\n * @template T\n * @return {Array<T>} An array of removed elements.\n */\nexport function remove<T>(array: Array<T>,predicate: Predicate<T>): Array<T> {\n const removed: Array<T> = [];\n let i = 0;\n\n while (i < array.length) {\n if (predicate(array[i])) {\n removed.push(array[i]);\n array.splice(i, 1); // muta el array\n } else {\n i++;\n }\n }\n\n return removed;\n}\n\n/**\n * Adds an element to the array if it's not already present.\n * The operation mutates the original array.\n * @template T\n * @param {Array<T>} array - The array to modify.\n * @param {T} element - The element to add if absent.\n * @return {boolean} True if the element was added, false otherwise.\n */\nexport function pushIfAbsent<T>(array: Array<T>, element: T): boolean {\n if (!array.includes(element)) {\n array.push(element);\n return true;\n }\n return false;\n}\n\n/**\n * Patches the Array prototype with the methods defined in this module:\n * - pull\n * - chunk\n * - clear\n * - remove\n * - pushIfAbsent\n *\n * Each method is added only if it doesn't already exist on Array.prototype.\n */\nexport function patchArray(): void {\n const target = Array as unknown as Constructor<Array<any>>;\n\n monkeyPatch(target, pull);\n monkeyPatch(target, chunk);\n monkeyPatch(target, clear);\n monkeyPatch(target, remove);\n monkeyPatch(target, pushIfAbsent);\n}"]}
@@ -1,79 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const utilities_js_1 = require("./utilities.js");
4
- function normalizeMaxConcurrency(value) {
5
- if (!Number.isInteger(value) || value < 1) {
6
- throw new RangeError("maxConcurrency must be a positive integer");
7
- }
8
- return value;
9
- }
10
- function normalizeCheckInterval(value) {
11
- return Number.isFinite(value) ? Math.max(0, value) : 0;
12
- }
13
- class AsyncPoolExecutor {
14
- get maxConcurrency() {
15
- return this._maxConcurrency;
16
- }
17
- set maxConcurrency(value) {
18
- this._maxConcurrency = normalizeMaxConcurrency(value);
19
- }
20
- /**
21
- * @param {number} maxConcurrency - The maximum number of concurrent tasks allowed.
22
- * @param {number} [checkInterval=50] - The interval, in milliseconds, to check for capacity to run new tasks. Defaults to 50 ms.
23
- */
24
- constructor(maxConcurrency, checkInterval = 50) {
25
- this._taskQueue = [];
26
- this._maxConcurrency = normalizeMaxConcurrency(maxConcurrency);
27
- this._checkIntervalMs = normalizeCheckInterval(checkInterval);
28
- this._activeTasks = 0;
29
- }
30
- /**
31
- * Submits a deferred task to a queue for asynchronous execution and returns a Promise resolving to its result.
32
- * @template T
33
- * @param {Supplier<T>} task - The deferred task to be executed asynchronously.
34
- */
35
- submit(task) {
36
- const promise = new Promise((resolve, reject) => {
37
- this._taskQueue.push({ task, resolve, reject });
38
- });
39
- if (this._process != null)
40
- return promise;
41
- this._process = setTimeout(async () => {
42
- while (this._taskQueue.length > 0) {
43
- while (this._activeTasks >= this._maxConcurrency) {
44
- await (0, utilities_js_1.delay)(this._checkIntervalMs);
45
- }
46
- const queuedTask = this._taskQueue.shift();
47
- if (queuedTask) {
48
- this._activeTasks++;
49
- setTimeout(async () => {
50
- try {
51
- queuedTask.resolve(await queuedTask.task());
52
- }
53
- catch (error) {
54
- queuedTask.reject(error);
55
- }
56
- finally {
57
- this._activeTasks--;
58
- }
59
- });
60
- }
61
- }
62
- this._process = null;
63
- }, 0);
64
- return promise;
65
- }
66
- /**
67
- * Waits until all tasks (both queued and active) are completed.
68
- * This method resolves only when there are no pending tasks.
69
- *
70
- * @return {Promise<void>} A promise that resolves when all tasks are complete.
71
- */
72
- async shutdown() {
73
- while (this._taskQueue.length > 0 || this._activeTasks > 0) {
74
- await (0, utilities_js_1.delay)(this._checkIntervalMs);
75
- }
76
- }
77
- }
78
- exports.default = AsyncPoolExecutor;
79
- //# sourceMappingURL=async-pool-executor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"async-pool-executor.js","sourceRoot":"","sources":["../../src/async-pool-executor.ts"],"names":[],"mappings":";;AACA,iDAAqC;AAQrC,SAAS,uBAAuB,CAAC,KAAa;IAC1C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,UAAU,CAAC,2CAA2C,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IACzC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAqB,iBAAiB;IAGlC,IAAI,cAAc;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED,IAAI,cAAc,CAAC,KAAa;QAC5B,IAAI,CAAC,eAAe,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAE1D,CAAC;IAQD;;;OAGG;IACH,YAAY,cAAsB,EAAE,gBAAwB,EAAE;QAPtD,eAAU,GAA2B,EAAE,CAAC;QAQ5C,IAAI,CAAC,eAAe,GAAG,uBAAuB,CAAC,cAAc,CAAC,CAAC;QAC/D,IAAI,CAAC,gBAAgB,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAI,IAA8B;QAC3C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAC,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI;YACrB,OAAO,OAAO,CAAC;QAEnB,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAElC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAEhC,OAAO,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBAC/C,MAAM,IAAA,oBAAK,EAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;gBACvC,CAAC;gBAED,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC3C,IAAI,UAAU,EAAE,CAAC;oBACb,IAAI,CAAC,YAAY,EAAE,CAAC;oBACpB,UAAU,CAAC,KAAK,IAAI,EAAE;wBAClB,IAAI,CAAC;4BACD,UAAU,CAAC,OAAO,CAAC,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;wBAChD,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACb,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;wBAC7B,CAAC;gCAAS,CAAC;4BACP,IAAI,CAAC,YAAY,EAAE,CAAC;wBACxB,CAAC;oBACL,CAAC,CAAC,CAAC;gBACP,CAAC;YAGL,CAAC;YAED,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACzB,CAAC,EAAE,CAAC,CAAC,CAAC;QAEN,OAAO,OAAO,CAAC;IAGnB,CAAC;IAGD;;;;;OAKG;IACI,KAAK,CAAC,QAAQ;QACjB,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YACzD,MAAM,IAAA,oBAAK,EAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACvC,CAAC;IACL,CAAC;CACJ;AAtFD,oCAsFC","sourcesContent":["import type {Supplier} from \"./types.js\";\nimport {delay} from \"./utilities.js\";\n\ntype QueuedTask<T> = {\n task: Supplier<T | Promise<T>>;\n resolve: (value: T) => void;\n reject: (reason?: any) => void;\n};\n\nfunction normalizeMaxConcurrency(value: number): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new RangeError(\"maxConcurrency must be a positive integer\");\n }\n return value;\n}\n\nfunction normalizeCheckInterval(value: number): number {\n return Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n\nexport default class AsyncPoolExecutor {\n private _checkIntervalMs: number;\n\n get maxConcurrency(): number {\n return this._maxConcurrency;\n }\n\n set maxConcurrency(value: number) {\n this._maxConcurrency = normalizeMaxConcurrency(value);\n\n }\n\n private _process: any;\n private _maxConcurrency: number;\n private _activeTasks: number;\n private _taskQueue: Array<QueuedTask<any>> = [];\n\n\n /**\n * @param {number} maxConcurrency - The maximum number of concurrent tasks allowed.\n * @param {number} [checkInterval=50] - The interval, in milliseconds, to check for capacity to run new tasks. Defaults to 50 ms.\n */\n constructor(maxConcurrency: number, checkInterval: number = 50) {\n this._maxConcurrency = normalizeMaxConcurrency(maxConcurrency);\n this._checkIntervalMs = normalizeCheckInterval(checkInterval);\n this._activeTasks = 0;\n }\n\n /**\n * Submits a deferred task to a queue for asynchronous execution and returns a Promise resolving to its result.\n * @template T\n * @param {Supplier<T>} task - The deferred task to be executed asynchronously.\n */\n public submit<T>(task: Supplier<T | Promise<T>>): Promise<T> {\n const promise = new Promise<T>((resolve, reject) => {\n this._taskQueue.push({task, resolve, reject});\n });\n\n if (this._process != null)\n return promise;\n\n this._process = setTimeout(async () => {\n\n while (this._taskQueue.length > 0) {\n\n while (this._activeTasks >= this._maxConcurrency) {\n await delay(this._checkIntervalMs);\n }\n\n const queuedTask = this._taskQueue.shift();\n if (queuedTask) {\n this._activeTasks++;\n setTimeout(async () => {\n try {\n queuedTask.resolve(await queuedTask.task());\n } catch (error) {\n queuedTask.reject(error);\n } finally {\n this._activeTasks--;\n }\n });\n }\n\n\n }\n\n this._process = null;\n }, 0);\n\n return promise;\n\n\n }\n\n\n /**\n * Waits until all tasks (both queued and active) are completed.\n * This method resolves only when there are no pending tasks.\n *\n * @return {Promise<void>} A promise that resolves when all tasks are complete.\n */\n public async shutdown(): Promise<void> {\n while (this._taskQueue.length > 0 || this._activeTasks > 0) {\n await delay(this._checkIntervalMs);\n }\n }\n}\n"]}