@nestjs/core 12.0.1 → 12.0.3
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/adapters/http-adapter.d.ts +367 -0
- package/adapters/http-adapter.js +127 -0
- package/discovery/discoverable-meta-host-collection.js +10 -1
- package/exceptions/base-exception-filter.js +11 -3
- package/injector/helpers/is-debug-mode.util.d.ts +1 -0
- package/injector/helpers/is-debug-mode.util.js +3 -0
- package/injector/injector.d.ts +0 -1
- package/injector/injector.js +20 -9
- package/injector/instance-loader.js +6 -0
- package/injector/instance-wrapper.d.ts +0 -1
- package/injector/instance-wrapper.js +3 -5
- package/injector/lazy-module-loader/lazy-module-loader.js +26 -18
- package/injector/module.js +4 -4
- package/nest-application-context.d.ts +16 -2
- package/nest-application-context.js +34 -32
- package/package.json +3 -3
- package/router/sse-stream.d.ts +1 -0
- package/router/sse-stream.js +10 -2
|
@@ -2,83 +2,450 @@ import type { HttpServer, RequestMethod, VersioningOptions } from '@nestjs/commo
|
|
|
2
2
|
import type { RequestHandler, VersionValue } from '@nestjs/common/internal';
|
|
3
3
|
import type { NestApplicationOptions } from '@nestjs/common';
|
|
4
4
|
/**
|
|
5
|
+
* Base class for HTTP platform adapters (see `ExpressAdapter` and
|
|
6
|
+
* `FastifyAdapter` for reference implementations).
|
|
7
|
+
*
|
|
8
|
+
* It implements the {@link HttpServer} contract that the Nest core relies on,
|
|
9
|
+
* and that interface is where each method's calling conventions are
|
|
10
|
+
* documented: when the core invokes it, with which arguments, and what it
|
|
11
|
+
* expects back. This class only adds:
|
|
12
|
+
*
|
|
13
|
+
* - default implementations that delegate to the wrapped framework
|
|
14
|
+
* `instance` (`use()`, the HTTP-verb methods, `listen()`) or are inert
|
|
15
|
+
* (`init()`, `normalizePath()`, `mapException()`, `beforeClose()`, the
|
|
16
|
+
* `setOn*Hook()` setters);
|
|
17
|
+
* - storage for the native server (`httpServer`) and the framework instance
|
|
18
|
+
* (`instance`), with their accessors;
|
|
19
|
+
* - the introspection hooks used by instrumentation tooling.
|
|
20
|
+
*
|
|
21
|
+
* Every remaining {@link HttpServer} member is declared abstract here, even
|
|
22
|
+
* the ones the interface marks optional, so subclasses cannot forget them.
|
|
23
|
+
* `setBaseViewsDir()`, `useBodyParser()` and `isRouteOrderSensitive()` are
|
|
24
|
+
* not declared on this class; implement them when the platform supports
|
|
25
|
+
* them (see {@link HttpServer} for what the core does when they are absent).
|
|
26
|
+
*
|
|
27
|
+
* Keep in mind that the core also reads and writes properties of the request
|
|
28
|
+
* object (`body`, `params`, `query`, `headers`, ...), that Server-Sent Events
|
|
29
|
+
* write directly to the Node.js response, and that `app.listen()` and the
|
|
30
|
+
* WebSocket adapters use the value returned by
|
|
31
|
+
* {@link AbstractHttpAdapter.getHttpServer} as a Node.js `net.Server`; see the
|
|
32
|
+
* {@link HttpServer} documentation for details.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam TServer - Type of the native HTTP server stored in `httpServer`
|
|
35
|
+
* (e.g. `http.Server | https.Server`).
|
|
36
|
+
* @typeParam TRequest - Type of the framework request object.
|
|
37
|
+
* @typeParam TResponse - Type of the framework response object.
|
|
38
|
+
*
|
|
39
|
+
* @see [HTTP adapter](https://docs.nestjs.com/faq/http-adapter)
|
|
40
|
+
*
|
|
5
41
|
* @publicApi
|
|
6
42
|
*/
|
|
7
43
|
export declare abstract class AbstractHttpAdapter<TServer = any, TRequest = any, TResponse = any> implements HttpServer<TRequest, TResponse> {
|
|
8
44
|
protected instance?: any | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Native HTTP server created by {@link AbstractHttpAdapter.initHttpServer}
|
|
47
|
+
* and returned by {@link AbstractHttpAdapter.getHttpServer}.
|
|
48
|
+
*/
|
|
9
49
|
protected httpServer: TServer;
|
|
50
|
+
/**
|
|
51
|
+
* Callback registered through
|
|
52
|
+
* {@link AbstractHttpAdapter.setOnRouteTriggered}, if any.
|
|
53
|
+
*/
|
|
10
54
|
protected onRouteTriggered: ((requestMethod: RequestMethod, path: string) => void) | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* @param instance The framework application instance to delegate to (e.g.
|
|
57
|
+
* an Express `Application`). Subclasses typically create a default one
|
|
58
|
+
* when none is given.
|
|
59
|
+
*/
|
|
11
60
|
constructor(instance?: any | undefined);
|
|
61
|
+
/**
|
|
62
|
+
* Asynchronous setup hook, awaited by `NestFactory.create()` and again by
|
|
63
|
+
* `app.init()`, so overrides must be idempotent. No-op by default.
|
|
64
|
+
*
|
|
65
|
+
* @see {@link HttpServer.init}
|
|
66
|
+
*/
|
|
12
67
|
init(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Registers a global middleware by delegating to `instance.use(...args)`.
|
|
70
|
+
*
|
|
71
|
+
* @see {@link HttpServer.use}
|
|
72
|
+
*/
|
|
13
73
|
use(...args: any[]): any;
|
|
74
|
+
/**
|
|
75
|
+
* Registers a `GET` route by delegating to `instance.get(...args)`.
|
|
76
|
+
*
|
|
77
|
+
* @see {@link HttpServer.get}
|
|
78
|
+
*/
|
|
14
79
|
get(handler: RequestHandler): any;
|
|
15
80
|
get(path: any, handler: RequestHandler): any;
|
|
81
|
+
/**
|
|
82
|
+
* Registers a `POST` route by delegating to `instance.post(...args)`.
|
|
83
|
+
*
|
|
84
|
+
* @see {@link HttpServer.post}
|
|
85
|
+
*/
|
|
16
86
|
post(handler: RequestHandler): any;
|
|
17
87
|
post(path: any, handler: RequestHandler): any;
|
|
88
|
+
/**
|
|
89
|
+
* Registers a `HEAD` route by delegating to `instance.head(...args)`.
|
|
90
|
+
*
|
|
91
|
+
* @see {@link HttpServer.head}
|
|
92
|
+
*/
|
|
18
93
|
head(handler: RequestHandler): any;
|
|
19
94
|
head(path: any, handler: RequestHandler): any;
|
|
95
|
+
/**
|
|
96
|
+
* Registers a `DELETE` route by delegating to `instance.delete(...args)`.
|
|
97
|
+
*
|
|
98
|
+
* @see {@link HttpServer.delete}
|
|
99
|
+
*/
|
|
20
100
|
delete(handler: RequestHandler): any;
|
|
21
101
|
delete(path: any, handler: RequestHandler): any;
|
|
102
|
+
/**
|
|
103
|
+
* Registers a `PUT` route by delegating to `instance.put(...args)`.
|
|
104
|
+
*
|
|
105
|
+
* @see {@link HttpServer.put}
|
|
106
|
+
*/
|
|
22
107
|
put(handler: RequestHandler): any;
|
|
23
108
|
put(path: any, handler: RequestHandler): any;
|
|
109
|
+
/**
|
|
110
|
+
* Registers a `PATCH` route by delegating to `instance.patch(...args)`.
|
|
111
|
+
*
|
|
112
|
+
* @see {@link HttpServer.patch}
|
|
113
|
+
*/
|
|
24
114
|
patch(handler: RequestHandler): any;
|
|
25
115
|
patch(path: any, handler: RequestHandler): any;
|
|
116
|
+
/**
|
|
117
|
+
* Registers a WebDAV `PROPFIND` route by delegating to
|
|
118
|
+
* `instance.propfind(...args)`. Override when the framework exposes the
|
|
119
|
+
* verb under a different API.
|
|
120
|
+
*
|
|
121
|
+
* @see {@link HttpServer.propfind}
|
|
122
|
+
*/
|
|
26
123
|
propfind(handler: RequestHandler): any;
|
|
27
124
|
propfind(path: any, handler: RequestHandler): any;
|
|
125
|
+
/**
|
|
126
|
+
* Registers a WebDAV `PROPPATCH` route by delegating to
|
|
127
|
+
* `instance.proppatch(...args)`. Override when the framework exposes the
|
|
128
|
+
* verb under a different API.
|
|
129
|
+
*
|
|
130
|
+
* @see {@link HttpServer.proppatch}
|
|
131
|
+
*/
|
|
28
132
|
proppatch(handler: RequestHandler): any;
|
|
29
133
|
proppatch(path: any, handler: RequestHandler): any;
|
|
134
|
+
/**
|
|
135
|
+
* Registers a WebDAV `MKCOL` route by delegating to
|
|
136
|
+
* `instance.mkcol(...args)`. Override when the framework exposes the verb
|
|
137
|
+
* under a different API.
|
|
138
|
+
*
|
|
139
|
+
* @see {@link HttpServer.mkcol}
|
|
140
|
+
*/
|
|
30
141
|
mkcol(handler: RequestHandler): any;
|
|
31
142
|
mkcol(path: any, handler: RequestHandler): any;
|
|
143
|
+
/**
|
|
144
|
+
* Registers a WebDAV `COPY` route by delegating to
|
|
145
|
+
* `instance.copy(...args)`. Override when the framework exposes the verb
|
|
146
|
+
* under a different API.
|
|
147
|
+
*
|
|
148
|
+
* @see {@link HttpServer.copy}
|
|
149
|
+
*/
|
|
32
150
|
copy(handler: RequestHandler): any;
|
|
33
151
|
copy(path: any, handler: RequestHandler): any;
|
|
152
|
+
/**
|
|
153
|
+
* Registers a WebDAV `MOVE` route by delegating to
|
|
154
|
+
* `instance.move(...args)`. Override when the framework exposes the verb
|
|
155
|
+
* under a different API.
|
|
156
|
+
*
|
|
157
|
+
* @see {@link HttpServer.move}
|
|
158
|
+
*/
|
|
34
159
|
move(handler: RequestHandler): any;
|
|
35
160
|
move(path: any, handler: RequestHandler): any;
|
|
161
|
+
/**
|
|
162
|
+
* Registers a WebDAV `LOCK` route by delegating to
|
|
163
|
+
* `instance.lock(...args)`. Override when the framework exposes the verb
|
|
164
|
+
* under a different API.
|
|
165
|
+
*
|
|
166
|
+
* @see {@link HttpServer.lock}
|
|
167
|
+
*/
|
|
36
168
|
lock(handler: RequestHandler): any;
|
|
37
169
|
lock(path: any, handler: RequestHandler): any;
|
|
170
|
+
/**
|
|
171
|
+
* Registers a WebDAV `UNLOCK` route by delegating to
|
|
172
|
+
* `instance.unlock(...args)`. Override when the framework exposes the verb
|
|
173
|
+
* under a different API.
|
|
174
|
+
*
|
|
175
|
+
* @see {@link HttpServer.unlock}
|
|
176
|
+
*/
|
|
38
177
|
unlock(handler: RequestHandler): any;
|
|
39
178
|
unlock(path: any, handler: RequestHandler): any;
|
|
179
|
+
/**
|
|
180
|
+
* Registers a route for every HTTP method by delegating to
|
|
181
|
+
* `instance.all(...args)`.
|
|
182
|
+
*
|
|
183
|
+
* @see {@link HttpServer.all}
|
|
184
|
+
*/
|
|
40
185
|
all(handler: RequestHandler): any;
|
|
41
186
|
all(path: any, handler: RequestHandler): any;
|
|
187
|
+
/**
|
|
188
|
+
* Registers a `SEARCH` route by delegating to `instance.search(...args)`.
|
|
189
|
+
* Override when the framework exposes the verb under a different API.
|
|
190
|
+
*
|
|
191
|
+
* @see {@link HttpServer.search}
|
|
192
|
+
*/
|
|
42
193
|
search(handler: RequestHandler): any;
|
|
43
194
|
search(path: any, handler: RequestHandler): any;
|
|
195
|
+
/**
|
|
196
|
+
* Registers a `QUERY` route by delegating to `instance.query(...args)`.
|
|
197
|
+
* Override when the framework exposes the verb under a different API.
|
|
198
|
+
*
|
|
199
|
+
* @see {@link HttpServer.query}
|
|
200
|
+
*/
|
|
44
201
|
query(handler: RequestHandler): any;
|
|
45
202
|
query(path: any, handler: RequestHandler): any;
|
|
203
|
+
/**
|
|
204
|
+
* Registers an `OPTIONS` route by delegating to
|
|
205
|
+
* `instance.options(...args)`.
|
|
206
|
+
*
|
|
207
|
+
* @see {@link HttpServer.options}
|
|
208
|
+
*/
|
|
46
209
|
options(handler: RequestHandler): any;
|
|
47
210
|
options(path: any, handler: RequestHandler): any;
|
|
211
|
+
/**
|
|
212
|
+
* Starts listening by delegating to
|
|
213
|
+
* `instance.listen(port, hostname, callback)`. The core always passes its
|
|
214
|
+
* own callback as the last argument and expects it to be invoked once the
|
|
215
|
+
* server is bound, or with an `Error` on failure. Override when the
|
|
216
|
+
* framework instance does not expose a Node-style `listen()`, e.g. to call
|
|
217
|
+
* `httpServer.listen()` instead.
|
|
218
|
+
*
|
|
219
|
+
* @see {@link HttpServer.listen}
|
|
220
|
+
*/
|
|
48
221
|
listen(port: string | number, callback?: () => void): any;
|
|
49
222
|
listen(port: string | number, hostname: string, callback?: () => void): any;
|
|
223
|
+
/**
|
|
224
|
+
* Returns the native HTTP server stored by
|
|
225
|
+
* {@link AbstractHttpAdapter.initHttpServer} (or
|
|
226
|
+
* {@link AbstractHttpAdapter.setHttpServer}).
|
|
227
|
+
*
|
|
228
|
+
* @see {@link HttpServer.getHttpServer}
|
|
229
|
+
*/
|
|
50
230
|
getHttpServer(): TServer;
|
|
231
|
+
/**
|
|
232
|
+
* Replaces the native HTTP server returned by
|
|
233
|
+
* {@link AbstractHttpAdapter.getHttpServer}. Mostly useful for tests and
|
|
234
|
+
* for adapters that obtain the server from elsewhere.
|
|
235
|
+
*/
|
|
51
236
|
setHttpServer(httpServer: TServer): void;
|
|
237
|
+
/**
|
|
238
|
+
* Replaces the framework application instance that the default method
|
|
239
|
+
* implementations delegate to.
|
|
240
|
+
*/
|
|
52
241
|
setInstance<T = any>(instance: T): void;
|
|
242
|
+
/**
|
|
243
|
+
* Returns the framework application instance passed to the constructor
|
|
244
|
+
* (or set through {@link AbstractHttpAdapter.setInstance}).
|
|
245
|
+
*
|
|
246
|
+
* @see {@link HttpServer.getInstance}
|
|
247
|
+
*/
|
|
53
248
|
getInstance<T = any>(): T;
|
|
249
|
+
/**
|
|
250
|
+
* Converts and validates a route path before registration. Returns the
|
|
251
|
+
* path unchanged by default; override to translate Nest's path syntax to
|
|
252
|
+
* the router's and to throw on invalid paths.
|
|
253
|
+
*
|
|
254
|
+
* @see {@link HttpServer.normalizePath}
|
|
255
|
+
*/
|
|
54
256
|
normalizePath(path: string): string;
|
|
257
|
+
/**
|
|
258
|
+
* Registers a callback that the router invokes right before each route
|
|
259
|
+
* handler runs, with the resolved `RequestMethod` and the route path as
|
|
260
|
+
* declared (before {@link AbstractHttpAdapter.normalizePath}). The router
|
|
261
|
+
* wraps every handler at registration time, so the callback must be set
|
|
262
|
+
* before `app.init()` to take effect. Intended for instrumentation and
|
|
263
|
+
* devtools; there is no need to override it.
|
|
264
|
+
*/
|
|
55
265
|
setOnRouteTriggered(onRouteTriggered: (requestMethod: RequestMethod, path: string) => void): void;
|
|
266
|
+
/**
|
|
267
|
+
* Returns the callback registered through
|
|
268
|
+
* {@link AbstractHttpAdapter.setOnRouteTriggered}, if any. Read by the
|
|
269
|
+
* router when registering routes.
|
|
270
|
+
*/
|
|
56
271
|
getOnRouteTriggered(): ((requestMethod: RequestMethod, path: string) => void) | undefined;
|
|
272
|
+
/**
|
|
273
|
+
* Registers a hook to run at the start of every request. No-op by default;
|
|
274
|
+
* the built-in adapters accept a `(req, res, done) => void | Promise<void>`
|
|
275
|
+
* function and call `done()` to continue processing. Intended for
|
|
276
|
+
* instrumentation and devtools.
|
|
277
|
+
*/
|
|
57
278
|
setOnRequestHook(onRequestHook: Function): void;
|
|
279
|
+
/**
|
|
280
|
+
* Registers a hook to run once a response has been sent. No-op by default;
|
|
281
|
+
* the built-in adapters accept a `(req, res) => void | Promise<void>`
|
|
282
|
+
* function. Intended for instrumentation and devtools.
|
|
283
|
+
*/
|
|
58
284
|
setOnResponseHook(onResponseHook: Function): void;
|
|
285
|
+
/**
|
|
286
|
+
* Called by `app.close()` before the shutdown hooks run. No-op by default;
|
|
287
|
+
* override to enter a "shutting down" state.
|
|
288
|
+
*
|
|
289
|
+
* @see {@link HttpServer.beforeClose}
|
|
290
|
+
*/
|
|
59
291
|
beforeClose(): void;
|
|
292
|
+
/**
|
|
293
|
+
* Translates a framework-native error into something the exception filters
|
|
294
|
+
* understand. Invoked by the global exception layer with every error it
|
|
295
|
+
* receives, before it reaches the filters; the returned value is what the
|
|
296
|
+
* filters see. Returns the error unchanged by default. The built-in
|
|
297
|
+
* adapters map, for instance, body-parser `SyntaxError`s to
|
|
298
|
+
* `BadRequestException`.
|
|
299
|
+
*/
|
|
60
300
|
mapException(error: unknown): unknown;
|
|
301
|
+
/**
|
|
302
|
+
* Stops the server; called by `app.close()`. May return a promise.
|
|
303
|
+
*
|
|
304
|
+
* @see {@link HttpServer.close}
|
|
305
|
+
*/
|
|
61
306
|
abstract close(): any;
|
|
307
|
+
/**
|
|
308
|
+
* Creates the native server and stores it in `httpServer`, honoring the
|
|
309
|
+
* `httpsOptions`, `forceCloseConnections` and `return503OnClosing`
|
|
310
|
+
* application options. Called once when the application is constructed.
|
|
311
|
+
*
|
|
312
|
+
* @see {@link HttpServer.initHttpServer}
|
|
313
|
+
*/
|
|
62
314
|
abstract initHttpServer(options: NestApplicationOptions): any;
|
|
315
|
+
/**
|
|
316
|
+
* Serves static files; pass-through for `app.useStaticAssets()`.
|
|
317
|
+
*
|
|
318
|
+
* @see {@link HttpServer.useStaticAssets}
|
|
319
|
+
*/
|
|
63
320
|
abstract useStaticAssets(...args: any[]): any;
|
|
321
|
+
/**
|
|
322
|
+
* Configures the template engine used by
|
|
323
|
+
* {@link AbstractHttpAdapter.render}; pass-through for
|
|
324
|
+
* `app.setViewEngine()`.
|
|
325
|
+
*
|
|
326
|
+
* @see {@link HttpServer.setViewEngine}
|
|
327
|
+
*/
|
|
64
328
|
abstract setViewEngine(engine: string): any;
|
|
329
|
+
/**
|
|
330
|
+
* Returns the request host name, used for `@Controller({ host })`.
|
|
331
|
+
*
|
|
332
|
+
* @see {@link HttpServer.getRequestHostname}
|
|
333
|
+
*/
|
|
65
334
|
abstract getRequestHostname(request: any): any;
|
|
335
|
+
/**
|
|
336
|
+
* Returns the upper-case request method (`'GET'`, `'POST'`, ...).
|
|
337
|
+
*
|
|
338
|
+
* @see {@link HttpServer.getRequestMethod}
|
|
339
|
+
*/
|
|
66
340
|
abstract getRequestMethod(request: any): any;
|
|
341
|
+
/**
|
|
342
|
+
* Returns the original request URL, including the query string.
|
|
343
|
+
*
|
|
344
|
+
* @see {@link HttpServer.getRequestUrl}
|
|
345
|
+
*/
|
|
67
346
|
abstract getRequestUrl(request: any): any;
|
|
347
|
+
/**
|
|
348
|
+
* Sets the status code without sending the response.
|
|
349
|
+
*
|
|
350
|
+
* @see {@link HttpServer.status}
|
|
351
|
+
*/
|
|
68
352
|
abstract status(response: any, statusCode: number): any;
|
|
353
|
+
/**
|
|
354
|
+
* Sends the response body, handling empty bodies, `StreamableFile`,
|
|
355
|
+
* objects (as JSON) and primitives.
|
|
356
|
+
*
|
|
357
|
+
* @see {@link HttpServer.reply}
|
|
358
|
+
*/
|
|
69
359
|
abstract reply(response: any, body: any, statusCode?: number): any;
|
|
360
|
+
/**
|
|
361
|
+
* Terminates the response, optionally writing `message` first.
|
|
362
|
+
*
|
|
363
|
+
* @see {@link HttpServer.end}
|
|
364
|
+
*/
|
|
70
365
|
abstract end(response: any, message?: string): any;
|
|
366
|
+
/**
|
|
367
|
+
* Renders a view template (`@Render()`).
|
|
368
|
+
*
|
|
369
|
+
* @see {@link HttpServer.render}
|
|
370
|
+
*/
|
|
71
371
|
abstract render(response: any, view: string, options: any): any;
|
|
372
|
+
/**
|
|
373
|
+
* Issues a redirect (`@Redirect()`).
|
|
374
|
+
*
|
|
375
|
+
* @see {@link HttpServer.redirect}
|
|
376
|
+
*/
|
|
72
377
|
abstract redirect(response: any, statusCode: number, url: string): any;
|
|
378
|
+
/**
|
|
379
|
+
* Installs the global exception layer.
|
|
380
|
+
*
|
|
381
|
+
* @see {@link HttpServer.setErrorHandler}
|
|
382
|
+
*/
|
|
73
383
|
abstract setErrorHandler(handler: Function, prefix?: string): any;
|
|
384
|
+
/**
|
|
385
|
+
* Installs the catch-all handler for unmatched requests.
|
|
386
|
+
*
|
|
387
|
+
* @see {@link HttpServer.setNotFoundHandler}
|
|
388
|
+
*/
|
|
74
389
|
abstract setNotFoundHandler(handler: Function, prefix?: string): any;
|
|
390
|
+
/**
|
|
391
|
+
* Reports whether response headers have already been flushed. Must return
|
|
392
|
+
* synchronously.
|
|
393
|
+
*
|
|
394
|
+
* @see {@link HttpServer.isHeadersSent}
|
|
395
|
+
*/
|
|
75
396
|
abstract isHeadersSent(response: any): any;
|
|
397
|
+
/**
|
|
398
|
+
* Reads a response header that was set earlier. Not used by the core
|
|
399
|
+
* router; part of the base class so ecosystem packages can read headers
|
|
400
|
+
* through the adapter regardless of the platform.
|
|
401
|
+
*/
|
|
76
402
|
abstract getHeader(response: any, name: string): any;
|
|
403
|
+
/**
|
|
404
|
+
* Sets (replaces) a response header (`@Header()`).
|
|
405
|
+
*
|
|
406
|
+
* @see {@link HttpServer.setHeader}
|
|
407
|
+
*/
|
|
77
408
|
abstract setHeader(response: any, name: string, value: string): any;
|
|
409
|
+
/**
|
|
410
|
+
* Appends a value to a response header, turning it into a multi-value
|
|
411
|
+
* header (e.g. several `Set-Cookie` entries) instead of replacing it. Not
|
|
412
|
+
* used by the core router; part of the base class so ecosystem packages
|
|
413
|
+
* can append headers through the adapter regardless of the platform.
|
|
414
|
+
*/
|
|
78
415
|
abstract appendHeader(response: any, name: string, value: string): any;
|
|
416
|
+
/**
|
|
417
|
+
* Registers the default JSON and URL-encoded body parsers, exposing
|
|
418
|
+
* `req.rawBody` when `rawBody` is `true`.
|
|
419
|
+
*
|
|
420
|
+
* @see {@link HttpServer.registerParserMiddleware}
|
|
421
|
+
*/
|
|
79
422
|
abstract registerParserMiddleware(prefix?: string, rawBody?: boolean): any;
|
|
423
|
+
/**
|
|
424
|
+
* Enables CORS. The core currently calls this with `options` only; the
|
|
425
|
+
* `prefix` parameter is reserved.
|
|
426
|
+
*
|
|
427
|
+
* @see {@link HttpServer.enableCors}
|
|
428
|
+
*/
|
|
80
429
|
abstract enableCors(options?: any, prefix?: string): any;
|
|
430
|
+
/**
|
|
431
|
+
* Returns the function used to mount Nest middleware for one HTTP method.
|
|
432
|
+
* May be asynchronous.
|
|
433
|
+
*
|
|
434
|
+
* @see {@link HttpServer.createMiddlewareFactory}
|
|
435
|
+
*/
|
|
81
436
|
abstract createMiddlewareFactory(requestMethod: RequestMethod): ((path: string, callback: Function) => any) | Promise<(path: string, callback: Function) => any>;
|
|
437
|
+
/**
|
|
438
|
+
* Returns the platform identifier (`'express'`, `'fastify'`) that
|
|
439
|
+
* ecosystem packages branch on.
|
|
440
|
+
*
|
|
441
|
+
* @see {@link HttpServer.getType}
|
|
442
|
+
*/
|
|
82
443
|
abstract getType(): string;
|
|
444
|
+
/**
|
|
445
|
+
* Guards a route handler by request version for header, media-type and
|
|
446
|
+
* custom versioning.
|
|
447
|
+
*
|
|
448
|
+
* @see {@link HttpServer.applyVersionFilter}
|
|
449
|
+
*/
|
|
83
450
|
abstract applyVersionFilter(handler: Function, version: VersionValue, versioningOptions: VersioningOptions): (req: TRequest, res: TResponse, next: () => void) => Function;
|
|
84
451
|
}
|
package/adapters/http-adapter.js
CHANGED
|
@@ -1,14 +1,74 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Base class for HTTP platform adapters (see `ExpressAdapter` and
|
|
3
|
+
* `FastifyAdapter` for reference implementations).
|
|
4
|
+
*
|
|
5
|
+
* It implements the {@link HttpServer} contract that the Nest core relies on,
|
|
6
|
+
* and that interface is where each method's calling conventions are
|
|
7
|
+
* documented: when the core invokes it, with which arguments, and what it
|
|
8
|
+
* expects back. This class only adds:
|
|
9
|
+
*
|
|
10
|
+
* - default implementations that delegate to the wrapped framework
|
|
11
|
+
* `instance` (`use()`, the HTTP-verb methods, `listen()`) or are inert
|
|
12
|
+
* (`init()`, `normalizePath()`, `mapException()`, `beforeClose()`, the
|
|
13
|
+
* `setOn*Hook()` setters);
|
|
14
|
+
* - storage for the native server (`httpServer`) and the framework instance
|
|
15
|
+
* (`instance`), with their accessors;
|
|
16
|
+
* - the introspection hooks used by instrumentation tooling.
|
|
17
|
+
*
|
|
18
|
+
* Every remaining {@link HttpServer} member is declared abstract here, even
|
|
19
|
+
* the ones the interface marks optional, so subclasses cannot forget them.
|
|
20
|
+
* `setBaseViewsDir()`, `useBodyParser()` and `isRouteOrderSensitive()` are
|
|
21
|
+
* not declared on this class; implement them when the platform supports
|
|
22
|
+
* them (see {@link HttpServer} for what the core does when they are absent).
|
|
23
|
+
*
|
|
24
|
+
* Keep in mind that the core also reads and writes properties of the request
|
|
25
|
+
* object (`body`, `params`, `query`, `headers`, ...), that Server-Sent Events
|
|
26
|
+
* write directly to the Node.js response, and that `app.listen()` and the
|
|
27
|
+
* WebSocket adapters use the value returned by
|
|
28
|
+
* {@link AbstractHttpAdapter.getHttpServer} as a Node.js `net.Server`; see the
|
|
29
|
+
* {@link HttpServer} documentation for details.
|
|
30
|
+
*
|
|
31
|
+
* @typeParam TServer - Type of the native HTTP server stored in `httpServer`
|
|
32
|
+
* (e.g. `http.Server | https.Server`).
|
|
33
|
+
* @typeParam TRequest - Type of the framework request object.
|
|
34
|
+
* @typeParam TResponse - Type of the framework response object.
|
|
35
|
+
*
|
|
36
|
+
* @see [HTTP adapter](https://docs.nestjs.com/faq/http-adapter)
|
|
37
|
+
*
|
|
2
38
|
* @publicApi
|
|
3
39
|
*/
|
|
4
40
|
export class AbstractHttpAdapter {
|
|
5
41
|
instance;
|
|
42
|
+
/**
|
|
43
|
+
* Native HTTP server created by {@link AbstractHttpAdapter.initHttpServer}
|
|
44
|
+
* and returned by {@link AbstractHttpAdapter.getHttpServer}.
|
|
45
|
+
*/
|
|
6
46
|
httpServer;
|
|
47
|
+
/**
|
|
48
|
+
* Callback registered through
|
|
49
|
+
* {@link AbstractHttpAdapter.setOnRouteTriggered}, if any.
|
|
50
|
+
*/
|
|
7
51
|
onRouteTriggered;
|
|
52
|
+
/**
|
|
53
|
+
* @param instance The framework application instance to delegate to (e.g.
|
|
54
|
+
* an Express `Application`). Subclasses typically create a default one
|
|
55
|
+
* when none is given.
|
|
56
|
+
*/
|
|
8
57
|
constructor(instance) {
|
|
9
58
|
this.instance = instance;
|
|
10
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Asynchronous setup hook, awaited by `NestFactory.create()` and again by
|
|
62
|
+
* `app.init()`, so overrides must be idempotent. No-op by default.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link HttpServer.init}
|
|
65
|
+
*/
|
|
11
66
|
async init() { }
|
|
67
|
+
/**
|
|
68
|
+
* Registers a global middleware by delegating to `instance.use(...args)`.
|
|
69
|
+
*
|
|
70
|
+
* @see {@link HttpServer.use}
|
|
71
|
+
*/
|
|
12
72
|
use(...args) {
|
|
13
73
|
return this.instance.use(...args);
|
|
14
74
|
}
|
|
@@ -66,30 +126,97 @@ export class AbstractHttpAdapter {
|
|
|
66
126
|
listen(port, hostname, callback) {
|
|
67
127
|
return this.instance.listen(port, hostname, callback);
|
|
68
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Returns the native HTTP server stored by
|
|
131
|
+
* {@link AbstractHttpAdapter.initHttpServer} (or
|
|
132
|
+
* {@link AbstractHttpAdapter.setHttpServer}).
|
|
133
|
+
*
|
|
134
|
+
* @see {@link HttpServer.getHttpServer}
|
|
135
|
+
*/
|
|
69
136
|
getHttpServer() {
|
|
70
137
|
return this.httpServer;
|
|
71
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Replaces the native HTTP server returned by
|
|
141
|
+
* {@link AbstractHttpAdapter.getHttpServer}. Mostly useful for tests and
|
|
142
|
+
* for adapters that obtain the server from elsewhere.
|
|
143
|
+
*/
|
|
72
144
|
setHttpServer(httpServer) {
|
|
73
145
|
this.httpServer = httpServer;
|
|
74
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Replaces the framework application instance that the default method
|
|
149
|
+
* implementations delegate to.
|
|
150
|
+
*/
|
|
75
151
|
setInstance(instance) {
|
|
76
152
|
this.instance = instance;
|
|
77
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Returns the framework application instance passed to the constructor
|
|
156
|
+
* (or set through {@link AbstractHttpAdapter.setInstance}).
|
|
157
|
+
*
|
|
158
|
+
* @see {@link HttpServer.getInstance}
|
|
159
|
+
*/
|
|
78
160
|
getInstance() {
|
|
79
161
|
return this.instance;
|
|
80
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Converts and validates a route path before registration. Returns the
|
|
165
|
+
* path unchanged by default; override to translate Nest's path syntax to
|
|
166
|
+
* the router's and to throw on invalid paths.
|
|
167
|
+
*
|
|
168
|
+
* @see {@link HttpServer.normalizePath}
|
|
169
|
+
*/
|
|
81
170
|
normalizePath(path) {
|
|
82
171
|
return path;
|
|
83
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Registers a callback that the router invokes right before each route
|
|
175
|
+
* handler runs, with the resolved `RequestMethod` and the route path as
|
|
176
|
+
* declared (before {@link AbstractHttpAdapter.normalizePath}). The router
|
|
177
|
+
* wraps every handler at registration time, so the callback must be set
|
|
178
|
+
* before `app.init()` to take effect. Intended for instrumentation and
|
|
179
|
+
* devtools; there is no need to override it.
|
|
180
|
+
*/
|
|
84
181
|
setOnRouteTriggered(onRouteTriggered) {
|
|
85
182
|
this.onRouteTriggered = onRouteTriggered;
|
|
86
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns the callback registered through
|
|
186
|
+
* {@link AbstractHttpAdapter.setOnRouteTriggered}, if any. Read by the
|
|
187
|
+
* router when registering routes.
|
|
188
|
+
*/
|
|
87
189
|
getOnRouteTriggered() {
|
|
88
190
|
return this.onRouteTriggered;
|
|
89
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Registers a hook to run at the start of every request. No-op by default;
|
|
194
|
+
* the built-in adapters accept a `(req, res, done) => void | Promise<void>`
|
|
195
|
+
* function and call `done()` to continue processing. Intended for
|
|
196
|
+
* instrumentation and devtools.
|
|
197
|
+
*/
|
|
90
198
|
setOnRequestHook(onRequestHook) { }
|
|
199
|
+
/**
|
|
200
|
+
* Registers a hook to run once a response has been sent. No-op by default;
|
|
201
|
+
* the built-in adapters accept a `(req, res) => void | Promise<void>`
|
|
202
|
+
* function. Intended for instrumentation and devtools.
|
|
203
|
+
*/
|
|
91
204
|
setOnResponseHook(onResponseHook) { }
|
|
205
|
+
/**
|
|
206
|
+
* Called by `app.close()` before the shutdown hooks run. No-op by default;
|
|
207
|
+
* override to enter a "shutting down" state.
|
|
208
|
+
*
|
|
209
|
+
* @see {@link HttpServer.beforeClose}
|
|
210
|
+
*/
|
|
92
211
|
beforeClose() { }
|
|
212
|
+
/**
|
|
213
|
+
* Translates a framework-native error into something the exception filters
|
|
214
|
+
* understand. Invoked by the global exception layer with every error it
|
|
215
|
+
* receives, before it reaches the filters; the returned value is what the
|
|
216
|
+
* filters see. Returns the error unchanged by default. The built-in
|
|
217
|
+
* adapters map, for instance, body-parser `SyntaxError`s to
|
|
218
|
+
* `BadRequestException`.
|
|
219
|
+
*/
|
|
93
220
|
mapException(error) {
|
|
94
221
|
return error;
|
|
95
222
|
}
|
|
@@ -44,6 +44,15 @@ export class DiscoverableMetaHostCollection {
|
|
|
44
44
|
static insertByMetaKey(metaKey, instanceWrapper, collection) {
|
|
45
45
|
if (collection.has(metaKey)) {
|
|
46
46
|
const wrappers = collection.get(metaKey);
|
|
47
|
+
// provider wrappers are re-created between registration and instantiation
|
|
48
|
+
// (the prototype phase replaces the map entry with a copy), so entries
|
|
49
|
+
// are keyed by the wrapper instance id to keep one entry per provider
|
|
50
|
+
for (const existing of wrappers) {
|
|
51
|
+
if (existing.id === instanceWrapper.id) {
|
|
52
|
+
wrappers.delete(existing);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
47
56
|
wrappers.add(instanceWrapper);
|
|
48
57
|
}
|
|
49
58
|
else {
|
|
@@ -87,7 +96,7 @@ export class DiscoverableMetaHostCollection {
|
|
|
87
96
|
// of `wrapper.metatype` to resolve processor's class properly.
|
|
88
97
|
// But since calling `wrapper.instance` could degrade overall performance
|
|
89
98
|
// we must defer it as much we can.
|
|
90
|
-
instanceWrapper.metatype || instanceWrapper.inject
|
|
99
|
+
!instanceWrapper.metatype || instanceWrapper.inject
|
|
91
100
|
? (instanceWrapper.instance?.constructor ?? instanceWrapper.metatype)
|
|
92
101
|
: instanceWrapper.metatype);
|
|
93
102
|
}
|
|
@@ -22,6 +22,9 @@ export class BaseExceptionFilter {
|
|
|
22
22
|
: {
|
|
23
23
|
statusCode: exception.getStatus(),
|
|
24
24
|
message: res,
|
|
25
|
+
...(exception.errorCode !== undefined && {
|
|
26
|
+
errorCode: exception.errorCode,
|
|
27
|
+
}),
|
|
25
28
|
};
|
|
26
29
|
const response = host.getArgByIndex(1);
|
|
27
30
|
if (!applicationRef.isHeadersSent(response)) {
|
|
@@ -75,9 +78,14 @@ export class BaseExceptionFilter {
|
|
|
75
78
|
err instanceof Error) {
|
|
76
79
|
return true;
|
|
77
80
|
}
|
|
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
|
|
81
|
+
// Plain "http error"-shaped values (e.g. non-Error objects thrown by third-party
|
|
82
|
+
// middleware) that carry a valid HTTP status code and a message
|
|
83
|
+
return (!(err instanceof Error) &&
|
|
84
|
+
Number.isInteger(err.statusCode) &&
|
|
85
|
+
err.statusCode >= 400 &&
|
|
86
|
+
err.statusCode < 600 &&
|
|
87
|
+
typeof err.message === 'string' &&
|
|
88
|
+
err.message !== '');
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
__decorate([
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isDebugMode(): boolean;
|
package/injector/injector.d.ts
CHANGED
package/injector/injector.js
CHANGED
|
@@ -9,6 +9,7 @@ import { UnknownDependenciesException } from '../errors/exceptions/unknown-depen
|
|
|
9
9
|
import { Barrier } from '../helpers/barrier.js';
|
|
10
10
|
import { makeSafeInstanceDecorator } from '../helpers/safe-instance-decorator.js';
|
|
11
11
|
import { STATIC_CONTEXT } from './constants.js';
|
|
12
|
+
import { isDebugMode } from './helpers/is-debug-mode.util.js';
|
|
12
13
|
import { INQUIRER } from './inquirer/index.js';
|
|
13
14
|
import { InstanceWrapper, } from './instance-wrapper.js';
|
|
14
15
|
import { SettlementSignal } from './settlement-signal.js';
|
|
@@ -195,11 +196,24 @@ export class Injector {
|
|
|
195
196
|
* We are duplicating it here because that one is not supposed to be exported.
|
|
196
197
|
*/
|
|
197
198
|
function isOptionalFactoryDependency(value) {
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
const token = value?.token;
|
|
200
|
+
if (isNil(token)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return (!isUndefined(value.optional) &&
|
|
200
204
|
!value.prototype);
|
|
201
205
|
}
|
|
202
206
|
const mapFactoryProviderInjectArray = (item, index) => {
|
|
207
|
+
const isWrappedDependency = !isNil(item) && typeof item === 'object' && 'token' in item;
|
|
208
|
+
const token = isWrappedDependency
|
|
209
|
+
? item.token
|
|
210
|
+
: item;
|
|
211
|
+
if (isNil(token)) {
|
|
212
|
+
throw new UndefinedDependencyException(wrapper.name, {
|
|
213
|
+
index,
|
|
214
|
+
dependencies: (wrapper.inject ?? undefined),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
203
217
|
if (typeof item !== 'object') {
|
|
204
218
|
return item;
|
|
205
219
|
}
|
|
@@ -270,7 +284,7 @@ export class Injector {
|
|
|
270
284
|
*/
|
|
271
285
|
if (instanceHost.donePromise) {
|
|
272
286
|
void instanceHost.donePromise
|
|
273
|
-
.then(() => this.loadProvider(instanceWrapper, moduleRef, resolutionContext))
|
|
287
|
+
.then(() => this.loadProvider(instanceWrapper, instanceWrapper.host ?? moduleRef, resolutionContext))
|
|
274
288
|
.catch(err => {
|
|
275
289
|
instanceWrapper.settlementSignal?.error(err);
|
|
276
290
|
});
|
|
@@ -610,7 +624,7 @@ export class Injector {
|
|
|
610
624
|
return isFunction(token) ? token.name : token.toString();
|
|
611
625
|
}
|
|
612
626
|
printResolvingDependenciesLog(token, inquirer) {
|
|
613
|
-
if (!
|
|
627
|
+
if (!isDebugMode()) {
|
|
614
628
|
return;
|
|
615
629
|
}
|
|
616
630
|
const tokenName = this.getTokenName(token);
|
|
@@ -620,7 +634,7 @@ export class Injector {
|
|
|
620
634
|
this.logger.log(messageToPrint);
|
|
621
635
|
}
|
|
622
636
|
printLookingForProviderLog(token, moduleRef) {
|
|
623
|
-
if (!
|
|
637
|
+
if (!isDebugMode()) {
|
|
624
638
|
return;
|
|
625
639
|
}
|
|
626
640
|
const tokenName = this.getTokenName(token);
|
|
@@ -628,16 +642,13 @@ export class Injector {
|
|
|
628
642
|
this.logger.log(`Looking for ${clc.cyanBright(tokenName)}${clc.green(' in ')}${clc.magentaBright(moduleRefName)}`);
|
|
629
643
|
}
|
|
630
644
|
printFoundInModuleLog(token, moduleRef) {
|
|
631
|
-
if (!
|
|
645
|
+
if (!isDebugMode()) {
|
|
632
646
|
return;
|
|
633
647
|
}
|
|
634
648
|
const tokenName = this.getTokenName(token);
|
|
635
649
|
const moduleRefName = moduleRef?.metatype?.name ?? 'unknown';
|
|
636
650
|
this.logger.log(`Found ${clc.cyanBright(tokenName)}${clc.green(' in ')}${clc.magentaBright(moduleRefName)}`);
|
|
637
651
|
}
|
|
638
|
-
isDebugMode() {
|
|
639
|
-
return !!process.env.NEST_DEBUG;
|
|
640
|
-
}
|
|
641
652
|
getContextId(contextId, instanceWrapper) {
|
|
642
653
|
return contextId.getParent
|
|
643
654
|
? contextId.getParent({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Logger } from '@nestjs/common';
|
|
2
|
+
import { DiscoverableMetaHostCollection } from '../discovery/discoverable-meta-host-collection.js';
|
|
2
3
|
import { MODULE_INIT_MESSAGE } from '../helpers/messages.js';
|
|
3
4
|
import { InternalCoreModule } from './internal-core-module/internal-core-module.js';
|
|
4
5
|
export class InstanceLoader {
|
|
@@ -56,6 +57,11 @@ export class InstanceLoader {
|
|
|
56
57
|
await Promise.all(wrappers.map(async (item) => {
|
|
57
58
|
await this.injector.loadProvider(item, moduleRef);
|
|
58
59
|
this.graphInspector.inspectInstanceWrapper(item, moduleRef);
|
|
60
|
+
if (!item.isAlias) {
|
|
61
|
+
// an alias wrapper is factory-shaped, so inspecting it would
|
|
62
|
+
// discover the aliased provider twice
|
|
63
|
+
DiscoverableMetaHostCollection.inspectProvider(this.container.getModules(), item);
|
|
64
|
+
}
|
|
59
65
|
}));
|
|
60
66
|
}
|
|
61
67
|
createPrototypesOfControllers(moduleRef) {
|
|
@@ -3,6 +3,7 @@ import { clc, isNil, isString, isUndefined, randomStringGenerator, } from '@nest
|
|
|
3
3
|
import { iterate } from 'iterare';
|
|
4
4
|
import { UuidFactory } from '../inspector/uuid-factory.js';
|
|
5
5
|
import { STATIC_CONTEXT } from './constants.js';
|
|
6
|
+
import { isDebugMode } from './helpers/is-debug-mode.util.js';
|
|
6
7
|
import { isClassProvider, isFactoryProvider, isValueProvider, } from './helpers/provider-classifier.js';
|
|
7
8
|
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
|
|
8
9
|
export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id');
|
|
@@ -368,7 +369,7 @@ export class InstanceWrapper {
|
|
|
368
369
|
this.scope === Scope.TRANSIENT && (this.transientMap = new Map());
|
|
369
370
|
}
|
|
370
371
|
printIntrospectedAsRequestScoped() {
|
|
371
|
-
if (!
|
|
372
|
+
if (!isDebugMode() || this.name === 'REQUEST') {
|
|
372
373
|
return;
|
|
373
374
|
}
|
|
374
375
|
if (isString(this.name)) {
|
|
@@ -376,16 +377,13 @@ export class InstanceWrapper {
|
|
|
376
377
|
}
|
|
377
378
|
}
|
|
378
379
|
printIntrospectedAsDurable() {
|
|
379
|
-
if (!
|
|
380
|
+
if (!isDebugMode()) {
|
|
380
381
|
return;
|
|
381
382
|
}
|
|
382
383
|
if (isString(this.name)) {
|
|
383
384
|
InstanceWrapper.logger.log(`${clc.cyanBright(this.name)}${clc.green(' introspected as ')}${clc.magentaBright('durable')}`);
|
|
384
385
|
}
|
|
385
386
|
}
|
|
386
|
-
isDebugMode() {
|
|
387
|
-
return !!process.env.NEST_DEBUG;
|
|
388
|
-
}
|
|
389
387
|
generateUuid() {
|
|
390
388
|
let key = this.name?.toString() ?? this.token?.toString();
|
|
391
389
|
key += this.host?.name ?? '';
|
|
@@ -14,25 +14,33 @@ export class LazyModuleLoader {
|
|
|
14
14
|
this.moduleOverrides = moduleOverrides;
|
|
15
15
|
}
|
|
16
16
|
async load(loaderFn, loadOpts) {
|
|
17
|
-
this.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const originalLogger = this.instanceLoader.logger;
|
|
18
|
+
try {
|
|
19
|
+
this.registerLoggerConfiguration(loadOpts);
|
|
20
|
+
const moduleClassOrDynamicDefinition = await loaderFn();
|
|
21
|
+
const moduleInstances = await this.dependenciesScanner.scanForModules({
|
|
22
|
+
moduleDefinition: moduleClassOrDynamicDefinition,
|
|
23
|
+
overrides: this.moduleOverrides,
|
|
24
|
+
lazy: true,
|
|
25
|
+
});
|
|
26
|
+
if (moduleInstances.length === 0) {
|
|
27
|
+
// The module has been loaded already. In this case, we must
|
|
28
|
+
// retrieve a module reference from the existing container.
|
|
29
|
+
const { token } = await this.moduleCompiler.compile(moduleClassOrDynamicDefinition);
|
|
30
|
+
const moduleInstance = this.modulesContainer.get(token);
|
|
31
|
+
return moduleInstance && this.getTargetModuleRef(moduleInstance);
|
|
32
|
+
}
|
|
33
|
+
const lazyModulesContainer = this.createLazyModulesContainer(moduleInstances);
|
|
34
|
+
await this.dependenciesScanner.scanModulesForDependencies(lazyModulesContainer);
|
|
35
|
+
await this.instanceLoader.createInstancesOfDependencies(lazyModulesContainer);
|
|
36
|
+
const [targetModule] = moduleInstances;
|
|
37
|
+
return this.getTargetModuleRef(targetModule);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
if (loadOpts?.logger === false) {
|
|
41
|
+
this.instanceLoader.setLogger(originalLogger);
|
|
42
|
+
}
|
|
30
43
|
}
|
|
31
|
-
const lazyModulesContainer = this.createLazyModulesContainer(moduleInstances);
|
|
32
|
-
await this.dependenciesScanner.scanModulesForDependencies(lazyModulesContainer);
|
|
33
|
-
await this.instanceLoader.createInstancesOfDependencies(lazyModulesContainer);
|
|
34
|
-
const [targetModule] = moduleInstances;
|
|
35
|
-
return this.getTargetModuleRef(targetModule);
|
|
36
44
|
}
|
|
37
45
|
registerLoggerConfiguration(loadOpts) {
|
|
38
46
|
if (loadOpts?.logger === false) {
|
package/injector/module.js
CHANGED
|
@@ -208,17 +208,17 @@ export class Module {
|
|
|
208
208
|
return provider.provide;
|
|
209
209
|
}
|
|
210
210
|
isCustomClass(provider) {
|
|
211
|
-
return !
|
|
211
|
+
return !isNil(provider.useClass);
|
|
212
212
|
}
|
|
213
213
|
isCustomValue(provider) {
|
|
214
214
|
return (isObject(provider) &&
|
|
215
|
-
Object.
|
|
215
|
+
Object.hasOwn(provider, 'useValue'));
|
|
216
216
|
}
|
|
217
217
|
isCustomFactory(provider) {
|
|
218
|
-
return !
|
|
218
|
+
return !isNil(provider.useFactory);
|
|
219
219
|
}
|
|
220
220
|
isCustomUseExisting(provider) {
|
|
221
|
-
return !
|
|
221
|
+
return !isNil(provider.useExisting);
|
|
222
222
|
}
|
|
223
223
|
isDynamicModule(exported) {
|
|
224
224
|
return exported && exported.module;
|
|
@@ -19,9 +19,9 @@ export declare class NestApplicationContext<TOptions extends NestApplicationCont
|
|
|
19
19
|
protected injector: Injector;
|
|
20
20
|
protected readonly logger: Logger;
|
|
21
21
|
private shouldFlushLogsOnOverride;
|
|
22
|
-
private readonly
|
|
22
|
+
private readonly shutdownCleanupRefs;
|
|
23
23
|
private readonly moduleCompiler;
|
|
24
|
-
private
|
|
24
|
+
private shutdownPromise?;
|
|
25
25
|
private _instanceLinksHost;
|
|
26
26
|
private _moduleRefsForHooksByDistance?;
|
|
27
27
|
private initializationPromise?;
|
|
@@ -103,6 +103,17 @@ export declare class NestApplicationContext<TOptions extends NestApplicationCont
|
|
|
103
103
|
* @returns {Promise<void>}
|
|
104
104
|
*/
|
|
105
105
|
close(signal?: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Runs the shutdown sequence, at most once per cycle. Callers that arrive
|
|
108
|
+
* while a shutdown is already in flight - a process signal delivered during
|
|
109
|
+
* an explicit `close()`, or the other way round - await the very same
|
|
110
|
+
* promise instead of starting a second, concurrent teardown.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} [signal] The system signal that triggered the shutdown
|
|
113
|
+
* @returns {Promise<void>}
|
|
114
|
+
*/
|
|
115
|
+
protected shutdown(signal?: string): Promise<void>;
|
|
116
|
+
private runShutdownSequence;
|
|
106
117
|
/**
|
|
107
118
|
* Sets custom logger service.
|
|
108
119
|
* Flushes buffered logs if auto flush is on.
|
|
@@ -123,6 +134,9 @@ export declare class NestApplicationContext<TOptions extends NestApplicationCont
|
|
|
123
134
|
* `onApplicationShutdown` function of a provider if the
|
|
124
135
|
* process receives a shutdown signal.
|
|
125
136
|
*
|
|
137
|
+
* Repeated calls are idempotent per signal. Shutdown hooks can be
|
|
138
|
+
* re-enabled after the application context has been closed.
|
|
139
|
+
*
|
|
126
140
|
* @param {ShutdownSignal[]} [signals=[]] The system signals it should listen to
|
|
127
141
|
* @param {ShutdownHooksOptions} [options={}] Options for configuring shutdown hooks behavior
|
|
128
142
|
*
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Logger, ShutdownSignal, } from '@nestjs/common';
|
|
2
|
-
import { iterate } from 'iterare';
|
|
3
2
|
import { MESSAGES } from './constants.js';
|
|
4
3
|
import { UnknownModuleException } from './errors/exceptions/index.js';
|
|
5
4
|
import { createContextId } from './helpers/context-id-factory.js';
|
|
@@ -22,9 +21,9 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
22
21
|
timestamp: true,
|
|
23
22
|
});
|
|
24
23
|
shouldFlushLogsOnOverride = false;
|
|
25
|
-
|
|
24
|
+
shutdownCleanupRefs = new Map();
|
|
26
25
|
moduleCompiler;
|
|
27
|
-
|
|
26
|
+
shutdownPromise;
|
|
28
27
|
_instanceLinksHost;
|
|
29
28
|
_moduleRefsForHooksByDistance;
|
|
30
29
|
initializationPromise;
|
|
@@ -121,6 +120,26 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
121
120
|
* @returns {Promise<void>}
|
|
122
121
|
*/
|
|
123
122
|
async close(signal) {
|
|
123
|
+
await this.shutdown(signal);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Runs the shutdown sequence, at most once per cycle. Callers that arrive
|
|
127
|
+
* while a shutdown is already in flight - a process signal delivered during
|
|
128
|
+
* an explicit `close()`, or the other way round - await the very same
|
|
129
|
+
* promise instead of starting a second, concurrent teardown.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} [signal] The system signal that triggered the shutdown
|
|
132
|
+
* @returns {Promise<void>}
|
|
133
|
+
*/
|
|
134
|
+
shutdown(signal) {
|
|
135
|
+
this.shutdownPromise ??= this.runShutdownSequence(signal).finally(() => {
|
|
136
|
+
// Let the context be shut down again once this cycle has settled,
|
|
137
|
+
// successfully or not.
|
|
138
|
+
this.shutdownPromise = undefined;
|
|
139
|
+
});
|
|
140
|
+
return this.shutdownPromise;
|
|
141
|
+
}
|
|
142
|
+
async runShutdownSequence(signal) {
|
|
124
143
|
await this.initializationPromise;
|
|
125
144
|
await this.prepareClose();
|
|
126
145
|
await this.callDestroyHook();
|
|
@@ -158,6 +177,9 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
158
177
|
* `onApplicationShutdown` function of a provider if the
|
|
159
178
|
* process receives a shutdown signal.
|
|
160
179
|
*
|
|
180
|
+
* Repeated calls are idempotent per signal. Shutdown hooks can be
|
|
181
|
+
* re-enabled after the application context has been closed.
|
|
182
|
+
*
|
|
161
183
|
* @param {ShutdownSignal[]} [signals=[]] The system signals it should listen to
|
|
162
184
|
* @param {ShutdownHooksOptions} [options={}] Options for configuring shutdown hooks behavior
|
|
163
185
|
*
|
|
@@ -167,16 +189,7 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
167
189
|
if (!signals || isEmptyArray(signals)) {
|
|
168
190
|
signals = Object.values(ShutdownSignal);
|
|
169
191
|
}
|
|
170
|
-
|
|
171
|
-
// given signals array should be unique because
|
|
172
|
-
// process shouldn't listen to the same signal more than once.
|
|
173
|
-
signals = Array.from(new Set(signals));
|
|
174
|
-
}
|
|
175
|
-
signals = iterate(signals)
|
|
176
|
-
.map((signal) => signal.toString().toUpperCase().trim())
|
|
177
|
-
// filter out the signals which is already listening to
|
|
178
|
-
.filter(signal => !this.activeShutdownSignals.includes(signal))
|
|
179
|
-
.toArray();
|
|
192
|
+
signals = Array.from(new Set(signals.map((signal) => signal.toString().toUpperCase().trim()))).filter(signal => !this.shutdownCleanupRefs.has(signal));
|
|
180
193
|
this.listenToShutdownSignals(signals, options);
|
|
181
194
|
return this;
|
|
182
195
|
}
|
|
@@ -198,22 +211,14 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
198
211
|
* @param {ShutdownHooksOptions} options Options for configuring shutdown hooks behavior
|
|
199
212
|
*/
|
|
200
213
|
listenToShutdownSignals(signals, options = {}) {
|
|
201
|
-
let receivedSignal = false;
|
|
202
214
|
const cleanup = async (signal) => {
|
|
203
215
|
try {
|
|
204
|
-
if (
|
|
205
|
-
// If
|
|
206
|
-
//
|
|
216
|
+
if (this.shutdownPromise) {
|
|
217
|
+
// If a shutdown is already under way - because of another signal or
|
|
218
|
+
// an explicit `close()` call - just ignore this one.
|
|
207
219
|
return;
|
|
208
220
|
}
|
|
209
|
-
|
|
210
|
-
await this.initializationPromise;
|
|
211
|
-
await this.prepareClose();
|
|
212
|
-
await this.callDestroyHook();
|
|
213
|
-
await this.callBeforeShutdownHook(signal);
|
|
214
|
-
await this.dispose();
|
|
215
|
-
await this.callShutdownHook(signal);
|
|
216
|
-
signals.forEach(sig => process.removeListener(sig, cleanup));
|
|
221
|
+
await this.shutdown(signal);
|
|
217
222
|
if (options.useProcessExit) {
|
|
218
223
|
// Use process.exit() to ensure the 'exit' event is properly triggered.
|
|
219
224
|
// This is required for async loggers (like Pino with transports)
|
|
@@ -229,9 +234,8 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
229
234
|
process.exit(1);
|
|
230
235
|
}
|
|
231
236
|
};
|
|
232
|
-
this.shutdownCleanupRef = cleanup;
|
|
233
237
|
signals.forEach((signal) => {
|
|
234
|
-
this.
|
|
238
|
+
this.shutdownCleanupRefs.set(signal, cleanup);
|
|
235
239
|
process.on(signal, cleanup);
|
|
236
240
|
});
|
|
237
241
|
}
|
|
@@ -239,12 +243,10 @@ export class NestApplicationContext extends AbstractInstanceResolver {
|
|
|
239
243
|
* Unsubscribes from shutdown signals (process events)
|
|
240
244
|
*/
|
|
241
245
|
unsubscribeFromProcessSignals() {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
this.activeShutdownSignals.forEach(signal => {
|
|
246
|
-
process.removeListener(signal, this.shutdownCleanupRef);
|
|
246
|
+
this.shutdownCleanupRefs.forEach((cleanup, signal) => {
|
|
247
|
+
process.removeListener(signal, cleanup);
|
|
247
248
|
});
|
|
249
|
+
this.shutdownCleanupRefs.clear();
|
|
248
250
|
}
|
|
249
251
|
/**
|
|
250
252
|
* Calls the `onModuleInit` function on the registered
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestjs/core",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.3",
|
|
4
4
|
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
|
|
5
5
|
"author": "Kamil Mysliwiec",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"uid": "2.0.2"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@nestjs/common": "12.0.
|
|
39
|
+
"@nestjs/common": "12.0.3"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@nestjs/common": "^12.0.0",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"optional": true
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "14151790e1abe870abc3bbf00f88786fc3ebe509"
|
|
61
61
|
}
|
package/router/sse-stream.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class SseStream extends Transform {
|
|
|
33
33
|
private _destination;
|
|
34
34
|
private _statusCode;
|
|
35
35
|
private _additionalHeaders;
|
|
36
|
+
private readonly _isHttp2;
|
|
36
37
|
constructor(req?: IncomingMessage);
|
|
37
38
|
get headersCommitted(): boolean;
|
|
38
39
|
pipe<T extends WritableHeaderStream>(destination: T, options?: {
|
package/router/sse-stream.js
CHANGED
|
@@ -41,9 +41,15 @@ export class SseStream extends Transform {
|
|
|
41
41
|
_destination = null;
|
|
42
42
|
_statusCode = 200;
|
|
43
43
|
_additionalHeaders;
|
|
44
|
+
_isHttp2;
|
|
44
45
|
constructor(req) {
|
|
45
46
|
super({ objectMode: true });
|
|
46
|
-
|
|
47
|
+
this._isHttp2 = (req?.httpVersionMajor ?? 1) > 1;
|
|
48
|
+
// Under HTTP/2 these calls are not request-scoped: `setTimeout` is routed
|
|
49
|
+
// to the shared `Http2Session` and the rest to the shared TCP socket, so
|
|
50
|
+
// tuning one SSE request would disable the idle timeout for every stream
|
|
51
|
+
// on that connection. See https://nodejs.org/api/http2.html#requestsocket
|
|
52
|
+
if (req && req.socket && !this._isHttp2) {
|
|
47
53
|
req.socket.setKeepAlive(true);
|
|
48
54
|
req.socket.setNoDelay(true);
|
|
49
55
|
req.socket.setTimeout(0);
|
|
@@ -79,7 +85,9 @@ export class SseStream extends Transform {
|
|
|
79
85
|
...additionalHeaders,
|
|
80
86
|
// See https://github.com/dunglas/mercure/blob/main/subscribe.go#L347-L362
|
|
81
87
|
'Content-Type': 'text/event-stream',
|
|
82
|
-
|
|
88
|
+
// Hop-by-hop header, forbidden in HTTP/2
|
|
89
|
+
// https://www.rfc-editor.org/rfc/rfc9113#section-8.2.2
|
|
90
|
+
...(!this._isHttp2 && { Connection: 'keep-alive' }),
|
|
83
91
|
// Disable cache, even for old browsers and proxies
|
|
84
92
|
'Cache-Control': 'private, no-cache, no-store, must-revalidate, max-age=0, no-transform',
|
|
85
93
|
Pragma: 'no-cache',
|