@hono-di/swagger 0.0.6

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,226 @@
1
+ 'use strict';
2
+
3
+ var core = require('@hono-di/core');
4
+ require('reflect-metadata');
5
+
6
+ // src/document-builder.ts
7
+ var DocumentBuilder = class {
8
+ document = {
9
+ openapi: "3.0.0",
10
+ info: {
11
+ title: "",
12
+ description: "",
13
+ version: "1.0.0",
14
+ contact: {}
15
+ },
16
+ tags: [],
17
+ servers: [],
18
+ paths: {},
19
+ components: {
20
+ securitySchemes: {}
21
+ }
22
+ };
23
+ setTitle(title) {
24
+ this.document.info.title = title;
25
+ return this;
26
+ }
27
+ setDescription(description) {
28
+ this.document.info.description = description;
29
+ return this;
30
+ }
31
+ setVersion(version) {
32
+ this.document.info.version = version;
33
+ return this;
34
+ }
35
+ setContact(name, url, email) {
36
+ this.document.info.contact = { name, url, email };
37
+ return this;
38
+ }
39
+ addServer(url, description) {
40
+ this.document.servers.push({ url, description });
41
+ return this;
42
+ }
43
+ addTag(name, description) {
44
+ this.document.tags.push({ name, description });
45
+ return this;
46
+ }
47
+ addBearerAuth(options = {}, name = "bearer") {
48
+ this.document.components.securitySchemes[name] = {
49
+ type: "http",
50
+ scheme: "bearer",
51
+ bearerFormat: "JWT",
52
+ ...options
53
+ };
54
+ return this;
55
+ }
56
+ build() {
57
+ return this.document;
58
+ }
59
+ };
60
+ var DECORATORS_PREFIX = "swagger";
61
+ var DECORATORS = {
62
+ API_TAGS: `${DECORATORS_PREFIX}/apiTags`,
63
+ API_OPERATION: `${DECORATORS_PREFIX}/apiOperation`,
64
+ API_RESPONSE: `${DECORATORS_PREFIX}/apiResponse`,
65
+ API_PARAM: `${DECORATORS_PREFIX}/apiParam`,
66
+ API_BODY: `${DECORATORS_PREFIX}/apiBody`,
67
+ API_PROPERTY: `${DECORATORS_PREFIX}/apiProperty`
68
+ };
69
+ function ApiTags(...tags) {
70
+ return (target) => {
71
+ Reflect.defineMetadata(DECORATORS.API_TAGS, tags, target);
72
+ };
73
+ }
74
+ function ApiOperation(options) {
75
+ return (target, propertyKey, descriptor) => {
76
+ Reflect.defineMetadata(DECORATORS.API_OPERATION, options, descriptor.value);
77
+ return descriptor;
78
+ };
79
+ }
80
+ function ApiResponse(options) {
81
+ return (target, propertyKey, descriptor) => {
82
+ const responses = Reflect.getMetadata(DECORATORS.API_RESPONSE, descriptor.value) || {};
83
+ responses[options.status] = options;
84
+ Reflect.defineMetadata(DECORATORS.API_RESPONSE, responses, descriptor.value);
85
+ return descriptor;
86
+ };
87
+ }
88
+ function ApiProperty(options = {}) {
89
+ return (target, propertyKey) => {
90
+ const properties = Reflect.getMetadata(DECORATORS.API_PROPERTY, target.constructor) || [];
91
+ properties.push({ key: propertyKey, ...options });
92
+ Reflect.defineMetadata(DECORATORS.API_PROPERTY, properties, target.constructor);
93
+ };
94
+ }
95
+
96
+ // src/explorers/api-scanner.ts
97
+ var SwaggerScanner = class {
98
+ constructor(container) {
99
+ this.container = container;
100
+ }
101
+ scan(document) {
102
+ const modules = this.container.getModules();
103
+ modules.forEach((module) => {
104
+ module.controllers.forEach((wrapper) => {
105
+ this.scanController(wrapper.metatype, document);
106
+ });
107
+ });
108
+ return document;
109
+ }
110
+ scanController(controller, document) {
111
+ if (!controller) return;
112
+ const { prefix } = Reflect.getMetadata(core.METADATA_KEYS.CONTROLLER, controller) || { prefix: "" };
113
+ const routes = Reflect.getMetadata(core.METADATA_KEYS.ROUTES, controller) || [];
114
+ const apiTags = Reflect.getMetadata(DECORATORS.API_TAGS, controller) || [];
115
+ apiTags.forEach((tag) => {
116
+ if (!document.tags.find((t) => t.name === tag)) {
117
+ document.tags.push({ name: tag });
118
+ }
119
+ });
120
+ routes.forEach((route) => {
121
+ const method = route.requestMethod;
122
+ const path = this.normalizePath(prefix, route.path);
123
+ const descriptor = Object.getOwnPropertyDescriptor(controller.prototype, route.methodName);
124
+ const handler = descriptor?.value;
125
+ if (!handler) return;
126
+ const operation = this.createOperation(handler, apiTags);
127
+ if (!document.paths[path]) {
128
+ document.paths[path] = {};
129
+ }
130
+ document.paths[path][method] = operation;
131
+ });
132
+ }
133
+ createOperation(handler, controllerTags) {
134
+ const apiOperation = Reflect.getMetadata(DECORATORS.API_OPERATION, handler) || {};
135
+ const apiResponses = Reflect.getMetadata(DECORATORS.API_RESPONSE, handler) || {};
136
+ const responses = {};
137
+ Object.keys(apiResponses).forEach((status) => {
138
+ responses[status] = {
139
+ description: apiResponses[status].description
140
+ };
141
+ });
142
+ if (Object.keys(responses).length === 0) {
143
+ responses["200"] = { description: "Successful operation" };
144
+ }
145
+ return {
146
+ summary: apiOperation.summary || "",
147
+ description: apiOperation.description || "",
148
+ tags: controllerTags,
149
+ responses
150
+ // TODO: Add parameters and request body scanning
151
+ };
152
+ }
153
+ normalizePath(prefix, path) {
154
+ const cleanPrefix = prefix ? prefix.replace(/^\/+/, "").replace(/\/+$/, "") : "";
155
+ const cleanPath = path ? path.replace(/^\/+/, "").replace(/\/+$/, "") : "";
156
+ let result = "";
157
+ if (cleanPrefix) result += `/${cleanPrefix}`;
158
+ if (cleanPath) result += `/${cleanPath}`;
159
+ return (result || "/").replace(/:([^\/]+)/g, "{$1}");
160
+ }
161
+ };
162
+
163
+ // src/swagger-module.ts
164
+ var SwaggerModule = class {
165
+ static createDocument(app, config) {
166
+ const container = app.getContainer();
167
+ const scanner = new SwaggerScanner(container);
168
+ const document = config;
169
+ return scanner.scan(document);
170
+ }
171
+ static setup(path, app, document, options) {
172
+ const httpAdapter = app.getHttpAdapter();
173
+ httpAdapter.get(`${path}-json`, (c) => c.json(document));
174
+ httpAdapter.get(path, (c) => {
175
+ const html = `
176
+ <!DOCTYPE html>
177
+ <html lang="en">
178
+ <head>
179
+ <meta charset="UTF-8">
180
+ <title>${document.info.title}</title>
181
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
182
+ <style>
183
+ html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
184
+ *, *:before, *:after { box-sizing: inherit; }
185
+ body { margin: 0; background: #fafafa; }
186
+ </style>
187
+ </head>
188
+ <body>
189
+ <div id="swagger-ui"></div>
190
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js" charset="UTF-8"> </script>
191
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
192
+ <script>
193
+ window.onload = function() {
194
+ const ui = SwaggerUIBundle({
195
+ spec: ${JSON.stringify(document)},
196
+ dom_id: '#swagger-ui',
197
+ deepLinking: true,
198
+ presets: [
199
+ SwaggerUIBundle.presets.apis,
200
+ SwaggerUIStandalonePreset
201
+ ],
202
+ plugins: [
203
+ SwaggerUIBundle.plugins.DownloadUrl
204
+ ],
205
+ layout: "StandaloneLayout"
206
+ });
207
+ window.ui = ui;
208
+ };
209
+ </script>
210
+ </body>
211
+ </html>`;
212
+ return c.html(html);
213
+ });
214
+ }
215
+ };
216
+
217
+ exports.ApiOperation = ApiOperation;
218
+ exports.ApiProperty = ApiProperty;
219
+ exports.ApiResponse = ApiResponse;
220
+ exports.ApiTags = ApiTags;
221
+ exports.DECORATORS = DECORATORS;
222
+ exports.DECORATORS_PREFIX = DECORATORS_PREFIX;
223
+ exports.DocumentBuilder = DocumentBuilder;
224
+ exports.SwaggerModule = SwaggerModule;
225
+ //# sourceMappingURL=index.cjs.map
226
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/document-builder.ts","../src/decorators.ts","../src/explorers/api-scanner.ts","../src/swagger-module.ts"],"names":["METADATA_KEYS"],"mappings":"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","file":"index.cjs","sourcesContent":["export class DocumentBuilder {\n private readonly document: any = {\n openapi: '3.0.0',\n info: {\n title: '',\n description: '',\n version: '1.0.0',\n contact: {},\n },\n tags: [],\n servers: [],\n paths: {},\n components: {\n securitySchemes: {},\n },\n };\n\n setTitle(title: string): this {\n this.document.info.title = title;\n return this;\n }\n\n setDescription(description: string): this {\n this.document.info.description = description;\n return this;\n }\n\n setVersion(version: string): this {\n this.document.info.version = version;\n return this;\n }\n\n setContact(name: string, url: string, email: string): this {\n this.document.info.contact = { name, url, email };\n return this;\n }\n\n addServer(url: string, description?: string): this {\n this.document.servers.push({ url, description });\n return this;\n }\n\n addTag(name: string, description?: string): this {\n this.document.tags.push({ name, description });\n return this;\n }\n\n addBearerAuth(options: any = {}, name = 'bearer'): this {\n this.document.components.securitySchemes[name] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n ...options,\n };\n return this;\n }\n\n build(): any {\n return this.document;\n }\n}\n","import 'reflect-metadata';\n\nexport const DECORATORS_PREFIX = 'swagger';\nexport const DECORATORS = {\n API_TAGS: `${DECORATORS_PREFIX}/apiTags`,\n API_OPERATION: `${DECORATORS_PREFIX}/apiOperation`,\n API_RESPONSE: `${DECORATORS_PREFIX}/apiResponse`,\n API_PARAM: `${DECORATORS_PREFIX}/apiParam`,\n API_BODY: `${DECORATORS_PREFIX}/apiBody`,\n API_PROPERTY: `${DECORATORS_PREFIX}/apiProperty`,\n};\n\nexport function ApiTags(...tags: string[]): ClassDecorator {\n return (target: any) => {\n Reflect.defineMetadata(DECORATORS.API_TAGS, tags, target);\n };\n}\n\nexport function ApiOperation(options: { summary?: string; description?: string; deprecated?: boolean }): MethodDecorator {\n return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n Reflect.defineMetadata(DECORATORS.API_OPERATION, options, descriptor.value);\n return descriptor;\n };\n}\n\nexport function ApiResponse(options: { status: number; description: string; type?: any }): MethodDecorator {\n return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const responses = Reflect.getMetadata(DECORATORS.API_RESPONSE, descriptor.value) || {};\n responses[options.status] = options;\n Reflect.defineMetadata(DECORATORS.API_RESPONSE, responses, descriptor.value);\n return descriptor;\n };\n}\n\nexport function ApiProperty(options: { description?: string; type?: any; required?: boolean; example?: any } = {}): PropertyDecorator {\n return (target: any, propertyKey: string | symbol) => {\n const properties = Reflect.getMetadata(DECORATORS.API_PROPERTY, target.constructor) || [];\n properties.push({ key: propertyKey, ...options });\n Reflect.defineMetadata(DECORATORS.API_PROPERTY, properties, target.constructor);\n };\n}\n","import { Container, METADATA_KEYS, RequestMethod } from '@hono-di/core';\nimport { DECORATORS } from '../decorators';\nimport { SwaggerDocument } from '../interfaces';\n\nexport class SwaggerScanner {\n constructor(private readonly container: Container) { }\n\n scan(document: SwaggerDocument): SwaggerDocument {\n const modules = this.container.getModules();\n\n modules.forEach((module) => {\n module.controllers.forEach((wrapper) => {\n this.scanController(wrapper.metatype, document);\n });\n });\n\n return document;\n }\n\n private scanController(controller: any, document: SwaggerDocument) {\n if (!controller) return;\n\n const { prefix } = Reflect.getMetadata(METADATA_KEYS.CONTROLLER, controller) || { prefix: '' };\n const routes = Reflect.getMetadata(METADATA_KEYS.ROUTES, controller) || [];\n const apiTags = Reflect.getMetadata(DECORATORS.API_TAGS, controller) || [];\n\n // Add tags to document\n apiTags.forEach((tag: string) => {\n if (!document.tags.find(t => t.name === tag)) {\n document.tags.push({ name: tag });\n }\n });\n\n routes.forEach((route: any) => {\n const method = route.requestMethod;\n const path = this.normalizePath(prefix, route.path);\n const descriptor = Object.getOwnPropertyDescriptor(controller.prototype, route.methodName);\n const handler = descriptor?.value;\n\n if (!handler) return;\n\n const operation = this.createOperation(handler, apiTags);\n\n if (!document.paths[path]) {\n document.paths[path] = {};\n }\n document.paths[path][method] = operation;\n });\n }\n\n private createOperation(handler: any, controllerTags: string[]) {\n const apiOperation = Reflect.getMetadata(DECORATORS.API_OPERATION, handler) || {};\n const apiResponses = Reflect.getMetadata(DECORATORS.API_RESPONSE, handler) || {};\n\n const responses: Record<string, any> = {};\n Object.keys(apiResponses).forEach(status => {\n responses[status] = {\n description: apiResponses[status].description,\n };\n });\n\n // Default response if none provided\n if (Object.keys(responses).length === 0) {\n responses['200'] = { description: 'Successful operation' };\n }\n\n return {\n summary: apiOperation.summary || '',\n description: apiOperation.description || '',\n tags: controllerTags,\n responses,\n // TODO: Add parameters and request body scanning\n };\n }\n\n private normalizePath(prefix: string, path: string): string {\n const cleanPrefix = prefix ? prefix.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n const cleanPath = path ? path.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n let result = '';\n if (cleanPrefix) result += `/${cleanPrefix}`;\n if (cleanPath) result += `/${cleanPath}`;\n return (result || '/').replace(/:([^\\/]+)/g, '{$1}'); // Convert :param to {param}\n }\n}\n","import { HonoDiApplication } from '@hono-di/core';\nimport { DocumentBuilder } from './document-builder';\nimport { SwaggerDocument, SwaggerCustomOptions } from './interfaces';\nimport { SwaggerScanner } from './explorers/api-scanner';\n\nexport class SwaggerModule {\n static createDocument(app: HonoDiApplication, config: SwaggerDocument): SwaggerDocument {\n const container = app.getContainer();\n const scanner = new SwaggerScanner(container);\n const document = config;\n return scanner.scan(document);\n }\n\n static setup(path: string, app: HonoDiApplication, document: SwaggerDocument, options?: SwaggerCustomOptions) {\n const httpAdapter = app.getHttpAdapter();\n\n // Serve Swagger JSON\n httpAdapter.get(`${path}-json`, (c: any) => c.json(document));\n\n // Serve Swagger UI\n httpAdapter.get(path, (c: any) => {\n const html = `\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <title>${document.info.title}</title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n <style>\n html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }\n *, *:before, *:after { box-sizing: inherit; }\n body { margin: 0; background: #fafafa; }\n </style>\n </head>\n <body>\n <div id=\"swagger-ui\"></div>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\" charset=\"UTF-8\"> </script>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js\" charset=\"UTF-8\"> </script>\n <script>\n window.onload = function() {\n const ui = SwaggerUIBundle({\n spec: ${JSON.stringify(document)},\n dom_id: '#swagger-ui',\n deepLinking: true,\n presets: [\n SwaggerUIBundle.presets.apis,\n SwaggerUIStandalonePreset\n ],\n plugins: [\n SwaggerUIBundle.plugins.DownloadUrl\n ],\n layout: \"StandaloneLayout\"\n });\n window.ui = ui;\n };\n </script>\n </body>\n </html>`;\n return c.html(html);\n });\n }\n}\n"]}
@@ -0,0 +1,83 @@
1
+ import { HonoDiApplication } from '@hono-di/core';
2
+
3
+ declare class DocumentBuilder {
4
+ private readonly document;
5
+ setTitle(title: string): this;
6
+ setDescription(description: string): this;
7
+ setVersion(version: string): this;
8
+ setContact(name: string, url: string, email: string): this;
9
+ addServer(url: string, description?: string): this;
10
+ addTag(name: string, description?: string): this;
11
+ addBearerAuth(options?: any, name?: string): this;
12
+ build(): any;
13
+ }
14
+
15
+ interface SwaggerDocument {
16
+ openapi: string;
17
+ info: {
18
+ title: string;
19
+ description: string;
20
+ version: string;
21
+ contact?: {
22
+ name: string;
23
+ url: string;
24
+ email: string;
25
+ };
26
+ };
27
+ tags: {
28
+ name: string;
29
+ description?: string;
30
+ }[];
31
+ servers: {
32
+ url: string;
33
+ description?: string;
34
+ }[];
35
+ paths: Record<string, Record<string, any>>;
36
+ components: {
37
+ schemas: Record<string, any>;
38
+ securitySchemes: Record<string, any>;
39
+ };
40
+ }
41
+ interface SwaggerCustomOptions {
42
+ explorer?: boolean;
43
+ swaggerOptions?: Record<string, any>;
44
+ customCss?: string;
45
+ customCssUrl?: string;
46
+ customJs?: string;
47
+ customJsUrl?: string;
48
+ customSiteTitle?: string;
49
+ }
50
+
51
+ declare class SwaggerModule {
52
+ static createDocument(app: HonoDiApplication, config: SwaggerDocument): SwaggerDocument;
53
+ static setup(path: string, app: HonoDiApplication, document: SwaggerDocument, options?: SwaggerCustomOptions): void;
54
+ }
55
+
56
+ declare const DECORATORS_PREFIX = "swagger";
57
+ declare const DECORATORS: {
58
+ API_TAGS: string;
59
+ API_OPERATION: string;
60
+ API_RESPONSE: string;
61
+ API_PARAM: string;
62
+ API_BODY: string;
63
+ API_PROPERTY: string;
64
+ };
65
+ declare function ApiTags(...tags: string[]): ClassDecorator;
66
+ declare function ApiOperation(options: {
67
+ summary?: string;
68
+ description?: string;
69
+ deprecated?: boolean;
70
+ }): MethodDecorator;
71
+ declare function ApiResponse(options: {
72
+ status: number;
73
+ description: string;
74
+ type?: any;
75
+ }): MethodDecorator;
76
+ declare function ApiProperty(options?: {
77
+ description?: string;
78
+ type?: any;
79
+ required?: boolean;
80
+ example?: any;
81
+ }): PropertyDecorator;
82
+
83
+ export { ApiOperation, ApiProperty, ApiResponse, ApiTags, DECORATORS, DECORATORS_PREFIX, DocumentBuilder, type SwaggerCustomOptions, type SwaggerDocument, SwaggerModule };
@@ -0,0 +1,83 @@
1
+ import { HonoDiApplication } from '@hono-di/core';
2
+
3
+ declare class DocumentBuilder {
4
+ private readonly document;
5
+ setTitle(title: string): this;
6
+ setDescription(description: string): this;
7
+ setVersion(version: string): this;
8
+ setContact(name: string, url: string, email: string): this;
9
+ addServer(url: string, description?: string): this;
10
+ addTag(name: string, description?: string): this;
11
+ addBearerAuth(options?: any, name?: string): this;
12
+ build(): any;
13
+ }
14
+
15
+ interface SwaggerDocument {
16
+ openapi: string;
17
+ info: {
18
+ title: string;
19
+ description: string;
20
+ version: string;
21
+ contact?: {
22
+ name: string;
23
+ url: string;
24
+ email: string;
25
+ };
26
+ };
27
+ tags: {
28
+ name: string;
29
+ description?: string;
30
+ }[];
31
+ servers: {
32
+ url: string;
33
+ description?: string;
34
+ }[];
35
+ paths: Record<string, Record<string, any>>;
36
+ components: {
37
+ schemas: Record<string, any>;
38
+ securitySchemes: Record<string, any>;
39
+ };
40
+ }
41
+ interface SwaggerCustomOptions {
42
+ explorer?: boolean;
43
+ swaggerOptions?: Record<string, any>;
44
+ customCss?: string;
45
+ customCssUrl?: string;
46
+ customJs?: string;
47
+ customJsUrl?: string;
48
+ customSiteTitle?: string;
49
+ }
50
+
51
+ declare class SwaggerModule {
52
+ static createDocument(app: HonoDiApplication, config: SwaggerDocument): SwaggerDocument;
53
+ static setup(path: string, app: HonoDiApplication, document: SwaggerDocument, options?: SwaggerCustomOptions): void;
54
+ }
55
+
56
+ declare const DECORATORS_PREFIX = "swagger";
57
+ declare const DECORATORS: {
58
+ API_TAGS: string;
59
+ API_OPERATION: string;
60
+ API_RESPONSE: string;
61
+ API_PARAM: string;
62
+ API_BODY: string;
63
+ API_PROPERTY: string;
64
+ };
65
+ declare function ApiTags(...tags: string[]): ClassDecorator;
66
+ declare function ApiOperation(options: {
67
+ summary?: string;
68
+ description?: string;
69
+ deprecated?: boolean;
70
+ }): MethodDecorator;
71
+ declare function ApiResponse(options: {
72
+ status: number;
73
+ description: string;
74
+ type?: any;
75
+ }): MethodDecorator;
76
+ declare function ApiProperty(options?: {
77
+ description?: string;
78
+ type?: any;
79
+ required?: boolean;
80
+ example?: any;
81
+ }): PropertyDecorator;
82
+
83
+ export { ApiOperation, ApiProperty, ApiResponse, ApiTags, DECORATORS, DECORATORS_PREFIX, DocumentBuilder, type SwaggerCustomOptions, type SwaggerDocument, SwaggerModule };
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ import { METADATA_KEYS } from '@hono-di/core';
2
+ import 'reflect-metadata';
3
+
4
+ // src/document-builder.ts
5
+ var DocumentBuilder = class {
6
+ document = {
7
+ openapi: "3.0.0",
8
+ info: {
9
+ title: "",
10
+ description: "",
11
+ version: "1.0.0",
12
+ contact: {}
13
+ },
14
+ tags: [],
15
+ servers: [],
16
+ paths: {},
17
+ components: {
18
+ securitySchemes: {}
19
+ }
20
+ };
21
+ setTitle(title) {
22
+ this.document.info.title = title;
23
+ return this;
24
+ }
25
+ setDescription(description) {
26
+ this.document.info.description = description;
27
+ return this;
28
+ }
29
+ setVersion(version) {
30
+ this.document.info.version = version;
31
+ return this;
32
+ }
33
+ setContact(name, url, email) {
34
+ this.document.info.contact = { name, url, email };
35
+ return this;
36
+ }
37
+ addServer(url, description) {
38
+ this.document.servers.push({ url, description });
39
+ return this;
40
+ }
41
+ addTag(name, description) {
42
+ this.document.tags.push({ name, description });
43
+ return this;
44
+ }
45
+ addBearerAuth(options = {}, name = "bearer") {
46
+ this.document.components.securitySchemes[name] = {
47
+ type: "http",
48
+ scheme: "bearer",
49
+ bearerFormat: "JWT",
50
+ ...options
51
+ };
52
+ return this;
53
+ }
54
+ build() {
55
+ return this.document;
56
+ }
57
+ };
58
+ var DECORATORS_PREFIX = "swagger";
59
+ var DECORATORS = {
60
+ API_TAGS: `${DECORATORS_PREFIX}/apiTags`,
61
+ API_OPERATION: `${DECORATORS_PREFIX}/apiOperation`,
62
+ API_RESPONSE: `${DECORATORS_PREFIX}/apiResponse`,
63
+ API_PARAM: `${DECORATORS_PREFIX}/apiParam`,
64
+ API_BODY: `${DECORATORS_PREFIX}/apiBody`,
65
+ API_PROPERTY: `${DECORATORS_PREFIX}/apiProperty`
66
+ };
67
+ function ApiTags(...tags) {
68
+ return (target) => {
69
+ Reflect.defineMetadata(DECORATORS.API_TAGS, tags, target);
70
+ };
71
+ }
72
+ function ApiOperation(options) {
73
+ return (target, propertyKey, descriptor) => {
74
+ Reflect.defineMetadata(DECORATORS.API_OPERATION, options, descriptor.value);
75
+ return descriptor;
76
+ };
77
+ }
78
+ function ApiResponse(options) {
79
+ return (target, propertyKey, descriptor) => {
80
+ const responses = Reflect.getMetadata(DECORATORS.API_RESPONSE, descriptor.value) || {};
81
+ responses[options.status] = options;
82
+ Reflect.defineMetadata(DECORATORS.API_RESPONSE, responses, descriptor.value);
83
+ return descriptor;
84
+ };
85
+ }
86
+ function ApiProperty(options = {}) {
87
+ return (target, propertyKey) => {
88
+ const properties = Reflect.getMetadata(DECORATORS.API_PROPERTY, target.constructor) || [];
89
+ properties.push({ key: propertyKey, ...options });
90
+ Reflect.defineMetadata(DECORATORS.API_PROPERTY, properties, target.constructor);
91
+ };
92
+ }
93
+
94
+ // src/explorers/api-scanner.ts
95
+ var SwaggerScanner = class {
96
+ constructor(container) {
97
+ this.container = container;
98
+ }
99
+ scan(document) {
100
+ const modules = this.container.getModules();
101
+ modules.forEach((module) => {
102
+ module.controllers.forEach((wrapper) => {
103
+ this.scanController(wrapper.metatype, document);
104
+ });
105
+ });
106
+ return document;
107
+ }
108
+ scanController(controller, document) {
109
+ if (!controller) return;
110
+ const { prefix } = Reflect.getMetadata(METADATA_KEYS.CONTROLLER, controller) || { prefix: "" };
111
+ const routes = Reflect.getMetadata(METADATA_KEYS.ROUTES, controller) || [];
112
+ const apiTags = Reflect.getMetadata(DECORATORS.API_TAGS, controller) || [];
113
+ apiTags.forEach((tag) => {
114
+ if (!document.tags.find((t) => t.name === tag)) {
115
+ document.tags.push({ name: tag });
116
+ }
117
+ });
118
+ routes.forEach((route) => {
119
+ const method = route.requestMethod;
120
+ const path = this.normalizePath(prefix, route.path);
121
+ const descriptor = Object.getOwnPropertyDescriptor(controller.prototype, route.methodName);
122
+ const handler = descriptor?.value;
123
+ if (!handler) return;
124
+ const operation = this.createOperation(handler, apiTags);
125
+ if (!document.paths[path]) {
126
+ document.paths[path] = {};
127
+ }
128
+ document.paths[path][method] = operation;
129
+ });
130
+ }
131
+ createOperation(handler, controllerTags) {
132
+ const apiOperation = Reflect.getMetadata(DECORATORS.API_OPERATION, handler) || {};
133
+ const apiResponses = Reflect.getMetadata(DECORATORS.API_RESPONSE, handler) || {};
134
+ const responses = {};
135
+ Object.keys(apiResponses).forEach((status) => {
136
+ responses[status] = {
137
+ description: apiResponses[status].description
138
+ };
139
+ });
140
+ if (Object.keys(responses).length === 0) {
141
+ responses["200"] = { description: "Successful operation" };
142
+ }
143
+ return {
144
+ summary: apiOperation.summary || "",
145
+ description: apiOperation.description || "",
146
+ tags: controllerTags,
147
+ responses
148
+ // TODO: Add parameters and request body scanning
149
+ };
150
+ }
151
+ normalizePath(prefix, path) {
152
+ const cleanPrefix = prefix ? prefix.replace(/^\/+/, "").replace(/\/+$/, "") : "";
153
+ const cleanPath = path ? path.replace(/^\/+/, "").replace(/\/+$/, "") : "";
154
+ let result = "";
155
+ if (cleanPrefix) result += `/${cleanPrefix}`;
156
+ if (cleanPath) result += `/${cleanPath}`;
157
+ return (result || "/").replace(/:([^\/]+)/g, "{$1}");
158
+ }
159
+ };
160
+
161
+ // src/swagger-module.ts
162
+ var SwaggerModule = class {
163
+ static createDocument(app, config) {
164
+ const container = app.getContainer();
165
+ const scanner = new SwaggerScanner(container);
166
+ const document = config;
167
+ return scanner.scan(document);
168
+ }
169
+ static setup(path, app, document, options) {
170
+ const httpAdapter = app.getHttpAdapter();
171
+ httpAdapter.get(`${path}-json`, (c) => c.json(document));
172
+ httpAdapter.get(path, (c) => {
173
+ const html = `
174
+ <!DOCTYPE html>
175
+ <html lang="en">
176
+ <head>
177
+ <meta charset="UTF-8">
178
+ <title>${document.info.title}</title>
179
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
180
+ <style>
181
+ html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
182
+ *, *:before, *:after { box-sizing: inherit; }
183
+ body { margin: 0; background: #fafafa; }
184
+ </style>
185
+ </head>
186
+ <body>
187
+ <div id="swagger-ui"></div>
188
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js" charset="UTF-8"> </script>
189
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
190
+ <script>
191
+ window.onload = function() {
192
+ const ui = SwaggerUIBundle({
193
+ spec: ${JSON.stringify(document)},
194
+ dom_id: '#swagger-ui',
195
+ deepLinking: true,
196
+ presets: [
197
+ SwaggerUIBundle.presets.apis,
198
+ SwaggerUIStandalonePreset
199
+ ],
200
+ plugins: [
201
+ SwaggerUIBundle.plugins.DownloadUrl
202
+ ],
203
+ layout: "StandaloneLayout"
204
+ });
205
+ window.ui = ui;
206
+ };
207
+ </script>
208
+ </body>
209
+ </html>`;
210
+ return c.html(html);
211
+ });
212
+ }
213
+ };
214
+
215
+ export { ApiOperation, ApiProperty, ApiResponse, ApiTags, DECORATORS, DECORATORS_PREFIX, DocumentBuilder, SwaggerModule };
216
+ //# sourceMappingURL=index.js.map
217
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/document-builder.ts","../src/decorators.ts","../src/explorers/api-scanner.ts","../src/swagger-module.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export class DocumentBuilder {\n private readonly document: any = {\n openapi: '3.0.0',\n info: {\n title: '',\n description: '',\n version: '1.0.0',\n contact: {},\n },\n tags: [],\n servers: [],\n paths: {},\n components: {\n securitySchemes: {},\n },\n };\n\n setTitle(title: string): this {\n this.document.info.title = title;\n return this;\n }\n\n setDescription(description: string): this {\n this.document.info.description = description;\n return this;\n }\n\n setVersion(version: string): this {\n this.document.info.version = version;\n return this;\n }\n\n setContact(name: string, url: string, email: string): this {\n this.document.info.contact = { name, url, email };\n return this;\n }\n\n addServer(url: string, description?: string): this {\n this.document.servers.push({ url, description });\n return this;\n }\n\n addTag(name: string, description?: string): this {\n this.document.tags.push({ name, description });\n return this;\n }\n\n addBearerAuth(options: any = {}, name = 'bearer'): this {\n this.document.components.securitySchemes[name] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n ...options,\n };\n return this;\n }\n\n build(): any {\n return this.document;\n }\n}\n","import 'reflect-metadata';\n\nexport const DECORATORS_PREFIX = 'swagger';\nexport const DECORATORS = {\n API_TAGS: `${DECORATORS_PREFIX}/apiTags`,\n API_OPERATION: `${DECORATORS_PREFIX}/apiOperation`,\n API_RESPONSE: `${DECORATORS_PREFIX}/apiResponse`,\n API_PARAM: `${DECORATORS_PREFIX}/apiParam`,\n API_BODY: `${DECORATORS_PREFIX}/apiBody`,\n API_PROPERTY: `${DECORATORS_PREFIX}/apiProperty`,\n};\n\nexport function ApiTags(...tags: string[]): ClassDecorator {\n return (target: any) => {\n Reflect.defineMetadata(DECORATORS.API_TAGS, tags, target);\n };\n}\n\nexport function ApiOperation(options: { summary?: string; description?: string; deprecated?: boolean }): MethodDecorator {\n return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n Reflect.defineMetadata(DECORATORS.API_OPERATION, options, descriptor.value);\n return descriptor;\n };\n}\n\nexport function ApiResponse(options: { status: number; description: string; type?: any }): MethodDecorator {\n return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const responses = Reflect.getMetadata(DECORATORS.API_RESPONSE, descriptor.value) || {};\n responses[options.status] = options;\n Reflect.defineMetadata(DECORATORS.API_RESPONSE, responses, descriptor.value);\n return descriptor;\n };\n}\n\nexport function ApiProperty(options: { description?: string; type?: any; required?: boolean; example?: any } = {}): PropertyDecorator {\n return (target: any, propertyKey: string | symbol) => {\n const properties = Reflect.getMetadata(DECORATORS.API_PROPERTY, target.constructor) || [];\n properties.push({ key: propertyKey, ...options });\n Reflect.defineMetadata(DECORATORS.API_PROPERTY, properties, target.constructor);\n };\n}\n","import { Container, METADATA_KEYS, RequestMethod } from '@hono-di/core';\nimport { DECORATORS } from '../decorators';\nimport { SwaggerDocument } from '../interfaces';\n\nexport class SwaggerScanner {\n constructor(private readonly container: Container) { }\n\n scan(document: SwaggerDocument): SwaggerDocument {\n const modules = this.container.getModules();\n\n modules.forEach((module) => {\n module.controllers.forEach((wrapper) => {\n this.scanController(wrapper.metatype, document);\n });\n });\n\n return document;\n }\n\n private scanController(controller: any, document: SwaggerDocument) {\n if (!controller) return;\n\n const { prefix } = Reflect.getMetadata(METADATA_KEYS.CONTROLLER, controller) || { prefix: '' };\n const routes = Reflect.getMetadata(METADATA_KEYS.ROUTES, controller) || [];\n const apiTags = Reflect.getMetadata(DECORATORS.API_TAGS, controller) || [];\n\n // Add tags to document\n apiTags.forEach((tag: string) => {\n if (!document.tags.find(t => t.name === tag)) {\n document.tags.push({ name: tag });\n }\n });\n\n routes.forEach((route: any) => {\n const method = route.requestMethod;\n const path = this.normalizePath(prefix, route.path);\n const descriptor = Object.getOwnPropertyDescriptor(controller.prototype, route.methodName);\n const handler = descriptor?.value;\n\n if (!handler) return;\n\n const operation = this.createOperation(handler, apiTags);\n\n if (!document.paths[path]) {\n document.paths[path] = {};\n }\n document.paths[path][method] = operation;\n });\n }\n\n private createOperation(handler: any, controllerTags: string[]) {\n const apiOperation = Reflect.getMetadata(DECORATORS.API_OPERATION, handler) || {};\n const apiResponses = Reflect.getMetadata(DECORATORS.API_RESPONSE, handler) || {};\n\n const responses: Record<string, any> = {};\n Object.keys(apiResponses).forEach(status => {\n responses[status] = {\n description: apiResponses[status].description,\n };\n });\n\n // Default response if none provided\n if (Object.keys(responses).length === 0) {\n responses['200'] = { description: 'Successful operation' };\n }\n\n return {\n summary: apiOperation.summary || '',\n description: apiOperation.description || '',\n tags: controllerTags,\n responses,\n // TODO: Add parameters and request body scanning\n };\n }\n\n private normalizePath(prefix: string, path: string): string {\n const cleanPrefix = prefix ? prefix.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n const cleanPath = path ? path.replace(/^\\/+/, '').replace(/\\/+$/, '') : '';\n let result = '';\n if (cleanPrefix) result += `/${cleanPrefix}`;\n if (cleanPath) result += `/${cleanPath}`;\n return (result || '/').replace(/:([^\\/]+)/g, '{$1}'); // Convert :param to {param}\n }\n}\n","import { HonoDiApplication } from '@hono-di/core';\nimport { DocumentBuilder } from './document-builder';\nimport { SwaggerDocument, SwaggerCustomOptions } from './interfaces';\nimport { SwaggerScanner } from './explorers/api-scanner';\n\nexport class SwaggerModule {\n static createDocument(app: HonoDiApplication, config: SwaggerDocument): SwaggerDocument {\n const container = app.getContainer();\n const scanner = new SwaggerScanner(container);\n const document = config;\n return scanner.scan(document);\n }\n\n static setup(path: string, app: HonoDiApplication, document: SwaggerDocument, options?: SwaggerCustomOptions) {\n const httpAdapter = app.getHttpAdapter();\n\n // Serve Swagger JSON\n httpAdapter.get(`${path}-json`, (c: any) => c.json(document));\n\n // Serve Swagger UI\n httpAdapter.get(path, (c: any) => {\n const html = `\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <title>${document.info.title}</title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n <style>\n html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }\n *, *:before, *:after { box-sizing: inherit; }\n body { margin: 0; background: #fafafa; }\n </style>\n </head>\n <body>\n <div id=\"swagger-ui\"></div>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\" charset=\"UTF-8\"> </script>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js\" charset=\"UTF-8\"> </script>\n <script>\n window.onload = function() {\n const ui = SwaggerUIBundle({\n spec: ${JSON.stringify(document)},\n dom_id: '#swagger-ui',\n deepLinking: true,\n presets: [\n SwaggerUIBundle.presets.apis,\n SwaggerUIStandalonePreset\n ],\n plugins: [\n SwaggerUIBundle.plugins.DownloadUrl\n ],\n layout: \"StandaloneLayout\"\n });\n window.ui = ui;\n };\n </script>\n </body>\n </html>`;\n return c.html(html);\n });\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@hono-di/swagger",
3
+ "version": "0.0.6",
4
+ "description": "OpenAPI (Swagger) module for Hono-DI",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "test": "bun test"
22
+ },
23
+ "dependencies": {
24
+ "@hono-di/core": "0.0.6",
25
+ "reflect-metadata": "^0.2.2"
26
+ },
27
+ "peerDependencies": {
28
+ "hono": "^4"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.5.1",
32
+ "typescript": "^5.4.0",
33
+ "@types/node": "^22.10.6"
34
+ }
35
+ }