@nestjs/common 11.1.8 → 11.1.10

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <a href="https://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
2
+ <a href="https://nestjs.com/" target="_blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
3
3
  </p>
4
4
 
5
5
  [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
@@ -49,7 +49,7 @@ Please make sure to read the [Issue Reporting Checklist](https://github.com/nest
49
49
 
50
50
  ## Consulting
51
51
 
52
- With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
52
+ With official support, you can get expert help straight from the Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
53
53
 
54
54
  ## Support
55
55
 
@@ -92,6 +92,8 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
92
92
  <td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
93
93
  <td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
94
94
  <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>
95
+ </tr><tr>
96
+ <td align="center" valign="middle"><a href="https://pandektes.com" target="_blank"><img src="https://nestjs.com/img/logos/pandektes-logo.png" width="65" valign="middle" /></a></td>
95
97
  </tr>
96
98
  </table>
97
99
 
@@ -1,6 +1,10 @@
1
+ import { METHOD_METADATA } from '../../constants';
2
+ import { RequestMethod } from '../../enums/request-method.enum';
1
3
  /**
2
4
  * Declares this route as a Server-Sent-Events endpoint
3
5
  *
4
6
  * @publicApi
5
7
  */
6
- export declare function Sse(path?: string): MethodDecorator;
8
+ export declare function Sse(path?: string, options?: {
9
+ [METHOD_METADATA]?: RequestMethod;
10
+ }): MethodDecorator;
@@ -8,11 +8,13 @@ const request_method_enum_1 = require("../../enums/request-method.enum");
8
8
  *
9
9
  * @publicApi
10
10
  */
11
- function Sse(path) {
11
+ function Sse(path, options = {
12
+ [constants_1.METHOD_METADATA]: request_method_enum_1.RequestMethod.GET,
13
+ }) {
12
14
  return (target, key, descriptor) => {
13
15
  path = path && path.length ? path : '/';
14
16
  Reflect.defineMetadata(constants_1.PATH_METADATA, path, descriptor.value);
15
- Reflect.defineMetadata(constants_1.METHOD_METADATA, request_method_enum_1.RequestMethod.GET, descriptor.value);
17
+ Reflect.defineMetadata(constants_1.METHOD_METADATA, options[constants_1.METHOD_METADATA], descriptor.value);
16
18
  Reflect.defineMetadata(constants_1.SSE_METADATA, true, descriptor.value);
17
19
  return descriptor;
18
20
  };
@@ -24,6 +24,7 @@ export * from './nest-application-options.interface';
24
24
  export * from './nest-application.interface';
25
25
  export * from './nest-microservice.interface';
26
26
  export * from './scope-options.interface';
27
+ export * from './shutdown-hooks-options.interface';
27
28
  export * from './type.interface';
28
29
  export * from './version-options.interface';
29
30
  export * from './websockets/web-socket-adapter.interface';
@@ -27,6 +27,7 @@ tslib_1.__exportStar(require("./nest-application-options.interface"), exports);
27
27
  tslib_1.__exportStar(require("./nest-application.interface"), exports);
28
28
  tslib_1.__exportStar(require("./nest-microservice.interface"), exports);
29
29
  tslib_1.__exportStar(require("./scope-options.interface"), exports);
30
+ tslib_1.__exportStar(require("./shutdown-hooks-options.interface"), exports);
30
31
  tslib_1.__exportStar(require("./type.interface"), exports);
31
32
  tslib_1.__exportStar(require("./version-options.interface"), exports);
32
33
  tslib_1.__exportStar(require("./websockets/web-socket-adapter.interface"), exports);
@@ -2,6 +2,7 @@ import { ShutdownSignal } from '../enums/shutdown-signal.enum';
2
2
  import { LoggerService, LogLevel } from '../services/logger.service';
3
3
  import { DynamicModule } from './modules';
4
4
  import { NestApplicationContextOptions } from './nest-application-context-options.interface';
5
+ import { ShutdownHooksOptions } from './shutdown-hooks-options.interface';
5
6
  import { Type } from './type.interface';
6
7
  export type SelectOptions = Pick<NestApplicationContextOptions, 'abortOnError'>;
7
8
  export interface GetOrResolveOptions {
@@ -121,9 +122,12 @@ export interface INestApplicationContext {
121
122
  * `onApplicationShutdown` function of a provider if the
122
123
  * process receives a shutdown signal.
123
124
  *
125
+ * @param {ShutdownSignal[] | string[]} [signals] The system signals to listen to
126
+ * @param {ShutdownHooksOptions} [options] Options for configuring shutdown hooks behavior
127
+ *
124
128
  * @returns {this} The Nest application context instance
125
129
  */
126
- enableShutdownHooks(signals?: ShutdownSignal[] | string[]): this;
130
+ enableShutdownHooks(signals?: ShutdownSignal[] | string[], options?: ShutdownHooksOptions): this;
127
131
  /**
128
132
  * Initializes the Nest application.
129
133
  * Calls the Nest lifecycle events.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Options for configuring shutdown hooks behavior.
3
+ *
4
+ * @publicApi
5
+ */
6
+ export interface ShutdownHooksOptions {
7
+ /**
8
+ * If true, uses `process.exit()` instead of `process.kill(process.pid, signal)`
9
+ * after shutdown hooks complete. This ensures the 'exit' event is properly
10
+ * triggered, which is required for async loggers (like Pino with transports)
11
+ * to flush their buffers before the process terminates.
12
+ *
13
+ * Note: Using `process.exit()` will:
14
+ * - Change the exit code (e.g., SIGTERM: 143 → 0)
15
+ * - May not trigger other signal handlers from third-party libraries
16
+ * - May affect orchestrator (Kubernetes, Docker) behavior
17
+ *
18
+ * @default false
19
+ */
20
+ useProcessExit?: boolean;
21
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs/common",
3
- "version": "11.1.8",
3
+ "version": "11.1.10",
4
4
  "description": "Nest - modern, fast, powerful node.js web framework (@common)",
5
5
  "author": "Kamil Mysliwiec",
6
6
  "homepage": "https://nestjs.com",
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "file-type": "21.0.0",
21
+ "file-type": "21.1.1",
22
22
  "iterare": "1.2.1",
23
23
  "load-esm": "1.0.3",
24
24
  "tslib": "2.8.1",
@@ -37,5 +37,6 @@
37
37
  "class-transformer": {
38
38
  "optional": true
39
39
  }
40
- }
40
+ },
41
+ "gitHead": "8ea5aaef561c87065bf0b358771de8c8b62e7835"
41
42
  }
@@ -1,7 +1,39 @@
1
+ import { FileValidatorContext } from './file-validator-context.interface';
1
2
  import { FileValidator } from './file-validator.interface';
2
3
  import { IFile } from './interfaces';
4
+ type FileTypeValidatorContext = FileValidatorContext<Omit<FileTypeValidatorOptions, 'errorMessage'>>;
3
5
  export type FileTypeValidatorOptions = {
6
+ /**
7
+ * Expected file type(s) for validation. Can be a string (MIME type)
8
+ * or a regular expression to match multiple types.
9
+ *
10
+ * @example
11
+ * // Match a single MIME type
12
+ * fileType: 'image/png'
13
+ *
14
+ * @example
15
+ * // Match multiple types using RegExp
16
+ * fileType: /^image\/(png|jpeg)$/
17
+ */
4
18
  fileType: string | RegExp;
19
+ /**
20
+ * Custom error message displayed when file type validation fails
21
+ * Can be provided as a static string, or as a factory function
22
+ * that receives the validation context (file and validator configuration)
23
+ * and returns a dynamic error message.
24
+ *
25
+ * @example
26
+ * // Static message
27
+ * new FileTypeValidator({ fileType: 'image/png', errorMessage: 'Only PNG allowed' })
28
+ *
29
+ * @example
30
+ * // Dynamic message based on file object and validator configuration
31
+ * new FileTypeValidator({
32
+ * fileType: 'image/png',
33
+ * errorMessage: ctx => `Received file type '${ctx.file?.mimetype}', but expected '${ctx.config.fileType}'`
34
+ * })
35
+ */
36
+ errorMessage?: string | ((ctx: FileTypeValidatorContext) => string);
5
37
  /**
6
38
  * If `true`, the validator will skip the magic numbers validation.
7
39
  * This can be useful when you can't identify some files as there are no common magic numbers available for some file types.
@@ -27,3 +59,4 @@ export declare class FileTypeValidator extends FileValidator<FileTypeValidatorOp
27
59
  buildErrorMessage(file?: IFile): string;
28
60
  isValid(file?: IFile): Promise<boolean>;
29
61
  }
62
+ export {};
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FileTypeValidator = void 0;
4
+ const logger_service_1 = require("../../services/logger.service");
4
5
  const file_validator_interface_1 = require("./file-validator.interface");
5
6
  const load_esm_1 = require("load-esm");
7
+ const logger = new logger_service_1.Logger('FileTypeValidator');
6
8
  /**
7
9
  * Defines the built-in FileTypeValidator. It validates incoming files by examining
8
10
  * their magic numbers using the file-type package, providing more reliable file type validation
@@ -14,9 +16,14 @@ const load_esm_1 = require("load-esm");
14
16
  */
15
17
  class FileTypeValidator extends file_validator_interface_1.FileValidator {
16
18
  buildErrorMessage(file) {
17
- const expected = this.validationOptions.fileType;
19
+ const { errorMessage, ...config } = this.validationOptions;
20
+ if (errorMessage) {
21
+ return typeof errorMessage === 'function'
22
+ ? errorMessage({ file, config })
23
+ : errorMessage;
24
+ }
18
25
  if (file?.mimetype) {
19
- const baseMessage = `Validation failed (current file type is ${file.mimetype}, expected type is ${expected})`;
26
+ const baseMessage = `Validation failed (current file type is ${file.mimetype}, expected type is ${this.validationOptions.fileType})`;
20
27
  /**
21
28
  * If fallbackToMimetype is enabled, this means the validator failed to detect the file type
22
29
  * via magic number inspection (e.g. due to an unknown or too short buffer),
@@ -29,7 +36,7 @@ class FileTypeValidator extends file_validator_interface_1.FileValidator {
29
36
  }
30
37
  return baseMessage;
31
38
  }
32
- return `Validation failed (expected type is ${expected})`;
39
+ return `Validation failed (expected type is ${this.validationOptions.fileType})`;
33
40
  }
34
41
  async isValid(file) {
35
42
  if (!this.validationOptions) {
@@ -40,10 +47,23 @@ class FileTypeValidator extends file_validator_interface_1.FileValidator {
40
47
  if (this.validationOptions.skipMagicNumbersValidation) {
41
48
  return (isFileValid && !!file.mimetype.match(this.validationOptions.fileType));
42
49
  }
43
- if (!isFileValid || !file.buffer)
50
+ if (!isFileValid)
51
+ return false;
52
+ if (!file.buffer) {
53
+ if (this.validationOptions.fallbackToMimetype) {
54
+ return !!file.mimetype.match(this.validationOptions.fileType);
55
+ }
44
56
  return false;
57
+ }
45
58
  try {
46
- const { fileTypeFromBuffer } = await (0, load_esm_1.loadEsm)('file-type');
59
+ let fileTypePath;
60
+ try {
61
+ fileTypePath = require.resolve('file-type');
62
+ }
63
+ catch {
64
+ fileTypePath = 'file-type';
65
+ }
66
+ const { fileTypeFromBuffer } = await (0, load_esm_1.loadEsm)(fileTypePath);
47
67
  const fileType = await fileTypeFromBuffer(file.buffer);
48
68
  if (fileType) {
49
69
  // Match detected mime type against allowed type
@@ -59,7 +79,20 @@ class FileTypeValidator extends file_validator_interface_1.FileValidator {
59
79
  }
60
80
  return false;
61
81
  }
62
- catch {
82
+ catch (error) {
83
+ const errorMessage = error instanceof Error ? error.message : String(error);
84
+ // Check for common ESM loading issues
85
+ if (errorMessage.includes('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING') ||
86
+ errorMessage.includes('Cannot find module') ||
87
+ errorMessage.includes('ERR_MODULE_NOT_FOUND')) {
88
+ logger.warn(`Failed to load the "file-type" package for magic number validation. ` +
89
+ `If you are using Jest, run it with NODE_OPTIONS="--experimental-vm-modules". ` +
90
+ `Error: ${errorMessage}`);
91
+ }
92
+ // Fallback to mimetype if enabled
93
+ if (this.validationOptions.fallbackToMimetype) {
94
+ return !!file.mimetype.match(this.validationOptions.fileType);
95
+ }
63
96
  return false;
64
97
  }
65
98
  }
@@ -0,0 +1,5 @@
1
+ import { IFile } from './interfaces';
2
+ export type FileValidatorContext<TConfig> = {
3
+ file?: IFile;
4
+ config: TConfig;
5
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,8 +1,34 @@
1
+ import { FileValidatorContext } from './file-validator-context.interface';
1
2
  import { FileValidator } from './file-validator.interface';
2
3
  import { IFile } from './interfaces';
4
+ type MaxFileSizeValidatorContext = FileValidatorContext<Omit<MaxFileSizeValidatorOptions, 'errorMessage' | 'message'>>;
3
5
  export type MaxFileSizeValidatorOptions = {
6
+ /**
7
+ * Maximum allowed file size in bytes.
8
+ */
4
9
  maxSize: number;
10
+ /**
11
+ * @deprecated Use `errorMessage` instead.
12
+ */
5
13
  message?: string | ((maxSize: number) => string);
14
+ /**
15
+ * Custom error message returned when file size validation fails.
16
+ * Can be provided as a static string, or as a factory function
17
+ * that receives the validation context (file and validator configuration)
18
+ * and returns a dynamic error message.
19
+ *
20
+ * @example
21
+ * // Static message
22
+ * new MaxFileSizeValidator({ maxSize: 1000, errorMessage: 'File size exceeds the limit' })
23
+ *
24
+ * @example
25
+ * // Dynamic message based on file object and validator configuration
26
+ * new MaxFileSizeValidator({
27
+ * maxSize: 1000,
28
+ * errorMessage: ctx => `Received file size is ${ctx.file?.size}, but it must be smaller than ${ctx.config.maxSize}.`
29
+ * })
30
+ */
31
+ errorMessage?: string | ((ctx: MaxFileSizeValidatorContext) => string);
6
32
  };
7
33
  /**
8
34
  * Defines the built-in MaxSize File Validator
@@ -15,3 +41,4 @@ export declare class MaxFileSizeValidator extends FileValidator<MaxFileSizeValid
15
41
  buildErrorMessage(file?: IFile): string;
16
42
  isValid(file?: IFile): boolean;
17
43
  }
44
+ export {};
@@ -11,11 +11,16 @@ const file_validator_interface_1 = require("./file-validator.interface");
11
11
  */
12
12
  class MaxFileSizeValidator extends file_validator_interface_1.FileValidator {
13
13
  buildErrorMessage(file) {
14
- if ('message' in this.validationOptions) {
15
- if (typeof this.validationOptions.message === 'function') {
16
- return this.validationOptions.message(this.validationOptions.maxSize);
17
- }
18
- return this.validationOptions.message;
14
+ const { errorMessage, message, ...config } = this.validationOptions;
15
+ if (errorMessage) {
16
+ return typeof errorMessage === 'function'
17
+ ? errorMessage({ file, config })
18
+ : errorMessage;
19
+ }
20
+ if (message) {
21
+ return typeof message === 'function'
22
+ ? message(this.validationOptions.maxSize)
23
+ : message;
19
24
  }
20
25
  if (file?.size) {
21
26
  return `Validation failed (current file size is ${file.size}, expected size is less than ${this.validationOptions.maxSize})`;
@@ -175,6 +175,8 @@ let ValidationPipe = class ValidationPipe {
175
175
  return;
176
176
  }
177
177
  delete value.__proto__;
178
+ delete value.constructor;
179
+ delete value.prototype;
178
180
  for (const key in value) {
179
181
  this.stripProtoKeys(value[key]);
180
182
  }
@@ -21,9 +21,12 @@ function isLogLevelEnabled(targetLevel, logLevels) {
21
21
  if (logLevels.includes(targetLevel)) {
22
22
  return true;
23
23
  }
24
- const highestLogLevelValue = logLevels
25
- .map(level => LOG_LEVEL_VALUES[level])
26
- .sort((a, b) => b - a)?.[0];
24
+ let highestLogLevelValue = -Infinity;
25
+ for (const level of logLevels) {
26
+ const v = LOG_LEVEL_VALUES[level];
27
+ if (v > highestLogLevelValue)
28
+ highestLogLevelValue = v;
29
+ }
27
30
  const targetLevelValue = LOG_LEVEL_VALUES[targetLevel];
28
31
  return targetLevelValue >= highestLogLevelValue;
29
32
  }