@navios/di 0.1.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 (41) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +2 -0
  3. package/dist/_tsup-dts-rollup.d.mts +536 -0
  4. package/dist/_tsup-dts-rollup.d.ts +536 -0
  5. package/dist/index.d.mts +54 -0
  6. package/dist/index.d.ts +54 -0
  7. package/dist/index.js +1019 -0
  8. package/dist/index.mjs +960 -0
  9. package/package.json +46 -0
  10. package/src/__tests__/injectable.spec.mts +167 -0
  11. package/src/__tests__/injection-token.spec.mts +127 -0
  12. package/src/decorators/index.mts +1 -0
  13. package/src/decorators/injectable.decorator.mts +107 -0
  14. package/src/enums/index.mts +2 -0
  15. package/src/enums/injectable-scope.enum.mts +10 -0
  16. package/src/enums/injectable-type.enum.mts +4 -0
  17. package/src/errors/errors.enum.mts +8 -0
  18. package/src/errors/factory-not-found.mts +8 -0
  19. package/src/errors/factory-token-not-resolved.mts +10 -0
  20. package/src/errors/index.mts +7 -0
  21. package/src/errors/instance-destroying.mts +8 -0
  22. package/src/errors/instance-expired.mts +8 -0
  23. package/src/errors/instance-not-found.mts +8 -0
  24. package/src/errors/unknown-error.mts +15 -0
  25. package/src/event-emitter.mts +107 -0
  26. package/src/factory-context.mts +16 -0
  27. package/src/index.mts +16 -0
  28. package/src/injection-token.mts +130 -0
  29. package/src/injector.mts +19 -0
  30. package/src/interfaces/factory.interface.mts +11 -0
  31. package/src/interfaces/index.mts +1 -0
  32. package/src/proxy-service-locator.mts +83 -0
  33. package/src/registry.mts +65 -0
  34. package/src/resolve-service.mts +43 -0
  35. package/src/service-locator-event-bus.mts +96 -0
  36. package/src/service-locator-instance-holder.mts +63 -0
  37. package/src/service-locator-manager.mts +89 -0
  38. package/src/service-locator.mts +571 -0
  39. package/src/utils/get-injectable-token.mts +19 -0
  40. package/src/utils/get-injectors.mts +133 -0
  41. package/src/utils/index.mts +2 -0
package/src/index.mts ADDED
@@ -0,0 +1,16 @@
1
+ export * from './decorators/index.mjs'
2
+ export * from './enums/index.mjs'
3
+ export * from './errors/index.mjs'
4
+ export * from './interfaces/index.mjs'
5
+ export * from './utils/index.mjs'
6
+ export * from './event-emitter.mjs'
7
+ export * from './factory-context.mjs'
8
+ export * from './injection-token.mjs'
9
+ export * from './injector.mjs'
10
+ export * from './proxy-service-locator.mjs'
11
+ export * from './registry.mjs'
12
+ export * from './resolve-service.mjs'
13
+ export * from './service-locator.mjs'
14
+ export * from './service-locator-event-bus.mjs'
15
+ export * from './service-locator-instance-holder.mjs'
16
+ export * from './service-locator-manager.mjs'
@@ -0,0 +1,130 @@
1
+ import type { AnyZodObject } from 'zod'
2
+
3
+ import { randomUUID } from 'crypto'
4
+
5
+ import { z, ZodOptional } from 'zod'
6
+
7
+ export type ClassType = new (...args: any[]) => any
8
+
9
+ export type ClassTypeWithInstance<T> = new (...args: any[]) => T
10
+
11
+ export class InjectionToken<
12
+ T,
13
+ S extends AnyZodObject | ZodOptional<AnyZodObject> | unknown = unknown,
14
+ > {
15
+ public id = randomUUID()
16
+ private formattedName: string | null = null
17
+
18
+ constructor(
19
+ public readonly name: string | symbol | ClassType,
20
+ public readonly schema: AnyZodObject | undefined,
21
+ ) {}
22
+
23
+ static create<T extends ClassType>(
24
+ name: T,
25
+ ): InjectionToken<InstanceType<T>, undefined>
26
+ static create<
27
+ T extends ClassType,
28
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject>,
29
+ >(name: T, schema: Schema): InjectionToken<InstanceType<T>, Schema>
30
+ static create<T>(name: string | symbol): InjectionToken<T, undefined>
31
+ static create<T, Schema extends AnyZodObject | ZodOptional<AnyZodObject>>(
32
+ name: string | any,
33
+ schema: Schema,
34
+ ): InjectionToken<T, Schema>
35
+ static create(name: string | symbol, schema?: unknown) {
36
+ // @ts-expect-error
37
+ return new InjectionToken(name, schema)
38
+ }
39
+
40
+ static bound<T, S extends AnyZodObject | ZodOptional<AnyZodObject>>(
41
+ token: InjectionToken<T, S>,
42
+ value: z.input<S>,
43
+ ): BoundInjectionToken<T, S> {
44
+ return new BoundInjectionToken(token, value)
45
+ }
46
+
47
+ static factory<T, S extends AnyZodObject | ZodOptional<AnyZodObject>>(
48
+ token: InjectionToken<T, S>,
49
+ factory: () => Promise<z.input<S>>,
50
+ ): FactoryInjectionToken<T, S> {
51
+ return new FactoryInjectionToken(token, factory)
52
+ }
53
+
54
+ static refineType<T>(
55
+ token: BoundInjectionToken<any, any>,
56
+ ): BoundInjectionToken<T, any> {
57
+ return token as BoundInjectionToken<T, any>
58
+ }
59
+
60
+ toString() {
61
+ if (this.formattedName) {
62
+ return this.formattedName
63
+ }
64
+ const { name } = this
65
+ if (typeof name === 'function') {
66
+ const className = name.name
67
+ this.formattedName = `${className}(${this.id})`
68
+ } else if (typeof name === 'symbol') {
69
+ this.formattedName = `${name.toString()}(${this.id})`
70
+ } else {
71
+ this.formattedName = `${name}(${this.id})`
72
+ }
73
+
74
+ return this.formattedName
75
+ }
76
+ }
77
+
78
+ export class BoundInjectionToken<
79
+ T,
80
+ S extends AnyZodObject | ZodOptional<AnyZodObject>,
81
+ > {
82
+ public id: string
83
+ public name: string | symbol | ClassType
84
+ public schema: AnyZodObject | ZodOptional<AnyZodObject>
85
+
86
+ constructor(
87
+ public readonly token: InjectionToken<T, S>,
88
+ public readonly value: z.input<S>,
89
+ ) {
90
+ this.name = token.name
91
+ this.id = token.id
92
+ this.schema = token.schema as AnyZodObject | ZodOptional<AnyZodObject>
93
+ }
94
+
95
+ toString() {
96
+ return this.token.toString()
97
+ }
98
+ }
99
+
100
+ export class FactoryInjectionToken<
101
+ T,
102
+ S extends AnyZodObject | ZodOptional<AnyZodObject>,
103
+ > {
104
+ public value?: z.input<S>
105
+ public resolved = false
106
+ public id: string
107
+ public name: string | symbol | ClassType
108
+ public schema: AnyZodObject | ZodOptional<AnyZodObject>
109
+
110
+ constructor(
111
+ public readonly token: InjectionToken<T, S>,
112
+ public readonly factory: () => Promise<z.input<S>>,
113
+ ) {
114
+ this.name = token.name
115
+ this.id = token.id
116
+ this.schema = token.schema as AnyZodObject | ZodOptional<AnyZodObject>
117
+ }
118
+
119
+ async resolve(): Promise<z.input<S>> {
120
+ if (!this.value) {
121
+ this.value = await this.factory()
122
+ this.resolved = true
123
+ }
124
+ return this.value
125
+ }
126
+
127
+ toString() {
128
+ return this.token.toString()
129
+ }
130
+ }
@@ -0,0 +1,19 @@
1
+ import { ServiceLocator } from './service-locator.mjs'
2
+ import { getInjectors } from './utils/index.mjs'
3
+
4
+ const globalServiceLocator: ServiceLocator = new ServiceLocator()
5
+
6
+ export function getGlobalServiceLocator(): ServiceLocator {
7
+ if (!globalServiceLocator) {
8
+ throw new Error(
9
+ '[ServiceLocator] Service locator is not initialized. Please provide the service locator before using the @Injectable decorator.',
10
+ )
11
+ }
12
+ return globalServiceLocator
13
+ }
14
+ const { inject, syncInject, wrapSyncInit, provideServiceLocator } =
15
+ getInjectors({
16
+ baseLocator: globalServiceLocator,
17
+ })
18
+
19
+ export { inject, syncInject, wrapSyncInit, provideServiceLocator }
@@ -0,0 +1,11 @@
1
+ import type { AnyZodObject } from 'zod'
2
+
3
+ import { z } from 'zod'
4
+
5
+ export interface Factory<T> {
6
+ create(ctx?: any): Promise<T> | T
7
+ }
8
+
9
+ export interface FactoryWithArgs<T, A extends AnyZodObject> {
10
+ create(ctx: any, args: z.output<A>): Promise<T> | T
11
+ }
@@ -0,0 +1 @@
1
+ export * from './factory.interface.mjs'
@@ -0,0 +1,83 @@
1
+ import type { AnyZodObject, z, ZodOptional } from 'zod'
2
+
3
+ import type { FactoryContext } from './factory-context.mjs'
4
+ import type {
5
+ BoundInjectionToken,
6
+ FactoryInjectionToken,
7
+ InjectionToken,
8
+ } from './injection-token.mjs'
9
+ import type { ServiceLocatorEventBus } from './service-locator-event-bus.mjs'
10
+ import type { ServiceLocator } from './service-locator.mjs'
11
+
12
+ export class ProxyServiceLocator implements ServiceLocator {
13
+ constructor(
14
+ private readonly serviceLocator: ServiceLocator,
15
+ private readonly ctx: FactoryContext,
16
+ ) {}
17
+
18
+ getEventBus(): ServiceLocatorEventBus {
19
+ return this.serviceLocator.getEventBus()
20
+ }
21
+
22
+ // @ts-expect-error We don't need all the properties of the class
23
+ public getInstance(
24
+ token:
25
+ | InjectionToken<any, any>
26
+ | BoundInjectionToken<any, any>
27
+ | FactoryInjectionToken<any, any>,
28
+ args?: any,
29
+ ) {
30
+ // @ts-expect-error We don't need all the properties of the class
31
+ return this.ctx.inject(token, args).then(
32
+ (instance) => {
33
+ return [undefined, instance]
34
+ },
35
+ (error) => {
36
+ return [error]
37
+ },
38
+ )
39
+ }
40
+ public getOrThrowInstance<
41
+ Instance,
42
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject> | undefined,
43
+ >(
44
+ token: InjectionToken<Instance, Schema>,
45
+ args: Schema extends AnyZodObject
46
+ ? z.input<Schema>
47
+ : Schema extends ZodOptional<AnyZodObject>
48
+ ? z.input<Schema> | undefined
49
+ : undefined,
50
+ ): Promise<Instance> {
51
+ return this.ctx.inject(token, args)
52
+ }
53
+ public getSyncInstance<
54
+ Instance,
55
+ Schema extends AnyZodObject | ZodOptional<AnyZodObject> | undefined,
56
+ >(
57
+ token: InjectionToken<Instance, Schema>,
58
+ args: Schema extends AnyZodObject
59
+ ? z.input<Schema>
60
+ : Schema extends ZodOptional<AnyZodObject>
61
+ ? z.input<Schema> | undefined
62
+ : undefined,
63
+ ): Instance | null {
64
+ return this.serviceLocator.getSyncInstance(token, args)
65
+ }
66
+ invalidate(service: string, round?: number): Promise<any> {
67
+ return this.serviceLocator.invalidate(service, round)
68
+ }
69
+ ready(): Promise<null> {
70
+ return this.serviceLocator.ready()
71
+ }
72
+ makeInstanceName(token: InjectionToken<any, any>, args: any): string {
73
+ return this.serviceLocator.makeInstanceName(token, args)
74
+ }
75
+ }
76
+
77
+ export function makeProxyServiceLocator(
78
+ serviceLocator: ServiceLocator,
79
+ ctx: FactoryContext,
80
+ ): ServiceLocator {
81
+ // @ts-expect-error We don't need all the properties of the class
82
+ return new ProxyServiceLocator(serviceLocator, ctx)
83
+ }
@@ -0,0 +1,65 @@
1
+ import type { AnyZodObject } from 'zod'
2
+
3
+ import { z } from 'zod'
4
+
5
+ import type { FactoryContext } from './factory-context.mjs'
6
+ import type { InjectionToken } from './injection-token.mjs'
7
+
8
+ import { InjectableScope } from './enums/index.mjs'
9
+
10
+ export type InjectionFactory<T = unknown, Args = unknown> = (
11
+ ctx: FactoryContext,
12
+ args: Args,
13
+ ) => Promise<T>
14
+
15
+ export type FactoryRecord<Instance = any, Schema = any> = {
16
+ scope: InjectableScope
17
+ originalToken: InjectionToken<Instance, Schema>
18
+ factory: InjectionFactory<
19
+ Instance,
20
+ Schema extends AnyZodObject ? z.input<Schema> : unknown
21
+ >
22
+ }
23
+
24
+ export class Registry {
25
+ private readonly factories = new Map<string, FactoryRecord>()
26
+
27
+ constructor(private readonly parent?: Registry) {}
28
+
29
+ has(token: InjectionToken<any, any>): boolean {
30
+ if (this.factories.has(token.id)) {
31
+ return true
32
+ }
33
+ if (this.parent) {
34
+ return this.parent.has(token)
35
+ }
36
+ return false
37
+ }
38
+
39
+ get<Instance, Schema>(
40
+ token: InjectionToken<Instance, Schema>,
41
+ ): FactoryRecord<Instance, Schema> {
42
+ const factory = this.factories.get(token.id)
43
+ if (!factory) {
44
+ if (this.parent) {
45
+ return this.parent.get(token)
46
+ }
47
+ throw new Error(`[Registry] No factory found for ${token.toString()}`)
48
+ }
49
+ return factory
50
+ }
51
+
52
+ set<Instance, Schema>(
53
+ token: InjectionToken<Instance, Schema>,
54
+ factory: InjectionFactory,
55
+ scope: InjectableScope,
56
+ ) {
57
+ this.factories.set(token.id, { factory, scope, originalToken: token })
58
+ }
59
+
60
+ delete(token: InjectionToken<any, any>) {
61
+ this.factories.delete(token.id)
62
+ }
63
+ }
64
+
65
+ export const globalRegistry = new Registry()
@@ -0,0 +1,43 @@
1
+ import { NaviosException } from '@navios/common'
2
+
3
+ import type { FactoryContext } from './factory-context.mjs'
4
+ import type { ClassType } from './injection-token.mjs'
5
+
6
+ import { makeProxyServiceLocator } from './proxy-service-locator.mjs'
7
+ import { getInjectors } from './utils/index.mjs'
8
+
9
+ export async function resolveService<T extends ClassType>(
10
+ ctx: FactoryContext,
11
+ target: T,
12
+ args: any[] = [],
13
+ ): Promise<InstanceType<T>> {
14
+ const proxyServiceLocator = makeProxyServiceLocator(ctx.locator, ctx)
15
+ const { wrapSyncInit, provideServiceLocator } = getInjectors({
16
+ baseLocator: ctx.locator,
17
+ })
18
+ const tryLoad = wrapSyncInit(() => {
19
+ const original = provideServiceLocator(proxyServiceLocator)
20
+ let result = new target(...args)
21
+ provideServiceLocator(original)
22
+ return result
23
+ })
24
+ let [instance, promises] = tryLoad()
25
+ if (promises.length > 0) {
26
+ await Promise.all(promises)
27
+ const newRes = tryLoad()
28
+ instance = newRes[0]
29
+ promises = newRes[1]
30
+ }
31
+ if (promises.length > 0) {
32
+ console.error(`[ServiceLocator] ${target.name} has problem with it's definition.
33
+
34
+ One or more of the dependencies are registered as a InjectableScope.Instance and are used with syncInject.
35
+
36
+ Please use inject instead of syncInject to load those dependencies.`)
37
+ throw new NaviosException(
38
+ `[ServiceLocator] Service ${target.name} cannot be instantiated.`,
39
+ )
40
+ }
41
+
42
+ return instance
43
+ }
@@ -0,0 +1,96 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-function-type */
3
+
4
+ type ListenersMap = Map<string, Map<string, Set<Function>>>
5
+
6
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
7
+ export class ServiceLocatorEventBus {
8
+ private listeners: ListenersMap = new Map()
9
+ constructor(private readonly logger: Console | null = null) {}
10
+
11
+ on<Event extends string | `pre:${string}` | `post:${string}`>(
12
+ ns: string,
13
+ event: Event,
14
+ listener: (event: Event) => void,
15
+ ) {
16
+ this.logger?.debug(`[ServiceLocatorEventBus]#on(): ns:${ns} event:${event}`)
17
+ if (!this.listeners.has(ns)) {
18
+ this.listeners.set(ns, new Map())
19
+ }
20
+
21
+ const nsEvents = this.listeners.get(ns)!
22
+ if (!nsEvents.has(event)) {
23
+ nsEvents.set(event, new Set())
24
+ }
25
+
26
+ nsEvents.get(event)!.add(listener)
27
+
28
+ return () => {
29
+ nsEvents.get(event)!.delete(listener)
30
+ if (nsEvents.get(event)?.size === 0) {
31
+ nsEvents.delete(event)
32
+ }
33
+ if (nsEvents.size === 0) {
34
+ this.listeners.delete(ns)
35
+ }
36
+ }
37
+ }
38
+
39
+ async emit(key: string, event: string) {
40
+ if (!this.listeners.has(key)) {
41
+ return
42
+ }
43
+
44
+ const events = this.listeners.get(key)!
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
+ this.logger?.debug(`[ServiceLocatorEventBus]#emit(): ${key}:${event}`)
62
+
63
+ const res = await Promise.allSettled(
64
+ [...(events.get(event) ?? [])!].map((listener) => listener(event)),
65
+ ).then((results) => {
66
+ const res = results
67
+ .filter((result) => result.status === 'rejected')
68
+ .map((result: PromiseRejectedResult) => {
69
+ this.logger?.warn(
70
+ `[ServiceLocatorEventBus]#emit(): ${key}:${event} rejected with`,
71
+ result.reason,
72
+ )
73
+ return result
74
+ })
75
+
76
+ if (res.length > 0) {
77
+ return Promise.reject(res)
78
+ }
79
+ return results
80
+ })
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
+ return res
95
+ }
96
+ }
@@ -0,0 +1,63 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
2
+
3
+ export enum ServiceLocatorInstanceHolderKind {
4
+ Instance = 'instance',
5
+ Factory = 'factory',
6
+ AbstractFactory = 'abstractFactory',
7
+ }
8
+
9
+ export enum ServiceLocatorInstanceHolderStatus {
10
+ Created = 'created',
11
+ Creating = 'creating',
12
+ Destroying = 'destroying',
13
+ }
14
+
15
+ export type ServiceLocatorInstanceEffect = () => void
16
+ export type ServiceLocatorInstanceDestroyListener = () => void | Promise<void>
17
+
18
+ export interface ServiceLocatorInstanceHolderCreating<Instance> {
19
+ status: ServiceLocatorInstanceHolderStatus.Creating
20
+ name: string
21
+ instance: null
22
+ creationPromise: Promise<[undefined, Instance]> | null
23
+ destroyPromise: null
24
+ kind: ServiceLocatorInstanceHolderKind
25
+ effects: ServiceLocatorInstanceEffect[]
26
+ deps: string[]
27
+ destroyListeners: ServiceLocatorInstanceDestroyListener[]
28
+ createdAt: number
29
+ ttl: number
30
+ }
31
+
32
+ export interface ServiceLocatorInstanceHolderCreated<Instance> {
33
+ status: ServiceLocatorInstanceHolderStatus.Created
34
+ name: string
35
+ instance: Instance
36
+ creationPromise: null
37
+ destroyPromise: null
38
+ kind: ServiceLocatorInstanceHolderKind
39
+ effects: ServiceLocatorInstanceEffect[]
40
+ deps: string[]
41
+ destroyListeners: ServiceLocatorInstanceDestroyListener[]
42
+ createdAt: number
43
+ ttl: number
44
+ }
45
+
46
+ export interface ServiceLocatorInstanceHolderDestroying<Instance> {
47
+ status: ServiceLocatorInstanceHolderStatus.Destroying
48
+ name: string
49
+ instance: Instance | null
50
+ creationPromise: null
51
+ destroyPromise: Promise<void>
52
+ kind: ServiceLocatorInstanceHolderKind
53
+ effects: ServiceLocatorInstanceEffect[]
54
+ deps: string[]
55
+ destroyListeners: ServiceLocatorInstanceDestroyListener[]
56
+ createdAt: number
57
+ ttl: number
58
+ }
59
+
60
+ export type ServiceLocatorInstanceHolder<Instance = unknown> =
61
+ | ServiceLocatorInstanceHolderCreating<Instance>
62
+ | ServiceLocatorInstanceHolderCreated<Instance>
63
+ | ServiceLocatorInstanceHolderDestroying<Instance>
@@ -0,0 +1,89 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
2
+ import type { ServiceLocatorInstanceHolder } from './service-locator-instance-holder.mjs'
3
+
4
+ import {
5
+ ErrorsEnum,
6
+ InstanceDestroying,
7
+ InstanceExpired,
8
+ InstanceNotFound,
9
+ } from './errors/index.mjs'
10
+ import { ServiceLocatorInstanceHolderStatus } from './service-locator-instance-holder.mjs'
11
+
12
+ export class ServiceLocatorManager {
13
+ private readonly instancesHolders: Map<string, ServiceLocatorInstanceHolder> =
14
+ new Map()
15
+
16
+ constructor(private readonly logger: Console | null = null) {}
17
+
18
+ get(
19
+ name: string,
20
+ ):
21
+ | [InstanceExpired | InstanceDestroying, ServiceLocatorInstanceHolder]
22
+ | [InstanceNotFound]
23
+ | [undefined, ServiceLocatorInstanceHolder] {
24
+ const holder = this.instancesHolders.get(name)
25
+ if (holder) {
26
+ if (holder.ttl !== Infinity) {
27
+ const now = Date.now()
28
+ if (now - holder.createdAt > holder.ttl) {
29
+ this.logger?.log(
30
+ `[ServiceLocatorManager]#getInstanceHolder() TTL expired for ${holder.name}`,
31
+ )
32
+ return [new InstanceExpired(holder.name), holder]
33
+ }
34
+ } else if (
35
+ holder.status === ServiceLocatorInstanceHolderStatus.Destroying
36
+ ) {
37
+ this.logger?.log(
38
+ `[ServiceLocatorManager]#getInstanceHolder() Instance ${holder.name} is destroying`,
39
+ )
40
+ return [new InstanceDestroying(holder.name), holder]
41
+ }
42
+
43
+ return [undefined, holder]
44
+ } else {
45
+ this.logger?.log(
46
+ `[ServiceLocatorManager]#getInstanceHolder() Instance ${name} not found`,
47
+ )
48
+ return [new InstanceNotFound(name)]
49
+ }
50
+ }
51
+
52
+ set(name: string, holder: ServiceLocatorInstanceHolder): void {
53
+ this.instancesHolders.set(name, holder)
54
+ }
55
+
56
+ has(
57
+ name: string,
58
+ ): [InstanceExpired | InstanceDestroying] | [undefined, boolean] {
59
+ const [error, holder] = this.get(name)
60
+ if (!error) {
61
+ return [undefined, true]
62
+ }
63
+ if (
64
+ [ErrorsEnum.InstanceExpired, ErrorsEnum.InstanceDestroying].includes(
65
+ error.code,
66
+ )
67
+ ) {
68
+ return [error]
69
+ }
70
+ return [undefined, !!holder]
71
+ }
72
+
73
+ delete(name: string): boolean {
74
+ return this.instancesHolders.delete(name)
75
+ }
76
+
77
+ filter(
78
+ predicate: (
79
+ value: ServiceLocatorInstanceHolder<any>,
80
+ key: string,
81
+ ) => boolean,
82
+ ): Map<string, ServiceLocatorInstanceHolder> {
83
+ return new Map(
84
+ [...this.instancesHolders].filter(([key, value]) =>
85
+ predicate(value, key),
86
+ ),
87
+ )
88
+ }
89
+ }