LifetimeManager.ts
97 lines
| 2.9 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r15 | import { ILifetime, ILifetimeContext, ILifetimeManager, ILifetimeSlot } from "../typings/interfaces"; | ||
| import { LifetimeSlot } from "./LifetimeSlot"; | ||||
| import { argumentNotNull } from "./traits"; | ||||
|
|
r0 | |||
|
|
r12 | const noop = () => { }; | ||
|
|
r11 | |||
|
|
r12 | const fail = (message: string) => (): never => { | ||
|
|
r11 | throw new Error(message); | ||
| }; | ||||
|
|
r10 | const _emptySlot = Object.freeze({ | ||
| has: () => false, | ||||
|
|
r0 | |||
|
|
r15 | initialize: () => false, | ||
|
|
r11 | |||
| get: fail("The specified item isn't registered with a lifetime manager"), | ||||
|
|
r0 | |||
|
|
r11 | store: noop, | ||
|
|
r0 | |||
|
|
r12 | remove: noop, | ||
|
|
r10 | |||
|
|
r12 | cleanup: noop, | ||
|
|
r0 | }); | ||
|
|
r9 | export class LifetimeManager implements ILifetimeManager { | ||
|
|
r0 | private _destroyed = false; | ||
|
|
r10 | private readonly _slots: Record<string, ILifetimeSlot<unknown>> = {}; | ||
|
|
r1 | |||
|
|
r15 | slot<T>(slotId: string | number): ILifetimeSlot<T> { | ||
|
|
r10 | if (this._destroyed) | ||
| throw new Error("The lifetime manager is destroyed"); | ||||
|
|
r15 | if (slotId in this._slots) | ||
| return this._slots[slotId] as ILifetimeSlot<T>; | ||||
| return this._slots[slotId] = new LifetimeSlot<T>(() => delete this._slots[slotId]); | ||||
|
|
r10 | } | ||
|
|
r15 | |||
|
|
r0 | destroy() { | ||
| if (!this._destroyed) { | ||||
| this._destroyed = true; | ||||
|
|
r15 | Object.values(this._slots).forEach(slot => { | ||
| try { | ||||
| slot.cleanup(); | ||||
| } catch { | ||||
| // ignore | ||||
| } | ||||
| }); | ||||
|
|
r0 | } | ||
| } | ||||
|
|
r9 | } | ||
|
|
r15 | export const emptySlot = <T>() => _emptySlot as ILifetimeSlot<T>; | ||
|
|
r0 | |||
|
|
r15 | export const emptyLifetime = <T>() => emptySlot as ILifetime<T>; | ||
|
|
r0 | |||
|
|
r15 | export const scopeLifetime = <T>(level: number, slotId: string | number) => | ||
| (context: ILifetimeContext) => | ||||
| context.scopeSlot<T>(level, slotId); | ||||
|
|
r13 | |||
|
|
r15 | export const hierarchyLifetime = <T>(slotId: string | number) => | ||
| (context: ILifetimeContext) => | ||||
| context.hierarchySlot<T>(slotId); | ||||
|
|
r0 | |||
|
|
r10 | /** | ||
| * 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. | ||||
| */ | ||||
|
|
r15 | export const contextLifetime = <T>(slotId: string | number) => | ||
| (context: ILifetimeContext) => | ||||
|
|
r12 | context.contextSlot<T>(slotId); | ||
|
|
r0 | |||
|
|
r12 | const singletons = new LifetimeManager(); | ||
|
|
r10 | /** | ||
| * 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 | ||||
| */ | ||||
|
|
r12 | export const singletonLifetime = <T>(typeId: string) => { | ||
|
|
r9 | argumentNotNull(typeId, "typeId"); | ||
|
|
r12 | |||
| return () => singletons.slot<T>(typeId); | ||||
|
|
r9 | }; | ||
