@nestia/core 14.0.0 → 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 (192) hide show
  1. package/MIGRATION.md +20 -0
  2. package/README.md +1 -1
  3. package/lib/adaptors/McpAdaptor.d.ts +29 -3
  4. package/lib/adaptors/McpAdaptor.js +150 -24
  5. package/lib/adaptors/McpAdaptor.js.map +1 -1
  6. package/lib/adaptors/WebSocketAdaptor.d.ts +37 -0
  7. package/lib/adaptors/WebSocketAdaptor.js +184 -48
  8. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  9. package/lib/adaptors/internal/RoutePathMatcher.d.ts +1 -0
  10. package/lib/adaptors/internal/RoutePathMatcher.js +88 -0
  11. package/lib/adaptors/internal/RoutePathMatcher.js.map +1 -0
  12. package/lib/adaptors/internal/external_context.d.ts +1 -0
  13. package/lib/adaptors/internal/external_context.js +56 -0
  14. package/lib/adaptors/internal/external_context.js.map +1 -0
  15. package/lib/decorators/DynamicModule.d.ts +12 -0
  16. package/lib/decorators/DynamicModule.js +12 -0
  17. package/lib/decorators/DynamicModule.js.map +1 -1
  18. package/lib/decorators/EncryptedBody.d.ts +14 -6
  19. package/lib/decorators/EncryptedBody.js +39 -16
  20. package/lib/decorators/EncryptedBody.js.map +1 -1
  21. package/lib/decorators/EncryptedController.d.ts +6 -1
  22. package/lib/decorators/EncryptedController.js +7 -2
  23. package/lib/decorators/EncryptedController.js.map +1 -1
  24. package/lib/decorators/EncryptedModule.d.ts +25 -6
  25. package/lib/decorators/EncryptedModule.js +87 -18
  26. package/lib/decorators/EncryptedModule.js.map +1 -1
  27. package/lib/decorators/EncryptedRoute.d.ts +20 -11
  28. package/lib/decorators/EncryptedRoute.js +27 -13
  29. package/lib/decorators/EncryptedRoute.js.map +1 -1
  30. package/lib/decorators/HumanRoute.d.ts +5 -0
  31. package/lib/decorators/HumanRoute.js +5 -0
  32. package/lib/decorators/HumanRoute.js.map +1 -1
  33. package/lib/decorators/McpRoute.d.ts +31 -19
  34. package/lib/decorators/McpRoute.js +12 -1
  35. package/lib/decorators/McpRoute.js.map +1 -1
  36. package/lib/decorators/PlainBody.d.ts +7 -1
  37. package/lib/decorators/PlainBody.js +16 -8
  38. package/lib/decorators/PlainBody.js.map +1 -1
  39. package/lib/decorators/SwaggerCustomizer.d.ts +15 -0
  40. package/lib/decorators/SwaggerCustomizer.js +5 -0
  41. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  42. package/lib/decorators/SwaggerExample.d.ts +51 -22
  43. package/lib/decorators/SwaggerExample.js +35 -22
  44. package/lib/decorators/SwaggerExample.js.map +1 -1
  45. package/lib/decorators/TypedBody.d.ts +10 -3
  46. package/lib/decorators/TypedBody.js +20 -10
  47. package/lib/decorators/TypedBody.js.map +1 -1
  48. package/lib/decorators/TypedException.d.ts +10 -0
  49. package/lib/decorators/TypedFormData.d.ts +81 -9
  50. package/lib/decorators/TypedFormData.js +121 -31
  51. package/lib/decorators/TypedFormData.js.map +1 -1
  52. package/lib/decorators/TypedHeaders.d.ts +5 -0
  53. package/lib/decorators/TypedHeaders.js +5 -0
  54. package/lib/decorators/TypedHeaders.js.map +1 -1
  55. package/lib/decorators/TypedParam.d.ts +12 -3
  56. package/lib/decorators/TypedParam.js +12 -3
  57. package/lib/decorators/TypedParam.js.map +1 -1
  58. package/lib/decorators/TypedQuery.d.ts +17 -1
  59. package/lib/decorators/TypedQuery.js +25 -4
  60. package/lib/decorators/TypedQuery.js.map +1 -1
  61. package/lib/decorators/TypedRoute.d.ts +25 -10
  62. package/lib/decorators/TypedRoute.js +19 -10
  63. package/lib/decorators/TypedRoute.js.map +1 -1
  64. package/lib/decorators/WebSocketRoute.d.ts +18 -1
  65. package/lib/decorators/WebSocketRoute.js +18 -1
  66. package/lib/decorators/WebSocketRoute.js.map +1 -1
  67. package/lib/decorators/doNotThrowTransformError.d.ts +14 -0
  68. package/lib/decorators/doNotThrowTransformError.js +14 -0
  69. package/lib/decorators/doNotThrowTransformError.js.map +1 -1
  70. package/lib/decorators/internal/EncryptedConstant.js +13 -2
  71. package/lib/decorators/internal/EncryptedConstant.js.map +1 -1
  72. package/lib/decorators/internal/IRequestBodyValidation.d.ts +66 -0
  73. package/lib/decorators/internal/IRequestBodyValidation.js +3 -0
  74. package/lib/decorators/internal/IRequestBodyValidation.js.map +1 -0
  75. package/lib/decorators/internal/IWebSocketRouteReflect.d.ts +93 -1
  76. package/lib/decorators/internal/get_encryption_password.d.ts +1 -0
  77. package/lib/decorators/internal/get_encryption_password.js +14 -0
  78. package/lib/decorators/internal/get_encryption_password.js.map +1 -0
  79. package/lib/decorators/internal/get_path_and_querify.js +23 -9
  80. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  81. package/lib/decorators/internal/get_path_and_stringify.js +3 -7
  82. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  83. package/lib/decorators/internal/get_text_body.js +9 -3
  84. package/lib/decorators/internal/get_text_body.js.map +1 -1
  85. package/lib/decorators/internal/group_entries.d.ts +1 -0
  86. package/lib/decorators/internal/group_entries.js +19 -0
  87. package/lib/decorators/internal/group_entries.js.map +1 -0
  88. package/lib/decorators/internal/load_controller.d.ts +14 -0
  89. package/lib/decorators/internal/load_controller.js +14 -0
  90. package/lib/decorators/internal/load_controller.js.map +1 -1
  91. package/lib/decorators/internal/split_route_arguments.d.ts +1 -0
  92. package/lib/decorators/internal/split_route_arguments.js +22 -0
  93. package/lib/decorators/internal/split_route_arguments.js.map +1 -0
  94. package/lib/decorators/internal/validate_request_body.js +68 -25
  95. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  96. package/lib/decorators/internal/validate_request_form_data.js +3 -1
  97. package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
  98. package/lib/decorators/internal/validate_request_headers.js +2 -1
  99. package/lib/decorators/internal/validate_request_headers.js.map +1 -1
  100. package/lib/decorators/internal/validate_request_query.js +3 -1
  101. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  102. package/lib/options/IRequestBodyValidator.d.ts +71 -0
  103. package/lib/options/IRequestFormDataProps.d.ts +80 -0
  104. package/lib/options/IRequestHeadersValidator.d.ts +67 -0
  105. package/lib/options/IRequestQueryValidator.d.ts +68 -0
  106. package/lib/options/IResponseBodyQuerifier.d.ts +125 -1
  107. package/lib/options/IResponseBodyStringifier.d.ts +107 -0
  108. package/lib/typings/Creator.d.ts +10 -0
  109. package/lib/utils/ArrayUtil.d.ts +21 -0
  110. package/lib/utils/ArrayUtil.js +22 -1
  111. package/lib/utils/ArrayUtil.js.map +1 -1
  112. package/lib/utils/ExceptionManager.d.ts +43 -4
  113. package/lib/utils/ExceptionManager.js +44 -6
  114. package/lib/utils/ExceptionManager.js.map +1 -1
  115. package/lib/utils/SourceFinder.d.ts +22 -0
  116. package/lib/utils/SourceFinder.js +60 -10
  117. package/lib/utils/SourceFinder.js.map +1 -1
  118. package/lib/utils/VersioningStrategy.d.ts +45 -2
  119. package/lib/utils/VersioningStrategy.js +40 -7
  120. package/lib/utils/VersioningStrategy.js.map +1 -1
  121. package/native/go.mod +1 -1
  122. package/native/go.sum +2 -2
  123. package/native/plugin/plan.go +68 -2
  124. package/native/transform/ast.go +23 -3
  125. package/native/transform/build.go +51 -57
  126. package/native/transform/buildoutput/output.go +147 -0
  127. package/native/transform/contributor.go +9 -0
  128. package/native/transform/core_querify.go +63 -9
  129. package/native/transform/core_transform.go +180 -98
  130. package/native/transform/core_websocket.go +111 -14
  131. package/native/transform/node_transform.go +17 -8
  132. package/native/transform/package.go +55 -0
  133. package/native/transform/printer.go +1 -3
  134. package/native/transform/run.go +18 -0
  135. package/native/transform/schemaprojection/project.go +213 -0
  136. package/native/transform/transform.go +29 -18
  137. package/native/transform/typia_fast.go +6 -0
  138. package/native/transform.cjs +49 -0
  139. package/package.json +13 -15
  140. package/src/adaptors/McpAdaptor.ts +215 -33
  141. package/src/adaptors/WebSocketAdaptor.ts +202 -52
  142. package/src/adaptors/internal/RoutePathMatcher.ts +91 -0
  143. package/src/adaptors/internal/external_context.ts +81 -0
  144. package/src/decorators/DynamicModule.ts +12 -0
  145. package/src/decorators/EncryptedBody.ts +39 -15
  146. package/src/decorators/EncryptedController.ts +12 -3
  147. package/src/decorators/EncryptedModule.ts +89 -18
  148. package/src/decorators/EncryptedRoute.ts +27 -16
  149. package/src/decorators/HumanRoute.ts +5 -0
  150. package/src/decorators/McpRoute.ts +31 -19
  151. package/src/decorators/PlainBody.ts +20 -10
  152. package/src/decorators/SwaggerCustomizer.ts +15 -0
  153. package/src/decorators/SwaggerExample.ts +51 -22
  154. package/src/decorators/TypedBody.ts +18 -9
  155. package/src/decorators/TypedException.ts +10 -0
  156. package/src/decorators/TypedFormData.ts +207 -45
  157. package/src/decorators/TypedHeaders.ts +5 -0
  158. package/src/decorators/TypedParam.ts +12 -3
  159. package/src/decorators/TypedQuery.ts +35 -7
  160. package/src/decorators/TypedRoute.ts +25 -10
  161. package/src/decorators/WebSocketRoute.ts +18 -1
  162. package/src/decorators/doNotThrowTransformError.ts +14 -0
  163. package/src/decorators/internal/EncryptedConstant.ts +14 -1
  164. package/src/decorators/internal/IMcpRouteReflect.ts +7 -1
  165. package/src/decorators/internal/IRequestBodyValidation.ts +65 -0
  166. package/src/decorators/internal/IWebSocketRouteReflect.ts +95 -1
  167. package/src/decorators/internal/get_encryption_password.ts +19 -0
  168. package/src/decorators/internal/get_path_and_querify.ts +31 -10
  169. package/src/decorators/internal/get_path_and_stringify.ts +3 -9
  170. package/src/decorators/internal/get_text_body.ts +8 -4
  171. package/src/decorators/internal/group_entries.ts +17 -0
  172. package/src/decorators/internal/load_controller.ts +14 -0
  173. package/src/decorators/internal/split_route_arguments.ts +20 -0
  174. package/src/decorators/internal/validate_request_body.ts +87 -34
  175. package/src/decorators/internal/validate_request_form_data.ts +3 -1
  176. package/src/decorators/internal/validate_request_headers.ts +2 -2
  177. package/src/decorators/internal/validate_request_query.ts +3 -2
  178. package/src/options/IRequestBodyValidator.ts +71 -0
  179. package/src/options/IRequestFormDataProps.ts +80 -0
  180. package/src/options/IRequestHeadersValidator.ts +67 -0
  181. package/src/options/IRequestQueryValidator.ts +68 -0
  182. package/src/options/IResponseBodyQuerifier.ts +126 -1
  183. package/src/options/IResponseBodyStringifier.ts +107 -0
  184. package/src/typings/Creator.ts +10 -0
  185. package/src/utils/ArrayUtil.ts +22 -3
  186. package/src/utils/ExceptionManager.ts +50 -6
  187. package/src/utils/SourceFinder.ts +62 -10
  188. package/src/utils/VersioningStrategy.ts +55 -11
  189. package/lib/transform.d.ts +0 -10
  190. package/lib/transform.js +0 -25
  191. package/lib/transform.js.map +0 -1
  192. package/src/transform.ts +0 -26
@@ -1,3 +1,5 @@
1
+ import { IRequestBodyValidation } from "./IRequestBodyValidation";
2
+
1
3
  /**
2
4
  * Reflection metadata stored by the {@link McpRoute} decorator.
3
5
  *
@@ -29,12 +31,16 @@ export namespace IMcpRouteReflect {
29
31
  *
30
32
  * `validate` is the closure returned by `validate_request_body(...)`; it
31
33
  * returns `null` on success or an Error carrying validation details.
34
+ * Factory-owned callbacks additionally resolve successful data. The optional
35
+ * resolver preserves compatibility with existing error-only metadata
36
+ * callbacks.
32
37
  *
33
38
  * @internal
34
39
  */
35
40
  export interface IArgument {
36
41
  category: "params";
37
42
  index: number;
38
- validate: (input: any) => Error | null;
43
+ validate: ((input: any) => Error | null) &
44
+ Pick<IRequestBodyValidation<any>, "resolve">;
39
45
  }
40
46
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Internal request validation callable with an optional successful-value
3
+ * resolver.
4
+ *
5
+ * The callable preserves the legacy error-or-null metadata contract. Factory
6
+ * validators also expose `resolve` so consumers can retain clone results
7
+ * without treating a valid Error, null or undefined value as a failure.
8
+ *
9
+ * @evidence contracts/common.md#principled-implementation Resolver input is untrusted unknown and only successful data is T. Existing public descriptor callbacks retain their input:T signature; the private adapter casts once at that compatibility boundary and relies on the callback's validation decision, without asserting input validity beforehand. Assert returns and validation.data are retained; is and legacy null success retain accepted input. Errors preserve 400 details and non-guard throws, and explicit tags permit successful Error/null/undefined.
10
+ * @evidence contracts/common.md#clear-and-simple-design One callable interface and namespace separate compatibility from successful data. The private factory installs both operations on one newly created function; descriptor adapters own their existing error translation, and the shared resolver chooses the supported protocol without request-result caching or double validation.
11
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The private constructor composes an owned function and resolver without modifying foreign callbacks or public descriptors. Unknown descriptor tags reject; legacy error-only callbacks remain supported without an invented replacement value or data sentinel.
12
+ * @evidence contracts/common.md#meaningful-documentation This type explains optional resolution and tagged values; private factory/resolver native comments explain construction, single-call behavior and legacy fallback. The owning type reviews those internal operations rather than exposing them to obtain standalone documentation hosts.
13
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callable and tagged result describe validated JavaScript values, not native file identities or process handles.
14
+ */
15
+ export interface IRequestBodyValidation<T> {
16
+ (input: T): Error | null;
17
+
18
+ /**
19
+ * Resolves validation once, retaining its successful value when supported.
20
+ *
21
+ * @evidence contracts/common.md#principled-implementation The optional operation returns a discriminated result so successful Error, null or undefined data cannot be confused with validation failure; absence identifies legacy error-only callbacks.
22
+ * @evidence contracts/common.md#clear-and-simple-design One optional function supplements the existing callable without changing its failure protocol.
23
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The member records a real factory-owned capability rather than requiring it on legacy callbacks or using raw-data sentinels.
24
+ * @evidence contracts/common.md#meaningful-documentation The comment states single validation and successful-value retention; the owning interface explains compatibility.
25
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callable and tagged result describe validated JavaScript values, not native file identities or process handles.
26
+ */
27
+ resolve?: (input: unknown) => IRequestBodyValidation.IResult<T>;
28
+ }
29
+
30
+ export namespace IRequestBodyValidation {
31
+ /**
32
+ * Successful data or a validation error, distinguished independently of data.
33
+ *
34
+ * @evidence contracts/common.md#principled-implementation The success literal distinguishes a valid Error, null or undefined from a rejected value; failure alone carries the error consumers throw.
35
+ * @evidence contracts/common.md#clear-and-simple-design A two-variant union gives consumers one exhaustive success check.
36
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The result uses no data sentinel or consumer-specific exception.
37
+ * @evidence contracts/common.md#meaningful-documentation The description states the distinction that the union preserves.
38
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callable and tagged result describe validated JavaScript values, not native file identities or process handles.
39
+ */
40
+ export type IResult<T> =
41
+ | { success: true; data: T }
42
+ | { success: false; error: Error };
43
+
44
+ /**
45
+ * A factory-owned checker whose resolver is always present.
46
+ *
47
+ * @evidence contracts/common.md#principled-implementation Factory callers can resolve successful data directly because construction installs the resolver together with the legacy callable.
48
+ * @evidence contracts/common.md#clear-and-simple-design This interface strengthens only the optional member of the compatibility interface.
49
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The required member describes the actual factory result rather than asserting a resolver exists on foreign legacy callbacks.
50
+ * @evidence contracts/common.md#meaningful-documentation The comment identifies which owner guarantees resolver availability.
51
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callable and tagged result describe validated JavaScript values, not native file identities or process handles.
52
+ */
53
+ export interface IResolved<T> extends IRequestBodyValidation<T> {
54
+ /**
55
+ * Resolves validation and retains successful data.
56
+ *
57
+ * @evidence contracts/common.md#principled-implementation Factory construction installs this operation with its callable, making successful callback data available without running validation twice.
58
+ * @evidence contracts/common.md#clear-and-simple-design The required function strengthens only the compatibility interface's optional resolver.
59
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The mandatory resolver applies only to the owned factory result, never to an arbitrary legacy callback.
60
+ * @evidence contracts/common.md#meaningful-documentation The description identifies the operation's result and the owning interface states its construction guarantee.
61
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation The callable and tagged result describe validated JavaScript values, not native file identities or process handles.
62
+ */
63
+ resolve: (input: unknown) => IResult<T>;
64
+ }
65
+ }
@@ -1,18 +1,112 @@
1
+ import { IRequestBodyValidation } from "./IRequestBodyValidation";
2
+
3
+ /**
4
+ * The metadata `@WebSocketRoute()` writes on a method: its route paths.
5
+ *
6
+ * @evidence contracts/common.md#principled-implementation The record holds the paths, which the adapter turns into the route table; the parameter records are a separate metadata list.
7
+ * @evidence contracts/common.md#clear-and-simple-design A one-member record and a namespace of the parameter record types.
8
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
9
+ * @evidence contracts/common.md#meaningful-documentation The comment states which metadata the type describes.
10
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
11
+ */
1
12
  export interface IWebSocketRouteReflect {
2
13
  paths: string[];
3
14
  }
4
15
  export namespace IWebSocketRouteReflect {
16
+ /**
17
+ * A parameter record: one of acceptor, driver, header, param, or query.
18
+ *
19
+ * @evidence contracts/common.md#principled-implementation The union is discriminated by the `category` literal, so the adapter can build the argument list with one comparison per parameter.
20
+ * @evidence contracts/common.md#clear-and-simple-design A union of five records.
21
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
22
+ * @evidence contracts/common.md#meaningful-documentation The comment lists the categories.
23
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
24
+ */
5
25
  export type IArgument = IAcceptor | IDriver | IHeader | IParam | IQuery;
26
+ /**
27
+ * The record of an acceptor parameter, which receives the WebSocket acceptor.
28
+ *
29
+ * @evidence contracts/common.md#principled-implementation The record has only the category and the position, because the adapter supplies the acceptor itself.
30
+ * @evidence contracts/common.md#clear-and-simple-design It extends the shared base and adds nothing.
31
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
32
+ * @evidence contracts/common.md#meaningful-documentation The comment states which argument it stands for.
33
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
34
+ */
6
35
  export interface IAcceptor extends IBase<"acceptor"> {}
36
+ /**
37
+ * The record of a driver parameter, which receives the remote driver of the
38
+ * connection.
39
+ *
40
+ * @evidence contracts/common.md#principled-implementation The record has only the category and the position, because the adapter supplies the driver from the acceptor.
41
+ * @evidence contracts/common.md#clear-and-simple-design It extends the shared base and adds nothing.
42
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
43
+ * @evidence contracts/common.md#meaningful-documentation The comment states which argument it stands for.
44
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
45
+ */
7
46
  export interface IDriver extends IBase<"driver"> {}
47
+ /**
48
+ * The record of a header parameter: the position and the validator of the
49
+ * handshake header.
50
+ *
51
+ * @evidence contracts/common.md#principled-implementation The callable retains its error-or-null signature. An optional resolver carries successful callback data to the route argument; legacy callbacks without it preserve the raw header and any validation error rejects the handshake.
52
+ * @evidence contracts/common.md#clear-and-simple-design It extends the shared base with one function member.
53
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
54
+ * @evidence contracts/common.md#meaningful-documentation The comment states which argument it stands for and what the validator returns.
55
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
56
+ */
8
57
  export interface IHeader extends IBase<"header"> {
9
- validate: (input?: any) => Error | null;
58
+ /**
59
+ * Validates the handshake header and returns an error, or `null` when it is
60
+ * valid. Factory-owned callbacks also resolve successful data for the
61
+ * decorated argument without replacing the acceptor's raw header.
62
+ */
63
+ validate: ((input?: any) => Error | null) &
64
+ Pick<IRequestBodyValidation<any>, "resolve">;
10
65
  }
66
+ /**
67
+ * The record of a path parameter: its position, the field name, and the
68
+ * conversion function.
69
+ *
70
+ * @evidence contracts/common.md#principled-implementation The field names the placeholder of the path, and the function converts the matched text to the declared type.
71
+ * @evidence contracts/common.md#clear-and-simple-design It extends the shared base with two members.
72
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
73
+ * @evidence contracts/common.md#meaningful-documentation The comment states which argument it stands for.
74
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
75
+ */
11
76
  export interface IParam extends IBase<"param"> {
12
77
  field: string;
78
+ /**
79
+ * Converts the matched path text to the declared type, or throws.
80
+ *
81
+ * @evidence contracts/common.md#principled-implementation The function is the assertion the transform generated for the declared type, and a throw rejects the handshake.
82
+ * @evidence contracts/common.md#clear-and-simple-design One function member.
83
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
84
+ * @evidence contracts/common.md#meaningful-documentation The comment states the conversion and the failure.
85
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
86
+ */
13
87
  assert: (value: string) => any;
14
88
  }
89
+ /**
90
+ * The record of a query parameter: the position and the validator of the
91
+ * query string.
92
+ *
93
+ * @evidence contracts/common.md#principled-implementation The validator receives the `URLSearchParams` after the first question mark of the path and returns the typed object or an error.
94
+ * @evidence contracts/common.md#clear-and-simple-design It extends the shared base with one function member.
95
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
96
+ * @evidence contracts/common.md#meaningful-documentation The comment states which argument it stands for.
97
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
98
+ */
15
99
  export interface IQuery extends IBase<"query"> {
100
+ /**
101
+ * Validates the query of the handshake and returns the typed object, or an
102
+ * error.
103
+ *
104
+ * @evidence contracts/common.md#principled-implementation The function is the query decoder generated for the declared type, and an `Error` result rejects the handshake.
105
+ * @evidence contracts/common.md#clear-and-simple-design One function member.
106
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
107
+ * @evidence contracts/common.md#meaningful-documentation The comment states the result.
108
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Handshake paths, parameter positions and callback signatures describe protocol metadata without native filesystem identities.
109
+ */
16
110
  validate: (input: URLSearchParams) => any | Error;
17
111
  }
18
112
 
@@ -0,0 +1,19 @@
1
+ import { IEncryptionPassword } from "@nestia/fetcher";
2
+
3
+ import {
4
+ ENCRYPTION_CONTROLLER_METADATA_KEY,
5
+ ENCRYPTION_METADATA_KEY,
6
+ } from "./EncryptedConstant";
7
+
8
+ /**
9
+ * The encryption password of a controller class. The password its own
10
+ * `@EncryptedController()` declares, which a subclass inherits, precedes the
11
+ * one an `EncryptedModule` gave it: the more specific declaration wins.
12
+ *
13
+ * @internal
14
+ */
15
+ export const get_encryption_password = (
16
+ target: Function,
17
+ ): IEncryptionPassword | IEncryptionPassword.Closure | undefined =>
18
+ Reflect.getMetadata(ENCRYPTION_CONTROLLER_METADATA_KEY, target) ??
19
+ Reflect.getMetadata(ENCRYPTION_METADATA_KEY, target);
@@ -3,26 +3,26 @@ import typia, { IValidation, TypeGuardError } from "typia";
3
3
 
4
4
  import { IResponseBodyQuerifier } from "../../options/IResponseBodyQuerifier";
5
5
  import { NoTransformConfigurationError } from "../NoTransformConfigurationError";
6
+ import type { TypedRoute } from "../TypedRoute";
7
+ import { split_route_arguments } from "./split_route_arguments";
6
8
 
7
9
  /** @internal */
8
10
  export const get_path_and_querify =
11
+ (logger: () => (log: TypedRoute.IValidateErrorLog) => void) =>
9
12
  (method: string) =>
10
13
  (
11
14
  ...args: any[]
12
- ): [string | string[] | undefined, (input: any) => URLSearchParams] => {
13
- const path: string | string[] | null | undefined =
14
- args[0] === undefined ||
15
- typeof args[0] === "string" ||
16
- Array.isArray(args[0])
17
- ? args[0]
18
- : null;
19
- const functor: IResponseBodyQuerifier<any> | undefined =
20
- path === null ? args[0] : args[1];
21
- return [path ?? undefined, take(method)(functor)];
15
+ ): [
16
+ string | string[] | undefined,
17
+ (input: any, method: string, path: string) => URLSearchParams,
18
+ ] => {
19
+ const [path, functor] = split_route_arguments<any>(args);
20
+ return [path, take(logger)(method)(functor)];
22
21
  };
23
22
 
24
23
  /** @internal */
25
24
  const take =
25
+ (logger: () => (log: TypedRoute.IValidateErrorLog) => void) =>
26
26
  (method: string) =>
27
27
  <T>(functor?: IResponseBodyQuerifier<T> | null) => {
28
28
  if (functor === undefined) {
@@ -33,6 +33,8 @@ const take =
33
33
  else if (functor.type === "assert") return assert(functor.assert);
34
34
  else if (functor.type === "is") return is(functor.is);
35
35
  else if (functor.type === "validate") return validate(functor.validate);
36
+ else if (functor.type === "validate.log")
37
+ return validateLog(logger)(functor.validate);
36
38
  throw new Error(
37
39
  `Error on nestia.core.${method}(): invalid typed stringify function.`,
38
40
  );
@@ -91,4 +93,23 @@ const validate =
91
93
  };
92
94
 
93
95
  /** @internal */
96
+ /**
97
+ * `validate.log`: an invalid response is reported to the validate-error logger
98
+ * and sent as it is, as `@TypedRoute`'s JSON responses are.
99
+ */
100
+ const validateLog =
101
+ (logger: () => (log: TypedRoute.IValidateErrorLog) => void) =>
102
+ <T>(closure: (data: T) => IValidation<URLSearchParams>) =>
103
+ (data: T, method: string, path: string): URLSearchParams => {
104
+ const result: IValidation<URLSearchParams> = closure(data);
105
+ if (result.success === true) return result.data;
106
+ logger()({
107
+ errors: result.errors,
108
+ method,
109
+ path,
110
+ data,
111
+ });
112
+ return querify(data as Record<string, any>);
113
+ };
114
+
94
115
  const MESSAGE = "Response body data is not following the promised type.";
@@ -4,6 +4,7 @@ import typia, { IValidation, TypeGuardError } from "typia";
4
4
  import { IResponseBodyStringifier } from "../../options/IResponseBodyStringifier";
5
5
  import { NoTransformConfigurationError } from "../NoTransformConfigurationError";
6
6
  import { TypedRoute } from "../TypedRoute";
7
+ import { split_route_arguments } from "./split_route_arguments";
7
8
 
8
9
  /** @internal */
9
10
  export const get_path_and_stringify =
@@ -15,15 +16,8 @@ export const get_path_and_stringify =
15
16
  string | string[] | undefined,
16
17
  (input: any, _method: string, _path: string) => string,
17
18
  ] => {
18
- const path: string | string[] | null | undefined =
19
- args[0] === undefined ||
20
- typeof args[0] === "string" ||
21
- Array.isArray(args[0])
22
- ? args[0]
23
- : null;
24
- const functor: IResponseBodyStringifier<any> | undefined =
25
- path === null ? args[0] : args[1];
26
- return [path ?? undefined, take(logger)(method)(functor)];
19
+ const [path, functor] = split_route_arguments<any>(args);
20
+ return [path, take(logger)(method)(functor)];
27
21
  };
28
22
 
29
23
  /** @internal */
@@ -5,10 +5,14 @@ import raw from "raw-body";
5
5
  /** @internal */
6
6
  export const get_text_body = async (
7
7
  request: express.Request | FastifyRequest,
8
- ): Promise<string> =>
9
- isExpressRequest(request)
10
- ? (await raw(request)).toString("utf8")
11
- : (request.body as string);
8
+ ): Promise<string> => {
9
+ if (isExpressRequest(request) === false) return request.body as string;
10
+ // a body parser the application registered, such as
11
+ // `app.useBodyParser("text")`, has already consumed the stream
12
+ if (typeof request.body === "string") return request.body;
13
+ if (Buffer.isBuffer(request.body)) return request.body.toString("utf8");
14
+ return (await raw(request)).toString("utf8");
15
+ };
12
16
 
13
17
  /** @internal */
14
18
  const isExpressRequest = (
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The entries of a `URLSearchParams` or `FormData` as a plain object: a key
3
+ * given once maps to its value, a repeated key to all of its values in order.
4
+ *
5
+ * @internal
6
+ */
7
+ export const group_entries = <Value>(source: {
8
+ keys(): Iterable<string>;
9
+ getAll(key: string): Value[];
10
+ }): Record<string, Value | Value[]> => {
11
+ const output: Record<string, Value | Value[]> = {};
12
+ for (const key of new Set(source.keys())) {
13
+ const values: Value[] = source.getAll(key);
14
+ output[key] = values.length === 1 ? values[0]! : values;
15
+ }
16
+ return output;
17
+ };
@@ -3,6 +3,20 @@ import { pathToFileURL } from "url";
3
3
  import { Creator } from "../../typings/Creator";
4
4
  import { SourceFinder } from "../../utils/SourceFinder";
5
5
 
6
+ /**
7
+ * Finds and imports the controllers under the given paths.
8
+ *
9
+ * A path, a list, or include and exclude lists are matched with globs, imported
10
+ * by file URL, and every export that carries Nest's `path` metadata is a
11
+ * controller. When no compiled `.js` controller is found and the process runs
12
+ * from source under `ttsx`, the TypeScript files are used instead.
13
+ *
14
+ * @evidence contracts/common.md#principled-implementation Files are selected by a filter chosen from the runtime (JavaScript modules, or TypeScript sources without declaration files), each is imported by `file:` URL through a native dynamic import that survives CommonJS downleveling, and a controller is recognized by Nest's own `path` metadata, so no naming convention is assumed.
15
+ * @evidence contracts/common.md#clear-and-simple-design One function with the fallback for the source-run case inside it and the filters and the mount loop as private helpers.
16
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Discovery follows the metadata Nest itself writes; the `ttsx` fallback is decided by the runtime's own environment variable, not by a project name.
17
+ * @evidence contracts/common.md#meaningful-documentation The comment states the path forms, the recognition rule, and the source-run fallback.
18
+ * @evidence contracts/portability.md#os-neutral-implementation SourceFinder returns native absolute file paths and pathToFileURL converts them to escaped file URLs before dynamic import, including drive letters, spaces and URL punctuation. Extension matching selects source formats rather than defining filesystem identity.
19
+ */
6
20
  export const load_controllers = async (
7
21
  path: string | string[] | { include: string[]; exclude?: string[] },
8
22
  isTsNode?: boolean,
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Splits the arguments of a route decorator into its path and its functor.
3
+ *
4
+ * A route decorator is called as `Route()`, `Route(path)`, `Route(functor)`, or
5
+ * `Route(path, functor)`. The first argument is the path when it is absent, a
6
+ * string, or an array of strings, and the functor otherwise.
7
+ *
8
+ * @internal
9
+ */
10
+ export const split_route_arguments = <Functor>(
11
+ args: any[],
12
+ ): [string | string[] | undefined, Functor | null | undefined] => {
13
+ const path: string | string[] | undefined | null =
14
+ args[0] === undefined ||
15
+ typeof args[0] === "string" ||
16
+ Array.isArray(args[0])
17
+ ? args[0]
18
+ : null;
19
+ return [path ?? undefined, path === null ? args[0] : args[1]];
20
+ };
@@ -3,62 +3,115 @@ import typia, { IValidation, TypeGuardError } from "typia";
3
3
 
4
4
  import { IRequestBodyValidator } from "../../options/IRequestBodyValidator";
5
5
  import { NoTransformConfigurationError } from "../NoTransformConfigurationError";
6
+ import { IRequestBodyValidation } from "./IRequestBodyValidation";
6
7
 
7
- /** @internal */
8
+ /**
9
+ * Builds the shared body checker while preserving successful callback values.
10
+ *
11
+ * The legacy callable returns an error or null; its resolver returns the same
12
+ * validation decision and the callback's successful data without validating
13
+ * twice. Private assert/is/validate adapters preserve each descriptor's error
14
+ * details. Resolver input is untrusted. The descriptor's existing input:T
15
+ * signature is retained for compatibility; adapters cast at invocation and rely
16
+ * on its validation result before exposing successful T data.
17
+ *
18
+ * @internal
19
+ */
8
20
  export const validate_request_body =
9
21
  (method: string) =>
10
- <T>(validator?: IRequestBodyValidator<T>): ((input: T) => Error | null) => {
22
+ <T>(
23
+ validator?: IRequestBodyValidator<T>,
24
+ ): IRequestBodyValidation.IResolved<T> => {
11
25
  if (!validator) {
12
26
  NoTransformConfigurationError(method);
13
- return () => null;
27
+ return create<T>((input: unknown) => ({
28
+ success: true,
29
+ data: input as T,
30
+ }));
14
31
  } else if (validator.type === "assert") return assert(validator.assert);
15
32
  else if (validator.type === "is") return is(validator.is);
16
33
  else if (validator.type === "validate") return validate(validator.validate);
17
- return () =>
18
- new Error(`Error on nestia.core.${method}(): invalid typed validator.`);
34
+ return create<T>(() => ({
35
+ success: false,
36
+ error: new Error(
37
+ `Error on nestia.core.${method}(): invalid typed validator.`,
38
+ ),
39
+ }));
19
40
  };
20
41
 
42
+ /**
43
+ * Resolves factory checkers or the existing error-only metadata protocol once.
44
+ *
45
+ * @internal
46
+ */
47
+ export const resolve_request_body = <T>(
48
+ checker: IRequestBodyValidation<T>,
49
+ input: unknown,
50
+ ): IRequestBodyValidation.IResult<T> => {
51
+ if (checker.resolve) return checker.resolve(input);
52
+ const error = checker(input as T);
53
+ return error === null
54
+ ? { success: true, data: input as T }
55
+ : { success: false, error };
56
+ };
57
+
58
+ /** Installs both protocols on one newly created, session-independent checker. */
59
+ const create = <T>(
60
+ resolve: (input: unknown) => IRequestBodyValidation.IResult<T>,
61
+ ): IRequestBodyValidation.IResolved<T> =>
62
+ Object.assign(
63
+ (input: T): Error | null => {
64
+ const result = resolve(input);
65
+ return result.success ? null : result.error;
66
+ },
67
+ { resolve },
68
+ );
69
+
21
70
  /** @internal */
22
- const assert =
23
- <T>(closure: (data: T) => T) =>
24
- (input: T) => {
71
+ const assert = <T>(closure: (data: T) => T) =>
72
+ create<T>((input: unknown): IRequestBodyValidation.IResult<T> => {
25
73
  try {
26
- closure(input);
27
- return null;
74
+ return { success: true, data: closure(input as T) };
28
75
  } catch (exp) {
29
76
  if (typia.is<TypeGuardError>(exp)) {
30
- return new BadRequestException({
31
- path: exp.path,
32
- reason: exp.message,
33
- expected: exp.expected,
34
- value: exp.value,
35
- message: MESSAGE,
36
- });
77
+ return {
78
+ success: false,
79
+ error: new BadRequestException({
80
+ path: exp.path,
81
+ reason: exp.message,
82
+ expected: exp.expected,
83
+ value: exp.value,
84
+ message: MESSAGE,
85
+ }),
86
+ };
37
87
  }
38
88
  throw exp;
39
89
  }
40
- };
90
+ });
41
91
 
42
92
  /** @internal */
43
- const is =
44
- <T>(closure: (data: T) => boolean) =>
45
- (input: T) => {
46
- const success: boolean = closure(input);
47
- return success ? null : new BadRequestException(MESSAGE);
48
- };
93
+ const is = <T>(closure: (data: T) => boolean) =>
94
+ create<T>((input: unknown): IRequestBodyValidation.IResult<T> => {
95
+ const success: boolean = closure(input as T);
96
+ return success
97
+ ? { success: true, data: input as T }
98
+ : { success: false, error: new BadRequestException(MESSAGE) };
99
+ });
49
100
 
50
101
  /** @internal */
51
- const validate =
52
- <T>(closure: (data: T) => IValidation<T>) =>
53
- (input: T) => {
54
- const result: IValidation<T> = closure(input);
102
+ const validate = <T>(closure: (data: T) => IValidation<T>) =>
103
+ create<T>((input: unknown): IRequestBodyValidation.IResult<T> => {
104
+ const result: IValidation<T> = closure(input as T);
55
105
  return result.success
56
- ? null
57
- : new BadRequestException({
58
- errors: result.errors,
59
- message: MESSAGE,
60
- });
61
- };
106
+ ? { success: true, data: result.data }
107
+ : {
108
+ success: false,
109
+ error: new BadRequestException({
110
+ errors: result.errors,
111
+ message: MESSAGE,
112
+ }),
113
+ };
114
+ });
62
115
 
63
116
  /** @internal */
64
117
  const MESSAGE = "Request body data is not following the promised type.";
@@ -3,6 +3,7 @@ import typia, { IValidation, TypeGuardError } from "typia";
3
3
 
4
4
  import { IRequestFormDataProps } from "../../options/IRequestFormDataProps";
5
5
  import { NoTransformConfigurationError } from "../NoTransformConfigurationError";
6
+ import { group_entries } from "./group_entries";
6
7
 
7
8
  /** @internal */
8
9
  export const validate_request_form_data = <T>(
@@ -10,7 +11,8 @@ export const validate_request_form_data = <T>(
10
11
  ): ((value: FormData) => T | Error) => {
11
12
  if (!props) {
12
13
  NoTransformConfigurationError("TypedFormData.Body");
13
- return (input: FormData) => Object.entries(input) as T;
14
+ // no transform: the fields and files as they arrived
15
+ return (input: FormData) => group_entries(input) as T;
14
16
  } else if (props.validator.type === "assert")
15
17
  return assert(props.validator.assert);
16
18
  else if (props.validator.type === "is") return is(props.validator.is);
@@ -10,8 +10,8 @@ export const validate_request_headers = <T>(
10
10
  ): ((input: Record<string, string | string[] | undefined>) => T | Error) => {
11
11
  if (!validator) {
12
12
  NoTransformConfigurationError("TypedHeaders");
13
- return (input: Record<string, string | string[] | undefined>) =>
14
- Object.entries(input) as T;
13
+ // no transform: the headers as they arrived
14
+ return (input: Record<string, string | string[] | undefined>) => input as T;
15
15
  } else if (validator.type === "assert") return assert(validator.assert);
16
16
  else if (validator.type === "is") return is(validator.is);
17
17
  else if (validator.type === "validate") return validate(validator.validate);
@@ -3,6 +3,7 @@ import typia, { IValidation, TypeGuardError } from "typia";
3
3
 
4
4
  import { IRequestQueryValidator } from "../../options/IRequestQueryValidator";
5
5
  import { NoTransformConfigurationError } from "../NoTransformConfigurationError";
6
+ import { group_entries } from "./group_entries";
6
7
 
7
8
  /** @internal */
8
9
  export const validate_request_query =
@@ -10,8 +11,8 @@ export const validate_request_query =
10
11
  <T>(validator?: IRequestQueryValidator<T>) => {
11
12
  if (!validator) {
12
13
  NoTransformConfigurationError(method);
13
- return (input: URLSearchParams) =>
14
- Object.fromEntries(input.entries()) as T;
14
+ // no transform: the query as it arrived, repeated keys as arrays
15
+ return (input: URLSearchParams) => group_entries(input) as T;
15
16
  } else if (validator.type === "assert") return assert(validator.assert);
16
17
  else if (validator.type === "is") return is(validator.is);
17
18
  else if (validator.type === "validate") return validate(validator.validate);