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.
- package/dist/cli/commands/init.command.js +1 -1
- package/dist/core/container/di-container.d.ts +7 -7
- package/dist/core/container/di-container.d.ts.map +1 -1
- package/dist/core/container/di-container.js +126 -138
- package/dist/core/container/di-container.js.map +1 -1
- package/dist/core/decorators/application.decorator.d.ts.map +1 -1
- package/dist/core/decorators/application.decorator.js +2 -1
- package/dist/core/decorators/application.decorator.js.map +1 -1
- package/dist/core/decorators/auto-configuration.decorator.d.ts.map +1 -1
- package/dist/core/decorators/auto-configuration.decorator.js +4 -3
- package/dist/core/decorators/auto-configuration.decorator.js.map +1 -1
- package/dist/core/decorators/controller.decorator.d.ts.map +1 -1
- package/dist/core/decorators/controller.decorator.js +1 -0
- package/dist/core/decorators/controller.decorator.js.map +1 -1
- package/dist/core/decorators/http.decorators.d.ts.map +1 -1
- package/dist/core/decorators/http.decorators.js +9 -0
- package/dist/core/decorators/http.decorators.js.map +1 -1
- package/dist/core/decorators/injectable.decorator.d.ts.map +1 -1
- package/dist/core/decorators/injectable.decorator.js +1 -0
- package/dist/core/decorators/injectable.decorator.js.map +1 -1
- package/dist/core/decorators/injection.decorators.d.ts.map +1 -1
- package/dist/core/decorators/injection.decorators.js +50 -21
- package/dist/core/decorators/injection.decorators.js.map +1 -1
- package/dist/core/decorators/repository.decorator.d.ts.map +1 -1
- package/dist/core/decorators/repository.decorator.js +1 -0
- package/dist/core/decorators/repository.decorator.js.map +1 -1
- package/dist/core/decorators/service.decorator.d.ts.map +1 -1
- package/dist/core/decorators/service.decorator.js +1 -0
- package/dist/core/decorators/service.decorator.js.map +1 -1
- package/dist/core/metadata/metadata-storage.d.ts +8 -13
- package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
- package/dist/core/metadata/metadata-storage.js +19 -18
- package/dist/core/metadata/metadata-storage.js.map +1 -1
- package/dist/web/application.d.ts +0 -6
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +93 -41
- package/dist/web/application.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/init.command.ts +1 -1
- package/src/core/container/di-container.ts +188 -203
- package/src/core/decorators/application.decorator.ts +2 -1
- package/src/core/decorators/auto-configuration.decorator.ts +9 -8
- package/src/core/decorators/controller.decorator.ts +2 -1
- package/src/core/decorators/http.decorators.ts +17 -0
- package/src/core/decorators/injectable.decorator.ts +1 -0
- package/src/core/decorators/injection.decorators.ts +64 -24
- package/src/core/decorators/repository.decorator.ts +1 -0
- package/src/core/decorators/service.decorator.ts +1 -0
- package/src/core/metadata/metadata-storage.ts +37 -37
- 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
|
-
//
|
|
102
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/web/application.ts
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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("
|
|
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("
|
|
98
|
+
console.log(" š¦ Production mode: scanning compiled files");
|
|
81
99
|
await ComponentScanner.scan();
|
|
82
100
|
} else {
|
|
83
|
-
console.warn("
|
|
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
|
-
//
|
|
128
|
-
classes.
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
162
|
+
let registered = 0;
|
|
163
|
+
let skipped = 0;
|
|
164
|
+
|
|
135
165
|
classes.forEach((metadata) => {
|
|
136
166
|
if (this.shouldRegister(metadata.target)) {
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
value !== conditionalProperty.expectedValue
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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("š£ļø
|
|
274
|
+
console.log("\nš£ļø No routes to register");
|
|
210
275
|
return;
|
|
211
276
|
}
|
|
212
277
|
|
|
213
278
|
let totalRoutes = 0;
|
|
214
|
-
console.log(
|
|
279
|
+
console.log(`\nš£ļø Registering routes...`);
|
|
215
280
|
|
|
216
281
|
controllers.forEach((controllerMetadata) => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|