@nestjs/core 12.0.0-alpha.5 → 12.0.0-alpha.7
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 +25 -46
- package/adapters/http-adapter.d.ts +2 -0
- package/adapters/http-adapter.js +3 -0
- package/exceptions/base-exception-filter.js +8 -3
- package/helpers/barrier.js +4 -1
- package/helpers/handler-metadata-storage.d.ts +1 -0
- package/helpers/router-method-factory.d.ts +1 -0
- package/helpers/router-method-factory.js +1 -0
- package/helpers/safe-instance-decorator.d.ts +10 -0
- package/helpers/safe-instance-decorator.js +20 -0
- package/hooks/before-app-shutdown.hook.js +11 -2
- package/hooks/on-app-shutdown.hook.js +11 -2
- package/hooks/on-module-destroy.hook.js +11 -2
- package/injector/container.js +2 -2
- package/injector/injector.js +20 -12
- package/injector/internal-core-module/internal-core-module-factory.js +1 -1
- package/injector/module.d.ts +6 -0
- package/injector/module.js +12 -1
- package/interceptors/interceptors-consumer.js +32 -7
- package/internal.d.ts +10 -6
- package/internal.js +10 -6
- package/middleware/builder.js +5 -1
- package/nest-application-context.js +1 -1
- package/nest-application.d.ts +2 -1
- package/nest-application.js +28 -6
- package/package.json +3 -14
- package/router/legacy-route-converter.d.ts +1 -1
- package/router/legacy-route-converter.js +24 -13
- package/router/router-execution-context.d.ts +1 -0
- package/router/router-execution-context.js +26 -3
- package/router/router-response-controller.d.ts +1 -0
- package/router/router-response-controller.js +126 -49
- package/router/sse-stream.d.ts +1 -0
- package/router/sse-stream.js +24 -13
- package/scanner.js +4 -1
package/Readme.md
CHANGED
|
@@ -59,11 +59,11 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
|
|
59
59
|
|
|
60
60
|
<table style="text-align:center;">
|
|
61
61
|
<tr>
|
|
62
|
-
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
|
63
|
-
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/
|
|
64
|
-
<td><a href="https://mojam.co" target="_blank"><img src="https://
|
|
62
|
+
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/github/trilon-logo.svg" width="200" valign="middle" /></a></td>
|
|
63
|
+
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/github/microsoft-logo.png" width="180" valign="middle" /></a></td>
|
|
64
|
+
<td><a href="https://mojam.co" target="_blank"><img src="https://images.opencollective.com/mojamco/981e9de/logo/256.png?height=256" width="80" valign="middle" /></a></td>
|
|
65
65
|
<td><a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
|
66
|
-
<td><a href="https://serpapi.com/" target="_blank"><img src="https://nestjs.com/img/
|
|
66
|
+
<td><a href="https://serpapi.com/" target="_blank"><img src="https://nestjs.com/img/github/serpapi-logo.png" width="150" valign="middle" /></a></td>
|
|
67
67
|
</tr>
|
|
68
68
|
</table>
|
|
69
69
|
|
|
@@ -71,15 +71,15 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
|
|
71
71
|
|
|
72
72
|
<table style="text-align:center;">
|
|
73
73
|
<tr>
|
|
74
|
-
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/
|
|
75
|
-
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="
|
|
76
|
-
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/
|
|
77
|
-
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/
|
|
78
|
-
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/
|
|
74
|
+
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/github/red-hat-logo.svg" width="150" valign="middle" /></a></td>
|
|
75
|
+
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="120" valign="middle" /></a></td>
|
|
76
|
+
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/github/nx-logo.png" height="45" valign="middle" /></a></td>
|
|
77
|
+
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/github/intrinisic-logo.png" width="150" valign="middle" /></a></td>
|
|
78
|
+
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/github/jetbrains-logo.svg" width="70" valign="middle" /></a></td>
|
|
79
79
|
</tr>
|
|
80
80
|
<tr>
|
|
81
|
-
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/
|
|
82
|
-
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://
|
|
81
|
+
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/github/snyk-logo-black.png" width="125" valign="middle" /></a></td>
|
|
82
|
+
<td align="center"><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://images.opencollective.com/movavi/1fb15e5/avatar/256.png?height=256" width="60" valign="middle" /></a></td>
|
|
83
83
|
</tr>
|
|
84
84
|
</table>
|
|
85
85
|
|
|
@@ -87,15 +87,15 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
|
|
87
87
|
|
|
88
88
|
<table style="text-align:center;">
|
|
89
89
|
<tr>
|
|
90
|
-
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/
|
|
91
|
-
<td><a href="https://
|
|
92
|
-
<td><a href="https://
|
|
93
|
-
<td align="center" valign="middle"><a href="https://
|
|
94
|
-
<td align="center" valign="middle"><a href="https://
|
|
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>
|
|
90
|
+
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/github/mercedes-logo.png" width="100" valign="middle" /></a></td>
|
|
91
|
+
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/github/handsontable-dark-logo.svg#2" width="150" valign="middle" /></a></td>
|
|
92
|
+
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/github/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
|
|
93
|
+
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/github/arcjet-logo.svg" width="170" valign="middle" /></a></td>
|
|
94
|
+
<td align="center" valign="middle"><a href="https://pandektes.com" target="_blank"><img src="https://nestjs.com/img/github/pandektes-logo.png" width="65" valign="middle" /></a></td>
|
|
96
95
|
</tr><tr>
|
|
97
|
-
<td align="center" valign="middle"><a href="https://
|
|
98
|
-
<td align="center" valign="middle"><a href="https://
|
|
96
|
+
<td align="center" valign="middle"><a href="https://www.fintechcrafts.com/" target="_blank"><img src="https://opencollective-production.s3.us-west-1.amazonaws.com/account-avatar/935813df-48a9-4b41-b923-7d70b5621c6b/2026-05-06%2010%2021%2033.png" width="65" valign="middle" /></a></td>
|
|
97
|
+
<td align="center" valign="middle"><a href="https://talordata.com/?campaignid=Ya5Xpw2gmhyee6Tg&utm_source=nestjs&utm_term=nestjs" target="_blank"><img src="https://images.opencollective.com/elowen/9ccac7f/avatar/512.png" width="65" valign="middle" /></a></td>
|
|
98
|
+
<td align="center" valign="middle"><a href="https://memory2.co/" target="_blank"><img src="https://images.opencollective.com/memory-squared/bbe37f5/avatar/256.png?height=50" width="65" valign="middle" /></a></td>
|
|
99
99
|
</tr>
|
|
100
100
|
</table>
|
|
101
101
|
|
|
@@ -103,33 +103,12 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
|
|
103
103
|
|
|
104
104
|
<table>
|
|
105
105
|
<tr>
|
|
106
|
-
<td align="center" valign="middle"><a href="https://
|
|
107
|
-
<td align="center" valign="middle"><a href="https://
|
|
108
|
-
<td align="center" valign="middle"><a href="https://
|
|
109
|
-
<td align="center" valign="middle"><a href="https://
|
|
110
|
-
<td align="center" valign="middle"><a href="https://
|
|
111
|
-
<td align="center" valign="middle"><a href="https://
|
|
112
|
-
</tr>
|
|
113
|
-
<tr>
|
|
114
|
-
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
|
|
115
|
-
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
|
116
|
-
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
|
117
|
-
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
|
118
|
-
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
|
119
|
-
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
|
120
|
-
</tr>
|
|
121
|
-
<tr>
|
|
122
|
-
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
|
123
|
-
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
|
124
|
-
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
|
125
|
-
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
|
126
|
-
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
|
127
|
-
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
|
128
|
-
</tr>
|
|
129
|
-
<tr>
|
|
130
|
-
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
|
131
|
-
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
|
|
132
|
-
<td align="center" valign="middle"><a href="https://memory2.co/" target="_blank"><img src="https://images.opencollective.com/memory-squared/bbe37f5/avatar/256.png?height=50" width="50" valign="middle" /></a></td>
|
|
106
|
+
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/github/mantro-logo.svg" width="95" valign="middle" /></a></td>
|
|
107
|
+
<td align="center" valign="middle"><a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/github/triplebyte-logo.png" width="107" valign="middle" /></a></td>
|
|
108
|
+
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/github/nearpod-logo.svg" width="100" valign="middle" /></a></td>
|
|
109
|
+
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/github/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
|
110
|
+
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/github/triplecore-logo.svg" width="50" valign="middle" /></a>
|
|
111
|
+
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/github/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
|
133
112
|
</tr>
|
|
134
113
|
</table>
|
|
135
114
|
|
|
@@ -41,6 +41,8 @@ export declare abstract class AbstractHttpAdapter<TServer = any, TRequest = any,
|
|
|
41
41
|
all(path: any, handler: RequestHandler): any;
|
|
42
42
|
search(handler: RequestHandler): any;
|
|
43
43
|
search(path: any, handler: RequestHandler): any;
|
|
44
|
+
query(handler: RequestHandler): any;
|
|
45
|
+
query(path: any, handler: RequestHandler): any;
|
|
44
46
|
options(handler: RequestHandler): any;
|
|
45
47
|
options(path: any, handler: RequestHandler): any;
|
|
46
48
|
listen(port: string | number, callback?: () => void): any;
|
package/adapters/http-adapter.js
CHANGED
|
@@ -63,16 +63,21 @@ export class BaseExceptionFilter {
|
|
|
63
63
|
if (!err || typeof err !== 'object') {
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
|
-
if (err.constructor
|
|
66
|
+
if (err.constructor?.name === 'FastifyError' &&
|
|
67
67
|
typeof err.code === 'string' &&
|
|
68
68
|
typeof err.statusCode === 'number') {
|
|
69
69
|
return true;
|
|
70
70
|
}
|
|
71
71
|
// "http-errors" error signature
|
|
72
|
-
|
|
72
|
+
if (typeof err.expose === 'boolean' &&
|
|
73
73
|
typeof err.statusCode === 'number' &&
|
|
74
74
|
err.status === err.statusCode &&
|
|
75
|
-
err instanceof Error)
|
|
75
|
+
err instanceof Error) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
// Plain "http error"-shaped values (e.g. objects thrown by third-party
|
|
79
|
+
// middleware) that carry a status code and a message
|
|
80
|
+
return !!(err.statusCode && err.message);
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
__decorate([
|
package/helpers/barrier.js
CHANGED
|
@@ -12,6 +12,9 @@ export class Barrier {
|
|
|
12
12
|
this.promise = new Promise(resolve => {
|
|
13
13
|
this.resolve = resolve;
|
|
14
14
|
});
|
|
15
|
+
if (targetCount === 0) {
|
|
16
|
+
this.resolve();
|
|
17
|
+
}
|
|
15
18
|
}
|
|
16
19
|
/**
|
|
17
20
|
* Signal that a participant has reached the barrier.
|
|
@@ -20,7 +23,7 @@ export class Barrier {
|
|
|
20
23
|
*/
|
|
21
24
|
signal() {
|
|
22
25
|
this.currentCount += 1;
|
|
23
|
-
if (this.currentCount
|
|
26
|
+
if (this.currentCount >= this.targetCount) {
|
|
24
27
|
this.resolve();
|
|
25
28
|
}
|
|
26
29
|
}
|
|
@@ -10,6 +10,7 @@ export type HandlerResponseBasicFn = <TResult, TResponse>(result: TResult, res:
|
|
|
10
10
|
export type HandleSseResponseFn = <TResult extends Observable<unknown> = any, TResponse extends HeaderStream = any, TRequest extends IncomingMessage = any>(result: TResult, res: TResponse, req: TRequest) => any;
|
|
11
11
|
export interface HandlerMetadata {
|
|
12
12
|
argsLength: number;
|
|
13
|
+
isSseHandler: boolean;
|
|
13
14
|
paramtypes: any[];
|
|
14
15
|
httpStatusCode: number;
|
|
15
16
|
responseHeaders: any[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type InstanceDecorator = (target: unknown) => unknown;
|
|
2
|
+
/**
|
|
3
|
+
* Wraps an `instrument.instanceDecorator` so that a decorator throwing on a
|
|
4
|
+
* given instance (e.g. when inspecting a Proxy whose traps throw outside of
|
|
5
|
+
* their intended context, such as `nestjs-cls` proxy providers) does not
|
|
6
|
+
* crash the application bootstrap. The original, undecorated instance is
|
|
7
|
+
* used instead and a warning is logged.
|
|
8
|
+
*/
|
|
9
|
+
export declare function makeSafeInstanceDecorator(decorator: InstanceDecorator): InstanceDecorator;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
const logger = new Logger('InstrumentLogger');
|
|
3
|
+
/**
|
|
4
|
+
* Wraps an `instrument.instanceDecorator` so that a decorator throwing on a
|
|
5
|
+
* given instance (e.g. when inspecting a Proxy whose traps throw outside of
|
|
6
|
+
* their intended context, such as `nestjs-cls` proxy providers) does not
|
|
7
|
+
* crash the application bootstrap. The original, undecorated instance is
|
|
8
|
+
* used instead and a warning is logged.
|
|
9
|
+
*/
|
|
10
|
+
export function makeSafeInstanceDecorator(decorator) {
|
|
11
|
+
return (target) => {
|
|
12
|
+
try {
|
|
13
|
+
return decorator(target);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
logger.warn(`The "instanceDecorator" function threw an error while decorating an instance (${err?.message ?? err}). The undecorated instance will be used instead.`);
|
|
17
|
+
return target;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
1
2
|
import { isFunction, isNil } from '@nestjs/common/internal';
|
|
2
3
|
import { iterate } from 'iterare';
|
|
3
4
|
import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js';
|
|
@@ -33,12 +34,20 @@ export async function callBeforeAppShutdownHook(moduleRef, signal) {
|
|
|
33
34
|
const groupedInstances = getInstancesGroupedByHierarchyLevel(moduleRef.controllers, moduleRef.injectables, moduleRef.middlewares, providers);
|
|
34
35
|
const levels = getSortedHierarchyLevels(groupedInstances, 'DESC');
|
|
35
36
|
for (const level of levels) {
|
|
36
|
-
await Promise.
|
|
37
|
+
const results = await Promise.allSettled(callOperator(groupedInstances.get(level), signal));
|
|
38
|
+
results
|
|
39
|
+
.filter((result) => result.status === 'rejected')
|
|
40
|
+
.forEach(result => Logger.error(result.reason, result.reason?.stack));
|
|
37
41
|
}
|
|
38
42
|
const moduleClassInstance = moduleClassHost.instance;
|
|
39
43
|
if (moduleClassInstance &&
|
|
40
44
|
hasBeforeApplicationShutdownHook(moduleClassInstance) &&
|
|
41
45
|
moduleClassHost.isDependencyTreeStatic()) {
|
|
42
|
-
|
|
46
|
+
try {
|
|
47
|
+
await moduleClassInstance.beforeApplicationShutdown(signal);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
Logger.error(err, err?.stack);
|
|
51
|
+
}
|
|
43
52
|
}
|
|
44
53
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
1
2
|
import { isFunction, isNil } from '@nestjs/common/internal';
|
|
2
3
|
import { iterate } from 'iterare';
|
|
3
4
|
import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js';
|
|
@@ -35,13 +36,21 @@ export async function callAppShutdownHook(moduleRef, signal) {
|
|
|
35
36
|
const groupedInstances = getInstancesGroupedByHierarchyLevel(moduleRef.controllers, moduleRef.injectables, moduleRef.middlewares, providers);
|
|
36
37
|
const levels = getSortedHierarchyLevels(groupedInstances, 'DESC');
|
|
37
38
|
for (const level of levels) {
|
|
38
|
-
await Promise.
|
|
39
|
+
const results = await Promise.allSettled(callOperator(groupedInstances.get(level), signal));
|
|
40
|
+
results
|
|
41
|
+
.filter((result) => result.status === 'rejected')
|
|
42
|
+
.forEach(result => Logger.error(result.reason, result.reason?.stack));
|
|
39
43
|
}
|
|
40
44
|
// Call the instance itself
|
|
41
45
|
const moduleClassInstance = moduleClassHost.instance;
|
|
42
46
|
if (moduleClassInstance &&
|
|
43
47
|
hasOnAppShutdownHook(moduleClassInstance) &&
|
|
44
48
|
moduleClassHost.isDependencyTreeStatic()) {
|
|
45
|
-
|
|
49
|
+
try {
|
|
50
|
+
await moduleClassInstance.onApplicationShutdown(signal);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
Logger.error(err, err?.stack);
|
|
54
|
+
}
|
|
46
55
|
}
|
|
47
56
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
1
2
|
import { isFunction, isNil } from '@nestjs/common/internal';
|
|
2
3
|
import { iterate } from 'iterare';
|
|
3
4
|
import { getInstancesGroupedByHierarchyLevel } from './utils/get-instances-grouped-by-hierarchy-level.js';
|
|
@@ -34,13 +35,21 @@ export async function callModuleDestroyHook(moduleRef) {
|
|
|
34
35
|
const groupedInstances = getInstancesGroupedByHierarchyLevel(moduleRef.controllers, moduleRef.injectables, moduleRef.middlewares, providers);
|
|
35
36
|
const levels = getSortedHierarchyLevels(groupedInstances, 'DESC');
|
|
36
37
|
for (const level of levels) {
|
|
37
|
-
await Promise.
|
|
38
|
+
const results = await Promise.allSettled(callOperator(groupedInstances.get(level)));
|
|
39
|
+
results
|
|
40
|
+
.filter((result) => result.status === 'rejected')
|
|
41
|
+
.forEach(result => Logger.error(result.reason, result.reason?.stack));
|
|
38
42
|
}
|
|
39
43
|
// Call the module instance itself
|
|
40
44
|
const moduleClassInstance = moduleClassHost.instance;
|
|
41
45
|
if (moduleClassInstance &&
|
|
42
46
|
hasOnModuleDestroyHook(moduleClassInstance) &&
|
|
43
47
|
moduleClassHost.isDependencyTreeStatic()) {
|
|
44
|
-
|
|
48
|
+
try {
|
|
49
|
+
await moduleClassInstance.onModuleDestroy();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
Logger.error(err, err?.stack);
|
|
53
|
+
}
|
|
45
54
|
}
|
|
46
55
|
}
|
package/injector/container.js
CHANGED
|
@@ -66,7 +66,7 @@ export class NestContainer {
|
|
|
66
66
|
if (this.modules.has(token)) {
|
|
67
67
|
return {
|
|
68
68
|
moduleRef: this.modules.get(token),
|
|
69
|
-
inserted:
|
|
69
|
+
inserted: false,
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
return {
|
|
@@ -92,7 +92,7 @@ export class NestContainer {
|
|
|
92
92
|
type,
|
|
93
93
|
dynamicMetadata,
|
|
94
94
|
}, scope),
|
|
95
|
-
inserted:
|
|
95
|
+
inserted: true,
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
async setModule({ token, dynamicMetadata, type }, scope) {
|
package/injector/injector.js
CHANGED
|
@@ -7,6 +7,7 @@ import { RuntimeException } from '../errors/exceptions/runtime.exception.js';
|
|
|
7
7
|
import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception.js';
|
|
8
8
|
import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception.js';
|
|
9
9
|
import { Barrier } from '../helpers/barrier.js';
|
|
10
|
+
import { makeSafeInstanceDecorator } from '../helpers/safe-instance-decorator.js';
|
|
10
11
|
import { STATIC_CONTEXT } from './constants.js';
|
|
11
12
|
import { INQUIRER } from './inquirer/index.js';
|
|
12
13
|
import { InstanceWrapper, } from './instance-wrapper.js';
|
|
@@ -18,7 +19,7 @@ export class Injector {
|
|
|
18
19
|
constructor(options) {
|
|
19
20
|
this.options = options;
|
|
20
21
|
if (options?.instanceDecorator) {
|
|
21
|
-
this.instanceDecorator = options.instanceDecorator;
|
|
22
|
+
this.instanceDecorator = makeSafeInstanceDecorator(options.instanceDecorator);
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
loadPrototype({ token }, collection, contextId = STATIC_CONTEXT) {
|
|
@@ -267,12 +268,23 @@ export class Injector {
|
|
|
267
268
|
* that eventual lazily created instance will be merged with the prototype
|
|
268
269
|
* instantiated beforehand.
|
|
269
270
|
*/
|
|
270
|
-
instanceHost.donePromise
|
|
271
|
+
if (instanceHost.donePromise) {
|
|
271
272
|
void instanceHost.donePromise
|
|
272
273
|
.then(() => this.loadProvider(instanceWrapper, moduleRef, resolutionContext))
|
|
273
274
|
.catch(err => {
|
|
274
275
|
instanceWrapper.settlementSignal?.error(err);
|
|
275
276
|
});
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
/**
|
|
280
|
+
* No load has ever been scheduled for this context (e.g., request-scoped
|
|
281
|
+
* providers are no longer instantiated during static bootstrap, so a fresh
|
|
282
|
+
* durable/request sub-tree host has no inherited `donePromise`).
|
|
283
|
+
* Load it now; if a circular dependency is truly in-flight, the nested
|
|
284
|
+
* lookup will find this host pending and defer through its `donePromise`.
|
|
285
|
+
*/
|
|
286
|
+
await this.loadProvider(instanceWrapper, instanceWrapper.host ?? moduleRef, resolutionContext);
|
|
287
|
+
}
|
|
276
288
|
}
|
|
277
289
|
if (instanceWrapper.async) {
|
|
278
290
|
const host = instanceWrapper.getInstanceByContextId(this.getContextId(resolutionContext.contextId, instanceWrapper), inquirerId);
|
|
@@ -329,16 +341,12 @@ export class Injector {
|
|
|
329
341
|
this.printFoundInModuleLog(name, relatedModule);
|
|
330
342
|
instanceWrapperRef = providers.get(name);
|
|
331
343
|
this.addDependencyMetadata(keyOrIndex, wrapper, instanceWrapperRef);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
* staticity of the dependency tree and lead to undefined / null injection.
|
|
339
|
-
*/
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
344
|
+
/*
|
|
345
|
+
* Stop at the first direct export match. Continuing when the provider is
|
|
346
|
+
* already resolved would let a later import (e.g. a global forRoot module)
|
|
347
|
+
* override an explicit forFeature import for the same token.
|
|
348
|
+
*/
|
|
349
|
+
break;
|
|
342
350
|
}
|
|
343
351
|
return instanceWrapperRef;
|
|
344
352
|
}
|
|
@@ -15,7 +15,7 @@ export class InternalCoreModuleFactory {
|
|
|
15
15
|
timestamp: false,
|
|
16
16
|
});
|
|
17
17
|
const injector = new Injector({
|
|
18
|
-
preview: container.contextOptions?.preview,
|
|
18
|
+
preview: container.contextOptions?.preview ?? false,
|
|
19
19
|
instanceDecorator: container.contextOptions?.instrument?.instanceDecorator,
|
|
20
20
|
});
|
|
21
21
|
const instanceLoader = new InstanceLoader(container, injector, graphInspector, logger);
|
package/injector/module.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export declare class Module {
|
|
|
35
35
|
get entryProviders(): Array<InstanceWrapper<Injectable>>;
|
|
36
36
|
get exports(): Set<InjectionToken>;
|
|
37
37
|
get instance(): NestModule;
|
|
38
|
+
/**
|
|
39
|
+
* Modules are registered in the container before they are scanned (dynamic
|
|
40
|
+
* `imports` are pre-registered by `NestContainer#addDynamicMetadata`), so
|
|
41
|
+
* registration alone does not imply the module was ever instantiated.
|
|
42
|
+
*/
|
|
43
|
+
get isInstantiated(): boolean;
|
|
38
44
|
get metatype(): Type<any>;
|
|
39
45
|
get distance(): number;
|
|
40
46
|
set distance(value: number);
|
package/injector/module.js
CHANGED
|
@@ -4,6 +4,7 @@ import { InvalidClassException, RuntimeException, UnknownExportException, } from
|
|
|
4
4
|
import { createContextId } from '../helpers/context-id-factory.js';
|
|
5
5
|
import { getClassScope } from '../helpers/get-class-scope.js';
|
|
6
6
|
import { isDurable } from '../helpers/is-durable.js';
|
|
7
|
+
import { makeSafeInstanceDecorator } from '../helpers/safe-instance-decorator.js';
|
|
7
8
|
import { UuidFactory } from '../inspector/uuid-factory.js';
|
|
8
9
|
import { CONTROLLER_ID_KEY } from './constants.js';
|
|
9
10
|
import { InstanceWrapper } from './instance-wrapper.js';
|
|
@@ -83,6 +84,14 @@ export class Module {
|
|
|
83
84
|
const moduleRef = this._providers.get(this._metatype);
|
|
84
85
|
return moduleRef.instance;
|
|
85
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Modules are registered in the container before they are scanned (dynamic
|
|
89
|
+
* `imports` are pre-registered by `NestContainer#addDynamicMetadata`), so
|
|
90
|
+
* registration alone does not imply the module was ever instantiated.
|
|
91
|
+
*/
|
|
92
|
+
get isInstantiated() {
|
|
93
|
+
return !isNil(this._providers.get(this._metatype)?.instance);
|
|
94
|
+
}
|
|
86
95
|
get metatype() {
|
|
87
96
|
return this._metatype;
|
|
88
97
|
}
|
|
@@ -243,7 +252,9 @@ export class Module {
|
|
|
243
252
|
token: providerToken,
|
|
244
253
|
name: providerToken?.name || providerToken,
|
|
245
254
|
metatype: null,
|
|
246
|
-
instance: instanceDecorator
|
|
255
|
+
instance: instanceDecorator
|
|
256
|
+
? makeSafeInstanceDecorator(instanceDecorator)(value)
|
|
257
|
+
: value,
|
|
247
258
|
isResolved: true,
|
|
248
259
|
async: value instanceof Promise,
|
|
249
260
|
host: this,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AsyncResource } from 'async_hooks';
|
|
2
|
-
import { Observable, defer, from
|
|
3
|
-
import { mergeAll
|
|
2
|
+
import { Observable, defer, from } from 'rxjs';
|
|
3
|
+
import { mergeAll } from 'rxjs/operators';
|
|
4
4
|
import { ExecutionContextHost } from '../helpers/execution-context-host.js';
|
|
5
5
|
import { isEmptyArray } from '@nestjs/common/internal';
|
|
6
6
|
export class InterceptorsConsumer {
|
|
7
7
|
async intercept(interceptors, args, instance, callback, next, type) {
|
|
8
|
-
if (isEmptyArray(interceptors)) {
|
|
8
|
+
if (!interceptors || isEmptyArray(interceptors)) {
|
|
9
9
|
return next();
|
|
10
10
|
}
|
|
11
11
|
const context = this.createContext(args, instance, callback);
|
|
@@ -25,9 +25,34 @@ export class InterceptorsConsumer {
|
|
|
25
25
|
return new ExecutionContextHost(args, instance.constructor, callback);
|
|
26
26
|
}
|
|
27
27
|
transformDeferred(next) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
// Call next() eagerly here — this method is invoked inside
|
|
29
|
+
// defer(AsyncResource.bind(...)), so the async context (e.g. AsyncLocalStorage)
|
|
30
|
+
// is correctly inherited. Deferring next() into the subscriber function would
|
|
31
|
+
// lose that context because the subscriber is called outside the bound scope.
|
|
32
|
+
const nextPromise = next();
|
|
33
|
+
return new Observable(subscriber => {
|
|
34
|
+
let innerSub;
|
|
35
|
+
nextPromise
|
|
36
|
+
.then(res => {
|
|
37
|
+
if (subscriber.closed) {
|
|
38
|
+
// The outer subscription was torn down (e.g. an SSE client disconnect)
|
|
39
|
+
// before the async handler resolved. Do not subscribe the producer
|
|
40
|
+
// Observable after the consumer has already gone away — subscribing
|
|
41
|
+
// only to unsubscribe in the same tick would start producer side
|
|
42
|
+
// effects just to immediately abort them.
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const isDeferred = res instanceof Promise || res instanceof Observable;
|
|
46
|
+
innerSub = from(isDeferred ? res : Promise.resolve(res)).subscribe(subscriber);
|
|
47
|
+
})
|
|
48
|
+
.catch(err => {
|
|
49
|
+
if (!subscriber.closed) {
|
|
50
|
+
subscriber.error(err);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return () => {
|
|
54
|
+
innerSub?.unsubscribe();
|
|
55
|
+
};
|
|
56
|
+
});
|
|
32
57
|
}
|
|
33
58
|
}
|
package/internal.d.ts
CHANGED
|
@@ -5,28 +5,32 @@
|
|
|
5
5
|
* @internal
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
|
-
export
|
|
8
|
+
export * from './errors/exceptions/index.js';
|
|
9
9
|
export { InvalidExceptionFilterException } from './errors/exceptions/invalid-exception-filter.exception.js';
|
|
10
|
+
export { RuntimeException } from './errors/exceptions/runtime.exception.js';
|
|
10
11
|
export { MESSAGES } from './constants.js';
|
|
11
12
|
export { DependenciesScanner } from './scanner.js';
|
|
13
|
+
export { STATIC_CONTEXT } from './injector/constants.js';
|
|
12
14
|
export { Injector, InjectorDependencyContext } from './injector/injector.js';
|
|
13
15
|
export { InstanceLoader } from './injector/instance-loader.js';
|
|
14
16
|
export { InstanceWrapper } from './injector/instance-wrapper.js';
|
|
17
|
+
export { InternalCoreModule } from './injector/internal-core-module/index.js';
|
|
15
18
|
export { Module } from './injector/module.js';
|
|
16
|
-
export
|
|
17
|
-
export { ExecutionContextHost } from './helpers/execution-context-host.js';
|
|
19
|
+
export * from './inspector/index.js';
|
|
18
20
|
export { ContextUtils, ParamProperties } from './helpers/context-utils.js';
|
|
21
|
+
export { ExecutionContextHost } from './helpers/execution-context-host.js';
|
|
19
22
|
export { HandlerMetadataStorage } from './helpers/handler-metadata-storage.js';
|
|
20
|
-
export { RouterMethodFactory } from './helpers/router-method-factory.js';
|
|
21
23
|
export { loadAdapter } from './helpers/load-adapter.js';
|
|
22
24
|
export { optionalRequire } from './helpers/optional-require.js';
|
|
25
|
+
export { RouterMethodFactory } from './helpers/router-method-factory.js';
|
|
26
|
+
export { makeSafeInstanceDecorator } from './helpers/safe-instance-decorator.js';
|
|
23
27
|
export { ParamsMetadata } from './helpers/interfaces/index.js';
|
|
28
|
+
export { FORBIDDEN_MESSAGE } from './guards/constants.js';
|
|
24
29
|
export { GuardsConsumer } from './guards/guards-consumer.js';
|
|
25
30
|
export { GuardsContextCreator } from './guards/guards-context-creator.js';
|
|
26
|
-
export {
|
|
31
|
+
export { ParamsTokenFactory } from './pipes/params-token-factory.js';
|
|
27
32
|
export { PipesConsumer } from './pipes/pipes-consumer.js';
|
|
28
33
|
export { PipesContextCreator } from './pipes/pipes-context-creator.js';
|
|
29
|
-
export { ParamsTokenFactory } from './pipes/params-token-factory.js';
|
|
30
34
|
export { InterceptorsConsumer } from './interceptors/interceptors-consumer.js';
|
|
31
35
|
export { InterceptorsContextCreator } from './interceptors/interceptors-context-creator.js';
|
|
32
36
|
export { BaseExceptionFilterContext } from './exceptions/base-exception-filter-context.js';
|
package/internal.js
CHANGED
|
@@ -6,33 +6,37 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
// Errors
|
|
9
|
-
export
|
|
9
|
+
export * from './errors/exceptions/index.js';
|
|
10
10
|
export { InvalidExceptionFilterException } from './errors/exceptions/invalid-exception-filter.exception.js';
|
|
11
|
+
export { RuntimeException } from './errors/exceptions/runtime.exception.js';
|
|
11
12
|
// Constants
|
|
12
13
|
export { MESSAGES } from './constants.js';
|
|
13
14
|
// Scanner
|
|
14
15
|
export { DependenciesScanner } from './scanner.js';
|
|
15
16
|
// Injector
|
|
17
|
+
export { STATIC_CONTEXT } from './injector/constants.js';
|
|
16
18
|
export { Injector } from './injector/injector.js';
|
|
17
19
|
export { InstanceLoader } from './injector/instance-loader.js';
|
|
18
20
|
export { InstanceWrapper } from './injector/instance-wrapper.js';
|
|
21
|
+
export { InternalCoreModule } from './injector/internal-core-module/index.js';
|
|
19
22
|
export { Module } from './injector/module.js';
|
|
20
|
-
export
|
|
23
|
+
export * from './inspector/index.js';
|
|
21
24
|
// Helpers
|
|
22
|
-
export { ExecutionContextHost } from './helpers/execution-context-host.js';
|
|
23
25
|
export { ContextUtils } from './helpers/context-utils.js';
|
|
26
|
+
export { ExecutionContextHost } from './helpers/execution-context-host.js';
|
|
24
27
|
export { HandlerMetadataStorage } from './helpers/handler-metadata-storage.js';
|
|
25
|
-
export { RouterMethodFactory } from './helpers/router-method-factory.js';
|
|
26
28
|
export { loadAdapter } from './helpers/load-adapter.js';
|
|
27
29
|
export { optionalRequire } from './helpers/optional-require.js';
|
|
30
|
+
export { RouterMethodFactory } from './helpers/router-method-factory.js';
|
|
31
|
+
export { makeSafeInstanceDecorator } from './helpers/safe-instance-decorator.js';
|
|
28
32
|
// Guards
|
|
33
|
+
export { FORBIDDEN_MESSAGE } from './guards/constants.js';
|
|
29
34
|
export { GuardsConsumer } from './guards/guards-consumer.js';
|
|
30
35
|
export { GuardsContextCreator } from './guards/guards-context-creator.js';
|
|
31
|
-
export { FORBIDDEN_MESSAGE } from './guards/constants.js';
|
|
32
36
|
// Pipes
|
|
37
|
+
export { ParamsTokenFactory } from './pipes/params-token-factory.js';
|
|
33
38
|
export { PipesConsumer } from './pipes/pipes-consumer.js';
|
|
34
39
|
export { PipesContextCreator } from './pipes/pipes-context-creator.js';
|
|
35
|
-
export { ParamsTokenFactory } from './pipes/params-token-factory.js';
|
|
36
40
|
// Interceptors
|
|
37
41
|
export { InterceptorsConsumer } from './interceptors/interceptors-consumer.js';
|
|
38
42
|
export { InterceptorsContextCreator } from './interceptors/interceptors-context-creator.js';
|