• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nestjs / nest / dc5be4d1-3bf5-4993-af5a-2ff5a301e315

27 Oct 2025 08:31AM UTC coverage: 88.888% (-0.04%) from 88.927%
dc5be4d1-3bf5-4993-af5a-2ff5a301e315

Pull #15820

circleci

web-flow
chore(deps): update mysql docker tag to v9.5.0
Pull Request #15820: chore(deps): update mysql docker tag to v9.5.0

2758 of 3490 branches covered (79.03%)

7319 of 8234 relevant lines covered (88.89%)

16.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.27
/packages/core/injector/instance-wrapper.ts
1
import { Logger, LoggerService, Provider, Scope, Type } from '@nestjs/common';
1✔
2
import { EnhancerSubtype } from '@nestjs/common/constants';
3
import { FactoryProvider, InjectionToken } from '@nestjs/common/interfaces';
4
import { clc } from '@nestjs/common/utils/cli-colors.util';
1✔
5
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
1✔
6
import {
1✔
7
  isNil,
8
  isString,
9
  isUndefined,
10
} from '@nestjs/common/utils/shared.utils';
11
import { iterate } from 'iterare';
1✔
12
import { UuidFactory } from '../inspector/uuid-factory';
1✔
13
import { STATIC_CONTEXT } from './constants';
1✔
14
import {
1✔
15
  isClassProvider,
16
  isFactoryProvider,
17
  isValueProvider,
18
} from './helpers/provider-classifier';
19
import { Module } from './module';
20
import { SettlementSignal } from './settlement-signal';
21

22
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
1✔
23
export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id');
1✔
24

25
export interface HostComponentInfo {
26
  /**
27
   * Injection token (or class reference)
28
   */
29
  token: InjectionToken;
30
  /**
31
   * Flag that indicates whether DI subtree is durable
32
   */
33
  isTreeDurable: boolean;
34
}
35

36
export interface ContextId {
37
  readonly id: number;
38
  payload?: unknown;
39
  getParent?(info: HostComponentInfo): ContextId;
40
}
41

42
export interface InstancePerContext<T> {
43
  instance: T;
44
  isResolved?: boolean;
45
  isPending?: boolean;
46
  donePromise?: Promise<unknown>;
47
  isConstructorCalled?: boolean;
48
}
49

50
export interface PropertyMetadata {
51
  key: symbol | string;
52
  wrapper: InstanceWrapper;
53
}
54

55
interface InstanceMetadataStore {
56
  dependencies?: InstanceWrapper[];
57
  properties?: PropertyMetadata[];
58
  enhancers?: InstanceWrapper[];
59
}
60

61
export class InstanceWrapper<T = any> {
1✔
62
  public readonly name: any;
63
  public readonly token: InjectionToken;
64
  public readonly async?: boolean;
65
  public readonly host?: Module;
66
  public readonly isAlias: boolean = false;
823✔
67
  public readonly subtype?: EnhancerSubtype;
68
  public scope?: Scope = Scope.DEFAULT;
823✔
69
  public metatype: Type<T> | Function | null;
70
  public inject?: FactoryProvider['inject'] | null;
71
  public forwardRef?: boolean;
72
  public durable?: boolean;
73
  public initTime?: number;
74
  public settlementSignal?: SettlementSignal;
75

76
  private static logger: LoggerService = new Logger(InstanceWrapper.name);
1✔
77

78
  private readonly values = new WeakMap<ContextId, InstancePerContext<T>>();
823✔
79
  private readonly [INSTANCE_METADATA_SYMBOL]: InstanceMetadataStore = {};
823✔
80
  private readonly [INSTANCE_ID_SYMBOL]: string;
81
  private transientMap?:
82
    | Map<string, WeakMap<ContextId, InstancePerContext<T>>>
83
    | undefined;
84
  private isTreeStatic: boolean | undefined;
85
  private isTreeDurable: boolean | undefined;
86
  /**
87
   * The root inquirer reference. Present only if child instance wrapper
88
   * is transient and has a parent inquirer.
89
   */
90
  private rootInquirer: InstanceWrapper | undefined;
91

92
  constructor(
93
    metadata: Partial<InstanceWrapper<T>> & Partial<InstancePerContext<T>> = {},
127✔
94
  ) {
95
    this.initialize(metadata);
823✔
96
    this[INSTANCE_ID_SYMBOL] =
823✔
97
      metadata[INSTANCE_ID_SYMBOL] ?? this.generateUuid();
1,575✔
98
  }
99

100
  get id(): string {
101
    return this[INSTANCE_ID_SYMBOL];
355✔
102
  }
103

104
  set instance(value: T) {
105
    this.values.set(STATIC_CONTEXT, { instance: value });
6✔
106
  }
107

108
  get instance(): T {
109
    const instancePerContext = this.getInstanceByContextId(STATIC_CONTEXT);
174✔
110
    return instancePerContext.instance;
174✔
111
  }
112

113
  get isNotMetatype(): boolean {
114
    return !this.metatype || this.isFactory;
2✔
115
  }
116

117
  get isFactory(): boolean {
118
    return !!this.metatype && !isNil(this.inject);
1✔
119
  }
120

121
  get isTransient(): boolean {
122
    return this.scope === Scope.TRANSIENT;
375✔
123
  }
124

125
  public getInstanceByContextId(
126
    contextId: ContextId,
127
    inquirerId?: string,
128
  ): InstancePerContext<T> {
129
    if (this.scope === Scope.TRANSIENT && inquirerId) {
597✔
130
      return this.getInstanceByInquirerId(contextId, inquirerId);
27✔
131
    }
132
    const instancePerContext = this.values.get(contextId);
570✔
133
    return instancePerContext
570✔
134
      ? instancePerContext
570✔
135
      : contextId !== STATIC_CONTEXT
136
        ? this.cloneStaticInstance(contextId)
10!
137
        : {
138
            instance: null as T,
139
            isResolved: true,
140
            isPending: false,
141
          };
142
  }
143

144
  public getInstanceByInquirerId(
145
    contextId: ContextId,
146
    inquirerId: string,
147
  ): InstancePerContext<T> {
148
    let collectionPerContext = this.transientMap!.get(inquirerId);
27✔
149
    if (!collectionPerContext) {
27✔
150
      collectionPerContext = new WeakMap();
4✔
151
      this.transientMap!.set(inquirerId, collectionPerContext);
4✔
152
    }
153
    const instancePerContext = collectionPerContext.get(contextId);
27✔
154
    return instancePerContext
27✔
155
      ? instancePerContext
27✔
156
      : this.cloneTransientInstance(contextId, inquirerId);
157
  }
158

159
  public setInstanceByContextId(
160
    contextId: ContextId,
161
    value: InstancePerContext<T>,
162
    inquirerId?: string,
163
  ) {
164
    if (this.scope === Scope.TRANSIENT && inquirerId) {
835✔
165
      return this.setInstanceByInquirerId(contextId, inquirerId, value);
2✔
166
    }
167
    this.values.set(contextId, value);
833✔
168
  }
169

170
  public setInstanceByInquirerId(
171
    contextId: ContextId,
172
    inquirerId: string,
173
    value: InstancePerContext<T>,
174
  ) {
175
    let collection = this.transientMap!.get(inquirerId);
13✔
176
    if (!collection) {
13✔
177
      collection = new WeakMap();
5✔
178
      this.transientMap!.set(inquirerId, collection);
5✔
179
    }
180
    collection.set(contextId, value);
13✔
181
  }
182

183
  public removeInstanceByContextId(contextId: ContextId, inquirerId?: string) {
184
    if (this.scope === Scope.TRANSIENT && inquirerId) {
2✔
185
      return this.removeInstanceByInquirerId(contextId, inquirerId);
1✔
186
    }
187
    this.values.delete(contextId);
1✔
188
  }
189

190
  public removeInstanceByInquirerId(contextId: ContextId, inquirerId: string) {
191
    const collection = this.transientMap!.get(inquirerId);
1✔
192
    if (!collection) {
1!
193
      return;
×
194
    }
195
    collection.delete(contextId);
1✔
196
  }
197

198
  public addCtorMetadata(index: number, wrapper: InstanceWrapper) {
199
    if (!this[INSTANCE_METADATA_SYMBOL].dependencies) {
49✔
200
      this[INSTANCE_METADATA_SYMBOL].dependencies = [];
36✔
201
    }
202
    this[INSTANCE_METADATA_SYMBOL].dependencies[index] = wrapper;
49✔
203
  }
204

205
  public getCtorMetadata(): InstanceWrapper[] {
206
    return this[INSTANCE_METADATA_SYMBOL].dependencies!;
199✔
207
  }
208

209
  public addPropertiesMetadata(key: symbol | string, wrapper: InstanceWrapper) {
210
    if (!this[INSTANCE_METADATA_SYMBOL].properties) {
33✔
211
      this[INSTANCE_METADATA_SYMBOL].properties = [];
23✔
212
    }
213
    this[INSTANCE_METADATA_SYMBOL].properties.push({
33✔
214
      key,
215
      wrapper,
216
    });
217
  }
218

219
  public getPropertiesMetadata(): PropertyMetadata[] {
220
    return this[INSTANCE_METADATA_SYMBOL].properties!;
199✔
221
  }
222

223
  public addEnhancerMetadata(wrapper: InstanceWrapper) {
224
    if (!this[INSTANCE_METADATA_SYMBOL].enhancers) {
31✔
225
      this[INSTANCE_METADATA_SYMBOL].enhancers = [];
23✔
226
    }
227
    this[INSTANCE_METADATA_SYMBOL].enhancers.push(wrapper);
31✔
228
  }
229

230
  public getEnhancersMetadata(): InstanceWrapper[] {
231
    return this[INSTANCE_METADATA_SYMBOL].enhancers!;
119✔
232
  }
233

234
  public isDependencyTreeDurable(lookupRegistry: string[] = []): boolean {
176✔
235
    if (!isUndefined(this.isTreeDurable)) {
197✔
236
      return this.isTreeDurable;
3✔
237
    }
238
    if (this.scope === Scope.REQUEST) {
194✔
239
      this.isTreeDurable = this.durable === undefined ? false : this.durable;
52✔
240
      if (this.isTreeDurable) {
52✔
241
        this.printIntrospectedAsDurable();
19✔
242
      }
243
      return this.isTreeDurable;
52✔
244
    }
245
    const isStatic = this.isDependencyTreeStatic();
142✔
246
    if (isStatic) {
142✔
247
      return false;
126✔
248
    }
249

250
    const isTreeNonDurable = this.introspectDepsAttribute(
16✔
251
      (collection, registry) =>
252
        collection.some(
18✔
253
          (item: InstanceWrapper) =>
254
            !item.isDependencyTreeStatic() &&
29✔
255
            !item.isDependencyTreeDurable(registry),
256
        ),
257
      lookupRegistry,
258
    );
259
    this.isTreeDurable = !isTreeNonDurable;
16✔
260
    if (this.isTreeDurable) {
16✔
261
      this.printIntrospectedAsDurable();
4✔
262
    }
263
    return this.isTreeDurable;
16✔
264
  }
265

266
  public introspectDepsAttribute(
267
    callback: (
268
      collection: InstanceWrapper[],
269
      lookupRegistry: string[],
270
    ) => boolean,
271
    lookupRegistry: string[] = [],
×
272
  ): boolean {
273
    if (lookupRegistry.includes(this[INSTANCE_ID_SYMBOL])) {
250✔
274
      return false;
2✔
275
    }
276
    lookupRegistry = lookupRegistry.concat(this[INSTANCE_ID_SYMBOL]);
248✔
277

278
    const { dependencies, properties, enhancers } =
279
      this[INSTANCE_METADATA_SYMBOL];
248✔
280

281
    let introspectionResult = dependencies
248✔
282
      ? callback(dependencies, lookupRegistry)
248✔
283
      : false;
284

285
    if (introspectionResult || !(properties || enhancers)) {
248✔
286
      return introspectionResult;
225✔
287
    }
288
    introspectionResult = properties
23✔
289
      ? callback(
23✔
290
          properties.map(item => item.wrapper),
26✔
291
          lookupRegistry,
292
        )
293
      : false;
294
    if (introspectionResult || !enhancers) {
23✔
295
      return introspectionResult;
11✔
296
    }
297
    return enhancers ? callback(enhancers, lookupRegistry) : false;
12!
298
  }
299

300
  public isDependencyTreeStatic(lookupRegistry: string[] = []): boolean {
707✔
301
    if (!isUndefined(this.isTreeStatic)) {
757✔
302
      return this.isTreeStatic;
471✔
303
    }
304
    if (this.scope === Scope.REQUEST) {
286✔
305
      this.isTreeStatic = false;
52✔
306
      this.printIntrospectedAsRequestScoped();
52✔
307
      return this.isTreeStatic;
52✔
308
    }
309
    this.isTreeStatic = !this.introspectDepsAttribute(
234✔
310
      (collection, registry) =>
311
        collection.some(
39✔
312
          (item: InstanceWrapper) => !item.isDependencyTreeStatic(registry),
50✔
313
        ),
314
      lookupRegistry,
315
    );
316
    if (!this.isTreeStatic) {
234✔
317
      this.printIntrospectedAsRequestScoped();
24✔
318
    }
319
    return this.isTreeStatic;
234✔
320
  }
321

322
  public cloneStaticInstance(contextId: ContextId): InstancePerContext<T> {
323
    const staticInstance = this.getInstanceByContextId(STATIC_CONTEXT);
12✔
324
    if (this.isDependencyTreeStatic()) {
12✔
325
      return staticInstance;
5✔
326
    }
327
    const instancePerContext: InstancePerContext<T> = {
7✔
328
      ...staticInstance,
329
      instance: undefined!,
330
      isResolved: false,
331
      isPending: false,
332
    };
333
    if (this.isNewable()) {
7✔
334
      instancePerContext.instance = Object.create(this.metatype!.prototype);
6✔
335
    }
336
    this.setInstanceByContextId(contextId, instancePerContext);
7✔
337
    return instancePerContext;
7✔
338
  }
339

340
  public cloneTransientInstance(
341
    contextId: ContextId,
342
    inquirerId: string,
343
  ): InstancePerContext<T> {
344
    const staticInstance = this.getInstanceByContextId(STATIC_CONTEXT);
8✔
345
    const instancePerContext: InstancePerContext<T> = {
8✔
346
      ...staticInstance,
347
      instance: undefined!,
348
      isResolved: false,
349
      isPending: false,
350
    };
351
    if (this.isNewable()) {
8✔
352
      instancePerContext.instance = Object.create(this.metatype!.prototype);
6✔
353
    }
354
    this.setInstanceByInquirerId(contextId, inquirerId, instancePerContext);
8✔
355
    return instancePerContext;
8✔
356
  }
357

358
  public createPrototype(contextId: ContextId) {
359
    const host = this.getInstanceByContextId(contextId);
122✔
360
    if (!this.isNewable() || host.isResolved) {
122✔
361
      return;
51✔
362
    }
363
    return Object.create(this.metatype!.prototype);
71✔
364
  }
365

366
  public isInRequestScope(
367
    contextId: ContextId,
368
    inquirer?: InstanceWrapper,
369
  ): boolean {
370
    const isDependencyTreeStatic = this.isDependencyTreeStatic();
41✔
371

372
    return (
41✔
373
      !isDependencyTreeStatic &&
68!
374
      contextId !== STATIC_CONTEXT &&
375
      (!this.isTransient || (this.isTransient && !!inquirer))
376
    );
377
  }
378

379
  public isLazyTransient(
380
    contextId: ContextId,
381
    inquirer: InstanceWrapper | undefined,
382
  ): boolean {
383
    const isInquirerRequestScoped = !!(
35✔
384
      inquirer && !inquirer.isDependencyTreeStatic()
44✔
385
    );
386

387
    return (
35✔
388
      this.isDependencyTreeStatic() &&
75✔
389
      contextId !== STATIC_CONTEXT &&
390
      this.isTransient &&
391
      isInquirerRequestScoped
392
    );
393
  }
394

395
  public isExplicitlyRequested(
396
    contextId: ContextId,
397
    inquirer?: InstanceWrapper,
398
  ): boolean {
399
    const isSelfRequested = inquirer === this;
32✔
400
    return (
32✔
401
      this.isDependencyTreeStatic() &&
62✔
402
      contextId !== STATIC_CONTEXT &&
403
      (isSelfRequested || !!(inquirer && inquirer.scope === Scope.TRANSIENT))
2!
404
    );
405
  }
406

407
  public isStatic(
408
    contextId: ContextId,
409
    inquirer: InstanceWrapper | undefined,
410
  ): boolean {
411
    const isInquirerRequestScoped =
412
      inquirer && !inquirer.isDependencyTreeStatic();
90✔
413
    const isStaticTransient = this.isTransient && !isInquirerRequestScoped;
90✔
414
    const rootInquirer = inquirer?.getRootInquirer();
90✔
415
    return (
90✔
416
      this.isDependencyTreeStatic() &&
274!
417
      contextId === STATIC_CONTEXT &&
418
      (!this.isTransient ||
419
        (isStaticTransient && !!inquirer && !inquirer.isTransient) ||
420
        (isStaticTransient && !!rootInquirer && !rootInquirer.isTransient))
421
    );
422
  }
423

424
  public attachRootInquirer(inquirer: InstanceWrapper) {
425
    if (!this.isTransient) {
×
426
      // Only attach root inquirer if the instance wrapper is transient
427
      return;
×
428
    }
429
    this.rootInquirer = inquirer.getRootInquirer() ?? inquirer;
×
430
  }
431

432
  getRootInquirer(): InstanceWrapper | undefined {
433
    return this.rootInquirer;
20✔
434
  }
435

436
  public getStaticTransientInstances() {
437
    if (!this.transientMap) {
57✔
438
      return [];
54✔
439
    }
440
    const instances = [...this.transientMap.values()];
3✔
441
    return iterate(instances)
3✔
442
      .map(item => item.get(STATIC_CONTEXT))
3✔
443
      .filter(item => {
444
        // Only return items where constructor has been actually called
445
        // This prevents calling lifecycle hooks on non-instantiated transient services
446
        return !!(item && item.isConstructorCalled);
3✔
447
      })
448
      .toArray();
449
  }
450

451
  public mergeWith(provider: Provider) {
452
    if (isValueProvider(provider)) {
4✔
453
      this.metatype = null;
1✔
454
      this.inject = null;
1✔
455

456
      this.scope = Scope.DEFAULT;
1✔
457

458
      this.setInstanceByContextId(STATIC_CONTEXT, {
1✔
459
        instance: provider.useValue,
460
        isResolved: true,
461
        isPending: false,
462
      });
463
    } else if (isClassProvider(provider)) {
3✔
464
      this.inject = null;
1✔
465
      this.metatype = provider.useClass;
1✔
466
    } else if (isFactoryProvider(provider)) {
2!
467
      this.metatype = provider.useFactory;
2✔
468
      this.inject = provider.inject || [];
2✔
469
    }
470
  }
471

472
  private isNewable(): boolean {
473
    return isNil(this.inject) && this.metatype && this.metatype.prototype;
137✔
474
  }
475

476
  private initialize(
477
    metadata: Partial<InstanceWrapper<T>> & Partial<InstancePerContext<T>>,
478
  ) {
479
    const { instance, isResolved, ...wrapperPartial } = metadata;
823✔
480
    Object.assign(this, wrapperPartial);
823✔
481

482
    this.setInstanceByContextId(STATIC_CONTEXT, {
823✔
483
      instance: instance as T,
484
      isResolved,
485
    });
486
    this.scope === Scope.TRANSIENT && (this.transientMap = new Map());
823✔
487
  }
488

489
  private printIntrospectedAsRequestScoped() {
490
    if (!this.isDebugMode() || this.name === 'REQUEST') {
76!
491
      return;
76✔
492
    }
493
    if (isString(this.name)) {
×
494
      InstanceWrapper.logger.log(
×
495
        `${clc.cyanBright(this.name)}${clc.green(
496
          ' introspected as ',
497
        )}${clc.magentaBright('request-scoped')}`,
498
      );
499
    }
500
  }
501

502
  private printIntrospectedAsDurable() {
503
    if (!this.isDebugMode()) {
23!
504
      return;
23✔
505
    }
506
    if (isString(this.name)) {
×
507
      InstanceWrapper.logger.log(
×
508
        `${clc.cyanBright(this.name)}${clc.green(
509
          ' introspected as ',
510
        )}${clc.magentaBright('durable')}`,
511
      );
512
    }
513
  }
514

515
  private isDebugMode(): boolean {
516
    return !!process.env.NEST_DEBUG;
99✔
517
  }
518

519
  private generateUuid(): string {
520
    let key = this.name?.toString() ?? this.token?.toString();
752✔
521
    key += this.host?.name ?? '';
752✔
522

523
    return key ? UuidFactory.get(key) : randomStringGenerator();
752✔
524
  }
525
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc