@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.
Files changed (62) hide show
  1. package/README.md +299 -38
  2. package/docs/README.md +121 -48
  3. package/docs/api-reference.md +763 -0
  4. package/docs/container.md +274 -0
  5. package/docs/examples/basic-usage.mts +97 -0
  6. package/docs/examples/factory-pattern.mts +318 -0
  7. package/docs/examples/injection-tokens.mts +225 -0
  8. package/docs/examples/request-scope-example.mts +254 -0
  9. package/docs/examples/service-lifecycle.mts +359 -0
  10. package/docs/factory.md +584 -0
  11. package/docs/getting-started.md +308 -0
  12. package/docs/injectable.md +496 -0
  13. package/docs/injection-tokens.md +400 -0
  14. package/docs/lifecycle.md +539 -0
  15. package/docs/scopes.md +749 -0
  16. package/lib/_tsup-dts-rollup.d.mts +494 -145
  17. package/lib/_tsup-dts-rollup.d.ts +494 -145
  18. package/lib/index.d.mts +26 -12
  19. package/lib/index.d.ts +26 -12
  20. package/lib/index.js +1021 -470
  21. package/lib/index.js.map +1 -1
  22. package/lib/index.mjs +1011 -461
  23. package/lib/index.mjs.map +1 -1
  24. package/package.json +2 -2
  25. package/project.json +10 -2
  26. package/src/__tests__/container.spec.mts +1301 -0
  27. package/src/__tests__/factory.spec.mts +137 -0
  28. package/src/__tests__/injectable.spec.mts +32 -88
  29. package/src/__tests__/injection-token.spec.mts +333 -17
  30. package/src/__tests__/request-scope.spec.mts +427 -0
  31. package/src/__type-tests__/factory.spec-d.mts +65 -0
  32. package/src/__type-tests__/inject.spec-d.mts +27 -28
  33. package/src/__type-tests__/injectable.spec-d.mts +42 -206
  34. package/src/container.mts +167 -0
  35. package/src/decorators/factory.decorator.mts +79 -0
  36. package/src/decorators/index.mts +1 -0
  37. package/src/decorators/injectable.decorator.mts +6 -56
  38. package/src/enums/injectable-scope.enum.mts +5 -1
  39. package/src/event-emitter.mts +18 -20
  40. package/src/factory-context.mts +2 -10
  41. package/src/index.mts +3 -2
  42. package/src/injection-token.mts +19 -4
  43. package/src/injector.mts +8 -20
  44. package/src/interfaces/factory.interface.mts +3 -3
  45. package/src/interfaces/index.mts +2 -0
  46. package/src/interfaces/on-service-destroy.interface.mts +3 -0
  47. package/src/interfaces/on-service-init.interface.mts +3 -0
  48. package/src/registry.mts +7 -16
  49. package/src/request-context-holder.mts +174 -0
  50. package/src/service-instantiator.mts +158 -0
  51. package/src/service-locator-event-bus.mts +0 -28
  52. package/src/service-locator-instance-holder.mts +27 -16
  53. package/src/service-locator-manager.mts +84 -0
  54. package/src/service-locator.mts +548 -393
  55. package/src/utils/defer.mts +73 -0
  56. package/src/utils/get-injectors.mts +91 -78
  57. package/src/utils/index.mts +2 -0
  58. package/src/utils/types.mts +52 -0
  59. package/docs/concepts/injectable.md +0 -182
  60. package/docs/concepts/injection-token.md +0 -145
  61. package/src/proxy-service-locator.mts +0 -83
  62. 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
- // #2 Factory without arguments
39
- export function Injectable<R>(options: {
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
- if (type === InjectableType.Class) {
127
- registry.set(
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
- Instance = 'Instance',
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
  }
@@ -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<keyof Events, symbol | number>
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>>(ns: Ns, event: E, ...args: Args): Promise<any>
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(Array.from(this.listeners.get(event)!).map(listener => listener(...args)))
98
- }
99
-
100
- addChannel<
101
- E extends EventsNames<Events>,
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
  }
@@ -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['inject']
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 './resolve-service.mjs'
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'
@@ -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 globalServiceLocator: ServiceLocator = new ServiceLocator(globalRegistry)
5
+ export const defaultInjectors = getInjectors()
8
6
 
9
- export function getGlobalServiceLocator(): ServiceLocator {
10
- if (!globalServiceLocator) {
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'] = values.inject
10
+ export const inject: Injectors['inject'] = defaultInjectors.inject
22
11
 
23
- export const syncInject: Injectors['syncInject'] = values.syncInject
12
+ export const wrapSyncInit: Injectors['wrapSyncInit'] =
13
+ defaultInjectors.wrapSyncInit
24
14
 
25
- export const wrapSyncInit: Injectors['wrapSyncInit'] = values.wrapSyncInit
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 Factory<T> {
6
+ export interface Factorable<T> {
7
7
  create(ctx?: FactoryContext): Promise<T> | T
8
8
  }
9
9
 
10
- export interface FactoryWithArgs<T, A extends InjectionTokenSchemaType> {
11
- create(...args: [FactoryContext, z.output<A>]): Promise<T> | T
10
+ export interface FactorableWithArgs<T, A extends InjectionTokenSchemaType> {
11
+ create(ctx?: FactoryContext, ...args: [z.output<A>]): Promise<T> | T
12
12
  }
@@ -1 +1,3 @@
1
1
  export * from './factory.interface.mjs'
2
+ export * from './on-service-init.interface.mjs'
3
+ export * from './on-service-destroy.interface.mjs'
@@ -0,0 +1,3 @@
1
+ export interface OnServiceDestroy {
2
+ onServiceDestroy(): Promise<void> | void
3
+ }
@@ -0,0 +1,3 @@
1
+ export interface OnServiceInit {
2
+ onServiceInit(): Promise<void> | void
3
+ }
package/src/registry.mts CHANGED
@@ -1,22 +1,12 @@
1
- import type { z, ZodObject } from 'zod/v4'
1
+ import type { ClassType, InjectionToken } from './injection-token.mjs'
2
2
 
3
- import type { FactoryContext } from './factory-context.mjs'
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
- factory: InjectionFactory<
17
- Instance,
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, { factory, scope, originalToken: token })
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
+ }