@nestia/sdk 2.5.4 → 2.5.5-dev.20240213

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.
@@ -1,440 +1,440 @@
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 { Metadata } from "typia/lib/schemas/metadata/Metadata";
7
- import { ValidationPipe } from "typia/lib/typings/ValidationPipe";
8
-
9
- import { INestiaConfig } from "../../INestiaConfig";
10
- import { IRoute } from "../../structures/IRoute";
11
- import { ISwaggerError } from "../../structures/ISwaggerError";
12
- import { ISwaggerLazyProperty } from "../../structures/ISwaggerLazyProperty";
13
- import { ISwaggerLazySchema } from "../../structures/ISwaggerLazySchema";
14
- import { ISwaggerRoute } from "../../structures/ISwaggerRoute";
15
- import { SwaggerSchemaValidator } from "./SwaggerSchemaValidator";
16
- import { JsonApplicationProgrammer } from "typia/lib/programmers/json/JsonApplicationProgrammer";
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 === "GET" ||
107
- route.method === "HEAD" ||
108
- route.method === "DELETE"
109
- ? "200"
110
- : "201";
111
-
112
- // SCHEMA
113
- const result = MetadataFactory.analyze(props.checker)({
114
- escape: true,
115
- constant: true,
116
- absorb: false,
117
- validate: (meta) => {
118
- const bigint: boolean =
119
- meta.atomics.some((a) => a.type === "bigint") ||
120
- meta.constants.some((a) => a.type === "bigint");
121
- return bigint ? ["bigint type is not allowed."] : [];
122
- },
123
- })(props.collection)(route.output.type);
124
- if (result.success === false)
125
- props.errors.push(
126
- ...result.errors.map((e) => ({
127
- ...e,
128
- route,
129
- from: "response",
130
- })),
131
- );
132
-
133
- // DO ASSIGN
134
- const description =
135
- describe(route, "return") ?? describe(route, "returns");
136
- output[status] = {
137
- description: route.encrypted
138
- ? `${warning.get(!!description, "response", route.method)}${
139
- description ?? ""
140
- }`
141
- : description ?? "",
142
- content:
143
- route.output.typeName === "void"
144
- ? undefined
145
- : {
146
- [route.output.contentType]: {
147
- schema: coalesce(props)(result),
148
- },
149
- },
150
- ...(props.config.additional === true
151
- ? {
152
- "x-nestia-encrypted": route.encrypted,
153
- }
154
- : route.encrypted === true
155
- ? {
156
- "x-nestia-encrypted": true,
157
- }
158
- : {}),
159
- };
160
- return output;
161
- };
162
-
163
- export const body =
164
- (props: IProps) =>
165
- (route: IRoute) =>
166
- (param: IRoute.IParameter): ISwaggerRoute.IRequestBody => {
167
- // ANALZE TYPE WITH VALIDATION
168
- const result = MetadataFactory.analyze(props.checker)({
169
- escape: true,
170
- constant: true,
171
- absorb: true,
172
- validate: (meta) => {
173
- const bigint: boolean =
174
- meta.atomics.some((a) => a.type === "bigint") ||
175
- meta.constants.some((a) => a.type === "bigint");
176
- return bigint ? ["bigint type is not allowed."] : [];
177
- },
178
- })(props.collection)(param.type);
179
- if (result.success === false)
180
- props.errors.push(
181
- ...result.errors.map((e) => ({
182
- ...e,
183
- route,
184
- from: param.name,
185
- })),
186
- );
187
-
188
- // LIST UP PROPERTIES
189
- const contentType =
190
- param.custom && param.category === "body"
191
- ? param.contentType
192
- : "application/json";
193
- const encrypted: boolean =
194
- param.custom && param.category === "body" && param.encrypted;
195
- const description: string | undefined = describe(
196
- route,
197
- "param",
198
- param.name,
199
- );
200
-
201
- // RETURNS WITH LAZY CONSTRUCTION
202
- const schema: IJsonSchema = coalesce(props)(result);
203
- return {
204
- description: encrypted
205
- ? `${warning.get(!!description, "request")}${description ?? ""}`
206
- : description,
207
- content: {
208
- [contentType]: {
209
- schema,
210
- },
211
- },
212
- required: true,
213
- ...(props.config.additional === true
214
- ? {
215
- "x-nestia-encrypted": encrypted,
216
- }
217
- : encrypted === true
218
- ? {
219
- "x-nestia-encrypted": true,
220
- }
221
- : {}),
222
- };
223
- };
224
-
225
- export const parameter =
226
- (props: IProps) =>
227
- (route: IRoute) =>
228
- (param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
229
- param.category === "headers"
230
- ? headers(props)(route)(param)
231
- : param.category === "param"
232
- ? [path(props)(route)(param)]
233
- : query(props)(route)(param);
234
-
235
- const path =
236
- (props: IProps) =>
237
- (route: IRoute) =>
238
- (param: IRoute.IParameter): ISwaggerRoute.IParameter => {
239
- // ANALZE TYPE WITH VALIDATION
240
- const result = MetadataFactory.analyze(props.checker)({
241
- escape: false,
242
- constant: true,
243
- absorb: true,
244
- validate: SwaggerSchemaValidator.path,
245
- })(props.collection)(param.type);
246
- if (result.success === false)
247
- props.errors.push(
248
- ...result.errors.map((e) => ({
249
- ...e,
250
- route,
251
- from: param.name,
252
- })),
253
- );
254
-
255
- // RETURNS WITH LAZY CONSTRUCTION
256
- return lazy(props)(route)(param, result);
257
- };
258
-
259
- const headers =
260
- (props: IProps) =>
261
- (route: IRoute) =>
262
- (param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
263
- decomposible(props)(route)(param)(
264
- MetadataFactory.analyze(props.checker)({
265
- escape: false,
266
- constant: true,
267
- absorb: true,
268
- validate: param.custom ? SwaggerSchemaValidator.headers : undefined,
269
- })(props.collection)(param.type),
270
- );
271
-
272
- const query =
273
- (props: IProps) =>
274
- (route: IRoute) =>
275
- (param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
276
- decomposible(props)(route)(param)(
277
- MetadataFactory.analyze(props.checker)({
278
- escape: false,
279
- constant: true,
280
- absorb: true,
281
- validate: param.custom ? SwaggerSchemaValidator.query : undefined,
282
- })(props.collection)(param.type),
283
- );
284
-
285
- const decomposible =
286
- (props: IProps) =>
287
- (route: IRoute) =>
288
- (param: IRoute.IParameter) =>
289
- (
290
- result: ValidationPipe<Metadata, MetadataFactory.IError>,
291
- ): ISwaggerRoute.IParameter[] => {
292
- const decoded: ISwaggerRoute.IParameter = lazy(props)(route)(
293
- param,
294
- result,
295
- );
296
- if (result.success === false) {
297
- props.errors.push(
298
- ...result.errors.map((e) => ({
299
- ...e,
300
- route,
301
- from: param.name,
302
- })),
303
- );
304
- return [decoded];
305
- } else if (
306
- props.config.decompose !== true ||
307
- result.data.objects.length === 0
308
- )
309
- return [decoded];
310
-
311
- return result.data.objects[0].properties
312
- .filter((p) => p.jsDocTags.every((tag) => tag.name !== "hidden"))
313
- .map((p) => {
314
- const schema: IJsonSchema = {};
315
- props.lazyProperties.push({
316
- schema,
317
- object: result.data.objects[0].name,
318
- property: p.key.constants[0].values[0] as string,
319
- });
320
- return {
321
- name: p.key.constants[0].values[0] as string,
322
- in: param.category === "headers" ? "header" : param.category,
323
- schema,
324
- description: p.description ?? undefined,
325
- required: p.value.isRequired(),
326
- };
327
- });
328
- };
329
-
330
- const lazy =
331
- (props: IProps) =>
332
- (route: IRoute) =>
333
- (
334
- param: IRoute.IParameter,
335
- result: ValidationPipe<Metadata, MetadataFactory.IError>,
336
- ): ISwaggerRoute.IParameter => {
337
- const schema: IJsonSchema = coalesce(props)(result);
338
- return {
339
- name: param.field ?? param.name,
340
- in:
341
- param.category === "headers"
342
- ? "header"
343
- : param.category === "param"
344
- ? "path"
345
- : param.category,
346
- schema,
347
- description: describe(route, "param", param.name) ?? "",
348
- required: result.success ? result.data.isRequired() : true,
349
- };
350
- };
351
-
352
- const coalesce =
353
- (props: IProps) =>
354
- (result: ValidationPipe<Metadata, MetadataFactory.IError>): IJsonSchema => {
355
- const schema: IJsonSchema = {} as any;
356
- props.lazySchemas.push({
357
- metadata: result.success ? result.data : any.get(),
358
- schema,
359
- });
360
- return schema;
361
- };
362
-
363
- const describe = (
364
- route: IRoute,
365
- tagName: string,
366
- parameterName?: string,
367
- ): string | undefined => {
368
- const parametric: (elem: ts.JSDocTagInfo) => boolean = parameterName
369
- ? (tag) =>
370
- tag.text!.find(
371
- (elem) =>
372
- elem.kind === "parameterName" && elem.text === parameterName,
373
- ) !== undefined
374
- : () => true;
375
-
376
- const tag: ts.JSDocTagInfo | undefined = route.jsDocTags.find(
377
- (tag) => tag.name === tagName && tag.text && parametric(tag),
378
- );
379
- return tag && tag.text
380
- ? tag.text.find((elem) => elem.kind === "text")?.text
381
- : undefined;
382
- };
383
- }
384
-
385
- const warning = new VariadicSingleton(
386
- (described: boolean, type: "request" | "response", method?: string) => {
387
- const summary =
388
- type === "request"
389
- ? "Request body must be encrypted."
390
- : "Response data have been encrypted.";
391
- const component =
392
- type === "request"
393
- ? "[EncryptedBody](https://github.com/samchon/@nestia/core#encryptedbody)"
394
- : `[EncryptedRoute.${method![0].toUpperCase()}.${method!
395
- .substring(1)
396
- .toLowerCase()}](https://github.com/samchon/@nestia/core#encryptedroute)`;
397
-
398
- const content: string[] = [
399
- "## Warning",
400
- "",
401
- summary,
402
- "",
403
- `The ${type} body data would be encrypted as "AES-128(256) / CBC mode / PKCS#5 Padding / Base64 Encoding", through the ${component} component.`,
404
- "",
405
- `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.`,
406
- ];
407
- if (described === true) content.push("", "----------------", "", "");
408
- return content.join("\n");
409
- },
410
- );
411
-
412
- const any = new Singleton(() =>
413
- Metadata.from(
414
- {
415
- any: true,
416
- required: true,
417
- optional: false,
418
- nullable: false,
419
- functional: false,
420
- atomics: [],
421
- constants: [],
422
- templates: [],
423
- escaped: null,
424
- rest: null,
425
- arrays: [],
426
- tuples: [],
427
- objects: [],
428
- aliases: [],
429
- natives: [],
430
- sets: [],
431
- maps: [],
432
- },
433
- {
434
- aliases: new Map(),
435
- arrays: new Map(),
436
- tuples: new Map(),
437
- objects: new Map(),
438
- },
439
- ),
440
- );
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 { Metadata } from "typia/lib/schemas/metadata/Metadata";
7
+ import { ValidationPipe } from "typia/lib/typings/ValidationPipe";
8
+
9
+ import { INestiaConfig } from "../../INestiaConfig";
10
+ import { IRoute } from "../../structures/IRoute";
11
+ import { ISwaggerError } from "../../structures/ISwaggerError";
12
+ import { ISwaggerLazyProperty } from "../../structures/ISwaggerLazyProperty";
13
+ import { ISwaggerLazySchema } from "../../structures/ISwaggerLazySchema";
14
+ import { ISwaggerRoute } from "../../structures/ISwaggerRoute";
15
+ import { SwaggerSchemaValidator } from "./SwaggerSchemaValidator";
16
+ import { JsonApplicationProgrammer } from "typia/lib/programmers/json/JsonApplicationProgrammer";
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 === "GET" ||
107
+ route.method === "HEAD" ||
108
+ route.method === "DELETE"
109
+ ? "200"
110
+ : "201";
111
+
112
+ // SCHEMA
113
+ const result = MetadataFactory.analyze(props.checker)({
114
+ escape: true,
115
+ constant: true,
116
+ absorb: false,
117
+ validate: (meta) => {
118
+ const bigint: boolean =
119
+ meta.atomics.some((a) => a.type === "bigint") ||
120
+ meta.constants.some((a) => a.type === "bigint");
121
+ return bigint ? ["bigint type is not allowed."] : [];
122
+ },
123
+ })(props.collection)(route.output.type);
124
+ if (result.success === false)
125
+ props.errors.push(
126
+ ...result.errors.map((e) => ({
127
+ ...e,
128
+ route,
129
+ from: "response",
130
+ })),
131
+ );
132
+
133
+ // DO ASSIGN
134
+ const description =
135
+ describe(route, "return") ?? describe(route, "returns");
136
+ output[status] = {
137
+ description: route.encrypted
138
+ ? `${warning.get(!!description, "response", route.method)}${
139
+ description ?? ""
140
+ }`
141
+ : description ?? "",
142
+ content:
143
+ route.output.typeName === "void"
144
+ ? undefined
145
+ : {
146
+ [route.output.contentType]: {
147
+ schema: coalesce(props)(result),
148
+ },
149
+ },
150
+ ...(props.config.additional === true
151
+ ? {
152
+ "x-nestia-encrypted": route.encrypted,
153
+ }
154
+ : route.encrypted === true
155
+ ? {
156
+ "x-nestia-encrypted": true,
157
+ }
158
+ : {}),
159
+ };
160
+ return output;
161
+ };
162
+
163
+ export const body =
164
+ (props: IProps) =>
165
+ (route: IRoute) =>
166
+ (param: IRoute.IParameter): ISwaggerRoute.IRequestBody => {
167
+ // ANALZE TYPE WITH VALIDATION
168
+ const result = MetadataFactory.analyze(props.checker)({
169
+ escape: true,
170
+ constant: true,
171
+ absorb: true,
172
+ validate: (meta) => {
173
+ const bigint: boolean =
174
+ meta.atomics.some((a) => a.type === "bigint") ||
175
+ meta.constants.some((a) => a.type === "bigint");
176
+ return bigint ? ["bigint type is not allowed."] : [];
177
+ },
178
+ })(props.collection)(param.type);
179
+ if (result.success === false)
180
+ props.errors.push(
181
+ ...result.errors.map((e) => ({
182
+ ...e,
183
+ route,
184
+ from: param.name,
185
+ })),
186
+ );
187
+
188
+ // LIST UP PROPERTIES
189
+ const contentType =
190
+ param.custom && param.category === "body"
191
+ ? param.contentType
192
+ : "application/json";
193
+ const encrypted: boolean =
194
+ param.custom && param.category === "body" && param.encrypted;
195
+ const description: string | undefined = describe(
196
+ route,
197
+ "param",
198
+ param.name,
199
+ );
200
+
201
+ // RETURNS WITH LAZY CONSTRUCTION
202
+ const schema: IJsonSchema = coalesce(props)(result);
203
+ return {
204
+ description: encrypted
205
+ ? `${warning.get(!!description, "request")}${description ?? ""}`
206
+ : description,
207
+ content: {
208
+ [contentType]: {
209
+ schema,
210
+ },
211
+ },
212
+ required: true,
213
+ ...(props.config.additional === true
214
+ ? {
215
+ "x-nestia-encrypted": encrypted,
216
+ }
217
+ : encrypted === true
218
+ ? {
219
+ "x-nestia-encrypted": true,
220
+ }
221
+ : {}),
222
+ };
223
+ };
224
+
225
+ export const parameter =
226
+ (props: IProps) =>
227
+ (route: IRoute) =>
228
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
229
+ param.category === "headers"
230
+ ? headers(props)(route)(param)
231
+ : param.category === "param"
232
+ ? [path(props)(route)(param)]
233
+ : query(props)(route)(param);
234
+
235
+ const path =
236
+ (props: IProps) =>
237
+ (route: IRoute) =>
238
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter => {
239
+ // ANALZE TYPE WITH VALIDATION
240
+ const result = MetadataFactory.analyze(props.checker)({
241
+ escape: false,
242
+ constant: true,
243
+ absorb: true,
244
+ validate: SwaggerSchemaValidator.path,
245
+ })(props.collection)(param.type);
246
+ if (result.success === false)
247
+ props.errors.push(
248
+ ...result.errors.map((e) => ({
249
+ ...e,
250
+ route,
251
+ from: param.name,
252
+ })),
253
+ );
254
+
255
+ // RETURNS WITH LAZY CONSTRUCTION
256
+ return lazy(props)(route)(param, result);
257
+ };
258
+
259
+ const headers =
260
+ (props: IProps) =>
261
+ (route: IRoute) =>
262
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
263
+ decomposible(props)(route)(param)(
264
+ MetadataFactory.analyze(props.checker)({
265
+ escape: false,
266
+ constant: true,
267
+ absorb: true,
268
+ validate: param.custom ? SwaggerSchemaValidator.headers : undefined,
269
+ })(props.collection)(param.type),
270
+ );
271
+
272
+ const query =
273
+ (props: IProps) =>
274
+ (route: IRoute) =>
275
+ (param: IRoute.IParameter): ISwaggerRoute.IParameter[] =>
276
+ decomposible(props)(route)(param)(
277
+ MetadataFactory.analyze(props.checker)({
278
+ escape: false,
279
+ constant: true,
280
+ absorb: true,
281
+ validate: param.custom ? SwaggerSchemaValidator.query : undefined,
282
+ })(props.collection)(param.type),
283
+ );
284
+
285
+ const decomposible =
286
+ (props: IProps) =>
287
+ (route: IRoute) =>
288
+ (param: IRoute.IParameter) =>
289
+ (
290
+ result: ValidationPipe<Metadata, MetadataFactory.IError>,
291
+ ): ISwaggerRoute.IParameter[] => {
292
+ const decoded: ISwaggerRoute.IParameter = lazy(props)(route)(
293
+ param,
294
+ result,
295
+ );
296
+ if (result.success === false) {
297
+ props.errors.push(
298
+ ...result.errors.map((e) => ({
299
+ ...e,
300
+ route,
301
+ from: param.name,
302
+ })),
303
+ );
304
+ return [decoded];
305
+ } else if (
306
+ props.config.decompose !== true ||
307
+ result.data.objects.length === 0
308
+ )
309
+ return [decoded];
310
+
311
+ return result.data.objects[0].properties
312
+ .filter((p) => p.jsDocTags.every((tag) => tag.name !== "hidden"))
313
+ .map((p) => {
314
+ const schema: IJsonSchema = {};
315
+ props.lazyProperties.push({
316
+ schema,
317
+ object: result.data.objects[0].name,
318
+ property: p.key.constants[0].values[0] as string,
319
+ });
320
+ return {
321
+ name: p.key.constants[0].values[0] as string,
322
+ in: param.category === "headers" ? "header" : param.category,
323
+ schema,
324
+ description: p.description ?? undefined,
325
+ required: p.value.isRequired(),
326
+ };
327
+ });
328
+ };
329
+
330
+ const lazy =
331
+ (props: IProps) =>
332
+ (route: IRoute) =>
333
+ (
334
+ param: IRoute.IParameter,
335
+ result: ValidationPipe<Metadata, MetadataFactory.IError>,
336
+ ): ISwaggerRoute.IParameter => {
337
+ const schema: IJsonSchema = coalesce(props)(result);
338
+ return {
339
+ name: param.field ?? param.name,
340
+ in:
341
+ param.category === "headers"
342
+ ? "header"
343
+ : param.category === "param"
344
+ ? "path"
345
+ : param.category,
346
+ schema,
347
+ description: describe(route, "param", param.name) ?? "",
348
+ required: result.success ? result.data.isRequired() : true,
349
+ };
350
+ };
351
+
352
+ const coalesce =
353
+ (props: IProps) =>
354
+ (result: ValidationPipe<Metadata, MetadataFactory.IError>): IJsonSchema => {
355
+ const schema: IJsonSchema = {} as any;
356
+ props.lazySchemas.push({
357
+ metadata: result.success ? result.data : any.get(),
358
+ schema,
359
+ });
360
+ return schema;
361
+ };
362
+
363
+ const describe = (
364
+ route: IRoute,
365
+ tagName: string,
366
+ parameterName?: string,
367
+ ): string | undefined => {
368
+ const parametric: (elem: ts.JSDocTagInfo) => boolean = parameterName
369
+ ? (tag) =>
370
+ tag.text!.find(
371
+ (elem) =>
372
+ elem.kind === "parameterName" && elem.text === parameterName,
373
+ ) !== undefined
374
+ : () => true;
375
+
376
+ const tag: ts.JSDocTagInfo | undefined = route.jsDocTags.find(
377
+ (tag) => tag.name === tagName && tag.text && parametric(tag),
378
+ );
379
+ return tag && tag.text
380
+ ? tag.text.find((elem) => elem.kind === "text")?.text
381
+ : undefined;
382
+ };
383
+ }
384
+
385
+ const warning = new VariadicSingleton(
386
+ (described: boolean, type: "request" | "response", method?: string) => {
387
+ const summary =
388
+ type === "request"
389
+ ? "Request body must be encrypted."
390
+ : "Response data have been encrypted.";
391
+ const component =
392
+ type === "request"
393
+ ? "[EncryptedBody](https://github.com/samchon/@nestia/core#encryptedbody)"
394
+ : `[EncryptedRoute.${method![0].toUpperCase()}.${method!
395
+ .substring(1)
396
+ .toLowerCase()}](https://github.com/samchon/@nestia/core#encryptedroute)`;
397
+
398
+ const content: string[] = [
399
+ "## Warning",
400
+ "",
401
+ summary,
402
+ "",
403
+ `The ${type} body data would be encrypted as "AES-128(256) / CBC mode / PKCS#5 Padding / Base64 Encoding", through the ${component} component.`,
404
+ "",
405
+ `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.`,
406
+ ];
407
+ if (described === true) content.push("", "----------------", "", "");
408
+ return content.join("\n");
409
+ },
410
+ );
411
+
412
+ const any = new Singleton(() =>
413
+ Metadata.from(
414
+ {
415
+ any: true,
416
+ required: true,
417
+ optional: false,
418
+ nullable: false,
419
+ functional: false,
420
+ atomics: [],
421
+ constants: [],
422
+ templates: [],
423
+ escaped: null,
424
+ rest: null,
425
+ arrays: [],
426
+ tuples: [],
427
+ objects: [],
428
+ aliases: [],
429
+ natives: [],
430
+ sets: [],
431
+ maps: [],
432
+ },
433
+ {
434
+ aliases: new Map(),
435
+ arrays: new Map(),
436
+ tuples: new Map(),
437
+ objects: new Map(),
438
+ },
439
+ ),
440
+ );