@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,145 @@
|
|
|
1
|
+
import type { ServiceLocatorInstanceHolder } from './service-locator-instance-holder.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Request context holder that manages pre-prepared instances for a specific request.
|
|
5
|
+
* This allows for efficient instantiation of request-scoped services.
|
|
6
|
+
*/
|
|
7
|
+
export interface RequestContextHolder {
|
|
8
|
+
/**
|
|
9
|
+
* Unique identifier for this request context.
|
|
10
|
+
*/
|
|
11
|
+
readonly requestId: string
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Pre-prepared instances for this request, keyed by instance name.
|
|
15
|
+
*/
|
|
16
|
+
readonly instances: Map<string, any>
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Instance holders for request-scoped services.
|
|
20
|
+
*/
|
|
21
|
+
readonly holders: Map<string, ServiceLocatorInstanceHolder>
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Priority for resolution in FactoryContext.inject method.
|
|
25
|
+
* Higher values take precedence.
|
|
26
|
+
*/
|
|
27
|
+
readonly priority: number
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Request-specific metadata that can be used during instantiation.
|
|
31
|
+
*/
|
|
32
|
+
readonly metadata: Map<string, any>
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Timestamp when this context was created.
|
|
36
|
+
*/
|
|
37
|
+
readonly createdAt: number
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Adds a pre-prepared instance to this context.
|
|
41
|
+
*/
|
|
42
|
+
addInstance(
|
|
43
|
+
instanceName: string,
|
|
44
|
+
instance: any,
|
|
45
|
+
holder: ServiceLocatorInstanceHolder,
|
|
46
|
+
): void
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets a pre-prepared instance from this context.
|
|
50
|
+
*/
|
|
51
|
+
getInstance(instanceName: string): any | undefined
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Gets an instance holder from this context.
|
|
55
|
+
*/
|
|
56
|
+
getHolder(instanceName: string): ServiceLocatorInstanceHolder | undefined
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checks if this context has a pre-prepared instance.
|
|
60
|
+
*/
|
|
61
|
+
hasInstance(instanceName: string): boolean
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Clears all instances and holders from this context.
|
|
65
|
+
*/
|
|
66
|
+
clear(): void
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets metadata value by key.
|
|
70
|
+
*/
|
|
71
|
+
getMetadata(key: string): any | undefined
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Sets metadata value by key.
|
|
75
|
+
*/
|
|
76
|
+
setMetadata(key: string, value: any): void
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Default implementation of RequestContextHolder.
|
|
81
|
+
*/
|
|
82
|
+
export class DefaultRequestContextHolder implements RequestContextHolder {
|
|
83
|
+
public readonly instances = new Map<string, any>()
|
|
84
|
+
public readonly holders = new Map<string, ServiceLocatorInstanceHolder>()
|
|
85
|
+
public readonly metadata = new Map<string, any>()
|
|
86
|
+
public readonly createdAt = Date.now()
|
|
87
|
+
|
|
88
|
+
constructor(
|
|
89
|
+
public readonly requestId: string,
|
|
90
|
+
public readonly priority: number = 100,
|
|
91
|
+
initialMetadata?: Record<string, any>,
|
|
92
|
+
) {
|
|
93
|
+
if (initialMetadata) {
|
|
94
|
+
Object.entries(initialMetadata).forEach(([key, value]) => {
|
|
95
|
+
this.metadata.set(key, value)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
addInstance(
|
|
101
|
+
instanceName: string,
|
|
102
|
+
instance: any,
|
|
103
|
+
holder: ServiceLocatorInstanceHolder,
|
|
104
|
+
): void {
|
|
105
|
+
this.instances.set(instanceName, instance)
|
|
106
|
+
this.holders.set(instanceName, holder)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getInstance(instanceName: string): any | undefined {
|
|
110
|
+
return this.instances.get(instanceName)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getHolder(instanceName: string): ServiceLocatorInstanceHolder | undefined {
|
|
114
|
+
return this.holders.get(instanceName)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
hasInstance(instanceName: string): boolean {
|
|
118
|
+
return this.instances.has(instanceName)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
clear(): void {
|
|
122
|
+
this.instances.clear()
|
|
123
|
+
this.holders.clear()
|
|
124
|
+
this.metadata.clear()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getMetadata(key: string): any | undefined {
|
|
128
|
+
return this.metadata.get(key)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
setMetadata(key: string, value: any): void {
|
|
132
|
+
this.metadata.set(key, value)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Creates a new request context holder with the given parameters.
|
|
138
|
+
*/
|
|
139
|
+
export function createRequestContextHolder(
|
|
140
|
+
requestId: string,
|
|
141
|
+
priority: number = 100,
|
|
142
|
+
initialMetadata?: Record<string, any>,
|
|
143
|
+
): RequestContextHolder {
|
|
144
|
+
return new DefaultRequestContextHolder(requestId, priority, initialMetadata)
|
|
145
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { FactoryContext } from './factory-context.mjs'
|
|
2
|
+
import type { FactoryRecord } from './registry.mjs'
|
|
3
|
+
import type { Injectors } from './utils/get-injectors.mjs'
|
|
4
|
+
|
|
5
|
+
import { InjectableType } from './enums/index.mjs'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ServiceInstantiator handles the instantiation of services based on registry records.
|
|
9
|
+
* It replaces the hard-coded logic in Injectable and Factory decorators.
|
|
10
|
+
*/
|
|
11
|
+
export class ServiceInstantiator {
|
|
12
|
+
constructor(private readonly injectors: Injectors) {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Instantiates a service based on its registry record.
|
|
16
|
+
* @param ctx The factory context for dependency injection
|
|
17
|
+
* @param record The factory record from the registry
|
|
18
|
+
* @param args Optional arguments for the service
|
|
19
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
20
|
+
*/
|
|
21
|
+
async instantiateService<T>(
|
|
22
|
+
ctx: FactoryContext,
|
|
23
|
+
record: FactoryRecord<T, any>,
|
|
24
|
+
args: any = undefined,
|
|
25
|
+
): Promise<[undefined, T] | [Error]> {
|
|
26
|
+
try {
|
|
27
|
+
switch (record.type) {
|
|
28
|
+
case InjectableType.Class:
|
|
29
|
+
return this.instantiateClass(ctx, record, args)
|
|
30
|
+
case InjectableType.Factory:
|
|
31
|
+
return this.instantiateFactory(ctx, record, args)
|
|
32
|
+
default:
|
|
33
|
+
throw new Error(
|
|
34
|
+
`[ServiceInstantiator] Unknown service type: ${record.type}`,
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return [error instanceof Error ? error : new Error(String(error))]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Instantiates a class-based service (Injectable decorator).
|
|
44
|
+
* @param ctx The factory context for dependency injection
|
|
45
|
+
* @param record The factory record from the registry
|
|
46
|
+
* @param args Optional arguments for the service constructor
|
|
47
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
48
|
+
*/
|
|
49
|
+
private async instantiateClass<T>(
|
|
50
|
+
ctx: FactoryContext,
|
|
51
|
+
record: FactoryRecord<T, any>,
|
|
52
|
+
args: any,
|
|
53
|
+
): Promise<[undefined, T] | [Error]> {
|
|
54
|
+
try {
|
|
55
|
+
const tryLoad = this.injectors.wrapSyncInit(() => {
|
|
56
|
+
const original = this.injectors.provideFactoryContext(ctx)
|
|
57
|
+
let result = new record.target(...(args ? [args] : []))
|
|
58
|
+
this.injectors.provideFactoryContext(original)
|
|
59
|
+
return result
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
let [instance, promises, injectState] = tryLoad()
|
|
63
|
+
if (promises.length > 0) {
|
|
64
|
+
const results = await Promise.allSettled(promises)
|
|
65
|
+
if (results.some((result) => result.status === 'rejected')) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`,
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
const newRes = tryLoad(injectState)
|
|
71
|
+
instance = newRes[0]
|
|
72
|
+
promises = newRes[1]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (promises.length > 0) {
|
|
76
|
+
console.error(`[ServiceInstantiator] ${record.target.name} has problem with it's definition.
|
|
77
|
+
|
|
78
|
+
One or more of the dependencies are registered as a InjectableScope.Instance and are used with inject.
|
|
79
|
+
|
|
80
|
+
Please use inject asyncInject of inject to load those dependencies.`)
|
|
81
|
+
throw new Error(
|
|
82
|
+
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Handle lifecycle hooks
|
|
87
|
+
if ('onServiceInit' in instance) {
|
|
88
|
+
await (instance as any).onServiceInit()
|
|
89
|
+
}
|
|
90
|
+
if ('onServiceDestroy' in instance) {
|
|
91
|
+
ctx.addDestroyListener(async () => {
|
|
92
|
+
await (instance as any).onServiceDestroy()
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return [undefined, instance]
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return [error instanceof Error ? error : new Error(String(error))]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Instantiates a factory-based service (Factory decorator).
|
|
104
|
+
* @param ctx The factory context for dependency injection
|
|
105
|
+
* @param record The factory record from the registry
|
|
106
|
+
* @param args Optional arguments for the factory
|
|
107
|
+
* @returns Promise resolving to [undefined, instance] or [error]
|
|
108
|
+
*/
|
|
109
|
+
private async instantiateFactory<T>(
|
|
110
|
+
ctx: FactoryContext,
|
|
111
|
+
record: FactoryRecord<T, any>,
|
|
112
|
+
args: any,
|
|
113
|
+
): Promise<[undefined, T] | [Error]> {
|
|
114
|
+
try {
|
|
115
|
+
const tryLoad = this.injectors.wrapSyncInit(() => {
|
|
116
|
+
const original = this.injectors.provideFactoryContext(ctx)
|
|
117
|
+
let result = new record.target()
|
|
118
|
+
this.injectors.provideFactoryContext(original)
|
|
119
|
+
return result
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
let [builder, promises, injectState] = tryLoad()
|
|
123
|
+
if (promises.length > 0) {
|
|
124
|
+
const results = await Promise.allSettled(promises)
|
|
125
|
+
if (results.some((result) => result.status === 'rejected')) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
const newRes = tryLoad(injectState)
|
|
131
|
+
builder = newRes[0]
|
|
132
|
+
promises = newRes[1]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (promises.length > 0) {
|
|
136
|
+
console.error(`[ServiceInstantiator] ${record.target.name} has problem with it's definition.
|
|
137
|
+
|
|
138
|
+
One or more of the dependencies are registered as a InjectableScope.Instance and are used with inject.
|
|
139
|
+
|
|
140
|
+
Please use asyncInject instead of inject to load those dependencies.`)
|
|
141
|
+
throw new Error(
|
|
142
|
+
`[ServiceInstantiator] Service ${record.target.name} cannot be instantiated.`,
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (typeof builder.create !== 'function') {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`[ServiceInstantiator] Factory ${record.target.name} does not implement the create method.`,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const instance = await builder.create(ctx, args)
|
|
153
|
+
return [undefined, instance]
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return [error instanceof Error ? error : new Error(String(error))]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -43,21 +43,6 @@ export class ServiceLocatorEventBus {
|
|
|
43
43
|
|
|
44
44
|
const events = this.listeners.get(key)!
|
|
45
45
|
|
|
46
|
-
const preEvent = `pre:${event}`
|
|
47
|
-
const postEvent = `post:${event}`
|
|
48
|
-
this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${preEvent}`)
|
|
49
|
-
await Promise.allSettled(
|
|
50
|
-
[...(events.get(preEvent) ?? [])].map((listener) => listener(preEvent)),
|
|
51
|
-
).then((results) => {
|
|
52
|
-
results
|
|
53
|
-
.filter((result) => result.status === 'rejected')
|
|
54
|
-
.forEach((result: PromiseRejectedResult) => {
|
|
55
|
-
this.logger?.warn(
|
|
56
|
-
`[ServiceLocatorEventBus]#emit(): ${key}:${preEvent} rejected with`,
|
|
57
|
-
result.reason,
|
|
58
|
-
)
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
46
|
this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${event}`)
|
|
62
47
|
|
|
63
48
|
const res = await Promise.allSettled(
|
|
@@ -78,19 +63,6 @@ export class ServiceLocatorEventBus {
|
|
|
78
63
|
}
|
|
79
64
|
return results
|
|
80
65
|
})
|
|
81
|
-
this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${postEvent}`)
|
|
82
|
-
await Promise.allSettled(
|
|
83
|
-
[...(events.get(postEvent) ?? [])].map((listener) => listener(postEvent)),
|
|
84
|
-
).then((results) => {
|
|
85
|
-
results
|
|
86
|
-
.filter((result) => result.status === 'rejected')
|
|
87
|
-
.forEach((result: PromiseRejectedResult) => {
|
|
88
|
-
this.logger?.warn(
|
|
89
|
-
`[ServiceLocatorEventBus]#emit(): ${key}:${postEvent} rejected with`,
|
|
90
|
-
result.reason,
|
|
91
|
-
)
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
66
|
return res
|
|
95
67
|
}
|
|
96
68
|
}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export enum ServiceLocatorInstanceHolderKind {
|
|
4
|
-
Instance = 'instance',
|
|
5
|
-
Factory = 'factory',
|
|
6
|
-
AbstractFactory = 'abstractFactory',
|
|
7
|
-
}
|
|
1
|
+
import type { InjectableScope, InjectableType } from './enums/index.mjs'
|
|
8
2
|
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
9
4
|
export enum ServiceLocatorInstanceHolderStatus {
|
|
10
5
|
Created = 'created',
|
|
11
6
|
Creating = 'creating',
|
|
12
7
|
Destroying = 'destroying',
|
|
8
|
+
Error = 'error',
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
export type ServiceLocatorInstanceEffect = () => void
|
|
@@ -21,9 +17,9 @@ export interface ServiceLocatorInstanceHolderCreating<Instance> {
|
|
|
21
17
|
instance: null
|
|
22
18
|
creationPromise: Promise<[undefined, Instance]> | null
|
|
23
19
|
destroyPromise: null
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
deps: string
|
|
20
|
+
type: InjectableType
|
|
21
|
+
scope: InjectableScope
|
|
22
|
+
deps: Set<string>
|
|
27
23
|
destroyListeners: ServiceLocatorInstanceDestroyListener[]
|
|
28
24
|
createdAt: number
|
|
29
25
|
ttl: number
|
|
@@ -35,9 +31,9 @@ export interface ServiceLocatorInstanceHolderCreated<Instance> {
|
|
|
35
31
|
instance: Instance
|
|
36
32
|
creationPromise: null
|
|
37
33
|
destroyPromise: null
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
deps: string
|
|
34
|
+
type: InjectableType
|
|
35
|
+
scope: InjectableScope
|
|
36
|
+
deps: Set<string>
|
|
41
37
|
destroyListeners: ServiceLocatorInstanceDestroyListener[]
|
|
42
38
|
createdAt: number
|
|
43
39
|
ttl: number
|
|
@@ -49,9 +45,23 @@ export interface ServiceLocatorInstanceHolderDestroying<Instance> {
|
|
|
49
45
|
instance: Instance | null
|
|
50
46
|
creationPromise: null
|
|
51
47
|
destroyPromise: Promise<void>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
deps: string
|
|
48
|
+
type: InjectableType
|
|
49
|
+
scope: InjectableScope
|
|
50
|
+
deps: Set<string>
|
|
51
|
+
destroyListeners: ServiceLocatorInstanceDestroyListener[]
|
|
52
|
+
createdAt: number
|
|
53
|
+
ttl: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ServiceLocatorInstanceHolderError {
|
|
57
|
+
status: ServiceLocatorInstanceHolderStatus.Error
|
|
58
|
+
name: string
|
|
59
|
+
instance: Error
|
|
60
|
+
creationPromise: null
|
|
61
|
+
destroyPromise: null
|
|
62
|
+
type: InjectableType
|
|
63
|
+
scope: InjectableScope
|
|
64
|
+
deps: Set<string>
|
|
55
65
|
destroyListeners: ServiceLocatorInstanceDestroyListener[]
|
|
56
66
|
createdAt: number
|
|
57
67
|
ttl: number
|
|
@@ -61,3 +71,4 @@ export type ServiceLocatorInstanceHolder<Instance = unknown> =
|
|
|
61
71
|
| ServiceLocatorInstanceHolderCreating<Instance>
|
|
62
72
|
| ServiceLocatorInstanceHolderCreated<Instance>
|
|
63
73
|
| ServiceLocatorInstanceHolderDestroying<Instance>
|
|
74
|
+
| ServiceLocatorInstanceHolderError
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
2
|
import type { ServiceLocatorInstanceHolder } from './service-locator-instance-holder.mjs'
|
|
3
3
|
|
|
4
|
+
import { InjectableScope, InjectableType } from './enums/index.mjs'
|
|
4
5
|
import {
|
|
5
6
|
ErrorsEnum,
|
|
6
7
|
InstanceDestroying,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
InstanceNotFound,
|
|
9
10
|
} from './errors/index.mjs'
|
|
10
11
|
import { ServiceLocatorInstanceHolderStatus } from './service-locator-instance-holder.mjs'
|
|
12
|
+
import { createDeferred } from './utils/defer.mjs'
|
|
11
13
|
|
|
12
14
|
export class ServiceLocatorManager {
|
|
13
15
|
private readonly instancesHolders: Map<string, ServiceLocatorInstanceHolder> =
|
|
@@ -38,6 +40,11 @@ export class ServiceLocatorManager {
|
|
|
38
40
|
`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is destroying`,
|
|
39
41
|
)
|
|
40
42
|
return [new InstanceDestroying(holder.name), holder]
|
|
43
|
+
} else if (holder.status === ServiceLocatorInstanceHolderStatus.Error) {
|
|
44
|
+
this.logger?.log(
|
|
45
|
+
`[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is in error state`,
|
|
46
|
+
)
|
|
47
|
+
return [holder.instance as InstanceNotFound, holder]
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
return [undefined, holder]
|
|
@@ -86,4 +93,81 @@ export class ServiceLocatorManager {
|
|
|
86
93
|
),
|
|
87
94
|
)
|
|
88
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new holder with Creating status and a deferred creation promise.
|
|
99
|
+
* This is useful for creating placeholder holders that can be fulfilled later.
|
|
100
|
+
* @param name The name of the instance
|
|
101
|
+
* @param type The injectable type
|
|
102
|
+
* @param scope The injectable scope
|
|
103
|
+
* @param deps Optional set of dependencies
|
|
104
|
+
* @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
|
|
105
|
+
* @returns A tuple containing the deferred promise and the holder
|
|
106
|
+
*/
|
|
107
|
+
createCreatingHolder<Instance>(
|
|
108
|
+
name: string,
|
|
109
|
+
type: InjectableType,
|
|
110
|
+
scope: InjectableScope,
|
|
111
|
+
deps: Set<string> = new Set(),
|
|
112
|
+
ttl: number = Infinity,
|
|
113
|
+
): [
|
|
114
|
+
ReturnType<typeof createDeferred<[undefined, Instance]>>,
|
|
115
|
+
ServiceLocatorInstanceHolder<Instance>,
|
|
116
|
+
] {
|
|
117
|
+
const deferred = createDeferred<[undefined, Instance]>()
|
|
118
|
+
|
|
119
|
+
const holder: ServiceLocatorInstanceHolder<Instance> = {
|
|
120
|
+
status: ServiceLocatorInstanceHolderStatus.Creating,
|
|
121
|
+
name,
|
|
122
|
+
instance: null,
|
|
123
|
+
creationPromise: deferred.promise,
|
|
124
|
+
destroyPromise: null,
|
|
125
|
+
type,
|
|
126
|
+
scope,
|
|
127
|
+
deps,
|
|
128
|
+
destroyListeners: [],
|
|
129
|
+
createdAt: Date.now(),
|
|
130
|
+
ttl,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [deferred, holder]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Creates a new holder with Created status and an actual instance.
|
|
138
|
+
* This is useful for creating holders that already have their instance ready.
|
|
139
|
+
* @param name The name of the instance
|
|
140
|
+
* @param instance The actual instance to store
|
|
141
|
+
* @param type The injectable type
|
|
142
|
+
* @param scope The injectable scope
|
|
143
|
+
* @param deps Optional set of dependencies
|
|
144
|
+
* @param ttl Optional time-to-live in milliseconds (defaults to Infinity)
|
|
145
|
+
* @returns The created holder
|
|
146
|
+
*/
|
|
147
|
+
storeCreatedHolder<Instance>(
|
|
148
|
+
name: string,
|
|
149
|
+
instance: Instance,
|
|
150
|
+
type: InjectableType,
|
|
151
|
+
scope: InjectableScope,
|
|
152
|
+
deps: Set<string> = new Set(),
|
|
153
|
+
ttl: number = Infinity,
|
|
154
|
+
): ServiceLocatorInstanceHolder<Instance> {
|
|
155
|
+
const holder: ServiceLocatorInstanceHolder<Instance> = {
|
|
156
|
+
status: ServiceLocatorInstanceHolderStatus.Created,
|
|
157
|
+
name,
|
|
158
|
+
instance,
|
|
159
|
+
creationPromise: null,
|
|
160
|
+
destroyPromise: null,
|
|
161
|
+
type,
|
|
162
|
+
scope,
|
|
163
|
+
deps,
|
|
164
|
+
destroyListeners: [],
|
|
165
|
+
createdAt: Date.now(),
|
|
166
|
+
ttl,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.instancesHolders.set(name, holder)
|
|
170
|
+
|
|
171
|
+
return holder
|
|
172
|
+
}
|
|
89
173
|
}
|