@nestia/sdk 2.0.0-dev.20230831-4 → 2.0.0-dev.20230901

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 (56) hide show
  1. package/assets/bundle/api/utils/NestiaSimulator.ts +1 -23
  2. package/lib/NestiaSdkApplication.js +1 -1
  3. package/lib/NestiaSdkApplication.js.map +1 -1
  4. package/lib/analyses/ControllerAnalyzer.js +11 -5
  5. package/lib/analyses/ControllerAnalyzer.js.map +1 -1
  6. package/lib/analyses/ExceptionAnalyzer.js +7 -2
  7. package/lib/analyses/ExceptionAnalyzer.js.map +1 -1
  8. package/lib/analyses/ImportAnalyzer.js +1 -1
  9. package/lib/analyses/ImportAnalyzer.js.map +1 -1
  10. package/lib/analyses/ReflectAnalyzer.js +0 -10
  11. package/lib/analyses/ReflectAnalyzer.js.map +1 -1
  12. package/lib/executable/internal/NestiaSdkConfig.js +37 -46
  13. package/lib/executable/internal/NestiaSdkConfig.js.map +1 -1
  14. package/lib/generates/SwaggerGenerator.d.ts +10 -0
  15. package/lib/generates/SwaggerGenerator.js +29 -497
  16. package/lib/generates/SwaggerGenerator.js.map +1 -1
  17. package/lib/generates/internal/E2eFileProgrammer.js +5 -44
  18. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  19. package/lib/generates/internal/SdkFunctionProgrammer.js +12 -10
  20. package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
  21. package/lib/generates/internal/SdkSimulationProgrammer.js +4 -4
  22. package/lib/generates/internal/SdkSimulationProgrammer.js.map +1 -1
  23. package/lib/generates/internal/SwaggerSchemaGenerator.d.ts +19 -0
  24. package/lib/generates/internal/SwaggerSchemaGenerator.js +301 -0
  25. package/lib/generates/internal/SwaggerSchemaGenerator.js.map +1 -0
  26. package/lib/generates/internal/SwaggerSchemaValidator.d.ts +7 -0
  27. package/lib/generates/internal/SwaggerSchemaValidator.js +200 -0
  28. package/lib/generates/internal/SwaggerSchemaValidator.js.map +1 -0
  29. package/lib/structures/IController.d.ts +0 -4
  30. package/lib/structures/IRoute.d.ts +6 -3
  31. package/lib/structures/ISwaggerError.d.ts +6 -0
  32. package/lib/structures/ISwaggerError.js +3 -0
  33. package/lib/structures/ISwaggerError.js.map +1 -0
  34. package/lib/structures/ISwaggerRoute.d.ts +2 -1
  35. package/lib/structures/ISwaggerSchemaTuple.d.ts +6 -0
  36. package/lib/structures/ISwaggerSchemaTuple.js +3 -0
  37. package/lib/structures/ISwaggerSchemaTuple.js.map +1 -0
  38. package/lib/structures/ITypeTuple.d.ts +1 -1
  39. package/package.json +5 -5
  40. package/src/NestiaSdkApplication.ts +1 -1
  41. package/src/analyses/ControllerAnalyzer.ts +13 -4
  42. package/src/analyses/ExceptionAnalyzer.ts +3 -2
  43. package/src/analyses/ImportAnalyzer.ts +1 -1
  44. package/src/analyses/ReflectAnalyzer.ts +0 -10
  45. package/src/generates/SwaggerGenerator.ts +86 -478
  46. package/src/generates/internal/E2eFileProgrammer.ts +7 -49
  47. package/src/generates/internal/SdkFunctionProgrammer.ts +13 -11
  48. package/src/generates/internal/SdkSimulationProgrammer.ts +5 -5
  49. package/src/generates/internal/SwaggerSchemaGenerator.ts +433 -0
  50. package/src/generates/internal/SwaggerSchemaValidator.ts +222 -0
  51. package/src/structures/IController.ts +0 -4
  52. package/src/structures/IRoute.ts +6 -3
  53. package/src/structures/ISwaggerError.ts +8 -0
  54. package/src/structures/ISwaggerRoute.ts +2 -1
  55. package/src/structures/ISwaggerSchemaTuple.ts +7 -0
  56. package/src/structures/ITypeTuple.ts +1 -1
@@ -19,15 +19,6 @@ export namespace E2eFileProgrammer {
19
19
  instance,
20
20
  });
21
21
 
22
- const additional: string[] = [];
23
- for (const param of route.parameters.filter(
24
- (p) => p.category !== "headers",
25
- )) {
26
- const type = getAdditional(param);
27
- if (type === "uuid") additional.push(UUID);
28
- else if (type === "date") additional.push(DATE);
29
- }
30
-
31
22
  importer.internal({
32
23
  type: false,
33
24
  file: props.api,
@@ -39,7 +30,6 @@ export namespace E2eFileProgrammer {
39
30
  importer.toScript(props.current),
40
31
  "",
41
32
  body,
42
- ...(additional.length ? ["", ...additional] : []),
43
33
  ].join("\n");
44
34
 
45
35
  await fs.promises.writeFile(
@@ -53,7 +43,7 @@ export namespace E2eFileProgrammer {
53
43
  (config: INestiaConfig) =>
54
44
  (importer: ImportDictionary) =>
55
45
  (route: IRoute): string => {
56
- const tab: number = route.output.name === "void" ? 2 : 3;
46
+ const tab: number = route.output.typeName === "void" ? 2 : 3;
57
47
  const headers = route.parameters.find(
58
48
  (p) => p.category === "headers" && p.field === undefined,
59
49
  );
@@ -67,7 +57,7 @@ export namespace E2eFileProgrammer {
67
57
  " ...(connection.headers ?? {}),",
68
58
  ` ...${SdkImportWizard.typia(
69
59
  importer,
70
- )}.random<${headers.type.name}>(),`,
60
+ )}.random<${headers.typeName}>(),`,
71
61
  " },",
72
62
  "},",
73
63
  ]
@@ -83,11 +73,11 @@ export namespace E2eFileProgrammer {
83
73
  `export const ${name(route)} = async (`,
84
74
  ` connection: api.IConnection`,
85
75
  `): Promise<void> => {`,
86
- ...(route.output.name === "void"
76
+ ...(route.output.typeName === "void"
87
77
  ? [` ${output}`]
88
78
  : [
89
79
  ` const output: ${primitive(config)(importer)(
90
- route.output.name,
80
+ route.output.typeName,
91
81
  )} = `,
92
82
  ` ${output}`,
93
83
  ` ${SdkImportWizard.typia(
@@ -103,16 +93,9 @@ export namespace E2eFileProgrammer {
103
93
  (importer: ImportDictionary) =>
104
94
  (tab: number) =>
105
95
  (param: IRoute.IParameter): string => {
106
- const middle: string =
107
- param.category === "param" &&
108
- param.custom &&
109
- (param.meta?.type === "uuid" || param.meta?.type === "date")
110
- ? param.meta.nullable
111
- ? `Math.random() < .2 ? null : ${param.meta.type}()`
112
- : `${param.meta.type}()`
113
- : `${SdkImportWizard.typia(importer)}.random<${primitive(
114
- config,
115
- )(importer)(param.type.name)}>()`;
96
+ const middle: string = `${SdkImportWizard.typia(
97
+ importer,
98
+ )}.random<${primitive(config)(importer)(param.typeName)}>()`;
116
99
  return `${" ".repeat(4 * tab)}${middle},`;
117
100
  };
118
101
 
@@ -129,29 +112,4 @@ export namespace E2eFileProgrammer {
129
112
  config.primitive !== false
130
113
  ? `${SdkImportWizard.Primitive(importer)}<${name}>`
131
114
  : name;
132
-
133
- const getAdditional = (
134
- param: IRoute.IParameter,
135
- ): "uuid" | "date" | null => {
136
- if (param.custom === false || param.category !== "param" || !param.meta)
137
- return null;
138
- else if (param.meta.type === "uuid") return "uuid";
139
- else if (param.meta.type === "date") return "date";
140
- return null;
141
- };
142
115
  }
143
-
144
- const UUID = `const uuid = (): string =>
145
- "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
146
- const r = (Math.random() * 16) | 0;
147
- const v = c === "x" ? r : (r & 0x3) | 0x8;
148
- return v.toString(16);
149
- });`;
150
- const DATE = `const date = (): string => {
151
- const date: Date = new Date(Math.floor(Math.random() * Date.now() * 2));
152
- return [
153
- date.getFullYear(),
154
- (date.getMonth() + 1).toString().padStart(2, "0"),
155
- date.getDate().toString().padStart(2, "0"),
156
- ].join("-");
157
- }`;
@@ -1,6 +1,6 @@
1
1
  import { Pair } from "tstl/utility/Pair";
2
2
 
3
- import { IJsDocTagInfo } from "typia/lib/metadata/IJsDocTagInfo";
3
+ import { IJsDocTagInfo } from "typia/lib/schemas/metadata/IJsDocTagInfo";
4
4
  import { Escaper } from "typia/lib/utils/Escaper";
5
5
 
6
6
  import { INestiaConfig } from "../../INestiaConfig";
@@ -283,7 +283,7 @@ export namespace SdkFunctionProgrammer {
283
283
  ? `${route.name}.${
284
284
  param === props.query ? "Query" : "Input"
285
285
  }`
286
- : param.type.name;
286
+ : param.typeName;
287
287
  return `${param.name}${
288
288
  param.optional ? "?" : ""
289
289
  }: ${type}`;
@@ -292,7 +292,9 @@ export namespace SdkFunctionProgrammer {
292
292
 
293
293
  // OUTPUT TYPE
294
294
  const output: string =
295
- route.output.name === "void" ? "void" : `${route.name}.Output`;
295
+ route.output.typeName === "void"
296
+ ? "void"
297
+ : `${route.name}.Output`;
296
298
 
297
299
  // RETURNS WITH CONSTRUCTION
298
300
  return (
@@ -319,13 +321,13 @@ export namespace SdkFunctionProgrammer {
319
321
  // LIST UP TYPES
320
322
  const types: Pair<string, string>[] = [];
321
323
  if (props.headers !== undefined)
322
- types.push(new Pair("Headers", props.headers.type.name));
324
+ types.push(new Pair("Headers", props.headers.typeName));
323
325
  if (props.query !== undefined)
324
- types.push(new Pair("Query", props.query.type.name));
326
+ types.push(new Pair("Query", props.query.typeName));
325
327
  if (props.input !== undefined)
326
- types.push(new Pair("Input", props.input.type.name));
327
- if (route.output.name !== "void")
328
- types.push(new Pair("Output", route.output.name));
328
+ types.push(new Pair("Input", props.input.typeName));
329
+ if (route.output.typeName !== "void")
330
+ types.push(new Pair("Output", route.output.typeName));
329
331
 
330
332
  // PATH WITH PARAMETERS
331
333
  const parameters: IRoute.IParameter[] = filter_path_parameters(
@@ -397,15 +399,15 @@ export namespace SdkFunctionProgrammer {
397
399
  (param) =>
398
400
  `${param.name}: ${
399
401
  param.category === "query" &&
400
- param.type.name === props.query?.type.name
402
+ param.typeName === props.query?.typeName
401
403
  ? `${route.name}.Query`
402
- : param.type.name
404
+ : param.typeName
403
405
  }`,
404
406
  )
405
407
  .join(", ")}): string => {\n` +
406
408
  `${path};\n` +
407
409
  ` }\n` +
408
- (config.simulate === true && route.output.name !== "void"
410
+ (config.simulate === true && route.output.typeName !== "void"
409
411
  ? ` export const random = (g?: Partial<${SdkImportWizard.typia(
410
412
  importer,
411
413
  )}.IRandomGenerator>): Output =>\n` +
@@ -8,7 +8,7 @@ export namespace SdkSimulationProgrammer {
8
8
  (config: INestiaConfig) =>
9
9
  (importer: ImportDictionary) =>
10
10
  (route: IRoute): string => {
11
- const output: boolean = route.output.name !== "void";
11
+ const output: boolean = route.output.typeName !== "void";
12
12
  const returns = () => [
13
13
  `return random(`,
14
14
  ` typeof connection.simulate === 'object' &&`,
@@ -28,7 +28,7 @@ export namespace SdkSimulationProgrammer {
28
28
  `export const simulate = async (`,
29
29
  ` ${
30
30
  route.parameters.filter((p) => p.category !== "headers")
31
- .length === 0 && route.output.name === "void"
31
+ .length === 0 && route.output.typeName === "void"
32
32
  ? "_connection"
33
33
  : "connection"
34
34
  }: ${
@@ -52,7 +52,7 @@ export namespace SdkSimulationProgrammer {
52
52
  ? "Query"
53
53
  : "Input"
54
54
  }`
55
- : p.type.name
55
+ : p.typeName
56
56
  },`,
57
57
  ),
58
58
  `): Promise<${output ? "Output" : "void"}> => {`,
@@ -97,8 +97,8 @@ export namespace SdkSimulationProgrammer {
97
97
  ? `assert.headers(() => ${SdkImportWizard.typia(
98
98
  importer,
99
99
  )}.assert(connection.headers);` // not reachable
100
- : `assert.param("${p.field}")("${
101
- p.custom && p.meta ? p.meta.type : p.type.name
100
+ : `assert.param("${
101
+ p.field
102
102
  }")(() => ${SdkImportWizard.typia(
103
103
  importer,
104
104
  )}.assert(${p.name}));`,
@@ -0,0 +1,433 @@
1
+ import { Singleton } from "tstl";
2
+ import ts from "typescript";
3
+
4
+ import { IJsonSchema } from "typia";
5
+ import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
6
+ import { MetadataFactory } from "typia/lib/factories/MetadataFactory";
7
+ import { Metadata } from "typia/lib/schemas/metadata/Metadata";
8
+ import { ValidationPipe } from "typia/lib/typings/ValidationPipe";
9
+
10
+ import { INestiaConfig } from "../../INestiaConfig";
11
+ import { IRoute } from "../../structures/IRoute";
12
+ import { ISwaggerError } from "../../structures/ISwaggerError";
13
+ import { ISwaggerRoute } from "../../structures/ISwaggerRoute";
14
+ import { ISwaggerSchemaTuple } from "../../structures/ISwaggerSchemaTuple";
15
+ import { SwaggerSchemaValidator } from "./SwaggerSchemaValidator";
16
+
17
+ export namespace SwaggerSchemaGenerator {
18
+ export interface IProps {
19
+ config: INestiaConfig.ISwaggerConfig;
20
+ checker: ts.TypeChecker;
21
+ collection: MetadataCollection;
22
+ tuples: Array<ISwaggerSchemaTuple>;
23
+ errors: ISwaggerError[];
24
+ }
25
+
26
+ export const response =
27
+ (props: IProps) =>
28
+ (route: IRoute): ISwaggerRoute.IResponseBody => {
29
+ const output: ISwaggerRoute.IResponseBody = {};
30
+
31
+ //----
32
+ // EXCEPTION STATUSES
33
+ //----
34
+ // FROM DECORATOR
35
+ for (const [status, exp] of Object.entries(route.exceptions)) {
36
+ const result = MetadataFactory.analyze(props.checker)({
37
+ escape: true,
38
+ constant: true,
39
+ absorb: false,
40
+ validate: (meta) => {
41
+ const bigint: boolean =
42
+ meta.atomics.some((a) => a.type === "bigint") ||
43
+ meta.constants.some((a) => a.type === "bigint");
44
+ return bigint ? ["bigint type is not allowed."] : [];
45
+ },
46
+ })(props.collection)(exp.type);
47
+ if (result.success === false)
48
+ props.errors.push(
49
+ ...result.errors.map((e) => ({
50
+ ...e,
51
+ route,
52
+ from: `response body (status: ${status})`,
53
+ })),
54
+ );
55
+
56
+ output[status] = {
57
+ description: exp.description ?? "",
58
+ content: {
59
+ "application/json": {
60
+ schema: coalesceSchema(props)(result),
61
+ },
62
+ },
63
+ };
64
+ }
65
+
66
+ // FROM COMMENT TAGS -> ANY
67
+ for (const tag of route.tags) {
68
+ if (tag.name !== "throw" && tag.name !== "throws") continue;
69
+
70
+ const text: string | undefined = tag.text?.find(
71
+ (elem) => elem.kind === "text",
72
+ )?.text;
73
+ if (text === undefined) continue;
74
+
75
+ const elements: string[] = text
76
+ .split(" ")
77
+ .map((str) => str.trim());
78
+ const status: string = elements[0];
79
+ if (
80
+ isNaN(Number(status)) &&
81
+ status !== "2XX" &&
82
+ status !== "3XX" &&
83
+ status !== "4XX" &&
84
+ status !== "5XX"
85
+ )
86
+ continue;
87
+
88
+ const description: string | undefined =
89
+ elements.length === 1
90
+ ? undefined
91
+ : elements.slice(1).join(" ");
92
+ const oldbie = output[status];
93
+ if (description && oldbie !== undefined)
94
+ oldbie.description = description;
95
+ else if (oldbie === undefined)
96
+ output[status] = {
97
+ description: description ?? "",
98
+ content: {
99
+ "application/json": {
100
+ schema: {},
101
+ },
102
+ },
103
+ };
104
+ }
105
+
106
+ //----
107
+ // SUCCESS
108
+ //----
109
+ // STATUS
110
+ const status: string =
111
+ route.status !== undefined
112
+ ? String(route.status)
113
+ : route.method === "GET" ||
114
+ route.method === "HEAD" ||
115
+ route.method === "DELETE"
116
+ ? "200"
117
+ : "201";
118
+
119
+ // SCHEMA
120
+ const result = MetadataFactory.analyze(props.checker)({
121
+ escape: true,
122
+ constant: true,
123
+ absorb: false,
124
+ validate: (meta) => {
125
+ const bigint: boolean =
126
+ meta.atomics.some((a) => a.type === "bigint") ||
127
+ meta.constants.some((a) => a.type === "bigint");
128
+ return bigint ? ["bigint type is not allowed."] : [];
129
+ },
130
+ })(props.collection)(route.output.type);
131
+ if (result.success === false)
132
+ props.errors.push(
133
+ ...result.errors.map((e) => ({
134
+ ...e,
135
+ route,
136
+ from: `response body (status: ${status})`,
137
+ })),
138
+ );
139
+
140
+ // DO ASSIGN
141
+ const description =
142
+ describe(route, "return") ?? describe(route, "returns");
143
+ output[status] = {
144
+ description: route.encrypted
145
+ ? `${warning
146
+ .get(!!description)
147
+ .get("response", route.method)}${description ?? ""}`
148
+ : description ?? "",
149
+ content:
150
+ route.output.typeName === "void"
151
+ ? undefined
152
+ : {
153
+ [route.output.contentType]: {
154
+ schema: coalesceSchema(props)(result),
155
+ },
156
+ },
157
+ "x-nestia-encrypted": route.encrypted,
158
+ };
159
+ return output;
160
+ };
161
+
162
+ export const body =
163
+ (props: IProps) =>
164
+ (route: IRoute) =>
165
+ (param: IRoute.IParameter): ISwaggerRoute.IRequestBody => {
166
+ // ANALZE TYPE WITH VALIDATION
167
+ const result = MetadataFactory.analyze(props.checker)({
168
+ escape: true,
169
+ constant: true,
170
+ absorb: true,
171
+ validate: (meta) => {
172
+ const bigint: boolean =
173
+ meta.atomics.some((a) => a.type === "bigint") ||
174
+ meta.constants.some((a) => a.type === "bigint");
175
+ return bigint ? ["bigint type is not allowed."] : [];
176
+ },
177
+ })(props.collection)(param.type);
178
+ if (result.success === false)
179
+ props.errors.push(
180
+ ...result.errors.map((e) => ({
181
+ ...e,
182
+ route,
183
+ from: `body parameter ${param.name}`,
184
+ })),
185
+ );
186
+
187
+ // LIST UP PROPERTIES
188
+ const contentType =
189
+ param.custom && param.category === "body"
190
+ ? param.contentType
191
+ : "application/json";
192
+ const encrypted: boolean =
193
+ param.custom && param.category === "body" && param.encrypted;
194
+ const description: string | undefined = describe(
195
+ route,
196
+ "param",
197
+ param.name,
198
+ );
199
+
200
+ // RETURNS WITH LAZY CONSTRUCTION
201
+ const schema: IJsonSchema = coalesceSchema(props)(result);
202
+ return {
203
+ description: encrypted
204
+ ? `${warning.get(!!description).get("request")}${
205
+ description ?? ""
206
+ }`
207
+ : description,
208
+ content: {
209
+ [contentType]: {
210
+ schema,
211
+ },
212
+ },
213
+ required: true,
214
+ "x-nestia-encrypted": encrypted,
215
+ };
216
+ };
217
+
218
+ export const parameter =
219
+ (props: IProps) =>
220
+ (route: IRoute) =>
221
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter =>
222
+ param.category === "headers"
223
+ ? headers(props)(route)(param)
224
+ : param.category === "param"
225
+ ? path(props)(route)(param)
226
+ : query(props)(route)(param);
227
+
228
+ const headers =
229
+ (props: IProps) =>
230
+ (route: IRoute) =>
231
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter => {
232
+ // ANALYZE TYPE WITH VALIDATIONS
233
+ const result = (() => {
234
+ const result = MetadataFactory.analyze(props.checker)({
235
+ escape: false,
236
+ constant: true,
237
+ absorb: true,
238
+ })(props.collection)(param.type);
239
+ if (result.success === false)
240
+ props.errors.push(
241
+ ...result.errors.map((e) => ({
242
+ ...e,
243
+ route,
244
+ from: `header parameter ${param.name}`,
245
+ })),
246
+ );
247
+ else if (result.data.objects.length === 1) {
248
+ // WHEN OBJECT CASE, VALIDATE IT MORE DETAILY
249
+ const again = MetadataFactory.analyze(props.checker)({
250
+ escape: false,
251
+ constant: true,
252
+ absorb: true,
253
+ validate: SwaggerSchemaValidator.headers,
254
+ })(new MetadataCollection())(param.type);
255
+ if (again.success === false) return again;
256
+ }
257
+ return result;
258
+ })();
259
+
260
+ // RETURNS WITH LAZY CONSTRUCTION
261
+ return lazyParameterReturns(props)(route)("header")(param, result);
262
+ };
263
+
264
+ const path =
265
+ (props: IProps) =>
266
+ (route: IRoute) =>
267
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter => {
268
+ // ANALZE TYPE WITH VALIDATION
269
+ const result = MetadataFactory.analyze(props.checker)({
270
+ escape: false,
271
+ constant: true,
272
+ absorb: true,
273
+ validate: SwaggerSchemaValidator.path,
274
+ })(props.collection)(param.type);
275
+ if (result.success === false)
276
+ props.errors.push(
277
+ ...result.errors.map((e) => ({
278
+ ...e,
279
+ route,
280
+ from: `path parameter ${param.name}`,
281
+ })),
282
+ );
283
+
284
+ // RETURNS WITH LAZY CONSTRUCTION
285
+ return lazyParameterReturns(props)(route)("path")(param, result);
286
+ };
287
+
288
+ const query =
289
+ (props: IProps) =>
290
+ (route: IRoute) =>
291
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter => {
292
+ // ANALYZE TYPE WITH VALIDATIONS
293
+ const result = (() => {
294
+ const result = MetadataFactory.analyze(props.checker)({
295
+ escape: false,
296
+ constant: true,
297
+ absorb: true,
298
+ })(props.collection)(param.type);
299
+ if (result.success === false) {
300
+ props.errors.push(
301
+ ...result.errors.map((e) => ({
302
+ ...e,
303
+ route,
304
+ from: `query parameter ${param.name}`,
305
+ })),
306
+ );
307
+ } else if (result.data.objects.length === 1) {
308
+ // WHEN OBJECT CASE, VALIDATE IT MORE DETAILY
309
+ const again = MetadataFactory.analyze(props.checker)({
310
+ escape: false,
311
+ constant: true,
312
+ absorb: true,
313
+ validate: SwaggerSchemaValidator.query,
314
+ })(new MetadataCollection())(param.type);
315
+ if (again.success === false) return again;
316
+ }
317
+ return result;
318
+ })();
319
+
320
+ // RETURNS WITH LAZY CONSTRUCTION
321
+ return lazyParameterReturns(props)(route)("query")(param, result);
322
+ };
323
+
324
+ const coalesceSchema =
325
+ (props: IProps) =>
326
+ (
327
+ result: ValidationPipe<Metadata, MetadataFactory.IError>,
328
+ ): IJsonSchema => {
329
+ const schema: IJsonSchema = {} as any;
330
+ props.tuples.push({
331
+ metadata: result.success ? result.data : any.get(),
332
+ schema,
333
+ });
334
+ return schema;
335
+ };
336
+
337
+ const lazyParameterReturns =
338
+ (props: IProps) =>
339
+ (route: IRoute) =>
340
+ (inKeyword: string) =>
341
+ (
342
+ param: IRoute.IParameter,
343
+ result: ValidationPipe<Metadata, MetadataFactory.IError>,
344
+ ) => {
345
+ const schema: IJsonSchema = coalesceSchema(props)(result);
346
+ return {
347
+ name: param.field ?? param.name,
348
+ in: inKeyword,
349
+ schema,
350
+ description: describe(route, "param", param.name) ?? "",
351
+ required: result.success ? result.data.isRequired() : true,
352
+ };
353
+ };
354
+
355
+ function describe(
356
+ route: IRoute,
357
+ tagName: string,
358
+ parameterName?: string,
359
+ ): string | undefined {
360
+ const parametric: (elem: ts.JSDocTagInfo) => boolean = parameterName
361
+ ? (tag) =>
362
+ tag.text!.find(
363
+ (elem) =>
364
+ elem.kind === "parameterName" &&
365
+ elem.text === parameterName,
366
+ ) !== undefined
367
+ : () => true;
368
+
369
+ const tag: ts.JSDocTagInfo | undefined = route.tags.find(
370
+ (tag) => tag.name === tagName && tag.text && parametric(tag),
371
+ );
372
+ return tag && tag.text
373
+ ? tag.text.find((elem) => elem.kind === "text")?.text
374
+ : undefined;
375
+ }
376
+ }
377
+
378
+ const warning = new Singleton((described: boolean) => {
379
+ return new Singleton((type: "request" | "response", method?: string) => {
380
+ const summary =
381
+ type === "request"
382
+ ? "Request body must be encrypted."
383
+ : "Response data have been encrypted.";
384
+ const component =
385
+ type === "request"
386
+ ? "[EncryptedBody](https://github.com/samchon/@nestia/core#encryptedbody)"
387
+ : `[EncryptedRoute.${method![0].toUpperCase()}.${method!
388
+ .substring(1)
389
+ .toLowerCase()}](https://github.com/samchon/@nestia/core#encryptedroute)`;
390
+
391
+ const content: string[] = [
392
+ "## Warning",
393
+ "",
394
+ summary,
395
+ "",
396
+ `The ${type} body data would be encrypted as "AES-128(256) / CBC mode / PKCS#5 Padding / Base64 Encoding", through the ${component} component.`,
397
+ "",
398
+ `Therefore, just utilize this swagger editor only for referencing. If you need to call the real API, using [SDK](https://github.com/samchon/nestia#software-development-kit) would be much better.`,
399
+ ];
400
+ if (described === true) content.push("----------------", "");
401
+ return content.join("\n");
402
+ });
403
+ });
404
+
405
+ const any = new Singleton(() =>
406
+ Metadata.from(
407
+ {
408
+ any: true,
409
+ required: true,
410
+ optional: false,
411
+ nullable: false,
412
+ functional: false,
413
+ atomics: [],
414
+ constants: [],
415
+ templates: [],
416
+ escaped: null,
417
+ rest: null,
418
+ arrays: [],
419
+ tuples: [],
420
+ objects: [],
421
+ aliases: [],
422
+ natives: [],
423
+ sets: [],
424
+ maps: [],
425
+ },
426
+ {
427
+ aliases: [],
428
+ arrays: [],
429
+ tuples: [],
430
+ objects: [],
431
+ },
432
+ ),
433
+ );