|
|
import { ActivationError, ActivationItem } from "./ActivationError";
|
|
|
import { IActivationContext, DescriptorMap, ILifetimeManager, ILifetimeSlot, ServiceLocator, IContainerBuilder } from "../typings/interfaces";
|
|
|
import { argumentNotNull, each, prototype } from "./traits";
|
|
|
import { LifetimeManager } from "./LifetimeManager";
|
|
|
import { ContainerBuilder } from "./ContainerBuilder";
|
|
|
|
|
|
/** This object 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.
|
|
|
*
|
|
|
* @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.
|
|
|
*/
|
|
|
export class ActivationContext<S> implements IActivationContext<S> {
|
|
|
|
|
|
private readonly _container: ServiceLocator<S>;
|
|
|
|
|
|
private readonly _contextScope: ILifetimeManager;
|
|
|
|
|
|
private _services: DescriptorMap<S>;
|
|
|
|
|
|
private readonly _scope: ILifetimeManager[];
|
|
|
|
|
|
/** Creates a new activation context with the specified parameters.
|
|
|
* @param containerLifetimeManager the container which starts the activation process
|
|
|
* @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.
|
|
|
*/
|
|
|
constructor(container: ServiceLocator<S>, scope: ILifetimeManager[], services: DescriptorMap<S>, contextScope: ILifetimeManager = new LifetimeManager()) {
|
|
|
this._container = container;
|
|
|
this._contextScope = contextScope;
|
|
|
this._services = services;
|
|
|
this._scope = scope;
|
|
|
}
|
|
|
|
|
|
resolve<K extends keyof S>(name: K, stack: ActivationItem[]): NonNullable<S[K]>;
|
|
|
resolve<K extends keyof S, T>(name: K, stack: ActivationItem[], def: T): NonNullable<S[K]> | T;
|
|
|
resolve<K extends keyof S, T>(name: K, stack: ActivationItem[], def?: T): NonNullable<S[K]> | T | undefined {
|
|
|
const service = this._services[name];
|
|
|
|
|
|
if (service !== undefined) {
|
|
|
return service.activate(
|
|
|
this,
|
|
|
stack.concat({
|
|
|
name: name.toString(),
|
|
|
descriptor: service.toString()
|
|
|
})
|
|
|
);
|
|
|
} else {
|
|
|
if (arguments.length > 2)
|
|
|
return def;
|
|
|
else
|
|
|
throw new Error("Service not found");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* registers services local to the the activation context
|
|
|
*
|
|
|
* @name{string} the name of the service
|
|
|
* @service{string} the service descriptor to register
|
|
|
*/
|
|
|
private _register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]) {
|
|
|
argumentNotNull(name, "name");
|
|
|
|
|
|
const d = this._services[name];
|
|
|
if (d !== undefined && !d.configurable)
|
|
|
throw new Error(`Service ${String(name)} can't be overridden`);
|
|
|
|
|
|
this._services[name] = service;
|
|
|
}
|
|
|
|
|
|
scopeSlot<T>(level: number, slotId: string | number): ILifetimeSlot<T> {
|
|
|
if (level < 0 || level >= this._scope.length)
|
|
|
throw new Error("The scope level is out of range");
|
|
|
return this._scope[level].slot(slotId);
|
|
|
}
|
|
|
|
|
|
hierarchySlot<T>(slotId: string | number): ILifetimeSlot<T> {
|
|
|
return this._scope[this._scope.length - 1].slot(slotId);
|
|
|
}
|
|
|
|
|
|
selfContainer(): ServiceLocator<S> {
|
|
|
return this._container;
|
|
|
}
|
|
|
|
|
|
createChildContainer(): IContainerBuilder<S, keyof S> {
|
|
|
return new ContainerBuilder(this._services, this._scope);
|
|
|
}
|
|
|
|
|
|
contextSlot<T>(slotId: string | number): ILifetimeSlot<T> {
|
|
|
return this._contextScope.slot(slotId);
|
|
|
}
|
|
|
|
|
|
withOverrides<X>(overrides: DescriptorMap<S>, action: () => X) {
|
|
|
const services = this._services;
|
|
|
this._services = prototype(this._services);
|
|
|
try {
|
|
|
each(overrides, (v, k) => this._register(k, v));
|
|
|
return action();
|
|
|
} finally {
|
|
|
this._services = services;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|