@nestjs/core 11.1.9 → 11.1.11

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.
@@ -31,7 +31,7 @@ class AbstractInstanceResolver {
31
31
  const pluckInstance = async (instanceLink) => {
32
32
  const { wrapperRef, collection } = instanceLink;
33
33
  if (wrapperRef.isDependencyTreeStatic() && !wrapperRef.isTransient) {
34
- return this.get(typeOrToken, { strict: options?.strict });
34
+ return wrapperRef.instance;
35
35
  }
36
36
  const ctorHost = wrapperRef.instance || { constructor: typeOrToken };
37
37
  const instance = await this.injector.loadPerContext(ctorHost, wrapperRef.host, collection, contextId, wrapperRef);
@@ -257,7 +257,11 @@ class Injector {
257
257
  * instantiated beforehand.
258
258
  */
259
259
  instanceHost.donePromise &&
260
- void instanceHost.donePromise.then(() => this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer));
260
+ void instanceHost.donePromise
261
+ .then(() => this.loadProvider(instanceWrapper, moduleRef, contextId, inquirer))
262
+ .catch(err => {
263
+ instanceWrapper.settlementSignal?.error(err);
264
+ });
261
265
  }
262
266
  if (instanceWrapper.async) {
263
267
  const host = instanceWrapper.getInstanceByContextId(this.getContextId(contextId, instanceWrapper), inquirerId);
@@ -240,14 +240,29 @@ class InstanceWrapper {
240
240
  (isSelfRequested || !!(inquirer && inquirer.scope === common_1.Scope.TRANSIENT)));
241
241
  }
242
242
  isStatic(contextId, inquirer) {
243
+ if (!this.isDependencyTreeStatic() || contextId !== constants_1.STATIC_CONTEXT) {
244
+ return false;
245
+ }
246
+ // Non-transient provider in static context
247
+ if (!this.isTransient) {
248
+ return true;
249
+ }
243
250
  const isInquirerRequestScoped = inquirer && !inquirer.isDependencyTreeStatic();
244
251
  const isStaticTransient = this.isTransient && !isInquirerRequestScoped;
245
252
  const rootInquirer = inquirer?.getRootInquirer();
246
- return (this.isDependencyTreeStatic() &&
247
- contextId === constants_1.STATIC_CONTEXT &&
248
- (!this.isTransient ||
249
- (isStaticTransient && !!inquirer && !inquirer.isTransient) ||
250
- (isStaticTransient && !!rootInquirer && !rootInquirer.isTransient)));
253
+ // Transient provider inquired by non-transient (e.g., DEFAULT -> TRANSIENT)
254
+ if (isStaticTransient && inquirer && !inquirer.isTransient) {
255
+ return true;
256
+ }
257
+ // Nested transient with non-transient root (e.g., DEFAULT -> TRANSIENT -> TRANSIENT)
258
+ if (isStaticTransient && rootInquirer && !rootInquirer.isTransient) {
259
+ return true;
260
+ }
261
+ // Nested transient during initial instantiation (rootInquirer not yet set)
262
+ if (isStaticTransient && inquirer?.isTransient && !rootInquirer) {
263
+ return true;
264
+ }
265
+ return false;
251
266
  }
252
267
  attachRootInquirer(inquirer) {
253
268
  if (!this.isTransient) {
@@ -1,5 +1,5 @@
1
1
  import { INestApplicationContext, Logger, LoggerService, LogLevel, ShutdownSignal } from '@nestjs/common';
2
- import { DynamicModule, SelectOptions, Type } from '@nestjs/common/interfaces';
2
+ import { DynamicModule, SelectOptions, ShutdownHooksOptions, Type } from '@nestjs/common/interfaces';
3
3
  import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface';
4
4
  import { AbstractInstanceResolver } from './injector/abstract-instance-resolver';
5
5
  import { NestContainer } from './injector/container';
@@ -124,18 +124,20 @@ export declare class NestApplicationContext<TOptions extends NestApplicationCont
124
124
  * process receives a shutdown signal.
125
125
  *
126
126
  * @param {ShutdownSignal[]} [signals=[]] The system signals it should listen to
127
+ * @param {ShutdownHooksOptions} [options={}] Options for configuring shutdown hooks behavior
127
128
  *
128
129
  * @returns {this} The Nest application context instance
129
130
  */
130
- enableShutdownHooks(signals?: (ShutdownSignal | string)[]): this;
131
+ enableShutdownHooks(signals?: (ShutdownSignal | string)[], options?: ShutdownHooksOptions): this;
131
132
  protected dispose(): Promise<void>;
132
133
  /**
133
134
  * Listens to shutdown signals by listening to
134
135
  * process events
135
136
  *
136
137
  * @param {string[]} signals The system signals it should listen to
138
+ * @param {ShutdownHooksOptions} options Options for configuring shutdown hooks behavior
137
139
  */
138
- protected listenToShutdownSignals(signals: string[]): void;
140
+ protected listenToShutdownSignals(signals: string[], options?: ShutdownHooksOptions): void;
139
141
  /**
140
142
  * Unsubscribes from shutdown signals (process events)
141
143
  */
@@ -161,10 +161,11 @@ class NestApplicationContext extends abstract_instance_resolver_1.AbstractInstan
161
161
  * process receives a shutdown signal.
162
162
  *
163
163
  * @param {ShutdownSignal[]} [signals=[]] The system signals it should listen to
164
+ * @param {ShutdownHooksOptions} [options={}] Options for configuring shutdown hooks behavior
164
165
  *
165
166
  * @returns {this} The Nest application context instance
166
167
  */
167
- enableShutdownHooks(signals = []) {
168
+ enableShutdownHooks(signals = [], options = {}) {
168
169
  if ((0, shared_utils_1.isEmpty)(signals)) {
169
170
  signals = Object.keys(common_1.ShutdownSignal).map((key) => common_1.ShutdownSignal[key]);
170
171
  }
@@ -178,7 +179,7 @@ class NestApplicationContext extends abstract_instance_resolver_1.AbstractInstan
178
179
  // filter out the signals which is already listening to
179
180
  .filter(signal => !this.activeShutdownSignals.includes(signal))
180
181
  .toArray();
181
- this.listenToShutdownSignals(signals);
182
+ this.listenToShutdownSignals(signals, options);
182
183
  return this;
183
184
  }
184
185
  async dispose() {
@@ -191,8 +192,9 @@ class NestApplicationContext extends abstract_instance_resolver_1.AbstractInstan
191
192
  * process events
192
193
  *
193
194
  * @param {string[]} signals The system signals it should listen to
195
+ * @param {ShutdownHooksOptions} options Options for configuring shutdown hooks behavior
194
196
  */
195
- listenToShutdownSignals(signals) {
197
+ listenToShutdownSignals(signals, options = {}) {
196
198
  let receivedSignal = false;
197
199
  const cleanup = async (signal) => {
198
200
  try {
@@ -208,7 +210,15 @@ class NestApplicationContext extends abstract_instance_resolver_1.AbstractInstan
208
210
  await this.dispose();
209
211
  await this.callShutdownHook(signal);
210
212
  signals.forEach(sig => process.removeListener(sig, cleanup));
211
- process.kill(process.pid, signal);
213
+ if (options.useProcessExit) {
214
+ // Use process.exit() to ensure the 'exit' event is properly triggered.
215
+ // This is required for async loggers (like Pino with transports)
216
+ // to flush their buffers before the process terminates.
217
+ process.exit(0);
218
+ }
219
+ else {
220
+ process.kill(process.pid, signal);
221
+ }
212
222
  }
213
223
  catch (err) {
214
224
  common_1.Logger.error(constants_1.MESSAGES.ERROR_DURING_SHUTDOWN, err?.stack, NestApplicationContext.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs/core",
3
- "version": "11.1.9",
3
+ "version": "11.1.11",
4
4
  "description": "Nest - modern, fast, powerful node.js web framework (@core)",
5
5
  "author": "Kamil Mysliwiec",
6
6
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  "uid": "2.0.2"
40
40
  },
41
41
  "devDependencies": {
42
- "@nestjs/common": "11.1.9"
42
+ "@nestjs/common": "11.1.11"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@nestjs/common": "^11.0.0",
@@ -59,5 +59,6 @@
59
59
  "@nestjs/platform-express": {
60
60
  "optional": true
61
61
  }
62
- }
62
+ },
63
+ "gitHead": "20529c07e3cc6647fa7611ef6eb776ad5507a325"
63
64
  }
@@ -166,8 +166,8 @@ class RouterExecutionContext {
166
166
  }
167
167
  const isSseHandler = !!this.reflectSse(callback);
168
168
  if (isSseHandler) {
169
- return (result, res, req) => {
170
- this.responseController.sse(result, res.raw || res, req.raw || req, { additionalHeaders: res.getHeaders?.() });
169
+ return async (result, res, req) => {
170
+ await this.responseController.sse(result, res.raw || res, req.raw || req, { additionalHeaders: res.getHeaders?.() });
171
171
  };
172
172
  }
173
173
  return async (result, res) => {
@@ -21,8 +21,8 @@ export declare class RouterResponseController {
21
21
  getStatusByMethod(requestMethod: RequestMethod): number;
22
22
  setHeaders<TResponse = unknown>(response: TResponse, headers: CustomHeader[]): void;
23
23
  setStatus<TResponse = unknown>(response: TResponse, statusCode: number): void;
24
- sse<TInput extends Observable<unknown> = any, TResponse extends WritableHeaderStream = any, TRequest extends IncomingMessage = any>(result: TInput, response: TResponse, request: TRequest, options?: {
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
25
  additionalHeaders: AdditionalHeaders;
26
- }): void;
26
+ }): Promise<void>;
27
27
  private assertObservable;
28
28
  }
@@ -48,15 +48,21 @@ class RouterResponseController {
48
48
  setStatus(response, statusCode) {
49
49
  this.applicationRef.status(response, statusCode);
50
50
  }
51
- sse(result, response, request, options) {
51
+ async sse(result, response, request, options) {
52
52
  // It's possible that we sent headers already so don't use a stream
53
53
  if (response.writableEnded) {
54
54
  return;
55
55
  }
56
- this.assertObservable(result);
56
+ const observableResult = await Promise.resolve(result);
57
+ this.assertObservable(observableResult);
57
58
  const stream = new sse_stream_1.SseStream(request);
58
- stream.pipe(response, options);
59
- const subscription = result
59
+ // Extract custom status code from response if it was set
60
+ const customStatusCode = response.statusCode;
61
+ const pipeOptions = typeof customStatusCode !== 'undefined'
62
+ ? { ...options, statusCode: customStatusCode }
63
+ : options;
64
+ stream.pipe(response, pipeOptions);
65
+ const subscription = observableResult
60
66
  .pipe((0, operators_1.map)((message) => {
61
67
  if ((0, shared_utils_1.isObject)(message)) {
62
68
  return message;
@@ -31,6 +31,7 @@ export declare class SseStream extends Transform {
31
31
  constructor(req?: IncomingMessage);
32
32
  pipe<T extends WritableHeaderStream>(destination: T, options?: {
33
33
  additionalHeaders?: AdditionalHeaders;
34
+ statusCode?: number;
34
35
  end?: boolean;
35
36
  }): T;
36
37
  _transform(message: MessageEvent, encoding: string, callback: (error?: Error | null, data?: any) => void): void;
@@ -37,7 +37,8 @@ class SseStream extends stream_1.Transform {
37
37
  }
38
38
  pipe(destination, options) {
39
39
  if (destination.writeHead) {
40
- destination.writeHead(200, {
40
+ const statusCode = options?.statusCode ?? 200;
41
+ destination.writeHead(statusCode, {
41
42
  ...options?.additionalHeaders,
42
43
  // See https://github.com/dunglas/mercure/blob/master/hub/subscribe.go#L124-L130
43
44
  'Content-Type': 'text/event-stream',