"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.instance = exports.typeInfo = void 0; const providers_1 = require("./providers"); const provider_1 = require("./providers/provider"); const injection_token_1 = require("./providers/injection-token"); const registry_1 = require("./registry"); const lifecycle_1 = require("./types/lifecycle"); const resolution_context_1 = require("./resolution-context"); const error_helpers_1 = require("./error-helpers"); const lazy_helpers_1 = require("./lazy-helpers"); const interceptors_1 = require("./interceptors"); exports.typeInfo = new Map(); class InternalDependencyContainer { constructor(parent) { this.parent = parent; this._registry = new registry_1.default(); this.interceptors = new interceptors_1.default(); } register(token, providerOrConstructor, options = { lifecycle: lifecycle_1.default.Transient }) { let provider; if (!provider_1.isProvider(providerOrConstructor)) { provider = { useClass: providerOrConstructor }; } else { provider = providerOrConstructor; } if (providers_1.isTokenProvider(provider)) { const path = [token]; let tokenProvider = provider; while (tokenProvider != null) { const currentToken = tokenProvider.useToken; if (path.includes(currentToken)) { throw new Error(`Token registration cycle detected! ${[...path, currentToken].join(" -> ")}`); } path.push(currentToken); const registration = this._registry.get(currentToken); if (registration && providers_1.isTokenProvider(registration.provider)) { tokenProvider = registration.provider; } else { tokenProvider = null; } } } if (options.lifecycle === lifecycle_1.default.Singleton || options.lifecycle == lifecycle_1.default.ContainerScoped || options.lifecycle == lifecycle_1.default.ResolutionScoped) { if (providers_1.isValueProvider(provider) || providers_1.isFactoryProvider(provider)) { throw new Error(`Cannot use lifecycle "${lifecycle_1.default[options.lifecycle]}" with ValueProviders or FactoryProviders`); } } this._registry.set(token, { provider, options }); return this; } registerType(from, to) { if (providers_1.isNormalToken(to)) { return this.register(from, { useToken: to }); } return this.register(from, { useClass: to }); } registerInstance(token, instance) { return this.register(token, { useValue: instance }); } registerSingleton(from, to) { if (providers_1.isNormalToken(from)) { if (providers_1.isNormalToken(to)) { return this.register(from, { useToken: to }, { lifecycle: lifecycle_1.default.Singleton }); } else if (to) { return this.register(from, { useClass: to }, { lifecycle: lifecycle_1.default.Singleton }); } throw new Error('Cannot register a type name as a singleton without a "to" token'); } let useClass = from; if (to && !providers_1.isNormalToken(to)) { useClass = to; } return this.register(from, { useClass }, { lifecycle: lifecycle_1.default.Singleton }); } resolve(token, context = new resolution_context_1.default()) { const registration = this.getRegistration(token); if (!registration && providers_1.isNormalToken(token)) { throw new Error(`Attempted to resolve unregistered dependency token: "${token.toString()}"`); } this.executePreResolutionInterceptor(token, "Single"); if (registration) { const result = this.resolveRegistration(registration, context); this.executePostResolutionInterceptor(token, result, "Single"); return result; } if (injection_token_1.isConstructorToken(token)) { const result = this.construct(token, context); this.executePostResolutionInterceptor(token, result, "Single"); return result; } throw new Error("Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function."); } executePreResolutionInterceptor(token, resolutionType) { if (this.interceptors.preResolution.has(token)) { const remainingInterceptors = []; for (const interceptor of this.interceptors.preResolution.getAll(token)) { if (interceptor.options.frequency != "Once") { remainingInterceptors.push(interceptor); } interceptor.callback(token, resolutionType); } this.interceptors.preResolution.setAll(token, remainingInterceptors); } } executePostResolutionInterceptor(token, result, resolutionType) { if (this.interceptors.postResolution.has(token)) { const remainingInterceptors = []; for (const interceptor of this.interceptors.postResolution.getAll(token)) { if (interceptor.options.frequency != "Once") { remainingInterceptors.push(interceptor); } interceptor.callback(token, result, resolutionType); } this.interceptors.postResolution.setAll(token, remainingInterceptors); } } resolveRegistration(registration, context) { if (registration.options.lifecycle === lifecycle_1.default.ResolutionScoped && context.scopedResolutions.has(registration)) { return context.scopedResolutions.get(registration); } const isSingleton = registration.options.lifecycle === lifecycle_1.default.Singleton; const isContainerScoped = registration.options.lifecycle === lifecycle_1.default.ContainerScoped; const returnInstance = isSingleton || isContainerScoped; let resolved; if (providers_1.isValueProvider(registration.provider)) { resolved = registration.provider.useValue; } else if (providers_1.isTokenProvider(registration.provider)) { resolved = returnInstance ? registration.instance || (registration.instance = this.resolve(registration.provider.useToken, context)) : this.resolve(registration.provider.useToken, context); } else if (providers_1.isClassProvider(registration.provider)) { resolved = returnInstance ? registration.instance || (registration.instance = this.construct(registration.provider.useClass, context)) : this.construct(registration.provider.useClass, context); } else if (providers_1.isFactoryProvider(registration.provider)) { resolved = registration.provider.useFactory(this); } else { resolved = this.construct(registration.provider, context); } if (registration.options.lifecycle === lifecycle_1.default.ResolutionScoped) { context.scopedResolutions.set(registration, resolved); } return resolved; } resolveAll(token, context = new resolution_context_1.default()) { const registrations = this.getAllRegistrations(token); if (!registrations && providers_1.isNormalToken(token)) { throw new Error(`Attempted to resolve unregistered dependency token: "${token.toString()}"`); } this.executePreResolutionInterceptor(token, "All"); if (registrations) { const result = registrations.map(item => this.resolveRegistration(item, context)); this.executePostResolutionInterceptor(token, result, "All"); return result; } const result = [this.construct(token, context)]; this.executePostResolutionInterceptor(token, result, "All"); return result; } isRegistered(token, recursive = false) { return (this._registry.has(token) || (recursive && (this.parent || false) && this.parent.isRegistered(token, true))); } reset() { this._registry.clear(); this.interceptors.preResolution.clear(); this.interceptors.postResolution.clear(); } clearInstances() { for (const [token, registrations] of this._registry.entries()) { this._registry.setAll(token, registrations .filter(registration => !providers_1.isValueProvider(registration.provider)) .map(registration => { registration.instance = undefined; return registration; })); } } createChildContainer() { const childContainer = new InternalDependencyContainer(this); for (const [token, registrations] of this._registry.entries()) { if (registrations.some(({ options }) => options.lifecycle === lifecycle_1.default.ContainerScoped)) { childContainer._registry.setAll(token, registrations.map(registration => { if (registration.options.lifecycle === lifecycle_1.default.ContainerScoped) { return { provider: registration.provider, options: registration.options }; } return registration; })); } } return childContainer; } beforeResolution(token, callback, options = { frequency: "Always" }) { this.interceptors.preResolution.set(token, { callback: callback, options: options }); } afterResolution(token, callback, options = { frequency: "Always" }) { this.interceptors.postResolution.set(token, { callback: callback, options: options }); } getRegistration(token) { if (this.isRegistered(token)) { return this._registry.get(token); } if (this.parent) { return this.parent.getRegistration(token); } return null; } getAllRegistrations(token) { if (this.isRegistered(token)) { return this._registry.getAll(token); } if (this.parent) { return this.parent.getAllRegistrations(token); } return null; } construct(ctor, context) { if (ctor instanceof lazy_helpers_1.DelayedConstructor) { return ctor.createProxy((target) => this.resolve(target, context)); } const paramInfo = exports.typeInfo.get(ctor); if (!paramInfo || paramInfo.length === 0) { if (ctor.length === 0) { return new ctor(); } else { throw new Error(`TypeInfo not known for "${ctor.name}"`); } } const params = paramInfo.map(this.resolveParams(context, ctor)); return new ctor(...params); } resolveParams(context, ctor) { return (param, idx) => { try { if (injection_token_1.isTokenDescriptor(param)) { if (injection_token_1.isTransformDescriptor(param)) { return param.multiple ? this.resolve(param.transform).transform(this.resolveAll(param.token), ...param.transformArgs) : this.resolve(param.transform).transform(this.resolve(param.token, context), ...param.transformArgs); } else { return param.multiple ? this.resolveAll(param.token) : this.resolve(param.token, context); } } else if (injection_token_1.isTransformDescriptor(param)) { return this.resolve(param.transform, context).transform(this.resolve(param.token, context), ...param.transformArgs); } return this.resolve(param, context); } catch (e) { throw new Error(error_helpers_1.formatErrorCtor(ctor, idx, e)); } }; } } exports.instance = new InternalDependencyContainer(); exports.default = exports.instance;