import { Descriptor, ILifetime, IActivationContext, DescriptorMap, DescriptorDepsMap } from "../typings/interfaces"; import { ActivationError, ActivationItem } from "./ActivationError"; import { each, key } from "./traits"; export interface DescriptorImplArgs { readonly lifetime: ILifetime; readonly factory: (refs: Record) => NonNullable; readonly cleanup?: (item: NonNullable) => void; readonly overrides?: DescriptorMap; readonly dependencies?: DescriptorDepsMap; } export class DescriptorImpl implements Descriptor { private readonly _overrides: DescriptorMap | undefined; private readonly _lifetime: ILifetime; private readonly _factory: (refs: Record) => NonNullable; private readonly _cleanup: (item: NonNullable) => void; private readonly _dependencies: DescriptorDepsMap | undefined; constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs) { this._lifetime = lifetime; this._factory = factory; this._cleanup = cleanup ? cleanup : () => { }; this._overrides = overrides; this._dependencies = dependencies; } activate(context: IActivationContext, stack: ActivationItem[]): NonNullable { const slot = this._lifetime(context); if (slot.has()) return slot.get(); if (!slot.initialize()) throw new Error("Cyclic reference detected"); const instance = this._overrides ? context.withOverrides(this._overrides, () => this._activate(context, stack)) : this._activate(context, stack); slot.store(instance, this._cleanup); return instance; } private _activate(context: IActivationContext, stack: ActivationItem[]) { const refs: Record = {}; if (this._dependencies) { const resolve = ({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K]; }) => { if (lazy) { return "default" in opts ? () => context.resolve(name, stack, opts.default) : () => context.resolve(name, stack); } else { return "default" in opts ? context.resolve(name, stack, opts.default) : context.resolve(name, stack); } }; // can throw activation exception each(this._dependencies, (v, k) => { refs[k] = resolve(v); }); } try { // call the factory method return (0, this._factory)(refs); } catch (e) { throw new ActivationError("Error creating instance", stack, e); } } toString() { return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`; } }