@nestia/sdk 12.0.0-dev.20260601.1 → 12.0.0-dev.20260619.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 (259) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +93 -93
  3. package/assets/bundle/api/HttpError.ts +1 -1
  4. package/assets/bundle/api/IConnection.ts +1 -1
  5. package/assets/bundle/api/Primitive.ts +1 -1
  6. package/assets/bundle/api/Resolved.ts +1 -1
  7. package/assets/bundle/api/index.ts +4 -4
  8. package/assets/bundle/api/module.ts +6 -6
  9. package/assets/bundle/distribute/README.md +37 -37
  10. package/assets/bundle/distribute/package.json +28 -28
  11. package/assets/bundle/distribute/tsconfig.json +109 -109
  12. package/assets/bundle/e2e/index.ts +42 -42
  13. package/assets/config/nestia.config.ts +97 -97
  14. package/lib/NestiaSdkApplication.js +29 -7
  15. package/lib/NestiaSdkApplication.js.map +1 -1
  16. package/lib/NestiaSwaggerComposer.js +21 -13
  17. package/lib/NestiaSwaggerComposer.js.map +1 -1
  18. package/lib/analyses/AccessorAnalyzer.d.ts +4 -1
  19. package/lib/analyses/AccessorAnalyzer.js.map +1 -1
  20. package/lib/analyses/ConfigAnalyzer.js +1 -1
  21. package/lib/analyses/PathAnalyzer.d.ts +18 -3
  22. package/lib/analyses/PathAnalyzer.js +32 -0
  23. package/lib/analyses/PathAnalyzer.js.map +1 -1
  24. package/lib/analyses/ReflectControllerAnalyzer.js +3 -2
  25. package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
  26. package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +1 -1
  27. package/lib/analyses/ReflectHttpOperationAnalyzer.js +1 -1
  28. package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
  29. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +1 -1
  30. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +53 -20
  31. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
  32. package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +14 -0
  33. package/lib/analyses/ReflectMcpOperationAnalyzer.js +79 -0
  34. package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -0
  35. package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +9 -0
  36. package/lib/analyses/TypedMcpRouteAnalyzer.js +31 -0
  37. package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -0
  38. package/lib/executable/internal/NestiaConfigLoader.js +5 -1
  39. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  40. package/lib/executable/internal/NestiaSdkCommand.js +30 -14
  41. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  42. package/lib/executable/internal/NestiaSdkWatcher.d.ts +10 -0
  43. package/lib/executable/internal/NestiaSdkWatcher.js +322 -0
  44. package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -0
  45. package/lib/executable/sdk.js +12 -12
  46. package/lib/executable/sdk.js.map +1 -1
  47. package/lib/factories/ExpressionFactory.d.ts +9 -0
  48. package/lib/factories/ExpressionFactory.js +16 -0
  49. package/lib/factories/ExpressionFactory.js.map +1 -0
  50. package/lib/factories/IdentifierFactory.d.ts +22 -0
  51. package/lib/factories/IdentifierFactory.js +50 -0
  52. package/lib/factories/IdentifierFactory.js.map +1 -0
  53. package/lib/factories/LiteralFactory.d.ts +9 -0
  54. package/lib/factories/LiteralFactory.js +48 -0
  55. package/lib/factories/LiteralFactory.js.map +1 -0
  56. package/lib/factories/StatementFactory.d.ts +14 -0
  57. package/lib/factories/StatementFactory.js +20 -0
  58. package/lib/factories/StatementFactory.js.map +1 -0
  59. package/lib/factories/TypeFactory.d.ts +24 -0
  60. package/lib/factories/TypeFactory.js +27 -0
  61. package/lib/factories/TypeFactory.js.map +1 -0
  62. package/lib/generates/CloneGenerator.js +6 -4
  63. package/lib/generates/CloneGenerator.js.map +1 -1
  64. package/lib/generates/SdkGenerator.js +50 -1
  65. package/lib/generates/SdkGenerator.js.map +1 -1
  66. package/lib/generates/SwaggerGenerator.js +18 -2
  67. package/lib/generates/SwaggerGenerator.js.map +1 -1
  68. package/lib/generates/internal/E2eFileProgrammer.js +32 -29
  69. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  70. package/lib/generates/internal/FilePrinter.d.ts +2 -2
  71. package/lib/generates/internal/FilePrinter.js +14 -25
  72. package/lib/generates/internal/FilePrinter.js.map +1 -1
  73. package/lib/generates/internal/ImportDictionary.d.ts +2 -1
  74. package/lib/generates/internal/ImportDictionary.js +16 -10
  75. package/lib/generates/internal/ImportDictionary.js.map +1 -1
  76. package/lib/generates/internal/SdkAliasCollection.d.ts +12 -10
  77. package/lib/generates/internal/SdkAliasCollection.js +29 -22
  78. package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
  79. package/lib/generates/internal/SdkDistributionComposer.d.ts +1 -0
  80. package/lib/generates/internal/SdkDistributionComposer.js +3 -0
  81. package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
  82. package/lib/generates/internal/SdkFileProgrammer.js +6 -3
  83. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  84. package/lib/generates/internal/SdkHttpCloneProgrammer.d.ts +1 -1
  85. package/lib/generates/internal/SdkHttpCloneProgrammer.js +3 -3
  86. package/lib/generates/internal/SdkHttpCloneProgrammer.js.map +1 -1
  87. package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +1 -1
  88. package/lib/generates/internal/SdkHttpCloneReferencer.js +42 -9
  89. package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
  90. package/lib/generates/internal/SdkHttpFunctionProgrammer.d.ts +1 -1
  91. package/lib/generates/internal/SdkHttpFunctionProgrammer.js +47 -51
  92. package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
  93. package/lib/generates/internal/SdkHttpNamespaceProgrammer.d.ts +1 -1
  94. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +63 -61
  95. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
  96. package/lib/generates/internal/SdkHttpParameterProgrammer.d.ts +2 -2
  97. package/lib/generates/internal/SdkHttpParameterProgrammer.js +11 -13
  98. package/lib/generates/internal/SdkHttpParameterProgrammer.js.map +1 -1
  99. package/lib/generates/internal/SdkHttpRouteProgrammer.d.ts +1 -1
  100. package/lib/generates/internal/SdkHttpSimulationProgrammer.d.ts +1 -1
  101. package/lib/generates/internal/SdkHttpSimulationProgrammer.js +52 -47
  102. package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
  103. package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +15 -0
  104. package/lib/generates/internal/SdkMcpRouteProgrammer.js +149 -0
  105. package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -0
  106. package/lib/generates/internal/SdkRouteDirectory.d.ts +2 -1
  107. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
  108. package/lib/generates/internal/SdkTypeProgrammer.d.ts +3 -3
  109. package/lib/generates/internal/SdkTypeProgrammer.js +42 -43
  110. package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -1
  111. package/lib/generates/internal/SdkTypeTagProgrammer.d.ts +2 -1
  112. package/lib/generates/internal/SdkTypeTagProgrammer.js +7 -9
  113. package/lib/generates/internal/SdkTypeTagProgrammer.js.map +1 -1
  114. package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +6 -0
  115. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +283 -0
  116. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -0
  117. package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.d.ts +2 -2
  118. package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js +45 -47
  119. package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js.map +1 -1
  120. package/lib/generates/internal/SdkWebSocketParameterProgrammer.d.ts +3 -3
  121. package/lib/generates/internal/SdkWebSocketParameterProgrammer.js +9 -9
  122. package/lib/generates/internal/SdkWebSocketParameterProgrammer.js.map +1 -1
  123. package/lib/generates/internal/SdkWebSocketRouteProgrammer.d.ts +2 -2
  124. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +37 -41
  125. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
  126. package/lib/generates/internal/SwaggerOperationParameterComposer.js +10 -2
  127. package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
  128. package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +1 -1
  129. package/lib/generates/internal/SwaggerOperationResponseComposer.js +6 -1
  130. package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
  131. package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +9 -0
  132. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +174 -0
  133. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +1 -0
  134. package/lib/structures/INestiaSdkInput.d.ts +9 -2
  135. package/lib/structures/IReflectController.d.ts +2 -1
  136. package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -2
  137. package/lib/structures/IReflectMcpOperation.d.ts +35 -0
  138. package/lib/structures/IReflectMcpOperation.js +3 -0
  139. package/lib/structures/IReflectMcpOperation.js.map +1 -0
  140. package/lib/structures/IReflectMcpOperationParameter.d.ts +19 -0
  141. package/lib/structures/IReflectMcpOperationParameter.js +3 -0
  142. package/lib/structures/IReflectMcpOperationParameter.js.map +1 -0
  143. package/lib/structures/ITypedApplication.d.ts +2 -1
  144. package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
  145. package/lib/structures/ITypedMcpRoute.d.ts +31 -0
  146. package/lib/structures/ITypedMcpRoute.js +3 -0
  147. package/lib/structures/ITypedMcpRoute.js.map +1 -0
  148. package/lib/transform.d.ts +5 -9
  149. package/lib/transform.js +8 -31
  150. package/lib/transform.js.map +1 -1
  151. package/lib/utils/HttpResponseContentTypeUtil.d.ts +5 -0
  152. package/lib/utils/HttpResponseContentTypeUtil.js +22 -0
  153. package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -0
  154. package/native/go.mod +52 -52
  155. package/native/go.sum +84 -54
  156. package/native/sdk/register.go +322 -165
  157. package/native/sdk/sdk.go +17 -17
  158. package/native/sdk/sdk_metadata_json.go +327 -327
  159. package/native/sdk/sdk_transform.go +1879 -1549
  160. package/package.json +13 -11
  161. package/src/INestiaConfig.ts +267 -267
  162. package/src/NestiaSdkApplication.ts +39 -8
  163. package/src/NestiaSwaggerComposer.ts +153 -142
  164. package/src/analyses/AccessorAnalyzer.ts +64 -67
  165. package/src/analyses/ConfigAnalyzer.ts +330 -330
  166. package/src/analyses/ImportAnalyzer.ts +92 -92
  167. package/src/analyses/PathAnalyzer.ts +130 -69
  168. package/src/analyses/ReflectControllerAnalyzer.ts +112 -105
  169. package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
  170. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +90 -90
  171. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +350 -350
  172. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +163 -130
  173. package/src/analyses/ReflectMcpOperationAnalyzer.ts +124 -0
  174. package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
  175. package/src/analyses/SecurityAnalyzer.ts +25 -25
  176. package/src/analyses/TypedMcpRouteAnalyzer.ts +34 -0
  177. package/src/decorators/OperationMetadata.ts +29 -29
  178. package/src/executable/internal/CommandParser.ts +15 -15
  179. package/src/executable/internal/NestiaConfigLoader.ts +451 -446
  180. package/src/executable/internal/NestiaSdkCommand.ts +124 -106
  181. package/src/executable/internal/NestiaSdkWatcher.ts +342 -0
  182. package/src/executable/sdk.ts +90 -88
  183. package/src/factories/ExpressionFactory.ts +23 -0
  184. package/src/factories/IdentifierFactory.ts +84 -0
  185. package/src/factories/LiteralFactory.ts +54 -0
  186. package/src/factories/StatementFactory.ts +56 -0
  187. package/src/factories/TypeFactory.ts +27 -0
  188. package/src/generates/CloneGenerator.ts +74 -66
  189. package/src/generates/E2eGenerator.ts +32 -32
  190. package/src/generates/SdkGenerator.ts +176 -118
  191. package/src/generates/SwaggerGenerator.ts +342 -310
  192. package/src/generates/internal/E2eFileProgrammer.ts +246 -233
  193. package/src/generates/internal/FilePrinter.ts +57 -65
  194. package/src/generates/internal/ImportDictionary.ts +207 -204
  195. package/src/generates/internal/SdkAliasCollection.ts +280 -261
  196. package/src/generates/internal/SdkDistributionComposer.ts +123 -116
  197. package/src/generates/internal/SdkFileProgrammer.ts +113 -112
  198. package/src/generates/internal/SdkHttpCloneProgrammer.ts +124 -126
  199. package/src/generates/internal/SdkHttpCloneReferencer.ts +131 -77
  200. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +290 -301
  201. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +507 -510
  202. package/src/generates/internal/SdkHttpParameterProgrammer.ts +163 -165
  203. package/src/generates/internal/SdkHttpRouteProgrammer.ts +109 -109
  204. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +322 -314
  205. package/src/generates/internal/SdkImportWizard.ts +62 -62
  206. package/src/generates/internal/SdkMcpRouteProgrammer.ts +447 -0
  207. package/src/generates/internal/SdkRouteDirectory.ts +21 -18
  208. package/src/generates/internal/SdkTypeProgrammer.ts +389 -382
  209. package/src/generates/internal/SdkTypeTagProgrammer.ts +110 -114
  210. package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +319 -0
  211. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +383 -389
  212. package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +91 -89
  213. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +304 -323
  214. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  215. package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
  216. package/src/generates/internal/SwaggerOperationParameterComposer.ts +175 -162
  217. package/src/generates/internal/SwaggerOperationResponseComposer.ts +115 -110
  218. package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +262 -0
  219. package/src/index.ts +4 -4
  220. package/src/internal/legacy.ts +492 -492
  221. package/src/module.ts +4 -4
  222. package/src/structures/INestiaProject.ts +10 -10
  223. package/src/structures/INestiaSdkInput.ts +27 -20
  224. package/src/structures/IOperationMetadata.ts +41 -41
  225. package/src/structures/IReflectController.ts +18 -15
  226. package/src/structures/IReflectHttpOperation.ts +26 -26
  227. package/src/structures/IReflectHttpOperationException.ts +18 -18
  228. package/src/structures/IReflectHttpOperationParameter.ts +79 -79
  229. package/src/structures/IReflectHttpOperationSuccess.ts +18 -21
  230. package/src/structures/IReflectImport.ts +6 -6
  231. package/src/structures/IReflectMcpOperation.ts +38 -0
  232. package/src/structures/IReflectMcpOperationParameter.ts +27 -0
  233. package/src/structures/IReflectOperationError.ts +26 -26
  234. package/src/structures/IReflectType.ts +4 -4
  235. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  236. package/src/structures/ITypedApplication.ts +12 -11
  237. package/src/structures/ITypedHttpRoute.ts +41 -41
  238. package/src/structures/ITypedHttpRouteException.ts +15 -15
  239. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  240. package/src/structures/ITypedHttpRouteSuccess.ts +18 -22
  241. package/src/structures/ITypedMcpRoute.ts +33 -0
  242. package/src/structures/ITypedWebSocketRoute.ts +24 -24
  243. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  244. package/src/transform.ts +28 -59
  245. package/src/typings/get-function-location.d.ts +7 -7
  246. package/src/utils/ArrayUtil.ts +26 -26
  247. package/src/utils/EmittedJavaScriptPatcher.ts +88 -88
  248. package/src/utils/FileRetriever.ts +22 -22
  249. package/src/utils/HttpResponseContentTypeUtil.ts +30 -0
  250. package/src/utils/MapUtil.ts +14 -14
  251. package/src/utils/PathUtil.ts +10 -10
  252. package/src/utils/SourceFinder.ts +63 -63
  253. package/src/utils/StringUtil.ts +17 -17
  254. package/src/utils/TsConfigReader.ts +108 -108
  255. package/src/utils/TtscExecutor.ts +68 -68
  256. package/src/utils/VersioningStrategy.ts +28 -28
  257. package/src/validators/HttpHeadersValidator.ts +11 -11
  258. package/src/validators/HttpQueryValidator.ts +11 -11
  259. package/src/validators/TextPlainValidator.ts +17 -17
@@ -1,310 +1,342 @@
1
- import { SwaggerCustomizer } from "@nestia/core";
2
- import { JsonSchemasProgrammer, MetadataSchema, sizeOf } from "../internal/legacy";
3
- import {
4
- OpenApi,
5
- OpenApiV3,
6
- OpenApiV3_1,
7
- OpenApiV3_2,
8
- SwaggerV2,
9
- } from "@typia/interface";
10
- import { OpenApiConverter } from "@typia/utils";
11
- import fs from "fs";
12
- import path from "path";
13
- import { Singleton } from "tstl";
14
- import type { IJsonSchemaCollection } from "typia";
15
-
16
- import { INestiaConfig } from "../INestiaConfig";
17
- import { ITypedApplication } from "../structures/ITypedApplication";
18
- import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
19
- import { FileRetriever } from "../utils/FileRetriever";
20
- import { SdkHttpParameterProgrammer } from "./internal/SdkHttpParameterProgrammer";
21
- import { SwaggerOperationComposer } from "./internal/SwaggerOperationComposer";
22
-
23
- export namespace SwaggerGenerator {
24
- export const generate = async (app: ITypedApplication): Promise<void> => {
25
- // GET CONFIGURATION
26
- console.log("Generating Swagger Document");
27
- if (app.project.config.swagger === undefined)
28
- throw new Error("Swagger configuration is not defined.");
29
- const config: INestiaConfig.ISwaggerConfig = app.project.config.swagger;
30
-
31
- // TARGET LOCATION
32
- const parsed: path.ParsedPath = path.parse(config.output);
33
- const directory: string = path.dirname(parsed.dir);
34
- if (fs.existsSync(directory) === false)
35
- try {
36
- await fs.promises.mkdir(directory);
37
- } catch {}
38
- if (fs.existsSync(directory) === false)
39
- throw new Error(
40
- `Error on NestiaApplication.swagger(): failed to create output directory: ${directory}`,
41
- );
42
- const location: string = !!parsed.ext
43
- ? path.resolve(config.output)
44
- : path.join(path.resolve(config.output), "swagger.json");
45
-
46
- // COMPOSE SWAGGER DOCUMENT
47
- const document: OpenApi.IDocument = compose({
48
- config,
49
- routes: app.routes.filter((route) => route.protocol === "http"),
50
- document: await initialize(config),
51
- });
52
- const specified:
53
- | OpenApi.IDocument
54
- | SwaggerV2.IDocument
55
- | OpenApiV3.IDocument
56
- | OpenApiV3_1.IDocument
57
- | OpenApiV3_2.IDocument =
58
- (config.openapi ?? "3.2") === "3.2"
59
- ? document
60
- : OpenApiConverter.downgradeDocument(document, config.openapi as "2.0");
61
- await fs.promises.writeFile(
62
- location,
63
- !config.beautify
64
- ? JSON.stringify(specified)
65
- : JSON.stringify(
66
- specified,
67
- null,
68
- typeof config.beautify === "number" ? config.beautify : 2,
69
- ),
70
- "utf8",
71
- );
72
- };
73
-
74
- export const compose = (props: {
75
- config: Omit<INestiaConfig.ISwaggerConfig, "output">;
76
- routes: ITypedHttpRoute[];
77
- document: OpenApi.IDocument;
78
- }): OpenApi.IDocument => {
79
- // GATHER METADATA
80
- const routes: ITypedHttpRoute[] = props.routes.filter((r) =>
81
- r.jsDocTags.every(
82
- (tag) => tag.name !== "internal" && tag.name !== "hidden",
83
- ),
84
- );
85
- const metadatas: MetadataSchema[] = routes
86
- .map((r) => [
87
- r.success.metadata,
88
- ...SdkHttpParameterProgrammer.getAll(r).map((p) => p.metadata),
89
- ...Object.values(r.exceptions).map((e) => e.metadata),
90
- ])
91
- .flat()
92
- .filter((m) => sizeOf(m) !== 0);
93
-
94
- // COMPOSE JSON SCHEMAS
95
- const json: IJsonSchemaCollection = JsonSchemasProgrammer.writeSchemas({
96
- version: "3.1",
97
- metadatas,
98
- });
99
- const dict: WeakMap<MetadataSchema, OpenApi.IJsonSchema> = new WeakMap();
100
- json.schemas.forEach((schema, i) => dict.set(metadatas[i]!, schema));
101
- const schema = (
102
- metadata: MetadataSchema,
103
- ): OpenApi.IJsonSchema | undefined => dict.get(metadata);
104
-
105
- // COMPOSE DOCUMENT
106
- const document: OpenApi.IDocument = props.document;
107
- document.components.schemas ??= {};
108
- Object.assign(document.components.schemas, json.components.schemas);
109
- fillPaths({
110
- ...props,
111
- routes,
112
- schema,
113
- document,
114
- });
115
- return document;
116
- };
117
-
118
- export const initialize = async (
119
- config: Omit<INestiaConfig.ISwaggerConfig, "output">,
120
- ): Promise<OpenApi.IDocument> => {
121
- const pack = new Singleton(
122
- async (): Promise<Partial<OpenApi.IDocument.IInfo> | null> => {
123
- const location: string | null = await FileRetriever.file(
124
- "package.json",
125
- )(process.cwd());
126
- if (location === null) return null;
127
-
128
- try {
129
- const content: string = await fs.promises.readFile(location, "utf8");
130
- const data = JSON.parse(content) as {
131
- name?: string;
132
- version?: string;
133
- description?: string;
134
- license?:
135
- | string
136
- | {
137
- type: string;
138
- /** @format uri */
139
- url: string;
140
- };
141
- };
142
- return {
143
- title: typeof data.name === "string" ? data.name : undefined,
144
- version:
145
- typeof data.version === "string" ? data.version : undefined,
146
- description:
147
- typeof data.description === "string"
148
- ? data.description
149
- : undefined,
150
- license: isLicense(data.license)
151
- ? typeof data.license === "string"
152
- ? { name: data.license }
153
- : typeof data.license === "object"
154
- ? {
155
- name: data.license.type,
156
- url: data.license.url,
157
- }
158
- : undefined
159
- : undefined,
160
- };
161
- } catch {
162
- return null;
163
- }
164
- },
165
- );
166
-
167
- return {
168
- openapi: "3.2.0",
169
- servers: config.servers ?? [
170
- {
171
- url: "https://github.com/samchon/nestia",
172
- description: "insert your server url",
173
- },
174
- ],
175
- info: {
176
- ...(config.info ?? {}),
177
- version: config.info?.version ?? (await pack.get())?.version ?? "0.1.0",
178
- title:
179
- config.info?.title ??
180
- (await pack.get())?.title ??
181
- "Swagger Documents",
182
- description:
183
- config.info?.description ??
184
- (await pack.get())?.description ??
185
- "Generated by nestia - https://github.com/samchon/nestia",
186
- license: config.info?.license ?? (await pack.get())?.license,
187
- },
188
- paths: {},
189
- components: {
190
- schemas: {},
191
- securitySchemes: config.security,
192
- },
193
- tags: config.tags ?? [],
194
- "x-typia-emended-v12": true,
195
- };
196
- };
197
-
198
- const isLicense = (
199
- input: unknown,
200
- ): input is
201
- | string
202
- | {
203
- type: string;
204
- url: string;
205
- } =>
206
- typeof input === "string" ||
207
- (typeof input === "object" &&
208
- input !== null &&
209
- Array.isArray(input) === false &&
210
- typeof (input as { type?: unknown }).type === "string" &&
211
- typeof (input as { url?: unknown }).url === "string");
212
-
213
- const fillPaths = (props: {
214
- config: Omit<INestiaConfig.ISwaggerConfig, "output">;
215
- document: OpenApi.IDocument;
216
- schema: (metadata: MetadataSchema) => OpenApi.IJsonSchema | undefined;
217
- routes: ITypedHttpRoute[];
218
- }): void => {
219
- // SWAGGER CUSTOMIZER
220
- const customizers: Array<() => void> = [];
221
- const neighbor = {
222
- at: new Singleton(() => {
223
- const functor: Map<Function, Endpoint> = new Map();
224
- for (const r of props.routes) {
225
- const method: OpenApi.Method =
226
- r.method.toLowerCase() as OpenApi.Method;
227
- const path: string = getPath(r);
228
- const operation: OpenApi.IOperation | undefined =
229
- props.document.paths?.[path]?.[method];
230
- if (operation === undefined) continue;
231
- functor.set(r.function, {
232
- method,
233
- path,
234
- route: operation,
235
- });
236
- }
237
- return functor;
238
- }),
239
- get: new Singleton(
240
- () =>
241
- (key: Accessor): OpenApi.IOperation | undefined => {
242
- const method: OpenApi.Method =
243
- key.method.toLowerCase() as OpenApi.Method;
244
- const path: string =
245
- "/" +
246
- key.path
247
- .split("/")
248
- .filter((str) => !!str.length)
249
- .map((str) =>
250
- str.startsWith(":") ? `{${str.substring(1)}}` : str,
251
- )
252
- .join("/");
253
- return props.document.paths?.[path]?.[method];
254
- },
255
- ),
256
- };
257
-
258
- // COMPOSE OPERATIONS
259
- for (const r of props.routes) {
260
- const operation: OpenApi.IOperation = SwaggerOperationComposer.compose({
261
- ...props,
262
- route: r,
263
- });
264
- const path: string = getPath(r);
265
- props.document.paths ??= {};
266
- props.document.paths[path] ??= {};
267
- props.document.paths[path][r.method.toLowerCase() as "get"] = operation;
268
-
269
- const closure: Function | Function[] | undefined = Reflect.getMetadata(
270
- "nestia/SwaggerCustomizer",
271
- r.controller.class.prototype,
272
- r.name,
273
- );
274
- if (closure !== undefined) {
275
- const array: Function[] = Array.isArray(closure) ? closure : [closure];
276
- customizers.push(() => {
277
- for (const closure of array)
278
- closure({
279
- swagger: props.document,
280
- method: r.method,
281
- path,
282
- route: operation,
283
- at: (func: Function) => neighbor.at.get().get(func),
284
- get: (accessor: Accessor) => neighbor.get.get()(accessor),
285
- } satisfies SwaggerCustomizer.IProps);
286
- });
287
- }
288
- }
289
-
290
- // DO CUSTOMIZE
291
- for (const fn of customizers) fn();
292
- };
293
-
294
- const getPath = (route: ITypedHttpRoute): string => {
295
- let str: string = route.path;
296
- for (const param of route.pathParameters)
297
- str = str.replace(`:${param.field}`, `{${param.field}}`);
298
- return str;
299
- };
300
- }
301
-
302
- interface Accessor {
303
- method: string;
304
- path: string;
305
- }
306
- interface Endpoint {
307
- method: string;
308
- path: string;
309
- route: OpenApi.IOperation;
310
- }
1
+ import { SwaggerCustomizer } from "@nestia/core";
2
+ import {
3
+ OpenApi,
4
+ OpenApiV3,
5
+ OpenApiV3_1,
6
+ OpenApiV3_2,
7
+ SwaggerV2,
8
+ } from "@typia/interface";
9
+ import { OpenApiConverter } from "@typia/utils";
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import { Singleton } from "tstl";
13
+ import type { IJsonSchemaCollection } from "typia";
14
+
15
+ import { INestiaConfig } from "../INestiaConfig";
16
+ import {
17
+ JsonSchemasProgrammer,
18
+ MetadataSchema,
19
+ sizeOf,
20
+ } from "../internal/legacy";
21
+ import { ITypedApplication } from "../structures/ITypedApplication";
22
+ import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
23
+ import { FileRetriever } from "../utils/FileRetriever";
24
+ import { SdkHttpParameterProgrammer } from "./internal/SdkHttpParameterProgrammer";
25
+ import { SwaggerOperationComposer } from "./internal/SwaggerOperationComposer";
26
+ import { SwaggerReadonlyArrayEmender } from "./internal/SwaggerReadonlyArrayEmender";
27
+
28
+ export namespace SwaggerGenerator {
29
+ export const generate = async (app: ITypedApplication): Promise<void> => {
30
+ // GET CONFIGURATION
31
+ console.log("Generating Swagger Document");
32
+ if (app.project.config.swagger === undefined)
33
+ throw new Error("Swagger configuration is not defined.");
34
+ const config: INestiaConfig.ISwaggerConfig = app.project.config.swagger;
35
+
36
+ // TARGET LOCATION
37
+ const parsed: path.ParsedPath = path.parse(config.output);
38
+ const directory: string = path.dirname(parsed.dir);
39
+ if (fs.existsSync(directory) === false)
40
+ try {
41
+ await fs.promises.mkdir(directory);
42
+ } catch {}
43
+ if (fs.existsSync(directory) === false)
44
+ throw new Error(
45
+ `Error on NestiaApplication.swagger(): failed to create output directory: ${directory}`,
46
+ );
47
+ const location: string = !!parsed.ext
48
+ ? path.resolve(config.output)
49
+ : path.join(path.resolve(config.output), "swagger.json");
50
+
51
+ // COMPOSE SWAGGER DOCUMENT
52
+ const document: OpenApi.IDocument = compose({
53
+ config,
54
+ routes: app.routes.filter((route) => route.protocol === "http"),
55
+ document: await initialize(config),
56
+ });
57
+ const specified:
58
+ | OpenApi.IDocument
59
+ | SwaggerV2.IDocument
60
+ | OpenApiV3.IDocument
61
+ | OpenApiV3_1.IDocument
62
+ | OpenApiV3_2.IDocument =
63
+ (config.openapi ?? "3.2") === "3.2"
64
+ ? document
65
+ : OpenApiConverter.downgradeDocument(document, config.openapi as "2.0");
66
+ await fs.promises.writeFile(
67
+ location,
68
+ !config.beautify
69
+ ? JSON.stringify(specified)
70
+ : JSON.stringify(
71
+ specified,
72
+ null,
73
+ typeof config.beautify === "number" ? config.beautify : 2,
74
+ ),
75
+ "utf8",
76
+ );
77
+ };
78
+
79
+ export const compose = (props: {
80
+ config: Omit<INestiaConfig.ISwaggerConfig, "output">;
81
+ routes: ITypedHttpRoute[];
82
+ document: OpenApi.IDocument;
83
+ }): OpenApi.IDocument => {
84
+ // GATHER METADATA
85
+ const routes: ITypedHttpRoute[] = props.routes.filter(
86
+ (r) =>
87
+ r.jsDocTags.every(
88
+ (tag) => tag.name !== "internal" && tag.name !== "hidden",
89
+ ) && isSwaggerExcluded(r) === false,
90
+ );
91
+ const metadatas: MetadataSchema[] = routes
92
+ .map((r) => [
93
+ r.success.metadata,
94
+ ...SdkHttpParameterProgrammer.getAll(r).map((p) => p.metadata),
95
+ ...Object.values(r.exceptions).map((e) => e.metadata),
96
+ ])
97
+ .flat()
98
+ .filter((m) => sizeOf(m) !== 0);
99
+
100
+ // COMPOSE JSON SCHEMAS
101
+ const json: IJsonSchemaCollection = JsonSchemasProgrammer.writeSchemas({
102
+ version: "3.1",
103
+ metadatas,
104
+ });
105
+ json.schemas.forEach((schema, i) =>
106
+ SwaggerReadonlyArrayEmender.emend({
107
+ components: json.components,
108
+ schema,
109
+ metadata: metadatas[i]!,
110
+ }),
111
+ );
112
+ const dict: WeakMap<MetadataSchema, OpenApi.IJsonSchema> = new WeakMap();
113
+ json.schemas.forEach((schema, i) => dict.set(metadatas[i]!, schema));
114
+ const schema = (
115
+ metadata: MetadataSchema,
116
+ ): OpenApi.IJsonSchema | undefined => dict.get(metadata);
117
+
118
+ // COMPOSE DOCUMENT
119
+ const document: OpenApi.IDocument = props.document;
120
+ document.components.schemas ??= {};
121
+ Object.assign(document.components.schemas, json.components.schemas);
122
+ fillPaths({
123
+ ...props,
124
+ routes,
125
+ schema,
126
+ document,
127
+ });
128
+ return document;
129
+ };
130
+
131
+ export const initialize = async (
132
+ config: Omit<INestiaConfig.ISwaggerConfig, "output">,
133
+ ): Promise<OpenApi.IDocument> => {
134
+ const pack = new Singleton(
135
+ async (): Promise<Partial<OpenApi.IDocument.IInfo> | null> => {
136
+ const location: string | null = await FileRetriever.file(
137
+ "package.json",
138
+ )(process.cwd());
139
+ if (location === null) return null;
140
+
141
+ try {
142
+ const content: string = await fs.promises.readFile(location, "utf8");
143
+ const data = JSON.parse(content) as {
144
+ name?: string;
145
+ version?: string;
146
+ description?: string;
147
+ license?:
148
+ | string
149
+ | {
150
+ type: string;
151
+ /** @format uri */
152
+ url: string;
153
+ };
154
+ };
155
+ return {
156
+ title: typeof data.name === "string" ? data.name : undefined,
157
+ version:
158
+ typeof data.version === "string" ? data.version : undefined,
159
+ description:
160
+ typeof data.description === "string"
161
+ ? data.description
162
+ : undefined,
163
+ license: isLicense(data.license)
164
+ ? typeof data.license === "string"
165
+ ? { name: data.license }
166
+ : typeof data.license === "object"
167
+ ? {
168
+ name: data.license.type,
169
+ url: data.license.url,
170
+ }
171
+ : undefined
172
+ : undefined,
173
+ };
174
+ } catch {
175
+ return null;
176
+ }
177
+ },
178
+ );
179
+
180
+ return {
181
+ openapi: "3.2.0",
182
+ servers: config.servers ?? [
183
+ {
184
+ url: "https://github.com/samchon/nestia",
185
+ description: "insert your server url",
186
+ },
187
+ ],
188
+ info: {
189
+ ...(config.info ?? {}),
190
+ version: config.info?.version ?? (await pack.get())?.version ?? "0.1.0",
191
+ title:
192
+ config.info?.title ??
193
+ (await pack.get())?.title ??
194
+ "Swagger Documents",
195
+ description:
196
+ config.info?.description ??
197
+ (await pack.get())?.description ??
198
+ "Generated by nestia - https://github.com/samchon/nestia",
199
+ license: config.info?.license ?? (await pack.get())?.license,
200
+ },
201
+ paths: {},
202
+ components: {
203
+ schemas: {},
204
+ securitySchemes: config.security,
205
+ },
206
+ tags: config.tags ?? [],
207
+ "x-typia-emended-v12": true,
208
+ };
209
+ };
210
+
211
+ const isLicense = (
212
+ input: unknown,
213
+ ): input is
214
+ | string
215
+ | {
216
+ type: string;
217
+ url: string;
218
+ } =>
219
+ typeof input === "string" ||
220
+ (typeof input === "object" &&
221
+ input !== null &&
222
+ Array.isArray(input) === false &&
223
+ typeof (input as { type?: unknown }).type === "string" &&
224
+ typeof (input as { url?: unknown }).url === "string");
225
+
226
+ const fillPaths = (props: {
227
+ config: Omit<INestiaConfig.ISwaggerConfig, "output">;
228
+ document: OpenApi.IDocument;
229
+ schema: (metadata: MetadataSchema) => OpenApi.IJsonSchema | undefined;
230
+ routes: ITypedHttpRoute[];
231
+ }): void => {
232
+ // SWAGGER CUSTOMIZER
233
+ const customizers: Array<() => void> = [];
234
+ const neighbor = {
235
+ at: new Singleton(() => {
236
+ const functor: Map<Function, Endpoint> = new Map();
237
+ for (const r of props.routes) {
238
+ const method: OpenApi.Method =
239
+ r.method.toLowerCase() as OpenApi.Method;
240
+ const path: string = getPath(r);
241
+ const operation: OpenApi.IOperation | undefined =
242
+ props.document.paths?.[path]?.[method];
243
+ if (operation === undefined) continue;
244
+ functor.set(r.function, {
245
+ method,
246
+ path,
247
+ route: operation,
248
+ });
249
+ }
250
+ return functor;
251
+ }),
252
+ get: new Singleton(
253
+ () =>
254
+ (key: Accessor): OpenApi.IOperation | undefined => {
255
+ const method: OpenApi.Method =
256
+ key.method.toLowerCase() as OpenApi.Method;
257
+ const path: string =
258
+ "/" +
259
+ key.path
260
+ .split("/")
261
+ .filter((str) => !!str.length)
262
+ .map((str) =>
263
+ str.startsWith(":") ? `{${str.substring(1)}}` : str,
264
+ )
265
+ .join("/");
266
+ return props.document.paths?.[path]?.[method];
267
+ },
268
+ ),
269
+ };
270
+
271
+ // COMPOSE OPERATIONS
272
+ for (const r of props.routes) {
273
+ const operation: OpenApi.IOperation = SwaggerOperationComposer.compose({
274
+ ...props,
275
+ route: r,
276
+ });
277
+ const path: string = getPath(r);
278
+ props.document.paths ??= {};
279
+ props.document.paths[path] ??= {};
280
+ props.document.paths[path][r.method.toLowerCase() as "get"] = operation;
281
+
282
+ const closure: Function | Function[] | undefined = Reflect.getMetadata(
283
+ "nestia/SwaggerCustomizer",
284
+ r.controller.class.prototype,
285
+ r.name,
286
+ );
287
+ if (closure !== undefined) {
288
+ const array: Function[] = Array.isArray(closure) ? closure : [closure];
289
+ customizers.push(() => {
290
+ for (const closure of array)
291
+ closure({
292
+ swagger: props.document,
293
+ method: r.method,
294
+ path,
295
+ route: operation,
296
+ at: (func: Function) => neighbor.at.get().get(func),
297
+ get: (accessor: Accessor) => neighbor.get.get()(accessor),
298
+ } satisfies SwaggerCustomizer.IProps);
299
+ });
300
+ }
301
+ }
302
+
303
+ // DO CUSTOMIZE
304
+ for (const fn of customizers) fn();
305
+ };
306
+
307
+ const getPath = (route: ITypedHttpRoute): string => {
308
+ let str: string = route.path;
309
+ for (const param of route.pathParameters)
310
+ str = str.replace(`:${param.field}`, `{${param.field}}`);
311
+ return str;
312
+ };
313
+
314
+ const isSwaggerExcluded = (route: ITypedHttpRoute): boolean => {
315
+ const controller: unknown = Reflect.getMetadata(
316
+ "swagger/apiExcludeController",
317
+ route.controller.class,
318
+ );
319
+ if (Array.isArray(controller) && controller[0] === true) return true;
320
+
321
+ const endpoint: unknown = Reflect.getMetadata(
322
+ "swagger/apiExcludeEndpoint",
323
+ route.function,
324
+ );
325
+ return (
326
+ endpoint !== undefined &&
327
+ (typeof endpoint !== "object" ||
328
+ endpoint === null ||
329
+ (endpoint as { disable?: boolean }).disable !== false)
330
+ );
331
+ };
332
+ }
333
+
334
+ interface Accessor {
335
+ method: string;
336
+ path: string;
337
+ }
338
+ interface Endpoint {
339
+ method: string;
340
+ path: string;
341
+ route: OpenApi.IOperation;
342
+ }