@nestia/sdk 3.0.0 → 3.0.1

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