import { Resolver, RegistrationBuilder } from "./interfaces"; import { Descriptor, ILifetime, ActivationType, PartialServiceMap, ServiceContainer } from "./interfaces"; import { DescriptorImpl } from "./DescriptorImpl"; import { LifetimeManager } from "./LifetimeManager"; export class DescriptorBuilder { private readonly _container: ServiceContainer; private readonly _cb: (d: Descriptor) => void; private readonly _eb: (err: any) => void; private _lifetime = LifetimeManager.empty(); private _overrides?: PartialServiceMap; private _cleanup?: (item: T) => void; private _factory?: (resolve: Resolver) => T; private _pending = 1; private _failed = false; constructor(container: ServiceContainer, cb: (d: Descriptor) => void, eb: (err: any) => void) { this._container = container; this._cb = cb; this._eb = eb; } build(): DescriptorBuilder { this._defer(); return new DescriptorBuilder(this._container, () => this._complete(), err => this._fail(err)); } override(name: K, builder: RegistrationBuilder): this; override(services: { [name in K]: RegistrationBuilder }): this; override(nameOrServices: K | { [name in K]: RegistrationBuilder }, builder?: RegistrationBuilder): this { const overrides: PartialServiceMap = this._overrides ? this._overrides : (this._overrides = {}); const guard = (v: void | Promise) => { if (isPromise(v)) v.catch(err => this._fail(err)); }; if (isPrimitive(nameOrServices)) { if (builder) { this._defer(); const d = new DescriptorBuilder( this._container, result => { overrides[nameOrServices] = result; this._complete(); }, err => this._fail(err) ); try { guard(builder(d)); } catch (err) { this._fail(err); } } } else { each(nameOrServices, (v, k) => this.override(k, v)); } return this; } lifetime(lifetime: "singleton", typeId: string): this; lifetime(lifetime: ILifetime | Exclude): this; lifetime(lifetime: ILifetime | ActivationType, typeId?: string): this { if (isString(lifetime)) { this._lifetime = this._resolveLifetime(lifetime, typeId); } else { this._lifetime = lifetime; } return this; } cleanup(cb: (item: T) => void): this { this._cleanup = cb; return this; } factory(f: (resolve: Resolver) => T): void { this._factory = f; this._complete(); } value(v: T): void { this._cb({ activate() { return v; } }); } _resolveLifetime(activation: ActivationType, typeId?: string | object) { switch (activation) { case "container": return LifetimeManager.containerLifetime(this._container); case "hierarchy": return LifetimeManager.hierarchyLifetime(); case "context": return LifetimeManager.contextLifetime(); case "singleton": if (!typeId) throw Error("The singleton activation requires a typeId"); const _oid = isString(typeId) ? typeId : oid(typeId); return LifetimeManager.singletonLifetime(_oid); default: return LifetimeManager.empty(); } } _defer() { this._pending++; } _complete() { if (--this._pending === 0) { if (!this._factory) throw new Error("The factory must be specified"); this._cb(new DescriptorImpl({ lifetime: this._lifetime, factory: this._factory, overrides: this._overrides, cleanup: this._cleanup })); } } _fail(err: any) { if (!this._failed) { this._failed = true; this._eb.call(undefined, err); } } }