@navios/di 0.2.1 → 0.3.1
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 +494 -145
- package/lib/_tsup-dts-rollup.d.ts +494 -145
- package/lib/index.d.mts +26 -12
- package/lib/index.d.ts +26 -12
- package/lib/index.js +1021 -470
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1011 -461
- 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 +427 -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 +174 -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
|
@@ -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
|
|
@@ -21,8 +23,10 @@ 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,
|
|
29
|
+
// oxlint-disable-next-line no-unused-vars
|
|
26
30
|
Required extends boolean = S extends ZodOptional<ZodObject>
|
|
27
31
|
? false
|
|
28
32
|
: S extends ZodOptional<ZodRecord>
|
|
@@ -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 ? 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>) {
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { ServiceLocatorInstanceHolder } from './service-locator-instance-holder.mjs'
|
|
2
|
+
|
|
3
|
+
import { InjectableScope, InjectableType } from './enums/index.mjs'
|
|
4
|
+
import { InjectionToken } from './injection-token.mjs'
|
|
5
|
+
import { ServiceLocatorInstanceHolderStatus } from './service-locator-instance-holder.mjs'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Request context holder that manages pre-prepared instances for a specific request.
|
|
9
|
+
* This allows for efficient instantiation of request-scoped services.
|
|
10
|
+
*/
|
|
11
|
+
export interface RequestContextHolder {
|
|
12
|
+
/**
|
|
13
|
+
* Unique identifier for this request context.
|
|
14
|
+
*/
|
|
15
|
+
readonly requestId: string
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Pre-prepared instances for this request, keyed by instance name.
|
|
19
|
+
*/
|
|
20
|
+
readonly instances: Map<string, any>
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Instance holders for request-scoped services.
|
|
24
|
+
*/
|
|
25
|
+
readonly holders: Map<string, ServiceLocatorInstanceHolder>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Priority for resolution in FactoryContext.inject method.
|
|
29
|
+
* Higher values take precedence.
|
|
30
|
+
*/
|
|
31
|
+
readonly priority: number
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Request-specific metadata that can be used during instantiation.
|
|
35
|
+
*/
|
|
36
|
+
readonly metadata: Map<string, any>
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Timestamp when this context was created.
|
|
40
|
+
*/
|
|
41
|
+
readonly createdAt: number
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Adds a pre-prepared instance to this context.
|
|
45
|
+
*/
|
|
46
|
+
addInstance(
|
|
47
|
+
instanceName: string,
|
|
48
|
+
instance: any,
|
|
49
|
+
holder: ServiceLocatorInstanceHolder,
|
|
50
|
+
): void
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Adds a pre-prepared instance to this context.
|
|
54
|
+
*/
|
|
55
|
+
addInstance(token: InjectionToken<any, undefined>, instance: any): void
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets a pre-prepared instance from this context.
|
|
59
|
+
*/
|
|
60
|
+
getInstance(instanceName: string): any | undefined
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gets an instance holder from this context.
|
|
64
|
+
*/
|
|
65
|
+
getHolder(instanceName: string): ServiceLocatorInstanceHolder | undefined
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Checks if this context has a pre-prepared instance.
|
|
69
|
+
*/
|
|
70
|
+
hasInstance(instanceName: string): boolean
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clears all instances and holders from this context.
|
|
74
|
+
*/
|
|
75
|
+
clear(): void
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gets metadata value by key.
|
|
79
|
+
*/
|
|
80
|
+
getMetadata(key: string): any | undefined
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Sets metadata value by key.
|
|
84
|
+
*/
|
|
85
|
+
setMetadata(key: string, value: any): void
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Default implementation of RequestContextHolder.
|
|
90
|
+
*/
|
|
91
|
+
export class DefaultRequestContextHolder implements RequestContextHolder {
|
|
92
|
+
public readonly instances = new Map<string, any>()
|
|
93
|
+
public readonly holders = new Map<string, ServiceLocatorInstanceHolder>()
|
|
94
|
+
public readonly metadata = new Map<string, any>()
|
|
95
|
+
public readonly createdAt = Date.now()
|
|
96
|
+
|
|
97
|
+
constructor(
|
|
98
|
+
public readonly requestId: string,
|
|
99
|
+
public readonly priority: number = 100,
|
|
100
|
+
initialMetadata?: Record<string, any>,
|
|
101
|
+
) {
|
|
102
|
+
if (initialMetadata) {
|
|
103
|
+
Object.entries(initialMetadata).forEach(([key, value]) => {
|
|
104
|
+
this.metadata.set(key, value)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
addInstance(
|
|
110
|
+
instanceName: string | InjectionToken<any, undefined>,
|
|
111
|
+
instance: any,
|
|
112
|
+
holder?: ServiceLocatorInstanceHolder,
|
|
113
|
+
): void {
|
|
114
|
+
if (instanceName instanceof InjectionToken) {
|
|
115
|
+
this.instances.set(instanceName.toString(), instance)
|
|
116
|
+
this.holders.set(instanceName.toString(), {
|
|
117
|
+
instance,
|
|
118
|
+
status: ServiceLocatorInstanceHolderStatus.Created,
|
|
119
|
+
creationPromise: null,
|
|
120
|
+
destroyPromise: null,
|
|
121
|
+
destroyListeners: [],
|
|
122
|
+
deps: new Set(),
|
|
123
|
+
name: instanceName.toString(),
|
|
124
|
+
type: InjectableType.Class,
|
|
125
|
+
scope: InjectableScope.Singleton,
|
|
126
|
+
createdAt: Date.now(),
|
|
127
|
+
ttl: Infinity,
|
|
128
|
+
})
|
|
129
|
+
} else {
|
|
130
|
+
if (!holder) {
|
|
131
|
+
throw new Error('Holder is required when adding an instance by name')
|
|
132
|
+
}
|
|
133
|
+
this.instances.set(instanceName, instance)
|
|
134
|
+
this.holders.set(instanceName, holder)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getInstance(instanceName: string): any | undefined {
|
|
139
|
+
return this.instances.get(instanceName)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getHolder(instanceName: string): ServiceLocatorInstanceHolder | undefined {
|
|
143
|
+
return this.holders.get(instanceName)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
hasInstance(instanceName: string): boolean {
|
|
147
|
+
return this.instances.has(instanceName)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
clear(): void {
|
|
151
|
+
this.instances.clear()
|
|
152
|
+
this.holders.clear()
|
|
153
|
+
this.metadata.clear()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
getMetadata(key: string): any | undefined {
|
|
157
|
+
return this.metadata.get(key)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
setMetadata(key: string, value: any): void {
|
|
161
|
+
this.metadata.set(key, value)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Creates a new request context holder with the given parameters.
|
|
167
|
+
*/
|
|
168
|
+
export function createRequestContextHolder(
|
|
169
|
+
requestId: string,
|
|
170
|
+
priority: number = 100,
|
|
171
|
+
initialMetadata?: Record<string, any>,
|
|
172
|
+
): RequestContextHolder {
|
|
173
|
+
return new DefaultRequestContextHolder(requestId, priority, initialMetadata)
|
|
174
|
+
}
|