@nestia/sdk 3.0.0-dev.20231209 → 3.0.0-dev.20240412

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 (168) hide show
  1. package/README.md +12 -9
  2. package/assets/config/nestia.config.ts +82 -79
  3. package/lib/INestiaConfig.d.ts +28 -6
  4. package/lib/NestiaSdkApplication.js +12 -10
  5. package/lib/NestiaSdkApplication.js.map +1 -1
  6. package/lib/analyses/ConfigAnalyzer.js +1 -1
  7. package/lib/analyses/ConfigAnalyzer.js.map +1 -1
  8. package/lib/analyses/ControllerAnalyzer.js +30 -15
  9. package/lib/analyses/ControllerAnalyzer.js.map +1 -1
  10. package/lib/analyses/ExceptionAnalyzer.js +35 -6
  11. package/lib/analyses/ExceptionAnalyzer.js.map +1 -1
  12. package/lib/analyses/ImportAnalyzer.d.ts +1 -2
  13. package/lib/analyses/ImportAnalyzer.js +2 -2
  14. package/lib/analyses/ImportAnalyzer.js.map +1 -1
  15. package/lib/analyses/PathAnalyzer.d.ts +2 -4
  16. package/lib/analyses/PathAnalyzer.js +27 -11
  17. package/lib/analyses/PathAnalyzer.js.map +1 -1
  18. package/lib/analyses/ReflectAnalyzer.js +34 -22
  19. package/lib/analyses/ReflectAnalyzer.js.map +1 -1
  20. package/lib/analyses/SecurityAnalyzer.js +13 -8
  21. package/lib/analyses/SecurityAnalyzer.js.map +1 -1
  22. package/lib/executable/internal/NestiaConfigLoader.js +300 -220
  23. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  24. package/lib/executable/sdk.js +11 -11
  25. package/lib/generates/CloneGenerator.d.ts +6 -0
  26. package/lib/generates/CloneGenerator.js +62 -0
  27. package/lib/generates/CloneGenerator.js.map +1 -0
  28. package/lib/generates/E2eGenerator.d.ts +2 -1
  29. package/lib/generates/E2eGenerator.js +2 -2
  30. package/lib/generates/E2eGenerator.js.map +1 -1
  31. package/lib/generates/SdkGenerator.js +3 -11
  32. package/lib/generates/SdkGenerator.js.map +1 -1
  33. package/lib/generates/SwaggerGenerator.d.ts +2 -0
  34. package/lib/generates/SwaggerGenerator.js +119 -62
  35. package/lib/generates/SwaggerGenerator.js.map +1 -1
  36. package/lib/generates/internal/E2eFileProgrammer.d.ts +2 -1
  37. package/lib/generates/internal/E2eFileProgrammer.js +49 -53
  38. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  39. package/lib/generates/internal/FilePrinter.d.ts +10 -0
  40. package/lib/generates/internal/FilePrinter.js +46 -0
  41. package/lib/generates/internal/FilePrinter.js.map +1 -0
  42. package/lib/{utils → generates/internal}/ImportDictionary.d.ts +2 -1
  43. package/lib/{utils → generates/internal}/ImportDictionary.js +20 -14
  44. package/lib/generates/internal/ImportDictionary.js.map +1 -0
  45. package/lib/generates/internal/SdkAliasCollection.d.ts +12 -0
  46. package/lib/generates/internal/SdkAliasCollection.js +97 -0
  47. package/lib/generates/internal/SdkAliasCollection.js.map +1 -0
  48. package/lib/generates/internal/SdkCloneProgrammer.d.ts +12 -0
  49. package/lib/generates/internal/SdkCloneProgrammer.js +99 -0
  50. package/lib/generates/internal/SdkCloneProgrammer.js.map +1 -0
  51. package/lib/generates/internal/SdkFileProgrammer.d.ts +2 -1
  52. package/lib/generates/internal/SdkFileProgrammer.js +27 -28
  53. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  54. package/lib/generates/internal/SdkFunctionProgrammer.d.ts +7 -2
  55. package/lib/generates/internal/SdkFunctionProgrammer.js +115 -322
  56. package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
  57. package/lib/generates/internal/SdkImportWizard.d.ts +1 -1
  58. package/lib/generates/internal/SdkNamespaceProgrammer.d.ts +11 -0
  59. package/lib/generates/internal/SdkNamespaceProgrammer.js +180 -0
  60. package/lib/generates/internal/SdkNamespaceProgrammer.js.map +1 -0
  61. package/lib/generates/internal/SdkRouteProgrammer.d.ts +7 -0
  62. package/lib/generates/internal/SdkRouteProgrammer.js +55 -0
  63. package/lib/generates/internal/SdkRouteProgrammer.js.map +1 -0
  64. package/lib/generates/internal/SdkSimulationProgrammer.d.ts +8 -2
  65. package/lib/generates/internal/SdkSimulationProgrammer.js +103 -89
  66. package/lib/generates/internal/SdkSimulationProgrammer.js.map +1 -1
  67. package/lib/generates/internal/SdkTypeProgrammer.d.ts +9 -0
  68. package/lib/generates/internal/SdkTypeProgrammer.js +228 -0
  69. package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -0
  70. package/lib/generates/internal/SwaggerSchemaGenerator.d.ts +4 -4
  71. package/lib/generates/internal/SwaggerSchemaGenerator.js +30 -28
  72. package/lib/generates/internal/SwaggerSchemaGenerator.js.map +1 -1
  73. package/lib/structures/IController.d.ts +4 -2
  74. package/lib/structures/IRoute.d.ts +5 -4
  75. package/lib/structures/ISwaggerLazyProperty.d.ts +2 -2
  76. package/lib/structures/ISwaggerLazySchema.d.ts +2 -2
  77. package/lib/structures/ParamCategory.d.ts +1 -1
  78. package/lib/structures/TypeEntry.js +2 -2
  79. package/lib/structures/TypeEntry.js.map +1 -1
  80. package/lib/utils/StringUtil.d.ts +3 -0
  81. package/lib/utils/StringUtil.js +8 -0
  82. package/lib/utils/StringUtil.js.map +1 -0
  83. package/package.json +12 -16
  84. package/src/INestiaConfig.ts +30 -6
  85. package/src/NestiaSdkApplication.ts +255 -253
  86. package/src/analyses/AccessorAnalyzer.ts +60 -60
  87. package/src/analyses/ConfigAnalyzer.ts +147 -147
  88. package/src/analyses/ControllerAnalyzer.ts +42 -19
  89. package/src/analyses/ExceptionAnalyzer.ts +148 -115
  90. package/src/analyses/GenericAnalyzer.ts +51 -51
  91. package/src/analyses/ImportAnalyzer.ts +1 -2
  92. package/src/analyses/PathAnalyzer.ts +110 -98
  93. package/src/analyses/ReflectAnalyzer.ts +39 -35
  94. package/src/analyses/SecurityAnalyzer.ts +24 -20
  95. package/src/executable/internal/CommandParser.ts +15 -15
  96. package/src/executable/internal/NestiaConfigLoader.ts +67 -67
  97. package/src/executable/internal/NestiaSdkCommand.ts +60 -60
  98. package/src/executable/sdk.ts +73 -73
  99. package/src/generates/CloneGenerator.ts +62 -0
  100. package/src/generates/E2eGenerator.ts +66 -64
  101. package/src/generates/SdkGenerator.ts +84 -96
  102. package/src/generates/SwaggerGenerator.ts +145 -53
  103. package/src/generates/internal/E2eFileProgrammer.ts +182 -123
  104. package/src/generates/internal/FilePrinter.ts +53 -0
  105. package/src/{utils → generates/internal}/ImportDictionary.ts +35 -13
  106. package/src/generates/internal/SdkAliasCollection.ts +152 -0
  107. package/src/generates/internal/SdkCloneProgrammer.ts +155 -0
  108. package/src/generates/internal/SdkDistributionComposer.ts +91 -91
  109. package/src/generates/internal/SdkFileProgrammer.ts +115 -106
  110. package/src/generates/internal/SdkFunctionProgrammer.ts +298 -518
  111. package/src/generates/internal/SdkImportWizard.ts +55 -55
  112. package/src/generates/internal/SdkNamespaceProgrammer.ts +510 -0
  113. package/src/generates/internal/SdkRouteDirectory.ts +17 -17
  114. package/src/generates/internal/SdkRouteProgrammer.ts +83 -0
  115. package/src/generates/internal/SdkSimulationProgrammer.ts +365 -133
  116. package/src/generates/internal/SdkTypeProgrammer.ts +386 -0
  117. package/src/generates/internal/SwaggerSchemaGenerator.ts +437 -427
  118. package/src/generates/internal/SwaggerSchemaValidator.ts +198 -198
  119. package/src/index.ts +4 -4
  120. package/src/module.ts +2 -2
  121. package/src/structures/IController.ts +94 -95
  122. package/src/structures/IErrorReport.ts +6 -6
  123. package/src/structures/INestiaProject.ts +13 -13
  124. package/src/structures/INormalizedInput.ts +20 -20
  125. package/src/structures/IRoute.ts +53 -53
  126. package/src/structures/ISwaggerLazyProperty.ts +2 -2
  127. package/src/structures/ISwaggerLazySchema.ts +2 -2
  128. package/src/structures/ITypeTuple.ts +6 -6
  129. package/src/structures/MethodType.ts +5 -5
  130. package/src/structures/ParamCategory.ts +1 -1
  131. package/src/structures/TypeEntry.ts +1 -1
  132. package/src/utils/ArrayUtil.ts +26 -26
  133. package/src/utils/FileRetriever.ts +22 -22
  134. package/src/utils/MapUtil.ts +14 -14
  135. package/src/utils/PathUtil.ts +10 -10
  136. package/src/utils/SourceFinder.ts +66 -66
  137. package/src/utils/StringUtil.ts +6 -0
  138. package/src/utils/StripEnums.ts +5 -5
  139. package/assets/bundle/api/utils/NestiaSimulator.ts +0 -70
  140. package/lib/generates/internal/SdkDtoGenerator.d.ts +0 -9
  141. package/lib/generates/internal/SdkDtoGenerator.js +0 -294
  142. package/lib/generates/internal/SdkDtoGenerator.js.map +0 -1
  143. package/lib/generates/internal/SdkTypeDefiner.d.ts +0 -11
  144. package/lib/generates/internal/SdkTypeDefiner.js +0 -82
  145. package/lib/generates/internal/SdkTypeDefiner.js.map +0 -1
  146. package/lib/structures/ISwagger.d.ts +0 -72
  147. package/lib/structures/ISwagger.js +0 -3
  148. package/lib/structures/ISwagger.js.map +0 -1
  149. package/lib/structures/ISwaggerComponents.d.ts +0 -26
  150. package/lib/structures/ISwaggerComponents.js +0 -3
  151. package/lib/structures/ISwaggerComponents.js.map +0 -1
  152. package/lib/structures/ISwaggerInfo.d.ts +0 -71
  153. package/lib/structures/ISwaggerInfo.js +0 -3
  154. package/lib/structures/ISwaggerInfo.js.map +0 -1
  155. package/lib/structures/ISwaggerRoute.d.ts +0 -47
  156. package/lib/structures/ISwaggerRoute.js +0 -3
  157. package/lib/structures/ISwaggerRoute.js.map +0 -1
  158. package/lib/structures/ISwaggerSecurityScheme.d.ts +0 -56
  159. package/lib/structures/ISwaggerSecurityScheme.js +0 -3
  160. package/lib/structures/ISwaggerSecurityScheme.js.map +0 -1
  161. package/lib/utils/ImportDictionary.js.map +0 -1
  162. package/src/generates/internal/SdkDtoGenerator.ts +0 -424
  163. package/src/generates/internal/SdkTypeDefiner.ts +0 -119
  164. package/src/structures/ISwagger.ts +0 -91
  165. package/src/structures/ISwaggerComponents.ts +0 -29
  166. package/src/structures/ISwaggerInfo.ts +0 -80
  167. package/src/structures/ISwaggerRoute.ts +0 -51
  168. package/src/structures/ISwaggerSecurityScheme.ts +0 -65
@@ -1,518 +1,298 @@
1
- import { Pair } from "tstl/utility/Pair";
2
- import { IJsDocTagInfo } from "typia/lib/schemas/metadata/IJsDocTagInfo";
3
- import { Escaper } from "typia/lib/utils/Escaper";
4
-
5
- import { INestiaConfig } from "../../INestiaConfig";
6
- import { IController } from "../../structures/IController";
7
- import { IRoute } from "../../structures/IRoute";
8
- import { ImportDictionary } from "../../utils/ImportDictionary";
9
- import { SdkDtoGenerator } from "./SdkDtoGenerator";
10
- import { SdkImportWizard } from "./SdkImportWizard";
11
- import { SdkSimulationProgrammer } from "./SdkSimulationProgrammer";
12
- import { SdkTypeDefiner } from "./SdkTypeDefiner";
13
-
14
- export namespace SdkFunctionProgrammer {
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
- };
33
-
34
- /* ---------------------------------------------------------
35
- BODY
36
- --------------------------------------------------------- */
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);
51
-
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
- ? "application/octet-stream"
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
- }
89
-
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
- : "";
105
-
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}`;
142
-
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
- };
175
-
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
- };
187
-
188
- const access =
189
- (x: string) =>
190
- (y: string): string =>
191
- y[0] === "[" ? `${x}${y}` : `${x}.${y}`;
192
-
193
- /* ---------------------------------------------------------
194
- HEAD & TAIL
195
- --------------------------------------------------------- */
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
- : [];
211
-
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
- }
228
-
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
- }
245
-
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
- );
253
-
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
- ];
277
-
278
- // OUTPUT TYPE
279
- const output: string =
280
- config.propagate !== true && route.output.typeName === "void"
281
- ? "void"
282
- : `${route.name}.Output`;
283
-
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
- };
296
-
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;
305
- }): string => {
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
- );
333
-
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
- ? "application/octet-stream"
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")}`,
446
- );
447
-
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}\``;
454
-
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");
464
-
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(`
485
- {
486
- ${rest_query_parameters(queryParams)}
487
- } as any`);
488
-
489
- return wrapper(`
490
- {
491
- ...${props.query.name},
492
- ${rest_query_parameters(queryParams)},
493
- } as any`);
494
- };
495
-
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)}`);
509
- }
510
-
511
- const space = (count: number) => " ".repeat(count);
512
- const getTypeName =
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;
1
+ import ts from "typescript";
2
+ import typia from "typia";
3
+ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
+
5
+ import { INestiaConfig } from "../../INestiaConfig";
6
+ import { IController } from "../../structures/IController";
7
+ import { IRoute } from "../../structures/IRoute";
8
+ import { StringUtil } from "../../utils/StringUtil";
9
+ import { ImportDictionary } from "./ImportDictionary";
10
+ import { SdkImportWizard } from "./SdkImportWizard";
11
+ import { SdkTypeProgrammer } from "./SdkTypeProgrammer";
12
+
13
+ export namespace SdkFunctionProgrammer {
14
+ export const write =
15
+ (config: INestiaConfig) =>
16
+ (importer: ImportDictionary) =>
17
+ (
18
+ route: IRoute,
19
+ props: {
20
+ headers: IRoute.IParameter | undefined;
21
+ query: IRoute.IParameter | undefined;
22
+ input: IRoute.IParameter | undefined;
23
+ },
24
+ ): ts.FunctionDeclaration => {
25
+ return ts.factory.createFunctionDeclaration(
26
+ [
27
+ ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
28
+ ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword),
29
+ ],
30
+ undefined,
31
+ route.name,
32
+ undefined,
33
+ [
34
+ IdentifierFactory.parameter(
35
+ "connection",
36
+ ts.factory.createTypeReferenceNode(
37
+ SdkImportWizard.IConnection(importer),
38
+ props.headers
39
+ ? [ts.factory.createTypeReferenceNode(`${route.name}.Headers`)]
40
+ : undefined,
41
+ ),
42
+ ),
43
+ ...route.parameters
44
+ .filter((p) => p.category !== "headers")
45
+ .map((p) =>
46
+ ts.factory.createParameterDeclaration(
47
+ [],
48
+ undefined,
49
+ p.name,
50
+ p.optional
51
+ ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
52
+ : undefined,
53
+ config.primitive !== false &&
54
+ (p === props.query || p === props.input)
55
+ ? ts.factory.createTypeReferenceNode(
56
+ `${route.name}.${p === props.query ? "Query" : "Input"}`,
57
+ )
58
+ : getTypeName(config)(importer)(p),
59
+ ),
60
+ ),
61
+ ],
62
+ ts.factory.createTypeReferenceNode("Promise", [
63
+ getReturnType(config)(route),
64
+ ]),
65
+ ts.factory.createBlock(
66
+ write_body(config)(importer)(route, props),
67
+ true,
68
+ ),
69
+ );
70
+ };
71
+
72
+ const write_body =
73
+ (config: INestiaConfig) =>
74
+ (importer: ImportDictionary) =>
75
+ (
76
+ route: IRoute,
77
+ props: {
78
+ headers: IRoute.IParameter | undefined;
79
+ query: IRoute.IParameter | undefined;
80
+ input: IRoute.IParameter | undefined;
81
+ },
82
+ ): ts.Statement[] => {
83
+ const encrypted: boolean =
84
+ route.encrypted === true ||
85
+ (props.input !== undefined &&
86
+ props.input.custom === true &&
87
+ props.input.category === "body" &&
88
+ props.input.encrypted === true);
89
+ const contentType: string | undefined =
90
+ props.input !== undefined
91
+ ? typia.is<IController.IBodyParameter>(props.input)
92
+ ? props.input.contentType
93
+ : "application/json"
94
+ : undefined;
95
+
96
+ const caller = () =>
97
+ ts.factory.createCallExpression(
98
+ IdentifierFactory.access(
99
+ ts.factory.createIdentifier(
100
+ SdkImportWizard.Fetcher(encrypted)(importer),
101
+ ),
102
+ )(config.propagate ? "propagate" : "fetch"),
103
+ undefined,
104
+ [
105
+ contentType && contentType !== "multipart/form-data"
106
+ ? ts.factory.createObjectLiteralExpression(
107
+ [
108
+ ts.factory.createSpreadAssignment(
109
+ ts.factory.createIdentifier("connection"),
110
+ ),
111
+ ts.factory.createPropertyAssignment(
112
+ "headers",
113
+ ts.factory.createObjectLiteralExpression(
114
+ [
115
+ ts.factory.createSpreadAssignment(
116
+ IdentifierFactory.access(
117
+ ts.factory.createIdentifier("connection"),
118
+ )("headers"),
119
+ ),
120
+ ts.factory.createPropertyAssignment(
121
+ ts.factory.createStringLiteral("Content-Type"),
122
+ ts.factory.createStringLiteral(contentType),
123
+ ),
124
+ ],
125
+ true,
126
+ ),
127
+ ),
128
+ ],
129
+ true,
130
+ )
131
+ : ts.factory.createIdentifier("connection"),
132
+ ts.factory.createObjectLiteralExpression(
133
+ [
134
+ ts.factory.createSpreadAssignment(
135
+ IdentifierFactory.access(
136
+ ts.factory.createIdentifier(route.name),
137
+ )("METADATA"),
138
+ ),
139
+ ts.factory.createPropertyAssignment(
140
+ "path",
141
+ ts.factory.createCallExpression(
142
+ IdentifierFactory.access(
143
+ ts.factory.createIdentifier(route.name),
144
+ )("path"),
145
+ undefined,
146
+ route.parameters
147
+ .filter(
148
+ (p) => p.category === "param" || p.category === "query",
149
+ )
150
+ .map((p) => ts.factory.createIdentifier(p.name)),
151
+ ),
152
+ ),
153
+ ],
154
+ true,
155
+ ),
156
+ ...(props.input
157
+ ? [ts.factory.createIdentifier(props.input.name)]
158
+ : []),
159
+ ...(config.json &&
160
+ typia.is<IController.IBodyParameter>(props.input) &&
161
+ (props.input.contentType === "application/json" ||
162
+ props.input.encrypted === true)
163
+ ? [ts.factory.createIdentifier(`${route.name}.stringify`)]
164
+ : []),
165
+ ],
166
+ );
167
+ const output = (awaiter: boolean) =>
168
+ config.simulate
169
+ ? ts.factory.createConditionalExpression(
170
+ ts.factory.createIdentifier("!!connection.simulate"),
171
+ undefined,
172
+ ts.factory.createCallExpression(
173
+ ts.factory.createIdentifier(`${route.name}.simulate`),
174
+ [],
175
+ [
176
+ ts.factory.createIdentifier("connection"),
177
+ ...route.parameters
178
+ .filter((p) => p.category !== "headers")
179
+ .map((p) => ts.factory.createIdentifier(p.name)),
180
+ ],
181
+ ),
182
+ undefined,
183
+ awaiter ? ts.factory.createAwaitExpression(caller()) : caller(),
184
+ )
185
+ : awaiter
186
+ ? ts.factory.createAwaitExpression(caller())
187
+ : caller();
188
+ return [
189
+ ...(config.assert
190
+ ? route.parameters
191
+ .filter((p) => p.category !== "headers")
192
+ .map((p) =>
193
+ ts.factory.createExpressionStatement(
194
+ ts.factory.createCallExpression(
195
+ IdentifierFactory.access(
196
+ ts.factory.createIdentifier(
197
+ SdkImportWizard.typia(importer),
198
+ ),
199
+ )("assert"),
200
+ [
201
+ ts.factory.createTypeQueryNode(
202
+ ts.factory.createIdentifier(p.name),
203
+ ),
204
+ ],
205
+ [ts.factory.createIdentifier(p.name)],
206
+ ),
207
+ ),
208
+ )
209
+ : []),
210
+ ...(route.setHeaders.length === 0
211
+ ? [ts.factory.createReturnStatement(output(false))]
212
+ : write_set_headers(config)(route)(output(true))),
213
+ ];
214
+ };
215
+
216
+ const write_set_headers =
217
+ (config: INestiaConfig) =>
218
+ (route: IRoute) =>
219
+ (condition: ts.Expression): ts.Statement[] => {
220
+ const accessor = (x: string) => (y: string) =>
221
+ x[0] === "[" ? `${x}${y}` : `${x}.${y}`;
222
+ const output: string = StringUtil.escapeDuplicate([
223
+ "connection",
224
+ ...route.parameters.map((p) => p.name),
225
+ ])("output");
226
+ const headers: string = accessor("connection")("headers");
227
+ const data: string = config.propagate ? accessor(output)("data") : output;
228
+
229
+ const assigners: ts.ExpressionStatement[] = [
230
+ ts.factory.createBinaryExpression(
231
+ ts.factory.createIdentifier(headers),
232
+ ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken),
233
+ ts.factory.createObjectLiteralExpression([]),
234
+ ),
235
+ ...route.setHeaders.map((tuple) =>
236
+ tuple.type === "assigner"
237
+ ? ts.factory.createCallExpression(
238
+ ts.factory.createIdentifier("Object.assign"),
239
+ [],
240
+ [
241
+ ts.factory.createIdentifier(headers),
242
+ ts.factory.createIdentifier(accessor(data)(tuple.source)),
243
+ ],
244
+ )
245
+ : ts.factory.createBinaryExpression(
246
+ ts.factory.createIdentifier(
247
+ accessor(headers)(tuple.target ?? tuple.source),
248
+ ),
249
+ ts.factory.createToken(ts.SyntaxKind.EqualsToken),
250
+ ts.factory.createIdentifier(accessor(data)(tuple.source)),
251
+ ),
252
+ ),
253
+ ].map(ts.factory.createExpressionStatement);
254
+ return [
255
+ ts.factory.createVariableStatement(
256
+ [],
257
+ ts.factory.createVariableDeclarationList(
258
+ [
259
+ ts.factory.createVariableDeclaration(
260
+ output,
261
+ undefined,
262
+ getReturnType(config)(route),
263
+ condition,
264
+ ),
265
+ ],
266
+ ts.NodeFlags.Const,
267
+ ),
268
+ ),
269
+ ...(config.propagate
270
+ ? [
271
+ ts.factory.createIfStatement(
272
+ ts.factory.createIdentifier(accessor(output)("success")),
273
+ assigners.length === 1
274
+ ? assigners[0]
275
+ : ts.factory.createBlock(assigners, true),
276
+ undefined,
277
+ ),
278
+ ]
279
+ : assigners),
280
+ ts.factory.createReturnStatement(ts.factory.createIdentifier(output)),
281
+ ];
282
+ };
283
+ }
284
+
285
+ const getTypeName =
286
+ (config: INestiaConfig) =>
287
+ (importer: ImportDictionary) =>
288
+ (p: IRoute.IParameter | IRoute.IOutput) =>
289
+ p.metadata
290
+ ? SdkTypeProgrammer.write(config)(importer)(p.metadata)
291
+ : ts.factory.createTypeReferenceNode(p.typeName);
292
+
293
+ const getReturnType = (config: INestiaConfig) => (route: IRoute) =>
294
+ ts.factory.createTypeReferenceNode(
295
+ config.propagate !== true && route.output.typeName === "void"
296
+ ? "void"
297
+ : `${route.name}.Output`,
298
+ );