@navios/di 0.2.0 → 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 +301 -39
- package/docs/README.md +122 -49
- 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 +495 -150
- package/lib/_tsup-dts-rollup.d.ts +495 -150
- 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 +24 -9
- 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 +550 -395
- package/src/utils/defer.mts +73 -0
- package/src/utils/get-injectors.mts +93 -80
- 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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { z, ZodType } from 'zod/v4'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
BoundInjectionToken,
|
|
5
|
+
ClassType,
|
|
6
|
+
FactoryInjectionToken,
|
|
7
|
+
InjectionToken,
|
|
8
|
+
InjectionTokenSchemaType,
|
|
9
|
+
} from './injection-token.mjs'
|
|
10
|
+
import type { Factorable } from './interfaces/factory.interface.mjs'
|
|
11
|
+
import type { Registry } from './registry.mjs'
|
|
12
|
+
import type { RequestContextHolder } from './request-context-holder.mjs'
|
|
13
|
+
import type { Injectors } from './utils/index.mjs'
|
|
14
|
+
import type { Join, UnionToArray } from './utils/types.mjs'
|
|
15
|
+
|
|
16
|
+
import { Injectable } from './decorators/injectable.decorator.mjs'
|
|
17
|
+
import { InjectableScope, InjectableType } from './enums/index.mjs'
|
|
18
|
+
import { defaultInjectors } from './injector.mjs'
|
|
19
|
+
import { globalRegistry } from './registry.mjs'
|
|
20
|
+
import { ServiceLocator } from './service-locator.mjs'
|
|
21
|
+
import { getInjectableToken } from './utils/get-injectable-token.mjs'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Container class that provides a simplified public API for dependency injection.
|
|
25
|
+
* It wraps a ServiceLocator instance and provides convenient methods for getting instances.
|
|
26
|
+
*/
|
|
27
|
+
@Injectable()
|
|
28
|
+
export class Container {
|
|
29
|
+
private readonly serviceLocator: ServiceLocator
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
registry: Registry = globalRegistry,
|
|
33
|
+
logger: Console | null = null,
|
|
34
|
+
injectors: Injectors = defaultInjectors,
|
|
35
|
+
) {
|
|
36
|
+
this.serviceLocator = new ServiceLocator(registry, logger, injectors)
|
|
37
|
+
this.registerSelf()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private registerSelf() {
|
|
41
|
+
const token = getInjectableToken(Container)
|
|
42
|
+
const instanceName = this.serviceLocator.getInstanceIdentifier(token)
|
|
43
|
+
this.serviceLocator
|
|
44
|
+
.getManager()
|
|
45
|
+
.storeCreatedHolder(
|
|
46
|
+
instanceName,
|
|
47
|
+
this,
|
|
48
|
+
InjectableType.Class,
|
|
49
|
+
InjectableScope.Singleton,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets an instance from the container.
|
|
55
|
+
* This method has the same type signature as the inject method from get-injectors.mts
|
|
56
|
+
*/
|
|
57
|
+
// #1 Simple class
|
|
58
|
+
get<T extends ClassType>(
|
|
59
|
+
token: T,
|
|
60
|
+
): InstanceType<T> extends Factorable<infer R>
|
|
61
|
+
? Promise<R>
|
|
62
|
+
: Promise<InstanceType<T>>
|
|
63
|
+
// #2 Token with required Schema
|
|
64
|
+
get<T, S extends InjectionTokenSchemaType>(
|
|
65
|
+
token: InjectionToken<T, S>,
|
|
66
|
+
args: z.input<S>,
|
|
67
|
+
): Promise<T>
|
|
68
|
+
// #3 Token with optional Schema
|
|
69
|
+
get<T, S extends InjectionTokenSchemaType, R extends boolean>(
|
|
70
|
+
token: InjectionToken<T, S, R>,
|
|
71
|
+
): R extends false
|
|
72
|
+
? Promise<T>
|
|
73
|
+
: S extends ZodType<infer Type>
|
|
74
|
+
? `Error: Your token requires args: ${Join<
|
|
75
|
+
UnionToArray<keyof Type>,
|
|
76
|
+
', '
|
|
77
|
+
>}`
|
|
78
|
+
: 'Error: Your token requires args'
|
|
79
|
+
// #4 Token with no Schema
|
|
80
|
+
get<T>(token: InjectionToken<T, undefined>): Promise<T>
|
|
81
|
+
get<T>(token: BoundInjectionToken<T, any>): Promise<T>
|
|
82
|
+
get<T>(token: FactoryInjectionToken<T, any>): Promise<T>
|
|
83
|
+
|
|
84
|
+
async get(
|
|
85
|
+
token:
|
|
86
|
+
| ClassType
|
|
87
|
+
| InjectionToken<any>
|
|
88
|
+
| BoundInjectionToken<any, any>
|
|
89
|
+
| FactoryInjectionToken<any, any>,
|
|
90
|
+
args?: unknown,
|
|
91
|
+
) {
|
|
92
|
+
return this.serviceLocator.getOrThrowInstance(token, args as any)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Gets the underlying ServiceLocator instance for advanced usage
|
|
97
|
+
*/
|
|
98
|
+
getServiceLocator(): ServiceLocator {
|
|
99
|
+
return this.serviceLocator
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Invalidates a service and its dependencies
|
|
104
|
+
*/
|
|
105
|
+
async invalidate(service: unknown): Promise<void> {
|
|
106
|
+
const holderMap = this.serviceLocator
|
|
107
|
+
.getManager()
|
|
108
|
+
.filter((holder) => holder.instance === service)
|
|
109
|
+
if (holderMap.size === 0) {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
const holder = holderMap.values().next().value
|
|
113
|
+
if (holder) {
|
|
114
|
+
await this.serviceLocator.invalidate(holder.name)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Waits for all pending operations to complete
|
|
120
|
+
*/
|
|
121
|
+
async ready(): Promise<void> {
|
|
122
|
+
await this.serviceLocator.ready()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// REQUEST CONTEXT MANAGEMENT
|
|
127
|
+
// ============================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Begins a new request context with the given parameters.
|
|
131
|
+
* @param requestId Unique identifier for this request
|
|
132
|
+
* @param metadata Optional metadata for the request
|
|
133
|
+
* @param priority Priority for resolution (higher = more priority)
|
|
134
|
+
* @returns The created request context holder
|
|
135
|
+
*/
|
|
136
|
+
beginRequest(
|
|
137
|
+
requestId: string,
|
|
138
|
+
metadata?: Record<string, any>,
|
|
139
|
+
priority: number = 100,
|
|
140
|
+
): RequestContextHolder {
|
|
141
|
+
return this.serviceLocator.beginRequest(requestId, metadata, priority)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Ends a request context and cleans up all associated instances.
|
|
146
|
+
* @param requestId The request ID to end
|
|
147
|
+
*/
|
|
148
|
+
async endRequest(requestId: string): Promise<void> {
|
|
149
|
+
await this.serviceLocator.endRequest(requestId)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Gets the current request context.
|
|
154
|
+
* @returns The current request context holder or null
|
|
155
|
+
*/
|
|
156
|
+
getCurrentRequestContext(): RequestContextHolder | null {
|
|
157
|
+
return this.serviceLocator.getCurrentRequestContext()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Sets the current request context.
|
|
162
|
+
* @param requestId The request ID to set as current
|
|
163
|
+
*/
|
|
164
|
+
setCurrentRequestContext(requestId: string): void {
|
|
165
|
+
this.serviceLocator.setCurrentRequestContext(requestId)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClassTypeWithInstance,
|
|
3
|
+
InjectionTokenSchemaType,
|
|
4
|
+
} from '../injection-token.mjs'
|
|
5
|
+
import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs'
|
|
6
|
+
import type { Registry } from '../registry.mjs'
|
|
7
|
+
|
|
8
|
+
import { InjectableScope, InjectableType } from '../enums/index.mjs'
|
|
9
|
+
import { InjectionToken } from '../injection-token.mjs'
|
|
10
|
+
import { globalRegistry } from '../registry.mjs'
|
|
11
|
+
import { InjectableTokenMeta } from '../symbols/index.mjs'
|
|
12
|
+
|
|
13
|
+
export interface FactoryOptions {
|
|
14
|
+
scope?: InjectableScope
|
|
15
|
+
token?: InjectionToken<any, any>
|
|
16
|
+
registry?: Registry
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// #1 Factory without arguments
|
|
20
|
+
export function Factory<R>(options?: {
|
|
21
|
+
scope?: InjectableScope
|
|
22
|
+
registry?: Registry
|
|
23
|
+
}): <T extends ClassTypeWithInstance<Factorable<R>>>(
|
|
24
|
+
target: T,
|
|
25
|
+
context?: ClassDecoratorContext,
|
|
26
|
+
) => T
|
|
27
|
+
|
|
28
|
+
// #2 Factory with typed token
|
|
29
|
+
export function Factory<R, S>(options: {
|
|
30
|
+
scope?: InjectableScope
|
|
31
|
+
token: InjectionToken<R, S>
|
|
32
|
+
registry?: Registry
|
|
33
|
+
}): R extends undefined // #2.1 Check that token has a type
|
|
34
|
+
? never // #2.1.1 Token must have a type
|
|
35
|
+
: S extends InjectionTokenSchemaType // #2.2 Check that schema is an object or a record
|
|
36
|
+
? <T extends ClassTypeWithInstance<FactorableWithArgs<R, S>>>( // #2.2.1 Token have a schema
|
|
37
|
+
target: T,
|
|
38
|
+
context?: ClassDecoratorContext,
|
|
39
|
+
) => T
|
|
40
|
+
: S extends undefined // #2.3 For a factory without schema
|
|
41
|
+
? <T extends ClassTypeWithInstance<Factorable<R>>>( // #2.3.1 Token without a schema
|
|
42
|
+
target: T,
|
|
43
|
+
context?: ClassDecoratorContext,
|
|
44
|
+
) => T
|
|
45
|
+
: never // #2.4 Cannot use a token without a type and schema
|
|
46
|
+
|
|
47
|
+
export function Factory({
|
|
48
|
+
scope = InjectableScope.Singleton,
|
|
49
|
+
token,
|
|
50
|
+
registry = globalRegistry,
|
|
51
|
+
}: FactoryOptions = {}) {
|
|
52
|
+
return <
|
|
53
|
+
T extends ClassTypeWithInstance<
|
|
54
|
+
Factorable<any> | FactorableWithArgs<any, any>
|
|
55
|
+
>,
|
|
56
|
+
>(
|
|
57
|
+
target: T,
|
|
58
|
+
context?: ClassDecoratorContext,
|
|
59
|
+
): T => {
|
|
60
|
+
if (
|
|
61
|
+
(context && context.kind !== 'class') ||
|
|
62
|
+
(target instanceof Function && !context)
|
|
63
|
+
) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'[ServiceLocator] @Factory decorator can only be used on classes.',
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let injectableToken: InjectionToken<any, any> =
|
|
70
|
+
token ?? InjectionToken.create(target)
|
|
71
|
+
|
|
72
|
+
registry.set(injectableToken, scope, target, InjectableType.Factory)
|
|
73
|
+
|
|
74
|
+
// @ts-expect-error
|
|
75
|
+
target[InjectableTokenMeta] = injectableToken
|
|
76
|
+
|
|
77
|
+
return target
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/decorators/index.mts
CHANGED
|
@@ -8,21 +8,17 @@ import type {
|
|
|
8
8
|
ClassTypeWithInstanceAndArgument,
|
|
9
9
|
ClassTypeWithInstanceAndOptionalArgument,
|
|
10
10
|
ClassTypeWithOptionalArgument,
|
|
11
|
-
InjectionTokenSchemaType,
|
|
12
11
|
OptionalInjectionTokenSchemaType,
|
|
13
12
|
} from '../injection-token.mjs'
|
|
14
|
-
import type { Factory, FactoryWithArgs } from '../interfaces/index.mjs'
|
|
15
13
|
import type { Registry } from '../registry.mjs'
|
|
16
14
|
|
|
17
15
|
import { InjectableScope, InjectableType } from '../enums/index.mjs'
|
|
18
16
|
import { InjectionToken } from '../injection-token.mjs'
|
|
19
17
|
import { globalRegistry } from '../registry.mjs'
|
|
20
|
-
import { resolveService } from '../resolve-service.mjs'
|
|
21
18
|
import { InjectableTokenMeta } from '../symbols/index.mjs'
|
|
22
19
|
|
|
23
20
|
export interface InjectableOptions {
|
|
24
21
|
scope?: InjectableScope
|
|
25
|
-
type?: InjectableType
|
|
26
22
|
token?: InjectionToken<any, any>
|
|
27
23
|
registry?: Registry
|
|
28
24
|
}
|
|
@@ -32,23 +28,16 @@ export function Injectable(): <T extends ClassType>(
|
|
|
32
28
|
context?: ClassDecoratorContext,
|
|
33
29
|
) => T
|
|
34
30
|
export function Injectable(options: {
|
|
31
|
+
scope?: InjectableScope
|
|
35
32
|
registry: Registry
|
|
36
33
|
}): <T extends ClassType>(target: T, context?: ClassDecoratorContext) => T
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
scope?: InjectableScope
|
|
41
|
-
type: InjectableType.Factory
|
|
42
|
-
registry?: Registry
|
|
43
|
-
}): <T extends ClassTypeWithInstance<Factory<R>>>(
|
|
44
|
-
target: T,
|
|
45
|
-
context?: ClassDecoratorContext,
|
|
46
|
-
) => T
|
|
34
|
+
export function Injectable(options: {
|
|
35
|
+
scope: InjectableScope
|
|
36
|
+
}): <T extends ClassType>(target: T, context?: ClassDecoratorContext) => T
|
|
47
37
|
|
|
48
38
|
// #3 Class with typeless token and schema
|
|
49
39
|
export function Injectable<Type, Schema>(options: {
|
|
50
40
|
scope?: InjectableScope
|
|
51
|
-
type?: InjectableType.Class
|
|
52
41
|
token: InjectionToken<Type, Schema>
|
|
53
42
|
registry?: Registry
|
|
54
43
|
}): Schema extends BaseInjectionTokenSchemaType // #3.1 Check that schema is an object or a record
|
|
@@ -84,28 +73,8 @@ export function Injectable<Type, Schema>(options: {
|
|
|
84
73
|
) => R
|
|
85
74
|
: never // #3.4 Cannot use a token without a type and schema
|
|
86
75
|
|
|
87
|
-
// #4 Factory with typed token
|
|
88
|
-
export function Injectable<R, S>(options: {
|
|
89
|
-
scope?: InjectableScope
|
|
90
|
-
type: InjectableType.Factory
|
|
91
|
-
token: InjectionToken<R, S>
|
|
92
|
-
registry?: Registry
|
|
93
|
-
}): R extends undefined // #4.1 Check that token has a type
|
|
94
|
-
? never // #4.1.1 Token must have a type
|
|
95
|
-
: S extends InjectionTokenSchemaType // #4.2 Check that schema is an object or a record
|
|
96
|
-
? <T extends ClassTypeWithInstance<FactoryWithArgs<R, S>>>( // #4.2.1 Token have a schema
|
|
97
|
-
target: T,
|
|
98
|
-
context?: ClassDecoratorContext,
|
|
99
|
-
) => T
|
|
100
|
-
: S extends undefined // #4.3 For a factory without schema
|
|
101
|
-
? <T extends ClassTypeWithInstance<Factory<R>>>( // #4.3.1 Token without a schema
|
|
102
|
-
target: T,
|
|
103
|
-
context?: ClassDecoratorContext,
|
|
104
|
-
) => T
|
|
105
|
-
: never // #4.4 Cannot use a token without a type and schema
|
|
106
76
|
export function Injectable({
|
|
107
77
|
scope = InjectableScope.Singleton,
|
|
108
|
-
type = InjectableType.Class,
|
|
109
78
|
token,
|
|
110
79
|
registry = globalRegistry,
|
|
111
80
|
}: InjectableOptions = {}) {
|
|
@@ -123,27 +92,8 @@ export function Injectable({
|
|
|
123
92
|
}
|
|
124
93
|
let injectableToken: InjectionToken<any, any> =
|
|
125
94
|
token ?? InjectionToken.create(target)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
injectableToken,
|
|
129
|
-
async (ctx, args: any) => resolveService(ctx, target, [args]),
|
|
130
|
-
scope,
|
|
131
|
-
)
|
|
132
|
-
} else if (type === InjectableType.Factory) {
|
|
133
|
-
registry.set(
|
|
134
|
-
injectableToken,
|
|
135
|
-
async (ctx, args: any) => {
|
|
136
|
-
const builder = await resolveService(ctx, target)
|
|
137
|
-
if (typeof builder.create !== 'function') {
|
|
138
|
-
throw new Error(
|
|
139
|
-
`[ServiceLocator] Factory ${target.name} does not implement the create method.`,
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
return builder.create(ctx, args)
|
|
143
|
-
},
|
|
144
|
-
scope,
|
|
145
|
-
)
|
|
146
|
-
}
|
|
95
|
+
|
|
96
|
+
registry.set(injectableToken, scope, target, InjectableType.Class)
|
|
147
97
|
|
|
148
98
|
// @ts-expect-error
|
|
149
99
|
target[InjectableTokenMeta] = injectableToken
|
|
@@ -6,5 +6,9 @@ export enum InjectableScope {
|
|
|
6
6
|
/**
|
|
7
7
|
* Instance scope: A new instance is created for each injection.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
Transient = 'Transient',
|
|
10
|
+
/**
|
|
11
|
+
* Request scope: The instance is created once per request and shared within that request context.
|
|
12
|
+
*/
|
|
13
|
+
Request = 'Request',
|
|
10
14
|
}
|
package/src/event-emitter.mts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
|
4
|
+
|
|
5
|
+
import { Injectable, InjectableScope } from './index.mjs'
|
|
6
|
+
|
|
4
7
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
8
|
export type EventsConfig = {
|
|
6
9
|
[event: string]: any[]
|
|
7
10
|
}
|
|
8
|
-
export type EventsNames<Events extends EventsConfig> = Exclude<
|
|
11
|
+
export type EventsNames<Events extends EventsConfig> = Exclude<
|
|
12
|
+
keyof Events,
|
|
13
|
+
symbol | number
|
|
14
|
+
>
|
|
9
15
|
export type EventsArgs<
|
|
10
16
|
Events extends EventsConfig,
|
|
11
17
|
Name extends EventsNames<Events>,
|
|
@@ -16,7 +22,11 @@ export type ChannelEmitter<
|
|
|
16
22
|
Ns extends string,
|
|
17
23
|
E extends EventsNames<Events>,
|
|
18
24
|
> = {
|
|
19
|
-
emit<Args extends EventsArgs<Events, E>>(
|
|
25
|
+
emit<Args extends EventsArgs<Events, E>>(
|
|
26
|
+
ns: Ns,
|
|
27
|
+
event: E,
|
|
28
|
+
...args: Args
|
|
29
|
+
): Promise<any>
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
export interface EventEmitterInterface<Events extends EventsConfig> {
|
|
@@ -28,17 +38,9 @@ export interface EventEmitterInterface<Events extends EventsConfig> {
|
|
|
28
38
|
event: E,
|
|
29
39
|
...args: Args
|
|
30
40
|
): void
|
|
31
|
-
addChannel<
|
|
32
|
-
E extends EventsNames<Events>,
|
|
33
|
-
Ns extends string,
|
|
34
|
-
Emmiter extends ChannelEmitter<Events, Ns, E>,
|
|
35
|
-
>(
|
|
36
|
-
ns: Ns,
|
|
37
|
-
event: E,
|
|
38
|
-
target: Emmiter,
|
|
39
|
-
): () => void
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
@Injectable({ scope: InjectableScope.Transient })
|
|
42
44
|
export class EventEmitter<Events extends EventsConfig = {}>
|
|
43
45
|
implements EventEmitterInterface<Events>
|
|
44
46
|
{
|
|
@@ -94,14 +96,10 @@ export class EventEmitter<Events extends EventsConfig = {}>
|
|
|
94
96
|
return
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
return Promise.all(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Ns extends string,
|
|
103
|
-
Emitter extends ChannelEmitter<Events, Ns, E>,
|
|
104
|
-
>(ns: Ns, event: E, target: Emitter) {
|
|
105
|
-
return this.on(event, (...args) => target.emit(ns, event, ...args))
|
|
99
|
+
return Promise.all(
|
|
100
|
+
Array.from(this.listeners.get(event)!).map((listener) =>
|
|
101
|
+
listener(...args),
|
|
102
|
+
),
|
|
103
|
+
)
|
|
106
104
|
}
|
|
107
105
|
}
|
package/src/factory-context.mts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
|
-
import type { ServiceLocatorEventBus } from './service-locator-event-bus.mjs'
|
|
3
1
|
import type { ServiceLocator } from './service-locator.mjs'
|
|
4
2
|
import type { Injectors } from './utils/index.mjs'
|
|
5
3
|
|
|
6
4
|
export interface FactoryContext {
|
|
7
|
-
inject: Injectors['
|
|
8
|
-
on: ServiceLocatorEventBus['on']
|
|
9
|
-
getDependencies: () => string[]
|
|
10
|
-
invalidate: () => void
|
|
11
|
-
addEffect: (listener: () => void) => void
|
|
12
|
-
getDestroyListeners: () => (() => void)[]
|
|
13
|
-
setTtl: (ttl: number) => void
|
|
14
|
-
getTtl: () => number
|
|
5
|
+
inject: Injectors['asyncInject']
|
|
15
6
|
locator: ServiceLocator
|
|
7
|
+
addDestroyListener: (listener: () => void) => void
|
|
16
8
|
}
|
package/src/index.mts
CHANGED
|
@@ -7,11 +7,12 @@ export * from './event-emitter.mjs'
|
|
|
7
7
|
export * from './factory-context.mjs'
|
|
8
8
|
export * from './injection-token.mjs'
|
|
9
9
|
export * from './injector.mjs'
|
|
10
|
-
export * from './proxy-service-locator.mjs'
|
|
11
10
|
export * from './registry.mjs'
|
|
12
|
-
export * from './
|
|
11
|
+
export * from './service-instantiator.mjs'
|
|
13
12
|
export * from './service-locator.mjs'
|
|
14
13
|
export * from './service-locator-event-bus.mjs'
|
|
15
14
|
export * from './service-locator-instance-holder.mjs'
|
|
16
15
|
export * from './service-locator-manager.mjs'
|
|
16
|
+
export * from './request-context-holder.mjs'
|
|
17
17
|
export * from './symbols/injectable-token.mjs'
|
|
18
|
+
export * from './container.mjs'
|
package/src/injection-token.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { z, ZodObject, ZodOptional, ZodRecord } from 'zod/v4'
|
|
2
2
|
|
|
3
|
+
import type { FactoryContext } from './factory-context.mjs'
|
|
4
|
+
|
|
3
5
|
export type ClassType = new (...args: any[]) => any
|
|
4
6
|
export type ClassTypeWithArgument<Arg> = new (arg: Arg) => any
|
|
5
7
|
export type ClassTypeWithOptionalArgument<Arg> = new (arg?: Arg) => any
|
|
@@ -10,10 +12,10 @@ export type ClassTypeWithInstanceAndOptionalArgument<T, Arg> = new (
|
|
|
10
12
|
arg?: Arg,
|
|
11
13
|
) => T
|
|
12
14
|
|
|
13
|
-
export type BaseInjectionTokenSchemaType = ZodObject
|
|
15
|
+
export type BaseInjectionTokenSchemaType = ZodObject | ZodRecord
|
|
14
16
|
|
|
15
17
|
export type OptionalInjectionTokenSchemaType =
|
|
16
|
-
| ZodOptional<ZodObject
|
|
18
|
+
| ZodOptional<ZodObject>
|
|
17
19
|
| ZodOptional<ZodRecord>
|
|
18
20
|
|
|
19
21
|
export type InjectionTokenSchemaType =
|
|
@@ -21,13 +23,15 @@ export type InjectionTokenSchemaType =
|
|
|
21
23
|
| OptionalInjectionTokenSchemaType
|
|
22
24
|
|
|
23
25
|
export class InjectionToken<
|
|
26
|
+
// oxlint-disable-next-line no-unused-vars
|
|
24
27
|
T,
|
|
25
28
|
S extends InjectionTokenSchemaType | unknown = unknown,
|
|
26
|
-
|
|
29
|
+
// oxlint-disable-next-line no-unused-vars
|
|
30
|
+
Required extends boolean = S extends ZodOptional<ZodObject>
|
|
27
31
|
? false
|
|
28
32
|
: S extends ZodOptional<ZodRecord>
|
|
29
33
|
? false
|
|
30
|
-
: S extends ZodObject
|
|
34
|
+
: S extends ZodObject
|
|
31
35
|
? true
|
|
32
36
|
: S extends ZodRecord
|
|
33
37
|
? true
|
|
@@ -38,7 +42,7 @@ export class InjectionToken<
|
|
|
38
42
|
|
|
39
43
|
constructor(
|
|
40
44
|
public readonly name: string | symbol | ClassType,
|
|
41
|
-
public readonly schema: ZodObject
|
|
45
|
+
public readonly schema: ZodObject | undefined,
|
|
42
46
|
) {}
|
|
43
47
|
|
|
44
48
|
static create<T extends ClassType>(
|
|
@@ -69,7 +73,7 @@ export class InjectionToken<
|
|
|
69
73
|
|
|
70
74
|
static factory<T, S extends InjectionTokenSchemaType>(
|
|
71
75
|
token: InjectionToken<T, S>,
|
|
72
|
-
factory: () => Promise<z.input<S>>,
|
|
76
|
+
factory: (ctx: FactoryContext) => Promise<z.input<S>>,
|
|
73
77
|
): FactoryInjectionToken<T, S> {
|
|
74
78
|
return new FactoryInjectionToken(token, factory)
|
|
75
79
|
}
|
|
@@ -126,16 +130,16 @@ export class FactoryInjectionToken<T, S extends InjectionTokenSchemaType> {
|
|
|
126
130
|
|
|
127
131
|
constructor(
|
|
128
132
|
public readonly token: InjectionToken<T, S>,
|
|
129
|
-
public readonly factory: () => Promise<z.input<S>>,
|
|
133
|
+
public readonly factory: (ctx: FactoryContext) => Promise<z.input<S>>,
|
|
130
134
|
) {
|
|
131
135
|
this.name = token.name
|
|
132
136
|
this.id = token.id
|
|
133
137
|
this.schema = token.schema as InjectionTokenSchemaType
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
async resolve(): Promise<z.input<S>> {
|
|
140
|
+
async resolve(ctx: FactoryContext): Promise<z.input<S>> {
|
|
137
141
|
if (!this.value) {
|
|
138
|
-
this.value = await this.factory()
|
|
142
|
+
this.value = await this.factory(ctx)
|
|
139
143
|
this.resolved = true
|
|
140
144
|
}
|
|
141
145
|
return this.value
|
|
@@ -145,3 +149,14 @@ export class FactoryInjectionToken<T, S extends InjectionTokenSchemaType> {
|
|
|
145
149
|
return this.token.toString()
|
|
146
150
|
}
|
|
147
151
|
}
|
|
152
|
+
|
|
153
|
+
export type AnyInjectableType =
|
|
154
|
+
| ClassType
|
|
155
|
+
| InjectionToken<any, any>
|
|
156
|
+
| BoundInjectionToken<any, any>
|
|
157
|
+
| FactoryInjectionToken<any, any>
|
|
158
|
+
|
|
159
|
+
export type InjectionTokenType =
|
|
160
|
+
| InjectionToken<any, any>
|
|
161
|
+
| BoundInjectionToken<any, any>
|
|
162
|
+
| FactoryInjectionToken<any, any>
|
package/src/injector.mts
CHANGED
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
import type { Injectors } from './utils/index.mjs'
|
|
2
2
|
|
|
3
|
-
import { globalRegistry } from './registry.mjs'
|
|
4
|
-
import { ServiceLocator } from './service-locator.mjs'
|
|
5
3
|
import { getInjectors } from './utils/index.mjs'
|
|
6
4
|
|
|
7
|
-
const
|
|
5
|
+
export const defaultInjectors = getInjectors()
|
|
8
6
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
throw new Error(
|
|
12
|
-
'[ServiceLocator] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator.',
|
|
13
|
-
)
|
|
14
|
-
}
|
|
15
|
-
return globalServiceLocator
|
|
16
|
-
}
|
|
17
|
-
const values = getInjectors({
|
|
18
|
-
baseLocator: globalServiceLocator,
|
|
19
|
-
})
|
|
7
|
+
export const asyncInject: Injectors['asyncInject'] =
|
|
8
|
+
defaultInjectors.asyncInject
|
|
20
9
|
|
|
21
|
-
export const inject: Injectors['inject'] =
|
|
10
|
+
export const inject: Injectors['inject'] = defaultInjectors.inject
|
|
22
11
|
|
|
23
|
-
export const
|
|
12
|
+
export const wrapSyncInit: Injectors['wrapSyncInit'] =
|
|
13
|
+
defaultInjectors.wrapSyncInit
|
|
24
14
|
|
|
25
|
-
export const
|
|
26
|
-
|
|
27
|
-
export const provideServiceLocator: Injectors['provideServiceLocator'] =
|
|
28
|
-
values.provideServiceLocator
|
|
15
|
+
export const provideFactoryContext: Injectors['provideFactoryContext'] =
|
|
16
|
+
defaultInjectors.provideFactoryContext
|
|
@@ -3,10 +3,10 @@ import { z } from 'zod/v4'
|
|
|
3
3
|
import type { FactoryContext } from '../factory-context.mjs'
|
|
4
4
|
import type { InjectionTokenSchemaType } from '../injection-token.mjs'
|
|
5
5
|
|
|
6
|
-
export interface
|
|
6
|
+
export interface Factorable<T> {
|
|
7
7
|
create(ctx?: FactoryContext): Promise<T> | T
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface
|
|
11
|
-
create(...args: [
|
|
10
|
+
export interface FactorableWithArgs<T, A extends InjectionTokenSchemaType> {
|
|
11
|
+
create(ctx?: FactoryContext, ...args: [z.output<A>]): Promise<T> | T
|
|
12
12
|
}
|
package/src/interfaces/index.mts
CHANGED
package/src/registry.mts
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ClassType, InjectionToken } from './injection-token.mjs'
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import type { InjectionToken } from './injection-token.mjs'
|
|
5
|
-
|
|
6
|
-
import { InjectableScope } from './enums/index.mjs'
|
|
7
|
-
|
|
8
|
-
export type InjectionFactory<T = unknown, Args = unknown> = (
|
|
9
|
-
ctx: FactoryContext,
|
|
10
|
-
args: Args,
|
|
11
|
-
) => Promise<T>
|
|
3
|
+
import { InjectableScope, InjectableType } from './enums/index.mjs'
|
|
12
4
|
|
|
13
5
|
export type FactoryRecord<Instance = any, Schema = any> = {
|
|
14
6
|
scope: InjectableScope
|
|
15
7
|
originalToken: InjectionToken<Instance, Schema>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Schema extends ZodObject<any> ? z.input<Schema> : unknown
|
|
19
|
-
>
|
|
8
|
+
target: ClassType
|
|
9
|
+
type: InjectableType
|
|
20
10
|
}
|
|
21
11
|
|
|
22
12
|
export class Registry {
|
|
@@ -49,10 +39,11 @@ export class Registry {
|
|
|
49
39
|
|
|
50
40
|
set<Instance, Schema>(
|
|
51
41
|
token: InjectionToken<Instance, Schema>,
|
|
52
|
-
factory: InjectionFactory,
|
|
53
42
|
scope: InjectableScope,
|
|
43
|
+
target: ClassType,
|
|
44
|
+
type: InjectableType,
|
|
54
45
|
) {
|
|
55
|
-
this.factories.set(token.id, {
|
|
46
|
+
this.factories.set(token.id, { scope, originalToken: token, target, type })
|
|
56
47
|
}
|
|
57
48
|
|
|
58
49
|
delete(token: InjectionToken<any, any>) {
|