@nestia/sdk 2.4.2 → 2.4.3

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 (111) hide show
  1. package/lib/NestiaSdkApplication.js +2 -6
  2. package/lib/NestiaSdkApplication.js.map +1 -1
  3. package/lib/analyses/AccessorAnalyzer.js.map +1 -1
  4. package/lib/analyses/ConfigAnalyzer.js +4 -8
  5. package/lib/analyses/ConfigAnalyzer.js.map +1 -1
  6. package/lib/analyses/ControllerAnalyzer.js +6 -8
  7. package/lib/analyses/ControllerAnalyzer.js.map +1 -1
  8. package/lib/analyses/ExceptionAnalyzer.js.map +1 -1
  9. package/lib/analyses/GenericAnalyzer.js +1 -2
  10. package/lib/analyses/GenericAnalyzer.js.map +1 -1
  11. package/lib/analyses/ImportAnalyzer.js +4 -4
  12. package/lib/analyses/ImportAnalyzer.js.map +1 -1
  13. package/lib/analyses/PathAnalyzer.js.map +1 -1
  14. package/lib/analyses/ReflectAnalyzer.js +7 -8
  15. package/lib/analyses/ReflectAnalyzer.js.map +1 -1
  16. package/lib/analyses/SecurityAnalyzer.js.map +1 -1
  17. package/lib/executable/internal/CommandParser.js.map +1 -1
  18. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  19. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  20. package/lib/executable/sdk.js +11 -11
  21. package/lib/executable/sdk.js.map +1 -1
  22. package/lib/generates/E2eGenerator.js.map +1 -1
  23. package/lib/generates/SdkGenerator.js.map +1 -1
  24. package/lib/generates/SwaggerGenerator.js +5 -11
  25. package/lib/generates/SwaggerGenerator.js.map +1 -1
  26. package/lib/generates/internal/E2eFileProgrammer.js +2 -8
  27. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  28. package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
  29. package/lib/generates/internal/SdkDtoGenerator.js +3 -9
  30. package/lib/generates/internal/SdkDtoGenerator.js.map +1 -1
  31. package/lib/generates/internal/SdkFileProgrammer.js +4 -4
  32. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  33. package/lib/generates/internal/SdkFunctionProgrammer.js +12 -20
  34. package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
  35. package/lib/generates/internal/SdkImportWizard.js.map +1 -1
  36. package/lib/generates/internal/SdkRouteDirectory.js +1 -3
  37. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
  38. package/lib/generates/internal/SdkSimulationProgrammer.js +5 -7
  39. package/lib/generates/internal/SdkSimulationProgrammer.js.map +1 -1
  40. package/lib/generates/internal/SdkTypeDefiner.js +2 -5
  41. package/lib/generates/internal/SdkTypeDefiner.js.map +1 -1
  42. package/lib/generates/internal/SwaggerSchemaGenerator.js +29 -44
  43. package/lib/generates/internal/SwaggerSchemaGenerator.js.map +1 -1
  44. package/lib/generates/internal/SwaggerSchemaValidator.js +3 -9
  45. package/lib/generates/internal/SwaggerSchemaValidator.js.map +1 -1
  46. package/lib/structures/MethodType.js +1 -7
  47. package/lib/structures/MethodType.js.map +1 -1
  48. package/lib/structures/TypeEntry.js.map +1 -1
  49. package/lib/utils/ArrayUtil.js.map +1 -1
  50. package/lib/utils/FileRetriever.js.map +1 -1
  51. package/lib/utils/ImportDictionary.js +1 -4
  52. package/lib/utils/ImportDictionary.js.map +1 -1
  53. package/lib/utils/MapUtil.js.map +1 -1
  54. package/lib/utils/PathUtil.js.map +1 -1
  55. package/lib/utils/SourceFinder.js.map +1 -1
  56. package/package.json +4 -7
  57. package/src/INestiaConfig.ts +234 -234
  58. package/src/NestiaSdkApplication.ts +253 -268
  59. package/src/analyses/AccessorAnalyzer.ts +60 -60
  60. package/src/analyses/ConfigAnalyzer.ts +147 -164
  61. package/src/analyses/ControllerAnalyzer.ts +379 -399
  62. package/src/analyses/ExceptionAnalyzer.ts +115 -124
  63. package/src/analyses/GenericAnalyzer.ts +51 -57
  64. package/src/analyses/ImportAnalyzer.ts +138 -159
  65. package/src/analyses/PathAnalyzer.ts +98 -100
  66. package/src/analyses/ReflectAnalyzer.ts +425 -433
  67. package/src/analyses/SecurityAnalyzer.ts +20 -20
  68. package/src/executable/internal/CommandParser.ts +15 -15
  69. package/src/executable/internal/NestiaConfigLoader.ts +67 -68
  70. package/src/executable/internal/NestiaSdkCommand.ts +60 -64
  71. package/src/executable/sdk.ts +73 -73
  72. package/src/generates/E2eGenerator.ts +64 -67
  73. package/src/generates/SdkGenerator.ts +96 -100
  74. package/src/generates/SwaggerGenerator.ts +372 -410
  75. package/src/generates/internal/E2eFileProgrammer.ts +123 -129
  76. package/src/generates/internal/SdkDistributionComposer.ts +91 -91
  77. package/src/generates/internal/SdkDtoGenerator.ts +424 -450
  78. package/src/generates/internal/SdkFileProgrammer.ts +106 -111
  79. package/src/generates/internal/SdkFunctionProgrammer.ts +466 -501
  80. package/src/generates/internal/SdkImportWizard.ts +55 -55
  81. package/src/generates/internal/SdkRouteDirectory.ts +17 -19
  82. package/src/generates/internal/SdkSimulationProgrammer.ts +133 -142
  83. package/src/generates/internal/SdkTypeDefiner.ts +119 -124
  84. package/src/generates/internal/SwaggerSchemaGenerator.ts +382 -401
  85. package/src/generates/internal/SwaggerSchemaValidator.ts +198 -210
  86. package/src/index.ts +4 -4
  87. package/src/module.ts +2 -2
  88. package/src/structures/IController.ts +79 -81
  89. package/src/structures/IErrorReport.ts +6 -6
  90. package/src/structures/INestiaProject.ts +13 -13
  91. package/src/structures/INormalizedInput.ts +20 -20
  92. package/src/structures/IRoute.ts +40 -41
  93. package/src/structures/ISwagger.ts +91 -91
  94. package/src/structures/ISwaggerComponents.ts +29 -29
  95. package/src/structures/ISwaggerError.ts +8 -8
  96. package/src/structures/ISwaggerInfo.ts +80 -80
  97. package/src/structures/ISwaggerLazyProperty.ts +7 -7
  98. package/src/structures/ISwaggerLazySchema.ts +7 -7
  99. package/src/structures/ISwaggerRoute.ts +51 -51
  100. package/src/structures/ISwaggerSecurityScheme.ts +65 -65
  101. package/src/structures/ITypeTuple.ts +6 -6
  102. package/src/structures/MethodType.ts +5 -11
  103. package/src/structures/ParamCategory.ts +1 -1
  104. package/src/structures/TypeEntry.ts +22 -22
  105. package/src/utils/ArrayUtil.ts +26 -26
  106. package/src/utils/FileRetriever.ts +22 -22
  107. package/src/utils/ImportDictionary.ts +125 -128
  108. package/src/utils/MapUtil.ts +14 -14
  109. package/src/utils/PathUtil.ts +10 -10
  110. package/src/utils/SourceFinder.ts +66 -70
  111. package/src/utils/StripEnums.ts +5 -10
@@ -1,5 +1,4 @@
1
1
  import { Pair } from "tstl/utility/Pair";
2
-
3
2
  import { IJsDocTagInfo } from "typia/lib/schemas/metadata/IJsDocTagInfo";
4
3
  import { Escaper } from "typia/lib/utils/Escaper";
5
4
 
@@ -13,541 +12,507 @@ import { SdkSimulationProgrammer } from "./SdkSimulationProgrammer";
13
12
  import { SdkTypeDefiner } from "./SdkTypeDefiner";
14
13
 
15
14
  export namespace SdkFunctionProgrammer {
16
- export const generate =
17
- (config: INestiaConfig) =>
18
- (importer: ImportDictionary) =>
19
- (route: IRoute): string => {
20
- const [x, y, z] = [head, body, tail].map((closure) =>
21
- closure(config)(importer)(route)({
22
- headers: route.parameters.find(
23
- (param) =>
24
- param.category === "headers" &&
25
- param.field === undefined,
26
- ),
27
- query: route.parameters.find(
28
- (param) =>
29
- param.category === "query" &&
30
- param.field === undefined,
31
- ),
32
- input: route.parameters.find(
33
- (param) => param.category === "body",
34
- ),
35
- }),
36
- );
37
- return `${x} ${y}\n${z}`;
38
- };
15
+ export const generate =
16
+ (config: INestiaConfig) =>
17
+ (importer: ImportDictionary) =>
18
+ (route: IRoute): string => {
19
+ const [x, y, z] = [head, body, tail].map((closure) =>
20
+ closure(config)(importer)(route)({
21
+ headers: route.parameters.find(
22
+ (param) =>
23
+ param.category === "headers" && param.field === undefined,
24
+ ),
25
+ query: route.parameters.find(
26
+ (param) => param.category === "query" && param.field === undefined,
27
+ ),
28
+ input: route.parameters.find((param) => param.category === "body"),
29
+ }),
30
+ );
31
+ return `${x} ${y}\n${z}`;
32
+ };
39
33
 
40
- /* ---------------------------------------------------------
34
+ /* ---------------------------------------------------------
41
35
  BODY
42
36
  --------------------------------------------------------- */
43
- const body =
44
- (config: INestiaConfig) =>
45
- (importer: ImportDictionary) =>
46
- (route: IRoute) =>
47
- (props: {
48
- query: IRoute.IParameter | undefined;
49
- input: IRoute.IParameter | undefined;
50
- }): string => {
51
- const encrypted: boolean =
52
- route.encrypted === true ||
53
- (props.input !== undefined &&
54
- props.input.custom === true &&
55
- props.input.category === "body" &&
56
- props.input.encrypted === true);
37
+ const body =
38
+ (config: INestiaConfig) =>
39
+ (importer: ImportDictionary) =>
40
+ (route: IRoute) =>
41
+ (props: {
42
+ query: IRoute.IParameter | undefined;
43
+ input: IRoute.IParameter | undefined;
44
+ }): string => {
45
+ const encrypted: boolean =
46
+ route.encrypted === true ||
47
+ (props.input !== undefined &&
48
+ props.input.custom === true &&
49
+ props.input.category === "body" &&
50
+ props.input.encrypted === true);
57
51
 
58
- // FETCH ARGUMENTS WITH REQUST BODY
59
- const parameters: IRoute.IParameter[] = filter_path_parameters(
60
- route,
61
- )(props.query);
62
- const contentType: string | undefined =
63
- props.input !== undefined
64
- ? (props.input as IController.IBodyParameter).encrypted
65
- ? "text/plain"
66
- : (props.input as IController.IBodyParameter)
67
- .contentType ?? "application/json"
68
- : undefined;
69
- const fetchArguments: Array<string | string[]> = [
70
- contentType
71
- ? [
72
- "{",
73
- " ...connection,",
74
- " headers: {",
75
- " ...(connection.headers ?? {}),",
76
- ` "Content-Type": "${contentType}",`,
77
- " },",
78
- "}",
79
- ]
80
- : "connection",
81
- [
82
- "{",
83
- ` ...${route.name}.METADATA,`,
84
- ` path: ${route.name}.path(${parameters
85
- .map((p) => p.name)
86
- .join(", ")}),`,
87
- "} as const",
88
- ],
89
- ];
90
- if (props.input !== undefined) {
91
- fetchArguments.push(props.input.name);
92
- if (config.json === true)
93
- fetchArguments.push(`${route.name}.stringify`);
94
- }
52
+ // FETCH ARGUMENTS WITH REQUST BODY
53
+ const parameters: IRoute.IParameter[] = filter_path_parameters(route)(
54
+ props.query,
55
+ );
56
+ const contentType: string | undefined =
57
+ props.input !== undefined
58
+ ? (props.input as IController.IBodyParameter).encrypted
59
+ ? "text/plain"
60
+ : (props.input as IController.IBodyParameter).contentType ??
61
+ "application/json"
62
+ : undefined;
63
+ const fetchArguments: Array<string | string[]> = [
64
+ contentType
65
+ ? [
66
+ "{",
67
+ " ...connection,",
68
+ " headers: {",
69
+ " ...(connection.headers ?? {}),",
70
+ ` "Content-Type": "${contentType}",`,
71
+ " },",
72
+ "}",
73
+ ]
74
+ : "connection",
75
+ [
76
+ "{",
77
+ ` ...${route.name}.METADATA,`,
78
+ ` path: ${route.name}.path(${parameters
79
+ .map((p) => p.name)
80
+ .join(", ")}),`,
81
+ "} as const",
82
+ ],
83
+ ];
84
+ if (props.input !== undefined) {
85
+ fetchArguments.push(props.input.name);
86
+ if (config.json === true)
87
+ fetchArguments.push(`${route.name}.stringify`);
88
+ }
95
89
 
96
- const assertions: string =
97
- config.assert === true &&
98
- route.parameters.filter(
99
- (p) => p.category !== "headers" || p.field === undefined,
100
- ).length !== 0
101
- ? route.parameters
102
- .filter(
103
- (p) =>
104
- p.category !== "headers" ||
105
- p.field === undefined,
106
- )
107
- .map(
108
- (param) =>
109
- ` ${SdkImportWizard.typia(
110
- importer,
111
- )}.assert<typeof ${param.name}>(${
112
- param.name
113
- });`,
114
- )
115
- .join("\n") + "\n\n"
116
- : "";
90
+ const assertions: string =
91
+ config.assert === true &&
92
+ route.parameters.filter(
93
+ (p) => p.category !== "headers" || p.field === undefined,
94
+ ).length !== 0
95
+ ? route.parameters
96
+ .filter((p) => p.category !== "headers" || p.field === undefined)
97
+ .map(
98
+ (param) =>
99
+ ` ${SdkImportWizard.typia(importer)}.assert<typeof ${
100
+ param.name
101
+ }>(${param.name});`,
102
+ )
103
+ .join("\n") + "\n\n"
104
+ : "";
117
105
 
118
- // FUNCTION CALL STATEMENT
119
- const caller = (awa: boolean) => {
120
- const random = () =>
121
- [
122
- `${awa ? "await " : ""}${route.name}.simulate(`,
123
- ` connection,`,
124
- ...route.parameters
125
- .filter((p) => p.category !== "headers")
126
- .map((p) => ` ${p.name},`),
127
- `)`,
128
- ]
129
- .map((line, i) =>
130
- i === 0 ? line : `${space(10)}${line}`,
131
- )
132
- .join("\n");
133
- const fetch = (tab: string) =>
134
- [
135
- `${awa ? "await " : ""}${SdkImportWizard.Fetcher(
136
- encrypted,
137
- )(importer)}.${
138
- config.propagate === true ? "propagate" : "fetch"
139
- }(`,
140
- fetchArguments
141
- .map((param) =>
142
- typeof param === "string"
143
- ? `${tab} ${param}`
144
- : param
145
- .map((str) => `${tab} ${str}`)
146
- .join("\n"),
147
- )
148
- .join(",\n") + ",",
149
- `${tab})`,
150
- ].join("\n");
151
- if (!config.simulate) return fetch(space(4));
152
- return (
153
- `!!connection.simulate\n` +
154
- ` ? ${random()}\n` +
155
- ` : ${fetch(space(10))}`
156
- );
157
- };
158
- if (route.setHeaders.length === 0)
159
- return `{\n${assertions} return ${caller(false)};\n}`;
106
+ // FUNCTION CALL STATEMENT
107
+ const caller = (awa: boolean) => {
108
+ const random = () =>
109
+ [
110
+ `${awa ? "await " : ""}${route.name}.simulate(`,
111
+ ` connection,`,
112
+ ...route.parameters
113
+ .filter((p) => p.category !== "headers")
114
+ .map((p) => ` ${p.name},`),
115
+ `)`,
116
+ ]
117
+ .map((line, i) => (i === 0 ? line : `${space(10)}${line}`))
118
+ .join("\n");
119
+ const fetch = (tab: string) =>
120
+ [
121
+ `${awa ? "await " : ""}${SdkImportWizard.Fetcher(encrypted)(
122
+ importer,
123
+ )}.${config.propagate === true ? "propagate" : "fetch"}(`,
124
+ fetchArguments
125
+ .map((param) =>
126
+ typeof param === "string"
127
+ ? `${tab} ${param}`
128
+ : param.map((str) => `${tab} ${str}`).join("\n"),
129
+ )
130
+ .join(",\n") + ",",
131
+ `${tab})`,
132
+ ].join("\n");
133
+ if (!config.simulate) return fetch(space(4));
134
+ return (
135
+ `!!connection.simulate\n` +
136
+ ` ? ${random()}\n` +
137
+ ` : ${fetch(space(10))}`
138
+ );
139
+ };
140
+ if (route.setHeaders.length === 0)
141
+ return `{\n${assertions} return ${caller(false)};\n}`;
160
142
 
161
- // SET HEADERS
162
- const content: string[] = [
163
- `{\n`,
164
- assertions,
165
- ` const output: ${route.name}.Output = ${caller(true)};\n`,
166
- "\n",
167
- ` // configure header(s)\n`,
168
- ` connection.headers ??= {};\n`,
169
- ];
170
- const headerContents = (variable: string) =>
171
- route.setHeaders.map((header) =>
172
- header.type === "assigner"
173
- ? `Object.assign(connection.headers, ${access(variable)(
174
- header.source,
175
- )});`
176
- : `${access("connection.headers")(
177
- header.target ?? header.source,
178
- )} = ${access(variable)(header.source)};`,
179
- );
180
- if (config.propagate === true) {
181
- content.push(` if (output.success) {\n`);
182
- content.push(
183
- ...headerContents("output.data").map(
184
- (line) => ` ${line}\n`,
185
- ),
186
- );
187
- content.push(` }\n`);
188
- } else
189
- content.push(
190
- ...headerContents("output").map((line) => ` ${line}\n`),
191
- );
192
- content.push("\n", " return output;\n", "}");
193
- return content.join("");
194
- };
143
+ // SET HEADERS
144
+ const content: string[] = [
145
+ `{\n`,
146
+ assertions,
147
+ ` const output: ${route.name}.Output = ${caller(true)};\n`,
148
+ "\n",
149
+ ` // configure header(s)\n`,
150
+ ` connection.headers ??= {};\n`,
151
+ ];
152
+ const headerContents = (variable: string) =>
153
+ route.setHeaders.map((header) =>
154
+ header.type === "assigner"
155
+ ? `Object.assign(connection.headers, ${access(variable)(
156
+ header.source,
157
+ )});`
158
+ : `${access("connection.headers")(
159
+ header.target ?? header.source,
160
+ )} = ${access(variable)(header.source)};`,
161
+ );
162
+ if (config.propagate === true) {
163
+ content.push(` if (output.success) {\n`);
164
+ content.push(
165
+ ...headerContents("output.data").map((line) => ` ${line}\n`),
166
+ );
167
+ content.push(` }\n`);
168
+ } else
169
+ content.push(
170
+ ...headerContents("output").map((line) => ` ${line}\n`),
171
+ );
172
+ content.push("\n", " return output;\n", "}");
173
+ return content.join("");
174
+ };
195
175
 
196
- const filter_path_parameters =
197
- (route: IRoute) =>
198
- (query: IRoute.IParameter | undefined): IRoute.IParameter[] => {
199
- const parameters: IRoute.IParameter[] = route.parameters.filter(
200
- (param) =>
201
- param.category === "param" ||
202
- (param.category === "query" && param.field !== undefined),
203
- );
204
- if (query) parameters.push(query);
205
- return parameters;
206
- };
176
+ const filter_path_parameters =
177
+ (route: IRoute) =>
178
+ (query: IRoute.IParameter | undefined): IRoute.IParameter[] => {
179
+ const parameters: IRoute.IParameter[] = route.parameters.filter(
180
+ (param) =>
181
+ param.category === "param" ||
182
+ (param.category === "query" && param.field !== undefined),
183
+ );
184
+ if (query) parameters.push(query);
185
+ return parameters;
186
+ };
207
187
 
208
- const access =
209
- (x: string) =>
210
- (y: string): string =>
211
- y[0] === "[" ? `${x}${y}` : `${x}.${y}`;
188
+ const access =
189
+ (x: string) =>
190
+ (y: string): string =>
191
+ y[0] === "[" ? `${x}${y}` : `${x}.${y}`;
212
192
 
213
- /* ---------------------------------------------------------
193
+ /* ---------------------------------------------------------
214
194
  HEAD & TAIL
215
195
  --------------------------------------------------------- */
216
- const head =
217
- (config: INestiaConfig) =>
218
- (importer: ImportDictionary) =>
219
- (route: IRoute) =>
220
- (props: {
221
- query: IRoute.IParameter | undefined;
222
- input: IRoute.IParameter | undefined;
223
- }): string => {
224
- //----
225
- // CONSTRUCT COMMENT
226
- //----
227
- // MAIN DESCRIPTION
228
- const comments: string[] = route.description
229
- ? route.description.split("\n")
230
- : [];
231
-
232
- // COMMENT TAGS
233
- const tags: IJsDocTagInfo[] = route.jsDocTags.filter(
234
- (tag) =>
235
- tag.name !== "param" ||
236
- route.parameters
237
- .filter((p) => p.category !== "headers")
238
- .some((p) => p.name === tag.text?.[0]?.text),
239
- );
240
- if (tags.length !== 0) {
241
- const content: string[] = tags.map((t) =>
242
- t.text?.length
243
- ? `@${t.name} ${t.text.map((e) => e.text).join("")}`
244
- : `@${t.name}`,
245
- );
246
- comments.push("", ...new Set(content));
247
- }
248
-
249
- // EXCEPTIONS
250
- for (const [key, value] of Object.entries(route.exceptions)) {
251
- if (
252
- comments.some(
253
- (str) =>
254
- str.startsWith(`@throw ${key}`) ||
255
- str.startsWith(`@throws ${key}`),
256
- )
257
- )
258
- continue;
259
- comments.push(
260
- value.description?.length
261
- ? `@throws ${key} ${value.description.split("\n")[0]}`
262
- : `@throws ${key}`,
263
- );
264
- }
196
+ const head =
197
+ (config: INestiaConfig) =>
198
+ (importer: ImportDictionary) =>
199
+ (route: IRoute) =>
200
+ (props: {
201
+ query: IRoute.IParameter | undefined;
202
+ input: IRoute.IParameter | undefined;
203
+ }): string => {
204
+ //----
205
+ // CONSTRUCT COMMENT
206
+ //----
207
+ // MAIN DESCRIPTION
208
+ const comments: string[] = route.description
209
+ ? route.description.split("\n")
210
+ : [];
265
211
 
266
- // POSTFIX
267
- if (!!comments.length) comments.push("");
268
- comments.push(
269
- `@controller ${route.symbol.class}.${route.symbol.function}`,
270
- `@path ${route.method} ${route.path}`,
271
- `@nestia Generated by Nestia - https://github.com/samchon/nestia`,
272
- );
212
+ // COMMENT TAGS
213
+ const tags: IJsDocTagInfo[] = route.jsDocTags.filter(
214
+ (tag) =>
215
+ tag.name !== "param" ||
216
+ route.parameters
217
+ .filter((p) => p.category !== "headers")
218
+ .some((p) => p.name === tag.text?.[0]?.text),
219
+ );
220
+ if (tags.length !== 0) {
221
+ const content: string[] = tags.map((t) =>
222
+ t.text?.length
223
+ ? `@${t.name} ${t.text.map((e) => e.text).join("")}`
224
+ : `@${t.name}`,
225
+ );
226
+ comments.push("", ...new Set(content));
227
+ }
273
228
 
274
- //----
275
- // FINALIZATION
276
- //----
277
- // REFORM PARAMETERS TEXT
278
- const parameters: string[] = [
279
- route.parameters.some(
280
- (p) => p.category === "headers" && p.field === undefined,
281
- )
282
- ? `connection: ${SdkImportWizard.IConnection(
283
- importer,
284
- )}<${`${route.name}.Headers`}>`
285
- : `connection: ${SdkImportWizard.IConnection(importer)}`,
286
- ...route.parameters
287
- .filter((p) => p.category !== "headers")
288
- .map((param) => {
289
- const type: string =
290
- config.primitive !== false &&
291
- (param === props.query || param === props.input)
292
- ? `${route.name}.${
293
- param === props.query ? "Query" : "Input"
294
- }`
295
- : getTypeName(config)(importer)(param);
296
- return `${param.name}${
297
- param.optional ? "?" : ""
298
- }: ${type}`;
299
- }),
300
- ];
229
+ // EXCEPTIONS
230
+ for (const [key, value] of Object.entries(route.exceptions)) {
231
+ if (
232
+ comments.some(
233
+ (str) =>
234
+ str.startsWith(`@throw ${key}`) ||
235
+ str.startsWith(`@throws ${key}`),
236
+ )
237
+ )
238
+ continue;
239
+ comments.push(
240
+ value.description?.length
241
+ ? `@throws ${key} ${value.description.split("\n")[0]}`
242
+ : `@throws ${key}`,
243
+ );
244
+ }
301
245
 
302
- // OUTPUT TYPE
303
- const output: string =
304
- config.propagate !== true && route.output.typeName === "void"
305
- ? "void"
306
- : `${route.name}.Output`;
246
+ // POSTFIX
247
+ if (!!comments.length) comments.push("");
248
+ comments.push(
249
+ `@controller ${route.symbol.class}.${route.symbol.function}`,
250
+ `@path ${route.method} ${route.path}`,
251
+ `@nestia Generated by Nestia - https://github.com/samchon/nestia`,
252
+ );
307
253
 
308
- // RETURNS WITH CONSTRUCTION
309
- return (
310
- "" +
311
- "/**\n" +
312
- comments.map((str) => ` * ${str}`).join("\n") +
313
- "\n" +
314
- " */\n" +
315
- `export async function ${route.name}(\n` +
316
- parameters.map((str) => ` ${str},\n`).join("") +
317
- `): Promise<${output}>`
318
- );
319
- };
254
+ //----
255
+ // FINALIZATION
256
+ //----
257
+ // REFORM PARAMETERS TEXT
258
+ const parameters: string[] = [
259
+ route.parameters.some(
260
+ (p) => p.category === "headers" && p.field === undefined,
261
+ )
262
+ ? `connection: ${SdkImportWizard.IConnection(
263
+ importer,
264
+ )}<${`${route.name}.Headers`}>`
265
+ : `connection: ${SdkImportWizard.IConnection(importer)}`,
266
+ ...route.parameters
267
+ .filter((p) => p.category !== "headers")
268
+ .map((param) => {
269
+ const type: string =
270
+ config.primitive !== false &&
271
+ (param === props.query || param === props.input)
272
+ ? `${route.name}.${param === props.query ? "Query" : "Input"}`
273
+ : getTypeName(config)(importer)(param);
274
+ return `${param.name}${param.optional ? "?" : ""}: ${type}`;
275
+ }),
276
+ ];
320
277
 
321
- const tail =
322
- (config: INestiaConfig) =>
323
- (importer: ImportDictionary) =>
324
- (route: IRoute) =>
325
- (props: {
326
- query: IRoute.IParameter | undefined;
327
- headers: IRoute.IParameter | undefined;
328
- input: IRoute.IParameter | undefined;
329
- }): string => {
330
- // LIST UP TYPES
331
- const types: Pair<string, string>[] = [];
332
- if (props.headers !== undefined)
333
- types.push(
334
- new Pair(
335
- "Headers",
336
- SdkTypeDefiner.headers(config)(importer)(props.headers),
337
- ),
338
- );
339
- if (props.query !== undefined)
340
- types.push(
341
- new Pair(
342
- "Query",
343
- SdkTypeDefiner.query(config)(importer)(props.query),
344
- ),
345
- );
346
- if (props.input !== undefined)
347
- types.push(
348
- new Pair(
349
- "Input",
350
- SdkTypeDefiner.input(config)(importer)(props.input),
351
- ),
352
- );
353
- if (config.propagate === true || route.output.typeName !== "void")
354
- types.push(
355
- new Pair(
356
- "Output",
357
- SdkTypeDefiner.output(config)(importer)(route),
358
- ),
359
- );
278
+ // OUTPUT TYPE
279
+ const output: string =
280
+ config.propagate !== true && route.output.typeName === "void"
281
+ ? "void"
282
+ : `${route.name}.Output`;
360
283
 
361
- // PATH WITH PARAMETERS
362
- const parameters: IRoute.IParameter[] = filter_path_parameters(
363
- route,
364
- )(props.query);
365
- const path: string = compute_path({
366
- path: route.path,
367
- query: props.query,
368
- parameters,
369
- });
370
- return (
371
- `export namespace ${route.name} {\n` +
372
- (types.length !== 0
373
- ? types
374
- .map(
375
- (tuple) =>
376
- ` export type ${tuple.first} = ${tuple.second};`,
377
- )
378
- .join("\n") + "\n"
379
- : "") +
380
- "\n" +
381
- [
382
- "export const METADATA = {",
383
- ` method: "${route.method}",`,
384
- ` path: "${route.path}",`,
385
- ...(props.input
386
- ? [
387
- `request: {`,
388
- ` type: "${
389
- (props.input as IController.IBodyParameter)
390
- .encrypted
391
- ? "text/plain"
392
- : (
393
- props.input as IController.IBodyParameter
394
- ).contentType ?? "application/json"
395
- }",`,
396
- ` encrypted: ${
397
- props.input.custom &&
398
- props.input.category === "body" &&
399
- props.input.encrypted
400
- }`,
401
- `},`,
402
- ].map((str) => ` ${str}`)
403
- : [" request: null,"]),
404
- ...(route.method !== "HEAD"
405
- ? [
406
- `response: {`,
407
- ` type: "${route.output.contentType}",`,
408
- ` encrypted: ${route.encrypted},`,
409
- `},`,
410
- ].map((str) => ` ${str}`)
411
- : [" response: null,"]),
412
- ...(route.status
413
- ? [` status: ${route.status},`]
414
- : [" status: null,"]),
415
- ...(route.output.contentType ===
416
- "application/x-www-form-urlencoded"
417
- ? [
418
- ` parseQuery: (input: URLSearchParams) => ${SdkImportWizard.typia(
419
- importer,
420
- )}.http.assertQuery<${
421
- route.output.typeName
422
- }>(input),`,
423
- ]
424
- : []),
425
- "} as const;",
426
- ]
427
- .map((line) => ` ${line}`)
428
- .join("\n") +
429
- "\n\n" +
430
- ` export const path = (${parameters
431
- .map(
432
- (param) =>
433
- `${param.name}: ${
434
- param.category === "query" &&
435
- param.typeName === props.query?.typeName
436
- ? `${route.name}.Query`
437
- : getTypeName(config)(importer)(param)
438
- }`,
439
- )
440
- .join(", ")}): string => {\n` +
441
- `${path};\n` +
442
- ` }\n` +
443
- (config.simulate === true && route.output.typeName !== "void"
444
- ? ` export const random = (g?: Partial<${SdkImportWizard.typia(
445
- importer,
446
- )}.IRandomGenerator>): ${SdkTypeDefiner.responseBody(
447
- config,
448
- )(importer)(route)} =>\n` +
449
- ` ${SdkImportWizard.typia(
450
- importer,
451
- )}.random<${SdkTypeDefiner.responseBody(config)(importer)(
452
- route,
453
- )}>(g);\n`
454
- : "") +
455
- (config.simulate === true
456
- ? SdkSimulationProgrammer.generate(config)(importer)(
457
- route,
458
- ) + "\n"
459
- : "") +
460
- (config.json === true &&
461
- route.parameters.find((param) => param.category === "body") !==
462
- undefined
463
- ? ` export const stringify = (input: Input) => ${SdkImportWizard.typia(
464
- importer,
465
- )}.json.assertStringify(input);\n`
466
- : "") +
467
- "}"
468
- );
469
- };
284
+ // RETURNS WITH CONSTRUCTION
285
+ return (
286
+ "" +
287
+ "/**\n" +
288
+ comments.map((str) => ` * ${str}`).join("\n") +
289
+ "\n" +
290
+ " */\n" +
291
+ `export async function ${route.name}(\n` +
292
+ parameters.map((str) => ` ${str},\n`).join("") +
293
+ `): Promise<${output}>`
294
+ );
295
+ };
470
296
 
471
- const compute_path = (props: {
472
- query: IRoute.IParameter | undefined;
473
- parameters: IRoute.IParameter[];
474
- path: string;
297
+ const tail =
298
+ (config: INestiaConfig) =>
299
+ (importer: ImportDictionary) =>
300
+ (route: IRoute) =>
301
+ (props: {
302
+ query: IRoute.IParameter | undefined;
303
+ headers: IRoute.IParameter | undefined;
304
+ input: IRoute.IParameter | undefined;
475
305
  }): string => {
476
- for (const param of props.parameters)
477
- if (param.category === "param")
478
- props.path = props.path.replace(
479
- `:${param.field}`,
480
- `\${encodeURIComponent(${param.name} ?? "null")}`,
481
- );
306
+ // LIST UP TYPES
307
+ const types: Pair<string, string>[] = [];
308
+ if (props.headers !== undefined)
309
+ types.push(
310
+ new Pair(
311
+ "Headers",
312
+ SdkTypeDefiner.headers(config)(importer)(props.headers),
313
+ ),
314
+ );
315
+ if (props.query !== undefined)
316
+ types.push(
317
+ new Pair(
318
+ "Query",
319
+ SdkTypeDefiner.query(config)(importer)(props.query),
320
+ ),
321
+ );
322
+ if (props.input !== undefined)
323
+ types.push(
324
+ new Pair(
325
+ "Input",
326
+ SdkTypeDefiner.input(config)(importer)(props.input),
327
+ ),
328
+ );
329
+ if (config.propagate === true || route.output.typeName !== "void")
330
+ types.push(
331
+ new Pair("Output", SdkTypeDefiner.output(config)(importer)(route)),
332
+ );
482
333
 
483
- // NO QUERY PARAMETER
484
- const queryParams: IRoute.IParameter[] = props.parameters.filter(
485
- (param) => param.category === "query" && param.field !== undefined,
334
+ // PATH WITH PARAMETERS
335
+ const parameters: IRoute.IParameter[] = filter_path_parameters(route)(
336
+ props.query,
337
+ );
338
+ const path: string = compute_path({
339
+ path: route.path,
340
+ query: props.query,
341
+ parameters,
342
+ });
343
+ return (
344
+ `export namespace ${route.name} {\n` +
345
+ (types.length !== 0
346
+ ? types
347
+ .map(
348
+ (tuple) => ` export type ${tuple.first} = ${tuple.second};`,
349
+ )
350
+ .join("\n") + "\n"
351
+ : "") +
352
+ "\n" +
353
+ [
354
+ "export const METADATA = {",
355
+ ` method: "${route.method}",`,
356
+ ` path: "${route.path}",`,
357
+ ...(props.input
358
+ ? [
359
+ `request: {`,
360
+ ` type: "${
361
+ (props.input as IController.IBodyParameter).encrypted
362
+ ? "text/plain"
363
+ : (props.input as IController.IBodyParameter).contentType ??
364
+ "application/json"
365
+ }",`,
366
+ ` encrypted: ${
367
+ props.input.custom &&
368
+ props.input.category === "body" &&
369
+ props.input.encrypted
370
+ }`,
371
+ `},`,
372
+ ].map((str) => ` ${str}`)
373
+ : [" request: null,"]),
374
+ ...(route.method !== "HEAD"
375
+ ? [
376
+ `response: {`,
377
+ ` type: "${route.output.contentType}",`,
378
+ ` encrypted: ${route.encrypted},`,
379
+ `},`,
380
+ ].map((str) => ` ${str}`)
381
+ : [" response: null,"]),
382
+ ...(route.status
383
+ ? [` status: ${route.status},`]
384
+ : [" status: null,"]),
385
+ ...(route.output.contentType === "application/x-www-form-urlencoded"
386
+ ? [
387
+ ` parseQuery: (input: URLSearchParams) => ${SdkImportWizard.typia(
388
+ importer,
389
+ )}.http.assertQuery<${route.output.typeName}>(input),`,
390
+ ]
391
+ : []),
392
+ "} as const;",
393
+ ]
394
+ .map((line) => ` ${line}`)
395
+ .join("\n") +
396
+ "\n\n" +
397
+ ` export const path = (${parameters
398
+ .map(
399
+ (param) =>
400
+ `${param.name}: ${
401
+ param.category === "query" &&
402
+ param.typeName === props.query?.typeName
403
+ ? `${route.name}.Query`
404
+ : getTypeName(config)(importer)(param)
405
+ }`,
406
+ )
407
+ .join(", ")}): string => {\n` +
408
+ `${path};\n` +
409
+ ` }\n` +
410
+ (config.simulate === true && route.output.typeName !== "void"
411
+ ? ` export const random = (g?: Partial<${SdkImportWizard.typia(
412
+ importer,
413
+ )}.IRandomGenerator>): ${SdkTypeDefiner.responseBody(config)(
414
+ importer,
415
+ )(route)} =>\n` +
416
+ ` ${SdkImportWizard.typia(
417
+ importer,
418
+ )}.random<${SdkTypeDefiner.responseBody(config)(importer)(
419
+ route,
420
+ )}>(g);\n`
421
+ : "") +
422
+ (config.simulate === true
423
+ ? SdkSimulationProgrammer.generate(config)(importer)(route) + "\n"
424
+ : "") +
425
+ (config.json === true &&
426
+ route.parameters.find((param) => param.category === "body") !==
427
+ undefined
428
+ ? ` export const stringify = (input: Input) => ${SdkImportWizard.typia(
429
+ importer,
430
+ )}.json.assertStringify(input);\n`
431
+ : "") +
432
+ "}"
433
+ );
434
+ };
435
+
436
+ const compute_path = (props: {
437
+ query: IRoute.IParameter | undefined;
438
+ parameters: IRoute.IParameter[];
439
+ path: string;
440
+ }): string => {
441
+ for (const param of props.parameters)
442
+ if (param.category === "param")
443
+ props.path = props.path.replace(
444
+ `:${param.field}`,
445
+ `\${encodeURIComponent(${param.name} ?? "null")}`,
486
446
  );
487
- if (props.query === undefined && queryParams.length === 0)
488
- return `${space(8)}return \`${props.path}\``;
489
447
 
490
- const computeName = (str: string): string =>
491
- props.parameters
492
- .filter((p) => p.category !== "headers")
493
- .find((p) => p.name === str) !== undefined
494
- ? computeName("_" + str)
495
- : str;
496
- const variables: string = computeName("variables");
497
- const search: string = computeName("search");
498
- const encoded: string = computeName("encoded");
448
+ // NO QUERY PARAMETER
449
+ const queryParams: IRoute.IParameter[] = props.parameters.filter(
450
+ (param) => param.category === "query" && param.field !== undefined,
451
+ );
452
+ if (props.query === undefined && queryParams.length === 0)
453
+ return `${space(8)}return \`${props.path}\``;
499
454
 
500
- const wrapper = (expr: string) =>
501
- [
502
- `const ${variables}: Record<any, any> = ${expr};`,
503
- `const ${search}: URLSearchParams = new URLSearchParams();`,
504
- `for (const [key, value] of Object.entries(${variables}))`,
505
- ` if (value === undefined) continue;`,
506
- ` else if (Array.isArray(value))`,
507
- ` value.forEach((elem) => ${search}.append(key, String(elem)));`,
508
- ` else`,
509
- ` ${search}.set(key, String(value));`,
510
- `const ${encoded}: string = ${search}.toString();`,
511
- `return \`${props.path}\${${encoded}.length ? \`?\${${encoded}}\` : ""}\`;`,
512
- ]
513
- .map((str) => `${space(8)}${str}`)
514
- .join("\n");
455
+ const computeName = (str: string): string =>
456
+ props.parameters
457
+ .filter((p) => p.category !== "headers")
458
+ .find((p) => p.name === str) !== undefined
459
+ ? computeName("_" + str)
460
+ : str;
461
+ const variables: string = computeName("variables");
462
+ const search: string = computeName("search");
463
+ const encoded: string = computeName("encoded");
515
464
 
516
- if (props.query !== undefined && queryParams.length === 0)
517
- return wrapper(`${props.query.name} as any`);
518
- else if (props.query === undefined)
519
- return wrapper(`
465
+ const wrapper = (expr: string) =>
466
+ [
467
+ `const ${variables}: Record<any, any> = ${expr};`,
468
+ `const ${search}: URLSearchParams = new URLSearchParams();`,
469
+ `for (const [key, value] of Object.entries(${variables}))`,
470
+ ` if (value === undefined) continue;`,
471
+ ` else if (Array.isArray(value))`,
472
+ ` value.forEach((elem) => ${search}.append(key, String(elem)));`,
473
+ ` else`,
474
+ ` ${search}.set(key, String(value));`,
475
+ `const ${encoded}: string = ${search}.toString();`,
476
+ `return \`${props.path}\${${encoded}.length ? \`?\${${encoded}}\` : ""}\`;`,
477
+ ]
478
+ .map((str) => `${space(8)}${str}`)
479
+ .join("\n");
480
+
481
+ if (props.query !== undefined && queryParams.length === 0)
482
+ return wrapper(`${props.query.name} as any`);
483
+ else if (props.query === undefined)
484
+ return wrapper(`
520
485
  {
521
486
  ${rest_query_parameters(queryParams)}
522
487
  } as any`);
523
488
 
524
- return wrapper(`
489
+ return wrapper(`
525
490
  {
526
491
  ...${props.query.name},
527
492
  ${rest_query_parameters(queryParams)},
528
493
  } as any`);
529
- };
494
+ };
530
495
 
531
- const rest_query_parameters = (parameters: IRoute.IParameter[]): string =>
532
- parameters
533
- .filter((param) => param.category !== "headers")
534
- .map((param) =>
535
- param.name === param.field
536
- ? param.name
537
- : `${
538
- Escaper.variable(param.field!)
539
- ? param.field
540
- : JSON.stringify(param.field)
541
- }: ${param.name}`,
542
- )
543
- .join(`,\n${space(12)}`);
496
+ const rest_query_parameters = (parameters: IRoute.IParameter[]): string =>
497
+ parameters
498
+ .filter((param) => param.category !== "headers")
499
+ .map((param) =>
500
+ param.name === param.field
501
+ ? param.name
502
+ : `${
503
+ Escaper.variable(param.field!)
504
+ ? param.field
505
+ : JSON.stringify(param.field)
506
+ }: ${param.name}`,
507
+ )
508
+ .join(`,\n${space(12)}`);
544
509
  }
545
510
 
546
511
  const space = (count: number) => " ".repeat(count);
547
512
  const getTypeName =
548
- (config: INestiaConfig) =>
549
- (importer: ImportDictionary) =>
550
- (p: IRoute.IParameter | IRoute.IOutput) =>
551
- p.metadata
552
- ? SdkDtoGenerator.decode(config)(importer)(p.metadata)
553
- : p.typeName;
513
+ (config: INestiaConfig) =>
514
+ (importer: ImportDictionary) =>
515
+ (p: IRoute.IParameter | IRoute.IOutput) =>
516
+ p.metadata
517
+ ? SdkDtoGenerator.decode(config)(importer)(p.metadata)
518
+ : p.typeName;