@nestia/sdk 2.6.3-dev.20240328 → 2.6.4-dev.20240401

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