@catbee/utils 0.0.6 → 0.0.7
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.
- package/README.md +51 -1106
- package/build/esm/config.d.ts +5 -13
- package/build/esm/config.js +0 -12
- package/build/esm/config.js.map +1 -1
- package/build/esm/servers/server.builder.d.ts +1 -118
- package/build/esm/servers/server.builder.js +0 -20
- package/build/esm/servers/server.builder.js.map +1 -1
- package/build/esm/servers/server.d.ts +2 -2
- package/build/esm/servers/server.js +3 -13
- package/build/esm/servers/server.js.map +1 -1
- package/build/esm/types/api-response.d.ts +28 -0
- package/build/esm/types/api-response.js +10 -1
- package/build/esm/types/api-response.js.map +1 -1
- package/build/esm/types/server.d.ts +0 -17
- package/build/esm/types/server.js.map +1 -1
- package/build/esm/utils/async.utils.d.ts +91 -0
- package/build/esm/utils/async.utils.js +244 -0
- package/build/esm/utils/async.utils.js.map +1 -1
- package/build/esm/utils/context-store.utils.d.ts +3 -0
- package/build/esm/utils/context-store.utils.js +9 -6
- package/build/esm/utils/context-store.utils.js.map +1 -1
- package/build/esm/utils/date.utils.d.ts +159 -0
- package/build/esm/utils/date.utils.js +389 -0
- package/build/esm/utils/date.utils.js.map +1 -0
- package/build/esm/utils/logger.utils.d.ts +3 -9
- package/build/esm/utils/logger.utils.js +3 -9
- package/build/esm/utils/logger.utils.js.map +1 -1
- package/build/esm/utils/middleware.utils.js +2 -1
- package/build/esm/utils/middleware.utils.js.map +1 -1
- package/build/esm/utils/performance.utils.d.ts +136 -0
- package/build/esm/utils/performance.utils.js +376 -0
- package/build/esm/utils/performance.utils.js.map +1 -0
- package/build/esm/utils/request.utils.d.ts +9 -0
- package/build/esm/utils/request.utils.js +32 -5
- package/build/esm/utils/request.utils.js.map +1 -1
- package/build/esm/utils/stream.utils.d.ts +88 -0
- package/build/esm/utils/stream.utils.js +284 -0
- package/build/esm/utils/stream.utils.js.map +1 -0
- package/build/esm/utils/type.utils.d.ts +90 -0
- package/build/esm/utils/type.utils.js +190 -0
- package/build/esm/utils/type.utils.js.map +1 -0
- package/build/esnext/config.d.ts +5 -13
- package/build/esnext/config.js +0 -12
- package/build/esnext/config.js.map +1 -1
- package/build/esnext/servers/server.builder.d.ts +1 -118
- package/build/esnext/servers/server.builder.js +0 -20
- package/build/esnext/servers/server.builder.js.map +1 -1
- package/build/esnext/servers/server.d.ts +2 -2
- package/build/esnext/servers/server.js +3 -13
- package/build/esnext/servers/server.js.map +1 -1
- package/build/esnext/types/api-response.d.ts +28 -0
- package/build/esnext/types/api-response.js +10 -1
- package/build/esnext/types/api-response.js.map +1 -1
- package/build/esnext/types/server.d.ts +0 -17
- package/build/esnext/types/server.js.map +1 -1
- package/build/esnext/utils/async.utils.d.ts +91 -0
- package/build/esnext/utils/async.utils.js +190 -0
- package/build/esnext/utils/async.utils.js.map +1 -1
- package/build/esnext/utils/context-store.utils.d.ts +3 -0
- package/build/esnext/utils/context-store.utils.js +9 -6
- package/build/esnext/utils/context-store.utils.js.map +1 -1
- package/build/esnext/utils/date.utils.d.ts +159 -0
- package/build/esnext/utils/date.utils.js +384 -0
- package/build/esnext/utils/date.utils.js.map +1 -0
- package/build/esnext/utils/logger.utils.d.ts +3 -9
- package/build/esnext/utils/logger.utils.js +3 -9
- package/build/esnext/utils/logger.utils.js.map +1 -1
- package/build/esnext/utils/middleware.utils.js +2 -1
- package/build/esnext/utils/middleware.utils.js.map +1 -1
- package/build/esnext/utils/performance.utils.d.ts +136 -0
- package/build/esnext/utils/performance.utils.js +271 -0
- package/build/esnext/utils/performance.utils.js.map +1 -0
- package/build/esnext/utils/request.utils.d.ts +9 -0
- package/build/esnext/utils/request.utils.js +23 -5
- package/build/esnext/utils/request.utils.js.map +1 -1
- package/build/esnext/utils/stream.utils.d.ts +88 -0
- package/build/esnext/utils/stream.utils.js +210 -0
- package/build/esnext/utils/stream.utils.js.map +1 -0
- package/build/esnext/utils/type.utils.d.ts +90 -0
- package/build/esnext/utils/type.utils.js +187 -0
- package/build/esnext/utils/type.utils.js.map +1 -0
- package/build/src/config.d.ts +5 -13
- package/build/src/config.js +0 -12
- package/build/src/config.js.map +1 -1
- package/build/src/servers/server.builder.d.ts +1 -118
- package/build/src/servers/server.builder.js +0 -20
- package/build/src/servers/server.builder.js.map +1 -1
- package/build/src/servers/server.d.ts +2 -2
- package/build/src/servers/server.js +2 -12
- package/build/src/servers/server.js.map +1 -1
- package/build/src/types/api-response.d.ts +28 -0
- package/build/src/types/api-response.js +11 -0
- package/build/src/types/api-response.js.map +1 -1
- package/build/src/types/server.d.ts +0 -17
- package/build/src/types/server.js.map +1 -1
- package/build/src/utils/async.utils.d.ts +91 -0
- package/build/src/utils/async.utils.js +194 -0
- package/build/src/utils/async.utils.js.map +1 -1
- package/build/src/utils/context-store.utils.d.ts +3 -0
- package/build/src/utils/context-store.utils.js +9 -6
- package/build/src/utils/context-store.utils.js.map +1 -1
- package/build/src/utils/date.utils.d.ts +159 -0
- package/build/src/utils/date.utils.js +396 -0
- package/build/src/utils/date.utils.js.map +1 -0
- package/build/src/utils/logger.utils.d.ts +3 -9
- package/build/src/utils/logger.utils.js +4 -10
- package/build/src/utils/logger.utils.js.map +1 -1
- package/build/src/utils/middleware.utils.js +2 -1
- package/build/src/utils/middleware.utils.js.map +1 -1
- package/build/src/utils/performance.utils.d.ts +136 -0
- package/build/src/utils/performance.utils.js +278 -0
- package/build/src/utils/performance.utils.js.map +1 -0
- package/build/src/utils/request.utils.d.ts +9 -0
- package/build/src/utils/request.utils.js +25 -5
- package/build/src/utils/request.utils.js.map +1 -1
- package/build/src/utils/stream.utils.d.ts +88 -0
- package/build/src/utils/stream.utils.js +218 -0
- package/build/src/utils/stream.utils.js.map +1 -0
- package/build/src/utils/type.utils.d.ts +90 -0
- package/build/src/utils/type.utils.js +196 -0
- package/build/src/utils/type.utils.js.map +1 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -2,16 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## 🧰 Utility Modules for Node.js
|
|
4
4
|
|
|
5
|
-
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications
|
|
5
|
+
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications. All utilities are tree-shakable and can be imported independently.
|
|
6
6
|
|
|
7
7
|
      
|
|
8
8
|
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🚀 Key Features
|
|
12
|
+
|
|
13
|
+
- Modular: Import only what you need
|
|
14
|
+
- TypeScript-first: Full typings and type safety
|
|
15
|
+
- Production-ready: Robust, well-tested utilities
|
|
16
|
+
- Tree-shakable: Zero bloat in your bundle
|
|
17
|
+
- Express-friendly: Designed for scalable server apps
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
9
21
|
## 📦 Installation
|
|
10
22
|
|
|
11
23
|
```bash
|
|
12
24
|
npm i @catbee/utils
|
|
13
25
|
```
|
|
14
26
|
|
|
27
|
+
---
|
|
28
|
+
|
|
15
29
|
## ⚡ Quick Start
|
|
16
30
|
|
|
17
31
|
```ts
|
|
@@ -33,1124 +47,55 @@ console.log(uuid()); // e.g. 2a563ec1-caf6-4fe2-b60c-9cf7fb1bdb7f
|
|
|
33
47
|
console.log(isEmail("user@example.com")); // true
|
|
34
48
|
```
|
|
35
49
|
|
|
36
|
-
## 📦 Modules Overview
|
|
37
|
-
|
|
38
|
-
- [**Array Utilities**](#-array-utilities)
|
|
39
|
-
- [**Async Utilities**](#-async-utilities)
|
|
40
|
-
- [**Cache Utilities**](#-cache-utilities)
|
|
41
|
-
- [**Config**](#-config)
|
|
42
|
-
- [**Context Store**](#-context-store)
|
|
43
|
-
- [**Crypto Utilities**](#-crypto-utilities)
|
|
44
|
-
- [**Directory Utilities**](#-directory-utilities)
|
|
45
|
-
- [**Environment Utilities**](#-environment-utilities)
|
|
46
|
-
- [**Exception Utilities**](#-exception-utilities)
|
|
47
|
-
- [**File System Utilities**](#-file-system-utilities)
|
|
48
|
-
- [**HTTP Status Codes**](#-http-status-codes)
|
|
49
|
-
- [**ID Utilities**](#-id-utilities)
|
|
50
|
-
- [**Logger Utilities**](#-logger-utilities)
|
|
51
|
-
- [**Middleware Utilities**](#-middleware-utilities)
|
|
52
|
-
- [**Object Utilities**](#-object-utilities)
|
|
53
|
-
- [**Request Utilities**](#-request-utilities)
|
|
54
|
-
- [**Response Utilities**](#-response-utilities)
|
|
55
|
-
- [**String Utilities**](#-string-utilities)
|
|
56
|
-
- [**URL Utilities**](#-url-utilities)
|
|
57
|
-
- [**Validate Utilities**](#-validate-utilities)
|
|
58
|
-
- [**Decorators Utilities**](#-decorators-utilities)
|
|
59
|
-
- [**Express Server**](#-express-server)
|
|
60
|
-
|
|
61
50
|
---
|
|
62
51
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
```
|
|
111
|
-
|
|
112
|
-
## ⏳ Async Utilities
|
|
113
|
-
|
|
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
|
-
```
|
|
169
|
-
|
|
170
|
-
## 🗃️ Cache Utilities
|
|
171
|
-
|
|
172
|
-
In-memory TTL cache with advanced features for efficient data caching and retrieval.
|
|
173
|
-
|
|
174
|
-
- `TTLCache<K, V>(options?: TTLCacheOptions)` – Create a time-to-live in-memory cache with various methods for managing entries.
|
|
175
|
-
|
|
176
|
-
**Examples:**
|
|
177
|
-
```ts
|
|
178
|
-
const cache = new TTLCache<string, number>({ ttlMs: 3600_000 });
|
|
179
|
-
cache.set("foo", 42);
|
|
180
|
-
cache.get("foo"); // 42
|
|
181
|
-
cache.has("foo"); // true
|
|
182
|
-
cache.cleanup(); // cleans expired
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## ⚙️ Config
|
|
186
|
-
|
|
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
|
-
```
|
|
206
|
-
|
|
207
|
-
## 🧩 Context Store
|
|
208
|
-
|
|
209
|
-
Per-request context management using AsyncLocalStorage, allowing for easy sharing of data across async calls.
|
|
210
|
-
|
|
211
|
-
- `ContextStore` – Per-request context using AsyncLocalStorage.
|
|
212
|
-
- `getInstance(): AsyncLocalStorage<Store>` – Get the AsyncLocalStorage instance.
|
|
213
|
-
- `getAll(): Store | undefined` – Get the current context store.
|
|
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.
|
|
223
|
-
|
|
224
|
-
- `StoreKeys` – Common context keys.
|
|
225
|
-
- `getRequestId(): string | undefined` – Get the current request ID from context.
|
|
226
|
-
|
|
227
|
-
**Example (Express middleware usage):**
|
|
228
|
-
```ts
|
|
229
|
-
import { ContextStore, StoreKeys, getLogger } from "@catbee/utils";
|
|
230
|
-
import crypto from "crypto";
|
|
231
|
-
|
|
232
|
-
export function setupRequestContext(req, res, next) {
|
|
233
|
-
const requestId = req.headers["x-request-id"]?.toString() || crypto.randomUUID();
|
|
234
|
-
ContextStore.run({ [StoreKeys.REQUEST_ID]: requestId }, () => {
|
|
235
|
-
const logger = getLogger().child({ requestId });
|
|
236
|
-
ContextStore.set(StoreKeys.LOGGER, logger);
|
|
237
|
-
logger.info("Request context initialized");
|
|
238
|
-
next();
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
app.use(setupRequestContext);
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## 🔐 Crypto Utilities
|
|
245
|
-
|
|
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
|
-
```
|
|
283
|
-
|
|
284
|
-
## 📂 Directory Utilities
|
|
285
|
-
|
|
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
|
-
```
|
|
334
|
-
|
|
335
|
-
## 🌱 Environment Utilities
|
|
336
|
-
|
|
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.
|
|
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:**
|
|
374
|
-
|
|
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
|
-
```
|
|
432
|
-
|
|
433
|
-
## 🚨 Exception Utilities
|
|
434
|
-
|
|
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.
|
|
458
|
-
|
|
459
|
-
## 📁 File System Utilities
|
|
460
|
-
|
|
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
|
-
```
|
|
485
|
-
|
|
486
|
-
## 📊 HTTP Status Codes
|
|
487
|
-
|
|
488
|
-
A typed enum with all HTTP status codes and their standard messages.
|
|
489
|
-
|
|
490
|
-
- `HttpStatusCodes.OK === 200` – HTTP 200 OK.
|
|
491
|
-
- `HttpStatusCodes.NOT_FOUND === 404` – HTTP 404 Not Found.
|
|
492
|
-
- ...and so on.
|
|
493
|
-
|
|
494
|
-
**Example:**
|
|
495
|
-
```ts
|
|
496
|
-
import { HttpStatusCodes } from "@catbee/utils";
|
|
497
|
-
res.status(HttpStatusCodes.BAD_REQUEST).send("Invalid payload");
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
## 🆔 ID Utilities
|
|
501
|
-
|
|
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
|
-
```
|
|
513
|
-
|
|
514
|
-
## 📄 Logger Utilities
|
|
515
|
-
|
|
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
|
-
```
|
|
537
|
-
|
|
538
|
-
## 🧩 Middleware Utilities
|
|
539
|
-
|
|
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.
|
|
545
|
-
|
|
546
|
-
**Example:**
|
|
547
|
-
```ts
|
|
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 }));
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
## 🧩 Object Utilities
|
|
555
|
-
|
|
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
|
-
```
|
|
712
|
-
|
|
713
|
-
## 📝 Response Utilities
|
|
714
|
-
|
|
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
|
-
```
|
|
777
|
-
|
|
778
|
-
## 🧵 String Utilities
|
|
779
|
-
|
|
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
|
-
```
|
|
806
|
-
|
|
807
|
-
## 🌐 URL Utilities
|
|
808
|
-
|
|
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.
|
|
822
|
-
|
|
823
|
-
**Example:**
|
|
824
|
-
```ts
|
|
825
|
-
const url = appendQueryParams('https://example.com', { page: 1, limit: 10 });
|
|
826
|
-
// → 'https://example.com/?page=1&limit=10'
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
## ✅ Validate Utilities
|
|
830
|
-
|
|
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.
|
|
52
|
+
## 📚 Modules Overview
|
|
53
|
+
|
|
54
|
+
| Module | Description |
|
|
55
|
+
| ------ | ----------- |
|
|
56
|
+
| [Express Server](https://catbee-utils.npm.hprasath.com/docs/express-server) | Fast, secure, and scalable server setup |
|
|
57
|
+
| [Array Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/array) | Advanced array manipulation |
|
|
58
|
+
| [Async Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/async) | Promise helpers, concurrency, timing |
|
|
59
|
+
| [Cache Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/cache) | In-memory caching with TTL |
|
|
60
|
+
| [Context Store](https://catbee-utils.npm.hprasath.com/docs/utils/context-store) | Per-request context via AsyncLocalStorage |
|
|
61
|
+
| [Crypto Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/crypto) | Hashing, encryption, tokens |
|
|
62
|
+
| [Date Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/date) | Date/time manipulation |
|
|
63
|
+
| [Decorators Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/decorators) | TypeScript decorators for Express |
|
|
64
|
+
| [Directory Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/directory) | Directory and file system helpers |
|
|
65
|
+
| [Environment Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/environment) | Env variable management |
|
|
66
|
+
| [Exception Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/exception) | HTTP and error handling |
|
|
67
|
+
| [File System Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/file-system) | File operations |
|
|
68
|
+
| [HTTP Status Codes](https://catbee-utils.npm.hprasath.com/docs/utils/http-status-codes) | Typed status codes |
|
|
69
|
+
| [ID Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/id) | UUID and ID generation |
|
|
70
|
+
| [Logger Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/logger) | Structured logging with Pino |
|
|
71
|
+
| [Middleware Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/middleware) | Express middleware collection |
|
|
72
|
+
| [Object Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/object) | Deep merge, flatten, pick/omit, etc. |
|
|
73
|
+
| [Performance Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/performance) | Timing, memoization, memory tracking |
|
|
74
|
+
| [Request Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/request) | HTTP request parameter parsing/validation |
|
|
75
|
+
| [Response Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/response) | Standardized API response formatting |
|
|
76
|
+
| [Stream Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/stream) | Stream conversion, batching, throttling, line splitting |
|
|
77
|
+
| [String Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/string) | Casing, masking, slugifying, formatting |
|
|
78
|
+
| [Type Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/type) | Type checking, conversion, guards |
|
|
79
|
+
| [URL Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/url) | URL parsing, query manipulation, normalization |
|
|
80
|
+
| [Validate Utilities](https://catbee-utils.npm.hprasath.com/docs/utils/validation) | Input validation functions |
|
|
856
81
|
|
|
857
82
|
---
|
|
858
83
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
## Available Decorators
|
|
862
|
-
|
|
863
|
-
- `@Controller(basePath: string)` – Class decorator to set the base route.
|
|
864
|
-
- `@Get(path)`, `@Post(path)`, `@Put(path)`, `@Patch(path)`, `@Delete(path)`, `@Options(path)`, `@Head(path)`, `@Trace(path)`, `@Connect(path)` – Method decorators for HTTP verbs.
|
|
865
|
-
- `@Use(...middlewares)` – Attach Express-style middleware to a route handler.
|
|
866
|
-
- `@Query(key?)`, `@Param(key?)`, `@Body(key?)`, `@Req()`, `@Res()` – Parameter decorators for extracting request data.
|
|
867
|
-
- `@HttpCode(status)` – Set custom HTTP status code for the response.
|
|
868
|
-
- `@Header(name, value)` – Set custom response headers.
|
|
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.
|
|
872
|
-
|
|
873
|
-
## Example Usage
|
|
874
|
-
|
|
875
|
-
```typescript
|
|
876
|
-
import {
|
|
877
|
-
Controller, Get, Post, Use, Query, Param, Body, Req, Res,
|
|
878
|
-
HttpCode, Header, Before, After, Roles, registerControllers
|
|
879
|
-
} from './src/utils/decorators.utils';
|
|
880
|
-
|
|
881
|
-
// Example middleware
|
|
882
|
-
function logMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
883
|
-
console.log('Request:', req.method, req.url);
|
|
884
|
-
next();
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// Example before/after hooks
|
|
888
|
-
function beforeHook(req: Request, res: Response) {
|
|
889
|
-
console.log('Before handler');
|
|
890
|
-
}
|
|
891
|
-
function afterHook(req: Request, res: Response, result: any) {
|
|
892
|
-
console.log('After handler', result);
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
@Controller('/api')
|
|
896
|
-
class ExampleController {
|
|
897
|
-
@Get('/items/:id')
|
|
898
|
-
@Use(logMiddleware)
|
|
899
|
-
@HttpCode(200)
|
|
900
|
-
@Header('X-Example', 'yes')
|
|
901
|
-
@Before(beforeHook)
|
|
902
|
-
@After(afterHook)
|
|
903
|
-
@Roles('admin', 'moderator')
|
|
904
|
-
@Redirect('/https://example.com')
|
|
905
|
-
getItem(
|
|
906
|
-
@Query('q') q: string,
|
|
907
|
-
@Param('id') id: string,
|
|
908
|
-
@Body('name') name: string,
|
|
909
|
-
@Req() req: Request,
|
|
910
|
-
@Res() res: Response
|
|
911
|
-
) {
|
|
912
|
-
return { q, id, name };
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
@Post('/items')
|
|
916
|
-
createItem(@Body() body: any) {
|
|
917
|
-
return { created: true, ...body };
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// Register controllers with your router (Express-like)
|
|
922
|
-
const router = /* your router instance */;
|
|
923
|
-
registerControllers(router, [ExampleController]);
|
|
924
|
-
```
|
|
925
|
-
|
|
926
|
-
## Notes
|
|
927
|
-
|
|
928
|
-
- Decorated methods **must** use standard method syntax, not arrow functions or property initializers.
|
|
929
|
-
- All parameter decorators (`@Query`, `@Param`, etc.) are optional and can be used in any order.
|
|
930
|
-
- `registerControllers(router, controllers)` will register all routes and apply middlewares, hooks, status codes, and headers as defined.
|
|
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
|
|
84
|
+
## 🏁 Usage
|
|
1110
85
|
|
|
1111
|
-
|
|
86
|
+
Import only what you need:
|
|
1112
87
|
|
|
1113
88
|
```ts
|
|
1114
|
-
|
|
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
|
-
});
|
|
89
|
+
import { chunk, sleep, TTLCache, getLogger } from "@catbee/utils";
|
|
1128
90
|
```
|
|
1129
91
|
|
|
1130
|
-
|
|
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
|
-
```
|
|
92
|
+
---
|
|
1146
93
|
|
|
1147
|
-
##
|
|
94
|
+
## 📖 Documentation
|
|
1148
95
|
|
|
1149
|
-
|
|
96
|
+
- [Full API Docs & Examples](https://catbee-utils.npm.hprasath.com)
|
|
1150
97
|
|
|
1151
|
-
|
|
1152
|
-
import { chunk, sleep, TTLCache, getLogger } from "@catbee/utils";
|
|
1153
|
-
```
|
|
98
|
+
---
|
|
1154
99
|
|
|
1155
100
|
## 📜 License
|
|
1156
101
|
|