@tsed/di 8.0.1 → 8.0.3
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/package.json +10 -9
- package/readme.md +6 -6
- package/src/common/constants/constants.ts +5 -0
- package/src/common/decorators/autoInjectable.spec.ts +135 -0
- package/src/common/decorators/autoInjectable.ts +59 -0
- package/src/common/decorators/configuration.spec.ts +45 -0
- package/src/common/decorators/configuration.ts +34 -0
- package/src/common/decorators/constant.spec.ts +67 -0
- package/src/common/decorators/constant.ts +72 -0
- package/src/common/decorators/controller.spec.ts +43 -0
- package/src/common/decorators/controller.ts +64 -0
- package/src/common/decorators/inject.spec.ts +298 -0
- package/src/common/decorators/inject.ts +144 -0
- package/src/common/decorators/injectable.spec.ts +43 -0
- package/src/common/decorators/injectable.ts +30 -0
- package/src/common/decorators/intercept.spec.ts +103 -0
- package/src/common/decorators/intercept.ts +77 -0
- package/src/common/decorators/interceptor.ts +17 -0
- package/src/common/decorators/lazyInject.spec.ts +53 -0
- package/src/common/decorators/lazyInject.ts +91 -0
- package/src/common/decorators/module.ts +52 -0
- package/src/common/decorators/opts.ts +51 -0
- package/src/common/decorators/overrideProvider.spec.ts +30 -0
- package/src/common/decorators/overrideProvider.ts +15 -0
- package/src/common/decorators/scope.spec.ts +25 -0
- package/src/common/decorators/scope.ts +13 -0
- package/src/common/decorators/service.ts +14 -0
- package/src/common/decorators/useOpts.spec.ts +90 -0
- package/src/common/decorators/useOpts.ts +61 -0
- package/src/common/decorators/value.spec.ts +59 -0
- package/src/common/decorators/value.ts +60 -0
- package/src/common/domain/Container.spec.ts +85 -0
- package/src/common/domain/Container.ts +80 -0
- package/src/common/domain/ControllerProvider.spec.ts +55 -0
- package/src/common/domain/ControllerProvider.ts +48 -0
- package/src/common/domain/InjectablePropertyType.ts +7 -0
- package/src/common/domain/LocalsContainer.spec.ts +20 -0
- package/src/common/domain/LocalsContainer.ts +11 -0
- package/src/common/domain/Provider.spec.ts +107 -0
- package/src/common/domain/Provider.ts +205 -0
- package/src/common/domain/ProviderScope.ts +5 -0
- package/src/common/domain/ProviderType.ts +8 -0
- package/src/common/errors/InjectionError.spec.ts +17 -0
- package/src/common/errors/InjectionError.ts +68 -0
- package/src/common/errors/InvalidPropertyTokenError.ts +13 -0
- package/src/common/fn/configuration.spec.ts +42 -0
- package/src/common/fn/configuration.ts +22 -0
- package/src/common/fn/constant.spec.ts +37 -0
- package/src/common/fn/constant.ts +7 -0
- package/src/common/fn/inject.spec.ts +54 -0
- package/src/common/fn/inject.ts +29 -0
- package/src/common/fn/injectMany.ts +16 -0
- package/src/common/fn/injectable.spec.ts +87 -0
- package/src/common/fn/injectable.ts +15 -0
- package/src/common/fn/injector.ts +26 -0
- package/src/common/fn/lazyInject.spec.ts +22 -0
- package/src/common/fn/lazyInject.ts +36 -0
- package/src/common/fn/localsContainer.ts +55 -0
- package/src/common/fn/refValue.spec.ts +47 -0
- package/src/common/fn/refValue.ts +33 -0
- package/src/common/index.ts +63 -0
- package/src/common/integration/async-factory.spec.ts +60 -0
- package/src/common/integration/di.spec.ts +153 -0
- package/src/common/integration/imports.spec.ts +39 -0
- package/src/common/integration/request.spec.ts +79 -0
- package/src/common/integration/singleton.spec.ts +125 -0
- package/src/common/interfaces/DIConfigurationOptions.ts +29 -0
- package/src/common/interfaces/DILogger.ts +13 -0
- package/src/common/interfaces/DILoggerOptions.ts +54 -0
- package/src/common/interfaces/ImportTokenProviderOpts.ts +29 -0
- package/src/common/interfaces/InterceptorContext.ts +13 -0
- package/src/common/interfaces/InterceptorMethods.ts +5 -0
- package/src/common/interfaces/InvokeOptions.ts +28 -0
- package/src/common/interfaces/OnDestroy.ts +3 -0
- package/src/common/interfaces/OnInit.ts +3 -0
- package/src/common/interfaces/ProviderOpts.ts +57 -0
- package/src/common/interfaces/RegistrySettings.ts +11 -0
- package/src/common/interfaces/ResolvedInvokeOptions.ts +14 -0
- package/src/common/interfaces/TokenProvider.ts +3 -0
- package/src/common/interfaces/TokenRoute.ts +6 -0
- package/src/common/registries/GlobalProviders.spec.ts +94 -0
- package/src/common/registries/GlobalProviders.ts +131 -0
- package/src/common/registries/ProviderRegistry.spec.ts +36 -0
- package/src/common/registries/ProviderRegistry.ts +32 -0
- package/src/common/services/DIConfiguration.spec.ts +60 -0
- package/src/common/services/DIConfiguration.ts +140 -0
- package/src/common/services/DILogger.spec.ts +22 -0
- package/src/common/services/DILogger.ts +8 -0
- package/src/common/services/InjectorService.spec.ts +793 -0
- package/src/common/services/InjectorService.ts +601 -0
- package/src/common/utils/colors.spec.ts +9 -0
- package/src/common/utils/colors.ts +11 -0
- package/src/common/utils/createContainer.spec.ts +18 -0
- package/src/common/utils/createContainer.ts +14 -0
- package/src/common/utils/discoverHooks.ts +15 -0
- package/src/common/utils/getConfiguration.spec.ts +40 -0
- package/src/common/utils/getConfiguration.ts +20 -0
- package/src/common/utils/getConstructorDependencies.ts +9 -0
- package/src/common/utils/providerBuilder.ts +81 -0
- package/src/index.ts +2 -0
- package/src/node/decorators/injectContext.spec.ts +60 -0
- package/src/node/decorators/injectContext.ts +29 -0
- package/src/node/domain/ContextLogger.spec.ts +277 -0
- package/src/node/domain/ContextLogger.ts +137 -0
- package/src/node/domain/DIContext.spec.ts +103 -0
- package/src/node/domain/DIContext.ts +115 -0
- package/src/node/fn/context.ts +24 -0
- package/src/node/fn/logger.spec.ts +46 -0
- package/src/node/fn/logger.ts +17 -0
- package/src/node/index.ts +16 -0
- package/src/node/integration/async-factory-invoke.spec.ts +58 -0
- package/src/node/integration/inject-context.spec.ts +29 -0
- package/src/node/integration/invoke-oninit.spec.ts +25 -0
- package/src/node/interfaces/AlterRunInContext.ts +3 -0
- package/src/node/services/DILogger.spec.ts +24 -0
- package/src/node/services/DILogger.ts +6 -0
- package/src/node/services/DITest.spec.ts +109 -0
- package/src/node/services/DITest.ts +119 -0
- package/src/node/utils/asyncHookContext.spec.ts +147 -0
- package/src/node/utils/asyncHookContext.ts +48 -0
- package/src/node/utils/attachLogger.spec.ts +14 -0
- package/src/node/utils/attachLogger.ts +7 -0
- package/src/node/utils/setLoggerConfiguration.spec.ts +43 -0
- package/src/node/utils/setLoggerConfiguration.ts +7 -0
- package/src/node/utils/setLoggerFormat.ts +33 -0
- 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
|
+
}
|