@hedystia/swagger 1.5.3 → 1.6.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.
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ interface SwaggerOptions {
22
22
  }
23
23
  declare class Swagger {
24
24
  private spec;
25
+ host: string;
25
26
  constructor(options?: SwaggerOptions);
26
27
  addRoute(method: string, path: string, schema: any, summary?: string, description?: string, tags?: string[]): void;
27
28
  private buildParameters;
@@ -38,7 +39,7 @@ declare class Swagger {
38
39
  * @returns Plugin instance
39
40
  */
40
41
  declare function swagger(options?: SwaggerOptions): {
41
- plugin: Hedystia<[{
42
+ plugin: (app: Hedystia<any, any>) => Hedystia<[{
42
43
  method: "GET";
43
44
  path: "/";
44
45
  params: any;
@@ -56,7 +57,6 @@ declare function swagger(options?: SwaggerOptions): {
56
57
  error: any;
57
58
  }], {}>;
58
59
  swagger: Swagger;
59
- captureRoutes: (app: any) => void;
60
60
  };
61
61
 
62
62
  export { swagger };
package/dist/index.js CHANGED
@@ -1,204 +1,545 @@
1
- "use strict";var h=Object.create;var a=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var b=Object.getPrototypeOf,w=Object.prototype.hasOwnProperty;var x=(i,e)=>{for(var s in e)a(i,s,{get:e[s],enumerable:!0})},g=(i,e,s,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of y(e))!w.call(i,r)&&r!==s&&a(i,r,{get:()=>e[r],enumerable:!(t=f(e,r))||t.enumerable});return i};var v=(i,e,s)=>(s=i!=null?h(b(i)):{},g(e||!i||!i.__esModule?a(s,"default",{value:i,enumerable:!0}):s,i)),S=i=>g(a({},"__esModule",{value:!0}),i);var j={};x(j,{swagger:()=>k});module.exports=S(j);var u=require("hedystia");var l=v(require("@apidevtools/swagger-parser")),o=class{spec={openapi:"3.0.0",info:{title:"API Documentation",version:"1.0.0"},paths:{},components:{schemas:{}}};constructor(e={}){this.spec.info.title=e.title||"API Documentation",this.spec.info.description=e.description,this.spec.info.version=e.version||"1.0.0",e.tags&&(this.spec.tags=e.tags),e.externalDocs&&(this.spec.externalDocs=e.externalDocs),e.securityDefinitions&&(this.spec.components=this.spec.components||{},this.spec.components.securitySchemes=e.securityDefinitions)}addRoute(e,s,t,r,n,d){if(!t)return;let c=s.replace(/:([^/]+)/g,"{$1}");this.spec.paths||(this.spec.paths={}),this.spec.paths[c]||(this.spec.paths[c]={});let m=e.toLowerCase(),p={summary:r||`${e} ${s}`,parameters:this.buildParameters(t),requestBody:this.buildRequestBody(t),responses:this.buildResponses(t)};n&&(p.description=n),d&&(p.tags=d),this.spec.paths[c][m]=p}buildParameters(e){let s=[];if(e.params)try{let t=e.params;t.properties&&Object.entries(t.properties).forEach(([r,n])=>{s.push({name:r,in:"path",required:t.required?.includes(r)??!0,schema:n})})}catch(t){console.error("Failed to convert params schema:",t)}if(e.query)try{let t=e.query;t.properties&&Object.entries(t.properties).forEach(([r,n])=>{s.push({name:r,in:"query",required:t.required?.includes(r)??!1,schema:n})})}catch(t){console.error("Failed to convert query schema:",t)}return s.length>0?s:void 0}buildRequestBody(e){if(e.body)try{return{required:!0,content:{"application/json":{schema:e.body}}}}catch(s){console.error("Failed to convert body schema:",s);return}}buildResponses(e){let s={200:{description:"Successful response"}};if(e.response)try{let t=e.response;s[200].content={"application/json":{schema:t}}}catch(t){console.error("Failed to convert response schema:",t)}return s}async validate(){try{return await l.default.validate(this.spec),!0}catch(e){return console.error("Swagger validation error:",e),!1}}getSpec(){return this.spec}generateHTML(){return`
2
- <!DOCTYPE html>
3
- <html lang="en">
1
+ "use strict";var P=Object.create;var k=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var z=(n,e)=>{for(var r in e)k(n,r,{get:e[r],enumerable:!0})},C=(n,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of I(e))!D.call(n,i)&&i!==r&&k(n,i,{get:()=>e[i],enumerable:!(t=R(e,i))||t.enumerable});return n};var M=(n,e,r)=>(r=n!=null?P(H(n)):{},C(e||!n||!n.__esModule?k(r,"default",{value:n,enumerable:!0}):r,n)),U=n=>C(k({},"__esModule",{value:!0}),n);var W={};z(W,{swagger:()=>G});module.exports=U(W);var B=require("hedystia");var T=M(require("@apidevtools/swagger-parser")),w=class{spec={openapi:"3.0.0",info:{title:"API Documentation",version:"1.0.0"},paths:{},components:{schemas:{}}};host;constructor(e={}){this.spec.info.title=e.title||"API Documentation",this.spec.info.description=e.description,this.spec.info.version=e.version||"1.0.0",this.host=e.host||"https://example.com",e.tags&&(this.spec.tags=e.tags),e.externalDocs&&(this.spec.externalDocs=e.externalDocs),e.securityDefinitions&&(this.spec.components=this.spec.components||{},this.spec.components.securitySchemes=e.securityDefinitions)}addRoute(e,r,t,i,s,p){if(!t)return;let o=r.replace(/:([^/]+)/g,"{$1}");this.spec.paths||(this.spec.paths={}),this.spec.paths[o]||(this.spec.paths[o]={});let m=e.toLowerCase(),b={summary:i||`${e} ${r}`,parameters:this.buildParameters(t),requestBody:this.buildRequestBody(t),responses:this.buildResponses(t)};s&&(b.description=s),p&&(b.tags=p),this.spec.paths[o][m]=b}buildParameters(e){let r=[];if(e.params)try{let t=e.params;t.properties&&Object.entries(t.properties).forEach(([i,s])=>{r.push({name:i,in:"path",required:t.required?.includes(i)??!0,schema:s})})}catch(t){console.error("Failed to convert params schema:",t)}if(e.query)try{let t=e.query;t.properties&&Object.entries(t.properties).forEach(([i,s])=>{r.push({name:i,in:"query",required:t.required?.includes(i)??!1,schema:s})})}catch(t){console.error("Failed to convert query schema:",t)}return r.length>0?r:void 0}buildRequestBody(e){if(e.body)try{return{required:!0,content:{"application/json":{schema:e.body}}}}catch(r){console.error("Failed to convert body schema:",r);return}}buildResponses(e){let r={200:{description:"Successful response"}};if(e.response)try{let t=e.response;r[200].content={"application/json":{schema:t}}}catch(t){console.error("Failed to convert response schema:",t)}return r}async validate(){try{return await T.default.validate(this.spec),!0}catch(e){return console.error("Swagger validation error:",e),!1}}getSpec(){return this.spec}generateHTML(){let e=new URL(this.host).hostname,r=o=>o?o.jsonSchema?o.jsonSchema:o.type||o.properties||o.$ref?o:{}:{},t={},i="API";Object.entries(this.spec.paths||{}).forEach(([o,m])=>{Object.entries(m).forEach(([b,y])=>{let l=(y.tags||[i])[0];t[l]||(t[l]=[]),t[l].push({path:o,method:b.toUpperCase(),operation:y})})});let s=Object.entries(t).map(([o,m])=>{let b=m.length,y=m.map(({path:x,method:l})=>`<li><a href="#${`${l.toLowerCase()}-${x.replace(/[^a-zA-Z0-9]/g,"-")}`}" class="block px-3 py-1.5 rounded-lg hover:bg-dark-700/50 transition-colors text-sm text-gray-400 hover:text-white">${l} ${x}</a></li>`).join(`
2
+ `);return`
3
+ <div>
4
+ <button class="sidebar-group-toggle w-full flex items-center justify-between px-3 py-2 hover:bg-dark-700/30 rounded-lg transition-colors group" data-target="${o.toLowerCase()}-group">
5
+ <div class="flex items-center space-x-2">
6
+ <h3 class="text-sm font-medium text-gray-300 group-hover:text-white">${o}</h3>
7
+ <span class="text-xs text-gray-500 bg-dark-600 px-2 py-0.5 rounded-full">${b}</span>
8
+ <svg class="w-4 h-4 text-gray-400 transform transition-transform duration-200 group-hover:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
10
+ </svg>
11
+ </div>
12
+ </button>
13
+ <div id="${o.toLowerCase()}-group" class="sidebar-group-content max-h-0 overflow-hidden transition-all duration-300">
14
+ <ul class="ml-6 mt-1 space-y-1">
15
+ ${y}
16
+ </ul>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="border-t border-dark-600 my-3"></div>`}).join(""),p=Object.entries(t).map(([o,m])=>{let b=this.spec.tags?.find(l=>l.name===o)?.description||`Manage ${o.toLowerCase()} operations`,y=m.map(({path:l,method:g,operation:c})=>{let L=`${g.toLowerCase()}-${l.replace(/[^a-zA-Z0-9]/g,"-")}`,$={GET:"bg-green-600",POST:"bg-blue-600",PUT:"bg-yellow-600",DELETE:"bg-red-600",PATCH:"bg-purple-600",WS:"bg-indigo-600",SUB:"bg-pink-600"}[g]||"bg-gray-600";return`
21
+ <a href="#${L}" class="block p-4 rounded-lg border border-dark-600 hover:border-blue-500 hover:bg-dark-700/30 transition-all group">
22
+ <div class="flex items-center justify-between mb-2">
23
+ <div class="flex items-center space-x-3">
24
+ <span class="${$} text-white px-3 py-1 rounded-lg text-sm font-medium">${g}</span>
25
+ <code class="text-blue-400 font-mono">${l}</code>
26
+ </div>
27
+ <svg class="w-5 h-5 text-gray-400 group-hover:text-blue-400 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
28
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
29
+ </svg>
30
+ </div>
31
+ <p class="text-gray-300 text-sm">${c.summary||c.description||`${g} ${l} operation`}</p>
32
+ </a>`}).join(`
33
+ `),x=m.map(({path:l,method:g,operation:c})=>{let L=`${g.toLowerCase()}-${l.replace(/[^a-zA-Z0-9]/g,"-")}`,$={GET:"bg-green-600",POST:"bg-blue-600",PUT:"bg-yellow-600",DELETE:"bg-red-600",PATCH:"bg-purple-600",WS:"bg-indigo-600",SUB:"bg-pink-600"}[g]||"bg-gray-600",S="";if(c.parameters&&c.parameters.length>0){let d=c.parameters.filter(a=>a.in==="path"),u=c.parameters.filter(a=>a.in==="query");d.length>0&&(S+=`
34
+ <div>
35
+ <h4 class="text-lg font-semibold text-white mb-3">Path Parameters</h4>
36
+ <div class="space-y-3">
37
+ ${d.map(a=>`
38
+ <div class="border border-dark-600 rounded-lg p-3">
39
+ <div class="flex items-center justify-between mb-1">
40
+ <code class="text-blue-400">${a.name}</code>
41
+ <span class="text-xs ${a.required?"text-red-400":"text-gray-400"}">${a.required?"required":"optional"}</span>
42
+ </div>
43
+ <p class="text-sm text-gray-300">${a.description||`${a.name} parameter`}</p>
44
+ </div>`).join("")}
45
+ </div>
46
+ </div>`),u.length>0&&(S+=`
47
+ <div>
48
+ <h4 class="text-lg font-semibold text-white mb-3">Query Parameters</h4>
49
+ <div class="space-y-3">
50
+ ${u.map(a=>`
51
+ <div class="border border-dark-600 rounded-lg p-3">
52
+ <div class="flex items-center justify-between mb-1">
53
+ <code class="text-blue-400">${a.name}</code>
54
+ <span class="text-xs ${a.required?"text-red-400":"text-gray-400"}">${a.required?"required":"optional"}</span>
55
+ </div>
56
+ <p class="text-sm text-gray-300">${a.description||`${a.name} parameter`}</p>
57
+ </div>`).join("")}
58
+ </div>
59
+ </div>`)}let q="";if(c.requestBody){let d=c.requestBody.content?.["application/json"]?.schema,u=r(d);u.properties&&(q=`
60
+ <div>
61
+ <h4 class="text-lg font-semibold text-white mb-3">Body Parameters</h4>
62
+ <div class="space-y-3">
63
+ ${Object.entries(u.properties).map(([a,h])=>`
64
+ <div class="border border-dark-600 rounded-lg p-3">
65
+ <div class="flex items-center justify-between mb-1">
66
+ <code class="text-blue-400">${a}</code>
67
+ <span class="text-xs ${u.required?.includes(a)?"text-red-400":"text-gray-400"}">${u.required?.includes(a)?"required":"optional"}</span>
68
+ </div>
69
+ <p class="text-sm text-gray-300">${h.description||`${a} field`}</p>
70
+ </div>`).join("")}
71
+ </div>
72
+ </div>`)}let O=`https://${e}`,E="";if(g!=="WS"&&g!=="SUB"){let d=`curl -X ${g} '${O}${l}'`;if(c.requestBody){d+=` \\
73
+ -H 'Content-Type: application/json'`;let u=c.requestBody.content?.["application/json"]?.schema,a=r(u);if(a.properties){let h={};Object.entries(a.properties).forEach(([f,v])=>{v.type==="string"?h[f]=v.format==="email"?"user@example.com":`example ${f}`:v.type==="number"?h[f]=123:v.type==="boolean"?h[f]=!0:h[f]=`example ${f}`}),d+=` \\
74
+ -d '${JSON.stringify(h,null,2).replace(/\n/g,"\\n ")}'`}}d+=` \\
75
+ -H 'Authorization: Bearer your-token'`,E=`
76
+ <div>
77
+ <div class="flex items-center justify-between mb-4">
78
+ <h4 class="text-lg font-semibold text-white">Request Example</h4>
79
+ <button class="copy-btn bg-dark-700 hover:bg-dark-600 px-3 py-1 rounded-lg text-sm transition-colors" data-copy="${d.replace(/'/g,"\\'")}">
80
+ Copy
81
+ </button>
82
+ </div>
83
+ <div class="code-block rounded-xl p-4 border border-dark-600">
84
+ <pre class="text-sm text-gray-300 overflow-x-auto"><code>${d}</code></pre>
85
+ </div>
86
+ </div>`}let A="",j=c.responses?.["200"];if(j?.content?.["application/json"]?.schema){let d=j.content["application/json"].schema,u=r(d);A=`
87
+ <div>
88
+ <h4 class="text-lg font-semibold text-white mb-4">Response Schema</h4>
89
+ <div class="code-block rounded-xl p-4 border border-dark-600">
90
+ <pre class="text-sm text-gray-300 overflow-x-auto"><code>${JSON.stringify(u,null,2)}</code></pre>
91
+ </div>
92
+ </div>`}return`
93
+ <section id="${L}" class="fade-in">
94
+ <div class="grid lg:grid-cols-3 gap-8">
95
+ <div class="lg:col-span-1 space-y-6">
96
+ <div>
97
+ <div class="flex items-center space-x-3 mb-4">
98
+ <span class="${$} text-white px-3 py-1 rounded-lg text-sm font-medium">${g}</span>
99
+ <code class="text-blue-400 font-mono">${l}</code>
100
+ </div>
101
+ <p class="text-gray-300">${c.description||c.summary||`${g} ${l} operation`}</p>
102
+ </div>
103
+
104
+ ${S}
105
+ ${q}
106
+
107
+ <div>
108
+ <h4 class="text-lg font-semibold text-white mb-3">Headers</h4>
109
+ <div class="border border-dark-600 rounded-lg p-3">
110
+ <div class="flex items-center justify-between mb-1">
111
+ <code class="text-blue-400">Authorization</code>
112
+ <span class="text-xs text-red-400">required</span>
113
+ </div>
114
+ <p class="text-sm text-gray-300">Bearer token for authentication</p>
115
+ </div>
116
+ </div>
117
+
118
+ <div>
119
+ <h4 class="text-lg font-semibold text-white mb-3">Responses</h4>
120
+ <div class="space-y-2">
121
+ ${Object.entries(c.responses||{}).map(([d,u])=>`
122
+ <div class="flex items-center space-x-3">
123
+ <span class="${d.startsWith("2")?"bg-green-600":d.startsWith("4")?"bg-red-600":"bg-yellow-600"} text-white px-2 py-1 rounded text-xs">${d}</span>
124
+ <span class="text-gray-300 text-sm">${u.description||"Response"}</span>
125
+ </div>`).join("")}
126
+ </div>
127
+ </div>
128
+ </div>
129
+
130
+ <div class="lg:col-span-2 space-y-6">
131
+ ${E}
132
+ ${A}
133
+ </div>
134
+ </div>
135
+ </section>`}).join(`
136
+ `);return`
137
+ <section id="${o.toLowerCase()}-tag" class="fade-in">
138
+ <div class="grid lg:grid-cols-3 gap-8 mb-12">
139
+ <div class="lg:col-span-1">
140
+ <div class="sticky top-24">
141
+ <h2 class="text-3xl font-bold text-white mb-4">${o}</h2>
142
+ <p class="text-gray-300 text-lg leading-relaxed">
143
+ ${b}
144
+ </p>
145
+ <div class="mt-6 flex items-center space-x-2">
146
+ <span class="bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-medium">${m.length} endpoints</span>
147
+ <span class="bg-gray-600 text-white px-3 py-1 rounded-full text-sm font-medium">Authentication required</span>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <div class="lg:col-span-2">
153
+ <div class="bg-dark-800/30 backdrop-blur-sm rounded-xl p-6 border border-dark-700">
154
+ <h3 class="text-xl font-semibold text-white mb-6">Available Endpoints</h3>
155
+ <div class="space-y-4">
156
+ ${y}
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </section>
162
+
163
+ <hr class="border-dark-600">
164
+
165
+ <div class="space-y-16">
166
+ ${x}
167
+ </div>`}).join(`
168
+
169
+ `);return`<!DOCTYPE html>
170
+ <html lang="es" class="dark">
4
171
  <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>${this.spec.info.title}</title>
8
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui.css">
9
- <style>
10
- :root {
11
- --primary-color: #3b82f6;
12
- --primary-dark: #1d4ed8;
13
- --secondary-color: #64748b;
14
- --background-color: #f8fafc;
15
- --card-background: #ffffff;
16
- --text-color: #1e293b;
17
- --border-color: #e2e8f0;
18
- }
19
-
20
- body {
21
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
22
- margin: 0;
23
- padding: 0;
24
- background-color: var(--background-color);
25
- color: var(--text-color);
26
- }
27
-
28
- .header {
29
- background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
30
- color: white;
31
- padding: 2rem;
32
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
33
- }
34
-
35
- .container {
36
- width: 100%;
37
- max-width: 1200px;
38
- margin: 0 auto;
39
- padding: 0 1rem;
40
- }
41
-
42
- .header-content {
43
- max-width: 1200px;
44
- margin: 0 auto;
45
- }
46
-
47
- .title {
48
- font-size: 2.25rem;
49
- font-weight: 700;
50
- margin: 0;
51
- line-height: 1.2;
52
- }
53
-
54
- .description {
55
- margin-top: 0.75rem;
56
- font-size: 1.125rem;
57
- opacity: 0.9;
58
- }
59
-
60
- .version {
61
- display: inline-block;
62
- background-color: rgba(255, 255, 255, 0.2);
63
- padding: 0.25rem 0.75rem;
64
- border-radius: 9999px;
65
- font-size: 0.875rem;
66
- margin-top: 0.75rem;
67
- }
68
-
69
- .main {
70
- padding: 2rem 0;
71
- }
72
-
73
- .swagger-container {
74
- background-color: var(--card-background);
75
- border-radius: 0.5rem;
76
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
77
- padding: 1rem;
78
- margin-bottom: 2rem;
79
- }
80
-
81
- .swagger-ui .topbar {
82
- display: none;
83
- }
84
-
85
- .swagger-ui .info {
86
- margin: 20px 0;
87
- }
88
-
89
- .swagger-ui .opblock-tag {
90
- font-size: 1.25rem;
91
- border-bottom: 1px solid var(--border-color);
92
- padding: 10px 0;
93
- }
94
-
95
- .swagger-ui .opblock {
96
- border-radius: 8px;
97
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
98
- margin: 0 0 15px;
99
- }
100
-
101
- .swagger-ui .opblock .opblock-summary {
102
- padding: 8px 20px;
103
- }
104
-
105
- .swagger-ui .btn {
106
- border-radius: 4px;
107
- }
108
-
109
- .footer {
110
- background-color: #1e293b;
111
- color: white;
112
- padding: 1.5rem 0;
113
- text-align: center;
114
- margin-top: 2rem;
115
- }
116
-
117
- .footer-content {
118
- font-size: 0.875rem;
119
- opacity: 0.8;
120
- }
121
-
122
- .footer-link {
123
- color: white;
124
- text-decoration: underline;
125
- margin: 0 0.5rem;
126
- }
127
-
128
- /* Responsive adjustments */
129
- @media (max-width: 768px) {
130
- .header {
131
- padding: 1.5rem 1rem;
132
- }
133
-
134
- .title {
135
- font-size: 1.75rem;
136
- }
137
-
138
- .swagger-ui .wrapper {
139
- padding: 0;
140
- }
141
-
142
- .swagger-ui .opblock .opblock-summary {
143
- padding: 8px 10px;
144
- }
145
- }
146
- </style>
172
+ <meta charset="UTF-8">
173
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
174
+ <title>${this.spec.info.title}</title>
175
+ <script src="https://cdn.tailwindcss.com"></script>
176
+ <script>
177
+ tailwind.config = {
178
+ darkMode: 'class',
179
+ theme: {
180
+ extend: {
181
+ colors: {
182
+ dark: {
183
+ 50: '#f8fafc',
184
+ 100: '#f1f5f9',
185
+ 200: '#e2e8f0',
186
+ 300: '#cbd5e1',
187
+ 400: '#94a3b8',
188
+ 500: '#64748b',
189
+ 600: '#475569',
190
+ 700: '#334155',
191
+ 800: '#1e293b',
192
+ 900: '#0f172a',
193
+ }
194
+ }
195
+ }
196
+ }
197
+ }
198
+ </script>
199
+ <style>
200
+ .sidebar-transition {
201
+ transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
202
+ }
203
+ .fade-in {
204
+ animation: fadeIn 0.3s ease-in-out;
205
+ }
206
+ @keyframes fadeIn {
207
+ from { opacity: 0; transform: translateY(10px); }
208
+ to { opacity: 1; transform: translateY(0); }
209
+ }
210
+ .code-block {
211
+ background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
212
+ }
213
+ </style>
147
214
  </head>
148
- <body>
149
- <header class="header">
150
- <div class="header-content">
151
- <h1 class="title">${this.spec.info.title}</h1>
152
- ${this.spec.info.description?`<p class="description">${this.spec.info.description}</p>`:""}
153
- <span class="version">Version ${this.spec.info.version}</span>
154
- </div>
155
- </header>
215
+ <body class="bg-dark-900 text-gray-100 font-sans">
156
216
 
157
- <main class="main">
158
- <div class="container">
159
- <div class="swagger-container">
160
- <div id="swagger-ui"></div>
161
- </div>
162
- </div>
163
- </main>
164
-
165
- <footer class="footer">
166
- <div class="container">
167
- <div class="footer-content">
168
- <p>Powered by Hedystia</p>
169
- <div>
170
- <a href="https://github.com/Hedystia/Framework" class="footer-link" target="_blank">GitHub</a>
171
- <a href="/swagger/json" class="footer-link" target="_blank">Raw JSON</a>
217
+ <nav class="fixed top-0 left-0 right-0 z-50 bg-dark-900/80 backdrop-blur-md border-b border-dark-700">
218
+ <div class="flex items-center justify-between px-4 py-3">
219
+ <div class="flex items-center space-x-4">
220
+ <button id="sidebarToggle" class="p-2 rounded-lg hover:bg-dark-700 transition-colors">
221
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
222
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
223
+ </svg>
224
+ </button>
225
+ <h1 class="text-xl font-bold text-blue-400">${this.spec.info.title}</h1>
226
+ </div>
227
+
228
+ <div class="flex items-center space-x-4">
229
+ <!-- <select id="languageSelect" class="bg-dark-800 border border-dark-600 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all">
230
+ <option value="shell">Shell</option>
231
+ <option value="nodejs">Node.js</option>
232
+ </select> -->
233
+
234
+ <button id="themeToggle" class="p-2 rounded-lg hover:bg-dark-700 transition-colors">
235
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
236
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
237
+ </svg>
238
+ </button>
239
+ </div>
172
240
  </div>
173
- </div>
241
+ </nav>
242
+
243
+ <div class="flex pt-16">
244
+
245
+ <aside id="sidebar" class="fixed top-16 bottom-0 left-0 z-40 w-64 bg-dark-800/50 backdrop-blur-sm border-r border-dark-700 sidebar-transition transform translate-x-0">
246
+ <div class="p-4 h-full overflow-y-auto pt-6">
247
+ <div class="space-y-1">
248
+ ${s}
249
+ </div>
250
+ </div>
251
+ </aside>
252
+
253
+ <div id="sidebarOverlay" class="fixed top-16 bottom-0 left-0 right-0 bg-black/50 z-30 lg:hidden hidden"></div>
254
+
255
+ <main id="mainContent" class="flex-1 ml-64 transition-all duration-300">
256
+ <div class="max-w-7xl mx-auto px-4 py-8">
257
+
258
+ <div class="mb-12 fade-in">
259
+ <h1 class="text-4xl font-bold text-white mb-4">${this.spec.info.title}</h1>
260
+ <p class="text-xl text-gray-300 mb-4">${this.spec.info.description||"Complete reference for our REST API"}</p>
261
+ <span class="inline-block bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-medium">v${this.spec.info.version}</span>
262
+ </div>
263
+
264
+ <div class="grid gap-8 mb-12 fade-in">
265
+ <div class="bg-dark-800/30 backdrop-blur-sm rounded-xl p-6 border border-dark-700">
266
+ <h3 class="text-xl font-semibold text-white mb-4">Server</h3>
267
+ <div class="space-y-3">
268
+ <a href="${this.host}" target="_blank" class="flex items-center space-x-3 hover:bg-dark-700/30 p-2 rounded-lg transition-colors group">
269
+ <div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
270
+ <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
271
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
272
+ </svg>
273
+ </div>
274
+ <span class="text-gray-300 group-hover:text-white transition-colors">${this.host}</span>
275
+ </a>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
280
+ <div class="space-y-20">
281
+ ${p}
282
+ </div>
283
+ </div>
284
+ </main>
174
285
  </div>
175
- </footer>
176
-
177
- <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui-bundle.js"></script>
178
- <script>
179
- window.onload = function() {
180
- const ui = SwaggerUIBundle({
181
- spec: ${JSON.stringify(this.spec)},
182
- dom_id: '#swagger-ui',
183
- deepLinking: true,
184
- presets: [
185
- SwaggerUIBundle.presets.apis,
186
- SwaggerUIBundle.SwaggerUIStandalonePreset
187
- ],
188
- layout: "BaseLayout",
189
- docExpansion: "list",
190
- defaultModelsExpandDepth: 1,
191
- defaultModelExpandDepth: 1,
192
- syntaxHighlight: {
193
- activated: true,
194
- theme: "agate"
195
- },
196
- filter: true,
197
- withCredentials: true,
198
- persistAuthorization: true
199
- });
200
- };
201
- </script>
286
+
287
+ <script>
288
+ const sidebarToggle = document.getElementById('sidebarToggle');
289
+ const sidebar = document.getElementById('sidebar');
290
+ const sidebarOverlay = document.getElementById('sidebarOverlay');
291
+ const mainContent = document.getElementById('mainContent');
292
+ const themeToggle = document.getElementById('themeToggle');
293
+ const html = document.documentElement;
294
+
295
+ themeToggle.addEventListener('click', () => {
296
+ if (html.classList.contains('dark')) {
297
+ html.classList.remove('dark');
298
+ html.classList.add('light');
299
+ document.body.className = 'bg-gray-50 text-gray-900 font-sans';
300
+
301
+ const navbar = document.querySelector('nav');
302
+ navbar.className = 'fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200';
303
+
304
+ const sidebar = document.getElementById('sidebar');
305
+ sidebar.className = sidebar.className.replace('bg-dark-800/50 backdrop-blur-sm border-r border-dark-700', 'bg-white/50 backdrop-blur-sm border-r border-gray-200');
306
+
307
+ document.querySelectorAll('h1, h2, h3, h4').forEach(heading => {
308
+ heading.classList.remove('text-white', 'text-gray-100');
309
+ heading.classList.add('text-gray-900');
310
+ });
311
+
312
+ document.querySelectorAll('.text-gray-300').forEach(el => {
313
+ el.classList.remove('text-gray-300');
314
+ el.classList.add('text-gray-700');
315
+ });
316
+
317
+ document.querySelectorAll('.text-gray-400').forEach(el => {
318
+ el.classList.remove('text-gray-400');
319
+ el.classList.add('text-gray-600');
320
+ });
321
+
322
+ document.querySelectorAll('.border-dark-600').forEach(el => {
323
+ el.classList.remove('border-dark-600');
324
+ el.classList.add('border-gray-300');
325
+ });
326
+
327
+ document.querySelectorAll('.bg-dark-700\\/30').forEach(el => {
328
+ el.classList.remove('bg-dark-700/30');
329
+ el.classList.add('bg-gray-100/30');
330
+ });
331
+
332
+ document.querySelectorAll('.bg-dark-800\\/20').forEach(el => {
333
+ el.classList.remove('bg-dark-800/20');
334
+ el.classList.add('bg-gray-50/20');
335
+ });
336
+
337
+ document.querySelectorAll('.bg-dark-600').forEach(el => {
338
+ el.classList.remove('bg-dark-600');
339
+ el.classList.add('bg-gray-400');
340
+ });
341
+
342
+ document.querySelectorAll('.text-gray-500').forEach(el => {
343
+ el.classList.remove('text-gray-500');
344
+ el.classList.add('text-gray-600');
345
+ });
346
+
347
+ document.querySelectorAll('aside a').forEach(link => {
348
+ link.classList.add('text-gray-700', 'hover:text-gray-900', 'hover:bg-gray-100');
349
+ });
350
+
351
+ document.querySelectorAll('pre code').forEach(code => {
352
+ code.classList.remove('text-gray-300');
353
+ code.classList.add('text-gray-800');
354
+ });
355
+
356
+ document.querySelectorAll('.bg-dark-800\\/30').forEach(el => {
357
+ el.classList.remove('bg-dark-800/30', 'border-dark-700');
358
+ el.classList.add('bg-white/30', 'border-gray-200');
359
+ });
360
+
361
+ document.querySelectorAll('.code-block').forEach(el => {
362
+ el.style.background = 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%)';
363
+ el.classList.remove('border-dark-600');
364
+ el.classList.add('border-gray-300');
365
+ });
366
+
367
+ document.querySelectorAll('.border-dark-600').forEach(el => {
368
+ el.classList.remove('border-dark-600');
369
+ el.classList.add('border-gray-300');
370
+ });
371
+
372
+ } else {
373
+ html.classList.remove('light');
374
+ html.classList.add('dark');
375
+ document.body.className = 'bg-dark-900 text-gray-100 font-sans';
376
+
377
+ const navbar = document.querySelector('nav');
378
+ navbar.className = 'fixed top-0 left-0 right-0 z-50 bg-dark-900/80 backdrop-blur-md border-b border-dark-700';
379
+
380
+ const sidebar = document.getElementById('sidebar');
381
+ sidebar.className = sidebar.className.replace('bg-white/50 backdrop-blur-sm border-r border-gray-200', 'bg-dark-800/50 backdrop-blur-sm border-r border-dark-700');
382
+
383
+ document.querySelectorAll('h1, h2, h3, h4').forEach(heading => {
384
+ heading.classList.remove('text-gray-900');
385
+ heading.classList.add('text-white');
386
+ });
387
+
388
+ document.querySelectorAll('.text-gray-700').forEach(el => {
389
+ el.classList.remove('text-gray-700');
390
+ el.classList.add('text-gray-300');
391
+ });
392
+
393
+ document.querySelectorAll('.text-gray-600').forEach(el => {
394
+ el.classList.remove('text-gray-600');
395
+ el.classList.add('text-gray-400');
396
+ });
397
+
398
+ document.querySelectorAll('.border-gray-300').forEach(el => {
399
+ el.classList.remove('border-gray-300');
400
+ el.classList.add('border-dark-600');
401
+ });
402
+
403
+ document.querySelectorAll('.bg-gray-100\\/30').forEach(el => {
404
+ el.classList.remove('bg-gray-100/30');
405
+ el.classList.add('bg-dark-700/30');
406
+ });
407
+
408
+ document.querySelectorAll('.bg-gray-50\\/20').forEach(el => {
409
+ el.classList.remove('bg-gray-50/20');
410
+ el.classList.add('bg-dark-800/20');
411
+ });
412
+
413
+ document.querySelectorAll('.bg-gray-400').forEach(el => {
414
+ el.classList.remove('bg-gray-400');
415
+ el.classList.add('bg-dark-600');
416
+ });
417
+
418
+ document.querySelectorAll('.text-gray-600').forEach(el => {
419
+ el.classList.remove('text-gray-600');
420
+ el.classList.add('text-gray-500');
421
+ });
422
+
423
+ document.querySelectorAll('aside a').forEach(link => {
424
+ link.classList.remove('text-gray-700', 'hover:text-gray-900', 'hover:bg-gray-100');
425
+ });
426
+
427
+ document.querySelectorAll('pre code').forEach(code => {
428
+ code.classList.remove('text-gray-800');
429
+ code.classList.add('text-gray-300');
430
+ });
431
+
432
+ document.querySelectorAll('.bg-white\\/30').forEach(el => {
433
+ el.classList.remove('bg-white/30', 'border-gray-200');
434
+ el.classList.add('bg-dark-800/30', 'border-dark-700');
435
+ });
436
+
437
+ document.querySelectorAll('.code-block').forEach(el => {
438
+ el.style.background = 'linear-gradient(135deg, #1e293b 0%, #334155 100%)';
439
+ el.classList.remove('border-gray-300');
440
+ el.classList.add('border-dark-600');
441
+ });
442
+
443
+ document.querySelectorAll('.border-gray-300').forEach(el => {
444
+ el.classList.remove('border-gray-300');
445
+ el.classList.add('border-dark-600');
446
+ });
447
+ }
448
+ });
449
+
450
+ function toggleSidebar() {
451
+ const isHidden = sidebar.classList.contains('-translate-x-full');
452
+
453
+ if (isHidden) {
454
+ sidebar.classList.remove('-translate-x-full');
455
+ mainContent.classList.remove('ml-0');
456
+ mainContent.classList.add('ml-64');
457
+ } else {
458
+ sidebar.classList.add('-translate-x-full');
459
+ mainContent.classList.remove('ml-64');
460
+ mainContent.classList.add('ml-0');
461
+ }
462
+
463
+ if (window.innerWidth < 1024) {
464
+ sidebarOverlay.classList.toggle('hidden');
465
+ }
466
+ }
467
+
468
+ sidebarToggle.addEventListener('click', toggleSidebar);
469
+ sidebarOverlay.addEventListener('click', toggleSidebar);
470
+
471
+ document.querySelectorAll('.copy-btn').forEach(btn => {
472
+ btn.addEventListener('click', async () => {
473
+ const textToCopy = btn.getAttribute('data-copy');
474
+ try {
475
+ await navigator.clipboard.writeText(textToCopy);
476
+ btn.textContent = 'Copied!';
477
+ setTimeout(() => {
478
+ btn.textContent = 'Copy';
479
+ }, 2000);
480
+ } catch (err) {
481
+ console.error('Failed to copy text: ', err);
482
+ }
483
+ });
484
+ });
485
+
486
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
487
+ anchor.addEventListener('click', function (e) {
488
+ e.preventDefault();
489
+ const target = document.querySelector(this.getAttribute('href'));
490
+ if (target) {
491
+ target.scrollIntoView({
492
+ behavior: 'smooth',
493
+ block: 'start'
494
+ });
495
+
496
+ if (window.innerWidth < 1024) {
497
+ toggleSidebar();
498
+ }
499
+ }
500
+ });
501
+ });
502
+
503
+ document.querySelectorAll('.sidebar-group-toggle').forEach(button => {
504
+ button.addEventListener('click', () => {
505
+ const targetId = button.getAttribute('data-target');
506
+ const targetGroup = document.getElementById(targetId);
507
+ const arrow = button.querySelector('svg');
508
+
509
+ if (targetGroup.style.maxHeight && targetGroup.style.maxHeight !== '0px') {
510
+ targetGroup.style.maxHeight = '0px';
511
+ arrow.style.transform = 'rotate(0deg)';
512
+ } else {
513
+ targetGroup.style.maxHeight = targetGroup.scrollHeight + 'px';
514
+ arrow.style.transform = 'rotate(180deg)';
515
+ }
516
+ });
517
+ });
518
+
519
+ document.querySelectorAll('.sidebar-group-content').forEach(group => {
520
+ group.style.maxHeight = group.scrollHeight + 'px';
521
+ });
522
+ document.querySelectorAll('.sidebar-group-toggle svg').forEach(arrow => {
523
+ arrow.style.transform = 'rotate(180deg)';
524
+ });
525
+
526
+ function handleResize() {
527
+ if (window.innerWidth < 1024) {
528
+ sidebar.classList.add('-translate-x-full');
529
+ mainContent.classList.remove('ml-64');
530
+ mainContent.classList.add('ml-0');
531
+ } else {
532
+ if (!sidebar.hasAttribute('data-manually-hidden')) {
533
+ sidebar.classList.remove('-translate-x-full');
534
+ mainContent.classList.remove('ml-0');
535
+ mainContent.classList.add('ml-64');
536
+ }
537
+ sidebarOverlay.classList.add('hidden');
538
+ }
539
+ }
540
+
541
+ window.addEventListener('resize', handleResize);
542
+ handleResize();
543
+ </script>
202
544
  </body>
203
- </html>
204
- `}};function k(i={}){let e=new o(i);return{plugin:new u.Hedystia().get("/",()=>new Response(e.generateHTML(),{headers:{"Content-Type":"text/html"}})).get("/json",()=>Response.json(e.getSpec())),swagger:e,captureRoutes:t=>{if(t?.routes)for(let r of t.routes){let n={params:r.schema.params?r.schema.params.jsonSchema:void 0,query:r.schema.query?r.schema.query.jsonSchema:void 0,body:r.schema.body?r.schema.body.jsonSchema:void 0,response:r.schema.response?r.schema.response.jsonSchema:void 0};e.addRoute(r.method,r.path,n,r.schema.description||`${r.method} ${r.path}`,r.schema.description,r.schema.tags)}}}}0&&(module.exports={swagger});
545
+ </html>`}};function G(n={}){let e=new w(n),r=new B.Hedystia().get("/",()=>new Response(e.generateHTML(),{headers:{"Content-Type":"text/html"}})).get("/json",()=>Response.json(e.getSpec()));function t(i){for(let s of i.routes)e.addRoute(s.method,s.path,{params:s.schema.params,query:s.schema.query,body:s.schema.body,response:s.schema.response},s.schema.description||`${s.method} ${s.path}`,s.schema.description,s.schema.tags);for(let s of i.staticRoutes)e.addRoute("GET",s.path,{},`Static route ${s.path}`);for(let[s]of i.wsRoutes)e.addRoute("WS",s,{},`WebSocket route ${s}`);for(let[s,p]of i.subscriptionHandlers)e.addRoute("SUB",s,{params:p.schema.params,query:p.schema.query,headers:p.schema.headers},p.schema.description||`SUB ${s}`,p.schema.description,p.schema.tags);return r}return{plugin:t,swagger:e}}0&&(module.exports={swagger});
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@hedystia/swagger",
3
- "version": "1.5.3",
3
+ "version": "1.6.1",
4
4
  "devDependencies": {
5
5
  "@types/bun": "latest",
6
6
  "tsup": "^8.3.5"
7
7
  },
8
8
  "dependencies": {
9
9
  "@apidevtools/swagger-parser": "^10.1.1",
10
- "hedystia": "^1.5.3",
10
+ "hedystia": "^1.6.1",
11
11
  "openapi-types": "^12.1.3"
12
12
  },
13
13
  "peerDependencies": {
14
14
  "@apidevtools/swagger-parser": "^10.1.1",
15
15
  "typescript": ">= 5.0.0",
16
- "hedystia": ">= 1.5.0",
16
+ "hedystia": ">= 1.6.0",
17
17
  "openapi-types": ">= 12.0.0"
18
18
  },
19
19
  "peerDependenciesMeta": {