fragment-ts 1.0.30 → 1.0.32
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/API.md +752 -0
- package/DOCS.md +555 -0
- package/README.md +76 -339
- package/USAGE.md +309 -1306
- 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/exception-filter.decorator.d.ts +5 -0
- package/dist/core/decorators/exception-filter.decorator.d.ts.map +1 -0
- package/dist/core/decorators/exception-filter.decorator.js +23 -0
- package/dist/core/decorators/exception-filter.decorator.js.map +1 -0
- package/dist/core/decorators/guard.decorator.d.ts +5 -0
- package/dist/core/decorators/guard.decorator.d.ts.map +1 -0
- package/dist/core/decorators/guard.decorator.js +23 -0
- package/dist/core/decorators/guard.decorator.js.map +1 -0
- 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/decorators/interceptor.decorator.d.ts +5 -0
- package/dist/core/decorators/interceptor.decorator.d.ts.map +1 -0
- package/dist/core/decorators/interceptor.decorator.js +23 -0
- package/dist/core/decorators/interceptor.decorator.js.map +1 -0
- package/dist/core/decorators/middleware.decorator.d.ts +8 -0
- package/dist/core/decorators/middleware.decorator.d.ts.map +1 -0
- package/dist/core/decorators/middleware.decorator.js +28 -0
- package/dist/core/decorators/middleware.decorator.js.map +1 -0
- package/dist/core/metadata/metadata-keys.d.ts +1 -0
- package/dist/core/metadata/metadata-keys.d.ts.map +1 -1
- package/dist/core/metadata/metadata-keys.js +1 -0
- package/dist/core/metadata/metadata-keys.js.map +1 -1
- package/dist/core/metadata/metadata-storage.d.ts +20 -4
- package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
- package/dist/core/metadata/metadata-storage.js +94 -14
- package/dist/core/metadata/metadata-storage.js.map +1 -1
- package/dist/web/application.d.ts.map +1 -1
- package/dist/web/application.js +79 -10
- package/dist/web/application.js.map +1 -1
- package/dist/web/interfaces.d.ts +5 -1
- package/dist/web/interfaces.d.ts.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/exception-filter.decorator.ts +20 -0
- package/src/core/decorators/guard.decorator.ts +20 -0
- package/src/core/decorators/injection.decorators.ts +5 -0
- package/src/core/decorators/interceptor.decorator.ts +20 -0
- package/src/core/decorators/middleware.decorator.ts +25 -0
- package/src/core/metadata/metadata-keys.ts +1 -0
- package/src/core/metadata/metadata-storage.ts +128 -24
- package/src/web/application.ts +207 -53
- package/src/web/interfaces.ts +11 -2
|
@@ -23,63 +23,52 @@ export class DIContainer {
|
|
|
23
23
|
|
|
24
24
|
register(token: any, instance?: any, factory?: () => any): void {
|
|
25
25
|
this.registered.add(token);
|
|
26
|
-
console.log(
|
|
27
|
-
` ✓ Registered token: ${typeof token === "function" ? token.name : token}`,
|
|
28
|
-
);
|
|
29
26
|
|
|
30
27
|
if (instance) {
|
|
31
28
|
this.singletons.set(token, instance);
|
|
32
|
-
console.log(` ➤ Singleton instance created`);
|
|
33
29
|
} else if (factory) {
|
|
34
30
|
this.factories.set(token, factory);
|
|
35
|
-
console.log(` ➤ Factory registered`);
|
|
36
31
|
}
|
|
37
32
|
}
|
|
38
33
|
|
|
39
34
|
resolve<T>(token: any): T {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
);
|
|
35
|
+
// Check if already resolved
|
|
36
|
+
if (this.singletons.has(token)) {
|
|
37
|
+
return this.singletons.get(token);
|
|
44
38
|
}
|
|
45
39
|
|
|
40
|
+
// Handle circular dependencies
|
|
46
41
|
if (this.constructing.has(token)) {
|
|
47
42
|
throw new Error(
|
|
48
43
|
`Circular dependency detected for ${token.name || token}`,
|
|
49
44
|
);
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
//
|
|
53
|
-
if (this.singletons.has(token)) {
|
|
54
|
-
console.log(
|
|
55
|
-
` ✓ Resolved from singleton cache: ${typeof token === "function" ? token.name : token}`,
|
|
56
|
-
);
|
|
57
|
-
return this.singletons.get(token);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Check factories
|
|
47
|
+
// Use factory if available
|
|
61
48
|
if (this.factories.has(token)) {
|
|
62
|
-
console.log(
|
|
63
|
-
` 🏭 Creating instance from factory: ${typeof token === "function" ? token.name : token}`,
|
|
64
|
-
);
|
|
65
49
|
const factory = this.factories.get(token)!;
|
|
66
50
|
const instance = factory();
|
|
67
|
-
|
|
68
51
|
const scope =
|
|
69
52
|
Reflect.getMetadata(METADATA_KEYS.SCOPE, token) || "singleton";
|
|
53
|
+
|
|
70
54
|
if (scope === "singleton") {
|
|
71
55
|
this.singletons.set(token, instance);
|
|
72
|
-
console.log(` ➤ Cached as singleton`);
|
|
73
56
|
}
|
|
74
|
-
|
|
75
57
|
return instance;
|
|
76
58
|
}
|
|
77
59
|
|
|
78
|
-
// Create
|
|
60
|
+
// Create instance for class tokens
|
|
79
61
|
if (typeof token === "function") {
|
|
80
|
-
|
|
81
|
-
|
|
62
|
+
if (
|
|
63
|
+
!this.registered.has(token) &&
|
|
64
|
+
!Reflect.getMetadata(METADATA_KEYS.INJECTABLE, token)
|
|
65
|
+
) {
|
|
66
|
+
console.warn(
|
|
67
|
+
`⚠️ ${token.name} is not registered as injectable. Consider adding @Injectable(), @Service(), @Controller(), or @Repository()`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
82
70
|
|
|
71
|
+
this.constructing.add(token);
|
|
83
72
|
try {
|
|
84
73
|
const instance = this.createInstance(token);
|
|
85
74
|
const scope =
|
|
@@ -87,35 +76,25 @@ export class DIContainer {
|
|
|
87
76
|
|
|
88
77
|
if (scope === "singleton") {
|
|
89
78
|
this.singletons.set(token, instance);
|
|
90
|
-
console.log(` ➤ Registered as singleton`);
|
|
91
79
|
}
|
|
92
80
|
|
|
93
81
|
this.constructing.delete(token);
|
|
94
82
|
return instance;
|
|
95
83
|
} catch (error) {
|
|
96
84
|
this.constructing.delete(token);
|
|
97
|
-
console.error(
|
|
98
|
-
` ❌ Failed to create instance of ${token.name}: ${error}`,
|
|
99
|
-
);
|
|
100
85
|
throw error;
|
|
101
86
|
}
|
|
102
87
|
}
|
|
103
88
|
|
|
104
|
-
throw new Error(
|
|
105
|
-
`Cannot resolve dependency: ${typeof token === "function" ? token.name : token}`,
|
|
106
|
-
);
|
|
89
|
+
throw new Error(`Cannot resolve dependency: ${token}`);
|
|
107
90
|
}
|
|
108
91
|
|
|
109
92
|
private createInstance(target: any): any {
|
|
110
93
|
const className = target.name;
|
|
111
|
-
console.log(`
|
|
94
|
+
console.log(` 🔨 Creating instance of ${className}`);
|
|
112
95
|
|
|
113
|
-
// Get constructor
|
|
96
|
+
// Get constructor parameters
|
|
114
97
|
const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
|
|
115
|
-
console.log(
|
|
116
|
-
` 📋 Constructor parameters for ${className}: ${paramTypes.length}`,
|
|
117
|
-
);
|
|
118
|
-
|
|
119
98
|
const params = paramTypes.map((type: any, index: number) => {
|
|
120
99
|
if (!type || type === Object) {
|
|
121
100
|
console.warn(
|
|
@@ -123,20 +102,19 @@ export class DIContainer {
|
|
|
123
102
|
);
|
|
124
103
|
return undefined;
|
|
125
104
|
}
|
|
126
|
-
|
|
127
105
|
console.log(
|
|
128
|
-
`
|
|
106
|
+
` 📦 Resolving constructor dependency [${index}]: ${type.name || type}`,
|
|
129
107
|
);
|
|
130
108
|
return this.resolve(type);
|
|
131
109
|
});
|
|
132
110
|
|
|
133
|
-
|
|
111
|
+
// Create instance
|
|
134
112
|
const instance = new target(...params);
|
|
135
113
|
|
|
136
|
-
// Inject properties
|
|
114
|
+
// CRITICAL FIX: Inject properties using metadata storage
|
|
137
115
|
this.injectProperties(instance);
|
|
138
116
|
|
|
139
|
-
// Call @PostConstruct
|
|
117
|
+
// Call @PostConstruct method if exists
|
|
140
118
|
const postConstructMethod = Reflect.getMetadata(
|
|
141
119
|
METADATA_KEYS.POST_CONSTRUCT,
|
|
142
120
|
target,
|
|
@@ -162,81 +140,77 @@ export class DIContainer {
|
|
|
162
140
|
console.log(` 🔍 Scanning for property injections on ${className}`);
|
|
163
141
|
|
|
164
142
|
// Get all property injections from metadata storage
|
|
165
|
-
const
|
|
166
|
-
this.metadataStorage.getPropertyInjections(constructor);
|
|
143
|
+
const injections = this.metadataStorage.getPropertyInjections(constructor);
|
|
167
144
|
|
|
168
|
-
if (
|
|
145
|
+
if (injections.length === 0) {
|
|
169
146
|
console.log(` ℹ️ No property injections registered for ${className}`);
|
|
170
147
|
return;
|
|
171
148
|
}
|
|
172
149
|
|
|
173
150
|
console.log(
|
|
174
|
-
` 📌 Found ${
|
|
151
|
+
` 📌 Found ${injections.length} property injection(s) for ${className}`,
|
|
175
152
|
);
|
|
176
153
|
|
|
177
|
-
for (const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
for (const injection of injections) {
|
|
181
|
-
console.log(` • Injection type: ${injection.type}`);
|
|
154
|
+
for (const injection of injections) {
|
|
155
|
+
const { propertyKey, type, metadata } = injection;
|
|
156
|
+
console.log(` ➤ Property: ${propertyKey} [${type}]`);
|
|
182
157
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
200
|
-
break;
|
|
201
|
-
case "repository":
|
|
202
|
-
this.injectRepositoryProperty(
|
|
203
|
-
instance,
|
|
204
|
-
propertyKey,
|
|
205
|
-
injection.metadata?.entity,
|
|
206
|
-
className,
|
|
207
|
-
);
|
|
208
|
-
break;
|
|
209
|
-
case "value":
|
|
210
|
-
this.injectValueProperty(
|
|
211
|
-
instance,
|
|
212
|
-
propertyKey,
|
|
213
|
-
injection.metadata?.expression,
|
|
214
|
-
className,
|
|
215
|
-
);
|
|
216
|
-
break;
|
|
217
|
-
case "lazy":
|
|
218
|
-
this.injectLazyProperty(instance, propertyKey, className);
|
|
219
|
-
break;
|
|
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}`,
|
|
158
|
+
try {
|
|
159
|
+
switch (type) {
|
|
160
|
+
case "autowired":
|
|
161
|
+
this.injectAutowiredProperty(
|
|
162
|
+
instance,
|
|
163
|
+
propertyKey,
|
|
164
|
+
metadata?.type,
|
|
165
|
+
className,
|
|
166
|
+
);
|
|
167
|
+
break;
|
|
168
|
+
case "inject":
|
|
169
|
+
this.injectTokenProperty(
|
|
170
|
+
instance,
|
|
171
|
+
propertyKey,
|
|
172
|
+
metadata?.token,
|
|
173
|
+
className,
|
|
231
174
|
);
|
|
232
|
-
|
|
233
|
-
|
|
175
|
+
break;
|
|
176
|
+
case "repository":
|
|
177
|
+
this.injectRepositoryProperty(
|
|
178
|
+
instance,
|
|
179
|
+
propertyKey,
|
|
180
|
+
metadata?.entity,
|
|
181
|
+
className,
|
|
182
|
+
);
|
|
183
|
+
break;
|
|
184
|
+
case "value":
|
|
185
|
+
this.injectValueProperty(
|
|
186
|
+
instance,
|
|
187
|
+
propertyKey,
|
|
188
|
+
metadata?.expression,
|
|
189
|
+
className,
|
|
190
|
+
);
|
|
191
|
+
break;
|
|
192
|
+
case "lazy":
|
|
193
|
+
this.injectLazyProperty(instance, propertyKey, className);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
const isOptional = Reflect.getMetadata(
|
|
198
|
+
METADATA_KEYS.OPTIONAL,
|
|
199
|
+
constructor.prototype,
|
|
200
|
+
propertyKey,
|
|
201
|
+
);
|
|
234
202
|
|
|
235
|
-
|
|
236
|
-
|
|
203
|
+
if (!isOptional) {
|
|
204
|
+
console.error(
|
|
205
|
+
` ❌ Failed to inject ${propertyKey} in ${className}: ${error}`,
|
|
237
206
|
);
|
|
238
|
-
|
|
207
|
+
throw error;
|
|
239
208
|
}
|
|
209
|
+
|
|
210
|
+
console.warn(
|
|
211
|
+
` ⚠️ Optional dependency ${propertyKey} not available for ${className}`,
|
|
212
|
+
);
|
|
213
|
+
instance[propertyKey] = undefined;
|
|
240
214
|
}
|
|
241
215
|
}
|
|
242
216
|
}
|
|
@@ -248,21 +222,16 @@ export class DIContainer {
|
|
|
248
222
|
className: string,
|
|
249
223
|
): void {
|
|
250
224
|
if (!type) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
// Fallback to design:type metadata
|
|
226
|
+
type = Reflect.getMetadata("design:type", instance, propertyKey);
|
|
227
|
+
if (!type || type === Object) {
|
|
262
228
|
throw new Error(
|
|
263
229
|
`Type information not available for ${className}.${propertyKey}`,
|
|
264
230
|
);
|
|
265
231
|
}
|
|
232
|
+
console.warn(
|
|
233
|
+
` ⚠️ Using design:type metadata as fallback for ${className}.${propertyKey}`,
|
|
234
|
+
);
|
|
266
235
|
}
|
|
267
236
|
|
|
268
237
|
console.log(` 💉 @Autowired: ${className}.${propertyKey} -> ${type.name}`);
|
|
@@ -276,12 +245,6 @@ export class DIContainer {
|
|
|
276
245
|
token: any,
|
|
277
246
|
className: string,
|
|
278
247
|
): void {
|
|
279
|
-
if (!token) {
|
|
280
|
-
throw new Error(
|
|
281
|
-
`Token not provided for @Inject on ${className}.${propertyKey}`,
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
248
|
const tokenName = typeof token === "string" ? token : token.name;
|
|
286
249
|
console.log(` 💉 @Inject: ${className}.${propertyKey} -> ${tokenName}`);
|
|
287
250
|
instance[propertyKey] = this.resolve(token);
|
|
@@ -294,12 +257,6 @@ export class DIContainer {
|
|
|
294
257
|
entity: any,
|
|
295
258
|
className: string,
|
|
296
259
|
): void {
|
|
297
|
-
if (!entity) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
`Entity not provided for @InjectRepository on ${className}.${propertyKey}`,
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
260
|
console.log(
|
|
304
261
|
` 💾 @InjectRepository: ${className}.${propertyKey} -> Repository<${entity.name}>`,
|
|
305
262
|
);
|
|
@@ -315,12 +272,6 @@ export class DIContainer {
|
|
|
315
272
|
expression: string,
|
|
316
273
|
className: string,
|
|
317
274
|
): void {
|
|
318
|
-
if (!expression) {
|
|
319
|
-
throw new Error(
|
|
320
|
-
`Expression not provided for @Value on ${className}.${propertyKey}`,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
275
|
console.log(` 🔧 @Value: ${className}.${propertyKey} = ${expression}`);
|
|
325
276
|
instance[propertyKey] = this.resolveValue(expression);
|
|
326
277
|
console.log(` ✅ Set value for ${className}.${propertyKey}`);
|
|
@@ -331,7 +282,6 @@ export class DIContainer {
|
|
|
331
282
|
propertyKey: string,
|
|
332
283
|
className: string,
|
|
333
284
|
): void {
|
|
334
|
-
// Get the type for later resolution
|
|
335
285
|
const type = Reflect.getMetadata("design:type", instance, propertyKey);
|
|
336
286
|
if (!type) {
|
|
337
287
|
throw new Error(
|
|
@@ -374,33 +324,17 @@ export class DIContainer {
|
|
|
374
324
|
key = defaultMatch[1].trim();
|
|
375
325
|
defaultValue = defaultMatch[2].trim();
|
|
376
326
|
|
|
377
|
-
// Try to parse
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
327
|
+
// Try to parse as boolean
|
|
328
|
+
if (defaultValue.toLowerCase() === "true") defaultValue = true;
|
|
329
|
+
else if (defaultValue.toLowerCase() === "false") defaultValue = false;
|
|
330
|
+
// Try to parse as number
|
|
331
|
+
else if (!isNaN(Number(defaultValue)))
|
|
332
|
+
defaultValue = Number(defaultValue);
|
|
393
333
|
}
|
|
394
334
|
|
|
395
|
-
|
|
396
|
-
` 🔍 Resolving config value: ${key}${defaultValue !== undefined ? ` (default: ${defaultValue})` : ""}`,
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
// Check environment variables first
|
|
335
|
+
// Check environment variables
|
|
400
336
|
const envValue = process.env[key.toUpperCase()] || process.env[key];
|
|
401
337
|
if (envValue !== undefined) {
|
|
402
|
-
console.log(` 🌍 Found in environment: ${envValue}`);
|
|
403
|
-
|
|
404
338
|
// Try to parse as JSON if it looks like an object/array
|
|
405
339
|
if (envValue.startsWith("{") || envValue.startsWith("[")) {
|
|
406
340
|
try {
|
|
@@ -424,19 +358,11 @@ export class DIContainer {
|
|
|
424
358
|
}
|
|
425
359
|
|
|
426
360
|
// Return default value if available
|
|
427
|
-
|
|
428
|
-
console.log(` 📦 Using default value: ${defaultValue}`);
|
|
429
|
-
return defaultValue;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
console.warn(` ⚠️ Config value not found: ${key}`);
|
|
433
|
-
return undefined;
|
|
361
|
+
return defaultValue !== undefined ? defaultValue : undefined;
|
|
434
362
|
}
|
|
435
363
|
|
|
436
364
|
private resolveRepository(entity: any): any {
|
|
437
365
|
try {
|
|
438
|
-
console.log(` 🔍 Resolving repository for entity: ${entity.name}`);
|
|
439
|
-
|
|
440
366
|
const { TypeORMModule } = require("../../typeorm/typeorm-module");
|
|
441
367
|
const dataSource = TypeORMModule.getDataSource();
|
|
442
368
|
|
|
@@ -444,14 +370,8 @@ export class DIContainer {
|
|
|
444
370
|
throw new Error("TypeORM DataSource not initialized");
|
|
445
371
|
}
|
|
446
372
|
|
|
447
|
-
console.log(
|
|
448
|
-
` ✅ Found DataSource, creating repository for ${entity.name}`,
|
|
449
|
-
);
|
|
450
373
|
return dataSource.getRepository(entity);
|
|
451
374
|
} catch (error) {
|
|
452
|
-
console.error(
|
|
453
|
-
` ❌ Failed to resolve repository for ${entity.name}: ${(error as Error).message}`,
|
|
454
|
-
);
|
|
455
375
|
throw new Error(
|
|
456
376
|
`Failed to resolve repository for ${entity.name}: ${(error as Error)?.message}`,
|
|
457
377
|
);
|
|
@@ -471,7 +391,6 @@ export class DIContainer {
|
|
|
471
391
|
}
|
|
472
392
|
|
|
473
393
|
clear(): void {
|
|
474
|
-
console.log("🧹 Clearing DI Container");
|
|
475
394
|
this.singletons.clear();
|
|
476
395
|
this.transients.clear();
|
|
477
396
|
this.factories.clear();
|
|
@@ -481,6 +400,5 @@ export class DIContainer {
|
|
|
481
400
|
|
|
482
401
|
reset(): void {
|
|
483
402
|
this.clear();
|
|
484
|
-
console.log("🔁 DI Container reset complete");
|
|
485
403
|
}
|
|
486
404
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ExceptionFilter } from "../../web/interfaces";
|
|
2
|
+
import { METADATA_KEYS } from "../metadata/metadata-keys";
|
|
3
|
+
import { MetadataStorage } from "../metadata/metadata-storage";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @ExceptionFilter - Handle exceptions globally or per controller/method
|
|
7
|
+
*/
|
|
8
|
+
export function ExceptionFilter(filterClass: new () => any): ClassDecorator & MethodDecorator {
|
|
9
|
+
return (target: any, propertyKey?: string | symbol) => {
|
|
10
|
+
if (propertyKey === undefined) {
|
|
11
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_FILTERS, filterClass, target);
|
|
12
|
+
const storage = MetadataStorage.getInstance();
|
|
13
|
+
storage.addClassMetadata?.(target, 'exceptionFilter', filterClass);
|
|
14
|
+
} else {
|
|
15
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_FILTERS, filterClass, target, propertyKey);
|
|
16
|
+
const storage = MetadataStorage.getInstance();
|
|
17
|
+
storage.addMethodMetadata?.(target.constructor, propertyKey.toString(), 'exceptionFilter', filterClass);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Guard } from "../../web/interfaces";
|
|
2
|
+
import { METADATA_KEYS } from "../metadata/metadata-keys";
|
|
3
|
+
import { MetadataStorage } from "../metadata/metadata-storage";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @Guard - Apply authorization/activation guard
|
|
7
|
+
*/
|
|
8
|
+
export function Guard(guardClass: new () => any): ClassDecorator & MethodDecorator {
|
|
9
|
+
return (target: any, propertyKey?: string | symbol) => {
|
|
10
|
+
if (propertyKey === undefined) {
|
|
11
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_GUARDS, guardClass, target);
|
|
12
|
+
const storage = MetadataStorage.getInstance();
|
|
13
|
+
storage.addClassMetadata?.(target, 'guard', guardClass);
|
|
14
|
+
} else {
|
|
15
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_GUARDS, guardClass, target, propertyKey);
|
|
16
|
+
const storage = MetadataStorage.getInstance();
|
|
17
|
+
storage.addMethodMetadata?.(target.constructor, propertyKey.toString(), 'guard', guardClass);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -23,6 +23,7 @@ export function Autowired(): PropertyDecorator {
|
|
|
23
23
|
type: "autowired",
|
|
24
24
|
key: propertyKey.toString(),
|
|
25
25
|
metadata: { type },
|
|
26
|
+
propertyKey: propertyKey.toString(),
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
// Also store in Reflect metadata for backward compatibility
|
|
@@ -43,6 +44,7 @@ export function Inject(token: string | Function): PropertyDecorator {
|
|
|
43
44
|
type: "inject",
|
|
44
45
|
key: propertyKey.toString(),
|
|
45
46
|
metadata: { token },
|
|
47
|
+
propertyKey: propertyKey.toString(),
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
// Also store in Reflect metadata for backward compatibility
|
|
@@ -62,6 +64,7 @@ export function InjectRepository(entity: Function): PropertyDecorator {
|
|
|
62
64
|
type: "repository",
|
|
63
65
|
key: propertyKey.toString(),
|
|
64
66
|
metadata: { entity },
|
|
67
|
+
propertyKey: propertyKey.toString(),
|
|
65
68
|
});
|
|
66
69
|
|
|
67
70
|
// Also store in Reflect metadata for backward compatibility
|
|
@@ -97,6 +100,7 @@ export function Value(expression: string): PropertyDecorator {
|
|
|
97
100
|
type: "value",
|
|
98
101
|
key: propertyKey.toString(),
|
|
99
102
|
metadata: { expression },
|
|
103
|
+
propertyKey: propertyKey.toString(),
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
// Also store in Reflect metadata for backward compatibility
|
|
@@ -133,6 +137,7 @@ export function Lazy(): PropertyDecorator {
|
|
|
133
137
|
storage.addPropertyInjection?.(target.constructor, propertyKey.toString(), {
|
|
134
138
|
type: "lazy",
|
|
135
139
|
key: propertyKey.toString(),
|
|
140
|
+
propertyKey: propertyKey.toString(),
|
|
136
141
|
});
|
|
137
142
|
|
|
138
143
|
// Get the type for later resolution
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Interceptor } from "../../web/interfaces";
|
|
2
|
+
import { METADATA_KEYS } from "../metadata/metadata-keys";
|
|
3
|
+
import { MetadataStorage } from "../metadata/metadata-storage";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @Interceptor - Intercept and transform request/response
|
|
7
|
+
*/
|
|
8
|
+
export function Interceptor(interceptorClass: new () => any): ClassDecorator & MethodDecorator {
|
|
9
|
+
return (target: any, propertyKey?: string | symbol) => {
|
|
10
|
+
if (propertyKey === undefined) {
|
|
11
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_INTERCEPTORS, interceptorClass, target);
|
|
12
|
+
const storage = MetadataStorage.getInstance();
|
|
13
|
+
storage.addClassMetadata?.(target, 'interceptor', interceptorClass);
|
|
14
|
+
} else {
|
|
15
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_INTERCEPTORS, interceptorClass, target, propertyKey);
|
|
16
|
+
const storage = MetadataStorage.getInstance();
|
|
17
|
+
storage.addMethodMetadata?.(target.constructor, propertyKey.toString(), 'interceptor', interceptorClass);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Middleware } from "../../web/interfaces";
|
|
2
|
+
import { METADATA_KEYS } from "../metadata/metadata-keys";
|
|
3
|
+
import { MetadataStorage } from "../metadata/metadata-storage";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @Middleware - Apply Express-compatible middleware at class or method level
|
|
7
|
+
* Usage:
|
|
8
|
+
* @Middleware(AuthMiddleware) class MyController { }
|
|
9
|
+
* @Middleware(AuthMiddleware) @Get('/') handler() { }
|
|
10
|
+
*/
|
|
11
|
+
export function Middleware(middlewareClass: new () => any): ClassDecorator & MethodDecorator {
|
|
12
|
+
return (target: any, propertyKey?: string | symbol) => {
|
|
13
|
+
if (propertyKey === undefined) {
|
|
14
|
+
// Class-level
|
|
15
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_MIDDLEWARE, middlewareClass, target);
|
|
16
|
+
const storage = MetadataStorage.getInstance();
|
|
17
|
+
storage.addClassMetadata?.(target, 'middleware', middlewareClass);
|
|
18
|
+
} else {
|
|
19
|
+
// Method-level
|
|
20
|
+
Reflect.defineMetadata(METADATA_KEYS.USE_MIDDLEWARE, middlewareClass, target, propertyKey);
|
|
21
|
+
const storage = MetadataStorage.getInstance();
|
|
22
|
+
storage.addMethodMetadata?.(target.constructor, propertyKey.toString(), 'middleware', middlewareClass);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -33,6 +33,7 @@ export const METADATA_KEYS = {
|
|
|
33
33
|
CONDITIONAL_ON_BEAN: "fragment:conditional-on-bean",
|
|
34
34
|
|
|
35
35
|
// Middleware & Guards
|
|
36
|
+
USE_MIDDLEWARE: "fragment:use-middleware",
|
|
36
37
|
USE_GUARDS: "fragment:use-guards",
|
|
37
38
|
USE_INTERCEPTORS: "fragment:use-interceptors",
|
|
38
39
|
USE_FILTERS: "fragment:use-filters",
|