@nestjs/core 12.0.0-alpha.2 → 12.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +2 -1
- package/errors/messages.js +4 -1
- package/index.d.ts +1 -1
- package/injector/injector.d.ts +28 -11
- package/injector/injector.js +138 -73
- package/injector/module-ref.js +24 -11
- package/middleware/middleware-module.js +4 -1
- package/nest-factory.d.ts +6 -2
- package/nest-factory.js +1 -0
- package/package.json +4 -4
- package/router/router-execution-context.js +6 -1
- package/router/router-response-controller.d.ts +2 -1
- package/router/router-response-controller.js +63 -29
- package/router/sse-stream.d.ts +12 -0
- package/router/sse-stream.js +42 -11
- package/injector/helpers/transient-instances.d.ts +0 -12
- package/injector/helpers/transient-instances.js +0 -28
package/Readme.md
CHANGED
|
@@ -29,7 +29,7 @@ Nest is a framework for building efficient, scalable <a href="https://nodejs.org
|
|
|
29
29
|
|
|
30
30
|
## Philosophy
|
|
31
31
|
|
|
32
|
-
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and
|
|
32
|
+
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front-end and back-end applications, giving rise to awesome projects like <a href="https://angular.dev/" target="_blank">Angular</a>, <a href="https://react.dev/" target="_blank">React</a>, and <a href="https://vuejs.org/" target="_blank">Vue</a>, which improve developer productivity and enable the construction of fast, testable, and extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers, and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
|
33
33
|
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, and loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
|
34
34
|
|
|
35
35
|
## Getting started
|
|
@@ -95,6 +95,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
|
|
95
95
|
<td align="center" valign="middle"><a href="https://crawljobs.com" target="_blank"><img src="https://nestjs.com/img/logos/crawljobs-logo.svg" width="130" valign="middle" /></a></td>
|
|
96
96
|
</tr><tr>
|
|
97
97
|
<td align="center" valign="middle"><a href="https://pandektes.com" target="_blank"><img src="https://nestjs.com/img/logos/pandektes-logo.png" width="65" valign="middle" /></a></td>
|
|
98
|
+
<td align="center" valign="middle"><a href="https://www.fintechcrafts.com/" target="_blank"><img src="https://nestjs.com/img/logos/fintechcrafts-logo.svg" width="65" valign="middle" /></a></td>
|
|
98
99
|
</tr>
|
|
99
100
|
</table>
|
|
100
101
|
|
package/errors/messages.js
CHANGED
|
@@ -96,9 +96,12 @@ For more common dependency resolution issues, see: https://docs.nestjs.com/faq/c
|
|
|
96
96
|
}
|
|
97
97
|
const dependenciesName = (dependencies || []).map(dependencyName => getDependencyName(dependencyName, '+', false));
|
|
98
98
|
dependenciesName[index] = '?';
|
|
99
|
+
const tokenFragment = !isImportTypeIssue && name !== undefined ? ` ${dependencyName}` : '';
|
|
100
|
+
const contextLabel = isImportTypeIssue ? 'current' : moduleName;
|
|
99
101
|
message += ` (`;
|
|
100
102
|
message += dependenciesName.join(', ');
|
|
101
|
-
message += `). Please make sure that the argument
|
|
103
|
+
message += `). Please make sure that the argument${tokenFragment} at index [${index}]`;
|
|
104
|
+
message += ` is available in the ${contextLabel} module.`;
|
|
102
105
|
message += potentialSolutions;
|
|
103
106
|
return message;
|
|
104
107
|
};
|
package/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export * from './metadata-scanner.js';
|
|
|
11
11
|
export * from './middleware/index.js';
|
|
12
12
|
export * from './nest-application.js';
|
|
13
13
|
export * from './nest-application-context.js';
|
|
14
|
-
export { NestFactory } from './nest-factory.js';
|
|
14
|
+
export { IEntryNestModule, NestFactory } from './nest-factory.js';
|
|
15
15
|
export * from './repl/index.js';
|
|
16
16
|
export * from './router/index.js';
|
|
17
17
|
export * from './services/index.js';
|
package/injector/injector.d.ts
CHANGED
|
@@ -40,6 +40,11 @@ export interface InjectorDependencyContext {
|
|
|
40
40
|
*/
|
|
41
41
|
dependencies?: InjectorDependency[];
|
|
42
42
|
}
|
|
43
|
+
interface ResolutionContext {
|
|
44
|
+
contextId: ContextId;
|
|
45
|
+
inquirer?: InstanceWrapper;
|
|
46
|
+
effectiveInquirerId?: string;
|
|
47
|
+
}
|
|
43
48
|
export declare class Injector {
|
|
44
49
|
private readonly options?;
|
|
45
50
|
private logger;
|
|
@@ -49,40 +54,48 @@ export declare class Injector {
|
|
|
49
54
|
* Whether to enable preview mode.
|
|
50
55
|
*/
|
|
51
56
|
preview: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether to enable deterministic graph snapshot generation.
|
|
59
|
+
*/
|
|
60
|
+
snapshot?: boolean;
|
|
52
61
|
/**
|
|
53
62
|
* Function to decorate a freshly created instance.
|
|
54
63
|
*/
|
|
55
64
|
instanceDecorator?: (target: unknown) => unknown;
|
|
56
65
|
} | undefined);
|
|
57
66
|
loadPrototype<T>({ token }: InstanceWrapper<T>, collection: Map<InjectionToken, InstanceWrapper<T>>, contextId?: ContextId): void;
|
|
58
|
-
loadInstance<T>(wrapper: InstanceWrapper<T>, collection: Map<InjectionToken, InstanceWrapper>, moduleRef: Module,
|
|
67
|
+
loadInstance<T>(wrapper: InstanceWrapper<T>, collection: Map<InjectionToken, InstanceWrapper>, moduleRef: Module, resolutionContext?: ResolutionContext): Promise<void>;
|
|
59
68
|
loadMiddleware(wrapper: InstanceWrapper, collection: Map<InjectionToken, InstanceWrapper>, moduleRef: Module, contextId?: ContextId, inquirer?: InstanceWrapper): Promise<void>;
|
|
60
69
|
loadController(wrapper: InstanceWrapper<Controller>, moduleRef: Module, contextId?: ContextId): Promise<void>;
|
|
61
70
|
loadInjectable<T = any>(wrapper: InstanceWrapper<T>, moduleRef: Module, contextId?: ContextId, inquirer?: InstanceWrapper): Promise<void>;
|
|
62
|
-
loadProvider(wrapper: InstanceWrapper<Injectable>, moduleRef: Module,
|
|
71
|
+
loadProvider(wrapper: InstanceWrapper<Injectable>, moduleRef: Module, resolutionContext?: ResolutionContext): Promise<void>;
|
|
63
72
|
applySettlementSignal<T>(instancePerContext: InstancePerContext<T>, host: InstanceWrapper<T>): SettlementSignal;
|
|
64
|
-
resolveConstructorParams<T>(wrapper: InstanceWrapper<T>, moduleRef: Module, inject: InjectorDependency[] | undefined, callback: (args: unknown[], depth?: number) => void | Promise<void>,
|
|
73
|
+
resolveConstructorParams<T>(wrapper: InstanceWrapper<T>, moduleRef: Module, inject: InjectorDependency[] | undefined, callback: (args: unknown[], depth?: number) => void | Promise<void>, resolutionContext?: ResolutionContext, parentInquirer?: InstanceWrapper): Promise<void>;
|
|
65
74
|
getClassDependencies<T>(wrapper: InstanceWrapper<T>): [InjectorDependency[], number[]];
|
|
66
75
|
getFactoryProviderDependencies<T>(wrapper: InstanceWrapper<T>): [InjectorDependency[], number[]];
|
|
67
76
|
reflectConstructorParams<T>(type: Type<T>): any[];
|
|
68
77
|
reflectOptionalParams<T>(type: Type<T>): any[];
|
|
69
78
|
reflectSelfParams<T>(type: Type<T>): any[];
|
|
70
|
-
resolveSingleParam<T>(wrapper: InstanceWrapper<T>, param: Type<any> | string | symbol, dependencyContext: InjectorDependencyContext, moduleRef: Module,
|
|
79
|
+
resolveSingleParam<T>(wrapper: InstanceWrapper<T>, param: Type<any> | string | symbol, dependencyContext: InjectorDependencyContext, moduleRef: Module, resolutionContext?: ResolutionContext, keyOrIndex?: symbol | string | number): Promise<InstanceWrapper<any>>;
|
|
71
80
|
resolveParamToken<T>(wrapper: InstanceWrapper<T>, param: Type<any> | string | symbol | ForwardReference): any;
|
|
72
|
-
resolveComponentWrapper<T>(moduleRef: Module, token: InjectionToken, dependencyContext: InjectorDependencyContext, wrapper: InstanceWrapper<T>,
|
|
73
|
-
resolveComponentHost<T>(moduleRef: Module, instanceWrapper: InstanceWrapper<T | Promise<T>>,
|
|
74
|
-
lookupComponent<T = any>(providers: Map<Function | string | symbol, InstanceWrapper>, moduleRef: Module, dependencyContext: InjectorDependencyContext, wrapper: InstanceWrapper<T>,
|
|
75
|
-
lookupComponentInParentModules<T = any>(dependencyContext: InjectorDependencyContext, moduleRef: Module, wrapper: InstanceWrapper<T>,
|
|
76
|
-
lookupComponentInImports(moduleRef: Module, name: InjectionToken, wrapper: InstanceWrapper, moduleRegistry?: Set<string>,
|
|
77
|
-
resolveProperties<T>(wrapper: InstanceWrapper<T>, moduleRef: Module, inject?: InjectorDependency[],
|
|
81
|
+
resolveComponentWrapper<T>(moduleRef: Module, token: InjectionToken, dependencyContext: InjectorDependencyContext, wrapper: InstanceWrapper<T>, resolutionContext?: ResolutionContext, keyOrIndex?: symbol | string | number): Promise<InstanceWrapper>;
|
|
82
|
+
resolveComponentHost<T>(moduleRef: Module, instanceWrapper: InstanceWrapper<T | Promise<T>>, resolutionContext?: ResolutionContext): Promise<InstanceWrapper>;
|
|
83
|
+
lookupComponent<T = any>(providers: Map<Function | string | symbol, InstanceWrapper>, moduleRef: Module, dependencyContext: InjectorDependencyContext, wrapper: InstanceWrapper<T>, resolutionContext?: ResolutionContext, keyOrIndex?: symbol | string | number): Promise<InstanceWrapper<T>>;
|
|
84
|
+
lookupComponentInParentModules<T = any>(dependencyContext: InjectorDependencyContext, moduleRef: Module, wrapper: InstanceWrapper<T>, resolutionContext?: ResolutionContext, keyOrIndex?: symbol | string | number): Promise<any>;
|
|
85
|
+
lookupComponentInImports(moduleRef: Module, name: InjectionToken, wrapper: InstanceWrapper, moduleRegistry?: Set<string>, resolutionContext?: ResolutionContext, keyOrIndex?: symbol | string | number, isTraversing?: boolean): Promise<any>;
|
|
86
|
+
resolveProperties<T>(wrapper: InstanceWrapper<T>, moduleRef: Module, inject?: InjectorDependency[], resolutionContext?: ResolutionContext, parentInquirer?: InstanceWrapper): Promise<PropertyDependency[]>;
|
|
78
87
|
reflectProperties<T>(type: Type<T>): PropertyDependency[];
|
|
79
88
|
applyProperties<T = any>(instance: T, properties: PropertyDependency[]): void;
|
|
80
|
-
instantiateClass<T = any>(instances: any[], wrapper: InstanceWrapper, targetMetatype: InstanceWrapper,
|
|
89
|
+
instantiateClass<T = any>(instances: any[], wrapper: InstanceWrapper, targetMetatype: InstanceWrapper, resolutionContext?: ResolutionContext): Promise<T>;
|
|
81
90
|
loadPerContext<T = any>(instance: T, moduleRef: Module, collection: Map<InjectionToken, InstanceWrapper>, ctx: ContextId, wrapper?: InstanceWrapper): Promise<T>;
|
|
82
91
|
loadEnhancersPerContext(wrapper: InstanceWrapper, ctx: ContextId, inquirer?: InstanceWrapper): Promise<void>;
|
|
83
92
|
loadCtorMetadata(metadata: InstanceWrapper<any>[], contextId: ContextId, inquirer?: InstanceWrapper, parentInquirer?: InstanceWrapper): Promise<any[]>;
|
|
84
93
|
loadPropertiesMetadata(metadata: PropertyMetadata[], contextId: ContextId, inquirer?: InstanceWrapper): Promise<PropertyDependency[]>;
|
|
85
94
|
private getInquirerId;
|
|
95
|
+
private createResolutionContext;
|
|
96
|
+
private getContextInquirerId;
|
|
97
|
+
private isInContext;
|
|
98
|
+
private shouldSkipProviderLoading;
|
|
86
99
|
/**
|
|
87
100
|
* For nested TRANSIENT dependencies (TRANSIENT -> TRANSIENT) in non-static contexts,
|
|
88
101
|
* returns parentInquirer to ensure each parent TRANSIENT gets its own instance.
|
|
@@ -91,6 +104,9 @@ export declare class Injector {
|
|
|
91
104
|
* For non-TRANSIENT -> TRANSIENT, returns inquirer (current wrapper being created).
|
|
92
105
|
*/
|
|
93
106
|
private getEffectiveInquirer;
|
|
107
|
+
private getEffectiveInquirerId;
|
|
108
|
+
private getStaticTransientResolutionContext;
|
|
109
|
+
private getEffectiveResolutionContext;
|
|
94
110
|
private resolveScopedComponentHost;
|
|
95
111
|
private isInquirerRequest;
|
|
96
112
|
private isInquirer;
|
|
@@ -103,3 +119,4 @@ export declare class Injector {
|
|
|
103
119
|
private getContextId;
|
|
104
120
|
private getNowTimestamp;
|
|
105
121
|
}
|
|
122
|
+
export {};
|
package/injector/injector.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger, } from '@nestjs/common';
|
|
1
|
+
import { Logger, Scope, } from '@nestjs/common';
|
|
2
2
|
import { OPTIONAL_DEPS_METADATA, OPTIONAL_PROPERTY_DEPS_METADATA, PARAMTYPES_METADATA, PROPERTY_DEPS_METADATA, SELF_DECLARED_DEPS_METADATA, clc, isFunction, isNil, isObject, isString, isSymbol, isUndefined, } from '@nestjs/common/internal';
|
|
3
3
|
import { iterate } from 'iterare';
|
|
4
4
|
import { performance } from 'perf_hooks';
|
|
@@ -35,12 +35,13 @@ export class Injector {
|
|
|
35
35
|
collection.set(token, wrapper);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
async loadInstance(wrapper, collection, moduleRef,
|
|
39
|
-
const inquirerId = this.
|
|
40
|
-
const instanceHost = wrapper.getInstanceByContextId(this.getContextId(contextId, wrapper), inquirerId);
|
|
38
|
+
async loadInstance(wrapper, collection, moduleRef, resolutionContext = { contextId: STATIC_CONTEXT }) {
|
|
39
|
+
const inquirerId = this.getContextInquirerId(resolutionContext);
|
|
40
|
+
const instanceHost = wrapper.getInstanceByContextId(this.getContextId(resolutionContext.contextId, wrapper), inquirerId);
|
|
41
41
|
if (instanceHost.isPending) {
|
|
42
42
|
const settlementSignal = wrapper.settlementSignal;
|
|
43
|
-
if (inquirer &&
|
|
43
|
+
if (resolutionContext.inquirer &&
|
|
44
|
+
settlementSignal?.isCycle(resolutionContext.inquirer.id)) {
|
|
44
45
|
throw new CircularDependencyException(`"${wrapper.name}"`);
|
|
45
46
|
}
|
|
46
47
|
return instanceHost.donePromise.then((err) => {
|
|
@@ -61,18 +62,19 @@ export class Injector {
|
|
|
61
62
|
}
|
|
62
63
|
try {
|
|
63
64
|
const t0 = this.getNowTimestamp();
|
|
65
|
+
const localResolutionContext = this.createResolutionContext(resolutionContext.contextId, wrapper, inquirerId);
|
|
64
66
|
const callback = async (instances, depth = 0) => {
|
|
65
|
-
const properties = await this.resolveProperties(wrapper, moduleRef, inject,
|
|
66
|
-
const instance = await this.instantiateClass(instances, wrapper, targetWrapper,
|
|
67
|
+
const properties = await this.resolveProperties(wrapper, moduleRef, inject, localResolutionContext, resolutionContext.inquirer);
|
|
68
|
+
const instance = await this.instantiateClass(instances, wrapper, targetWrapper, wrapper.isTransient ? localResolutionContext : resolutionContext);
|
|
67
69
|
this.applyProperties(instance, properties);
|
|
68
70
|
wrapper.hierarchyLevel = depth + 1;
|
|
69
71
|
wrapper.initTime = this.getNowTimestamp() - t0;
|
|
70
72
|
settlementSignal.complete();
|
|
71
73
|
};
|
|
72
|
-
await this.resolveConstructorParams(wrapper, moduleRef, inject, callback,
|
|
74
|
+
await this.resolveConstructorParams(wrapper, moduleRef, inject, callback, localResolutionContext, resolutionContext.inquirer);
|
|
73
75
|
}
|
|
74
76
|
catch (err) {
|
|
75
|
-
wrapper.removeInstanceByContextId(this.getContextId(contextId, wrapper), inquirerId);
|
|
77
|
+
wrapper.removeInstanceByContextId(this.getContextId(resolutionContext.contextId, wrapper), inquirerId);
|
|
76
78
|
settlementSignal.error(err);
|
|
77
79
|
throw err;
|
|
78
80
|
}
|
|
@@ -84,21 +86,24 @@ export class Injector {
|
|
|
84
86
|
return;
|
|
85
87
|
}
|
|
86
88
|
targetWrapper.instance = Object.create(metatype.prototype);
|
|
87
|
-
await this.loadInstance(wrapper, collection, moduleRef, contextId, inquirer || wrapper);
|
|
89
|
+
await this.loadInstance(wrapper, collection, moduleRef, this.createResolutionContext(contextId, inquirer || wrapper));
|
|
88
90
|
}
|
|
89
91
|
async loadController(wrapper, moduleRef, contextId = STATIC_CONTEXT) {
|
|
90
92
|
const controllers = moduleRef.controllers;
|
|
91
|
-
await this.loadInstance(wrapper, controllers, moduleRef, contextId, wrapper);
|
|
93
|
+
await this.loadInstance(wrapper, controllers, moduleRef, this.createResolutionContext(contextId, wrapper));
|
|
92
94
|
await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
|
|
93
95
|
}
|
|
94
96
|
async loadInjectable(wrapper, moduleRef, contextId = STATIC_CONTEXT, inquirer) {
|
|
95
97
|
const injectables = moduleRef.injectables;
|
|
96
|
-
await this.loadInstance(wrapper, injectables, moduleRef, contextId, inquirer);
|
|
98
|
+
await this.loadInstance(wrapper, injectables, moduleRef, this.createResolutionContext(contextId, inquirer));
|
|
97
99
|
}
|
|
98
|
-
async loadProvider(wrapper, moduleRef,
|
|
100
|
+
async loadProvider(wrapper, moduleRef, resolutionContext = { contextId: STATIC_CONTEXT }) {
|
|
101
|
+
if (this.shouldSkipProviderLoading(wrapper, resolutionContext)) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
99
104
|
const providers = moduleRef.providers;
|
|
100
|
-
await this.loadInstance(wrapper, providers, moduleRef,
|
|
101
|
-
await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
|
|
105
|
+
await this.loadInstance(wrapper, providers, moduleRef, resolutionContext);
|
|
106
|
+
await this.loadEnhancersPerContext(wrapper, resolutionContext.contextId, wrapper);
|
|
102
107
|
}
|
|
103
108
|
applySettlementSignal(instancePerContext, host) {
|
|
104
109
|
const settlementSignal = new SettlementSignal();
|
|
@@ -107,10 +112,10 @@ export class Injector {
|
|
|
107
112
|
host.settlementSignal = settlementSignal;
|
|
108
113
|
return settlementSignal;
|
|
109
114
|
}
|
|
110
|
-
async resolveConstructorParams(wrapper, moduleRef, inject, callback,
|
|
115
|
+
async resolveConstructorParams(wrapper, moduleRef, inject, callback, resolutionContext = { contextId: STATIC_CONTEXT }, parentInquirer) {
|
|
111
116
|
const metadata = wrapper.getCtorMetadata();
|
|
112
|
-
if (metadata && contextId !== STATIC_CONTEXT) {
|
|
113
|
-
const deps = await this.loadCtorMetadata(metadata, contextId, inquirer, parentInquirer);
|
|
117
|
+
if (metadata && resolutionContext.contextId !== STATIC_CONTEXT) {
|
|
118
|
+
const deps = await this.loadCtorMetadata(metadata, resolutionContext.contextId, resolutionContext.inquirer, parentInquirer);
|
|
114
119
|
return callback(deps);
|
|
115
120
|
}
|
|
116
121
|
const isFactoryProvider = !isNil(inject);
|
|
@@ -129,25 +134,26 @@ export class Injector {
|
|
|
129
134
|
paramBarrier.signal();
|
|
130
135
|
return parentInquirer && parentInquirer.instance;
|
|
131
136
|
}
|
|
132
|
-
if (inquirer?.isTransient && parentInquirer) {
|
|
137
|
+
if (resolutionContext.inquirer?.isTransient && parentInquirer) {
|
|
133
138
|
// When `inquirer` is transient too, inherit the parent inquirer
|
|
134
139
|
// This is required to ensure that transient providers are only resolved
|
|
135
140
|
// when requested
|
|
136
|
-
inquirer.attachRootInquirer(parentInquirer);
|
|
141
|
+
resolutionContext.inquirer.attachRootInquirer(parentInquirer);
|
|
137
142
|
}
|
|
138
|
-
const
|
|
143
|
+
const nestedResolutionContext = this.getStaticTransientResolutionContext(resolutionContext, parentInquirer);
|
|
144
|
+
const paramWrapper = await this.resolveSingleParam(wrapper, param, { index, dependencies }, moduleRef, nestedResolutionContext, index);
|
|
139
145
|
/*
|
|
140
146
|
* Ensure that all instance wrappers are resolved at this point before we continue.
|
|
141
147
|
* Otherwise the staticity of `wrapper`'s dependency tree may be evaluated incorrectly
|
|
142
148
|
* and result in undefined / null injection.
|
|
143
149
|
*/
|
|
144
150
|
await paramBarrier.signalAndWait();
|
|
145
|
-
const
|
|
146
|
-
const paramWrapperWithInstance = await this.resolveComponentHost(moduleRef, paramWrapper,
|
|
151
|
+
const effectiveResolutionContext = this.getEffectiveResolutionContext(paramWrapper, resolutionContext, parentInquirer);
|
|
152
|
+
const paramWrapperWithInstance = await this.resolveComponentHost(moduleRef, paramWrapper, effectiveResolutionContext);
|
|
147
153
|
if (paramWrapperWithInstance.hierarchyLevel > depth) {
|
|
148
154
|
depth = paramWrapperWithInstance.hierarchyLevel;
|
|
149
155
|
}
|
|
150
|
-
const instanceHost = paramWrapperWithInstance.getInstanceByContextId(this.getContextId(contextId, paramWrapperWithInstance),
|
|
156
|
+
const instanceHost = paramWrapperWithInstance.getInstanceByContextId(this.getContextId(effectiveResolutionContext.contextId, paramWrapperWithInstance), effectiveResolutionContext.effectiveInquirerId);
|
|
151
157
|
if (!instanceHost.isResolved && !paramWrapperWithInstance.forwardRef) {
|
|
152
158
|
isResolved = false;
|
|
153
159
|
}
|
|
@@ -211,7 +217,7 @@ export class Injector {
|
|
|
211
217
|
];
|
|
212
218
|
const selfParams = this.reflectSelfParams(type);
|
|
213
219
|
selfParams.forEach(({ index, param }) => (paramtypes[index] = param));
|
|
214
|
-
return paramtypes;
|
|
220
|
+
return Array.from(paramtypes);
|
|
215
221
|
}
|
|
216
222
|
reflectOptionalParams(type) {
|
|
217
223
|
return Reflect.getOwnMetadata(OPTIONAL_DEPS_METADATA, type) || [];
|
|
@@ -219,13 +225,13 @@ export class Injector {
|
|
|
219
225
|
reflectSelfParams(type) {
|
|
220
226
|
return Reflect.getMetadata(SELF_DECLARED_DEPS_METADATA, type) || [];
|
|
221
227
|
}
|
|
222
|
-
async resolveSingleParam(wrapper, param, dependencyContext, moduleRef,
|
|
228
|
+
async resolveSingleParam(wrapper, param, dependencyContext, moduleRef, resolutionContext = { contextId: STATIC_CONTEXT }, keyOrIndex) {
|
|
223
229
|
if (isUndefined(param)) {
|
|
224
230
|
this.logger.log('Nest encountered an undefined dependency. This may be due to a circular import or a missing dependency declaration.');
|
|
225
231
|
throw new UndefinedDependencyException(wrapper.name, dependencyContext, moduleRef);
|
|
226
232
|
}
|
|
227
233
|
const token = this.resolveParamToken(wrapper, param);
|
|
228
|
-
return this.resolveComponentWrapper(moduleRef, token, dependencyContext, wrapper,
|
|
234
|
+
return this.resolveComponentWrapper(moduleRef, token, dependencyContext, wrapper, resolutionContext, keyOrIndex);
|
|
229
235
|
}
|
|
230
236
|
resolveParamToken(wrapper, param) {
|
|
231
237
|
if (typeof param === 'object' && 'forwardRef' in param) {
|
|
@@ -234,22 +240,22 @@ export class Injector {
|
|
|
234
240
|
}
|
|
235
241
|
return param;
|
|
236
242
|
}
|
|
237
|
-
async resolveComponentWrapper(moduleRef, token, dependencyContext, wrapper,
|
|
238
|
-
this.printResolvingDependenciesLog(token, inquirer);
|
|
243
|
+
async resolveComponentWrapper(moduleRef, token, dependencyContext, wrapper, resolutionContext = { contextId: STATIC_CONTEXT }, keyOrIndex) {
|
|
244
|
+
this.printResolvingDependenciesLog(token, resolutionContext.inquirer);
|
|
239
245
|
this.printLookingForProviderLog(token, moduleRef);
|
|
240
246
|
const providers = moduleRef.providers;
|
|
241
|
-
return this.lookupComponent(providers, moduleRef, { ...dependencyContext, name: token }, wrapper,
|
|
247
|
+
return this.lookupComponent(providers, moduleRef, { ...dependencyContext, name: token }, wrapper, resolutionContext, keyOrIndex);
|
|
242
248
|
}
|
|
243
|
-
async resolveComponentHost(moduleRef, instanceWrapper,
|
|
244
|
-
const inquirerId = this.
|
|
245
|
-
const instanceHost = instanceWrapper.getInstanceByContextId(this.getContextId(contextId, instanceWrapper), inquirerId);
|
|
249
|
+
async resolveComponentHost(moduleRef, instanceWrapper, resolutionContext = { contextId: STATIC_CONTEXT }) {
|
|
250
|
+
const inquirerId = this.getContextInquirerId(resolutionContext);
|
|
251
|
+
const instanceHost = instanceWrapper.getInstanceByContextId(this.getContextId(resolutionContext.contextId, instanceWrapper), inquirerId);
|
|
246
252
|
if (!instanceHost.isResolved && !instanceWrapper.forwardRef) {
|
|
247
|
-
inquirer?.settlementSignal?.insertRef(instanceWrapper.id);
|
|
248
|
-
await this.loadProvider(instanceWrapper, instanceWrapper.host ?? moduleRef,
|
|
253
|
+
resolutionContext.inquirer?.settlementSignal?.insertRef(instanceWrapper.id);
|
|
254
|
+
await this.loadProvider(instanceWrapper, instanceWrapper.host ?? moduleRef, resolutionContext);
|
|
249
255
|
}
|
|
250
256
|
else if (!instanceHost.isResolved &&
|
|
251
257
|
instanceWrapper.forwardRef &&
|
|
252
|
-
(contextId !== STATIC_CONTEXT || !!inquirerId)) {
|
|
258
|
+
(resolutionContext.contextId !== STATIC_CONTEXT || !!inquirerId)) {
|
|
253
259
|
/**
|
|
254
260
|
* When circular dependency has been detected between
|
|
255
261
|
* either request/transient providers, we have to asynchronously
|
|
@@ -259,19 +265,19 @@ export class Injector {
|
|
|
259
265
|
*/
|
|
260
266
|
instanceHost.donePromise &&
|
|
261
267
|
void instanceHost.donePromise
|
|
262
|
-
.then(() => this.loadProvider(instanceWrapper, moduleRef,
|
|
268
|
+
.then(() => this.loadProvider(instanceWrapper, moduleRef, resolutionContext))
|
|
263
269
|
.catch(err => {
|
|
264
270
|
instanceWrapper.settlementSignal?.error(err);
|
|
265
271
|
});
|
|
266
272
|
}
|
|
267
273
|
if (instanceWrapper.async) {
|
|
268
|
-
const host = instanceWrapper.getInstanceByContextId(this.getContextId(contextId, instanceWrapper), inquirerId);
|
|
274
|
+
const host = instanceWrapper.getInstanceByContextId(this.getContextId(resolutionContext.contextId, instanceWrapper), inquirerId);
|
|
269
275
|
host.instance = await host.instance;
|
|
270
|
-
instanceWrapper.setInstanceByContextId(contextId, host, inquirerId);
|
|
276
|
+
instanceWrapper.setInstanceByContextId(resolutionContext.contextId, host, inquirerId);
|
|
271
277
|
}
|
|
272
278
|
return instanceWrapper;
|
|
273
279
|
}
|
|
274
|
-
async lookupComponent(providers, moduleRef, dependencyContext, wrapper,
|
|
280
|
+
async lookupComponent(providers, moduleRef, dependencyContext, wrapper, resolutionContext = { contextId: STATIC_CONTEXT }, keyOrIndex) {
|
|
275
281
|
const token = wrapper.token || wrapper.name;
|
|
276
282
|
const { name } = dependencyContext;
|
|
277
283
|
if (wrapper && token === name) {
|
|
@@ -283,16 +289,16 @@ export class Injector {
|
|
|
283
289
|
this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapper);
|
|
284
290
|
return instanceWrapper;
|
|
285
291
|
}
|
|
286
|
-
return this.lookupComponentInParentModules(dependencyContext, moduleRef, wrapper,
|
|
292
|
+
return this.lookupComponentInParentModules(dependencyContext, moduleRef, wrapper, resolutionContext, keyOrIndex);
|
|
287
293
|
}
|
|
288
|
-
async lookupComponentInParentModules(dependencyContext, moduleRef, wrapper,
|
|
289
|
-
const instanceWrapper = await this.lookupComponentInImports(moduleRef, dependencyContext.name, wrapper, new Set(),
|
|
294
|
+
async lookupComponentInParentModules(dependencyContext, moduleRef, wrapper, resolutionContext = { contextId: STATIC_CONTEXT }, keyOrIndex) {
|
|
295
|
+
const instanceWrapper = await this.lookupComponentInImports(moduleRef, dependencyContext.name, wrapper, new Set(), resolutionContext, keyOrIndex);
|
|
290
296
|
if (isNil(instanceWrapper)) {
|
|
291
297
|
throw new UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef, { id: wrapper.id });
|
|
292
298
|
}
|
|
293
299
|
return instanceWrapper;
|
|
294
300
|
}
|
|
295
|
-
async lookupComponentInImports(moduleRef, name, wrapper, moduleRegistry = new Set(),
|
|
301
|
+
async lookupComponentInImports(moduleRef, name, wrapper, moduleRegistry = new Set(), resolutionContext = { contextId: STATIC_CONTEXT }, keyOrIndex, isTraversing) {
|
|
296
302
|
let instanceWrapperRef = null;
|
|
297
303
|
const imports = moduleRef.imports || new Set();
|
|
298
304
|
const identity = (item) => item;
|
|
@@ -309,7 +315,7 @@ export class Injector {
|
|
|
309
315
|
moduleRegistry.add(relatedModule.id);
|
|
310
316
|
const { providers, exports } = relatedModule;
|
|
311
317
|
if (!exports.has(name) || !providers.has(name)) {
|
|
312
|
-
const instanceRef = await this.lookupComponentInImports(relatedModule, name, wrapper, moduleRegistry,
|
|
318
|
+
const instanceRef = await this.lookupComponentInImports(relatedModule, name, wrapper, moduleRegistry, resolutionContext, keyOrIndex, true);
|
|
313
319
|
if (instanceRef) {
|
|
314
320
|
this.addDependencyMetadata(keyOrIndex, wrapper, instanceRef);
|
|
315
321
|
return instanceRef;
|
|
@@ -319,8 +325,8 @@ export class Injector {
|
|
|
319
325
|
this.printFoundInModuleLog(name, relatedModule);
|
|
320
326
|
instanceWrapperRef = providers.get(name);
|
|
321
327
|
this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapperRef);
|
|
322
|
-
const inquirerId = this.
|
|
323
|
-
const instanceHost = instanceWrapperRef.getInstanceByContextId(this.getContextId(contextId, instanceWrapperRef), inquirerId);
|
|
328
|
+
const inquirerId = this.getContextInquirerId(resolutionContext);
|
|
329
|
+
const instanceHost = instanceWrapperRef.getInstanceByContextId(this.getContextId(resolutionContext.contextId, instanceWrapperRef), inquirerId);
|
|
324
330
|
if (!instanceHost.isResolved && !instanceWrapperRef.forwardRef) {
|
|
325
331
|
/*
|
|
326
332
|
* Provider will be loaded shortly in resolveComponentHost() once we pass the current
|
|
@@ -332,13 +338,13 @@ export class Injector {
|
|
|
332
338
|
}
|
|
333
339
|
return instanceWrapperRef;
|
|
334
340
|
}
|
|
335
|
-
async resolveProperties(wrapper, moduleRef, inject,
|
|
341
|
+
async resolveProperties(wrapper, moduleRef, inject, resolutionContext = { contextId: STATIC_CONTEXT }, parentInquirer) {
|
|
336
342
|
if (!isNil(inject)) {
|
|
337
343
|
return [];
|
|
338
344
|
}
|
|
339
345
|
const metadata = wrapper.getPropertiesMetadata();
|
|
340
|
-
if (metadata && contextId !== STATIC_CONTEXT) {
|
|
341
|
-
return this.loadPropertiesMetadata(metadata, contextId, inquirer);
|
|
346
|
+
if (metadata && resolutionContext.contextId !== STATIC_CONTEXT) {
|
|
347
|
+
return this.loadPropertiesMetadata(metadata, resolutionContext.contextId, resolutionContext.inquirer);
|
|
342
348
|
}
|
|
343
349
|
const properties = this.reflectProperties(wrapper.metatype);
|
|
344
350
|
const propertyBarrier = new Barrier(properties.length);
|
|
@@ -355,19 +361,20 @@ export class Injector {
|
|
|
355
361
|
propertyBarrier.signal();
|
|
356
362
|
return parentInquirer && parentInquirer.instance;
|
|
357
363
|
}
|
|
358
|
-
const
|
|
364
|
+
const nestedResolutionContext = this.getStaticTransientResolutionContext(resolutionContext, parentInquirer);
|
|
365
|
+
const paramWrapper = await this.resolveSingleParam(wrapper, item.name, dependencyContext, moduleRef, nestedResolutionContext, item.key);
|
|
359
366
|
/*
|
|
360
367
|
* Ensure that all instance wrappers are resolved at this point before we continue.
|
|
361
368
|
* Otherwise the staticity of `wrapper`'s dependency tree may be evaluated incorrectly
|
|
362
369
|
* and result in undefined / null injection.
|
|
363
370
|
*/
|
|
364
371
|
await propertyBarrier.signalAndWait();
|
|
365
|
-
const
|
|
366
|
-
const paramWrapperWithInstance = await this.resolveComponentHost(moduleRef, paramWrapper,
|
|
372
|
+
const effectivePropertyResolutionContext = this.getEffectiveResolutionContext(paramWrapper, resolutionContext, parentInquirer);
|
|
373
|
+
const paramWrapperWithInstance = await this.resolveComponentHost(moduleRef, paramWrapper, effectivePropertyResolutionContext);
|
|
367
374
|
if (!paramWrapperWithInstance) {
|
|
368
375
|
return undefined;
|
|
369
376
|
}
|
|
370
|
-
const instanceHost = paramWrapperWithInstance.getInstanceByContextId(this.getContextId(contextId, paramWrapperWithInstance),
|
|
377
|
+
const instanceHost = paramWrapperWithInstance.getInstanceByContextId(this.getContextId(effectivePropertyResolutionContext.contextId, paramWrapperWithInstance), effectivePropertyResolutionContext.effectiveInquirerId);
|
|
371
378
|
return instanceHost.instance;
|
|
372
379
|
}
|
|
373
380
|
catch (err) {
|
|
@@ -405,14 +412,11 @@ export class Injector {
|
|
|
405
412
|
.filter(item => !isNil(item.instance))
|
|
406
413
|
.forEach(item => (instance[item.key] = item.instance));
|
|
407
414
|
}
|
|
408
|
-
async instantiateClass(instances, wrapper, targetMetatype,
|
|
415
|
+
async instantiateClass(instances, wrapper, targetMetatype, resolutionContext = { contextId: STATIC_CONTEXT }) {
|
|
409
416
|
const { metatype, inject } = wrapper;
|
|
410
|
-
const inquirerId = this.
|
|
411
|
-
const instanceHost = targetMetatype.getInstanceByContextId(this.getContextId(contextId, targetMetatype), inquirerId);
|
|
412
|
-
const isInContext =
|
|
413
|
-
wrapper.isInRequestScope(contextId, inquirer) ||
|
|
414
|
-
wrapper.isLazyTransient(contextId, inquirer) ||
|
|
415
|
-
wrapper.isExplicitlyRequested(contextId, inquirer);
|
|
417
|
+
const inquirerId = this.getContextInquirerId(resolutionContext);
|
|
418
|
+
const instanceHost = targetMetatype.getInstanceByContextId(this.getContextId(resolutionContext.contextId, targetMetatype), inquirerId);
|
|
419
|
+
const isInContext = this.isInContext(wrapper, resolutionContext);
|
|
416
420
|
if (this.options?.preview && !wrapper.host?.initOnPreview) {
|
|
417
421
|
instanceHost.isResolved = true;
|
|
418
422
|
return instanceHost.instance;
|
|
@@ -438,16 +442,22 @@ export class Injector {
|
|
|
438
442
|
const injectionToken = instance.constructor;
|
|
439
443
|
wrapper = collection.get(injectionToken);
|
|
440
444
|
}
|
|
441
|
-
|
|
445
|
+
else {
|
|
446
|
+
wrapper = collection.get(wrapper.token) ?? wrapper;
|
|
447
|
+
}
|
|
448
|
+
await this.loadInstance(wrapper, collection, moduleRef, this.createResolutionContext(ctx, wrapper));
|
|
442
449
|
await this.loadEnhancersPerContext(wrapper, ctx, wrapper);
|
|
443
450
|
const host = wrapper.getInstanceByContextId(this.getContextId(ctx, wrapper), wrapper.id);
|
|
444
451
|
return host && host.instance;
|
|
445
452
|
}
|
|
446
453
|
async loadEnhancersPerContext(wrapper, ctx, inquirer) {
|
|
454
|
+
if (ctx === STATIC_CONTEXT) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
447
457
|
const enhancers = wrapper.getEnhancersMetadata() || [];
|
|
448
458
|
const loadEnhancer = (item) => {
|
|
449
459
|
const hostModule = item.host;
|
|
450
|
-
return this.loadInstance(item, hostModule.injectables, hostModule, ctx, inquirer);
|
|
460
|
+
return this.loadInstance(item, hostModule.injectables, hostModule, this.createResolutionContext(ctx, inquirer));
|
|
451
461
|
};
|
|
452
462
|
await Promise.all(enhancers.map(loadEnhancer));
|
|
453
463
|
}
|
|
@@ -455,14 +465,14 @@ export class Injector {
|
|
|
455
465
|
const hosts = await Promise.all(metadata.map(async (item) => this.resolveScopedComponentHost(item, contextId, inquirer, parentInquirer)));
|
|
456
466
|
return hosts.map((item, index) => {
|
|
457
467
|
const dependency = metadata[index];
|
|
458
|
-
const
|
|
459
|
-
return item?.getInstanceByContextId(this.getContextId(contextId, item),
|
|
468
|
+
const effectiveInquirerId = this.getEffectiveInquirerId(dependency, this.createResolutionContext(contextId, inquirer), parentInquirer);
|
|
469
|
+
return item?.getInstanceByContextId(this.getContextId(contextId, item), effectiveInquirerId).instance;
|
|
460
470
|
});
|
|
461
471
|
}
|
|
462
472
|
async loadPropertiesMetadata(metadata, contextId, inquirer) {
|
|
463
473
|
const dependenciesHosts = await Promise.all(metadata.map(async ({ wrapper: item, key }) => ({
|
|
464
474
|
key,
|
|
465
|
-
host: await this.resolveComponentHost(item.host, item, contextId, inquirer),
|
|
475
|
+
host: await this.resolveComponentHost(item.host, item, this.createResolutionContext(contextId, inquirer)),
|
|
466
476
|
})));
|
|
467
477
|
const inquirerId = this.getInquirerId(inquirer);
|
|
468
478
|
return dependenciesHosts.map(({ key, host }) => ({
|
|
@@ -474,6 +484,34 @@ export class Injector {
|
|
|
474
484
|
getInquirerId(inquirer) {
|
|
475
485
|
return inquirer ? inquirer.id : undefined;
|
|
476
486
|
}
|
|
487
|
+
createResolutionContext(contextId, inquirer, effectiveInquirerId) {
|
|
488
|
+
return {
|
|
489
|
+
contextId,
|
|
490
|
+
inquirer,
|
|
491
|
+
effectiveInquirerId,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
getContextInquirerId({ inquirer, effectiveInquirerId, }) {
|
|
495
|
+
return effectiveInquirerId ?? this.getInquirerId(inquirer);
|
|
496
|
+
}
|
|
497
|
+
isInContext(wrapper, resolutionContext) {
|
|
498
|
+
return (wrapper.isStatic(resolutionContext.contextId, resolutionContext.inquirer) ||
|
|
499
|
+
wrapper.isInRequestScope(resolutionContext.contextId, resolutionContext.inquirer) ||
|
|
500
|
+
wrapper.isLazyTransient(resolutionContext.contextId, resolutionContext.inquirer) ||
|
|
501
|
+
wrapper.isExplicitlyRequested(resolutionContext.contextId, resolutionContext.inquirer));
|
|
502
|
+
}
|
|
503
|
+
shouldSkipProviderLoading(wrapper, resolutionContext) {
|
|
504
|
+
const isSnapshotGraphCompilation = !!this.options?.snapshot;
|
|
505
|
+
const isStaticContext = resolutionContext.contextId === STATIC_CONTEXT;
|
|
506
|
+
const hasNoInquirer = !resolutionContext.inquirer;
|
|
507
|
+
const isTopLevelStaticTransientOrRequestProvider = hasNoInquirer && (wrapper.isTransient || wrapper.scope === Scope.REQUEST);
|
|
508
|
+
const isStaticInquirerOutsideResolutionContext = !!resolutionContext.inquirer &&
|
|
509
|
+
!this.isInContext(resolutionContext.inquirer, this.createResolutionContext(resolutionContext.contextId, resolutionContext.inquirer));
|
|
510
|
+
const shouldSkipForStaticBootstrap = isStaticContext &&
|
|
511
|
+
(isTopLevelStaticTransientOrRequestProvider ||
|
|
512
|
+
isStaticInquirerOutsideResolutionContext);
|
|
513
|
+
return !isSnapshotGraphCompilation && shouldSkipForStaticBootstrap;
|
|
514
|
+
}
|
|
477
515
|
/**
|
|
478
516
|
* For nested TRANSIENT dependencies (TRANSIENT -> TRANSIENT) in non-static contexts,
|
|
479
517
|
* returns parentInquirer to ensure each parent TRANSIENT gets its own instance.
|
|
@@ -481,18 +519,45 @@ export class Injector {
|
|
|
481
519
|
* can be used by multiple parents, causing nested TRANSIENTs to be shared incorrectly.
|
|
482
520
|
* For non-TRANSIENT -> TRANSIENT, returns inquirer (current wrapper being created).
|
|
483
521
|
*/
|
|
484
|
-
getEffectiveInquirer(dependency,
|
|
485
|
-
|
|
522
|
+
getEffectiveInquirer(dependency, resolutionContext, parentInquirer) {
|
|
523
|
+
const { inquirer, contextId } = resolutionContext;
|
|
524
|
+
if (dependency?.isTransient && inquirer?.isTransient && parentInquirer) {
|
|
525
|
+
if (contextId === STATIC_CONTEXT) {
|
|
526
|
+
return inquirer.getRootInquirer() ?? parentInquirer;
|
|
527
|
+
}
|
|
528
|
+
return parentInquirer;
|
|
529
|
+
}
|
|
530
|
+
return inquirer;
|
|
531
|
+
}
|
|
532
|
+
getEffectiveInquirerId(dependency, resolutionContext, parentInquirer) {
|
|
533
|
+
const { contextId, inquirer, effectiveInquirerId } = resolutionContext;
|
|
534
|
+
if (contextId === STATIC_CONTEXT &&
|
|
535
|
+
dependency?.isTransient &&
|
|
486
536
|
inquirer?.isTransient &&
|
|
487
|
-
parentInquirer
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
537
|
+
parentInquirer) {
|
|
538
|
+
const baseInquirerId = effectiveInquirerId ?? this.getInquirerId(parentInquirer);
|
|
539
|
+
return `${baseInquirerId}:${inquirer.id}`;
|
|
540
|
+
}
|
|
541
|
+
const effectiveInquirer = this.getEffectiveInquirer(dependency, resolutionContext, parentInquirer);
|
|
542
|
+
return this.getInquirerId(effectiveInquirer);
|
|
543
|
+
}
|
|
544
|
+
getStaticTransientResolutionContext(resolutionContext, parentInquirer) {
|
|
545
|
+
const { contextId, inquirer, effectiveInquirerId } = resolutionContext;
|
|
546
|
+
if (contextId === STATIC_CONTEXT &&
|
|
547
|
+
inquirer?.isTransient &&
|
|
548
|
+
parentInquirer) {
|
|
549
|
+
const baseInquirerId = effectiveInquirerId ?? this.getInquirerId(parentInquirer);
|
|
550
|
+
return this.createResolutionContext(contextId, inquirer, `${baseInquirerId}:${inquirer.id}`);
|
|
551
|
+
}
|
|
552
|
+
return resolutionContext;
|
|
553
|
+
}
|
|
554
|
+
getEffectiveResolutionContext(dependency, resolutionContext, parentInquirer) {
|
|
555
|
+
return this.createResolutionContext(resolutionContext.contextId, this.getEffectiveInquirer(dependency, resolutionContext, parentInquirer), this.getEffectiveInquirerId(dependency, resolutionContext, parentInquirer));
|
|
491
556
|
}
|
|
492
557
|
resolveScopedComponentHost(item, contextId, inquirer, parentInquirer) {
|
|
493
558
|
return this.isInquirerRequest(item, parentInquirer)
|
|
494
559
|
? parentInquirer
|
|
495
|
-
: this.resolveComponentHost(item.host, item,
|
|
560
|
+
: this.resolveComponentHost(item.host, item, this.getEffectiveResolutionContext(item, this.createResolutionContext(contextId, inquirer), parentInquirer));
|
|
496
561
|
}
|
|
497
562
|
isInquirerRequest(item, parentInquirer) {
|
|
498
563
|
return item.isTransient && item.name === INQUIRER && parentInquirer;
|
package/injector/module-ref.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Scope } from '@nestjs/common';
|
|
|
2
2
|
import { getClassScope } from '../helpers/get-class-scope.js';
|
|
3
3
|
import { isDurable } from '../helpers/is-durable.js';
|
|
4
4
|
import { AbstractInstanceResolver } from './abstract-instance-resolver.js';
|
|
5
|
+
import { STATIC_CONTEXT } from './constants.js';
|
|
5
6
|
import { Injector } from './injector.js';
|
|
6
7
|
import { InstanceLinksHost } from './instance-links-host.js';
|
|
7
8
|
import { InstanceWrapper } from './instance-wrapper.js';
|
|
@@ -18,9 +19,11 @@ export class ModuleRef extends AbstractInstanceResolver {
|
|
|
18
19
|
constructor(container) {
|
|
19
20
|
super();
|
|
20
21
|
this.container = container;
|
|
22
|
+
const contextOptions = container.contextOptions;
|
|
21
23
|
this.injector = new Injector({
|
|
22
|
-
preview:
|
|
23
|
-
|
|
24
|
+
preview: contextOptions?.preview ?? false,
|
|
25
|
+
snapshot: contextOptions?.snapshot,
|
|
26
|
+
instanceDecorator: contextOptions?.instrument?.instanceDecorator,
|
|
24
27
|
});
|
|
25
28
|
}
|
|
26
29
|
introspect(token) {
|
|
@@ -46,20 +49,30 @@ export class ModuleRef extends AbstractInstanceResolver {
|
|
|
46
49
|
durable: isDurable(type),
|
|
47
50
|
host: moduleRef,
|
|
48
51
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
if (type?.prototype) {
|
|
53
|
+
wrapper.setInstanceByContextId(contextId ?? STATIC_CONTEXT, {
|
|
54
|
+
instance: Object.create(type.prototype),
|
|
55
|
+
isResolved: false,
|
|
56
|
+
isPending: false,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const loadInstance = async () => {
|
|
52
61
|
const callback = async (instances) => {
|
|
53
|
-
const properties = await this.injector.resolveProperties(wrapper, moduleRef, undefined,
|
|
62
|
+
const properties = await this.injector.resolveProperties(wrapper, moduleRef, undefined, {
|
|
63
|
+
contextId: contextId ?? STATIC_CONTEXT,
|
|
64
|
+
inquirer: wrapper,
|
|
65
|
+
});
|
|
54
66
|
const instance = new type(...instances);
|
|
55
67
|
this.injector.applyProperties(instance, properties);
|
|
56
68
|
resolve(instance);
|
|
57
69
|
};
|
|
58
|
-
await this.injector.resolveConstructorParams(wrapper, moduleRef, undefined, callback,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
70
|
+
await this.injector.resolveConstructorParams(wrapper, moduleRef, undefined, callback, {
|
|
71
|
+
contextId: contextId ?? STATIC_CONTEXT,
|
|
72
|
+
inquirer: wrapper,
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
void loadInstance().catch(reject);
|
|
63
76
|
});
|
|
64
77
|
}
|
|
65
78
|
}
|
|
@@ -178,7 +178,10 @@ export class MiddlewareModule {
|
|
|
178
178
|
const middlewareFunction = isMethodAll
|
|
179
179
|
? proxy
|
|
180
180
|
: (req, res, next) => {
|
|
181
|
-
|
|
181
|
+
const actualRequestMethod = applicationRef.getRequestMethod?.(req);
|
|
182
|
+
if (actualRequestMethod === requestMethod ||
|
|
183
|
+
(actualRequestMethod === RequestMethod[RequestMethod.HEAD] &&
|
|
184
|
+
requestMethod === RequestMethod[RequestMethod.GET])) {
|
|
182
185
|
return proxy(req, res, next);
|
|
183
186
|
}
|
|
184
187
|
return next();
|
package/nest-factory.d.ts
CHANGED
|
@@ -2,7 +2,12 @@ import type { DynamicModule, ForwardReference, INestApplication, INestApplicatio
|
|
|
2
2
|
import { AbstractHttpAdapter } from './adapters/http-adapter.js';
|
|
3
3
|
import { type NestMicroserviceOptions, type NestApplicationContextOptions } from '@nestjs/common/internal';
|
|
4
4
|
import { type NestApplicationOptions } from '@nestjs/common';
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Represents the entry (root) module type accepted by the NestFactory methods.
|
|
7
|
+
*
|
|
8
|
+
* @publicApi
|
|
9
|
+
*/
|
|
10
|
+
export type IEntryNestModule = Type<any> | DynamicModule | ForwardReference | Promise<IEntryNestModule>;
|
|
6
11
|
/**
|
|
7
12
|
* @publicApi
|
|
8
13
|
*/
|
|
@@ -79,4 +84,3 @@ export declare class NestFactoryStatic {
|
|
|
79
84
|
* @publicApi
|
|
80
85
|
*/
|
|
81
86
|
export declare const NestFactory: NestFactoryStatic;
|
|
82
|
-
export {};
|
package/nest-factory.js
CHANGED
|
@@ -91,6 +91,7 @@ export class NestFactoryStatic {
|
|
|
91
91
|
: UuidFactoryMode.Random;
|
|
92
92
|
const injector = new Injector({
|
|
93
93
|
preview: options.preview,
|
|
94
|
+
snapshot: options.snapshot,
|
|
94
95
|
instanceDecorator: options.instrument?.instanceDecorator,
|
|
95
96
|
});
|
|
96
97
|
const instanceLoader = new InstanceLoader(container, injector, graphInspector);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestjs/core",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.4",
|
|
4
4
|
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
|
|
5
5
|
"author": "Kamil Mysliwiec",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"@nuxt/opencollective": "0.4.1",
|
|
43
43
|
"fast-safe-stringify": "2.1.1",
|
|
44
44
|
"iterare": "1.2.1",
|
|
45
|
-
"path-to-regexp": "8.
|
|
45
|
+
"path-to-regexp": "8.4.2",
|
|
46
46
|
"tslib": "2.8.1",
|
|
47
47
|
"uid": "2.0.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@nestjs/common": "^12.0.0-alpha.
|
|
50
|
+
"@nestjs/common": "^12.0.0-alpha.4"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@nestjs/common": "^11.0.0",
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"optional": true
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "415e4d33707e74998d8456be7bd305cedb6508e3"
|
|
72
72
|
}
|
|
@@ -176,7 +176,12 @@ export class RouterExecutionContext {
|
|
|
176
176
|
const isSseHandler = !!this.reflectSse(callback);
|
|
177
177
|
if (isSseHandler) {
|
|
178
178
|
return async (result, res, req) => {
|
|
179
|
-
|
|
179
|
+
const rawResponse = res.raw ?? res;
|
|
180
|
+
await this.responseController.sse(result, rawResponse, req.raw || req, {
|
|
181
|
+
additionalHeaders: res.getHeaders?.(),
|
|
182
|
+
statusCode: res.statusCode ??
|
|
183
|
+
rawResponse.statusCode,
|
|
184
|
+
});
|
|
180
185
|
};
|
|
181
186
|
}
|
|
182
187
|
return async (result, res) => {
|
|
@@ -22,7 +22,8 @@ export declare class RouterResponseController {
|
|
|
22
22
|
setHeaders<TResponse = unknown>(response: TResponse, headers: CustomHeader[]): void;
|
|
23
23
|
setStatus<TResponse = unknown>(response: TResponse, statusCode: number): void;
|
|
24
24
|
sse<TInput extends Observable<unknown> = any, TResponse extends WritableHeaderStream = any, TRequest extends IncomingMessage = any>(result: TInput | Promise<TInput>, response: TResponse, request: TRequest, options?: {
|
|
25
|
-
additionalHeaders
|
|
25
|
+
additionalHeaders?: AdditionalHeaders;
|
|
26
|
+
statusCode?: number;
|
|
26
27
|
}): Promise<void>;
|
|
27
28
|
private assertObservable;
|
|
28
29
|
}
|
|
@@ -54,37 +54,71 @@ export class RouterResponseController {
|
|
|
54
54
|
const observableResult = await Promise.resolve(result);
|
|
55
55
|
this.assertObservable(observableResult);
|
|
56
56
|
const stream = new SseStream(request);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
: options
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
stream.writeMessage({ type: 'error', data }, writeError => {
|
|
72
|
-
if (writeError) {
|
|
73
|
-
this.logger.error(writeError);
|
|
57
|
+
const statusCode = options?.statusCode ??
|
|
58
|
+
response.statusCode ??
|
|
59
|
+
200;
|
|
60
|
+
stream.pipe(response, {
|
|
61
|
+
additionalHeaders: options?.additionalHeaders,
|
|
62
|
+
statusCode,
|
|
63
|
+
});
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
let settled = false;
|
|
66
|
+
const onClose = () => {
|
|
67
|
+
settled = true;
|
|
68
|
+
subscription.unsubscribe();
|
|
69
|
+
if (!stream.writableEnded) {
|
|
70
|
+
stream.end();
|
|
74
71
|
}
|
|
75
|
-
});
|
|
76
|
-
return EMPTY;
|
|
77
|
-
}))
|
|
78
|
-
.subscribe({
|
|
79
|
-
complete: () => {
|
|
80
72
|
response.end();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
resolve();
|
|
74
|
+
};
|
|
75
|
+
const subscription = observableResult
|
|
76
|
+
.pipe(map((message) => {
|
|
77
|
+
if (isObject(message)) {
|
|
78
|
+
return message;
|
|
79
|
+
}
|
|
80
|
+
return { data: message };
|
|
81
|
+
}), concatMap(message => new Promise(resolve => stream.writeMessage(message, () => resolve()))), catchError(err => {
|
|
82
|
+
if (!stream.headersCommitted) {
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
const data = err instanceof Error ? err.message : err;
|
|
86
|
+
stream.writeMessage({ type: 'error', data }, writeError => {
|
|
87
|
+
if (writeError) {
|
|
88
|
+
this.logger.error(writeError);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return EMPTY;
|
|
92
|
+
}))
|
|
93
|
+
.subscribe({
|
|
94
|
+
error: err => {
|
|
95
|
+
settled = true;
|
|
96
|
+
request.removeListener('close', onClose);
|
|
97
|
+
if (!stream.writableEnded) {
|
|
98
|
+
stream.end();
|
|
99
|
+
}
|
|
100
|
+
reject(err);
|
|
101
|
+
},
|
|
102
|
+
complete: () => {
|
|
103
|
+
settled = true;
|
|
104
|
+
request.removeListener('close', onClose);
|
|
105
|
+
if (!stream.writableEnded) {
|
|
106
|
+
stream.end();
|
|
107
|
+
}
|
|
108
|
+
resolve();
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
// Commit SSE headers on the next macrotask. Pipe validation errors
|
|
112
|
+
// propagate through microtasks (which complete before macrotasks),
|
|
113
|
+
// so if the lifecycle errored, `settled` is already true and we
|
|
114
|
+
// skip the write. Otherwise headers are sent immediately rather
|
|
115
|
+
// than waiting for the first Observable emission.
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
if (!settled) {
|
|
118
|
+
stream.commitHeaders();
|
|
119
|
+
}
|
|
120
|
+
}, 0);
|
|
121
|
+
request.on('close', onClose);
|
|
88
122
|
});
|
|
89
123
|
}
|
|
90
124
|
assertObservable(value) {
|
package/router/sse-stream.d.ts
CHANGED
|
@@ -28,12 +28,24 @@ export type HeaderStream = WritableHeaderStream & ReadHeaders;
|
|
|
28
28
|
*/
|
|
29
29
|
export declare class SseStream extends Transform {
|
|
30
30
|
private lastEventId;
|
|
31
|
+
private _headersCommitted;
|
|
32
|
+
private _destination;
|
|
33
|
+
private _statusCode;
|
|
34
|
+
private _additionalHeaders;
|
|
31
35
|
constructor(req?: IncomingMessage);
|
|
36
|
+
get headersCommitted(): boolean;
|
|
32
37
|
pipe<T extends WritableHeaderStream>(destination: T, options?: {
|
|
33
38
|
additionalHeaders?: AdditionalHeaders;
|
|
34
39
|
statusCode?: number;
|
|
35
40
|
end?: boolean;
|
|
36
41
|
}): T;
|
|
42
|
+
/**
|
|
43
|
+
* Writes SSE headers to the destination if they have not been sent yet.
|
|
44
|
+
* Headers are deferred until the first message so that, if the observable
|
|
45
|
+
* errors before any data is emitted, the HTTP status code can still be
|
|
46
|
+
* changed by an exception filter.
|
|
47
|
+
*/
|
|
48
|
+
commitHeaders(): void;
|
|
37
49
|
_transform(message: MessageEvent, encoding: string, callback: (error?: Error | null, data?: any) => void): void;
|
|
38
50
|
/**
|
|
39
51
|
* Calls `.write` but handles the drain if needed
|
package/router/sse-stream.js
CHANGED
|
@@ -24,6 +24,10 @@ function toDataString(data) {
|
|
|
24
24
|
*/
|
|
25
25
|
export class SseStream extends Transform {
|
|
26
26
|
lastEventId = null;
|
|
27
|
+
_headersCommitted = false;
|
|
28
|
+
_destination = null;
|
|
29
|
+
_statusCode = 200;
|
|
30
|
+
_additionalHeaders;
|
|
27
31
|
constructor(req) {
|
|
28
32
|
super({ objectMode: true });
|
|
29
33
|
if (req && req.socket) {
|
|
@@ -32,11 +36,34 @@ export class SseStream extends Transform {
|
|
|
32
36
|
req.socket.setTimeout(0);
|
|
33
37
|
}
|
|
34
38
|
}
|
|
39
|
+
get headersCommitted() {
|
|
40
|
+
return this._headersCommitted;
|
|
41
|
+
}
|
|
35
42
|
pipe(destination, options) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
this._destination = destination;
|
|
44
|
+
this._statusCode = options?.statusCode ?? 200;
|
|
45
|
+
this._additionalHeaders = options?.additionalHeaders;
|
|
46
|
+
return super.pipe(destination, options);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Writes SSE headers to the destination if they have not been sent yet.
|
|
50
|
+
* Headers are deferred until the first message so that, if the observable
|
|
51
|
+
* errors before any data is emitted, the HTTP status code can still be
|
|
52
|
+
* changed by an exception filter.
|
|
53
|
+
*/
|
|
54
|
+
commitHeaders() {
|
|
55
|
+
if (this._headersCommitted || !this._destination) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this._destination.writableEnded) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this._headersCommitted = true;
|
|
62
|
+
const statusCode = this._statusCode ?? 200;
|
|
63
|
+
const additionalHeaders = this._additionalHeaders;
|
|
64
|
+
if (this._destination.writeHead) {
|
|
65
|
+
this._destination.writeHead(statusCode, {
|
|
66
|
+
...additionalHeaders,
|
|
40
67
|
// See https://github.com/dunglas/mercure/blob/master/hub/subscribe.go#L124-L130
|
|
41
68
|
'Content-Type': 'text/event-stream',
|
|
42
69
|
Connection: 'keep-alive',
|
|
@@ -47,15 +74,19 @@ export class SseStream extends Transform {
|
|
|
47
74
|
// NGINX support https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-buffering
|
|
48
75
|
'X-Accel-Buffering': 'no',
|
|
49
76
|
});
|
|
50
|
-
|
|
77
|
+
this._destination.flushHeaders?.();
|
|
51
78
|
}
|
|
52
|
-
|
|
53
|
-
return super.pipe(destination, options);
|
|
79
|
+
this._destination.write('\n');
|
|
54
80
|
}
|
|
55
81
|
_transform(message, encoding, callback) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
data
|
|
82
|
+
this.commitHeaders();
|
|
83
|
+
const sanitize = (val) => String(val).replace(/[\r\n]/g, '');
|
|
84
|
+
let data = message.type ? `event: ${sanitize(message.type)}\n` : '';
|
|
85
|
+
data +=
|
|
86
|
+
message.id !== undefined && message.id !== null
|
|
87
|
+
? `id: ${sanitize(message.id)}\n`
|
|
88
|
+
: '';
|
|
89
|
+
data += message.retry ? `retry: ${sanitize(message.retry)}\n` : '';
|
|
59
90
|
data += message.data ? toDataString(message.data) : '';
|
|
60
91
|
data += '\n';
|
|
61
92
|
this.push(data);
|
|
@@ -65,7 +96,7 @@ export class SseStream extends Transform {
|
|
|
65
96
|
* Calls `.write` but handles the drain if needed
|
|
66
97
|
*/
|
|
67
98
|
writeMessage(message, cb) {
|
|
68
|
-
if (
|
|
99
|
+
if (message.id === undefined || message.id === null) {
|
|
69
100
|
this.lastEventId++;
|
|
70
101
|
message.id = this.lastEventId.toString();
|
|
71
102
|
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { InjectionToken } from '@nestjs/common';
|
|
2
|
-
import { InstanceWrapper } from '../instance-wrapper';
|
|
3
|
-
/**
|
|
4
|
-
* Returns the instances which are transient
|
|
5
|
-
* @param instances The instances which should be checked whether they are transient
|
|
6
|
-
*/
|
|
7
|
-
export declare function getTransientInstances(instances: [InjectionToken, InstanceWrapper][]): InstanceWrapper[];
|
|
8
|
-
/**
|
|
9
|
-
* Returns the instances which are not transient
|
|
10
|
-
* @param instances The instances which should be checked whether they are transient
|
|
11
|
-
*/
|
|
12
|
-
export declare function getNonTransientInstances(instances: [InjectionToken, InstanceWrapper][]): InstanceWrapper[];
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getTransientInstances = getTransientInstances;
|
|
4
|
-
exports.getNonTransientInstances = getNonTransientInstances;
|
|
5
|
-
const iterare_1 = require("iterare");
|
|
6
|
-
/**
|
|
7
|
-
* Returns the instances which are transient
|
|
8
|
-
* @param instances The instances which should be checked whether they are transient
|
|
9
|
-
*/
|
|
10
|
-
function getTransientInstances(instances) {
|
|
11
|
-
return (0, iterare_1.iterate)(instances)
|
|
12
|
-
.filter(([_, wrapper]) => wrapper.isDependencyTreeStatic())
|
|
13
|
-
.map(([_, wrapper]) => wrapper.getStaticTransientInstances())
|
|
14
|
-
.flatten()
|
|
15
|
-
.filter(item => !!item)
|
|
16
|
-
.map(({ instance }) => instance)
|
|
17
|
-
.toArray();
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Returns the instances which are not transient
|
|
21
|
-
* @param instances The instances which should be checked whether they are transient
|
|
22
|
-
*/
|
|
23
|
-
function getNonTransientInstances(instances) {
|
|
24
|
-
return (0, iterare_1.iterate)(instances)
|
|
25
|
-
.filter(([key, wrapper]) => wrapper.isDependencyTreeStatic() && !wrapper.isTransient)
|
|
26
|
-
.map(([key, { instance }]) => instance)
|
|
27
|
-
.toArray();
|
|
28
|
-
}
|