@tsed/di 8.0.0 → 8.0.2

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 (125) hide show
  1. package/package.json +10 -9
  2. package/src/common/constants/constants.ts +5 -0
  3. package/src/common/decorators/autoInjectable.spec.ts +135 -0
  4. package/src/common/decorators/autoInjectable.ts +59 -0
  5. package/src/common/decorators/configuration.spec.ts +45 -0
  6. package/src/common/decorators/configuration.ts +34 -0
  7. package/src/common/decorators/constant.spec.ts +67 -0
  8. package/src/common/decorators/constant.ts +72 -0
  9. package/src/common/decorators/controller.spec.ts +43 -0
  10. package/src/common/decorators/controller.ts +64 -0
  11. package/src/common/decorators/inject.spec.ts +298 -0
  12. package/src/common/decorators/inject.ts +144 -0
  13. package/src/common/decorators/injectable.spec.ts +43 -0
  14. package/src/common/decorators/injectable.ts +30 -0
  15. package/src/common/decorators/intercept.spec.ts +103 -0
  16. package/src/common/decorators/intercept.ts +77 -0
  17. package/src/common/decorators/interceptor.ts +17 -0
  18. package/src/common/decorators/lazyInject.spec.ts +53 -0
  19. package/src/common/decorators/lazyInject.ts +91 -0
  20. package/src/common/decorators/module.ts +52 -0
  21. package/src/common/decorators/opts.ts +51 -0
  22. package/src/common/decorators/overrideProvider.spec.ts +30 -0
  23. package/src/common/decorators/overrideProvider.ts +15 -0
  24. package/src/common/decorators/scope.spec.ts +25 -0
  25. package/src/common/decorators/scope.ts +13 -0
  26. package/src/common/decorators/service.ts +14 -0
  27. package/src/common/decorators/useOpts.spec.ts +90 -0
  28. package/src/common/decorators/useOpts.ts +61 -0
  29. package/src/common/decorators/value.spec.ts +59 -0
  30. package/src/common/decorators/value.ts +60 -0
  31. package/src/common/domain/Container.spec.ts +85 -0
  32. package/src/common/domain/Container.ts +80 -0
  33. package/src/common/domain/ControllerProvider.spec.ts +55 -0
  34. package/src/common/domain/ControllerProvider.ts +48 -0
  35. package/src/common/domain/InjectablePropertyType.ts +7 -0
  36. package/src/common/domain/LocalsContainer.spec.ts +20 -0
  37. package/src/common/domain/LocalsContainer.ts +11 -0
  38. package/src/common/domain/Provider.spec.ts +107 -0
  39. package/src/common/domain/Provider.ts +205 -0
  40. package/src/common/domain/ProviderScope.ts +5 -0
  41. package/src/common/domain/ProviderType.ts +8 -0
  42. package/src/common/errors/InjectionError.spec.ts +17 -0
  43. package/src/common/errors/InjectionError.ts +68 -0
  44. package/src/common/errors/InvalidPropertyTokenError.ts +13 -0
  45. package/src/common/fn/configuration.spec.ts +42 -0
  46. package/src/common/fn/configuration.ts +22 -0
  47. package/src/common/fn/constant.spec.ts +37 -0
  48. package/src/common/fn/constant.ts +7 -0
  49. package/src/common/fn/inject.spec.ts +54 -0
  50. package/src/common/fn/inject.ts +29 -0
  51. package/src/common/fn/injectMany.ts +16 -0
  52. package/src/common/fn/injectable.spec.ts +87 -0
  53. package/src/common/fn/injectable.ts +15 -0
  54. package/src/common/fn/injector.ts +26 -0
  55. package/src/common/fn/lazyInject.spec.ts +22 -0
  56. package/src/common/fn/lazyInject.ts +36 -0
  57. package/src/common/fn/localsContainer.ts +55 -0
  58. package/src/common/fn/refValue.spec.ts +47 -0
  59. package/src/common/fn/refValue.ts +33 -0
  60. package/src/common/index.ts +63 -0
  61. package/src/common/integration/async-factory.spec.ts +60 -0
  62. package/src/common/integration/di.spec.ts +153 -0
  63. package/src/common/integration/imports.spec.ts +39 -0
  64. package/src/common/integration/request.spec.ts +79 -0
  65. package/src/common/integration/singleton.spec.ts +125 -0
  66. package/src/common/interfaces/DIConfigurationOptions.ts +29 -0
  67. package/src/common/interfaces/DILogger.ts +13 -0
  68. package/src/common/interfaces/DILoggerOptions.ts +54 -0
  69. package/src/common/interfaces/ImportTokenProviderOpts.ts +29 -0
  70. package/src/common/interfaces/InterceptorContext.ts +13 -0
  71. package/src/common/interfaces/InterceptorMethods.ts +5 -0
  72. package/src/common/interfaces/InvokeOptions.ts +28 -0
  73. package/src/common/interfaces/OnDestroy.ts +3 -0
  74. package/src/common/interfaces/OnInit.ts +3 -0
  75. package/src/common/interfaces/ProviderOpts.ts +57 -0
  76. package/src/common/interfaces/RegistrySettings.ts +11 -0
  77. package/src/common/interfaces/ResolvedInvokeOptions.ts +14 -0
  78. package/src/common/interfaces/TokenProvider.ts +3 -0
  79. package/src/common/interfaces/TokenRoute.ts +6 -0
  80. package/src/common/registries/GlobalProviders.spec.ts +94 -0
  81. package/src/common/registries/GlobalProviders.ts +131 -0
  82. package/src/common/registries/ProviderRegistry.spec.ts +36 -0
  83. package/src/common/registries/ProviderRegistry.ts +32 -0
  84. package/src/common/services/DIConfiguration.spec.ts +60 -0
  85. package/src/common/services/DIConfiguration.ts +140 -0
  86. package/src/common/services/DILogger.spec.ts +22 -0
  87. package/src/common/services/DILogger.ts +8 -0
  88. package/src/common/services/InjectorService.spec.ts +793 -0
  89. package/src/common/services/InjectorService.ts +601 -0
  90. package/src/common/utils/colors.spec.ts +9 -0
  91. package/src/common/utils/colors.ts +11 -0
  92. package/src/common/utils/createContainer.spec.ts +18 -0
  93. package/src/common/utils/createContainer.ts +14 -0
  94. package/src/common/utils/discoverHooks.ts +15 -0
  95. package/src/common/utils/getConfiguration.spec.ts +40 -0
  96. package/src/common/utils/getConfiguration.ts +20 -0
  97. package/src/common/utils/getConstructorDependencies.ts +9 -0
  98. package/src/common/utils/providerBuilder.ts +81 -0
  99. package/src/index.ts +2 -0
  100. package/src/node/decorators/injectContext.spec.ts +60 -0
  101. package/src/node/decorators/injectContext.ts +29 -0
  102. package/src/node/domain/ContextLogger.spec.ts +277 -0
  103. package/src/node/domain/ContextLogger.ts +137 -0
  104. package/src/node/domain/DIContext.spec.ts +103 -0
  105. package/src/node/domain/DIContext.ts +115 -0
  106. package/src/node/fn/context.ts +24 -0
  107. package/src/node/fn/logger.spec.ts +46 -0
  108. package/src/node/fn/logger.ts +17 -0
  109. package/src/node/index.ts +16 -0
  110. package/src/node/integration/async-factory-invoke.spec.ts +58 -0
  111. package/src/node/integration/inject-context.spec.ts +29 -0
  112. package/src/node/integration/invoke-oninit.spec.ts +25 -0
  113. package/src/node/interfaces/AlterRunInContext.ts +3 -0
  114. package/src/node/services/DILogger.spec.ts +24 -0
  115. package/src/node/services/DILogger.ts +6 -0
  116. package/src/node/services/DITest.spec.ts +109 -0
  117. package/src/node/services/DITest.ts +119 -0
  118. package/src/node/utils/asyncHookContext.spec.ts +147 -0
  119. package/src/node/utils/asyncHookContext.ts +48 -0
  120. package/src/node/utils/attachLogger.spec.ts +14 -0
  121. package/src/node/utils/attachLogger.ts +7 -0
  122. package/src/node/utils/setLoggerConfiguration.spec.ts +43 -0
  123. package/src/node/utils/setLoggerConfiguration.ts +7 -0
  124. package/src/node/utils/setLoggerFormat.ts +33 -0
  125. package/src/node/utils/setLoggerLevel.ts +12 -0
@@ -0,0 +1,17 @@
1
+ import {ProviderType} from "../domain/ProviderType.js";
2
+ import {Injectable} from "./injectable.js";
3
+
4
+ /**
5
+ * The decorators `@Interceptor()` declare a new service can be injected in other service or controller on there `constructor`.
6
+ * All services annotated with `@Interceptor()` are constructed one time.
7
+ *
8
+ * > `@Service()` use the `reflect-metadata` to collect and inject service on controllers or other services.
9
+ *
10
+ * @returns {Function}
11
+ * @decorator
12
+ */
13
+ export function Interceptor(): Function {
14
+ return Injectable({
15
+ type: ProviderType.INTERCEPTOR
16
+ });
17
+ }
@@ -0,0 +1,53 @@
1
+ import {catchAsyncError, classOf, nameOf} from "@tsed/core";
2
+
3
+ import {inject} from "../fn/inject.js";
4
+ import {injector} from "../fn/injector.js";
5
+ import type {MyLazyModule} from "./__mock__/lazy.module.js";
6
+ import {Injectable} from "./injectable.js";
7
+ import {LazyInject, OptionalLazyInject} from "./lazyInject.js";
8
+
9
+ describe("LazyInject", () => {
10
+ it("should lazy load module (import)", async () => {
11
+ @Injectable()
12
+ class MyInjectable {
13
+ @LazyInject("MyLazyModule", () => import("./__mock__/lazy.import.module.js"))
14
+ lazy: Promise<MyLazyModule>;
15
+ }
16
+
17
+ const service = inject<MyInjectable>(MyInjectable);
18
+ const nbProviders = injector().getProviders().length;
19
+
20
+ const lazyService = await service.lazy;
21
+
22
+ expect(nameOf(classOf(lazyService))).toEqual("MyLazyModule");
23
+ expect(nbProviders).not.toEqual(injector().getProviders().length);
24
+ });
25
+
26
+ it("should throw an error when the module doesn't exists", async () => {
27
+ @Injectable()
28
+ class MyInjectable {
29
+ // @ts-ignore
30
+ @LazyInject("Lazy", () => import("lazy-module"), {})
31
+ lazy?: Promise<MyLazyModule>;
32
+ }
33
+
34
+ const service = inject<MyInjectable>(MyInjectable);
35
+ const error = await catchAsyncError(() => service.lazy);
36
+
37
+ expect(error?.message).toContain("Failed to load url lazy-module");
38
+ });
39
+
40
+ it("should lazy load optionally a module", async () => {
41
+ @Injectable()
42
+ class MyInjectable {
43
+ // @ts-ignore
44
+ @OptionalLazyInject("default", () => import("lazy-module-optional"))
45
+ lazy?: Promise<MyLazyModule>;
46
+ }
47
+
48
+ const service = inject(MyInjectable);
49
+ const lazyService = await service.lazy;
50
+
51
+ expect(lazyService).toEqual(undefined);
52
+ });
53
+ });
@@ -0,0 +1,91 @@
1
+ import {catchError, isFunction} from "@tsed/core";
2
+
3
+ import {lazyInject, optionalLazyInject} from "../fn/lazyInject.js";
4
+
5
+ function mapOptions(args: any[], optional = false) {
6
+ function wrap(key: string, resolver: any) {
7
+ return async () => {
8
+ try {
9
+ const {[key]: token} = await resolver();
10
+
11
+ return {default: token};
12
+ } catch (er) {
13
+ if (!optional) {
14
+ throw er;
15
+ }
16
+
17
+ return;
18
+ }
19
+ };
20
+ }
21
+
22
+ if (isFunction(args[0])) {
23
+ return wrap("default", args[0]);
24
+ }
25
+
26
+ return wrap(args[0], args[1]);
27
+ }
28
+
29
+ /**
30
+ * Lazy load a provider from his package and invoke only when the provider is used
31
+ *
32
+ * ```typescript
33
+ * import type {PlatformException} from "@tsed/platform-exceptions";
34
+ *
35
+ * @Injectable()
36
+ * export class MyService {
37
+ * @LazyInject(() => import("@tsed/platform-exceptions"))
38
+ * exceptions: Promise<PlatformException>;
39
+ * }
40
+ * ```
41
+ *
42
+ * @param key - The named export to import from the module.
43
+ * @param resolver - A function that returns a promise resolving to the module.
44
+ * @returns {Function}
45
+ * @decorator
46
+ */
47
+ export function LazyInject(resolver: () => Promise<{default: unknown}>): PropertyDecorator;
48
+ export function LazyInject(key: string, resolver: () => Promise<{default: unknown}>): PropertyDecorator;
49
+ export function LazyInject(...args: any[]): PropertyDecorator {
50
+ let resolver = mapOptions(args);
51
+
52
+ return (target: any, propertyKey: string | symbol): any | void => {
53
+ catchError(() => Reflect.deleteProperty(target, propertyKey));
54
+ Reflect.defineProperty(target, propertyKey, {
55
+ async get() {
56
+ return lazyInject<any>(resolver as any);
57
+ }
58
+ });
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Optionally Lazy load a provider from his package and invoke only when the provider is used
64
+ *
65
+ * ```typescript
66
+ * import type {PlatformException} from "@tsed/platform-exceptions";
67
+ *
68
+ * @Injectable()
69
+ * export class MyService {
70
+ * @OptionalLazyInject(() => import("@tsed/platform-exceptions"))
71
+ * exceptions: Promise<PlatformException>;
72
+ * }
73
+ * ```
74
+ * @param key - The named export to import from the module.
75
+ * @param resolver - A function that returns a promise resolving to the module.
76
+ * @decorator
77
+ */
78
+ export function OptionalLazyInject(resolver: () => Promise<{default: unknown}>): PropertyDecorator;
79
+ export function OptionalLazyInject(key: string, resolver: () => Promise<{default: unknown}>): PropertyDecorator;
80
+ export function OptionalLazyInject(...args: any[]): PropertyDecorator {
81
+ const resolver = mapOptions(args, true);
82
+
83
+ return (target: any, propertyKey: string | symbol): any | void => {
84
+ catchError(() => Reflect.deleteProperty(target, propertyKey));
85
+ Reflect.defineProperty(target, propertyKey, {
86
+ async get() {
87
+ return optionalLazyInject(resolver as any);
88
+ }
89
+ });
90
+ };
91
+ }
@@ -0,0 +1,52 @@
1
+ import {useDecorators} from "@tsed/core";
2
+
3
+ import {ProviderScope} from "../domain/ProviderScope.js";
4
+ import {ProviderType} from "../domain/ProviderType.js";
5
+ import {TokenProvider} from "../interfaces/TokenProvider.js";
6
+ import {Configuration} from "./configuration.js";
7
+ import {Injectable} from "./injectable.js";
8
+
9
+ export interface ModuleOptions extends Omit<TsED.Configuration, "scopes"> {
10
+ /**
11
+ * Provider scope
12
+ */
13
+ scope?: ProviderScope;
14
+ /**
15
+ * Providers must be initialized before building this module
16
+ */
17
+ imports?: TokenProvider[];
18
+ /**
19
+ * Explicit token must be injected in the constructor
20
+ */
21
+ deps?: TokenProvider[];
22
+
23
+ /**
24
+ * Additional properties are stored as provider configuration.
25
+ */
26
+ [key: string]: any;
27
+ }
28
+
29
+ /**
30
+ * Declare a new Ts.ED module
31
+ *
32
+ * ## Options
33
+ * - imports: List of Provider which must be built by injector before invoking the module
34
+ * - deps: List of provider must be injected to the module constructor (explicit declaration)
35
+ *
36
+ * @param options
37
+ * @decorator
38
+ */
39
+ export function Module(options: Partial<ModuleOptions> = {}) {
40
+ const {scopes, imports, deps, scope, ...configuration} = options;
41
+
42
+ return useDecorators(
43
+ Configuration(configuration),
44
+ Injectable({
45
+ type: ProviderType.MODULE,
46
+ scope: ProviderScope.SINGLETON,
47
+ imports,
48
+ deps,
49
+ injectable: false
50
+ })
51
+ );
52
+ }
@@ -0,0 +1,51 @@
1
+ import {classOf} from "@tsed/core";
2
+
3
+ import {DI_USE_PARAM_OPTIONS} from "../constants/constants.js";
4
+ import {ProviderScope} from "../domain/ProviderScope.js";
5
+ import {Inject} from "./inject.js";
6
+ import {Scope} from "./scope.js";
7
+
8
+ /**
9
+ * Get instance options. This options depending on his invocation context.
10
+ *
11
+ * ```typescript
12
+ * import {Injectable, Opts, UseOpts} from "@tsed/di";
13
+ *
14
+ * @Injectable()
15
+ * class MyConfigurableService {
16
+ * source: string;
17
+ * constructor(@Opts options: any = {}) {
18
+ * console.log("Hello ", options.source); // log: Hello Service1 then Hello Service2
19
+ *
20
+ * this.source = options.source;
21
+ * }
22
+ * }
23
+ *
24
+ * @Injectable()
25
+ * class MyService1 {
26
+ * constructor(@UseOpts({source: 'Service1'}) service: MyConfigurableService) {
27
+ * console.log(service.source) // log: Service1
28
+ * }
29
+ * }
30
+ *
31
+ * @Injectable()
32
+ * class MyService2 {
33
+ * constructor(@UseOpts({source: 'Service2'}) service: MyConfigurableService) {
34
+ * console.log(service.source) // log: Service2
35
+ * }
36
+ * }
37
+ * ```
38
+ *
39
+ * ::: warning
40
+ * Using @@Opts@@ decorator on a constructor parameter change the Scope of the provider to `ProviderScope.INSTANCE`.
41
+ * :::
42
+ *
43
+ * @param target
44
+ * @param propertyKey
45
+ * @param index
46
+ * @decorator
47
+ */
48
+ export function Opts(target: any, propertyKey: string | symbol | undefined, index: number) {
49
+ Scope(ProviderScope.INSTANCE)(classOf(target));
50
+ Inject(DI_USE_PARAM_OPTIONS)(target, propertyKey, index);
51
+ }
@@ -0,0 +1,30 @@
1
+ import {Provider} from "../domain/Provider.js";
2
+ import type {TokenProvider} from "../interfaces/TokenProvider.js";
3
+ import {GlobalProviders} from "../registries/GlobalProviders.js";
4
+ import {OverrideProvider} from "./overrideProvider.js";
5
+
6
+ describe("OverrideProvider", () => {
7
+ class Test {}
8
+
9
+ class Test2 {}
10
+
11
+ beforeAll(() => {
12
+ vi.spyOn(GlobalProviders, "get");
13
+ });
14
+ it("should use OverrideProvider", () => {
15
+ // GIVEN
16
+ const provider = new Provider(Test);
17
+
18
+ vi.mocked(GlobalProviders.get).mockImplementation((token: TokenProvider) => {
19
+ if (token === Test) {
20
+ return provider;
21
+ }
22
+ });
23
+ // WHEN
24
+ OverrideProvider(Test)(Test2);
25
+
26
+ // THEN
27
+ expect(provider.token).toEqual(Test);
28
+ expect(provider.useClass).toEqual(Test2);
29
+ });
30
+ });
@@ -0,0 +1,15 @@
1
+ import {Type} from "@tsed/core";
2
+
3
+ import {GlobalProviders} from "../registries/GlobalProviders.js";
4
+
5
+ /**
6
+ * Override a provider which is already registered in ProviderRegistry.
7
+ * @returns {Function}
8
+ * @decorators
9
+ * @param originalProvider
10
+ */
11
+ export function OverrideProvider(originalProvider: Type<any>): Function {
12
+ return (target: Type<any>): void => {
13
+ GlobalProviders.get(originalProvider)!.useClass = target;
14
+ };
15
+ }
@@ -0,0 +1,25 @@
1
+ import {Store} from "@tsed/core";
2
+
3
+ import {Scope} from "./scope.js";
4
+
5
+ class Test {}
6
+
7
+ describe("Scope", () => {
8
+ describe("when parameters is given", () => {
9
+ it("should set metadata", () => {
10
+ Scope("request")(Test);
11
+
12
+ expect(Store.from(Test).get("scope")).toEqual("request");
13
+ });
14
+ });
15
+
16
+ describe("when parameters is not given", () => {
17
+ beforeAll(() => {});
18
+
19
+ it("should set metadata", () => {
20
+ Scope()(Test);
21
+
22
+ expect(Store.from(Test).get("scope")).toEqual("request");
23
+ });
24
+ });
25
+ });
@@ -0,0 +1,13 @@
1
+ import {StoreSet} from "@tsed/core";
2
+
3
+ import {ProviderScope} from "../domain/ProviderScope.js";
4
+
5
+ /**
6
+ *
7
+ * @param {"request" | "singleton" | ProviderScope} scope
8
+ * @returns {Function}
9
+ * @constructor
10
+ */
11
+ export function Scope(scope: "request" | "singleton" | ProviderScope = ProviderScope.REQUEST) {
12
+ return StoreSet("scope", scope);
13
+ }
@@ -0,0 +1,14 @@
1
+ import {Injectable} from "./injectable.js";
2
+
3
+ /**
4
+ * The decorators `@Service()` declare a new service can be injected in other service or controller on there `constructor`.
5
+ * All services annotated with `@Service()` are constructed one time.
6
+ *
7
+ * > `@Service()` use the `reflect-metadata` to collect and inject service on controllers or other services.
8
+ *
9
+ * @returns {Function}
10
+ * @decorator
11
+ */
12
+ export function Service(): Function {
13
+ return Injectable();
14
+ }
@@ -0,0 +1,90 @@
1
+ import {descriptorOf} from "@tsed/core";
2
+
3
+ import {DITest} from "../../node/index.js";
4
+ import {ProviderScope} from "../domain/ProviderScope.js";
5
+ import {Inject} from "./inject.js";
6
+ import {Injectable} from "./injectable.js";
7
+ import {Opts} from "./opts.js";
8
+ import {Scope} from "./scope.js";
9
+ import {UseOpts} from "./useOpts.js";
10
+
11
+ describe("UseOpts", () => {
12
+ let testContext: any;
13
+
14
+ beforeEach(() => {
15
+ testContext = {};
16
+ });
17
+
18
+ beforeEach(() => DITest.create());
19
+ afterEach(() => DITest.reset());
20
+ it("should inject provider with options", async () => {
21
+ @Injectable()
22
+ class MyProvider {
23
+ source: any;
24
+
25
+ constructor(@Opts options: any = {}) {
26
+ testContext.source = options.source;
27
+ this.source = options.source;
28
+ }
29
+ }
30
+
31
+ @Injectable()
32
+ class MyService {
33
+ @Inject()
34
+ @UseOpts({
35
+ source: "test1"
36
+ })
37
+ service1: MyProvider;
38
+
39
+ constructor(@UseOpts({source: "test2"}) public service2: MyProvider) {}
40
+ }
41
+
42
+ const service = await DITest.invoke<MyService>(MyService);
43
+
44
+ expect(service.service2.source).toEqual("test2");
45
+ expect(service.service2).not.toEqual(service.service1);
46
+
47
+ expect(service.service1).toBeInstanceOf(MyProvider);
48
+ expect(service.service1.source).toEqual("test1");
49
+
50
+ expect(service.service2).toBeInstanceOf(MyProvider);
51
+ });
52
+ it("should invoke a service which use a configurable provider", async () => {
53
+ @Injectable()
54
+ class MyProvider {
55
+ source: any;
56
+
57
+ constructor(@Opts options: Partial<any> = {}) {
58
+ testContext.source = options.source;
59
+ }
60
+ }
61
+
62
+ @Injectable()
63
+ class MyService {
64
+ constructor(@UseOpts({source: "test2"}) public service2: MyProvider) {}
65
+ }
66
+
67
+ @Injectable()
68
+ @Scope(ProviderScope.SINGLETON)
69
+ class MyRepoService extends MyProvider {}
70
+
71
+ await DITest.invoke<MyRepoService>(MyRepoService);
72
+ });
73
+ it("should store metadata", () => {
74
+ // GIVEN
75
+ class Test {
76
+ test() {}
77
+ }
78
+
79
+ // WHEN
80
+ let actualError;
81
+ try {
82
+ UseOpts({})(Test, "test", descriptorOf(Test, "test"));
83
+ } catch (er) {
84
+ actualError = er;
85
+ }
86
+
87
+ // THEN
88
+ expect(actualError.message).toEqual("UseOpts cannot be used as method.static decorator on Test.test");
89
+ });
90
+ });
@@ -0,0 +1,61 @@
1
+ import {decoratorTypeOf, DecoratorTypes, Store, UnsupportedDecoratorType} from "@tsed/core";
2
+
3
+ import {DI_USE_OPTIONS, DI_USE_PARAM_OPTIONS} from "../constants/constants.js";
4
+
5
+ /**
6
+ * Add options to invoke the Service.
7
+ *
8
+ * ```typescript
9
+ * import {Injectable, Opts, UseOpts} from "@tsed/di";
10
+ *
11
+ * @Injectable()
12
+ * class MyConfigurableService {
13
+ * source: string;
14
+ * constructor(@Opts options: any = {}) {
15
+ * console.log("Hello ", options.source); // log: Hello Service1 then Hello Service2
16
+ *
17
+ * this.source = options.source;
18
+ * }
19
+ * }
20
+ *
21
+ * @Injectable()
22
+ * class MyService1 {
23
+ * constructor(@UseOpts({source: 'Service1'}) service: MyConfigurableService) {
24
+ * console.log(service.source) // log: Service1
25
+ * }
26
+ * }
27
+ *
28
+ * @Injectable()
29
+ * class MyService2 {
30
+ * constructor(@UseOpts({source: 'Service2'}) service: MyConfigurableService) {
31
+ * console.log(service.source) // log: Service2
32
+ * }
33
+ * }
34
+ * ```
35
+ *
36
+ * ::: warning
37
+ * Using @@Opts@@ decorator on a constructor parameter change the Scope of the provider to `ProviderScope.INSTANCE`.
38
+ * :::
39
+ *
40
+ * @returns {Function}
41
+ * @decorator
42
+ * @param options
43
+ */
44
+ export function UseOpts(options: {[key: string]: any}): Function {
45
+ return (target: Object, propertyKey: string | symbol, index?: number): any => {
46
+ const bindingType = decoratorTypeOf([target, propertyKey, index]);
47
+
48
+ switch (bindingType) {
49
+ case DecoratorTypes.PARAM_CTOR:
50
+ Store.from(target).merge(`${DI_USE_PARAM_OPTIONS}:${index}`, options);
51
+ break;
52
+
53
+ case DecoratorTypes.PROP:
54
+ Store.from(target, propertyKey).set(DI_USE_OPTIONS, options);
55
+ break;
56
+
57
+ default:
58
+ throw new UnsupportedDecoratorType(UseOpts, [target, propertyKey, index]);
59
+ }
60
+ };
61
+ }
@@ -0,0 +1,59 @@
1
+ import {DITest} from "../../node/index.js";
2
+ import {configuration} from "../fn/configuration.js";
3
+ import {Value} from "./value.js";
4
+
5
+ describe("@Value()", () => {
6
+ beforeEach(() =>
7
+ DITest.create({
8
+ logger: {
9
+ level: "off"
10
+ }
11
+ })
12
+ );
13
+ afterEach(() => DITest.reset());
14
+ describe("when decorator is used as property decorator", () => {
15
+ it("should create a getter", async () => {
16
+ // WHEN
17
+ class Test {
18
+ @Value("logger.level", "default value")
19
+ test: string;
20
+ }
21
+
22
+ // THEN
23
+
24
+ const test = await DITest.invoke<Test>(Test);
25
+
26
+ expect(test.test).toEqual("off");
27
+ });
28
+ it("should create a getter with default value", async () => {
29
+ expect(configuration().get("logger.test")).toEqual(undefined);
30
+
31
+ // WHEN
32
+ class Test {
33
+ @Value("logger.test", "default value")
34
+ test: string;
35
+ }
36
+
37
+ // THEN
38
+
39
+ const test = await DITest.invoke<Test>(Test);
40
+
41
+ expect(test.test).toEqual("default value");
42
+ expect(configuration().get("logger.test")).toEqual(undefined);
43
+ });
44
+ it("should create a getter with native default value", async () => {
45
+ // WHEN
46
+ class Test {
47
+ @Value("logger.test")
48
+ test: string = "default prop";
49
+ }
50
+
51
+ // THEN
52
+
53
+ const test = await DITest.invoke<Test>(Test);
54
+
55
+ expect(test.test).toEqual("default prop");
56
+ expect(configuration().get("logger.test")).toEqual("default prop");
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,60 @@
1
+ import {catchError} from "@tsed/core";
2
+
3
+ import {injector} from "../fn/injector.js";
4
+
5
+ export function bindValue(target: any, propertyKey: string | symbol, expression: string, defaultValue?: any) {
6
+ const descriptor = {
7
+ get() {
8
+ return injector().settings.get(expression, defaultValue);
9
+ },
10
+ set(value: unknown) {
11
+ injector().settings.set(expression, value);
12
+ },
13
+ enumerable: true,
14
+ configurable: true
15
+ };
16
+
17
+ catchError(() => Reflect.deleteProperty(target, propertyKey));
18
+ catchError(() => Reflect.defineProperty(target, propertyKey, descriptor));
19
+ }
20
+
21
+ /**
22
+ * Return value from Configuration.
23
+ *
24
+ * ## Example
25
+ *
26
+ * ```typescript
27
+ * import {Env} from "@tsed/core";
28
+ * import {Constant, Value} from "@tsed/di";
29
+ *
30
+ * export class MyClass {
31
+ *
32
+ * @Constant("env")
33
+ * env: Env;
34
+ *
35
+ * @Value("swagger.path")
36
+ * swaggerPath: string;
37
+ *
38
+ * @Value("swagger.path", "defaultValue")
39
+ * swaggerPath: string;
40
+ *
41
+ * constructor() {
42
+ * console.log(this.swaggerPath) // undefined. Not available on constructor
43
+ * }
44
+ *
45
+ * $onInit() {
46
+ * console.log(this.swaggerPath) // something
47
+ * }
48
+ * }
49
+ * ```
50
+ *
51
+ * @param expression
52
+ * @param defaultValue
53
+ * @returns {(targetClass: any, attributeName: string) => any}
54
+ * @decorator
55
+ */
56
+ export function Value(expression: string, defaultValue?: unknown): PropertyDecorator {
57
+ return (target, propertyKey) => {
58
+ return bindValue(target, propertyKey, expression, defaultValue);
59
+ };
60
+ }