Container.ts
79 lines
| 3.2 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r0 | import { ActivationContext } from "./ActivationContext"; | ||
| import { ActivationError } from "./ActivationError"; | ||||
|
|
r1 | import { RegistrationMap, ContainerKeys, ServiceContainer, ContainerServices, ConfigurableServices, ConfigurableKeys, ServiceLocator, Descriptor} from "./interfaces"; | ||
|
|
r0 | import { LifetimeManager } from "./LifetimeManager"; | ||
|
|
r1 | import { each, isKey } from "./traits"; | ||
|
|
r0 | |||
|
|
r1 | export class Container<S extends object > implements ServiceContainer<S> { | ||
| private readonly _services: Partial<RegistrationMap<ContainerServices<S>>>; | ||||
|
|
r0 | |||
|
|
r1 | private readonly _lifetimeManager: LifetimeManager; | ||
|
|
r0 | |||
|
|
r1 | private readonly _cleanup: (() => void)[]; | ||
|
|
r0 | |||
|
|
r1 | private _disposed: boolean; | ||
|
|
r0 | |||
| constructor(parent?: Container<S>) { | ||||
|
|
r1 | this._services = Object.create(parent ? parent._services : null) as typeof this._services; | ||
|
|
r0 | this._cleanup = []; | ||
|
|
r1 | this._services.container = { activate: () => this as ServiceLocator<ContainerServices<S>>}; | ||
|
|
r0 | this._services.childContainer = { activate: () => this.createChildContainer() }; | ||
| this._disposed = false; | ||||
| this._lifetimeManager = new LifetimeManager(); | ||||
| } | ||||
|
|
r1 | register<K extends ConfigurableKeys<S>>(name: K, service: Descriptor<ContainerServices<S>, ConfigurableServices<S>[K]>): void; | ||
| register<K extends ConfigurableKeys<S>>(services: {[k in K]: Descriptor<ContainerServices<S>, ConfigurableServices<S>[k]>}): void; | ||||
| register<K extends ConfigurableKeys<S>>(nameOrCollection: K | {[k in K]: Descriptor<ContainerServices<S>, ConfigurableServices<S>[k]>}, service?: RegistrationMap<ContainerServices<S>>[K]) { | ||||
| if (!isKey(nameOrCollection)) { | ||||
| each(nameOrCollection, (v, k) => this.register(k, v)); | ||||
| } else { | ||||
| if (!service) | ||||
| throw new Error("The service parameter must be a descriptor"); | ||||
|
|
r0 | |||
|
|
r1 | this._services[nameOrCollection] = service; | ||
| } | ||||
|
|
r0 | } | ||
|
|
r1 | createChildContainer(): ServiceContainer<S> { | ||
| return new Container(this); | ||||
|
|
r0 | } | ||
|
|
r1 | resolve<K extends ContainerKeys<S>>(name: K): NonNullable<ContainerServices<S>[K]>; | ||
| resolve<K extends ContainerKeys<S>>(name: K, def: ContainerServices<S>[K]): ContainerServices<S>[K]; | ||||
| resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerServices<S>[K]): ContainerServices<S>[K] | undefined { | ||||
| // TODO: add logging | ||||
| // trace.debug("resolve {0}", name); | ||||
|
|
r0 | const d = this._services[name]; | ||
| if (d === undefined) { | ||||
| if (arguments.length > 1) | ||||
| return def; | ||||
| else | ||||
|
|
r1 | throw new Error(`Service '${String(name)}' isn't found`); | ||
|
|
r0 | } else { | ||
|
|
r1 | const context = new ActivationContext(this, this._services, String(name), d); | ||
|
|
r0 | try { | ||
| return d.activate(context); | ||||
| } catch (error) { | ||||
| throw new ActivationError(name.toString(), context.getStack(), error); | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
r1 | createLifetime<T>() { | ||
| return this._lifetimeManager.create<T>(); | ||||
|
|
r0 | } | ||
| destroy() { | ||||
| return this.dispose(); | ||||
| } | ||||
| dispose() { | ||||
| if (this._disposed) | ||||
| return; | ||||
| this._disposed = true; | ||||
| for (const f of this._cleanup) | ||||
| f(); | ||||
| this._lifetimeManager.destroy(); | ||||
| } | ||||
| } | ||||
