@hedystia/swagger 1.10.1 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,108 +1,303 @@
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`
1
+ import SwaggerParser from "@apidevtools/swagger-parser";
2
+ //#region src/swagger.ts
3
+ var Swagger = class {
4
+ spec = {
5
+ openapi: "3.0.0",
6
+ info: {
7
+ title: "API Documentation",
8
+ version: "1.0.0"
9
+ },
10
+ paths: {},
11
+ components: { schemas: {} }
12
+ };
13
+ host;
14
+ constructor(options = {}) {
15
+ this.spec.info.title = options.title || "API Documentation";
16
+ this.spec.info.description = options.description;
17
+ this.spec.info.version = options.version || "1.0.0";
18
+ this.host = options.host || "https://example.com";
19
+ if (options.tags) this.spec.tags = options.tags;
20
+ if (options.externalDocs) this.spec.externalDocs = options.externalDocs;
21
+ if (options.securityDefinitions) {
22
+ this.spec.components = this.spec.components || {};
23
+ this.spec.components.securitySchemes = options.securityDefinitions;
24
+ }
25
+ }
26
+ addRoute(method, path, schema, summary, description, tags) {
27
+ if (!schema) return;
28
+ const normalizedPath = path.replace(/:([^/]+)/g, "{$1}");
29
+ if (!this.spec.paths) this.spec.paths = {};
30
+ if (!this.spec.paths[normalizedPath]) this.spec.paths[normalizedPath] = {};
31
+ const methodLower = method.toLowerCase();
32
+ const operationObject = {
33
+ summary: summary || `${method} ${path}`,
34
+ parameters: this.buildParameters(schema),
35
+ requestBody: this.buildRequestBody(schema),
36
+ responses: this.buildResponses(schema)
37
+ };
38
+ if (description) operationObject.description = description;
39
+ if (tags) operationObject.tags = tags;
40
+ this.spec.paths[normalizedPath][methodLower] = operationObject;
41
+ }
42
+ buildParameters(schema) {
43
+ const parameters = [];
44
+ if (schema.params) try {
45
+ const jsonSchema = schema.params;
46
+ if (jsonSchema.properties) Object.entries(jsonSchema.properties).forEach(([name, propSchema]) => {
47
+ parameters.push({
48
+ name,
49
+ in: "path",
50
+ required: jsonSchema.required?.includes(name) ?? true,
51
+ schema: propSchema
52
+ });
53
+ });
54
+ } catch (e) {
55
+ console.error("Failed to convert params schema:", e);
56
+ }
57
+ if (schema.query) try {
58
+ const jsonSchema = schema.query;
59
+ if (jsonSchema.properties) Object.entries(jsonSchema.properties).forEach(([name, propSchema]) => {
60
+ parameters.push({
61
+ name,
62
+ in: "query",
63
+ required: jsonSchema.required?.includes(name) ?? false,
64
+ schema: propSchema
65
+ });
66
+ });
67
+ } catch (e) {
68
+ console.error("Failed to convert query schema:", e);
69
+ }
70
+ return parameters.length > 0 ? parameters : void 0;
71
+ }
72
+ buildRequestBody(schema) {
73
+ if (!schema.body) return;
74
+ try {
75
+ const jsonSchema = schema.body;
76
+ return {
77
+ required: true,
78
+ content: { "application/json": { schema: jsonSchema } }
79
+ };
80
+ } catch (e) {
81
+ console.error("Failed to convert body schema:", e);
82
+ return;
83
+ }
84
+ }
85
+ buildResponses(schema) {
86
+ const responses = { "200": { description: "Successful response" } };
87
+ if (schema.response) try {
88
+ const jsonSchema = schema.response;
89
+ responses["200"].content = { "application/json": { schema: jsonSchema } };
90
+ } catch (e) {
91
+ console.error("Failed to convert response schema:", e);
92
+ }
93
+ return responses;
94
+ }
95
+ async validate() {
96
+ try {
97
+ await SwaggerParser.validate(this.spec);
98
+ return true;
99
+ } catch (error) {
100
+ console.error("Swagger validation error:", error);
101
+ return false;
102
+ }
103
+ }
104
+ getSpec() {
105
+ return this.spec;
106
+ }
107
+ generateHTML() {
108
+ const hostname = new URL(this.host).hostname;
109
+ const extractJsonSchema = (schema) => {
110
+ if (!schema) return {};
111
+ if (schema.jsonSchema) return schema.jsonSchema;
112
+ if (schema.type || schema.properties || schema.$ref) return schema;
113
+ return {};
114
+ };
115
+ const groupedPaths = {};
116
+ const defaultGroup = "API";
117
+ Object.entries(this.spec.paths || {}).forEach(([path, pathItem]) => {
118
+ Object.entries(pathItem).forEach(([method, operation]) => {
119
+ const primaryTag = (operation.tags || [defaultGroup])[0];
120
+ if (!groupedPaths[primaryTag]) groupedPaths[primaryTag] = [];
121
+ groupedPaths[primaryTag].push({
122
+ path,
123
+ method: method.toUpperCase(),
124
+ operation
125
+ });
126
+ });
127
+ });
128
+ const sidebarSections = Object.entries(groupedPaths).map(([tag, endpoints]) => {
129
+ const endpointCount = endpoints.length;
130
+ const endpointLinks = endpoints.map(({ path, method }) => {
131
+ return `<li><a href="#${`${method.toLowerCase()}-${path.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">${method} ${path}</a></li>`;
132
+ }).join("\n ");
133
+ return `
3
134
  <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">
135
+ <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="${tag.toLowerCase()}-group">
5
136
  <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>
137
+ <h3 class="text-sm font-medium text-gray-300 group-hover:text-white">${tag}</h3>
138
+ <span class="text-xs text-gray-500 bg-dark-600 px-2 py-0.5 rounded-full">${endpointCount}</span>
8
139
  <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
140
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
10
141
  </svg>
11
142
  </div>
12
143
  </button>
13
- <div id="${o.toLowerCase()}-group" class="sidebar-group-content max-h-0 overflow-hidden transition-all duration-300">
144
+ <div id="${tag.toLowerCase()}-group" class="sidebar-group-content max-h-0 overflow-hidden transition-all duration-300">
14
145
  <ul class="ml-6 mt-1 space-y-1">
15
- ${y}
146
+ ${endpointLinks}
16
147
  </ul>
17
148
  </div>
18
149
  </div>
19
150
 
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">
151
+ <div class="border-t border-dark-600 my-3"></div>`;
152
+ }).join("");
153
+ const contentSections = Object.entries(groupedPaths).map(([tag, endpoints]) => {
154
+ const tagDescription = this.spec.tags?.find((t) => t.name === tag)?.description || `Manage ${tag.toLowerCase()} operations`;
155
+ const endpointCards = endpoints.map(({ path, method, operation }) => {
156
+ return `
157
+ <a href="#${`${method.toLowerCase()}-${path.replace(/[^a-zA-Z0-9]/g, "-")}`}" class="block p-4 rounded-lg border border-dark-600 hover:border-blue-500 hover:bg-dark-700/30 transition-all group">
22
158
  <div class="flex items-center justify-between mb-2">
23
159
  <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>
160
+ <span class="${{
161
+ GET: "bg-green-600",
162
+ POST: "bg-blue-600",
163
+ PUT: "bg-yellow-600",
164
+ DELETE: "bg-red-600",
165
+ PATCH: "bg-purple-600",
166
+ WS: "bg-indigo-600",
167
+ SUB: "bg-pink-600"
168
+ }[method] || "bg-gray-600"} text-white px-3 py-1 rounded-lg text-sm font-medium">${method}</span>
169
+ <code class="text-blue-400 font-mono">${path}</code>
26
170
  </div>
27
171
  <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
172
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
29
173
  </svg>
30
174
  </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+=`
175
+ <p class="text-gray-300 text-sm">${operation.summary || operation.description || `${method} ${path} operation`}</p>
176
+ </a>`;
177
+ }).join("\n");
178
+ const detailedEndpoints = endpoints.map(({ path, method, operation }) => {
179
+ const operationId = `${method.toLowerCase()}-${path.replace(/[^a-zA-Z0-9]/g, "-")}`;
180
+ const methodColor = {
181
+ GET: "bg-green-600",
182
+ POST: "bg-blue-600",
183
+ PUT: "bg-yellow-600",
184
+ DELETE: "bg-red-600",
185
+ PATCH: "bg-purple-600",
186
+ WS: "bg-indigo-600",
187
+ SUB: "bg-pink-600"
188
+ }[method] || "bg-gray-600";
189
+ let parametersSection = "";
190
+ if (operation.parameters && operation.parameters.length > 0) {
191
+ const pathParams = operation.parameters.filter((p) => p.in === "path");
192
+ const queryParams = operation.parameters.filter((p) => p.in === "query");
193
+ if (pathParams.length > 0) parametersSection += `
34
194
  <div>
35
195
  <h4 class="text-lg font-semibold text-white mb-3">Path Parameters</h4>
36
196
  <div class="space-y-3">
37
- ${d.map(a=>`
197
+ ${pathParams.map((param) => `
38
198
  <div class="border border-dark-600 rounded-lg p-3">
39
199
  <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>
200
+ <code class="text-blue-400">${param.name}</code>
201
+ <span class="text-xs ${param.required ? "text-red-400" : "text-gray-400"}">${param.required ? "required" : "optional"}</span>
42
202
  </div>
43
- <p class="text-sm text-gray-300">${a.description||`${a.name} parameter`}</p>
203
+ <p class="text-sm text-gray-300">${param.description || `${param.name} parameter`}</p>
44
204
  </div>`).join("")}
45
205
  </div>
46
- </div>`),u.length>0&&(S+=`
206
+ </div>`;
207
+ if (queryParams.length > 0) parametersSection += `
47
208
  <div>
48
209
  <h4 class="text-lg font-semibold text-white mb-3">Query Parameters</h4>
49
210
  <div class="space-y-3">
50
- ${u.map(a=>`
211
+ ${queryParams.map((param) => `
51
212
  <div class="border border-dark-600 rounded-lg p-3">
52
213
  <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>
214
+ <code class="text-blue-400">${param.name}</code>
215
+ <span class="text-xs ${param.required ? "text-red-400" : "text-gray-400"}">${param.required ? "required" : "optional"}</span>
55
216
  </div>
56
- <p class="text-sm text-gray-300">${a.description||`${a.name} parameter`}</p>
217
+ <p class="text-sm text-gray-300">${param.description || `${param.name} parameter`}</p>
57
218
  </div>`).join("")}
58
219
  </div>
59
- </div>`)}let q="";if(c.requestBody){let d=c.requestBody.content?.["application/json"]?.schema,u=r(d);u.properties&&(q=`
220
+ </div>`;
221
+ }
222
+ let requestBodySection = "";
223
+ if (operation.requestBody) {
224
+ const schema = operation.requestBody.content?.["application/json"]?.schema;
225
+ const cleanSchema = extractJsonSchema(schema);
226
+ if (cleanSchema.properties) requestBodySection = `
60
227
  <div>
61
228
  <h4 class="text-lg font-semibold text-white mb-3">Body Parameters</h4>
62
229
  <div class="space-y-3">
63
- ${Object.entries(u.properties).map(([a,h])=>`
230
+ ${Object.entries(cleanSchema.properties).map(([propName, propSchema]) => `
64
231
  <div class="border border-dark-600 rounded-lg p-3">
65
232
  <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>
233
+ <code class="text-blue-400">${propName}</code>
234
+ <span class="text-xs ${cleanSchema.required?.includes(propName) ? "text-red-400" : "text-gray-400"}">${cleanSchema.required?.includes(propName) ? "required" : "optional"}</span>
68
235
  </div>
69
- <p class="text-sm text-gray-300">${h.description||`${a} field`}</p>
236
+ <p class="text-sm text-gray-300">${propSchema.description || `${propName} field`}</p>
70
237
  </div>`).join("")}
71
238
  </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=`
239
+ </div>`;
240
+ }
241
+ const baseUrl = `https://${hostname}`;
242
+ let requestExample = "";
243
+ if (method !== "WS" && method !== "SUB") {
244
+ let curlCommand = `curl -X ${method} '${baseUrl}${path}'`;
245
+ if (operation.requestBody) {
246
+ curlCommand += ` \\\n -H 'Content-Type: application/json'`;
247
+ const schema = operation.requestBody.content?.["application/json"]?.schema;
248
+ const cleanSchema = extractJsonSchema(schema);
249
+ if (cleanSchema.properties) {
250
+ const exampleData = {};
251
+ Object.entries(cleanSchema.properties).forEach(([propName, propSchema]) => {
252
+ if (propSchema.type === "string") exampleData[propName] = propSchema.format === "email" ? "user@example.com" : `example ${propName}`;
253
+ else if (propSchema.type === "number") exampleData[propName] = 123;
254
+ else if (propSchema.type === "boolean") exampleData[propName] = true;
255
+ else exampleData[propName] = `example ${propName}`;
256
+ });
257
+ curlCommand += ` \\\n -d '${JSON.stringify(exampleData, null, 2).replace(/\n/g, "\\n ")}'`;
258
+ }
259
+ }
260
+ curlCommand += ` \\\n -H 'Authorization: Bearer your-token'`;
261
+ requestExample = `
76
262
  <div>
77
263
  <div class="flex items-center justify-between mb-4">
78
264
  <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,"\\'")}">
265
+ <button class="copy-btn bg-dark-700 hover:bg-dark-600 px-3 py-1 rounded-lg text-sm transition-colors" data-copy="${curlCommand.replace(/'/g, "\\'")}">
80
266
  Copy
81
267
  </button>
82
268
  </div>
83
269
  <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>
270
+ <pre class="text-sm text-gray-300 overflow-x-auto"><code>${curlCommand}</code></pre>
85
271
  </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=`
272
+ </div>`;
273
+ }
274
+ let responseSection = "";
275
+ const response200 = operation.responses?.["200"];
276
+ if (response200?.content?.["application/json"]?.schema) {
277
+ const schema = response200.content["application/json"].schema;
278
+ const cleanSchema = extractJsonSchema(schema);
279
+ responseSection = `
87
280
  <div>
88
281
  <h4 class="text-lg font-semibold text-white mb-4">Response Schema</h4>
89
282
  <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>
283
+ <pre class="text-sm text-gray-300 overflow-x-auto"><code>${JSON.stringify(cleanSchema, null, 2)}</code></pre>
91
284
  </div>
92
- </div>`}return`
93
- <section id="${L}" class="fade-in">
285
+ </div>`;
286
+ }
287
+ return `
288
+ <section id="${operationId}" class="fade-in">
94
289
  <div class="grid lg:grid-cols-3 gap-8">
95
290
  <div class="lg:col-span-1 space-y-6">
96
291
  <div>
97
292
  <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>
293
+ <span class="${methodColor} text-white px-3 py-1 rounded-lg text-sm font-medium">${method}</span>
294
+ <code class="text-blue-400 font-mono">${path}</code>
100
295
  </div>
101
- <p class="text-gray-300">${c.description||c.summary||`${g} ${l} operation`}</p>
296
+ <p class="text-gray-300">${operation.description || operation.summary || `${method} ${path} operation`}</p>
102
297
  </div>
103
298
 
104
- ${S}
105
- ${q}
299
+ ${parametersSection}
300
+ ${requestBodySection}
106
301
 
107
302
  <div>
108
303
  <h4 class="text-lg font-semibold text-white mb-3">Headers</h4>
@@ -118,32 +313,35 @@
118
313
  <div>
119
314
  <h4 class="text-lg font-semibold text-white mb-3">Responses</h4>
120
315
  <div class="space-y-2">
121
- ${Object.entries(c.responses||{}).map(([d,u])=>`
316
+ ${Object.entries(operation.responses || {}).map(([code, response]) => {
317
+ return `
122
318
  <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("")}
319
+ <span class="${code.startsWith("2") ? "bg-green-600" : code.startsWith("4") ? "bg-red-600" : "bg-yellow-600"} text-white px-2 py-1 rounded text-xs">${code}</span>
320
+ <span class="text-gray-300 text-sm">${response.description || "Response"}</span>
321
+ </div>`;
322
+ }).join("")}
126
323
  </div>
127
324
  </div>
128
325
  </div>
129
326
 
130
327
  <div class="lg:col-span-2 space-y-6">
131
- ${E}
132
- ${A}
328
+ ${requestExample}
329
+ ${responseSection}
133
330
  </div>
134
331
  </div>
135
- </section>`}).join(`
136
- `);return`
137
- <section id="${o.toLowerCase()}-tag" class="fade-in">
332
+ </section>`;
333
+ }).join("\n");
334
+ return `
335
+ <section id="${tag.toLowerCase()}-tag" class="fade-in">
138
336
  <div class="grid lg:grid-cols-3 gap-8 mb-12">
139
337
  <div class="lg:col-span-1">
140
338
  <div class="sticky top-24">
141
- <h2 class="text-3xl font-bold text-white mb-4">${o}</h2>
339
+ <h2 class="text-3xl font-bold text-white mb-4">${tag}</h2>
142
340
  <p class="text-gray-300 text-lg leading-relaxed">
143
- ${b}
341
+ ${tagDescription}
144
342
  </p>
145
343
  <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>
344
+ <span class="bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-medium">${endpoints.length} endpoints</span>
147
345
  <span class="bg-gray-600 text-white px-3 py-1 rounded-full text-sm font-medium">Authentication required</span>
148
346
  </div>
149
347
  </div>
@@ -153,7 +351,7 @@
153
351
  <div class="bg-dark-800/30 backdrop-blur-sm rounded-xl p-6 border border-dark-700">
154
352
  <h3 class="text-xl font-semibold text-white mb-6">Available Endpoints</h3>
155
353
  <div class="space-y-4">
156
- ${y}
354
+ ${endpointCards}
157
355
  </div>
158
356
  </div>
159
357
  </div>
@@ -163,16 +361,16 @@
163
361
  <hr class="border-dark-600">
164
362
 
165
363
  <div class="space-y-16">
166
- ${x}
167
- </div>`}).join(`
168
-
169
- `);return`<!DOCTYPE html>
364
+ ${detailedEndpoints}
365
+ </div>`;
366
+ }).join("\n\n");
367
+ return `<!DOCTYPE html>
170
368
  <html lang="es" class="dark">
171
369
  <head>
172
370
  <meta charset="UTF-8">
173
371
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
174
372
  <title>${this.spec.info.title}</title>
175
- <script src="https://cdn.tailwindcss.com"></script>
373
+ <script src="https://cdn.tailwindcss.com"><\/script>
176
374
  <script>
177
375
  tailwind.config = {
178
376
  darkMode: 'class',
@@ -195,7 +393,7 @@
195
393
  }
196
394
  }
197
395
  }
198
- </script>
396
+ <\/script>
199
397
  <style>
200
398
  .sidebar-transition {
201
399
  transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
@@ -245,7 +443,7 @@
245
443
  <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
444
  <div class="p-4 h-full overflow-y-auto pt-6">
247
445
  <div class="space-y-1">
248
- ${s}
446
+ ${sidebarSections}
249
447
  </div>
250
448
  </div>
251
449
  </aside>
@@ -257,7 +455,7 @@
257
455
 
258
456
  <div class="mb-12 fade-in">
259
457
  <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>
458
+ <p class="text-xl text-gray-300 mb-4">${this.spec.info.description || "Complete reference for our REST API"}</p>
261
459
  <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
460
  </div>
263
461
 
@@ -278,7 +476,7 @@
278
476
  </div>
279
477
 
280
478
  <div class="space-y-20">
281
- ${p}
479
+ ${contentSections}
282
480
  </div>
283
481
  </div>
284
482
  </main>
@@ -540,6 +738,12 @@
540
738
 
541
739
  window.addEventListener('resize', handleResize);
542
740
  handleResize();
543
- </script>
741
+ <\/script>
544
742
  </body>
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",()=>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});
743
+ </html>`;
744
+ }
745
+ };
746
+ //#endregion
747
+ export { Swagger };
748
+
749
+ //# sourceMappingURL=swagger.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swagger.mjs","names":[],"sources":["../src/swagger.ts"],"sourcesContent":["import SwaggerParser from \"@apidevtools/swagger-parser\";\nimport type { OpenAPI } from \"openapi-types\";\n\nexport interface SwaggerOptions {\n title?: string;\n description?: string;\n version?: string;\n basePath?: string;\n schemes?: string[];\n consumes?: string[];\n produces?: string[];\n tags?: { name: string; description?: string }[];\n securityDefinitions?: Record<string, any>;\n externalDocs?: { description: string; url: string };\n host?: string;\n}\n\nexport class Swagger {\n private spec: OpenAPI.Document = {\n openapi: \"3.0.0\",\n info: {\n title: \"API Documentation\",\n version: \"1.0.0\",\n },\n paths: {},\n components: {\n schemas: {},\n },\n } as OpenAPI.Document;\n host: string;\n\n constructor(options: SwaggerOptions = {}) {\n this.spec.info.title = options.title || \"API Documentation\";\n this.spec.info.description = options.description;\n this.spec.info.version = options.version || \"1.0.0\";\n this.host = options.host || \"https://example.com\";\n\n if (options.tags) {\n this.spec.tags = options.tags;\n }\n\n if (options.externalDocs) {\n this.spec.externalDocs = options.externalDocs;\n }\n\n if (options.securityDefinitions) {\n (this.spec as any).components = (this.spec as any).components || {};\n (this.spec as any).components.securitySchemes = options.securityDefinitions;\n }\n }\n\n addRoute(\n method: string,\n path: string,\n schema: any,\n summary?: string,\n description?: string,\n tags?: string[],\n ) {\n if (!schema) {\n return;\n }\n\n const normalizedPath = path.replace(/:([^/]+)/g, \"{$1}\");\n if (!this.spec.paths) {\n this.spec.paths = {};\n }\n if (!this.spec.paths[normalizedPath]) {\n this.spec.paths[normalizedPath] = {};\n }\n\n const methodLower = method.toLowerCase();\n const operationObject: any = {\n summary: summary || `${method} ${path}`,\n parameters: this.buildParameters(schema),\n requestBody: this.buildRequestBody(schema),\n responses: this.buildResponses(schema),\n };\n\n if (description) {\n operationObject.description = description;\n }\n\n if (tags) {\n operationObject.tags = tags;\n }\n\n (this.spec.paths as any)[normalizedPath][methodLower] = operationObject;\n }\n\n private buildParameters(schema: any) {\n const parameters: any[] = [];\n\n if (schema.params) {\n try {\n const jsonSchema = schema.params;\n if (jsonSchema.properties) {\n Object.entries(jsonSchema.properties).forEach(([name, propSchema]: [string, any]) => {\n parameters.push({\n name,\n in: \"path\",\n required: jsonSchema.required?.includes(name) ?? true,\n schema: propSchema,\n });\n });\n }\n } catch (e) {\n console.error(\"Failed to convert params schema:\", e);\n }\n }\n\n if (schema.query) {\n try {\n const jsonSchema = schema.query;\n if (jsonSchema.properties) {\n Object.entries(jsonSchema.properties).forEach(([name, propSchema]: [string, any]) => {\n parameters.push({\n name,\n in: \"query\",\n required: jsonSchema.required?.includes(name) ?? false,\n schema: propSchema,\n });\n });\n }\n } catch (e) {\n console.error(\"Failed to convert query schema:\", e);\n }\n }\n\n return parameters.length > 0 ? parameters : undefined;\n }\n\n private buildRequestBody(schema: any) {\n if (!schema.body) {\n return undefined;\n }\n\n try {\n const jsonSchema = schema.body;\n return {\n required: true,\n content: {\n \"application/json\": {\n schema: jsonSchema,\n },\n },\n };\n } catch (e) {\n console.error(\"Failed to convert body schema:\", e);\n return undefined;\n }\n }\n\n private buildResponses(schema: any) {\n const responses: Record<string, any> = {\n \"200\": {\n description: \"Successful response\",\n },\n };\n\n if (schema.response) {\n try {\n const jsonSchema = schema.response;\n responses[\"200\"].content = {\n \"application/json\": {\n schema: jsonSchema,\n },\n };\n } catch (e) {\n console.error(\"Failed to convert response schema:\", e);\n }\n }\n\n return responses;\n }\n\n async validate() {\n try {\n await SwaggerParser.validate(this.spec as any);\n return true;\n } catch (error) {\n console.error(\"Swagger validation error:\", error);\n return false;\n }\n }\n\n getSpec() {\n return this.spec;\n }\n\n generateHTML(): string {\n const hostname = new URL(this.host).hostname;\n\n const extractJsonSchema = (schema: any): any => {\n // biome-ignore lint/style/useBlockStatements: off\n if (!schema) return {};\n\n if (schema.jsonSchema) {\n return schema.jsonSchema;\n }\n\n if (schema.type || schema.properties || schema.$ref) {\n return schema;\n }\n\n return {};\n };\n\n const groupedPaths: Record<\n string,\n Array<{\n path: string;\n method: string;\n operation: any;\n }>\n > = {};\n\n const defaultGroup = \"API\";\n\n Object.entries(this.spec.paths || {}).forEach(([path, pathItem]: [string, any]) => {\n Object.entries(pathItem).forEach(([method, operation]: [string, any]) => {\n const tags = operation.tags || [defaultGroup];\n const primaryTag = tags[0];\n\n if (!groupedPaths[primaryTag]) {\n groupedPaths[primaryTag] = [];\n }\n\n groupedPaths[primaryTag].push({\n path,\n method: method.toUpperCase(),\n operation,\n });\n });\n });\n\n const sidebarSections = Object.entries(groupedPaths)\n .map(([tag, endpoints]) => {\n const endpointCount = endpoints.length;\n const endpointLinks = endpoints\n .map(({ path, method }) => {\n const operationId = `${method.toLowerCase()}-${path.replace(/[^a-zA-Z0-9]/g, \"-\")}`;\n return `<li><a href=\"#${operationId}\" class=\"block px-3 py-1.5 rounded-lg hover:bg-dark-700/50 transition-colors text-sm text-gray-400 hover:text-white\">${method} ${path}</a></li>`;\n })\n .join(\"\\n \");\n\n return `\n <div>\n <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=\"${tag.toLowerCase()}-group\">\n <div class=\"flex items-center space-x-2\">\n <h3 class=\"text-sm font-medium text-gray-300 group-hover:text-white\">${tag}</h3>\n <span class=\"text-xs text-gray-500 bg-dark-600 px-2 py-0.5 rounded-full\">${endpointCount}</span>\n <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\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\"></path>\n </svg>\n </div>\n </button>\n <div id=\"${tag.toLowerCase()}-group\" class=\"sidebar-group-content max-h-0 overflow-hidden transition-all duration-300\">\n <ul class=\"ml-6 mt-1 space-y-1\">\n ${endpointLinks}\n </ul>\n </div>\n </div>\n\n <div class=\"border-t border-dark-600 my-3\"></div>`;\n })\n .join(\"\");\n\n const contentSections = Object.entries(groupedPaths)\n .map(([tag, endpoints]) => {\n const tagDescription =\n this.spec.tags?.find((t) => t.name === tag)?.description ||\n `Manage ${tag.toLowerCase()} operations`;\n\n const endpointCards = endpoints\n .map(({ path, method, operation }) => {\n const operationId = `${method.toLowerCase()}-${path.replace(/[^a-zA-Z0-9]/g, \"-\")}`;\n const methodColor =\n {\n GET: \"bg-green-600\",\n POST: \"bg-blue-600\",\n PUT: \"bg-yellow-600\",\n DELETE: \"bg-red-600\",\n PATCH: \"bg-purple-600\",\n WS: \"bg-indigo-600\",\n SUB: \"bg-pink-600\",\n }[method] || \"bg-gray-600\";\n\n return `\n <a href=\"#${operationId}\" class=\"block p-4 rounded-lg border border-dark-600 hover:border-blue-500 hover:bg-dark-700/30 transition-all group\">\n <div class=\"flex items-center justify-between mb-2\">\n <div class=\"flex items-center space-x-3\">\n <span class=\"${methodColor} text-white px-3 py-1 rounded-lg text-sm font-medium\">${method}</span>\n <code class=\"text-blue-400 font-mono\">${path}</code>\n </div>\n <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\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n </div>\n <p class=\"text-gray-300 text-sm\">${operation.summary || operation.description || `${method} ${path} operation`}</p>\n </a>`;\n })\n .join(\"\\n\");\n\n const detailedEndpoints = endpoints\n .map(({ path, method, operation }) => {\n const operationId = `${method.toLowerCase()}-${path.replace(/[^a-zA-Z0-9]/g, \"-\")}`;\n const methodColor =\n {\n GET: \"bg-green-600\",\n POST: \"bg-blue-600\",\n PUT: \"bg-yellow-600\",\n DELETE: \"bg-red-600\",\n PATCH: \"bg-purple-600\",\n WS: \"bg-indigo-600\",\n SUB: \"bg-pink-600\",\n }[method] || \"bg-gray-600\";\n\n let parametersSection = \"\";\n if (operation.parameters && operation.parameters.length > 0) {\n const pathParams = operation.parameters.filter((p: any) => p.in === \"path\");\n const queryParams = operation.parameters.filter((p: any) => p.in === \"query\");\n\n if (pathParams.length > 0) {\n parametersSection += `\n <div>\n <h4 class=\"text-lg font-semibold text-white mb-3\">Path Parameters</h4>\n <div class=\"space-y-3\">\n ${pathParams\n .map(\n (param: any) => `\n <div class=\"border border-dark-600 rounded-lg p-3\">\n <div class=\"flex items-center justify-between mb-1\">\n <code class=\"text-blue-400\">${param.name}</code>\n <span class=\"text-xs ${param.required ? \"text-red-400\" : \"text-gray-400\"}\">${param.required ? \"required\" : \"optional\"}</span>\n </div>\n <p class=\"text-sm text-gray-300\">${param.description || `${param.name} parameter`}</p>\n </div>`,\n )\n .join(\"\")}\n </div>\n </div>`;\n }\n\n if (queryParams.length > 0) {\n parametersSection += `\n <div>\n <h4 class=\"text-lg font-semibold text-white mb-3\">Query Parameters</h4>\n <div class=\"space-y-3\">\n ${queryParams\n .map(\n (param: any) => `\n <div class=\"border border-dark-600 rounded-lg p-3\">\n <div class=\"flex items-center justify-between mb-1\">\n <code class=\"text-blue-400\">${param.name}</code>\n <span class=\"text-xs ${param.required ? \"text-red-400\" : \"text-gray-400\"}\">${param.required ? \"required\" : \"optional\"}</span>\n </div>\n <p class=\"text-sm text-gray-300\">${param.description || `${param.name} parameter`}</p>\n </div>`,\n )\n .join(\"\")}\n </div>\n </div>`;\n }\n }\n\n let requestBodySection = \"\";\n if (operation.requestBody) {\n const schema = operation.requestBody.content?.[\"application/json\"]?.schema;\n const cleanSchema = extractJsonSchema(schema);\n\n if (cleanSchema.properties) {\n requestBodySection = `\n <div>\n <h4 class=\"text-lg font-semibold text-white mb-3\">Body Parameters</h4>\n <div class=\"space-y-3\">\n ${Object.entries(cleanSchema.properties)\n .map(\n ([propName, propSchema]: [string, any]) => `\n <div class=\"border border-dark-600 rounded-lg p-3\">\n <div class=\"flex items-center justify-between mb-1\">\n <code class=\"text-blue-400\">${propName}</code>\n <span class=\"text-xs ${cleanSchema.required?.includes(propName) ? \"text-red-400\" : \"text-gray-400\"}\">${cleanSchema.required?.includes(propName) ? \"required\" : \"optional\"}</span>\n </div>\n <p class=\"text-sm text-gray-300\">${propSchema.description || `${propName} field`}</p>\n </div>`,\n )\n .join(\"\")}\n </div>\n </div>`;\n }\n }\n\n const baseUrl = `https://${hostname}`;\n let requestExample = \"\";\n\n if (method !== \"WS\" && method !== \"SUB\") {\n let curlCommand = `curl -X ${method} '${baseUrl}${path}'`;\n\n if (operation.requestBody) {\n curlCommand += ` \\\\\\n -H 'Content-Type: application/json'`;\n const schema = operation.requestBody.content?.[\"application/json\"]?.schema;\n const cleanSchema = extractJsonSchema(schema);\n\n if (cleanSchema.properties) {\n const exampleData: Record<string, any> = {};\n Object.entries(cleanSchema.properties).forEach(\n ([propName, propSchema]: [string, any]) => {\n if (propSchema.type === \"string\") {\n exampleData[propName] =\n propSchema.format === \"email\"\n ? \"user@example.com\"\n : `example ${propName}`;\n } else if (propSchema.type === \"number\") {\n exampleData[propName] = 123;\n } else if (propSchema.type === \"boolean\") {\n exampleData[propName] = true;\n } else {\n exampleData[propName] = `example ${propName}`;\n }\n },\n );\n curlCommand += ` \\\\\\n -d '${JSON.stringify(exampleData, null, 2).replace(/\\n/g, \"\\\\n \")}'`;\n }\n }\n\n curlCommand += ` \\\\\\n -H 'Authorization: Bearer your-token'`;\n\n requestExample = `\n <div>\n <div class=\"flex items-center justify-between mb-4\">\n <h4 class=\"text-lg font-semibold text-white\">Request Example</h4>\n <button class=\"copy-btn bg-dark-700 hover:bg-dark-600 px-3 py-1 rounded-lg text-sm transition-colors\" data-copy=\"${curlCommand.replace(/'/g, \"\\\\'\")}\">\n Copy\n </button>\n </div>\n <div class=\"code-block rounded-xl p-4 border border-dark-600\">\n <pre class=\"text-sm text-gray-300 overflow-x-auto\"><code>${curlCommand}</code></pre>\n </div>\n </div>`;\n }\n\n let responseSection = \"\";\n const response200 = operation.responses?.[\"200\"];\n if (response200?.content?.[\"application/json\"]?.schema) {\n const schema = response200.content[\"application/json\"].schema;\n const cleanSchema = extractJsonSchema(schema);\n\n responseSection = `\n <div>\n <h4 class=\"text-lg font-semibold text-white mb-4\">Response Schema</h4>\n <div class=\"code-block rounded-xl p-4 border border-dark-600\">\n <pre class=\"text-sm text-gray-300 overflow-x-auto\"><code>${JSON.stringify(cleanSchema, null, 2)}</code></pre>\n </div>\n </div>`;\n }\n\n return `\n <section id=\"${operationId}\" class=\"fade-in\">\n <div class=\"grid lg:grid-cols-3 gap-8\">\n <div class=\"lg:col-span-1 space-y-6\">\n <div>\n <div class=\"flex items-center space-x-3 mb-4\">\n <span class=\"${methodColor} text-white px-3 py-1 rounded-lg text-sm font-medium\">${method}</span>\n <code class=\"text-blue-400 font-mono\">${path}</code>\n </div>\n <p class=\"text-gray-300\">${operation.description || operation.summary || `${method} ${path} operation`}</p>\n </div>\n\n ${parametersSection}\n ${requestBodySection}\n\n <div>\n <h4 class=\"text-lg font-semibold text-white mb-3\">Headers</h4>\n <div class=\"border border-dark-600 rounded-lg p-3\">\n <div class=\"flex items-center justify-between mb-1\">\n <code class=\"text-blue-400\">Authorization</code>\n <span class=\"text-xs text-red-400\">required</span>\n </div>\n <p class=\"text-sm text-gray-300\">Bearer token for authentication</p>\n </div>\n </div>\n\n <div>\n <h4 class=\"text-lg font-semibold text-white mb-3\">Responses</h4>\n <div class=\"space-y-2\">\n ${Object.entries(operation.responses || {})\n .map(([code, response]: [string, any]) => {\n const statusColor = code.startsWith(\"2\")\n ? \"bg-green-600\"\n : code.startsWith(\"4\")\n ? \"bg-red-600\"\n : \"bg-yellow-600\";\n return `\n <div class=\"flex items-center space-x-3\">\n <span class=\"${statusColor} text-white px-2 py-1 rounded text-xs\">${code}</span>\n <span class=\"text-gray-300 text-sm\">${response.description || \"Response\"}</span>\n </div>`;\n })\n .join(\"\")}\n </div>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-6\">\n ${requestExample}\n ${responseSection}\n </div>\n </div>\n </section>`;\n })\n .join(\"\\n\");\n\n return `\n <section id=\"${tag.toLowerCase()}-tag\" class=\"fade-in\">\n <div class=\"grid lg:grid-cols-3 gap-8 mb-12\">\n <div class=\"lg:col-span-1\">\n <div class=\"sticky top-24\">\n <h2 class=\"text-3xl font-bold text-white mb-4\">${tag}</h2>\n <p class=\"text-gray-300 text-lg leading-relaxed\">\n ${tagDescription}\n </p>\n <div class=\"mt-6 flex items-center space-x-2\">\n <span class=\"bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-medium\">${endpoints.length} endpoints</span>\n <span class=\"bg-gray-600 text-white px-3 py-1 rounded-full text-sm font-medium\">Authentication required</span>\n </div>\n </div>\n </div>\n\n <div class=\"lg:col-span-2\">\n <div class=\"bg-dark-800/30 backdrop-blur-sm rounded-xl p-6 border border-dark-700\">\n <h3 class=\"text-xl font-semibold text-white mb-6\">Available Endpoints</h3>\n <div class=\"space-y-4\">\n ${endpointCards}\n </div>\n </div>\n </div>\n </div>\n </section>\n\n <hr class=\"border-dark-600\">\n\n <div class=\"space-y-16\">\n ${detailedEndpoints}\n </div>`;\n })\n .join(\"\\n\\n\");\n\n return `<!DOCTYPE html>\n<html lang=\"es\" class=\"dark\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${this.spec.info.title}</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <script>\n tailwind.config = {\n darkMode: 'class',\n theme: {\n extend: {\n colors: {\n dark: {\n 50: '#f8fafc',\n 100: '#f1f5f9',\n 200: '#e2e8f0',\n 300: '#cbd5e1',\n 400: '#94a3b8',\n 500: '#64748b',\n 600: '#475569',\n 700: '#334155',\n 800: '#1e293b',\n 900: '#0f172a',\n }\n }\n }\n }\n }\n </script>\n <style>\n .sidebar-transition {\n transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;\n }\n .fade-in {\n animation: fadeIn 0.3s ease-in-out;\n }\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .code-block {\n background: linear-gradient(135deg, #1e293b 0%, #334155 100%);\n }\n </style>\n</head>\n<body class=\"bg-dark-900 text-gray-100 font-sans\">\n\n <nav class=\"fixed top-0 left-0 right-0 z-50 bg-dark-900/80 backdrop-blur-md border-b border-dark-700\">\n <div class=\"flex items-center justify-between px-4 py-3\">\n <div class=\"flex items-center space-x-4\">\n <button id=\"sidebarToggle\" class=\"p-2 rounded-lg hover:bg-dark-700 transition-colors\">\n <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"></path>\n </svg>\n </button>\n <h1 class=\"text-xl font-bold text-blue-400\">${this.spec.info.title}</h1>\n </div>\n\n <div class=\"flex items-center space-x-4\">\n <!-- <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\">\n <option value=\"shell\">Shell</option>\n <option value=\"nodejs\">Node.js</option>\n </select> -->\n\n <button id=\"themeToggle\" class=\"p-2 rounded-lg hover:bg-dark-700 transition-colors\">\n <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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>\n </svg>\n </button>\n </div>\n </div>\n </nav>\n\n <div class=\"flex pt-16\">\n\n <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\">\n <div class=\"p-4 h-full overflow-y-auto pt-6\">\n <div class=\"space-y-1\">\n ${sidebarSections}\n </div>\n </div>\n </aside>\n\n <div id=\"sidebarOverlay\" class=\"fixed top-16 bottom-0 left-0 right-0 bg-black/50 z-30 lg:hidden hidden\"></div>\n\n <main id=\"mainContent\" class=\"flex-1 ml-64 transition-all duration-300\">\n <div class=\"max-w-7xl mx-auto px-4 py-8\">\n\n <div class=\"mb-12 fade-in\">\n <h1 class=\"text-4xl font-bold text-white mb-4\">${this.spec.info.title}</h1>\n <p class=\"text-xl text-gray-300 mb-4\">${this.spec.info.description || \"Complete reference for our REST API\"}</p>\n <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>\n </div>\n\n <div class=\"grid gap-8 mb-12 fade-in\">\n <div class=\"bg-dark-800/30 backdrop-blur-sm rounded-xl p-6 border border-dark-700\">\n <h3 class=\"text-xl font-semibold text-white mb-4\">Server</h3>\n <div class=\"space-y-3\">\n <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\">\n <div class=\"w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center\">\n <svg class=\"w-4 h-4 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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>\n </svg>\n </div>\n <span class=\"text-gray-300 group-hover:text-white transition-colors\">${this.host}</span>\n </a>\n </div>\n </div>\n </div>\n\n <div class=\"space-y-20\">\n ${contentSections}\n </div>\n </div>\n </main>\n </div>\n\n <script>\n const sidebarToggle = document.getElementById('sidebarToggle');\n const sidebar = document.getElementById('sidebar');\n const sidebarOverlay = document.getElementById('sidebarOverlay');\n const mainContent = document.getElementById('mainContent');\n const themeToggle = document.getElementById('themeToggle');\n const html = document.documentElement;\n\n themeToggle.addEventListener('click', () => {\n if (html.classList.contains('dark')) {\n html.classList.remove('dark');\n html.classList.add('light');\n document.body.className = 'bg-gray-50 text-gray-900 font-sans';\n\n const navbar = document.querySelector('nav');\n navbar.className = 'fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200';\n\n const sidebar = document.getElementById('sidebar');\n 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');\n\n document.querySelectorAll('h1, h2, h3, h4').forEach(heading => {\n heading.classList.remove('text-white', 'text-gray-100');\n heading.classList.add('text-gray-900');\n });\n\n document.querySelectorAll('.text-gray-300').forEach(el => {\n el.classList.remove('text-gray-300');\n el.classList.add('text-gray-700');\n });\n\n document.querySelectorAll('.text-gray-400').forEach(el => {\n el.classList.remove('text-gray-400');\n el.classList.add('text-gray-600');\n });\n\n document.querySelectorAll('.border-dark-600').forEach(el => {\n el.classList.remove('border-dark-600');\n el.classList.add('border-gray-300');\n });\n\n document.querySelectorAll('.bg-dark-700\\\\/30').forEach(el => {\n el.classList.remove('bg-dark-700/30');\n el.classList.add('bg-gray-100/30');\n });\n\n document.querySelectorAll('.bg-dark-800\\\\/20').forEach(el => {\n el.classList.remove('bg-dark-800/20');\n el.classList.add('bg-gray-50/20');\n });\n\n document.querySelectorAll('.bg-dark-600').forEach(el => {\n el.classList.remove('bg-dark-600');\n el.classList.add('bg-gray-400');\n });\n\n document.querySelectorAll('.text-gray-500').forEach(el => {\n el.classList.remove('text-gray-500');\n el.classList.add('text-gray-600');\n });\n\n document.querySelectorAll('aside a').forEach(link => {\n link.classList.add('text-gray-700', 'hover:text-gray-900', 'hover:bg-gray-100');\n });\n\n document.querySelectorAll('pre code').forEach(code => {\n code.classList.remove('text-gray-300');\n code.classList.add('text-gray-800');\n });\n\n document.querySelectorAll('.bg-dark-800\\\\/30').forEach(el => {\n el.classList.remove('bg-dark-800/30', 'border-dark-700');\n el.classList.add('bg-white/30', 'border-gray-200');\n });\n\n document.querySelectorAll('.code-block').forEach(el => {\n el.style.background = 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%)';\n el.classList.remove('border-dark-600');\n el.classList.add('border-gray-300');\n });\n\n document.querySelectorAll('.border-dark-600').forEach(el => {\n el.classList.remove('border-dark-600');\n el.classList.add('border-gray-300');\n });\n\n } else {\n html.classList.remove('light');\n html.classList.add('dark');\n document.body.className = 'bg-dark-900 text-gray-100 font-sans';\n\n const navbar = document.querySelector('nav');\n navbar.className = 'fixed top-0 left-0 right-0 z-50 bg-dark-900/80 backdrop-blur-md border-b border-dark-700';\n\n const sidebar = document.getElementById('sidebar');\n 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');\n\n document.querySelectorAll('h1, h2, h3, h4').forEach(heading => {\n heading.classList.remove('text-gray-900');\n heading.classList.add('text-white');\n });\n\n document.querySelectorAll('.text-gray-700').forEach(el => {\n el.classList.remove('text-gray-700');\n el.classList.add('text-gray-300');\n });\n\n document.querySelectorAll('.text-gray-600').forEach(el => {\n el.classList.remove('text-gray-600');\n el.classList.add('text-gray-400');\n });\n\n document.querySelectorAll('.border-gray-300').forEach(el => {\n el.classList.remove('border-gray-300');\n el.classList.add('border-dark-600');\n });\n\n document.querySelectorAll('.bg-gray-100\\\\/30').forEach(el => {\n el.classList.remove('bg-gray-100/30');\n el.classList.add('bg-dark-700/30');\n });\n\n document.querySelectorAll('.bg-gray-50\\\\/20').forEach(el => {\n el.classList.remove('bg-gray-50/20');\n el.classList.add('bg-dark-800/20');\n });\n\n document.querySelectorAll('.bg-gray-400').forEach(el => {\n el.classList.remove('bg-gray-400');\n el.classList.add('bg-dark-600');\n });\n\n document.querySelectorAll('.text-gray-600').forEach(el => {\n el.classList.remove('text-gray-600');\n el.classList.add('text-gray-500');\n });\n\n document.querySelectorAll('aside a').forEach(link => {\n link.classList.remove('text-gray-700', 'hover:text-gray-900', 'hover:bg-gray-100');\n });\n\n document.querySelectorAll('pre code').forEach(code => {\n code.classList.remove('text-gray-800');\n code.classList.add('text-gray-300');\n });\n\n document.querySelectorAll('.bg-white\\\\/30').forEach(el => {\n el.classList.remove('bg-white/30', 'border-gray-200');\n el.classList.add('bg-dark-800/30', 'border-dark-700');\n });\n\n document.querySelectorAll('.code-block').forEach(el => {\n el.style.background = 'linear-gradient(135deg, #1e293b 0%, #334155 100%)';\n el.classList.remove('border-gray-300');\n el.classList.add('border-dark-600');\n });\n\n document.querySelectorAll('.border-gray-300').forEach(el => {\n el.classList.remove('border-gray-300');\n el.classList.add('border-dark-600');\n });\n }\n });\n\n function toggleSidebar() {\n const isHidden = sidebar.classList.contains('-translate-x-full');\n\n if (isHidden) {\n sidebar.classList.remove('-translate-x-full');\n mainContent.classList.remove('ml-0');\n mainContent.classList.add('ml-64');\n } else {\n sidebar.classList.add('-translate-x-full');\n mainContent.classList.remove('ml-64');\n mainContent.classList.add('ml-0');\n }\n\n if (window.innerWidth < 1024) {\n sidebarOverlay.classList.toggle('hidden');\n }\n }\n\n sidebarToggle.addEventListener('click', toggleSidebar);\n sidebarOverlay.addEventListener('click', toggleSidebar);\n\n document.querySelectorAll('.copy-btn').forEach(btn => {\n btn.addEventListener('click', async () => {\n const textToCopy = btn.getAttribute('data-copy');\n try {\n await navigator.clipboard.writeText(textToCopy);\n btn.textContent = 'Copied!';\n setTimeout(() => {\n btn.textContent = 'Copy';\n }, 2000);\n } catch (err) {\n console.error('Failed to copy text: ', err);\n }\n });\n });\n\n document.querySelectorAll('a[href^=\"#\"]').forEach(anchor => {\n anchor.addEventListener('click', function (e) {\n e.preventDefault();\n const target = document.querySelector(this.getAttribute('href'));\n if (target) {\n target.scrollIntoView({\n behavior: 'smooth',\n block: 'start'\n });\n\n if (window.innerWidth < 1024) {\n toggleSidebar();\n }\n }\n });\n });\n\n document.querySelectorAll('.sidebar-group-toggle').forEach(button => {\n button.addEventListener('click', () => {\n const targetId = button.getAttribute('data-target');\n const targetGroup = document.getElementById(targetId);\n const arrow = button.querySelector('svg');\n\n if (targetGroup.style.maxHeight && targetGroup.style.maxHeight !== '0px') {\n targetGroup.style.maxHeight = '0px';\n arrow.style.transform = 'rotate(0deg)';\n } else {\n targetGroup.style.maxHeight = targetGroup.scrollHeight + 'px';\n arrow.style.transform = 'rotate(180deg)';\n }\n });\n });\n\n document.querySelectorAll('.sidebar-group-content').forEach(group => {\n group.style.maxHeight = group.scrollHeight + 'px';\n });\n document.querySelectorAll('.sidebar-group-toggle svg').forEach(arrow => {\n arrow.style.transform = 'rotate(180deg)';\n });\n\n function handleResize() {\n if (window.innerWidth < 1024) {\n sidebar.classList.add('-translate-x-full');\n mainContent.classList.remove('ml-64');\n mainContent.classList.add('ml-0');\n } else {\n if (!sidebar.hasAttribute('data-manually-hidden')) {\n sidebar.classList.remove('-translate-x-full');\n mainContent.classList.remove('ml-0');\n mainContent.classList.add('ml-64');\n }\n sidebarOverlay.classList.add('hidden');\n }\n }\n\n window.addEventListener('resize', handleResize);\n handleResize();\n </script>\n</body>\n</html>`;\n }\n}\n"],"mappings":"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"}