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.
- package/dist/cli/commands/init.command.js +1 -1
- package/dist/core/container/di-container.d.ts +7 -6
- package/dist/core/container/di-container.d.ts.map +1 -1
- package/dist/core/container/di-container.js +162 -129
- 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 +49 -19
- 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 +11 -0
- package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
- package/dist/core/metadata/metadata-storage.js +22 -0
- 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 +75 -38
- 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 +247 -181
- 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 +62 -22
- 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 +47 -0
- package/src/web/application.ts +107 -64
- package/tsconfig.json +4 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { METADATA_KEYS } from "../metadata/metadata-keys";
|
|
2
|
-
import {
|
|
2
|
+
import { MetadataStorage } from "../metadata/metadata-storage";
|
|
3
3
|
|
|
4
4
|
export class DIContainer {
|
|
5
5
|
private static instance: DIContainer;
|
|
@@ -8,6 +8,11 @@ export class DIContainer {
|
|
|
8
8
|
private factories: Map<any, () => any> = new Map();
|
|
9
9
|
private constructing: Set<any> = new Set();
|
|
10
10
|
private registered: Set<any> = new Set();
|
|
11
|
+
private metadataStorage: MetadataStorage;
|
|
12
|
+
|
|
13
|
+
private constructor() {
|
|
14
|
+
this.metadataStorage = MetadataStorage.getInstance();
|
|
15
|
+
}
|
|
11
16
|
|
|
12
17
|
static getInstance(): DIContainer {
|
|
13
18
|
if (!DIContainer.instance) {
|
|
@@ -18,19 +23,23 @@ export class DIContainer {
|
|
|
18
23
|
|
|
19
24
|
register(token: any, instance?: any, factory?: () => any): void {
|
|
20
25
|
this.registered.add(token);
|
|
26
|
+
console.log(
|
|
27
|
+
` ✓ Registered token: ${typeof token === "function" ? token.name : token}`,
|
|
28
|
+
);
|
|
21
29
|
|
|
22
30
|
if (instance) {
|
|
23
31
|
this.singletons.set(token, instance);
|
|
32
|
+
console.log(` ➤ Singleton instance created`);
|
|
24
33
|
} else if (factory) {
|
|
25
34
|
this.factories.set(token, factory);
|
|
35
|
+
console.log(` ➤ Factory registered`);
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
resolve<T>(token: any): T {
|
|
30
40
|
if (typeof token === "function" && !this.registered.has(token)) {
|
|
31
|
-
|
|
32
|
-
`
|
|
33
|
-
`Did you forget to add @Injectable(), @Service(), @Controller(), or @Repository()?`,
|
|
41
|
+
console.log(
|
|
42
|
+
` ℹ️ Token ${token.name} not explicitly registered, attempting to create instance`,
|
|
34
43
|
);
|
|
35
44
|
}
|
|
36
45
|
|
|
@@ -40,24 +49,35 @@ export class DIContainer {
|
|
|
40
49
|
);
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
// Check singletons first
|
|
43
53
|
if (this.singletons.has(token)) {
|
|
54
|
+
console.log(
|
|
55
|
+
` ✓ Resolved from singleton cache: ${typeof token === "function" ? token.name : token}`,
|
|
56
|
+
);
|
|
44
57
|
return this.singletons.get(token);
|
|
45
58
|
}
|
|
46
59
|
|
|
60
|
+
// Check factories
|
|
47
61
|
if (this.factories.has(token)) {
|
|
62
|
+
console.log(
|
|
63
|
+
` 🏭 Creating instance from factory: ${typeof token === "function" ? token.name : token}`,
|
|
64
|
+
);
|
|
48
65
|
const factory = this.factories.get(token)!;
|
|
49
66
|
const instance = factory();
|
|
67
|
+
|
|
50
68
|
const scope =
|
|
51
69
|
Reflect.getMetadata(METADATA_KEYS.SCOPE, token) || "singleton";
|
|
52
|
-
|
|
53
70
|
if (scope === "singleton") {
|
|
54
71
|
this.singletons.set(token, instance);
|
|
72
|
+
console.log(` ➤ Cached as singleton`);
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
return instance;
|
|
58
76
|
}
|
|
59
77
|
|
|
78
|
+
// Create new instance for class tokens
|
|
60
79
|
if (typeof token === "function") {
|
|
80
|
+
console.log(` 🔨 Creating new instance: ${token.name}`);
|
|
61
81
|
this.constructing.add(token);
|
|
62
82
|
|
|
63
83
|
try {
|
|
@@ -67,38 +87,53 @@ export class DIContainer {
|
|
|
67
87
|
|
|
68
88
|
if (scope === "singleton") {
|
|
69
89
|
this.singletons.set(token, instance);
|
|
90
|
+
console.log(` ➤ Registered as singleton`);
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
this.constructing.delete(token);
|
|
73
94
|
return instance;
|
|
74
95
|
} catch (error) {
|
|
75
96
|
this.constructing.delete(token);
|
|
97
|
+
console.error(
|
|
98
|
+
` ❌ Failed to create instance of ${token.name}: ${error}`,
|
|
99
|
+
);
|
|
76
100
|
throw error;
|
|
77
101
|
}
|
|
78
102
|
}
|
|
79
103
|
|
|
80
|
-
throw new Error(
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Cannot resolve dependency: ${typeof token === "function" ? token.name : token}`,
|
|
106
|
+
);
|
|
81
107
|
}
|
|
82
108
|
|
|
83
109
|
private createInstance(target: any): any {
|
|
84
|
-
|
|
110
|
+
const className = target.name;
|
|
111
|
+
console.log(` 🏗️ Constructing instance of ${className}`);
|
|
85
112
|
|
|
113
|
+
// Get constructor parameter types
|
|
86
114
|
const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
|
|
115
|
+
console.log(
|
|
116
|
+
` 📋 Constructor parameters for ${className}: ${paramTypes.length}`,
|
|
117
|
+
);
|
|
87
118
|
|
|
88
119
|
const params = paramTypes.map((type: any, index: number) => {
|
|
89
120
|
if (!type || type === Object) {
|
|
90
121
|
console.warn(
|
|
91
|
-
`
|
|
122
|
+
` ⚠️ Constructor param ${index} of ${className} has no type information`,
|
|
92
123
|
);
|
|
93
124
|
return undefined;
|
|
94
125
|
}
|
|
95
|
-
|
|
126
|
+
|
|
127
|
+
console.log(
|
|
128
|
+
` 🔌 Resolving constructor dependency [${index}]: ${type.name || type}`,
|
|
129
|
+
);
|
|
96
130
|
return this.resolve(type);
|
|
97
131
|
});
|
|
98
132
|
|
|
133
|
+
console.log(` 🚀 Instantiating ${className}`);
|
|
99
134
|
const instance = new target(...params);
|
|
100
135
|
|
|
101
|
-
//
|
|
136
|
+
// Inject properties
|
|
102
137
|
this.injectProperties(instance);
|
|
103
138
|
|
|
104
139
|
// Call @PostConstruct
|
|
@@ -106,12 +141,13 @@ export class DIContainer {
|
|
|
106
141
|
METADATA_KEYS.POST_CONSTRUCT,
|
|
107
142
|
target,
|
|
108
143
|
);
|
|
144
|
+
|
|
109
145
|
if (
|
|
110
146
|
postConstructMethod &&
|
|
111
147
|
typeof instance[postConstructMethod] === "function"
|
|
112
148
|
) {
|
|
113
149
|
console.log(
|
|
114
|
-
`
|
|
150
|
+
` 🎯 Calling @PostConstruct method: ${className}.${String(postConstructMethod)}()`,
|
|
115
151
|
);
|
|
116
152
|
instance[postConstructMethod]();
|
|
117
153
|
}
|
|
@@ -121,220 +157,204 @@ export class DIContainer {
|
|
|
121
157
|
|
|
122
158
|
private injectProperties(instance: any): void {
|
|
123
159
|
const constructor = instance.constructor;
|
|
124
|
-
|
|
160
|
+
const className = constructor.name;
|
|
125
161
|
|
|
126
|
-
|
|
127
|
-
// But primarily own enumerable + non-enumerable properties
|
|
128
|
-
let props: string[] = [];
|
|
129
|
-
let obj = instance;
|
|
162
|
+
console.log(` 🔍 Scanning for property injections on ${className}`);
|
|
130
163
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
obj = Object.getPrototypeOf(obj);
|
|
135
|
-
}
|
|
164
|
+
// Get all property injections from metadata storage
|
|
165
|
+
const propertyInjections =
|
|
166
|
+
this.metadataStorage.getPropertyInjections(constructor);
|
|
136
167
|
|
|
137
|
-
|
|
138
|
-
|
|
168
|
+
if (propertyInjections.length === 0) {
|
|
169
|
+
console.log(` ℹ️ No property injections registered for ${className}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
139
172
|
|
|
140
173
|
console.log(
|
|
141
|
-
|
|
174
|
+
` 📌 Found ${propertyInjections.length} property injection(s) for ${className}`,
|
|
142
175
|
);
|
|
143
176
|
|
|
144
|
-
for (const
|
|
145
|
-
|
|
177
|
+
for (const { propertyKey, injections } of propertyInjections) {
|
|
178
|
+
console.log(` ➤ Property: ${propertyKey}`);
|
|
146
179
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this.tryInjectByToken(instance, instance, prop, constructor.name);
|
|
150
|
-
this.tryInjectValue(instance, instance, prop, constructor.name);
|
|
151
|
-
this.tryInjectRepository(instance, instance, prop, constructor.name);
|
|
152
|
-
this.tryInjectLazy(instance, instance, prop, constructor.name);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
180
|
+
for (const injection of injections) {
|
|
181
|
+
console.log(` • Injection type: ${injection.type}`);
|
|
155
182
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Check all metadata keys that might be attached to properties
|
|
161
|
-
const propertyKeys = [
|
|
162
|
-
...(Reflect.getMetadataKeys(constructor.prototype) || []),
|
|
163
|
-
...(Reflect.getMetadataKeys(instance) || []),
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
// Filter for property-related metadata
|
|
167
|
-
for (const key of propertyKeys) {
|
|
168
|
-
if (key.includes("fragment:")) {
|
|
169
|
-
// This is a Fragment metadata key, need to check if it's attached to a property
|
|
170
|
-
const metadataValue = Reflect.getMetadata(key, constructor.prototype);
|
|
171
|
-
if (metadataValue && typeof metadataValue === "object") {
|
|
172
|
-
// Could be a property metadata
|
|
173
|
-
for (const prop of Object.keys(metadataValue)) {
|
|
174
|
-
if (prop !== "constructor") {
|
|
175
|
-
this.tryInjectAutowired(
|
|
183
|
+
try {
|
|
184
|
+
switch (injection.type) {
|
|
185
|
+
case "autowired":
|
|
186
|
+
this.injectAutowiredProperty(
|
|
176
187
|
instance,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
188
|
+
propertyKey,
|
|
189
|
+
injection.metadata?.type,
|
|
190
|
+
className,
|
|
180
191
|
);
|
|
181
|
-
|
|
192
|
+
break;
|
|
193
|
+
case "inject":
|
|
194
|
+
this.injectTokenProperty(
|
|
182
195
|
instance,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
propertyKey,
|
|
197
|
+
injection.metadata?.token,
|
|
198
|
+
className,
|
|
186
199
|
);
|
|
187
|
-
|
|
200
|
+
break;
|
|
201
|
+
case "repository":
|
|
202
|
+
this.injectRepositoryProperty(
|
|
188
203
|
instance,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
204
|
+
propertyKey,
|
|
205
|
+
injection.metadata?.entity,
|
|
206
|
+
className,
|
|
192
207
|
);
|
|
193
|
-
|
|
208
|
+
break;
|
|
209
|
+
case "value":
|
|
210
|
+
this.injectValueProperty(
|
|
194
211
|
instance,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
propertyKey,
|
|
213
|
+
injection.metadata?.expression,
|
|
214
|
+
className,
|
|
198
215
|
);
|
|
199
|
-
|
|
216
|
+
break;
|
|
217
|
+
case "lazy":
|
|
218
|
+
this.injectLazyProperty(instance, propertyKey, className);
|
|
219
|
+
break;
|
|
200
220
|
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
const isOptional = Reflect.getMetadata(
|
|
223
|
+
METADATA_KEYS.OPTIONAL,
|
|
224
|
+
constructor.prototype,
|
|
225
|
+
propertyKey,
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (!isOptional) {
|
|
229
|
+
console.error(
|
|
230
|
+
` ❌ Failed to inject ${propertyKey} in ${className}: ${error}`,
|
|
231
|
+
);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.warn(
|
|
236
|
+
` ⚠️ Optional dependency ${propertyKey} not available for ${className}`,
|
|
237
|
+
);
|
|
238
|
+
instance[propertyKey] = undefined;
|
|
201
239
|
}
|
|
202
240
|
}
|
|
203
241
|
}
|
|
204
242
|
}
|
|
205
243
|
|
|
206
|
-
private
|
|
244
|
+
private injectAutowiredProperty(
|
|
207
245
|
instance: any,
|
|
208
|
-
|
|
209
|
-
|
|
246
|
+
propertyKey: string,
|
|
247
|
+
type: any,
|
|
210
248
|
className: string,
|
|
211
249
|
): void {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (!isOptional) {
|
|
250
|
+
if (!type) {
|
|
251
|
+
const designType = Reflect.getMetadata(
|
|
252
|
+
"design:type",
|
|
253
|
+
instance,
|
|
254
|
+
propertyKey,
|
|
255
|
+
);
|
|
256
|
+
if (designType && designType !== Object) {
|
|
257
|
+
type = designType;
|
|
258
|
+
console.warn(
|
|
259
|
+
` ⚠️ Using design:type metadata for ${className}.${propertyKey} as fallback`,
|
|
260
|
+
);
|
|
261
|
+
} else {
|
|
225
262
|
throw new Error(
|
|
226
|
-
`
|
|
263
|
+
`Type information not available for ${className}.${propertyKey}`,
|
|
227
264
|
);
|
|
228
265
|
}
|
|
229
|
-
console.warn(
|
|
230
|
-
` ⚠️ Optional dependency ${type.name} not available for ${className}.${prop}`,
|
|
231
|
-
);
|
|
232
|
-
instance[prop] = undefined;
|
|
233
266
|
}
|
|
267
|
+
|
|
268
|
+
console.log(` 💉 @Autowired: ${className}.${propertyKey} -> ${type.name}`);
|
|
269
|
+
instance[propertyKey] = this.resolve(type);
|
|
270
|
+
console.log(` ✅ Injected ${type.name} into ${className}.${propertyKey}`);
|
|
234
271
|
}
|
|
235
272
|
|
|
236
|
-
private
|
|
273
|
+
private injectTokenProperty(
|
|
237
274
|
instance: any,
|
|
238
|
-
|
|
239
|
-
|
|
275
|
+
propertyKey: string,
|
|
276
|
+
token: any,
|
|
240
277
|
className: string,
|
|
241
278
|
): void {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const tokenName = typeof token === "string" ? token : token.name;
|
|
247
|
-
console.log(` 💉 @Inject found: ${className}.${prop} -> ${tokenName}`);
|
|
248
|
-
|
|
249
|
-
const isOptional = Reflect.getMetadata(METADATA_KEYS.OPTIONAL, proto, prop);
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
instance[prop] = this.resolve(token);
|
|
253
|
-
console.log(` ✅ Injected ${tokenName} into ${className}.${prop}`);
|
|
254
|
-
} catch (error) {
|
|
255
|
-
if (!isOptional) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`Failed to inject ${tokenName} into ${className}.${prop}: ${error}`,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
console.warn(
|
|
261
|
-
` ⚠️ Optional dependency ${tokenName} not available for ${className}.${prop}`,
|
|
279
|
+
if (!token) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Token not provided for @Inject on ${className}.${propertyKey}`,
|
|
262
282
|
);
|
|
263
|
-
instance[prop] = undefined;
|
|
264
283
|
}
|
|
284
|
+
|
|
285
|
+
const tokenName = typeof token === "string" ? token : token.name;
|
|
286
|
+
console.log(` 💉 @Inject: ${className}.${propertyKey} -> ${tokenName}`);
|
|
287
|
+
instance[propertyKey] = this.resolve(token);
|
|
288
|
+
console.log(` ✅ Injected ${tokenName} into ${className}.${propertyKey}`);
|
|
265
289
|
}
|
|
266
290
|
|
|
267
|
-
private
|
|
291
|
+
private injectRepositoryProperty(
|
|
268
292
|
instance: any,
|
|
269
|
-
|
|
270
|
-
|
|
293
|
+
propertyKey: string,
|
|
294
|
+
entity: any,
|
|
271
295
|
className: string,
|
|
272
296
|
): void {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
297
|
+
if (!entity) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`Entity not provided for @InjectRepository on ${className}.${propertyKey}`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
276
302
|
|
|
277
|
-
console.log(
|
|
278
|
-
|
|
279
|
-
|
|
303
|
+
console.log(
|
|
304
|
+
` 💾 @InjectRepository: ${className}.${propertyKey} -> Repository<${entity.name}>`,
|
|
305
|
+
);
|
|
306
|
+
instance[propertyKey] = this.resolveRepository(entity);
|
|
307
|
+
console.log(
|
|
308
|
+
` ✅ Injected Repository<${entity.name}> into ${className}.${propertyKey}`,
|
|
309
|
+
);
|
|
280
310
|
}
|
|
281
311
|
|
|
282
|
-
private
|
|
312
|
+
private injectValueProperty(
|
|
283
313
|
instance: any,
|
|
284
|
-
|
|
285
|
-
|
|
314
|
+
propertyKey: string,
|
|
315
|
+
expression: string,
|
|
286
316
|
className: string,
|
|
287
317
|
): void {
|
|
288
|
-
|
|
289
|
-
METADATA_KEYS.INJECT_REPOSITORY,
|
|
290
|
-
proto,
|
|
291
|
-
prop,
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
if (!entity) return;
|
|
295
|
-
|
|
296
|
-
console.log(
|
|
297
|
-
` 💾 @InjectRepository found: ${className}.${prop} -> Repository<${entity.name}>`,
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
try {
|
|
301
|
-
instance[prop] = this.resolveRepository(entity);
|
|
302
|
-
console.log(
|
|
303
|
-
` ✅ Injected Repository<${entity.name}> into ${className}.${prop}`,
|
|
304
|
-
);
|
|
305
|
-
} catch (error) {
|
|
318
|
+
if (!expression) {
|
|
306
319
|
throw new Error(
|
|
307
|
-
`
|
|
320
|
+
`Expression not provided for @Value on ${className}.${propertyKey}`,
|
|
308
321
|
);
|
|
309
322
|
}
|
|
323
|
+
|
|
324
|
+
console.log(` 🔧 @Value: ${className}.${propertyKey} = ${expression}`);
|
|
325
|
+
instance[propertyKey] = this.resolveValue(expression);
|
|
326
|
+
console.log(` ✅ Set value for ${className}.${propertyKey}`);
|
|
310
327
|
}
|
|
311
328
|
|
|
312
|
-
private
|
|
329
|
+
private injectLazyProperty(
|
|
313
330
|
instance: any,
|
|
314
|
-
|
|
315
|
-
prop: string,
|
|
331
|
+
propertyKey: string,
|
|
316
332
|
className: string,
|
|
317
333
|
): void {
|
|
318
|
-
|
|
334
|
+
// Get the type for later resolution
|
|
335
|
+
const type = Reflect.getMetadata("design:type", instance, propertyKey);
|
|
336
|
+
if (!type) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`Type information not available for @Lazy ${className}.${propertyKey}`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
319
341
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const type = Reflect.getMetadata("design:type", proto, prop);
|
|
323
|
-
console.log(
|
|
324
|
-
` ⏳ @Lazy found: ${className}.${prop} (${type?.name || "unknown"})`,
|
|
325
|
-
);
|
|
342
|
+
console.log(` ⏳ @Lazy: ${className}.${propertyKey} (${type.name})`);
|
|
326
343
|
|
|
327
344
|
let cached: any = null;
|
|
328
345
|
let resolved = false;
|
|
329
346
|
|
|
330
|
-
Object.defineProperty(instance,
|
|
347
|
+
Object.defineProperty(instance, propertyKey, {
|
|
331
348
|
get: () => {
|
|
332
349
|
if (!resolved) {
|
|
333
350
|
console.log(
|
|
334
|
-
`
|
|
351
|
+
` 🔓 Lazy-loading ${type.name} for ${className}.${propertyKey}`,
|
|
335
352
|
);
|
|
336
353
|
cached = this.resolve(type);
|
|
337
354
|
resolved = true;
|
|
355
|
+
console.log(
|
|
356
|
+
` ✅ Lazy-loaded ${type.name} for ${className}.${propertyKey}`,
|
|
357
|
+
);
|
|
338
358
|
}
|
|
339
359
|
return cached;
|
|
340
360
|
},
|
|
@@ -344,45 +364,79 @@ export class DIContainer {
|
|
|
344
364
|
}
|
|
345
365
|
|
|
346
366
|
private resolveValue(expression: string): any {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
367
|
+
// Standardize expression format (remove ${} if present)
|
|
368
|
+
let key = expression.replace(/\$\{|\}/g, "");
|
|
369
|
+
let defaultValue: any = undefined;
|
|
370
|
+
|
|
371
|
+
// Handle default values (format: KEY:default)
|
|
372
|
+
const defaultMatch = key.match(/^(.*?):(.*)$/);
|
|
373
|
+
if (defaultMatch) {
|
|
374
|
+
key = defaultMatch[1].trim();
|
|
375
|
+
defaultValue = defaultMatch[2].trim();
|
|
376
|
+
|
|
377
|
+
// Try to parse default value as JSON or number if possible
|
|
378
|
+
try {
|
|
379
|
+
defaultValue = JSON.parse(defaultValue);
|
|
380
|
+
} catch {
|
|
381
|
+
// Try number conversion
|
|
382
|
+
const numValue = parseFloat(defaultValue);
|
|
383
|
+
if (!isNaN(numValue) && defaultValue.trim() === numValue.toString()) {
|
|
384
|
+
defaultValue = numValue;
|
|
385
|
+
}
|
|
386
|
+
// Try boolean conversion
|
|
387
|
+
else if (defaultValue.toLowerCase() === "true") {
|
|
388
|
+
defaultValue = true;
|
|
389
|
+
} else if (defaultValue.toLowerCase() === "false") {
|
|
390
|
+
defaultValue = false;
|
|
358
391
|
}
|
|
359
|
-
console.warn(` ⚠️ Environment variable "${key}" not defined`);
|
|
360
|
-
return "";
|
|
361
392
|
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
console.log(
|
|
396
|
+
` 🔍 Resolving config value: ${key}${defaultValue !== undefined ? ` (default: ${defaultValue})` : ""}`,
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
// Check environment variables first
|
|
400
|
+
const envValue = process.env[key.toUpperCase()] || process.env[key];
|
|
401
|
+
if (envValue !== undefined) {
|
|
402
|
+
console.log(` 🌍 Found in environment: ${envValue}`);
|
|
362
403
|
|
|
363
|
-
if
|
|
404
|
+
// Try to parse as JSON if it looks like an object/array
|
|
405
|
+
if (envValue.startsWith("{") || envValue.startsWith("[")) {
|
|
364
406
|
try {
|
|
365
|
-
return JSON.parse(
|
|
407
|
+
return JSON.parse(envValue);
|
|
366
408
|
} catch {
|
|
367
|
-
return
|
|
409
|
+
return envValue;
|
|
368
410
|
}
|
|
369
411
|
}
|
|
370
412
|
|
|
371
|
-
|
|
372
|
-
|
|
413
|
+
// Try to parse as number
|
|
414
|
+
const numValue = parseFloat(envValue);
|
|
415
|
+
if (!isNaN(numValue) && envValue.trim() === numValue.toString()) {
|
|
416
|
+
return numValue;
|
|
373
417
|
}
|
|
374
418
|
|
|
375
|
-
|
|
376
|
-
if (
|
|
419
|
+
// Try to parse as boolean
|
|
420
|
+
if (envValue.toLowerCase() === "true") return true;
|
|
421
|
+
if (envValue.toLowerCase() === "false") return false;
|
|
422
|
+
|
|
423
|
+
return envValue;
|
|
424
|
+
}
|
|
377
425
|
|
|
378
|
-
|
|
426
|
+
// Return default value if available
|
|
427
|
+
if (defaultValue !== undefined) {
|
|
428
|
+
console.log(` 📦 Using default value: ${defaultValue}`);
|
|
429
|
+
return defaultValue;
|
|
379
430
|
}
|
|
380
431
|
|
|
381
|
-
|
|
432
|
+
console.warn(` ⚠️ Config value not found: ${key}`);
|
|
433
|
+
return undefined;
|
|
382
434
|
}
|
|
383
435
|
|
|
384
436
|
private resolveRepository(entity: any): any {
|
|
385
437
|
try {
|
|
438
|
+
console.log(` 🔍 Resolving repository for entity: ${entity.name}`);
|
|
439
|
+
|
|
386
440
|
const { TypeORMModule } = require("../../typeorm/typeorm-module");
|
|
387
441
|
const dataSource = TypeORMModule.getDataSource();
|
|
388
442
|
|
|
@@ -390,8 +444,14 @@ export class DIContainer {
|
|
|
390
444
|
throw new Error("TypeORM DataSource not initialized");
|
|
391
445
|
}
|
|
392
446
|
|
|
447
|
+
console.log(
|
|
448
|
+
` ✅ Found DataSource, creating repository for ${entity.name}`,
|
|
449
|
+
);
|
|
393
450
|
return dataSource.getRepository(entity);
|
|
394
451
|
} catch (error) {
|
|
452
|
+
console.error(
|
|
453
|
+
` ❌ Failed to resolve repository for ${entity.name}: ${(error as Error).message}`,
|
|
454
|
+
);
|
|
395
455
|
throw new Error(
|
|
396
456
|
`Failed to resolve repository for ${entity.name}: ${(error as Error)?.message}`,
|
|
397
457
|
);
|
|
@@ -399,7 +459,11 @@ export class DIContainer {
|
|
|
399
459
|
}
|
|
400
460
|
|
|
401
461
|
has(token: any): boolean {
|
|
402
|
-
return
|
|
462
|
+
return (
|
|
463
|
+
this.registered.has(token) ||
|
|
464
|
+
this.singletons.has(token) ||
|
|
465
|
+
this.factories.has(token)
|
|
466
|
+
);
|
|
403
467
|
}
|
|
404
468
|
|
|
405
469
|
getAllInstances(): any[] {
|
|
@@ -407,6 +471,7 @@ export class DIContainer {
|
|
|
407
471
|
}
|
|
408
472
|
|
|
409
473
|
clear(): void {
|
|
474
|
+
console.log("🧹 Clearing DI Container");
|
|
410
475
|
this.singletons.clear();
|
|
411
476
|
this.transients.clear();
|
|
412
477
|
this.factories.clear();
|
|
@@ -416,5 +481,6 @@ export class DIContainer {
|
|
|
416
481
|
|
|
417
482
|
reset(): void {
|
|
418
483
|
this.clear();
|
|
484
|
+
console.log("🔁 DI Container reset complete");
|
|
419
485
|
}
|
|
420
486
|
}
|
|
@@ -11,6 +11,7 @@ export function FragmentApplication(
|
|
|
11
11
|
options: ApplicationOptions = {},
|
|
12
12
|
): ClassDecorator {
|
|
13
13
|
return (target: any) => {
|
|
14
|
+
console.log(`🚀 Registering Fragment application: ${target.name}`);
|
|
14
15
|
Reflect.defineMetadata(
|
|
15
16
|
METADATA_KEYS.APPLICATION,
|
|
16
17
|
{
|
|
@@ -18,7 +19,7 @@ export function FragmentApplication(
|
|
|
18
19
|
port: options.port || 3000,
|
|
19
20
|
host: options.host || "0.0.0.0",
|
|
20
21
|
configPath: options.configPath || "fragment.json",
|
|
21
|
-
autoScan: options.autoScan
|
|
22
|
+
autoScan: options.autoScan !== false, // default to true
|
|
22
23
|
},
|
|
23
24
|
target,
|
|
24
25
|
);
|