From 4ed0503e5616cba657fbb728ed199c4f7ad2e4c2 Mon Sep 17 00:00:00 2001 From: notaphplover Date: Sun, 8 Dec 2024 09:28:14 +0100 Subject: [PATCH] refactor(core): update resolution flow with binding activations --- .../calculations/stringifyBinding.spec.ts | 1 + .../src/binding/models/BindingActivation.ts | 4 +- .../binding/models/ConstantValueBinding.ts | 14 ++-- .../services/ActivationsService.spec.ts | 4 +- .../binding/services/ActivationsService.ts | 4 +- .../BindingServiceImplementation.spec.ts | 3 + ...BindingNodeSingleInjectionBindings.spec.ts | 1 + .../planning/calculations/plan.int.spec.ts | 1 + .../src/planning/calculations/plan.spec.ts | 6 ++ ...rWhenUnexpectedBindingsAmountFound.spec.ts | 3 + .../resolution/actions/resolve.int.spec.ts | 53 +++++++++++-- .../src/resolution/actions/resolve.spec.ts | 2 + .../actions/resolveBindingActivations.ts | 77 +++++++++++++++++++ .../resolution/actions/resolveScoped.spec.ts | 55 +++++++++++-- .../src/resolution/actions/resolveScoped.ts | 37 ++++++++- ...solveServiceRedirectionBindingNode.spec.ts | 1 + .../resolveSingletonScopedBinding.spec.ts | 23 +++++- .../actions/resolveSingletonScopedBinding.ts | 7 +- ...esolveConstantValueBindingCallback.spec.ts | 52 +++++++++---- .../resolveConstantValueBindingCallback.ts | 15 ++-- .../src/resolution/models/ResolutionParams.ts | 2 +- 21 files changed, 310 insertions(+), 55 deletions(-) create mode 100644 packages/container/libraries/core/src/resolution/actions/resolveBindingActivations.ts diff --git a/packages/container/libraries/core/src/binding/calculations/stringifyBinding.spec.ts b/packages/container/libraries/core/src/binding/calculations/stringifyBinding.spec.ts index 1d64dd4..6fdf83f 100644 --- a/packages/container/libraries/core/src/binding/calculations/stringifyBinding.spec.ts +++ b/packages/container/libraries/core/src/binding/calculations/stringifyBinding.spec.ts @@ -47,6 +47,7 @@ describe(stringifyBinding.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: 'service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }, `[ type: "${bindingTypeValues.ConstantValue}", serviceIdentifier: "service-id", scope: "${bindingScopeValues.Singleton}" ]`, ], diff --git a/packages/container/libraries/core/src/binding/models/BindingActivation.ts b/packages/container/libraries/core/src/binding/models/BindingActivation.ts index 8fa8807..e5af3b7 100644 --- a/packages/container/libraries/core/src/binding/models/BindingActivation.ts +++ b/packages/container/libraries/core/src/binding/models/BindingActivation.ts @@ -1 +1,3 @@ -export type BindingActivation = (injectable: T) => T | Promise; +import { Resolved } from '../../resolution/models/Resolved'; + +export type BindingActivation = (injectable: T) => Resolved; diff --git a/packages/container/libraries/core/src/binding/models/ConstantValueBinding.ts b/packages/container/libraries/core/src/binding/models/ConstantValueBinding.ts index d187408..e817bfa 100644 --- a/packages/container/libraries/core/src/binding/models/ConstantValueBinding.ts +++ b/packages/container/libraries/core/src/binding/models/ConstantValueBinding.ts @@ -1,9 +1,13 @@ +import { Resolved } from '../../resolution/models/Resolved'; import { bindingScopeValues } from './BindingScope'; import { bindingTypeValues } from './BindingType'; import { ScopedBinding } from './ScopedBinding'; -export type ConstantValueBinding = ScopedBinding< - typeof bindingTypeValues.ConstantValue, - typeof bindingScopeValues.Singleton, - TActivated | Promise ->; +export interface ConstantValueBinding + extends ScopedBinding< + typeof bindingTypeValues.ConstantValue, + typeof bindingScopeValues.Singleton, + TActivated | Promise + > { + readonly value: Resolved; +} diff --git a/packages/container/libraries/core/src/binding/services/ActivationsService.spec.ts b/packages/container/libraries/core/src/binding/services/ActivationsService.spec.ts index cf8d7a6..190b236 100644 --- a/packages/container/libraries/core/src/binding/services/ActivationsService.spec.ts +++ b/packages/container/libraries/core/src/binding/services/ActivationsService.spec.ts @@ -112,9 +112,7 @@ describe(ActivationsService.name, () => { beforeAll(() => { bindingActivationFixture = Symbol() as unknown as BindingActivation; - activationMapsMock.get.mockReturnValueOnce( - [bindingActivationFixture].values(), - ); + activationMapsMock.get.mockReturnValueOnce([bindingActivationFixture]); result = activationsService.get(serviceIdFixture); }); diff --git a/packages/container/libraries/core/src/binding/services/ActivationsService.ts b/packages/container/libraries/core/src/binding/services/ActivationsService.ts index 7403c71..b571b1f 100644 --- a/packages/container/libraries/core/src/binding/services/ActivationsService.ts +++ b/packages/container/libraries/core/src/binding/services/ActivationsService.ts @@ -42,7 +42,7 @@ export class ActivationsService { public get( serviceIdentifier: ServiceIdentifier, - ): BindingActivation[] | undefined { + ): Iterable | undefined { const bindings: Iterable | undefined = this.#activationMaps.get( ActivationRelationKind.serviceId, @@ -53,7 +53,7 @@ export class ActivationsService { return undefined; } - return [...bindings]; + return bindings; } public removeAllByModuleId(moduleId: number): void { diff --git a/packages/container/libraries/core/src/binding/services/BindingServiceImplementation.spec.ts b/packages/container/libraries/core/src/binding/services/BindingServiceImplementation.spec.ts index 576338a..d9abd9f 100644 --- a/packages/container/libraries/core/src/binding/services/BindingServiceImplementation.spec.ts +++ b/packages/container/libraries/core/src/binding/services/BindingServiceImplementation.spec.ts @@ -23,6 +23,7 @@ describe(BindingServiceImplementation.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: 'service-identifier-fixture', type: bindingTypeValues.ConstantValue, + value: Symbol(), }; }); @@ -82,6 +83,7 @@ describe(BindingServiceImplementation.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: Symbol(), type: bindingTypeValues.ConstantValue, + value: Symbol(), }; }); @@ -128,6 +130,7 @@ describe(BindingServiceImplementation.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: Symbol(), type: bindingTypeValues.ConstantValue, + value: Symbol(), }; }); diff --git a/packages/container/libraries/core/src/planning/calculations/checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.spec.ts b/packages/container/libraries/core/src/planning/calculations/checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.spec.ts index ace1dfe..74de1c8 100644 --- a/packages/container/libraries/core/src/planning/calculations/checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.spec.ts @@ -87,6 +87,7 @@ describe( scope: bindingScopeValues.Singleton, serviceIdentifier: 'service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }, parent: Symbol() as unknown as BindingNodeParent, }, diff --git a/packages/container/libraries/core/src/planning/calculations/plan.int.spec.ts b/packages/container/libraries/core/src/planning/calculations/plan.int.spec.ts index f1d64fa..26395b6 100644 --- a/packages/container/libraries/core/src/planning/calculations/plan.int.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/plan.int.spec.ts @@ -220,6 +220,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: ServiceIds.constantValue, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; dynamicValueBinding = { diff --git a/packages/container/libraries/core/src/planning/calculations/plan.spec.ts b/packages/container/libraries/core/src/planning/calculations/plan.spec.ts index e40a41e..2e317bc 100644 --- a/packages/container/libraries/core/src/planning/calculations/plan.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/plan.spec.ts @@ -64,6 +64,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; planParamsMock.getBindings.mockReturnValueOnce([constantValueBinding]); @@ -191,6 +192,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; planParamsMock.getBindings.mockReturnValueOnce([constantValueBinding]); @@ -361,6 +363,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; constructorArgumentMetadata = { kind: ClassElementMetadataKind.multipleInjection, @@ -536,6 +539,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; constructorArgumentMetadata = { kind: ClassElementMetadataKind.multipleInjection, @@ -715,6 +719,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: planParamsMock.rootConstraints.serviceIdentifier, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; constructorArgumentMetadata = { kind: ClassElementMetadataKind.singleInjection, @@ -1108,6 +1113,7 @@ describe(plan.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: serviceRedirectionBinding.targetServiceIdentifier, type: bindingTypeValues.ConstantValue, + value: Symbol(), }; planParamsMock.getBindings diff --git a/packages/container/libraries/core/src/planning/calculations/throwErrorWhenUnexpectedBindingsAmountFound.spec.ts b/packages/container/libraries/core/src/planning/calculations/throwErrorWhenUnexpectedBindingsAmountFound.spec.ts index 80eefcb..06133f7 100644 --- a/packages/container/libraries/core/src/planning/calculations/throwErrorWhenUnexpectedBindingsAmountFound.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/throwErrorWhenUnexpectedBindingsAmountFound.spec.ts @@ -113,6 +113,7 @@ Trying to resolve bindings for "${stringifiedServiceIdentifier} (Root service)". scope: bindingScopeValues.Singleton, serviceIdentifier: 'target-service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }, parent: parentNode, }; @@ -369,6 +370,7 @@ Trying to resolve bindings for "${stringifiedServiceIdentifier} (Root service)". scope: bindingScopeValues.Singleton, serviceIdentifier: 'target-service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }, parent: parentNode, }, @@ -386,6 +388,7 @@ Trying to resolve bindings for "${stringifiedServiceIdentifier} (Root service)". scope: bindingScopeValues.Singleton, serviceIdentifier: 'target-service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }, parent: parentNode, }, diff --git a/packages/container/libraries/core/src/resolution/actions/resolve.int.spec.ts b/packages/container/libraries/core/src/resolution/actions/resolve.int.spec.ts index 43920cb..46ceb24 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolve.int.spec.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolve.int.spec.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from '@jest/globals'; import 'reflect-metadata'; -import { Newable, Right, ServiceIdentifier } from '@inversifyjs/common'; +import { Newable, ServiceIdentifier } from '@inversifyjs/common'; import { getReflectMetadata } from '@inversifyjs/reflect-metadata-utils'; import { BindingActivation } from '../../binding/models/BindingActivation'; @@ -39,6 +39,7 @@ import { resolve } from './resolve'; enum ServiceIds { constantValue = 'constant-value-service-id', + constantValueWithActivation = 'constant-value-with-activation-service-id', dynamicValue = 'dynamic-value-service-id', factory = 'factory-service-id', instance = 'instance-service-id', @@ -59,7 +60,10 @@ class Foo { } describe(resolve.name, () => { + let activatedResolvedResult: unknown; + let constantValueBinding: ConstantValueBinding; + let constantValueBindingWithActivation: ConstantValueBinding; let dynamicValueBinding: DynamicValueBinding; let factoryBinding: FactoryBinding>; let instanceBinding: InstanceBinding; @@ -74,10 +78,12 @@ describe(resolve.name, () => { let resolutionContext: ResolutionContext; beforeAll(() => { + activatedResolvedResult = { foo: 'bar' }; + constantValueBinding = { cache: { - isRight: true, - value: Symbol(), + isRight: false, + value: undefined, }, id: 0, isSatisfiedBy: () => true, @@ -87,6 +93,23 @@ describe(resolve.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: ServiceIds.constantValue, type: bindingTypeValues.ConstantValue, + value: Symbol(), + }; + + constantValueBindingWithActivation = { + cache: { + isRight: false, + value: undefined, + }, + id: 0, + isSatisfiedBy: () => true, + moduleId: undefined, + onActivation: () => activatedResolvedResult, + onDeactivation: undefined, + scope: bindingScopeValues.Singleton, + serviceIdentifier: ServiceIds.constantValueWithActivation, + type: bindingTypeValues.ConstantValue, + value: Symbol(), }; dynamicValueBinding = { @@ -178,7 +201,15 @@ describe(resolve.name, () => { activationService = new ActivationsService(); bindingService = new BindingServiceImplementation(); + activationService.add( + constantValueBindingWithActivation.onActivation as BindingActivation, + { + serviceId: constantValueBindingWithActivation.serviceIdentifier, + }, + ); + bindingService.set(constantValueBinding); + bindingService.set(constantValueBindingWithActivation); bindingService.set(dynamicValueBinding); bindingService.set(factoryBinding); bindingService.set(instanceBinding); @@ -353,7 +384,15 @@ describe(resolve.name, () => { isMultiple: false, serviceIdentifier: constantValueBinding.serviceIdentifier, }), - () => (constantValueBinding.cache as Right).value, + () => constantValueBinding.value, + ], + [ + 'with constant value bound service with activation', + (): PlanParamsConstraint => ({ + isMultiple: false, + serviceIdentifier: constantValueBindingWithActivation.serviceIdentifier, + }), + () => activatedResolvedResult, ], [ 'with dynamic value bound service', @@ -387,9 +426,7 @@ describe(resolve.name, () => { }), () => ((): Foo => { - const instance: Foo = new Foo( - (constantValueBinding.cache as Right).value, - ); + const instance: Foo = new Foo(constantValueBinding.value as symbol); (instance as Writable).property = dynamicValueBinding.value( resolutionContext, @@ -404,7 +441,7 @@ describe(resolve.name, () => { isMultiple: false, serviceIdentifier: serviceRedirectionBinding.serviceIdentifier, }), - () => (constantValueBinding.cache as Right).value, + () => constantValueBinding.value, ], ])( 'having plan params %s', diff --git a/packages/container/libraries/core/src/resolution/actions/resolve.spec.ts b/packages/container/libraries/core/src/resolution/actions/resolve.spec.ts index 0508a51..1fbd62c 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolve.spec.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolve.spec.ts @@ -164,6 +164,7 @@ describe(resolve.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: 'service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }; const bindingNode: PlanBindingNode = { @@ -241,6 +242,7 @@ describe(resolve.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: 'service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }; const bindingNode: PlanBindingNode = { diff --git a/packages/container/libraries/core/src/resolution/actions/resolveBindingActivations.ts b/packages/container/libraries/core/src/resolution/actions/resolveBindingActivations.ts new file mode 100644 index 0000000..a54df23 --- /dev/null +++ b/packages/container/libraries/core/src/resolution/actions/resolveBindingActivations.ts @@ -0,0 +1,77 @@ +import { ServiceIdentifier } from '@inversifyjs/common'; + +import { BindingActivation } from '../../binding/models/BindingActivation'; +import { isPromise } from '../../common/calculations/isPromise'; +import { ResolutionParams } from '../models/ResolutionParams'; +import { Resolved } from '../models/Resolved'; + +export function resolveBindingActivations( + params: ResolutionParams, + serviceIdentifier: ServiceIdentifier, + value: Resolved, +): Resolved { + const activations: Iterable> | undefined = + params.getActivations(serviceIdentifier); + + if (activations !== undefined) { + if (isPromise(value)) { + return resolveBindingActivationsFromIteratorAsync( + value, + activations[Symbol.iterator](), + ); + } else { + return resolveBindingActivationsFromIterator( + value, + activations[Symbol.iterator](), + ); + } + } + + return value; +} + +function resolveBindingActivationsFromIterator( + value: Awaited, + activationsIterator: Iterator>, +): Resolved { + let activatedValue: Awaited = value; + + let activationIteratorResult: IteratorResult> = + activationsIterator.next(); + + while (activationIteratorResult.done !== true) { + const nextActivatedValue: Resolved = + activationIteratorResult.value(activatedValue); + + if (isPromise(nextActivatedValue)) { + return resolveBindingActivationsFromIteratorAsync( + nextActivatedValue, + activationsIterator, + ); + } else { + activatedValue = nextActivatedValue; + } + + activationIteratorResult = activationsIterator.next(); + } + + return activatedValue; +} + +async function resolveBindingActivationsFromIteratorAsync( + value: Promise, + activationsIterator: Iterator>, +): Promise> { + let activatedValue: Awaited = await value; + + let activationIteratorResult: IteratorResult> = + activationsIterator.next(); + + while (activationIteratorResult.done !== true) { + activatedValue = await activationIteratorResult.value(activatedValue); + + activationIteratorResult = activationsIterator.next(); + } + + return activatedValue; +} diff --git a/packages/container/libraries/core/src/resolution/actions/resolveScoped.spec.ts b/packages/container/libraries/core/src/resolution/actions/resolveScoped.spec.ts index f264190..48f6afd 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolveScoped.spec.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolveScoped.spec.ts @@ -2,6 +2,8 @@ import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; import { Right } from '@inversifyjs/common'; +jest.mock('./resolveBindingActivations'); + import { BindingScope, bindingScopeValues, @@ -12,6 +14,7 @@ import { } from '../../binding/models/BindingType'; import { ScopedBinding } from '../../binding/models/ScopedBinding'; import { ResolutionParams } from '../models/ResolutionParams'; +import { resolveBindingActivations } from './resolveBindingActivations'; import { resolveScoped } from './resolveScoped'; describe(resolveScoped.name, () => { @@ -100,6 +103,7 @@ describe(resolveScoped.name, () => { >; let resolveResult: unknown; + let activatedResolveResult: unknown; let result: unknown; @@ -120,10 +124,14 @@ describe(resolveScoped.name, () => { }; resolveResult = Symbol(); + activatedResolveResult = Symbol(); getBindingMock.mockReturnValueOnce(bindingFixture); resolveMock.mockReturnValueOnce(resolveResult); + ( + resolveBindingActivations as jest.Mock + ).mockReturnValueOnce(activatedResolveResult); result = resolveScoped(getBindingMock, resolveMock)( paramsMock, @@ -145,17 +153,26 @@ describe(resolveScoped.name, () => { expect(resolveMock).toHaveBeenCalledWith(paramsMock, argFixture); }); + it('should call resolveBindingActivations()', () => { + expect(resolveBindingActivations).toHaveBeenCalledTimes(1); + expect(resolveBindingActivations).toHaveBeenCalledWith( + paramsMock, + bindingFixture.serviceIdentifier, + resolveResult, + ); + }); + it('should cache value', () => { const expectedCache: Right = { isRight: true, - value: resolveResult, + value: activatedResolveResult, }; expect(bindingFixture.cache).toStrictEqual(expectedCache); }); it('should return expected result', () => { - expect(result).toBe(resolveResult); + expect(result).toBe(activatedResolveResult); }); }); @@ -235,6 +252,7 @@ describe(resolveScoped.name, () => { >; let resolveResult: unknown; + let activatedResolveResult: unknown; let result: unknown; @@ -255,10 +273,14 @@ describe(resolveScoped.name, () => { }; resolveResult = Symbol(); + activatedResolveResult = Symbol(); getBindingMock.mockReturnValueOnce(bindingFixture); resolveMock.mockReturnValueOnce(resolveResult); + ( + resolveBindingActivations as jest.Mock + ).mockReturnValueOnce(activatedResolveResult); paramsMock.requestScopeCache.has.mockReturnValueOnce(false); @@ -289,16 +311,25 @@ describe(resolveScoped.name, () => { expect(resolveMock).toHaveBeenCalledWith(paramsMock, argFixture); }); + it('should call resolveBindingActivations()', () => { + expect(resolveBindingActivations).toHaveBeenCalledTimes(1); + expect(resolveBindingActivations).toHaveBeenCalledWith( + paramsMock, + bindingFixture.serviceIdentifier, + resolveResult, + ); + }); + it('should call params.requestScopeCache.set()', () => { expect(paramsMock.requestScopeCache.set).toHaveBeenCalledTimes(1); expect(paramsMock.requestScopeCache.set).toHaveBeenCalledWith( bindingFixture.id, - resolveResult, + activatedResolveResult, ); }); it('should return expected result', () => { - expect(result).toBe(resolveResult); + expect(result).toBe(activatedResolveResult); }); }); @@ -310,6 +341,7 @@ describe(resolveScoped.name, () => { >; let resolveResult: unknown; + let activatedResolveResult: unknown; let result: unknown; @@ -330,10 +362,14 @@ describe(resolveScoped.name, () => { }; resolveResult = Symbol(); + activatedResolveResult = Symbol(); getBindingMock.mockReturnValueOnce(bindingFixture); resolveMock.mockReturnValueOnce(resolveResult); + ( + resolveBindingActivations as jest.Mock + ).mockReturnValueOnce(activatedResolveResult); result = resolveScoped(getBindingMock, resolveMock)( paramsMock, @@ -355,8 +391,17 @@ describe(resolveScoped.name, () => { expect(resolveMock).toHaveBeenCalledWith(paramsMock, argFixture); }); + it('should call resolveBindingActivations()', () => { + expect(resolveBindingActivations).toHaveBeenCalledTimes(1); + expect(resolveBindingActivations).toHaveBeenCalledWith( + paramsMock, + bindingFixture.serviceIdentifier, + resolveResult, + ); + }); + it('should return expected result', () => { - expect(result).toBe(resolveResult); + expect(result).toBe(activatedResolveResult); }); }); }); diff --git a/packages/container/libraries/core/src/resolution/actions/resolveScoped.ts b/packages/container/libraries/core/src/resolution/actions/resolveScoped.ts index 5616ad4..bdb2112 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolveScoped.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolveScoped.ts @@ -1,3 +1,5 @@ +import { ServiceIdentifier } from '@inversifyjs/common'; + import { BindingScope, bindingScopeValues, @@ -6,6 +8,7 @@ import { BindingType } from '../../binding/models/BindingType'; import { ScopedBinding } from '../../binding/models/ScopedBinding'; import { ResolutionParams } from '../models/ResolutionParams'; import { Resolved } from '../models/Resolved'; +import { resolveBindingActivations } from './resolveBindingActivations'; export function resolveScoped< TActivated, @@ -26,7 +29,12 @@ export function resolveScoped< return binding.cache.value; } - const resolvedValue: Resolved = resolve(params, arg); + const resolvedValue: Resolved = resolveAndActivate( + params, + arg, + binding.serviceIdentifier, + resolve, + ); binding.cache = { isRight: true, @@ -42,14 +50,37 @@ export function resolveScoped< ) as Resolved; } - const resolvedValue: Resolved = resolve(params, arg); + const resolvedValue: Resolved = resolveAndActivate( + params, + arg, + binding.serviceIdentifier, + resolve, + ); params.requestScopeCache.set(binding.id, resolvedValue); return resolvedValue; } case bindingScopeValues.Transient: - return resolve(params, arg); + return resolveAndActivate( + params, + arg, + binding.serviceIdentifier, + resolve, + ); } }; } + +function resolveAndActivate( + params: ResolutionParams, + arg: TArg, + serviceIdentifier: ServiceIdentifier, + resolve: (params: ResolutionParams, arg: TArg) => Resolved, +): Resolved { + return resolveBindingActivations( + params, + serviceIdentifier, + resolve(params, arg), + ); +} diff --git a/packages/container/libraries/core/src/resolution/actions/resolveServiceRedirectionBindingNode.spec.ts b/packages/container/libraries/core/src/resolution/actions/resolveServiceRedirectionBindingNode.spec.ts index b0a0640..4c0e451 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolveServiceRedirectionBindingNode.spec.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolveServiceRedirectionBindingNode.spec.ts @@ -65,6 +65,7 @@ describe(resolveServiceRedirectionBindingNode.name, () => { scope: bindingScopeValues.Singleton, serviceIdentifier: 'service-id', type: bindingTypeValues.ConstantValue, + value: Symbol(), }, parent: nodeRedirectionFixture, }; diff --git a/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.spec.ts b/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.spec.ts index 4f0a0e5..523ea85 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.spec.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.spec.ts @@ -1,5 +1,7 @@ import { beforeAll, describe, expect, it, jest } from '@jest/globals'; +jest.mock('./resolveBindingActivations'); + import { Right } from '@inversifyjs/common'; import { bindingScopeValues } from '../../binding/models/BindingScope'; @@ -9,6 +11,7 @@ import { } from '../../binding/models/BindingType'; import { ScopedBinding } from '../../binding/models/ScopedBinding'; import { ResolutionParams } from '../models/ResolutionParams'; +import { resolveBindingActivations } from './resolveBindingActivations'; import { resolveSingletonScopedBinding } from './resolveSingletonScopedBinding'; describe(resolveSingletonScopedBinding.name, () => { @@ -108,13 +111,20 @@ describe(resolveSingletonScopedBinding.name, () => { describe('when called', () => { let resolveResult: unknown; + let activatedResolveResult: unknown; let result: unknown; beforeAll(() => { resolveResult = Symbol(); + activatedResolveResult = Symbol(); resolveMock.mockReturnValueOnce(resolveResult); + ( + resolveBindingActivations as jest.Mock< + typeof resolveBindingActivations + > + ).mockReturnValueOnce(activatedResolveResult); result = resolveSingletonScopedBinding(resolveMock)( resolutionParamsFixture, @@ -130,17 +140,26 @@ describe(resolveSingletonScopedBinding.name, () => { ); }); + it('should call resolveBindingActivations()', () => { + expect(resolveBindingActivations).toHaveBeenCalledTimes(1); + expect(resolveBindingActivations).toHaveBeenCalledWith( + resolutionParamsFixture, + bindingFixture.serviceIdentifier, + resolveResult, + ); + }); + it('should cache value', () => { const expectedCache: Right = { isRight: true, - value: resolveResult, + value: activatedResolveResult, }; expect(bindingFixture.cache).toStrictEqual(expectedCache); }); it('should return cached value', () => { - expect(result).toBe(resolveResult); + expect(result).toBe(activatedResolveResult); }); }); }); diff --git a/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.ts b/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.ts index 8ce41a7..d5ad9f8 100644 --- a/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.ts +++ b/packages/container/libraries/core/src/resolution/actions/resolveSingletonScopedBinding.ts @@ -3,6 +3,7 @@ import { BindingType } from '../../binding/models/BindingType'; import { ScopedBinding } from '../../binding/models/ScopedBinding'; import { ResolutionParams } from '../models/ResolutionParams'; import { Resolved } from '../models/Resolved'; +import { resolveBindingActivations } from './resolveBindingActivations'; export function resolveSingletonScopedBinding< TActivated, @@ -26,7 +27,11 @@ export function resolveSingletonScopedBinding< return binding.cache.value; } - const resolvedValue: Resolved = resolve(params, binding); + const resolvedValue: Resolved = resolveBindingActivations( + params, + binding.serviceIdentifier, + resolve(params, binding), + ); binding.cache = { isRight: true, diff --git a/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.spec.ts b/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.spec.ts index 54a6fd4..da8fb3d 100644 --- a/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.spec.ts +++ b/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.spec.ts @@ -1,31 +1,49 @@ import { beforeAll, describe, expect, it } from '@jest/globals'; -import { InversifyCoreError } from '../../error/models/InversifyCoreError'; -import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; +import { bindingScopeValues } from '../../binding/models/BindingScope'; +import { bindingTypeValues } from '../../binding/models/BindingType'; +import { ConstantValueBinding } from '../../binding/models/ConstantValueBinding'; +import { ResolutionParams } from '../models/ResolutionParams'; import { resolveConstantValueBindingCallback } from './resolveConstantValueBindingCallback'; describe(resolveConstantValueBindingCallback.name, () => { + let resolutionParamsFixture: ResolutionParams; + let constantValueBindingFixture: ConstantValueBinding; + + beforeAll(() => { + resolutionParamsFixture = { + context: Symbol() as unknown, + } as Partial as ResolutionParams; + + constantValueBindingFixture = { + cache: { + isRight: false, + value: undefined, + }, + id: 1, + isSatisfiedBy: () => true, + moduleId: undefined, + onActivation: undefined, + onDeactivation: undefined, + scope: bindingScopeValues.Singleton, + serviceIdentifier: 'service-id', + type: bindingTypeValues.ConstantValue, + value: Symbol(), + }; + }); + describe('when called', () => { let result: unknown; beforeAll(() => { - try { - resolveConstantValueBindingCallback(); - } catch (error: unknown) { - result = error; - } + result = resolveConstantValueBindingCallback( + resolutionParamsFixture, + constantValueBindingFixture, + ); }); - it('should throw an InversifyCoreError', () => { - const expectedErrorProperties: Partial = { - kind: InversifyCoreErrorKind.unknown, - message: 'Expected constant value binding with value, none found', - }; - - expect(result).toBeInstanceOf(InversifyCoreError); - expect(result).toStrictEqual( - expect.objectContaining(expectedErrorProperties), - ); + it('should return expected value', () => { + expect(result).toBe(constantValueBindingFixture.value); }); }); }); diff --git a/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.ts b/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.ts index 43150ef..0859ebc 100644 --- a/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.ts +++ b/packages/container/libraries/core/src/resolution/calculations/resolveConstantValueBindingCallback.ts @@ -1,9 +1,10 @@ -import { InversifyCoreError } from '../../error/models/InversifyCoreError'; -import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; +import { ConstantValueBinding } from '../../binding/models/ConstantValueBinding'; +import { ResolutionParams } from '../models/ResolutionParams'; +import { Resolved } from '../models/Resolved'; -export function resolveConstantValueBindingCallback(): never { - throw new InversifyCoreError( - InversifyCoreErrorKind.unknown, - 'Expected constant value binding with value, none found', - ); +export function resolveConstantValueBindingCallback( + _params: ResolutionParams, + binding: ConstantValueBinding, +): Resolved { + return binding.value; } diff --git a/packages/container/libraries/core/src/resolution/models/ResolutionParams.ts b/packages/container/libraries/core/src/resolution/models/ResolutionParams.ts index 5eb42b1..f10109b 100644 --- a/packages/container/libraries/core/src/resolution/models/ResolutionParams.ts +++ b/packages/container/libraries/core/src/resolution/models/ResolutionParams.ts @@ -8,7 +8,7 @@ export interface ResolutionParams { context: ResolutionContext; getActivations: ( serviceIdentifier: ServiceIdentifier, - ) => BindingActivation[] | undefined; + ) => Iterable> | undefined; planResult: PlanResult; requestScopeCache: Map; }