ActivationContext.ts
144 lines
| 4.5 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r1 | import { Descriptor, ILifetime, RegistrationMap, LifetimeContainer, ConfigurableKeys } from "./interfaces"; | ||
| import { argumentNotNull } from "./traits"; | ||||
|
|
r0 | |||
| export interface ActivationContextInfo { | ||||
| name: string; | ||||
| service: string; | ||||
| } | ||||
| let nextId = 1; | ||||
| /** This class is created once per `Container.resolve` method call and used to | ||||
| * cache dependencies and to track created instances. The activation context | ||||
| * tracks services with `context` activation type. | ||||
| */ | ||||
| export class ActivationContext<S extends object> { | ||||
|
|
r1 | private _cache: { [K: string]: unknown }; | ||
|
|
r0 | |||
|
|
r1 | private _services: Partial<RegistrationMap<S>>; | ||
|
|
r0 | |||
|
|
r1 | private _name: string; | ||
|
|
r0 | |||
|
|
r1 | private _service: Descriptor<S, unknown>; | ||
|
|
r0 | |||
|
|
r1 | private readonly _containerLifetimeManager: LifetimeContainer; | ||
|
|
r0 | |||
|
|
r1 | private _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. | ||||
| */ | ||||
|
|
r1 | constructor(containerLifetimeManager: LifetimeContainer, services: Partial<RegistrationMap<S>>, name: string, service: Descriptor<S, unknown>) { | ||
|
|
r0 | this._name = name; | ||
| this._service = service; | ||||
| this._cache = {}; | ||||
| this._services = services; | ||||
|
|
r1 | this._containerLifetimeManager = containerLifetimeManager; | ||
|
|
r0 | } | ||
| /** the name of the current resolving dependency */ | ||||
| getName() { | ||||
| return this._name; | ||||
| } | ||||
|
|
r1 | createContainerLifetime<T>() { | ||
| return this._containerLifetimeManager.createLifetime<T>(); | ||||
|
|
r0 | } | ||
| /** Resolves the specified dependency in the current context | ||||
| * @param name The name of the dependency being resolved | ||||
| */ | ||||
|
|
r1 | resolve<K extends keyof S>(name: K): NonNullable<S[K]>; | ||
|
|
r0 | /** Resolves the specified dependency with the specified default value if | ||
| * the dependency is missing. | ||||
| * | ||||
| * @param name The name of the dependency being resolved | ||||
| * @param def A default value to return in case of the specified dependency | ||||
| * is missing. | ||||
| */ | ||||
|
|
r1 | resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T; | ||
| resolve<K extends keyof S, T>(name: K, def?: T): 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 | ||||
| */ | ||||
|
|
r1 | register<K extends ConfigurableKeys<S>>(name: K, service: RegistrationMap<S>[K]) { | ||
| argumentNotNull(name, "name"); | ||||
|
|
r0 | |||
|
|
r1 | this._services[name] = service; | ||
|
|
r0 | } | ||
|
|
r1 | createLifetime<T>(): ILifetime<T> { | ||
|
|
r0 | const id = nextId++; | ||
| return { | ||||
|
|
r1 | initialize() {}, | ||
| has: () => id in this._cache, | ||||
| get: () => this._cache[id] as T, | ||||
| 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 | |||
| const ctx = this.enter(d, name); | ||||
| 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; | ||||
| } | ||||
|
|
r1 | private enter(service: Descriptor<S, unknown>, name: string): this { | ||
| const clone = Object.create(this) as this; | ||||
|
|
r0 | clone._name = name; | ||
|
|
r1 | clone._services = Object.create(this._services) as typeof this._services; | ||
|
|
r0 | clone._parent = this; | ||
| clone._service = service; | ||||
| return clone; | ||||
| } | ||||
| /** Creates a clone for the current context, used to protect it from modifications */ | ||||
| clone(): this { | ||||
|
|
r1 | const clone = Object.create(this) as this; | ||
| clone._services = Object.create(this._services) as typeof this._services; | ||||
|
|
r0 | return clone; | ||
| } | ||||
| } | ||||
