@nestia/migrate 0.13.0 → 0.13.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.
Files changed (31) hide show
  1. package/lib/MigrateApplication.js +45 -9
  2. package/lib/MigrateApplication.js.map +1 -1
  3. package/lib/analyzers/MigrateMethodAnalyzer.js +19 -3
  4. package/lib/analyzers/MigrateMethodAnalyzer.js.map +1 -1
  5. package/lib/bundles/NEST_TEMPLATE.js +2 -2
  6. package/lib/bundles/SDK_TEMPLATE.js +1 -1
  7. package/lib/internal/MigrateCommander.js.map +1 -1
  8. package/lib/programmers/MigrateSchemaProgrammer.js +3 -4
  9. package/lib/programmers/MigrateSchemaProgrammer.js.map +1 -1
  10. package/package.json +79 -79
  11. package/src/analyzers/MigrateControllerAnalyzer.ts +137 -137
  12. package/src/analyzers/MigrateMethodAnalyzer.ts +383 -363
  13. package/src/bundles/NEST_TEMPLATE.ts +2 -2
  14. package/src/bundles/SDK_TEMPLATE.ts +1 -1
  15. package/src/internal/MigrateCommander.ts +76 -70
  16. package/src/module.ts +8 -8
  17. package/src/programmers/MigrateApiFileProgrammer.ts +53 -53
  18. package/src/programmers/MigrateApiFunctionProgrammer.ts +199 -199
  19. package/src/programmers/MigrateApiNamespaceProgrammer.ts +431 -431
  20. package/src/programmers/MigrateApiProgrammer.ts +172 -172
  21. package/src/programmers/MigrateApiSimulatationProgrammer.ts +327 -327
  22. package/src/programmers/MigrateDtoProgrammer.ts +77 -77
  23. package/src/programmers/MigrateE2eFileProgrammer.ts +117 -117
  24. package/src/programmers/MigrateE2eProgrammer.ts +36 -36
  25. package/src/programmers/MigrateNestControllerProgrammer.ts +50 -50
  26. package/src/programmers/MigrateNestMethodProgrammer.ts +249 -249
  27. package/src/programmers/MigrateNestProgrammer.ts +74 -74
  28. package/src/structures/IMigrateDto.ts +8 -8
  29. package/src/structures/IMigrateProgram.ts +27 -27
  30. package/src/structures/IMigrateRoute.ts +51 -51
  31. package/src/utils/OpenApiTypeChecker.ts +73 -73
@@ -1,363 +1,383 @@
1
- import { OpenApi } from "@samchon/openapi";
2
- import { Escaper } from "typia/lib/utils/Escaper";
3
-
4
- import { IMigrateProgram } from "../structures/IMigrateProgram";
5
- import { IMigrateRoute } from "../structures/IMigrateRoute";
6
- import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker";
7
- import { StringUtil } from "../utils/StringUtil";
8
-
9
- export namespace MigrateMethodAnalzyer {
10
- export const analyze =
11
- (props: Omit<IMigrateProgram.IProps, "dictionary">) =>
12
- (endpoint: { path: string; method: string }) =>
13
- (route: OpenApi.IOperation): IMigrateRoute | null => {
14
- const body = emplaceBodySchema("request")(
15
- emplaceReference(props.document)("body"),
16
- )(route.requestBody);
17
- const success = emplaceBodySchema("response")(
18
- emplaceReference(props.document)("response"),
19
- )(
20
- route.responses?.["201"] ??
21
- route.responses?.["200"] ??
22
- route.responses?.default,
23
- );
24
-
25
- const failures: string[] = [];
26
- if (body === false)
27
- failures.push(
28
- `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
29
- );
30
- if (success === false)
31
- failures.push(
32
- `supports only "application/json", "application/x-www-form-urlencoded" and "text/plain" content type in the response body.`,
33
- );
34
- if (SUPPORTED_METHODS.has(endpoint.method.toUpperCase()) === false)
35
- failures.push(
36
- `does not support ${endpoint.method.toUpperCase()} method.`,
37
- );
38
-
39
- const [headers, query] = ["header", "query"].map((type) => {
40
- const parameters: OpenApi.IOperation.IParameter[] = (
41
- route.parameters ?? []
42
- ).filter((p) => p.in === type);
43
- if (parameters.length === 0) return null;
44
-
45
- const objects = parameters
46
- .map((p) =>
47
- OpenApiTypeChecker.isObject(p.schema)
48
- ? p.schema
49
- : OpenApiTypeChecker.isReference(p.schema) &&
50
- OpenApiTypeChecker.isObject(
51
- props.document.components.schemas[
52
- p.schema.$ref.replace(`#/components/schemas/`, ``)
53
- ] ?? {},
54
- )
55
- ? p.schema
56
- : null!,
57
- )
58
- .filter((s) => !!s);
59
- const primitives = parameters.filter(
60
- (p) =>
61
- OpenApiTypeChecker.isBoolean(p.schema) ||
62
- OpenApiTypeChecker.isInteger(p.schema) ||
63
- OpenApiTypeChecker.isNumber(p.schema) ||
64
- OpenApiTypeChecker.isString(p.schema) ||
65
- OpenApiTypeChecker.isArray(p.schema),
66
- );
67
- if (objects.length === 1 && primitives.length === 0) return objects[0];
68
- else if (objects.length > 1) {
69
- failures.push(
70
- `${type} typed parameters must be only one object type`,
71
- );
72
- return false;
73
- }
74
-
75
- const dto: OpenApi.IJsonSchema.IObject | null = objects[0]
76
- ? OpenApiTypeChecker.isObject(objects[0])
77
- ? objects[0]
78
- : ((props.document.components.schemas ?? {})[
79
- (objects[0] as OpenApi.IJsonSchema.IReference).$ref.replace(
80
- `#/components/schemas/`,
81
- ``,
82
- )
83
- ] as OpenApi.IJsonSchema.IObject)
84
- : null;
85
- const entire: OpenApi.IJsonSchema.IObject[] = [
86
- ...objects.map((o) =>
87
- OpenApiTypeChecker.isObject(o)
88
- ? o
89
- : (props.document.components.schemas?.[
90
- o.$ref.replace(`#/components/schemas/`, ``)
91
- ]! as OpenApi.IJsonSchema.IObject),
92
- ),
93
- {
94
- type: "object",
95
- properties: Object.fromEntries([
96
- ...primitives.map((p) => [
97
- p.name,
98
- {
99
- ...p.schema,
100
- description: p.schema.description ?? p.description,
101
- },
102
- ]),
103
- ...(dto ? Object.entries(dto.properties ?? {}) : []),
104
- ]),
105
- required: [
106
- ...primitives.filter((p) => p.required).map((p) => p.name!),
107
- ...(dto ? dto.required ?? [] : []),
108
- ],
109
- },
110
- ];
111
- return parameters.length === 0
112
- ? null
113
- : emplaceReference(props.document)(
114
- StringUtil.pascal(`I/Api/${endpoint.path}`) +
115
- "." +
116
- StringUtil.pascal(`${endpoint.method}/${type}`),
117
- )({
118
- type: "object",
119
- properties: Object.fromEntries([
120
- ...new Map<string, OpenApi.IJsonSchema>(
121
- entire
122
- .map((o) =>
123
- Object.entries(o.properties ?? {}).map(
124
- ([name, schema]) =>
125
- [
126
- name,
127
- {
128
- ...schema,
129
- description:
130
- schema.description ?? schema.description,
131
- } as OpenApi.IJsonSchema,
132
- ] as const,
133
- ),
134
- )
135
- .flat(),
136
- ),
137
- ]),
138
- required: [
139
- ...new Set(entire.map((o) => o.required ?? []).flat()),
140
- ],
141
- });
142
- });
143
-
144
- const parameterNames: string[] = StringUtil.splitWithNormalization(
145
- endpoint.path,
146
- )
147
- .filter((str) => str[0] === "{" || str[0] === ":")
148
- .map((str) =>
149
- str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
150
- );
151
- if (
152
- parameterNames.length !==
153
- route.parameters.filter((p) => p.in === "path").length
154
- )
155
- failures.push(
156
- "number of path parameters are not matched with its full path.",
157
- );
158
-
159
- if (failures.length) {
160
- console.log(
161
- `Failed to migrate ${endpoint.method.toUpperCase()} ${endpoint.path}`,
162
- ...failures.map((f) => ` - ${f}`),
163
- );
164
- return null;
165
- }
166
- return {
167
- name: "@lazy",
168
- originalPath: endpoint.path,
169
- path: endpoint.path,
170
- method: endpoint.method,
171
- accessor: ["@lazy"],
172
- headers: headers
173
- ? {
174
- name: "headers",
175
- key: "headers",
176
- schema: headers,
177
- }
178
- : null,
179
- parameters: route.parameters
180
- .filter((p) => p.in === "path")
181
- .map((p, i) => ({
182
- name: parameterNames[i],
183
- key: (() => {
184
- let key: string = StringUtil.normalize(parameterNames[i]);
185
- if (Escaper.variable(key)) return key;
186
- while (true) {
187
- key = "_" + key;
188
- if (!parameterNames.some((s) => s === key)) return key;
189
- }
190
- })(),
191
- schema: {
192
- ...p!.schema,
193
- description: p!.schema.description ?? p!.description,
194
- },
195
- })),
196
- query: query
197
- ? {
198
- name: "query",
199
- key: "query",
200
- schema: query,
201
- }
202
- : null,
203
- body: body as IMigrateRoute.IBody | null,
204
- success: success as IMigrateRoute.IBody | null,
205
- exceptions: Object.fromEntries(
206
- Object.entries(route.responses ?? {})
207
- .filter(
208
- ([key]) => key !== "200" && key !== "201" && key !== "default",
209
- )
210
- .map(([key, value]) => [
211
- key,
212
- {
213
- description: value.description,
214
- schema: value.content?.["application/json"]?.schema ?? {},
215
- },
216
- ]),
217
- ),
218
- deprecated: route.deprecated ?? false,
219
- comment: () => describe(route),
220
- tags: route.tags ?? [],
221
- };
222
- };
223
-
224
- const describe = (route: OpenApi.IOperation): string => {
225
- const commentTags: string[] = [];
226
- const add = (text: string) => {
227
- if (commentTags.every((line) => line !== text)) commentTags.push(text);
228
- };
229
-
230
- let description: string = route.description ?? "";
231
- if (route.summary) {
232
- const emended: string = route.summary.endsWith(".")
233
- ? route.summary
234
- : route.summary + ".";
235
- if (!!description.length && !description.startsWith(route.summary))
236
- description = `${emended}\n${description}`;
237
- }
238
- for (const p of route.parameters)
239
- if (p.description) add(`@param ${p.name} ${p.description}`);
240
- if (route.requestBody?.description)
241
- add(`@param body ${route.requestBody.description}`);
242
- for (const security of route.security ?? [])
243
- for (const [name, scopes] of Object.entries(security))
244
- add(`@security ${[name, ...scopes].join("")}`);
245
- if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
246
- if (route.deprecated) add("@deprecated");
247
- return description.length
248
- ? commentTags.length
249
- ? `${description}\n\n${commentTags.join("\n")}`
250
- : description
251
- : commentTags.join("\n");
252
- };
253
-
254
- const isNotObjectLiteral = (schema: OpenApi.IJsonSchema): boolean =>
255
- OpenApiTypeChecker.isReference(schema) ||
256
- OpenApiTypeChecker.isBoolean(schema) ||
257
- OpenApiTypeChecker.isNumber(schema) ||
258
- OpenApiTypeChecker.isString(schema) ||
259
- OpenApiTypeChecker.isUnknown(schema) ||
260
- (OpenApiTypeChecker.isOneOf(schema) &&
261
- schema.oneOf.every(isNotObjectLiteral)) ||
262
- (OpenApiTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
263
-
264
- const emplaceBodySchema =
265
- (from: "request" | "response") =>
266
- (
267
- emplacer: (schema: OpenApi.IJsonSchema) => OpenApi.IJsonSchema.IReference,
268
- ) =>
269
- (meta?: {
270
- description?: string;
271
- content?: Record<string, OpenApi.IOperation.IMediaType>; // ISwaggerRouteBodyContent;
272
- "x-nestia-encrypted"?: boolean;
273
- }): false | null | IMigrateRoute.IBody => {
274
- if (!meta?.content) return null;
275
-
276
- const entries: [string, OpenApi.IOperation.IMediaType][] = Object.entries(
277
- meta.content,
278
- ).filter(([_, v]) => !!v);
279
- const json = entries.find((e) =>
280
- meta["x-nestia-encrypted"] === true
281
- ? e[0].includes("text/plain") || e[0].includes("application/json")
282
- : e[0].includes("application/json") || e[0].includes("*/*"),
283
- );
284
- if (json) {
285
- const { schema } = json[1];
286
- return {
287
- type: "application/json",
288
- name: "body",
289
- key: "body",
290
- schema: schema
291
- ? isNotObjectLiteral(schema)
292
- ? schema
293
- : emplacer(schema)
294
- : {},
295
- "x-nestia-encrypted": meta["x-nestia-encrypted"],
296
- };
297
- }
298
-
299
- const query = entries.find((e) =>
300
- e[0].includes("application/x-www-form-urlencoded"),
301
- );
302
- if (query) {
303
- const { schema } = query[1];
304
- return {
305
- type: "application/x-www-form-urlencoded",
306
- name: "body",
307
- key: "body",
308
- schema: schema
309
- ? isNotObjectLiteral(schema)
310
- ? schema
311
- : emplacer(schema)
312
- : {},
313
- };
314
- }
315
-
316
- const text = entries.find((e) => e[0].includes("text/plain"));
317
- if (text)
318
- return {
319
- type: "text/plain",
320
- name: "body",
321
- key: "body",
322
- schema: { type: "string" },
323
- };
324
-
325
- if (from === "request") {
326
- const multipart = entries.find((e) =>
327
- e[0].includes("multipart/form-data"),
328
- );
329
- if (multipart) {
330
- const { schema } = multipart[1];
331
- return {
332
- type: "multipart/form-data",
333
- name: "body",
334
- key: "body",
335
- schema: schema
336
- ? isNotObjectLiteral(schema)
337
- ? schema
338
- : emplacer(schema)
339
- : {},
340
- };
341
- }
342
- }
343
- return false;
344
- };
345
-
346
- const emplaceReference =
347
- (swagger: OpenApi.IDocument) =>
348
- (name: string) =>
349
- (schema: OpenApi.IJsonSchema): OpenApi.IJsonSchema.IReference => {
350
- swagger.components.schemas ??= {};
351
- swagger.components.schemas[name] = schema;
352
- return { $ref: `#/components/schemas/${name}` };
353
- };
354
- }
355
-
356
- const SUPPORTED_METHODS: Set<string> = new Set([
357
- "GET",
358
- "POST",
359
- "PUT",
360
- "PATCH",
361
- "DELETE",
362
- "HEAD",
363
- ]);
1
+ import { OpenApi } from "@samchon/openapi";
2
+ import { Escaper } from "typia/lib/utils/Escaper";
3
+
4
+ import { IMigrateProgram } from "../structures/IMigrateProgram";
5
+ import { IMigrateRoute } from "../structures/IMigrateRoute";
6
+ import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker";
7
+ import { StringUtil } from "../utils/StringUtil";
8
+
9
+ export namespace MigrateMethodAnalzyer {
10
+ export const analyze =
11
+ (props: Omit<IMigrateProgram.IProps, "dictionary">) =>
12
+ (endpoint: { path: string; method: string }) =>
13
+ (route: OpenApi.IOperation): IMigrateRoute | null => {
14
+ const body = emplaceBodySchema("request")(
15
+ emplaceReference(props.document)("body"),
16
+ )(route.requestBody);
17
+ const success = emplaceBodySchema("response")(
18
+ emplaceReference(props.document)("response"),
19
+ )(
20
+ route.responses?.["201"] ??
21
+ route.responses?.["200"] ??
22
+ route.responses?.default,
23
+ );
24
+
25
+ const failures: string[] = [];
26
+ if (body === false)
27
+ failures.push(
28
+ `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
29
+ );
30
+ if (success === false)
31
+ failures.push(
32
+ `supports only "application/json", "application/x-www-form-urlencoded" and "text/plain" content type in the response body.`,
33
+ );
34
+ if (SUPPORTED_METHODS.has(endpoint.method.toUpperCase()) === false)
35
+ failures.push(
36
+ `does not support ${endpoint.method.toUpperCase()} method.`,
37
+ );
38
+
39
+ const [headers, query] = ["header", "query"].map((type) => {
40
+ const parameters: OpenApi.IOperation.IParameter[] = (
41
+ route.parameters ?? []
42
+ ).filter((p) => p.in === type);
43
+ if (parameters.length === 0) return null;
44
+
45
+ const objects = parameters
46
+ .map((p) =>
47
+ OpenApiTypeChecker.isObject(p.schema)
48
+ ? p.schema
49
+ : OpenApiTypeChecker.isReference(p.schema) &&
50
+ OpenApiTypeChecker.isObject(
51
+ props.document.components.schemas[
52
+ p.schema.$ref.replace(`#/components/schemas/`, ``)
53
+ ] ?? {},
54
+ )
55
+ ? p.schema
56
+ : null!,
57
+ )
58
+ .filter((s) => !!s);
59
+ const primitives = parameters.filter(
60
+ (p) =>
61
+ OpenApiTypeChecker.isBoolean(p.schema) ||
62
+ OpenApiTypeChecker.isInteger(p.schema) ||
63
+ OpenApiTypeChecker.isNumber(p.schema) ||
64
+ OpenApiTypeChecker.isString(p.schema) ||
65
+ OpenApiTypeChecker.isArray(p.schema),
66
+ );
67
+ if (objects.length === 1 && primitives.length === 0) return objects[0];
68
+ else if (objects.length > 1) {
69
+ failures.push(
70
+ `${type} typed parameters must be only one object type`,
71
+ );
72
+ return false;
73
+ }
74
+
75
+ const dto: OpenApi.IJsonSchema.IObject | null = objects[0]
76
+ ? OpenApiTypeChecker.isObject(objects[0])
77
+ ? objects[0]
78
+ : ((props.document.components.schemas ?? {})[
79
+ (objects[0] as OpenApi.IJsonSchema.IReference).$ref.replace(
80
+ `#/components/schemas/`,
81
+ ``,
82
+ )
83
+ ] as OpenApi.IJsonSchema.IObject)
84
+ : null;
85
+ const entire: OpenApi.IJsonSchema.IObject[] = [
86
+ ...objects.map((o) =>
87
+ OpenApiTypeChecker.isObject(o)
88
+ ? o
89
+ : (props.document.components.schemas?.[
90
+ o.$ref.replace(`#/components/schemas/`, ``)
91
+ ]! as OpenApi.IJsonSchema.IObject),
92
+ ),
93
+ {
94
+ type: "object",
95
+ properties: Object.fromEntries([
96
+ ...primitives.map((p) => [
97
+ p.name,
98
+ {
99
+ ...p.schema,
100
+ description: p.schema.description ?? p.description,
101
+ },
102
+ ]),
103
+ ...(dto ? Object.entries(dto.properties ?? {}) : []),
104
+ ]),
105
+ required: [
106
+ ...primitives.filter((p) => p.required).map((p) => p.name!),
107
+ ...(dto ? dto.required ?? [] : []),
108
+ ],
109
+ },
110
+ ];
111
+ return parameters.length === 0
112
+ ? null
113
+ : emplaceReference(props.document)(
114
+ StringUtil.pascal(`I/Api/${endpoint.path}`) +
115
+ "." +
116
+ StringUtil.pascal(`${endpoint.method}/${type}`),
117
+ )({
118
+ type: "object",
119
+ properties: Object.fromEntries([
120
+ ...new Map<string, OpenApi.IJsonSchema>(
121
+ entire
122
+ .map((o) =>
123
+ Object.entries(o.properties ?? {}).map(
124
+ ([name, schema]) =>
125
+ [
126
+ name,
127
+ {
128
+ ...schema,
129
+ description:
130
+ schema.description ?? schema.description,
131
+ } as OpenApi.IJsonSchema,
132
+ ] as const,
133
+ ),
134
+ )
135
+ .flat(),
136
+ ),
137
+ ]),
138
+ required: [
139
+ ...new Set(entire.map((o) => o.required ?? []).flat()),
140
+ ],
141
+ });
142
+ });
143
+
144
+ const parameterNames: string[] = StringUtil.splitWithNormalization(
145
+ endpoint.path,
146
+ )
147
+ .filter((str) => str[0] === "{" || str[0] === ":")
148
+ .map((str) =>
149
+ str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
150
+ );
151
+ const pathParameters: OpenApi.IOperation.IParameter[] =
152
+ route.parameters.filter((p) => p.in === "path");
153
+ if (parameterNames.length !== pathParameters.length)
154
+ if (
155
+ pathParameters.length < parameterNames.length &&
156
+ pathParameters.every(
157
+ (p) => p.name !== undefined && parameterNames.includes(p.name),
158
+ )
159
+ ) {
160
+ for (const name of parameterNames)
161
+ if (pathParameters.find((p) => p.name === name) === undefined)
162
+ pathParameters.push({
163
+ name,
164
+ in: "path",
165
+ schema: { type: "string" },
166
+ });
167
+ pathParameters.sort(
168
+ (a, b) =>
169
+ parameterNames.indexOf(a.name!) - parameterNames.indexOf(b.name!),
170
+ );
171
+ route.parameters = [
172
+ ...pathParameters,
173
+ ...route.parameters.filter((p) => p.in !== "path"),
174
+ ];
175
+ } else
176
+ failures.push(
177
+ "number of path parameters are not matched with its full path.",
178
+ );
179
+ if (failures.length) {
180
+ console.log(
181
+ `Failed to migrate ${endpoint.method.toUpperCase()} ${endpoint.path}`,
182
+ ...failures.map((f) => ` - ${f}`),
183
+ );
184
+ return null;
185
+ }
186
+ return {
187
+ name: "@lazy",
188
+ originalPath: endpoint.path,
189
+ path: endpoint.path,
190
+ method: endpoint.method,
191
+ accessor: ["@lazy"],
192
+ headers: headers
193
+ ? {
194
+ name: "headers",
195
+ key: "headers",
196
+ schema: headers,
197
+ }
198
+ : null,
199
+ parameters: route.parameters
200
+ .filter((p) => p.in === "path")
201
+ .map((p, i) => ({
202
+ name: parameterNames[i],
203
+ key: (() => {
204
+ let key: string = StringUtil.normalize(parameterNames[i]);
205
+ if (Escaper.variable(key)) return key;
206
+ while (true) {
207
+ key = "_" + key;
208
+ if (!parameterNames.some((s) => s === key)) return key;
209
+ }
210
+ })(),
211
+ schema: {
212
+ ...p!.schema,
213
+ description: p!.schema.description ?? p!.description,
214
+ },
215
+ })),
216
+ query: query
217
+ ? {
218
+ name: "query",
219
+ key: "query",
220
+ schema: query,
221
+ }
222
+ : null,
223
+ body: body as IMigrateRoute.IBody | null,
224
+ success: success as IMigrateRoute.IBody | null,
225
+ exceptions: Object.fromEntries(
226
+ Object.entries(route.responses ?? {})
227
+ .filter(
228
+ ([key]) => key !== "200" && key !== "201" && key !== "default",
229
+ )
230
+ .map(([key, value]) => [
231
+ key,
232
+ {
233
+ description: value.description,
234
+ schema: value.content?.["application/json"]?.schema ?? {},
235
+ },
236
+ ]),
237
+ ),
238
+ deprecated: route.deprecated ?? false,
239
+ comment: () => describe(route),
240
+ tags: route.tags ?? [],
241
+ };
242
+ };
243
+
244
+ const describe = (route: OpenApi.IOperation): string => {
245
+ const commentTags: string[] = [];
246
+ const add = (text: string) => {
247
+ if (commentTags.every((line) => line !== text)) commentTags.push(text);
248
+ };
249
+
250
+ let description: string = route.description ?? "";
251
+ if (route.summary) {
252
+ const emended: string = route.summary.endsWith(".")
253
+ ? route.summary
254
+ : route.summary + ".";
255
+ if (!!description.length && !description.startsWith(route.summary))
256
+ description = `${emended}\n${description}`;
257
+ }
258
+ for (const p of route.parameters)
259
+ if (p.description) add(`@param ${p.name} ${p.description}`);
260
+ if (route.requestBody?.description)
261
+ add(`@param body ${route.requestBody.description}`);
262
+ for (const security of route.security ?? [])
263
+ for (const [name, scopes] of Object.entries(security))
264
+ add(`@security ${[name, ...scopes].join("")}`);
265
+ if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
266
+ if (route.deprecated) add("@deprecated");
267
+ return description.length
268
+ ? commentTags.length
269
+ ? `${description}\n\n${commentTags.join("\n")}`
270
+ : description
271
+ : commentTags.join("\n");
272
+ };
273
+
274
+ const isNotObjectLiteral = (schema: OpenApi.IJsonSchema): boolean =>
275
+ OpenApiTypeChecker.isReference(schema) ||
276
+ OpenApiTypeChecker.isBoolean(schema) ||
277
+ OpenApiTypeChecker.isNumber(schema) ||
278
+ OpenApiTypeChecker.isString(schema) ||
279
+ OpenApiTypeChecker.isUnknown(schema) ||
280
+ (OpenApiTypeChecker.isOneOf(schema) &&
281
+ schema.oneOf.every(isNotObjectLiteral)) ||
282
+ (OpenApiTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
283
+
284
+ const emplaceBodySchema =
285
+ (from: "request" | "response") =>
286
+ (
287
+ emplacer: (schema: OpenApi.IJsonSchema) => OpenApi.IJsonSchema.IReference,
288
+ ) =>
289
+ (meta?: {
290
+ description?: string;
291
+ content?: Record<string, OpenApi.IOperation.IMediaType>; // ISwaggerRouteBodyContent;
292
+ "x-nestia-encrypted"?: boolean;
293
+ }): false | null | IMigrateRoute.IBody => {
294
+ if (!meta?.content) return null;
295
+
296
+ const entries: [string, OpenApi.IOperation.IMediaType][] = Object.entries(
297
+ meta.content,
298
+ ).filter(([_, v]) => !!v);
299
+ const json = entries.find((e) =>
300
+ meta["x-nestia-encrypted"] === true
301
+ ? e[0].includes("text/plain") || e[0].includes("application/json")
302
+ : e[0].includes("application/json") || e[0].includes("*/*"),
303
+ );
304
+ if (json) {
305
+ const { schema } = json[1];
306
+ return {
307
+ type: "application/json",
308
+ name: "body",
309
+ key: "body",
310
+ schema: schema
311
+ ? isNotObjectLiteral(schema)
312
+ ? schema
313
+ : emplacer(schema)
314
+ : {},
315
+ "x-nestia-encrypted": meta["x-nestia-encrypted"],
316
+ };
317
+ }
318
+
319
+ const query = entries.find((e) =>
320
+ e[0].includes("application/x-www-form-urlencoded"),
321
+ );
322
+ if (query) {
323
+ const { schema } = query[1];
324
+ return {
325
+ type: "application/x-www-form-urlencoded",
326
+ name: "body",
327
+ key: "body",
328
+ schema: schema
329
+ ? isNotObjectLiteral(schema)
330
+ ? schema
331
+ : emplacer(schema)
332
+ : {},
333
+ };
334
+ }
335
+
336
+ const text = entries.find((e) => e[0].includes("text/plain"));
337
+ if (text)
338
+ return {
339
+ type: "text/plain",
340
+ name: "body",
341
+ key: "body",
342
+ schema: { type: "string" },
343
+ };
344
+
345
+ if (from === "request") {
346
+ const multipart = entries.find((e) =>
347
+ e[0].includes("multipart/form-data"),
348
+ );
349
+ if (multipart) {
350
+ const { schema } = multipart[1];
351
+ return {
352
+ type: "multipart/form-data",
353
+ name: "body",
354
+ key: "body",
355
+ schema: schema
356
+ ? isNotObjectLiteral(schema)
357
+ ? schema
358
+ : emplacer(schema)
359
+ : {},
360
+ };
361
+ }
362
+ }
363
+ return false;
364
+ };
365
+
366
+ const emplaceReference =
367
+ (swagger: OpenApi.IDocument) =>
368
+ (name: string) =>
369
+ (schema: OpenApi.IJsonSchema): OpenApi.IJsonSchema.IReference => {
370
+ swagger.components.schemas ??= {};
371
+ swagger.components.schemas[name] = schema;
372
+ return { $ref: `#/components/schemas/${name}` };
373
+ };
374
+ }
375
+
376
+ const SUPPORTED_METHODS: Set<string> = new Set([
377
+ "GET",
378
+ "POST",
379
+ "PUT",
380
+ "PATCH",
381
+ "DELETE",
382
+ "HEAD",
383
+ ]);