xypriss-swagger 1.0.0

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.
@@ -0,0 +1,381 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{title}} - XyPriss API Docs</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ <style>
14
+ :root {
15
+ --bg: #05070a;
16
+ --surface: #0f1218;
17
+ --surface-hover: #161b22;
18
+ --primary: #6366f1;
19
+ --primary-hover: #818cf8;
20
+ --text: #f1f5f9;
21
+ --text-muted: #94a3b8;
22
+ --border: rgba(255, 255, 255, 0.06);
23
+
24
+ --get: #10b981;
25
+ --post: #3b82f6;
26
+ --put: #f59e0b;
27
+ --delete: #ef4444;
28
+ }
29
+
30
+ * {
31
+ margin: 0;
32
+ padding: 0;
33
+ box-sizing: border-box;
34
+ }
35
+ body {
36
+ font-family: "Outfit", sans-serif;
37
+ background-color: var(--bg);
38
+ color: var(--text);
39
+ display: flex;
40
+ min-height: 100vh;
41
+ }
42
+
43
+ aside {
44
+ width: 320px;
45
+ background-color: var(--surface);
46
+ border-right: 1px solid var(--border);
47
+ padding: 2.5rem 1.5rem;
48
+ position: sticky;
49
+ top: 0;
50
+ height: 100vh;
51
+ display: flex;
52
+ flex-direction: column;
53
+ overflow-y: auto;
54
+ }
55
+
56
+ .logo {
57
+ font-size: 1.25rem;
58
+ font-weight: 700;
59
+ margin-bottom: 2.5rem;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 0.75rem;
63
+ }
64
+
65
+ .logo-box {
66
+ width: 32px;
67
+ height: 32px;
68
+ background: linear-gradient(135deg, var(--primary), #a855f7);
69
+ border-radius: 8px;
70
+ }
71
+
72
+ .nav-group-title {
73
+ text-transform: uppercase;
74
+ font-size: 0.7rem;
75
+ font-weight: 700;
76
+ letter-spacing: 0.1em;
77
+ color: var(--text-muted);
78
+ margin-bottom: 1rem;
79
+ margin-top: 2rem;
80
+ }
81
+
82
+ .nav-item {
83
+ display: block;
84
+ padding: 0.75rem 1rem;
85
+ border-radius: 8px;
86
+ color: var(--text-muted);
87
+ text-decoration: none;
88
+ font-size: 0.9rem;
89
+ transition: all 0.2s;
90
+ margin-bottom: 0.25rem;
91
+ white-space: nowrap;
92
+ overflow: hidden;
93
+ text-overflow: ellipsis;
94
+ }
95
+
96
+ .nav-item:hover {
97
+ background: var(--surface-hover);
98
+ color: var(--text);
99
+ }
100
+
101
+ .nav-item.active {
102
+ background: rgba(99, 102, 241, 0.1);
103
+ color: var(--primary);
104
+ font-weight: 600;
105
+ }
106
+
107
+ main {
108
+ flex: 1;
109
+ padding: 4rem;
110
+ max-width: 1100px;
111
+ margin: 0 auto;
112
+ }
113
+
114
+ header {
115
+ margin-bottom: 4rem;
116
+ }
117
+
118
+ h1 {
119
+ font-size: 3rem;
120
+ font-weight: 700;
121
+ margin-bottom: 1rem;
122
+ letter-spacing: -0.02em;
123
+ }
124
+
125
+ .description {
126
+ font-size: 1.1rem;
127
+ color: var(--text-muted);
128
+ line-height: 1.7;
129
+ max-width: 800px;
130
+ }
131
+
132
+ .endpoint-section {
133
+ margin-bottom: 4rem;
134
+ scroll-margin-top: 4rem;
135
+ }
136
+
137
+ .endpoint-header {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 1.5rem;
141
+ margin-bottom: 1.5rem;
142
+ padding-bottom: 1rem;
143
+ border-bottom: 1px solid var(--border);
144
+ }
145
+
146
+ .method-badge {
147
+ padding: 0.4rem 0.8rem;
148
+ border-radius: 6px;
149
+ font-size: 0.8rem;
150
+ font-weight: 700;
151
+ text-transform: uppercase;
152
+ min-width: 70px;
153
+ text-align: center;
154
+ }
155
+
156
+ .method-get {
157
+ background: rgba(16, 185, 129, 0.1);
158
+ color: var(--get);
159
+ border: 1px solid rgba(16, 185, 129, 0.2);
160
+ }
161
+ .method-post {
162
+ background: rgba(59, 130, 246, 0.1);
163
+ color: var(--post);
164
+ border: 1px solid rgba(59, 130, 246, 0.2);
165
+ }
166
+ .method-put {
167
+ background: rgba(245, 158, 11, 0.1);
168
+ color: var(--put);
169
+ border: 1px solid rgba(245, 158, 11, 0.2);
170
+ }
171
+ .method-delete {
172
+ background: rgba(239, 68, 68, 0.1);
173
+ color: var(--delete);
174
+ border: 1px solid rgba(239, 68, 68, 0.2);
175
+ }
176
+
177
+ .path {
178
+ font-family: "Monaco", "Consolas", monospace;
179
+ font-size: 1.1rem;
180
+ font-weight: 600;
181
+ color: var(--text);
182
+ }
183
+
184
+ .endpoint-card {
185
+ background: var(--surface);
186
+ border: 1px solid var(--border);
187
+ border-radius: 16px;
188
+ padding: 2rem;
189
+ margin-top: 1rem;
190
+ }
191
+
192
+ .summary {
193
+ font-size: 1.25rem;
194
+ font-weight: 600;
195
+ margin-bottom: 1rem;
196
+ }
197
+
198
+ .detail-item {
199
+ margin-top: 2rem;
200
+ }
201
+
202
+ .detail-label {
203
+ font-size: 0.8rem;
204
+ font-weight: 700;
205
+ text-transform: uppercase;
206
+ color: var(--text-muted);
207
+ margin-bottom: 0.75rem;
208
+ display: block;
209
+ }
210
+
211
+ .loader {
212
+ display: flex;
213
+ flex-direction: column;
214
+ align-items: center;
215
+ justify-content: center;
216
+ gap: 1.5rem;
217
+ height: 60vh;
218
+ }
219
+
220
+ .spinner {
221
+ width: 40px;
222
+ height: 40px;
223
+ border: 3px solid rgba(99, 102, 241, 0.1);
224
+ border-top-color: var(--primary);
225
+ border-radius: 50%;
226
+ animation: spin 0.8s linear infinite;
227
+ }
228
+
229
+ @keyframes spin {
230
+ to {
231
+ transform: rotate(360deg);
232
+ }
233
+ }
234
+
235
+ #error-view {
236
+ display: none;
237
+ background: rgba(239, 68, 68, 0.05);
238
+ border: 1px solid rgba(239, 68, 68, 0.2);
239
+ padding: 2rem;
240
+ border-radius: 12px;
241
+ color: #ef4444;
242
+ }
243
+ </style>
244
+ </head>
245
+ <body>
246
+ <aside>
247
+ <div class="logo">
248
+ <div class="logo-box"></div>
249
+ XyPriss Docs
250
+ </div>
251
+ <div class="nav-group-title">Endpoints</div>
252
+ <nav id="sidebar-nav">
253
+ <!-- Sidebar links generated dynamically -->
254
+ </nav>
255
+ </aside>
256
+
257
+ <main>
258
+ <div id="loading-view" class="loader">
259
+ <div class="spinner"></div>
260
+ <p style="color: var(--text-muted)">
261
+ Synchronizing with API Core...
262
+ </p>
263
+ </div>
264
+
265
+ <div id="error-view">
266
+ <h3>Failed to load API specification</h3>
267
+ <p id="error-msg"></p>
268
+ </div>
269
+
270
+ <div id="docs-view" style="display: none">
271
+ <header>
272
+ <h1 id="doc-title">{{title}}</h1>
273
+ <p class="description" id="doc-description"></p>
274
+ </header>
275
+
276
+ <div id="endpoints-container">
277
+ <!-- Endpoints generated dynamically -->
278
+ </div>
279
+ </div>
280
+ </main>
281
+
282
+ <script>
283
+ const specUrl = "{{specUrl}}";
284
+ const endpointsContainer = document.getElementById(
285
+ "endpoints-container",
286
+ );
287
+ const sidebarNav = document.getElementById("sidebar-nav");
288
+ const loadingView = document.getElementById("loading-view");
289
+ const docsView = document.getElementById("docs-view");
290
+ const errorView = document.getElementById("error-view");
291
+
292
+ async function init() {
293
+ try {
294
+ const response = await fetch(specUrl);
295
+ if (!response.ok)
296
+ throw new Error(`HTTP Error: ${response.status}`);
297
+ const spec = await response.json();
298
+ render(spec);
299
+ } catch (err) {
300
+ loadingView.style.display = "none";
301
+ errorView.style.display = "block";
302
+ document.getElementById("error-msg").innerText =
303
+ err.message;
304
+ }
305
+ }
306
+
307
+ function render(spec) {
308
+ document.getElementById("doc-title").innerText =
309
+ spec.info.title || "API Documentation";
310
+ document.getElementById("doc-description").innerText =
311
+ spec.info.description || "";
312
+
313
+ const paths = spec.paths || {};
314
+ for (const [path, methods] of Object.entries(paths)) {
315
+ for (const [method, details] of Object.entries(methods)) {
316
+ const id = `${method}-${path.replace(/\//g, "-")}`;
317
+
318
+ // Create Sidebar Link
319
+ const navItem = document.createElement("a");
320
+ navItem.href = `#${id}`;
321
+ navItem.className = "nav-item";
322
+ navItem.innerText = `${method.toUpperCase()} ${path}`;
323
+ sidebarNav.appendChild(navItem);
324
+
325
+ // Create Endpoint Section
326
+ const section = document.createElement("section");
327
+ section.className = "endpoint-section";
328
+ section.id = id;
329
+
330
+ section.innerHTML = `
331
+ <div class="endpoint-header">
332
+ <span class="method-badge method-${method}">${method}</span>
333
+ <span class="path">${path}</span>
334
+ </div>
335
+ <div class="endpoint-card">
336
+ <div class="summary">${details.summary || "No Summary"}</div>
337
+ <p style="color: var(--text-muted)">${details.description || "No description provided."}</p>
338
+
339
+ ${
340
+ details.parameters && details.parameters.length
341
+ ? `
342
+ <div class="detail-item">
343
+ <span class="detail-label">Parameters</span>
344
+ <ul style="list-style: none; color: var(--text-muted)">
345
+ ${details.parameters.map((p) => `<li><code>${p.name}</code> (${p.in})</li>`).join("")}
346
+ </ul>
347
+ </div>
348
+ `
349
+ : ""
350
+ }
351
+
352
+ <div class="detail-item">
353
+ <span class="detail-label">Responses</span>
354
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
355
+ ${Object.entries(details.responses)
356
+ .map(
357
+ ([code, res]) => `
358
+ <div style="background: rgba(255,255,255,0.03); padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid var(--border)">
359
+ <span style="font-weight: 700; color: ${code.startsWith("2") ? "var(--get)" : "var(--delete)"}">${code}</span>
360
+ <span style="font-size: 0.8rem; margin-left: 0.5rem">${res.description}</span>
361
+ </div>
362
+ `,
363
+ )
364
+ .join("")}
365
+ </div>
366
+ </div>
367
+ </div>
368
+ `;
369
+ endpointsContainer.appendChild(section);
370
+ }
371
+ }
372
+
373
+ loadingView.style.display = "none";
374
+ docsView.style.display = "block";
375
+ }
376
+
377
+ init();
378
+ </script>
379
+ </body>
380
+ </html>
381
+
package/src/types.ts ADDED
@@ -0,0 +1,25 @@
1
+ export interface SwaggerConfig {
2
+ /**
3
+ * The path where the UI will be served.
4
+ * @default "/docs"
5
+ */
6
+ path?: string;
7
+
8
+ title?: string;
9
+ version?: string;
10
+ description?: string;
11
+ /**
12
+ * Port to launch the isolated documentation server on.
13
+ * @default 7070
14
+ */
15
+ port?: number;
16
+ }
17
+
18
+
19
+
20
+ export interface ISwaggerJSONStructure {
21
+ name: string;
22
+ version: string;
23
+ description: string;
24
+
25
+ }
package/src/ui.ts ADDED
@@ -0,0 +1,26 @@
1
+ export function getSwaggerUIHtml(specUrl: string, title: string): string {
2
+ const templatePath = require("path").join(
3
+ __dirname,
4
+ "..",
5
+ "src",
6
+ "template",
7
+ "ui.html",
8
+ );
9
+
10
+ try {
11
+ let html = require("fs").readFileSync(templatePath, "utf-8");
12
+
13
+ // Simple template engine using {{}} syntax
14
+ html = html.replace(/\{\{title\}\}/g, title);
15
+ html = html.replace(/\{\{specUrl\}\}/g, specUrl);
16
+
17
+ return html;
18
+ } catch (error) {
19
+ console.error(
20
+ `[SWAGGER] Error reading UI template at ${templatePath}:`,
21
+ error,
22
+ );
23
+ return `<h1>Error loading Swagger UI</h1><p>${String(error)}</p>`;
24
+ }
25
+ }
26
+
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "CommonJS",
5
+ "declaration": true,
6
+ "outDir": "./dist",
7
+ // "rootDir": "./src",
8
+ "rootDirs": ["./src", "../../src"],
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "paths": {
14
+ "xypriss": ["../../src/index.ts"] // for local dev purpose
15
+ }
16
+ },
17
+ "include": ["src/**/*", "../../src/index.ts"]
18
+ }
19
+
20
+