fragment-ts 1.0.30 ā 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.map +1 -1
- package/dist/core/container/di-container.js +62 -106
- package/dist/core/container/di-container.js.map +1 -1
- package/dist/core/decorators/injection.decorators.d.ts.map +1 -1
- package/dist/core/decorators/injection.decorators.js +5 -0
- package/dist/core/decorators/injection.decorators.js.map +1 -1
- package/dist/core/metadata/metadata-storage.d.ts +2 -4
- package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
- package/dist/core/metadata/metadata-storage.js +13 -15
- package/dist/core/metadata/metadata-storage.js.map +1 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +5 -5
- 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 +95 -177
- package/src/core/decorators/injection.decorators.ts +5 -0
- package/src/core/metadata/metadata-storage.ts +17 -27
- package/src/web/application.ts +66 -47
package/src/web/application.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class FragmentWebApplication {
|
|
|
34
34
|
|
|
35
35
|
async bootstrap(appClass: any): Promise<void> {
|
|
36
36
|
console.log("\nš Bootstrapping application");
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
try {
|
|
39
39
|
await TypeORMModule.initialize();
|
|
40
40
|
console.log("ā
TypeORM initialized successfully");
|
|
@@ -47,7 +47,7 @@ export class FragmentWebApplication {
|
|
|
47
47
|
METADATA_KEYS.APPLICATION,
|
|
48
48
|
appClass,
|
|
49
49
|
);
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
console.log(`šÆ Application metadata:`, appMetadata);
|
|
52
52
|
|
|
53
53
|
// CRITICAL: Scan and load all component files first
|
|
@@ -63,11 +63,13 @@ export class FragmentWebApplication {
|
|
|
63
63
|
|
|
64
64
|
const port = appMetadata?.port || 3000;
|
|
65
65
|
const host = appMetadata?.host || "0.0.0.0";
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
this.app.use(this.errorHandler.bind(this));
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
this.app.listen(port, host, () => {
|
|
70
|
-
console.log(
|
|
70
|
+
console.log(
|
|
71
|
+
`\n⨠Fragment application running on http://${host}:${port}`,
|
|
72
|
+
);
|
|
71
73
|
console.log("========================================\n");
|
|
72
74
|
});
|
|
73
75
|
}
|
|
@@ -76,15 +78,17 @@ export class FragmentWebApplication {
|
|
|
76
78
|
const cwd = process.cwd();
|
|
77
79
|
const distExists = fs.existsSync(path.join(cwd, "dist"));
|
|
78
80
|
const srcExists = fs.existsSync(path.join(cwd, "src"));
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
console.log(`š Current working directory: ${cwd}`);
|
|
81
83
|
console.log(`š dist/ exists: ${distExists}`);
|
|
82
84
|
console.log(`š src/ exists: ${srcExists}`);
|
|
83
85
|
|
|
84
86
|
// Determine if we're running TypeScript directly (dev) or compiled JS (prod)
|
|
85
87
|
const isDevMode = this.isRunningTypeScript();
|
|
86
|
-
console.log(
|
|
87
|
-
|
|
88
|
+
console.log(
|
|
89
|
+
`š» Running in ${isDevMode ? "development" : "production"} mode`,
|
|
90
|
+
);
|
|
91
|
+
|
|
88
92
|
if (isDevMode && srcExists) {
|
|
89
93
|
// Development mode: scan TypeScript source files
|
|
90
94
|
console.log(" š Development mode: scanning TypeScript files");
|
|
@@ -103,11 +107,11 @@ export class FragmentWebApplication {
|
|
|
103
107
|
if (require.extensions[".ts"]) {
|
|
104
108
|
return true;
|
|
105
109
|
}
|
|
106
|
-
|
|
110
|
+
|
|
107
111
|
// Check if process is running with ts-node or tsx
|
|
108
112
|
const execPath = process.argv[0];
|
|
109
113
|
const scriptPath = process.argv[1] || "";
|
|
110
|
-
|
|
114
|
+
|
|
111
115
|
if (
|
|
112
116
|
execPath.includes("ts-node") ||
|
|
113
117
|
execPath.includes("tsx") ||
|
|
@@ -116,24 +120,24 @@ export class FragmentWebApplication {
|
|
|
116
120
|
) {
|
|
117
121
|
return true;
|
|
118
122
|
}
|
|
119
|
-
|
|
123
|
+
|
|
120
124
|
// Check if main module has .ts extension
|
|
121
125
|
if (require.main?.filename.endsWith(".ts")) {
|
|
122
126
|
return true;
|
|
123
127
|
}
|
|
124
|
-
|
|
128
|
+
|
|
125
129
|
// Check NODE_ENV or explicit flag
|
|
126
130
|
if (process.env.FRAGMENT_DEV_MODE === "true") {
|
|
127
131
|
return true;
|
|
128
132
|
}
|
|
129
|
-
|
|
133
|
+
|
|
130
134
|
return false;
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
private discoverAndRegisterComponents(): void {
|
|
134
138
|
const classes = this.metadataStorage.getAllClasses();
|
|
135
139
|
console.log(`\nš¦ Discovered ${classes.length} component(s)`);
|
|
136
|
-
|
|
140
|
+
|
|
137
141
|
// Group by type for display
|
|
138
142
|
const grouped = classes.reduce(
|
|
139
143
|
(acc, cls) => {
|
|
@@ -143,19 +147,21 @@ export class FragmentWebApplication {
|
|
|
143
147
|
},
|
|
144
148
|
{} as Record<string, any[]>,
|
|
145
149
|
);
|
|
146
|
-
|
|
150
|
+
|
|
147
151
|
Object.entries(grouped).forEach(([type, items]) => {
|
|
148
152
|
const icon = this.getTypeIcon(type);
|
|
149
153
|
console.log(` ${icon} ${items.length} ${type}(s)`);
|
|
150
|
-
|
|
151
|
-
items.forEach(item => {
|
|
152
|
-
console.log(
|
|
154
|
+
|
|
155
|
+
items.forEach((item) => {
|
|
156
|
+
console.log(
|
|
157
|
+
` ⢠${item.target.name}${item.path ? ` (${item.path})` : ""}`,
|
|
158
|
+
);
|
|
153
159
|
});
|
|
154
160
|
});
|
|
155
161
|
|
|
156
162
|
let registered = 0;
|
|
157
163
|
let skipped = 0;
|
|
158
|
-
|
|
164
|
+
|
|
159
165
|
classes.forEach((metadata) => {
|
|
160
166
|
if (this.shouldRegister(metadata.target)) {
|
|
161
167
|
// CRITICAL: Register with container
|
|
@@ -178,10 +184,10 @@ export class FragmentWebApplication {
|
|
|
178
184
|
if (skipped > 0) {
|
|
179
185
|
console.log(`ā Skipped ${skipped} component(s) (conditions not met)`);
|
|
180
186
|
}
|
|
181
|
-
|
|
187
|
+
|
|
182
188
|
// Pre-resolve all singleton components to ensure dependencies are injected
|
|
183
189
|
console.log("\nš§ Initializing components");
|
|
184
|
-
classes.forEach(metadata => {
|
|
190
|
+
classes.forEach((metadata) => {
|
|
185
191
|
if (this.shouldRegister(metadata.target)) {
|
|
186
192
|
const instance = this.container.resolve(metadata.target);
|
|
187
193
|
console.log(` ā Initialized: ${metadata.target.name}`);
|
|
@@ -205,9 +211,11 @@ export class FragmentWebApplication {
|
|
|
205
211
|
METADATA_KEYS.CONDITIONAL_ON_CLASS,
|
|
206
212
|
target,
|
|
207
213
|
);
|
|
208
|
-
|
|
214
|
+
|
|
209
215
|
if (conditionalClass && !this.isClassAvailable(conditionalClass)) {
|
|
210
|
-
console.log(
|
|
216
|
+
console.log(
|
|
217
|
+
` š« Conditional check failed for ${target.name}: Class not available`,
|
|
218
|
+
);
|
|
211
219
|
return false;
|
|
212
220
|
}
|
|
213
221
|
|
|
@@ -215,9 +223,11 @@ export class FragmentWebApplication {
|
|
|
215
223
|
METADATA_KEYS.CONDITIONAL_ON_MISSING_BEAN,
|
|
216
224
|
target,
|
|
217
225
|
);
|
|
218
|
-
|
|
226
|
+
|
|
219
227
|
if (conditionalMissingBean && this.container.has(conditionalMissingBean)) {
|
|
220
|
-
console.log(
|
|
228
|
+
console.log(
|
|
229
|
+
` š« Conditional check failed for ${target.name}: Bean already exists`,
|
|
230
|
+
);
|
|
221
231
|
return false;
|
|
222
232
|
}
|
|
223
233
|
|
|
@@ -225,17 +235,21 @@ export class FragmentWebApplication {
|
|
|
225
235
|
METADATA_KEYS.CONDITIONAL_ON_PROPERTY,
|
|
226
236
|
target,
|
|
227
237
|
);
|
|
228
|
-
|
|
238
|
+
|
|
229
239
|
if (conditionalProperty) {
|
|
230
240
|
const value = process.env[conditionalProperty.key];
|
|
231
|
-
|
|
241
|
+
|
|
232
242
|
if (conditionalProperty.expectedValue !== undefined) {
|
|
233
243
|
if (value !== conditionalProperty.expectedValue) {
|
|
234
|
-
console.log(
|
|
244
|
+
console.log(
|
|
245
|
+
` š« Conditional check failed for ${target.name}: Expected ${conditionalProperty.expectedValue}, got ${value}`,
|
|
246
|
+
);
|
|
235
247
|
return false;
|
|
236
248
|
}
|
|
237
249
|
} else if (!value) {
|
|
238
|
-
console.log(
|
|
250
|
+
console.log(
|
|
251
|
+
` š« Conditional check failed for ${target.name}: Property not set`,
|
|
252
|
+
);
|
|
239
253
|
return false;
|
|
240
254
|
}
|
|
241
255
|
}
|
|
@@ -255,44 +269,44 @@ export class FragmentWebApplication {
|
|
|
255
269
|
const controllers = this.metadataStorage
|
|
256
270
|
.getAllClasses()
|
|
257
271
|
.filter((c) => c.type === "controller");
|
|
258
|
-
|
|
272
|
+
|
|
259
273
|
if (controllers.length === 0) {
|
|
260
274
|
console.log("\nš£ļø No routes to register");
|
|
261
275
|
return;
|
|
262
276
|
}
|
|
263
|
-
|
|
277
|
+
|
|
264
278
|
let totalRoutes = 0;
|
|
265
279
|
console.log(`\nš£ļø Registering routes...`);
|
|
266
|
-
|
|
280
|
+
|
|
267
281
|
controllers.forEach((controllerMetadata) => {
|
|
268
282
|
try {
|
|
269
283
|
console.log(`\nš Controller: ${controllerMetadata.target.name}`);
|
|
270
|
-
console.log(` Base path: ${controllerMetadata.path ||
|
|
271
|
-
|
|
284
|
+
console.log(` Base path: ${controllerMetadata.path || "/"}`);
|
|
285
|
+
|
|
272
286
|
const controller = this.container.resolve(controllerMetadata.target);
|
|
273
287
|
const basePath = controllerMetadata.path || "";
|
|
274
|
-
|
|
288
|
+
|
|
275
289
|
const methods = this.metadataStorage
|
|
276
290
|
.getAllMethods()
|
|
277
291
|
.filter((m) => m.target === controllerMetadata.target);
|
|
278
|
-
|
|
292
|
+
|
|
279
293
|
if (methods.length === 0) {
|
|
280
294
|
console.log(` ā ļø No routes defined for this controller`);
|
|
281
295
|
return;
|
|
282
296
|
}
|
|
283
|
-
|
|
297
|
+
|
|
284
298
|
methods.forEach((methodMetadata) => {
|
|
285
299
|
const fullPath = this.normalizePath(basePath + methodMetadata.path);
|
|
286
300
|
const httpMethod = methodMetadata.method.toLowerCase();
|
|
287
301
|
const methodColor = this.getMethodColor(httpMethod);
|
|
288
302
|
const methodIcon = this.getMethodIcon(httpMethod);
|
|
289
|
-
|
|
303
|
+
|
|
290
304
|
console.log(
|
|
291
305
|
` ${methodIcon} ${methodColor}${httpMethod.toUpperCase().padEnd(7)}\x1b[0m ${fullPath}`,
|
|
292
306
|
);
|
|
293
|
-
|
|
307
|
+
|
|
294
308
|
totalRoutes++;
|
|
295
|
-
|
|
309
|
+
|
|
296
310
|
(this.app as any)[httpMethod](
|
|
297
311
|
fullPath,
|
|
298
312
|
async (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -302,14 +316,16 @@ export class FragmentWebApplication {
|
|
|
302
316
|
req,
|
|
303
317
|
res,
|
|
304
318
|
);
|
|
305
|
-
|
|
306
|
-
console.log(
|
|
319
|
+
|
|
320
|
+
console.log(
|
|
321
|
+
`\nš Handling ${httpMethod.toUpperCase()} ${fullPath}`,
|
|
322
|
+
);
|
|
307
323
|
// console.log(` Parameters:`, args);
|
|
308
|
-
|
|
324
|
+
|
|
309
325
|
const result = await (controller as any)[
|
|
310
326
|
methodMetadata.propertyKey
|
|
311
327
|
](...args);
|
|
312
|
-
|
|
328
|
+
|
|
313
329
|
if (!res.headersSent) {
|
|
314
330
|
res.json(result);
|
|
315
331
|
}
|
|
@@ -321,7 +337,10 @@ export class FragmentWebApplication {
|
|
|
321
337
|
);
|
|
322
338
|
});
|
|
323
339
|
} catch (error) {
|
|
324
|
-
console.error(
|
|
340
|
+
console.error(
|
|
341
|
+
`ā Failed to register controller ${controllerMetadata.target.name}:`,
|
|
342
|
+
error,
|
|
343
|
+
);
|
|
325
344
|
}
|
|
326
345
|
});
|
|
327
346
|
|
|
@@ -358,7 +377,7 @@ export class FragmentWebApplication {
|
|
|
358
377
|
const params = [...methodMetadata.paramMetadata].sort(
|
|
359
378
|
(a: any, b: any) => a.index - b.index,
|
|
360
379
|
);
|
|
361
|
-
|
|
380
|
+
|
|
362
381
|
return params.map((param: any) => {
|
|
363
382
|
switch (param.type) {
|
|
364
383
|
case "body":
|
|
@@ -402,4 +421,4 @@ export class FragmentWebApplication {
|
|
|
402
421
|
getExpressApp(): Express {
|
|
403
422
|
return this.app;
|
|
404
423
|
}
|
|
405
|
-
}
|
|
424
|
+
}
|