@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
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tsed/di",
|
|
3
3
|
"description": "DI module for Ts.ED Framework",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.0.
|
|
5
|
+
"version": "8.0.3",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"browser": "./lib/browser/di.umd.min.js",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"@tsed/source": "./src/index.ts",
|
|
13
14
|
"types": "./lib/types/index.d.ts",
|
|
14
15
|
"browser": "./lib/browser/di.umd.min.js",
|
|
15
16
|
"import": "./lib/esm/index.js",
|
|
@@ -29,12 +30,12 @@
|
|
|
29
30
|
"uuid": "^10.0.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
|
-
"@tsed/barrels": "8.0.
|
|
33
|
-
"@tsed/core": "8.0.
|
|
34
|
-
"@tsed/hooks": "8.0.
|
|
33
|
+
"@tsed/barrels": "8.0.3",
|
|
34
|
+
"@tsed/core": "8.0.3",
|
|
35
|
+
"@tsed/hooks": "8.0.3",
|
|
35
36
|
"@tsed/logger": "^6.7.8",
|
|
36
|
-
"@tsed/schema": "8.0.
|
|
37
|
-
"@tsed/typescript": "8.0.
|
|
37
|
+
"@tsed/schema": "8.0.3",
|
|
38
|
+
"@tsed/typescript": "8.0.3",
|
|
38
39
|
"cross-env": "7.0.3",
|
|
39
40
|
"eslint": "9.12.0",
|
|
40
41
|
"typescript": "5.4.5",
|
|
@@ -42,10 +43,10 @@
|
|
|
42
43
|
"webpack": "^5.75.0"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
|
-
"@tsed/core": "8.0.
|
|
46
|
-
"@tsed/hooks": "8.0.
|
|
46
|
+
"@tsed/core": "8.0.3",
|
|
47
|
+
"@tsed/hooks": "8.0.3",
|
|
47
48
|
"@tsed/logger": ">=6.7.5",
|
|
48
|
-
"@tsed/schema": "8.0.
|
|
49
|
+
"@tsed/schema": "8.0.3"
|
|
49
50
|
},
|
|
50
51
|
"peerDependenciesMeta": {
|
|
51
52
|
"@tsed/core": {
|
package/readme.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p style="text-align: center" align="center">
|
|
2
|
-
<a href="https://tsed.
|
|
2
|
+
<a href="https://tsed.dev" target="_blank"><img src="https://tsed.dev/tsed-og.png" width="200" alt="Ts.ED logo"/></a>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<div align="center">
|
|
19
|
-
<a href="https://tsed.
|
|
19
|
+
<a href="https://tsed.dev/">Website</a>
|
|
20
20
|
<span> • </span>
|
|
21
|
-
<a href="https://tsed.
|
|
21
|
+
<a href="https://tsed.dev/getting-started/">Getting started</a>
|
|
22
22
|
<span> • </span>
|
|
23
23
|
<a href="https://api.tsed.io/rest/slack/tsedio/tsed">Slack</a>
|
|
24
24
|
<span> • </span>
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
<hr />
|
|
29
29
|
|
|
30
|
-
A package of Ts.ED framework. See website: https://tsed.
|
|
30
|
+
A package of Ts.ED framework. See website: https://tsed.dev/
|
|
31
31
|
|
|
32
32
|
# Installation
|
|
33
33
|
|
|
@@ -150,11 +150,11 @@ To organize your code Ts.ED DI provide different kind of providers:
|
|
|
150
150
|
- Interceptor can be declared with `@Interceptor`,
|
|
151
151
|
- Factory and Value can be declared with `registerProvider` and `registerValue`.
|
|
152
152
|
|
|
153
|
-
See more details on our documentation https://tsed.
|
|
153
|
+
See more details on our documentation https://tsed.dev/providers.html
|
|
154
154
|
|
|
155
155
|
## Contributors
|
|
156
156
|
|
|
157
|
-
Please read [contributing guidelines here](https://tsed.
|
|
157
|
+
Please read [contributing guidelines here](https://tsed.dev/contributing.html)
|
|
158
158
|
|
|
159
159
|
<a href="https://github.com/tsedio/tsed/graphs/contributors"><img src="https://opencollective.com/tsed/contributors.svg?width=890" /></a>
|
|
160
160
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const DI_INVOKE_OPTIONS = Symbol("DI_INVOKE_OPTIONS");
|
|
2
|
+
export const DI_INJECTABLE_PROPS = Symbol("DI_INJECTABLE_PROPS");
|
|
3
|
+
export const DI_USE_OPTIONS = "DI_USE_OPTIONS";
|
|
4
|
+
export const DI_USE_PARAM_OPTIONS = "DI_USE_PARAM_OPTIONS";
|
|
5
|
+
export const DI_INTERCEPTOR_OPTIONS = "DI_INTERCEPTOR_OPTIONS";
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {Logger} from "@tsed/logger";
|
|
2
|
+
import {beforeEach} from "vitest";
|
|
3
|
+
|
|
4
|
+
import {DITest} from "../../node/index.js";
|
|
5
|
+
import {registerProvider} from "../registries/ProviderRegistry.js";
|
|
6
|
+
import {InjectorService} from "../services/InjectorService.js";
|
|
7
|
+
import {AutoInjectable} from "./autoInjectable.js";
|
|
8
|
+
import {Inject} from "./inject.js";
|
|
9
|
+
import {Injectable} from "./injectable.js";
|
|
10
|
+
|
|
11
|
+
const TOKEN_GROUPS = Symbol.for("groups:1");
|
|
12
|
+
|
|
13
|
+
interface InterfaceGroup {
|
|
14
|
+
type: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Injectable({
|
|
18
|
+
type: TOKEN_GROUPS
|
|
19
|
+
})
|
|
20
|
+
class MyService1 implements InterfaceGroup {
|
|
21
|
+
readonly type: string = "service1";
|
|
22
|
+
|
|
23
|
+
constructor(@Inject(InjectorService) readonly injector: any) {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Injectable({
|
|
27
|
+
type: TOKEN_GROUPS
|
|
28
|
+
})
|
|
29
|
+
class MyService2 implements InterfaceGroup {
|
|
30
|
+
readonly type: string = "service2";
|
|
31
|
+
|
|
32
|
+
constructor(@Inject(InjectorService) readonly injector: any) {}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const TokenAsync = Symbol.for("MyService2");
|
|
36
|
+
|
|
37
|
+
registerProvider({
|
|
38
|
+
token: TokenAsync,
|
|
39
|
+
type: TOKEN_GROUPS,
|
|
40
|
+
deps: [],
|
|
41
|
+
useAsyncFactory() {
|
|
42
|
+
return Promise.resolve({
|
|
43
|
+
type: "async"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("AutoInjectable", () => {
|
|
49
|
+
describe("when the instance is created during an injection context", () => {
|
|
50
|
+
beforeEach(() => DITest.create());
|
|
51
|
+
afterEach(() => DITest.reset());
|
|
52
|
+
it("should return a class that extends the original class", () => {
|
|
53
|
+
@AutoInjectable()
|
|
54
|
+
class Test {
|
|
55
|
+
@Inject(Logger)
|
|
56
|
+
logger: Logger;
|
|
57
|
+
|
|
58
|
+
foo() {
|
|
59
|
+
this.logger.info("test");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const test = new Test();
|
|
64
|
+
const test2 = new Test();
|
|
65
|
+
|
|
66
|
+
expect(test).toBeInstanceOf(Test);
|
|
67
|
+
expect(test2).toBeInstanceOf(Test);
|
|
68
|
+
expect(test).not.toBe(test2);
|
|
69
|
+
|
|
70
|
+
vi.spyOn(test.logger, "info").mockResolvedValue(undefined as never);
|
|
71
|
+
|
|
72
|
+
test.foo();
|
|
73
|
+
|
|
74
|
+
expect(test.logger.info).toHaveBeenCalledWith("test");
|
|
75
|
+
});
|
|
76
|
+
it("should return a class that extends the original class (with additional args)", () => {
|
|
77
|
+
@AutoInjectable()
|
|
78
|
+
class Test {
|
|
79
|
+
@Inject(Logger)
|
|
80
|
+
logger: Logger;
|
|
81
|
+
|
|
82
|
+
private value: string;
|
|
83
|
+
|
|
84
|
+
constructor(initialValue: string, @Inject(InjectorService) injector?: InjectorService) {
|
|
85
|
+
this.value = initialValue;
|
|
86
|
+
expect(injector).toBeInstanceOf(InjectorService);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
foo() {
|
|
90
|
+
this.logger.info("test_" + this.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const test = new Test("test");
|
|
95
|
+
|
|
96
|
+
vi.spyOn(test.logger, "info").mockResolvedValue(undefined as never);
|
|
97
|
+
|
|
98
|
+
test.foo();
|
|
99
|
+
|
|
100
|
+
expect(test.logger.info).toHaveBeenCalledWith("test_test");
|
|
101
|
+
});
|
|
102
|
+
it("should return a class that extends the original class (with inject many)", () => {
|
|
103
|
+
@AutoInjectable()
|
|
104
|
+
class Test {
|
|
105
|
+
@Inject(Logger)
|
|
106
|
+
logger: Logger;
|
|
107
|
+
instances?: InterfaceGroup[];
|
|
108
|
+
private value: string;
|
|
109
|
+
|
|
110
|
+
constructor(initialValue: string, @Inject(TOKEN_GROUPS) instances?: InterfaceGroup[]) {
|
|
111
|
+
this.value = initialValue;
|
|
112
|
+
this.instances = instances;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
expect(new Test("test").instances).toHaveLength(3);
|
|
117
|
+
});
|
|
118
|
+
it("should return a class that extends the original class (with 3 arguments)", () => {
|
|
119
|
+
@AutoInjectable()
|
|
120
|
+
class Test {
|
|
121
|
+
constructor(
|
|
122
|
+
public items: string[],
|
|
123
|
+
public group: string,
|
|
124
|
+
@Inject() logger?: Logger
|
|
125
|
+
) {
|
|
126
|
+
expect(items).toEqual(["item1", "item2", "item3"]);
|
|
127
|
+
expect(group).toBe("group");
|
|
128
|
+
expect(logger).toBeInstanceOf(Logger);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
new Test(["item1", "item2", "item3"], "group");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {isArray, type Type} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {LocalsContainer} from "../domain/LocalsContainer.js";
|
|
4
|
+
import {injector} from "../fn/injector.js";
|
|
5
|
+
import type {TokenProvider} from "../interfaces/TokenProvider.js";
|
|
6
|
+
import {getConstructorDependencies} from "../utils/getConstructorDependencies.js";
|
|
7
|
+
|
|
8
|
+
function resolveAutoInjectableArgs(token: Type, args: unknown[]) {
|
|
9
|
+
const inj = injector();
|
|
10
|
+
const locals = new LocalsContainer();
|
|
11
|
+
const deps: TokenProvider[] = getConstructorDependencies(token);
|
|
12
|
+
const list: any[] = [];
|
|
13
|
+
const length = Math.max(deps.length, args.length);
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < length; i++) {
|
|
16
|
+
if (args[i] !== undefined) {
|
|
17
|
+
list.push(args[i]);
|
|
18
|
+
} else {
|
|
19
|
+
const value = deps[i];
|
|
20
|
+
const instance = isArray(value) ? inj.getMany(value[0], {locals, parent: token}) : inj.invoke(value, {locals, parent: token});
|
|
21
|
+
|
|
22
|
+
list.push(instance);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return list;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* AutoInjectable decorator is used to automatically inject dependencies in the constructor.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* @AutoInjectable()
|
|
35
|
+
* class UserService {
|
|
36
|
+
* constructor(
|
|
37
|
+
* private database: Database, // automatically injected
|
|
38
|
+
* customConfig?: Config // can be passed manually
|
|
39
|
+
* ) {}
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @description
|
|
44
|
+
* When applied to a class, this decorator:
|
|
45
|
+
* - Automatically resolves and injects dependencies based on constructor parameter types
|
|
46
|
+
* - Preserves the ability to manually pass arguments which override automatic injection
|
|
47
|
+
*
|
|
48
|
+
* @decorator
|
|
49
|
+
* @returns {ClassDecorator} A class decorator that enables automatic dependency injection
|
|
50
|
+
*/
|
|
51
|
+
export function AutoInjectable() {
|
|
52
|
+
return <T extends {new (...args: any[]): NonNullable<unknown>}>(constr: T): T => {
|
|
53
|
+
return class AutoInjectable extends constr {
|
|
54
|
+
constructor(...args: any[]) {
|
|
55
|
+
super(...resolveAutoInjectableArgs(constr, args));
|
|
56
|
+
}
|
|
57
|
+
} as unknown as T;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {Store} from "@tsed/core";
|
|
2
|
+
import {afterEach} from "vitest";
|
|
3
|
+
|
|
4
|
+
import {Container} from "../domain/Container.js";
|
|
5
|
+
import {Provider} from "../domain/Provider.js";
|
|
6
|
+
import {destroyInjector, injector} from "../fn/injector.js";
|
|
7
|
+
import {GlobalProviders} from "../registries/GlobalProviders.js";
|
|
8
|
+
import {InjectorService} from "../services/InjectorService.js";
|
|
9
|
+
import {Configuration} from "./configuration.js";
|
|
10
|
+
import {Injectable} from "./injectable.js";
|
|
11
|
+
|
|
12
|
+
describe("@Configuration", () => {
|
|
13
|
+
afterEach(() => destroyInjector());
|
|
14
|
+
it("should declare a new provider with custom configuration", () => {
|
|
15
|
+
@Configuration({})
|
|
16
|
+
class Test {}
|
|
17
|
+
|
|
18
|
+
const provider = new Provider(Test);
|
|
19
|
+
provider.injectable = false;
|
|
20
|
+
provider.configuration = {};
|
|
21
|
+
|
|
22
|
+
expect(Store.from(Test).get("configuration")).toEqual({});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should inject configuration", async () => {
|
|
26
|
+
@Configuration({
|
|
27
|
+
feature: "feature"
|
|
28
|
+
})
|
|
29
|
+
@Injectable()
|
|
30
|
+
class Test {
|
|
31
|
+
constructor(@Configuration() public config: Configuration) {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const container = new Container();
|
|
35
|
+
|
|
36
|
+
injector().setProvider(Test, GlobalProviders.get(Test)!.clone());
|
|
37
|
+
|
|
38
|
+
await injector().load(container);
|
|
39
|
+
|
|
40
|
+
const instance = injector().invoke<Test>(Test);
|
|
41
|
+
|
|
42
|
+
expect(instance.config).toEqual(injector().settings);
|
|
43
|
+
expect(instance.config.get("feature")).toEqual("feature");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {DecoratorParameters, decoratorTypeOf, DecoratorTypes} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {configuration} from "../fn/configuration.js";
|
|
4
|
+
import {injectable} from "../fn/injectable.js";
|
|
5
|
+
import {injector} from "../fn/injector.js";
|
|
6
|
+
import {CONFIGURATION, DIConfiguration} from "../services/DIConfiguration.js";
|
|
7
|
+
import {Inject} from "./inject.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get or set Configuration on a class.
|
|
11
|
+
*
|
|
12
|
+
* @decorator
|
|
13
|
+
*/
|
|
14
|
+
export function Configuration(): any;
|
|
15
|
+
export function Configuration(settings: Partial<TsED.Configuration>): ClassDecorator;
|
|
16
|
+
export function Configuration(settings: Partial<TsED.Configuration> = {}): Function {
|
|
17
|
+
return (...args: DecoratorParameters) => {
|
|
18
|
+
switch (decoratorTypeOf(args)) {
|
|
19
|
+
case DecoratorTypes.CLASS:
|
|
20
|
+
configuration(args[0], settings);
|
|
21
|
+
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
case DecoratorTypes.PARAM_CTOR:
|
|
25
|
+
return Inject(DIConfiguration)(args[0], args[1], args[2] as number);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type Configuration = TsED.DIConfiguration & DIConfiguration;
|
|
31
|
+
|
|
32
|
+
// To maintain compatibility with the previous implementation, we need to declare Configuration as
|
|
33
|
+
// injectable token.
|
|
34
|
+
injectable(Configuration).factory(() => injector().settings);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {DITest} from "../../node/index.js";
|
|
2
|
+
import {Constant} from "./constant.js";
|
|
3
|
+
|
|
4
|
+
describe("@Constant()", () => {
|
|
5
|
+
beforeEach(() =>
|
|
6
|
+
DITest.create({
|
|
7
|
+
logger: {
|
|
8
|
+
level: "off"
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
);
|
|
12
|
+
afterEach(() => DITest.reset());
|
|
13
|
+
it("should create a getter", async () => {
|
|
14
|
+
// WHEN
|
|
15
|
+
class Test {
|
|
16
|
+
@Constant("logger.level", "default value")
|
|
17
|
+
test: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// THEN
|
|
21
|
+
|
|
22
|
+
const test = await DITest.invoke<Test>(Test);
|
|
23
|
+
|
|
24
|
+
expect(test.test).toEqual("off");
|
|
25
|
+
});
|
|
26
|
+
it("should create a getter with default value", async () => {
|
|
27
|
+
// WHEN
|
|
28
|
+
class Test {
|
|
29
|
+
@Constant("logger.test", "default value")
|
|
30
|
+
test: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// THEN
|
|
34
|
+
|
|
35
|
+
const test = await DITest.invoke<Test>(Test);
|
|
36
|
+
|
|
37
|
+
expect(test.test).toEqual("default value");
|
|
38
|
+
});
|
|
39
|
+
it("shouldn't be possible to modify injected value from injector.settings", async () => {
|
|
40
|
+
// WHEN
|
|
41
|
+
class Test {
|
|
42
|
+
@Constant("logger.level")
|
|
43
|
+
test: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// THEN
|
|
47
|
+
|
|
48
|
+
const test = await DITest.invoke<Test>(Test);
|
|
49
|
+
|
|
50
|
+
test.test = "new value";
|
|
51
|
+
|
|
52
|
+
expect(test.test).toEqual("off");
|
|
53
|
+
});
|
|
54
|
+
it("should create a getter with native default value", async () => {
|
|
55
|
+
// WHEN
|
|
56
|
+
class Test {
|
|
57
|
+
@Constant("logger.test")
|
|
58
|
+
test: string = "default prop";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// THEN
|
|
62
|
+
|
|
63
|
+
const test = await DITest.invoke<Test>(Test);
|
|
64
|
+
|
|
65
|
+
expect(test.test).toEqual("default prop");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {catchError, deepClone} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {constant} from "../fn/constant.js";
|
|
4
|
+
|
|
5
|
+
export function bindConstant(target: any, propertyKey: string | symbol, expression: string, defaultValue?: any) {
|
|
6
|
+
const symbol = Symbol();
|
|
7
|
+
|
|
8
|
+
catchError(() => Reflect.deleteProperty(target, propertyKey));
|
|
9
|
+
Reflect.defineProperty(target, propertyKey, {
|
|
10
|
+
get() {
|
|
11
|
+
if (this[symbol] !== undefined) {
|
|
12
|
+
return this[symbol];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const value = constant(expression, defaultValue);
|
|
16
|
+
|
|
17
|
+
this[symbol] = Object.freeze(deepClone(value));
|
|
18
|
+
|
|
19
|
+
return this[symbol];
|
|
20
|
+
},
|
|
21
|
+
set(value: unknown) {
|
|
22
|
+
const bean = constant(expression, defaultValue) || this[symbol];
|
|
23
|
+
|
|
24
|
+
if (bean === undefined && value !== undefined) {
|
|
25
|
+
this[symbol] = value;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Return value from Configuration.
|
|
35
|
+
*
|
|
36
|
+
* ## Example
|
|
37
|
+
*
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import {Env} from "@tsed/core";
|
|
40
|
+
* import {Constant, Value} from "@tsed/di";
|
|
41
|
+
*
|
|
42
|
+
* export class MyClass {
|
|
43
|
+
*
|
|
44
|
+
* @Constant("env")
|
|
45
|
+
* env: Env;
|
|
46
|
+
*
|
|
47
|
+
* @Value("swagger.path")
|
|
48
|
+
* swaggerPath: string;
|
|
49
|
+
*
|
|
50
|
+
* @Value("swagger.path", "defaultValue")
|
|
51
|
+
* swaggerPath: string;
|
|
52
|
+
*
|
|
53
|
+
* constructor() {
|
|
54
|
+
* console.log(this.swaggerPath) // undefined. Not available on constructor
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* $onInit() {
|
|
58
|
+
* console.log(this.swaggerPath) // something
|
|
59
|
+
* }
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @param {string} expression
|
|
64
|
+
* @param defaultValue
|
|
65
|
+
* @returns {(targetClass: any, attributeName: string) => any}
|
|
66
|
+
* @decorator
|
|
67
|
+
*/
|
|
68
|
+
export function Constant<Type = unknown>(expression: string, defaultValue?: Type): PropertyDecorator {
|
|
69
|
+
return (target, propertyKey) => {
|
|
70
|
+
return bindConstant(target, propertyKey, expression, defaultValue);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {ProviderScope} from "../domain/ProviderScope.js";
|
|
2
|
+
import {ProviderType} from "../domain/ProviderType.js";
|
|
3
|
+
import {GlobalProviders} from "../registries/GlobalProviders.js";
|
|
4
|
+
import {Controller} from "./controller.js";
|
|
5
|
+
|
|
6
|
+
class Test {}
|
|
7
|
+
|
|
8
|
+
class Dep {}
|
|
9
|
+
|
|
10
|
+
describe("@Controller", () => {
|
|
11
|
+
afterAll(() => {
|
|
12
|
+
GlobalProviders.delete(Test);
|
|
13
|
+
});
|
|
14
|
+
it("should register a controller with his path and Dependency", () => {
|
|
15
|
+
// WHEN
|
|
16
|
+
Controller("/test")(Test);
|
|
17
|
+
|
|
18
|
+
// THEN
|
|
19
|
+
const provider = GlobalProviders.get(Test)!;
|
|
20
|
+
|
|
21
|
+
// THEN
|
|
22
|
+
expect(provider.type).toEqual(ProviderType.CONTROLLER);
|
|
23
|
+
expect(provider.path).toEqual("/test");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should register a controller with customer provider options", () => {
|
|
27
|
+
// WHEN
|
|
28
|
+
Controller({
|
|
29
|
+
path: "/test",
|
|
30
|
+
children: [Dep],
|
|
31
|
+
scope: ProviderScope.REQUEST
|
|
32
|
+
})(Test);
|
|
33
|
+
|
|
34
|
+
// THEN
|
|
35
|
+
const provider = GlobalProviders.get(Test)!;
|
|
36
|
+
|
|
37
|
+
// THEN
|
|
38
|
+
expect(provider.type).toEqual(ProviderType.CONTROLLER);
|
|
39
|
+
expect(provider.scope).toEqual(ProviderScope.REQUEST);
|
|
40
|
+
expect(provider.path).toEqual("/test");
|
|
41
|
+
expect(provider.children).toEqual([Dep]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {isArrayOrArrayClass, Type, useDecorators} from "@tsed/core";
|
|
2
|
+
import {Children, Path} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import type {ControllerMiddlewares} from "../domain/ControllerProvider.js";
|
|
5
|
+
import {controller} from "../fn/injectable.js";
|
|
6
|
+
import {ProviderOpts} from "../interfaces/ProviderOpts.js";
|
|
7
|
+
|
|
8
|
+
export type PathType = string | RegExp | (string | RegExp)[];
|
|
9
|
+
|
|
10
|
+
export interface ControllerOptions extends Partial<ProviderOpts<any>> {
|
|
11
|
+
path?: PathType;
|
|
12
|
+
children?: Type<any>[];
|
|
13
|
+
middlewares?: Partial<ControllerMiddlewares>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function mapOptions(options: any): ControllerOptions {
|
|
17
|
+
if (typeof options === "string" || options instanceof RegExp || isArrayOrArrayClass(options)) {
|
|
18
|
+
return {
|
|
19
|
+
path: options
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return options;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Declare a new controller with his Rest path. His methods annotated will be collected to build the routing list.
|
|
28
|
+
* This routing listing will be built with the `express.Router` object.
|
|
29
|
+
*
|
|
30
|
+
* ::: tip
|
|
31
|
+
* See [Controllers](/docs/controllers.md) section for more details
|
|
32
|
+
* :::
|
|
33
|
+
*
|
|
34
|
+
* ```typescript
|
|
35
|
+
* @Controller("/calendars")
|
|
36
|
+
* export class CalendarCtrl {
|
|
37
|
+
*
|
|
38
|
+
* @Get("/:id")
|
|
39
|
+
* public get(
|
|
40
|
+
* @Req() request: Req,
|
|
41
|
+
* @Res() response: Res,
|
|
42
|
+
* @Next() next: Next
|
|
43
|
+
* ): void {
|
|
44
|
+
*
|
|
45
|
+
* }
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @param options
|
|
50
|
+
* @controller
|
|
51
|
+
* @decorator
|
|
52
|
+
* @classDecorator
|
|
53
|
+
*/
|
|
54
|
+
export function Controller(options: PathType | ControllerOptions): ClassDecorator {
|
|
55
|
+
const {children = [], path, ...opts} = mapOptions(options);
|
|
56
|
+
|
|
57
|
+
return useDecorators(
|
|
58
|
+
(target: Type) => {
|
|
59
|
+
controller(target, opts);
|
|
60
|
+
},
|
|
61
|
+
path && Path(path as any),
|
|
62
|
+
Children(...children)
|
|
63
|
+
);
|
|
64
|
+
}
|