@catbee/utils 2.0.0-next.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +102 -52
  3. package/array/index.cjs +215 -74
  4. package/array/index.d.ts +345 -2
  5. package/array/index.mjs +201 -74
  6. package/async/index.cjs +116 -39
  7. package/async/index.d.ts +292 -2
  8. package/async/index.mjs +116 -40
  9. package/cache/index.cjs +2 -2
  10. package/cache/index.d.ts +156 -2
  11. package/cache/index.mjs +3 -3
  12. package/config/index.cjs +80 -66
  13. package/config/index.d.ts +65 -3
  14. package/config/index.mjs +77 -65
  15. package/context-store/index.cjs +2 -3
  16. package/context-store/index.d.ts +193 -2
  17. package/context-store/index.mjs +2 -3
  18. package/crypto/index.cjs +55 -5
  19. package/crypto/index.d.ts +225 -2
  20. package/crypto/index.mjs +52 -7
  21. package/date/index.cjs +676 -2
  22. package/date/index.d.ts +676 -2
  23. package/date/index.mjs +665 -3
  24. package/decorator/index.cjs +2172 -0
  25. package/{decorators/decorators.utils.d.ts → decorator/index.d.ts} +58 -54
  26. package/decorator/index.mjs +2131 -0
  27. package/{dir → directory}/index.cjs +5 -4
  28. package/{dir/dir.utils.d.ts → directory/index.d.ts} +24 -21
  29. package/{dir → directory}/index.mjs +5 -4
  30. package/env/index.cjs +100 -68
  31. package/env/index.d.ts +391 -2
  32. package/env/index.mjs +100 -68
  33. package/exception/index.cjs +1 -1
  34. package/exception/index.d.ts +233 -2
  35. package/exception/index.mjs +1 -1
  36. package/fs/index.cjs +71 -37
  37. package/fs/index.d.ts +206 -2
  38. package/fs/index.mjs +65 -35
  39. package/http-status-codes/index.cjs +1 -1
  40. package/http-status-codes/index.d.ts +268 -2
  41. package/http-status-codes/index.mjs +1 -1
  42. package/id/index.cjs +1 -1
  43. package/id/index.d.ts +38 -2
  44. package/id/index.mjs +1 -1
  45. package/index.cjs +13 -13
  46. package/index.d.ts +5 -5
  47. package/index.mjs +5 -5
  48. package/logger/index.cjs +13 -15
  49. package/logger/index.d.ts +190 -2
  50. package/logger/index.mjs +14 -16
  51. package/middleware/index.cjs +1 -1
  52. package/middleware/index.d.ts +104 -2
  53. package/middleware/index.mjs +1 -1
  54. package/object/index.cjs +379 -0
  55. package/{obj/obj.utils.d.ts → object/index.d.ts} +73 -33
  56. package/object/index.mjs +360 -0
  57. package/package.json +41 -23
  58. package/performance/index.cjs +4 -4
  59. package/performance/index.d.ts +139 -2
  60. package/performance/index.mjs +4 -4
  61. package/request/index.cjs +37 -25
  62. package/request/index.d.ts +242 -3
  63. package/request/index.mjs +37 -25
  64. package/response/index.cjs +1 -1
  65. package/response/index.d.ts +319 -3
  66. package/response/index.mjs +1 -1
  67. package/server/index.cjs +249 -146
  68. package/server/index.d.ts +866 -5
  69. package/server/index.mjs +248 -144
  70. package/stream/index.cjs +1 -1
  71. package/stream/index.d.ts +91 -2
  72. package/stream/index.mjs +1 -1
  73. package/string/index.cjs +34 -1
  74. package/string/index.d.ts +146 -2
  75. package/string/index.mjs +31 -2
  76. package/type/index.cjs +19 -2
  77. package/type/index.d.ts +144 -2
  78. package/type/index.mjs +17 -3
  79. package/types/index.cjs +1 -1
  80. package/types/index.d.ts +775 -5
  81. package/types/index.mjs +1 -1
  82. package/url/index.cjs +63 -5
  83. package/url/index.d.ts +200 -2
  84. package/url/index.mjs +59 -6
  85. package/{validate → validation}/index.cjs +91 -44
  86. package/{validate/validate.utils.d.ts → validation/index.d.ts} +33 -24
  87. package/{validate → validation}/index.mjs +87 -44
  88. package/array/array.utils.d.ts +0 -191
  89. package/async/async.utils.d.ts +0 -296
  90. package/cache/cache.utils.d.ts +0 -176
  91. package/config/config.d.ts +0 -57
  92. package/context-store/context-store.utils.d.ts +0 -212
  93. package/crypto/crypto.utils.d.ts +0 -183
  94. package/date/date.utils.d.ts +0 -190
  95. package/decorators/index.cjs +0 -913
  96. package/decorators/index.d.ts +0 -25
  97. package/decorators/index.mjs +0 -872
  98. package/dir/index.d.ts +0 -25
  99. package/env/env.utils.d.ts +0 -400
  100. package/exception/exception.utils.d.ts +0 -253
  101. package/fs/fs.utils.d.ts +0 -196
  102. package/http-status-codes/http-status-codes.d.ts +0 -289
  103. package/id/id.utils.d.ts +0 -59
  104. package/logger/logger.utils.d.ts +0 -210
  105. package/middleware/middleware.utils.d.ts +0 -123
  106. package/obj/index.cjs +0 -317
  107. package/obj/index.d.ts +0 -25
  108. package/obj/index.mjs +0 -301
  109. package/performance/performance.utils.d.ts +0 -159
  110. package/request/request.utils.d.ts +0 -109
  111. package/response/response.utils.d.ts +0 -186
  112. package/server/server.builder.d.ts +0 -531
  113. package/server/server.d.ts +0 -303
  114. package/stream/stream.utils.d.ts +0 -111
  115. package/string/string.utils.d.ts +0 -124
  116. package/type/type.utils.d.ts +0 -129
  117. package/types/api-response.d.ts +0 -175
  118. package/types/common.d.ts +0 -148
  119. package/types/config.d.ts +0 -88
  120. package/types/server.d.ts +0 -291
  121. package/url/url.utils.d.ts +0 -164
  122. package/validate/index.d.ts +0 -25
package/array/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
5
  *
6
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  * of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,8 @@
22
22
  * SOFTWARE.
23
23
  */
24
24
 
25
- import { getValueByPath } from '@catbee/utils/obj';
25
+ import { getValueByPath } from '@catbee/utils/object';
26
+ import { randomBytes } from 'crypto';
26
27
 
27
28
  var __defProp = Object.defineProperty;
28
29
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -41,49 +42,64 @@ function unique(array, keyFn) {
41
42
  if (!Array.isArray(array) || array.length === 0) return [];
42
43
  if (!keyFn) return Array.from(new Set(array));
43
44
  const seen = /* @__PURE__ */ new Set();
44
- return array.filter((item) => {
45
+ const result = [];
46
+ for (const item of array) {
45
47
  const key = keyFn(item);
46
- if (seen.has(key)) return false;
47
- seen.add(key);
48
- return true;
49
- });
48
+ if (!seen.has(key)) {
49
+ seen.add(key);
50
+ result.push(item);
51
+ }
52
+ }
53
+ return result;
50
54
  }
51
55
  __name(unique, "unique");
52
56
  function flattenDeep(array) {
53
57
  if (!Array.isArray(array)) return [];
54
58
  const result = [];
55
- for (const val of array) {
59
+ const stack = [
60
+ ...array
61
+ ];
62
+ while (stack.length) {
63
+ const val = stack.pop();
56
64
  if (Array.isArray(val)) {
57
- result.push(...flattenDeep(val));
65
+ stack.push(...val);
58
66
  } else {
59
67
  result.push(val);
60
68
  }
61
69
  }
62
- return result;
70
+ return result.reverse();
63
71
  }
64
72
  __name(flattenDeep, "flattenDeep");
65
73
  function random(array) {
66
74
  if (!Array.isArray(array) || array.length === 0) return void 0;
67
- const idx = Math.floor(Math.random() * array.length);
68
- return array[idx];
75
+ return array[secureIndex(array.length)];
69
76
  }
70
77
  __name(random, "random");
71
78
  function groupBy(array, keyOrFn) {
72
79
  if (!Array.isArray(array) || array.length === 0) return {};
73
- const keyFn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn];
74
- return array.reduce((acc, item) => {
75
- const key = keyFn(item);
76
- if (!acc[key]) acc[key] = [];
77
- acc[key].push(item);
78
- return acc;
79
- }, {});
80
+ const keyFn = typeof keyOrFn === "function" ? keyOrFn : (item) => String(getValueByPath(item, keyOrFn));
81
+ const result = {};
82
+ for (const item of array) {
83
+ const key = String(keyFn(item));
84
+ if (Object.hasOwn(result, key)) {
85
+ result[key].push(item);
86
+ } else {
87
+ result[key] = [
88
+ item
89
+ ];
90
+ }
91
+ }
92
+ return Object.fromEntries(Object.entries(result).map(([k, v]) => [
93
+ k,
94
+ v
95
+ ]));
80
96
  }
81
97
  __name(groupBy, "groupBy");
82
98
  function shuffle(array) {
83
99
  if (!Array.isArray(array)) throw new TypeError("Expected an array");
84
100
  const copy = array.slice();
85
101
  for (let i = copy.length - 1; i > 0; i--) {
86
- const j = Math.floor(Math.random() * (i + 1));
102
+ const j = secureIndex(i + 1);
87
103
  [copy[i], copy[j]] = [
88
104
  copy[j],
89
105
  copy[i]
@@ -94,7 +110,7 @@ function shuffle(array) {
94
110
  __name(shuffle, "shuffle");
95
111
  function pluck(array, key) {
96
112
  if (!Array.isArray(array)) return [];
97
- return array.map((item) => item[key]);
113
+ return array.map((item) => item?.[key]);
98
114
  }
99
115
  __name(pluck, "pluck");
100
116
  function difference(a, b) {
@@ -109,35 +125,41 @@ function intersect(a, b) {
109
125
  return a.filter((item) => setB.has(item));
110
126
  }
111
127
  __name(intersect, "intersect");
112
- function mergeSort(array, key, direction = "asc") {
128
+ function mergeSort(array, key, direction = "asc", compareFn) {
113
129
  if (!Array.isArray(array)) throw new TypeError("Expected array");
114
- if (array.length <= 1) return array.slice();
130
+ const arr = array.slice();
131
+ if (arr.length <= 1) return arr;
115
132
  const keyFn = typeof key === "function" ? key : (item) => getValueByPath(item, key);
116
- const compare = /* @__PURE__ */ __name((a, b) => {
133
+ const collator = new Intl.Collator("en", {
134
+ numeric: true
135
+ });
136
+ const compare = compareFn ?? function(a, b) {
117
137
  const aVal = keyFn(a);
118
138
  const bVal = keyFn(b);
119
139
  if (aVal === bVal) return 0;
120
140
  if (aVal == null) return direction === "asc" ? 1 : -1;
121
141
  if (bVal == null) return direction === "asc" ? -1 : 1;
122
- return direction === "asc" ? aVal < bVal ? -1 : 1 : aVal > bVal ? -1 : 1;
123
- }, "compare");
142
+ return direction === "asc" ? collator.compare(String(aVal), String(bVal)) : collator.compare(String(bVal), String(aVal));
143
+ };
124
144
  const merge = /* @__PURE__ */ __name((left, right) => {
125
145
  const result = [];
126
146
  let i = 0, j = 0;
127
147
  while (i < left.length && j < right.length) {
128
- if (compare(left[i], right[j]) <= 0) result.push(left[i++]);
129
- else result.push(right[j++]);
148
+ result.push(compare(left[i], right[j]) <= 0 ? left[i++] : right[j++]);
130
149
  }
131
- return result.concat(left.slice(i)).concat(right.slice(j));
150
+ while (i < left.length) result.push(left[i++]);
151
+ while (j < right.length) result.push(right[j++]);
152
+ return result;
132
153
  }, "merge");
133
- const sort = /* @__PURE__ */ __name((arr) => {
134
- if (arr.length <= 1) return arr;
135
- const mid = Math.floor(arr.length / 2);
136
- const left = sort(arr.slice(0, mid));
137
- const right = sort(arr.slice(mid));
154
+ const sort = /* @__PURE__ */ __name((input) => {
155
+ const len = input.length;
156
+ if (len <= 1) return input;
157
+ const mid = len >> 1;
158
+ const left = sort(input.slice(0, mid));
159
+ const right = sort(input.slice(mid));
138
160
  return merge(left, right);
139
161
  }, "sort");
140
- return sort(array);
162
+ return sort(arr);
141
163
  }
142
164
  __name(mergeSort, "mergeSort");
143
165
  function zip(...arrays) {
@@ -154,28 +176,20 @@ function zip(...arrays) {
154
176
  }
155
177
  __name(zip, "zip");
156
178
  function partition(array, predicate) {
179
+ const pass = [];
180
+ const fail = [];
157
181
  if (!Array.isArray(array)) return [
158
- [],
159
- []
182
+ pass,
183
+ fail
184
+ ];
185
+ for (let i = 0; i < array.length; i++) {
186
+ const item = array[i];
187
+ (predicate(item, i, array) ? pass : fail).push(item);
188
+ }
189
+ return [
190
+ pass,
191
+ fail
160
192
  ];
161
- return array.reduce(([pass, fail], item, index) => {
162
- return predicate(item, index, array) ? [
163
- [
164
- ...pass,
165
- item
166
- ],
167
- fail
168
- ] : [
169
- pass,
170
- [
171
- ...fail,
172
- item
173
- ]
174
- ];
175
- }, [
176
- [],
177
- []
178
- ]);
179
193
  }
180
194
  __name(partition, "partition");
181
195
  function range(start, end, step = 1) {
@@ -183,46 +197,159 @@ function range(start, end, step = 1) {
183
197
  throw new TypeError("Arguments must be finite numbers");
184
198
  }
185
199
  if (step === 0) throw new Error("Step cannot be zero");
186
- const isAscending = step > 0;
187
- if (isAscending && start >= end || !isAscending && start <= end) {
188
- return [];
189
- }
190
- const length = Math.max(Math.ceil((end - start) / step), 0);
191
- const result = new Array(length);
192
- for (let i = 0, value = start; i < length; i++, value += step) {
193
- result[i] = value;
200
+ const result = [];
201
+ if (step > 0) {
202
+ for (let i = start; i < end; i += step) result.push(i);
203
+ } else {
204
+ for (let i = start; i > end; i += step) result.push(i);
194
205
  }
195
206
  return result;
196
207
  }
197
208
  __name(range, "range");
198
209
  function take(array, n = 1) {
199
210
  if (!Array.isArray(array) || n <= 0) return [];
200
- return array.slice(0, n);
211
+ return n >= array.length ? array.slice() : array.slice(0, n);
201
212
  }
202
213
  __name(take, "take");
203
214
  function takeWhile(array, predicate) {
204
- if (!Array.isArray(array)) return [];
205
215
  const result = [];
206
- for (let i = 0; i < array.length; i++) {
207
- if (!predicate(array[i], i)) break;
208
- result.push(array[i]);
216
+ if (!Array.isArray(array)) return result;
217
+ const len = array.length;
218
+ for (let i = 0; i < len; i++) {
219
+ const item = array[i];
220
+ if (!predicate(item, i)) break;
221
+ result.push(item);
209
222
  }
210
223
  return result;
211
224
  }
212
225
  __name(takeWhile, "takeWhile");
213
226
  function compact(array) {
214
227
  if (!Array.isArray(array)) return [];
215
- return array.filter(Boolean);
228
+ const result = [];
229
+ for (const v of array) {
230
+ if (v) result.push(v);
231
+ }
232
+ return result;
216
233
  }
217
234
  __name(compact, "compact");
218
235
  function countBy(array, keyFn) {
219
236
  if (!Array.isArray(array)) return {};
220
- return array.reduce((acc, item) => {
237
+ const result = {};
238
+ for (const item of array) {
221
239
  const key = String(keyFn(item));
222
- acc[key] = (acc[key] || 0) + 1;
223
- return acc;
224
- }, {});
240
+ result[key] = (result[key] ?? 0) + 1;
241
+ }
242
+ return result;
225
243
  }
226
244
  __name(countBy, "countBy");
245
+ function toggle(array, item) {
246
+ if (!Array.isArray(array)) return [
247
+ item
248
+ ];
249
+ const exists = array.includes(item);
250
+ if (exists) return array.filter((x) => x !== item);
251
+ return [
252
+ ...array,
253
+ item
254
+ ];
255
+ }
256
+ __name(toggle, "toggle");
257
+ function secureIndex(max) {
258
+ if (!Number.isInteger(max) || max <= 0) throw new RangeError("Max must be a positive integer");
259
+ const limit = 4294967295 - 4294967295 % max;
260
+ let rand;
261
+ do {
262
+ rand = randomBytes(4).readUInt32BE(0);
263
+ } while (rand >= limit);
264
+ return rand % max;
265
+ }
266
+ __name(secureIndex, "secureIndex");
267
+ var secureRandom = /* @__PURE__ */ __name((array) => {
268
+ if (!Array.isArray(array) || array.length === 0) return void 0;
269
+ const idx = secureIndex(array.length);
270
+ return array[idx];
271
+ }, "secureRandom");
272
+ function findLast(array, predicate) {
273
+ if (!Array.isArray(array)) return void 0;
274
+ for (let i = array.length - 1; i >= 0; i--) {
275
+ if (predicate(array[i], i, array)) return array[i];
276
+ }
277
+ return void 0;
278
+ }
279
+ __name(findLast, "findLast");
280
+ function findLastIndex(array, predicate) {
281
+ if (!Array.isArray(array)) return -1;
282
+ for (let i = array.length - 1; i >= 0; i--) {
283
+ if (predicate(array[i], i, array)) return i;
284
+ }
285
+ return -1;
286
+ }
287
+ __name(findLastIndex, "findLastIndex");
288
+ function chunkBy(array, predicate) {
289
+ if (!Array.isArray(array) || array.length === 0) return [];
290
+ const result = [];
291
+ let chunk2 = [];
292
+ for (let i = 0; i < array.length; i++) {
293
+ if (predicate(array[i], i, array) && chunk2.length) {
294
+ result.push(chunk2);
295
+ chunk2 = [];
296
+ }
297
+ chunk2.push(array[i]);
298
+ }
299
+ if (chunk2.length) result.push(chunk2);
300
+ return result;
301
+ }
302
+ __name(chunkBy, "chunkBy");
303
+ function remove(array, value) {
304
+ if (!Array.isArray(array)) return [];
305
+ return array.filter((item) => item !== value);
306
+ }
307
+ __name(remove, "remove");
308
+ function isSorted(array, direction = "asc", compareFn) {
309
+ if (!Array.isArray(array) || array.length <= 1) return true;
310
+ const cmp = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
311
+ for (let i = 1; i < array.length; i++) {
312
+ const res = cmp(array[i - 1], array[i]);
313
+ if (direction === "asc" && res > 0 || direction === "desc" && res < 0) return false;
314
+ }
315
+ return true;
316
+ }
317
+ __name(isSorted, "isSorted");
318
+ function headOfArr(array) {
319
+ return Array.isArray(array) && array.length > 0 ? array[0] : void 0;
320
+ }
321
+ __name(headOfArr, "headOfArr");
322
+ function lastOfArr(array) {
323
+ return Array.isArray(array) && array.length > 0 ? array.at(-1) : void 0;
324
+ }
325
+ __name(lastOfArr, "lastOfArr");
326
+ function drop(array, n) {
327
+ if (!Array.isArray(array) || n <= 0) return [
328
+ ...array
329
+ ];
330
+ return array.slice(n);
331
+ }
332
+ __name(drop, "drop");
333
+ function dropWhile(array, predicate) {
334
+ if (!Array.isArray(array)) return [];
335
+ let i = 0;
336
+ while (i < array.length && predicate(array[i], i)) {
337
+ i++;
338
+ }
339
+ return array.slice(i);
340
+ }
341
+ __name(dropWhile, "dropWhile");
342
+ function maxBy(array, keyOrFn) {
343
+ if (!Array.isArray(array) || array.length === 0) return void 0;
344
+ const fn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn];
345
+ return array.reduce((max, item) => fn(item) > fn(max) ? item : max, array[0]);
346
+ }
347
+ __name(maxBy, "maxBy");
348
+ function minBy(array, keyOrFn) {
349
+ if (!Array.isArray(array) || array.length === 0) return void 0;
350
+ const fn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn];
351
+ return array.reduce((min, item) => fn(item) < fn(min) ? item : min, array[0]);
352
+ }
353
+ __name(minBy, "minBy");
227
354
 
228
- export { chunk, compact, countBy, difference, flattenDeep, groupBy, intersect, mergeSort, partition, pluck, random, range, shuffle, take, takeWhile, unique, zip };
355
+ export { chunk, chunkBy, compact, countBy, difference, drop, dropWhile, findLast, findLastIndex, flattenDeep, groupBy, headOfArr, intersect, isSorted, lastOfArr, maxBy, mergeSort, minBy, partition, pluck, random, range, remove, secureIndex, secureRandom, shuffle, take, takeWhile, toggle, unique, zip };
package/async/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
5
  *
6
6
  * Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  * of this software and associated documentation files (the "Software"), to deal
@@ -45,7 +45,13 @@ function debounce(fn, delay) {
45
45
  pendingArgs = args;
46
46
  if (timer) clearTimeout(timer);
47
47
  timer = setTimeout(() => {
48
- fn(...pendingArgs);
48
+ try {
49
+ fn(...pendingArgs);
50
+ } catch (err) {
51
+ logger.getLogger().error({
52
+ err
53
+ }, "Error in debounced function");
54
+ }
49
55
  timer = null;
50
56
  }, delay);
51
57
  }
@@ -58,7 +64,13 @@ function debounce(fn, delay) {
58
64
  debounced.flush = () => {
59
65
  if (timer) {
60
66
  clearTimeout(timer);
61
- fn(...pendingArgs);
67
+ try {
68
+ fn(...pendingArgs);
69
+ } catch (err) {
70
+ logger.getLogger().error({
71
+ err
72
+ }, "Error in debounced.flush");
73
+ }
62
74
  timer = null;
63
75
  pendingArgs = null;
64
76
  }
@@ -66,10 +78,11 @@ function debounce(fn, delay) {
66
78
  return debounced;
67
79
  }
68
80
  __name(debounce, "debounce");
69
- function throttle(fn, limit, opts = {
81
+ var DEFAULT_THROTTLE_OPTS = {
70
82
  leading: true,
71
83
  trailing: false
72
- }) {
84
+ };
85
+ function throttle(fn, limit, opts = DEFAULT_THROTTLE_OPTS) {
73
86
  let lastCall = 0;
74
87
  let timer = null;
75
88
  let savedArgs = null;
@@ -84,14 +97,28 @@ function throttle(fn, limit, opts = {
84
97
  timer = null;
85
98
  }
86
99
  lastCall = now;
87
- fn(...args);
100
+ try {
101
+ fn(...args);
102
+ } catch (err) {
103
+ logger.getLogger().error({
104
+ err
105
+ }, "Error in throttled function");
106
+ }
88
107
  } else if (trailing) {
89
108
  savedArgs = args;
90
109
  if (!timer) {
91
110
  timer = setTimeout(() => {
92
111
  lastCall = leading ? Date.now() : 0;
93
112
  timer = null;
94
- if (savedArgs) fn(...savedArgs);
113
+ if (savedArgs) {
114
+ try {
115
+ fn(...savedArgs);
116
+ } catch (err) {
117
+ logger.getLogger().error({
118
+ err
119
+ }, "Error in throttled trailing call");
120
+ }
121
+ }
95
122
  }, remaining);
96
123
  }
97
124
  }
@@ -142,7 +169,7 @@ function singletonAsync(fn, drop = false) {
142
169
  promise = null;
143
170
  });
144
171
  } else if (drop) {
145
- return Promise.reject(new Error("Busy: function already running"));
172
+ throw new Error("Function is already in progress");
146
173
  }
147
174
  return promise;
148
175
  };
@@ -213,11 +240,19 @@ async function runInSeries(tasks) {
213
240
  __name(runInSeries, "runInSeries");
214
241
  function memoizeAsync(fn, options = {}) {
215
242
  const cache = /* @__PURE__ */ new Map();
216
- const { ttl, keyFn = JSON.stringify } = options;
217
- return async function(...args) {
218
- const key = keyFn(args);
243
+ const { ttl } = options;
244
+ const generateKey = /* @__PURE__ */ __name((args) => {
245
+ try {
246
+ return options.keyFn?.(args) ?? JSON.stringify(args);
247
+ } catch {
248
+ logger.getLogger().warn("memoizeAsync: key generation failed, falling back to JSON.stringify");
249
+ return JSON.stringify(args);
250
+ }
251
+ }, "generateKey");
252
+ return async (...args) => {
253
+ const key = generateKey(args);
219
254
  const cached = cache.get(key);
220
- if (cached && (!ttl || Date.now() < cached.expires)) {
255
+ if (cached && Date.now() < cached.expires) {
221
256
  return cached.value;
222
257
  }
223
258
  const result = await fn(...args);
@@ -236,7 +271,10 @@ function abortable(promise, signal, abortValue = new Error("Operation aborted"))
236
271
  return Promise.race([
237
272
  promise,
238
273
  new Promise((_, reject) => {
239
- const abort = /* @__PURE__ */ __name(() => reject(abortValue), "abort");
274
+ const abort = /* @__PURE__ */ __name(() => {
275
+ reject(abortValue);
276
+ signal.removeEventListener("abort", abort);
277
+ }, "abort");
240
278
  signal.addEventListener("abort", abort, {
241
279
  once: true
242
280
  });
@@ -305,39 +343,53 @@ var CircuitBreakerState = /* @__PURE__ */ (function(CircuitBreakerState2) {
305
343
  function circuitBreaker(fn, options = {}) {
306
344
  const { failureThreshold = 5, resetTimeout = 1e4, successThreshold = 1, onOpen, onClose, onHalfOpen } = options;
307
345
  let state = "CLOSED";
308
- let failureCount = 0;
309
- let successCount = 0;
310
- let nextAttempt = Date.now();
311
- return async function(...args) {
312
- if (state === "OPEN") {
313
- if (Date.now() < nextAttempt) {
314
- throw new CircuitBreakerOpenError();
315
- }
316
- state = "HALF_OPEN";
317
- if (onHalfOpen) onHalfOpen();
346
+ let fails = 0;
347
+ let halfSuccess = 0;
348
+ let unlockAt = 0;
349
+ const reset = /* @__PURE__ */ __name(() => {
350
+ fails = 0;
351
+ halfSuccess = 0;
352
+ state = "CLOSED";
353
+ onClose?.();
354
+ }, "reset");
355
+ const open = /* @__PURE__ */ __name(() => {
356
+ state = "OPEN";
357
+ unlockAt = Date.now() + resetTimeout;
358
+ onOpen?.();
359
+ }, "open");
360
+ const halfOpen = /* @__PURE__ */ __name(() => {
361
+ state = "HALF_OPEN";
362
+ onHalfOpen?.();
363
+ }, "halfOpen");
364
+ return async (...args) => {
365
+ const now = Date.now();
366
+ if (state === "OPEN" && now < unlockAt) {
367
+ throw new CircuitBreakerOpenError();
368
+ }
369
+ if (state === "OPEN" && now >= unlockAt) {
370
+ halfOpen();
318
371
  }
319
372
  try {
320
- const result = await fn(...args);
373
+ const res = await fn(...args);
321
374
  if (state === "HALF_OPEN") {
322
- successCount++;
323
- if (successCount >= successThreshold) {
324
- successCount = 0;
325
- failureCount = 0;
326
- state = "CLOSED";
327
- if (onClose) onClose();
375
+ halfSuccess++;
376
+ if (halfSuccess >= successThreshold) {
377
+ reset();
378
+ } else {
379
+ state = "HALF_OPEN";
328
380
  }
329
381
  } else {
330
- failureCount = 0;
382
+ fails = 0;
331
383
  }
332
- return result;
333
- } catch (error) {
334
- failureCount++;
335
- if ((state === "CLOSED" || state === "HALF_OPEN") && failureCount >= failureThreshold) {
336
- state = "OPEN";
337
- nextAttempt = Date.now() + resetTimeout;
338
- if (onOpen) onOpen();
384
+ return res;
385
+ } catch (err) {
386
+ fails++;
387
+ if (state === "HALF_OPEN" || state === "CLOSED") {
388
+ if (fails >= failureThreshold) {
389
+ open();
390
+ }
339
391
  }
340
- throw error;
392
+ throw err;
341
393
  }
342
394
  };
343
395
  }
@@ -353,12 +405,15 @@ async function runWithConcurrency(tasks, options = {}) {
353
405
  }
354
406
  return new Promise((resolve, reject) => {
355
407
  let taskIndex = 0;
408
+ let rejected = false;
356
409
  const processNext = /* @__PURE__ */ __name(async () => {
410
+ if (rejected) return;
357
411
  const currentTaskIndex = taskIndex++;
358
412
  if (currentTaskIndex >= totalTasks) return;
359
413
  try {
360
414
  const result = await tasks[currentTaskIndex]();
361
415
  if (signal?.aborted) {
416
+ rejected = true;
362
417
  reject(new Error("Aborted"));
363
418
  return;
364
419
  }
@@ -374,6 +429,7 @@ async function runWithConcurrency(tasks, options = {}) {
374
429
  }
375
430
  }
376
431
  } catch (error) {
432
+ rejected = true;
377
433
  reject(error);
378
434
  return;
379
435
  }
@@ -389,6 +445,7 @@ async function runWithConcurrency(tasks, options = {}) {
389
445
  }
390
446
  if (signal) {
391
447
  signal.addEventListener("abort", () => {
448
+ rejected = true;
392
449
  reject(new Error("Aborted"));
393
450
  }, {
394
451
  once: true
@@ -405,6 +462,25 @@ function optionalRequire(name) {
405
462
  }
406
463
  }
407
464
  __name(optionalRequire, "optionalRequire");
465
+ async function raceWithValue(promises) {
466
+ return new Promise((resolve, reject) => {
467
+ let rejectedCount = 0;
468
+ let lastError;
469
+ promises.forEach((p, index) => {
470
+ p.then((value) => resolve({
471
+ value,
472
+ index
473
+ })).catch((err) => {
474
+ rejectedCount++;
475
+ lastError = err;
476
+ if (rejectedCount === promises.length) {
477
+ reject(lastError);
478
+ }
479
+ });
480
+ });
481
+ });
482
+ }
483
+ __name(raceWithValue, "raceWithValue");
408
484
 
409
485
  exports.CircuitBreakerOpenError = CircuitBreakerOpenError;
410
486
  exports.CircuitBreakerState = CircuitBreakerState;
@@ -415,6 +491,7 @@ exports.createTaskQueue = createTaskQueue;
415
491
  exports.debounce = debounce;
416
492
  exports.memoizeAsync = memoizeAsync;
417
493
  exports.optionalRequire = optionalRequire;
494
+ exports.raceWithValue = raceWithValue;
418
495
  exports.rateLimit = rateLimit;
419
496
  exports.retry = retry;
420
497
  exports.runInBatches = runInBatches;