@husky-di/decorator 1.2.2 → 1.3.0
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/README.md +359 -134
- package/dist/types/injection-metadata.type.d.ts +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,238 +1,463 @@
|
|
|
1
1
|
# @husky-di/decorator
|
|
2
2
|
|
|
3
|
-
`@husky-di/decorator`
|
|
3
|
+
`@husky-di/decorator` adds decorator support to husky-di.
|
|
4
|
+
It translates TypeScript decorator metadata into `@husky-di/core` resolution behavior, so you can declare dependencies directly on constructor parameters.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
You can think of it as a syntax layer on top of `core`:
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
- it makes dependency declaration feel more natural
|
|
9
|
+
- it does not replace the container itself, and lifecycle, middleware, `ref`, and `dynamic` still come from `@husky-di/core`
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
This package currently supports TypeScript experimental decorators only, not ES decorators.
|
|
12
|
+
The reason is straightforward: husky-di depends on parameter decorators for constructor injection, and ES decorators do not provide that capability.
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
## Is This The Right Package?
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
This package is a good fit when:
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
- you want dependencies to live directly on constructor parameters
|
|
19
|
+
- you do not want to hand-write wiring for every class
|
|
20
|
+
- you are comfortable using TypeScript experimental decorators and `reflect-metadata`
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
If what you really want is:
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
- a low-level container with no decorator dependency:
|
|
25
|
+
see `../core/README.md`
|
|
26
|
+
- module import/export boundaries:
|
|
27
|
+
pair it with `../module/README.md`
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
## What You Get
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
- `@injectable()` to mark classes as instantiable through decorator-aware resolution
|
|
32
|
+
- `@inject()` to declare service identifiers and resolve options for constructor parameters
|
|
33
|
+
- `@tagged()` as the low-level metadata decorator for custom abstractions
|
|
34
|
+
- `decoratorMiddleware` to read injection metadata during class resolution
|
|
35
|
+
- stable error exports such as `DecoratorException` and `DecoratorErrorCodeEnum`
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
import { injectable } from "@husky-di/decorator";
|
|
37
|
+
## Installation
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
```bash
|
|
40
|
+
pnpm add @husky-di/core @husky-di/decorator reflect-metadata
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`reflect-metadata` is a peer dependency.
|
|
44
|
+
At runtime, you need to load it first or provide a compatible Reflect metadata implementation.
|
|
45
|
+
|
|
46
|
+
## TypeScript Configuration
|
|
47
|
+
|
|
48
|
+
Enable TypeScript experimental decorators and metadata emission:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"compilerOptions": {
|
|
53
|
+
"experimentalDecorators": true,
|
|
54
|
+
"emitDecoratorMetadata": true
|
|
55
|
+
}
|
|
33
56
|
}
|
|
34
57
|
```
|
|
35
58
|
|
|
36
|
-
|
|
59
|
+
## Quick Start
|
|
37
60
|
|
|
38
|
-
|
|
61
|
+
The example below shows the most common setup: register the middleware once, then declare dependencies directly on constructor parameters.
|
|
39
62
|
|
|
40
63
|
```typescript
|
|
41
|
-
import
|
|
64
|
+
import "reflect-metadata";
|
|
65
|
+
import {
|
|
66
|
+
createContainer,
|
|
67
|
+
createServiceIdentifier,
|
|
68
|
+
globalMiddleware,
|
|
69
|
+
} from "@husky-di/core";
|
|
70
|
+
import {
|
|
71
|
+
decoratorMiddleware,
|
|
72
|
+
inject,
|
|
73
|
+
injectable,
|
|
74
|
+
} from "@husky-di/decorator";
|
|
75
|
+
|
|
76
|
+
interface Logger {
|
|
77
|
+
log(message: string): void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const ILogger = createServiceIdentifier<Logger>("ILogger");
|
|
42
81
|
|
|
43
82
|
@injectable()
|
|
44
|
-
class
|
|
83
|
+
class ConsoleLogger implements Logger {
|
|
45
84
|
log(message: string) {
|
|
46
|
-
console.log(message);
|
|
85
|
+
console.log(`[log] ${message}`);
|
|
47
86
|
}
|
|
48
87
|
}
|
|
49
88
|
|
|
50
89
|
@injectable()
|
|
51
90
|
class UserService {
|
|
52
|
-
constructor(@inject(
|
|
91
|
+
constructor(@inject(ILogger) private readonly logger: Logger) {}
|
|
92
|
+
|
|
93
|
+
getUser(id: string) {
|
|
94
|
+
this.logger.log(`load user: ${id}`);
|
|
95
|
+
return { id, name: "Ada" };
|
|
96
|
+
}
|
|
53
97
|
}
|
|
54
|
-
```
|
|
55
98
|
|
|
56
|
-
|
|
99
|
+
globalMiddleware.use(decoratorMiddleware);
|
|
100
|
+
|
|
101
|
+
const container = createContainer("AppContainer");
|
|
102
|
+
container.register(ILogger, { useClass: ConsoleLogger });
|
|
103
|
+
|
|
104
|
+
const userService = container.resolve(UserService);
|
|
105
|
+
console.log(userService.getUser("u-1"));
|
|
106
|
+
```
|
|
57
107
|
|
|
58
|
-
|
|
108
|
+
In this example:
|
|
59
109
|
|
|
60
|
-
|
|
110
|
+
- `decoratorMiddleware` reads constructor parameter metadata
|
|
111
|
+
- `@inject(ILogger)` maps an interface dependency to a runtime-visible service identifier
|
|
61
112
|
|
|
62
|
-
|
|
113
|
+
With plain `@husky-di/core`, a common alternative is to resolve the dependency inside the class directly:
|
|
63
114
|
|
|
64
115
|
```typescript
|
|
65
|
-
@
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
import { resolve } from "@husky-di/core";
|
|
117
|
+
|
|
118
|
+
class UserService {
|
|
119
|
+
private readonly logger = resolve(ILogger);
|
|
120
|
+
|
|
121
|
+
getUser(id: string) {
|
|
122
|
+
this.logger.log(`load user: ${id}`);
|
|
123
|
+
return { id, name: "Ada" };
|
|
69
124
|
}
|
|
70
125
|
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Adding It To An Existing Project
|
|
129
|
+
|
|
130
|
+
### Register Middleware Globally
|
|
131
|
+
|
|
132
|
+
This is the recommended option for most applications:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { globalMiddleware } from "@husky-di/core";
|
|
136
|
+
import { decoratorMiddleware } from "@husky-di/decorator";
|
|
137
|
+
|
|
138
|
+
globalMiddleware.use(decoratorMiddleware);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
That enables decorator-based constructor injection for all containers.
|
|
142
|
+
|
|
143
|
+
### Register Middleware Locally
|
|
144
|
+
|
|
145
|
+
If you only want decorator support in one container, register it locally instead:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const container = createContainer("FeatureContainer");
|
|
149
|
+
container.use(decoratorMiddleware);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Main APIs
|
|
153
|
+
|
|
154
|
+
### `@injectable()`
|
|
155
|
+
|
|
156
|
+
Marks a class as instantiable by the decorator middleware and merges its parameter metadata into the internal metadata store.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { injectable } from "@husky-di/decorator";
|
|
71
160
|
|
|
72
161
|
@injectable()
|
|
73
|
-
class
|
|
74
|
-
|
|
75
|
-
@inject(ConfigService, { dynamic: true })
|
|
76
|
-
private configRef: Ref<ConfigService>
|
|
77
|
-
) {}
|
|
162
|
+
class UserService {}
|
|
163
|
+
```
|
|
78
164
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
165
|
+
Key points:
|
|
166
|
+
|
|
167
|
+
- the same class cannot be decorated with `@injectable()` more than once
|
|
168
|
+
- parameters without explicit `@inject()` are inferred from `design:paramtypes`
|
|
169
|
+
- if inference resolves to a primitive instead of a class, resolution fails immediately
|
|
170
|
+
|
|
171
|
+
### `@inject()`
|
|
172
|
+
|
|
173
|
+
Explicitly declares the service identifier for a constructor parameter.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
@injectable()
|
|
177
|
+
class UserService {
|
|
178
|
+
constructor(@inject(ILogger) private readonly logger: Logger) {}
|
|
83
179
|
}
|
|
84
180
|
```
|
|
85
181
|
|
|
86
|
-
|
|
182
|
+
The supported service identifier kinds match `core`:
|
|
183
|
+
|
|
184
|
+
- class constructor
|
|
185
|
+
- `symbol`
|
|
186
|
+
- `string`
|
|
87
187
|
|
|
88
|
-
|
|
188
|
+
### `@tagged()`
|
|
189
|
+
|
|
190
|
+
`@tagged()` is the lower-level metadata decorator.
|
|
191
|
+
It accepts the full `InjectionMetadata` object directly.
|
|
89
192
|
|
|
90
193
|
```typescript
|
|
194
|
+
import { tagged, injectable } from "@husky-di/decorator";
|
|
195
|
+
|
|
91
196
|
@injectable()
|
|
92
|
-
class
|
|
197
|
+
class UserService {
|
|
93
198
|
constructor(
|
|
94
|
-
@
|
|
95
|
-
|
|
199
|
+
@tagged({ serviceIdentifier: ILogger, optional: true })
|
|
200
|
+
private readonly logger?: Logger
|
|
96
201
|
) {}
|
|
97
202
|
}
|
|
98
203
|
```
|
|
99
204
|
|
|
100
|
-
|
|
205
|
+
This is useful when:
|
|
206
|
+
|
|
207
|
+
- you want to build a domain-specific custom decorator
|
|
208
|
+
- you want full control over the metadata instead of the `@inject()` shorthand
|
|
209
|
+
|
|
210
|
+
## Cases Where You Should Explicitly Use `@inject()`
|
|
101
211
|
|
|
102
|
-
|
|
212
|
+
### Interface Types
|
|
213
|
+
|
|
214
|
+
Interfaces do not exist at runtime, so you must provide a runtime-visible service identifier explicitly.
|
|
103
215
|
|
|
104
216
|
```typescript
|
|
217
|
+
interface Logger {
|
|
218
|
+
log(message: string): void;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const ILogger = createServiceIdentifier<Logger>("ILogger");
|
|
222
|
+
|
|
105
223
|
@injectable()
|
|
106
|
-
class
|
|
107
|
-
constructor(
|
|
108
|
-
@inject(ExistingService, { optional: true })
|
|
109
|
-
public service: ExistingService
|
|
110
|
-
) {}
|
|
224
|
+
class UserService {
|
|
225
|
+
constructor(@inject(ILogger) private readonly logger: Logger) {}
|
|
111
226
|
}
|
|
112
227
|
```
|
|
113
228
|
|
|
114
|
-
|
|
229
|
+
### Primitive Types
|
|
115
230
|
|
|
116
|
-
|
|
231
|
+
Primitive types such as `string`, `number`, and `boolean` also need an explicit identifier.
|
|
117
232
|
|
|
118
233
|
```typescript
|
|
119
|
-
|
|
120
|
-
import { decoratorMiddleware } from "@husky-di/decorator";
|
|
234
|
+
const API_BASE_URL = Symbol("API_BASE_URL");
|
|
121
235
|
|
|
122
|
-
|
|
123
|
-
|
|
236
|
+
@injectable()
|
|
237
|
+
class ApiClient {
|
|
238
|
+
constructor(@inject(API_BASE_URL) private readonly baseUrl: string) {}
|
|
239
|
+
}
|
|
124
240
|
```
|
|
125
241
|
|
|
126
|
-
|
|
242
|
+
### When You Want To Override Inference
|
|
127
243
|
|
|
128
|
-
|
|
129
|
-
import "reflect-metadata";
|
|
130
|
-
import { createContainer, globalMiddleware } from "@husky-di/core";
|
|
131
|
-
import { decoratorMiddleware, inject, injectable } from "@husky-di/decorator";
|
|
244
|
+
Even if the parameter type is a class, you should still write `@inject()` or `@tagged()` when you want to:
|
|
132
245
|
|
|
133
|
-
|
|
134
|
-
|
|
246
|
+
- use a different token
|
|
247
|
+
- enable `optional`
|
|
248
|
+
- enable `ref`
|
|
249
|
+
- enable `dynamic`
|
|
250
|
+
- change the `core.resolve()` container scope
|
|
251
|
+
|
|
252
|
+
## Injection Options
|
|
253
|
+
|
|
254
|
+
The decorator layer supports the same resolve options as `core.resolve()`.
|
|
255
|
+
|
|
256
|
+
### `optional`
|
|
135
257
|
|
|
136
|
-
|
|
137
|
-
const container = createContainer();
|
|
258
|
+
Return `undefined` instead of throwing when the dependency is missing.
|
|
138
259
|
|
|
139
|
-
|
|
260
|
+
```typescript
|
|
140
261
|
@injectable()
|
|
141
|
-
class
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
262
|
+
class UserService {
|
|
263
|
+
constructor(
|
|
264
|
+
@inject("auditLogger", { optional: true })
|
|
265
|
+
private readonly auditLogger?: { log(message: string): void }
|
|
266
|
+
) {}
|
|
145
267
|
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### `ref`
|
|
271
|
+
|
|
272
|
+
Return a lazy reference, which is useful for deferred access or partially breaking circular dependencies.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import type { Ref } from "@husky-di/core";
|
|
146
276
|
|
|
147
277
|
@injectable()
|
|
148
|
-
class
|
|
149
|
-
constructor(
|
|
278
|
+
class UserService {
|
|
279
|
+
constructor(
|
|
280
|
+
@inject(ILogger, { ref: true })
|
|
281
|
+
private readonly loggerRef: Ref<Logger>
|
|
282
|
+
) {}
|
|
150
283
|
|
|
151
|
-
|
|
152
|
-
|
|
284
|
+
run() {
|
|
285
|
+
this.loggerRef.current.log("run");
|
|
153
286
|
}
|
|
154
287
|
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### `dynamic`
|
|
291
|
+
|
|
292
|
+
Return a dynamic reference whose `.current` value is re-resolved on every access.
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import type { Ref } from "@husky-di/core";
|
|
155
296
|
|
|
156
297
|
@injectable()
|
|
157
298
|
class UserService {
|
|
158
299
|
constructor(
|
|
159
|
-
@inject(
|
|
160
|
-
|
|
300
|
+
@inject(ILogger, { dynamic: true })
|
|
301
|
+
private readonly loggerRef: Ref<Logger>
|
|
161
302
|
) {}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
162
305
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
306
|
+
Prefer `ref` unless you specifically need to re-run resolution every time the value is read.
|
|
307
|
+
|
|
308
|
+
### `scope`
|
|
309
|
+
|
|
310
|
+
Choose which `core.resolve()` container perspective the decorator should use for
|
|
311
|
+
that parameter.
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import { ResolveContainerScopeEnum } from "@husky-di/core";
|
|
315
|
+
|
|
316
|
+
@injectable()
|
|
317
|
+
class DatabaseConsumer {
|
|
318
|
+
constructor(
|
|
319
|
+
@inject(IDatabaseOptions, { scope: ResolveContainerScopeEnum.origin })
|
|
320
|
+
private readonly options: { baseURL: string }
|
|
321
|
+
) {}
|
|
166
322
|
}
|
|
323
|
+
```
|
|
167
324
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
325
|
+
This is useful when a parent-provided class should consume child-container
|
|
326
|
+
overrides for a specific constructor parameter.
|
|
327
|
+
|
|
328
|
+
## When Automatic Inference Works
|
|
329
|
+
|
|
330
|
+
### Cases Where Type Inference Is Enough
|
|
331
|
+
|
|
332
|
+
If the parameter itself is a class, and that class is also marked with `@injectable()`, you can omit `@inject()`:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
@injectable()
|
|
336
|
+
class LoggerService {}
|
|
337
|
+
|
|
338
|
+
@injectable()
|
|
339
|
+
class UserService {
|
|
340
|
+
constructor(private readonly logger: LoggerService) {}
|
|
341
|
+
}
|
|
172
342
|
```
|
|
173
343
|
|
|
174
|
-
|
|
344
|
+
### Cases Where You Should Not Rely On Inference
|
|
345
|
+
|
|
346
|
+
Do not rely on automatic inference in these cases:
|
|
175
347
|
|
|
176
|
-
|
|
348
|
+
- the parameter type is an interface
|
|
349
|
+
- the parameter type is a primitive
|
|
350
|
+
- you need `optional`
|
|
351
|
+
- you need `ref`
|
|
352
|
+
- you need `dynamic`
|
|
353
|
+
- you want to bind the parameter to a different token than its runtime class
|
|
177
354
|
|
|
178
|
-
|
|
355
|
+
## Relationship To `core`
|
|
179
356
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
@injectable()
|
|
183
|
-
@injectable()
|
|
184
|
-
class TestService {}
|
|
185
|
-
```
|
|
357
|
+
`@husky-di/decorator` does not replace `core`.
|
|
358
|
+
It is a syntax layer built on top of it.
|
|
186
359
|
|
|
187
|
-
|
|
360
|
+
You still keep using:
|
|
188
361
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
362
|
+
- `createContainer()`
|
|
363
|
+
- `createServiceIdentifier()`
|
|
364
|
+
- `LifecycleEnum`
|
|
365
|
+
- `globalMiddleware`
|
|
366
|
+
- `resolve()` / `ref` / `dynamic`
|
|
192
367
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
constructor(@inject(NonInjectableService) dep: NonInjectableService) {}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
368
|
+
The decorator middleware only participates in the class-instantiation phase.
|
|
369
|
+
Registrations such as `useValue`, `useFactory`, and `useAlias` still follow normal `core` rules.
|
|
198
370
|
|
|
199
|
-
|
|
371
|
+
## Common Pitfalls
|
|
200
372
|
|
|
201
|
-
|
|
202
|
-
// 错误:检测到循环依赖
|
|
203
|
-
@injectable()
|
|
204
|
-
class ServiceA {
|
|
205
|
-
constructor(@inject(ServiceB) public serviceB: ServiceB) {}
|
|
206
|
-
}
|
|
373
|
+
### Forgetting To Register `decoratorMiddleware`
|
|
207
374
|
|
|
208
|
-
|
|
209
|
-
class ServiceB {
|
|
210
|
-
constructor(@inject(ServiceA) public serviceA: ServiceA) {}
|
|
211
|
-
}
|
|
212
|
-
```
|
|
375
|
+
If the middleware is not registered, the container does not read decorator metadata.
|
|
213
376
|
|
|
214
|
-
|
|
377
|
+
### Forgetting To Import `reflect-metadata`
|
|
215
378
|
|
|
216
|
-
|
|
379
|
+
Without Reflect metadata at runtime, the implementation cannot access `design:paramtypes`.
|
|
217
380
|
|
|
218
|
-
|
|
219
|
-
- `@inject()` 为特定参数位置设置注入配置
|
|
220
|
-
- `decoratorMiddleware` 在实例化时读取元数据并执行注入
|
|
381
|
+
### Using Interfaces Or Primitives Without `@inject()`
|
|
221
382
|
|
|
222
|
-
|
|
383
|
+
That leaves the metadata incomplete or points inference at the wrong runtime identifier.
|
|
223
384
|
|
|
224
|
-
|
|
225
|
-
2. **为所有依赖参数使用 @inject() 装饰器**
|
|
226
|
-
3. **合理使用注入选项(dynamic、ref、optional)**
|
|
227
|
-
4. **避免循环依赖,必要时使用 ref 选项**
|
|
228
|
-
5. **在应用启动时注册 decoratorMiddleware**
|
|
385
|
+
### Applying `@injectable()` Twice To The Same Class
|
|
229
386
|
|
|
230
|
-
|
|
387
|
+
This throws `E_DUPLICATE_INJECTABLE`.
|
|
231
388
|
|
|
232
|
-
|
|
389
|
+
### Using `dynamic` And `ref` Together
|
|
233
390
|
|
|
234
|
-
|
|
391
|
+
These options are mutually exclusive and throw `E_CONFLICTING_OPTIONS`.
|
|
235
392
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
393
|
+
## Complete Example
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import "reflect-metadata";
|
|
397
|
+
import {
|
|
398
|
+
createContainer,
|
|
399
|
+
createServiceIdentifier,
|
|
400
|
+
globalMiddleware,
|
|
401
|
+
type Ref,
|
|
402
|
+
} from "@husky-di/core";
|
|
403
|
+
import {
|
|
404
|
+
decoratorMiddleware,
|
|
405
|
+
inject,
|
|
406
|
+
injectable,
|
|
407
|
+
} from "@husky-di/decorator";
|
|
408
|
+
|
|
409
|
+
interface Config {
|
|
410
|
+
apiBaseUrl: string;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
interface Logger {
|
|
414
|
+
log(message: string): void;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const IConfig = createServiceIdentifier<Config>("IConfig");
|
|
418
|
+
const ILogger = createServiceIdentifier<Logger>("ILogger");
|
|
419
|
+
|
|
420
|
+
@injectable()
|
|
421
|
+
class ConsoleLogger implements Logger {
|
|
422
|
+
log(message: string) {
|
|
423
|
+
console.log(message);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
@injectable()
|
|
428
|
+
class ApiClient {
|
|
429
|
+
constructor(
|
|
430
|
+
@inject(IConfig) private readonly config: Config,
|
|
431
|
+
@inject(ILogger, { ref: true }) private readonly loggerRef: Ref<Logger>
|
|
432
|
+
) {}
|
|
433
|
+
|
|
434
|
+
getUser(id: string) {
|
|
435
|
+
this.loggerRef.current.log(`GET ${this.config.apiBaseUrl}/users/${id}`);
|
|
436
|
+
return { id, name: "Ada" };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
globalMiddleware.use(decoratorMiddleware);
|
|
441
|
+
|
|
442
|
+
const container = createContainer("AppContainer");
|
|
443
|
+
container.register(IConfig, {
|
|
444
|
+
useValue: { apiBaseUrl: "https://api.example.com" },
|
|
445
|
+
});
|
|
446
|
+
container.register(ILogger, { useClass: ConsoleLogger });
|
|
447
|
+
|
|
448
|
+
const apiClient = container.resolve(ApiClient);
|
|
449
|
+
console.log(apiClient.getUser("u-1"));
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Related Docs
|
|
453
|
+
|
|
454
|
+
- container and resolution model: `../core/README.md`
|
|
455
|
+
- decorator behavior specification: `./docs/SPECIFICATION.md`
|
|
456
|
+
- module system: `../module/README.md`
|
|
457
|
+
|
|
458
|
+
## Local Development
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
pnpm build
|
|
462
|
+
pnpm test
|
|
463
|
+
```
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @author AEPKILL
|
|
4
4
|
* @created 2023-05-24 10:47:34
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
7
|
-
export type InjectionMetadata<T> =
|
|
6
|
+
import type { ResolveHelperOptions, ServiceIdentifier } from "@husky-di/core";
|
|
7
|
+
export type InjectionMetadata<T> = ResolveHelperOptions<T> & {
|
|
8
8
|
serviceIdentifier: ServiceIdentifier<T>;
|
|
9
9
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@husky-di/decorator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@husky-di/core": "1.
|
|
18
|
+
"@husky-di/core": "1.3.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@biomejs/biome": "2.4.15",
|