@nestjs/common 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.
Files changed (45) hide show
  1. package/Readme.md +25 -46
  2. package/decorators/http/create-route-param-metadata.decorator.js +5 -10
  3. package/decorators/http/index.d.ts +1 -0
  4. package/decorators/http/index.js +1 -0
  5. package/decorators/http/request-mapping.decorator.d.ts +11 -0
  6. package/decorators/http/request-mapping.decorator.js +11 -0
  7. package/decorators/http/route-params.decorator.d.ts +28 -2
  8. package/decorators/http/route-params.decorator.js +56 -32
  9. package/decorators/http/sse-signal.decorator.d.ts +47 -0
  10. package/decorators/http/sse-signal.decorator.js +52 -0
  11. package/enums/request-method.enum.d.ts +2 -1
  12. package/enums/request-method.enum.js +1 -0
  13. package/exceptions/misdirected.exception.d.ts +1 -1
  14. package/exceptions/misdirected.exception.js +1 -1
  15. package/exceptions/not-acceptable.exception.d.ts +2 -2
  16. package/exceptions/not-acceptable.exception.js +2 -2
  17. package/index.d.ts +1 -1
  18. package/interfaces/http/http-server.interface.d.ts +2 -0
  19. package/interfaces/http/message-event.interface.d.ts +2 -1
  20. package/interfaces/index.d.ts +1 -0
  21. package/interfaces/index.js +1 -0
  22. package/interfaces/microservices/transport-server.interface.d.ts +18 -0
  23. package/interfaces/microservices/transport-server.interface.js +1 -0
  24. package/interfaces/nest-microservice.interface.d.ts +7 -0
  25. package/internal.d.ts +1 -0
  26. package/internal.js +1 -0
  27. package/package.json +4 -3
  28. package/pipes/file/file-type.validator.d.ts +8 -0
  29. package/pipes/file/file-type.validator.js +17 -5
  30. package/pipes/parse-array.pipe.d.ts +5 -3
  31. package/pipes/parse-date.pipe.d.ts +6 -0
  32. package/pipes/parse-date.pipe.js +3 -0
  33. package/pipes/parse-enum.pipe.js +7 -1
  34. package/pipes/parse-float.pipe.js +14 -3
  35. package/pipes/standard-schema-validation.pipe.d.ts +5 -0
  36. package/pipes/standard-schema-validation.pipe.js +13 -1
  37. package/pipes/validation.pipe.js +10 -5
  38. package/serializer/standard-schema-serializer.interceptor.js +2 -2
  39. package/services/console-logger.service.js +7 -1
  40. package/utils/http-error-by-code.util.d.ts +1 -1
  41. package/utils/http-error-by-code.util.js +3 -1
  42. package/utils/load-package.util.d.ts +1 -1
  43. package/utils/load-package.util.js +7 -1
  44. package/utils/parameter-decorator-options.util.d.ts +13 -0
  45. package/utils/parameter-decorator-options.util.js +22 -0
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/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
64
- <td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
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/logos/serpapi-logo.png" width="150" valign="middle" /></a></td>
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/logos/red-hat-logo.svg" width="200" 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="180" valign="middle" /></a></td>
76
- <td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
77
- <td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
78
- <td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
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/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
82
- <td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
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/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
91
- <td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
92
- <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/logos/handsontable-dark-logo.svg#2" width="150" valign="middle" /></a></td>
93
- <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>
94
- <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>
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://pandektes.com" target="_blank"><img src="https://nestjs.com/img/logos/pandektes-logo.png" width="65" valign="middle" /></a></td>
98
- <td align="center" valign="middle"><a href="https://www.fintechcrafts.com/" target="_blank"><img src="https://nestjs.com/img/logos/fintechcrafts-logo.svg" width="65" valign="middle" /></a></td>
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://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/swingdev-logo.svg#1" width="110" valign="middle" /></a></td>
107
- <td align="center" valign="middle"><a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/novologic.png" width="110" valign="middle" /></a></td>
108
- <td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/mantro-logo.svg" width="95" valign="middle" /></a></td>
109
- <td align="center" valign="middle"><a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/logos/triplebyte.png" width="107" valign="middle" /></a></td>
110
- <td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
111
- <td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
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
 
@@ -2,6 +2,7 @@ import { uid } from 'uid';
2
2
  import { ROUTE_ARGS_METADATA } from '../../constants.js';
3
3
  import { assignCustomParameterMetadata } from '../../utils/assign-custom-metadata.util.js';
4
4
  import { isFunction, isNil } from '../../utils/shared.utils.js';
5
+ import { isParameterDecoratorOptions } from '../../utils/parameter-decorator-options.util.js';
5
6
  /**
6
7
  * Defines route param decorator
7
8
  *
@@ -20,18 +21,11 @@ export function createParamDecorator(factory, enhancers = []) {
20
21
  pipe.prototype &&
21
22
  isFunction(pipe.prototype.transform)) ||
22
23
  isFunction(pipe.transform));
23
- const isParameterDecoratorOptions = (value) => value &&
24
- typeof value === 'object' &&
25
- !isPipe(value) &&
26
- ('schema' in value || 'pipes' in value);
27
24
  const hasParamData = isNil(data) || !isPipe(data);
28
25
  const paramData = hasParamData ? data : undefined;
29
26
  const paramPipes = hasParamData ? pipes : [data, ...pipes];
30
- // Check if data itself is an options object (when used as the first and only argument)
31
- const isDataOptions = hasParamData &&
32
- !isNil(data) &&
33
- paramPipes.length === 0 &&
34
- isParameterDecoratorOptions(data);
27
+ // Check if data itself is an options object (when used as the first argument)
28
+ const isDataOptions = hasParamData && !isNil(data) && isParameterDecoratorOptions(data);
35
29
  // Check if the last pipe argument is actually an options object
36
30
  const lastPipeArg = paramPipes.length > 0 ? paramPipes[paramPipes.length - 1] : undefined;
37
31
  const isLastPipeOptions = !isDataOptions && isParameterDecoratorOptions(lastPipeArg);
@@ -42,7 +36,8 @@ export function createParamDecorator(factory, enhancers = []) {
42
36
  const opts = data;
43
37
  finalData = undefined;
44
38
  finalSchema = opts.schema;
45
- finalPipes = (opts.pipes ?? []);
39
+ // Merge positional pipes passed after the options object
40
+ finalPipes = [...(opts.pipes ?? []), ...paramPipes];
46
41
  }
47
42
  else if (isLastPipeOptions) {
48
43
  const opts = lastPipeArg;
@@ -6,3 +6,4 @@ export * from './render.decorator.js';
6
6
  export * from './header.decorator.js';
7
7
  export * from './redirect.decorator.js';
8
8
  export * from './sse.decorator.js';
9
+ export * from './sse-signal.decorator.js';
@@ -6,3 +6,4 @@ export * from './render.decorator.js';
6
6
  export * from './header.decorator.js';
7
7
  export * from './redirect.decorator.js';
8
8
  export * from './sse.decorator.js';
9
+ export * from './sse-signal.decorator.js';
@@ -76,6 +76,17 @@ export declare const All: (path?: string | string[]) => MethodDecorator;
76
76
  * @publicApi
77
77
  */
78
78
  export declare const Search: (path?: string | string[]) => MethodDecorator;
79
+ /**
80
+ * Route handler (method) Decorator. Routes HTTP QUERY requests to the specified path.
81
+ *
82
+ * **Note**: this is named `QueryMethod` (rather than `Query`) to avoid a name clash
83
+ * with the `@Query()` parameter decorator used to extract query-string params.
84
+ *
85
+ * @see [Routing](https://docs.nestjs.com/controllers#routing)
86
+ *
87
+ * @publicApi
88
+ */
89
+ export declare const QueryMethod: (path?: string | string[]) => MethodDecorator;
79
90
  /**
80
91
  * Route handler (method) Decorator. Routes Webdav PROPFIND requests to the specified path.
81
92
  *
@@ -92,6 +92,17 @@ export const All = createMappingDecorator(RequestMethod.ALL);
92
92
  * @publicApi
93
93
  */
94
94
  export const Search = createMappingDecorator(RequestMethod.SEARCH);
95
+ /**
96
+ * Route handler (method) Decorator. Routes HTTP QUERY requests to the specified path.
97
+ *
98
+ * **Note**: this is named `QueryMethod` (rather than `Query`) to avoid a name clash
99
+ * with the `@Query()` parameter decorator used to extract query-string params.
100
+ *
101
+ * @see [Routing](https://docs.nestjs.com/controllers#routing)
102
+ *
103
+ * @publicApi
104
+ */
105
+ export const QueryMethod = createMappingDecorator(RequestMethod.QUERY);
95
106
  /**
96
107
  * Route handler (method) Decorator. Routes Webdav PROPFIND requests to the specified path.
97
108
  *
@@ -34,9 +34,9 @@ export interface RouteParamMetadata {
34
34
  index: number;
35
35
  data?: ParamData;
36
36
  }
37
- export declare function assignMetadata<TParamtype = any, TArgs = any>(args: TArgs, paramtype: TParamtype, index: number, options: {
37
+ export declare function assignMetadata<TParamtype = any, TArgs = any>(args: TArgs, paramtype: TParamtype, index: number, options?: ({
38
38
  data?: ParamData;
39
- } & ParameterDecoratorOptions): TArgs & {
39
+ } & ParameterDecoratorOptions) | ParamData, ...legacyPipes: (Type<PipeTransform> | PipeTransform)[]): TArgs & {
40
40
  [x: string]: {
41
41
  schema?: StandardSchemaV1<unknown, unknown> | undefined;
42
42
  index: number;
@@ -591,5 +591,31 @@ export declare function HostParam(): ParameterDecorator;
591
591
  * @publicApi
592
592
  */
593
593
  export declare function HostParam(property: string): ParameterDecorator;
594
+ /**
595
+ * Route handler parameter decorator. Extracts the `Request`
596
+ * object from the underlying platform and populates the decorated
597
+ * parameter with the value of `Request`.
598
+ *
599
+ * Alias for @Request().
600
+ *
601
+ * Example: `logout(@Req() req)`
602
+ *
603
+ * @see [Request object](https://docs.nestjs.com/controllers#request-object)
604
+ *
605
+ * @publicApi
606
+ */
594
607
  export declare const Req: () => ParameterDecorator;
608
+ /**
609
+ * Route handler parameter decorator. Extracts the `Response`
610
+ * object from the underlying platform and populates the decorated
611
+ * parameter with the value of `Response`.
612
+ *
613
+ * Alias for @Response().
614
+ *
615
+ * Example: `logout(@Res() res)`
616
+ *
617
+ * @see [Response object](https://docs.nestjs.com/controllers#response-object)
618
+ *
619
+ * @publicApi
620
+ */
595
621
  export declare const Res: (options?: ResponseDecoratorOptions) => ParameterDecorator;
@@ -1,14 +1,29 @@
1
1
  import { RESPONSE_PASSTHROUGH_METADATA, ROUTE_ARGS_METADATA, } from '../../constants.js';
2
2
  import { RouteParamtypes } from '../../enums/route-paramtypes.enum.js';
3
3
  import { isNil, isString } from '../../utils/shared.utils.js';
4
- export function assignMetadata(args, paramtype, index, options) {
4
+ import { isParameterDecoratorOptions } from '../../utils/parameter-decorator-options.util.js';
5
+ export function assignMetadata(args, paramtype, index, options, ...legacyPipes) {
6
+ // Callers built against the pre-v12 signature
7
+ // `assignMetadata(args, paramtype, index, data?, ...pipes)` pass the raw
8
+ // `data` value (and pipes positionally); detect that shape and normalize it
9
+ // instead of silently misreading `data` as the options object.
10
+ const isOptionsObject = options !== null &&
11
+ typeof options === 'object' &&
12
+ (isParameterDecoratorOptions(options) || 'data' in options) &&
13
+ !('transform' in options) &&
14
+ legacyPipes.length === 0;
15
+ const normalizedOptions = isOptionsObject
16
+ ? options
17
+ : { data: options, pipes: legacyPipes };
5
18
  return {
6
19
  ...args,
7
20
  [`${paramtype}:${index}`]: {
8
21
  index,
9
- data: options.data,
10
- pipes: options.pipes ?? [],
11
- ...(options.schema !== undefined && { schema: options.schema }),
22
+ data: normalizedOptions.data,
23
+ pipes: normalizedOptions.pipes ?? [],
24
+ ...(normalizedOptions.schema !== undefined && {
25
+ schema: normalizedOptions.schema,
26
+ }),
12
27
  },
13
28
  };
14
29
  }
@@ -166,21 +181,16 @@ export const Headers = createRouteParamDecorator(RouteParamtypes.HEADERS);
166
181
  * @publicApi
167
182
  */
168
183
  export function Query(property, optionsOrPipe, ...pipes) {
169
- const isPropertyOptions = property &&
170
- typeof property === 'object' &&
171
- !('transform' in property) &&
172
- ('schema' in property || 'pipes' in property);
184
+ const isPropertyOptions = isParameterDecoratorOptions(property);
173
185
  if (isPropertyOptions) {
174
186
  return createPipesRouteParamDecorator(RouteParamtypes.QUERY)({
175
187
  pipes: property.pipes,
176
188
  schema: property.schema,
177
189
  });
178
190
  }
179
- const isOptions = optionsOrPipe &&
180
- typeof optionsOrPipe === 'object' &&
181
- ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe);
191
+ const isOptions = isParameterDecoratorOptions(optionsOrPipe);
182
192
  const actualPipes = isOptions
183
- ? optionsOrPipe.pipes
193
+ ? [...(optionsOrPipe.pipes ?? []), ...pipes]
184
194
  : [optionsOrPipe, ...pipes].filter(Boolean);
185
195
  return createPipesRouteParamDecorator(RouteParamtypes.QUERY)({
186
196
  data: property,
@@ -210,21 +220,16 @@ export function Query(property, optionsOrPipe, ...pipes) {
210
220
  * @publicApi
211
221
  */
212
222
  export function Body(property, optionsOrPipe, ...pipes) {
213
- const isPropertyOptions = property &&
214
- typeof property === 'object' &&
215
- !('transform' in property) &&
216
- ('schema' in property || 'pipes' in property);
223
+ const isPropertyOptions = isParameterDecoratorOptions(property);
217
224
  if (isPropertyOptions) {
218
225
  return createPipesRouteParamDecorator(RouteParamtypes.BODY)({
219
226
  pipes: property.pipes,
220
227
  schema: property.schema,
221
228
  });
222
229
  }
223
- const isOptions = optionsOrPipe &&
224
- typeof optionsOrPipe === 'object' &&
225
- ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe);
230
+ const isOptions = isParameterDecoratorOptions(optionsOrPipe);
226
231
  const actualPipes = isOptions
227
- ? optionsOrPipe.pipes
232
+ ? [...(optionsOrPipe.pipes ?? []), ...pipes]
228
233
  : [optionsOrPipe, ...pipes].filter(Boolean);
229
234
  return createPipesRouteParamDecorator(RouteParamtypes.BODY)({
230
235
  data: property,
@@ -253,11 +258,9 @@ export function Body(property, optionsOrPipe, ...pipes) {
253
258
  * @publicApi
254
259
  */
255
260
  export function RawBody(optionsOrPipe, ...pipes) {
256
- const isOptions = optionsOrPipe &&
257
- typeof optionsOrPipe === 'object' &&
258
- ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe);
261
+ const isOptions = isParameterDecoratorOptions(optionsOrPipe);
259
262
  const actualPipes = isOptions
260
- ? optionsOrPipe.pipes
263
+ ? [...(optionsOrPipe.pipes ?? []), ...pipes]
261
264
  : [optionsOrPipe, ...pipes].filter(Boolean);
262
265
  return createPipesRouteParamDecorator(RouteParamtypes.RAW_BODY)({
263
266
  pipes: actualPipes,
@@ -290,21 +293,16 @@ export function RawBody(optionsOrPipe, ...pipes) {
290
293
  * @publicApi
291
294
  */
292
295
  export function Param(property, optionsOrPipe, ...pipes) {
293
- const isPropertyOptions = property &&
294
- typeof property === 'object' &&
295
- !('transform' in property) &&
296
- ('schema' in property || 'pipes' in property);
296
+ const isPropertyOptions = isParameterDecoratorOptions(property);
297
297
  if (isPropertyOptions) {
298
298
  return createPipesRouteParamDecorator(RouteParamtypes.PARAM)({
299
299
  pipes: property.pipes,
300
300
  schema: property.schema,
301
301
  });
302
302
  }
303
- const isOptions = optionsOrPipe &&
304
- typeof optionsOrPipe === 'object' &&
305
- ('schema' in optionsOrPipe || 'pipes' in optionsOrPipe);
303
+ const isOptions = isParameterDecoratorOptions(optionsOrPipe);
306
304
  const actualPipes = isOptions
307
- ? optionsOrPipe.pipes
305
+ ? [...(optionsOrPipe.pipes ?? []), ...pipes]
308
306
  : [optionsOrPipe, ...pipes].filter(Boolean);
309
307
  return createPipesRouteParamDecorator(RouteParamtypes.PARAM)({
310
308
  data: property,
@@ -336,5 +334,31 @@ export function Param(property, optionsOrPipe, ...pipes) {
336
334
  export function HostParam(property) {
337
335
  return createRouteParamDecorator(RouteParamtypes.HOST)(property);
338
336
  }
337
+ /**
338
+ * Route handler parameter decorator. Extracts the `Request`
339
+ * object from the underlying platform and populates the decorated
340
+ * parameter with the value of `Request`.
341
+ *
342
+ * Alias for @Request().
343
+ *
344
+ * Example: `logout(@Req() req)`
345
+ *
346
+ * @see [Request object](https://docs.nestjs.com/controllers#request-object)
347
+ *
348
+ * @publicApi
349
+ */
339
350
  export const Req = Request;
351
+ /**
352
+ * Route handler parameter decorator. Extracts the `Response`
353
+ * object from the underlying platform and populates the decorated
354
+ * parameter with the value of `Response`.
355
+ *
356
+ * Alias for @Response().
357
+ *
358
+ * Example: `logout(@Res() res)`
359
+ *
360
+ * @see [Response object](https://docs.nestjs.com/controllers#response-object)
361
+ *
362
+ * @publicApi
363
+ */
340
364
  export const Res = Response;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Symbol used to carry the per-request SSE `AbortController` on the request object.
3
+ *
4
+ * The controller is attached by the framework before the route handler runs and is
5
+ * aborted when the client disconnects. Its signal is exposed to route handlers
6
+ * through the `@SseSignal()` parameter decorator.
7
+ *
8
+ * @publicApi
9
+ */
10
+ export declare const SSE_ABORT_CONTROLLER: unique symbol;
11
+ /**
12
+ * Route handler parameter decorator that injects the `AbortSignal` associated
13
+ * with a Server-Sent-Events (SSE) request.
14
+ *
15
+ * The signal represents the lifetime of the SSE response: it is aborted once the
16
+ * stream terminates, whether because the client disconnected, the `Observable`
17
+ * completed, or it errored. Tying setup-phase resources to the signal therefore
18
+ * releases them exactly once on every exit path, including resources allocated
19
+ * before the returned `Observable` exists — which would otherwise leak, since the
20
+ * producer is never subscribed once the client has disconnected.
21
+ *
22
+ * Because the signal also aborts on normal completion, `signal.aborted` is only
23
+ * meaningful as a "did the client go away?" check *during setup*, before the
24
+ * `Observable` is returned; at that point the stream cannot have completed yet.
25
+ * Cleanup wired to the `abort` event should be idempotent, as it may run
26
+ * alongside the `Observable`'s own teardown.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * @Sse('stream')
31
+ * async stream(@SseSignal() signal: AbortSignal): Promise<Observable<MessageEvent>> {
32
+ * const session = await createSession();
33
+ * if (signal.aborted) {
34
+ * await session.close();
35
+ * return EMPTY;
36
+ * }
37
+ * return new Observable(subscriber => {
38
+ * const stream = startGeneration(session);
39
+ * signal.addEventListener('abort', () => stream.stop(), { once: true });
40
+ * return () => stream.stop();
41
+ * });
42
+ * }
43
+ * ```
44
+ *
45
+ * @publicApi
46
+ */
47
+ export declare const SseSignal: () => ParameterDecorator;
@@ -0,0 +1,52 @@
1
+ import { createParamDecorator } from './create-route-param-metadata.decorator.js';
2
+ /**
3
+ * Symbol used to carry the per-request SSE `AbortController` on the request object.
4
+ *
5
+ * The controller is attached by the framework before the route handler runs and is
6
+ * aborted when the client disconnects. Its signal is exposed to route handlers
7
+ * through the `@SseSignal()` parameter decorator.
8
+ *
9
+ * @publicApi
10
+ */
11
+ export const SSE_ABORT_CONTROLLER = Symbol('SSE_ABORT_CONTROLLER');
12
+ /**
13
+ * Route handler parameter decorator that injects the `AbortSignal` associated
14
+ * with a Server-Sent-Events (SSE) request.
15
+ *
16
+ * The signal represents the lifetime of the SSE response: it is aborted once the
17
+ * stream terminates, whether because the client disconnected, the `Observable`
18
+ * completed, or it errored. Tying setup-phase resources to the signal therefore
19
+ * releases them exactly once on every exit path, including resources allocated
20
+ * before the returned `Observable` exists — which would otherwise leak, since the
21
+ * producer is never subscribed once the client has disconnected.
22
+ *
23
+ * Because the signal also aborts on normal completion, `signal.aborted` is only
24
+ * meaningful as a "did the client go away?" check *during setup*, before the
25
+ * `Observable` is returned; at that point the stream cannot have completed yet.
26
+ * Cleanup wired to the `abort` event should be idempotent, as it may run
27
+ * alongside the `Observable`'s own teardown.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * @Sse('stream')
32
+ * async stream(@SseSignal() signal: AbortSignal): Promise<Observable<MessageEvent>> {
33
+ * const session = await createSession();
34
+ * if (signal.aborted) {
35
+ * await session.close();
36
+ * return EMPTY;
37
+ * }
38
+ * return new Observable(subscriber => {
39
+ * const stream = startGeneration(session);
40
+ * signal.addEventListener('abort', () => stream.stop(), { once: true });
41
+ * return () => stream.stop();
42
+ * });
43
+ * }
44
+ * ```
45
+ *
46
+ * @publicApi
47
+ */
48
+ export const SseSignal = createParamDecorator((_data, ctx) => {
49
+ const request = ctx.switchToHttp().getRequest();
50
+ const controller = request?.[SSE_ABORT_CONTROLLER];
51
+ return controller?.signal;
52
+ });
@@ -14,5 +14,6 @@ export declare enum RequestMethod {
14
14
  COPY = 12,
15
15
  MOVE = 13,
16
16
  LOCK = 14,
17
- UNLOCK = 15
17
+ UNLOCK = 15,
18
+ QUERY = 16
18
19
  }
@@ -16,4 +16,5 @@ export var RequestMethod;
16
16
  RequestMethod[RequestMethod["MOVE"] = 13] = "MOVE";
17
17
  RequestMethod[RequestMethod["LOCK"] = 14] = "LOCK";
18
18
  RequestMethod[RequestMethod["UNLOCK"] = 15] = "UNLOCK";
19
+ RequestMethod[RequestMethod["QUERY"] = 16] = "QUERY";
19
20
  })(RequestMethod || (RequestMethod = {}));
@@ -20,7 +20,7 @@ export declare class MisdirectedException extends HttpException {
20
20
  *
21
21
  * By default, the JSON response body contains two properties:
22
22
  * - `statusCode`: this will be the value 421.
23
- * - `message`: the string `'Bad Gateway'` by default; override this by supplying
23
+ * - `message`: the string `'Misdirected'` by default; override this by supplying
24
24
  * a string in the `objectOrError` parameter.
25
25
  *
26
26
  * If the parameter `objectOrError` is a string, the response body will contain an
@@ -21,7 +21,7 @@ export class MisdirectedException extends HttpException {
21
21
  *
22
22
  * By default, the JSON response body contains two properties:
23
23
  * - `statusCode`: this will be the value 421.
24
- * - `message`: the string `'Bad Gateway'` by default; override this by supplying
24
+ * - `message`: the string `'Misdirected'` by default; override this by supplying
25
25
  * a string in the `objectOrError` parameter.
26
26
  *
27
27
  * If the parameter `objectOrError` is a string, the response body will contain an
@@ -20,8 +20,8 @@ export declare class NotAcceptableException extends HttpException {
20
20
  *
21
21
  * By default, the JSON response body contains two properties:
22
22
  * - `statusCode`: this will be the value 406.
23
- * - `error`: the string `'Not Acceptable'` by default; override this by supplying
24
- * a string in the `error` parameter.
23
+ * - `message`: the string `'Not Acceptable'` by default; override this by supplying
24
+ * a string in the `objectOrError` parameter.
25
25
  *
26
26
  * If the parameter `objectOrError` is a string, the response body will contain an
27
27
  * additional property, `error`, with a short description of the HTTP error. To override the
@@ -21,8 +21,8 @@ export class NotAcceptableException extends HttpException {
21
21
  *
22
22
  * By default, the JSON response body contains two properties:
23
23
  * - `statusCode`: this will be the value 406.
24
- * - `error`: the string `'Not Acceptable'` by default; override this by supplying
25
- * a string in the `error` parameter.
24
+ * - `message`: the string `'Not Acceptable'` by default; override this by supplying
25
+ * a string in the `objectOrError` parameter.
26
26
  *
27
27
  * If the parameter `objectOrError` is a string, the response body will contain an
28
28
  * additional property, `error`, with a short description of the HTTP error. To override the
package/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export * from './decorators/index.js';
3
3
  export * from './enums/index.js';
4
4
  export * from './exceptions/index.js';
5
5
  export * from './file-stream/index.js';
6
- export { Abstract, ArgumentMetadata, ArgumentsHost, BeforeApplicationShutdown, CallHandler, CanActivate, ClassProvider, ContextType, DynamicModule, ExceptionFilter, ExecutionContext, ExistingProvider, FactoryProvider, ForwardReference, HttpServer, HttpExceptionBody, HttpExceptionBodyMessage, HttpRedirectResponse, INestApplication, INestApplicationContext, INestMicroservice, InjectionToken, IntrospectionResult, MessageEvent, MiddlewareConsumer, ModuleMetadata, NestApplicationOptions, NestHybridApplicationOptions, NestInterceptor, NestMiddleware, PreRequestHook, NestModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy, OnModuleInit, OptionalFactoryDependency, Paramtype, PipeTransform, Provider, RawBodyRequest, RouteConflictPolicy, RouteConflictPolicyLevel, RouteResolutionStrategy, RpcExceptionFilter, Scope, ScopeOptions, Type, ValidationError, ValueProvider, VersioningOptions, VERSION_NEUTRAL, WebSocketAdapter, WsExceptionFilter, WsMessageHandler, } from './interfaces/index.js';
6
+ export { Abstract, ArgumentMetadata, ArgumentsHost, BeforeApplicationShutdown, CallHandler, CanActivate, ClassProvider, ContextType, DynamicModule, ExceptionFilter, ExecutionContext, ExistingProvider, FactoryProvider, ForwardReference, HttpServer, HttpExceptionBody, HttpExceptionBodyMessage, HttpRedirectResponse, INestApplication, INestApplicationContext, INestMicroservice, ITransportServer, InjectionToken, IntrospectionResult, MessageEvent, MiddlewareConsumer, ModuleMetadata, NestApplicationOptions, NestHybridApplicationOptions, NestInterceptor, NestMiddleware, PreRequestHook, NestModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy, OnModuleInit, OptionalFactoryDependency, Paramtype, PipeTransform, Provider, RawBodyRequest, RouteConflictPolicy, RouteConflictPolicyLevel, RouteResolutionStrategy, RpcExceptionFilter, Scope, ScopeOptions, Type, ValidationError, ValueProvider, VersioningOptions, VERSION_NEUTRAL, WebSocketAdapter, WsExceptionFilter, WsMessageHandler, } from './interfaces/index.js';
7
7
  export * from './module-utils/index.js';
8
8
  export * from './pipes/index.js';
9
9
  export * from './serializer/index.js';
@@ -39,6 +39,8 @@ export interface HttpServer<TRequest = any, TResponse = any, ServerInstance = an
39
39
  options(path: string, handler: RequestHandler<TRequest, TResponse>): any;
40
40
  search?(handler: RequestHandler<TRequest, TResponse>): any;
41
41
  search?(path: string, handler: RequestHandler<TRequest, TResponse>): any;
42
+ query?(handler: RequestHandler<TRequest, TResponse>): any;
43
+ query?(path: string, handler: RequestHandler<TRequest, TResponse>): any;
42
44
  listen(port: number | string, callback?: () => void): any;
43
45
  listen(port: number | string, hostname: string, callback?: () => void): any;
44
46
  reply(response: any, body: any, statusCode?: number): any;
@@ -1,6 +1,7 @@
1
1
  export interface MessageEvent {
2
- data: string | object;
2
+ data?: string | object;
3
3
  id?: string;
4
4
  type?: string;
5
5
  retry?: number;
6
+ comment?: string;
6
7
  }
@@ -18,6 +18,7 @@ export * from './http/index.js';
18
18
  export * from './injectable.interface.js';
19
19
  export * from './microservices/nest-hybrid-application-options.interface.js';
20
20
  export * from './microservices/pre-request-hook.interface.js';
21
+ export * from './microservices/transport-server.interface.js';
21
22
  export * from './middleware/index.js';
22
23
  export * from './modules/index.js';
23
24
  export * from './nest-application-context.interface.js';
@@ -18,6 +18,7 @@ export * from './http/index.js';
18
18
  export * from './injectable.interface.js';
19
19
  export * from './microservices/nest-hybrid-application-options.interface.js';
20
20
  export * from './microservices/pre-request-hook.interface.js';
21
+ export * from './microservices/transport-server.interface.js';
21
22
  export * from './middleware/index.js';
22
23
  export * from './modules/index.js';
23
24
  export * from './nest-application-context.interface.js';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Interface describing the shape of a transport server.
3
+ * Used as the return type of `getTransportServer()` to provide
4
+ * autocomplete without creating circular dependencies.
5
+ *
6
+ * @publicApi
7
+ */
8
+ export interface ITransportServer {
9
+ /**
10
+ * Starts the transport server.
11
+ * @param callback Function to be called upon initialization
12
+ */
13
+ listen(callback: (...optionalParams: unknown[]) => any): any;
14
+ /**
15
+ * Closes the transport server (stops listening on port/connection).
16
+ */
17
+ close(): any;
18
+ }
@@ -3,6 +3,7 @@ import { ExceptionFilter } from './exceptions/exception-filter.interface.js';
3
3
  import { CanActivate } from './features/can-activate.interface.js';
4
4
  import { NestInterceptor } from './features/nest-interceptor.interface.js';
5
5
  import { PipeTransform } from './features/pipe-transform.interface.js';
6
+ import { ITransportServer } from './microservices/transport-server.interface.js';
6
7
  import { PreRequestHook } from './microservices/pre-request-hook.interface.js';
7
8
  import { INestApplicationContext } from './nest-application-context.interface.js';
8
9
  import { WebSocketAdapter } from './websockets/web-socket-adapter.interface.js';
@@ -81,4 +82,10 @@ export interface INestMicroservice extends INestApplicationContext {
81
82
  * or a group of servers if there are more than one.
82
83
  */
83
84
  unwrap<T>(): T;
85
+ /**
86
+ * Returns the underlying transport server instance.
87
+ * Use this to close only the transport (port/connection) without
88
+ * triggering the full application shutdown lifecycle.
89
+ */
90
+ getTransportServer(): ITransportServer;
84
91
  }
package/internal.d.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  export * from './constants.js';
9
9
  export { RouteParamtypes } from './enums/route-paramtypes.enum.js';
10
10
  export * from './utils/shared.utils.js';
11
+ export * from './utils/parameter-decorator-options.util.js';
11
12
  export * from './utils/load-package.util.js';
12
13
  export * from './utils/cli-colors.util.js';
13
14
  export * from './utils/random-string-generator.util.js';
package/internal.js CHANGED
@@ -11,6 +11,7 @@ export * from './constants.js';
11
11
  export { RouteParamtypes } from './enums/route-paramtypes.enum.js';
12
12
  // Utils
13
13
  export * from './utils/shared.utils.js';
14
+ export * from './utils/parameter-decorator-options.util.js';
14
15
  export * from './utils/load-package.util.js';
15
16
  export * from './utils/cli-colors.util.js';
16
17
  export * from './utils/random-string-generator.util.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs/common",
3
- "version": "12.0.0-alpha.5",
3
+ "version": "12.0.0-alpha.7",
4
4
  "description": "Nest - modern, fast, powerful node.js web framework (@common)",
5
5
  "author": "Kamil Mysliwiec",
6
6
  "homepage": "https://nestjs.com",
@@ -26,7 +26,8 @@
26
26
  "./*": "./*.js"
27
27
  },
28
28
  "dependencies": {
29
- "file-type": "21.3.4",
29
+ "@standard-schema/spec": "1.1.0",
30
+ "file-type": "22.0.2",
30
31
  "iterare": "1.2.1",
31
32
  "load-esm": "1.0.3",
32
33
  "tslib": "2.8.1",
@@ -46,5 +47,5 @@
46
47
  "optional": true
47
48
  }
48
49
  },
49
- "gitHead": "d91b72cc9567e6a09e3ad6075b2fb801e71adfc8"
50
+ "gitHead": "66e89ffc3d14e47d572bd6ea3f8714fa6916160b"
50
51
  }
@@ -45,6 +45,13 @@ export type FileTypeValidatorOptions = {
45
45
  * @default false
46
46
  */
47
47
  fallbackToMimetype?: boolean;
48
+ /**
49
+ * If `true`, replaces the client-provided mimetype with the mimetype
50
+ * detected from the file content using magic number validation.
51
+ *
52
+ * @default false
53
+ */
54
+ overrideMimeType?: boolean;
48
55
  };
49
56
  /**
50
57
  * Defines the built-in FileTypeValidator. It validates incoming files by examining
@@ -58,5 +65,6 @@ export type FileTypeValidatorOptions = {
58
65
  export declare class FileTypeValidator extends FileValidator<FileTypeValidatorOptions, IFile> {
59
66
  buildErrorMessage(file?: IFile): string;
60
67
  isValid(file?: IFile): Promise<boolean>;
68
+ private matchesFileType;
61
69
  }
62
70
  export {};
@@ -53,13 +53,13 @@ export class FileTypeValidator extends FileValidator {
53
53
  const isFileValid = !!file && 'mimetype' in file;
54
54
  // Skip magic number validation if set
55
55
  if (this.validationOptions.skipMagicNumbersValidation) {
56
- return (isFileValid && !!file.mimetype.match(this.validationOptions.fileType));
56
+ return isFileValid && this.matchesFileType(file.mimetype);
57
57
  }
58
58
  if (!isFileValid)
59
59
  return false;
60
60
  if (!file.buffer) {
61
61
  if (this.validationOptions.fallbackToMimetype) {
62
- return !!file.mimetype.match(this.validationOptions.fileType);
62
+ return this.matchesFileType(file.mimetype);
63
63
  }
64
64
  return false;
65
65
  }
@@ -67,8 +67,11 @@ export class FileTypeValidator extends FileValidator {
67
67
  const { fileTypeFromBuffer } = await import('file-type');
68
68
  const fileType = await fileTypeFromBuffer(file.buffer);
69
69
  if (fileType) {
70
+ if (this.validationOptions.overrideMimeType) {
71
+ file.mimetype = fileType.mime;
72
+ }
70
73
  // Match detected mime type against allowed type
71
- return !!fileType.mime.match(this.validationOptions.fileType);
74
+ return this.matchesFileType(fileType.mime);
72
75
  }
73
76
  /**
74
77
  * Fallback logic: If file-type cannot detect magic number (e.g. file too small),
@@ -76,7 +79,7 @@ export class FileTypeValidator extends FileValidator {
76
79
  * This is useful for plain text, CSVs, or files without recognizable signatures.
77
80
  */
78
81
  if (this.validationOptions.fallbackToMimetype) {
79
- return !!file.mimetype.match(this.validationOptions.fileType);
82
+ return this.matchesFileType(file.mimetype);
80
83
  }
81
84
  return false;
82
85
  }
@@ -92,9 +95,18 @@ export class FileTypeValidator extends FileValidator {
92
95
  }
93
96
  // Fallback to mimetype if enabled
94
97
  if (this.validationOptions.fallbackToMimetype) {
95
- return !!file.mimetype.match(this.validationOptions.fileType);
98
+ return this.matchesFileType(file.mimetype);
96
99
  }
97
100
  return false;
98
101
  }
99
102
  }
103
+ matchesFileType(mimetype) {
104
+ const { fileType } = this.validationOptions;
105
+ // A string is coerced into a RegExp by `String#match`, so MIME types holding
106
+ // regex metacharacters (the `+` in `image/svg+xml`) never match themselves.
107
+ if (typeof fileType === 'string' && mimetype === fileType) {
108
+ return true;
109
+ }
110
+ return !!mimetype.match(fileType);
111
+ }
100
112
  }
@@ -4,7 +4,7 @@ import { ValidationPipe, ValidationPipeOptions } from './validation.pipe.js';
4
4
  /**
5
5
  * @publicApi
6
6
  */
7
- export interface ParseArrayOptions extends Omit<ValidationPipeOptions, 'transform' | 'validateCustomDecorators' | 'exceptionFactory'> {
7
+ export interface ParseArrayPipeOptions extends Omit<ValidationPipeOptions, 'transform' | 'validateCustomDecorators' | 'exceptionFactory'> {
8
8
  /**
9
9
  * Type for items to be converted into
10
10
  */
@@ -27,6 +27,8 @@ export interface ParseArrayOptions extends Omit<ValidationPipeOptions, 'transfor
27
27
  */
28
28
  exceptionFactory?: (error: any) => any;
29
29
  }
30
+ /** @deprecated Use `ParseArrayPipeOptions` instead. */
31
+ export type ParseArrayOptions = ParseArrayPipeOptions;
30
32
  /**
31
33
  * Defines the built-in ParseArray Pipe
32
34
  *
@@ -35,10 +37,10 @@ export interface ParseArrayOptions extends Omit<ValidationPipeOptions, 'transfor
35
37
  * @publicApi
36
38
  */
37
39
  export declare class ParseArrayPipe implements PipeTransform {
38
- protected readonly options: ParseArrayOptions;
40
+ protected readonly options: ParseArrayPipeOptions;
39
41
  protected readonly validationPipe: ValidationPipe;
40
42
  protected exceptionFactory: (error: string) => any;
41
- constructor(options?: ParseArrayOptions);
43
+ constructor(options?: ParseArrayPipeOptions);
42
44
  /**
43
45
  * Method that accesses and performs optional transformation on argument for
44
46
  * in-flight requests.
@@ -1,5 +1,8 @@
1
1
  import { PipeTransform } from '../interfaces/features/pipe-transform.interface.js';
2
2
  import { ErrorHttpStatusCode } from '../utils/http-error-by-code.util.js';
3
+ /**
4
+ * @publicApi
5
+ */
3
6
  export interface ParseDatePipeOptions {
4
7
  /**
5
8
  * If true, the pipe will return null or undefined if the value is not provided
@@ -22,6 +25,9 @@ export interface ParseDatePipeOptions {
22
25
  */
23
26
  exceptionFactory?: (error: string) => any;
24
27
  }
28
+ /**
29
+ * @publicApi
30
+ */
25
31
  export declare class ParseDatePipe implements PipeTransform {
26
32
  private readonly options;
27
33
  protected exceptionFactory: (error: string) => any;
@@ -3,6 +3,9 @@ import { Injectable } from '../decorators/core/injectable.decorator.js';
3
3
  import { HttpStatus } from '../enums/http-status.enum.js';
4
4
  import { HttpErrorByCode, } from '../utils/http-error-by-code.util.js';
5
5
  import { isNil, isNumber, isString } from '../utils/shared.utils.js';
6
+ /**
7
+ * @publicApi
8
+ */
6
9
  let ParseDatePipe = class ParseDatePipe {
7
10
  options;
8
11
  exceptionFactory;
@@ -43,7 +43,13 @@ let ParseEnumPipe = class ParseEnumPipe {
43
43
  return value;
44
44
  }
45
45
  isEnum(value) {
46
- const enumValues = Object.keys(this.enumType).map(item => this.enumType[item]);
46
+ const enumValues = Object.keys(this.enumType)
47
+ .filter(key => {
48
+ const enumValue = this.enumType[key];
49
+ return !(typeof enumValue === 'string' &&
50
+ typeof this.enumType[enumValue] === 'number');
51
+ })
52
+ .map(key => this.enumType[key]);
47
53
  return enumValues.includes(value);
48
54
  }
49
55
  };
@@ -42,9 +42,20 @@ let ParseFloatPipe = class ParseFloatPipe {
42
42
  * @returns `true` if `value` is a valid float number
43
43
  */
44
44
  isNumeric(value) {
45
- return (['string', 'number'].includes(typeof value) &&
46
- !isNaN(parseFloat(String(value))) &&
47
- isFinite(value));
45
+ if (typeof value === 'number')
46
+ return Number.isFinite(value);
47
+ if (typeof value !== 'string' || value === '' || value !== value.trim()) {
48
+ return false;
49
+ }
50
+ if (value.startsWith('0x') ||
51
+ value.startsWith('0X') ||
52
+ value.startsWith('0b') ||
53
+ value.startsWith('0B') ||
54
+ value.startsWith('0o') ||
55
+ value.startsWith('0O')) {
56
+ return false;
57
+ }
58
+ return Number.isFinite(Number(value));
48
59
  }
49
60
  };
50
61
  ParseFloatPipe = __decorate([
@@ -53,6 +53,11 @@ export declare class StandardSchemaValidationPipe implements PipeTransform {
53
53
  protected validateOptions: Record<string, unknown> | undefined;
54
54
  protected exceptionFactory: (issues: readonly StandardSchemaV1.Issue[]) => any;
55
55
  constructor(options?: StandardSchemaValidationPipeOptions | undefined);
56
+ /**
57
+ * Formats Standard Schema validation issues into a flat list of error messages.
58
+ * When an issue includes a path, the path is prefixed to the message.
59
+ */
60
+ protected formatIssueMessages(issues: readonly StandardSchemaV1.Issue[]): string[];
56
61
  /**
57
62
  * Method that validates the incoming value against the standard schema
58
63
  * provided in the parameter metadata.
@@ -34,10 +34,22 @@ let StandardSchemaValidationPipe = class StandardSchemaValidationPipe {
34
34
  this.exceptionFactory =
35
35
  exceptionFactory ||
36
36
  (issues => {
37
- const messages = issues.map(issue => issue.message);
37
+ const messages = this.formatIssueMessages(issues);
38
38
  return new HttpErrorByCode[errorHttpStatusCode](messages);
39
39
  });
40
40
  }
41
+ /**
42
+ * Formats Standard Schema validation issues into a flat list of error messages.
43
+ * When an issue includes a path, the path is prefixed to the message.
44
+ */
45
+ formatIssueMessages(issues) {
46
+ return issues.map(issue => {
47
+ if (issue.path?.length) {
48
+ return `${issue.path.map(String).join('.')}: ${issue.message}`;
49
+ }
50
+ return issue.message;
51
+ });
52
+ }
41
53
  /**
42
54
  * Method that validates the incoming value against the standard schema
43
55
  * provided in the parameter metadata.
@@ -31,7 +31,7 @@ let ValidationPipe = class ValidationPipe {
31
31
  errorFormat;
32
32
  constructor(options) {
33
33
  options = options || {};
34
- const { transform, disableErrorMessages, errorHttpStatusCode, expectedType, transformOptions, validateCustomDecorators, errorFormat, ...validatorOptions } = options;
34
+ const { transform, disableErrorMessages, errorHttpStatusCode, expectedType, transformOptions, exceptionFactory, validatorPackage, transformerPackage, validateCustomDecorators, errorFormat, ...validatorOptions } = options;
35
35
  // @see [https://github.com/nestjs/nest/issues/10683#issuecomment-1413690508](https://github.com/nestjs/nest/issues/10683#issuecomment-1413690508)
36
36
  this.validatorOptions = { forbidUnknownValues: false, ...validatorOptions };
37
37
  this.isTransformEnabled = !!transform;
@@ -41,10 +41,9 @@ let ValidationPipe = class ValidationPipe {
41
41
  this.errorHttpStatusCode = errorHttpStatusCode || HttpStatus.BAD_REQUEST;
42
42
  this.expectedType = expectedType;
43
43
  this.errorFormat = errorFormat || 'list';
44
- this.exceptionFactory =
45
- options.exceptionFactory || this.createExceptionFactory();
46
- classValidator = this.loadValidator(options.validatorPackage);
47
- classTransformer = this.loadTransformer(options.transformerPackage);
44
+ this.exceptionFactory = exceptionFactory || this.createExceptionFactory();
45
+ classValidator = this.loadValidator(validatorPackage);
46
+ classTransformer = this.loadTransformer(transformerPackage);
48
47
  }
49
48
  loadValidator(validatorPackage) {
50
49
  return (validatorPackage ??
@@ -119,8 +118,14 @@ let ValidationPipe = class ValidationPipe {
119
118
  }
120
119
  if (this.errorFormat === 'grouped') {
121
120
  const errors = this.groupValidationErrors(validationErrors);
121
+ // Custom (object) bodies are returned verbatim by
122
+ // `HttpException.createBody`, so add the standard envelope fields
123
+ // explicitly to match the shape of the "list" format.
124
+ const { message: error, statusCode } = new HttpErrorByCode[this.errorHttpStatusCode]().getResponse();
122
125
  return new HttpErrorByCode[this.errorHttpStatusCode]({
123
126
  message: errors,
127
+ error,
128
+ statusCode,
124
129
  });
125
130
  }
126
131
  const errors = this.flattenValidationErrors(validationErrors);
@@ -1,5 +1,5 @@
1
1
  import { __decorate, __metadata, __param } from "tslib";
2
- import { map } from 'rxjs/operators';
2
+ import { concatMap } from 'rxjs/operators';
3
3
  import { Inject, Injectable, Optional } from '../decorators/core/index.js';
4
4
  import { StreamableFile } from '../file-stream/index.js';
5
5
  import { isObject } from '../utils/shared.utils.js';
@@ -36,7 +36,7 @@ let StandardSchemaSerializerInterceptor = class StandardSchemaSerializerIntercep
36
36
  const validateOptions = contextOptions?.validateOptions ?? this.defaultOptions.validateOptions;
37
37
  return next
38
38
  .handle()
39
- .pipe(map((res) => this.serialize(res, schema, validateOptions)));
39
+ .pipe(concatMap(async (res) => this.serialize(res, schema, validateOptions)));
40
40
  }
41
41
  /**
42
42
  * Serializes responses that are non-null objects nor streamable files.
@@ -215,7 +215,13 @@ let ConsoleLogger = class ConsoleLogger {
215
215
  }
216
216
  if (options.params) {
217
217
  if (this.options.flattenParams) {
218
- Object.assign(logObject, options.params);
218
+ // Framework fields win on key collisions: a param named "message" or
219
+ // "level" must not clobber the actual log message or severity.
220
+ for (const [key, value] of Object.entries(options.params)) {
221
+ if (!(key in logObject)) {
222
+ logObject[key] = value;
223
+ }
224
+ }
219
225
  }
220
226
  else {
221
227
  logObject.params = options.params;
@@ -1,4 +1,4 @@
1
1
  import { HttpStatus } from '../enums/index.js';
2
2
  import { Type } from '../interfaces/index.js';
3
- export type ErrorHttpStatusCode = HttpStatus.BAD_GATEWAY | HttpStatus.BAD_REQUEST | HttpStatus.CONFLICT | HttpStatus.FORBIDDEN | HttpStatus.GATEWAY_TIMEOUT | HttpStatus.GONE | HttpStatus.I_AM_A_TEAPOT | HttpStatus.INTERNAL_SERVER_ERROR | HttpStatus.METHOD_NOT_ALLOWED | HttpStatus.NOT_ACCEPTABLE | HttpStatus.NOT_FOUND | HttpStatus.NOT_IMPLEMENTED | HttpStatus.PAYLOAD_TOO_LARGE | HttpStatus.PRECONDITION_FAILED | HttpStatus.REQUEST_TIMEOUT | HttpStatus.SERVICE_UNAVAILABLE | HttpStatus.UNAUTHORIZED | HttpStatus.UNPROCESSABLE_ENTITY | HttpStatus.UNSUPPORTED_MEDIA_TYPE;
3
+ export type ErrorHttpStatusCode = HttpStatus.BAD_GATEWAY | HttpStatus.BAD_REQUEST | HttpStatus.CONFLICT | HttpStatus.FORBIDDEN | HttpStatus.GATEWAY_TIMEOUT | HttpStatus.GONE | HttpStatus.HTTP_VERSION_NOT_SUPPORTED | HttpStatus.I_AM_A_TEAPOT | HttpStatus.INTERNAL_SERVER_ERROR | HttpStatus.METHOD_NOT_ALLOWED | HttpStatus.MISDIRECTED | HttpStatus.NOT_ACCEPTABLE | HttpStatus.NOT_FOUND | HttpStatus.NOT_IMPLEMENTED | HttpStatus.PAYLOAD_TOO_LARGE | HttpStatus.PRECONDITION_FAILED | HttpStatus.REQUEST_TIMEOUT | HttpStatus.SERVICE_UNAVAILABLE | HttpStatus.UNAUTHORIZED | HttpStatus.UNPROCESSABLE_ENTITY | HttpStatus.UNSUPPORTED_MEDIA_TYPE;
4
4
  export declare const HttpErrorByCode: Record<ErrorHttpStatusCode, Type<unknown>>;
@@ -1,5 +1,5 @@
1
1
  import { HttpStatus } from '../enums/index.js';
2
- import { BadGatewayException, BadRequestException, ConflictException, ForbiddenException, GatewayTimeoutException, GoneException, ImATeapotException, InternalServerErrorException, MethodNotAllowedException, NotAcceptableException, NotFoundException, NotImplementedException, PayloadTooLargeException, PreconditionFailedException, RequestTimeoutException, ServiceUnavailableException, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, } from '../exceptions/index.js';
2
+ import { BadGatewayException, BadRequestException, ConflictException, ForbiddenException, GatewayTimeoutException, GoneException, HttpVersionNotSupportedException, ImATeapotException, InternalServerErrorException, MethodNotAllowedException, MisdirectedException, NotAcceptableException, NotFoundException, NotImplementedException, PayloadTooLargeException, PreconditionFailedException, RequestTimeoutException, ServiceUnavailableException, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, } from '../exceptions/index.js';
3
3
  export const HttpErrorByCode = {
4
4
  [HttpStatus.BAD_GATEWAY]: BadGatewayException,
5
5
  [HttpStatus.BAD_REQUEST]: BadRequestException,
@@ -7,9 +7,11 @@ export const HttpErrorByCode = {
7
7
  [HttpStatus.FORBIDDEN]: ForbiddenException,
8
8
  [HttpStatus.GATEWAY_TIMEOUT]: GatewayTimeoutException,
9
9
  [HttpStatus.GONE]: GoneException,
10
+ [HttpStatus.HTTP_VERSION_NOT_SUPPORTED]: HttpVersionNotSupportedException,
10
11
  [HttpStatus.I_AM_A_TEAPOT]: ImATeapotException,
11
12
  [HttpStatus.INTERNAL_SERVER_ERROR]: InternalServerErrorException,
12
13
  [HttpStatus.METHOD_NOT_ALLOWED]: MethodNotAllowedException,
14
+ [HttpStatus.MISDIRECTED]: MisdirectedException,
13
15
  [HttpStatus.NOT_ACCEPTABLE]: NotAcceptableException,
14
16
  [HttpStatus.NOT_FOUND]: NotFoundException,
15
17
  [HttpStatus.NOT_IMPLEMENTED]: NotImplementedException,
@@ -18,7 +18,7 @@ export declare function loadPackageSync(packageName: string, context: string, lo
18
18
  * Ensure that `loadPackage()` has been `await`ed for the same package name
19
19
  * before calling this function (typically during `init()` or `compile()`).
20
20
  */
21
- export declare function loadPackageCached(packageName: string): any;
21
+ export declare function loadPackageCached(packageName: string, context?: string): any;
22
22
  /**
23
23
  * Attempts to load and cache a package. Returns the loaded module on success
24
24
  * or `null` if the package is not installed.
@@ -60,9 +60,15 @@ export function loadPackageSync(packageName, context, loaderFn) {
60
60
  * Ensure that `loadPackage()` has been `await`ed for the same package name
61
61
  * before calling this function (typically during `init()` or `compile()`).
62
62
  */
63
- export function loadPackageCached(packageName) {
63
+ export function loadPackageCached(packageName, context) {
64
64
  const cached = packageCache.get(packageName);
65
65
  if (!cached) {
66
+ if (context) {
67
+ // The package was not preloaded (typically because it is not
68
+ // installed). Fall back to a synchronous load so the user gets the
69
+ // actionable "package is missing" message instead of an internal error.
70
+ return loadPackageSync(packageName, context);
71
+ }
66
72
  throw new Error(`Package "${packageName}" has not been loaded yet. ` +
67
73
  `Ensure loadPackage("${packageName}", ...) has been awaited before calling loadPackageCached.`);
68
74
  }
@@ -0,0 +1,13 @@
1
+ import type { ParameterDecoratorOptions } from '../decorators/http/route-params.decorator.js';
2
+ /**
3
+ * Determines whether a parameter-decorator argument is a
4
+ * `ParameterDecoratorOptions` object rather than a pipe (instance or class).
5
+ *
6
+ * Kept in one place so every decorator (`@Query`, `@Body`, `@Param`,
7
+ * `@RawBody`, `@Payload`, `@MessageBody`, custom decorators, ...) classifies
8
+ * arguments identically: a pipe instance that happens to expose a `schema` or
9
+ * `pipes` property must never be mistaken for an options object.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare function isParameterDecoratorOptions(value: unknown): value is ParameterDecoratorOptions;
@@ -0,0 +1,22 @@
1
+ import { isFunction } from './shared.utils.js';
2
+ const isPipeLike = (value) => isFunction(value?.transform) ||
3
+ (isFunction(value) &&
4
+ value.prototype &&
5
+ isFunction(value.prototype.transform));
6
+ /**
7
+ * Determines whether a parameter-decorator argument is a
8
+ * `ParameterDecoratorOptions` object rather than a pipe (instance or class).
9
+ *
10
+ * Kept in one place so every decorator (`@Query`, `@Body`, `@Param`,
11
+ * `@RawBody`, `@Payload`, `@MessageBody`, custom decorators, ...) classifies
12
+ * arguments identically: a pipe instance that happens to expose a `schema` or
13
+ * `pipes` property must never be mistaken for an options object.
14
+ *
15
+ * @internal
16
+ */
17
+ export function isParameterDecoratorOptions(value) {
18
+ return (!!value &&
19
+ typeof value === 'object' &&
20
+ !isPipeLike(value) &&
21
+ ('schema' in value || 'pipes' in value));
22
+ }