UNPKG

@inversifyjs/container

Version:

InversifyJs container

169 lines 6.85 kB
import { isPromise, stringifyServiceIdentifier, } from '@inversifyjs/common'; import { plan, resolve, } from '@inversifyjs/core'; import { InversifyContainerError } from '../../error/models/InversifyContainerError.js'; import { InversifyContainerErrorKind } from '../../error/models/InversifyContainerErrorKind.js'; export class ServiceResolutionManager { #autobind; #defaultScope; #getActivationsResolutionParam; #resolutionContext; #onPlanHandlers; #planParamsOperationsManager; #serviceReferenceManager; constructor(planParamsOperationsManager, serviceReferenceManager, autobind, defaultScope) { this.#planParamsOperationsManager = planParamsOperationsManager; this.#serviceReferenceManager = serviceReferenceManager; this.#resolutionContext = this.#buildResolutionContext(); this.#autobind = autobind; this.#defaultScope = defaultScope; this.#getActivationsResolutionParam = (serviceIdentifier) => this.#serviceReferenceManager.activationService.get(serviceIdentifier); this.#onPlanHandlers = []; this.#serviceReferenceManager.onReset(() => { this.#resetComputedProperties(); }); } get(serviceIdentifier, options) { const planResult = this.#buildPlanResult(false, serviceIdentifier, options); const resolvedValue = this.#getFromPlanResult(planResult); if (isPromise(resolvedValue)) { throw new InversifyContainerError(InversifyContainerErrorKind.invalidOperation, `Unexpected asynchronous service when resolving service "${stringifyServiceIdentifier(serviceIdentifier)}"`); } return resolvedValue; } getAll(serviceIdentifier, options) { const planResult = this.#buildPlanResult(true, serviceIdentifier, options); const resolvedValue = this.#getFromPlanResult(planResult); if (isPromise(resolvedValue)) { throw new InversifyContainerError(InversifyContainerErrorKind.invalidOperation, `Unexpected asynchronous service when resolving service "${stringifyServiceIdentifier(serviceIdentifier)}"`); } return resolvedValue; } async getAllAsync(serviceIdentifier, options) { const planResult = this.#buildPlanResult(true, serviceIdentifier, options); return this.#getFromPlanResult(planResult); } async getAsync(serviceIdentifier, options) { const planResult = this.#buildPlanResult(false, serviceIdentifier, options); return this.#getFromPlanResult(planResult); } onPlan(handler) { this.#onPlanHandlers.push(handler); } #resetComputedProperties() { this.#resolutionContext = this.#buildResolutionContext(); } #buildGetPlanOptions(isMultiple, serviceIdentifier, options) { const name = options?.name; const optional = options?.optional ?? false; const tag = options?.tag; if (isMultiple) { return { chained: options?.chained ?? false, isMultiple, name, optional, serviceIdentifier, tag, }; } else { return { isMultiple, name, optional, serviceIdentifier, tag, }; } } #buildPlanParams(serviceIdentifier, isMultiple, options) { const planParams = { autobindOptions: (options?.autobind ?? this.#autobind) ? { scope: this.#defaultScope, } : undefined, operations: this.#planParamsOperationsManager.planParamsOperations, rootConstraints: this.#buildPlanParamsConstraints(serviceIdentifier, isMultiple, options), servicesBranch: [], }; this.#handlePlanParamsRootConstraints(planParams, options); return planParams; } #buildPlanParamsConstraints(serviceIdentifier, isMultiple, options) { if (isMultiple) { return { chained: options?.chained ?? false, isMultiple, serviceIdentifier, }; } else { return { isMultiple, serviceIdentifier, }; } } #buildPlanResult(isMultiple, serviceIdentifier, options) { /** * This avoids allocating a {@link GetPlanOptions} object and the extra * branching performed by the plan result cache service. */ if (!isMultiple && options === undefined) { const cachedPlanResultFromServiceIdentifier = this.#serviceReferenceManager.planResultCacheService.getByServiceIdentifier(serviceIdentifier); if (cachedPlanResultFromServiceIdentifier !== undefined) { return cachedPlanResultFromServiceIdentifier; } } const getPlanOptions = this.#buildGetPlanOptions(isMultiple, serviceIdentifier, options); const planResultFromCache = this.#serviceReferenceManager.planResultCacheService.get(getPlanOptions); if (planResultFromCache !== undefined) { return planResultFromCache; } const planResult = plan(this.#buildPlanParams(serviceIdentifier, isMultiple, options)); for (const handler of this.#onPlanHandlers) { handler(getPlanOptions, planResult); } return planResult; } #buildResolutionContext() { return { get: this.get.bind(this), getAll: this.getAll.bind(this), getAllAsync: this.getAllAsync.bind(this), getAsync: this.getAsync.bind(this), }; } // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters #getFromPlanResult(planResult) { return resolve({ context: this.#resolutionContext, getActivations: this.#getActivationsResolutionParam, planResult, requestScopeCache: undefined, }); } #handlePlanParamsRootConstraints(planParams, options) { if (options === undefined) { return; } if (options.name !== undefined) { planParams.rootConstraints.name = options.name; } if (options.optional === true) { planParams.rootConstraints.isOptional = true; } if (options.tag !== undefined) { planParams.rootConstraints.tag = { key: options.tag.key, value: options.tag.value, }; } if (planParams.rootConstraints.isMultiple) { planParams.rootConstraints.chained = options?.chained ?? false; } } } //# sourceMappingURL=ServiceResolutionManager.js.map