@nestia/core 14.0.1 → 14.0.2

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 (156) hide show
  1. package/lib/adaptors/McpAdaptor.d.ts +24 -3
  2. package/lib/adaptors/McpAdaptor.js +72 -34
  3. package/lib/adaptors/McpAdaptor.js.map +1 -1
  4. package/lib/adaptors/WebSocketAdaptor.d.ts +37 -0
  5. package/lib/adaptors/WebSocketAdaptor.js +60 -16
  6. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  7. package/lib/adaptors/internal/external_context.js +18 -5
  8. package/lib/adaptors/internal/external_context.js.map +1 -1
  9. package/lib/decorators/DynamicModule.d.ts +12 -0
  10. package/lib/decorators/DynamicModule.js +12 -0
  11. package/lib/decorators/DynamicModule.js.map +1 -1
  12. package/lib/decorators/EncryptedBody.d.ts +14 -6
  13. package/lib/decorators/EncryptedBody.js +37 -14
  14. package/lib/decorators/EncryptedBody.js.map +1 -1
  15. package/lib/decorators/EncryptedController.d.ts +6 -1
  16. package/lib/decorators/EncryptedController.js +6 -1
  17. package/lib/decorators/EncryptedController.js.map +1 -1
  18. package/lib/decorators/EncryptedModule.d.ts +18 -3
  19. package/lib/decorators/EncryptedModule.js +18 -3
  20. package/lib/decorators/EncryptedModule.js.map +1 -1
  21. package/lib/decorators/EncryptedRoute.d.ts +20 -11
  22. package/lib/decorators/EncryptedRoute.js +20 -11
  23. package/lib/decorators/EncryptedRoute.js.map +1 -1
  24. package/lib/decorators/HumanRoute.d.ts +5 -0
  25. package/lib/decorators/HumanRoute.js +5 -0
  26. package/lib/decorators/HumanRoute.js.map +1 -1
  27. package/lib/decorators/McpRoute.d.ts +31 -19
  28. package/lib/decorators/McpRoute.js +12 -1
  29. package/lib/decorators/McpRoute.js.map +1 -1
  30. package/lib/decorators/PlainBody.d.ts +7 -1
  31. package/lib/decorators/PlainBody.js +16 -8
  32. package/lib/decorators/PlainBody.js.map +1 -1
  33. package/lib/decorators/SwaggerCustomizer.d.ts +15 -0
  34. package/lib/decorators/SwaggerCustomizer.js +5 -0
  35. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  36. package/lib/decorators/SwaggerExample.d.ts +51 -22
  37. package/lib/decorators/SwaggerExample.js +35 -22
  38. package/lib/decorators/SwaggerExample.js.map +1 -1
  39. package/lib/decorators/TypedBody.d.ts +9 -2
  40. package/lib/decorators/TypedBody.js +19 -9
  41. package/lib/decorators/TypedBody.js.map +1 -1
  42. package/lib/decorators/TypedException.d.ts +10 -0
  43. package/lib/decorators/TypedFormData.d.ts +59 -1
  44. package/lib/decorators/TypedFormData.js +5 -0
  45. package/lib/decorators/TypedFormData.js.map +1 -1
  46. package/lib/decorators/TypedHeaders.d.ts +5 -0
  47. package/lib/decorators/TypedHeaders.js +5 -0
  48. package/lib/decorators/TypedHeaders.js.map +1 -1
  49. package/lib/decorators/TypedParam.d.ts +9 -0
  50. package/lib/decorators/TypedParam.js +9 -0
  51. package/lib/decorators/TypedParam.js.map +1 -1
  52. package/lib/decorators/TypedQuery.d.ts +16 -0
  53. package/lib/decorators/TypedQuery.js +16 -0
  54. package/lib/decorators/TypedQuery.js.map +1 -1
  55. package/lib/decorators/TypedRoute.d.ts +25 -10
  56. package/lib/decorators/TypedRoute.js +19 -10
  57. package/lib/decorators/TypedRoute.js.map +1 -1
  58. package/lib/decorators/WebSocketRoute.d.ts +18 -1
  59. package/lib/decorators/WebSocketRoute.js +18 -1
  60. package/lib/decorators/WebSocketRoute.js.map +1 -1
  61. package/lib/decorators/doNotThrowTransformError.d.ts +14 -0
  62. package/lib/decorators/doNotThrowTransformError.js +14 -0
  63. package/lib/decorators/doNotThrowTransformError.js.map +1 -1
  64. package/lib/decorators/internal/IRequestBodyValidation.d.ts +66 -0
  65. package/lib/decorators/internal/IRequestBodyValidation.js +3 -0
  66. package/lib/decorators/internal/IRequestBodyValidation.js.map +1 -0
  67. package/lib/decorators/internal/IWebSocketRouteReflect.d.ts +93 -1
  68. package/lib/decorators/internal/get_path_and_querify.js +3 -7
  69. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  70. package/lib/decorators/internal/get_path_and_stringify.js +3 -7
  71. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  72. package/lib/decorators/internal/load_controller.d.ts +14 -0
  73. package/lib/decorators/internal/load_controller.js +14 -0
  74. package/lib/decorators/internal/load_controller.js.map +1 -1
  75. package/lib/decorators/internal/split_route_arguments.d.ts +1 -0
  76. package/lib/decorators/internal/split_route_arguments.js +22 -0
  77. package/lib/decorators/internal/split_route_arguments.js.map +1 -0
  78. package/lib/decorators/internal/validate_request_body.js +68 -25
  79. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  80. package/lib/options/IRequestBodyValidator.d.ts +71 -0
  81. package/lib/options/IRequestFormDataProps.d.ts +80 -0
  82. package/lib/options/IRequestHeadersValidator.d.ts +67 -0
  83. package/lib/options/IRequestQueryValidator.d.ts +68 -0
  84. package/lib/options/IResponseBodyQuerifier.d.ts +120 -0
  85. package/lib/options/IResponseBodyStringifier.d.ts +107 -0
  86. package/lib/typings/Creator.d.ts +10 -0
  87. package/lib/utils/ArrayUtil.d.ts +21 -0
  88. package/lib/utils/ArrayUtil.js +22 -1
  89. package/lib/utils/ArrayUtil.js.map +1 -1
  90. package/lib/utils/ExceptionManager.d.ts +40 -0
  91. package/lib/utils/ExceptionManager.js +34 -0
  92. package/lib/utils/ExceptionManager.js.map +1 -1
  93. package/lib/utils/SourceFinder.d.ts +22 -0
  94. package/lib/utils/SourceFinder.js +34 -7
  95. package/lib/utils/SourceFinder.js.map +1 -1
  96. package/lib/utils/VersioningStrategy.d.ts +36 -1
  97. package/lib/utils/VersioningStrategy.js +30 -9
  98. package/lib/utils/VersioningStrategy.js.map +1 -1
  99. package/native/go.mod +1 -1
  100. package/native/go.sum +2 -2
  101. package/native/plugin/plan.go +68 -2
  102. package/native/transform/ast.go +19 -1
  103. package/native/transform/build.go +51 -57
  104. package/native/transform/buildoutput/output.go +147 -0
  105. package/native/transform/contributor.go +9 -0
  106. package/native/transform/core_querify.go +37 -3
  107. package/native/transform/core_transform.go +105 -98
  108. package/native/transform/core_websocket.go +21 -5
  109. package/native/transform/node_transform.go +11 -8
  110. package/native/transform/package.go +55 -0
  111. package/native/transform/printer.go +1 -3
  112. package/native/transform/run.go +18 -0
  113. package/native/transform/schemaprojection/project.go +213 -0
  114. package/native/transform/transform.go +21 -0
  115. package/package.json +5 -4
  116. package/src/adaptors/McpAdaptor.ts +86 -26
  117. package/src/adaptors/WebSocketAdaptor.ts +66 -14
  118. package/src/adaptors/internal/external_context.ts +37 -6
  119. package/src/decorators/DynamicModule.ts +12 -0
  120. package/src/decorators/EncryptedBody.ts +37 -13
  121. package/src/decorators/EncryptedController.ts +6 -1
  122. package/src/decorators/EncryptedModule.ts +18 -3
  123. package/src/decorators/EncryptedRoute.ts +20 -11
  124. package/src/decorators/HumanRoute.ts +5 -0
  125. package/src/decorators/McpRoute.ts +31 -19
  126. package/src/decorators/PlainBody.ts +20 -10
  127. package/src/decorators/SwaggerCustomizer.ts +15 -0
  128. package/src/decorators/SwaggerExample.ts +51 -22
  129. package/src/decorators/TypedBody.ts +17 -8
  130. package/src/decorators/TypedException.ts +10 -0
  131. package/src/decorators/TypedFormData.ts +59 -1
  132. package/src/decorators/TypedHeaders.ts +5 -0
  133. package/src/decorators/TypedParam.ts +9 -0
  134. package/src/decorators/TypedQuery.ts +16 -0
  135. package/src/decorators/TypedRoute.ts +25 -10
  136. package/src/decorators/WebSocketRoute.ts +18 -1
  137. package/src/decorators/doNotThrowTransformError.ts +14 -0
  138. package/src/decorators/internal/IMcpRouteReflect.ts +7 -1
  139. package/src/decorators/internal/IRequestBodyValidation.ts +65 -0
  140. package/src/decorators/internal/IWebSocketRouteReflect.ts +95 -1
  141. package/src/decorators/internal/get_path_and_querify.ts +3 -9
  142. package/src/decorators/internal/get_path_and_stringify.ts +3 -9
  143. package/src/decorators/internal/load_controller.ts +14 -0
  144. package/src/decorators/internal/split_route_arguments.ts +20 -0
  145. package/src/decorators/internal/validate_request_body.ts +87 -34
  146. package/src/options/IRequestBodyValidator.ts +71 -0
  147. package/src/options/IRequestFormDataProps.ts +80 -0
  148. package/src/options/IRequestHeadersValidator.ts +67 -0
  149. package/src/options/IRequestQueryValidator.ts +68 -0
  150. package/src/options/IResponseBodyQuerifier.ts +120 -0
  151. package/src/options/IResponseBodyStringifier.ts +107 -0
  152. package/src/typings/Creator.ts +10 -0
  153. package/src/utils/ArrayUtil.ts +22 -3
  154. package/src/utils/ExceptionManager.ts +40 -0
  155. package/src/utils/SourceFinder.ts +38 -7
  156. package/src/utils/VersioningStrategy.ts +43 -13
@@ -21,20 +21,59 @@ import path from "path";
21
21
  import { Duplex } from "stream";
22
22
  import { WebSocketAcceptor } from "tgrid";
23
23
  import typia from "typia";
24
+ import { fileURLToPath } from "url";
24
25
  import WebSocket from "ws";
25
26
 
26
27
  import { IWebSocketRouteReflect } from "../decorators/internal/IWebSocketRouteReflect";
28
+ import { resolve_request_body } from "../decorators/internal/validate_request_body";
27
29
  import { ArrayUtil } from "../utils/ArrayUtil";
28
30
  import { VersioningStrategy } from "../utils/VersioningStrategy";
29
31
  import { RoutePathMatcher } from "./internal/RoutePathMatcher";
30
32
 
33
+ /**
34
+ * Serves the `@WebSocketRoute()` methods of a NestJS application over
35
+ * WebSocket.
36
+ *
37
+ * Call {@link upgrade} after the application has been created. It finds the
38
+ * routes, checks them, and takes over the HTTP server's `upgrade` events. A
39
+ * handshake whose path matches no route is rejected with close code 1002.
40
+ *
41
+ * @evidence contracts/common.md#principled-implementation Routes are matched by paths built from the global prefix, the version, the module prefix, the controller path, and the method path, joined and normalized the same way for every combination; the adapter answers upgrade events itself through a `ws` server in no-server mode and removes its listeners when the HTTP server closes.
42
+ * @evidence contracts/common.md#clear-and-simple-design A class with one static factory, one `close` property, and the private handler; discovery, method checks, termination, and close reasons are module-private functions.
43
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The global prefix, versioning options, and module graph are read from `app.config` and `app.container`, which NestJS keeps internal because `INestApplication` has no public accessor for them; this is the limitation of the adapter, not a fixture-specific branch.
44
+ * @evidence contracts/common.md#meaningful-documentation The comment states when to call it and what happens to an unmatched handshake.
45
+ * @evidence contracts/portability.md#os-neutral-implementation Diagnostic source URLs are converted by fileURLToPath before path.relative uses native paths; route matching uses HTTP path segments separately from filesystem paths. No filesystem case policy is inferred from the OS.
46
+ */
31
47
  export class WebSocketAdaptor {
48
+ /**
49
+ * Creates the adapter for the application: it visits every controller,
50
+ * validates the WebSocket routes, and starts listening for upgrade events.
51
+ *
52
+ * All route errors of the application are collected and thrown together, with
53
+ * the controller, method, source location, and reasons of each.
54
+ *
55
+ * @evidence contracts/common.md#principled-implementation Controller data-method descriptors are checked before any handshake is served, with child properties shadowing inherited methods and accessors never executed, and all defects are reported in one error, so a misconfigured route fails at start and not at the first client; source locations come from `get-function-location`, converted with `fileURLToPath` so a `file:` URL becomes the path on every platform.
56
+ * @evidence contracts/common.md#clear-and-simple-design One asynchronous factory that awaits the visitor and hands the operator list to the private constructor.
57
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The visitor reports through the same error path for every controller; no controller or route name is special-cased.
58
+ * @evidence contracts/common.md#meaningful-documentation The comment states the collected error report and the effect on the server.
59
+ * @evidence contracts/portability.md#os-neutral-implementation Diagnostic source URLs are converted by fileURLToPath before path.relative uses native paths; route matching uses HTTP path segments separately from filesystem paths. No filesystem case policy is inferred from the OS.
60
+ */
32
61
  public static async upgrade(
33
62
  app: INestApplication,
34
63
  ): Promise<WebSocketAdaptor> {
35
64
  return new this(app, await visitApplication(app));
36
65
  }
37
66
 
67
+ /**
68
+ * Stops the adapter: removes its listeners from the HTTP server and closes
69
+ * the WebSocket server.
70
+ *
71
+ * @evidence contracts/common.md#principled-implementation The listeners are removed before the `ws` server closes, so no upgrade event reaches a closing server, and the returned promise resolves when the server has closed.
72
+ * @evidence contracts/common.md#clear-and-simple-design An arrow property so the same function value can be registered and unregistered as the HTTP server's close listener.
73
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It releases exactly what the adapter registered.
74
+ * @evidence contracts/common.md#meaningful-documentation The comment states what is released.
75
+ * @evidence contracts/portability.md#os-neutral-implementation Diagnostic source URLs are converted by fileURLToPath before path.relative uses native paths; route matching uses HTTP path segments separately from filesystem paths. No filesystem case policy is inferred from the OS.
76
+ */
38
77
  public readonly close = async (): Promise<void> =>
39
78
  new Promise((resolve) => {
40
79
  this.http.off("close", this.close);
@@ -207,8 +246,7 @@ const visitController = async (props: {
207
246
  modulePrefix: props.modulePrefix,
208
247
  };
209
248
  for (const mk of getOwnPropertyNames(controller.prototype).filter(
210
- (key) =>
211
- key !== "constructor" && typeof controller.prototype[key] === "function",
249
+ (key) => key !== "constructor",
212
250
  )) {
213
251
  const errorMessages: string[] = [];
214
252
  visitMethod({
@@ -229,7 +267,9 @@ const visitController = async (props: {
229
267
  ...file,
230
268
  source: path.relative(
231
269
  process.cwd(),
232
- file.source.replace("file:///", ""),
270
+ file.source.startsWith("file:")
271
+ ? fileURLToPath(file.source)
272
+ : file.source,
233
273
  ),
234
274
  });
235
275
  }
@@ -342,9 +382,12 @@ const visitMethod = (props: {
342
382
  else if (p.category === "driver")
343
383
  args.push(input.acceptor.getDriver());
344
384
  else if (p.category === "header") {
345
- const error: Error | null = p.validate(input.acceptor.header);
346
- if (error !== null) throw error;
347
- args.push(input.acceptor.header);
385
+ const result = resolve_request_body(
386
+ p.validate,
387
+ input.acceptor.header,
388
+ );
389
+ if (!result.success) throw result.error;
390
+ args.push(result.data);
348
391
  } else if (p.category === "param")
349
392
  args.push(p.assert(input.params[p.field]!));
350
393
  else if (p.category === "query") {
@@ -475,14 +518,23 @@ const MAX_CLOSE_REASON_BYTES: number = 123;
475
518
 
476
519
  const wrapPaths = (value: string[]) => (value.length === 0 ? [""] : value);
477
520
  const getOwnPropertyNames = (prototype: any): string[] => {
478
- const result: Set<string> = new Set();
479
- const iterate = (m: any) => {
480
- if (m === null) return;
481
- for (const k of Object.getOwnPropertyNames(m)) result.add(k);
482
- iterate(Object.getPrototypeOf(m));
483
- };
484
- iterate(prototype);
485
- return Array.from(result);
521
+ const visited: Set<string> = new Set();
522
+ const result: string[] = [];
523
+ for (
524
+ let current = prototype;
525
+ current !== null;
526
+ current = Object.getPrototypeOf(current)
527
+ )
528
+ for (const key of Object.getOwnPropertyNames(current)) {
529
+ if (visited.has(key)) continue;
530
+ visited.add(key);
531
+ if (
532
+ typeof Object.getOwnPropertyDescriptor(current, key)?.value ===
533
+ "function"
534
+ )
535
+ result.push(key);
536
+ }
537
+ return result;
486
538
  };
487
539
 
488
540
  interface Entry<T> {
@@ -1,5 +1,12 @@
1
1
  import { ContextId, ContextIdFactory, NestContainer } from "@nestjs/core";
2
+ import { ExternalExceptionFilterContext } from "@nestjs/core/exceptions/external-exception-filter-context";
3
+ import { GuardsConsumer, GuardsContextCreator } from "@nestjs/core/guards";
2
4
  import { ExternalContextCreator } from "@nestjs/core/helpers/external-context-creator";
5
+ import {
6
+ InterceptorsConsumer,
7
+ InterceptorsContextCreator,
8
+ } from "@nestjs/core/interceptors";
9
+ import { PipesConsumer, PipesContextCreator } from "@nestjs/core/pipes";
3
10
  import { REQUEST_CONTEXT_ID } from "@nestjs/core/router/request/request-constants";
4
11
 
5
12
  /**
@@ -15,12 +22,36 @@ import { REQUEST_CONTEXT_ID } from "@nestjs/core/router/request/request-constant
15
22
  export const create_external_context_creator = (
16
23
  container: NestContainer,
17
24
  moduleKey: string,
18
- ): ExternalContextCreator => {
19
- const creator: ExternalContextCreator =
20
- ExternalContextCreator.fromContainer(container);
21
- creator.getContextModuleKey = () => moduleKey;
22
- return creator;
23
- };
25
+ ): ExternalContextCreator => new ControllerContextCreator(container, moduleKey);
26
+
27
+ class ControllerContextCreator extends ExternalContextCreator {
28
+ public constructor(
29
+ private readonly owner: NestContainer,
30
+ private readonly moduleKey: string,
31
+ ) {
32
+ super(
33
+ new GuardsContextCreator(owner, owner.applicationConfig),
34
+ new GuardsConsumer(),
35
+ new InterceptorsContextCreator(owner, owner.applicationConfig),
36
+ new InterceptorsConsumer(),
37
+ owner.getModules(),
38
+ new PipesContextCreator(owner, owner.applicationConfig),
39
+ new PipesConsumer(),
40
+ new ExternalExceptionFilterContext(owner, owner.applicationConfig),
41
+ );
42
+ }
43
+
44
+ public override getContextModuleKey(): string {
45
+ return this.moduleKey;
46
+ }
47
+
48
+ public override registerRequestProvider<T = any>(
49
+ request: T,
50
+ contextId: ContextId,
51
+ ): void {
52
+ this.owner.registerRequestProvider(request, contextId);
53
+ }
54
+ }
24
55
 
25
56
  /**
26
57
  * The context a request-scoped provider is built in for this request, the one
@@ -11,6 +11,11 @@ import { load_controllers } from "./internal/load_controller";
11
11
  * controller classes dynamically just by specifying their directory path.
12
12
  *
13
13
  * @author Jeongho Nam - https://github.com/samchon
14
+ * @evidence contracts/common.md#principled-implementation The namespace builds a Nest module whose controllers are found on disk at startup instead of listed in code, using the shared loader.
15
+ * @evidence contracts/common.md#clear-and-simple-design One function that delegates discovery to `load_controllers` and decorates a class with the result.
16
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Paths are caller input, and no controller name is assumed.
17
+ * @evidence contracts/common.md#meaningful-documentation The comment describes the purpose and the path forms.
18
+ * @evidence contracts/portability.md#os-neutral-implementation Controller discovery delegates native path handling to SourceFinder and conversion of import paths to file URLs to load_controllers; module metadata contains controller constructors rather than filesystem identities.
14
19
  */
15
20
  export namespace DynamicModule {
16
21
  /**
@@ -23,7 +28,14 @@ export namespace DynamicModule {
23
28
  *
24
29
  * @param path Path of controllers
25
30
  * @param metadata Additional metadata except controllers
31
+ * @param isTsNode Select TypeScript source files explicitly; otherwise
32
+ * compiled JavaScript is tried first, with a source fallback under `ttsx`.
26
33
  * @returns Module instance
34
+ * @evidence contracts/common.md#principled-implementation Controllers are loaded from the paths, given as a path, a list, or include and exclude lists, and then a class decorated with `@Module` and those controllers is returned, so Nest treats the result as an ordinary module.
35
+ * @evidence contracts/common.md#clear-and-simple-design A short function whose discovery details live in `load_controllers`.
36
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It calls Nest's public `Module` decorator with the metadata; nothing is patched.
37
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the path forms and the `isTsNode` option.
38
+ * @evidence contracts/portability.md#os-neutral-implementation Controller discovery delegates native path handling to SourceFinder and conversion of import paths to file URLs to load_controllers; module metadata contains controller constructors rather than filesystem identities.
27
39
  */
28
40
  export async function mount(
29
41
  path: string | string[] | { include: string[]; exclude?: string[] },
@@ -20,22 +20,30 @@ import { validate_request_body } from "./internal/validate_request_body";
20
20
  * Encrypted body decorator.
21
21
  *
22
22
  * `EncryptedBody` is a decorator function getting `application/json` typed data
23
- * from request body which has been encrypted by AES-128/256 algorithm. Also,
24
- * `EncryptedBody` validates the request body data type through
25
- * [typia](https://github.com/samchon/typia) ad the validation speed is maximum
26
- * 15,000x times faster than `class-validator`.
23
+ * from request body which has been encrypted by AES-128/192/256 algorithm.
24
+ * Also, `EncryptedBody` validates the request body data type through
25
+ * [typia](https://github.com/samchon/typia) using the declared TypeScript
26
+ * type.
27
27
  *
28
28
  * For reference, when the request body data is not following the promised type
29
29
  * `T`, `BadRequestException` error (status code: 400) would be thrown. Also,
30
- * `EncryptedRoute` decrypts request body using those options.
30
+ * This decorator decrypts the request body using these options.
31
31
  *
32
- * - AES-128/256
32
+ * - AES-128/192/256
33
33
  * - CBC mode
34
34
  * - PKCS #5 Padding
35
35
  * - Base64 Encoding
36
36
  *
37
+ * Successful validator data becomes the decorated argument, including copies
38
+ * returned by clone validators after decryption and JSON parsing.
39
+ *
37
40
  * @author Jeongho Nam - https://github.com/samchon
38
41
  * @returns Parameter decorator
42
+ * @evidence contracts/common.md#principled-implementation The body must be `text/plain` ciphertext; it is decrypted with the password of the controller or module, the plain text is parsed as JSON and validated by the transformed validator, and a decryption or parse failure becomes a 400 with one fixed message that does not echo the ciphertext and does not tell the two failures apart; a key or initialization vector the cipher itself refuses is the server's configuration, independent of the request, and is thrown as it is.
43
+ * @evidence contracts/common.md#clear-and-simple-design One parameter decorator composes shared text reading, password lookup and validator resolution; the successfully resolved data becomes the argument without repeating validation or changing decryption error handling.
44
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The algorithm comes from `AesPkcs5` of the fetcher, the same one the client uses, and no key is embedded; a missing password is an error rather than a plain pass-through.
45
+ * @evidence contracts/common.md#meaningful-documentation The comment describes the protocol and the requirement of a password on the controller or module, and the private decode helper documents why every decryption failure collapses into one response.
46
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Ciphertext parsing, AES decryption and request validation operate on HTTP values, with no native filesystem or subprocess boundary.
39
47
  */
40
48
  export function EncryptedBody<T>(
41
49
  validator?: IRequestBodyValidator<T>,
@@ -70,9 +78,9 @@ export function EncryptedBody<T>(
70
78
 
71
79
  // PARSE AND VALIDATE DATA
72
80
  const data: any = decode(body, password.key, password.iv);
73
- const error: Error | null = checker(data);
74
- if (error !== null) throw error;
75
- return data;
81
+ const result = checker.resolve(data);
82
+ if (!result.success) throw result.error;
83
+ return result.data;
76
84
  })();
77
85
  }
78
86
 
@@ -89,9 +97,13 @@ export function EncryptedBody<T>(
89
97
  *
90
98
  * Both failure modes must therefore collapse into one indistinguishable
91
99
  * `BadRequestException` (identical status, message, and body). Only the
92
- * downstream type validation of an already-parsed body may report its own
93
- * distinct error, because reaching it requires plaintext the attacker cannot
94
- * forge without already knowing it.
100
+ * downstream type validation of an already-parsed body reports its own distinct
101
+ * error. AES-CBC provides no authentication; collapsing parse and padding
102
+ * errors does not establish ciphertext authenticity.
103
+ *
104
+ * A key or initialization vector the cipher refuses is thrown as it is. It says
105
+ * nothing about the body, so it opens no oracle, and it is a defect of the
106
+ * server's configuration that a 400 would blame on the client.
95
107
  *
96
108
  * @internal
97
109
  */
@@ -99,10 +111,22 @@ const decode = (body: string, key: string, iv: string): unknown => {
99
111
  try {
100
112
  return JSON.parse(AesPkcs5.decrypt(body, key, iv));
101
113
  } catch (exp) {
102
- if (exp instanceof Error)
114
+ // A password the cipher itself refuses is the server's configuration, which
115
+ // no request body can influence, so it is no oracle and no client's fault.
116
+ if (exp instanceof Error && PASSWORD_ERRORS.has((exp as any).code))
117
+ throw exp;
118
+ else if (exp instanceof Error)
103
119
  throw new BadRequestException(
104
120
  "Failed to decrypt the request body. Check your body content or encryption password.",
105
121
  );
106
122
  else throw exp;
107
123
  }
108
124
  };
125
+
126
+ /** The error codes of a key or an initialization vector the cipher refuses. */
127
+ const PASSWORD_ERRORS: Set<unknown> = new Set([
128
+ "ERR_INVALID_ARG_TYPE",
129
+ "ERR_CRYPTO_INVALID_IV",
130
+ "ERR_CRYPTO_INVALID_KEYLEN",
131
+ "ERR_CRYPTO_UNKNOWN_CIPHER",
132
+ ]);
@@ -7,7 +7,7 @@ import { ENCRYPTION_CONTROLLER_METADATA_KEY } from "./internal/EncryptedConstant
7
7
  * Encrypted controller.
8
8
  *
9
9
  * `EncryptedController` is an extension of the {@link nest.Controller} class
10
- * decorator function who configures encryption password of the AES-128/256
10
+ * decorator function who configures encryption password of the AES-128/192/256
11
11
  * algorithm. The encryption algorithm and password would be used by
12
12
  * {@link EncryptedRoute} and {@link EncryptedBody} to encrypt the request and
13
13
  * response body of the HTTP protocol.
@@ -28,6 +28,11 @@ import { ENCRYPTION_CONTROLLER_METADATA_KEY } from "./internal/EncryptedConstant
28
28
  * @param path Path of the HTTP request
29
29
  * @param password Encryption password or its getter function
30
30
  * @returns Class decorator
31
+ * @evidence contracts/common.md#principled-implementation The decorator stores the password as metadata on the class and then applies Nest's `Controller(path)`, so the encrypted decorators can find the password by reading the class.
32
+ * @evidence contracts/common.md#clear-and-simple-design One decorator that composes a metadata write with the standard decorator.
33
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses Nest's public `Controller` decorator and the reflect metadata API.
34
+ * @evidence contracts/common.md#meaningful-documentation The comment states the password forms and the encrypted decorators it enables.
35
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Controller paths are HTTP routing metadata and passwords are in-memory values; neither is a native filesystem path.
31
36
  */
32
37
  export function EncryptedController(
33
38
  path: string,
@@ -9,9 +9,9 @@ import { load_controllers } from "./internal/load_controller";
9
9
  * Encrypted module.
10
10
  *
11
11
  * `EncryptedModule` is an extension of the {@link Module} class decorator
12
- * function who configures encryption password of the AES-128/256 algorithm. The
13
- * encryption algorithm and password would be used by {@link EncryptedRoute} and
14
- * {@link EncryptedBody} to encrypt the request and response bod of the HTTP
12
+ * function who configures encryption password of the AES-128/192/256 algorithm.
13
+ * The encryption algorithm and password would be used by {@link EncryptedRoute}
14
+ * and {@link EncryptedBody} to encrypt the request and response bod of the HTTP
15
15
  * protocol.
16
16
  *
17
17
  * By using this `EncryptedModule` decorator function, all of the
@@ -38,6 +38,11 @@ import { load_controllers } from "./internal/load_controller";
38
38
  * @param metadata Module configuration metadata
39
39
  * @param password Encryption password or its getter function
40
40
  * @returns Class decorator
41
+ * @evidence contracts/common.md#principled-implementation A visited set breaks import cycles, the controllers of each visited module get the password as metadata, asynchronous modules are awaited through the same promise Nest awaits, and a forward reference is resolved once its class exists.
42
+ * @evidence contracts/common.md#clear-and-simple-design One decorator whose traversal is a private function; the dynamic-module helper is in the merged namespace.
43
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The traversal uses the public metadata keys of Nest modules; no module name is special-cased.
44
+ * @evidence contracts/common.md#meaningful-documentation The comment states which module shapes are followed.
45
+ * @evidence contracts/portability.md#os-neutral-implementation The decorator traverses Nest module metadata in memory; its dynamic helper delegates native paths and file-URL imports to load_controllers instead of composing platform-specific import strings.
41
46
  */
42
47
  export function EncryptedModule(
43
48
  metadata: Parameters<typeof Module>[0],
@@ -49,6 +54,16 @@ export function EncryptedModule(
49
54
  };
50
55
  }
51
56
 
57
+ /**
58
+ * Helpers of {@link EncryptedModule}, namely `dynamic`, which builds an
59
+ * encrypted module from controller paths.
60
+ *
61
+ * @evidence contracts/common.md#principled-implementation `dynamic` loads the controllers with the shared loader, wraps an object password into a closure, and returns a class decorated by `EncryptedModule`.
62
+ * @evidence contracts/common.md#clear-and-simple-design One function that reuses the loader and the decorator.
63
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Paths and password are caller input.
64
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the path forms and the password argument.
65
+ * @evidence contracts/portability.md#os-neutral-implementation The decorator traverses Nest module metadata in memory; its dynamic helper delegates native paths and file-URL imports to load_controllers instead of composing platform-specific import strings.
66
+ */
52
67
  export namespace EncryptedModule {
53
68
  /**
54
69
  * Dynamic encrypted module.
@@ -29,22 +29,26 @@ import { route_error } from "./internal/route_error";
29
29
  * Encrypted router decorator functions.
30
30
  *
31
31
  * `EncryptedRoute` is a module containing router decorator functions which
32
- * encrypts response body data through AES-128/256 encryption. Furthermore, they
33
- * can boost up JSON string conversion speed about 50x times faster than
34
- * `class-transformer`, even type safe through
35
- * [typia](https://github.com/samchon/typia).
32
+ * encrypts response body data through AES-128/192/256 encryption after
33
+ * serializing it with a [typia](https://github.com/samchon/typia) function
34
+ * generated from the declared return type.
36
35
  *
37
- * For reference, if you try to invalid data that is not following the promised
38
- * type `T`, 500 internal server error would be thrown. Also, as
39
- * `EncryptedRoute` composes JSON string through `typia.assertStringify<T>()`
40
- * function, it is not possible to modify response data through interceptors.
36
+ * The default assert serializer rejects a response outside the declared type
37
+ * with a 500 error. The configured stringify mode chooses the validation policy
38
+ * before encryption; `validate.log` logs failures and serializes the original
39
+ * value. Interceptors running afterwards receive ciphertext.
41
40
  *
42
- * - AES-128/256
41
+ * - AES-128/192/256
43
42
  * - CBC mode
44
43
  * - PKCS #5 Padding
45
44
  * - Base64 Encoding
46
45
  *
47
46
  * @author Jeongho Nam - https://github.com/samchon
47
+ * @evidence contracts/common.md#principled-implementation Each route method decorates Nest's router decorator with an interceptor that serializes the value as the transform chose, encrypts it with the password of the controller or module, and answers `text/plain` on success only, so an error keeps its own JSON type.
48
+ * @evidence contracts/common.md#clear-and-simple-design One generator function creates the five HTTP-method decorators, and the interceptor and the router table are module-private.
49
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The password lookup and the serializer come from shared modules; the loop after the namespace copies the marker properties of typia's stringify functions onto the five decorators as the transform expects, which mutates only these exported functions.
50
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the decorator family and its password requirement.
51
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Route paths and serialized ciphertext are HTTP protocol values; response interception does not access native files or launch a process.
48
52
  */
49
53
  export namespace EncryptedRoute {
50
54
  /**
@@ -56,7 +60,7 @@ export namespace EncryptedRoute {
56
60
  export const Get = Generator("Get");
57
61
 
58
62
  /**
59
- * Encrypted router decorator function for the GET method.
63
+ * Encrypted router decorator function for the POST method.
60
64
  *
61
65
  * @param paths Path(s) of the HTTP request
62
66
  * @returns Method decorator
@@ -93,7 +97,7 @@ export namespace EncryptedRoute {
93
97
  * If you've configured the transformation option to `validate.log` in the
94
98
  * `tsconfig.json` file, then the error log information of the response
95
99
  * validation failure would be logged through this function instead of
96
- * throwing the 400 bad request error.
100
+ * throwing the 500 internal server error.
97
101
  *
98
102
  * By the way, be careful. If you've configured the response transformation
99
103
  * option to be `validate.log`, client may get wrong response data. Therefore,
@@ -101,6 +105,11 @@ export namespace EncryptedRoute {
101
105
  *
102
106
  * @default console.log
103
107
  * @param func Logger function
108
+ * @evidence contracts/common.md#principled-implementation It stores the logger in `TypedRoute`, which owns the single logger both route families use.
109
+ * @evidence contracts/common.md#clear-and-simple-design A one-line delegation, so the logger has one owner.
110
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It changes only the logger variable.
111
+ * @evidence contracts/common.md#meaningful-documentation The comment states which validate mode uses the logger.
112
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Route paths and serialized ciphertext are HTTP protocol values; response interception does not access native files or launch a process.
104
113
  */
105
114
  export function setValidateErrorLogger(
106
115
  func: (log: IValidateErrorLog) => void,
@@ -13,6 +13,11 @@ import { SwaggerCustomizer } from "./SwaggerCustomizer";
13
13
  *
14
14
  * @author Jeongho Nam - https://github.com/samchon
15
15
  * @returns Method decorator
16
+ * @evidence contracts/common.md#principled-implementation The decorator marks the operation with the `x-samchon-human` extension through `SwaggerCustomizer`, so the generated document carries the mark without another metadata path.
17
+ * @evidence contracts/common.md#clear-and-simple-design One call to the customizer.
18
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The extension name is the documented contract of the generated document.
19
+ * @evidence contracts/common.md#meaningful-documentation The comment states that a marked API is left out of the LLM function calling schema.
20
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The OpenAPI extension is an in-memory document property and has no native path or process representation.
16
21
  */
17
22
  export function HumanRoute(): MethodDecorator {
18
23
  return SwaggerCustomizer((props) => {
@@ -25,27 +25,28 @@ import { validate_request_body } from "./internal/validate_request_body";
25
25
  * the {@link INestApplication} instance at bootstrap. The decorator alone only
26
26
  * stores reflection metadata.
27
27
  *
28
- * @author wildduck - https://github.com/wildduck2
29
- * @example
30
- * ```typescript
31
- * import core from "@nestia/core";
28
+ * Example:
29
+ *
30
+ * ```typescript
31
+ * import core from "@nestia/core";
32
32
  *
33
- * @Controller()
34
- * export class WeatherController {
35
- * /**
36
- * * Return current weather for a city.
37
- * *
38
- * * @title Get weather
39
- * *\/
40
- * @core.McpRoute("get_weather")
41
- * public async get(
42
- * @core.McpRoute.Params() params: { city: string },
43
- * ): Promise<{ temp: number }> {
44
- * return { temp: 22 };
45
- * }
33
+ * @Controller()
34
+ * export class WeatherController {
35
+ * /**
36
+ * * Return current weather for a city.
37
+ * *
38
+ * * @title Get weather
39
+ * *\/
40
+ * @core.McpRoute("get_weather")
41
+ * public async get(
42
+ * @core.McpRoute.Params() params: { city: string },
43
+ * ): Promise<{ temp: number }> {
44
+ * return { temp: 22 };
46
45
  * }
47
- * ```;
46
+ * }
47
+ * ```
48
48
  *
49
+ * @author wildduck - https://github.com/wildduck2
49
50
  * @param name Unique tool identifier exposed to MCP clients via `tools/list`.
50
51
  * @returns Method decorator.
51
52
  */
@@ -78,6 +79,16 @@ export function McpRoute(input: string | McpRoute.IConfig): MethodDecorator {
78
79
  };
79
80
  }
80
81
 
82
+ /**
83
+ * Types and parameter decorator of {@link McpRoute}: the tool configuration and
84
+ * `McpRoute.Params`.
85
+ *
86
+ * @evidence contracts/common.md#principled-implementation Tool metadata retains its definition and default empty-object schema. Params records its argument position and shared checker; successful assert/validate data becomes the tool argument, while errors retain InvalidParams mapping and legacy error-only callbacks retain raw input. The adaptor reads the same metadata field names when registering tools.
87
+ * @evidence contracts/common.md#clear-and-simple-design Two decorators and one config type in one namespace, sharing one `emplace` helper for parameter metadata.
88
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses the reflect metadata API and no NestJS internals; the input schema is filled by the generator, not hardcoded here.
89
+ * @evidence contracts/common.md#meaningful-documentation The comment documents the two call forms, the configuration fields, and the parameter decorator.
90
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Tool schemas and parameter metadata are protocol data held on controller methods, without native filesystem access.
91
+ */
81
92
  export namespace McpRoute {
82
93
  /**
83
94
  * Configuration object emitted by the nestia transformer at compile time.
@@ -104,7 +115,8 @@ export namespace McpRoute {
104
115
  * `tools/call` request against the TypeScript type `T` using typia. A failed
105
116
  * validation surfaces to the client as a JSON-RPC `-32602` (`InvalidParams`)
106
117
  * error with structured diagnostics, giving the LLM precise feedback to
107
- * self-correct.
118
+ * self-correct. Successful assert and validate data becomes the tool
119
+ * argument, so clone validators can supply a copy of the incoming arguments.
108
120
  *
109
121
  * MCP tools accept exactly one arguments object; applying this decorator more
110
122
  * than once on a single method is a compile-time error. The decorated type
@@ -15,7 +15,8 @@ import { validate_request_body } from "./internal/validate_request_body";
15
15
  * Plain body decorator.
16
16
  *
17
17
  * `PlainBody` is a decorator function getting full body text from the HTTP
18
- * request.
18
+ * request. The transformed assertion's successful string is the decorated
19
+ * argument.
19
20
  *
20
21
  * If you adjust the regular {@link Body} decorator function to the body
21
22
  * parameter, you can't get the full body text because the {@link Body} tries to
@@ -32,10 +33,18 @@ import { validate_request_body } from "./internal/validate_request_body";
32
33
  *
33
34
  * @author Jeongho Nam - https://github.com/samchon
34
35
  * @returns Parameter decorator
36
+ * @evidence contracts/common.md#principled-implementation The decorator reads text only for text/plain requests; an installed assertion resolves the argument value through the shared body checker, while no assertion retains raw text. Absent input is accepted when its assertion accepts undefined and otherwise continues through the media-type error path.
37
+ * @evidence contracts/common.md#clear-and-simple-design One parameter decorator shares body absence, media recognition, text reading and successful-value resolution with the other request decorators.
38
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Text comes from the request parser or shared raw-body reader, not a fabricated value; no runtime controller or body identity is special-cased.
39
+ * @evidence contracts/common.md#meaningful-documentation The comment distinguishes full body text from JSON parsing and states that the transformed assertion supplies the argument.
40
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation HTTP text bodies and decoded string values carry no native filesystem or subprocess boundary.
35
41
  */
36
42
  export function PlainBody(): ParameterDecorator;
37
43
 
38
- /** @internal */
44
+ /**
45
+ * Registers the transformed text assertion and binds its successful value. The
46
+ * public overload accepts no runtime assertion; the transform supplies it.
47
+ */
39
48
  export function PlainBody(
40
49
  assert?: (input: unknown) => string,
41
50
  ): ParameterDecorator {
@@ -52,17 +61,18 @@ export function PlainBody(
52
61
  const request: express.Request | FastifyRequest = context
53
62
  .switchToHttp()
54
63
  .getRequest();
55
- if (
56
- is_request_body_undefined(request) &&
57
- (checker ?? (() => null))(undefined as any) === null
58
- )
59
- return undefined;
60
- else if (!is_media_type(request.headers["content-type"], "text/plain"))
64
+ if (is_request_body_undefined(request)) {
65
+ if (checker === null) return undefined;
66
+ const result = checker.resolve(undefined);
67
+ if (result.success) return result.data;
68
+ }
69
+ if (!is_media_type(request.headers["content-type"], "text/plain"))
61
70
  throw new BadRequestException(`Request body type is not "text/plain".`);
62
71
  const value: string = await get_text_body(request);
63
72
  if (checker) {
64
- const error: Error | null = checker(value);
65
- if (error !== null) throw error;
73
+ const result = checker.resolve(value);
74
+ if (!result.success) throw result.error;
75
+ return result.data;
66
76
  }
67
77
  return value;
68
78
  })();
@@ -13,6 +13,11 @@ import { OpenApi } from "@typia/interface";
13
13
  * @author Jeongho Nam - https://github.com/samchon
14
14
  * @param closure Callback function which can customize the swagger data
15
15
  * @returns Method decorator
16
+ * @evidence contracts/common.md#principled-implementation Each use appends the closure to an array of metadata owned by the method, created on first use, so several customizers on one method run in order.
17
+ * @evidence contracts/common.md#clear-and-simple-design One decorator function with one metadata list per method.
18
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses the reflect metadata API; the closure is the caller's.
19
+ * @evidence contracts/common.md#meaningful-documentation The comment documents that the closure receives the document, the operation, and lookup helpers.
20
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callback and endpoint accessors edit an OpenAPI document; their path fields are HTTP routes rather than native file identities.
16
21
  */
17
22
  export function SwaggerCustomizer(
18
23
  closure: (props: SwaggerCustomizer.IProps) => unknown,
@@ -44,6 +49,16 @@ export function SwaggerCustomizer(
44
49
  return descriptor;
45
50
  };
46
51
  }
52
+ /**
53
+ * Types of {@link SwaggerCustomizer}: the properties the closure receives and
54
+ * the endpoint accessors.
55
+ *
56
+ * @evidence contracts/common.md#principled-implementation The types describe the document, the current operation, and lookups by function or by path and method, which is what a customization needs to edit the generated document.
57
+ * @evidence contracts/common.md#clear-and-simple-design Three types with no runtime members.
58
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It contains types only.
59
+ * @evidence contracts/common.md#meaningful-documentation Each type and member documents its meaning.
60
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callback and endpoint accessors edit an OpenAPI document; their path fields are HTTP routes rather than native file identities.
61
+ */
47
62
  export namespace SwaggerCustomizer {
48
63
  /**
49
64
  * Properties for the `SwaggerCustomizer` decorator.