@bloomneo/appkit 1.2.9 โ†’ 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/AGENTS.md +195 -0
  2. package/CHANGELOG.md +253 -0
  3. package/README.md +147 -799
  4. package/bin/commands/generate.js +7 -7
  5. package/cookbook/README.md +26 -0
  6. package/cookbook/api-key-service.ts +106 -0
  7. package/cookbook/auth-protected-crud.ts +112 -0
  8. package/cookbook/file-upload-pipeline.ts +113 -0
  9. package/cookbook/multi-tenant-saas.ts +87 -0
  10. package/cookbook/real-time-chat.ts +121 -0
  11. package/dist/auth/auth.d.ts +21 -4
  12. package/dist/auth/auth.d.ts.map +1 -1
  13. package/dist/auth/auth.js +56 -44
  14. package/dist/auth/auth.js.map +1 -1
  15. package/dist/auth/defaults.d.ts +1 -1
  16. package/dist/auth/defaults.js +35 -35
  17. package/dist/cache/cache.d.ts +29 -6
  18. package/dist/cache/cache.d.ts.map +1 -1
  19. package/dist/cache/cache.js +72 -44
  20. package/dist/cache/cache.js.map +1 -1
  21. package/dist/cache/defaults.js +29 -29
  22. package/dist/cache/index.d.ts +19 -10
  23. package/dist/cache/index.d.ts.map +1 -1
  24. package/dist/cache/index.js +21 -18
  25. package/dist/cache/index.js.map +1 -1
  26. package/dist/config/defaults.d.ts +1 -1
  27. package/dist/config/defaults.js +11 -11
  28. package/dist/config/index.d.ts +3 -3
  29. package/dist/config/index.js +4 -4
  30. package/dist/database/adapters/mongoose.d.ts +4 -4
  31. package/dist/database/adapters/mongoose.js +7 -7
  32. package/dist/database/adapters/prisma.d.ts +4 -4
  33. package/dist/database/adapters/prisma.js +7 -7
  34. package/dist/database/defaults.d.ts +1 -1
  35. package/dist/database/defaults.js +4 -4
  36. package/dist/database/index.js +2 -2
  37. package/dist/database/index.js.map +1 -1
  38. package/dist/email/defaults.js +26 -26
  39. package/dist/email/index.js +7 -7
  40. package/dist/email/strategies/resend.js +1 -1
  41. package/dist/error/defaults.d.ts +1 -1
  42. package/dist/error/defaults.js +13 -13
  43. package/dist/error/error.d.ts +12 -0
  44. package/dist/error/error.d.ts.map +1 -1
  45. package/dist/error/error.js +19 -0
  46. package/dist/error/error.js.map +1 -1
  47. package/dist/error/index.d.ts +14 -3
  48. package/dist/error/index.d.ts.map +1 -1
  49. package/dist/error/index.js +14 -3
  50. package/dist/error/index.js.map +1 -1
  51. package/dist/event/defaults.js +35 -35
  52. package/dist/event/index.js +7 -7
  53. package/dist/logger/defaults.d.ts +1 -1
  54. package/dist/logger/defaults.js +40 -40
  55. package/dist/logger/index.d.ts +1 -0
  56. package/dist/logger/index.d.ts.map +1 -1
  57. package/dist/logger/index.js.map +1 -1
  58. package/dist/logger/logger.d.ts +8 -0
  59. package/dist/logger/logger.d.ts.map +1 -1
  60. package/dist/logger/logger.js +13 -3
  61. package/dist/logger/logger.js.map +1 -1
  62. package/dist/logger/transports/console.js +2 -2
  63. package/dist/logger/transports/http.d.ts +1 -1
  64. package/dist/logger/transports/http.js +2 -2
  65. package/dist/logger/transports/webhook.d.ts +1 -1
  66. package/dist/logger/transports/webhook.js +3 -3
  67. package/dist/queue/defaults.d.ts +2 -2
  68. package/dist/queue/defaults.js +38 -38
  69. package/dist/security/defaults.d.ts +1 -1
  70. package/dist/security/defaults.js +30 -30
  71. package/dist/security/index.d.ts +1 -1
  72. package/dist/security/index.js +3 -3
  73. package/dist/security/security.d.ts +1 -1
  74. package/dist/security/security.js +4 -4
  75. package/dist/storage/defaults.js +26 -26
  76. package/dist/storage/index.js +3 -3
  77. package/dist/util/defaults.d.ts +1 -1
  78. package/dist/util/defaults.js +41 -41
  79. package/dist/util/env.d.ts +35 -0
  80. package/dist/util/env.d.ts.map +1 -0
  81. package/dist/util/env.js +50 -0
  82. package/dist/util/env.js.map +1 -0
  83. package/dist/util/errors.d.ts +52 -0
  84. package/dist/util/errors.d.ts.map +1 -0
  85. package/dist/util/errors.js +82 -0
  86. package/dist/util/errors.js.map +1 -0
  87. package/dist/util/util.js +1 -1
  88. package/examples/.env.example +80 -0
  89. package/examples/README.md +16 -0
  90. package/examples/auth.ts +228 -0
  91. package/examples/cache.ts +36 -0
  92. package/examples/config.ts +45 -0
  93. package/examples/database.ts +69 -0
  94. package/examples/email.ts +53 -0
  95. package/examples/error.ts +50 -0
  96. package/examples/event.ts +42 -0
  97. package/examples/logger.ts +41 -0
  98. package/examples/queue.ts +58 -0
  99. package/examples/security.ts +46 -0
  100. package/examples/storage.ts +44 -0
  101. package/examples/util.ts +47 -0
  102. package/llms.txt +591 -0
  103. package/package.json +19 -10
  104. package/src/auth/README.md +850 -0
  105. package/src/cache/README.md +756 -0
  106. package/src/config/README.md +604 -0
  107. package/src/database/README.md +818 -0
  108. package/src/email/README.md +759 -0
  109. package/src/error/README.md +660 -0
  110. package/src/event/README.md +729 -0
  111. package/src/logger/README.md +435 -0
  112. package/src/queue/README.md +851 -0
  113. package/src/security/README.md +612 -0
  114. package/src/storage/README.md +1008 -0
  115. package/src/util/README.md +955 -0
  116. package/bin/templates/backend/docs/APPKIT_CLI.md +0 -507
  117. package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +0 -61
  118. package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +0 -2539
@@ -0,0 +1,660 @@
1
+ # @bloomneo/appkit - Error Module โš ๏ธ
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@bloomneo/appkit.svg)](https://www.npmjs.com/package/@bloomneo/appkit)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ > Ultra-simple semantic error handling with HTTP status codes, Express
7
+ > middleware, and environment-aware smart defaults.
8
+
9
+ **One function** returns an error object with semantic methods. Built-in
10
+ middleware handles everything automatically. Works with any Express-compatible
11
+ framework.
12
+
13
+ ## ๐Ÿš€ Why Choose This?
14
+
15
+ - **โšก One Function** - Just `error.get()`, everything else is automatic
16
+ - **๐ŸŽฏ Semantic HTTP Codes** - `badRequest(400)`, `unauthorized(401)`,
17
+ `notFound(404)`
18
+ - **๐Ÿ”ง Zero Configuration** - Smart defaults for development vs production
19
+ - **๐ŸŒ Environment-First** - Auto-detects dev/prod behavior
20
+ - **๐Ÿ›ก๏ธ Production-Safe** - Hides stack traces and sensitive info in production
21
+ - **๐Ÿ”„ Framework Agnostic** - Express, Fastify, Koa, any Node.js framework
22
+ - **๐Ÿค– AI-Ready** - Optimized for LLM code generation
23
+
24
+ ## ๐Ÿ“ฆ Installation
25
+
26
+ ```bash
27
+ npm install @bloomneo/appkit
28
+ ```
29
+
30
+ ## ๐Ÿƒโ€โ™‚๏ธ Quick Start (30 seconds)
31
+
32
+ ```typescript
33
+ import express from 'express';
34
+ import { errorClass } from '@bloomneo/appkit/error';
35
+
36
+ const app = express();
37
+ const error = errorClass.get();
38
+
39
+ // Setup (must be last middleware)
40
+ app.use(error.handleErrors());
41
+
42
+ // Create semantic errors
43
+ app.post(
44
+ '/users',
45
+ error.asyncRoute(async (req, res) => {
46
+ if (!req.body.email) throw error.badRequest('Email required');
47
+ if (!req.body.password) throw error.badRequest('Password required');
48
+
49
+ const existingUser = await findUser(req.body.email);
50
+ if (existingUser) throw error.conflict('Email already exists');
51
+
52
+ const user = await createUser(req.body);
53
+ res.json({ user });
54
+ })
55
+ );
56
+ ```
57
+
58
+ **That's it!** Semantic errors with automatic middleware handling.
59
+
60
+ ## ๐Ÿค– LLM Quick Reference - Copy These Patterns
61
+
62
+ ### **Error Creation (Copy Exactly)**
63
+
64
+ ```typescript
65
+ // โœ… CORRECT - Use semantic methods
66
+ throw error.badRequest('Email is required');
67
+ throw error.unauthorized('Login required');
68
+ throw error.forbidden('Admin access required');
69
+ throw error.notFound('User not found');
70
+ throw error.conflict('Email already exists');
71
+ throw error.serverError('Database unavailable');
72
+
73
+ // โŒ WRONG - Manual status codes
74
+ res.status(400).json({ error: 'Bad request' }); // Don't do this
75
+ throw new Error('Something went wrong'); // No status code
76
+ ```
77
+
78
+ ### **Framework Setup (Copy Exactly)**
79
+
80
+ #### **Express Setup**
81
+
82
+ ```typescript
83
+ // โœ… CORRECT - Express middleware setup
84
+ const error = errorClass.get();
85
+ app.use(error.handleErrors()); // Must be LAST middleware
86
+
87
+ // โœ… CORRECT - Express async route pattern
88
+ app.post(
89
+ '/api',
90
+ error.asyncRoute(async (req, res) => {
91
+ if (!data) throw error.badRequest('Data required');
92
+ })
93
+ );
94
+ ```
95
+
96
+ #### **Fastify Setup**
97
+
98
+ ```typescript
99
+ // โœ… CORRECT - Fastify error handler setup
100
+ import Fastify from 'fastify';
101
+ const fastify = Fastify();
102
+ const error = errorClass.get();
103
+
104
+ fastify.setErrorHandler((error, request, reply) => {
105
+ const appError = error.statusCode ? error : error.serverError(error.message);
106
+ reply.status(appError.statusCode).send({
107
+ error: appError.type,
108
+ message: appError.message,
109
+ });
110
+ });
111
+
112
+ // โœ… CORRECT - Fastify route pattern
113
+ fastify.post('/api', async (request, reply) => {
114
+ if (!request.body.data) throw error.badRequest('Data required');
115
+ // Fastify automatically catches errors
116
+ });
117
+ ```
118
+
119
+ #### **Koa Setup**
120
+
121
+ ```typescript
122
+ // โœ… CORRECT - Koa error handler setup
123
+ import Koa from 'koa';
124
+ const app = new Koa();
125
+ const error = errorClass.get();
126
+
127
+ app.use(async (ctx, next) => {
128
+ try {
129
+ await next();
130
+ } catch (error) {
131
+ const appError = error.statusCode
132
+ ? error
133
+ : error.serverError(error.message);
134
+ ctx.status = appError.statusCode;
135
+ ctx.body = {
136
+ error: appError.type,
137
+ message: appError.message,
138
+ };
139
+ }
140
+ });
141
+
142
+ // โœ… CORRECT - Koa route pattern
143
+ app.use(async (ctx, next) => {
144
+ if (!ctx.request.body.data) throw error.badRequest('Data required');
145
+ });
146
+ ```
147
+
148
+ ### **Error Type Selection (Copy These Rules)**
149
+
150
+ ```typescript
151
+ // โœ… Input validation (client's fault)
152
+ if (!email) throw error.badRequest('Email required');
153
+ if (password.length < 8) throw error.badRequest('Password too short');
154
+
155
+ // โœ… Authentication (missing/invalid auth)
156
+ if (!token) throw error.unauthorized('Token required');
157
+ if (tokenExpired) throw error.unauthorized('Session expired');
158
+
159
+ // โœ… Authorization (user authenticated but no permission)
160
+ if (!user.isAdmin) throw error.forbidden('Admin access required');
161
+ if (user.blocked) throw error.forbidden('Account suspended');
162
+
163
+ // โœ… Resource not found
164
+ if (!user) throw error.notFound('User not found');
165
+ if (!post) throw error.notFound('Post not found');
166
+
167
+ // โœ… Business logic conflicts
168
+ if (emailExists) throw error.conflict('Email already registered');
169
+ if (usernameExists) throw error.conflict('Username taken');
170
+
171
+ // โœ… Server/external failures
172
+ catch (dbError) { throw error.serverError('Database unavailable'); }
173
+ catch (apiError) { throw error.serverError('External service down'); }
174
+ ```
175
+
176
+ ## โš ๏ธ Common LLM Mistakes - Avoid These
177
+
178
+ ### **Wrong Error Types**
179
+
180
+ ```typescript
181
+ // โŒ Using wrong error for situation
182
+ throw error.serverError('Email required'); // Should be badRequest
183
+ throw error.badRequest('Database connection failed'); // Should be serverError
184
+ throw error.unauthorized('Admin access required'); // Should be forbidden
185
+
186
+ // โœ… Use correct error for situation
187
+ throw error.badRequest('Email required'); // Client input issue
188
+ throw error.serverError('Database connection failed'); // Server issue
189
+ throw error.forbidden('Admin access required'); // Permission issue
190
+ ```
191
+
192
+ ### **Missing Middleware Setup**
193
+
194
+ ```typescript
195
+ // โŒ Forgetting error middleware
196
+ const app = express();
197
+ app.post('/api', (req, res) => {
198
+ throw error.badRequest('Error'); // Nothing will catch this!
199
+ });
200
+
201
+ // โœ… Proper middleware setup
202
+ const app = express();
203
+ const error = errorClass.get();
204
+ app.use(error.handleErrors()); // Catches all errors
205
+ app.post('/api', (req, res) => {
206
+ throw error.badRequest('Error'); // Automatically handled
207
+ });
208
+ ```
209
+
210
+ ### **Mixing Error Approaches**
211
+
212
+ ```typescript
213
+ // โŒ Mixing manual and automatic error handling
214
+ app.post(
215
+ '/api',
216
+ error.asyncRoute(async (req, res) => {
217
+ try {
218
+ if (!data) throw error.badRequest('Data required');
219
+ // ... logic
220
+ } catch (err) {
221
+ res.status(500).json({ error: 'Failed' }); // Don't catch manually!
222
+ }
223
+ })
224
+ );
225
+
226
+ // โœ… Let the system handle errors
227
+ app.post(
228
+ '/api',
229
+ error.asyncRoute(async (req, res) => {
230
+ if (!data) throw error.badRequest('Data required'); // Just throw - system handles
231
+ // ... logic
232
+ })
233
+ );
234
+ ```
235
+
236
+ ## ๐Ÿšจ Error Handling Patterns
237
+
238
+ ### **Input Validation Pattern**
239
+
240
+ ```typescript
241
+ app.post(
242
+ '/users',
243
+ error.asyncRoute(async (req, res) => {
244
+ const { email, password, name } = req.body;
245
+
246
+ // Validate required fields
247
+ if (!email) throw error.badRequest('Email is required');
248
+ if (!password) throw error.badRequest('Password is required');
249
+ if (!name) throw error.badRequest('Name is required');
250
+
251
+ // Validate format/rules
252
+ if (!email.includes('@')) throw error.badRequest('Invalid email format');
253
+ if (password.length < 8)
254
+ throw error.badRequest('Password must be 8+ characters');
255
+
256
+ // Success path
257
+ const user = await createUser({ email, password, name });
258
+ res.json({ user });
259
+ })
260
+ );
261
+ ```
262
+
263
+ ### **Authentication Middleware Pattern**
264
+
265
+ ```typescript
266
+ const requireAuth = error.asyncRoute(async (req, res, next) => {
267
+ const token = req.headers.authorization?.replace('Bearer ', '');
268
+
269
+ if (!token) {
270
+ throw error.unauthorized('Authentication token required');
271
+ }
272
+
273
+ try {
274
+ const decoded = jwt.verify(token, JWT_SECRET);
275
+ const user = await findUser(decoded.userId);
276
+
277
+ if (!user) {
278
+ throw error.unauthorized('Invalid token - user not found');
279
+ }
280
+
281
+ req.user = user;
282
+ next();
283
+ } catch (jwtError) {
284
+ throw error.unauthorized('Invalid or expired token');
285
+ }
286
+ });
287
+
288
+ // Usage
289
+ app.get(
290
+ '/profile',
291
+ requireAuth,
292
+ error.asyncRoute(async (req, res) => {
293
+ res.json({ user: req.user });
294
+ })
295
+ );
296
+ ```
297
+
298
+ ### **Database Error Handling Pattern**
299
+
300
+ ```typescript
301
+ app.post(
302
+ '/posts',
303
+ error.asyncRoute(async (req, res) => {
304
+ const { title, content } = req.body;
305
+
306
+ if (!title) throw error.badRequest('Title required');
307
+ if (!content) throw error.badRequest('Content required');
308
+
309
+ try {
310
+ // Check for duplicates (business logic)
311
+ const existing = await findPostByTitle(title);
312
+ if (existing) {
313
+ throw error.conflict('Post with this title already exists');
314
+ }
315
+
316
+ // Create post
317
+ const post = await createPost({ title, content });
318
+ res.json({ post });
319
+ } catch (dbError) {
320
+ // Database connection/query failures
321
+ if (dbError.code === 'ECONNREFUSED') {
322
+ throw error.serverError('Database connection failed');
323
+ }
324
+ if (dbError.code === 'ETIMEDOUT') {
325
+ throw error.serverError('Database query timeout');
326
+ }
327
+
328
+ // Re-throw if it's already our error
329
+ if (dbError.statusCode) {
330
+ throw dbError;
331
+ }
332
+
333
+ // Unknown database error
334
+ throw error.serverError('Database operation failed');
335
+ }
336
+ })
337
+ );
338
+ ```
339
+
340
+ ## ๐ŸŒ Environment Variables
341
+
342
+ ### **Framework Configuration (Bloomneo Internal)**
343
+
344
+ ```bash
345
+ # Error handling behavior (optional)
346
+ BLOOM_ERROR_STACK=false # Show stack traces (default: true in dev, false in prod)
347
+ BLOOM_ERROR_LOG=true # Enable error logging (default: true)
348
+
349
+ # Framework detection
350
+ NODE_ENV=production # Environment mode (development, production, test, staging)
351
+ ```
352
+
353
+ ### **Your Application Configuration**
354
+
355
+ ```bash
356
+ # Your app-specific environment variables
357
+ DATABASE_URL=postgresql://...
358
+ API_KEY=your-api-key
359
+ SESSION_SECRET=your-session-secret
360
+
361
+ # Note: Use any naming convention for your app config
362
+ # Bloomneo only reads BLOOM_* prefixed variables
363
+ ```
364
+
365
+ ### **Configuration Separation**
366
+
367
+ The error module follows **clear separation**:
368
+
369
+ - **VOILA*ERROR*\*** - Framework behavior (stack traces, logging)
370
+ - **Everything else** - Your application configuration
371
+ - **No interference** - Your app config remains untouched
372
+
373
+ ## ๐Ÿš€ Production Deployment
374
+
375
+ ### **Environment Configuration**
376
+
377
+ ```bash
378
+ # โœ… Production settings
379
+ NODE_ENV=production # Enables production mode
380
+ BLOOM_ERROR_STACK=false # Hide stack traces for security
381
+ BLOOM_ERROR_LOG=true # Enable error logging for monitoring
382
+
383
+ # โœ… Development settings
384
+ NODE_ENV=development # Enables development mode
385
+ BLOOM_ERROR_STACK=true # Show stack traces for debugging
386
+ BLOOM_ERROR_LOG=true # Enable error logging
387
+ ```
388
+
389
+ ### **Framework-Specific Setup**
390
+
391
+ #### **Express Production Setup**
392
+
393
+ ```typescript
394
+ import express from 'express';
395
+ import { errorClass } from '@bloomneo/appkit/error';
396
+
397
+ const app = express();
398
+
399
+ // Standard middleware
400
+ app.use(express.json());
401
+ app.use(express.urlencoded({ extended: true }));
402
+
403
+ // Your routes here
404
+ app.get('/health', (req, res) => res.json({ status: 'ok' }));
405
+ app.use('/api', apiRoutes);
406
+
407
+ // ERROR MIDDLEWARE MUST BE LAST!
408
+ const error = errorClass.get();
409
+ app.use(error.handleErrors());
410
+
411
+ app.listen(3000, () => {
412
+ console.log('Server running on port 3000');
413
+ });
414
+ ```
415
+
416
+ #### **Fastify Production Setup**
417
+
418
+ ```typescript
419
+ import Fastify from 'fastify';
420
+ import { errorClass } from '@bloomneo/appkit/error';
421
+
422
+ const fastify = Fastify({ logger: true });
423
+ const error = errorClass.get();
424
+
425
+ // Global error handler
426
+ fastify.setErrorHandler((error, request, reply) => {
427
+ const appError = error.statusCode ? error : error.serverError(error.message);
428
+
429
+ // Log in production
430
+ if (error.getEnvironmentInfo().isProduction) {
431
+ fastify.log.error(error);
432
+ }
433
+
434
+ reply.status(appError.statusCode).send({
435
+ error: appError.type,
436
+ message: appError.message,
437
+ });
438
+ });
439
+
440
+ // Your routes
441
+ fastify.register(apiRoutes, { prefix: '/api' });
442
+
443
+ const start = async () => {
444
+ try {
445
+ await fastify.listen({ port: 3000 });
446
+ } catch (err) {
447
+ fastify.log.error(err);
448
+ process.exit(1);
449
+ }
450
+ };
451
+ start();
452
+ ```
453
+
454
+ ### **Security Validation**
455
+
456
+ ```typescript
457
+ // App startup validation
458
+ try {
459
+ const error = errorClass.get();
460
+ const env = error.getEnvironmentInfo();
461
+
462
+ console.log(`โœ… Error handling initialized`);
463
+ console.log(`Environment: ${env.nodeEnv}`);
464
+ console.log(`Development mode: ${env.isDevelopment}`);
465
+
466
+ // Production security check
467
+ if (env.isProduction && process.env.BLOOM_ERROR_STACK === 'true') {
468
+ console.warn('โš ๏ธ Stack traces enabled in production - security risk!');
469
+ }
470
+ } catch (setupError) {
471
+ console.error('โŒ Error setup failed:', setupError.message);
472
+ process.exit(1);
473
+ }
474
+ ```
475
+
476
+ ### **Common Issues & Solutions**
477
+
478
+ - **"Error not caught"** โ†’ Ensure error middleware is LAST in Express
479
+ - **"Stack traces in production"** โ†’ Check `NODE_ENV=production` and
480
+ `BLOOM_ERROR_STACK=false`
481
+ - **"Async errors not handled"** โ†’ Use `error.asyncRoute()` wrapper for async
482
+ routes
483
+ - **"Wrong error types"** โ†’ Review error type selection guide above
484
+ - **"Fastify errors not caught"** โ†’ Use `fastify.setErrorHandler()` with error
485
+ module
486
+ - **"Koa errors not handled"** โ†’ Wrap routes in try/catch with error module
487
+
488
+ ## ๐Ÿ“– Complete API Reference
489
+
490
+ ### **Core Function**
491
+
492
+ ```typescript
493
+ const error = errorClass.get(); // One function, all methods
494
+ ```
495
+
496
+ ### **Error Creation Methods**
497
+
498
+ ```typescript
499
+ // Semantic HTTP errors
500
+ error.badRequest(message?); // 400 - Invalid input
501
+ error.unauthorized(message?); // 401 - Auth required
502
+ error.forbidden(message?); // 403 - Access denied
503
+ error.notFound(message?); // 404 - Resource missing
504
+ error.conflict(message?); // 409 - Business conflicts
505
+ error.serverError(message?); // 500 - Internal failures
506
+
507
+ // Custom errors
508
+ error.createError(statusCode, message, type?); // Any status code
509
+ ```
510
+
511
+ ### **Express Middleware**
512
+
513
+ ```typescript
514
+ // Error handling (must be last middleware)
515
+ error.handleErrors(options?);
516
+
517
+ // Async route wrapper
518
+ error.asyncRoute(handler);
519
+
520
+ // Error categorization
521
+ error.isClientError(error); // 4xx status codes
522
+ error.isServerError(error); // 5xx status codes
523
+ ```
524
+
525
+ ### **Utility Methods**
526
+
527
+ ```typescript
528
+ // Environment info
529
+ error.getEnvironmentInfo(); // Current environment details
530
+
531
+ // Configuration access
532
+ error.getConfig(); // Current error configuration
533
+
534
+ // Shortcut methods (direct usage without get())
535
+ error.badRequest('Message');
536
+ error.unauthorized('Message');
537
+ error.forbidden('Message');
538
+ error.notFound('Message');
539
+ error.conflict('Message');
540
+ error.serverError('Message');
541
+
542
+ // Direct middleware usage
543
+ error.handleErrors();
544
+ error.asyncRoute(handler);
545
+ ```
546
+
547
+ ## ๐Ÿ’ก Simple Usage Patterns
548
+
549
+ ### **Basic Validation**
550
+
551
+ ```typescript
552
+ // Input validation
553
+ if (!email) throw error.badRequest('Email required');
554
+ if (!password) throw error.badRequest('Password required');
555
+
556
+ // Business validation
557
+ if (userExists) throw error.conflict('User already exists');
558
+ if (!userFound) throw error.notFound('User not found');
559
+ ```
560
+
561
+ ### **Authentication Flow**
562
+
563
+ ```typescript
564
+ // Check for token
565
+ if (!token) throw error.unauthorized('Token required');
566
+
567
+ // Verify token
568
+ try {
569
+ const user = await verifyToken(token);
570
+ req.user = user;
571
+ } catch {
572
+ throw error.unauthorized('Invalid token');
573
+ }
574
+
575
+ // Check permissions
576
+ if (!user.isAdmin) throw error.forbidden('Admin required');
577
+ ```
578
+
579
+ ### **Database Operations**
580
+
581
+ ```typescript
582
+ try {
583
+ const result = await db.query(sql);
584
+ return result;
585
+ } catch (dbError) {
586
+ throw error.serverError('Database operation failed');
587
+ }
588
+ ```
589
+
590
+ ## ๐Ÿงช Testing
591
+
592
+ ```typescript
593
+ import { errorClass } from '@bloomneo/appkit/error';
594
+
595
+ // Reset for clean testing
596
+ const error = errorClass.reset({
597
+ middleware: {
598
+ showStack: false,
599
+ logErrors: false,
600
+ },
601
+ });
602
+
603
+ // Test error creation
604
+ const badRequestError = error.badRequest('Test message');
605
+ expect(badRequestError.statusCode).toBe(400);
606
+ expect(badRequestError.type).toBe('BAD_REQUEST');
607
+
608
+ // Test error categorization
609
+ const clientError = error.badRequest('Client error');
610
+ const serverError = error.serverError('Server error');
611
+
612
+ expect(error.isClientError(clientError)).toBe(true);
613
+ expect(error.isServerError(serverError)).toBe(true);
614
+
615
+ // Test environment detection
616
+ const env = error.getEnvironmentInfo();
617
+ expect(env.isDevelopment).toBeDefined();
618
+ expect(env.isProduction).toBeDefined();
619
+ ```
620
+
621
+ ## ๐Ÿ“ˆ Performance
622
+
623
+ - **Error Creation**: ~0.01ms per error
624
+ - **Middleware Processing**: ~0.1ms per request
625
+ - **Memory Usage**: <100KB overhead
626
+ - **Environment Parsing**: Once per app startup
627
+ - **Framework Agnostic**: Works with any Node.js framework
628
+
629
+ ## ๐Ÿ” TypeScript Support
630
+
631
+ ```typescript
632
+ import type {
633
+ AppError,
634
+ ErrorConfig,
635
+ ErrorHandlerOptions,
636
+ ExpressErrorHandler,
637
+ AsyncRouteHandler,
638
+ } from '@bloomneo/appkit/error';
639
+
640
+ // All methods are fully typed
641
+ const error = errorClass.get();
642
+ const middleware: ExpressErrorHandler = error.handleErrors();
643
+ const wrapper: AsyncRouteHandler = error.asyncRoute(handler);
644
+
645
+ // Environment info is typed
646
+ const env = error.getEnvironmentInfo();
647
+ // env.isDevelopment: boolean
648
+ // env.isProduction: boolean
649
+ // env.nodeEnv: string
650
+ ```
651
+
652
+ ## ๐Ÿ“„ License
653
+
654
+ MIT ยฉ [Bloomneo](https://github.com/bloomneo)
655
+
656
+ ---
657
+
658
+ <p align="center">
659
+ Built with โค๏ธ in India by the <a href="https://github.com/orgs/bloomneo/people">Bloomneo Team</a>
660
+ </p>