fix(core): fix race condition in class dependency resolution

Fix race condition in class dependency resolution, which could otherwise
lead to undefined or null injection. This is a followup to #15405 that
fixes another case of this bug that was not taken care of in the
previous PR. In this case specifically, the staticity of the dependency
tree could be checked before all dependencies are loaded and the param /
property Barrier is passed, resulting in a potentially wrong evaluation
of the `isTreeStatic` property of the `InstanceWrapper`.

Closes #4873
This commit is contained in:
Jiri Hajek
2025-08-05 15:41:03 +02:00
parent d559b81c38
commit ab93e4b0c2
5 changed files with 113 additions and 146 deletions

View File

@@ -49,21 +49,42 @@ class TransientProvider {}
@Injectable()
class RequestProvider {}
@Injectable()
class ForeignTransientProvider {}
@Injectable()
export class Dependant {
constructor(
private readonly transientProvider: TransientProvider,
private readonly foreignTransientProvider: ForeignTransientProvider,
@Inject(RequestProvider)
private readonly requestProvider: RequestProvider,
) {}
public checkDependencies() {
expect(this.transientProvider).to.be.instanceOf(TransientProvider);
expect(this.foreignTransientProvider).to.be.instanceOf(
ForeignTransientProvider,
);
expect(this.requestProvider).to.be.instanceOf(RequestProvider);
}
}
@Global()
@Module({
providers: [
{
provide: ForeignTransientProvider,
scope: Scope.TRANSIENT,
useClass: ForeignTransientProvider,
},
],
exports: [ForeignTransientProvider],
})
export class ModuleWithForeignTransientProvider {}
@Global()
@Module({
providers: [
@@ -98,6 +119,12 @@ export class GlobalModuleWithRequestProvider {}
@Module({
imports: [
/*
* ForeginTransientProvider will be resolved quickly because its host module is imported first.
* IMPORTANT: Do not move this module, otherwise we may not catch future regressions.
*/
ModuleWithForeignTransientProvider,
GlobalModule1,
GlobalModule2,
GlobalModule3,
@@ -115,7 +142,7 @@ export class GlobalModuleWithRequestProvider {}
export class AppModule {}
describe('Many global modules', () => {
it('should inject request-scoped useFactory provider and transient-scoped useClass provider from different modules', async () => {
it('should inject request-scoped and transient-scoped providers from different modules', async () => {
const moduleBuilder = Test.createTestingModule({
imports: [AppModule],
});