123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- import { Service } from "./service"
- import { Observable, Subject } from 'rxjs'
- export let currentContainer: Container | null = null
- export type ContainerEvent =
- | {
- type: 'SERVICE_BIND';
-
- boundeeID: string;
-
- bounderID: string | undefined
- }
- | {
- type: 'SERVICE_INIT';
-
- serviceID: string
- }
- export class Container {
-
- private bindStack: string[] = []
-
- protected boundMap = new Map<string, Service<unknown>>()
-
- protected event$ = new Subject<ContainerEvent>()
-
- public hasBound<
- T extends typeof Service<any> & { ID: string }
- >(service: T): boolean {
- return this.boundMap.has(service.ID)
- }
-
- public getBoundServiceWithID(serviceID: string): Service<unknown> | undefined {
- return this.boundMap.get(serviceID)
- }
-
- public bind<T extends typeof Service<any> & { ID: string }>(
- service: T,
- bounder: ((typeof Service<T>) & { ID: string }) | undefined = undefined
- ): InstanceType<T> {
-
- const oldCurrentContainer = currentContainer;
- currentContainer = this;
-
- if (this.hasBound(service)) {
- this.event$.next({
- type: 'SERVICE_BIND',
- boundeeID: service.ID,
- bounderID: bounder?.ID
- })
- return this.boundMap.get(service.ID) as InstanceType<T>
- }
-
- if (this.bindStack.findIndex((serviceID) => serviceID === service.ID) !== -1) {
- const circularServices = `${this.bindStack.join(' -> ')} -> ${service.ID}`
- throw new Error(`Circular dependency detected.\nChain: ${circularServices}`)
- }
-
- this.bindStack.push(service.ID)
-
-
- const instance: Service<any> = new (service as any)()
- this.boundMap.set(service.ID, instance)
- this.bindStack.pop()
- this.event$.next({
- type: 'SERVICE_INIT',
- serviceID: service.ID,
- })
- this.event$.next({
- type: 'SERVICE_BIND',
- boundeeID: service.ID,
- bounderID: bounder?.ID
- })
-
- currentContainer = oldCurrentContainer;
-
- return instance as InstanceType<T>
- }
-
- public getBoundServices(): IterableIterator<[string, Service<any>]> {
- return this.boundMap.entries()
- }
-
- public getEventStream(): Observable<ContainerEvent> {
- return this.event$.asObservable()
- }
- }
|