@nestia/sdk 12.0.0-dev.20260601.1 → 12.0.0-dev.20260612.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 (209) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +93 -93
  3. package/assets/bundle/api/HttpError.ts +1 -1
  4. package/assets/bundle/api/IConnection.ts +1 -1
  5. package/assets/bundle/api/Primitive.ts +1 -1
  6. package/assets/bundle/api/Resolved.ts +1 -1
  7. package/assets/bundle/api/index.ts +4 -4
  8. package/assets/bundle/api/module.ts +6 -6
  9. package/assets/bundle/distribute/README.md +37 -37
  10. package/assets/bundle/distribute/package.json +28 -28
  11. package/assets/bundle/distribute/tsconfig.json +109 -109
  12. package/assets/bundle/e2e/index.ts +42 -42
  13. package/assets/config/nestia.config.ts +97 -97
  14. package/lib/NestiaSdkApplication.js +29 -7
  15. package/lib/NestiaSdkApplication.js.map +1 -1
  16. package/lib/NestiaSwaggerComposer.js +21 -13
  17. package/lib/NestiaSwaggerComposer.js.map +1 -1
  18. package/lib/analyses/AccessorAnalyzer.d.ts +4 -1
  19. package/lib/analyses/AccessorAnalyzer.js.map +1 -1
  20. package/lib/analyses/ConfigAnalyzer.js +1 -1
  21. package/lib/analyses/PathAnalyzer.d.ts +18 -3
  22. package/lib/analyses/PathAnalyzer.js +32 -0
  23. package/lib/analyses/PathAnalyzer.js.map +1 -1
  24. package/lib/analyses/ReflectControllerAnalyzer.js +3 -2
  25. package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
  26. package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +1 -1
  27. package/lib/analyses/ReflectHttpOperationAnalyzer.js +1 -1
  28. package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
  29. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +1 -1
  30. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +53 -20
  31. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
  32. package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +14 -0
  33. package/lib/analyses/ReflectMcpOperationAnalyzer.js +79 -0
  34. package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -0
  35. package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +9 -0
  36. package/lib/analyses/TypedMcpRouteAnalyzer.js +31 -0
  37. package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -0
  38. package/lib/executable/internal/NestiaConfigLoader.js +5 -1
  39. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  40. package/lib/executable/internal/NestiaSdkCommand.js +30 -14
  41. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  42. package/lib/executable/internal/NestiaSdkWatcher.d.ts +10 -0
  43. package/lib/executable/internal/NestiaSdkWatcher.js +322 -0
  44. package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -0
  45. package/lib/executable/sdk.js +12 -12
  46. package/lib/executable/sdk.js.map +1 -1
  47. package/lib/generates/CloneGenerator.js +4 -2
  48. package/lib/generates/CloneGenerator.js.map +1 -1
  49. package/lib/generates/SdkGenerator.js +50 -1
  50. package/lib/generates/SdkGenerator.js.map +1 -1
  51. package/lib/generates/SwaggerGenerator.js +18 -2
  52. package/lib/generates/SwaggerGenerator.js.map +1 -1
  53. package/lib/generates/internal/E2eFileProgrammer.js +3 -1
  54. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  55. package/lib/generates/internal/ImportDictionary.d.ts +1 -0
  56. package/lib/generates/internal/ImportDictionary.js +9 -4
  57. package/lib/generates/internal/ImportDictionary.js.map +1 -1
  58. package/lib/generates/internal/SdkAliasCollection.d.ts +2 -0
  59. package/lib/generates/internal/SdkAliasCollection.js +11 -2
  60. package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
  61. package/lib/generates/internal/SdkDistributionComposer.d.ts +1 -0
  62. package/lib/generates/internal/SdkDistributionComposer.js +3 -0
  63. package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
  64. package/lib/generates/internal/SdkFileProgrammer.js +4 -1
  65. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  66. package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +1 -1
  67. package/lib/generates/internal/SdkHttpCloneReferencer.js +42 -9
  68. package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
  69. package/lib/generates/internal/SdkHttpFunctionProgrammer.js +3 -4
  70. package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
  71. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +2 -1
  72. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
  73. package/lib/generates/internal/SdkHttpSimulationProgrammer.js +6 -3
  74. package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
  75. package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +15 -0
  76. package/lib/generates/internal/SdkMcpRouteProgrammer.js +148 -0
  77. package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -0
  78. package/lib/generates/internal/SdkRouteDirectory.d.ts +2 -1
  79. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
  80. package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +6 -0
  81. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +283 -0
  82. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -0
  83. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +11 -9
  84. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
  85. package/lib/generates/internal/SwaggerOperationParameterComposer.js +10 -2
  86. package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
  87. package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +1 -1
  88. package/lib/generates/internal/SwaggerOperationResponseComposer.js +6 -1
  89. package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
  90. package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +9 -0
  91. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +174 -0
  92. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +1 -0
  93. package/lib/structures/INestiaSdkInput.d.ts +9 -2
  94. package/lib/structures/IReflectController.d.ts +2 -1
  95. package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -2
  96. package/lib/structures/IReflectMcpOperation.d.ts +35 -0
  97. package/lib/structures/IReflectMcpOperation.js +3 -0
  98. package/lib/structures/IReflectMcpOperation.js.map +1 -0
  99. package/lib/structures/IReflectMcpOperationParameter.d.ts +19 -0
  100. package/lib/structures/IReflectMcpOperationParameter.js +3 -0
  101. package/lib/structures/IReflectMcpOperationParameter.js.map +1 -0
  102. package/lib/structures/ITypedApplication.d.ts +2 -1
  103. package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
  104. package/lib/structures/ITypedMcpRoute.d.ts +31 -0
  105. package/lib/structures/ITypedMcpRoute.js +3 -0
  106. package/lib/structures/ITypedMcpRoute.js.map +1 -0
  107. package/lib/utils/HttpResponseContentTypeUtil.d.ts +5 -0
  108. package/lib/utils/HttpResponseContentTypeUtil.js +22 -0
  109. package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -0
  110. package/native/go.mod +52 -52
  111. package/native/go.sum +84 -54
  112. package/native/sdk/register.go +322 -165
  113. package/native/sdk/sdk.go +17 -17
  114. package/native/sdk/sdk_metadata_json.go +327 -327
  115. package/native/sdk/sdk_transform.go +1879 -1549
  116. package/package.json +11 -9
  117. package/src/INestiaConfig.ts +267 -267
  118. package/src/NestiaSdkApplication.ts +39 -8
  119. package/src/NestiaSwaggerComposer.ts +153 -142
  120. package/src/analyses/AccessorAnalyzer.ts +64 -67
  121. package/src/analyses/ConfigAnalyzer.ts +330 -330
  122. package/src/analyses/ImportAnalyzer.ts +92 -92
  123. package/src/analyses/PathAnalyzer.ts +130 -69
  124. package/src/analyses/ReflectControllerAnalyzer.ts +112 -105
  125. package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
  126. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +90 -90
  127. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +350 -350
  128. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +163 -130
  129. package/src/analyses/ReflectMcpOperationAnalyzer.ts +124 -0
  130. package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
  131. package/src/analyses/SecurityAnalyzer.ts +25 -25
  132. package/src/analyses/TypedMcpRouteAnalyzer.ts +34 -0
  133. package/src/decorators/OperationMetadata.ts +29 -29
  134. package/src/executable/internal/CommandParser.ts +15 -15
  135. package/src/executable/internal/NestiaConfigLoader.ts +451 -446
  136. package/src/executable/internal/NestiaSdkCommand.ts +124 -106
  137. package/src/executable/internal/NestiaSdkWatcher.ts +342 -0
  138. package/src/executable/sdk.ts +90 -88
  139. package/src/generates/CloneGenerator.ts +73 -66
  140. package/src/generates/E2eGenerator.ts +32 -32
  141. package/src/generates/SdkGenerator.ts +176 -118
  142. package/src/generates/SwaggerGenerator.ts +342 -310
  143. package/src/generates/internal/E2eFileProgrammer.ts +240 -233
  144. package/src/generates/internal/FilePrinter.ts +65 -65
  145. package/src/generates/internal/ImportDictionary.ts +209 -204
  146. package/src/generates/internal/SdkAliasCollection.ts +274 -261
  147. package/src/generates/internal/SdkDistributionComposer.ts +123 -116
  148. package/src/generates/internal/SdkFileProgrammer.ts +116 -112
  149. package/src/generates/internal/SdkHttpCloneProgrammer.ts +126 -126
  150. package/src/generates/internal/SdkHttpCloneReferencer.ts +131 -77
  151. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +301 -301
  152. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +520 -510
  153. package/src/generates/internal/SdkHttpParameterProgrammer.ts +165 -165
  154. package/src/generates/internal/SdkHttpRouteProgrammer.ts +109 -109
  155. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +331 -314
  156. package/src/generates/internal/SdkImportWizard.ts +62 -62
  157. package/src/generates/internal/SdkMcpRouteProgrammer.ts +452 -0
  158. package/src/generates/internal/SdkRouteDirectory.ts +21 -18
  159. package/src/generates/internal/SdkTypeTagProgrammer.ts +114 -114
  160. package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +319 -0
  161. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +389 -389
  162. package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +89 -89
  163. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +331 -323
  164. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  165. package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
  166. package/src/generates/internal/SwaggerOperationParameterComposer.ts +175 -162
  167. package/src/generates/internal/SwaggerOperationResponseComposer.ts +115 -110
  168. package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +262 -0
  169. package/src/index.ts +4 -4
  170. package/src/internal/legacy.ts +492 -492
  171. package/src/module.ts +4 -4
  172. package/src/structures/INestiaProject.ts +10 -10
  173. package/src/structures/INestiaSdkInput.ts +27 -20
  174. package/src/structures/IOperationMetadata.ts +41 -41
  175. package/src/structures/IReflectController.ts +18 -15
  176. package/src/structures/IReflectHttpOperation.ts +26 -26
  177. package/src/structures/IReflectHttpOperationException.ts +18 -18
  178. package/src/structures/IReflectHttpOperationParameter.ts +79 -79
  179. package/src/structures/IReflectHttpOperationSuccess.ts +18 -21
  180. package/src/structures/IReflectImport.ts +6 -6
  181. package/src/structures/IReflectMcpOperation.ts +38 -0
  182. package/src/structures/IReflectMcpOperationParameter.ts +27 -0
  183. package/src/structures/IReflectOperationError.ts +26 -26
  184. package/src/structures/IReflectType.ts +4 -4
  185. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  186. package/src/structures/ITypedApplication.ts +12 -11
  187. package/src/structures/ITypedHttpRoute.ts +41 -41
  188. package/src/structures/ITypedHttpRouteException.ts +15 -15
  189. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  190. package/src/structures/ITypedHttpRouteSuccess.ts +18 -22
  191. package/src/structures/ITypedMcpRoute.ts +33 -0
  192. package/src/structures/ITypedWebSocketRoute.ts +24 -24
  193. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  194. package/src/transform.ts +59 -59
  195. package/src/typings/get-function-location.d.ts +7 -7
  196. package/src/utils/ArrayUtil.ts +26 -26
  197. package/src/utils/EmittedJavaScriptPatcher.ts +88 -88
  198. package/src/utils/FileRetriever.ts +22 -22
  199. package/src/utils/HttpResponseContentTypeUtil.ts +30 -0
  200. package/src/utils/MapUtil.ts +14 -14
  201. package/src/utils/PathUtil.ts +10 -10
  202. package/src/utils/SourceFinder.ts +63 -63
  203. package/src/utils/StringUtil.ts +17 -17
  204. package/src/utils/TsConfigReader.ts +108 -108
  205. package/src/utils/TtscExecutor.ts +68 -68
  206. package/src/utils/VersioningStrategy.ts +28 -28
  207. package/src/validators/HttpHeadersValidator.ts +11 -11
  208. package/src/validators/HttpQueryValidator.ts +11 -11
  209. package/src/validators/TextPlainValidator.ts +17 -17
@@ -1,350 +1,350 @@
1
- import { SwaggerExample } from "@nestia/core";
2
- import { ROUTE_ARGS_METADATA } from "@nestjs/common/constants";
3
- import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum";
4
- import {
5
- HttpFormDataProgrammer,
6
- HttpHeadersProgrammer,
7
- HttpParameterProgrammer,
8
- HttpQueryProgrammer,
9
- JsonMetadataFactory,
10
- } from "../internal/legacy";
11
-
12
- import { IReflectController } from "../structures/IReflectController";
13
- import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter";
14
- import { IReflectOperationError } from "../structures/IReflectOperationError";
15
- import { IOperationMetadata } from "../structures/IOperationMetadata";
16
- import { TextPlainValidator } from "../validators/TextPlainValidator";
17
- import { HttpHeadersValidator } from "../validators/HttpHeadersValidator";
18
- import { HttpQueryValidator } from "../validators/HttpQueryValidator";
19
-
20
- export namespace ReflectHttpOperationParameterAnalyzer {
21
- export interface IContext {
22
- controller: IReflectController;
23
- function: Function;
24
- functionName: string;
25
- httpMethod: string;
26
- metadata: IOperationMetadata;
27
- errors: IReflectOperationError[];
28
- }
29
- export const analyze = (ctx: IContext): IReflectHttpOperationParameter[] => {
30
- const preconfigured: IReflectHttpOperationParameter.IPreconfigured[] =
31
- analyzePreconfigured(ctx);
32
- const errors: IReflectOperationError[] = [];
33
-
34
- //----
35
- // FIND CONTRADICTIONS
36
- //----
37
- // GET AND HEAD METHOD
38
- const contradictErrors: string[] = [];
39
- const contradict = (message: string) => {
40
- contradictErrors.push(message);
41
- };
42
- if (
43
- (ctx.httpMethod === "GET" || ctx.httpMethod === "HEAD") &&
44
- preconfigured.some((x) => x.category === "body")
45
- )
46
- contradict(`@Body() is not allowed in the ${ctx.httpMethod} method.`);
47
-
48
- // FIND DUPLICATED BODY
49
- if (
50
- preconfigured.filter(
51
- (x) => x.category === "body" && x.field === undefined,
52
- ).length > 1
53
- )
54
- contradict(`Duplicated @Body() is not allowed.`);
55
- if (
56
- preconfigured.filter(
57
- (x) => x.category === "query" && x.field === undefined,
58
- ).length > 1
59
- )
60
- contradict(`Duplicated @Query() without field name is not allowed.`);
61
- if (
62
- preconfigured.filter(
63
- (x) => x.category === "headers" && x.field === undefined,
64
- ).length > 1
65
- )
66
- contradict(`Duplicated @Headers() without field name is not allowed.`);
67
-
68
- // FIND DUPLICATED FIELDS
69
- if (
70
- isUnique(
71
- preconfigured
72
- .filter((x) => x.category === "param")
73
- .map((x) => x.field)
74
- .filter((field) => field !== undefined),
75
- ) === false
76
- )
77
- contradict(`Duplicated field names of path are not allowed.`);
78
- if (
79
- isUnique(
80
- preconfigured
81
- .filter((x) => x.category === "query")
82
- .map((x) => x.field)
83
- .filter((field) => field !== undefined),
84
- ) === false
85
- )
86
- contradict(`Duplicated field names of query are not allowed.`);
87
- if (
88
- isUnique(
89
- preconfigured
90
- .filter((x) => x.category === "headers")
91
- .map((x) => x.field)
92
- .filter((field) => field !== undefined),
93
- ) === false
94
- )
95
- contradict(`Duplicated field names of headers are not allowed.`);
96
- if (contradictErrors.length)
97
- errors.push({
98
- file: ctx.controller.file,
99
- class: ctx.controller.class.name,
100
- function: ctx.functionName,
101
- from: "",
102
- contents: contradictErrors,
103
- });
104
-
105
- //----
106
- // COMPOSE PARAMETERS
107
- //----
108
- const parameters: IReflectHttpOperationParameter[] = preconfigured
109
- .map((p): IReflectHttpOperationParameter | null => {
110
- // METADATA INFO
111
- const pErrorContents: Array<string | IOperationMetadata.IError> = [];
112
- const matched: IOperationMetadata.IParameter | undefined =
113
- ctx.metadata.parameters.find((x) => x.index === p.index);
114
- const report = () => {
115
- errors.push({
116
- file: ctx.controller.file,
117
- class: ctx.controller.class.name,
118
- function: ctx.functionName,
119
- from: `parameter ${matched ? JSON.stringify(matched.name) : `of ${p.index} th`}`,
120
- contents: pErrorContents,
121
- });
122
- return null;
123
- };
124
-
125
- // VALIDATE TYPE
126
- if (matched === undefined)
127
- pErrorContents.push(`Unable to find parameter type.`);
128
- else if (matched.type === null)
129
- pErrorContents.push(`Failed to get the type info.`);
130
-
131
- // CONSIDER KIND
132
- const schema: IOperationMetadata.ISchema | null = (() => {
133
- if (matched === undefined) return null;
134
- const result =
135
- p.category === "body" &&
136
- (p.contentType === "application/json" || p.encrypted === true)
137
- ? matched.primitive
138
- : matched.resolved;
139
- return result.success ? result.data : null;
140
- })();
141
- if (p.category === "body" && p.field !== undefined)
142
- pErrorContents.push(`@Body() must not have a field name.`);
143
- else if (p.category === "param" && p.field === undefined)
144
- pErrorContents.push(`@Param() must have a field name.`);
145
-
146
- if (pErrorContents.length) return report();
147
- else if (
148
- matched === undefined ||
149
- matched.type === null ||
150
- schema === null
151
- )
152
- return null; // unreachable
153
-
154
- const example: SwaggerExample.IData<any> | undefined = (
155
- Reflect.getMetadata(
156
- "nestia/SwaggerExample/Parameters",
157
- ctx.controller.class.prototype,
158
- ctx.functionName,
159
- ) ?? []
160
- ).find((x: SwaggerExample.IData<any>) => x.index === matched.index);
161
-
162
- // COMPOSITION
163
- if (p.category === "param")
164
- return {
165
- category: p.category,
166
- index: p.index,
167
- field: p.field!,
168
- name: matched.name,
169
- type: matched.type,
170
- validate: HttpParameterProgrammer.validate,
171
- description: matched.description,
172
- jsDocTags: matched.jsDocTags,
173
- example: example?.example,
174
- examples: example?.examples,
175
- ...schema,
176
- };
177
- else if (p.category === "query")
178
- return {
179
- category: p.category,
180
- index: p.index,
181
- field: p.field ?? null,
182
- name: matched.name,
183
- type: matched.type,
184
- validate: p.field
185
- ? HttpQueryValidator.validate
186
- : HttpQueryProgrammer.validate,
187
- description: matched.description,
188
- jsDocTags: matched.jsDocTags,
189
- example: example?.example,
190
- examples: example?.examples,
191
- ...schema,
192
- };
193
- else if (p.category === "headers")
194
- return {
195
- category: p.category,
196
- index: p.index,
197
- field: p.field ?? null,
198
- name: matched.name,
199
- type: matched.type,
200
- validate: p.field
201
- ? HttpHeadersValidator.validate
202
- : HttpHeadersProgrammer.validate,
203
- description: matched.description,
204
- jsDocTags: matched.jsDocTags,
205
- example: example?.example,
206
- examples: example?.examples,
207
- ...schema,
208
- };
209
- else if (p.category === "body")
210
- return {
211
- category: p.category,
212
- index: p.index,
213
- encrypted: !!p.encrypted,
214
- contentType: p.contentType,
215
- name: matched.name,
216
- type: matched.type,
217
- validate:
218
- p.contentType === "application/json" || p.encrypted === true
219
- ? JsonMetadataFactory.validate
220
- : p.contentType === "application/x-www-form-urlencoded"
221
- ? HttpQueryProgrammer.validate
222
- : p.contentType === "multipart/form-data"
223
- ? HttpFormDataProgrammer.validate
224
- : TextPlainValidator.validate,
225
- description: matched.description,
226
- jsDocTags: matched.jsDocTags,
227
- example: example?.example,
228
- examples: example?.examples,
229
- ...schema,
230
- };
231
- else {
232
- pErrorContents.push(`Unknown kind of the parameter.`);
233
- return report();
234
- }
235
- })
236
- .filter((x): x is IReflectHttpOperationParameter => x !== null);
237
-
238
- if (errors.length) ctx.errors.push(...errors);
239
- return parameters;
240
- };
241
-
242
- const analyzePreconfigured = (
243
- props: IContext,
244
- ): IReflectHttpOperationParameter.IPreconfigured[] => {
245
- const dict: NestParameters | undefined = Reflect.getMetadata(
246
- ROUTE_ARGS_METADATA,
247
- props.controller.class,
248
- props.functionName,
249
- );
250
- if (dict === undefined) return [];
251
- return Object.entries(dict)
252
- .map(([key, param]) => analyzeHttpParameter(key, param))
253
- .filter(
254
- (x): x is IReflectHttpOperationParameter.IPreconfigured => x !== null,
255
- )
256
- .sort((x, y) => x.index - y.index);
257
- };
258
-
259
- const analyzeHttpParameter = (
260
- key: string,
261
- param: INestParam,
262
- ): IReflectHttpOperationParameter.IPreconfigured | null => {
263
- const symbol: string = key.split(":")[0]!;
264
- if (symbol.indexOf("__custom") !== -1) return analyzeCustomParameter(param);
265
-
266
- const category:
267
- | IReflectHttpOperationParameter.IPreconfigured["category"]
268
- | null = getNestParamType(Number(symbol[0]) as RouteParamtypes);
269
- if (category === null) return null;
270
- if (category === "body")
271
- return {
272
- category: "body",
273
- index: param.index,
274
- field: param.data,
275
- contentType: "application/json",
276
- };
277
- else
278
- return {
279
- category,
280
- index: param.index,
281
- field: param.data,
282
- };
283
- };
284
-
285
- const analyzeCustomParameter = (
286
- param: INestParam,
287
- ): IReflectHttpOperationParameter.IPreconfigured | null => {
288
- if (param.factory === undefined) return null;
289
- else if (
290
- param.factory.name === "EncryptedBody" ||
291
- param.factory.name === "PlainBody" ||
292
- param.factory.name === "TypedQueryBody" ||
293
- param.factory.name === "TypedBody" ||
294
- param.factory.name === "TypedFormDataBody"
295
- )
296
- return {
297
- category: "body",
298
- index: param.index,
299
- encrypted: param.factory.name === "EncryptedBody",
300
- contentType:
301
- param.factory.name === "PlainBody" ||
302
- param.factory.name === "EncryptedBody"
303
- ? "text/plain"
304
- : param.factory.name === "TypedQueryBody"
305
- ? "application/x-www-form-urlencoded"
306
- : param.factory.name === "TypedFormDataBody"
307
- ? "multipart/form-data"
308
- : "application/json",
309
- };
310
- else if (param.factory.name === "TypedHeaders")
311
- return {
312
- category: "headers",
313
- index: param.index,
314
- field: param.data,
315
- };
316
- else if (param.factory.name === "TypedParam")
317
- return {
318
- category: "param",
319
- index: param.index,
320
- field: param.data,
321
- };
322
- else if (param.factory.name === "TypedQuery")
323
- return {
324
- category: "query",
325
- index: param.index,
326
- field: undefined,
327
- };
328
- else return null;
329
- };
330
-
331
- const isUnique = (values: string[]) => new Set(values).size === values.length;
332
- }
333
-
334
- type NestParameters = {
335
- [key: string]: INestParam;
336
- };
337
- interface INestParam {
338
- name: string;
339
- index: number;
340
- factory?: (...args: any) => any;
341
- data: string | undefined;
342
- }
343
-
344
- const getNestParamType = (value: RouteParamtypes) => {
345
- if (value === RouteParamtypes.BODY) return "body";
346
- else if (value === RouteParamtypes.HEADERS) return "headers";
347
- else if (value === RouteParamtypes.QUERY) return "query";
348
- else if (value === RouteParamtypes.PARAM) return "param";
349
- return null;
350
- };
1
+ import { SwaggerExample } from "@nestia/core";
2
+ import { ROUTE_ARGS_METADATA } from "@nestjs/common/constants";
3
+ import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum";
4
+ import {
5
+ HttpFormDataProgrammer,
6
+ HttpHeadersProgrammer,
7
+ HttpParameterProgrammer,
8
+ HttpQueryProgrammer,
9
+ JsonMetadataFactory,
10
+ } from "../internal/legacy";
11
+
12
+ import { IReflectController } from "../structures/IReflectController";
13
+ import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter";
14
+ import { IReflectOperationError } from "../structures/IReflectOperationError";
15
+ import { IOperationMetadata } from "../structures/IOperationMetadata";
16
+ import { TextPlainValidator } from "../validators/TextPlainValidator";
17
+ import { HttpHeadersValidator } from "../validators/HttpHeadersValidator";
18
+ import { HttpQueryValidator } from "../validators/HttpQueryValidator";
19
+
20
+ export namespace ReflectHttpOperationParameterAnalyzer {
21
+ export interface IContext {
22
+ controller: IReflectController;
23
+ function: Function;
24
+ functionName: string;
25
+ httpMethod: string;
26
+ metadata: IOperationMetadata;
27
+ errors: IReflectOperationError[];
28
+ }
29
+ export const analyze = (ctx: IContext): IReflectHttpOperationParameter[] => {
30
+ const preconfigured: IReflectHttpOperationParameter.IPreconfigured[] =
31
+ analyzePreconfigured(ctx);
32
+ const errors: IReflectOperationError[] = [];
33
+
34
+ //----
35
+ // FIND CONTRADICTIONS
36
+ //----
37
+ // GET AND HEAD METHOD
38
+ const contradictErrors: string[] = [];
39
+ const contradict = (message: string) => {
40
+ contradictErrors.push(message);
41
+ };
42
+ if (
43
+ (ctx.httpMethod === "GET" || ctx.httpMethod === "HEAD") &&
44
+ preconfigured.some((x) => x.category === "body")
45
+ )
46
+ contradict(`@Body() is not allowed in the ${ctx.httpMethod} method.`);
47
+
48
+ // FIND DUPLICATED BODY
49
+ if (
50
+ preconfigured.filter(
51
+ (x) => x.category === "body" && x.field === undefined,
52
+ ).length > 1
53
+ )
54
+ contradict(`Duplicated @Body() is not allowed.`);
55
+ if (
56
+ preconfigured.filter(
57
+ (x) => x.category === "query" && x.field === undefined,
58
+ ).length > 1
59
+ )
60
+ contradict(`Duplicated @Query() without field name is not allowed.`);
61
+ if (
62
+ preconfigured.filter(
63
+ (x) => x.category === "headers" && x.field === undefined,
64
+ ).length > 1
65
+ )
66
+ contradict(`Duplicated @Headers() without field name is not allowed.`);
67
+
68
+ // FIND DUPLICATED FIELDS
69
+ if (
70
+ isUnique(
71
+ preconfigured
72
+ .filter((x) => x.category === "param")
73
+ .map((x) => x.field)
74
+ .filter((field) => field !== undefined),
75
+ ) === false
76
+ )
77
+ contradict(`Duplicated field names of path are not allowed.`);
78
+ if (
79
+ isUnique(
80
+ preconfigured
81
+ .filter((x) => x.category === "query")
82
+ .map((x) => x.field)
83
+ .filter((field) => field !== undefined),
84
+ ) === false
85
+ )
86
+ contradict(`Duplicated field names of query are not allowed.`);
87
+ if (
88
+ isUnique(
89
+ preconfigured
90
+ .filter((x) => x.category === "headers")
91
+ .map((x) => x.field)
92
+ .filter((field) => field !== undefined),
93
+ ) === false
94
+ )
95
+ contradict(`Duplicated field names of headers are not allowed.`);
96
+ if (contradictErrors.length)
97
+ errors.push({
98
+ file: ctx.controller.file,
99
+ class: ctx.controller.class.name,
100
+ function: ctx.functionName,
101
+ from: "",
102
+ contents: contradictErrors,
103
+ });
104
+
105
+ //----
106
+ // COMPOSE PARAMETERS
107
+ //----
108
+ const parameters: IReflectHttpOperationParameter[] = preconfigured
109
+ .map((p): IReflectHttpOperationParameter | null => {
110
+ // METADATA INFO
111
+ const pErrorContents: Array<string | IOperationMetadata.IError> = [];
112
+ const matched: IOperationMetadata.IParameter | undefined =
113
+ ctx.metadata.parameters.find((x) => x.index === p.index);
114
+ const report = () => {
115
+ errors.push({
116
+ file: ctx.controller.file,
117
+ class: ctx.controller.class.name,
118
+ function: ctx.functionName,
119
+ from: `parameter ${matched ? JSON.stringify(matched.name) : `of ${p.index} th`}`,
120
+ contents: pErrorContents,
121
+ });
122
+ return null;
123
+ };
124
+
125
+ // VALIDATE TYPE
126
+ if (matched === undefined)
127
+ pErrorContents.push(`Unable to find parameter type.`);
128
+ else if (matched.type === null)
129
+ pErrorContents.push(`Failed to get the type info.`);
130
+
131
+ // CONSIDER KIND
132
+ const schema: IOperationMetadata.ISchema | null = (() => {
133
+ if (matched === undefined) return null;
134
+ const result =
135
+ p.category === "body" &&
136
+ (p.contentType === "application/json" || p.encrypted === true)
137
+ ? matched.primitive
138
+ : matched.resolved;
139
+ return result.success ? result.data : null;
140
+ })();
141
+ if (p.category === "body" && p.field !== undefined)
142
+ pErrorContents.push(`@Body() must not have a field name.`);
143
+ else if (p.category === "param" && p.field === undefined)
144
+ pErrorContents.push(`@Param() must have a field name.`);
145
+
146
+ if (pErrorContents.length) return report();
147
+ else if (
148
+ matched === undefined ||
149
+ matched.type === null ||
150
+ schema === null
151
+ )
152
+ return null; // unreachable
153
+
154
+ const example: SwaggerExample.IData<any> | undefined = (
155
+ Reflect.getMetadata(
156
+ "nestia/SwaggerExample/Parameters",
157
+ ctx.controller.class.prototype,
158
+ ctx.functionName,
159
+ ) ?? []
160
+ ).find((x: SwaggerExample.IData<any>) => x.index === matched.index);
161
+
162
+ // COMPOSITION
163
+ if (p.category === "param")
164
+ return {
165
+ category: p.category,
166
+ index: p.index,
167
+ field: p.field!,
168
+ name: matched.name,
169
+ type: matched.type,
170
+ validate: HttpParameterProgrammer.validate,
171
+ description: matched.description,
172
+ jsDocTags: matched.jsDocTags,
173
+ example: example?.example,
174
+ examples: example?.examples,
175
+ ...schema,
176
+ };
177
+ else if (p.category === "query")
178
+ return {
179
+ category: p.category,
180
+ index: p.index,
181
+ field: p.field ?? null,
182
+ name: matched.name,
183
+ type: matched.type,
184
+ validate: p.field
185
+ ? HttpQueryValidator.validate
186
+ : HttpQueryProgrammer.validate,
187
+ description: matched.description,
188
+ jsDocTags: matched.jsDocTags,
189
+ example: example?.example,
190
+ examples: example?.examples,
191
+ ...schema,
192
+ };
193
+ else if (p.category === "headers")
194
+ return {
195
+ category: p.category,
196
+ index: p.index,
197
+ field: p.field ?? null,
198
+ name: matched.name,
199
+ type: matched.type,
200
+ validate: p.field
201
+ ? HttpHeadersValidator.validate
202
+ : HttpHeadersProgrammer.validate,
203
+ description: matched.description,
204
+ jsDocTags: matched.jsDocTags,
205
+ example: example?.example,
206
+ examples: example?.examples,
207
+ ...schema,
208
+ };
209
+ else if (p.category === "body")
210
+ return {
211
+ category: p.category,
212
+ index: p.index,
213
+ encrypted: !!p.encrypted,
214
+ contentType: p.contentType,
215
+ name: matched.name,
216
+ type: matched.type,
217
+ validate:
218
+ p.contentType === "application/json" || p.encrypted === true
219
+ ? JsonMetadataFactory.validate
220
+ : p.contentType === "application/x-www-form-urlencoded"
221
+ ? HttpQueryProgrammer.validate
222
+ : p.contentType === "multipart/form-data"
223
+ ? HttpFormDataProgrammer.validate
224
+ : TextPlainValidator.validate,
225
+ description: matched.description,
226
+ jsDocTags: matched.jsDocTags,
227
+ example: example?.example,
228
+ examples: example?.examples,
229
+ ...schema,
230
+ };
231
+ else {
232
+ pErrorContents.push(`Unknown kind of the parameter.`);
233
+ return report();
234
+ }
235
+ })
236
+ .filter((x): x is IReflectHttpOperationParameter => x !== null);
237
+
238
+ if (errors.length) ctx.errors.push(...errors);
239
+ return parameters;
240
+ };
241
+
242
+ const analyzePreconfigured = (
243
+ props: IContext,
244
+ ): IReflectHttpOperationParameter.IPreconfigured[] => {
245
+ const dict: NestParameters | undefined = Reflect.getMetadata(
246
+ ROUTE_ARGS_METADATA,
247
+ props.controller.class,
248
+ props.functionName,
249
+ );
250
+ if (dict === undefined) return [];
251
+ return Object.entries(dict)
252
+ .map(([key, param]) => analyzeHttpParameter(key, param))
253
+ .filter(
254
+ (x): x is IReflectHttpOperationParameter.IPreconfigured => x !== null,
255
+ )
256
+ .sort((x, y) => x.index - y.index);
257
+ };
258
+
259
+ const analyzeHttpParameter = (
260
+ key: string,
261
+ param: INestParam,
262
+ ): IReflectHttpOperationParameter.IPreconfigured | null => {
263
+ const symbol: string = key.split(":")[0]!;
264
+ if (symbol.indexOf("__custom") !== -1) return analyzeCustomParameter(param);
265
+
266
+ const category:
267
+ | IReflectHttpOperationParameter.IPreconfigured["category"]
268
+ | null = getNestParamType(Number(symbol[0]) as RouteParamtypes);
269
+ if (category === null) return null;
270
+ if (category === "body")
271
+ return {
272
+ category: "body",
273
+ index: param.index,
274
+ field: param.data,
275
+ contentType: "application/json",
276
+ };
277
+ else
278
+ return {
279
+ category,
280
+ index: param.index,
281
+ field: param.data,
282
+ };
283
+ };
284
+
285
+ const analyzeCustomParameter = (
286
+ param: INestParam,
287
+ ): IReflectHttpOperationParameter.IPreconfigured | null => {
288
+ if (param.factory === undefined) return null;
289
+ else if (
290
+ param.factory.name === "EncryptedBody" ||
291
+ param.factory.name === "PlainBody" ||
292
+ param.factory.name === "TypedQueryBody" ||
293
+ param.factory.name === "TypedBody" ||
294
+ param.factory.name === "TypedFormDataBody"
295
+ )
296
+ return {
297
+ category: "body",
298
+ index: param.index,
299
+ encrypted: param.factory.name === "EncryptedBody",
300
+ contentType:
301
+ param.factory.name === "PlainBody" ||
302
+ param.factory.name === "EncryptedBody"
303
+ ? "text/plain"
304
+ : param.factory.name === "TypedQueryBody"
305
+ ? "application/x-www-form-urlencoded"
306
+ : param.factory.name === "TypedFormDataBody"
307
+ ? "multipart/form-data"
308
+ : "application/json",
309
+ };
310
+ else if (param.factory.name === "TypedHeaders")
311
+ return {
312
+ category: "headers",
313
+ index: param.index,
314
+ field: param.data,
315
+ };
316
+ else if (param.factory.name === "TypedParam")
317
+ return {
318
+ category: "param",
319
+ index: param.index,
320
+ field: param.data,
321
+ };
322
+ else if (param.factory.name === "TypedQuery")
323
+ return {
324
+ category: "query",
325
+ index: param.index,
326
+ field: undefined,
327
+ };
328
+ else return null;
329
+ };
330
+
331
+ const isUnique = (values: string[]) => new Set(values).size === values.length;
332
+ }
333
+
334
+ type NestParameters = {
335
+ [key: string]: INestParam;
336
+ };
337
+ interface INestParam {
338
+ name: string;
339
+ index: number;
340
+ factory?: (...args: any) => any;
341
+ data: string | undefined;
342
+ }
343
+
344
+ const getNestParamType = (value: RouteParamtypes) => {
345
+ if (value === RouteParamtypes.BODY) return "body";
346
+ else if (value === RouteParamtypes.HEADERS) return "headers";
347
+ else if (value === RouteParamtypes.QUERY) return "query";
348
+ else if (value === RouteParamtypes.PARAM) return "param";
349
+ return null;
350
+ };