@tezx/devtools 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -1
- package/cjs/devtools/dumpRoutes.js +4 -13
- package/cjs/devtools/index.js +5 -15
- package/cjs/devtools/middlewares.js +29 -19
- package/cjs/html/cookies.js +146 -127
- package/cjs/html/env.js +145 -0
- package/cjs/html/index.js +22 -8
- package/cjs/html/middlewares.js +184 -0
- package/cjs/html/routes.js +114 -85
- package/cjs/index.js +39 -12
- package/devtools/dumpRoutes.d.ts +2 -4
- package/devtools/dumpRoutes.js +2 -10
- package/devtools/index.d.ts +3 -10
- package/devtools/index.js +2 -12
- package/devtools/middlewares.d.ts +6 -6
- package/devtools/middlewares.js +28 -18
- package/html/cookies.js +146 -127
- package/html/env.d.ts +2 -0
- package/html/env.js +142 -0
- package/html/index.d.ts +2 -2
- package/html/index.js +22 -8
- package/html/middlewares.d.ts +7 -0
- package/html/middlewares.js +181 -0
- package/html/routes.js +113 -84
- package/index.d.ts +3 -2
- package/index.js +36 -12
- package/package.json +1 -1
package/cjs/html/env.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnvInspector = EnvInspector;
|
|
4
|
+
function EnvInspector(ctx) {
|
|
5
|
+
const env = ctx.env;
|
|
6
|
+
const entries = Object.entries(env);
|
|
7
|
+
const tableRows = entries.length
|
|
8
|
+
? entries
|
|
9
|
+
.map(([key, value], i) => {
|
|
10
|
+
const safeKey = key.replace(/"/g, """);
|
|
11
|
+
const safeValue = value.replace(/`/g, "\\`").replace(/"/g, """);
|
|
12
|
+
return `
|
|
13
|
+
<tr data-key="${safeKey.toLowerCase()}" data-value="${value.toLowerCase()}">
|
|
14
|
+
<td>${i + 1}</td>
|
|
15
|
+
<td>${safeKey}</td>
|
|
16
|
+
<td>${value}</td>
|
|
17
|
+
<td><button onclick="copyRowEnv('${safeKey}', \`${safeValue}\`)">📋</button></td>
|
|
18
|
+
</tr>
|
|
19
|
+
`;
|
|
20
|
+
})
|
|
21
|
+
.join("")
|
|
22
|
+
: `
|
|
23
|
+
<tr>
|
|
24
|
+
<td colspan="4">
|
|
25
|
+
<p>⚠️ <strong>No environment variables found.</strong></p>
|
|
26
|
+
<pre style="margin-top: 1rem; background: #f1f5f9; padding: 1rem; border-radius: 0.5rem;">
|
|
27
|
+
const env = loadEnv();
|
|
28
|
+
|
|
29
|
+
export const app = new TezX({
|
|
30
|
+
env: env,
|
|
31
|
+
debugMode: true,
|
|
32
|
+
// basePath: 'v1',
|
|
33
|
+
allowDuplicateMw: true,
|
|
34
|
+
});
|
|
35
|
+
</pre>
|
|
36
|
+
</td>
|
|
37
|
+
</tr>`;
|
|
38
|
+
return `
|
|
39
|
+
<style>
|
|
40
|
+
table td button {
|
|
41
|
+
background: #e2e8f0;
|
|
42
|
+
border: none;
|
|
43
|
+
padding: 0.3rem 0.6rem;
|
|
44
|
+
border-radius: 0.375rem;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
}
|
|
47
|
+
table td button:hover {
|
|
48
|
+
background: #cbd5e1;
|
|
49
|
+
}
|
|
50
|
+
pre#json-output {
|
|
51
|
+
margin-top: 1rem;
|
|
52
|
+
padding: 1rem;
|
|
53
|
+
border-radius: 0.5rem;
|
|
54
|
+
background: #f9fafb;
|
|
55
|
+
border: 1px solid #e5e7eb;
|
|
56
|
+
font-size: 0.875rem;
|
|
57
|
+
overflow-x: auto;
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
.search-container {
|
|
61
|
+
margin-bottom: 1rem;
|
|
62
|
+
}
|
|
63
|
+
.search-container input {
|
|
64
|
+
padding: 0.5rem;
|
|
65
|
+
width: 100%;
|
|
66
|
+
max-width: 400px;
|
|
67
|
+
border: 1px solid #e5e7eb;
|
|
68
|
+
border-radius: 0.375rem;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
|
|
72
|
+
<div class="tabs">
|
|
73
|
+
<a onclick="exportEnv()">📤 Export JSON</a>
|
|
74
|
+
<a onclick="copyAllEnv()">📋 Copy All</a>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="search-container">
|
|
78
|
+
<input type="text" id="env-search" placeholder="🔍 Search environment variables..." oninput="filterEnvTable()" />
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="table-container">
|
|
82
|
+
<table>
|
|
83
|
+
<thead>
|
|
84
|
+
<tr>
|
|
85
|
+
<th>#</th>
|
|
86
|
+
<th>Key</th>
|
|
87
|
+
<th>Value</th>
|
|
88
|
+
<th>Copy</th>
|
|
89
|
+
</tr>
|
|
90
|
+
</thead>
|
|
91
|
+
<tbody id="env-body">
|
|
92
|
+
${tableRows}
|
|
93
|
+
</tbody>
|
|
94
|
+
</table>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<pre id="json-output">${JSON.stringify(env, null, 2)}</pre>
|
|
98
|
+
|
|
99
|
+
<script>
|
|
100
|
+
function exportEnv() {
|
|
101
|
+
const output = document.getElementById('json-output');
|
|
102
|
+
const blob = new Blob([output.textContent], { type: 'application/json' });
|
|
103
|
+
const url = URL.createObjectURL(blob);
|
|
104
|
+
const a = document.createElement('a');
|
|
105
|
+
a.href = url;
|
|
106
|
+
a.download = 'env.json';
|
|
107
|
+
a.click();
|
|
108
|
+
URL.revokeObjectURL(url);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function copyAllEnv() {
|
|
112
|
+
const output = document.getElementById('json-output');
|
|
113
|
+
navigator.clipboard.writeText(output.textContent).then(() => {
|
|
114
|
+
alert('✅ All environment variables copied!');
|
|
115
|
+
}).catch(err => {
|
|
116
|
+
alert('❌ Failed to copy: ' + err);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function copyRowEnv(key, value) {
|
|
121
|
+
const text = \`\${key}=\${value}\`;
|
|
122
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
123
|
+
alert(\`✅ Copied: \${text}\`);
|
|
124
|
+
}).catch(err => {
|
|
125
|
+
alert('❌ Failed to copy: ' + err);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function filterEnvTable() {
|
|
130
|
+
const input = document.getElementById('env-search').value.toLowerCase();
|
|
131
|
+
const rows = document.querySelectorAll('#env-body tr');
|
|
132
|
+
|
|
133
|
+
rows.forEach(row => {
|
|
134
|
+
const key = row.getAttribute('data-key') || '';
|
|
135
|
+
const value = row.getAttribute('data-value') || '';
|
|
136
|
+
if (key.includes(input) || value.includes(input)) {
|
|
137
|
+
row.style.display = '';
|
|
138
|
+
} else {
|
|
139
|
+
row.style.display = 'none';
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
</script>
|
|
144
|
+
`;
|
|
145
|
+
}
|
package/cjs/html/index.js
CHANGED
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.html = html;
|
|
4
|
-
const routes_js_1 = require("./routes.js");
|
|
5
4
|
const cookies_js_1 = require("./cookies.js");
|
|
5
|
+
const env_js_1 = require("./env.js");
|
|
6
|
+
const middlewares_js_1 = require("./middlewares.js");
|
|
7
|
+
const routes_js_1 = require("./routes.js");
|
|
6
8
|
function html(ctx, app) {
|
|
7
9
|
let tabDb = [
|
|
8
10
|
{
|
|
9
11
|
doc_title: "DevTools - Route Inspector",
|
|
10
12
|
label: "Routes",
|
|
11
|
-
tab:
|
|
12
|
-
content: (0, routes_js_1.Routes)(ctx, app)
|
|
13
|
+
tab: "routes",
|
|
14
|
+
content: (0, routes_js_1.Routes)(ctx, app),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
doc_title: "DevTools - Middleware Inspector",
|
|
18
|
+
label: "Middlewares",
|
|
19
|
+
tab: "middleware",
|
|
20
|
+
content: (0, middlewares_js_1.Middlewares)(ctx, app),
|
|
13
21
|
},
|
|
14
22
|
{
|
|
15
|
-
tab:
|
|
16
|
-
label:
|
|
17
|
-
doc_title:
|
|
18
|
-
content: (0, cookies_js_1.CookiesInspector)(ctx)
|
|
19
|
-
}
|
|
23
|
+
tab: "cookies",
|
|
24
|
+
label: "Cookies",
|
|
25
|
+
doc_title: "DevTools - Cookie Inspector",
|
|
26
|
+
content: (0, cookies_js_1.CookiesInspector)(ctx),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
tab: ".env",
|
|
30
|
+
label: "Environment",
|
|
31
|
+
doc_title: "DevTools - Environment",
|
|
32
|
+
content: (0, env_js_1.EnvInspector)(ctx),
|
|
33
|
+
},
|
|
20
34
|
];
|
|
21
35
|
return tabDb;
|
|
22
36
|
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Middlewares = Middlewares;
|
|
4
|
+
const index_js_1 = require("../devtools/index.js");
|
|
5
|
+
function Middlewares(ctx, app) {
|
|
6
|
+
const allRoutes = (0, index_js_1.dumpMiddlewares)(app);
|
|
7
|
+
const totalRoutes = allRoutes.length;
|
|
8
|
+
const middlewareRoutes = {};
|
|
9
|
+
for (const route of allRoutes) {
|
|
10
|
+
for (const mw of route.appliedMiddlewares || []) {
|
|
11
|
+
(middlewareRoutes[mw] ||= []).push(route);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const rawJSON = JSON.stringify(allRoutes, null, 2);
|
|
15
|
+
const toCSV = (data) => {
|
|
16
|
+
const headers = ["pattern", "type", "appliedMiddlewares"];
|
|
17
|
+
const csvRows = [
|
|
18
|
+
headers.join(","),
|
|
19
|
+
...data.map((r) => headers.map((h) => JSON.stringify(r[h] ?? "")).join(",")),
|
|
20
|
+
];
|
|
21
|
+
return csvRows.join("\n");
|
|
22
|
+
};
|
|
23
|
+
const csvString = toCSV(allRoutes).replace(/"/g, """);
|
|
24
|
+
return `
|
|
25
|
+
<style>
|
|
26
|
+
.download {
|
|
27
|
+
margin-top: 1rem;
|
|
28
|
+
display: flex;
|
|
29
|
+
gap: 1rem;
|
|
30
|
+
}
|
|
31
|
+
.download a {
|
|
32
|
+
padding: 0.5rem 1rem;
|
|
33
|
+
border: 1px solid var(--accent);
|
|
34
|
+
color: var(--accent);
|
|
35
|
+
text-decoration: none;
|
|
36
|
+
border-radius: 6px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.download a:hover {
|
|
40
|
+
background: var(--accent);
|
|
41
|
+
color: white;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#searchBar {
|
|
45
|
+
margin: 1rem 0;
|
|
46
|
+
display: flex;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#searchBar input {
|
|
50
|
+
flex: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
table td button {
|
|
54
|
+
background: #e2e8f0;
|
|
55
|
+
border: none;
|
|
56
|
+
padding: 0.2rem 0.5rem;
|
|
57
|
+
border-radius: 0.375rem;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
margin-left: 0.5rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
table td button:hover {
|
|
63
|
+
background: #cbd5e1;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
|
|
67
|
+
<div class="tabs toolbar">
|
|
68
|
+
<a class="tab-btn active counting" data-count="${totalRoutes}" onclick="showTab('routes')">📋 Routes</a>
|
|
69
|
+
<a class="tab-btn counting" data-count="${Object.keys(middlewareRoutes).length}" onclick="showTab('middlewares')">🧩 Middlewares</a>
|
|
70
|
+
<a class="tab-btn" onclick="showTab('json')">🧾 Raw JSON</a>
|
|
71
|
+
<a class="tab-btn" onclick="showTab('export')">📤 Export</a>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div id="searchBar">
|
|
75
|
+
<input type="text" id="search" placeholder="Filter by pattern/type/middleware..." />
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<!-- ROUTES -->
|
|
79
|
+
<div id="routesTab" class="table-container">
|
|
80
|
+
<table id="routesTable">
|
|
81
|
+
<thead>
|
|
82
|
+
<tr>
|
|
83
|
+
<th>#</th>
|
|
84
|
+
<th>Pattern</th>
|
|
85
|
+
<th>Type</th>
|
|
86
|
+
<th>Middlewares</th>
|
|
87
|
+
</tr>
|
|
88
|
+
</thead>
|
|
89
|
+
<tbody>
|
|
90
|
+
${allRoutes
|
|
91
|
+
.map((r, i) => `
|
|
92
|
+
<tr>
|
|
93
|
+
<td>${i + 1}</td>
|
|
94
|
+
<td>
|
|
95
|
+
${r.pattern}
|
|
96
|
+
<button onclick="copyText(\`${r.pattern}\`)">📋</button>
|
|
97
|
+
</td>
|
|
98
|
+
<td>
|
|
99
|
+
<span style="
|
|
100
|
+
display: inline-block;
|
|
101
|
+
padding: 0.2rem 0.5rem;
|
|
102
|
+
border-radius: 0.375rem;
|
|
103
|
+
color: white;
|
|
104
|
+
font-size: 0.75rem;
|
|
105
|
+
background-color: ${r.type === "static"
|
|
106
|
+
? "#16a34a"
|
|
107
|
+
: r.type === "wildcard"
|
|
108
|
+
? "#f97316"
|
|
109
|
+
: r.type === "optional params"
|
|
110
|
+
? "#3b82f6"
|
|
111
|
+
: r.type === "dynamic params"
|
|
112
|
+
? "#9333ea"
|
|
113
|
+
: "#6b7280"};
|
|
114
|
+
">
|
|
115
|
+
${r.type}
|
|
116
|
+
</span>
|
|
117
|
+
</td>
|
|
118
|
+
<td>${(r.appliedMiddlewares || []).join(", ")}</td>
|
|
119
|
+
</tr>
|
|
120
|
+
`)
|
|
121
|
+
.join("")}
|
|
122
|
+
</tbody>
|
|
123
|
+
</table>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- MIDDLEWARES -->
|
|
127
|
+
<div id="middlewaresTab" style="display:none">
|
|
128
|
+
${Object.entries(middlewareRoutes)
|
|
129
|
+
.map(([mw, routes]) => `
|
|
130
|
+
<h3>🔹 ${mw} (${routes.length})</h3>
|
|
131
|
+
<ul>
|
|
132
|
+
${routes.map((r) => `<li><code>${r.pattern}</code> <span>(${r.type})</span></li>`).join("")}
|
|
133
|
+
</ul>
|
|
134
|
+
`)
|
|
135
|
+
.join("")}
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<!-- RAW JSON -->
|
|
139
|
+
<div id="jsonTab" style="display:none">
|
|
140
|
+
<div class="json-view"><pre>${rawJSON}</pre></div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<!-- EXPORT -->
|
|
144
|
+
<div id="exportTab" style="display:none">
|
|
145
|
+
<div class="download">
|
|
146
|
+
<a href="data:text/json;charset=utf-8,${encodeURIComponent(rawJSON)}" download="middlewares.json">📥 JSON</a>
|
|
147
|
+
<a href="data:text/csv;charset=utf-8,${csvString}" download="middlewares.csv">📥 CSV</a>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<script>
|
|
152
|
+
const tabs = ['routes', 'middlewares', 'json', 'export'];
|
|
153
|
+
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
154
|
+
|
|
155
|
+
function showTab(tab) {
|
|
156
|
+
tabs.forEach(t => {
|
|
157
|
+
document.getElementById(t + 'Tab').style.display = t === tab ? 'block' : 'none';
|
|
158
|
+
document.getElementById(t + 'Tab').classList.toggle('active', t === tab);
|
|
159
|
+
});
|
|
160
|
+
tabBtns.forEach(btn => {
|
|
161
|
+
const active = btn.textContent.toLowerCase().includes(tab);
|
|
162
|
+
btn.classList.toggle('active', active);
|
|
163
|
+
});
|
|
164
|
+
document.getElementById('searchBar').style.display = (tab === 'routes') ? 'flex' : 'none';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
document.getElementById('search').addEventListener('input', (e) => {
|
|
168
|
+
const keyword = e.target.value.toLowerCase();
|
|
169
|
+
const rows = document.querySelectorAll('#routesTable tbody tr');
|
|
170
|
+
rows.forEach(row => {
|
|
171
|
+
row.style.display = row.textContent.toLowerCase().includes(keyword) ? '' : 'none';
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
function copyText(text) {
|
|
176
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
177
|
+
alert('✅ Copied: ' + text);
|
|
178
|
+
}).catch(err => {
|
|
179
|
+
alert('❌ Failed to copy: ' + err);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
</script>
|
|
183
|
+
`;
|
|
184
|
+
}
|
package/cjs/html/routes.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Routes = Routes;
|
|
4
|
-
const
|
|
4
|
+
const index_js_1 = require("../devtools/index.js");
|
|
5
5
|
function Routes(ctx, app) {
|
|
6
|
-
const allRoutes = (0,
|
|
6
|
+
const allRoutes = (0, index_js_1.dumpRoutes)(app);
|
|
7
7
|
const totalRoutes = allRoutes.length;
|
|
8
8
|
const middlewareStats = {};
|
|
9
9
|
const middlewareRoutes = {};
|
|
@@ -15,14 +15,14 @@ function Routes(ctx, app) {
|
|
|
15
15
|
}
|
|
16
16
|
const rawJSON = JSON.stringify(allRoutes, null, 2);
|
|
17
17
|
const toCSV = (data) => {
|
|
18
|
-
const headers = [
|
|
18
|
+
const headers = ["method", "endpoint", "pattern", "appliedMiddlewares"];
|
|
19
19
|
const csvRows = [
|
|
20
|
-
headers.join(
|
|
21
|
-
...data.map(r => headers.map(h => JSON.stringify(r[h] ??
|
|
20
|
+
headers.join(","),
|
|
21
|
+
...data.map((r) => headers.map((h) => JSON.stringify(r[h] ?? "")).join(",")),
|
|
22
22
|
];
|
|
23
|
-
return csvRows.join(
|
|
23
|
+
return csvRows.join("\n");
|
|
24
24
|
};
|
|
25
|
-
const csvString = toCSV(allRoutes).replace(/"/g,
|
|
25
|
+
const csvString = toCSV(allRoutes).replace(/"/g, """);
|
|
26
26
|
return `
|
|
27
27
|
<style>
|
|
28
28
|
.download {
|
|
@@ -52,107 +52,136 @@ function Routes(ctx, app) {
|
|
|
52
52
|
#searchBar input {
|
|
53
53
|
flex: 1;
|
|
54
54
|
}
|
|
55
|
-
</style>
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
table td button {
|
|
57
|
+
background: #e2e8f0;
|
|
58
|
+
border: none;
|
|
59
|
+
padding: 0.2rem 0.5rem;
|
|
60
|
+
border-radius: 0.375rem;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
margin-left: 0.5rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
table td button:hover {
|
|
66
|
+
background: #cbd5e1;
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
69
|
+
|
|
70
|
+
<div class="tabs toolbar">
|
|
58
71
|
<a class="tab-btn active counting" data-count="${totalRoutes}" onclick="showTab('routes')">📋 Routes</a>
|
|
59
|
-
<a class="tab-btn counting" data-count="${Object.keys(middlewareStats).length}" onclick="showTab('stats')">📈
|
|
60
|
-
Stats</a>
|
|
72
|
+
<a class="tab-btn counting" data-count="${Object.keys(middlewareStats).length}" onclick="showTab('stats')">📈 Stats</a>
|
|
61
73
|
<a class="tab-btn" onclick="showTab('middlewares')">🧩 Middlewares</a>
|
|
62
74
|
<a class="tab-btn" onclick="showTab('json')">🧾 Raw JSON</a>
|
|
63
75
|
<a class="tab-btn" onclick="showTab('export')">📤 Export</a>
|
|
64
|
-
</div>
|
|
76
|
+
</div>
|
|
65
77
|
|
|
66
|
-
<div id="searchBar"
|
|
78
|
+
<div id="searchBar">
|
|
67
79
|
<input type="text" id="search" placeholder="Filter by method/path/middleware..." />
|
|
68
|
-
</div>
|
|
80
|
+
</div>
|
|
69
81
|
|
|
70
|
-
<!-- ROUTES -->
|
|
71
|
-
<div id="routesTab">
|
|
82
|
+
<!-- ROUTES -->
|
|
83
|
+
<div id="routesTab" class="table-container">
|
|
72
84
|
<table id="routesTable">
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
<thead>
|
|
86
|
+
<tr>
|
|
87
|
+
<th>#</th>
|
|
88
|
+
<th>Method</th>
|
|
89
|
+
<th>Endpoint</th>
|
|
90
|
+
<th>Pattern</th>
|
|
91
|
+
<th>Middlewares</th>
|
|
92
|
+
</tr>
|
|
93
|
+
</thead>
|
|
94
|
+
<tbody>
|
|
95
|
+
${allRoutes
|
|
96
|
+
.map((r, i) => `
|
|
97
|
+
<tr>
|
|
98
|
+
<td>${i + 1}</td>
|
|
99
|
+
<td>${r.method}</td>
|
|
100
|
+
<td>
|
|
101
|
+
${r.endpoint}
|
|
102
|
+
<button onclick="copyText(\`${r.endpoint}\`)">📋</button>
|
|
103
|
+
</td>
|
|
104
|
+
<td>
|
|
105
|
+
${r.pattern}
|
|
106
|
+
<button onclick="copyText(\`${r.pattern}\`)">📋</button>
|
|
107
|
+
</td>
|
|
108
|
+
<td>${(r.appliedMiddlewares || []).join(", ")}</td>
|
|
109
|
+
</tr>
|
|
110
|
+
`)
|
|
111
|
+
.join("")}
|
|
112
|
+
</tbody>
|
|
92
113
|
</table>
|
|
93
|
-
</div>
|
|
114
|
+
</div>
|
|
94
115
|
|
|
95
|
-
<!-- STATS -->
|
|
96
|
-
<div id="statsTab" style="display:none">
|
|
116
|
+
<!-- STATS -->
|
|
117
|
+
<div id="statsTab" style="display:none">
|
|
97
118
|
<div class="json-view">
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
119
|
+
Total Routes: ${totalRoutes}<br />
|
|
120
|
+
Middleware Used: ${Object.keys(middlewareStats).length}<br />
|
|
121
|
+
<pre>
|
|
122
|
+
${Object.entries(middlewareStats)
|
|
123
|
+
.map(([mw, count]) => `- ${mw}: ${count} routes`)
|
|
124
|
+
.join("\n")}
|
|
125
|
+
</pre>
|
|
102
126
|
</div>
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
<!-- MIDDLEWARES -->
|
|
106
|
-
<div id="middlewaresTab" style="display:none">
|
|
107
|
-
${Object.entries(middlewareRoutes)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<!-- MIDDLEWARES -->
|
|
130
|
+
<div id="middlewaresTab" style="display:none">
|
|
131
|
+
${Object.entries(middlewareRoutes)
|
|
132
|
+
.map(([mw, routes]) => `
|
|
133
|
+
<h3>🔹 ${mw} (${routes.length})</h3>
|
|
134
|
+
<ul>
|
|
135
|
+
${routes.map((r) => `<li><code>${r.method} ${r.endpoint}</code></li>`).join("")}
|
|
136
|
+
</ul>
|
|
137
|
+
`)
|
|
138
|
+
.join("")}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<!-- RAW JSON -->
|
|
142
|
+
<div id="jsonTab" style="display:none">
|
|
143
|
+
<div class="json-view"><pre>${rawJSON}</pre></div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<!-- EXPORT -->
|
|
147
|
+
<div id="exportTab" style="display:none">
|
|
122
148
|
<div class="download">
|
|
123
|
-
|
|
124
|
-
|
|
149
|
+
<a href="data:text/json;charset=utf-8,${encodeURIComponent(rawJSON)}" download="routes.json">📥 JSON</a>
|
|
150
|
+
<a href="data:text/csv;charset=utf-8,${csvString}" download="routes.csv">📥 CSV</a>
|
|
125
151
|
</div>
|
|
126
|
-
</div>
|
|
152
|
+
</div>
|
|
127
153
|
|
|
128
|
-
<script>
|
|
154
|
+
<script>
|
|
129
155
|
const tabs = ['routes', 'stats', 'middlewares', 'json', 'export'];
|
|
130
156
|
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
131
157
|
|
|
132
158
|
function showTab(tab) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
tabBtns.forEach(btn => {
|
|
143
|
-
const active = btn.textContent.toLowerCase().includes(tab);
|
|
144
|
-
btn.classList.toggle('active', active);
|
|
145
|
-
});
|
|
146
|
-
document.getElementById('searchBar').style.display = (tab === 'routes') ? 'flex' : 'none';
|
|
159
|
+
tabs.forEach(t => {
|
|
160
|
+
document.getElementById(t + 'Tab').style.display = t === tab ? 'block' : 'none';
|
|
161
|
+
document.getElementById(t + 'Tab').classList.toggle('active', t === tab);
|
|
162
|
+
});
|
|
163
|
+
tabBtns.forEach(btn => {
|
|
164
|
+
const active = btn.textContent.toLowerCase().includes(tab);
|
|
165
|
+
btn.classList.toggle('active', active);
|
|
166
|
+
});
|
|
167
|
+
document.getElementById('searchBar').style.display = (tab === 'routes') ? 'flex' : 'none';
|
|
147
168
|
}
|
|
148
169
|
|
|
149
170
|
document.getElementById('search').addEventListener('input', (e) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
const keyword = e.target.value.toLowerCase();
|
|
172
|
+
const rows = document.querySelectorAll('#routesTable tbody tr');
|
|
173
|
+
rows.forEach(row => {
|
|
174
|
+
row.style.display = row.textContent.toLowerCase().includes(keyword) ? '' : 'none';
|
|
175
|
+
});
|
|
155
176
|
});
|
|
156
|
-
|
|
177
|
+
|
|
178
|
+
function copyText(text) {
|
|
179
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
180
|
+
alert('✅ Copied: ' + text);
|
|
181
|
+
}).catch(err => {
|
|
182
|
+
alert('❌ Failed to copy: ' + err);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
</script>
|
|
157
186
|
`;
|
|
158
187
|
}
|