From cc0c57b7c5d847cc53a521842855232ee236166e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:32:27 +0000 Subject: [PATCH 01/11] Initial plan From 35c49a0a128c25983683caf5349ec528e04093a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 17:42:10 +0000 Subject: [PATCH 02/11] fix(core): respect jitless flag for resolved value bindings --- .changeset/fix-resolved-value-jitless.md | 6 + .../actions/curryBuildServiceNodeBindings.ts | 2 +- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 89 +++++++++++++ ...ldOneResolvedValueArgumentResolverOnCsp.ts | 39 ++++++ ...esolvedValueArgumentsResolverOnCsp.spec.ts | 118 ++++++++++++++++++ ...uildResolvedValueArgumentsResolverOnCsp.ts | 42 +++++++ .../buildResolvedValueBindingNodeResolver.ts | 90 ++++++++----- ...esolvedValueArgumentsResolverOnCsp.spec.ts | 72 +++++++++++ ...ZeroResolvedValueArgumentsResolverOnCsp.ts | 22 ++++ .../ResolvedValueBindingNodeImplementation.ts | 7 +- 10 files changed, 456 insertions(+), 31 deletions(-) create mode 100644 .changeset/fix-resolved-value-jitless.md create mode 100644 packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts diff --git a/.changeset/fix-resolved-value-jitless.md b/.changeset/fix-resolved-value-jitless.md new file mode 100644 index 000000000..9030cec8c --- /dev/null +++ b/.changeset/fix-resolved-value-jitless.md @@ -0,0 +1,6 @@ +--- +"@inversifyjs/core": patch +"inversify": patch +--- + +- Fixed resolved value binding resolution to respect the `jitless` container option, avoiding `Function` constructor usage in CSP-restricted environments. diff --git a/packages/container/libraries/core/src/planning/actions/curryBuildServiceNodeBindings.ts b/packages/container/libraries/core/src/planning/actions/curryBuildServiceNodeBindings.ts index 54f2bf066..8fb4b6b47 100644 --- a/packages/container/libraries/core/src/planning/actions/curryBuildServiceNodeBindings.ts +++ b/packages/container/libraries/core/src/planning/actions/curryBuildServiceNodeBindings.ts @@ -201,7 +201,7 @@ function curryBuildResolvedValuePlanBindingNode( bindingConstraintsList: SingleImmutableLinkedList, ): PlanBindingNode => { const childNode: ResolvedValueBindingNode = - new ResolvedValueBindingNodeImplementation(binding); + new ResolvedValueBindingNodeImplementation(binding, params.jitEnabled); const subplanParams: SubplanParams = { autobindOptions: params.autobindOptions, diff --git a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts new file mode 100644 index 000000000..467785e86 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts @@ -0,0 +1,89 @@ +import { beforeAll, describe, expect, it } from 'vitest'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildOneResolvedValueArgumentResolverOnCsp } from './buildOneResolvedValueArgumentResolverOnCsp.js'; + +class TestFixtures { + public static get node(): ResolvedValueBindingNode< + ResolvedValueBinding + > { + return { + binding: { + metadata: { + arguments: [Symbol()], + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } + + public static get params(): ResolutionParams { + return Symbol() as unknown as ResolutionParams; + } +} + +describe(buildOneResolvedValueArgumentResolverOnCsp, () => { + describe('having a resolved value binding node', () => { + describe('when called, and node.params is populated after the resolver is built', () => { + let result: unknown; + + beforeAll(() => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildOneResolvedValueArgumentResolverOnCsp( + nodeFixture, + (value: unknown): string => `factory:${String(value)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push({ + resolve: (): string => 'value-0', + } as Partial as PlanServiceNode); + + result = resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved argument', () => { + expect(result).toBe('factory:value-0'); + }); + }); + + describe('when called, and the argument resolves asynchronously', () => { + let result: unknown; + + beforeAll(async () => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildOneResolvedValueArgumentResolverOnCsp( + nodeFixture, + (value: unknown): string => `factory:${String(value)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push({ + resolve: async (): Promise => 'value-0', + } as Partial as PlanServiceNode); + + result = await resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved argument', () => { + expect(result).toBe('factory:value-0'); + }); + }); + }); +}); diff --git a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts new file mode 100644 index 000000000..28d8f370e --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts @@ -0,0 +1,39 @@ +import { isPromise } from '@inversifyjs/common'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; + +/** + * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for + * one-argument resolved value bindings. Equivalent to + * `buildOneResolvedValueArgumentResolver`, but implemented with a plain + * closure instead of the `Function` constructor, so it works in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildOneResolvedValueArgumentResolverOnCsp( + node: ResolvedValueBindingNode>, + factory: (arg: unknown) => Resolved, + resolveActivations: ( + params: ResolutionParams, + resolvedValue: Resolved, + ) => Resolved, +): (params: ResolutionParams) => Resolved { + return function resolveNode(params: ResolutionParams): Resolved { + const resolvedValue: unknown = (node.params[0] as PlanServiceNode).resolve( + params, + ); + + if (isPromise(resolvedValue)) { + return resolvedValue.then( + (resolvedValue: unknown): Resolved => + resolveActivations(params, factory(resolvedValue)), + ); + } + + return resolveActivations(params, factory(resolvedValue)); + }; +} diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts new file mode 100644 index 000000000..154a299fc --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts @@ -0,0 +1,118 @@ +import { beforeAll, describe, expect, it } from 'vitest'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildResolvedValueArgumentsResolverOnCsp } from './buildResolvedValueArgumentsResolverOnCsp.js'; +import { resolveFour } from './resolveFour.js'; +import { resolveThree } from './resolveThree.js'; +import { resolveTwo } from './resolveTwo.js'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ResolveAsyncValues = (...args: any[]) => any; + +class TestFixtures { + public static get params(): ResolutionParams { + return Symbol() as unknown as ResolutionParams; + } + + public static node( + argumentsCount: number, + ): ResolvedValueBindingNode> { + return { + binding: { + metadata: { + arguments: new Array(argumentsCount).fill(Symbol()), + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } +} + +describe(buildResolvedValueArgumentsResolverOnCsp, () => { + describe.each<[string, ResolveAsyncValues, string[]]>([ + ['two', resolveTwo, ['value-0', 'value-1']], + ['three', resolveThree, ['value-0', 'value-1', 'value-2']], + ['four', resolveFour, ['value-0', 'value-1', 'value-2', 'value-3']], + ])( + 'having %s resolved value arguments', + ( + _description: string, + resolveAsyncValues: ResolveAsyncValues, + valueFixtures: string[], + ) => { + describe('when called, and node.params is populated after the resolver is built', () => { + let result: unknown; + + beforeAll(() => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node(valueFixtures.length); + const resolveNode: (params: ResolutionParams) => Resolved = + buildResolvedValueArgumentsResolverOnCsp( + nodeFixture, + (...values: unknown[]): string[] => values as string[], + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + resolveAsyncValues, + ); + + nodeFixture.params.push( + ...valueFixtures.map( + (value: string): PlanServiceNode => + ({ + resolve: (): string => value, + }) as Partial as PlanServiceNode, + ), + ); + + result = resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments in order', () => { + expect(result).toStrictEqual(valueFixtures); + }); + }); + + describe('when called, and the arguments resolve asynchronously', () => { + let result: unknown; + + beforeAll(async () => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node(valueFixtures.length); + const resolveNode: (params: ResolutionParams) => Resolved = + buildResolvedValueArgumentsResolverOnCsp( + nodeFixture, + (...values: unknown[]): string[] => values as string[], + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + resolveAsyncValues, + ); + + nodeFixture.params.push( + ...valueFixtures.map( + (value: string): PlanServiceNode => + ({ + resolve: async (): Promise => value, + }) as Partial as PlanServiceNode, + ), + ); + + result = await resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments in order', () => { + expect(result).toStrictEqual(valueFixtures); + }); + }); + }, + ); +}); diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts new file mode 100644 index 000000000..b4720b34a --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts @@ -0,0 +1,42 @@ +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; + +/** + * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for + * resolved value bindings with two or more arguments. Equivalent to + * `buildResolvedValueArgumentsResolver`, but implemented with a plain + * closure instead of the `Function` constructor, so it works in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildResolvedValueArgumentsResolverOnCsp( + node: ResolvedValueBindingNode>, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + factory: (...args: any[]) => Resolved, + resolveActivations: ( + params: ResolutionParams, + resolvedValue: Resolved, + ) => Resolved, + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type + resolveAsyncValues: Function, +): (params: ResolutionParams) => Resolved { + const argumentsCount: number = node.binding.metadata.arguments.length; + + return function resolveNode(params: ResolutionParams): Resolved { + const values: unknown[] = new Array(argumentsCount); + + for (let index: number = 0; index < argumentsCount; index++) { + values[index] = (node.params[index] as PlanServiceNode).resolve(params); + } + + function build(...resolvedValues: unknown[]): Resolved { + return resolveActivations(params, factory(...resolvedValues)); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return + return resolveAsyncValues(...values, build); + }; +} diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts index 23a2c3392..404217507 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts @@ -10,8 +10,11 @@ import { type ResolutionParams } from '../../resolution/models/ResolutionParams. import { type Resolved } from '../../resolution/models/Resolved.js'; import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; import { buildOneResolvedValueArgumentResolver } from './buildOneResolvedValueArgumentResolver.js'; +import { buildOneResolvedValueArgumentResolverOnCsp } from './buildOneResolvedValueArgumentResolverOnCsp.js'; import { buildResolvedValueArgumentsResolver } from './buildResolvedValueArgumentsResolver.js'; +import { buildResolvedValueArgumentsResolverOnCsp } from './buildResolvedValueArgumentsResolverOnCsp.js'; import { buildZeroResolvedValueArgumentsResolver } from './buildZeroResolvedValueArgumentsResolver.js'; +import { buildZeroResolvedValueArgumentsResolverOnCsp } from './buildZeroResolvedValueArgumentsResolverOnCsp.js'; import { resolveFour } from './resolveFour.js'; import { resolveThree } from './resolveThree.js'; import { resolveTwo } from './resolveTwo.js'; @@ -36,6 +39,7 @@ const resolveScopedResolvedValueBindingNode: ( function buildSimpleResolvedValueBindingNodeResolver( node: ResolvedValueBindingNode>, + jitEnabled: boolean, ): (params: ResolutionParams) => Resolved { // eslint-disable-next-line @typescript-eslint/no-explicit-any const factory: (...args: any[]) => Resolved = @@ -52,41 +56,70 @@ function buildSimpleResolvedValueBindingNodeResolver( switch (node.binding.metadata.arguments.length) { case ZERO_PARAMS: - resolveNode = buildZeroResolvedValueArgumentsResolver( - factory, - resolveActivations, - ); + resolveNode = jitEnabled + ? buildZeroResolvedValueArgumentsResolver(factory, resolveActivations) + : buildZeroResolvedValueArgumentsResolverOnCsp( + factory, + resolveActivations, + ); break; case ONE_PARAM: - resolveNode = buildOneResolvedValueArgumentResolver( - node, - factory, - resolveActivations, - ); + resolveNode = jitEnabled + ? buildOneResolvedValueArgumentResolver( + node, + factory, + resolveActivations, + ) + : buildOneResolvedValueArgumentResolverOnCsp( + node, + factory, + resolveActivations, + ); break; case TWO_PARAMS: - resolveNode = buildResolvedValueArgumentsResolver( - node, - factory, - resolveActivations, - resolveTwo, - ); + resolveNode = jitEnabled + ? buildResolvedValueArgumentsResolver( + node, + factory, + resolveActivations, + resolveTwo, + ) + : buildResolvedValueArgumentsResolverOnCsp( + node, + factory, + resolveActivations, + resolveTwo, + ); break; case THREE_PARAMS: - resolveNode = buildResolvedValueArgumentsResolver( - node, - factory, - resolveActivations, - resolveThree, - ); + resolveNode = jitEnabled + ? buildResolvedValueArgumentsResolver( + node, + factory, + resolveActivations, + resolveThree, + ) + : buildResolvedValueArgumentsResolverOnCsp( + node, + factory, + resolveActivations, + resolveThree, + ); break; case FOUR_PARAMS: - resolveNode = buildResolvedValueArgumentsResolver( - node, - factory, - resolveActivations, - resolveFour, - ); + resolveNode = jitEnabled + ? buildResolvedValueArgumentsResolver( + node, + factory, + resolveActivations, + resolveFour, + ) + : buildResolvedValueArgumentsResolverOnCsp( + node, + factory, + resolveActivations, + resolveFour, + ); break; default: resolveNode = (params: ResolutionParams): Resolved => @@ -101,9 +134,10 @@ function buildSimpleResolvedValueBindingNodeResolver( export function buildResolvedValueBindingNodeResolver( node: ResolvedValueBindingNode>, + jitEnabled: boolean, ): (params: ResolutionParams) => Resolved { if (node.binding.onActivation === undefined) { - return buildSimpleResolvedValueBindingNodeResolver(node); + return buildSimpleResolvedValueBindingNodeResolver(node, jitEnabled); } return resolveScopedResolvedValueBindingNode(node); diff --git a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts new file mode 100644 index 000000000..dab2c9f8a --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts @@ -0,0 +1,72 @@ +import { beforeAll, describe, expect, it } from 'vitest'; + +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { buildZeroResolvedValueArgumentsResolverOnCsp } from './buildZeroResolvedValueArgumentsResolverOnCsp.js'; + +class TestFixtures { + public static get params(): ResolutionParams { + return Symbol() as unknown as ResolutionParams; + } + + public static get resolvedValue(): object { + return {}; + } +} + +describe(buildZeroResolvedValueArgumentsResolverOnCsp, () => { + describe('having a factory and an activation resolver', () => { + describe('when called', () => { + let expectedResult: object; + let paramsFixture: ResolutionParams; + + let result: unknown; + + beforeAll(() => { + expectedResult = TestFixtures.resolvedValue; + paramsFixture = TestFixtures.params; + + const resolveNode: (params: ResolutionParams) => Resolved = + buildZeroResolvedValueArgumentsResolverOnCsp( + (): object => expectedResult, + ( + params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => + params === paramsFixture ? resolvedValue : {}, + ); + + result = resolveNode(paramsFixture); + }); + + it('should return the activated factory result', () => { + expect(result).toBe(expectedResult); + }); + }); + + describe('when called, and the activation resolver returns a promise', () => { + let expectedResult: object; + + let result: unknown; + + beforeAll(async () => { + expectedResult = TestFixtures.resolvedValue; + + const resolveNode: (params: ResolutionParams) => Resolved = + buildZeroResolvedValueArgumentsResolverOnCsp( + (): object => expectedResult, + async ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Promise => resolvedValue, + ); + + result = await resolveNode(TestFixtures.params); + }); + + it('should return the activated factory result', () => { + expect(result).toBe(expectedResult); + }); + }); + }); +}); diff --git a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts new file mode 100644 index 000000000..afc6143a6 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts @@ -0,0 +1,22 @@ +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; + +/** + * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for + * zero-argument resolved value bindings. Equivalent to + * `buildZeroResolvedValueArgumentsResolver`, but implemented with a plain + * closure instead of the `Function` constructor, so it works in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildZeroResolvedValueArgumentsResolverOnCsp( + factory: () => Resolved, + resolveActivations: ( + params: ResolutionParams, + resolvedValue: Resolved, + ) => Resolved, +): (params: ResolutionParams) => Resolved { + return function resolveNode(params: ResolutionParams): Resolved { + return resolveActivations(params, factory()); + }; +} diff --git a/packages/container/libraries/core/src/planning/models/ResolvedValueBindingNodeImplementation.ts b/packages/container/libraries/core/src/planning/models/ResolvedValueBindingNodeImplementation.ts index f21466c19..e7cdde6a0 100644 --- a/packages/container/libraries/core/src/planning/models/ResolvedValueBindingNodeImplementation.ts +++ b/packages/container/libraries/core/src/planning/models/ResolvedValueBindingNodeImplementation.ts @@ -12,8 +12,11 @@ export class ResolvedValueBindingNodeImplementation< public readonly params: PlanServiceNode[]; public readonly resolve: (params: ResolutionParams) => Resolved; - constructor(public readonly binding: ResolvedValueBinding) { + constructor( + public readonly binding: ResolvedValueBinding, + jitEnabled: boolean, + ) { this.params = []; - this.resolve = buildResolvedValueBindingNodeResolver(this); + this.resolve = buildResolvedValueBindingNodeResolver(this, jitEnabled); } } From f4a71d0a9555a7a330ba5a1df1a0e7c80a3079b2 Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:22:49 +0200 Subject: [PATCH 03/11] refactor: add buildTwoResolvedValueArgumentResolverOnCsp --- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 125 ++++++++++++++++++ ...ldTwoResolvedValueArgumentResolverOnCsp.ts | 42 ++++++ 2 files changed, 167 insertions(+) create mode 100644 packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts diff --git a/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts new file mode 100644 index 000000000..76881de35 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts @@ -0,0 +1,125 @@ +import { beforeAll, describe, expect, it } from 'vitest'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildTwoResolvedValueArgumentResolverOnCsp } from './buildTwoResolvedValueArgumentResolverOnCsp.js'; + +class TestFixtures { + public static get node(): ResolvedValueBindingNode< + ResolvedValueBinding + > { + return { + binding: { + metadata: { + arguments: [Symbol(), Symbol()], + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } + + public static get params(): ResolutionParams { + return Symbol() as unknown as ResolutionParams; + } +} + +function buildParamsFixture( + resolve0: () => unknown, + resolve1: () => unknown, +): PlanServiceNode[] { + return [ + { + resolve: resolve0, + } as Partial as PlanServiceNode, + { + resolve: resolve1, + } as Partial as PlanServiceNode, + ]; +} + +describe(buildTwoResolvedValueArgumentResolverOnCsp, () => { + describe('having a resolved value binding node', () => { + describe('when called, and node.params is populated after the resolver is built', () => { + let result: unknown; + + beforeAll(() => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildTwoResolvedValueArgumentResolverOnCsp( + nodeFixture, + (value0: unknown, value1: unknown): string => + `factory:${String(value0)}:${String(value1)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push( + ...buildParamsFixture( + (): string => 'value-0', + (): string => 'value-1', + ), + ); + + result = resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments', () => { + expect(result).toBe('factory:value-0:value-1'); + }); + }); + + describe.each<[string, () => unknown, () => unknown]>([ + [ + 'both arguments resolve asynchronously', + async (): Promise => 'value-0', + async (): Promise => 'value-1', + ], + [ + 'the first argument resolves asynchronously', + async (): Promise => 'value-0', + (): string => 'value-1', + ], + [ + 'the second argument resolves asynchronously', + (): string => 'value-0', + async (): Promise => 'value-1', + ], + ])( + 'when called, and %s', + (_: string, resolve0: () => unknown, resolve1: () => unknown) => { + let result: unknown; + + beforeAll(async () => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildTwoResolvedValueArgumentResolverOnCsp( + nodeFixture, + (value0: unknown, value1: unknown): string => + `factory:${String(value0)}:${String(value1)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push(...buildParamsFixture(resolve0, resolve1)); + + result = await resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments', () => { + expect(result).toBe('factory:value-0:value-1'); + }); + }, + ); + }); +}); diff --git a/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts new file mode 100644 index 000000000..48f8b6823 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts @@ -0,0 +1,42 @@ +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { resolveTwo } from './resolveTwo.js'; + +/** + * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for + * two-argument resolved value bindings. Equivalent to + * `buildResolvedValueArgumentsResolver` with `resolveTwo`, but implemented + * with a plain closure instead of the `Function` constructor, so it works in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildTwoResolvedValueArgumentResolverOnCsp( + node: ResolvedValueBindingNode>, + factory: (arg0: unknown, arg1: unknown) => Resolved, + resolveActivations: ( + params: ResolutionParams, + resolvedValue: Resolved, + ) => Resolved, +): (params: ResolutionParams) => Resolved { + return function resolveNode(params: ResolutionParams): Resolved { + const resolvedValue0: unknown = (node.params[0] as PlanServiceNode).resolve( + params, + ); + const resolvedValue1: unknown = (node.params[1] as PlanServiceNode).resolve( + params, + ); + + return resolveTwo( + resolvedValue0, + resolvedValue1, + ( + resolvedValue0: unknown, + resolvedValue1: unknown, + ): Resolved => + resolveActivations(params, factory(resolvedValue0, resolvedValue1)), + ); + }; +} From 6368b93480796efb950a3e521290bf7714746c16 Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:23:37 +0200 Subject: [PATCH 04/11] refactor: add buildThreeResolvedValueArgumentResolverOnCsp --- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 146 ++++++++++++++++++ ...ThreeResolvedValueArgumentResolverOnCsp.ts | 54 +++++++ 2 files changed, 200 insertions(+) create mode 100644 packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts diff --git a/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts new file mode 100644 index 000000000..354adea58 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts @@ -0,0 +1,146 @@ +import { beforeAll, describe, expect, it } from 'vitest'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildThreeResolvedValueArgumentResolverOnCsp } from './buildThreeResolvedValueArgumentResolverOnCsp.js'; + +class TestFixtures { + public static get node(): ResolvedValueBindingNode< + ResolvedValueBinding + > { + return { + binding: { + metadata: { + arguments: [Symbol(), Symbol(), Symbol()], + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } + + public static get params(): ResolutionParams { + return Symbol() as unknown as ResolutionParams; + } +} + +function buildParamsFixture( + resolve0: () => unknown, + resolve1: () => unknown, + resolve2: () => unknown, +): PlanServiceNode[] { + return [ + { + resolve: resolve0, + } as Partial as PlanServiceNode, + { + resolve: resolve1, + } as Partial as PlanServiceNode, + { + resolve: resolve2, + } as Partial as PlanServiceNode, + ]; +} + +describe(buildThreeResolvedValueArgumentResolverOnCsp, () => { + describe('having a resolved value binding node', () => { + describe('when called, and node.params is populated after the resolver is built', () => { + let result: unknown; + + beforeAll(() => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildThreeResolvedValueArgumentResolverOnCsp( + nodeFixture, + (value0: unknown, value1: unknown, value2: unknown): string => + `factory:${String(value0)}:${String(value1)}:${String(value2)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push( + ...buildParamsFixture( + (): string => 'value-0', + (): string => 'value-1', + (): string => 'value-2', + ), + ); + + result = resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments', () => { + expect(result).toBe('factory:value-0:value-1:value-2'); + }); + }); + + describe.each<[string, () => unknown, () => unknown, () => unknown]>([ + [ + 'all arguments resolve asynchronously', + async (): Promise => 'value-0', + async (): Promise => 'value-1', + async (): Promise => 'value-2', + ], + [ + 'the first argument resolves asynchronously', + async (): Promise => 'value-0', + (): string => 'value-1', + (): string => 'value-2', + ], + [ + 'the second argument resolves asynchronously', + (): string => 'value-0', + async (): Promise => 'value-1', + (): string => 'value-2', + ], + [ + 'the third argument resolves asynchronously', + (): string => 'value-0', + (): string => 'value-1', + async (): Promise => 'value-2', + ], + ])( + 'when called, and %s', + ( + _: string, + resolve0: () => unknown, + resolve1: () => unknown, + resolve2: () => unknown, + ) => { + let result: unknown; + + beforeAll(async () => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildThreeResolvedValueArgumentResolverOnCsp( + nodeFixture, + (value0: unknown, value1: unknown, value2: unknown): string => + `factory:${String(value0)}:${String(value1)}:${String(value2)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push( + ...buildParamsFixture(resolve0, resolve1, resolve2), + ); + + result = await resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments', () => { + expect(result).toBe('factory:value-0:value-1:value-2'); + }); + }, + ); + }); +}); diff --git a/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts new file mode 100644 index 000000000..aef94950a --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts @@ -0,0 +1,54 @@ +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { resolveThree } from './resolveThree.js'; + +/** + * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for + * three-argument resolved value bindings. Equivalent to + * `buildResolvedValueArgumentsResolver` with `resolveThree`, but implemented + * with a plain closure instead of the `Function` constructor, so it works in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildThreeResolvedValueArgumentResolverOnCsp( + node: ResolvedValueBindingNode>, + factory: ( + arg0: unknown, + arg1: unknown, + arg2: unknown, + ) => Resolved, + resolveActivations: ( + params: ResolutionParams, + resolvedValue: Resolved, + ) => Resolved, +): (params: ResolutionParams) => Resolved { + return function resolveNode(params: ResolutionParams): Resolved { + const resolvedValue0: unknown = (node.params[0] as PlanServiceNode).resolve( + params, + ); + const resolvedValue1: unknown = (node.params[1] as PlanServiceNode).resolve( + params, + ); + const resolvedValue2: unknown = (node.params[2] as PlanServiceNode).resolve( + params, + ); + + return resolveThree( + resolvedValue0, + resolvedValue1, + resolvedValue2, + ( + resolvedValue0: unknown, + resolvedValue1: unknown, + resolvedValue2: unknown, + ): Resolved => + resolveActivations( + params, + factory(resolvedValue0, resolvedValue1, resolvedValue2), + ), + ); + }; +} From f168dea04e84dedd79a684260b90a20efdd50985 Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:23:53 +0200 Subject: [PATCH 05/11] refactor: add buildFourResolvedValueArgumentResolverOnCsp --- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 175 ++++++++++++++++++ ...dFourResolvedValueArgumentResolverOnCsp.ts | 65 +++++++ 2 files changed, 240 insertions(+) create mode 100644 packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts create mode 100644 packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts diff --git a/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts new file mode 100644 index 000000000..b560c6229 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts @@ -0,0 +1,175 @@ +import { beforeAll, describe, expect, it } from 'vitest'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildFourResolvedValueArgumentResolverOnCsp } from './buildFourResolvedValueArgumentResolverOnCsp.js'; + +class TestFixtures { + public static get node(): ResolvedValueBindingNode< + ResolvedValueBinding + > { + return { + binding: { + metadata: { + arguments: [Symbol(), Symbol(), Symbol(), Symbol()], + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } + + public static get params(): ResolutionParams { + return Symbol() as unknown as ResolutionParams; + } +} + +function buildParamsFixture( + resolve0: () => unknown, + resolve1: () => unknown, + resolve2: () => unknown, + resolve3: () => unknown, +): PlanServiceNode[] { + return [ + { + resolve: resolve0, + } as Partial as PlanServiceNode, + { + resolve: resolve1, + } as Partial as PlanServiceNode, + { + resolve: resolve2, + } as Partial as PlanServiceNode, + { + resolve: resolve3, + } as Partial as PlanServiceNode, + ]; +} + +describe(buildFourResolvedValueArgumentResolverOnCsp, () => { + describe('having a resolved value binding node', () => { + describe('when called, and node.params is populated after the resolver is built', () => { + let result: unknown; + + beforeAll(() => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildFourResolvedValueArgumentResolverOnCsp( + nodeFixture, + ( + value0: unknown, + value1: unknown, + value2: unknown, + value3: unknown, + ): string => + `factory:${String(value0)}:${String(value1)}:${String(value2)}:${String(value3)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push( + ...buildParamsFixture( + (): string => 'value-0', + (): string => 'value-1', + (): string => 'value-2', + (): string => 'value-3', + ), + ); + + result = resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments', () => { + expect(result).toBe('factory:value-0:value-1:value-2:value-3'); + }); + }); + + describe.each< + [string, () => unknown, () => unknown, () => unknown, () => unknown] + >([ + [ + 'all arguments resolve asynchronously', + async (): Promise => 'value-0', + async (): Promise => 'value-1', + async (): Promise => 'value-2', + async (): Promise => 'value-3', + ], + [ + 'the first argument resolves asynchronously', + async (): Promise => 'value-0', + (): string => 'value-1', + (): string => 'value-2', + (): string => 'value-3', + ], + [ + 'the second argument resolves asynchronously', + (): string => 'value-0', + async (): Promise => 'value-1', + (): string => 'value-2', + (): string => 'value-3', + ], + [ + 'the third argument resolves asynchronously', + (): string => 'value-0', + (): string => 'value-1', + async (): Promise => 'value-2', + (): string => 'value-3', + ], + [ + 'the fourth argument resolves asynchronously', + (): string => 'value-0', + (): string => 'value-1', + (): string => 'value-2', + async (): Promise => 'value-3', + ], + ])( + 'when called, and %s', + ( + _: string, + resolve0: () => unknown, + resolve1: () => unknown, + resolve2: () => unknown, + resolve3: () => unknown, + ) => { + let result: unknown; + + beforeAll(async () => { + const nodeFixture: ResolvedValueBindingNode< + ResolvedValueBinding + > = TestFixtures.node; + const resolveNode: (params: ResolutionParams) => Resolved = + buildFourResolvedValueArgumentResolverOnCsp( + nodeFixture, + ( + value0: unknown, + value1: unknown, + value2: unknown, + value3: unknown, + ): string => + `factory:${String(value0)}:${String(value1)}:${String(value2)}:${String(value3)}`, + ( + _params: ResolutionParams, + resolvedValue: Resolved, + ): Resolved => resolvedValue, + ); + + nodeFixture.params.push( + ...buildParamsFixture(resolve0, resolve1, resolve2, resolve3), + ); + + result = await resolveNode(TestFixtures.params); + }); + + it('should call the factory with the resolved arguments', () => { + expect(result).toBe('factory:value-0:value-1:value-2:value-3'); + }); + }, + ); + }); +}); diff --git a/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts new file mode 100644 index 000000000..4d1f08ba5 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts @@ -0,0 +1,65 @@ +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type PlanServiceNode } from '../models/PlanServiceNode.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { resolveFour } from './resolveFour.js'; + +/** + * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for + * four-argument resolved value bindings. Equivalent to + * `buildResolvedValueArgumentsResolver` with `resolveFour`, but implemented + * with a plain closure instead of the `Function` constructor, so it works in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildFourResolvedValueArgumentResolverOnCsp( + node: ResolvedValueBindingNode>, + factory: ( + arg0: unknown, + arg1: unknown, + arg2: unknown, + arg3: unknown, + ) => Resolved, + resolveActivations: ( + params: ResolutionParams, + resolvedValue: Resolved, + ) => Resolved, +): (params: ResolutionParams) => Resolved { + return function resolveNode(params: ResolutionParams): Resolved { + const resolvedValue0: unknown = (node.params[0] as PlanServiceNode).resolve( + params, + ); + const resolvedValue1: unknown = (node.params[1] as PlanServiceNode).resolve( + params, + ); + const resolvedValue2: unknown = (node.params[2] as PlanServiceNode).resolve( + params, + ); + const resolvedValue3: unknown = (node.params[3] as PlanServiceNode).resolve( + params, + ); + + return resolveFour( + resolvedValue0, + resolvedValue1, + resolvedValue2, + resolvedValue3, + ( + resolvedValue0: unknown, + resolvedValue1: unknown, + resolvedValue2: unknown, + resolvedValue3: unknown, + ): Resolved => + resolveActivations( + params, + factory( + resolvedValue0, + resolvedValue1, + resolvedValue2, + resolvedValue3, + ), + ), + ); + }; +} From 96bdcb3b0cafc6f19c78e6ee22821e8382410289 Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:27:00 +0200 Subject: [PATCH 06/11] refactor: add buildNoActivationsResolvedValueBindingNodeResolver --- ...vationsResolvedValueBindingNodeResolver.ts | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts diff --git a/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts new file mode 100644 index 000000000..43e1cad06 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts @@ -0,0 +1,93 @@ +import { type ServiceIdentifier } from '@inversifyjs/common'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { resolveResolvedValueBindingNode } from '../../resolution/actions/resolveResolvedValueBindingNode.js'; +import { resolveScopedWithNoActivations } from '../../resolution/actions/resolveScopedWithNoActivations.js'; +import { resolveServiceActivations } from '../../resolution/actions/resolveServiceActivations.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildOneResolvedValueArgumentResolver } from './buildOneResolvedValueArgumentResolver.js'; +import { buildResolvedValueArgumentsResolver } from './buildResolvedValueArgumentsResolver.js'; +import { buildZeroResolvedValueArgumentsResolver } from './buildZeroResolvedValueArgumentsResolver.js'; +import { resolveFour } from './resolveFour.js'; +import { resolveThree } from './resolveThree.js'; +import { resolveTwo } from './resolveTwo.js'; + +const ZERO_PARAMS: number = 0; +const ONE_PARAM: number = 1; +const TWO_PARAMS: number = 2; +const THREE_PARAMS: number = 3; +const FOUR_PARAMS: number = 4; + +/** + * Builds a resolver for resolved value binding nodes with no binding + * activation. + * + * The resolution logic is specialized by argument arity to minimize function + * call dispatch overhead on the hottest resolution path. + */ +export function buildNoActivationsResolvedValueBindingNodeResolver( + node: ResolvedValueBindingNode>, +): (params: ResolutionParams) => Resolved { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const factory: (...args: any[]) => Resolved = + node.binding.factory; + const serviceIdentifier: ServiceIdentifier = + node.binding.serviceIdentifier; + + const resolveActivations: ( + params: ResolutionParams, + value: Resolved, + ) => Resolved = resolveServiceActivations(serviceIdentifier); + + let resolveNode: (params: ResolutionParams) => Resolved; + + switch (node.binding.metadata.arguments.length) { + case ZERO_PARAMS: + resolveNode = buildZeroResolvedValueArgumentsResolver( + factory, + resolveActivations, + ); + break; + case ONE_PARAM: + resolveNode = buildOneResolvedValueArgumentResolver( + node, + factory, + resolveActivations, + ); + break; + case TWO_PARAMS: + resolveNode = buildResolvedValueArgumentsResolver( + node, + factory, + resolveActivations, + resolveTwo, + ); + break; + case THREE_PARAMS: + resolveNode = buildResolvedValueArgumentsResolver( + node, + factory, + resolveActivations, + resolveThree, + ); + break; + case FOUR_PARAMS: + resolveNode = buildResolvedValueArgumentsResolver( + node, + factory, + resolveActivations, + resolveFour, + ); + break; + default: + resolveNode = (params: ResolutionParams): Resolved => + resolveActivations( + params, + resolveResolvedValueBindingNode(params, node), + ); + } + + return resolveScopedWithNoActivations(node.binding, resolveNode); +} From 7d551f778cc3d9f5dd825bb40cad4cc200317fef Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:27:18 +0200 Subject: [PATCH 07/11] refactor: add buildNoActivationsResolvedValueBindingNodeResolverOnCsp --- ...nsResolvedValueBindingNodeResolverOnCsp.ts | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts diff --git a/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts new file mode 100644 index 000000000..7aafb66d7 --- /dev/null +++ b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts @@ -0,0 +1,93 @@ +import { type ServiceIdentifier } from '@inversifyjs/common'; + +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; +import { resolveResolvedValueBindingNode } from '../../resolution/actions/resolveResolvedValueBindingNode.js'; +import { resolveScopedWithNoActivations } from '../../resolution/actions/resolveScopedWithNoActivations.js'; +import { resolveServiceActivations } from '../../resolution/actions/resolveServiceActivations.js'; +import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; +import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; +import { buildFourResolvedValueArgumentResolverOnCsp } from './buildFourResolvedValueArgumentResolverOnCsp.js'; +import { buildOneResolvedValueArgumentResolverOnCsp } from './buildOneResolvedValueArgumentResolverOnCsp.js'; +import { buildThreeResolvedValueArgumentResolverOnCsp } from './buildThreeResolvedValueArgumentResolverOnCsp.js'; +import { buildTwoResolvedValueArgumentResolverOnCsp } from './buildTwoResolvedValueArgumentResolverOnCsp.js'; +import { buildZeroResolvedValueArgumentsResolverOnCsp } from './buildZeroResolvedValueArgumentsResolverOnCsp.js'; + +const ZERO_PARAMS: number = 0; +const ONE_PARAM: number = 1; +const TWO_PARAMS: number = 2; +const THREE_PARAMS: number = 3; +const FOUR_PARAMS: number = 4; + +/** + * Builds a resolver for resolved value binding nodes with no binding + * activation. + * + * Unlike the JIT path, specialized CSP resolvers are implemented with plain + * closures instead of the `Function` constructor, so they work in + * environments enforcing a strict Content Security Policy (no + * `unsafe-eval`). + */ +export function buildNoActivationsResolvedValueBindingNodeResolverOnCsp< + TActivated, +>( + node: ResolvedValueBindingNode>, +): (params: ResolutionParams) => Resolved { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const factory: (...args: any[]) => Resolved = + node.binding.factory; + const serviceIdentifier: ServiceIdentifier = + node.binding.serviceIdentifier; + + const resolveActivations: ( + params: ResolutionParams, + value: Resolved, + ) => Resolved = resolveServiceActivations(serviceIdentifier); + + let resolveNode: (params: ResolutionParams) => Resolved; + + switch (node.binding.metadata.arguments.length) { + case ZERO_PARAMS: + resolveNode = buildZeroResolvedValueArgumentsResolverOnCsp( + factory, + resolveActivations, + ); + break; + case ONE_PARAM: + resolveNode = buildOneResolvedValueArgumentResolverOnCsp( + node, + factory, + resolveActivations, + ); + break; + case TWO_PARAMS: + resolveNode = buildTwoResolvedValueArgumentResolverOnCsp( + node, + factory, + resolveActivations, + ); + break; + case THREE_PARAMS: + resolveNode = buildThreeResolvedValueArgumentResolverOnCsp( + node, + factory, + resolveActivations, + ); + break; + case FOUR_PARAMS: + resolveNode = buildFourResolvedValueArgumentResolverOnCsp( + node, + factory, + resolveActivations, + ); + break; + default: + resolveNode = (params: ResolutionParams): Resolved => + resolveActivations( + params, + resolveResolvedValueBindingNode(params, node), + ); + } + + return resolveScopedWithNoActivations(node.binding, resolveNode); +} From e91ad430a2a55ddf1eb2f3125923a292c5b2a9de Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:28:00 +0200 Subject: [PATCH 08/11] refactor: update buildResolvedValueBindingNodeResolver to rely on more specific csp resolvers --- .../buildResolvedValueBindingNodeResolver.ts | 122 +----------------- 1 file changed, 7 insertions(+), 115 deletions(-) diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts index 404217507..202cd3c10 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueBindingNodeResolver.ts @@ -1,29 +1,12 @@ -import { type ServiceIdentifier } from '@inversifyjs/common'; - import { type bindingTypeValues } from '../../binding/models/BindingType.js'; import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; import { resolveResolvedValueBindingNode } from '../../resolution/actions/resolveResolvedValueBindingNode.js'; import { resolveScoped } from '../../resolution/actions/resolveScoped.js'; -import { resolveScopedWithNoActivations } from '../../resolution/actions/resolveScopedWithNoActivations.js'; -import { resolveServiceActivations } from '../../resolution/actions/resolveServiceActivations.js'; import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; import { type Resolved } from '../../resolution/models/Resolved.js'; import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; -import { buildOneResolvedValueArgumentResolver } from './buildOneResolvedValueArgumentResolver.js'; -import { buildOneResolvedValueArgumentResolverOnCsp } from './buildOneResolvedValueArgumentResolverOnCsp.js'; -import { buildResolvedValueArgumentsResolver } from './buildResolvedValueArgumentsResolver.js'; -import { buildResolvedValueArgumentsResolverOnCsp } from './buildResolvedValueArgumentsResolverOnCsp.js'; -import { buildZeroResolvedValueArgumentsResolver } from './buildZeroResolvedValueArgumentsResolver.js'; -import { buildZeroResolvedValueArgumentsResolverOnCsp } from './buildZeroResolvedValueArgumentsResolverOnCsp.js'; -import { resolveFour } from './resolveFour.js'; -import { resolveThree } from './resolveThree.js'; -import { resolveTwo } from './resolveTwo.js'; - -const ZERO_PARAMS: number = 0; -const ONE_PARAM: number = 1; -const TWO_PARAMS: number = 2; -const THREE_PARAMS: number = 3; -const FOUR_PARAMS: number = 4; +import { buildNoActivationsResolvedValueBindingNodeResolver } from './buildNoActivationsResolvedValueBindingNodeResolver.js'; +import { buildNoActivationsResolvedValueBindingNodeResolverOnCsp } from './buildNoActivationsResolvedValueBindingNodeResolverOnCsp.js'; const resolveScopedResolvedValueBindingNode: ( node: ResolvedValueBindingNode>, @@ -37,107 +20,16 @@ const resolveScopedResolvedValueBindingNode: ( ResolvedValueBindingNode> >(node, resolveResolvedValueBindingNode); -function buildSimpleResolvedValueBindingNodeResolver( - node: ResolvedValueBindingNode>, - jitEnabled: boolean, -): (params: ResolutionParams) => Resolved { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const factory: (...args: any[]) => Resolved = - node.binding.factory; - const serviceIdentifier: ServiceIdentifier = - node.binding.serviceIdentifier; - - const resolveActivations: ( - params: ResolutionParams, - value: Resolved, - ) => Resolved = resolveServiceActivations(serviceIdentifier); - - let resolveNode: (params: ResolutionParams) => Resolved; - - switch (node.binding.metadata.arguments.length) { - case ZERO_PARAMS: - resolveNode = jitEnabled - ? buildZeroResolvedValueArgumentsResolver(factory, resolveActivations) - : buildZeroResolvedValueArgumentsResolverOnCsp( - factory, - resolveActivations, - ); - break; - case ONE_PARAM: - resolveNode = jitEnabled - ? buildOneResolvedValueArgumentResolver( - node, - factory, - resolveActivations, - ) - : buildOneResolvedValueArgumentResolverOnCsp( - node, - factory, - resolveActivations, - ); - break; - case TWO_PARAMS: - resolveNode = jitEnabled - ? buildResolvedValueArgumentsResolver( - node, - factory, - resolveActivations, - resolveTwo, - ) - : buildResolvedValueArgumentsResolverOnCsp( - node, - factory, - resolveActivations, - resolveTwo, - ); - break; - case THREE_PARAMS: - resolveNode = jitEnabled - ? buildResolvedValueArgumentsResolver( - node, - factory, - resolveActivations, - resolveThree, - ) - : buildResolvedValueArgumentsResolverOnCsp( - node, - factory, - resolveActivations, - resolveThree, - ); - break; - case FOUR_PARAMS: - resolveNode = jitEnabled - ? buildResolvedValueArgumentsResolver( - node, - factory, - resolveActivations, - resolveFour, - ) - : buildResolvedValueArgumentsResolverOnCsp( - node, - factory, - resolveActivations, - resolveFour, - ); - break; - default: - resolveNode = (params: ResolutionParams): Resolved => - resolveActivations( - params, - resolveResolvedValueBindingNode(params, node), - ); - } - - return resolveScopedWithNoActivations(node.binding, resolveNode); -} - export function buildResolvedValueBindingNodeResolver( node: ResolvedValueBindingNode>, jitEnabled: boolean, ): (params: ResolutionParams) => Resolved { if (node.binding.onActivation === undefined) { - return buildSimpleResolvedValueBindingNodeResolver(node, jitEnabled); + if (jitEnabled) { + return buildNoActivationsResolvedValueBindingNodeResolver(node); + } else { + return buildNoActivationsResolvedValueBindingNodeResolverOnCsp(node); + } } return resolveScopedResolvedValueBindingNode(node); From 1bbe3ed43c4c78a023942c227f5f31f301f53c5a Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 00:51:57 +0200 Subject: [PATCH 09/11] refactor: update resolver without redundant params --- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 21 ++++++------------ ...dFourResolvedValueArgumentResolverOnCsp.ts | 8 +------ ...nsResolvedValueBindingNodeResolverOnCsp.ts | 9 +------- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 3 +-- ...ldOneResolvedValueArgumentResolverOnCsp.ts | 5 ++--- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 6 ++--- ...ThreeResolvedValueArgumentResolverOnCsp.ts | 7 +----- ...ResolvedValueArgumentResolverOnCsp.spec.ts | 6 ++--- ...ldTwoResolvedValueArgumentResolverOnCsp.ts | 6 +++-- ...esolvedValueArgumentsResolverOnCsp.spec.ts | 22 ++++++++++++++++--- ...ZeroResolvedValueArgumentsResolverOnCsp.ts | 6 +++-- 11 files changed, 44 insertions(+), 55 deletions(-) diff --git a/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts index b560c6229..47e71d4ed 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.spec.ts @@ -13,6 +13,13 @@ class TestFixtures { > { return { binding: { + factory: ( + value0: unknown, + value1: unknown, + value2: unknown, + value3: unknown, + ): string => + `factory:${String(value0)}:${String(value1)}:${String(value2)}:${String(value3)}`, metadata: { arguments: [Symbol(), Symbol(), Symbol(), Symbol()], }, @@ -60,13 +67,6 @@ describe(buildFourResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildFourResolvedValueArgumentResolverOnCsp( nodeFixture, - ( - value0: unknown, - value1: unknown, - value2: unknown, - value3: unknown, - ): string => - `factory:${String(value0)}:${String(value1)}:${String(value2)}:${String(value3)}`, ( _params: ResolutionParams, resolvedValue: Resolved, @@ -146,13 +146,6 @@ describe(buildFourResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildFourResolvedValueArgumentResolverOnCsp( nodeFixture, - ( - value0: unknown, - value1: unknown, - value2: unknown, - value3: unknown, - ): string => - `factory:${String(value0)}:${String(value1)}:${String(value2)}:${String(value3)}`, ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts index 4d1f08ba5..d920eaff2 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildFourResolvedValueArgumentResolverOnCsp.ts @@ -15,12 +15,6 @@ import { resolveFour } from './resolveFour.js'; */ export function buildFourResolvedValueArgumentResolverOnCsp( node: ResolvedValueBindingNode>, - factory: ( - arg0: unknown, - arg1: unknown, - arg2: unknown, - arg3: unknown, - ) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -53,7 +47,7 @@ export function buildFourResolvedValueArgumentResolverOnCsp( ): Resolved => resolveActivations( params, - factory( + node.binding.factory( resolvedValue0, resolvedValue1, resolvedValue2, diff --git a/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts index 7aafb66d7..9a2f03c4c 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolverOnCsp.ts @@ -33,9 +33,6 @@ export function buildNoActivationsResolvedValueBindingNodeResolverOnCsp< >( node: ResolvedValueBindingNode>, ): (params: ResolutionParams) => Resolved { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const factory: (...args: any[]) => Resolved = - node.binding.factory; const serviceIdentifier: ServiceIdentifier = node.binding.serviceIdentifier; @@ -49,35 +46,31 @@ export function buildNoActivationsResolvedValueBindingNodeResolverOnCsp< switch (node.binding.metadata.arguments.length) { case ZERO_PARAMS: resolveNode = buildZeroResolvedValueArgumentsResolverOnCsp( - factory, + node, resolveActivations, ); break; case ONE_PARAM: resolveNode = buildOneResolvedValueArgumentResolverOnCsp( node, - factory, resolveActivations, ); break; case TWO_PARAMS: resolveNode = buildTwoResolvedValueArgumentResolverOnCsp( node, - factory, resolveActivations, ); break; case THREE_PARAMS: resolveNode = buildThreeResolvedValueArgumentResolverOnCsp( node, - factory, resolveActivations, ); break; case FOUR_PARAMS: resolveNode = buildFourResolvedValueArgumentResolverOnCsp( node, - factory, resolveActivations, ); break; diff --git a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts index 467785e86..3a37b7126 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.spec.ts @@ -13,6 +13,7 @@ class TestFixtures { > { return { binding: { + factory: (value: unknown): string => `factory:${String(value)}`, metadata: { arguments: [Symbol()], }, @@ -38,7 +39,6 @@ describe(buildOneResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildOneResolvedValueArgumentResolverOnCsp( nodeFixture, - (value: unknown): string => `factory:${String(value)}`, ( _params: ResolutionParams, resolvedValue: Resolved, @@ -67,7 +67,6 @@ describe(buildOneResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildOneResolvedValueArgumentResolverOnCsp( nodeFixture, - (value: unknown): string => `factory:${String(value)}`, ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts index 28d8f370e..2738c340f 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolverOnCsp.ts @@ -16,7 +16,6 @@ import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNod */ export function buildOneResolvedValueArgumentResolverOnCsp( node: ResolvedValueBindingNode>, - factory: (arg: unknown) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -30,10 +29,10 @@ export function buildOneResolvedValueArgumentResolverOnCsp( if (isPromise(resolvedValue)) { return resolvedValue.then( (resolvedValue: unknown): Resolved => - resolveActivations(params, factory(resolvedValue)), + resolveActivations(params, node.binding.factory(resolvedValue)), ); } - return resolveActivations(params, factory(resolvedValue)); + return resolveActivations(params, node.binding.factory(resolvedValue)); }; } diff --git a/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts index 354adea58..64d1d578c 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.spec.ts @@ -13,6 +13,8 @@ class TestFixtures { > { return { binding: { + factory: (value0: unknown, value1: unknown, value2: unknown): string => + `factory:${String(value0)}:${String(value1)}:${String(value2)}`, metadata: { arguments: [Symbol(), Symbol(), Symbol()], }, @@ -56,8 +58,6 @@ describe(buildThreeResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildThreeResolvedValueArgumentResolverOnCsp( nodeFixture, - (value0: unknown, value1: unknown, value2: unknown): string => - `factory:${String(value0)}:${String(value1)}:${String(value2)}`, ( _params: ResolutionParams, resolvedValue: Resolved, @@ -122,8 +122,6 @@ describe(buildThreeResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildThreeResolvedValueArgumentResolverOnCsp( nodeFixture, - (value0: unknown, value1: unknown, value2: unknown): string => - `factory:${String(value0)}:${String(value1)}:${String(value2)}`, ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts index aef94950a..e88ec5ce4 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildThreeResolvedValueArgumentResolverOnCsp.ts @@ -15,11 +15,6 @@ import { resolveThree } from './resolveThree.js'; */ export function buildThreeResolvedValueArgumentResolverOnCsp( node: ResolvedValueBindingNode>, - factory: ( - arg0: unknown, - arg1: unknown, - arg2: unknown, - ) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -47,7 +42,7 @@ export function buildThreeResolvedValueArgumentResolverOnCsp( ): Resolved => resolveActivations( params, - factory(resolvedValue0, resolvedValue1, resolvedValue2), + node.binding.factory(resolvedValue0, resolvedValue1, resolvedValue2), ), ); }; diff --git a/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts index 76881de35..efff0fa6c 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.spec.ts @@ -13,6 +13,8 @@ class TestFixtures { > { return { binding: { + factory: (value0: unknown, value1: unknown): string => + `factory:${String(value0)}:${String(value1)}`, metadata: { arguments: [Symbol(), Symbol()], }, @@ -52,8 +54,6 @@ describe(buildTwoResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildTwoResolvedValueArgumentResolverOnCsp( nodeFixture, - (value0: unknown, value1: unknown): string => - `factory:${String(value0)}:${String(value1)}`, ( _params: ResolutionParams, resolvedValue: Resolved, @@ -103,8 +103,6 @@ describe(buildTwoResolvedValueArgumentResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildTwoResolvedValueArgumentResolverOnCsp( nodeFixture, - (value0: unknown, value1: unknown): string => - `factory:${String(value0)}:${String(value1)}`, ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts index 48f8b6823..2d568009a 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildTwoResolvedValueArgumentResolverOnCsp.ts @@ -15,7 +15,6 @@ import { resolveTwo } from './resolveTwo.js'; */ export function buildTwoResolvedValueArgumentResolverOnCsp( node: ResolvedValueBindingNode>, - factory: (arg0: unknown, arg1: unknown) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -36,7 +35,10 @@ export function buildTwoResolvedValueArgumentResolverOnCsp( resolvedValue0: unknown, resolvedValue1: unknown, ): Resolved => - resolveActivations(params, factory(resolvedValue0, resolvedValue1)), + resolveActivations( + params, + node.binding.factory(resolvedValue0, resolvedValue1), + ), ); }; } diff --git a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts index dab2c9f8a..29176ec1b 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.spec.ts @@ -1,10 +1,26 @@ import { beforeAll, describe, expect, it } from 'vitest'; +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; import { buildZeroResolvedValueArgumentsResolverOnCsp } from './buildZeroResolvedValueArgumentsResolverOnCsp.js'; class TestFixtures { + public static node( + factory: () => object, + ): ResolvedValueBindingNode> { + return { + binding: { + factory, + metadata: { + arguments: [], + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } + public static get params(): ResolutionParams { return Symbol() as unknown as ResolutionParams; } @@ -15,7 +31,7 @@ class TestFixtures { } describe(buildZeroResolvedValueArgumentsResolverOnCsp, () => { - describe('having a factory and an activation resolver', () => { + describe('having a resolved value binding node and an activation resolver', () => { describe('when called', () => { let expectedResult: object; let paramsFixture: ResolutionParams; @@ -28,7 +44,7 @@ describe(buildZeroResolvedValueArgumentsResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildZeroResolvedValueArgumentsResolverOnCsp( - (): object => expectedResult, + TestFixtures.node((): object => expectedResult), ( params: ResolutionParams, resolvedValue: Resolved, @@ -54,7 +70,7 @@ describe(buildZeroResolvedValueArgumentsResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildZeroResolvedValueArgumentsResolverOnCsp( - (): object => expectedResult, + TestFixtures.node((): object => expectedResult), async ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts index afc6143a6..886628c86 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolverOnCsp.ts @@ -1,5 +1,7 @@ +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; /** * Same rationale as buildZeroConstructorArgumentsResolverOnCsp, but for @@ -10,13 +12,13 @@ import { type Resolved } from '../../resolution/models/Resolved.js'; * `unsafe-eval`). */ export function buildZeroResolvedValueArgumentsResolverOnCsp( - factory: () => Resolved, + node: ResolvedValueBindingNode>, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, ) => Resolved, ): (params: ResolutionParams) => Resolved { return function resolveNode(params: ResolutionParams): Resolved { - return resolveActivations(params, factory()); + return resolveActivations(params, node.binding.factory()); }; } From 04e5218963de3f368c66bd7ffc74f0c8dbed626e Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 01:23:37 +0200 Subject: [PATCH 10/11] refactor: update resolver without redundant params --- ...vationsResolvedValueBindingNodeResolver.ts | 9 +------- ...ldOneResolvedValueArgumentResolver.spec.ts | 3 +-- .../buildOneResolvedValueArgumentResolver.ts | 10 +++------ ...uildResolvedValueArgumentsResolver.spec.ts | 3 +-- .../buildResolvedValueArgumentsResolver.ts | 14 ++---------- ...esolvedValueArgumentsResolverOnCsp.spec.ts | 3 +-- ...uildResolvedValueArgumentsResolverOnCsp.ts | 7 +++--- ...ZeroResolvedValueArgumentsResolver.spec.ts | 22 ++++++++++++++++--- ...buildZeroResolvedValueArgumentsResolver.ts | 14 +++++++----- 9 files changed, 40 insertions(+), 45 deletions(-) diff --git a/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts index 43e1cad06..4f701084d 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildNoActivationsResolvedValueBindingNodeResolver.ts @@ -30,9 +30,6 @@ const FOUR_PARAMS: number = 4; export function buildNoActivationsResolvedValueBindingNodeResolver( node: ResolvedValueBindingNode>, ): (params: ResolutionParams) => Resolved { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const factory: (...args: any[]) => Resolved = - node.binding.factory; const serviceIdentifier: ServiceIdentifier = node.binding.serviceIdentifier; @@ -46,21 +43,19 @@ export function buildNoActivationsResolvedValueBindingNodeResolver( switch (node.binding.metadata.arguments.length) { case ZERO_PARAMS: resolveNode = buildZeroResolvedValueArgumentsResolver( - factory, + node, resolveActivations, ); break; case ONE_PARAM: resolveNode = buildOneResolvedValueArgumentResolver( node, - factory, resolveActivations, ); break; case TWO_PARAMS: resolveNode = buildResolvedValueArgumentsResolver( node, - factory, resolveActivations, resolveTwo, ); @@ -68,7 +63,6 @@ export function buildNoActivationsResolvedValueBindingNodeResolver( case THREE_PARAMS: resolveNode = buildResolvedValueArgumentsResolver( node, - factory, resolveActivations, resolveThree, ); @@ -76,7 +70,6 @@ export function buildNoActivationsResolvedValueBindingNodeResolver( case FOUR_PARAMS: resolveNode = buildResolvedValueArgumentsResolver( node, - factory, resolveActivations, resolveFour, ); diff --git a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.spec.ts index 1cef25f37..7634e72bc 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.spec.ts @@ -13,6 +13,7 @@ class TestFixtures { > { return { binding: { + factory: (value: unknown): string => `factory:${String(value)}`, metadata: { arguments: [Symbol()], }, @@ -38,7 +39,6 @@ describe(buildOneResolvedValueArgumentResolver, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildOneResolvedValueArgumentResolver( nodeFixture, - (value: unknown): string => `factory:${String(value)}`, ( _params: ResolutionParams, resolvedValue: Resolved, @@ -67,7 +67,6 @@ describe(buildOneResolvedValueArgumentResolver, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildOneResolvedValueArgumentResolver( nodeFixture, - (value: unknown): string => `factory:${String(value)}`, ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.ts index e57efdca2..240d82e3f 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildOneResolvedValueArgumentResolver.ts @@ -8,7 +8,6 @@ import { getGeneratedResolverId } from './getGeneratedResolverId.js'; export function buildOneResolvedValueArgumentResolver( node: ResolvedValueBindingNode>, - factory: (arg: unknown) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -18,7 +17,6 @@ export function buildOneResolvedValueArgumentResolver( const buildResolveNode: ( boundNode: ResolvedValueBindingNode>, - boundFactory: (arg: unknown) => Resolved, activate: ( params: ResolutionParams, resolvedValue: Resolved, @@ -27,7 +25,6 @@ export function buildOneResolvedValueArgumentResolver( // eslint-disable-next-line @typescript-eslint/no-implied-eval ) => (params: ResolutionParams) => Resolved = new Function( `node$${id}`, - `factory$${id}`, `activate$${id}`, `isPromise$${id}`, `return function resolveNode$${id}(params$${id}) { @@ -35,15 +32,14 @@ export function buildOneResolvedValueArgumentResolver( if (isPromise$${id}(resolvedValue$${id})) { return resolvedValue$${id}.then(function (resolvedValue$${id}) { - return activate$${id}(params$${id}, factory$${id}(resolvedValue$${id})); + return activate$${id}(params$${id}, node$${id}.binding.factory(resolvedValue$${id})); }); } - return activate$${id}(params$${id}, factory$${id}(resolvedValue$${id})); + return activate$${id}(params$${id}, node$${id}.binding.factory(resolvedValue$${id})); };`, ) as ( node: ResolvedValueBindingNode>, - factory: (arg: unknown) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -51,5 +47,5 @@ export function buildOneResolvedValueArgumentResolver( isPromise: (object: unknown) => object is Promise, ) => (params: ResolutionParams) => Resolved; - return buildResolveNode(node, factory, resolveActivations, isPromise); + return buildResolveNode(node, resolveActivations, isPromise); } diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.spec.ts index 327c31b74..33948c442 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.spec.ts @@ -23,6 +23,7 @@ class TestFixtures { ): ResolvedValueBindingNode> { return { binding: { + factory: (...values: unknown[]): string[] => values as string[], metadata: { arguments: new Array(argumentsCount).fill(Symbol()), }, @@ -54,7 +55,6 @@ describe(buildResolvedValueArgumentsResolver, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildResolvedValueArgumentsResolver( nodeFixture, - (...values: unknown[]): string[] => values as string[], ( _params: ResolutionParams, resolvedValue: Resolved, @@ -89,7 +89,6 @@ describe(buildResolvedValueArgumentsResolver, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildResolvedValueArgumentsResolver( nodeFixture, - (...values: unknown[]): string[] => values as string[], ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.ts index 202016d70..89b6ad586 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolver.ts @@ -6,8 +6,6 @@ import { getGeneratedResolverId } from './getGeneratedResolverId.js'; export function buildResolvedValueArgumentsResolver( node: ResolvedValueBindingNode>, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - factory: (...args: any[]) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -31,7 +29,6 @@ export function buildResolvedValueArgumentsResolver( const buildResolveNode: ( boundNode: ResolvedValueBindingNode>, - boundFactory: (...args: unknown[]) => Resolved, activate: ( params: ResolutionParams, resolvedValue: Resolved, @@ -41,7 +38,6 @@ export function buildResolvedValueArgumentsResolver( // eslint-disable-next-line @typescript-eslint/no-implied-eval ) => (params: ResolutionParams) => Resolved = new Function( `node$${id}`, - `factory$${id}`, `activate$${id}`, `resolveAsyncValues$${id}`, `return function resolveNode$${id}(params$${id}) { @@ -52,14 +48,13 @@ export function buildResolvedValueArgumentsResolver( function (${resolvedValueConcatenation}) { return activate$${id}( params$${id}, - factory$${id}(${resolvedValueConcatenation}), + node$${id}.binding.factory(${resolvedValueConcatenation}), ); }, ); }`, ) as ( node: ResolvedValueBindingNode>, - factory: (...args: unknown[]) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -68,10 +63,5 @@ export function buildResolvedValueArgumentsResolver( resolveAsyncValues: Function, ) => (params: ResolutionParams) => Resolved; - return buildResolveNode( - node, - factory, - resolveActivations, - resolveAsyncValues, - ); + return buildResolveNode(node, resolveActivations, resolveAsyncValues); } diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts index 154a299fc..8778871c5 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.spec.ts @@ -23,6 +23,7 @@ class TestFixtures { ): ResolvedValueBindingNode> { return { binding: { + factory: (...values: unknown[]): string[] => values as string[], metadata: { arguments: new Array(argumentsCount).fill(Symbol()), }, @@ -54,7 +55,6 @@ describe(buildResolvedValueArgumentsResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildResolvedValueArgumentsResolverOnCsp( nodeFixture, - (...values: unknown[]): string[] => values as string[], ( _params: ResolutionParams, resolvedValue: Resolved, @@ -89,7 +89,6 @@ describe(buildResolvedValueArgumentsResolverOnCsp, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildResolvedValueArgumentsResolverOnCsp( nodeFixture, - (...values: unknown[]): string[] => values as string[], ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts index b4720b34a..80132fba6 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildResolvedValueArgumentsResolverOnCsp.ts @@ -14,8 +14,6 @@ import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNod */ export function buildResolvedValueArgumentsResolverOnCsp( node: ResolvedValueBindingNode>, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - factory: (...args: any[]) => Resolved, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -33,7 +31,10 @@ export function buildResolvedValueArgumentsResolverOnCsp( } function build(...resolvedValues: unknown[]): Resolved { - return resolveActivations(params, factory(...resolvedValues)); + return resolveActivations( + params, + node.binding.factory(...resolvedValues), + ); } // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return diff --git a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.spec.ts b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.spec.ts index dcdc68b6f..a0d53ede1 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.spec.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.spec.ts @@ -1,10 +1,26 @@ import { beforeAll, describe, expect, it } from 'vitest'; +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; import { buildZeroResolvedValueArgumentsResolver } from './buildZeroResolvedValueArgumentsResolver.js'; class TestFixtures { + public static node( + factory: () => object, + ): ResolvedValueBindingNode> { + return { + binding: { + factory, + metadata: { + arguments: [], + }, + }, + params: [], + } as unknown as ResolvedValueBindingNode>; + } + public static get params(): ResolutionParams { return Symbol() as unknown as ResolutionParams; } @@ -15,7 +31,7 @@ class TestFixtures { } describe(buildZeroResolvedValueArgumentsResolver, () => { - describe('having a factory and an activation resolver', () => { + describe('having a resolved value binding node and an activation resolver', () => { describe('when called', () => { let expectedResult: object; let paramsFixture: ResolutionParams; @@ -28,7 +44,7 @@ describe(buildZeroResolvedValueArgumentsResolver, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildZeroResolvedValueArgumentsResolver( - (): object => expectedResult, + TestFixtures.node((): object => expectedResult), ( params: ResolutionParams, resolvedValue: Resolved, @@ -54,7 +70,7 @@ describe(buildZeroResolvedValueArgumentsResolver, () => { const resolveNode: (params: ResolutionParams) => Resolved = buildZeroResolvedValueArgumentsResolver( - (): object => expectedResult, + TestFixtures.node((): object => expectedResult), async ( _params: ResolutionParams, resolvedValue: Resolved, diff --git a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.ts b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.ts index 272fe4869..a69408bac 100644 --- a/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.ts +++ b/packages/container/libraries/core/src/planning/calculations/buildZeroResolvedValueArgumentsResolver.ts @@ -1,9 +1,11 @@ +import { type ResolvedValueBinding } from '../../binding/models/ResolvedValueBinding.js'; import { type ResolutionParams } from '../../resolution/models/ResolutionParams.js'; import { type Resolved } from '../../resolution/models/Resolved.js'; +import { type ResolvedValueBindingNode } from '../models/ResolvedValueBindingNode.js'; import { getGeneratedResolverId } from './getGeneratedResolverId.js'; export function buildZeroResolvedValueArgumentsResolver( - factory: () => Resolved, + node: ResolvedValueBindingNode>, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, @@ -12,25 +14,25 @@ export function buildZeroResolvedValueArgumentsResolver( const id: string = getGeneratedResolverId().toString(); const buildResolveNode: ( - boundFactory: () => Resolved, + boundNode: ResolvedValueBindingNode>, activate: ( params: ResolutionParams, resolvedValue: Resolved, ) => Resolved, // eslint-disable-next-line @typescript-eslint/no-implied-eval ) => (params: ResolutionParams) => Resolved = new Function( - `factory$${id}`, + `node$${id}`, `activate$${id}`, `return function resolveNode$${id}(params$${id}) { - return activate$${id}(params$${id}, factory$${id}()); + return activate$${id}(params$${id}, node$${id}.binding.factory()); };`, ) as ( - factory: () => Resolved, + node: ResolvedValueBindingNode>, resolveActivations: ( params: ResolutionParams, resolvedValue: Resolved, ) => Resolved, ) => (params: ResolutionParams) => Resolved; - return buildResolveNode(factory, resolveActivations); + return buildResolveNode(node, resolveActivations); } From 7175426eb64438a0f0fe951403603efd5a86b7a0 Mon Sep 17 00:00:00 2001 From: notaphplover Date: Fri, 17 Jul 2026 01:36:00 +0200 Subject: [PATCH 11/11] fix: update currySubplan with missing depth detection on resolve value plans --- .changeset/tough-moons-leave.md | 5 +++++ .../libraries/core/src/planning/actions/currySubplan.ts | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/tough-moons-leave.md diff --git a/.changeset/tough-moons-leave.md b/.changeset/tough-moons-leave.md new file mode 100644 index 000000000..2007a71f9 --- /dev/null +++ b/.changeset/tough-moons-leave.md @@ -0,0 +1,5 @@ +--- +"@inversifyjs/core": patch +--- + +- Updated `currySubplan` with missing depth detection on resolve value plans. diff --git a/packages/container/libraries/core/src/planning/actions/currySubplan.ts b/packages/container/libraries/core/src/planning/actions/currySubplan.ts index 601c9868e..1d1694757 100644 --- a/packages/container/libraries/core/src/planning/actions/currySubplan.ts +++ b/packages/container/libraries/core/src/planning/actions/currySubplan.ts @@ -453,6 +453,13 @@ function curryHandlePlanServiceNodeBuildFromResolvedValueElementMetadata( bindingConstraintsList: SingleImmutableLinkedList, elementMetadata: ResolvedValueElementMetadata, ): PlanServiceNode => { + if (bindingConstraintsList.length > MAX_PLAN_DEPTH) { + throw new InversifyCoreError( + InversifyCoreErrorKind.planningMaxDepthExceeded, + 'Maximum plan depth exceeded. This is likely caused by a circular dependency.', + ); + } + const getPlanOptions: GetPlanOptions | undefined = tryBuildGetPlanOptionsFromResolvedValueElementMetadata(elementMetadata);