fragment-ts 1.0.28 → 1.0.30

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 (51) hide show
  1. package/dist/cli/commands/init.command.js +1 -1
  2. package/dist/core/container/di-container.d.ts +7 -6
  3. package/dist/core/container/di-container.d.ts.map +1 -1
  4. package/dist/core/container/di-container.js +162 -129
  5. package/dist/core/container/di-container.js.map +1 -1
  6. package/dist/core/decorators/application.decorator.d.ts.map +1 -1
  7. package/dist/core/decorators/application.decorator.js +2 -1
  8. package/dist/core/decorators/application.decorator.js.map +1 -1
  9. package/dist/core/decorators/auto-configuration.decorator.d.ts.map +1 -1
  10. package/dist/core/decorators/auto-configuration.decorator.js +4 -3
  11. package/dist/core/decorators/auto-configuration.decorator.js.map +1 -1
  12. package/dist/core/decorators/controller.decorator.d.ts.map +1 -1
  13. package/dist/core/decorators/controller.decorator.js +1 -0
  14. package/dist/core/decorators/controller.decorator.js.map +1 -1
  15. package/dist/core/decorators/http.decorators.d.ts.map +1 -1
  16. package/dist/core/decorators/http.decorators.js +9 -0
  17. package/dist/core/decorators/http.decorators.js.map +1 -1
  18. package/dist/core/decorators/injectable.decorator.d.ts.map +1 -1
  19. package/dist/core/decorators/injectable.decorator.js +1 -0
  20. package/dist/core/decorators/injectable.decorator.js.map +1 -1
  21. package/dist/core/decorators/injection.decorators.d.ts.map +1 -1
  22. package/dist/core/decorators/injection.decorators.js +49 -19
  23. package/dist/core/decorators/injection.decorators.js.map +1 -1
  24. package/dist/core/decorators/repository.decorator.d.ts.map +1 -1
  25. package/dist/core/decorators/repository.decorator.js +1 -0
  26. package/dist/core/decorators/repository.decorator.js.map +1 -1
  27. package/dist/core/decorators/service.decorator.d.ts.map +1 -1
  28. package/dist/core/decorators/service.decorator.js +1 -0
  29. package/dist/core/decorators/service.decorator.js.map +1 -1
  30. package/dist/core/metadata/metadata-storage.d.ts +11 -0
  31. package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
  32. package/dist/core/metadata/metadata-storage.js +22 -0
  33. package/dist/core/metadata/metadata-storage.js.map +1 -1
  34. package/dist/web/application.d.ts +0 -6
  35. package/dist/web/application.d.ts.map +1 -1
  36. package/dist/web/application.js +75 -38
  37. package/dist/web/application.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/cli/commands/init.command.ts +1 -1
  40. package/src/core/container/di-container.ts +247 -181
  41. package/src/core/decorators/application.decorator.ts +2 -1
  42. package/src/core/decorators/auto-configuration.decorator.ts +9 -8
  43. package/src/core/decorators/controller.decorator.ts +2 -1
  44. package/src/core/decorators/http.decorators.ts +17 -0
  45. package/src/core/decorators/injectable.decorator.ts +1 -0
  46. package/src/core/decorators/injection.decorators.ts +62 -22
  47. package/src/core/decorators/repository.decorator.ts +1 -0
  48. package/src/core/decorators/service.decorator.ts +1 -0
  49. package/src/core/metadata/metadata-storage.ts +47 -0
  50. package/src/web/application.ts +107 -64
  51. package/tsconfig.json +4 -3
@@ -1,17 +1,18 @@
1
- import { Injectable } from './injectable.decorator';
2
- import { METADATA_KEYS } from '../metadata/metadata-keys';
3
- import { MetadataStorage } from '../metadata/metadata-storage';
1
+ import { Injectable } from "./injectable.decorator";
2
+ import { METADATA_KEYS } from "../metadata/metadata-keys";
3
+ import { MetadataStorage } from "../metadata/metadata-storage";
4
4
 
5
5
  export function AutoConfiguration(): ClassDecorator {
6
6
  return (target: any) => {
7
- Injectable('singleton')(target);
7
+ console.log(`šŸ”§ Registering AutoConfiguration: ${target.name}`);
8
+ Injectable("singleton")(target);
8
9
  Reflect.defineMetadata(METADATA_KEYS.AUTO_CONFIGURATION, true, target);
9
-
10
+
10
11
  const storage = MetadataStorage.getInstance();
11
12
  storage.addClass({
12
13
  target,
13
- type: 'auto-configuration',
14
- scope: 'singleton'
14
+ type: "auto-configuration",
15
+ scope: "singleton",
15
16
  });
16
17
  };
17
- }
18
+ }
@@ -4,9 +4,10 @@ import { MetadataStorage } from "../metadata/metadata-storage";
4
4
 
5
5
  export function Controller(path: string = ""): ClassDecorator {
6
6
  return (target: any) => {
7
+ console.log(`šŸŽ® Registering Controller: ${target.name} at ${path || '/'}`);
7
8
  Injectable("singleton")(target);
8
9
  Reflect.defineMetadata(METADATA_KEYS.CONTROLLER, path, target);
9
-
10
+
10
11
  const storage = MetadataStorage.getInstance();
11
12
  storage.addClass({
12
13
  target,
@@ -8,6 +8,13 @@ function createHttpMethodDecorator(method: string) {
8
8
  propertyKey: string | symbol,
9
9
  descriptor: PropertyDescriptor,
10
10
  ) => {
11
+ const className = target.constructor.name;
12
+ const methodName = String(propertyKey);
13
+
14
+ console.log(
15
+ `⚔ Registering ${method.toUpperCase()} route: ${className}.${methodName} at ${path || "/"}`,
16
+ );
17
+
11
18
  Reflect.defineMetadata(
12
19
  METADATA_KEYS.HTTP_METHOD,
13
20
  method,
@@ -50,6 +57,16 @@ function createParamDecorator(type: string) {
50
57
  throw new Error(`@${type}() cannot be used on constructor parameters`);
51
58
  }
52
59
 
60
+ const className = target.constructor.name;
61
+ const methodName = String(propertyKey);
62
+ const paramDisplay = paramName
63
+ ? `${paramName} (index: ${parameterIndex})`
64
+ : `index: ${parameterIndex}`;
65
+
66
+ console.log(
67
+ ` šŸ“„ Registering @${type} parameter for ${className}.${methodName}: ${paramDisplay}`,
68
+ );
69
+
53
70
  const storage = MetadataStorage.getInstance();
54
71
 
55
72
  storage.addParam({
@@ -5,6 +5,7 @@ export type Scope = 'singleton' | 'request' | 'transient';
5
5
 
6
6
  export function Injectable(scope: Scope = 'singleton'): ClassDecorator {
7
7
  return (target: any) => {
8
+ console.log(`šŸ’‰ Registering Injectable: ${target.name} [${scope}]`);
8
9
  Reflect.defineMetadata(METADATA_KEYS.INJECTABLE, true, target);
9
10
  Reflect.defineMetadata(METADATA_KEYS.SCOPE, scope, target);
10
11
 
@@ -1,4 +1,6 @@
1
1
  import { METADATA_KEYS } from "../metadata/metadata-keys";
2
+ import { MetadataStorage } from "../metadata/metadata-storage";
3
+ import { DIContainer } from "../container/di-container";
2
4
 
3
5
  /**
4
6
  * @Autowired - Automatically inject dependencies by type
@@ -8,15 +10,22 @@ export function Autowired(): PropertyDecorator {
8
10
  return (target: any, propertyKey: string | symbol) => {
9
11
  // Get the design type from TypeScript metadata
10
12
  const type = Reflect.getMetadata("design:type", target, propertyKey);
11
-
12
13
  if (!type || type === Object) {
13
- throw new Error(
14
- `Cannot use @Autowired on property "${String(propertyKey)}" in ${target.constructor.name}. ` +
15
- `Make sure TypeScript emitDecoratorMetadata is enabled and the type is explicitly declared.`,
14
+ console.warn(
15
+ `āš ļø Could not determine type for ${target.constructor.name}.${String(propertyKey)}. ` +
16
+ `Ensure emitDecoratorMetadata is enabled in tsconfig.json`,
16
17
  );
17
18
  }
18
19
 
19
- // Store the type in metadata so DI container can resolve it
20
+ // Store metadata for property injection
21
+ const storage = MetadataStorage.getInstance();
22
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
23
+ type: "autowired",
24
+ key: propertyKey.toString(),
25
+ metadata: { type },
26
+ });
27
+
28
+ // Also store in Reflect metadata for backward compatibility
20
29
  Reflect.defineMetadata(METADATA_KEYS.AUTOWIRED, type, target, propertyKey);
21
30
  };
22
31
  }
@@ -28,6 +37,15 @@ export function Autowired(): PropertyDecorator {
28
37
  */
29
38
  export function Inject(token: string | Function): PropertyDecorator {
30
39
  return (target: any, propertyKey: string | symbol) => {
40
+ // Store metadata for property injection
41
+ const storage = MetadataStorage.getInstance();
42
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
43
+ type: "inject",
44
+ key: propertyKey.toString(),
45
+ metadata: { token },
46
+ });
47
+
48
+ // Also store in Reflect metadata for backward compatibility
31
49
  Reflect.defineMetadata(METADATA_KEYS.INJECT, token, target, propertyKey);
32
50
  };
33
51
  }
@@ -38,6 +56,15 @@ export function Inject(token: string | Function): PropertyDecorator {
38
56
  */
39
57
  export function InjectRepository(entity: Function): PropertyDecorator {
40
58
  return (target: any, propertyKey: string | symbol) => {
59
+ // Store metadata for property injection
60
+ const storage = MetadataStorage.getInstance();
61
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
62
+ type: "repository",
63
+ key: propertyKey.toString(),
64
+ metadata: { entity },
65
+ });
66
+
67
+ // Also store in Reflect metadata for backward compatibility
41
68
  Reflect.defineMetadata(
42
69
  METADATA_KEYS.INJECT_REPOSITORY,
43
70
  entity,
@@ -64,6 +91,15 @@ export function Qualifier(name: string): PropertyDecorator {
64
91
  */
65
92
  export function Value(expression: string): PropertyDecorator {
66
93
  return (target: any, propertyKey: string | symbol) => {
94
+ // Store metadata for property injection
95
+ const storage = MetadataStorage.getInstance();
96
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
97
+ type: "value",
98
+ key: propertyKey.toString(),
99
+ metadata: { expression },
100
+ });
101
+
102
+ // Also store in Reflect metadata for backward compatibility
67
103
  Reflect.defineMetadata(
68
104
  METADATA_KEYS.VALUE,
69
105
  expression,
@@ -89,27 +125,21 @@ export function Optional(): PropertyDecorator {
89
125
  */
90
126
  export function Lazy(): PropertyDecorator {
91
127
  return (target: any, propertyKey: string | symbol) => {
128
+ // Mark as lazy in metadata
92
129
  Reflect.defineMetadata(METADATA_KEYS.LAZY, true, target, propertyKey);
93
130
 
131
+ // Store metadata for property injection
132
+ const storage = MetadataStorage.getInstance();
133
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
134
+ type: "lazy",
135
+ key: propertyKey.toString(),
136
+ });
137
+
138
+ // Get the type for later resolution
94
139
  const type = Reflect.getMetadata("design:type", target, propertyKey);
95
140
 
96
- // Create a getter that resolves on first access
97
- let cached: any = null;
98
- let resolved = false;
99
-
100
- Object.defineProperty(target, propertyKey, {
101
- get() {
102
- if (!resolved) {
103
- const { DIContainer } = require("../container/di-container");
104
- const container = DIContainer.getInstance();
105
- cached = container.resolve(type);
106
- resolved = true;
107
- }
108
- return cached;
109
- },
110
- enumerable: true,
111
- configurable: true,
112
- });
141
+ // This will be handled by the container during injection
142
+ // (not directly creating the getter here to avoid container dependency)
113
143
  };
114
144
  }
115
145
 
@@ -123,6 +153,11 @@ export function PostConstruct(): MethodDecorator {
123
153
  propertyKey: string | symbol,
124
154
  descriptor: PropertyDescriptor,
125
155
  ) => {
156
+ const className = target.constructor.name;
157
+ console.log(
158
+ ` šŸ—ļø Registering @PostConstruct: ${className}.${String(propertyKey)}`,
159
+ );
160
+
126
161
  Reflect.defineMetadata(
127
162
  METADATA_KEYS.POST_CONSTRUCT,
128
163
  propertyKey,
@@ -141,6 +176,11 @@ export function PreDestroy(): MethodDecorator {
141
176
  propertyKey: string | symbol,
142
177
  descriptor: PropertyDescriptor,
143
178
  ) => {
179
+ const className = target.constructor.name;
180
+ console.log(
181
+ ` 🧹 Registering @PreDestroy: ${className}.${String(propertyKey)}`,
182
+ );
183
+
144
184
  Reflect.defineMetadata(
145
185
  METADATA_KEYS.PRE_DESTROY,
146
186
  propertyKey,
@@ -4,6 +4,7 @@ import { MetadataStorage } from '../metadata/metadata-storage';
4
4
 
5
5
  export function Repository(): ClassDecorator {
6
6
  return (target: any) => {
7
+ console.log(`šŸ’¾ Registering Repository: ${target.name}`);
7
8
  Injectable('singleton')(target);
8
9
  Reflect.defineMetadata(METADATA_KEYS.REPOSITORY, true, target);
9
10
 
@@ -4,6 +4,7 @@ import { MetadataStorage } from '../metadata/metadata-storage';
4
4
 
5
5
  export function Service(): ClassDecorator {
6
6
  return (target: any) => {
7
+ console.log(`āš™ļø Registering Service: ${target.name}`);
7
8
  Injectable('singleton')(target);
8
9
  Reflect.defineMetadata(METADATA_KEYS.SERVICE, true, target);
9
10
 
@@ -28,11 +28,19 @@ export interface ParamMetadata {
28
28
  paramName?: string;
29
29
  }
30
30
 
31
+ export interface PropertyInjectionMetadata {
32
+ type: "autowired" | "inject" | "repository" | "value" | "lazy";
33
+ key: string;
34
+ metadata?: any;
35
+ }
36
+
31
37
  export class MetadataStorage {
32
38
  private static instance: MetadataStorage;
33
39
  private classes: Map<any, ClassMetadata> = new Map();
34
40
  private methods: Map<string, MethodMetadata> = new Map();
35
41
  private params: Map<string, ParamMetadata[]> = new Map();
42
+ private propertyInjections: Map<string, PropertyInjectionMetadata[]> =
43
+ new Map();
36
44
 
37
45
  static getInstance(): MetadataStorage {
38
46
  if (!MetadataStorage.instance) {
@@ -88,4 +96,43 @@ export class MetadataStorage {
88
96
  const key = `${targetName}.${propertyKey}`;
89
97
  return this.params.get(key) || [];
90
98
  }
99
+
100
+ addPropertyInjection(
101
+ target: any,
102
+ propertyKey: string,
103
+ injection: PropertyInjectionMetadata,
104
+ ): void {
105
+ const className = target.name || target.constructor?.name;
106
+ const key = `${className}.${propertyKey}`;
107
+
108
+ if (!this.propertyInjections.has(key)) {
109
+ this.propertyInjections.set(key, []);
110
+ }
111
+
112
+ const injections = this.propertyInjections.get(key)!;
113
+ injections.push(injection);
114
+
115
+ console.log(
116
+ ` šŸŽÆ Registered property injection: ${className}.${propertyKey} [${injection.type}]`,
117
+ );
118
+ }
119
+
120
+ getPropertyInjections(
121
+ target: any,
122
+ ): { propertyKey: string; injections: PropertyInjectionMetadata[] }[] {
123
+ const className = target.name || target.constructor?.name;
124
+ const result: {
125
+ propertyKey: string;
126
+ injections: PropertyInjectionMetadata[];
127
+ }[] = [];
128
+
129
+ this.propertyInjections.forEach((injections, key) => {
130
+ if (key.startsWith(`${className}.`)) {
131
+ const propertyKey = key.split(".")[1];
132
+ result.push({ propertyKey, injections });
133
+ }
134
+ });
135
+
136
+ return result;
137
+ }
91
138
  }
@@ -16,14 +16,15 @@ export class FragmentWebApplication {
16
16
  private metadataStorage: MetadataStorage;
17
17
 
18
18
  constructor() {
19
+ console.log("🌱 Initializing Fragment Framework");
19
20
  this.app = express();
20
21
  this.container = DIContainer.getInstance();
21
22
  this.metadataStorage = MetadataStorage.getInstance();
22
23
  this.setupMiddleware();
23
- this;
24
24
  }
25
25
 
26
26
  private setupMiddleware(): void {
27
+ console.log("āš™ļø Setting up middleware");
27
28
  this.app.use(helmet());
28
29
  this.app.use(cors());
29
30
  this.app.use(compression());
@@ -32,17 +33,27 @@ export class FragmentWebApplication {
32
33
  }
33
34
 
34
35
  async bootstrap(appClass: any): Promise<void> {
35
- await TypeORMModule.initialize();
36
+ console.log("\nšŸš€ Bootstrapping application");
37
+
38
+ try {
39
+ await TypeORMModule.initialize();
40
+ console.log("āœ… TypeORM initialized successfully");
41
+ } catch (error) {
42
+ console.error("āŒ Failed to initialize TypeORM:", error);
43
+ throw error;
44
+ }
36
45
 
37
46
  const appMetadata = Reflect.getMetadata(
38
47
  METADATA_KEYS.APPLICATION,
39
48
  appClass,
40
49
  );
50
+
51
+ console.log(`šŸŽÆ Application metadata:`, appMetadata);
41
52
 
42
53
  // CRITICAL: Scan and load all component files first
43
54
  // This triggers decorator execution
44
55
  if (appMetadata?.autoScan !== false) {
45
- console.log("šŸ” Scanning for components...");
56
+ console.log("\nšŸ” Scanning for components...");
46
57
  await this.scanComponents();
47
58
  }
48
59
 
@@ -52,51 +63,51 @@ export class FragmentWebApplication {
52
63
 
53
64
  const port = appMetadata?.port || 3000;
54
65
  const host = appMetadata?.host || "0.0.0.0";
55
-
66
+
56
67
  this.app.use(this.errorHandler.bind(this));
57
-
68
+
58
69
  this.app.listen(port, host, () => {
59
- console.log(`✨ Fragment application running on http://${host}:${port}`);
70
+ console.log(`\n✨ Fragment application running on http://${host}:${port}`);
71
+ console.log("========================================\n");
60
72
  });
61
73
  }
62
74
 
63
- /**
64
- * Detects environment and scans appropriate files
65
- */
66
75
  private async scanComponents(): Promise<void> {
67
76
  const cwd = process.cwd();
68
77
  const distExists = fs.existsSync(path.join(cwd, "dist"));
69
78
  const srcExists = fs.existsSync(path.join(cwd, "src"));
79
+
80
+ console.log(`šŸ“ Current working directory: ${cwd}`);
81
+ console.log(`šŸ“ dist/ exists: ${distExists}`);
82
+ console.log(`šŸ“ src/ exists: ${srcExists}`);
70
83
 
71
84
  // Determine if we're running TypeScript directly (dev) or compiled JS (prod)
72
85
  const isDevMode = this.isRunningTypeScript();
73
-
86
+ console.log(`šŸ’» Running in ${isDevMode ? 'development' : 'production'} mode`);
87
+
74
88
  if (isDevMode && srcExists) {
75
89
  // Development mode: scan TypeScript source files
76
- console.log(" šŸ“ Development mode: scanning TypeScript files");
90
+ console.log(" šŸ“ Development mode: scanning TypeScript files");
77
91
  await ComponentScanner.scanSource();
78
92
  } else if (distExists) {
79
93
  // Production mode: scan compiled JavaScript files
80
- console.log(" šŸ“¦ Production mode: scanning compiled files");
94
+ console.log(" šŸ“¦ Production mode: scanning compiled files");
81
95
  await ComponentScanner.scan();
82
96
  } else {
83
- console.warn(" āš ļø Warning: No src/ or dist/ directory found");
97
+ console.warn(" āš ļø Warning: No src/ or dist/ directory found");
84
98
  }
85
99
  }
86
100
 
87
- /**
88
- * Detects if we're running TypeScript directly (ts-node or similar)
89
- */
90
101
  private isRunningTypeScript(): boolean {
91
102
  // Check if ts-node is in require.extensions
92
103
  if (require.extensions[".ts"]) {
93
104
  return true;
94
105
  }
95
-
106
+
96
107
  // Check if process is running with ts-node or tsx
97
108
  const execPath = process.argv[0];
98
109
  const scriptPath = process.argv[1] || "";
99
-
110
+
100
111
  if (
101
112
  execPath.includes("ts-node") ||
102
113
  execPath.includes("tsx") ||
@@ -105,25 +116,24 @@ export class FragmentWebApplication {
105
116
  ) {
106
117
  return true;
107
118
  }
108
-
119
+
109
120
  // Check if main module has .ts extension
110
121
  if (require.main?.filename.endsWith(".ts")) {
111
122
  return true;
112
123
  }
113
-
124
+
114
125
  // Check NODE_ENV or explicit flag
115
126
  if (process.env.FRAGMENT_DEV_MODE === "true") {
116
127
  return true;
117
128
  }
118
-
129
+
119
130
  return false;
120
131
  }
121
132
 
122
133
  private discoverAndRegisterComponents(): void {
123
134
  const classes = this.metadataStorage.getAllClasses();
124
-
125
135
  console.log(`\nšŸ“¦ Discovered ${classes.length} component(s)`);
126
-
136
+
127
137
  // Group by type for display
128
138
  const grouped = classes.reduce(
129
139
  (acc, cls) => {
@@ -133,38 +143,50 @@ export class FragmentWebApplication {
133
143
  },
134
144
  {} as Record<string, any[]>,
135
145
  );
136
-
146
+
137
147
  Object.entries(grouped).forEach(([type, items]) => {
138
148
  const icon = this.getTypeIcon(type);
139
- console.log(` ${icon} ${items.length} ${type}(s)`);
149
+ console.log(` ${icon} ${items.length} ${type}(s)`);
150
+
151
+ items.forEach(item => {
152
+ console.log(` • ${item.target.name}${item.path ? ` (${item.path})` : ''}`);
153
+ });
140
154
  });
141
155
 
142
156
  let registered = 0;
143
157
  let skipped = 0;
144
-
158
+
145
159
  classes.forEach((metadata) => {
146
160
  if (this.shouldRegister(metadata.target)) {
147
161
  // CRITICAL: Register with container
148
162
  if (!this.container.has(metadata.target)) {
149
163
  this.container.register(metadata.target);
150
164
  registered++;
151
- console.log(` āœ“ Registered: ${metadata.target.name}`);
165
+ console.log(` āœ“ Registered: ${metadata.target.name}`);
166
+ } else {
167
+ console.log(` āœ“ Already registered: ${metadata.target.name}`);
152
168
  }
153
169
  } else {
154
170
  skipped++;
155
171
  console.log(
156
- ` ⊘ Skipped: ${metadata.target.name} (conditional check failed)`,
172
+ ` ⊘ Skipped: ${metadata.target.name} (conditional check failed)`,
157
173
  );
158
174
  }
159
175
  });
160
176
 
161
- console.log(
162
- `\n āœ“ Registered ${registered}/${classes.length} component(s)`,
163
- );
177
+ console.log(`\nāœ“ Registered ${registered} component(s)`);
164
178
  if (skipped > 0) {
165
- console.log(` ⊘ Skipped ${skipped} (conditions not met)`);
179
+ console.log(`⊘ Skipped ${skipped} component(s) (conditions not met)`);
166
180
  }
167
- console.log("");
181
+
182
+ // Pre-resolve all singleton components to ensure dependencies are injected
183
+ console.log("\nšŸ”§ Initializing components");
184
+ classes.forEach(metadata => {
185
+ if (this.shouldRegister(metadata.target)) {
186
+ const instance = this.container.resolve(metadata.target);
187
+ console.log(` āœ“ Initialized: ${metadata.target.name}`);
188
+ }
189
+ });
168
190
  }
169
191
 
170
192
  private getTypeIcon(type: string): string {
@@ -183,7 +205,9 @@ export class FragmentWebApplication {
183
205
  METADATA_KEYS.CONDITIONAL_ON_CLASS,
184
206
  target,
185
207
  );
208
+
186
209
  if (conditionalClass && !this.isClassAvailable(conditionalClass)) {
210
+ console.log(` 🚫 Conditional check failed for ${target.name}: Class not available`);
187
211
  return false;
188
212
  }
189
213
 
@@ -191,7 +215,9 @@ export class FragmentWebApplication {
191
215
  METADATA_KEYS.CONDITIONAL_ON_MISSING_BEAN,
192
216
  target,
193
217
  );
218
+
194
219
  if (conditionalMissingBean && this.container.has(conditionalMissingBean)) {
220
+ console.log(` 🚫 Conditional check failed for ${target.name}: Bean already exists`);
195
221
  return false;
196
222
  }
197
223
 
@@ -199,15 +225,17 @@ export class FragmentWebApplication {
199
225
  METADATA_KEYS.CONDITIONAL_ON_PROPERTY,
200
226
  target,
201
227
  );
228
+
202
229
  if (conditionalProperty) {
203
230
  const value = process.env[conditionalProperty.key];
204
- if (
205
- conditionalProperty.expectedValue !== undefined &&
206
- value !== conditionalProperty.expectedValue
207
- ) {
208
- return false;
209
- }
210
- if (!value) {
231
+
232
+ if (conditionalProperty.expectedValue !== undefined) {
233
+ if (value !== conditionalProperty.expectedValue) {
234
+ console.log(` 🚫 Conditional check failed for ${target.name}: Expected ${conditionalProperty.expectedValue}, got ${value}`);
235
+ return false;
236
+ }
237
+ } else if (!value) {
238
+ console.log(` 🚫 Conditional check failed for ${target.name}: Property not set`);
211
239
  return false;
212
240
  }
213
241
  }
@@ -227,37 +255,44 @@ export class FragmentWebApplication {
227
255
  const controllers = this.metadataStorage
228
256
  .getAllClasses()
229
257
  .filter((c) => c.type === "controller");
230
-
258
+
231
259
  if (controllers.length === 0) {
232
- console.log("šŸ›£ļø No routes to register\n");
260
+ console.log("\nšŸ›£ļø No routes to register");
233
261
  return;
234
262
  }
235
-
263
+
236
264
  let totalRoutes = 0;
237
- console.log(`šŸ›£ļø Registering routes...\n`);
238
-
265
+ console.log(`\nšŸ›£ļø Registering routes...`);
266
+
239
267
  controllers.forEach((controllerMetadata) => {
240
- const controller = this.container.resolve(controllerMetadata.target);
241
- const basePath = controllerMetadata.path || "";
242
- const methods = this.metadataStorage
243
- .getAllMethods()
244
- .filter((m) => m.target === controllerMetadata.target);
245
-
246
- if (methods.length > 0) {
247
- const controllerName = controllerMetadata.target.name;
248
- console.log(` šŸ“ ${controllerName}`);
249
-
268
+ try {
269
+ console.log(`\nšŸ“ Controller: ${controllerMetadata.target.name}`);
270
+ console.log(` Base path: ${controllerMetadata.path || '/'}`);
271
+
272
+ const controller = this.container.resolve(controllerMetadata.target);
273
+ const basePath = controllerMetadata.path || "";
274
+
275
+ const methods = this.metadataStorage
276
+ .getAllMethods()
277
+ .filter((m) => m.target === controllerMetadata.target);
278
+
279
+ if (methods.length === 0) {
280
+ console.log(` āš ļø No routes defined for this controller`);
281
+ return;
282
+ }
283
+
250
284
  methods.forEach((methodMetadata) => {
251
285
  const fullPath = this.normalizePath(basePath + methodMetadata.path);
252
286
  const httpMethod = methodMetadata.method.toLowerCase();
253
287
  const methodColor = this.getMethodColor(httpMethod);
254
288
  const methodIcon = this.getMethodIcon(httpMethod);
255
-
289
+
256
290
  console.log(
257
- ` ${methodIcon} ${methodColor}${httpMethod.toUpperCase().padEnd(7)}\x1b[0m ${fullPath}`,
291
+ ` ${methodIcon} ${methodColor}${httpMethod.toUpperCase().padEnd(7)}\x1b[0m ${fullPath}`,
258
292
  );
293
+
259
294
  totalRoutes++;
260
-
295
+
261
296
  (this.app as any)[httpMethod](
262
297
  fullPath,
263
298
  async (req: Request, res: Response, next: NextFunction) => {
@@ -267,23 +302,30 @@ export class FragmentWebApplication {
267
302
  req,
268
303
  res,
269
304
  );
305
+
306
+ console.log(`\nšŸ” Handling ${httpMethod.toUpperCase()} ${fullPath}`);
307
+ // console.log(` Parameters:`, args);
308
+
270
309
  const result = await (controller as any)[
271
310
  methodMetadata.propertyKey
272
311
  ](...args);
312
+
273
313
  if (!res.headersSent) {
274
314
  res.json(result);
275
315
  }
276
316
  } catch (error) {
317
+ console.error(`āŒ Error handling route ${fullPath}:`, error);
277
318
  next(error);
278
319
  }
279
320
  },
280
321
  );
281
322
  });
282
- console.log("");
323
+ } catch (error) {
324
+ console.error(`āŒ Failed to register controller ${controllerMetadata.target.name}:`, error);
283
325
  }
284
326
  });
285
327
 
286
- console.log(` āœ“ Registered ${totalRoutes} route(s)\n`);
328
+ console.log(`\nāœ“ Registered ${totalRoutes} route(s)`);
287
329
  }
288
330
 
289
331
  private getMethodColor(method: string): string {
@@ -313,10 +355,10 @@ export class FragmentWebApplication {
313
355
  req: Request,
314
356
  res: Response,
315
357
  ): any[] {
316
- const params = methodMetadata.paramMetadata.sort(
358
+ const params = [...methodMetadata.paramMetadata].sort(
317
359
  (a: any, b: any) => a.index - b.index,
318
360
  );
319
-
361
+
320
362
  return params.map((param: any) => {
321
363
  switch (param.type) {
322
364
  case "body":
@@ -349,14 +391,15 @@ export class FragmentWebApplication {
349
391
  res: Response,
350
392
  next: NextFunction,
351
393
  ): void {
352
- console.error(err.stack);
394
+ console.error(`\nāŒ Global error handler:`, err);
353
395
  res.status(500).json({
354
396
  error: "Internal Server Error",
355
397
  message: err.message,
398
+ timestamp: new Date().toISOString(),
356
399
  });
357
400
  }
358
401
 
359
402
  getExpressApp(): Express {
360
403
  return this.app;
361
404
  }
362
- }
405
+ }