fragment-ts 1.0.23 → 1.0.25

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 (58) hide show
  1. package/dist/cli/commands/init.command.js +1 -1
  2. package/dist/core/container/di-container.d.ts +1 -1
  3. package/dist/core/container/di-container.d.ts.map +1 -1
  4. package/dist/core/container/di-container.js +118 -63
  5. package/dist/core/container/di-container.js.map +1 -1
  6. package/dist/core/decorators/application.decorator.d.ts +1 -6
  7. package/dist/core/decorators/application.decorator.d.ts.map +1 -1
  8. package/dist/core/decorators/application.decorator.js +2 -7
  9. package/dist/core/decorators/application.decorator.js.map +1 -1
  10. package/dist/core/decorators/auto-configuration.decorator.d.ts.map +1 -1
  11. package/dist/core/decorators/auto-configuration.decorator.js +5 -4
  12. package/dist/core/decorators/auto-configuration.decorator.js.map +1 -1
  13. package/dist/core/decorators/conditional.decorators.d.ts +0 -4
  14. package/dist/core/decorators/conditional.decorators.d.ts.map +1 -1
  15. package/dist/core/decorators/conditional.decorators.js +0 -32
  16. package/dist/core/decorators/conditional.decorators.js.map +1 -1
  17. package/dist/core/decorators/controller.decorator.d.ts.map +1 -1
  18. package/dist/core/decorators/controller.decorator.js +2 -1
  19. package/dist/core/decorators/controller.decorator.js.map +1 -1
  20. package/dist/core/decorators/http.decorators.d.ts +0 -1
  21. package/dist/core/decorators/http.decorators.d.ts.map +1 -1
  22. package/dist/core/decorators/http.decorators.js +10 -10
  23. package/dist/core/decorators/http.decorators.js.map +1 -1
  24. package/dist/core/decorators/injectable.decorator.d.ts +0 -3
  25. package/dist/core/decorators/injectable.decorator.d.ts.map +1 -1
  26. package/dist/core/decorators/injectable.decorator.js +5 -12
  27. package/dist/core/decorators/injectable.decorator.js.map +1 -1
  28. package/dist/core/decorators/injection.decorators.d.ts +37 -2
  29. package/dist/core/decorators/injection.decorators.d.ts.map +1 -1
  30. package/dist/core/decorators/injection.decorators.js +75 -43
  31. package/dist/core/decorators/injection.decorators.js.map +1 -1
  32. package/dist/core/decorators/repository.decorator.d.ts.map +1 -1
  33. package/dist/core/decorators/repository.decorator.js +5 -4
  34. package/dist/core/decorators/repository.decorator.js.map +1 -1
  35. package/dist/core/decorators/service.decorator.d.ts.map +1 -1
  36. package/dist/core/decorators/service.decorator.js +5 -4
  37. package/dist/core/decorators/service.decorator.js.map +1 -1
  38. package/dist/core/metadata/metadata-storage.d.ts +20 -9
  39. package/dist/core/metadata/metadata-storage.d.ts.map +1 -1
  40. package/dist/core/metadata/metadata-storage.js +10 -58
  41. package/dist/core/metadata/metadata-storage.js.map +1 -1
  42. package/dist/web/application.d.ts.map +1 -1
  43. package/dist/web/application.js +16 -7
  44. package/dist/web/application.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/cli/commands/init.command.ts +1 -1
  47. package/src/core/container/di-container.ts +193 -77
  48. package/src/core/decorators/application.decorator.ts +3 -13
  49. package/src/core/decorators/auto-configuration.decorator.ts +10 -8
  50. package/src/core/decorators/conditional.decorators.ts +1 -37
  51. package/src/core/decorators/controller.decorator.ts +3 -1
  52. package/src/core/decorators/http.decorators.ts +32 -11
  53. package/src/core/decorators/injectable.decorator.ts +8 -15
  54. package/src/core/decorators/injection.decorators.ts +103 -50
  55. package/src/core/decorators/repository.decorator.ts +10 -8
  56. package/src/core/decorators/service.decorator.ts +10 -8
  57. package/src/core/metadata/metadata-storage.ts +43 -75
  58. package/src/web/application.ts +22 -12
@@ -173,7 +173,7 @@ export class InitCommand {
173
173
  "migrate:revert": "fragment migrate:revert",
174
174
  },
175
175
  dependencies: {
176
- "fragment-ts": "^1.0.23",
176
+ "fragment-ts": "^1.0.25",
177
177
  "reflect-metadata": "^0.1.13",
178
178
  },
179
179
  devDependencies: {
@@ -6,7 +6,8 @@ export class DIContainer {
6
6
  private singletons: Map<any, any> = new Map();
7
7
  private transients: Map<any, any> = new Map();
8
8
  private factories: Map<any, () => any> = new Map();
9
- private constructing: Set<any> = new Set(); // Prevent circular dependencies
9
+ private constructing: Set<any> = new Set();
10
+ private registered: Set<any> = new Set(); // Track what's actually injectable
10
11
 
11
12
  static getInstance(): DIContainer {
12
13
  if (!DIContainer.instance) {
@@ -16,27 +17,36 @@ export class DIContainer {
16
17
  }
17
18
 
18
19
  register(token: any, instance?: any, factory?: () => any): void {
20
+ // Mark this token as registered
21
+ this.registered.add(token);
22
+
19
23
  if (instance) {
20
24
  this.singletons.set(token, instance);
21
25
  } else if (factory) {
22
26
  this.factories.set(token, factory);
23
27
  }
28
+ // If neither instance nor factory, it will be created on-demand
24
29
  }
25
30
 
26
31
  resolve<T>(token: any): T {
27
- // Check for circular dependency
32
+ // Check if this is actually an injectable class
33
+ if (typeof token === "function" && !this.registered.has(token)) {
34
+ throw new Error(
35
+ `Cannot resolve ${token.name}: Not registered as injectable. ` +
36
+ `Did you forget to add @Injectable(), @Service(), @Controller(), or @Repository()?`,
37
+ );
38
+ }
39
+
28
40
  if (this.constructing.has(token)) {
29
41
  throw new Error(
30
42
  `Circular dependency detected for ${token.name || token}`,
31
43
  );
32
44
  }
33
45
 
34
- // Return existing singleton
35
46
  if (this.singletons.has(token)) {
36
47
  return this.singletons.get(token);
37
48
  }
38
49
 
39
- // Use factory if available
40
50
  if (this.factories.has(token)) {
41
51
  const factory = this.factories.get(token)!;
42
52
  const instance = factory();
@@ -50,7 +60,6 @@ export class DIContainer {
50
60
  return instance;
51
61
  }
52
62
 
53
- // Create new instance
54
63
  if (typeof token === "function") {
55
64
  this.constructing.add(token);
56
65
 
@@ -75,103 +84,211 @@ export class DIContainer {
75
84
  }
76
85
 
77
86
  private createInstance(target: any): any {
78
- // Get constructor parameter types
87
+ console.log(` 🔨 Creating instance of ${target.name}`);
88
+
79
89
  const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
80
90
 
81
- // Resolve constructor dependencies
82
- const params = paramTypes.map((type: any) => {
91
+ const params = paramTypes.map((type: any, index: number) => {
83
92
  if (!type || type === Object) {
93
+ console.warn(
94
+ ` ⚠️ Constructor param ${index} of ${target.name} has no type metadata`,
95
+ );
84
96
  return undefined;
85
97
  }
98
+ console.log(` 📦 Resolving constructor dependency: ${type.name}`);
86
99
  return this.resolve(type);
87
100
  });
88
101
 
89
- // Create instance
90
102
  const instance = new target(...params);
91
103
 
92
- // Inject properties AFTER construction
104
+ // Inject properties
93
105
  this.injectProperties(instance);
94
106
 
95
- // Call post-construct hook if it exists
96
- if (typeof instance.onInit === "function") {
97
- instance.onInit();
107
+ // Call post-construct
108
+ const postConstructMethod = Reflect.getMetadata(
109
+ METADATA_KEYS.POST_CONSTRUCT,
110
+ target,
111
+ );
112
+ if (
113
+ postConstructMethod &&
114
+ typeof instance[postConstructMethod] === "function"
115
+ ) {
116
+ console.log(
117
+ ` 🎯 Calling @PostConstruct: ${target.name}.${postConstructMethod}()`,
118
+ );
119
+ instance[postConstructMethod]();
98
120
  }
99
121
 
100
122
  return instance;
101
123
  }
102
124
 
103
125
  private injectProperties(instance: any): void {
104
- const prototype = Object.getPrototypeOf(instance);
126
+ const target = instance.constructor;
127
+ let currentProto = target.prototype;
128
+
129
+ // Walk the prototype chain
130
+ while (currentProto && currentProto !== Object.prototype) {
131
+ // Get all property names including inherited ones
132
+ const propertyNames = Object.getOwnPropertyNames(currentProto);
133
+
134
+ for (const prop of propertyNames) {
135
+ // Skip constructor
136
+ if (prop === "constructor") continue;
137
+
138
+ // Check @Autowired - stores the TYPE to inject
139
+ const autowiredType = Reflect.getMetadata(
140
+ METADATA_KEYS.AUTOWIRED,
141
+ currentProto,
142
+ prop,
143
+ );
105
144
 
106
- // Get all property keys from the prototype chain
107
- const properties = this.getAllPropertyKeys(instance);
145
+ if (autowiredType) {
146
+ console.log(
147
+ ` 💉 @Autowired: ${target.name}.${prop} = ${autowiredType.name}`,
148
+ );
149
+
150
+ const isOptional = Reflect.getMetadata(
151
+ METADATA_KEYS.OPTIONAL,
152
+ currentProto,
153
+ prop,
154
+ );
155
+
156
+ try {
157
+ instance[prop] = this.resolve(autowiredType);
158
+ } catch (error) {
159
+ if (!isOptional) {
160
+ throw new Error(
161
+ `Failed to autowire ${autowiredType.name} into ${target.name}.${prop}: ${error}`,
162
+ );
163
+ }
164
+ console.warn(
165
+ ` ⚠️ Optional autowired dependency ${autowiredType.name} not available`,
166
+ );
167
+ instance[prop] = undefined;
168
+ }
169
+ continue;
170
+ }
108
171
 
109
- properties.forEach((prop) => {
110
- // Check for @Autowired
111
- const autowired = Reflect.getMetadata(
112
- METADATA_KEYS.AUTOWIRED,
113
- prototype,
114
- prop,
115
- );
116
- if (autowired) {
117
- instance[prop] = this.resolve(autowired);
118
- return;
119
- }
172
+ // Check @Inject - stores the TOKEN to inject
173
+ const injectToken = Reflect.getMetadata(
174
+ METADATA_KEYS.INJECT,
175
+ currentProto,
176
+ prop,
177
+ );
120
178
 
121
- // Check for @Inject
122
- const inject = Reflect.getMetadata(METADATA_KEYS.INJECT, prototype, prop);
123
- if (inject) {
124
- instance[prop] = this.resolve(inject);
125
- return;
126
- }
179
+ if (injectToken) {
180
+ const tokenName =
181
+ typeof injectToken === "string" ? injectToken : injectToken.name;
182
+ console.log(` 💉 @Inject: ${target.name}.${prop} = ${tokenName}`);
183
+
184
+ const isOptional = Reflect.getMetadata(
185
+ METADATA_KEYS.OPTIONAL,
186
+ currentProto,
187
+ prop,
188
+ );
189
+
190
+ try {
191
+ instance[prop] = this.resolve(injectToken);
192
+ } catch (error) {
193
+ if (!isOptional) {
194
+ throw new Error(
195
+ `Failed to inject ${tokenName} into ${target.name}.${prop}: ${error}`,
196
+ );
197
+ }
198
+ console.warn(
199
+ ` ⚠️ Optional inject dependency ${tokenName} not available`,
200
+ );
201
+ instance[prop] = undefined;
202
+ }
203
+ continue;
204
+ }
127
205
 
128
- // Check for @Value
129
- const value = Reflect.getMetadata(METADATA_KEYS.VALUE, prototype, prop);
130
- if (value !== undefined) {
131
- instance[prop] = this.resolveValue(value);
132
- return;
133
- }
206
+ // Check @Value
207
+ const valueExpression = Reflect.getMetadata(
208
+ METADATA_KEYS.VALUE,
209
+ currentProto,
210
+ prop,
211
+ );
134
212
 
135
- // Check for @InjectRepository - special TypeORM handling
136
- const repositoryEntity = Reflect.getMetadata(
137
- METADATA_KEYS.INJECT_REPOSITORY,
138
- prototype,
139
- prop,
140
- );
141
- if (repositoryEntity) {
142
- instance[prop] = this.resolveRepository(repositoryEntity);
143
- return;
144
- }
145
- });
146
- }
213
+ if (valueExpression !== undefined) {
214
+ console.log(
215
+ ` 🔧 @Value: ${target.name}.${prop} = ${valueExpression}`,
216
+ );
217
+ instance[prop] = this.resolveValue(valueExpression);
218
+ continue;
219
+ }
147
220
 
148
- private getAllPropertyKeys(obj: any): string[] {
149
- const keys = new Set<string>();
150
- let current = obj;
221
+ // Check @InjectRepository
222
+ const repositoryEntity = Reflect.getMetadata(
223
+ METADATA_KEYS.INJECT_REPOSITORY,
224
+ currentProto,
225
+ prop,
226
+ );
151
227
 
152
- while (current && current !== Object.prototype) {
153
- Object.getOwnPropertyNames(current).forEach((key) => keys.add(key));
154
- current = Object.getPrototypeOf(current);
155
- }
228
+ if (repositoryEntity) {
229
+ console.log(
230
+ ` 💾 @InjectRepository: ${target.name}.${prop} = Repository<${repositoryEntity.name}>`,
231
+ );
232
+ instance[prop] = this.resolveRepository(repositoryEntity);
233
+ continue;
234
+ }
235
+
236
+ // Check @Lazy
237
+ const isLazy = Reflect.getMetadata(
238
+ METADATA_KEYS.LAZY,
239
+ currentProto,
240
+ prop,
241
+ );
242
+
243
+ if (isLazy) {
244
+ const type = Reflect.getMetadata("design:type", currentProto, prop);
245
+ console.log(
246
+ ` ⏳ @Lazy: ${target.name}.${prop} (will resolve on access)`,
247
+ );
248
+
249
+ let cached: any = null;
250
+ let resolved = false;
251
+
252
+ Object.defineProperty(instance, prop, {
253
+ get: () => {
254
+ if (!resolved) {
255
+ console.log(
256
+ ` 🔓 Lazy-loading ${type.name} for ${target.name}.${prop}`,
257
+ );
258
+ cached = this.resolve(type);
259
+ resolved = true;
260
+ }
261
+ return cached;
262
+ },
263
+ enumerable: true,
264
+ configurable: true,
265
+ });
266
+ continue;
267
+ }
268
+ }
156
269
 
157
- return Array.from(keys);
270
+ currentProto = Object.getPrototypeOf(currentProto);
271
+ }
158
272
  }
159
273
 
160
274
  private resolveValue(expression: string): any {
161
- // Handle environment variable syntax: ${VAR_NAME} or $VAR_NAME
162
- const match = expression.match(/\$\{([^}]+)\}|\$([A-Z_][A-Z0-9_]*)/i);
275
+ const match = expression.match(
276
+ /\$\{([^:}]+)(?::([^}]*))?\}|\$([A-Z_][A-Z0-9_]*)/i,
277
+ );
163
278
  if (match) {
164
- const key = match[1] || match[2];
279
+ const key = match[1] || match[3];
280
+ const defaultValue = match[2];
165
281
  const value = process.env[key];
166
282
 
167
283
  if (value === undefined) {
168
- console.warn(
169
- `⚠️ Warning: Environment variable "${key}" is not defined`,
170
- );
284
+ if (defaultValue !== undefined) {
285
+ return defaultValue;
286
+ }
287
+ console.warn(`⚠️ Environment variable "${key}" not defined`);
171
288
  return "";
172
289
  }
173
290
 
174
- // Try to parse as JSON for complex types
291
+ // Parse JSON
175
292
  if (value.startsWith("{") || value.startsWith("[")) {
176
293
  try {
177
294
  return JSON.parse(value);
@@ -181,8 +298,8 @@ export class DIContainer {
181
298
  }
182
299
 
183
300
  // Parse numbers
184
- if (/^\d+$/.test(value)) {
185
- return parseInt(value, 10);
301
+ if (/^\d+(\.\d+)?$/.test(value)) {
302
+ return parseFloat(value);
186
303
  }
187
304
 
188
305
  // Parse booleans
@@ -197,24 +314,23 @@ export class DIContainer {
197
314
 
198
315
  private resolveRepository(entity: any): any {
199
316
  try {
200
- // Import TypeORM module dynamically to avoid circular dependencies
201
317
  const { TypeORMModule } = require("../../typeorm/typeorm-module");
202
318
  const dataSource = TypeORMModule.getDataSource();
319
+
320
+ if (!dataSource || !dataSource.isInitialized) {
321
+ throw new Error("TypeORM DataSource not initialized");
322
+ }
323
+
203
324
  return dataSource.getRepository(entity);
204
325
  } catch (error) {
205
326
  throw new Error(
206
- `Failed to resolve repository for entity ${entity.name}: ${(error as Error)?.message}. ` +
207
- `Make sure TypeORM is initialized before using @InjectRepository.`,
327
+ `Failed to resolve repository for ${entity.name}: ${(error as Error)?.message}`,
208
328
  );
209
329
  }
210
330
  }
211
331
 
212
332
  has(token: any): boolean {
213
- return (
214
- this.singletons.has(token) ||
215
- this.factories.has(token) ||
216
- typeof token === "function"
217
- );
333
+ return this.registered.has(token);
218
334
  }
219
335
 
220
336
  getAllInstances(): any[] {
@@ -226,9 +342,9 @@ export class DIContainer {
226
342
  this.transients.clear();
227
343
  this.factories.clear();
228
344
  this.constructing.clear();
345
+ this.registered.clear();
229
346
  }
230
347
 
231
- // For testing purposes
232
348
  reset(): void {
233
349
  this.clear();
234
350
  }
@@ -1,5 +1,4 @@
1
1
  import { METADATA_KEYS } from "../metadata/metadata-keys";
2
- import { MetadataStorage } from "../metadata/metadata-storage";
3
2
 
4
3
  export interface ApplicationOptions {
5
4
  port?: number;
@@ -9,28 +8,19 @@ export interface ApplicationOptions {
9
8
  }
10
9
 
11
10
  export function FragmentApplication(
12
- options: {
13
- port?: number;
14
- host?: string;
15
- configPath?: string;
16
- autoScan?: boolean;
17
- } = {},
11
+ options: ApplicationOptions = {},
18
12
  ): ClassDecorator {
19
13
  return (target: any) => {
20
14
  Reflect.defineMetadata(
21
15
  METADATA_KEYS.APPLICATION,
22
16
  {
17
+ ...options,
23
18
  port: options.port || 3000,
24
19
  host: options.host || "0.0.0.0",
25
20
  configPath: options.configPath || "fragment.json",
26
- autoScan: options.autoScan ?? true,
21
+ autoScan: options.autoScan || true,
27
22
  },
28
23
  target,
29
24
  );
30
- MetadataStorage.getInstance().addClass({
31
- target,
32
- type: "injectable",
33
- scope: "singleton",
34
- });
35
25
  };
36
26
  }
@@ -1,15 +1,17 @@
1
- import { Injectable } from "./injectable.decorator";
2
- import { METADATA_KEYS } from "../metadata/metadata-keys";
3
- import { MetadataStorage } from "../metadata/metadata-storage";
1
+ import { Injectable } from './injectable.decorator';
2
+ import { METADATA_KEYS } from '../metadata/metadata-keys';
3
+ import { MetadataStorage } from '../metadata/metadata-storage';
4
4
 
5
5
  export function AutoConfiguration(): ClassDecorator {
6
6
  return (target: any) => {
7
- Injectable("singleton")(target);
7
+ Injectable('singleton')(target);
8
8
  Reflect.defineMetadata(METADATA_KEYS.AUTO_CONFIGURATION, true, target);
9
- MetadataStorage.getInstance().addClass({
9
+
10
+ const storage = MetadataStorage.getInstance();
11
+ storage.addClass({
10
12
  target,
11
- type: "auto-configuration",
12
- scope: "singleton",
13
+ type: 'auto-configuration',
14
+ scope: 'singleton'
13
15
  });
14
16
  };
15
- }
17
+ }
@@ -1,55 +1,19 @@
1
- import { METADATA_KEYS } from "../metadata/metadata-keys";
2
- import { MetadataStorage } from "../metadata/metadata-storage";
3
-
4
- /**
5
- * Conditional decorators
6
- * Ensures the class is registered in MetadataStorage
7
- */
1
+ import { METADATA_KEYS } from '../metadata/metadata-keys';
8
2
 
9
3
  export function ConditionalOnClass(classRef: any): ClassDecorator {
10
4
  return (target: any) => {
11
5
  Reflect.defineMetadata(METADATA_KEYS.CONDITIONAL_ON_CLASS, classRef, target);
12
-
13
- // Register in MetadataStorage if not already
14
- const storage = MetadataStorage.getInstance();
15
- if (!storage.getClass(target)) {
16
- storage.addClass({
17
- target,
18
- type: 'injectable', // default type
19
- scope: 'singleton',
20
- });
21
- }
22
6
  };
23
7
  }
24
8
 
25
9
  export function ConditionalOnMissingBean(token: any): ClassDecorator {
26
10
  return (target: any) => {
27
11
  Reflect.defineMetadata(METADATA_KEYS.CONDITIONAL_ON_MISSING_BEAN, token, target);
28
-
29
- // Register in MetadataStorage if not already
30
- const storage = MetadataStorage.getInstance();
31
- if (!storage.getClass(target)) {
32
- storage.addClass({
33
- target,
34
- type: 'injectable',
35
- scope: 'singleton',
36
- });
37
- }
38
12
  };
39
13
  }
40
14
 
41
15
  export function ConditionalOnProperty(key: string, expectedValue?: any): ClassDecorator {
42
16
  return (target: any) => {
43
17
  Reflect.defineMetadata(METADATA_KEYS.CONDITIONAL_ON_PROPERTY, { key, expectedValue }, target);
44
-
45
- // Register in MetadataStorage if not already
46
- const storage = MetadataStorage.getInstance();
47
- if (!storage.getClass(target)) {
48
- storage.addClass({
49
- target,
50
- type: 'injectable',
51
- scope: 'singleton',
52
- });
53
- }
54
18
  };
55
19
  }
@@ -6,7 +6,9 @@ export function Controller(path: string = ""): ClassDecorator {
6
6
  return (target: any) => {
7
7
  Injectable("singleton")(target);
8
8
  Reflect.defineMetadata(METADATA_KEYS.CONTROLLER, path, target);
9
- MetadataStorage.getInstance().addClass({
9
+
10
+ const storage = MetadataStorage.getInstance();
11
+ storage.addClass({
10
12
  target,
11
13
  type: "controller",
12
14
  scope: "singleton",
@@ -1,20 +1,33 @@
1
- import "reflect-metadata";
2
1
  import { METADATA_KEYS } from "../metadata/metadata-keys";
3
2
  import { MetadataStorage } from "../metadata/metadata-storage";
4
3
 
5
4
  function createHttpMethodDecorator(method: string) {
6
5
  return (path: string = ""): MethodDecorator => {
7
- return (target: any, propertyKey: string | symbol) => {
8
- Reflect.defineMetadata(METADATA_KEYS.HTTP_METHOD, method, target, propertyKey);
9
- Reflect.defineMetadata(METADATA_KEYS.ROUTE_PATH, path, target, propertyKey);
6
+ return (
7
+ target: any,
8
+ propertyKey: string | symbol,
9
+ descriptor: PropertyDescriptor,
10
+ ) => {
11
+ Reflect.defineMetadata(
12
+ METADATA_KEYS.HTTP_METHOD,
13
+ method,
14
+ target,
15
+ propertyKey,
16
+ );
17
+ Reflect.defineMetadata(
18
+ METADATA_KEYS.ROUTE_PATH,
19
+ path,
20
+ target,
21
+ propertyKey,
22
+ );
10
23
 
11
24
  const storage = MetadataStorage.getInstance();
12
25
  storage.addMethod({
13
26
  target: target.constructor,
14
- propertyKey: propertyKey.toString(),
27
+ propertyKey: propertyKey as string,
15
28
  method,
16
29
  path,
17
- paramMetadata: storage.getParams(target, propertyKey.toString()),
30
+ paramMetadata: storage.getParams(target, propertyKey as string),
18
31
  });
19
32
  };
20
33
  };
@@ -26,15 +39,23 @@ export const Put = createHttpMethodDecorator("PUT");
26
39
  export const Delete = createHttpMethodDecorator("DELETE");
27
40
  export const Patch = createHttpMethodDecorator("PATCH");
28
41
 
29
- // Parameter decorators
30
42
  function createParamDecorator(type: string) {
31
43
  return (paramName?: string): ParameterDecorator => {
32
- return (target, propertyKey, index) => {
33
- if (!propertyKey) throw new Error(`@${type} cannot be used on constructor params`);
34
- MetadataStorage.getInstance().addParam({
44
+ return (
45
+ target: Object,
46
+ propertyKey: string | symbol | undefined,
47
+ parameterIndex: number,
48
+ ): void => {
49
+ if (!propertyKey) {
50
+ throw new Error(`@${type}() cannot be used on constructor parameters`);
51
+ }
52
+
53
+ const storage = MetadataStorage.getInstance();
54
+
55
+ storage.addParam({
35
56
  target,
36
57
  propertyKey: propertyKey.toString(),
37
- index,
58
+ index: parameterIndex,
38
59
  type: type as any,
39
60
  paramName,
40
61
  });
@@ -1,25 +1,18 @@
1
- import { METADATA_KEYS } from "../metadata/metadata-keys";
2
- import { MetadataStorage } from "../metadata/metadata-storage";
1
+ import { METADATA_KEYS } from '../metadata/metadata-keys';
2
+ import { MetadataStorage } from '../metadata/metadata-storage';
3
3
 
4
4
  export type Scope = 'singleton' | 'request' | 'transient';
5
5
 
6
- /**
7
- * @Injectable - Marks a class as injectable with a scope
8
- */
9
6
  export function Injectable(scope: Scope = 'singleton'): ClassDecorator {
10
7
  return (target: any) => {
11
- // Define Reflect metadata
12
8
  Reflect.defineMetadata(METADATA_KEYS.INJECTABLE, true, target);
13
9
  Reflect.defineMetadata(METADATA_KEYS.SCOPE, scope, target);
14
-
15
- // Register in MetadataStorage if not already
10
+
16
11
  const storage = MetadataStorage.getInstance();
17
- if (!storage.getClass(target)) {
18
- storage.addClass({
19
- target,
20
- type: 'injectable',
21
- scope
22
- });
23
- }
12
+ storage.addClass({
13
+ target,
14
+ type: 'injectable',
15
+ scope
16
+ });
24
17
  };
25
18
  }