@navios/di 0.2.1 → 0.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 +299 -38
- package/docs/README.md +121 -48
- package/docs/api-reference.md +763 -0
- package/docs/container.md +274 -0
- package/docs/examples/basic-usage.mts +97 -0
- package/docs/examples/factory-pattern.mts +318 -0
- package/docs/examples/injection-tokens.mts +225 -0
- package/docs/examples/request-scope-example.mts +254 -0
- package/docs/examples/service-lifecycle.mts +359 -0
- package/docs/factory.md +584 -0
- package/docs/getting-started.md +308 -0
- package/docs/injectable.md +496 -0
- package/docs/injection-tokens.md +400 -0
- package/docs/lifecycle.md +539 -0
- package/docs/scopes.md +749 -0
- package/lib/_tsup-dts-rollup.d.mts +490 -145
- package/lib/_tsup-dts-rollup.d.ts +490 -145
- package/lib/index.d.mts +26 -12
- package/lib/index.d.ts +26 -12
- package/lib/index.js +993 -462
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +983 -453
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
- package/project.json +10 -2
- package/src/__tests__/container.spec.mts +1301 -0
- package/src/__tests__/factory.spec.mts +137 -0
- package/src/__tests__/injectable.spec.mts +32 -88
- package/src/__tests__/injection-token.spec.mts +333 -17
- package/src/__tests__/request-scope.spec.mts +263 -0
- package/src/__type-tests__/factory.spec-d.mts +65 -0
- package/src/__type-tests__/inject.spec-d.mts +27 -28
- package/src/__type-tests__/injectable.spec-d.mts +42 -206
- package/src/container.mts +167 -0
- package/src/decorators/factory.decorator.mts +79 -0
- package/src/decorators/index.mts +1 -0
- package/src/decorators/injectable.decorator.mts +6 -56
- package/src/enums/injectable-scope.enum.mts +5 -1
- package/src/event-emitter.mts +18 -20
- package/src/factory-context.mts +2 -10
- package/src/index.mts +3 -2
- package/src/injection-token.mts +19 -4
- package/src/injector.mts +8 -20
- package/src/interfaces/factory.interface.mts +3 -3
- package/src/interfaces/index.mts +2 -0
- package/src/interfaces/on-service-destroy.interface.mts +3 -0
- package/src/interfaces/on-service-init.interface.mts +3 -0
- package/src/registry.mts +7 -16
- package/src/request-context-holder.mts +145 -0
- package/src/service-instantiator.mts +158 -0
- package/src/service-locator-event-bus.mts +0 -28
- package/src/service-locator-instance-holder.mts +27 -16
- package/src/service-locator-manager.mts +84 -0
- package/src/service-locator.mts +548 -393
- package/src/utils/defer.mts +73 -0
- package/src/utils/get-injectors.mts +91 -78
- package/src/utils/index.mts +2 -0
- package/src/utils/types.mts +52 -0
- package/docs/concepts/injectable.md +0 -182
- package/docs/concepts/injection-token.md +0 -145
- package/src/proxy-service-locator.mts +0 -83
- package/src/resolve-service.mts +0 -41
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# Injection Token
|
|
2
|
-
|
|
3
|
-
Injection tokens are used to identify and retrieve services from the dependency injection container. They are typically defined as constants and used to inject services into classes.
|
|
4
|
-
|
|
5
|
-
## Creating an Injection Token
|
|
6
|
-
|
|
7
|
-
To create an injection token, you can use the `InjectionToken` class from the `@navios/di` or `@navios/core` package. Here's an example:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { InjectionToken } from '@navios/di'
|
|
11
|
-
|
|
12
|
-
export const GREETER_SERVICE =
|
|
13
|
-
InjectionToken.create<GreeterService>('GreeterService')
|
|
14
|
-
|
|
15
|
-
@Injectable({ token: GREETER_SERVICE })
|
|
16
|
-
class GreeterService {}
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Using an Injection Token
|
|
20
|
-
|
|
21
|
-
To use an injection token, you can inject it into a class or a function.
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
import { inject } from '@navios/di'
|
|
25
|
-
|
|
26
|
-
const greeterService = await inject(GREETER_SERVICE)
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
or
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
@Injectable()
|
|
33
|
-
class UserService {
|
|
34
|
-
private readonly greeterService = inject(GREETER_SERVICE) // This will be a Promise<GreeterService>
|
|
35
|
-
private readonly greeterServiceSync = syncInject(GREETER_SERVICE) // This will be a GreeterService
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Adding a parameter to the injection token
|
|
40
|
-
|
|
41
|
-
You can add a parameter to the injection token to make it more specific.
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
import { z } from 'zod/v4'
|
|
45
|
-
|
|
46
|
-
const GreeterServiceParams = z.object({
|
|
47
|
-
context: z.string(),
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
export const GREETER_SERVICE = InjectionToken.create<
|
|
51
|
-
GreeterService,
|
|
52
|
-
typeof GreeterServiceParams
|
|
53
|
-
>('GreeterService', GreeterServiceParams)
|
|
54
|
-
|
|
55
|
-
@Injectable({ token: GREETER_SERVICE })
|
|
56
|
-
class GreeterService {}
|
|
57
|
-
|
|
58
|
-
const greeterService = await inject(GREETER_SERVICE, { context: 'Hello' })
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Providing a value to an injection token
|
|
62
|
-
|
|
63
|
-
You can provide a value to an injection token by using the `bound` function.
|
|
64
|
-
|
|
65
|
-
```ts
|
|
66
|
-
import { inject, InjectionToken } from '@navios/di'
|
|
67
|
-
|
|
68
|
-
const helloGreeterService = InjectionToken.bound(GREETER_SERVICE, {
|
|
69
|
-
context: 'Hello',
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const greeterService = await inject(helloGreeterService)
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Providing a factory to an injection token
|
|
76
|
-
|
|
77
|
-
You can provide a factory to an injection token by using the `factory` function.
|
|
78
|
-
|
|
79
|
-
It is useful, when you need to bind a value to a result of another service (e.g. a configuration).
|
|
80
|
-
|
|
81
|
-
```ts
|
|
82
|
-
import { inject, InjectionToken } from '@navios/di'
|
|
83
|
-
|
|
84
|
-
@Injectable()
|
|
85
|
-
class GreeterConfigService {
|
|
86
|
-
getContext() {
|
|
87
|
-
return 'Hello'
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const helloGreeterService = InjectionToken.factory(
|
|
91
|
-
GREETER_SERVICE,
|
|
92
|
-
async () => {
|
|
93
|
-
const config = await inject(GreeterConfigService)
|
|
94
|
-
return { context: config.getContext() }
|
|
95
|
-
},
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
const greeterService = await inject(helloGreeterService)
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## API
|
|
102
|
-
|
|
103
|
-
### `InjectionToken.create`
|
|
104
|
-
|
|
105
|
-
Creates a new injection token.
|
|
106
|
-
|
|
107
|
-
Generic arguments:
|
|
108
|
-
|
|
109
|
-
- `T`: The type of the value to bind to the injection token.
|
|
110
|
-
- `P`: Zod schema type of the parameters of the injection token.
|
|
111
|
-
|
|
112
|
-
Arguments:
|
|
113
|
-
|
|
114
|
-
- `name: string | Class | symbol`: The name of the injection token.
|
|
115
|
-
- `params: ZodSchema | undefined`: The parameters of the injection token.
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
|
|
119
|
-
- `InjectionToken`: The new injection token.
|
|
120
|
-
|
|
121
|
-
### `InjectionToken.bound`
|
|
122
|
-
|
|
123
|
-
Creates a new injection token that is bound to a value.
|
|
124
|
-
|
|
125
|
-
Arguments:
|
|
126
|
-
|
|
127
|
-
- `token: InjectionToken`: The injection token to bind.
|
|
128
|
-
- `value: any`: The value to bind to the injection token.
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
|
|
132
|
-
- `BoundInjectionToken`: The new injection token.
|
|
133
|
-
|
|
134
|
-
### `InjectionToken.factory`
|
|
135
|
-
|
|
136
|
-
Creates a new injection token that is a factory.
|
|
137
|
-
|
|
138
|
-
Arguments:
|
|
139
|
-
|
|
140
|
-
- `token: InjectionToken`: The injection token to bind.
|
|
141
|
-
- `factory: () => Promise<any>`: The factory to bind to the injection token.
|
|
142
|
-
|
|
143
|
-
Returns:
|
|
144
|
-
|
|
145
|
-
- `FactoryInjectionToken`: The new injection token.
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { z, ZodObject, ZodOptional } from 'zod/v4'
|
|
2
|
-
|
|
3
|
-
import type { FactoryContext } from './factory-context.mjs'
|
|
4
|
-
import type {
|
|
5
|
-
BoundInjectionToken,
|
|
6
|
-
FactoryInjectionToken,
|
|
7
|
-
InjectionToken,
|
|
8
|
-
} from './injection-token.mjs'
|
|
9
|
-
import type { ServiceLocatorEventBus } from './service-locator-event-bus.mjs'
|
|
10
|
-
import type { ServiceLocator } from './service-locator.mjs'
|
|
11
|
-
|
|
12
|
-
export class ProxyServiceLocator implements ServiceLocator {
|
|
13
|
-
constructor(
|
|
14
|
-
private readonly serviceLocator: ServiceLocator,
|
|
15
|
-
private readonly ctx: FactoryContext,
|
|
16
|
-
) {}
|
|
17
|
-
|
|
18
|
-
getEventBus(): ServiceLocatorEventBus {
|
|
19
|
-
return this.serviceLocator.getEventBus()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// @ts-expect-error We don't need all the properties of the class
|
|
23
|
-
public getInstance(
|
|
24
|
-
token:
|
|
25
|
-
| InjectionToken<any, any>
|
|
26
|
-
| BoundInjectionToken<any, any>
|
|
27
|
-
| FactoryInjectionToken<any, any>,
|
|
28
|
-
args?: any,
|
|
29
|
-
) {
|
|
30
|
-
// @ts-expect-error We don't need all the properties of the class
|
|
31
|
-
return this.ctx.inject(token, args).then(
|
|
32
|
-
(instance) => {
|
|
33
|
-
return [undefined, instance]
|
|
34
|
-
},
|
|
35
|
-
(error) => {
|
|
36
|
-
return [error]
|
|
37
|
-
},
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
public getOrThrowInstance<
|
|
41
|
-
Instance,
|
|
42
|
-
Schema extends ZodObject | ZodOptional<ZodObject> | undefined,
|
|
43
|
-
>(
|
|
44
|
-
token: InjectionToken<Instance, Schema>,
|
|
45
|
-
args: Schema extends ZodObject
|
|
46
|
-
? z.input<Schema>
|
|
47
|
-
: Schema extends ZodOptional<ZodObject>
|
|
48
|
-
? z.input<Schema> | undefined
|
|
49
|
-
: undefined,
|
|
50
|
-
): Promise<Instance> {
|
|
51
|
-
return this.ctx.inject(token, args)
|
|
52
|
-
}
|
|
53
|
-
public getSyncInstance<
|
|
54
|
-
Instance,
|
|
55
|
-
Schema extends ZodObject | ZodOptional<ZodObject> | undefined,
|
|
56
|
-
>(
|
|
57
|
-
token: InjectionToken<Instance, Schema>,
|
|
58
|
-
args: Schema extends ZodObject
|
|
59
|
-
? z.input<Schema>
|
|
60
|
-
: Schema extends ZodOptional<ZodObject>
|
|
61
|
-
? z.input<Schema> | undefined
|
|
62
|
-
: undefined,
|
|
63
|
-
): Instance | null {
|
|
64
|
-
return this.serviceLocator.getSyncInstance<Instance, Schema>(token, args)
|
|
65
|
-
}
|
|
66
|
-
invalidate(service: string, round?: number): Promise<any> {
|
|
67
|
-
return this.serviceLocator.invalidate(service, round)
|
|
68
|
-
}
|
|
69
|
-
ready(): Promise<null> {
|
|
70
|
-
return this.serviceLocator.ready()
|
|
71
|
-
}
|
|
72
|
-
makeInstanceName(token: InjectionToken<any, any>, args: any): string {
|
|
73
|
-
return this.serviceLocator.makeInstanceName(token, args)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function makeProxyServiceLocator(
|
|
78
|
-
serviceLocator: ServiceLocator,
|
|
79
|
-
ctx: FactoryContext,
|
|
80
|
-
): ServiceLocator {
|
|
81
|
-
// @ts-expect-error We don't need all the properties of the class
|
|
82
|
-
return new ProxyServiceLocator(serviceLocator, ctx)
|
|
83
|
-
}
|
package/src/resolve-service.mts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { FactoryContext } from './factory-context.mjs'
|
|
2
|
-
import type { ClassType } from './injection-token.mjs'
|
|
3
|
-
|
|
4
|
-
import { makeProxyServiceLocator } from './proxy-service-locator.mjs'
|
|
5
|
-
import { getInjectors } from './utils/index.mjs'
|
|
6
|
-
|
|
7
|
-
export async function resolveService<T extends ClassType>(
|
|
8
|
-
ctx: FactoryContext,
|
|
9
|
-
target: T,
|
|
10
|
-
args: any[] = [],
|
|
11
|
-
): Promise<InstanceType<T>> {
|
|
12
|
-
const { wrapSyncInit, provideServiceLocator } = getInjectors({
|
|
13
|
-
baseLocator: ctx.locator,
|
|
14
|
-
})
|
|
15
|
-
const proxyServiceLocator = makeProxyServiceLocator(ctx.locator, ctx)
|
|
16
|
-
const tryLoad = wrapSyncInit(() => {
|
|
17
|
-
const original = provideServiceLocator(proxyServiceLocator)
|
|
18
|
-
let result = new target(...args)
|
|
19
|
-
provideServiceLocator(original)
|
|
20
|
-
return result
|
|
21
|
-
})
|
|
22
|
-
let [instance, promises] = tryLoad()
|
|
23
|
-
if (promises.length > 0) {
|
|
24
|
-
await Promise.allSettled(promises)
|
|
25
|
-
const newRes = tryLoad()
|
|
26
|
-
instance = newRes[0]
|
|
27
|
-
promises = newRes[1]
|
|
28
|
-
}
|
|
29
|
-
if (promises.length > 0) {
|
|
30
|
-
console.error(`[ServiceLocator] ${target.name} has problem with it's definition.
|
|
31
|
-
|
|
32
|
-
One or more of the dependencies are registered as a InjectableScope.Instance and are used with syncInject.
|
|
33
|
-
|
|
34
|
-
Please use inject instead of syncInject to load those dependencies.`)
|
|
35
|
-
throw new Error(
|
|
36
|
-
`[ServiceLocator] Service ${target.name} cannot be instantiated.`,
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return instance
|
|
41
|
-
}
|