import { ILifetime, ILifetimeContext, ILifetimeManager, ILifetimeSlot } from "../typings/interfaces"; import { LifetimeSlot } from "./LifetimeSlot"; import { argumentNotNull } from "./traits"; const noop = () => { }; const fail = (message: string) => (): never => { throw new Error(message); }; const _emptySlot = Object.freeze({ has: () => false, initialize: () => false, get: fail("The specified item isn't registered with a lifetime manager"), store: noop, remove: noop, cleanup: noop, }); export class LifetimeManager implements ILifetimeManager { private _destroyed = false; private readonly _slots: Record> = {}; slot(slotId: string | number): ILifetimeSlot { if (this._destroyed) throw new Error("The lifetime manager is destroyed"); if (slotId in this._slots) return this._slots[slotId] as ILifetimeSlot; return this._slots[slotId] = new LifetimeSlot(() => delete this._slots[slotId]); } destroy() { if (!this._destroyed) { this._destroyed = true; Object.values(this._slots).forEach(slot => { try { slot.cleanup(); } catch { // ignore } }); } } } export const emptySlot = () => _emptySlot as ILifetimeSlot; export const emptyLifetime = () => emptySlot as ILifetime; export const scopeLifetime = (level: number, slotId: string | number) => (context: ILifetimeContext) => context.scopeSlot(level, slotId); export const hierarchyLifetime = (slotId: string | number) => (context: ILifetimeContext) => context.hierarchySlot(slotId); /** * Creates a lifetime instance bound to the current activation context. This * lifetime will store the service instance per activation context. Every * top level service resolution will create a new activation context. This * context is propagated to subsequent service resolution thus all services * with context lifetime will be shared among their consumers. * * @returns The instance of the lifetime. */ export const contextLifetime = (slotId: string | number) => (context: ILifetimeContext) => context.contextSlot(slotId); const singletons = new LifetimeManager(); /** * Creates the lifetime for the service which will allow existence only one * instance with the specified {@linkcode typeId}. If there will be created * several lifetime instances with same `typeId` in the runtime, they will * share the same service instance. * * @param typeId The identified for the global instance, usually this is a * fully qualified class name * @returns The lifetime instance */ export const singletonLifetime = (typeId: string) => { argumentNotNull(typeId, "typeId"); return () => singletons.slot(typeId); };