@nestia/sdk 1.1.1 → 1.2.0-dev.20230504

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 (79) hide show
  1. package/assets/bundle/api/index.ts +4 -0
  2. package/assets/bundle/api/module.ts +5 -0
  3. package/assets/bundle/e2e/index.ts +41 -0
  4. package/lib/INestiaConfig.d.ts +6 -0
  5. package/lib/NestiaSdkApplication.d.ts +1 -0
  6. package/lib/NestiaSdkApplication.js +35 -14
  7. package/lib/NestiaSdkApplication.js.map +1 -1
  8. package/lib/analyses/ControllerAnalyzer.js +1 -0
  9. package/lib/analyses/ControllerAnalyzer.js.map +1 -1
  10. package/lib/analyses/ReflectAnalyzer.js +12 -1
  11. package/lib/analyses/ReflectAnalyzer.js.map +1 -1
  12. package/lib/executable/internal/NestiaSdkCommand.d.ts +3 -2
  13. package/lib/executable/internal/NestiaSdkCommand.js +52 -56
  14. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  15. package/lib/executable/internal/NestiaSdkConfig.js +5 -1
  16. package/lib/executable/internal/NestiaSdkConfig.js.map +1 -1
  17. package/lib/executable/sdk.js +5 -8
  18. package/lib/executable/sdk.js.map +1 -1
  19. package/lib/generates/E2eGenerator.d.ts +5 -0
  20. package/lib/generates/E2eGenerator.js +52 -0
  21. package/lib/generates/E2eGenerator.js.map +1 -0
  22. package/lib/generates/SdkGenerator.d.ts +1 -2
  23. package/lib/generates/SdkGenerator.js +20 -22
  24. package/lib/generates/SdkGenerator.js.map +1 -1
  25. package/lib/generates/SwaggerGenerator.d.ts +1 -1
  26. package/lib/generates/SwaggerGenerator.js +40 -43
  27. package/lib/generates/SwaggerGenerator.js.map +1 -1
  28. package/lib/generates/internal/E2eFileProgrammer.d.ts +8 -0
  29. package/lib/generates/internal/E2eFileProgrammer.js +101 -0
  30. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -0
  31. package/lib/generates/internal/SdkFileProgrammer.d.ts +5 -0
  32. package/lib/generates/internal/SdkFileProgrammer.js +121 -0
  33. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -0
  34. package/lib/generates/internal/SdkFunctionProgrammer.d.ts +5 -0
  35. package/lib/generates/{FunctionGenerator.js → internal/SdkFunctionProgrammer.js} +58 -57
  36. package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -0
  37. package/lib/generates/internal/SdkRouteDirectory.d.ts +10 -0
  38. package/lib/generates/internal/SdkRouteDirectory.js +18 -0
  39. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -0
  40. package/lib/structures/IController.d.ts +4 -0
  41. package/lib/structures/IRoute.d.ts +4 -0
  42. package/lib/utils/NestiaConfigUtil.d.ts +4 -0
  43. package/lib/utils/NestiaConfigUtil.js +24 -0
  44. package/lib/utils/NestiaConfigUtil.js.map +1 -0
  45. package/lib/utils/SourceFinder.d.ts +9 -0
  46. package/lib/utils/SourceFinder.js +60 -0
  47. package/lib/utils/SourceFinder.js.map +1 -0
  48. package/package.json +2 -3
  49. package/src/INestiaConfig.ts +7 -0
  50. package/src/NestiaSdkApplication.ts +54 -17
  51. package/src/analyses/ControllerAnalyzer.ts +1 -0
  52. package/src/analyses/ReflectAnalyzer.ts +12 -2
  53. package/src/executable/internal/NestiaSdkCommand.ts +87 -105
  54. package/src/executable/sdk.ts +4 -8
  55. package/src/generates/E2eGenerator.ts +65 -0
  56. package/src/generates/SdkGenerator.ts +29 -30
  57. package/src/generates/SwaggerGenerator.ts +66 -64
  58. package/src/generates/internal/E2eFileProgrammer.ts +119 -0
  59. package/src/generates/internal/SdkFileProgrammer.ts +144 -0
  60. package/src/generates/internal/SdkFunctionProgrammer.ts +371 -0
  61. package/src/generates/internal/SdkRouteDirectory.ts +21 -0
  62. package/src/structures/IController.ts +4 -0
  63. package/src/structures/IRoute.ts +4 -0
  64. package/src/utils/NestiaConfigUtil.ts +21 -0
  65. package/src/utils/SourceFinder.ts +60 -0
  66. package/lib/analyses/SourceFinder.d.ts +0 -4
  67. package/lib/analyses/SourceFinder.js +0 -71
  68. package/lib/analyses/SourceFinder.js.map +0 -1
  69. package/lib/generates/FileGenerator.d.ts +0 -5
  70. package/lib/generates/FileGenerator.js +0 -138
  71. package/lib/generates/FileGenerator.js.map +0 -1
  72. package/lib/generates/FunctionGenerator.d.ts +0 -5
  73. package/lib/generates/FunctionGenerator.js.map +0 -1
  74. package/src/analyses/SourceFinder.ts +0 -59
  75. package/src/generates/FileGenerator.ts +0 -156
  76. package/src/generates/FunctionGenerator.ts +0 -348
  77. /package/assets/bundle/{HttpError.ts → api/HttpError.ts} +0 -0
  78. /package/assets/bundle/{IConnection.ts → api/IConnection.ts} +0 -0
  79. /package/assets/bundle/{Primitive.ts → api/Primitive.ts} +0 -0
@@ -1,348 +0,0 @@
1
- import { Vector } from "tstl/container/Vector";
2
- import { Pair } from "tstl/utility/Pair";
3
- import ts from "typescript";
4
-
5
- import { Escaper } from "typia/lib/utils/Escaper";
6
-
7
- import { INestiaConfig } from "../INestiaConfig";
8
- import { IRoute } from "../structures/IRoute";
9
-
10
- export namespace FunctionGenerator {
11
- export function generate(config: INestiaConfig, route: IRoute): string {
12
- const query: IRoute.IParameter | undefined = route.parameters.find(
13
- (param) => param.category === "query" && param.field === undefined,
14
- );
15
- const input: IRoute.IParameter | undefined = route.parameters.find(
16
- (param) => param.category === "body",
17
- );
18
-
19
- return [head, body, tail]
20
- .map((closure) => closure(route, query, input, config))
21
- .filter((str) => !!str)
22
- .join("\n");
23
- }
24
-
25
- /* ---------------------------------------------------------
26
- BODY
27
- --------------------------------------------------------- */
28
- function body(
29
- route: IRoute,
30
- query: IRoute.IParameter | undefined,
31
- input: IRoute.IParameter | undefined,
32
- config: INestiaConfig,
33
- ): string {
34
- // FETCH ARGUMENTS WITH REQUST BODY
35
- const parameters = filter_parameters(route, query);
36
- const fetchArguments: string[] = [
37
- "connection",
38
- `${route.name}.ENCRYPTED`,
39
- `${route.name}.METHOD`,
40
- `${route.name}.path(${parameters.map((p) => p.name).join(", ")})`,
41
- ];
42
- if (input !== undefined) {
43
- fetchArguments.push(input.name);
44
- if (config.json === true)
45
- fetchArguments.push(`${route.name}.stringify`);
46
- }
47
-
48
- const assertions: string =
49
- config.assert === true && route.parameters.length !== 0
50
- ? route.parameters
51
- .map(
52
- (param) =>
53
- ` typia.assert<typeof ${param.name}>(${param.name});`,
54
- )
55
- .join("\n") + "\n\n"
56
- : "";
57
-
58
- // FUNCTION CALL STATEMENT
59
- const caller: string =
60
- "Fetcher.fetch\n" +
61
- " (\n" +
62
- fetchArguments.map((param) => ` ${param}`).join(",\n") +
63
- "\n" +
64
- " )";
65
- if (route.setHeaders.length === 0)
66
- return `{\n${assertions} return ${caller};\n}`;
67
-
68
- // SET HEADERS
69
- const content: string[] = [
70
- `{\n`,
71
- assertions,
72
- ` const output: ${route.name}.Output = await ${caller};\n`,
73
- "\n",
74
- ` // configure header(s)\n`,
75
- ` connection.headers ??= {};\n`,
76
- ];
77
-
78
- for (const header of route.setHeaders) {
79
- if (header.type === "assigner")
80
- content.push(
81
- " ",
82
- `Object.assign(connection.headers, ${access(
83
- "output",
84
- header.source,
85
- )});\n`,
86
- );
87
- else
88
- content.push(
89
- " ",
90
- `${access(
91
- "connection.headers",
92
- header.target ?? header.source,
93
- )} = ${access("output", header.source)};\n`,
94
- );
95
- }
96
- content.push("\n", " return output;\n", "}");
97
- return content.join("");
98
- }
99
-
100
- function filter_parameters(
101
- route: IRoute,
102
- query: IRoute.IParameter | undefined,
103
- ): IRoute.IParameter[] {
104
- const parameters: IRoute.IParameter[] = route.parameters.filter(
105
- (param) =>
106
- param.category === "param" ||
107
- (param.category === "query" && param.field !== undefined),
108
- );
109
- if (query) parameters.push(query);
110
- return parameters;
111
- }
112
-
113
- function access(x: string, y: string): string {
114
- return y[0] === "[" ? `${x}${y}` : `${x}.${y}`;
115
- }
116
-
117
- /* ---------------------------------------------------------
118
- HEAD & TAIL
119
- --------------------------------------------------------- */
120
- function head(
121
- route: IRoute,
122
- query: IRoute.IParameter | undefined,
123
- input: IRoute.IParameter | undefined,
124
- config: INestiaConfig,
125
- ): string {
126
- //----
127
- // CONSTRUCT COMMENT
128
- //----
129
- // MAIN DESCRIPTION
130
- const comments: string[] = route.comments
131
- .map((part) => `${part.kind === "linkText" ? " " : ""}${part.text}`)
132
- .map((str) => str.split("\r\n").join("\n"))
133
- .join("")
134
- .split("\n")
135
- .filter((str, i, array) => str !== "" || i !== array.length - 1);
136
- if (comments.length) comments.push("");
137
-
138
- // FILTER TAGS (VULNERABLE PARAMETERS WOULD BE REMOVED)
139
- const tagList: ts.JSDocTagInfo[] = route.tags.slice();
140
- if (tagList.length !== 0) {
141
- const index: number = tagList.findIndex((t) => t.name === "param");
142
- if (index !== -1) {
143
- const capsule: Vector<ts.JSDocTagInfo> = Vector.wrap(tagList);
144
- capsule.insert(capsule.nth(index), {
145
- name: "param",
146
- text: [
147
- {
148
- kind: "parameterName",
149
- text: "connection",
150
- },
151
- {
152
- kind: "space",
153
- text: " ",
154
- },
155
- {
156
- kind: "text",
157
- text: "connection Information of the remote HTTP(s) server with headers (+encryption password)",
158
- },
159
- ],
160
- });
161
- }
162
- comments.push(
163
- ...tagList.map((tag) =>
164
- tag.text
165
- ? `@${tag.name} ${tag.text
166
- .map((elem) => elem.text)
167
- .join("")}`
168
- : `@${tag.name}`,
169
- ),
170
- "",
171
- );
172
- }
173
-
174
- // COMPLETE THE COMMENT
175
- comments.push(
176
- `@controller ${route.symbol}`,
177
- `@path ${route.method} ${route.path}`,
178
- `@nestia Generated by Nestia - https://github.com/samchon/nestia`,
179
- );
180
-
181
- //----
182
- // FINALIZATION
183
- //----
184
- // REFORM PARAMETERS TEXT
185
- const parameters: string[] = [
186
- "connection: IConnection",
187
- ...route.parameters.map((param) => {
188
- const type: string =
189
- config.primitive !== false &&
190
- (param === query || param === input)
191
- ? `Primitive<${route.name}.${
192
- param === query ? "Query" : "Input"
193
- }>`
194
- : param.type.name;
195
- return `${param.name}${param.optional ? "?" : ""}: ${type}`;
196
- }),
197
- ];
198
-
199
- // OUTPUT TYPE
200
- const output: string =
201
- route.output.name === "void" ? "void" : `${route.name}.Output`;
202
-
203
- // RETURNS WITH CONSTRUCTION
204
- return (
205
- "" +
206
- "/**\n" +
207
- comments.map((str) => ` * ${str}`).join("\n") +
208
- "\n" +
209
- " */\n" +
210
- `export${route.setHeaders.length ? " async" : ""} function ${
211
- route.name
212
- }\n` +
213
- ` (\n` +
214
- `${parameters.map((str) => ` ${str}`).join(",\n")}\n` +
215
- ` ): Promise<${output}>`
216
- );
217
- }
218
-
219
- function tail(
220
- route: IRoute,
221
- query: IRoute.IParameter | undefined,
222
- input: IRoute.IParameter | undefined,
223
- config: INestiaConfig,
224
- ): string | null {
225
- // LIST UP TYPES
226
- const types: Pair<string, string>[] = [];
227
- if (query !== undefined) types.push(new Pair("Query", query.type.name));
228
- if (input !== undefined) types.push(new Pair("Input", input.type.name));
229
- if (route.output.name !== "void")
230
- types.push(new Pair("Output", route.output.name));
231
-
232
- // PATH WITH PARAMETERS
233
- const parameters: IRoute.IParameter[] = filter_parameters(route, query);
234
- const path: string = compute_path(query, parameters, route.path);
235
-
236
- return (
237
- `export namespace ${route.name}\n` +
238
- "{\n" +
239
- (types.length !== 0
240
- ? types
241
- .map(
242
- (tuple) =>
243
- ` export type ${tuple.first} = ${
244
- config.primitive !== false
245
- ? `Primitive<${tuple.second}>`
246
- : tuple.second
247
- };`,
248
- )
249
- .join("\n") + "\n"
250
- : "") +
251
- "\n" +
252
- ` export const METHOD = "${route.method}" as const;\n` +
253
- ` export const PATH: string = "${route.path}";\n` +
254
- ` export const ENCRYPTED: Fetcher.IEncrypted = {\n` +
255
- ` request: ${input !== undefined && input.encrypted},\n` +
256
- ` response: ${route.encrypted},\n` +
257
- (route.status !== undefined
258
- ? ` status: ${route.status},\n`
259
- : "") +
260
- ` };\n` +
261
- "\n" +
262
- ` export function path(${parameters
263
- .map((param) => `${param.name}: ${param.type.name}`)
264
- .join(", ")}): string\n` +
265
- ` {\n` +
266
- `${path};\n` +
267
- ` }\n` +
268
- (config.json === true &&
269
- route.parameters.find((param) => param.category === "body") !==
270
- undefined
271
- ? ` export const stringify = (input: Input) => typia.assertStringify(input);\n`
272
- : "") +
273
- "}"
274
- );
275
- }
276
-
277
- function compute_path(
278
- query: IRoute.IParameter | undefined,
279
- parameters: IRoute.IParameter[],
280
- path: string,
281
- ): string {
282
- for (const param of parameters)
283
- if (param.category === "param")
284
- path = path.replace(
285
- `:${param.field}`,
286
- `\${encodeURIComponent(${param.name} ?? "null")}`,
287
- );
288
-
289
- // NO QUERY PARAMETER
290
- const queryParams: IRoute.IParameter[] = parameters.filter(
291
- (param) => param.category === "query" && param.field !== undefined,
292
- );
293
- if (query === undefined && queryParams.length === 0)
294
- return `${" ".repeat(8)}return \`${path}\``;
295
-
296
- const computeName = (str: string): string =>
297
- parameters.find((p) => p.name === str) !== undefined
298
- ? computeName("_" + str)
299
- : str;
300
- const variables: string = computeName("variables");
301
- const search: string = computeName("search");
302
- const encoded: string = computeName("encoded");
303
-
304
- const wrapper = (expr: string) =>
305
- [
306
- `const ${variables}: Record<any, any> = ${expr};`,
307
- `const ${search}: URLSearchParams = new URLSearchParams();`,
308
- `for (const [key, value] of Object.entries(${variables}))`,
309
- ` if (value === undefined) continue;`,
310
- ` else if (Array.isArray(value))`,
311
- ` value.forEach((elem) => ${search}.append(key, String(elem)));`,
312
- ` else`,
313
- ` ${search}.set(key, String(value));`,
314
- `const ${encoded}: string = ${search}.toString();`,
315
- `return \`${path}\${${encoded}.length ? \`?\${${encoded}}\` : ""}\`;`,
316
- ]
317
- .map((str) => `${" ".repeat(8)}${str}`)
318
- .join("\n");
319
-
320
- if (query !== undefined && queryParams.length === 0)
321
- return wrapper(`${query.name} as any`);
322
- else if (query === undefined)
323
- return wrapper(`
324
- {
325
- ${rest_query_parameters(queryParams)}
326
- } as any`);
327
-
328
- return wrapper(`
329
- {
330
- ...${query.name},
331
- ${rest_query_parameters(queryParams)},
332
- } as any`);
333
- }
334
-
335
- function rest_query_parameters(parameters: IRoute.IParameter[]): string {
336
- return parameters
337
- .map((param) =>
338
- param.name === param.field
339
- ? param.name
340
- : `${
341
- Escaper.variable(param.field!)
342
- ? param.field
343
- : JSON.stringify(param.field)
344
- }: ${param.name}`,
345
- )
346
- .join(`,\n${" ".repeat(12)}`);
347
- }
348
- }