ActivationContext.ts
145 lines
| 4.5 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r11 | import { ActivationError } from "./ActivationError"; | ||
|
|
r9 | import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager } from "./interfaces"; | ||
|
|
r11 | import { argumentNotNull, prototype } from "./traits"; | ||
|
|
r0 | |||
| export interface ActivationContextInfo { | ||||
| name: string; | ||||
| service: string; | ||||
| } | ||||
| let nextId = 1; | ||||
|
|
r9 | /** This object is created once per `Container.resolve` method call and used to | ||
|
|
r0 | * cache dependencies and to track created instances. The activation context | ||
| * tracks services with `context` activation type. | ||||
|
|
r9 | * | ||
| * @template S The service map used in the activation context, services from | ||||
| * this map are available to resolution. | ||||
| * @template U A set of keys from the service map which can be overridden in | ||||
| * this activation context. | ||||
|
|
r0 | */ | ||
|
|
r9 | export class ActivationContext<S> implements IActivationContext<S> { | ||
|
|
r3 | private readonly _cache: Record<string, unknown>; | ||
|
|
r0 | |||
|
|
r9 | private readonly _services: DescriptorMap<S>; | ||
|
|
r0 | |||
|
|
r3 | private readonly _name: string; | ||
|
|
r0 | |||
|
|
r3 | private readonly _service: Descriptor<S, unknown>; | ||
|
|
r0 | |||
|
|
r9 | private readonly _containerLifetimeManager: ILifetimeManager; | ||
|
|
r0 | |||
|
|
r3 | private readonly _parent: ActivationContext<S> | undefined; | ||
|
|
r0 | |||
| /** Creates a new activation context with the specified parameters. | ||||
|
|
r1 | * @param containerLifetimeManager the container which starts the activation process | ||
|
|
r0 | * @param services the initial service registrations | ||
| * @param name the name of the service being activated, this parameter is | ||||
| * used for the debug purpose. | ||||
| * @param service the service to activate, this parameter is used for the | ||||
| * debug purpose. | ||||
| */ | ||||
|
|
r9 | constructor(containerLifetimeManager: ILifetimeManager, services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) { | ||
|
|
r0 | this._name = name; | ||
| this._service = service; | ||||
|
|
r3 | this._cache = cache; | ||
|
|
r0 | this._services = services; | ||
|
|
r1 | this._containerLifetimeManager = containerLifetimeManager; | ||
|
|
r0 | } | ||
| /** the name of the current resolving dependency */ | ||||
| getName() { | ||||
| return this._name; | ||||
| } | ||||
|
|
r1 | createContainerLifetime<T>() { | ||
|
|
r5 | return this._containerLifetimeManager.create<T>(); | ||
|
|
r0 | } | ||
|
|
r1 | resolve<K extends keyof S>(name: K): NonNullable<S[K]>; | ||
| resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T; | ||||
|
|
r9 | resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined { | ||
|
|
r0 | const d = this._services[name]; | ||
| if (d !== undefined) { | ||||
| return this.activate(d, name.toString()); | ||||
| } else { | ||||
| if (arguments.length > 1) | ||||
| return def; | ||||
| else | ||||
|
|
r1 | throw new Error(`Service ${String(name)} not found`); | ||
|
|
r0 | } | ||
| } | ||||
| /** | ||||
| * registers services local to the the activation context | ||||
| * | ||||
| * @name{string} the name of the service | ||||
| * @service{string} the service descriptor to register | ||||
| */ | ||||
|
|
r9 | register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]) { | ||
|
|
r1 | argumentNotNull(name, "name"); | ||
|
|
r0 | |||
|
|
r9 | const d = this._services[name]; | ||
| if (d !== undefined && !d.configurable) | ||||
| throw new Error(`Service ${String(name)} can't be overridden`); | ||||
|
|
r1 | this._services[name] = service; | ||
|
|
r0 | } | ||
|
|
r1 | createLifetime<T>(): ILifetime<T> { | ||
|
|
r0 | const id = nextId++; | ||
| return { | ||||
|
|
r9 | initialize() { }, | ||
|
|
r1 | has: () => id in this._cache, | ||
|
|
r9 | get: () => { | ||
| const v = this._cache[id] as T; | ||||
| if (v === undefined || v === null) | ||||
| throw new Error("The value isn't present in the activation context"); | ||||
| return v; | ||||
| }, | ||||
|
|
r1 | store: item => { | ||
| this._cache[id] = item; | ||||
|
|
r0 | } | ||
| }; | ||||
| } | ||||
| activate<T>(d: Descriptor<S, T>, name: string) { | ||||
|
|
r1 | // TODO: add logging | ||
| // if (trace.isLogEnabled()) | ||||
| // trace.log("enter {0} {1}", name, d); | ||||
|
|
r0 | |||
|
|
r11 | const ctx = new ActivationContext( | ||
| this._containerLifetimeManager, | ||||
| d.hasOverrides ? prototype(this._services) : this._services, | ||||
| name, | ||||
| d, | ||||
| this._cache | ||||
| ); | ||||
|
|
r0 | const v = d.activate(ctx); | ||
|
|
r1 | // if (trace.isLogEnabled()) | ||
| // trace.log(`leave ${name}`); | ||||
|
|
r0 | |||
| return v; | ||||
| } | ||||
| getStack(): ActivationContextInfo[] { | ||||
| const stack = [{ | ||||
| name: this._name, | ||||
| service: this._service.toString() | ||||
| }]; | ||||
| return this._parent ? | ||||
| stack.concat(this._parent.getStack()) : | ||||
| stack; | ||||
| } | ||||
|
|
r11 | fail(innerException: unknown): never { | ||
| throw new ActivationError(this._name, this.getStack(), innerException); | ||||
|
|
r0 | } | ||
|
|
r11 | |||
|
|
r0 | } | ||
