@catbee/utils 0.0.4 → 0.0.6

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 (213) hide show
  1. package/LICENSE +21 -201
  2. package/README.md +911 -242
  3. package/build/esm/config.d.ts +108 -8
  4. package/build/esm/config.js +122 -6
  5. package/build/esm/config.js.map +1 -1
  6. package/build/esm/index.d.ts +6 -0
  7. package/build/esm/index.js +29 -0
  8. package/build/esm/index.js.map +1 -1
  9. package/build/esm/servers/server.builder.d.ts +625 -0
  10. package/build/esm/servers/server.builder.js +722 -0
  11. package/build/esm/servers/server.builder.js.map +1 -0
  12. package/build/esm/servers/server.d.ts +256 -0
  13. package/build/esm/servers/server.js +1211 -0
  14. package/build/esm/servers/server.js.map +1 -0
  15. package/build/esm/types/api-response.d.ts +5 -7
  16. package/build/esm/types/api-response.js +23 -0
  17. package/build/esm/types/api-response.js.map +1 -1
  18. package/build/esm/types/index.d.ts +125 -0
  19. package/build/esm/types/index.js +25 -0
  20. package/build/esm/types/index.js.map +1 -0
  21. package/build/esm/types/server.d.ts +285 -0
  22. package/build/esm/types/server.js +25 -0
  23. package/build/esm/types/server.js.map +1 -0
  24. package/build/esm/utils/array.utils.js +23 -0
  25. package/build/esm/utils/array.utils.js.map +1 -1
  26. package/build/esm/utils/async.utils.js +23 -0
  27. package/build/esm/utils/async.utils.js.map +1 -1
  28. package/build/esm/utils/cache.utils.js +39 -16
  29. package/build/esm/utils/cache.utils.js.map +1 -1
  30. package/build/esm/utils/context-store.utils.d.ts +1 -1
  31. package/build/esm/utils/context-store.utils.js +24 -1
  32. package/build/esm/utils/context-store.utils.js.map +1 -1
  33. package/build/esm/utils/crypto.utils.js +26 -2
  34. package/build/esm/utils/crypto.utils.js.map +1 -1
  35. package/build/esm/utils/decorators.utils.d.ts +291 -0
  36. package/build/esm/utils/decorators.utils.js +395 -41
  37. package/build/esm/utils/decorators.utils.js.map +1 -1
  38. package/build/esm/utils/dir.utils.js +23 -0
  39. package/build/esm/utils/dir.utils.js.map +1 -1
  40. package/build/esm/utils/env.utils.d.ts +186 -23
  41. package/build/esm/utils/env.utils.js +517 -91
  42. package/build/esm/utils/env.utils.js.map +1 -1
  43. package/build/esm/utils/exception.utils.js +31 -8
  44. package/build/esm/utils/exception.utils.js.map +1 -1
  45. package/build/esm/utils/fs.utils.js +23 -0
  46. package/build/esm/utils/fs.utils.js.map +1 -1
  47. package/build/esm/utils/http-status-codes.js +23 -0
  48. package/build/esm/utils/http-status-codes.js.map +1 -1
  49. package/build/esm/utils/id.utils.js +23 -0
  50. package/build/esm/utils/id.utils.js.map +1 -1
  51. package/build/esm/utils/logger.utils.d.ts +118 -0
  52. package/build/esm/utils/logger.utils.js +251 -15
  53. package/build/esm/utils/logger.utils.js.map +1 -1
  54. package/build/esm/utils/middleware.utils.d.ts +29 -7
  55. package/build/esm/utils/middleware.utils.js +81 -55
  56. package/build/esm/utils/middleware.utils.js.map +1 -1
  57. package/build/esm/utils/obj.utils.d.ts +10 -3
  58. package/build/esm/utils/obj.utils.js +225 -15
  59. package/build/esm/utils/obj.utils.js.map +1 -1
  60. package/build/esm/utils/request.utils.d.ts +77 -0
  61. package/build/esm/utils/request.utils.js +196 -0
  62. package/build/esm/utils/request.utils.js.map +1 -0
  63. package/build/esm/utils/response.utils.d.ts +15 -1
  64. package/build/esm/utils/response.utils.js +55 -4
  65. package/build/esm/utils/response.utils.js.map +1 -1
  66. package/build/esm/utils/string.utils.js +23 -0
  67. package/build/esm/utils/string.utils.js.map +1 -1
  68. package/build/esm/utils/url.utils.js +23 -0
  69. package/build/esm/utils/url.utils.js.map +1 -1
  70. package/build/esm/utils/validate.utils.d.ts +7 -0
  71. package/build/esm/utils/validate.utils.js +35 -2
  72. package/build/esm/utils/validate.utils.js.map +1 -1
  73. package/build/esnext/config.d.ts +108 -8
  74. package/build/esnext/config.js +122 -6
  75. package/build/esnext/config.js.map +1 -1
  76. package/build/esnext/index.d.ts +6 -0
  77. package/build/esnext/index.js +29 -0
  78. package/build/esnext/index.js.map +1 -1
  79. package/build/esnext/servers/server.builder.d.ts +625 -0
  80. package/build/esnext/servers/server.builder.js +677 -0
  81. package/build/esnext/servers/server.builder.js.map +1 -0
  82. package/build/esnext/servers/server.d.ts +256 -0
  83. package/build/esnext/servers/server.js +918 -0
  84. package/build/esnext/servers/server.js.map +1 -0
  85. package/build/esnext/types/api-response.d.ts +5 -7
  86. package/build/esnext/types/api-response.js +23 -0
  87. package/build/esnext/types/api-response.js.map +1 -1
  88. package/build/esnext/types/index.d.ts +125 -0
  89. package/build/esnext/types/index.js +25 -0
  90. package/build/esnext/types/index.js.map +1 -0
  91. package/build/esnext/types/server.d.ts +285 -0
  92. package/build/esnext/types/server.js +25 -0
  93. package/build/esnext/types/server.js.map +1 -0
  94. package/build/esnext/utils/array.utils.js +23 -0
  95. package/build/esnext/utils/array.utils.js.map +1 -1
  96. package/build/esnext/utils/async.utils.js +23 -0
  97. package/build/esnext/utils/async.utils.js.map +1 -1
  98. package/build/esnext/utils/cache.utils.js +39 -16
  99. package/build/esnext/utils/cache.utils.js.map +1 -1
  100. package/build/esnext/utils/context-store.utils.d.ts +1 -1
  101. package/build/esnext/utils/context-store.utils.js +24 -1
  102. package/build/esnext/utils/context-store.utils.js.map +1 -1
  103. package/build/esnext/utils/crypto.utils.js +26 -2
  104. package/build/esnext/utils/crypto.utils.js.map +1 -1
  105. package/build/esnext/utils/decorators.utils.d.ts +291 -0
  106. package/build/esnext/utils/decorators.utils.js +371 -22
  107. package/build/esnext/utils/decorators.utils.js.map +1 -1
  108. package/build/esnext/utils/dir.utils.js +23 -0
  109. package/build/esnext/utils/dir.utils.js.map +1 -1
  110. package/build/esnext/utils/env.utils.d.ts +186 -23
  111. package/build/esnext/utils/env.utils.js +468 -77
  112. package/build/esnext/utils/env.utils.js.map +1 -1
  113. package/build/esnext/utils/exception.utils.js +30 -7
  114. package/build/esnext/utils/exception.utils.js.map +1 -1
  115. package/build/esnext/utils/fs.utils.js +23 -0
  116. package/build/esnext/utils/fs.utils.js.map +1 -1
  117. package/build/esnext/utils/http-status-codes.js +23 -0
  118. package/build/esnext/utils/http-status-codes.js.map +1 -1
  119. package/build/esnext/utils/id.utils.js +23 -0
  120. package/build/esnext/utils/id.utils.js.map +1 -1
  121. package/build/esnext/utils/logger.utils.d.ts +118 -0
  122. package/build/esnext/utils/logger.utils.js +222 -15
  123. package/build/esnext/utils/logger.utils.js.map +1 -1
  124. package/build/esnext/utils/middleware.utils.d.ts +29 -7
  125. package/build/esnext/utils/middleware.utils.js +77 -55
  126. package/build/esnext/utils/middleware.utils.js.map +1 -1
  127. package/build/esnext/utils/obj.utils.d.ts +10 -3
  128. package/build/esnext/utils/obj.utils.js +176 -12
  129. package/build/esnext/utils/obj.utils.js.map +1 -1
  130. package/build/esnext/utils/request.utils.d.ts +77 -0
  131. package/build/esnext/utils/request.utils.js +168 -0
  132. package/build/esnext/utils/request.utils.js.map +1 -0
  133. package/build/esnext/utils/response.utils.d.ts +15 -1
  134. package/build/esnext/utils/response.utils.js +55 -4
  135. package/build/esnext/utils/response.utils.js.map +1 -1
  136. package/build/esnext/utils/string.utils.js +23 -0
  137. package/build/esnext/utils/string.utils.js.map +1 -1
  138. package/build/esnext/utils/url.utils.js +23 -0
  139. package/build/esnext/utils/url.utils.js.map +1 -1
  140. package/build/esnext/utils/validate.utils.d.ts +7 -0
  141. package/build/esnext/utils/validate.utils.js +33 -0
  142. package/build/esnext/utils/validate.utils.js.map +1 -1
  143. package/build/src/config.d.ts +108 -8
  144. package/build/src/config.js +125 -7
  145. package/build/src/config.js.map +1 -1
  146. package/build/src/index.d.ts +6 -0
  147. package/build/src/index.js +32 -0
  148. package/build/src/index.js.map +1 -1
  149. package/build/src/servers/server.builder.d.ts +625 -0
  150. package/build/src/servers/server.builder.js +681 -0
  151. package/build/src/servers/server.builder.js.map +1 -0
  152. package/build/src/servers/server.d.ts +256 -0
  153. package/build/src/servers/server.js +958 -0
  154. package/build/src/servers/server.js.map +1 -0
  155. package/build/src/types/api-response.d.ts +5 -7
  156. package/build/src/types/api-response.js +23 -0
  157. package/build/src/types/api-response.js.map +1 -1
  158. package/build/src/types/index.d.ts +125 -0
  159. package/build/src/types/index.js +26 -0
  160. package/build/src/types/index.js.map +1 -0
  161. package/build/src/types/server.d.ts +285 -0
  162. package/build/src/types/server.js +26 -0
  163. package/build/src/types/server.js.map +1 -0
  164. package/build/src/utils/array.utils.js +23 -0
  165. package/build/src/utils/array.utils.js.map +1 -1
  166. package/build/src/utils/async.utils.js +23 -0
  167. package/build/src/utils/async.utils.js.map +1 -1
  168. package/build/src/utils/cache.utils.js +38 -15
  169. package/build/src/utils/cache.utils.js.map +1 -1
  170. package/build/src/utils/context-store.utils.d.ts +1 -1
  171. package/build/src/utils/context-store.utils.js +24 -1
  172. package/build/src/utils/context-store.utils.js.map +1 -1
  173. package/build/src/utils/crypto.utils.js +25 -1
  174. package/build/src/utils/crypto.utils.js.map +1 -1
  175. package/build/src/utils/decorators.utils.d.ts +291 -0
  176. package/build/src/utils/decorators.utils.js +373 -22
  177. package/build/src/utils/decorators.utils.js.map +1 -1
  178. package/build/src/utils/dir.utils.js +23 -0
  179. package/build/src/utils/dir.utils.js.map +1 -1
  180. package/build/src/utils/env.utils.d.ts +186 -23
  181. package/build/src/utils/env.utils.js +467 -76
  182. package/build/src/utils/env.utils.js.map +1 -1
  183. package/build/src/utils/exception.utils.js +30 -7
  184. package/build/src/utils/exception.utils.js.map +1 -1
  185. package/build/src/utils/fs.utils.js +23 -0
  186. package/build/src/utils/fs.utils.js.map +1 -1
  187. package/build/src/utils/http-status-codes.js +23 -0
  188. package/build/src/utils/http-status-codes.js.map +1 -1
  189. package/build/src/utils/id.utils.js +23 -0
  190. package/build/src/utils/id.utils.js.map +1 -1
  191. package/build/src/utils/logger.utils.d.ts +118 -0
  192. package/build/src/utils/logger.utils.js +228 -15
  193. package/build/src/utils/logger.utils.js.map +1 -1
  194. package/build/src/utils/middleware.utils.d.ts +29 -7
  195. package/build/src/utils/middleware.utils.js +76 -54
  196. package/build/src/utils/middleware.utils.js.map +1 -1
  197. package/build/src/utils/obj.utils.d.ts +10 -3
  198. package/build/src/utils/obj.utils.js +177 -12
  199. package/build/src/utils/obj.utils.js.map +1 -1
  200. package/build/src/utils/request.utils.d.ts +77 -0
  201. package/build/src/utils/request.utils.js +175 -0
  202. package/build/src/utils/request.utils.js.map +1 -0
  203. package/build/src/utils/response.utils.d.ts +15 -1
  204. package/build/src/utils/response.utils.js +56 -4
  205. package/build/src/utils/response.utils.js.map +1 -1
  206. package/build/src/utils/string.utils.js +23 -0
  207. package/build/src/utils/string.utils.js.map +1 -1
  208. package/build/src/utils/url.utils.js +23 -0
  209. package/build/src/utils/url.utils.js.map +1 -1
  210. package/build/src/utils/validate.utils.d.ts +7 -0
  211. package/build/src/utils/validate.utils.js +36 -2
  212. package/build/src/utils/validate.utils.js.map +1 -1
  213. package/package.json +40 -9
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
6
6
 
7
- [build](https://github.com/catbee-technologies/catbee-utils/actions/workflows/node-build.yml/badge.svg) ![test](https://github.com/catbee-technologies/catbee-utils/actions/workflows/code-coverage.yml/badge.svg) ![coverage](https://codecov.io/gh/catbee-technologies/catbee-utils/branch/main/graph/badge.svg) ![dependencies](https://img.shields.io/librariesio/release/npm/@catbee%2Futils)
7
+ ![build](https://img.shields.io/badge/build-passing-brightgreen) ![coverage](https://codecov.io/gh/catbee-technologies/catbee-utils/graph/badge.svg?token=XAJHK6R1OQ) ![node](https://img.shields.io/node/v/@catbee/utils) ![npm](https://img.shields.io/npm/v/@catbee/utils) ![downloads](https://img.shields.io/npm/dm/@catbee/utils) ![dependencies](https://img.shields.io/librariesio/release/npm/@catbee%2Futils) ![license](https://img.shields.io/npm/l/@catbee/utils)
8
8
 
9
9
  ## 📦 Installation
10
10
 
@@ -50,58 +50,130 @@ console.log(isEmail("user@example.com")); // true
50
50
  - [**Logger Utilities**](#-logger-utilities)
51
51
  - [**Middleware Utilities**](#-middleware-utilities)
52
52
  - [**Object Utilities**](#-object-utilities)
53
+ - [**Request Utilities**](#-request-utilities)
53
54
  - [**Response Utilities**](#-response-utilities)
54
55
  - [**String Utilities**](#-string-utilities)
55
56
  - [**URL Utilities**](#-url-utilities)
56
57
  - [**Validate Utilities**](#-validate-utilities)
57
- - [**Decorators Utilities**](#decorators-utilities)
58
+ - [**Decorators Utilities**](#-decorators-utilities)
59
+ - [**Express Server**](#-express-server)
58
60
 
59
61
  ---
60
62
 
61
63
  ## 📦 Array Utilities
62
64
 
63
- - `chunk<T>(array: T[], size: number): T[][]` – Split array into chunks.
64
- - `unique<T>(array: T[], keyFn?: (item: T) => unknown): T[]` – Remove duplicates.
65
- - `flattenDeep<T>(array: any[]): T[]` – Deep flatten nested arrays.
66
- - `random<T>(array: T[]): T | undefined` – Random element.
67
- - `groupBy<T>(array: T[], keyOrFn: keyof T | ((item: T) => string | number | symbol)): Record<string | number | symbol, T[]>` – Group by key.
68
- - `shuffle<T>(array: T[]): T[]` – Fisher-Yates shuffle.
69
- - `pluck<T, K extends keyof T>(array: T[], key: K): T[K][]` – Pluck values by key.
70
- - `difference<T>(a: T[], b: T[]): T[]` – Elements in `a` not in `b`.
71
- - `intersect<T>(a: T[], b: T[]): T[]` – Intersection.
72
- - `mergeSort<T>(array: T[], key: string | ((item: T) => any), direction?: "asc" | "desc"): T[]` – Merge sort by nested key.
73
- - `zip<T>(...arrays: T[][]): T[][]` – Zip arrays together.
74
- - `partition<T>(array: T[], predicate: (item: T, index: number, array: T[]) => boolean): [T[], T[]]` – Partition array by predicate.
75
- - `range(start: number, end: number, step?: number): number[]` – Generate a range of numbers.
76
- - `take<T>(array: T[], n?: number): T[]` – Take first `n` elements.
77
- - `takeWhile<T>(array: T[], predicate: (item: T, index: number) => boolean): T[]` – Take elements while predicate is true.
78
- - `compact<T>(array: T[]): NonNullable<T>[]` – Remove falsy values.
79
- - `countBy<T>(array: T[], keyFn: (item: T) => string | number | symbol): Record<string, number>` – Count occurrences by key.
80
- - `sample<T>(array: T[], n?: number): T[]` – Sample `n` elements.
65
+ A collection of functions for handling arrays with type-safety and efficiency.
66
+
67
+ - `chunk<T>(array: T[], size: number): T[][]` – Split array into chunks of specified size.
68
+ - `unique<T>(array: T[], keyFn?: (item: T) => unknown): T[]` – Remove duplicate items from an array, optionally by key.
69
+ - `flattenDeep<T>(array: any[]): T[]` – Deeply flatten a nested array.
70
+ - `random<T>(array: T[]): T | undefined` – Get a random element from an array.
71
+ - `groupBy<T>(array: T[], keyOrFn: keyof T | ((item: T) => string | number | symbol)): Record<string | number | symbol, T[]>` – Group array items by a key or function.
72
+ - `shuffle<T>(array: T[]): T[]` – Shuffle array elements randomly.
73
+ - `pluck<T, K extends keyof T>(array: T[], key: K): T[K][]` – Extract values for a given key from an array of objects.
74
+ - `difference<T>(a: T[], b: T[]): T[]` – Get elements in array `a` not present in array `b`.
75
+ - `intersect<T>(a: T[], b: T[]): T[]` – Get elements common to both arrays.
76
+ - `mergeSort<T>(array: T[], key: string | ((item: T) => any), direction?: "asc" | "desc"): T[]` – Sort array by key or function using merge sort.
77
+ - `zip<T>(...arrays: T[][]): T[][]` – Combine multiple arrays element-wise.
78
+ - `partition<T>(array: T[], predicate: (item: T, index: number, array: T[]) => boolean): [T[], T[]]` – Split array into two based on predicate.
79
+ - `range(start: number, end: number, step?: number): number[]` – Create an array of numbers in a range.
80
+ - `take<T>(array: T[], n?: number): T[]` – Take the first `n` elements from an array.
81
+ - `takeWhile<T>(array: T[], predicate: (item: T, index: number) => boolean): T[]` – Take elements from array while predicate is true.
82
+ - `compact<T>(array: T[]): NonNullable<T>[]` – Remove falsy values from an array.
83
+ - `countBy<T>(array: T[], keyFn: (item: T) => string | number | symbol): Record<string, number>` – Count occurrences by key or function.
84
+
85
+ **Examples:**
86
+
87
+ ```ts
88
+ // Group users by their role
89
+ const users = [
90
+ { id: 1, role: 'admin', name: 'Alice' },
91
+ { id: 2, role: 'user', name: 'Bob' },
92
+ { id: 3, role: 'user', name: 'Charlie' }
93
+ ];
94
+ const groupedByRole = groupBy(users, 'role');
95
+ // { admin: [{ id: 1, ... }], user: [{ id: 2, ... }, { id: 3, ... }] }
96
+
97
+ // Partition an array into two groups
98
+ const numbers = [1, 2, 3, 4, 5, 6];
99
+ const [evens, odds] = partition(numbers, n => n % 2 === 0);
100
+ // evens: [2, 4, 6], odds: [1, 3, 5]
101
+
102
+ // Complex sorting with mergeSort
103
+ const items = [
104
+ { nested: { value: 5 } },
105
+ { nested: { value: 2 } },
106
+ { nested: { value: 8 } }
107
+ ];
108
+ const sorted = mergeSort(items, (item) => item.nested.value);
109
+ // [{ nested: { value: 2 } }, { nested: { value: 5 } }, { nested: { value: 8 } }]
110
+ ```
81
111
 
82
112
  ## ⏳ Async Utilities
83
113
 
84
- - `sleep(ms: number): Promise<void>`
85
- - `debounce<T>(fn: T, delay: number): T & { cancel(): void; flush(): void }`
86
- - `throttle<T>(fn: T, limit: number, opts?): (...args: Parameters<T>) => void`
87
- - `retry<T>(fn: () => Promise<T>, retries?: number, delay?: number, backoff?: boolean, onRetry?): Promise<T>`
88
- - `withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>`
89
- - `runInBatches<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]>`
90
- - `singletonAsync<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, drop?: boolean): (...args: TArgs) => Promise<TResult>`
91
- - `settleAll<T>(tasks: (() => Promise<T>)[]): Promise<PromiseSettledResult<T>[]>`
92
- - `createTaskQueue(limit: number): TaskQueue`
93
- - `runInSeries<T>(tasks: (() => Promise<T>)[]): Promise<T[]>`
94
- - `memoizeAsync<T, Args extends any[]>(fn: (...args: Args) => Promise<T>, options?): (...args: Args) => Promise<T>`
95
- - `abortable<T>(promise: Promise<T>, signal: AbortSignal, abortValue?: any): Promise<T>`
96
- - `createDeferred<T>(): [Promise<T>, (value: T | PromiseLike<T>) => void, (reason?: any) => void]`
97
- - `waterfall<T>(fns: Array<(input: any) => Promise<any>>): (initialValue: any) => Promise<T>`
98
- - `rateLimit<T>(fn: (...args: any[]) => Promise<T>, maxCalls: number, interval: number): (...args: any[]) => Promise<T>`
114
+ Functions for handling asynchronous operations with better control flow and error handling.
115
+
116
+ - `sleep(ms: number): Promise<void>` – Pause execution for a given number of milliseconds.
117
+ - `debounce<T>(fn: T, delay: number): T & { cancel(): void; flush(): void }` – Debounce function calls, only invoking after delay.
118
+ - `throttle<T>(fn: T, limit: number, opts?): (...args: Parameters<T>) => void` – Throttle function calls to a maximum rate.
119
+ - `retry<T>(fn: () => Promise<T>, retries?: number, delay?: number, backoff?: boolean, onRetry?): Promise<T>` – Retry a promise-returning function with optional backoff.
120
+ - `withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>` – Reject promise if it takes longer than specified time.
121
+ - `runInBatches<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]>` – Run async tasks in batches with concurrency limit.
122
+ - `singletonAsync<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, drop?: boolean): (...args: TArgs) => Promise<TResult>` – Ensure only one instance of an async function runs at a time.
123
+ - `settleAll<T>(tasks: (() => Promise<T>)[]): Promise<PromiseSettledResult<T>[]>` – Run all tasks and return their settled results.
124
+ - `createTaskQueue(limit: number): TaskQueue` – Create a queue to run tasks with concurrency control.
125
+ - `runInSeries<T>(tasks: (() => Promise<T>)[]): Promise<T[]>` – Run async tasks one after another.
126
+ - `memoizeAsync<T, Args extends any[]>(fn: (...args: Args) => Promise<T>, options?): (...args: Args) => Promise<T>` – Memoize async function results.
127
+ - `abortable<T>(promise: Promise<T>, signal: AbortSignal, abortValue?: any): Promise<T>` – Make a promise abortable via AbortSignal.
128
+ - `createDeferred<T>(): [Promise<T>, (value: T | PromiseLike<T>) => void, (reason?: any) => void]` – Create a deferred promise with resolve/reject.
129
+ - `waterfall<T>(fns: Array<(input: any) => Promise<any>>): (initialValue: any) => Promise<T>` – Run async functions in sequence, passing result to next.
130
+ - `rateLimit<T>(fn: (...args: any[]) => Promise<T>, maxCalls: number, interval: number): (...args: any[]) => Promise<T>` – Limit the rate of async function calls.
131
+
132
+ **Examples:**
133
+
134
+ ```ts
135
+ // Retry a flaky API call
136
+ const fetchData = async () => {
137
+ const response = await fetch('https://api.example.com/data');
138
+ if (!response.ok) throw new Error(`HTTP error ${response.status}`);
139
+ return await response.json();
140
+ };
141
+
142
+ const result = await retry(
143
+ fetchData,
144
+ 3, // retry 3 times
145
+ 1000, // wait 1 second between retries
146
+ true, // use exponential backoff
147
+ (err, attempt) => console.log(`Attempt ${attempt} failed: ${err.message}`)
148
+ );
149
+
150
+ // Process items in batches to avoid overloading resources
151
+ const processItem = async (id) => {
152
+ // Process a single item...
153
+ return `Processed ${id}`;
154
+ };
155
+
156
+ const itemIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
157
+ const tasks = itemIds.map(id => processItem(id));
158
+ const results = await runInBatches(tasks, 3); // Process 3 items concurrently
159
+
160
+ // Function waterfall (pipeline)
161
+ const pipeline = waterfall([
162
+ async (input) => input * 2,
163
+ async (input) => input + 10,
164
+ async (input) => `Result: ${input}`
165
+ ]);
166
+
167
+ const finalResult = await pipeline(5); // "Result: 20"
168
+ ```
99
169
 
100
170
  ## 🗃️ Cache Utilities
101
171
 
102
- - `TTLCache<K, V>(options?: TTLCacheOptions)` – In-memory TTL cache with `.set`, `.get`, `.has`, `.delete`, `.clear`, `.cleanup`, `.entries`, `.keys`, `.values`, `.refresh`, `.stats`, `.destroy`, `.setMany`, `.getMany`, `.getOrCompute`.
172
+ In-memory TTL cache with advanced features for efficient data caching and retrieval.
103
173
 
104
- **Example:**
174
+ - `TTLCache<K, V>(options?: TTLCacheOptions)` – Create a time-to-live in-memory cache with various methods for managing entries.
175
+
176
+ **Examples:**
105
177
  ```ts
106
178
  const cache = new TTLCache<string, number>({ ttlMs: 3600_000 });
107
179
  cache.set("foo", 42);
@@ -112,28 +184,42 @@ cache.cleanup(); // cleans expired
112
184
 
113
185
  ## ⚙️ Config
114
186
 
115
- - `Config.Logger.level` – Logging level (e.g., 'info', 'debug')
116
- - `Config.Logger.name` – Logger name
117
- - `Config.Logger.isoTimestamp` – Use ISO timestamps in logs
118
- - `Config.Http.timeout` – HTTP request timeout (ms)
119
- - `Config.Cache.defaultTtl` – Default cache TTL (seconds)
187
+ Global configuration settings for the application, including logging, HTTP, and cache settings.
188
+
189
+ - `config.logger.level` – Set the logging level (e.g., 'info', 'debug').
190
+ - `config.logger.name` – Set the logger name.
191
+ - `config.logger.pretty` – Enable pretty print for logs.
192
+ - `config.http.timeout` – Set HTTP request timeout in milliseconds.
193
+ - `config.cache.defaultTtl` – Set default cache TTL in seconds.
194
+ - `setConfig(value: Partial<typeof config>): void` – Update configuration settings.
195
+ - `getConfig(): typeof config` – Get the current configuration settings.
196
+
197
+ **Example:**
198
+ ```ts
199
+ import { setConfig } from "@catbee/utils";
200
+
201
+ // Configure logging
202
+ setConfig({ logger: { pretty: true } });
203
+
204
+ console.log(getConfig());
205
+ ```
120
206
 
121
207
  ## 🧩 Context Store
122
208
 
123
- - `ContextStore` – Per-request context using AsyncLocalStorage.
209
+ Per-request context management using AsyncLocalStorage, allowing for easy sharing of data across async calls.
124
210
 
125
- **Static Methods:**
126
- - `getInstance(): AsyncLocalStorage<Store>`
211
+ - `ContextStore` – Per-request context using AsyncLocalStorage.
212
+ - `getInstance(): AsyncLocalStorage<Store>` – Get the AsyncLocalStorage instance.
127
213
  - `getAll(): Store | undefined` – Get the current context store.
128
- - `run(store: Store, callback: () => void): void`
129
- - `set(key: symbol, value: unknown): void`
130
- - `get<T>(key: symbol): T | undefined`
131
- - `has(key: symbol): boolean`
132
- - `delete(key: symbol): boolean`
133
- - `patch(values: Partial<Record<symbol, unknown>>): void`
134
- - `withValue(key: symbol, value: unknown, callback: () => T): T`
135
- - `extend(newValues: Partial<Record<symbol, unknown>>, callback: () => T): T`
136
- - `createExpressMiddleware(initialValuesFactory?): Middleware`
214
+ - `run(store: Store, callback: () => void): void` – Run a callback with a specific store context.
215
+ - `set(key: symbol, value: unknown): void` – Set a value in the current store.
216
+ - `get<T>(key: symbol): T | undefined` – Get a value from the current store.
217
+ - `has(key: symbol): boolean` – Check if a key exists in the store.
218
+ - `delete(key: symbol): boolean` – Remove a key from the store.
219
+ - `patch(values: Partial<Record<symbol, unknown>>): void` – Update multiple values in the store.
220
+ - `withValue(key: symbol, value: unknown, callback: () => T): T` – Temporarily set a value for a callback.
221
+ - `extend(newValues: Partial<Record<symbol, unknown>>, callback: () => T): T` – Temporarily extend the store for a callback.
222
+ - `createExpressMiddleware(initialValuesFactory?): Middleware` – Create Express middleware for context.
137
223
 
138
224
  - `StoreKeys` – Common context keys.
139
225
  - `getRequestId(): string | undefined` – Get the current request ID from context.
@@ -146,7 +232,7 @@ import crypto from "crypto";
146
232
  export function setupRequestContext(req, res, next) {
147
233
  const requestId = req.headers["x-request-id"]?.toString() || crypto.randomUUID();
148
234
  ContextStore.run({ [StoreKeys.REQUEST_ID]: requestId }, () => {
149
- const logger = getLogger().child({ reqId: requestId });
235
+ const logger = getLogger().child({ requestId });
150
236
  ContextStore.set(StoreKeys.LOGGER, logger);
151
237
  logger.info("Request context initialized");
152
238
  next();
@@ -157,111 +243,252 @@ app.use(setupRequestContext);
157
243
 
158
244
  ## 🔐 Crypto Utilities
159
245
 
160
- - `hmac(algorithm: string, input: string, secret: string, encoding?: BinaryToTextEncoding): string`
161
- - `hash(algorithm: string, input: string, encoding?: BinaryToTextEncoding): string`
162
- - `sha256Hmac(input: string, secret: string): string`
163
- - `sha1(input: string, encoding?: BinaryToTextEncoding): string`
164
- - `sha256(input: string, encoding?: BinaryToTextEncoding): string`
165
- - `md5(input: string): string`
166
- - `randomString(): string`
167
- - `generateRandomBytes(byteLength?: number): Buffer`
168
- - `generateRandomBytesAsString(byteLength?: number, encoding?: BinaryToTextEncoding): string`
169
- - `generateApiKey(prefix?: string, byteLength?: number): string`
170
- - `safeCompare(a: string | Buffer | Uint8Array, b: string | Buffer | Uint8Array): boolean`
171
- - `encrypt(data: string | Buffer, key: string | Buffer, options?): Promise<EncryptionResult>`
172
- - `decrypt(encryptedData: EncryptionResult, key: string | Buffer, options?): Promise<string | Buffer>`
173
- - `createSignedToken(payload: object, secret: string, expiresInSeconds?: number): string`
174
- - `verifySignedToken(token: string, secret: string): object | null`
246
+ Secure cryptographic functions for encryption, hashing, and token generation.
247
+
248
+ - `hmac(algorithm: string, input: string, secret: string, encoding?: BinaryToTextEncoding): string` – Generate an HMAC hash.
249
+ - `hash(algorithm: string, input: string, encoding?: BinaryToTextEncoding): string` – Generate a hash using a specified algorithm.
250
+ - `sha256Hmac(input: string, secret: string): string` – Generate a SHA256 HMAC.
251
+ - `sha1(input: string, encoding?: BinaryToTextEncoding): string` – Generate a SHA1 hash.
252
+ - `sha256(input: string, encoding?: BinaryToTextEncoding): string` – Generate a SHA256 hash.
253
+ - `md5(input: string): string` – Generate an MD5 hash.
254
+ - `randomString(): string` – Generate a cryptographically secure random string.
255
+ - `generateRandomBytes(byteLength?: number): Buffer` – Generate random bytes.
256
+ - `generateRandomBytesAsString(byteLength?: number, encoding?: BinaryToTextEncoding): string` – Generate random bytes as a string.
257
+ - `generateApiKey(prefix?: string, byteLength?: number): string` – Generate an API key with optional prefix.
258
+ - `safeCompare(a: string | Buffer | Uint8Array, b: string | Buffer | Uint8Array): boolean` – Timing-safe string comparison.
259
+ - `encrypt(data: string | Buffer, key: string | Buffer, options?): Promise<EncryptionResult>` – Encrypt data using AES.
260
+ - `decrypt(encryptedData: EncryptionResult, key: string | Buffer, options?): Promise<string | Buffer>` – Decrypt AES encrypted data.
261
+ - `createSignedToken(payload: object, secret: string, expiresInSeconds?: number): string` – Create a signed token (JWT-like).
262
+ - `verifySignedToken(token: string, secret: string): object | null` – Verify and decode a signed token.
263
+
264
+ **Examples:**
265
+
266
+ ```ts
267
+ // Generate and verify JWT-like tokens
268
+ const payload = { userId: 123, permissions: ['read', 'write'] };
269
+ const secret = 'secret-key';
270
+
271
+ const token = createSignedToken(payload, secret, 3600); // expires in 1 hour
272
+
273
+ // Later, verify and decode
274
+ const decodedPayload = verifySignedToken(token, secret);
275
+ if (decodedPayload) {
276
+ console.log(`User ID: ${decodedPayload.userId}`);
277
+ }
278
+
279
+ // Generate API keys for your service
280
+ const apiKey = generateApiKey('usr_', 32);
281
+ // usr_8f7d8937a9f27cb6b3f8a0928f5c9c1e
282
+ ```
175
283
 
176
284
  ## 📂 Directory Utilities
177
285
 
178
- - `ensureDir(dirPath: string): Promise<void>`
179
- - `listFiles(dirPath: string, recursive?: boolean): Promise<string[]>`
180
- - `deleteDirRecursive(dirPath: string): Promise<void>`
181
- - `isDirectory(pathStr: string): Promise<boolean>`
182
- - `copyDir(src: string, dest: string): Promise<void>`
183
- - `moveDir(src: string, dest: string): Promise<void>`
184
- - `emptyDir(dirPath: string): Promise<void>`
185
- - `getDirSize(dirPath: string): Promise<number>`
186
- - `watchDir(dirPath: string, callback): () => void`
187
- - `findFilesByPattern(pattern: string, options?): Promise<string[]>`
188
- - `getSubdirectories(dirPath: string, recursive?: boolean): Promise<string[]>`
189
- - `ensureEmptyDir(dirPath: string): Promise<void>`
190
- - `createTempDir(options?): Promise<{ path: string, cleanup: () => Promise<void> }>`
191
- - `findNewestFile(dirPath: string, recursive?: boolean): Promise<string | null>`
192
- - `findOldestFile(dirPath: string, recursive?: boolean): Promise<string | null>`
193
- - `findInDir(dirPath: string, predicate, recursive?: boolean): Promise<string[]>`
194
- - `watchDirRecursive(dirPath: string, callback, includeSubdirs?: boolean): Promise<() => void>`
195
- - `getDirStats(dirPath: string): Promise<{ fileCount: number, dirCount: number, totalSize: number }>`
196
- - `walkDir(dirPath: string, options): Promise<void>`
286
+ File and directory manipulation utilities for managing file system interactions.
287
+
288
+ - `ensureDir(dirPath: string): Promise<void>` – Ensure a directory exists.
289
+ - `listFiles(dirPath: string, recursive?: boolean): Promise<string[]>` – List files in a directory.
290
+ - `deleteDirRecursive(dirPath: string): Promise<void>` – Delete a directory and its contents.
291
+ - `isDirectory(pathStr: string): Promise<boolean>` – Check if a path is a directory.
292
+ - `copyDir(src: string, dest: string): Promise<void>` – Copy a directory recursively.
293
+ - `moveDir(src: string, dest: string): Promise<void>` – Move a directory.
294
+ - `emptyDir(dirPath: string): Promise<void>` – Remove all contents from a directory.
295
+ - `getDirSize(dirPath: string): Promise<number>` – Get the total size of a directory.
296
+ - `watchDir(dirPath: string, callback): () => void` – Watch a directory for changes.
297
+ - `findFilesByPattern(pattern: string, options?): Promise<string[]>` – Find files matching a pattern.
298
+ - `getSubdirectories(dirPath: string, recursive?: boolean): Promise<string[]>` – Get subdirectories of a directory.
299
+ - `ensureEmptyDir(dirPath: string): Promise<void>` – Ensure a directory exists and is empty.
300
+ - `createTempDir(options?): Promise<{ path: string, cleanup: () => Promise<void> }>` – Create a temporary directory.
301
+ - `findNewestFile(dirPath: string, recursive?: boolean): Promise<string | null>` – Find the newest file in a directory.
302
+ - `findOldestFile(dirPath: string, recursive?: boolean): Promise<string | null>` – Find the oldest file in a directory.
303
+ - `findInDir(dirPath: string, predicate, recursive?: boolean): Promise<string[]>` – Find files in a directory by predicate.
304
+ - `watchDirRecursive(dirPath: string, callback, includeSubdirs?: boolean): Promise<() => void>` – Watch a directory and subdirectories for changes.
305
+ - `getDirStats(dirPath: string): Promise<{ fileCount: number, dirCount: number, totalSize: number }>` – Get statistics for a directory.
306
+ - `walkDir(dirPath: string, options): Promise<void>` – Walk a directory tree with callbacks.
307
+
308
+ **Examples:**
309
+
310
+ ```ts
311
+ // Safely ensure a directory exists and is empty
312
+ await ensureEmptyDir('./temp/uploads');
313
+
314
+ // Find specific files in a directory structure
315
+ const imageFiles = await findInDir('./content',
316
+ (path) => path.endsWith('.jpg') || path.endsWith('.png'),
317
+ true // recursive search
318
+ );
319
+
320
+ // Create a temporary directory that cleans itself up
321
+ const { path, cleanup } = await createTempDir({
322
+ prefix: 'app-',
323
+ parentDir: './temp'
324
+ });
325
+
326
+ try {
327
+ // Use the temporary directory...
328
+ await writeTextFile(`${path}/data.txt`, 'Hello world');
329
+ } finally {
330
+ // Clean up when done
331
+ await cleanup();
332
+ }
333
+ ```
197
334
 
198
335
  ## 🌱 Environment Utilities
199
336
 
200
- - `Environment` enum – (`DEVELOPMENT`, `PRODUCTION`, `STAGING`, `TESTING`)
337
+ Environment variable management with powerful type-safe access, validation, and transformation capabilities.
338
+
339
+ - `Environment` enum – (`DEVELOPMENT`, `PRODUCTION`, `STAGING`, `TESTING`) – Enum for environment types.
201
340
  - `Env` class – Environment variable helpers.
341
+ - `isDev(): boolean` – Check if running in development environment.
342
+ - `isProd(): boolean` – Check if running in production environment.
343
+ - `isTest(): boolean` – Check if running in test environment.
344
+ - `isStaging(): boolean` – Check if running in staging environment.
345
+ - `set(key: string, value: string): void` – Set an environment variable.
346
+ - `getAll(): object` – Get all environment variables.
347
+ - `get(key: string, defaultValue: string): string` – Get a variable with a default value.
348
+ - `getRequired(key: string): string` – Get a required variable, throw if missing.
349
+ - `getOrFail(key: string): string` – Alias for getRequired.
350
+ - `getNumber(key: string, defaultValue: number): number` – Get a variable as a number.
351
+ - `getNumberRequired(key: string): number` – Get a required number variable.
352
+ - `getInteger(key: string, defaultValue: number, options?): number` – Get an integer with optional validation.
353
+ - `getBoolean(key: string, defaultValue?: boolean): boolean` – Get a variable as a boolean.
354
+ - `getBooleanRequired(key: string): boolean` – Get a required boolean variable.
355
+ - `getJSON<T>(key: string, defaultValue: T): T` – Parse a variable as JSON.
356
+ - `getArray<T = string>(key: string, defaultValue?: T[], splitter?: string, transform?): T[]` – Parse a variable as an array.
357
+ - `getNumberArray(key: string, defaultValue?: number[], splitter?: string): number[]` – Parse a variable as an array of numbers.
358
+ - `getEnum<T extends string>(key: string, allowedValues: T[], defaultValue: T): T` – Get a variable as an enum value.
359
+ - `getNumberEnum(key: string, allowedValues: number[], defaultValue: number): number` – Get a variable as a numeric enum.
360
+ - `getUrl(key: string, defaultValue: string, options?): string` – Get and validate a URL variable.
361
+ - `getEmail(key: string, defaultValue: string): string` – Get and validate an email variable.
362
+ - `getPath(key: string, defaultValue: string, options?): string` – Get and validate a file path variable.
363
+ - `getPort(key: string, defaultValue: number): number` – Get and validate a port number.
364
+ - `getDate(key: string, defaultValue?: string | Date): Date` – Parse a variable as a date.
365
+ - `getDuration(key: string, defaultValue?: string | number): number` – Parse a variable as a duration in ms.
366
+ - `getSafeEnv(sensitiveKeys?: string[]): Record<string, string>` – Get environment with sensitive values masked.
367
+ - `getWithDefault(key: string, defaultFn: () => string): string` – Get a variable or compute a default.
368
+ - `loadFromFile(path: string): Record<string, string>` – Load variables from a .env file.
369
+ - `has(key: string): boolean` – Check if a variable exists.
370
+ - `delete(key: string): void` – Delete a variable.
371
+ - `clearCache(): void` – Clear the internal cache.
372
+
373
+ **Examples:**
202
374
 
203
- **Static Methods:**
204
- - `isDev(): boolean`
205
- - `isProd(): boolean`
206
- - `isTest(): boolean`
207
- - `isStaging(): boolean`
208
- - `set(key: string, value: string): void`
209
- - `getAll(): object`
210
- - `get(key: string, defaultValue?: string): string | undefined`
211
- - `getRequired(key: string): string`
212
- - `getNumber(key: string, defaultValue: number): number`
213
- - `getNumberRequired(key: string): number`
214
- - `getBoolean(key: string, defaultValue?: boolean): boolean`
215
- - `getBooleanRequired(key: string): boolean`
216
- - `getJSON<T>(key: string, defaultValue: T): T`
217
- - `getArray<T = string>(key: string, defaultValue?: T[], splitter?: string): string[] | T[]`
218
- - `getEnum<T extends string>(key: string, allowedValues: T[], defaultValue?: T): T`
219
- - `getUrl(key: string, defaultValue?: string, options?): string`
220
- - `getEmail(key: string, defaultValue?: string): string`
221
- - `getPath(key: string, defaultValue?: string, options?): string`
222
- - `getPort(key: string, defaultValue?: number): number`
223
- - `getDuration(key: string, defaultValue?: string | number): number`
224
- - `getSafeEnv(sensitiveKeys?: string[]): Record<string, string>`
225
- - `getWithDefault(key: string, defaultFn: () => string): string`
226
- - `has(key: string): boolean`
227
- - `delete(key: string): void`
375
+ ```ts
376
+ import { Env, Environment } from "@catbee/utils";
377
+
378
+ // Check current environment
379
+ if (Env.isDev()) {
380
+ console.log("Running in development mode");
381
+ }
382
+
383
+ // Variable expansion (reference other environment variables)
384
+ // If DATABASE_HOST=localhost and DATABASE_NAME=myapp
385
+ Env.set('DATABASE_URL', 'postgres://${DATABASE_HOST}:5432/${DATABASE_NAME}');
386
+ const dbUrl = Env.get('DATABASE_URL', ''); // "postgres://localhost:5432/myapp"
387
+
388
+ // Load from .env file
389
+ Env.loadFromFile('.env.local');
390
+
391
+ // Number validation with range checks
392
+ const port = Env.getInteger('PORT', 3000, { min: 1024, max: 49151 });
393
+ const workerCount = Env.getNumber('WORKER_COUNT', 4);
394
+
395
+ // URL validation with protocol restrictions
396
+ const apiUrl = Env.getUrl('API_URL', 'https://api.example.com', {
397
+ protocols: ['https'],
398
+ requireTld: true,
399
+ allowLocalhost: Env.isDev() // allow localhost in development
400
+ });
401
+
402
+ // Path validation with existence check
403
+ const configPath = Env.getPath('CONFIG_PATH', './config.json', {
404
+ mustExist: true,
405
+ allowedExtensions: ['.json', '.yaml']
406
+ });
407
+
408
+ // Parse arrays with automatic transformation
409
+ const ports = Env.getNumberArray('ALLOWED_PORTS', [80, 443]);
410
+ const features = Env.getArray('ENABLED_FEATURES', [], ',');
411
+
412
+ // Parse dates and durations
413
+ const launchDate = Env.getDate('LAUNCH_DATE');
414
+ const cacheTtl = Env.getDuration('CACHE_TTL', '1h'); // in milliseconds
415
+
416
+ // Complex configurations from JSON
417
+ const serverConfig = Env.getJSON('SERVER_CONFIG', {
418
+ maxConnections: 100,
419
+ timeout: 30000,
420
+ retries: 3
421
+ });
422
+
423
+ // Safe environment printing (hiding secrets)
424
+ console.log(Env.getSafeEnv(['PASSWORD', 'API_KEY', 'SECRET']));
425
+
426
+ // Lazy default evaluation (only runs if variable is missing)
427
+ const hostname = Env.getWithDefault('HOSTNAME', () => {
428
+ console.log('Computing hostname...');
429
+ return require('os').hostname();
430
+ });
431
+ ```
228
432
 
229
433
  ## 🚨 Exception Utilities
230
434
 
231
- - `HttpError`, `InternalServerErrorException`, `UnauthorizedException`, `BadRequestException`, `NotFoundException`, `ForbiddenException`, `ConflictException`, `BadGatewayException`, `TooManyRequestsException`, `ServiceUnavailableException`, `GatewayTimeoutException`, `UnprocessableEntityException`, `MethodNotAllowedException`, `NotAcceptableException`, `RequestTimeoutException`, `UnsupportedMediaTypeException`, `PayloadTooLargeException`, `InsufficientStorageException`
232
- - `isHttpError(error: unknown): error is ErrorResponse`
233
- - `createHttpError(status: number, message?: string): ErrorResponse`
234
- - `hasErrorShape(error: unknown): error is { message: string; status?: number; code?: string }`
235
- - `getErrorMessage(error: unknown): string`
236
- - `withErrorHandling<T extends (...args: any[]) => Promise<any>>(handler: T): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>`
435
+ - `HttpError` – Base HTTP error class.
436
+ - `InternalServerErrorException` – HTTP 500 error.
437
+ - `UnauthorizedException` – HTTP 401 error.
438
+ - `BadRequestException` – HTTP 400 error.
439
+ - `NotFoundException` – HTTP 404 error.
440
+ - `ForbiddenException` – HTTP 403 error.
441
+ - `ConflictException` – HTTP 409 error.
442
+ - `BadGatewayException` – HTTP 502 error.
443
+ - `TooManyRequestsException` – HTTP 429 error.
444
+ - `ServiceUnavailableException` – HTTP 503 error.
445
+ - `GatewayTimeoutException` – HTTP 504 error.
446
+ - `UnprocessableEntityException` – HTTP 422 error.
447
+ - `MethodNotAllowedException` – HTTP 405 error.
448
+ - `NotAcceptableException` – HTTP 406 error.
449
+ - `RequestTimeoutException` – HTTP 408 error.
450
+ - `UnsupportedMediaTypeException` – HTTP 415 error.
451
+ - `PayloadTooLargeException` – HTTP 413 error.
452
+ - `InsufficientStorageException` – HTTP 507 error.
453
+ - `isHttpError(error: unknown): error is ErrorResponse` – Check if an error is an HTTP error.
454
+ - `createHttpError(status: number, message?: string): ErrorResponse` – Create a custom HTTP error.
455
+ - `hasErrorShape(error: unknown): error is { message: string; status?: number; code?: string }` – Check if an error has a standard shape.
456
+ - `getErrorMessage(error: unknown): string` – Extract a message from any error.
457
+ - `withErrorHandling<T extends (...args: any[]) => Promise<any>>(handler: T): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>` – Wrap an async function with error handling.
237
458
 
238
459
  ## 📁 File System Utilities
239
460
 
240
- - `fileExists(path: string): Promise<boolean>`
241
- - `readJsonFile<T>(path: string): Promise<T | null>`
242
- - `writeJsonFile(path: string, data: any, space?: number): Promise<void>`
243
- - `deleteFileIfExists(path: string): Promise<boolean>`
244
- - `readTextFile(path: string, encoding?: BufferEncoding): Promise<string | null>`
245
- - `writeTextFile(path: string, content: string, encoding?: BufferEncoding): Promise<boolean>`
246
- - `appendTextFile(path: string, content: string, encoding?: BufferEncoding): Promise<boolean>`
247
- - `copyFile(source: string, destination: string, overwrite?: boolean): Promise<boolean>`
248
- - `moveFile(oldPath: string, newPath: string): Promise<boolean>`
249
- - `getFileStats(path: string): Promise<fs.Stats | null>`
250
- - `createTempFile(options?): Promise<string>`
251
- - `streamFile(source: string, destination: string): Promise<void>`
252
- - `readDirectory(dirPath: string, options?): Promise<string[]>`
253
- - `createDirectory(dirPath: string, recursive?: boolean): Promise<boolean>`
254
- - `safeReadJsonFile<T>(path: string): Promise<{ data: T | null; error: Error | null }>`
255
- - `isFile(path: string): Promise<boolean>`
256
- - `getFileSize(path: string): Promise<number>`
257
- - `readFileBuffer(path: string): Promise<Buffer | null>`
461
+ - `fileExists(path: string): Promise<boolean>` – Check if a file exists.
462
+ - `readJsonFile<T>(path: string): Promise<T | null>` – Read and parse a JSON file.
463
+ - `writeJsonFile(path: string, data: any, space?: number): Promise<void>` – Write data to a JSON file.
464
+ - `deleteFileIfExists(path: string): Promise<boolean>` – Delete a file if it exists.
465
+ - `readTextFile(path: string, encoding?: BufferEncoding): Promise<string | null>` – Read a text file.
466
+ - `writeTextFile(path: string, content: string, encoding?: BufferEncoding): Promise<boolean>` – Write text to a file.
467
+ - `appendTextFile(path: string, content: string, encoding?: BufferEncoding): Promise<boolean>` – Append text to a file.
468
+ - `copyFile(source: string, destination: string, overwrite?: boolean): Promise<boolean>` – Copy a file.
469
+ - `moveFile(oldPath: string, newPath: string): Promise<boolean>` – Move or rename a file.
470
+ - `getFileStats(path: string): Promise<fs.Stats | null>` – Get file statistics.
471
+ - `createTempFile(options?): Promise<string>` – Create a temporary file.
472
+ - `streamFile(source: string, destination: string): Promise<void>` – Stream file contents.
473
+ - `readDirectory(dirPath: string, options?): Promise<string[]>` – Read directory contents.
474
+ - `createDirectory(dirPath: string, recursive?: boolean): Promise<boolean>` – Create a directory.
475
+ - `safeReadJsonFile<T>(path: string): Promise<{ data: T | null; error: Error | null }>` – Safely read a JSON file.
476
+ - `isFile(path: string): Promise<boolean>` – Check if a path is a file.
477
+ - `getFileSize(path: string): Promise<number>` – Get the size of a file.
478
+ - `readFileBuffer(path: string): Promise<Buffer | null>` – Read a file as a buffer.
479
+
480
+ **Examples:**
481
+ ```ts
482
+ import { readFileBuffer } from "@catbee/utils";
483
+ const buffer = await readFileBuffer("./file.txt");
484
+ ```
258
485
 
259
486
  ## 📊 HTTP Status Codes
260
487
 
261
488
  A typed enum with all HTTP status codes and their standard messages.
262
489
 
263
- - `HttpStatusCodes.OK === 200`
264
- - `HttpStatusCodes.NOT_FOUND === 404`
490
+ - `HttpStatusCodes.OK === 200` – HTTP 200 OK.
491
+ - `HttpStatusCodes.NOT_FOUND === 404` – HTTP 404 Not Found.
265
492
  - ...and so on.
266
493
 
267
494
  **Example:**
@@ -272,103 +499,326 @@ res.status(HttpStatusCodes.BAD_REQUEST).send("Invalid payload");
272
499
 
273
500
  ## 🆔 ID Utilities
274
501
 
275
- - `uuid(): string`
276
- - `nanoId(size?: number): string`
277
- - `randomHex(byteLength?: number): string`
278
- - `randomInt(min: number, max: number): number`
279
- - `randomBase64(byteLength?: number): number`
502
+ - `uuid(): string` – Generate a UUID v4.
503
+ - `nanoId(size?: number): string` – Generate a compact, URL-friendly unique ID.
504
+ - `randomHex(byteLength?: number): string` – Generate a random hexadecimal string.
505
+ - `randomInt(min: number, max: number): number` – Generate a random integer in a range.
506
+ - `randomBase64(byteLength?: number): number` – Generate a random Base64 string.
507
+
508
+ **Examples:**
509
+ ```ts
510
+ import { nanoId } from "@catbee/utils";
511
+ const id = nanoId();
512
+ ```
280
513
 
281
514
  ## 📄 Logger Utilities
282
515
 
283
- - `getLogger(): Logger`
284
- - `createChildLogger(bindings: Record<string, any>, parentLogger?: Logger): Logger`
285
- - `createRequestLogger(requestId: string, additionalContext?: Record<string, any>): Logger`
286
- - `logError(error: Error | unknown, message?: string, context?: Record<string, any>): void`
287
- - `resetLogger(): void`
516
+ - `getLogger(): Logger` – Get the default logger instance.
517
+ - `createChildLogger(bindings: Record<string, any>, parentLogger?: Logger): Logger` – Create a child logger with bindings.
518
+ - `createRequestLogger(requestId: string, additionalContext?: Record<string, any>): Logger` – Create a logger for a specific request.
519
+ - `logError(error: Error | unknown, message?: string, context?: Record<string, any>): void` – Log an error with optional context.
520
+ - `resetLogger(): void` – Reset the logger to its initial state.
521
+ - `getRedactCensor(): (value: any, paths: string[]) => any` – Get a censor function for redacting sensitive info.
522
+ - `setRedactCensor(censor: (value: any, paths: string[]) => any): void` – Set a custom censor function.
523
+ - `addRedactFields(fields: string[]): void` – Add fields to redact.
524
+ - `setSensitiveFields(): void` – Set the list of sensitive fields to redact.
525
+ - `addSensitiveFields(fields: string[]): void` – Add sensitive fields to redact.
526
+ - `LoggerLevels`: Enum for log levels (e.g., INFO, WARN, ERROR).
527
+ - `Logger`: Interface for the logger instance.
528
+
529
+ **Examples:**
530
+ ```ts
531
+ import { getLogger } from "@catbee/utils";
532
+ const logger = getLogger();
533
+ logger.info("App started");
534
+ logger.warn("Potential issue detected");
535
+ logger.error({ context: "some context" }, "Error occurred");
536
+ ```
288
537
 
289
538
  ## 🧩 Middleware Utilities
290
539
 
291
- - `requestId(options?): Middleware`
292
- - `responseTime(options?): Middleware`
293
- - `timeout(timeoutMs?: number): Middleware`
294
- - `setupRequestContext: void`
295
- - `errorHandler(options?): Middleware`
296
- - `validateRequest(schema, location?): Middleware`
540
+ - `requestId(options: { headerName?: string; exposeHeader?: boolean; generator?: () => string }): Middleware` – Generate and attach a unique request ID to each request.
541
+ - `responseTime(options?: { addHeader?: boolean; logOnComplete?: boolean }): Middleware` – Measure and log the response time for each request.
542
+ - `timeout(timeoutMs?: number): Middleware` – Abort requests that exceed a specified timeout.
543
+ - `setupRequestContext(): Middleware` – Set up the request context for each incoming request.
544
+ - `errorHandler(options: { logErrors?: boolean; includeDetails?: boolean }): Middleware` – Centralized error handling middleware.
297
545
 
298
546
  **Example:**
299
547
  ```ts
300
- import { requestId, responseTime, errorHandler } from "@catbee/utils";
301
- app.use(requestId());
302
- app.use(responseTime());
303
- app.use(errorHandler());
548
+ import { Env, requestId, responseTime, errorHandler } from "@catbee/utils";
549
+ app.use(requestId({ headerName: "X-Request-ID", autoLog: true }));
550
+ app.use(responseTime({ addHeaders: true, logOnComplete: true }));
551
+ app.use(errorHandler({ logErrors: true, includeDetails: true }));
304
552
  ```
305
553
 
306
554
  ## 🧩 Object Utilities
307
555
 
308
- - `isObjEmpty(obj: Record<any, any>): boolean`
309
- - `pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>`
310
- - `omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>`
311
- - `deepObjMerge<T>(target: T, source: Partial<T>): T`
312
- - `flattenObject<T>(obj: T, prefix?: string): Record<string, any>`
313
- - `getValueByPath<T>(obj: T, path: string): any`
314
- - `setValueByPath<T>(obj: T, path: string, value: any): T`
315
- - `deepClone<T>(obj: T): T`
316
- - `unflattenObject(obj: Record<string, any>): Record<string, any>`
317
- - `isEqual(a: any, b: any): boolean`
318
- - `filterObject<T>(obj: T, predicate): Partial<T>`
319
- - `mapObject<T, U>(obj: T, mapFn): Record<keyof T, U>`
320
- - `deepFreeze<T>(obj: T): Readonly<T>`
321
- - `isObject(value: unknown): value is Record<string, any>`
322
- - `getAllPaths(obj: Record<string, any>, parentPath?: string): string[]`
556
+ A set of helpers for working with JavaScript objects, including deep merging, flattening, picking/omitting keys, and more. These utilities help safely manipulate and inspect objects in a type-safe way.
557
+
558
+ - `isObjEmpty(obj: Record<any, any>): boolean` – Check if an object has no own properties.
559
+ - `pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>` – Create a new object with only the specified keys.
560
+ - `omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>` – Create a new object without the specified keys.
561
+ - `deepObjMerge<T extends object>(target: T, ...sources: any[]): T` – Deeply merge multiple objects into the target.
562
+ - `isPlainObject(value: any): value is Record<string, any>` – Check if a value is a plain object.
563
+ - `flattenObject<T>(obj: T, prefix?: string): Record<string, any>` – Flatten a nested object into dot notation.
564
+ - `getValueByPath<T>(obj: T, path: string): any` – Get a value from an object by dot path.
565
+ - `setValueByPath<T>(obj: T, path: string, value: any): T` – Set a value in an object by dot path.
566
+ - `isEqual(a: any, b: any): boolean` – Deeply compare two values for equality.
567
+ - `filterObject<T>(obj: T, predicate): Partial<T>` – Filter object properties by predicate.
568
+ - `mapObject<T, U>(obj: T, mapFn): Record<keyof T, U>` – Map object values to a new object.
569
+ - `deepFreeze<T>(obj: T): Readonly<T>` – Recursively freeze an object.
570
+ - `isObject(value: unknown): value is Record<string, any>` – Check if a value is a plain object.
571
+ - `getAllPaths(obj: Record<string, any>, parentPath?: string): string[]` – Get all dot paths in an object.
572
+
573
+ **Examples:**
574
+
575
+ ```ts
576
+ // Deep merge configurations
577
+ const defaultConfig = {
578
+ server: { port: 3000, host: 'localhost' },
579
+ logging: { level: 'info', format: 'json' }
580
+ };
581
+
582
+ const userConfig = {
583
+ server: { port: 8080 },
584
+ logging: { pretty: true }
585
+ };
586
+
587
+ const finalConfig = deepObjMerge(defaultConfig, userConfig);
588
+ /* Result:
589
+ {
590
+ server: { port: 8080, host: 'localhost' },
591
+ logging: { level: 'info', format: 'json', pretty: true }
592
+ }
593
+ */
594
+
595
+ // Flatten and unflatten nested objects
596
+ const user = {
597
+ name: 'Alice',
598
+ details: {
599
+ address: {
600
+ city: 'New York',
601
+ zip: '10001'
602
+ },
603
+ preferences: {
604
+ theme: 'dark'
605
+ }
606
+ }
607
+ };
608
+
609
+ const flat = flattenObject(user);
610
+ /* Result:
611
+ {
612
+ 'name': 'Alice',
613
+ 'details.address.city': 'New York',
614
+ 'details.address.zip': '10001',
615
+ 'details.preferences.theme': 'dark'
616
+ }
617
+ */
618
+
619
+ // Getting and setting values by path
620
+ const theme = getValueByPath(user, 'details.preferences.theme'); // 'dark'
621
+ const updated = setValueByPath(user, 'details.address.state', 'NY');
622
+ ```
623
+
624
+ ## 📝 Request Utilities
625
+
626
+ Functions for safely parsing, validating, and extracting parameters from HTTP requests.
627
+
628
+ - `parseNumberParam(value: string | undefined, options?: ValidationOptions): ValidationResult<number>` – Parse and validate a numeric parameter from a string.
629
+ - `parseBooleanParam(value: string | undefined, options?: ValidationOptions): ValidationResult<boolean>` – Parse and validate a boolean parameter from a string.
630
+ - `extractPaginationParams(query: Record<string, string | string[]>, defaultPage?: number, defaultLimit?: number, maxLimitSize?: number): { page: number; limit: number }` – Extract and validate pagination parameters from a query object.
631
+ - `extractSortParams(query: Record<string, string | string[]>, allowedFields: string[], defaultSort?): { sortBy: string; sortOrder: 'asc' | 'desc' }` – Extract and validate sorting parameters from a query object.
632
+ - `extractFilterParams(query: Record<string, string | string[]>, allowedFilters: string[]): Record<string, string | string[]>` – Extract filter parameters from a query object based on allowed fields.
633
+
634
+ **Types:**
635
+ ```ts
636
+ interface ValidationOptions {
637
+ throwOnError?: boolean;
638
+ errorMessage?: string;
639
+ defaultValue?: any;
640
+ required?: boolean;
641
+ }
642
+
643
+ interface ValidationResult<T> {
644
+ isValid: boolean;
645
+ value: T | null;
646
+ error?: string;
647
+ }
648
+ ```
649
+
650
+ **Examples:**
651
+
652
+ ```ts
653
+ import {
654
+ parseNumberParam,
655
+ parseBooleanParam,
656
+ extractPaginationParams,
657
+ extractSortParams,
658
+ extractFilterParams
659
+ } from '@catbee/utils';
660
+
661
+ // Parse and validate query parameters
662
+ function handleRequest(req, res) {
663
+ // Parse numeric parameter with validation
664
+ const idResult = parseNumberParam(req.query.id, {
665
+ required: true,
666
+ throwOnError: false
667
+ });
668
+
669
+ if (!idResult.isValid) {
670
+ return res.status(400).send({ error: idResult.error });
671
+ }
672
+
673
+ // Parse boolean parameter
674
+ const isActiveResult = parseBooleanParam(req.query.isActive, {
675
+ defaultValue: false
676
+ });
677
+
678
+ // Extract standard pagination parameters
679
+ const { page, limit } = extractPaginationParams(
680
+ req.query,
681
+ 1, // default page
682
+ 20, // default limit
683
+ 100 // max limit
684
+ );
685
+
686
+ // Extract sorting parameters
687
+ const { sortBy, sortOrder } = extractSortParams(
688
+ req.query,
689
+ ['name', 'createdAt', 'price'], // allowed fields
690
+ { sortBy: 'createdAt', sortOrder: 'desc' } // defaults
691
+ );
692
+
693
+ // Extract filters based on allowed fields
694
+ const filters = extractFilterParams(
695
+ req.query,
696
+ ['category', 'status', 'price']
697
+ );
698
+
699
+ // Process the request with validated parameters
700
+ const items = getItems({
701
+ page,
702
+ limit,
703
+ sortBy,
704
+ sortOrder,
705
+ filters,
706
+ isActive: isActiveResult.value
707
+ });
708
+
709
+ return res.json(items);
710
+ }
711
+ ```
323
712
 
324
713
  ## 📝 Response Utilities
325
714
 
326
- - `SuccessResponse<T>` – Standard API success wrapper.
327
- - `ErrorResponse` – Standard API error wrapper.
328
- - `PaginatedResponse<T>` – Paginated API response.
329
- - `NoContentResponse` – 204 No Content response.
330
- - `RedirectResponse` – Redirect response.
331
- - `createSuccessResponse<T>(data: T, message?: string): SuccessResponse<T>`
332
- - `createErrorResponse(message: string, statusCode?: number): ErrorResponse`
333
- - `createPaginatedResponse<T>(allItems: T[], page: number, pageSize: number, message?: string): PaginatedResponse<T>`
334
- - `sendResponse(res, apiResponse): void`
335
- - `isApiResponse(value: any): value is ApiResponse<any>`
715
+ Helpers for creating and sending standardized API responses, including success, error, paginated, and redirect responses. Ensures consistent structure and status codes for all API endpoints.
716
+
717
+ - `SuccessResponse<T>` – Standard API success wrapper object.
718
+ - `ErrorResponse` – Standard API error wrapper object.
719
+ - `PaginatedResponse<T>` – Paginated API response object.
720
+ - `NoContentResponse` – 204 No Content response object.
721
+ - `RedirectResponse` – Redirect response object.
722
+ - `createSuccessResponse<T>(data: T, message?: string): SuccessResponse<T>` – Create a success response.
723
+ - `createErrorResponse(message: string, statusCode?: number): ErrorResponse` – Create an error response.
724
+ - `createPaginatedResponse<T>(allItems: T[], page: number, pageSize: number, message?: string): PaginatedResponse<T>` – Create a paginated response.
725
+ - `sendResponse(res, apiResponse): void` – Send an API response using an HTTP response object.
726
+ - `isApiResponse(value: any): value is ApiResponse<any>` – Check if a value is an API response.
727
+
728
+ **Examples:**
729
+
730
+ ```ts
731
+ import {
732
+ createSuccessResponse,
733
+ createErrorResponse,
734
+ createPaginatedResponse,
735
+ sendResponse
736
+ } from "@catbee/utils";
737
+ import express from "express";
738
+
739
+ const app = express();
740
+
741
+ app.get('/api/users', (req, res) => {
742
+ try {
743
+ const allUsers = [/* ...users from database... */];
744
+ const page = parseInt(req.query.page as string) || 1;
745
+ const pageSize = parseInt(req.query.limit as string) || 10;
746
+
747
+ const response = createPaginatedResponse(
748
+ allUsers,
749
+ page,
750
+ pageSize,
751
+ 'Users retrieved successfully'
752
+ );
753
+
754
+ sendResponse(res, response);
755
+ } catch (error) {
756
+ const errorResponse = createErrorResponse(
757
+ 'Failed to retrieve users',
758
+ 500
759
+ );
760
+ sendResponse(res, errorResponse);
761
+ }
762
+ });
763
+
764
+ app.get('/api/profile', (req, res) => {
765
+ const user = { id: 123, name: 'Alice' };
766
+ const response = createSuccessResponse(user, 'Profile retrieved');
767
+ sendResponse(res, response);
768
+
769
+ // This sends:
770
+ // {
771
+ // success: true,
772
+ // data: { id: 123, name: 'Alice' },
773
+ // message: 'Profile retrieved'
774
+ // }
775
+ });
776
+ ```
336
777
 
337
778
  ## 🧵 String Utilities
338
779
 
339
- - `capitalize(str: string): string`
340
- - `toKebabCase(str: string): string`
341
- - `toCamelCase(str: string): string`
342
- - `slugify(str: string): string`
343
- - `truncate(str: string, len: number): string`
344
- - `toPascalCase(str: string): string`
345
- - `toSnakeCase(str: string): string`
346
- - `format(template: string, values: Record<string, any> | any[]): string`
347
- - `isValidEmail(str: string): boolean`
348
- - `isValidUrl(str: string, requireProtocol?: boolean): boolean`
349
- - `mask(str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string): string`
350
- - `stripHtml(str: string): string`
351
- - `equalsIgnoreCase(a: string, b: string): boolean`
352
- - `reverse(str: string): string`
353
- - `countOccurrences(str: string, substring: string, caseSensitive?: boolean): number`
354
- - `randomString(length?: number, charset?: string): string`
355
- - `pluralize(singular: string, count: number, plural?: string): string`
356
- - `toTitleCase(str: string): string`
357
- - `pad(str: string, length: number, padChar?: string, padEnd?: boolean): string`
780
+ A set of utilities for manipulating, formatting, and transforming strings. Includes helpers for casing, masking, slugifying, truncating, and more.
781
+
782
+ - `capitalize(str: string): string` – Capitalize the first letter of a string.
783
+ - `toKebabCase(str: string): string` – Convert a string to kebab-case.
784
+ - `toCamelCase(str: string): string` – Convert a string to camelCase.
785
+ - `slugify(str: string): string` – Convert a string to a URL-friendly slug.
786
+ - `truncate(str: string, len: number): string` – Truncate a string to a maximum length.
787
+ - `toPascalCase(str: string): string` – Convert a string to PascalCase.
788
+ - `toSnakeCase(str: string): string` – Convert a string to snake_case.
789
+ - `mask(str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string): string` – Mask part of a string for privacy.
790
+ - `stripHtml(str: string): string` – Remove HTML tags from a string.
791
+ - `equalsIgnoreCase(a: string, b: string): boolean` – Compare two strings ignoring case.
792
+ - `reverse(str: string): string` – Reverse the characters in a string.
793
+ - `countOccurrences(str: string, substring: string, caseSensitive?: boolean): number` – Count how many times a substring appears.
794
+
795
+ ** Examples **
796
+ ```ts
797
+ import { capitalize, toCamelCase, slugify, mask } from "@catbee/utils";
798
+
799
+ const exampleString = "Hello World";
800
+
801
+ console.log(capitalize(exampleString)); // "Hello world"
802
+ console.log(toCamelCase(exampleString)); // "helloWorld"
803
+ console.log(slugify(exampleString)); // "hello-world"
804
+ console.log(mask(exampleString, 2, 5, "*")); // "He***"
805
+ ```
358
806
 
359
807
  ## 🌐 URL Utilities
360
808
 
361
- - `appendQueryParams(url: string, params: Record<string, string | number>): string`
362
- - `parseQueryString(query: string): Record<string, string>`
363
- - `isValidUrl(url: string, requireHttps?: boolean): boolean`
364
- - `getDomain(url: string, removeSubdomains?: boolean): string`
365
- - `joinPaths(...segments: string[]): string`
366
- - `normalizeUrl(url: string, base?: string): string`
367
- - `createUrlBuilder(baseUrl: string): { path(path: string, params?: Record<string, any>): string; query(params: Record<string, any>): string }`
368
- - `extractQueryParams(url: string, paramNames: string[]): Record<string, string>`
369
- - `removeQueryParams(url: string, paramsToRemove: string[]): string`
370
- - `getExtension(url: string): string`
371
- - `parseTypedQueryParams<T>(url: string, converters?: Record<keyof T, (val: string) => any>): Partial<T>`
809
+ Helpers for parsing, validating, building, and manipulating URLs and query strings. Useful for web, API, and routing logic.
810
+
811
+ - `appendQueryParams(url: string, params: Record<string, string | number>): string` – Add query parameters to a URL.
812
+ - `parseQueryString(query: string): Record<string, string>` – Parse a query string into an object.
813
+ - `isValidUrl(url: string, requireHttps?: boolean): boolean` – Check if a string is a valid URL.
814
+ - `getDomain(url: string, removeSubdomains?: boolean): string` – Extract the domain from a URL.
815
+ - `joinPaths(...segments: string[]): string` – Join multiple URL path segments.
816
+ - `normalizeUrl(url: string, base?: string): string` – Normalize a URL, optionally with a base.
817
+ - `createUrlBuilder(baseUrl: string): { path(path: string, params?: Record<string, any>): string; query(params: Record<string, any>): string }` – Build URLs with paths and query parameters.
818
+ - `extractQueryParams(url: string, paramNames: string[]): Record<string, string>` – Extract specific query parameters from a URL.
819
+ - `removeQueryParams(url: string, paramsToRemove: string[]): string` – Remove specific query parameters from a URL.
820
+ - `getExtension(url: string): string` – Get the file extension from a URL.
821
+ - `parseTypedQueryParams<T>(url: string, converters?: Record<keyof T, (val: string) => any>): Partial<T>` – Parse and convert query parameters to typed values.
372
822
 
373
823
  **Example:**
374
824
  ```ts
@@ -378,31 +828,31 @@ const url = appendQueryParams('https://example.com', { page: 1, limit: 10 });
378
828
 
379
829
  ## ✅ Validate Utilities
380
830
 
381
- A comprehensive suite of string/format validators for safe input and API checks.
382
-
383
- - `isEmail(str: string): boolean`
384
- - `isUUID(str: string): boolean`
385
- - `isURL(str: string): boolean`
386
- - `isPhone(str: string): boolean`
387
- - `isAlphanumeric(str: string): boolean`
388
- - `isNumeric(value: string | number): boolean`
389
- - `isHexColor(str: string): boolean`
390
- - `isISODate(str: string): boolean`
391
- - `isLengthBetween(str: string, min: number, max: number): boolean`
392
- - `isNumberBetween(value: number, min: number, max: number): boolean`
393
- - `isAlpha(str: string): boolean`
394
- - `isStrongPassword(str: string): boolean`
395
- - `isIPv4(str: string): boolean`
396
- - `isIPv6(str: string): boolean`
397
- - `isCreditCard(str: string): boolean`
398
- - `isValidJSON(str: string): boolean`
399
- - `isObject(value: unknown): value is Record<string, unknown>`
400
- - `isArray<T = unknown>(value: unknown, itemGuard?: (item: unknown) => item is T): value is T[]`
401
- - `isBase64(str: string): boolean`
402
- - `hasRequiredProps(obj: Record<string, unknown>, requiredProps: string[]): boolean`
403
- - `isDateInRange(date: Date, minDate?: Date, maxDate?: Date): boolean`
404
- - `matchesPattern(str: string, pattern: RegExp): boolean`
405
- - `validateAll(value: unknown, validators: Array<(value: unknown) => boolean>): boolean`
831
+ A comprehensive suite of validators for checking strings, numbers, objects, arrays, and common formats such as email, UUID, IP, and more. Useful for input validation and API parameter checks.
832
+
833
+ - `isPort(str: string | number): boolean` – Check if a string or number is a valid port number.
834
+ - `isEmail(str: string): boolean` – Check if a string is a valid email address.
835
+ - `isUUID(str: string): boolean` – Check if a string is a valid UUID.
836
+ - `isURL(str: string): boolean` – Check if a string is a valid URL.
837
+ - `isPhone(str: string): boolean` – Check if a string is a valid phone number.
838
+ - `isAlphanumeric(str: string): boolean` – Check if a string contains only letters and numbers.
839
+ - `isNumeric(value: string | number): boolean` – Check if a value is numeric.
840
+ - `isHexColor(str: string): boolean` – Check if a string is a valid hex color code.
841
+ - `isISODate(str: string): boolean` – Check if a string is a valid ISO date.
842
+ - `isLengthBetween(str: string, min: number, max: number): boolean` – Check if a string's length is within a range.
843
+ - `isNumberBetween(value: number, min: number, max: number): boolean` – Check if a number is within a range.
844
+ - `isAlpha(str: string): boolean` – Check if a string contains only letters.
845
+ - `isStrongPassword(str: string): boolean` – Check if a string is a strong password.
846
+ - `isIPv4(str: string): boolean` – Check if a string is a valid IPv4 address.
847
+ - `isIPv6(str: string): boolean` – Check if a string is a valid IPv6 address.
848
+ - `isCreditCard(str: string): boolean` – Check if a string is a valid credit card number.
849
+ - `isValidJSON(str: string): boolean` – Check if a string is valid JSON.
850
+ - `isArray<T = unknown>(value: unknown, itemGuard?: (item: unknown) => item is T): value is T[]` – Check if a value is an array (optionally with type guard).
851
+ - `isObject(value: unknown): value is Record<string, unknown>` – Check if a value is a plain object.
852
+ - `hasRequiredProps(obj: Record<string, unknown>, requiredProps: string[]): boolean` – Check if an object has all required properties.
853
+ - `isDateInRange(date: Date, minDate?: Date, maxDate?: Date): boolean` – Check if a date is within a range.
854
+ - `matchesPattern(str: string, pattern: RegExp): boolean` – Check if a string matches a regular expression.
855
+ - `validateAll(value: unknown, validators: Array<(value: unknown) => boolean>): boolean` – Check if all validators pass for a value.
406
856
 
407
857
  ---
408
858
 
@@ -417,13 +867,15 @@ A comprehensive suite of string/format validators for safe input and API checks.
417
867
  - `@HttpCode(status)` – Set custom HTTP status code for the response.
418
868
  - `@Header(name, value)` – Set custom response headers.
419
869
  - `@Before(fn)`, `@After(fn)` – Register before/after hooks for a route handler.
870
+ - `@Redirect({ url?: string, statusCode: number })` – Redirect to a different URL.
871
+ - `@Roles(...roles: string[])` – Restrict access to certain roles - req.user.roles[] should include at least one of the specified roles.
420
872
 
421
873
  ## Example Usage
422
874
 
423
875
  ```typescript
424
876
  import {
425
877
  Controller, Get, Post, Use, Query, Param, Body, Req, Res,
426
- HttpCode, Header, Before, After, registerControllers, Request, Response, NextFunction
878
+ HttpCode, Header, Before, After, Roles, registerControllers
427
879
  } from './src/utils/decorators.utils';
428
880
 
429
881
  // Example middleware
@@ -448,6 +900,8 @@ class ExampleController {
448
900
  @Header('X-Example', 'yes')
449
901
  @Before(beforeHook)
450
902
  @After(afterHook)
903
+ @Roles('admin', 'moderator')
904
+ @Redirect('/https://example.com')
451
905
  getItem(
452
906
  @Query('q') q: string,
453
907
  @Param('id') id: string,
@@ -475,6 +929,221 @@ registerControllers(router, [ExampleController]);
475
929
  - All parameter decorators (`@Query`, `@Param`, etc.) are optional and can be used in any order.
476
930
  - `registerControllers(router, controllers)` will register all routes and apply middlewares, hooks, status codes, and headers as defined.
477
931
 
932
+ ## 🚀 Express Server
933
+
934
+ A production-ready Express server with enterprise-grade features for building robust web services and APIs.
935
+
936
+ ### Features
937
+
938
+ - **Security**: CORS, Helmet, rate limiting, timeouts, body size limits
939
+ - **Monitoring**: Request logging, Prometheus metrics, health checks
940
+ - **Performance**: Response compression, static file serving, conditional settings
941
+ - **Reliability**: Graceful shutdown, connection tracking, error handling
942
+ - **Developer UX**: OpenAPI docs, debugging tools
943
+ - **Extensibility**: Lifecycle hooks, middleware registration, custom routes
944
+
945
+ ### Getting Started
946
+
947
+ Create a server with the fluent builder pattern:
948
+
949
+ ```ts
950
+ import { ServerConfigBuilder, ExpressServer } from "@catbee/utils";
951
+
952
+ // Configure the server
953
+ const config = new ServerConfigBuilder()
954
+ .withPort(3000)
955
+ .withHost('localhost')
956
+ .enableCors()
957
+ .enableHelmet()
958
+ .enableCompression()
959
+ .withGlobalPrefix('/api')
960
+ .withRequestLogging({
961
+ enable: true,
962
+ ignorePaths: ['/healthz', '/metrics']
963
+ })
964
+ .enableMetrics()
965
+ .build();
966
+
967
+ // Create and start the server
968
+ const server = new ExpressServer(config, {
969
+ beforeStart: (app) => console.log('Server about to start...'),
970
+ afterStart: (server) => console.log('Server started successfully!')
971
+ });
972
+
973
+ // Register health checks
974
+ server.registerHealthCheck('database', async () => {
975
+ return await checkDatabaseConnection();
976
+ });
977
+
978
+ // Register routes
979
+ const router = server.createRouter('/users');
980
+ router.get('/', (req, res) => {
981
+ res.json({ users: [] });
982
+ });
983
+
984
+ // Start the server
985
+ await server.start();
986
+
987
+ // Enable graceful shutdown handling
988
+ server.enableGracefulShutdown();
989
+ ```
990
+
991
+ ### Server Configuration Builder
992
+
993
+ Use the fluent builder API to configure all aspects of your server:
994
+
995
+ ```ts
996
+ import { ServerConfigBuilder } from "@catbee/utils";
997
+
998
+ const config = new ServerConfigBuilder()
999
+ // Basic configuration
1000
+ .withPort(3000) // Set listen port
1001
+ .withHost('0.0.0.0') // Set listen address
1002
+ .withGlobalPrefix('/api/v1') // Set global route prefix
1003
+
1004
+ // Security
1005
+ .enableCors() // Enable CORS (simple)
1006
+ .withCors({ // Configure CORS (advanced)
1007
+ origin: ['https://example.com'],
1008
+ methods: ['GET', 'POST']
1009
+ })
1010
+ .enableHelmet() // Enable secure headers
1011
+ .enableRateLimit({ // Configure rate limiting
1012
+ max: 100,
1013
+ windowMs: 15 * 60 * 1000
1014
+ })
1015
+
1016
+ // Performance
1017
+ .enableCompression() // Enable response compression
1018
+ .withStaticFolder({ // Serve static files
1019
+ path: '/assets',
1020
+ directory: './public/assets',
1021
+ maxAge: '1d'
1022
+ })
1023
+
1024
+ // Monitoring
1025
+ .enableMetrics({ // Enable Prometheus metrics
1026
+ path: '/metrics'
1027
+ })
1028
+ .withHealthCheck({ // Configure health checks
1029
+ path: '/health',
1030
+ detailed: true
1031
+ })
1032
+ .enableResponseTime() // Track response times
1033
+ .enableRequestLogging() // Log requests
1034
+
1035
+ // Documentation
1036
+ .enableOpenApi('./openapi.yaml', { // Enable OpenAPI docs
1037
+ mountPath: '/docs'
1038
+ })
1039
+
1040
+ // Microservice configuration
1041
+ .withMicroService({ // Configure as microservice
1042
+ appName: 'user-service',
1043
+ serviceVersion: {
1044
+ enable: true,
1045
+ version: '1.0.0'
1046
+ }
1047
+ })
1048
+
1049
+ // Build final configuration
1050
+ .build();
1051
+ ```
1052
+
1053
+ ### Server Lifecycle Hooks
1054
+
1055
+ Register hooks for key server lifecycle events:
1056
+
1057
+ ```ts
1058
+ const server = new ExpressServer(config, {
1059
+ // Before server initialization (middleware setup)
1060
+ beforeInit: (server) => {
1061
+ console.log('Initializing server...');
1062
+ },
1063
+
1064
+ // After server initialization (routes registered)
1065
+ afterInit: (server) => {
1066
+ console.log('Server initialized');
1067
+ },
1068
+
1069
+ // Before server starts listening
1070
+ beforeStart: (app) => {
1071
+ console.log('Starting server...');
1072
+ },
1073
+
1074
+ // After server has started listening
1075
+ afterStart: (httpServer) => {
1076
+ console.log('Server started');
1077
+ },
1078
+
1079
+ // Before server begins shutdown
1080
+ beforeStop: (httpServer) => {
1081
+ console.log('Shutting down server...');
1082
+ },
1083
+
1084
+ // After server has fully stopped
1085
+ afterStop: () => {
1086
+ console.log('Server stopped');
1087
+ },
1088
+
1089
+ // Global request handler
1090
+ onRequest: (req, res, next) => {
1091
+ console.log('Processing request...');
1092
+ next();
1093
+ },
1094
+
1095
+ // Global response handler
1096
+ onResponse: (req, res, next) => {
1097
+ res.setHeader('X-Server-Time', new Date().toISOString());
1098
+ next();
1099
+ },
1100
+
1101
+ // Global error handler
1102
+ onError: (err, req, res, next) => {
1103
+ console.error('Error processing request:', err);
1104
+ res.status(500).json({ error: 'Something went wrong' });
1105
+ }
1106
+ });
1107
+ ```
1108
+
1109
+ ### Health Checks
1110
+
1111
+ Register health checks for monitoring service dependencies:
1112
+
1113
+ ```ts
1114
+ // Simple health check
1115
+ server.registerHealthCheck('storage', () => {
1116
+ return fs.existsSync('./data');
1117
+ });
1118
+
1119
+ // Async health check
1120
+ server.registerHealthCheck('database', async () => {
1121
+ try {
1122
+ await db.ping();
1123
+ return true;
1124
+ } catch (err) {
1125
+ return false;
1126
+ }
1127
+ });
1128
+ ```
1129
+
1130
+ ### Graceful Shutdown
1131
+
1132
+ Enable zero-downtime deployments with graceful shutdown:
1133
+
1134
+ ```ts
1135
+ // Enable graceful shutdown handling
1136
+ server.enableGracefulShutdown();
1137
+
1138
+ // Manually trigger a graceful shutdown
1139
+ async function shutdown() {
1140
+ console.log('Starting graceful shutdown...');
1141
+ await server.stop();
1142
+ console.log('Server stopped gracefully');
1143
+ process.exit(0);
1144
+ }
1145
+ ```
1146
+
478
1147
  ## 🏁 Usage
479
1148
 
480
1149
  Import only what you need: