fragment-ts 1.0.33 → 1.0.35

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 (88) hide show
  1. package/API.md +248 -38
  2. package/DOCS.md +327 -63
  3. package/NewCLIGENERATECOMMANDS.txt +5 -0
  4. package/README.md +168 -3
  5. package/USAGE.md +395 -2
  6. package/dist/cli/commands/build.command.d.ts.map +1 -1
  7. package/dist/cli/commands/build.command.js +20 -8
  8. package/dist/cli/commands/build.command.js.map +1 -1
  9. package/dist/cli/commands/diagnostics.command.d.ts +1 -2
  10. package/dist/cli/commands/diagnostics.command.d.ts.map +1 -1
  11. package/dist/cli/commands/diagnostics.command.js +37 -23
  12. package/dist/cli/commands/diagnostics.command.js.map +1 -1
  13. package/dist/cli/commands/generate.command.d.ts +5 -1
  14. package/dist/cli/commands/generate.command.d.ts.map +1 -1
  15. package/dist/cli/commands/generate.command.js +171 -39
  16. package/dist/cli/commands/generate.command.js.map +1 -1
  17. package/dist/cli/commands/init.command.d.ts.map +1 -1
  18. package/dist/cli/commands/init.command.js +98 -28
  19. package/dist/cli/commands/init.command.js.map +1 -1
  20. package/dist/cli/commands/migrate.command.d.ts +10 -17
  21. package/dist/cli/commands/migrate.command.d.ts.map +1 -1
  22. package/dist/cli/commands/migrate.command.js +133 -170
  23. package/dist/cli/commands/migrate.command.js.map +1 -1
  24. package/dist/cli/commands/serve.command.d.ts.map +1 -1
  25. package/dist/cli/commands/serve.command.js +9 -4
  26. package/dist/cli/commands/serve.command.js.map +1 -1
  27. package/dist/cli/commands/test.command.d.ts.map +1 -1
  28. package/dist/cli/commands/test.command.js +24 -6
  29. package/dist/cli/commands/test.command.js.map +1 -1
  30. package/dist/core/decorators/exception-filter.decorator.d.ts.map +1 -1
  31. package/dist/core/decorators/exception-filter.decorator.js +11 -4
  32. package/dist/core/decorators/exception-filter.decorator.js.map +1 -1
  33. package/dist/core/decorators/guard.decorator.d.ts.map +1 -1
  34. package/dist/core/decorators/guard.decorator.js +11 -4
  35. package/dist/core/decorators/guard.decorator.js.map +1 -1
  36. package/dist/core/decorators/interceptor.decorator.d.ts.map +1 -1
  37. package/dist/core/decorators/interceptor.decorator.js +10 -4
  38. package/dist/core/decorators/interceptor.decorator.js.map +1 -1
  39. package/dist/core/decorators/middleware.decorator.d.ts.map +1 -1
  40. package/dist/core/decorators/middleware.decorator.js +8 -4
  41. package/dist/core/decorators/middleware.decorator.js.map +1 -1
  42. package/dist/core/scanner/component-scanner.d.ts +12 -0
  43. package/dist/core/scanner/component-scanner.d.ts.map +1 -1
  44. package/dist/core/scanner/component-scanner.js +72 -14
  45. package/dist/core/scanner/component-scanner.js.map +1 -1
  46. package/dist/shared/config.utils.d.ts +58 -0
  47. package/dist/shared/config.utils.d.ts.map +1 -0
  48. package/dist/shared/config.utils.js +137 -0
  49. package/dist/shared/config.utils.js.map +1 -0
  50. package/dist/shared/env.utils.d.ts +27 -0
  51. package/dist/shared/env.utils.d.ts.map +1 -0
  52. package/dist/shared/env.utils.js +68 -0
  53. package/dist/shared/env.utils.js.map +1 -0
  54. package/dist/shared/tsconfig.utils.d.ts +122 -0
  55. package/dist/shared/tsconfig.utils.d.ts.map +1 -0
  56. package/dist/shared/tsconfig.utils.js +305 -0
  57. package/dist/shared/tsconfig.utils.js.map +1 -0
  58. package/dist/testing/runner.d.ts +9 -1
  59. package/dist/testing/runner.d.ts.map +1 -1
  60. package/dist/testing/runner.js +50 -10
  61. package/dist/testing/runner.js.map +1 -1
  62. package/dist/typeorm/typeorm-module.d.ts +1 -0
  63. package/dist/typeorm/typeorm-module.d.ts.map +1 -1
  64. package/dist/typeorm/typeorm-module.js +193 -85
  65. package/dist/typeorm/typeorm-module.js.map +1 -1
  66. package/dist/web/application.d.ts +0 -1
  67. package/dist/web/application.d.ts.map +1 -1
  68. package/dist/web/application.js +4 -26
  69. package/dist/web/application.js.map +1 -1
  70. package/package.json +1 -1
  71. package/src/cli/commands/build.command.ts +24 -9
  72. package/src/cli/commands/diagnostics.command.ts +42 -30
  73. package/src/cli/commands/generate.command.ts +212 -52
  74. package/src/cli/commands/init.command.ts +100 -29
  75. package/src/cli/commands/migrate.command.ts +145 -198
  76. package/src/cli/commands/serve.command.ts +181 -170
  77. package/src/cli/commands/test.command.ts +25 -11
  78. package/src/core/decorators/exception-filter.decorator.ts +29 -6
  79. package/src/core/decorators/guard.decorator.ts +28 -5
  80. package/src/core/decorators/interceptor.decorator.ts +32 -6
  81. package/src/core/decorators/middleware.decorator.ts +30 -6
  82. package/src/core/scanner/component-scanner.ts +100 -18
  83. package/src/shared/config.utils.ts +148 -0
  84. package/src/shared/env.utils.ts +72 -0
  85. package/src/shared/tsconfig.utils.ts +360 -0
  86. package/src/testing/runner.ts +62 -14
  87. package/src/typeorm/typeorm-module.ts +209 -86
  88. package/src/web/application.ts +4 -33
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ---
2
+
1
3
  # Fragment TS - A Modern TypeScript Framework
2
4
 
3
5
  [![npm version](https://img.shields.io/npm/v/fragment-ts)](https://www.npmjs.com/package/fragment-ts)
@@ -15,6 +17,7 @@ Fragment TS is a lightweight, dependency injection-based framework for building
15
17
  - 🔧 **Auto-configuration**: Smart component scanning and registration
16
18
  - 🧪 **Testable Design**: Easily mock dependencies for unit testing
17
19
  - 📦 **Production Ready**: Works with compiled JavaScript and TypeScript source
20
+ - 🛡️ **Enhancement Pipeline**: Middleware, guards, interceptors, and exception filters for advanced request processing
18
21
 
19
22
  ## Installation
20
23
 
@@ -109,7 +112,7 @@ export class UserRepository {
109
112
 
110
113
  ### 5. Configure TypeORM:
111
114
 
112
- ```typescript
115
+ ```json
113
116
  // fragment.json
114
117
  {
115
118
  "type": "mysql",
@@ -123,6 +126,169 @@ export class UserRepository {
123
126
  }
124
127
  ```
125
128
 
129
+ ## Enhancement Pipeline
130
+
131
+ Fragment TS provides a powerful request processing pipeline with four types of enhancements that can be applied at both **class** and **method** levels:
132
+
133
+ ### Middleware (`@FragmentMiddleware`)
134
+
135
+ Apply Express-compatible middleware to log requests, handle authentication headers, or modify the request/response cycle.
136
+
137
+ ```typescript
138
+ // src/middlewares/logging.middleware.ts
139
+ import { Injectable } from 'fragment-ts';
140
+ import { Request, Response, NextFunction } from 'express';
141
+
142
+ @Injectable()
143
+ export class LoggingMiddleware {
144
+ use(req: Request, res: Response, next: NextFunction): void {
145
+ console.log(`📥 ${req.method} ${req.path}`);
146
+ next();
147
+ }
148
+ }
149
+
150
+ // Usage
151
+ import { Controller, Get } from 'fragment-ts';
152
+ import { LoggingMiddleware } from '../middlewares/logging.middleware';
153
+
154
+ @FragmentMiddleware(LoggingMiddleware)
155
+ @Controller('/api')
156
+ export class AppController {
157
+ @Get('/health')
158
+ health() {
159
+ return { status: 'OK' };
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### Guards (`@FragmentGuard`)
165
+
166
+ Control access to routes with authorization logic. Guards run after middleware but before the route handler.
167
+
168
+ ```typescript
169
+ // src/guards/auth.guard.ts
170
+ import { Injectable } from 'fragment-ts';
171
+ import { Request } from 'express';
172
+
173
+ @Injectable()
174
+ export class AuthGuard {
175
+ canActivate(req: Request): boolean {
176
+ const token = req.headers.authorization;
177
+ return token === 'Bearer valid-token';
178
+ }
179
+ }
180
+
181
+ // Usage
182
+ import { Controller, Get } from 'fragment-ts';
183
+ import { AuthGuard } from '../guards/auth.guard';
184
+
185
+ @FragmentGuard(AuthGuard)
186
+ @Controller('/secure')
187
+ export class SecureController {
188
+ @Get('/data')
189
+ getData() {
190
+ return { secret: 'Protected data' };
191
+ }
192
+ }
193
+ ```
194
+
195
+ ### Interceptors (`@FragmentInterceptor`)
196
+
197
+ Transform the response data before it's sent to the client. Interceptors run after the route handler executes.
198
+
199
+ ```typescript
200
+ // src/interceptors/transform.interceptor.ts
201
+ import { Injectable } from 'fragment-ts';
202
+ import { Request, Response } from 'express';
203
+
204
+ @Injectable()
205
+ export class TransformInterceptor {
206
+ intercept(req: Request, res: Response, result: any): any {
207
+ return {
208
+ success: true,
209
+ result,
210
+ timestamp: new Date().toISOString()
211
+ };
212
+ }
213
+ }
214
+
215
+ // Usage
216
+ import { Controller, Get } from 'fragment-ts';
217
+ import { TransformInterceptor } from '../interceptors/transform.interceptor';
218
+
219
+ @FragmentInterceptor(TransformInterceptor)
220
+ @Controller('/api')
221
+ export class DataController {
222
+ @Get('/info')
223
+ getInfo() {
224
+ return { message: 'Hello World' };
225
+ }
226
+ }
227
+ ```
228
+
229
+ ### Exception Filters (`@FragmentExceptionFilter`)
230
+
231
+ Handle errors gracefully with custom error responses. Filters are checked in method → controller → application order.
232
+
233
+ ```typescript
234
+ // src/filters/http-exception.filter.ts
235
+ import { Injectable } from 'fragment-ts';
236
+ import { Request, Response } from 'express';
237
+
238
+ @Injectable()
239
+ export class HttpExceptionFilter {
240
+ catch(exception: Error, req: Request, res: Response): void {
241
+ if (exception.name === 'ValidationError') {
242
+ res.status(400).json({ error: 'Validation failed', message: exception.message });
243
+ } else if (exception.name === 'NotFoundError') {
244
+ res.status(404).json({ error: 'Not found', message: exception.message });
245
+ } else {
246
+ res.status(500).json({ error: 'Internal server error' });
247
+ }
248
+ }
249
+ }
250
+
251
+ // Usage
252
+ import { FragmentApplication } from 'fragment-ts';
253
+ import { HttpExceptionFilter } from './filters/http-exception.filter';
254
+
255
+ @FragmentApplication({
256
+ port: 3000,
257
+ autoScan: true,
258
+ })
259
+ @FragmentExceptionFilter(HttpExceptionFilter)
260
+ export class Application {
261
+ constructor() {
262
+ console.log('Application starting...');
263
+ }
264
+ }
265
+ ```
266
+
267
+ ### Combined Usage
268
+
269
+ All enhancement decorators can be combined and applied at different levels:
270
+
271
+ ```typescript
272
+ @FragmentMiddleware(LoggingMiddleware)
273
+ @FragmentGuard(AuthGuard)
274
+ @FragmentInterceptor(TransformInterceptor)
275
+ @Controller('/api')
276
+ export class ComplexController {
277
+ @Get('/public')
278
+ @FragmentGuard(PublicGuard) // Overrides class-level guard
279
+ publicData() {
280
+ return { message: 'Public data' };
281
+ }
282
+
283
+ @Post('/admin')
284
+ @FragmentGuard(AdminGuard) // Additional guard
285
+ @FragmentInterceptor(CacheInterceptor) // Additional interceptor
286
+ createAdminData(@Body() data: any) {
287
+ return { created: true, data };
288
+ }
289
+ }
290
+ ```
291
+
126
292
  ## Running the Application
127
293
 
128
294
  ```bash
@@ -151,5 +317,4 @@ For issues and feature requests, please [open an issue](https://github.com/youru
151
317
 
152
318
  ## License
153
319
 
154
- MIT © [Your Name]
155
- ```
320
+ MIT © [Digitwhale Innovations](https://digitwhale.com)
package/USAGE.md CHANGED
@@ -1,3 +1,5 @@
1
+ ---
2
+
1
3
  # Usage Guide
2
4
 
3
5
  This guide demonstrates practical usage patterns for Fragment TS framework.
@@ -19,8 +21,16 @@ src/
19
21
  │ └── user.repository.ts
20
22
  ├── entities/
21
23
  │ └── user.entity.ts
22
- └── dtos/
23
- └── create-user.dto.ts
24
+ ├── dtos/
25
+ └── create-user.dto.ts
26
+ ├── middlewares/
27
+ │ └── logging.middleware.ts
28
+ ├── guards/
29
+ │ └── auth.guard.ts
30
+ ├── interceptors/
31
+ │ └── transform.interceptor.ts
32
+ └── filters/
33
+ └── http-exception.filter.ts
24
34
  ```
25
35
 
26
36
  ## Dependency Injection
@@ -160,6 +170,282 @@ export class ProductController {
160
170
  }
161
171
  ```
162
172
 
173
+ ## Enhancement Pipeline
174
+
175
+ ### Middleware Implementation
176
+
177
+ ```typescript
178
+ // src/middlewares/request-id.middleware.ts
179
+ import { Injectable } from 'fragment-ts';
180
+ import { Request, Response, NextFunction } from 'express';
181
+ import { v4 as uuidv4 } from 'uuid';
182
+
183
+ @Injectable()
184
+ export class RequestIdMiddleware {
185
+ use(req: Request, res: Response, next: NextFunction): void {
186
+ const requestId = req.headers['x-request-id'] || uuidv4();
187
+ req.headers['x-request-id'] = requestId as string;
188
+ res.setHeader('X-Request-ID', requestId);
189
+ next();
190
+ }
191
+ }
192
+
193
+ // src/middlewares/logging.middleware.ts
194
+ import { Injectable } from 'fragment-ts';
195
+ import { Request, Response, NextFunction } from 'express';
196
+
197
+ @Injectable()
198
+ export class LoggingMiddleware {
199
+ use(req: Request, res: Response, next: NextFunction): void {
200
+ const start = Date.now();
201
+ console.log(`📥 [${req.method}] ${req.path} - ${req.ip || 'unknown'}`);
202
+
203
+ res.on('finish', () => {
204
+ const duration = Date.now() - start;
205
+ console.log(`📤 [${req.method}] ${req.path} → ${res.statusCode} (${duration}ms)`);
206
+ });
207
+
208
+ next();
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### Guard Implementation
214
+
215
+ ```typescript
216
+ // src/guards/role.guard.ts
217
+ import { Injectable } from 'fragment-ts';
218
+ import { Request } from 'express';
219
+
220
+ @Injectable()
221
+ export class RoleGuard {
222
+ canActivate(req: Request): boolean {
223
+ const userRole = req.headers['x-user-role'];
224
+ const requiredRoles = ['admin', 'moderator'];
225
+ return requiredRoles.includes(userRole as string);
226
+ }
227
+ }
228
+
229
+ // src/guards/api-key.guard.ts
230
+ import { Injectable } from 'fragment-ts';
231
+ import { Request } from 'express';
232
+
233
+ @Injectable()
234
+ export class ApiKeyGuard {
235
+ canActivate(req: Request): boolean {
236
+ const apiKey = req.headers['x-api-key'];
237
+ const validKeys = process.env.VALID_API_KEYS?.split(',') || [];
238
+ return validKeys.includes(apiKey as string);
239
+ }
240
+ }
241
+ ```
242
+
243
+ ### Interceptor Implementation
244
+
245
+ ```typescript
246
+ // src/interceptors/cache.interceptor.ts
247
+ import { Injectable } from 'fragment-ts';
248
+ import { Request, Response } from 'express';
249
+ import { CacheService } from '../services/cache.service';
250
+
251
+ @Injectable()
252
+ export class CacheInterceptor {
253
+ constructor(private cacheService: CacheService) {}
254
+
255
+ async intercept(req: Request, res: Response, result: any): Promise<any> {
256
+ const cacheKey = `${req.method}:${req.path}`;
257
+
258
+ // Store in cache for GET requests
259
+ if (req.method === 'GET') {
260
+ await this.cacheService.set(cacheKey, result, 300); // 5 minutes
261
+ }
262
+
263
+ return result;
264
+ }
265
+ }
266
+
267
+ // src/interceptors/validation.interceptor.ts
268
+ import { Injectable } from 'fragment-ts';
269
+ import { Request, Response } from 'express';
270
+ import { validate } from 'class-validator';
271
+
272
+ @Injectable()
273
+ export class ValidationInterceptor {
274
+ async intercept(req: Request, res: Response, result: any): Promise<any> {
275
+ // Validate response data if it's an object with validation decorators
276
+ if (result && typeof result === 'object' && !Array.isArray(result)) {
277
+ const errors = await validate(result);
278
+ if (errors.length > 0) {
279
+ console.warn('Response validation failed:', errors);
280
+ }
281
+ }
282
+ return result;
283
+ }
284
+ }
285
+ ```
286
+
287
+ ### Exception Filter Implementation
288
+
289
+ ```typescript
290
+ // src/filters/validation-exception.filter.ts
291
+ import { Injectable } from 'fragment-ts';
292
+ import { Request, Response } from 'express';
293
+ import { ValidationError } from 'class-validator';
294
+
295
+ @Injectable()
296
+ export class ValidationExceptionFilter {
297
+ catch(exception: Error, req: Request, res: Response): void {
298
+ if (exception instanceof ValidationError) {
299
+ const errors = this.formatValidationErrors(exception);
300
+ res.status(400).json({
301
+ statusCode: 400,
302
+ error: 'Bad Request',
303
+ message: 'Validation failed',
304
+ details: errors
305
+ });
306
+ }
307
+ }
308
+
309
+ private formatValidationErrors(error: ValidationError): Record<string, string[]> {
310
+ const errors: Record<string, string[]> = {};
311
+ for (const constraint of Object.values(error.constraints || {})) {
312
+ const property = error.property;
313
+ if (!errors[property]) errors[property] = [];
314
+ errors[property].push(constraint);
315
+ }
316
+ return errors;
317
+ }
318
+ }
319
+
320
+ // src/filters/database-exception.filter.ts
321
+ import { Injectable } from 'fragment-ts';
322
+ import { Request, Response } from 'express';
323
+
324
+ @Injectable()
325
+ export class DatabaseExceptionFilter {
326
+ catch(exception: Error, req: Request, res: Response): void {
327
+ if (exception.name === 'QueryFailedError' || exception.message.includes('database')) {
328
+ res.status(500).json({
329
+ statusCode: 500,
330
+ error: 'Database Error',
331
+ message: 'An error occurred while accessing the database'
332
+ });
333
+ }
334
+ }
335
+ }
336
+ ```
337
+
338
+ ### Applying Enhancements
339
+
340
+ #### Class-Level Enhancements
341
+
342
+ ```typescript
343
+ import { Controller, Get } from 'fragment-ts';
344
+ import { LoggingMiddleware } from '../middlewares/logging.middleware';
345
+ import { AuthGuard } from '../guards/auth.guard';
346
+ import { TransformInterceptor } from '../interceptors/transform.interceptor';
347
+ import { HttpExceptionFilter } from '../filters/http-exception.filter';
348
+
349
+ @FragmentMiddleware(LoggingMiddleware)
350
+ @FragmentGuard(AuthGuard)
351
+ @FragmentInterceptor(TransformInterceptor)
352
+ @FragmentExceptionFilter(HttpExceptionFilter)
353
+ @Controller('/api/v1')
354
+ export class ApiController {
355
+ @Get('/users')
356
+ async getUsers() {
357
+ return [{ id: 1, name: 'John' }];
358
+ }
359
+
360
+ @Get('/status')
361
+ async getStatus() {
362
+ return { status: 'healthy' };
363
+ }
364
+ }
365
+ ```
366
+
367
+ #### Method-Level Enhancements
368
+
369
+ ```typescript
370
+ import { Controller, Get, Post, Body } from 'fragment-ts';
371
+ import { ApiKeyGuard } from '../guards/api-key.guard';
372
+ import { CacheInterceptor } from '../interceptors/cache.interceptor';
373
+ import { ValidationExceptionFilter } from '../filters/validation-exception.filter';
374
+ import { CreateUserDto } from '../dtos/create-user.dto';
375
+
376
+ @Controller('/api')
377
+ export class UserController {
378
+ @Get('/public-data')
379
+ @FragmentGuard(ApiKeyGuard) // Only this method requires API key
380
+ getPublicData() {
381
+ return { data: 'public information' };
382
+ }
383
+
384
+ @Get('/users')
385
+ @FragmentInterceptor(CacheInterceptor) // Cache only this endpoint
386
+ async getUsers() {
387
+ // Fetch from database
388
+ return [{ id: 1, name: 'John' }];
389
+ }
390
+
391
+ @Post('/users')
392
+ @FragmentExceptionFilter(ValidationExceptionFilter) // Specific error handling
393
+ async createUser(@Body() userData: CreateUserDto) {
394
+ // Create user logic
395
+ return { id: 1, ...userData };
396
+ }
397
+ }
398
+ ```
399
+
400
+ #### Mixed Class and Method Enhancements
401
+
402
+ ```typescript
403
+ @FragmentMiddleware(LoggingMiddleware)
404
+ @FragmentGuard(AuthGuard) // Applied to all methods
405
+ @Controller('/admin')
406
+ export class AdminController {
407
+ @Get('/dashboard')
408
+ getDashboard() {
409
+ // Uses both LoggingMiddleware and AuthGuard
410
+ return { dashboard: 'data' };
411
+ }
412
+
413
+ @Get('/public-stats')
414
+ @FragmentGuard(PublicGuard) // Overrides class-level AuthGuard
415
+ getPublicStats() {
416
+ // Uses LoggingMiddleware and PublicGuard (not AuthGuard)
417
+ return { stats: 'public' };
418
+ }
419
+
420
+ @Post('/settings')
421
+ @FragmentInterceptor(AuditInterceptor) // Additional interceptor
422
+ updateSettings(@Body() settings: any) {
423
+ // Uses LoggingMiddleware, AuthGuard, and AuditInterceptor
424
+ return { updated: true };
425
+ }
426
+ }
427
+ ```
428
+
429
+ #### Application-Level Exception Filters
430
+
431
+ ```typescript
432
+ import { FragmentApplication } from 'fragment-ts';
433
+ import { GlobalExceptionFilter } from './filters/global-exception.filter';
434
+ import { DatabaseExceptionFilter } from './filters/database-exception.filter';
435
+
436
+ @FragmentApplication({
437
+ port: 3000,
438
+ autoScan: true
439
+ })
440
+ @FragmentExceptionFilter(DatabaseExceptionFilter)
441
+ @FragmentExceptionFilter(GlobalExceptionFilter)
442
+ export class Application {
443
+ constructor() {
444
+ console.log('Application starting...');
445
+ }
446
+ }
447
+ ```
448
+
163
449
  ## Repositories
164
450
 
165
451
  ### TypeORM Repository Injection
@@ -331,6 +617,101 @@ describe('UserService', () => {
331
617
  });
332
618
  ```
333
619
 
620
+ ### Testing Guards
621
+
622
+ ```typescript
623
+ import { AuthGuard } from '../guards/auth.guard';
624
+
625
+ describe('AuthGuard', () => {
626
+ let guard: AuthGuard;
627
+
628
+ beforeEach(() => {
629
+ guard = new AuthGuard();
630
+ });
631
+
632
+ it('should allow valid tokens', () => {
633
+ const mockReq = { headers: { authorization: 'Bearer valid-token' } } as any;
634
+ expect(guard.canActivate(mockReq)).toBe(true);
635
+ });
636
+
637
+ it('should reject invalid tokens', () => {
638
+ const mockReq = { headers: { authorization: 'Bearer invalid' } } as any;
639
+ expect(guard.canActivate(mockReq)).toBe(false);
640
+ });
641
+
642
+ it('should reject missing tokens', () => {
643
+ const mockReq = { headers: {} } as any;
644
+ expect(guard.canActivate(mockReq)).toBe(false);
645
+ });
646
+ });
647
+ ```
648
+
649
+ ### Testing Interceptors
650
+
651
+ ```typescript
652
+ import { TransformInterceptor } from '../interceptors/transform.interceptor';
653
+
654
+ describe('TransformInterceptor', () => {
655
+ let interceptor: TransformInterceptor;
656
+ let mockReq: any;
657
+ let mockRes: any;
658
+
659
+ beforeEach(() => {
660
+ interceptor = new TransformInterceptor();
661
+ mockReq = { path: '/test', method: 'GET' };
662
+ mockRes = {};
663
+ });
664
+
665
+ it('should wrap result with metadata', () => {
666
+ const originalResult = { data: 'test' };
667
+ const result = interceptor.intercept(mockReq, mockRes, originalResult);
668
+
669
+ expect(result).toHaveProperty('success', true);
670
+ expect(result).toHaveProperty('data', originalResult);
671
+ expect(result).toHaveProperty('timestamp');
672
+ });
673
+ });
674
+ ```
675
+
676
+ ### Integration Testing with Enhancements
677
+
678
+ ```typescript
679
+ import request from 'supertest';
680
+ import { FragmentWebApplication } from 'fragment-ts';
681
+ import { Application } from '../app';
682
+
683
+ describe('API with Enhancements', () => {
684
+ let app: FragmentWebApplication;
685
+
686
+ beforeAll(async () => {
687
+ app = new FragmentWebApplication();
688
+ await app.bootstrap(Application);
689
+ });
690
+
691
+ it('should log requests with middleware', async () => {
692
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
693
+
694
+ await request(app.getExpressApp())
695
+ .get('/api/status')
696
+ .expect(200);
697
+
698
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('📥 [GET] /api/status'));
699
+ consoleSpy.mockRestore();
700
+ });
701
+
702
+ it('should require authentication with guard', async () => {
703
+ await request(app.getExpressApp())
704
+ .get('/admin/dashboard')
705
+ .expect(403); // Assuming guard returns false without proper auth
706
+
707
+ await request(app.getExpressApp())
708
+ .get('/admin/dashboard')
709
+ .set('Authorization', 'Bearer valid-token')
710
+ .expect(200);
711
+ });
712
+ });
713
+ ```
714
+
334
715
  ## Environment Configuration
335
716
 
336
717
  ### fragment.json Example
@@ -361,6 +742,7 @@ DB_HOST=production-db.example.com
361
742
  DB_USER=app_user
362
743
  DB_PASSWORD=secure_password
363
744
  FEATURE_FLAG_NEW_UI=true
745
+ VALID_API_KEYS=key1,key2,key3
364
746
  ```
365
747
 
366
748
  ## Production Deployment
@@ -417,6 +799,11 @@ CMD ["node", "dist/app.js"]
417
799
  - Check route methods have appropriate HTTP decorators (`@Get()`, `@Post()`, etc.)
418
800
  - Ensure controller classes are exported and discoverable
419
801
 
802
+ 4. **Enhancement decorators not working**:
803
+ - Ensure enhancement classes have `@Injectable()` or similar decorator
804
+ - Verify enhancement classes implement the correct interface
805
+ - Check that metadata is being stored as arrays (not single values)
806
+
420
807
  ### Debugging Tips
421
808
 
422
809
  1. Enable detailed logging by setting environment variable:
@@ -440,3 +827,9 @@ CMD ["node", "dist/app.js"]
440
827
  console.log('Discovered classes:', storage.getAllClasses());
441
828
  console.log('Discovered methods:', storage.getAllMethods());
442
829
  ```
830
+
831
+ 4. Debug enhancement pipeline execution:
832
+ ```typescript
833
+ // Add console logs to your middleware/guards/interceptors
834
+ // to verify they're being called in the expected order
835
+ ```
@@ -1 +1 @@
1
- {"version":3,"file":"build.command.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,qBAAa,YAAY;IACvB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;WAW1B,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA8CjD,OAAO,CAAC,MAAM,CAAC,OAAO;IA+BtB,OAAO,CAAC,MAAM,CAAC,YAAY;CAqC5B"}
1
+ {"version":3,"file":"build.command.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,qBAAa,YAAY;IACvB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;WAW1B,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA4DjD,OAAO,CAAC,MAAM,CAAC,OAAO;IA+BtB,OAAO,CAAC,MAAM,CAAC,YAAY;CAqC5B"}
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
42
42
  const fs = __importStar(require("fs"));
43
43
  const chalk_1 = __importDefault(require("chalk"));
44
44
  const ora_1 = __importDefault(require("ora"));
45
+ const tsconfig_utils_1 = require("../../shared/tsconfig.utils");
45
46
  class BuildCommand {
46
47
  static register(program) {
47
48
  program
@@ -64,13 +65,25 @@ class BuildCommand {
64
65
  console.log(chalk_1.default.cyan(" npm install --save-dev typescript"));
65
66
  process.exit(1);
66
67
  }
67
- // Check if tsconfig.json exists
68
- const tsconfigPath = path.join(process.cwd(), "tsconfig.json");
69
- if (!fs.existsSync(tsconfigPath)) {
68
+ // Check if tsconfig.json exists using utility
69
+ if (!tsconfig_utils_1.TsConfigUtils.exists()) {
70
70
  spinner.fail("tsconfig.json not found");
71
71
  console.log(chalk_1.default.yellow("\nCreate a tsconfig.json file in your project root"));
72
72
  process.exit(1);
73
73
  }
74
+ // Validate required compiler options for Fragment
75
+ if (!tsconfig_utils_1.TsConfigUtils.hasDecoratorSupport()) {
76
+ spinner.fail("Required TypeScript compiler options not enabled");
77
+ console.log(chalk_1.default.yellow("\nYour tsconfig.json must include:"));
78
+ console.log(chalk_1.default.cyan(' "compilerOptions": {'));
79
+ console.log(chalk_1.default.cyan(' "experimentalDecorators": true,'));
80
+ console.log(chalk_1.default.cyan(' "emitDecoratorMetadata": true'));
81
+ console.log(chalk_1.default.cyan(" }"));
82
+ process.exit(1);
83
+ }
84
+ // Get output directory from tsconfig instead of hardcoding
85
+ const outDir = tsconfig_utils_1.TsConfigUtils.getOutDir();
86
+ const relativeOutDir = path.relative(process.cwd(), outDir);
74
87
  // Run TypeScript compiler
75
88
  spinner.text = "Compiling TypeScript...";
76
89
  (0, child_process_1.execSync)(`"${tscPath}"`, {
@@ -80,9 +93,9 @@ class BuildCommand {
80
93
  spinner.succeed("Build completed successfully!");
81
94
  if (options.analyze) {
82
95
  console.log(chalk_1.default.blue("\n📊 Bundle Analysis:"));
83
- this.analyzeBuild();
96
+ this.analyzeBuild(outDir); // Pass the actual output directory
84
97
  }
85
- console.log(chalk_1.default.green("\n✨ Build output: dist/"));
98
+ console.log(chalk_1.default.green(`\n✨ Build output: ${relativeOutDir}/`));
86
99
  }
87
100
  catch (error) {
88
101
  spinner.fail("Build failed");
@@ -111,10 +124,9 @@ class BuildCommand {
111
124
  }
112
125
  return null;
113
126
  }
114
- static analyzeBuild() {
115
- const distPath = path.join(process.cwd(), "dist");
127
+ static analyzeBuild(distPath) {
116
128
  if (!fs.existsSync(distPath)) {
117
- console.log(chalk_1.default.yellow(" No dist/ directory found"));
129
+ console.log(chalk_1.default.yellow(` No ${path.basename(distPath)}/ directory found`));
118
130
  return;
119
131
  }
120
132
  let totalSize = 0;