exisjs 0.7.0 → 0.7.1

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 (91) hide show
  1. package/README.md +9 -3
  2. package/dist/config/config.d.ts.map +1 -1
  3. package/dist/config/config.js +4 -3
  4. package/dist/config/config.js.map +1 -1
  5. package/dist/config/env.d.ts.map +1 -1
  6. package/dist/config/env.js +3 -2
  7. package/dist/config/env.js.map +1 -1
  8. package/dist/decorators/constants.d.ts +7 -0
  9. package/dist/decorators/constants.d.ts.map +1 -1
  10. package/dist/decorators/constants.js +2 -1
  11. package/dist/decorators/constants.js.map +1 -1
  12. package/dist/decorators/core/metadata.d.ts +31 -0
  13. package/dist/decorators/core/metadata.d.ts.map +1 -1
  14. package/dist/decorators/core/metadata.js +110 -1
  15. package/dist/decorators/core/metadata.js.map +1 -1
  16. package/dist/decorators/core.d.ts +16 -2
  17. package/dist/decorators/core.d.ts.map +1 -1
  18. package/dist/decorators/core.js +22 -0
  19. package/dist/decorators/core.js.map +1 -1
  20. package/dist/decorators/middleware.d.ts +17 -7
  21. package/dist/decorators/middleware.d.ts.map +1 -1
  22. package/dist/decorators/middleware.js +111 -183
  23. package/dist/decorators/middleware.js.map +1 -1
  24. package/dist/decorators/params.d.ts +54 -12
  25. package/dist/decorators/params.d.ts.map +1 -1
  26. package/dist/decorators/params.js +40 -20
  27. package/dist/decorators/params.js.map +1 -1
  28. package/dist/decorators/routing.d.ts.map +1 -1
  29. package/dist/decorators/routing.js +2 -1
  30. package/dist/decorators/routing.js.map +1 -1
  31. package/dist/di/container.d.ts +5 -1
  32. package/dist/di/container.d.ts.map +1 -1
  33. package/dist/di/container.js +56 -8
  34. package/dist/di/container.js.map +1 -1
  35. package/dist/di/decorators.d.ts +30 -0
  36. package/dist/di/decorators.d.ts.map +1 -0
  37. package/dist/di/decorators.js +59 -0
  38. package/dist/di/decorators.js.map +1 -0
  39. package/dist/di/index.d.ts +1 -0
  40. package/dist/di/index.d.ts.map +1 -1
  41. package/dist/di/index.js +4 -1
  42. package/dist/di/index.js.map +1 -1
  43. package/dist/di/inject.d.ts +2 -1
  44. package/dist/di/inject.d.ts.map +1 -1
  45. package/dist/di/inject.js.map +1 -1
  46. package/dist/docs/content/decorators.mdx +44 -10
  47. package/dist/docs/content/modules.mdx +22 -1
  48. package/dist/docs/content/providers.mdx +33 -1
  49. package/dist/docs/reference/decorators.mdx +14 -3
  50. package/dist/docs/reference/di.mdx +35 -11
  51. package/dist/docs/reference/logger.mdx +100 -0
  52. package/dist/docs/reference/router.mdx +9 -9
  53. package/dist/error/errors.d.ts.map +1 -1
  54. package/dist/error/errors.js +3 -5
  55. package/dist/error/errors.js.map +1 -1
  56. package/dist/index.d.ts +0 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +1 -7
  59. package/dist/index.js.map +1 -1
  60. package/dist/middleware/rate-limit.d.ts +0 -2
  61. package/dist/middleware/rate-limit.d.ts.map +1 -1
  62. package/dist/middleware/rate-limit.js +2 -14
  63. package/dist/middleware/rate-limit.js.map +1 -1
  64. package/dist/module/index.d.ts +2 -0
  65. package/dist/module/index.d.ts.map +1 -1
  66. package/dist/module/index.js +3 -1
  67. package/dist/module/index.js.map +1 -1
  68. package/dist/plugin/manager.d.ts +1 -1
  69. package/dist/plugin/manager.d.ts.map +1 -1
  70. package/dist/plugin/manager.js +47 -0
  71. package/dist/plugin/manager.js.map +1 -1
  72. package/dist/router/controller-registrar.d.ts.map +1 -1
  73. package/dist/router/controller-registrar.js +33 -6
  74. package/dist/router/controller-registrar.js.map +1 -1
  75. package/dist/router/route-builder.d.ts.map +1 -1
  76. package/dist/router/route-builder.js +2 -1
  77. package/dist/router/route-builder.js.map +1 -1
  78. package/dist/router/route-scanner.d.ts.map +1 -1
  79. package/dist/router/route-scanner.js +10 -11
  80. package/dist/router/route-scanner.js.map +1 -1
  81. package/dist/server/app.d.ts +1 -1
  82. package/dist/server/app.d.ts.map +1 -1
  83. package/dist/server/app.js +1 -1
  84. package/dist/server/app.js.map +1 -1
  85. package/dist/server/response.d.ts.map +1 -1
  86. package/dist/server/response.js +2 -1
  87. package/dist/server/response.js.map +1 -1
  88. package/dist/utils/logger.d.ts.map +1 -1
  89. package/dist/utils/logger.js +0 -1
  90. package/dist/utils/logger.js.map +1 -1
  91. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  export type ProviderToken<T = any> = string | symbol | (new (...args: any[]) => T);
2
2
  export interface BaseProvider {
3
- scope?: 'singleton' | 'request';
3
+ scope?: 'singleton' | 'request' | 'transient';
4
4
  }
5
5
  export interface ValueProvider<T> extends BaseProvider {
6
6
  useValue: T;
@@ -12,11 +12,15 @@ export interface ClassProvider<T> extends BaseProvider {
12
12
  useClass: new (...args: any[]) => T;
13
13
  }
14
14
  export type ProviderDefinition<T> = ValueProvider<T> | FactoryProvider<T> | ClassProvider<T> | T;
15
+ export declare const INJECT_METADATA: unique symbol;
16
+ export declare const OPTIONAL_METADATA: unique symbol;
17
+ export declare const SCOPE_METADATA: unique symbol;
15
18
  export declare class Container {
16
19
  private providers;
17
20
  private singletonCache;
18
21
  provide<T>(token: ProviderToken<T>, provider: ProviderDefinition<T>): void;
19
22
  clearCache(): void;
23
+ instantiateClass<T>(TargetClass: new (...args: any[]) => T, requestCache?: Map<ProviderToken, any>): T;
20
24
  resolve<T>(token: ProviderToken<T>, requestCache?: Map<ProviderToken, any>): T;
21
25
  }
22
26
  //# sourceMappingURL=container.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,GAAG,IAC/B,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAE/C,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAChC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,YAAY;IACpD,QAAQ,EAAE,CAAC,CAAA;CACZ;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,CAAE,SAAQ,YAAY;IACtD,UAAU,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,YAAY;IACpD,QAAQ,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;CACpC;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAC9B,aAAa,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAE9D,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,cAAc,CAAgC;IAEtD,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,IAAI;IAK1E,UAAU,IAAI,IAAI;IAIlB,OAAO,CAAC,CAAC,EACP,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,YAAY,CAAC,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,GACrC,CAAC;CAgFL"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,GAAG,IAC/B,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;AAE/C,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,CAAA;CAC9C;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,YAAY;IACpD,QAAQ,EAAE,CAAC,CAAA;CACZ;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,CAAE,SAAQ,YAAY;IACtD,UAAU,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,YAAY;IACpD,QAAQ,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;CACpC;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAC9B,aAAa,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAE9D,eAAO,MAAM,eAAe,eAAqC,CAAA;AACjE,eAAO,MAAM,iBAAiB,eAAuC,CAAA;AACrE,eAAO,MAAM,cAAc,eAA6B,CAAA;AAExD,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,cAAc,CAAgC;IAEtD,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,IAAI;IAK1E,UAAU,IAAI,IAAI;IAIlB,gBAAgB,CAAC,CAAC,EAChB,WAAW,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EACtC,YAAY,CAAC,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,GACrC,CAAC;IAmDJ,OAAO,CAAC,CAAC,EACP,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,YAAY,CAAC,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,GACrC,CAAC;CA6FL"}
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Container = void 0;
3
+ exports.Container = exports.SCOPE_METADATA = exports.OPTIONAL_METADATA = exports.INJECT_METADATA = void 0;
4
+ exports.INJECT_METADATA = Symbol.for('exisjs:inject_tokens');
5
+ exports.OPTIONAL_METADATA = Symbol.for('exisjs:optional_tokens');
6
+ exports.SCOPE_METADATA = Symbol.for('exisjs:scope');
4
7
  class Container {
5
8
  constructor() {
6
9
  this.providers = new Map();
@@ -13,6 +16,46 @@ class Container {
13
16
  clearCache() {
14
17
  this.singletonCache.clear();
15
18
  }
19
+ instantiateClass(TargetClass, requestCache) {
20
+ const injectTokens = TargetClass[exports.INJECT_METADATA] ||
21
+ (TargetClass.prototype &&
22
+ TargetClass.prototype[exports.INJECT_METADATA]) ||
23
+ {};
24
+ const optionalParams = TargetClass[exports.OPTIONAL_METADATA] ||
25
+ (TargetClass.prototype &&
26
+ TargetClass.prototype[exports.OPTIONAL_METADATA]) ||
27
+ new Set();
28
+ const maxIndex = Object.keys(injectTokens).reduce((max, curr) => Math.max(max, Number(curr)), -1);
29
+ if (maxIndex === -1 && optionalParams.size === 0) {
30
+ return new TargetClass();
31
+ }
32
+ const args = [];
33
+ const totalParams = Math.max(maxIndex + 1, ...Array.from(optionalParams).map((idx) => idx + 1));
34
+ for (let i = 0; i < totalParams; i++) {
35
+ const token = injectTokens[i];
36
+ const isOptional = optionalParams.has(i);
37
+ if (token !== undefined) {
38
+ try {
39
+ args.push(this.resolve(token, requestCache));
40
+ }
41
+ catch (err) {
42
+ if (isOptional) {
43
+ args.push(undefined);
44
+ }
45
+ else {
46
+ throw err;
47
+ }
48
+ }
49
+ }
50
+ else if (isOptional) {
51
+ args.push(undefined);
52
+ }
53
+ else {
54
+ args.push(undefined);
55
+ }
56
+ }
57
+ return new TargetClass(...args);
58
+ }
16
59
  resolve(token, requestCache) {
17
60
  // 1. Check singleton cache
18
61
  if (this.singletonCache.has(token)) {
@@ -26,9 +69,11 @@ class Container {
26
69
  if (provider === undefined) {
27
70
  if (typeof token === 'function') {
28
71
  try {
29
- const SCOPE_METADATA = Symbol.for('exisjs:scope');
30
- const scope = token.prototype[SCOPE_METADATA] || 'singleton';
31
- const instance = new token();
72
+ const scope = token.prototype?.[exports.SCOPE_METADATA] || 'singleton';
73
+ const instance = this.instantiateClass(token, requestCache);
74
+ if (scope === 'transient') {
75
+ return instance;
76
+ }
32
77
  if (scope === 'request') {
33
78
  if (!requestCache) {
34
79
  throw new Error(`Cannot resolve request-scoped provider '${String(token)}' outside of a request context. This usually happens if you try to resolve a request-scoped dependency during app startup, inside a background job, or without passing the request context cache.`);
@@ -52,8 +97,8 @@ class Container {
52
97
  let resolvedValue;
53
98
  let scope = 'singleton';
54
99
  if (provider && typeof provider === 'object') {
55
- if ('scope' in provider && provider.scope === 'request') {
56
- scope = 'request';
100
+ if ('scope' in provider && provider.scope) {
101
+ scope = provider.scope;
57
102
  }
58
103
  if ('useValue' in provider) {
59
104
  resolvedValue = provider.useValue;
@@ -62,15 +107,18 @@ class Container {
62
107
  resolvedValue = provider.useFactory();
63
108
  }
64
109
  else if ('useClass' in provider) {
65
- resolvedValue = new provider.useClass();
110
+ resolvedValue = this.instantiateClass(provider.useClass, requestCache);
66
111
  }
67
112
  else {
68
- resolvedValue = provider; // For T that happens to be an object without these keys
113
+ resolvedValue = provider;
69
114
  }
70
115
  }
71
116
  else {
72
117
  resolvedValue = provider;
73
118
  }
119
+ if (scope === 'transient') {
120
+ return resolvedValue;
121
+ }
74
122
  if (scope === 'request') {
75
123
  if (!requestCache) {
76
124
  throw new Error(`Cannot resolve request-scoped provider '${String(token)}' outside of a request context. This usually happens if you try to resolve a request-scoped dependency during app startup, inside a background job, or without passing the request context cache.`);
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { ProviderToken } from './container';
2
+ /**
3
+ * Parameter decorator that specifies a dependency token to inject into a class constructor.
4
+ *
5
+ * Example:
6
+ * ```ts
7
+ * class UserService {
8
+ * constructor(
9
+ * @Inject('DATABASE_URL') private dbUrl: string,
10
+ * @Inject(LoggerService) private logger: LoggerService
11
+ * ) {}
12
+ * }
13
+ * ```
14
+ */
15
+ export declare function Inject(token: ProviderToken): ParameterDecorator;
16
+ /**
17
+ * Parameter decorator that marks a dependency as optional.
18
+ * If the dependency cannot be resolved, `undefined` is injected instead of throwing an error.
19
+ *
20
+ * Example:
21
+ * ```ts
22
+ * class NotificationService {
23
+ * constructor(
24
+ * @Inject('SLACK_WEBHOOK') @Optional() private webhook?: string
25
+ * ) {}
26
+ * }
27
+ * ```
28
+ */
29
+ export declare function Optional(): ParameterDecorator;
30
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../src/di/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,aAAa,EAAE,MAAM,aAAa,CAAA;AAE/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,kBAAkB,CAmB/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,IAAI,kBAAkB,CAkB7C"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Inject = Inject;
4
+ exports.Optional = Optional;
5
+ const container_1 = require("./container");
6
+ /**
7
+ * Parameter decorator that specifies a dependency token to inject into a class constructor.
8
+ *
9
+ * Example:
10
+ * ```ts
11
+ * class UserService {
12
+ * constructor(
13
+ * @Inject('DATABASE_URL') private dbUrl: string,
14
+ * @Inject(LoggerService) private logger: LoggerService
15
+ * ) {}
16
+ * }
17
+ * ```
18
+ */
19
+ function Inject(token) {
20
+ return function (target, propertyKey, parameterIndex) {
21
+ // For constructor parameters, propertyKey is undefined and target is the constructor function
22
+ const targetObj = propertyKey !== undefined ? target : target;
23
+ const currentTokens = (targetObj[container_1.INJECT_METADATA] =
24
+ targetObj[container_1.INJECT_METADATA] || {});
25
+ currentTokens[parameterIndex] = token;
26
+ // Also attach to prototype just in case target varies across transpiler settings
27
+ if (targetObj.prototype) {
28
+ const protoTokens = (targetObj.prototype[container_1.INJECT_METADATA] =
29
+ targetObj.prototype[container_1.INJECT_METADATA] || {});
30
+ protoTokens[parameterIndex] = token;
31
+ }
32
+ };
33
+ }
34
+ /**
35
+ * Parameter decorator that marks a dependency as optional.
36
+ * If the dependency cannot be resolved, `undefined` is injected instead of throwing an error.
37
+ *
38
+ * Example:
39
+ * ```ts
40
+ * class NotificationService {
41
+ * constructor(
42
+ * @Inject('SLACK_WEBHOOK') @Optional() private webhook?: string
43
+ * ) {}
44
+ * }
45
+ * ```
46
+ */
47
+ function Optional() {
48
+ return function (target, propertyKey, parameterIndex) {
49
+ const targetObj = propertyKey !== undefined ? target : target;
50
+ const currentOptional = (targetObj[container_1.OPTIONAL_METADATA] =
51
+ targetObj[container_1.OPTIONAL_METADATA] || new Set());
52
+ currentOptional.add(parameterIndex);
53
+ if (targetObj.prototype) {
54
+ const protoOptional = (targetObj.prototype[container_1.OPTIONAL_METADATA] = targetObj.prototype[container_1.OPTIONAL_METADATA] || new Set());
55
+ protoOptional.add(parameterIndex);
56
+ }
57
+ };
58
+ }
59
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../src/di/decorators.ts"],"names":[],"mappings":";;AAeA,wBAmBC;AAeD,4BAkBC;AAnED,2CAA+E;AAE/E;;;;;;;;;;;;GAYG;AACH,SAAgB,MAAM,CAAC,KAAoB;IACzC,OAAO,UACL,MAAW,EACX,WAAwC,EACxC,cAAsB;QAEtB,8FAA8F;QAC9F,MAAM,SAAS,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;QAC7D,MAAM,aAAa,GAAG,CAAC,SAAS,CAAC,2BAAe,CAAC;YAC/C,SAAS,CAAC,2BAAe,CAAC,IAAI,EAAE,CAAC,CAAA;QACnC,aAAa,CAAC,cAAc,CAAC,GAAG,KAAK,CAAA;QAErC,iFAAiF;QACjF,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;YACxB,MAAM,WAAW,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,2BAAe,CAAC;gBACvD,SAAS,CAAC,SAAS,CAAC,2BAAe,CAAC,IAAI,EAAE,CAAC,CAAA;YAC7C,WAAW,CAAC,cAAc,CAAC,GAAG,KAAK,CAAA;QACrC,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,QAAQ;IACtB,OAAO,UACL,MAAW,EACX,WAAwC,EACxC,cAAsB;QAEtB,MAAM,SAAS,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;QAC7D,MAAM,eAAe,GAAgB,CAAC,SAAS,CAAC,6BAAiB,CAAC;YAChE,SAAS,CAAC,6BAAiB,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC,CAAA;QACpD,eAAe,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAEnC,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;YACxB,MAAM,aAAa,GAAgB,CAAC,SAAS,CAAC,SAAS,CACrD,6BAAiB,CAClB,GAAG,SAAS,CAAC,SAAS,CAAC,6BAAiB,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC,CAAA;YAChE,aAAa,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QACnC,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { inject } from './inject';
2
2
  export { Container } from './container';
3
+ export { Inject, Optional } from './decorators';
3
4
  export type { ProviderToken, ProviderDefinition, ValueProvider, FactoryProvider, ClassProvider, } from './container';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/di/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,aAAa,GACd,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/di/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC/C,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,aAAa,GACd,MAAM,aAAa,CAAA"}
package/dist/di/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Container = exports.inject = void 0;
3
+ exports.Optional = exports.Inject = exports.Container = exports.inject = void 0;
4
4
  var inject_1 = require("./inject");
5
5
  Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return inject_1.inject; } });
6
6
  var container_1 = require("./container");
7
7
  Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return container_1.Container; } });
8
+ var decorators_1 = require("./decorators");
9
+ Object.defineProperty(exports, "Inject", { enumerable: true, get: function () { return decorators_1.Inject; } });
10
+ Object.defineProperty(exports, "Optional", { enumerable: true, get: function () { return decorators_1.Optional; } });
8
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/di/index.ts"],"names":[],"mappings":";;;AAAA,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,yCAAuC;AAA9B,sGAAA,SAAS,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/di/index.ts"],"names":[],"mappings":";;;AAAA,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,yCAAuC;AAA9B,sGAAA,SAAS,OAAA;AAClB,2CAA+C;AAAtC,oGAAA,MAAM,OAAA;AAAE,sGAAA,QAAQ,OAAA"}
@@ -1,3 +1,4 @@
1
+ import type { ProviderToken } from './container';
1
2
  /**
2
3
  * Injects a dependency from the Exis Application container.
3
4
  * Must be called inside an active route or middleware context.
@@ -15,5 +16,5 @@
15
16
  * @return {T} The resolved dependency
16
17
  * @public
17
18
  */
18
- export declare function inject<T>(token: (new (...args: any[]) => T) | string): T;
19
+ export declare function inject<T>(token: ProviderToken<T>): T;
19
20
  //# sourceMappingURL=inject.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inject.d.ts","sourceRoot":"","sources":["../../src/di/inject.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,CAQxE"}
1
+ {"version":3,"file":"inject.d.ts","sourceRoot":"","sources":["../../src/di/inject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAQpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/di/inject.ts"],"names":[],"mappings":";;AAmBA,wBAQC;AA3BD,+CAAoD;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,MAAM,CAAI,KAA2C;IACnE,MAAM,KAAK,GAAG,0BAAgB,CAAC,QAAQ,EAAE,CAAA;IACzC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;AAChD,CAAC"}
1
+ {"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/di/inject.ts"],"names":[],"mappings":";;AAoBA,wBAQC;AA5BD,+CAAoD;AAGpD;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,MAAM,CAAI,KAAuB;IAC/C,MAAM,KAAK,GAAG,0BAAgB,CAAC,QAAQ,EAAE,CAAA;IACzC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;AAChD,CAAC"}
@@ -132,18 +132,25 @@ export default class UsersController {
132
132
  ### Available Parameter Decorators
133
133
  - `@Body(nameOrPipe?)` — Extracts `req.body` or runs through a validation schema/pipe.
134
134
  - `@Param(nameOrPipe?)` — Extracts a route path param (e.g. `:id`).
135
+ - `@QueryParam(nameOrPipe?)` / `@Query(nameOrPipe?)` — Extracts a query parameter from the URL query string.
135
136
  - `@Headers(nameOrPipe?)` — Extracts a request header by name.
136
137
  - `@HostParam(nameOrPipe?)` — Extracts a host subdomain parameter.
137
138
  - `@Req()` — Injects the raw request object.
138
139
  - `@Res(options?)` — Injects the response object (e.g. `{ passthrough: true }`).
139
140
  - `@Ip()` — Injects the client IP address.
140
141
  - `@Next()` — Injects the `next` function.
142
+ - `createParamDecorator(factory)` — Creates custom reusable route parameter decorators:
143
+ ```typescript
144
+ export const CurrentUser = createParamDecorator((data: string | undefined, ctx) => {
145
+ return data ? ctx.req.user?.[data] : ctx.req.user
146
+ })
147
+ ```
141
148
 
142
149
  ---
143
150
 
144
151
  ## 4. Middleware & Pipeline Decorators
145
152
 
146
- You can bind middleware, rate limiting, and idempotency to controllers or route methods using `@Use()` and utility decorators:
153
+ You can bind middlewares, guards, interceptors, and exception filters to controllers or route methods using the unified `@Use()` decorator:
147
154
 
148
155
  ```typescript filename="src/http/checkout/route.ts"
149
156
  import { Controller, Post, Use, Idempotent } from 'exisjs/decorators'
@@ -161,23 +168,25 @@ export default class CheckoutController {
161
168
  ```
162
169
 
163
170
  ### Available Lifecycle & Middleware Decorators
164
- - `@Use(...middlewares)` — Attaches middleware functions to a class or method.
171
+ - `@Use(...items)` — Unified decorator that attaches middlewares, guards (`canActivate`), interceptors (`intercept`), or exception filters (`catch`) to a class or method.
165
172
  - `@Idempotent(options?)` — Caches responses based on the `Idempotency-Key` header using off-heap native memory.
166
173
 
167
174
  ---
168
175
 
169
- ## 5. Response Modifiers
176
+ ## 5. Response & Metadata Modifiers
170
177
 
171
178
  Modify HTTP response status codes, redirect clients, or set headers declaratively:
172
179
 
173
180
  ```typescript filename="src/http/api/route.ts"
174
- import { Controller, Post, Get, HttpCode, Header, Redirect } from 'exisjs/decorators'
181
+ import { Controller, Post, Get, HttpCode, Header, Redirect, SetMetadata, Reflector } from 'exisjs/decorators'
175
182
 
176
183
  @Controller('/api')
184
+ @SetMetadata('roles', ['admin'])
177
185
  export default class ApiController {
178
186
  @Post('/create')
179
187
  @HttpCode(201) // Forces 201 Created
180
188
  @Header('X-Custom-Version', '2.0')
189
+ @SetMetadata('permissions', ['write:api'])
181
190
  async create() {
182
191
  return { success: true }
183
192
  }
@@ -195,17 +204,19 @@ export default class ApiController {
195
204
  - `@Redirect(url: string, statusCode?: number)` — Redirects the client.
196
205
  - `@Permissions(...permissions: string[])` — Attaches route permissions metadata.
197
206
  - `@Hosts(...hosts: string[])` — Restricts route/controller to specific Host headers.
207
+ - `@SetMetadata(key, value)` — Attaches custom metadata to a class or method.
208
+ - `Reflector` — Static utility to inspect metadata attached via `@SetMetadata` across classes and methods (`Reflector.get`, `Reflector.getAllAndOverride`, `Reflector.getAllAndMerge`).
198
209
 
199
210
  ---
200
211
 
201
- ## 6. Dependency Injection
212
+ ## 6. Dependency Injection & Modules
202
213
 
203
- To mark a service class as an injectable provider for the IoC container, use the `@Injectable()` decorator:
214
+ To mark a service class as an injectable provider for the IoC container, use the `@Injectable()` decorator. ExisJS supports `singleton` (default), `request`, and `transient` scopes:
204
215
 
205
216
  ```typescript filename="src/http/users/service.ts"
206
217
  import { Injectable } from 'exisjs/decorators'
207
218
 
208
- @Injectable({ scope: 'singleton' })
219
+ @Injectable({ scope: 'transient' })
209
220
  export class UserService {
210
221
  async fetchUser(id: string) {
211
222
  return { id, name: 'Alice' }
@@ -213,15 +224,21 @@ export class UserService {
213
224
  }
214
225
  ```
215
226
 
216
- Inside your route classes or controllers, inject services via constructor injection:
227
+ ### Constructor Parameter Injection (`@Inject` & `@Optional`)
228
+
229
+ You can inject dependencies using tokens or classes, and mark optional dependencies with `@Optional()`:
217
230
 
218
231
  ```typescript filename="src/http/users/route.ts"
219
- import { Controller, Get, Param } from 'exisjs/decorators'
232
+ import { Controller, Get, Param, Inject, Optional } from 'exisjs/decorators'
220
233
  import { UserService } from './service'
221
234
 
222
235
  @Controller('/users')
223
236
  export default class UsersController {
224
- constructor(private userService: UserService) {}
237
+ constructor(
238
+ private userService: UserService,
239
+ @Inject('DATABASE_URL') private dbUrl: string,
240
+ @Optional() @Inject('ANALYTICS_SERVICE') private analytics?: any
241
+ ) {}
225
242
 
226
243
  @Get('/:id')
227
244
  async getUser(@Param('id') id: string) {
@@ -229,3 +246,20 @@ export default class UsersController {
229
246
  }
230
247
  }
231
248
  ```
249
+
250
+ ### Application Modules (`@Module`)
251
+
252
+ You can group controllers, providers, and imports into high-level OOP modules using `@Module`:
253
+
254
+ ```typescript filename="src/http/users/users.module.ts"
255
+ import { Module } from 'exisjs/module' // or 'exisjs/decorators'
256
+ import UsersController from './route'
257
+ import { UserService } from './service'
258
+
259
+ @Module({
260
+ controllers: [UsersController],
261
+ providers: [UserService],
262
+ exports: [UserService]
263
+ })
264
+ export class UsersModule {}
265
+ ```
@@ -74,7 +74,28 @@ export default exis({
74
74
  })
75
75
  ```
76
76
 
77
- #### Class-Based (OOP) Paradigm
77
+ ```
78
+
79
+ #### Class-Based (OOP) Paradigm (`@Module`)
80
+
81
+ For applications following the OOP paradigm, you can define modules using the `@Module` class decorator:
82
+
83
+ ```typescript filename="src/modules/auth.module.ts"
84
+ import { Module } from 'exisjs/module'
85
+ import { AuthService } from './auth.service'
86
+ import { AuthController } from './auth.controller'
87
+ import { DatabaseModule } from './database.module'
88
+
89
+ @Module({
90
+ imports: [DatabaseModule],
91
+ controllers: [AuthController],
92
+ providers: [AuthService],
93
+ exports: [AuthService]
94
+ })
95
+ export class AuthModule {}
96
+ ```
97
+
98
+ You can then register it into your server:
78
99
 
79
100
  ```typescript filename="src/http/server.ts"
80
101
  import { Server } from 'exisjs/decorators'
@@ -134,14 +134,46 @@ import { UsersService } from './users/service'
134
134
  export default class RootServer {}
135
135
  ```
136
136
 
137
+ ### Constructor Injection (`@Inject` and `@Optional`)
138
+
139
+ In addition to property-level `inject()`, ExisJS provides full OOP constructor parameter injection with `@Inject(token)` and `@Optional()`:
140
+
141
+ ```typescript filename="src/http/users/route.ts"
142
+ import { Controller, Get, Param, Inject, Optional } from 'exisjs/decorators'
143
+ import { UsersService } from './service'
144
+
145
+ @Controller('/users')
146
+ export default class UsersController {
147
+ constructor(
148
+ private usersService: UsersService,
149
+ @Inject('DATABASE_URL') private dbUrl: string,
150
+ @Optional() @Inject('METRICS') private metrics?: any
151
+ ) {}
152
+
153
+ @Get('/:id')
154
+ async getUser(@Param('id') id: string) {
155
+ return this.usersService.findAll()
156
+ }
157
+ }
158
+ ```
159
+
137
160
  ## Injection Scopes
138
161
 
139
162
  For people coming from different programming language backgrounds, it might be unexpected to learn that in ExisJS, almost everything is shared across incoming requests. We have a connection pool to the database, singleton services with global state, etc. This is because Node.js doesn't follow the request/response Multi-Threaded Stateless Model.
140
163
 
141
- However, there are edge-cases when request-based lifetime may be the desired behavior (e.g., per-request caching in GraphQL, request tracking, multi-tenancy). ExisJS supports scopes directly in the `@Injectable()` decorator:
164
+ However, there are use-cases when request-based or transient lifetime is desired. ExisJS supports three provider scopes in the `@Injectable()` decorator and `app.provide()`:
142
165
 
143
166
  - **Singleton (Default)**: A single instance of the provider is shared across the entire application.
144
167
  - **Request**: A new instance of the provider is created exclusively for each incoming request, and garbage collected afterward.
168
+ - **Transient**: A brand-new instance is created every single time the dependency is requested/injected.
169
+
170
+ ```typescript
171
+ @Injectable({ scope: 'transient' })
172
+ export class OperationContext {
173
+ public readonly id = crypto.randomUUID()
174
+ }
175
+ ```
145
176
 
146
177
  > [!WARNING]
147
178
  > **Request Scope Constraints**: A provider marked as `scope: 'request'` **must** be resolved within the lifecycle of an active HTTP request. If you try to `inject()` or resolve a request-scoped provider during application startup, inside a background queue worker, or from a cron job, the container will throw a runtime error.
179
+
@@ -67,17 +67,19 @@ Parameter decorators automatically extract data from the incoming request and in
67
67
  - `@Body(nameOrPipe?)`: Parses and injects the JSON request body (or passes it through a validator/pipe).
68
68
  - `@Param(nameOrPipe?)`: Injects a URL path parameter (e.g., `id` from `/:id`).
69
69
  - `@Headers(nameOrPipe?)`: Injects an HTTP header by name.
70
+ - `@QueryParam(nameOrPipe?)` / `@Query(nameOrPipe?)`: Injects a URL query parameter (e.g., `?search=term`).
70
71
  - `@HostParam(nameOrPipe?)`: Injects a subdomain parameter.
71
72
  - `@Req()`: Injects the raw ExisJS Request object.
72
73
  - `@Res(options?)`: Injects the raw ExisJS Response object (`{ passthrough: true }`).
73
74
  - `@Ip()`: Injects the client's IP address.
74
75
  - `@Next()`: Injects the Next function.
76
+ - `createParamDecorator(factory)`: Exports a factory to create custom parameter decorators (e.g. `@CurrentUser()`).
75
77
 
76
78
  ---
77
79
 
78
80
  ## 3. Middleware & Pipeline Decorators
79
81
 
80
- - `@Use(...middlewares)`: Binds standard Express-style or ExisJS middleware to a controller class or method.
82
+ - `@Use(...middlewaresOrGuardsOrInterceptorsOrFilters)`: Unified decorator that binds middlewares, guards (`canActivate`), interceptors (`intercept`), or exception filters (`catch`) to a controller class or method.
81
83
  - `@Idempotent(options?)`: Caches responses based on the `Idempotency-Key` header using native in-memory off-heap storage.
82
84
 
83
85
  ---
@@ -90,11 +92,20 @@ Parameter decorators automatically extract data from the incoming request and in
90
92
  - `@Redirect(url: string, statusCode?: number)`: Redirects request to target URL.
91
93
  - `@Permissions(...permissions: string[])`: Sets required permissions on the route.
92
94
  - `@Hosts(...hosts: string[])`: Restricts routes to specific Host headers.
95
+ - `@SetMetadata(key, value)`: Attaches arbitrary custom metadata to a class or handler method, retrievable via `Reflector`.
96
+ - `Reflector`: Utility class to inspect metadata attached by `@SetMetadata`:
97
+ - `Reflector.get(key, target)`: Retrieves metadata for a specific target.
98
+ - `Reflector.getAllAndOverride(key, targets)`: Retrieves metadata, overriding higher-level targets with lower-level targets.
99
+ - `Reflector.getAllAndMerge(key, targets)`: Merges array/object metadata across multiple targets.
93
100
 
94
101
  ---
95
102
 
96
- ## 5. Dependency Injection
103
+ ## 5. Dependency Injection & Modules
97
104
 
98
- - `@Injectable(options?)`: Marks a service class as an IoC provider managed by the container.
105
+ - `@Module({ imports?, controllers?, providers?, exports? })`: OOP class decorator equivalent to `defineModule()` for structuring application modules.
106
+ - `@Injectable(options?)`: Marks a service class as an IoC provider managed by the container. Supports `{ scope: 'singleton' | 'request' | 'transient' }`.
107
+ - `@Inject(token)`: Constructor parameter decorator for injecting dependencies by class, string, or symbol token.
108
+ - `@Optional()`: Constructor parameter decorator resolving missing dependencies to `undefined` without throwing.
99
109
  - `@Boundary(config)`: Marks a class as a folder-scoped Boundary configuring CORS, headers, exclusions, and pipeline steps.
100
110
  - `@Server(config)`: Marks a class as a root server definition.
111
+