@catbee/utils 2.0.0-next.0 → 2.0.0-next.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 (97) hide show
  1. package/README.md +52 -16
  2. package/array/index.cjs +180 -71
  3. package/array/index.d.ts +293 -1
  4. package/array/index.mjs +171 -72
  5. package/async/index.cjs +92 -36
  6. package/async/index.d.ts +275 -1
  7. package/async/index.mjs +92 -36
  8. package/cache/index.cjs +1 -1
  9. package/cache/index.d.ts +155 -1
  10. package/cache/index.mjs +2 -2
  11. package/config/index.cjs +78 -64
  12. package/config/index.d.ts +64 -2
  13. package/config/index.mjs +76 -64
  14. package/context-store/index.d.ts +192 -1
  15. package/crypto/index.d.ts +163 -1
  16. package/date/index.cjs +46 -1
  17. package/date/index.d.ts +190 -1
  18. package/date/index.mjs +45 -2
  19. package/decorators/index.cjs +1156 -18
  20. package/decorators/index.d.ts +684 -1
  21. package/decorators/index.mjs +1156 -18
  22. package/dir/index.cjs +4 -3
  23. package/dir/index.d.ts +195 -1
  24. package/dir/index.mjs +4 -3
  25. package/env/index.cjs +10 -26
  26. package/env/index.d.ts +379 -1
  27. package/env/index.mjs +10 -26
  28. package/exception/index.d.ts +232 -1
  29. package/fs/index.cjs +70 -36
  30. package/fs/index.d.ts +205 -1
  31. package/fs/index.mjs +64 -34
  32. package/http-status-codes/index.d.ts +267 -1
  33. package/id/index.d.ts +37 -1
  34. package/index.cjs +3 -3
  35. package/index.d.ts +1 -1
  36. package/index.mjs +1 -1
  37. package/logger/index.cjs +11 -11
  38. package/logger/index.d.ts +189 -1
  39. package/logger/index.mjs +12 -12
  40. package/middleware/index.d.ts +103 -1
  41. package/obj/index.cjs +150 -162
  42. package/obj/index.d.ts +136 -1
  43. package/obj/index.mjs +150 -162
  44. package/package.json +11 -11
  45. package/performance/index.cjs +2 -2
  46. package/performance/index.d.ts +138 -1
  47. package/performance/index.mjs +2 -2
  48. package/request/index.cjs +1 -1
  49. package/request/index.d.ts +241 -2
  50. package/request/index.mjs +1 -1
  51. package/response/index.d.ts +318 -2
  52. package/server/index.cjs +27 -23
  53. package/server/index.d.ts +785 -4
  54. package/server/index.mjs +28 -23
  55. package/stream/index.d.ts +90 -1
  56. package/string/index.d.ts +102 -1
  57. package/type/index.cjs +1 -1
  58. package/type/index.d.ts +107 -1
  59. package/type/index.mjs +1 -1
  60. package/types/index.d.ts +774 -4
  61. package/url/index.cjs +2 -4
  62. package/url/index.d.ts +142 -1
  63. package/url/index.mjs +2 -4
  64. package/{validate → validation}/index.cjs +89 -42
  65. package/{validate/validate.utils.d.ts → validation/index.d.ts} +32 -23
  66. package/{validate → validation}/index.mjs +85 -42
  67. package/array/array.utils.d.ts +0 -191
  68. package/async/async.utils.d.ts +0 -296
  69. package/cache/cache.utils.d.ts +0 -176
  70. package/config/config.d.ts +0 -57
  71. package/context-store/context-store.utils.d.ts +0 -212
  72. package/crypto/crypto.utils.d.ts +0 -183
  73. package/date/date.utils.d.ts +0 -190
  74. package/decorators/decorators.utils.d.ts +0 -705
  75. package/dir/dir.utils.d.ts +0 -216
  76. package/env/env.utils.d.ts +0 -400
  77. package/exception/exception.utils.d.ts +0 -253
  78. package/fs/fs.utils.d.ts +0 -196
  79. package/http-status-codes/http-status-codes.d.ts +0 -289
  80. package/id/id.utils.d.ts +0 -59
  81. package/logger/logger.utils.d.ts +0 -210
  82. package/middleware/middleware.utils.d.ts +0 -123
  83. package/obj/obj.utils.d.ts +0 -156
  84. package/performance/performance.utils.d.ts +0 -159
  85. package/request/request.utils.d.ts +0 -109
  86. package/response/response.utils.d.ts +0 -186
  87. package/server/server.builder.d.ts +0 -531
  88. package/server/server.d.ts +0 -303
  89. package/stream/stream.utils.d.ts +0 -111
  90. package/string/string.utils.d.ts +0 -124
  91. package/type/type.utils.d.ts +0 -129
  92. package/types/api-response.d.ts +0 -175
  93. package/types/common.d.ts +0 -148
  94. package/types/config.d.ts +0 -88
  95. package/types/server.d.ts +0 -291
  96. package/url/url.utils.d.ts +0 -164
  97. package/validate/index.d.ts +0 -25
package/README.md CHANGED
@@ -53,22 +53,58 @@ npm i @catbee/utils
53
53
  ## ⚡ Quick Start
54
54
 
55
55
  ```ts
56
- import { chunk, sleep, getLogger, uuid, isEmail } from "@catbee/utils";
57
-
58
- // Chunk an array
59
- const result = chunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
60
-
61
- // Sleep for 1 second
62
- await sleep(1000);
63
-
64
- // Log with context
65
- getLogger().info("App started");
66
-
67
- // Generate a secure UUID
68
- console.log(uuid()); // e.g. 2a563ec1-caf6-4fe2-b60c-9cf7fb1bdb7f
69
-
70
- // Basic validation
71
- console.log(isEmail("user@example.com")); // true
56
+ import { ServerConfigBuilder, ExpressServer } from '@catbee/utils/server';
57
+ import { Router } from 'express';
58
+
59
+ const config = new ServerConfigBuilder()
60
+ .withPort(3000)
61
+ .withCors({ origin: '*' })
62
+ .enableRateLimit({ max: 50, windowMs: 60000 })
63
+ .enableRequestLogging({ ignorePaths: ['/healthz', '/metrics'] })
64
+ .withHealthCheck({ path: '/health', detailed: true })
65
+ .enableOpenApi('./openapi.yaml', { mountPath: '/docs' })
66
+ .withGlobalHeaders({ 'X-Powered-By': 'Catbee' })
67
+ .withGlobalPrefix('/api')
68
+ .withRequestId({ headerName: 'X-Request-Id', exposeHeader: true })
69
+ .enableResponseTime({ addHeader: true, logOnComplete: true })
70
+ .build();
71
+
72
+ const server = new ExpressServer(config, {
73
+ beforeInit: srv => console.log('Initializing server...'),
74
+ afterInit: srv => console.log('Server initialized'),
75
+ beforeStart: app => console.log('Starting server...'),
76
+ afterStart: srv => console.log('Server started!'),
77
+ beforeStop: srv => console.log('Stopping server...'),
78
+ afterStop: () => console.log('Server stopped.'),
79
+ onRequest: (req, res, next) => {
80
+ console.log('Processing request:', req.method, req.url);
81
+ next();
82
+ },
83
+ onResponse: (req, res, next) => {
84
+ res.setHeader('X-Processed-By', 'ExpressServer');
85
+ next();
86
+ },
87
+ onError: (err, req, res, next) => {
88
+ console.error('Custom error handler:', err);
89
+ res.status(500).json({ error: 'Custom error: ' + err.message });
90
+ }
91
+ });
92
+
93
+ // Register routes
94
+ const router = server.createRouter('/users');
95
+ router.get('/', (req, res) => res.json({ users: [] }));
96
+ router.post('/', (req, res) => res.json({ created: true }));
97
+
98
+ // Or set a base router for all routes
99
+ const baseRouter = Router();
100
+ baseRouter.use('/users', router);
101
+ server.setBaseRouter(baseRouter);
102
+
103
+ server.registerHealthCheck('database', async () => await checkDatabaseConnection());
104
+ server.useMiddleware(loggingMiddleware, errorMiddleware);
105
+
106
+ await server.start();
107
+ server.enableGracefulShutdown();
72
108
  ```
73
109
 
74
110
  ---
package/array/index.cjs CHANGED
@@ -25,6 +25,7 @@
25
25
  'use strict';
26
26
 
27
27
  var obj = require('@catbee/utils/obj');
28
+ var crypto = require('crypto');
28
29
 
29
30
  var __defProp = Object.defineProperty;
30
31
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -43,49 +44,64 @@ function unique(array, keyFn) {
43
44
  if (!Array.isArray(array) || array.length === 0) return [];
44
45
  if (!keyFn) return Array.from(new Set(array));
45
46
  const seen = /* @__PURE__ */ new Set();
46
- return array.filter((item) => {
47
+ const result = [];
48
+ for (const item of array) {
47
49
  const key = keyFn(item);
48
- if (seen.has(key)) return false;
49
- seen.add(key);
50
- return true;
51
- });
50
+ if (!seen.has(key)) {
51
+ seen.add(key);
52
+ result.push(item);
53
+ }
54
+ }
55
+ return result;
52
56
  }
53
57
  __name(unique, "unique");
54
58
  function flattenDeep(array) {
55
59
  if (!Array.isArray(array)) return [];
56
60
  const result = [];
57
- for (const val of array) {
61
+ const stack = [
62
+ ...array
63
+ ];
64
+ while (stack.length) {
65
+ const val = stack.pop();
58
66
  if (Array.isArray(val)) {
59
- result.push(...flattenDeep(val));
67
+ stack.push(...val);
60
68
  } else {
61
69
  result.push(val);
62
70
  }
63
71
  }
64
- return result;
72
+ return result.reverse();
65
73
  }
66
74
  __name(flattenDeep, "flattenDeep");
67
75
  function random(array) {
68
76
  if (!Array.isArray(array) || array.length === 0) return void 0;
69
- const idx = Math.floor(Math.random() * array.length);
70
- return array[idx];
77
+ return array[secureIndex(array.length)];
71
78
  }
72
79
  __name(random, "random");
73
80
  function groupBy(array, keyOrFn) {
74
81
  if (!Array.isArray(array) || array.length === 0) return {};
75
- const keyFn = typeof keyOrFn === "function" ? keyOrFn : (item) => item[keyOrFn];
76
- return array.reduce((acc, item) => {
77
- const key = keyFn(item);
78
- if (!acc[key]) acc[key] = [];
79
- acc[key].push(item);
80
- return acc;
81
- }, {});
82
+ const keyFn = typeof keyOrFn === "function" ? keyOrFn : (item) => String(obj.getValueByPath(item, keyOrFn));
83
+ const result = {};
84
+ for (const item of array) {
85
+ const key = String(keyFn(item));
86
+ if (Object.hasOwn?.(result, key) ?? Object.prototype.hasOwnProperty.call(result, key)) {
87
+ result[key].push(item);
88
+ } else {
89
+ result[key] = [
90
+ item
91
+ ];
92
+ }
93
+ }
94
+ return Object.fromEntries(Object.entries(result).map(([k, v]) => [
95
+ k,
96
+ v
97
+ ]));
82
98
  }
83
99
  __name(groupBy, "groupBy");
84
100
  function shuffle(array) {
85
101
  if (!Array.isArray(array)) throw new TypeError("Expected an array");
86
102
  const copy = array.slice();
87
103
  for (let i = copy.length - 1; i > 0; i--) {
88
- const j = Math.floor(Math.random() * (i + 1));
104
+ const j = secureIndex(i + 1);
89
105
  [copy[i], copy[j]] = [
90
106
  copy[j],
91
107
  copy[i]
@@ -96,7 +112,7 @@ function shuffle(array) {
96
112
  __name(shuffle, "shuffle");
97
113
  function pluck(array, key) {
98
114
  if (!Array.isArray(array)) return [];
99
- return array.map((item) => item[key]);
115
+ return array.map((item) => item?.[key]);
100
116
  }
101
117
  __name(pluck, "pluck");
102
118
  function difference(a, b) {
@@ -111,35 +127,41 @@ function intersect(a, b) {
111
127
  return a.filter((item) => setB.has(item));
112
128
  }
113
129
  __name(intersect, "intersect");
114
- function mergeSort(array, key, direction = "asc") {
130
+ function mergeSort(array, key, direction = "asc", compareFn) {
115
131
  if (!Array.isArray(array)) throw new TypeError("Expected array");
116
- if (array.length <= 1) return array.slice();
132
+ const arr = array.slice();
133
+ if (arr.length <= 1) return arr;
117
134
  const keyFn = typeof key === "function" ? key : (item) => obj.getValueByPath(item, key);
118
- const compare = /* @__PURE__ */ __name((a, b) => {
135
+ const collator = new Intl.Collator("en", {
136
+ numeric: true
137
+ });
138
+ const compare = compareFn ?? function(a, b) {
119
139
  const aVal = keyFn(a);
120
140
  const bVal = keyFn(b);
121
141
  if (aVal === bVal) return 0;
122
142
  if (aVal == null) return direction === "asc" ? 1 : -1;
123
143
  if (bVal == null) return direction === "asc" ? -1 : 1;
124
- return direction === "asc" ? aVal < bVal ? -1 : 1 : aVal > bVal ? -1 : 1;
125
- }, "compare");
144
+ return direction === "asc" ? collator.compare(String(aVal), String(bVal)) : collator.compare(String(bVal), String(aVal));
145
+ };
126
146
  const merge = /* @__PURE__ */ __name((left, right) => {
127
147
  const result = [];
128
148
  let i = 0, j = 0;
129
149
  while (i < left.length && j < right.length) {
130
- if (compare(left[i], right[j]) <= 0) result.push(left[i++]);
131
- else result.push(right[j++]);
150
+ result.push(compare(left[i], right[j]) <= 0 ? left[i++] : right[j++]);
132
151
  }
133
- return result.concat(left.slice(i)).concat(right.slice(j));
152
+ while (i < left.length) result.push(left[i++]);
153
+ while (j < right.length) result.push(right[j++]);
154
+ return result;
134
155
  }, "merge");
135
- const sort = /* @__PURE__ */ __name((arr) => {
136
- if (arr.length <= 1) return arr;
137
- const mid = Math.floor(arr.length / 2);
138
- const left = sort(arr.slice(0, mid));
139
- const right = sort(arr.slice(mid));
156
+ const sort = /* @__PURE__ */ __name((input) => {
157
+ const len = input.length;
158
+ if (len <= 1) return input;
159
+ const mid = len >> 1;
160
+ const left = sort(input.slice(0, mid));
161
+ const right = sort(input.slice(mid));
140
162
  return merge(left, right);
141
163
  }, "sort");
142
- return sort(array);
164
+ return sort(arr);
143
165
  }
144
166
  __name(mergeSort, "mergeSort");
145
167
  function zip(...arrays) {
@@ -156,28 +178,20 @@ function zip(...arrays) {
156
178
  }
157
179
  __name(zip, "zip");
158
180
  function partition(array, predicate) {
181
+ const pass = [];
182
+ const fail = [];
159
183
  if (!Array.isArray(array)) return [
160
- [],
161
- []
184
+ pass,
185
+ fail
186
+ ];
187
+ for (let i = 0; i < array.length; i++) {
188
+ const item = array[i];
189
+ (predicate(item, i, array) ? pass : fail).push(item);
190
+ }
191
+ return [
192
+ pass,
193
+ fail
162
194
  ];
163
- return array.reduce(([pass, fail], item, index) => {
164
- return predicate(item, index, array) ? [
165
- [
166
- ...pass,
167
- item
168
- ],
169
- fail
170
- ] : [
171
- pass,
172
- [
173
- ...fail,
174
- item
175
- ]
176
- ];
177
- }, [
178
- [],
179
- []
180
- ]);
181
195
  }
182
196
  __name(partition, "partition");
183
197
  function range(start, end, step = 1) {
@@ -185,62 +199,157 @@ function range(start, end, step = 1) {
185
199
  throw new TypeError("Arguments must be finite numbers");
186
200
  }
187
201
  if (step === 0) throw new Error("Step cannot be zero");
188
- const isAscending = step > 0;
189
- if (isAscending && start >= end || !isAscending && start <= end) {
190
- return [];
191
- }
192
- const length = Math.max(Math.ceil((end - start) / step), 0);
193
- const result = new Array(length);
194
- for (let i = 0, value = start; i < length; i++, value += step) {
195
- result[i] = value;
202
+ const result = [];
203
+ if (step > 0) {
204
+ for (let i = start; i < end; i += step) result.push(i);
205
+ } else {
206
+ for (let i = start; i > end; i += step) result.push(i);
196
207
  }
197
208
  return result;
198
209
  }
199
210
  __name(range, "range");
200
211
  function take(array, n = 1) {
201
212
  if (!Array.isArray(array) || n <= 0) return [];
202
- return array.slice(0, n);
213
+ return n >= array.length ? array.slice() : array.slice(0, n);
203
214
  }
204
215
  __name(take, "take");
205
216
  function takeWhile(array, predicate) {
206
- if (!Array.isArray(array)) return [];
207
217
  const result = [];
208
- for (let i = 0; i < array.length; i++) {
209
- if (!predicate(array[i], i)) break;
210
- result.push(array[i]);
218
+ if (!Array.isArray(array)) return result;
219
+ const len = array.length;
220
+ for (let i = 0; i < len; i++) {
221
+ const item = array[i];
222
+ if (!predicate(item, i)) break;
223
+ result.push(item);
211
224
  }
212
225
  return result;
213
226
  }
214
227
  __name(takeWhile, "takeWhile");
215
228
  function compact(array) {
216
229
  if (!Array.isArray(array)) return [];
217
- return array.filter(Boolean);
230
+ const result = [];
231
+ for (const v of array) {
232
+ if (v) result.push(v);
233
+ }
234
+ return result;
218
235
  }
219
236
  __name(compact, "compact");
220
237
  function countBy(array, keyFn) {
221
238
  if (!Array.isArray(array)) return {};
222
- return array.reduce((acc, item) => {
239
+ const result = {};
240
+ for (const item of array) {
223
241
  const key = String(keyFn(item));
224
- acc[key] = (acc[key] || 0) + 1;
225
- return acc;
226
- }, {});
242
+ result[key] = (result[key] ?? 0) + 1;
243
+ }
244
+ return result;
227
245
  }
228
246
  __name(countBy, "countBy");
247
+ function toggle(array, item) {
248
+ if (!Array.isArray(array)) return [
249
+ item
250
+ ];
251
+ const exists = array.includes(item);
252
+ if (exists) return array.filter((x) => x !== item);
253
+ return [
254
+ ...array,
255
+ item
256
+ ];
257
+ }
258
+ __name(toggle, "toggle");
259
+ function secureIndex(max) {
260
+ if (!Number.isInteger(max) || max <= 0) throw new RangeError("Max must be a positive integer");
261
+ const limit = 4294967295 - 4294967295 % max;
262
+ let rand;
263
+ do {
264
+ rand = crypto.randomBytes(4).readUInt32BE(0);
265
+ } while (rand >= limit);
266
+ return rand % max;
267
+ }
268
+ __name(secureIndex, "secureIndex");
269
+ var secureRandom = /* @__PURE__ */ __name((array) => {
270
+ if (!Array.isArray(array) || array.length === 0) return void 0;
271
+ const idx = secureIndex(array.length);
272
+ return array[idx];
273
+ }, "secureRandom");
274
+ function findLast(array, predicate) {
275
+ if (!Array.isArray(array)) return void 0;
276
+ for (let i = array.length - 1; i >= 0; i--) {
277
+ if (predicate(array[i], i, array)) return array[i];
278
+ }
279
+ return void 0;
280
+ }
281
+ __name(findLast, "findLast");
282
+ function findLastIndex(array, predicate) {
283
+ if (!Array.isArray(array)) return -1;
284
+ for (let i = array.length - 1; i >= 0; i--) {
285
+ if (predicate(array[i], i, array)) return i;
286
+ }
287
+ return -1;
288
+ }
289
+ __name(findLastIndex, "findLastIndex");
290
+ function chunkBy(array, predicate) {
291
+ if (!Array.isArray(array) || array.length === 0) return [];
292
+ const result = [];
293
+ let chunk2 = [];
294
+ for (let i = 0; i < array.length; i++) {
295
+ if (predicate(array[i], i, array) && chunk2.length) {
296
+ result.push(chunk2);
297
+ chunk2 = [];
298
+ }
299
+ chunk2.push(array[i]);
300
+ }
301
+ if (chunk2.length) result.push(chunk2);
302
+ return result;
303
+ }
304
+ __name(chunkBy, "chunkBy");
305
+ function remove(array, value) {
306
+ if (!Array.isArray(array)) return [];
307
+ return array.filter((item) => item !== value);
308
+ }
309
+ __name(remove, "remove");
310
+ function isSorted(array, direction = "asc", compareFn) {
311
+ if (!Array.isArray(array) || array.length <= 1) return true;
312
+ const cmp = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
313
+ for (let i = 1; i < array.length; i++) {
314
+ const res = cmp(array[i - 1], array[i]);
315
+ if (direction === "asc" && res > 0 || direction === "desc" && res < 0) return false;
316
+ }
317
+ return true;
318
+ }
319
+ __name(isSorted, "isSorted");
320
+ function headOfArr(array) {
321
+ return Array.isArray(array) && array.length > 0 ? array[0] : void 0;
322
+ }
323
+ __name(headOfArr, "headOfArr");
324
+ function lastOfArr(array) {
325
+ return Array.isArray(array) && array.length > 0 ? array.at(-1) : void 0;
326
+ }
327
+ __name(lastOfArr, "lastOfArr");
229
328
 
230
329
  exports.chunk = chunk;
330
+ exports.chunkBy = chunkBy;
231
331
  exports.compact = compact;
232
332
  exports.countBy = countBy;
233
333
  exports.difference = difference;
334
+ exports.findLast = findLast;
335
+ exports.findLastIndex = findLastIndex;
234
336
  exports.flattenDeep = flattenDeep;
235
337
  exports.groupBy = groupBy;
338
+ exports.headOfArr = headOfArr;
236
339
  exports.intersect = intersect;
340
+ exports.isSorted = isSorted;
341
+ exports.lastOfArr = lastOfArr;
237
342
  exports.mergeSort = mergeSort;
238
343
  exports.partition = partition;
239
344
  exports.pluck = pluck;
240
345
  exports.random = random;
241
346
  exports.range = range;
347
+ exports.remove = remove;
348
+ exports.secureIndex = secureIndex;
349
+ exports.secureRandom = secureRandom;
242
350
  exports.shuffle = shuffle;
243
351
  exports.take = take;
244
352
  exports.takeWhile = takeWhile;
353
+ exports.toggle = toggle;
245
354
  exports.unique = unique;
246
355
  exports.zip = zip;