fragment-ts 1.0.29 → 1.0.31

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 (50) hide show
  1. package/dist/cli/commands/init.command.js +1 -1
  2. package/dist/core/container/di-container.d.ts +7 -7
  3. package/dist/core/container/di-container.d.ts.map +1 -1
  4. package/dist/core/container/di-container.js +126 -138
  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 +50 -21
  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 +8 -13
  31. package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
  32. package/dist/core/metadata/metadata-storage.js +19 -18
  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 +93 -41
  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 +188 -203
  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 +64 -24
  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 +37 -37
  50. package/src/web/application.ts +129 -44
@@ -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,5 +1,6 @@
1
1
  import { METADATA_KEYS } from "../metadata/metadata-keys";
2
2
  import { MetadataStorage } from "../metadata/metadata-storage";
3
+ import { DIContainer } from "../container/di-container";
3
4
 
4
5
  /**
5
6
  * @Autowired - Automatically inject dependencies by type
@@ -7,21 +8,25 @@ import { MetadataStorage } from "../metadata/metadata-storage";
7
8
  */
8
9
  export function Autowired(): PropertyDecorator {
9
10
  return (target: any, propertyKey: string | symbol) => {
11
+ // Get the design type from TypeScript metadata
12
+ const type = Reflect.getMetadata("design:type", target, propertyKey);
13
+ if (!type || type === Object) {
14
+ console.warn(
15
+ `āš ļø Could not determine type for ${target.constructor.name}.${String(propertyKey)}. ` +
16
+ `Ensure emitDecoratorMetadata is enabled in tsconfig.json`,
17
+ );
18
+ }
19
+
10
20
  // Store metadata for property injection
11
21
  const storage = MetadataStorage.getInstance();
12
22
  storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
13
23
  type: "autowired",
14
24
  key: propertyKey.toString(),
25
+ metadata: { type },
26
+ propertyKey: propertyKey.toString(),
15
27
  });
16
28
 
17
29
  // Also store in Reflect metadata for backward compatibility
18
- const type = Reflect.getMetadata("design:type", target, propertyKey);
19
- if (!type || type === Object) {
20
- console.warn(
21
- `āš ļø Could not determine type for ${target.constructor.name}.${String(propertyKey)}. ` +
22
- `Ensure emitDecoratorMetadata is enabled in tsconfig.json`,
23
- );
24
- }
25
30
  Reflect.defineMetadata(METADATA_KEYS.AUTOWIRED, type, target, propertyKey);
26
31
  };
27
32
  }
@@ -33,6 +38,16 @@ export function Autowired(): PropertyDecorator {
33
38
  */
34
39
  export function Inject(token: string | Function): PropertyDecorator {
35
40
  return (target: any, propertyKey: string | symbol) => {
41
+ // Store metadata for property injection
42
+ const storage = MetadataStorage.getInstance();
43
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
44
+ type: "inject",
45
+ key: propertyKey.toString(),
46
+ metadata: { token },
47
+ propertyKey: propertyKey.toString(),
48
+ });
49
+
50
+ // Also store in Reflect metadata for backward compatibility
36
51
  Reflect.defineMetadata(METADATA_KEYS.INJECT, token, target, propertyKey);
37
52
  };
38
53
  }
@@ -43,6 +58,16 @@ export function Inject(token: string | Function): PropertyDecorator {
43
58
  */
44
59
  export function InjectRepository(entity: Function): PropertyDecorator {
45
60
  return (target: any, propertyKey: string | symbol) => {
61
+ // Store metadata for property injection
62
+ const storage = MetadataStorage.getInstance();
63
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
64
+ type: "repository",
65
+ key: propertyKey.toString(),
66
+ metadata: { entity },
67
+ propertyKey: propertyKey.toString(),
68
+ });
69
+
70
+ // Also store in Reflect metadata for backward compatibility
46
71
  Reflect.defineMetadata(
47
72
  METADATA_KEYS.INJECT_REPOSITORY,
48
73
  entity,
@@ -69,6 +94,16 @@ export function Qualifier(name: string): PropertyDecorator {
69
94
  */
70
95
  export function Value(expression: string): PropertyDecorator {
71
96
  return (target: any, propertyKey: string | symbol) => {
97
+ // Store metadata for property injection
98
+ const storage = MetadataStorage.getInstance();
99
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
100
+ type: "value",
101
+ key: propertyKey.toString(),
102
+ metadata: { expression },
103
+ propertyKey: propertyKey.toString(),
104
+ });
105
+
106
+ // Also store in Reflect metadata for backward compatibility
72
107
  Reflect.defineMetadata(
73
108
  METADATA_KEYS.VALUE,
74
109
  expression,
@@ -94,27 +129,22 @@ export function Optional(): PropertyDecorator {
94
129
  */
95
130
  export function Lazy(): PropertyDecorator {
96
131
  return (target: any, propertyKey: string | symbol) => {
132
+ // Mark as lazy in metadata
97
133
  Reflect.defineMetadata(METADATA_KEYS.LAZY, true, target, propertyKey);
98
134
 
135
+ // Store metadata for property injection
136
+ const storage = MetadataStorage.getInstance();
137
+ storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
138
+ type: "lazy",
139
+ key: propertyKey.toString(),
140
+ propertyKey: propertyKey.toString(),
141
+ });
142
+
143
+ // Get the type for later resolution
99
144
  const type = Reflect.getMetadata("design:type", target, propertyKey);
100
145
 
101
- // Create a getter that resolves on first access
102
- let cached: any = null;
103
- let resolved = false;
104
-
105
- Object.defineProperty(target, propertyKey, {
106
- get() {
107
- if (!resolved) {
108
- const { DIContainer } = require("../container/di-container");
109
- const container = DIContainer.getInstance();
110
- cached = container.resolve(type);
111
- resolved = true;
112
- }
113
- return cached;
114
- },
115
- enumerable: true,
116
- configurable: true,
117
- });
146
+ // This will be handled by the container during injection
147
+ // (not directly creating the getter here to avoid container dependency)
118
148
  };
119
149
  }
120
150
 
@@ -128,6 +158,11 @@ export function PostConstruct(): MethodDecorator {
128
158
  propertyKey: string | symbol,
129
159
  descriptor: PropertyDescriptor,
130
160
  ) => {
161
+ const className = target.constructor.name;
162
+ console.log(
163
+ ` šŸ—ļø Registering @PostConstruct: ${className}.${String(propertyKey)}`,
164
+ );
165
+
131
166
  Reflect.defineMetadata(
132
167
  METADATA_KEYS.POST_CONSTRUCT,
133
168
  propertyKey,
@@ -146,6 +181,11 @@ export function PreDestroy(): MethodDecorator {
146
181
  propertyKey: string | symbol,
147
182
  descriptor: PropertyDescriptor,
148
183
  ) => {
184
+ const className = target.constructor.name;
185
+ console.log(
186
+ ` 🧹 Registering @PreDestroy: ${className}.${String(propertyKey)}`,
187
+ );
188
+
149
189
  Reflect.defineMetadata(
150
190
  METADATA_KEYS.PRE_DESTROY,
151
191
  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,19 +28,20 @@ export interface ParamMetadata {
28
28
  paramName?: string;
29
29
  }
30
30
 
31
+ export interface PropertyInjectionMetadata {
32
+ propertyKey: string;
33
+ type: "autowired" | "inject" | "repository" | "value" | "lazy";
34
+ key: string;
35
+ metadata?: any;
36
+ }
37
+
31
38
  export class MetadataStorage {
32
39
  private static instance: MetadataStorage;
33
40
  private classes: Map<any, ClassMetadata> = new Map();
34
41
  private methods: Map<string, MethodMetadata> = new Map();
35
42
  private params: Map<string, ParamMetadata[]> = new Map();
36
- private propertyInjections: Map<
37
- string,
38
- {
39
- type: string;
40
- key: string;
41
- metadata?: any;
42
- }[]
43
- > = new Map();
43
+ private propertyInjections: Map<string, PropertyInjectionMetadata[]> =
44
+ new Map();
44
45
 
45
46
  static getInstance(): MetadataStorage {
46
47
  if (!MetadataStorage.instance) {
@@ -49,35 +50,6 @@ export class MetadataStorage {
49
50
  return MetadataStorage.instance;
50
51
  }
51
52
 
52
- addPropertyInjection(
53
- target: any,
54
- propertyKey: string,
55
- injection: { type: string; key: string; metadata?: any },
56
- ): void {
57
- const className = target.name || target.constructor?.name;
58
- const key = `${className}.${propertyKey}`;
59
- const injections = this.propertyInjections.get(key) || [];
60
- injections.push(injection);
61
- this.propertyInjections.set(key, injections);
62
- }
63
-
64
- getAllPropertyInjections(target: any): {
65
- propertyKey: string;
66
- injections: { type: string; key: string; metadata?: any }[];
67
- }[] {
68
- const className = target.name || target.constructor?.name;
69
- const injections: { propertyKey: string; injections: any[] }[] = [];
70
-
71
- this.propertyInjections.forEach((value, key) => {
72
- if (key.startsWith(`${className}.`)) {
73
- const propertyKey = key.split(".")[1];
74
- injections.push({ propertyKey, injections: value });
75
- }
76
- });
77
-
78
- return injections;
79
- }
80
-
81
53
  addClass(metadata: ClassMetadata): void {
82
54
  this.classes.set(metadata.target, metadata);
83
55
  }
@@ -125,4 +97,32 @@ export class MetadataStorage {
125
97
  const key = `${targetName}.${propertyKey}`;
126
98
  return this.params.get(key) || [];
127
99
  }
100
+
101
+ addPropertyInjection(
102
+ target: any,
103
+ propertyKey: string,
104
+ injection: PropertyInjectionMetadata,
105
+ ): void {
106
+ if (!this.propertyInjections.has(target)) {
107
+ this.propertyInjections.set(target, []);
108
+ }
109
+
110
+ const injections = this.propertyInjections.get(target)!;
111
+ injections.push(injection);
112
+ }
113
+
114
+ getPropertyInjections(target: any): PropertyInjectionMetadata[] {
115
+ // Get injections for this specific class
116
+ let injections = this.propertyInjections.get(target) || [];
117
+
118
+ // Also get injections from parent classes
119
+ let parent = Object.getPrototypeOf(target);
120
+ while (parent && parent !== Object && parent !== Function.prototype) {
121
+ const parentInjections = this.propertyInjections.get(parent) || [];
122
+ injections = [...injections, ...parentInjections];
123
+ parent = Object.getPrototypeOf(parent);
124
+ }
125
+
126
+ return injections;
127
+ }
128
128
  }
@@ -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
  );
41
50
 
51
+ console.log(`šŸŽÆ Application metadata:`, appMetadata);
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
 
@@ -56,37 +67,41 @@ export class FragmentWebApplication {
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(
71
+ `\n✨ Fragment application running on http://${host}:${port}`,
72
+ );
73
+ console.log("========================================\n");
60
74
  });
61
75
  }
62
76
 
63
- /**
64
- * Detects environment and scans appropriate files
65
- */
66
77
  private async scanComponents(): Promise<void> {
67
78
  const cwd = process.cwd();
68
79
  const distExists = fs.existsSync(path.join(cwd, "dist"));
69
80
  const srcExists = fs.existsSync(path.join(cwd, "src"));
70
81
 
82
+ console.log(`šŸ“ Current working directory: ${cwd}`);
83
+ console.log(`šŸ“ dist/ exists: ${distExists}`);
84
+ console.log(`šŸ“ src/ exists: ${srcExists}`);
85
+
71
86
  // Determine if we're running TypeScript directly (dev) or compiled JS (prod)
72
87
  const isDevMode = this.isRunningTypeScript();
88
+ console.log(
89
+ `šŸ’» Running in ${isDevMode ? "development" : "production"} mode`,
90
+ );
73
91
 
74
92
  if (isDevMode && srcExists) {
75
93
  // Development mode: scan TypeScript source files
76
- console.log(" šŸ“ Development mode: scanning TypeScript files");
94
+ console.log(" šŸ“ Development mode: scanning TypeScript files");
77
95
  await ComponentScanner.scanSource();
78
96
  } else if (distExists) {
79
97
  // Production mode: scan compiled JavaScript files
80
- console.log(" šŸ“¦ Production mode: scanning compiled files");
98
+ console.log(" šŸ“¦ Production mode: scanning compiled files");
81
99
  await ComponentScanner.scan();
82
100
  } else {
83
- console.warn(" āš ļø Warning: No src/ or dist/ directory found");
101
+ console.warn(" āš ļø Warning: No src/ or dist/ directory found");
84
102
  }
85
103
  }
86
104
 
87
- /**
88
- * Detects if we're running TypeScript directly (ts-node or similar)
89
- */
90
105
  private isRunningTypeScript(): boolean {
91
106
  // Check if ts-node is in require.extensions
92
107
  if (require.extensions[".ts"]) {
@@ -120,28 +135,64 @@ export class FragmentWebApplication {
120
135
  }
121
136
 
122
137
  private discoverAndRegisterComponents(): void {
123
- // First, register all components with the container
124
138
  const classes = this.metadataStorage.getAllClasses();
125
139
  console.log(`\nšŸ“¦ Discovered ${classes.length} component(s)`);
126
140
 
127
- // First pass: register all targets
128
- classes.forEach((metadata) => {
129
- if (!this.container.has(metadata.target)) {
130
- this.container.register(metadata.target);
131
- }
141
+ // Group by type for display
142
+ const grouped = classes.reduce(
143
+ (acc, cls) => {
144
+ if (!acc[cls.type]) acc[cls.type] = [];
145
+ acc[cls.type].push(cls);
146
+ return acc;
147
+ },
148
+ {} as Record<string, any[]>,
149
+ );
150
+
151
+ Object.entries(grouped).forEach(([type, items]) => {
152
+ const icon = this.getTypeIcon(type);
153
+ console.log(` ${icon} ${items.length} ${type}(s)`);
154
+
155
+ items.forEach((item) => {
156
+ console.log(
157
+ ` • ${item.target.name}${item.path ? ` (${item.path})` : ""}`,
158
+ );
159
+ });
132
160
  });
133
161
 
134
- // Second pass: create and inject dependencies
162
+ let registered = 0;
163
+ let skipped = 0;
164
+
135
165
  classes.forEach((metadata) => {
136
166
  if (this.shouldRegister(metadata.target)) {
137
- this.container.resolve(metadata.target);
138
- console.log(` āœ“ Initialized: ${metadata.target.name}`);
167
+ // CRITICAL: Register with container
168
+ if (!this.container.has(metadata.target)) {
169
+ this.container.register(metadata.target);
170
+ registered++;
171
+ console.log(` āœ“ Registered: ${metadata.target.name}`);
172
+ } else {
173
+ console.log(` āœ“ Already registered: ${metadata.target.name}`);
174
+ }
139
175
  } else {
176
+ skipped++;
140
177
  console.log(
141
178
  ` ⊘ Skipped: ${metadata.target.name} (conditional check failed)`,
142
179
  );
143
180
  }
144
181
  });
182
+
183
+ console.log(`\nāœ“ Registered ${registered} component(s)`);
184
+ if (skipped > 0) {
185
+ console.log(`⊘ Skipped ${skipped} component(s) (conditions not met)`);
186
+ }
187
+
188
+ // Pre-resolve all singleton components to ensure dependencies are injected
189
+ console.log("\nšŸ”§ Initializing components");
190
+ classes.forEach((metadata) => {
191
+ if (this.shouldRegister(metadata.target)) {
192
+ const instance = this.container.resolve(metadata.target);
193
+ console.log(` āœ“ Initialized: ${metadata.target.name}`);
194
+ }
195
+ });
145
196
  }
146
197
 
147
198
  private getTypeIcon(type: string): string {
@@ -160,7 +211,11 @@ export class FragmentWebApplication {
160
211
  METADATA_KEYS.CONDITIONAL_ON_CLASS,
161
212
  target,
162
213
  );
214
+
163
215
  if (conditionalClass && !this.isClassAvailable(conditionalClass)) {
216
+ console.log(
217
+ ` 🚫 Conditional check failed for ${target.name}: Class not available`,
218
+ );
164
219
  return false;
165
220
  }
166
221
 
@@ -168,7 +223,11 @@ export class FragmentWebApplication {
168
223
  METADATA_KEYS.CONDITIONAL_ON_MISSING_BEAN,
169
224
  target,
170
225
  );
226
+
171
227
  if (conditionalMissingBean && this.container.has(conditionalMissingBean)) {
228
+ console.log(
229
+ ` 🚫 Conditional check failed for ${target.name}: Bean already exists`,
230
+ );
172
231
  return false;
173
232
  }
174
233
 
@@ -176,15 +235,21 @@ export class FragmentWebApplication {
176
235
  METADATA_KEYS.CONDITIONAL_ON_PROPERTY,
177
236
  target,
178
237
  );
238
+
179
239
  if (conditionalProperty) {
180
240
  const value = process.env[conditionalProperty.key];
181
- if (
182
- conditionalProperty.expectedValue !== undefined &&
183
- value !== conditionalProperty.expectedValue
184
- ) {
185
- return false;
186
- }
187
- if (!value) {
241
+
242
+ if (conditionalProperty.expectedValue !== undefined) {
243
+ if (value !== conditionalProperty.expectedValue) {
244
+ console.log(
245
+ ` 🚫 Conditional check failed for ${target.name}: Expected ${conditionalProperty.expectedValue}, got ${value}`,
246
+ );
247
+ return false;
248
+ }
249
+ } else if (!value) {
250
+ console.log(
251
+ ` 🚫 Conditional check failed for ${target.name}: Property not set`,
252
+ );
188
253
  return false;
189
254
  }
190
255
  }
@@ -206,23 +271,29 @@ export class FragmentWebApplication {
206
271
  .filter((c) => c.type === "controller");
207
272
 
208
273
  if (controllers.length === 0) {
209
- console.log("šŸ›£ļø No routes to register\n");
274
+ console.log("\nšŸ›£ļø No routes to register");
210
275
  return;
211
276
  }
212
277
 
213
278
  let totalRoutes = 0;
214
- console.log(`šŸ›£ļø Registering routes...\n`);
279
+ console.log(`\nšŸ›£ļø Registering routes...`);
215
280
 
216
281
  controllers.forEach((controllerMetadata) => {
217
- const controller = this.container.resolve(controllerMetadata.target);
218
- const basePath = controllerMetadata.path || "";
219
- const methods = this.metadataStorage
220
- .getAllMethods()
221
- .filter((m) => m.target === controllerMetadata.target);
282
+ try {
283
+ console.log(`\nšŸ“ Controller: ${controllerMetadata.target.name}`);
284
+ console.log(` Base path: ${controllerMetadata.path || "/"}`);
285
+
286
+ const controller = this.container.resolve(controllerMetadata.target);
287
+ const basePath = controllerMetadata.path || "";
222
288
 
223
- if (methods.length > 0) {
224
- const controllerName = controllerMetadata.target.name;
225
- console.log(` šŸ“ ${controllerName}`);
289
+ const methods = this.metadataStorage
290
+ .getAllMethods()
291
+ .filter((m) => m.target === controllerMetadata.target);
292
+
293
+ if (methods.length === 0) {
294
+ console.log(` āš ļø No routes defined for this controller`);
295
+ return;
296
+ }
226
297
 
227
298
  methods.forEach((methodMetadata) => {
228
299
  const fullPath = this.normalizePath(basePath + methodMetadata.path);
@@ -231,8 +302,9 @@ export class FragmentWebApplication {
231
302
  const methodIcon = this.getMethodIcon(httpMethod);
232
303
 
233
304
  console.log(
234
- ` ${methodIcon} ${methodColor}${httpMethod.toUpperCase().padEnd(7)}\x1b[0m ${fullPath}`,
305
+ ` ${methodIcon} ${methodColor}${httpMethod.toUpperCase().padEnd(7)}\x1b[0m ${fullPath}`,
235
306
  );
307
+
236
308
  totalRoutes++;
237
309
 
238
310
  (this.app as any)[httpMethod](
@@ -244,23 +316,35 @@ export class FragmentWebApplication {
244
316
  req,
245
317
  res,
246
318
  );
319
+
320
+ console.log(
321
+ `\nšŸ” Handling ${httpMethod.toUpperCase()} ${fullPath}`,
322
+ );
323
+ // console.log(` Parameters:`, args);
324
+
247
325
  const result = await (controller as any)[
248
326
  methodMetadata.propertyKey
249
327
  ](...args);
328
+
250
329
  if (!res.headersSent) {
251
330
  res.json(result);
252
331
  }
253
332
  } catch (error) {
333
+ console.error(`āŒ Error handling route ${fullPath}:`, error);
254
334
  next(error);
255
335
  }
256
336
  },
257
337
  );
258
338
  });
259
- console.log("");
339
+ } catch (error) {
340
+ console.error(
341
+ `āŒ Failed to register controller ${controllerMetadata.target.name}:`,
342
+ error,
343
+ );
260
344
  }
261
345
  });
262
346
 
263
- console.log(` āœ“ Registered ${totalRoutes} route(s)\n`);
347
+ console.log(`\nāœ“ Registered ${totalRoutes} route(s)`);
264
348
  }
265
349
 
266
350
  private getMethodColor(method: string): string {
@@ -290,7 +374,7 @@ export class FragmentWebApplication {
290
374
  req: Request,
291
375
  res: Response,
292
376
  ): any[] {
293
- const params = methodMetadata.paramMetadata.sort(
377
+ const params = [...methodMetadata.paramMetadata].sort(
294
378
  (a: any, b: any) => a.index - b.index,
295
379
  );
296
380
 
@@ -326,10 +410,11 @@ export class FragmentWebApplication {
326
410
  res: Response,
327
411
  next: NextFunction,
328
412
  ): void {
329
- console.error(err.stack);
413
+ console.error(`\nāŒ Global error handler:`, err);
330
414
  res.status(500).json({
331
415
  error: "Internal Server Error",
332
416
  message: err.message,
417
+ timestamp: new Date().toISOString(),
333
418
  });
334
419
  }
335
420