@gerync/utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +385 -0
- package/SECURITY.md +45 -0
- package/dist/functions/Colorlog.d.ts +17 -0
- package/dist/functions/Colorlog.d.ts.map +1 -0
- package/dist/functions/Colorlog.js +44 -0
- package/dist/functions/Config.d.ts +45 -0
- package/dist/functions/Config.d.ts.map +1 -0
- package/dist/functions/Config.js +91 -0
- package/dist/functions/ObjectKeys.d.ts +44 -0
- package/dist/functions/ObjectKeys.d.ts.map +1 -0
- package/dist/functions/ObjectKeys.js +96 -0
- package/dist/functions/handleError.d.ts +8 -0
- package/dist/functions/handleError.d.ts.map +1 -0
- package/dist/functions/handleError.js +137 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 @gerync
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# @gerync/utils
|
|
2
|
+
|
|
3
|
+
A collection of utilities for any type of project, providing logging, error handling, configuration management, and object manipulation helpers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @gerync/utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Colored Logging**: Console logging with hex, RGB, and named color support
|
|
14
|
+
- **Error Handler Middleware**: Express-compatible error handler with multi-language support
|
|
15
|
+
- **Configuration Manager**: Dynamic setup for preferences and localized response messages
|
|
16
|
+
- **Object Utilities**: Helper functions for object key validation and inspection
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## API Reference
|
|
21
|
+
|
|
22
|
+
### 1. coloredlog
|
|
23
|
+
|
|
24
|
+
Log messages to the console with color support. Accepts hex, RGB, or CSS named colors.
|
|
25
|
+
|
|
26
|
+
**Import:**
|
|
27
|
+
```typescript
|
|
28
|
+
import utils from '@gerync/utils';
|
|
29
|
+
utils.coloredlog(message, color);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Usage:**
|
|
33
|
+
```typescript
|
|
34
|
+
// Named colors
|
|
35
|
+
utils.coloredlog('This is an error', 'red');
|
|
36
|
+
utils.coloredlog('This is a warning', 'orange');
|
|
37
|
+
utils.coloredlog('Success!', 'green');
|
|
38
|
+
|
|
39
|
+
// Hex colors
|
|
40
|
+
utils.coloredlog('Custom color', '#FF5733');
|
|
41
|
+
utils.coloredlog('Another shade', '#FF5733CC'); // with alpha
|
|
42
|
+
|
|
43
|
+
// RGB colors
|
|
44
|
+
utils.coloredlog('RGB color', 'rgb(255, 87, 51)');
|
|
45
|
+
utils.coloredlog('RGBA with alpha', 'rgba(255, 87, 51, 0.8)');
|
|
46
|
+
|
|
47
|
+
// If color is invalid, logs without styling
|
|
48
|
+
utils.coloredlog('No styling', 'invalid-color');
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Parameters:**
|
|
52
|
+
- `message` (string): The text to log
|
|
53
|
+
- `color` (string): A valid hex (`#XXX`, `#XXXX`, `#XXXXXX`, `#XXXXXXXX`), RGB (`rgb(r, g, b)`), RGBA (`rgba(r, g, b, a)`), or CSS named color
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### 2. config
|
|
58
|
+
|
|
59
|
+
Manage application preferences and localized response messages.
|
|
60
|
+
|
|
61
|
+
**Import:**
|
|
62
|
+
```typescript
|
|
63
|
+
import utils from '@gerync/utils';
|
|
64
|
+
utils.configure({ ... });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Usage:**
|
|
68
|
+
|
|
69
|
+
#### Initial Setup
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import utils from '@gerync/utils';
|
|
73
|
+
|
|
74
|
+
utils.configure({
|
|
75
|
+
prefs: {
|
|
76
|
+
acceptedLanguages: ['en', 'fr', 'de'],
|
|
77
|
+
noDupesAllowedof: ['email', 'username', 'phone']
|
|
78
|
+
},
|
|
79
|
+
responses: {
|
|
80
|
+
en: {
|
|
81
|
+
dupes: {
|
|
82
|
+
email: 'Email already exists.',
|
|
83
|
+
username: 'Username is taken.',
|
|
84
|
+
phone: 'Phone number is already in use.'
|
|
85
|
+
},
|
|
86
|
+
general: {
|
|
87
|
+
error: 'An error occurred. Please try again later.'
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
fr: {
|
|
91
|
+
dupes: {
|
|
92
|
+
email: 'Cet email existe déjà.',
|
|
93
|
+
username: 'Ce nom d\'utilisateur est pris.',
|
|
94
|
+
phone: 'Ce numéro est déjà utilisé.'
|
|
95
|
+
},
|
|
96
|
+
general: {
|
|
97
|
+
error: 'Une erreur est survenue. Veuillez réessayer.'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Retrieve Configuration
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const responses = utils.config.getResponses();
|
|
108
|
+
const prefs = utils.config.getPrefs();
|
|
109
|
+
|
|
110
|
+
console.log(responses['en'].dupes.email); // "Email already exists."
|
|
111
|
+
console.log(prefs.acceptedLanguages); // ['en', 'fr', 'de']
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Methods:**
|
|
115
|
+
- `configure(options)`: Set preferences and responses at runtime
|
|
116
|
+
- `getResponses()`: Return the current response messages object
|
|
117
|
+
- `getPrefs()`: Return the current preferences object
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### 3. errorHandler
|
|
122
|
+
|
|
123
|
+
Express middleware for centralized error handling with multi-language support, smart logging, and user-friendly messages.
|
|
124
|
+
|
|
125
|
+
**Import:**
|
|
126
|
+
```typescript
|
|
127
|
+
import utils from '@gerync/utils';
|
|
128
|
+
app.use(utils.errorHandler);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Setup:**
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import express from 'express';
|
|
135
|
+
import utils from '@gerync/utils';
|
|
136
|
+
|
|
137
|
+
const app = express();
|
|
138
|
+
|
|
139
|
+
// Configure responses and preferences BEFORE using the middleware
|
|
140
|
+
utils.configure({
|
|
141
|
+
prefs: {
|
|
142
|
+
acceptedLanguages: ['en', 'hu', 'es'],
|
|
143
|
+
noDupesAllowedof: ['email', 'username', 'phone']
|
|
144
|
+
},
|
|
145
|
+
responses: {
|
|
146
|
+
en: {
|
|
147
|
+
dupes: {
|
|
148
|
+
email: 'Email address already exists.',
|
|
149
|
+
username: 'Username is already taken.',
|
|
150
|
+
phone: 'Phone number is already in use.'
|
|
151
|
+
},
|
|
152
|
+
general: {
|
|
153
|
+
error: 'An internal server error occurred.'
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Use the middleware at the end
|
|
160
|
+
app.use(utils.errorHandler);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Features:**
|
|
164
|
+
|
|
165
|
+
- **Language Detection**: Detects language from `req.lang`, `req.language`, or `Accept-Language` header
|
|
166
|
+
- **User-Friendly Responses**: Returns localized messages to clients based on their language
|
|
167
|
+
- **Smart Logging**: Only logs detailed error reports for server-side issues (5xx), not user errors (4xx)
|
|
168
|
+
- **Multi-Colored Console Output**: Color-coded error reports for easy debugging
|
|
169
|
+
- **Automatic Error Classification**: Recognizes user-caused errors (validation, duplicates, etc.)
|
|
170
|
+
|
|
171
|
+
**Error Response Format:**
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"status": "error",
|
|
176
|
+
"code": "ER_DUP_ENTRY",
|
|
177
|
+
"message": "Email address already exists."
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Console Output (Server Errors Only):**
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
===================== ERROR REPORT =====================
|
|
185
|
+
Time: 2025-12-29 19:52:09
|
|
186
|
+
Route: POST /api/users
|
|
187
|
+
RequestId: abc123def456
|
|
188
|
+
Status: 500
|
|
189
|
+
Code: DATABASE_ERROR
|
|
190
|
+
UserMessage: An internal server error occurred.
|
|
191
|
+
RawMessage: Connection timeout
|
|
192
|
+
Stack: Error: Connection timeout
|
|
193
|
+
at Database.query (...)
|
|
194
|
+
at ...
|
|
195
|
+
========================================================
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Supported Error Codes:**
|
|
199
|
+
|
|
200
|
+
User-caused (4xx):
|
|
201
|
+
- `BAD_REQUEST`: Malformed request
|
|
202
|
+
- `UNAUTHORIZED`: Auth required
|
|
203
|
+
- `FORBIDDEN`: Permission denied
|
|
204
|
+
- `NOT_FOUND`: Resource not found
|
|
205
|
+
- `CONFLICT`: Resource conflict
|
|
206
|
+
- `METHOD_NOT_ALLOWED`
|
|
207
|
+
- `NOT_ACCEPTABLE`
|
|
208
|
+
- `UNSUPPORTED_MEDIA_TYPE`
|
|
209
|
+
- `PAYLOAD_TOO_LARGE`
|
|
210
|
+
- `UNPROCESSABLE_ENTITY`
|
|
211
|
+
- `TOO_MANY_REQUESTS`
|
|
212
|
+
- `VALIDATION_ERROR`: User input validation failure
|
|
213
|
+
- `ER_DUP_ENTRY`: Duplicate database entry
|
|
214
|
+
|
|
215
|
+
Server-side (5xx):
|
|
216
|
+
- `INTERNAL_SERVER_ERROR`: Unhandled error
|
|
217
|
+
- `DATABASE_ERROR`: Database operation failed
|
|
218
|
+
- `SERVICE_UNAVAILABLE`: Temporary service outage
|
|
219
|
+
- `BAD_GATEWAY`
|
|
220
|
+
- `GATEWAY_TIMEOUT`
|
|
221
|
+
- `NOT_IMPLEMENTED`
|
|
222
|
+
- `NETWORK_ERROR`
|
|
223
|
+
- `TIMEOUT`
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### 4. object utilities
|
|
228
|
+
|
|
229
|
+
Helper functions for validating and inspecting object keys.
|
|
230
|
+
|
|
231
|
+
**Import:**
|
|
232
|
+
```typescript
|
|
233
|
+
import utils from '@gerync/utils';
|
|
234
|
+
const { Keysamount, KeysInRange, KeysInRangeDetailed, AllowedKeys } = utils.object;
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Keysamount
|
|
238
|
+
Get the number of keys in an object.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
const user = { name: 'John', email: 'john@example.com', age: 30 };
|
|
242
|
+
const count = utils.object.Keysamount(user); // 3
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### KeysInRange
|
|
246
|
+
Check if an object has a key count within a specified range.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const user = { name: 'John', email: 'john@example.com', age: 30 };
|
|
250
|
+
|
|
251
|
+
// Exact count
|
|
252
|
+
utils.object.KeysInRange(user, 3); // true
|
|
253
|
+
utils.object.KeysInRange(user, 2); // false
|
|
254
|
+
|
|
255
|
+
// Range check
|
|
256
|
+
utils.object.KeysInRange(user, 2, 5); // true (3 is between 2 and 5)
|
|
257
|
+
utils.object.KeysInRange(user, 4, 10); // false (3 is not between 4 and 10)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### KeysInRangeDetailed
|
|
261
|
+
Check key count within range and return detailed status.
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
const user = { name: 'John', email: 'john@example.com' };
|
|
265
|
+
|
|
266
|
+
utils.object.KeysInRangeDetailed(user, 1, 5); // 0 (within range)
|
|
267
|
+
utils.object.KeysInRangeDetailed(user, 5, 10); // -1 (below minimum)
|
|
268
|
+
utils.object.KeysInRangeDetailed(user, 1, 1); // 1 (above maximum)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
- `0`: Key count is within range
|
|
273
|
+
- `-1`: Key count is below minimum
|
|
274
|
+
- `1`: Key count is above maximum
|
|
275
|
+
|
|
276
|
+
#### AllowedKeys
|
|
277
|
+
Validate that an object contains only allowed keys, with required and optional key support.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const createUserPayload = { name: 'John', email: 'john@example.com' };
|
|
281
|
+
const updateUserPayload = { name: 'Jane' };
|
|
282
|
+
|
|
283
|
+
// Required keys: name, email
|
|
284
|
+
// Optional keys: phone, bio
|
|
285
|
+
const requiredKeys = ['name', 'email'];
|
|
286
|
+
const optionalKeys = ['phone', 'bio'];
|
|
287
|
+
|
|
288
|
+
utils.object.AllowedKeys(createUserPayload, requiredKeys, optionalKeys); // true
|
|
289
|
+
utils.object.AllowedKeys(updateUserPayload, requiredKeys, optionalKeys); // false (missing email)
|
|
290
|
+
|
|
291
|
+
// All keys in object must be allowed
|
|
292
|
+
const invalidPayload = { name: 'John', email: 'john@example.com', admin: true };
|
|
293
|
+
utils.object.AllowedKeys(invalidPayload, requiredKeys, optionalKeys); // false (admin not allowed)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Parameters:**
|
|
297
|
+
- `obj` (Record<string, any>): Object to validate
|
|
298
|
+
- `keys` (string[]): Required keys that must be present
|
|
299
|
+
- `optionalKeys` (string[], default `[]`): Optional keys that may be present
|
|
300
|
+
|
|
301
|
+
**Returns:** `true` if validation passes, `false` otherwise
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Complete Example
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import express from 'express';
|
|
309
|
+
import utils from '@gerync/utils';
|
|
310
|
+
|
|
311
|
+
const app = express();
|
|
312
|
+
|
|
313
|
+
// Setup Config
|
|
314
|
+
utils.configure({
|
|
315
|
+
prefs: {
|
|
316
|
+
acceptedLanguages: ['en', 'es'],
|
|
317
|
+
noDupesAllowedof: ['email', 'username']
|
|
318
|
+
},
|
|
319
|
+
responses: {
|
|
320
|
+
en: {
|
|
321
|
+
dupes: {
|
|
322
|
+
email: 'Email already exists.',
|
|
323
|
+
username: 'Username is taken.'
|
|
324
|
+
},
|
|
325
|
+
general: {
|
|
326
|
+
error: 'An error occurred.'
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
es: {
|
|
330
|
+
dupes: {
|
|
331
|
+
email: 'El email ya existe.',
|
|
332
|
+
username: 'El nombre de usuario ya está en uso.'
|
|
333
|
+
},
|
|
334
|
+
general: {
|
|
335
|
+
error: 'Ocurrió un error.'
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
app.use(express.json());
|
|
342
|
+
|
|
343
|
+
// Routes
|
|
344
|
+
app.post('/users', (req, res, next) => {
|
|
345
|
+
try {
|
|
346
|
+
// Validate request payload
|
|
347
|
+
const requiredKeys = ['name', 'email'];
|
|
348
|
+
const optionalKeys = ['phone'];
|
|
349
|
+
|
|
350
|
+
if (!utils.object.AllowedKeys(req.body, requiredKeys, optionalKeys)) {
|
|
351
|
+
return res.status(400).json({
|
|
352
|
+
status: 'error',
|
|
353
|
+
message: 'Invalid request payload'
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
utils.coloredlog(`User creation requested: ${req.body.email}`, 'cyan');
|
|
358
|
+
|
|
359
|
+
// Simulate user creation
|
|
360
|
+
const user = { id: 1, ...req.body };
|
|
361
|
+
res.json(user);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
next(error);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Error handler middleware (must be last)
|
|
368
|
+
app.use(utils.errorHandler);
|
|
369
|
+
|
|
370
|
+
app.listen(3000, () => {
|
|
371
|
+
utils.coloredlog('Server running on port 3000', 'green');
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
This project is licensed under the MIT License.
|
|
380
|
+
|
|
381
|
+
- Permissions: Commercial use, modification, distribution, private use
|
|
382
|
+
- Conditions: Include copyright and license notices
|
|
383
|
+
- Limitations: No warranty; authors are not liable for damages
|
|
384
|
+
|
|
385
|
+
See the full license text in [LICENSE](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
@gerync/utils is a lightweight utility library with no sensitive data handling, no network calls, and no authentication mechanisms. It primarily provides:
|
|
6
|
+
- Console logging utilities
|
|
7
|
+
- Error handling and configuration
|
|
8
|
+
- Object validation helpers
|
|
9
|
+
|
|
10
|
+
As such, security risks are minimal. However, we appreciate responsible disclosure.
|
|
11
|
+
|
|
12
|
+
## Reporting Security Issues
|
|
13
|
+
|
|
14
|
+
If you discover a security concern:
|
|
15
|
+
|
|
16
|
+
1. **Please do not open a public issue** on GitHub
|
|
17
|
+
2. Email: contact@gerync.com with details
|
|
18
|
+
3. Include a description of the issue and steps to reproduce (if applicable)
|
|
19
|
+
|
|
20
|
+
We'll acknowledge receipt and respond as time permits.
|
|
21
|
+
|
|
22
|
+
## Known Limitations
|
|
23
|
+
|
|
24
|
+
- This is a hobby/utility project with no guaranteed maintenance SLA
|
|
25
|
+
- Security updates may take time or may not happen immediately
|
|
26
|
+
- Users are responsible for validating their own use cases
|
|
27
|
+
|
|
28
|
+
## Best Practices for Users
|
|
29
|
+
|
|
30
|
+
- **Sanitize user input** before passing to object validation functions
|
|
31
|
+
- **Avoid logging sensitive data** (credentials, tokens, PII) through coloredlog
|
|
32
|
+
- **Configure error responses** carefully to avoid exposing internal details to clients
|
|
33
|
+
- **Keep dependencies updated** in your own projects
|
|
34
|
+
|
|
35
|
+
## Third-Party Dependencies
|
|
36
|
+
|
|
37
|
+
- **express**: Security maintained by the Express community
|
|
38
|
+
- **typescript**: Security maintained by the TypeScript team
|
|
39
|
+
|
|
40
|
+
We don't actively monitor these, but we recommend keeping them updated in your own projects.
|
|
41
|
+
|
|
42
|
+
## No Warranty
|
|
43
|
+
|
|
44
|
+
This library is provided "as-is" without warranty. See [LICENSE](LICENSE) for full details.
|
|
45
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log a message to console with color support.
|
|
3
|
+
* Accepts hex, RGB, RGBA, and CSS named colors.
|
|
4
|
+
* Falls back to unstyled console.log if color is invalid.
|
|
5
|
+
*
|
|
6
|
+
* @param message - Text to log
|
|
7
|
+
* @param color - Valid hex (#XXX, #XXXX, #XXXXXX, #XXXXXXXX),
|
|
8
|
+
* RGB (rgb(r, g, b)), RGBA (rgba(r, g, b, a)),
|
|
9
|
+
* or CSS named color
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* coloredlog('Error!', 'red');
|
|
13
|
+
* coloredlog('Success', '#00FF00');
|
|
14
|
+
* coloredlog('Info', 'rgb(0, 0, 255)');
|
|
15
|
+
*/
|
|
16
|
+
export default function Coloredlog(message: string, color: string): void;
|
|
17
|
+
//# sourceMappingURL=Colorlog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Colorlog.d.ts","sourceRoot":"","sources":["../../functions/Colorlog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CA8BvE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log a message to console with color support.
|
|
3
|
+
* Accepts hex, RGB, RGBA, and CSS named colors.
|
|
4
|
+
* Falls back to unstyled console.log if color is invalid.
|
|
5
|
+
*
|
|
6
|
+
* @param message - Text to log
|
|
7
|
+
* @param color - Valid hex (#XXX, #XXXX, #XXXXXX, #XXXXXXXX),
|
|
8
|
+
* RGB (rgb(r, g, b)), RGBA (rgba(r, g, b, a)),
|
|
9
|
+
* or CSS named color
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* coloredlog('Error!', 'red');
|
|
13
|
+
* coloredlog('Success', '#00FF00');
|
|
14
|
+
* coloredlog('Info', 'rgb(0, 0, 255)');
|
|
15
|
+
*/
|
|
16
|
+
export default function Coloredlog(message, color) {
|
|
17
|
+
// #region Color Validation Regexes
|
|
18
|
+
/** Regex patterns to validate different color formats */
|
|
19
|
+
const ColorRegex = {
|
|
20
|
+
/** Hex colors: #RGB, #RGBA, #RRGGBB, #RRGGBBAA */
|
|
21
|
+
hex: /^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/,
|
|
22
|
+
/** RGB/RGBA colors: rgb(r, g, b) or rgba(r, g, b, a) */
|
|
23
|
+
rgb: /^rgba?\(\s*(\d{1,3}%?)\s*[\s,]\s*(\d{1,3}%?)\s*[\s,]\s*(\d{1,3}%?)(?:\s*[\s,/]\s*((?:0|1|0?\.\d+)(?:%?)))?\s*\)$/,
|
|
24
|
+
/** CSS named colors (e.g., 'red', 'blue', 'aliceblue') */
|
|
25
|
+
named: /^(?:aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|mineskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|limegreen|linen|magenta|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|oldlace|olivedrab|orangered|palegoldenrod|palegreen|palevioletred)$/i,
|
|
26
|
+
};
|
|
27
|
+
// #endregion
|
|
28
|
+
// #region Validate Color Format
|
|
29
|
+
/** Test if color matches any valid format */
|
|
30
|
+
const isValid = ColorRegex.hex.test(color) ||
|
|
31
|
+
ColorRegex.rgb.test(color) ||
|
|
32
|
+
ColorRegex.named.test(color);
|
|
33
|
+
// #endregion
|
|
34
|
+
// #region Output
|
|
35
|
+
if (isValid) {
|
|
36
|
+
/** Use CSS styling for valid colors */
|
|
37
|
+
console.log(`%c${message}`, `color: ${color}; font-weight: bold;`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
/** Fallback: plain console.log if color is invalid */
|
|
41
|
+
console.log(message);
|
|
42
|
+
}
|
|
43
|
+
// #endregion
|
|
44
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration manager for preferences and localized response messages.
|
|
3
|
+
* Stores global state for error messages, language preferences, and custom messages.
|
|
4
|
+
*/
|
|
5
|
+
declare const Configs: {
|
|
6
|
+
/**
|
|
7
|
+
* Initialize configuration with custom responses and preferences.
|
|
8
|
+
* Merges or replaces the runtime state.
|
|
9
|
+
* @param options.responses - Localized message objects by language
|
|
10
|
+
* @param options.prefs - Preferences including acceptedLanguages and noDupesAllowedof
|
|
11
|
+
*/
|
|
12
|
+
configure(options: {
|
|
13
|
+
responses: any;
|
|
14
|
+
prefs: any;
|
|
15
|
+
}): void;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieve all configured localized response messages.
|
|
18
|
+
* @returns The current responses object (by language and code)
|
|
19
|
+
*/
|
|
20
|
+
getResponses(): any;
|
|
21
|
+
/**
|
|
22
|
+
* Retrieve current preferences.
|
|
23
|
+
* @returns The current preferences object
|
|
24
|
+
*/
|
|
25
|
+
getPrefs(): any;
|
|
26
|
+
/**
|
|
27
|
+
* Set or update a single message for a language and error code.
|
|
28
|
+
* Useful for dynamically adding custom messages at runtime.
|
|
29
|
+
* @param lang - Language code (e.g., 'en', 'es', 'fr')
|
|
30
|
+
* @param code - Error or message code (e.g., 'CUSTOM_ERROR', 'ObjectTooLong')
|
|
31
|
+
* @param message - The message text
|
|
32
|
+
*/
|
|
33
|
+
setMessage(lang: string, code: string, message: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Retrieve a message by language and code with graceful fallbacks.
|
|
36
|
+
* Fallback chain: requested language → English → provided fallback → undefined
|
|
37
|
+
* @param lang - Language code to look up
|
|
38
|
+
* @param code - Message code to retrieve
|
|
39
|
+
* @param fallback - Optional fallback message if not found
|
|
40
|
+
* @returns The message, fallback, or undefined
|
|
41
|
+
*/
|
|
42
|
+
getMessage(lang: string, code: string, fallback?: string): string | undefined;
|
|
43
|
+
};
|
|
44
|
+
export default Configs;
|
|
45
|
+
//# sourceMappingURL=Config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../functions/Config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,QAAA,MAAM,OAAO;IAET;;;;;OAKG;uBACgB;QAAE,SAAS,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE;IAOjD;;;OAGG;;IAOH;;;OAGG;;IAOH;;;;;;OAMG;qBACc,MAAM,QAAQ,MAAM,WAAW,MAAM;IAetD;;;;;;;OAOG;qBACc,MAAM,QAAQ,MAAM,aAAa,MAAM,GAAG,MAAM,GAAG,SAAS;CAehF,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration manager for preferences and localized response messages.
|
|
3
|
+
* Stores global state for error messages, language preferences, and custom messages.
|
|
4
|
+
*/
|
|
5
|
+
// #region Runtime State
|
|
6
|
+
/** Cached localized response messages for all languages */
|
|
7
|
+
let runtimeResponses = {};
|
|
8
|
+
/** Cached preferences (languages, duplicate field names, etc.) */
|
|
9
|
+
let runtimePrefs = {
|
|
10
|
+
noDupesAllowedof: [],
|
|
11
|
+
acceptedLanguages: ['en']
|
|
12
|
+
};
|
|
13
|
+
// #endregion
|
|
14
|
+
const Configs = {
|
|
15
|
+
// #region Configure
|
|
16
|
+
/**
|
|
17
|
+
* Initialize configuration with custom responses and preferences.
|
|
18
|
+
* Merges or replaces the runtime state.
|
|
19
|
+
* @param options.responses - Localized message objects by language
|
|
20
|
+
* @param options.prefs - Preferences including acceptedLanguages and noDupesAllowedof
|
|
21
|
+
*/
|
|
22
|
+
configure(options) {
|
|
23
|
+
runtimeResponses = options.responses;
|
|
24
|
+
runtimePrefs = options.prefs;
|
|
25
|
+
},
|
|
26
|
+
// #endregion
|
|
27
|
+
// #region Get Responses
|
|
28
|
+
/**
|
|
29
|
+
* Retrieve all configured localized response messages.
|
|
30
|
+
* @returns The current responses object (by language and code)
|
|
31
|
+
*/
|
|
32
|
+
getResponses() {
|
|
33
|
+
return runtimeResponses;
|
|
34
|
+
},
|
|
35
|
+
// #endregion
|
|
36
|
+
// #region Get Preferences
|
|
37
|
+
/**
|
|
38
|
+
* Retrieve current preferences.
|
|
39
|
+
* @returns The current preferences object
|
|
40
|
+
*/
|
|
41
|
+
getPrefs() {
|
|
42
|
+
return runtimePrefs;
|
|
43
|
+
},
|
|
44
|
+
// #endregion
|
|
45
|
+
// #region Set Message
|
|
46
|
+
/**
|
|
47
|
+
* Set or update a single message for a language and error code.
|
|
48
|
+
* Useful for dynamically adding custom messages at runtime.
|
|
49
|
+
* @param lang - Language code (e.g., 'en', 'es', 'fr')
|
|
50
|
+
* @param code - Error or message code (e.g., 'CUSTOM_ERROR', 'ObjectTooLong')
|
|
51
|
+
* @param message - The message text
|
|
52
|
+
*/
|
|
53
|
+
setMessage(lang, code, message) {
|
|
54
|
+
// Initialize responses if not yet configured
|
|
55
|
+
if (!runtimeResponses || typeof runtimeResponses !== 'object') {
|
|
56
|
+
runtimeResponses = {};
|
|
57
|
+
}
|
|
58
|
+
// Initialize language pack if not exists
|
|
59
|
+
if (!runtimeResponses[lang] || typeof runtimeResponses[lang] !== 'object') {
|
|
60
|
+
runtimeResponses[lang] = {};
|
|
61
|
+
}
|
|
62
|
+
// Set the message
|
|
63
|
+
runtimeResponses[lang][code] = message;
|
|
64
|
+
},
|
|
65
|
+
// #endregion
|
|
66
|
+
// #region Get Message
|
|
67
|
+
/**
|
|
68
|
+
* Retrieve a message by language and code with graceful fallbacks.
|
|
69
|
+
* Fallback chain: requested language → English → provided fallback → undefined
|
|
70
|
+
* @param lang - Language code to look up
|
|
71
|
+
* @param code - Message code to retrieve
|
|
72
|
+
* @param fallback - Optional fallback message if not found
|
|
73
|
+
* @returns The message, fallback, or undefined
|
|
74
|
+
*/
|
|
75
|
+
getMessage(lang, code, fallback) {
|
|
76
|
+
// Try requested language first
|
|
77
|
+
const langPack = runtimeResponses?.[lang];
|
|
78
|
+
if (langPack && typeof langPack[code] === 'string') {
|
|
79
|
+
return langPack[code];
|
|
80
|
+
}
|
|
81
|
+
// Fall back to English
|
|
82
|
+
const englishPack = runtimeResponses?.['en'];
|
|
83
|
+
if (englishPack && typeof englishPack[code] === 'string') {
|
|
84
|
+
return englishPack[code];
|
|
85
|
+
}
|
|
86
|
+
// Return provided fallback or undefined
|
|
87
|
+
return fallback;
|
|
88
|
+
}
|
|
89
|
+
// #endregion
|
|
90
|
+
};
|
|
91
|
+
export default Configs;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object utilities for key validation and inspection.
|
|
3
|
+
* Provides functions to count, range-check, and validate object keys.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Count the number of keys in an object.
|
|
7
|
+
* @param obj - The object to inspect
|
|
8
|
+
* @returns The number of keys (properties) in the object
|
|
9
|
+
*/
|
|
10
|
+
export declare function Keysamount(obj: Record<string, any>): number;
|
|
11
|
+
/**
|
|
12
|
+
* Check if an object's key count is within a specified range.
|
|
13
|
+
* If only min is provided, checks for exact match.
|
|
14
|
+
* @param obj - The object to inspect
|
|
15
|
+
* @param min - Minimum key count (or exact count if max is undefined)
|
|
16
|
+
* @param max - Maximum key count (optional)
|
|
17
|
+
* @returns true if key count is within range, false otherwise
|
|
18
|
+
*/
|
|
19
|
+
export declare function KeysInRange(obj: Record<string, any>, min: number, max: number): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Check if an object's key count is within range and return detailed status.
|
|
22
|
+
* @param obj - The object to inspect
|
|
23
|
+
* @param min - Minimum key count
|
|
24
|
+
* @param max - Maximum key count
|
|
25
|
+
* @returns -1 if below minimum, 1 if above maximum, 0 if within range
|
|
26
|
+
*/
|
|
27
|
+
export declare function KeysInRangeDetailed(obj: Record<string, any>, min: number, max: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Validate that an object contains only allowed keys.
|
|
30
|
+
* All required keys must be present; object can only contain required + optional keys.
|
|
31
|
+
* @param obj - The object to validate
|
|
32
|
+
* @param keys - Required keys that must be present
|
|
33
|
+
* @param optionalKeys - Optional keys that may be present (default: [])
|
|
34
|
+
* @returns true if validation passes, false if any rule violated
|
|
35
|
+
*/
|
|
36
|
+
export declare function AllowedKeys(obj: Record<string, any>, keys: string[], optionalKeys?: string[]): boolean;
|
|
37
|
+
declare const _default: {
|
|
38
|
+
Keysamount: typeof Keysamount;
|
|
39
|
+
KeysInRange: typeof KeysInRange;
|
|
40
|
+
KeysInRangeDetailed: typeof KeysInRangeDetailed;
|
|
41
|
+
AllowedKeys: typeof AllowedKeys;
|
|
42
|
+
};
|
|
43
|
+
export default _default;
|
|
44
|
+
//# sourceMappingURL=ObjectKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObjectKeys.d.ts","sourceRoot":"","sources":["../../functions/ObjectKeys.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAE3D;AAID;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAOvF;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAa9F;AAID;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,OAAO,CAwB1G;;;;;;;AAID,wBAKE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object utilities for key validation and inspection.
|
|
3
|
+
* Provides functions to count, range-check, and validate object keys.
|
|
4
|
+
*/
|
|
5
|
+
// #region Keysamount
|
|
6
|
+
/**
|
|
7
|
+
* Count the number of keys in an object.
|
|
8
|
+
* @param obj - The object to inspect
|
|
9
|
+
* @returns The number of keys (properties) in the object
|
|
10
|
+
*/
|
|
11
|
+
export function Keysamount(obj) {
|
|
12
|
+
return Object.keys(obj).length;
|
|
13
|
+
}
|
|
14
|
+
// #endregion
|
|
15
|
+
// #region KeysInRange
|
|
16
|
+
/**
|
|
17
|
+
* Check if an object's key count is within a specified range.
|
|
18
|
+
* If only min is provided, checks for exact match.
|
|
19
|
+
* @param obj - The object to inspect
|
|
20
|
+
* @param min - Minimum key count (or exact count if max is undefined)
|
|
21
|
+
* @param max - Maximum key count (optional)
|
|
22
|
+
* @returns true if key count is within range, false otherwise
|
|
23
|
+
*/
|
|
24
|
+
export function KeysInRange(obj, min, max) {
|
|
25
|
+
const keyCount = Keysamount(obj);
|
|
26
|
+
// If max not provided, treat min as exact count
|
|
27
|
+
if (max === undefined) {
|
|
28
|
+
max = min;
|
|
29
|
+
}
|
|
30
|
+
return keyCount >= min && keyCount <= max;
|
|
31
|
+
}
|
|
32
|
+
// #endregion
|
|
33
|
+
// #region KeysInRangeDetailed
|
|
34
|
+
/**
|
|
35
|
+
* Check if an object's key count is within range and return detailed status.
|
|
36
|
+
* @param obj - The object to inspect
|
|
37
|
+
* @param min - Minimum key count
|
|
38
|
+
* @param max - Maximum key count
|
|
39
|
+
* @returns -1 if below minimum, 1 if above maximum, 0 if within range
|
|
40
|
+
*/
|
|
41
|
+
export function KeysInRangeDetailed(obj, min, max) {
|
|
42
|
+
const keyCount = Keysamount(obj);
|
|
43
|
+
// If max not provided, treat min as exact count
|
|
44
|
+
if (max === undefined) {
|
|
45
|
+
max = min;
|
|
46
|
+
}
|
|
47
|
+
if (keyCount < min) {
|
|
48
|
+
return -1;
|
|
49
|
+
}
|
|
50
|
+
else if (keyCount > max) {
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// #endregion
|
|
58
|
+
// #region AllowedKeys
|
|
59
|
+
/**
|
|
60
|
+
* Validate that an object contains only allowed keys.
|
|
61
|
+
* All required keys must be present; object can only contain required + optional keys.
|
|
62
|
+
* @param obj - The object to validate
|
|
63
|
+
* @param keys - Required keys that must be present
|
|
64
|
+
* @param optionalKeys - Optional keys that may be present (default: [])
|
|
65
|
+
* @returns true if validation passes, false if any rule violated
|
|
66
|
+
*/
|
|
67
|
+
export function AllowedKeys(obj, keys, optionalKeys = []) {
|
|
68
|
+
const objKeys = Object.keys(obj);
|
|
69
|
+
const allowedSet = new Set([...keys, ...optionalKeys]);
|
|
70
|
+
// Check: object doesn't have extra keys
|
|
71
|
+
if (objKeys.length > allowedSet.size) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Check: all required keys are present
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
if (!(key in obj)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check: all keys in object are allowed
|
|
81
|
+
for (const key of objKeys) {
|
|
82
|
+
if (!allowedSet.has(key)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
// #endregion
|
|
89
|
+
// #region Exports
|
|
90
|
+
export default {
|
|
91
|
+
Keysamount,
|
|
92
|
+
KeysInRange,
|
|
93
|
+
KeysInRangeDetailed,
|
|
94
|
+
AllowedKeys
|
|
95
|
+
};
|
|
96
|
+
// #endregion
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express error handler middleware for centralized error handling
|
|
3
|
+
* with multi-language support, smart logging, and user-friendly messages.
|
|
4
|
+
*
|
|
5
|
+
* Usage: app.use(errorHandler) at the end of middleware chain
|
|
6
|
+
*/
|
|
7
|
+
export default function errorHandler(err: any, req: any, res: any, next: any): void;
|
|
8
|
+
//# sourceMappingURL=handleError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleError.d.ts","sourceRoot":"","sources":["../../functions/handleError.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CA+IlF"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import Configs from './Config';
|
|
2
|
+
import Coloredlog from './Colorlog';
|
|
3
|
+
/**
|
|
4
|
+
* Express error handler middleware for centralized error handling
|
|
5
|
+
* with multi-language support, smart logging, and user-friendly messages.
|
|
6
|
+
*
|
|
7
|
+
* Usage: app.use(errorHandler) at the end of middleware chain
|
|
8
|
+
*/
|
|
9
|
+
export default function errorHandler(err, req, res, next) {
|
|
10
|
+
// #region Initialize Configuration
|
|
11
|
+
const responses = Configs.getResponses();
|
|
12
|
+
const prefs = Configs.getPrefs();
|
|
13
|
+
// #endregion
|
|
14
|
+
// #region Extract Request Context
|
|
15
|
+
/** Detect user language from request or Accept-Language header */
|
|
16
|
+
const lang = (req.lang || req.language || (req.headers['accept-language'] ? req.headers['accept-language'].split(',')[0] : 'en'));
|
|
17
|
+
/** Extract error details from error object */
|
|
18
|
+
const statusCode = err.status || 500;
|
|
19
|
+
const code = err.code || 'INTERNAL_SERVER_ERROR';
|
|
20
|
+
const rawErrorMessage = err.message || '';
|
|
21
|
+
// #endregion
|
|
22
|
+
// #region Server Default Messages
|
|
23
|
+
/** Fallback messages for server-side (5xx) errors when localized responses unavailable */
|
|
24
|
+
const serverDefaultMessages = {
|
|
25
|
+
INTERNAL_SERVER_ERROR: 'An internal server error occurred.',
|
|
26
|
+
DATABASE_ERROR: 'A database error occurred.',
|
|
27
|
+
SERVICE_UNAVAILABLE: 'Service is temporarily unavailable.',
|
|
28
|
+
BAD_GATEWAY: 'Bad gateway.',
|
|
29
|
+
GATEWAY_TIMEOUT: 'Gateway timed out.',
|
|
30
|
+
NOT_IMPLEMENTED: 'This feature is not implemented.',
|
|
31
|
+
NETWORK_ERROR: 'A network error occurred.',
|
|
32
|
+
TIMEOUT: 'The operation timed out.'
|
|
33
|
+
};
|
|
34
|
+
// #endregion
|
|
35
|
+
// #region Build User-Facing Response Message
|
|
36
|
+
/** Get localized responses for detected language, fallback to English */
|
|
37
|
+
const possibleResponses = responses[lang] || responses['en'] || {};
|
|
38
|
+
let responseMessage = '';
|
|
39
|
+
/** Handle duplicate entry errors (e.g., unique constraint violations) */
|
|
40
|
+
if (code === 'ER_DUP_ENTRY' || rawErrorMessage.includes('ER_DUP_ENTRY')) {
|
|
41
|
+
const noDupesof = prefs.noDupesAllowedof || [];
|
|
42
|
+
for (const field of noDupesof) {
|
|
43
|
+
if (rawErrorMessage.includes(field)) {
|
|
44
|
+
responseMessage = possibleResponses.dupes?.[field];
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Handle validation errors with per-field messages */
|
|
50
|
+
else if (code === 'VALIDATION_ERROR' && err.errors) {
|
|
51
|
+
for (const fieldError of err.errors) {
|
|
52
|
+
const fieldName = fieldError.param || fieldError.path;
|
|
53
|
+
if (possibleResponses.validation?.[fieldName]) {
|
|
54
|
+
responseMessage = possibleResponses.validation[fieldName];
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Fallback: use raw error message if no specific handler matched */
|
|
60
|
+
else {
|
|
61
|
+
responseMessage = rawErrorMessage;
|
|
62
|
+
}
|
|
63
|
+
/** Apply fallback logic for message resolution */
|
|
64
|
+
if (!responseMessage) {
|
|
65
|
+
// Try localized message by error code
|
|
66
|
+
if (possibleResponses[code]) {
|
|
67
|
+
responseMessage = possibleResponses[code];
|
|
68
|
+
}
|
|
69
|
+
// Try server default for 5xx errors
|
|
70
|
+
else if (statusCode >= 500 && serverDefaultMessages[code]) {
|
|
71
|
+
responseMessage = serverDefaultMessages[code];
|
|
72
|
+
}
|
|
73
|
+
// Final fallback: generic error message
|
|
74
|
+
else {
|
|
75
|
+
responseMessage = possibleResponses.general?.error || serverDefaultMessages['INTERNAL_SERVER_ERROR'];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// #endregion
|
|
79
|
+
// #region Error Classification (User vs Server)
|
|
80
|
+
/**
|
|
81
|
+
* Classify errors to determine whether to log details for maintainers.
|
|
82
|
+
* User-caused errors (4xx) are silenced; server errors (5xx) are logged.
|
|
83
|
+
*/
|
|
84
|
+
const userCausedCodes = new Set([
|
|
85
|
+
'VALIDATION_ERROR',
|
|
86
|
+
'ER_DUP_ENTRY',
|
|
87
|
+
'BAD_REQUEST',
|
|
88
|
+
'UNAUTHORIZED',
|
|
89
|
+
'FORBIDDEN',
|
|
90
|
+
'NOT_FOUND',
|
|
91
|
+
'CONFLICT',
|
|
92
|
+
'METHOD_NOT_ALLOWED',
|
|
93
|
+
'NOT_ACCEPTABLE',
|
|
94
|
+
'UNSUPPORTED_MEDIA_TYPE',
|
|
95
|
+
'PAYLOAD_TOO_LARGE',
|
|
96
|
+
'UNPROCESSABLE_ENTITY',
|
|
97
|
+
'TOO_MANY_REQUESTS'
|
|
98
|
+
]);
|
|
99
|
+
const isUserError = (statusCode >= 400 && statusCode < 500) || userCausedCodes.has(code);
|
|
100
|
+
// #endregion
|
|
101
|
+
// #region Maintainer Logging (Server Errors Only)
|
|
102
|
+
/** Extract route information for logging */
|
|
103
|
+
const routeInfo = `${req.method || ''} ${req.originalUrl || req.url || ''}`.trim();
|
|
104
|
+
const requestId = (req.id || req.requestId || '');
|
|
105
|
+
/** Log detailed, color-coded error report for maintainers (skip for user-caused errors) */
|
|
106
|
+
if (!isUserError) {
|
|
107
|
+
Coloredlog('===================== ERROR REPORT =====================', 'red');
|
|
108
|
+
// Timestamp
|
|
109
|
+
let exactTime = new Date().toISOString();
|
|
110
|
+
exactTime = exactTime.replace('T', ' ').replace('Z', '');
|
|
111
|
+
Coloredlog(`Time: ${exactTime}`, 'yellow');
|
|
112
|
+
// Request context
|
|
113
|
+
if (routeInfo)
|
|
114
|
+
Coloredlog(`Route: ${routeInfo}`, 'yellow');
|
|
115
|
+
if (requestId)
|
|
116
|
+
Coloredlog(`RequestId: ${requestId}`, 'yellow');
|
|
117
|
+
// Error details
|
|
118
|
+
Coloredlog(`Status: ${statusCode}`, 'magenta');
|
|
119
|
+
Coloredlog(`Code: ${code}`, 'magenta');
|
|
120
|
+
// Messages for comparison
|
|
121
|
+
Coloredlog(`UserMessage: ${responseMessage}`, 'cyan');
|
|
122
|
+
Coloredlog(`RawMessage: ${rawErrorMessage}`, 'white');
|
|
123
|
+
// Stack trace for debugging
|
|
124
|
+
Coloredlog('Stack:', 'gray');
|
|
125
|
+
Coloredlog(err && err.stack ? String(err.stack) : 'N/A', 'gray');
|
|
126
|
+
Coloredlog('========================================================', 'red');
|
|
127
|
+
}
|
|
128
|
+
// #endregion
|
|
129
|
+
// #region Send Response to Client
|
|
130
|
+
/** Return JSON response with error details (safe for client) */
|
|
131
|
+
res.status(statusCode).json({
|
|
132
|
+
status: 'error',
|
|
133
|
+
code: code,
|
|
134
|
+
message: responseMessage
|
|
135
|
+
});
|
|
136
|
+
// #endregion
|
|
137
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import coloredlog from "./functions/Colorlog";
|
|
2
|
+
import errorHandler from "./functions/handleError";
|
|
3
|
+
declare const _default: {
|
|
4
|
+
coloredlog: typeof coloredlog;
|
|
5
|
+
errorHandler: typeof errorHandler;
|
|
6
|
+
config: {
|
|
7
|
+
configure(options: {
|
|
8
|
+
responses: any;
|
|
9
|
+
prefs: any;
|
|
10
|
+
}): void;
|
|
11
|
+
getResponses(): any;
|
|
12
|
+
getPrefs(): any;
|
|
13
|
+
setMessage(lang: string, code: string, message: string): void;
|
|
14
|
+
getMessage(lang: string, code: string, fallback?: string): string | undefined;
|
|
15
|
+
};
|
|
16
|
+
configure: (options: {
|
|
17
|
+
responses: any;
|
|
18
|
+
prefs: any;
|
|
19
|
+
}) => void;
|
|
20
|
+
object: {
|
|
21
|
+
Keysamount: typeof import("./functions/ObjectKeys").Keysamount;
|
|
22
|
+
KeysInRange: typeof import("./functions/ObjectKeys").KeysInRange;
|
|
23
|
+
KeysInRangeDetailed: typeof import("./functions/ObjectKeys").KeysInRangeDetailed;
|
|
24
|
+
AllowedKeys: typeof import("./functions/ObjectKeys").AllowedKeys;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,sBAAsB,CAAC;AAC9C,OAAO,YAAY,MAAM,yBAAyB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AAKnD,wBAME"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import coloredlog from "./functions/Colorlog";
|
|
2
|
+
import errorHandler from "./functions/handleError";
|
|
3
|
+
import config from "./functions/Config";
|
|
4
|
+
import object from "./functions/ObjectKeys";
|
|
5
|
+
export default {
|
|
6
|
+
coloredlog,
|
|
7
|
+
errorHandler,
|
|
8
|
+
config,
|
|
9
|
+
configure: config.configure,
|
|
10
|
+
object
|
|
11
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gerync/utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A collection of utilities for any type of project, providing logging, error handling, configuration management, and object manipulation helpers.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"SECURITY.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"utilities",
|
|
28
|
+
"error-handling",
|
|
29
|
+
"logging",
|
|
30
|
+
"express",
|
|
31
|
+
"middleware",
|
|
32
|
+
"validation",
|
|
33
|
+
"configuration",
|
|
34
|
+
"i18n",
|
|
35
|
+
"object-manipulation",
|
|
36
|
+
"typescript",
|
|
37
|
+
"multi-language",
|
|
38
|
+
"utils",
|
|
39
|
+
"multilingual"
|
|
40
|
+
],
|
|
41
|
+
"author": "gerync",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/gerync/utils.git"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/gerync/utils/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/gerync/utils#readme",
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"express": "^5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.11.30",
|
|
57
|
+
"@types/express": "^4.17.21",
|
|
58
|
+
"express": "^5.2.1",
|
|
59
|
+
"typescript": "^5.6.3",
|
|
60
|
+
"vitest": "^1.6.0"
|
|
61
|
+
}
|
|
62
|
+
}
|