@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.
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 +490 -145
  17. package/lib/_tsup-dts-rollup.d.ts +490 -145
  18. package/lib/index.d.mts +26 -12
  19. package/lib/index.d.ts +26 -12
  20. package/lib/index.js +993 -462
  21. package/lib/index.js.map +1 -1
  22. package/lib/index.mjs +983 -453
  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 +263 -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 +145 -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
@@ -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
+ }
@@ -1 +1,2 @@
1
+ export * from './factory.decorator.mjs'
1
2
  export * from './injectable.decorator.mjs'
@@ -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>) {