@tezx/devtools 1.0.10 โ 1.0.11
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/cjs/html/StaticFile.js +145 -0
- package/cjs/html/cookies.js +140 -0
- package/cjs/html/env.js +168 -0
- package/cjs/html/index.js +36 -0
- package/cjs/html/routes.js +160 -0
- package/cjs/index.js +398 -0
- package/html/StaticFile.d.ts +2 -0
- package/html/StaticFile.js +142 -0
- package/html/cookies.d.ts +2 -0
- package/html/cookies.js +137 -0
- package/html/env.d.ts +2 -0
- package/html/env.js +165 -0
- package/html/index.d.ts +9 -0
- package/html/index.js +33 -0
- package/html/routes.d.ts +2 -0
- package/html/routes.js +157 -0
- package/index.d.ts +8 -0
- package/index.js +395 -0
- package/package.json +5 -2
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StaticFile = StaticFile;
|
|
4
|
+
function StaticFile(ctx, app) {
|
|
5
|
+
const staticMap = Object.keys(app.staticFile);
|
|
6
|
+
const rawJSON = JSON.stringify(staticMap.map(r => {
|
|
7
|
+
const [method, path] = r.split(" ");
|
|
8
|
+
return {
|
|
9
|
+
method, path
|
|
10
|
+
};
|
|
11
|
+
}), null, 2);
|
|
12
|
+
const toCSV = (data) => {
|
|
13
|
+
const headers = ["Path", "Method"];
|
|
14
|
+
const rows = data.map((s) => {
|
|
15
|
+
const [method, path] = s.split(" ");
|
|
16
|
+
return [JSON.stringify(path), JSON.stringify(method)];
|
|
17
|
+
});
|
|
18
|
+
return [headers.join(","), ...rows.map((r) => r.join(","))].join("\n");
|
|
19
|
+
};
|
|
20
|
+
const csvString = toCSV(staticMap).replace(/"/g, """);
|
|
21
|
+
return `
|
|
22
|
+
<style>
|
|
23
|
+
.download {
|
|
24
|
+
margin-top: 1rem;
|
|
25
|
+
display: flex;
|
|
26
|
+
gap: 1rem;
|
|
27
|
+
}
|
|
28
|
+
.download a {
|
|
29
|
+
padding: 0.5rem 1rem;
|
|
30
|
+
border: 1px solid var(--accent);
|
|
31
|
+
color: var(--accent);
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
border-radius: 6px;
|
|
34
|
+
}
|
|
35
|
+
.download a:hover {
|
|
36
|
+
background: var(--accent);
|
|
37
|
+
color: white;
|
|
38
|
+
}
|
|
39
|
+
#searchBar {
|
|
40
|
+
margin: 1rem 0;
|
|
41
|
+
display: flex;
|
|
42
|
+
}
|
|
43
|
+
#searchBar input {
|
|
44
|
+
flex: 1;
|
|
45
|
+
}
|
|
46
|
+
pre {
|
|
47
|
+
margin: 0;
|
|
48
|
+
font-size: 0.875rem;
|
|
49
|
+
line-height: 1.4;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
|
|
53
|
+
<div class="tabs toolbar">
|
|
54
|
+
<a class="tab-btn active" onclick="showTab('static')">๐ Static Files (${staticMap?.length})</a>
|
|
55
|
+
<a class="tab-btn" onclick="showTab('json')">๐งพ Raw JSON</a>
|
|
56
|
+
<a class="tab-btn" onclick="showTab('export')">๐ค Export</a>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div id="searchBar">
|
|
60
|
+
<input type="text" id="search" placeholder="Filter by path or handler..." />
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div id="staticTab" class="table-container">
|
|
64
|
+
<table id="staticTable">
|
|
65
|
+
<thead>
|
|
66
|
+
<tr>
|
|
67
|
+
<th>#</th>
|
|
68
|
+
<th>Path</th>
|
|
69
|
+
<th>Method</th>
|
|
70
|
+
<th>Copy</th>
|
|
71
|
+
</tr>
|
|
72
|
+
</thead>
|
|
73
|
+
<tbody>
|
|
74
|
+
${staticMap
|
|
75
|
+
.map((s, i) => {
|
|
76
|
+
const [method, path] = s.split(" ");
|
|
77
|
+
return `
|
|
78
|
+
<tr>
|
|
79
|
+
<td>${i + 1}</td>
|
|
80
|
+
<td>
|
|
81
|
+
<a href="${path}" target="_blank" style="color: var(--accent); text-decoration: underline; font-weight: 500; font-size: 0.95rem;">
|
|
82
|
+
${path}
|
|
83
|
+
</a>
|
|
84
|
+
</td>
|
|
85
|
+
<td>${method}</td>
|
|
86
|
+
<td>
|
|
87
|
+
<button class="copy-btn" onclick="copyToClipboard('${path}')">
|
|
88
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
89
|
+
<path d="M10 1.5A1.5 1.5 0 0 1 11.5 3v1h-1V3a.5.5 0 0 0-.5-.5H4A1.5 1.5 0 0 0 2.5 4v8A1.5 1.5 0 0 0 4 13.5H5v1H4A2.5 2.5 0 0 1 1.5 12V4A2.5 2.5 0 0 1 4 1.5h6z"/>
|
|
90
|
+
<path d="M5.5 5A1.5 1.5 0 0 0 4 6.5v7A1.5 1.5 0 0 0 5.5 15h6A1.5 1.5 0 0 0 13 13.5v-7A1.5 1.5 0 0 0 11.5 5h-6zM5 6.5A.5.5 0 0 1 5.5 6h6a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-7z"/>
|
|
91
|
+
</svg>
|
|
92
|
+
</button>
|
|
93
|
+
</td>
|
|
94
|
+
</tr>
|
|
95
|
+
`;
|
|
96
|
+
})
|
|
97
|
+
.join("")}
|
|
98
|
+
</tbody>
|
|
99
|
+
</table>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div id="jsonTab" style="display:none">
|
|
103
|
+
<div class="json-view"><pre><code>${rawJSON}</code></pre></div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div id="exportTab" style="display:none">
|
|
107
|
+
<div class="download">
|
|
108
|
+
<a href="data:text/json;charset=utf-8,${encodeURIComponent(rawJSON)}" download="static.json">๐ฅ JSON</a>
|
|
109
|
+
<a href="data:text/csv;charset=utf-8,${csvString}" download="static.csv">๐ฅ CSV</a>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<script>
|
|
114
|
+
const tabs = ['static', 'json', 'export'];
|
|
115
|
+
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
116
|
+
|
|
117
|
+
function showTab(tab) {
|
|
118
|
+
tabs.forEach(t => {
|
|
119
|
+
document.getElementById(t + 'Tab').style.display = t === tab ? 'block' : 'none';
|
|
120
|
+
});
|
|
121
|
+
tabBtns.forEach(btn => {
|
|
122
|
+
const active = btn.textContent.toLowerCase().includes(tab);
|
|
123
|
+
btn.classList.toggle('active', active);
|
|
124
|
+
});
|
|
125
|
+
document.getElementById('searchBar').style.display = (tab === 'static') ? 'flex' : 'none';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
document.getElementById('search').addEventListener('input', (e) => {
|
|
129
|
+
const keyword = e.target.value.toLowerCase();
|
|
130
|
+
const rows = document.querySelectorAll('#staticTable tbody tr');
|
|
131
|
+
rows.forEach(row => {
|
|
132
|
+
row.style.display = row.textContent.toLowerCase().includes(keyword) ? '' : 'none';
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
function copyToClipboard(text) {
|
|
137
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
138
|
+
showToast("Copied: " + text);
|
|
139
|
+
}).catch(err => {
|
|
140
|
+
alert("Failed to copy "+ err?.message);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
</script>
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CookiesInspector = CookiesInspector;
|
|
4
|
+
function CookiesInspector(ctx) {
|
|
5
|
+
const html = `
|
|
6
|
+
<style>
|
|
7
|
+
td[contenteditable="true"] {
|
|
8
|
+
outline: 0px;
|
|
9
|
+
}
|
|
10
|
+
td[contenteditable="true"]:focus {
|
|
11
|
+
outline: 1px solid var(--accent);
|
|
12
|
+
background: #fef9e7;
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
15
|
+
|
|
16
|
+
<div class="tabs">
|
|
17
|
+
<a onclick="addCookieRow()">โ Add Cookie</a>
|
|
18
|
+
<a onclick="saveCookies()">๐พ Save All</a>
|
|
19
|
+
<a onclick="exportCookies()">๐ค Export JSON</a>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="table-container">
|
|
23
|
+
<table>
|
|
24
|
+
<thead>
|
|
25
|
+
<tr>
|
|
26
|
+
<th>#</th>
|
|
27
|
+
<th>Key</th>
|
|
28
|
+
<th>Value</th>
|
|
29
|
+
<th>Actions</th>
|
|
30
|
+
</tr>
|
|
31
|
+
</thead>
|
|
32
|
+
<tbody id="cookie-body"></tbody>
|
|
33
|
+
</table>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<pre class="json-view" id="json-output">
|
|
37
|
+
|
|
38
|
+
</pre>
|
|
39
|
+
|
|
40
|
+
<script>
|
|
41
|
+
function getCookies() {
|
|
42
|
+
const cookies = {};
|
|
43
|
+
document.cookie.split(';').forEach(c => {
|
|
44
|
+
const [k, ...v] = c.trim().split('=');
|
|
45
|
+
cookies[k] = decodeURIComponent(v.join('='));
|
|
46
|
+
});
|
|
47
|
+
return cookies;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function renderCookies() {
|
|
51
|
+
const tbody = document.getElementById('cookie-body');
|
|
52
|
+
tbody.innerHTML = '';
|
|
53
|
+
const cookies = getCookies();
|
|
54
|
+
let index = 1;
|
|
55
|
+
for (const [key, val] of Object.entries(cookies)) {
|
|
56
|
+
const row = document.createElement('tr');
|
|
57
|
+
row.innerHTML = \`
|
|
58
|
+
<td>\${index++}</td>
|
|
59
|
+
<td contenteditable="true" class="cookie-key" style="white-space: nowrap">\${key}</td>
|
|
60
|
+
<td contenteditable="true" class="cookie-value">\${val}</td>
|
|
61
|
+
<td class="action">
|
|
62
|
+
<button class="copy-btn" onclick="copyRow(this)">
|
|
63
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
64
|
+
<path d="M10 1.5A1.5 1.5 0 0 1 11.5 3v1h-1V3a.5.5 0 0 0-.5-.5H4A1.5 1.5 0 0 0 2.5 4v8A1.5 1.5 0 0 0 4 13.5H5v1H4A2.5 2.5 0 0 1 1.5 12V4A2.5 2.5 0 0 1 4 1.5h6z"/>
|
|
65
|
+
<path d="M5.5 5A1.5 1.5 0 0 0 4 6.5v7A1.5 1.5 0 0 0 5.5 15h6A1.5 1.5 0 0 0 13 13.5v-7A1.5 1.5 0 0 0 11.5 5h-6zM5 6.5A.5.5 0 0 1 5.5 6h6a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-7z"/>
|
|
66
|
+
</svg>
|
|
67
|
+
</button>
|
|
68
|
+
<button class="delete-btn" onclick="deleteRow(this)">Delete</button>
|
|
69
|
+
</td>
|
|
70
|
+
\`;
|
|
71
|
+
tbody.appendChild(row);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function addCookieRow() {
|
|
76
|
+
const tbody = document.getElementById('cookie-body');
|
|
77
|
+
const row = document.createElement('tr');
|
|
78
|
+
row.innerHTML = \`
|
|
79
|
+
<td>\${tbody.children.length + 1}</td>
|
|
80
|
+
<td contenteditable="true" class="cookie-key" style="white-space: nowrap"></td>
|
|
81
|
+
<td contenteditable="true" class="cookie-value"></td>
|
|
82
|
+
<td class="action">
|
|
83
|
+
<button class="copy-btn" onclick="copyRow(this)">๐</button>
|
|
84
|
+
<button class="delete-btn" onclick="deleteRow(this)">Delete</button>
|
|
85
|
+
</td>
|
|
86
|
+
\`;
|
|
87
|
+
tbody.appendChild(row);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function deleteRow(button) {
|
|
91
|
+
const row = button.closest('tr');
|
|
92
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
93
|
+
if (key) {
|
|
94
|
+
document.cookie = key + '=; Max-Age=0; path=/';
|
|
95
|
+
}
|
|
96
|
+
row.remove();
|
|
97
|
+
renderCookies();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function saveCookies() {
|
|
101
|
+
const rows = document.querySelectorAll('#cookie-body tr');
|
|
102
|
+
rows.forEach(row => {
|
|
103
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
104
|
+
const value = row.querySelector('.cookie-value')?.innerText.trim();
|
|
105
|
+
if (key) {
|
|
106
|
+
document.cookie = key + '=' + encodeURIComponent(value) + '; path=/';
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
renderCookies();
|
|
110
|
+
showToast('โ
Cookies saved!');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
function exportCookies() {
|
|
115
|
+
const blob = new Blob([JSON.stringify(getCookies(), null, 2)], { type: 'application/json' });
|
|
116
|
+
const url = URL.createObjectURL(blob);
|
|
117
|
+
const a = document.createElement('a');
|
|
118
|
+
a.href = url;
|
|
119
|
+
a.download = 'cookie.json';
|
|
120
|
+
a.click();
|
|
121
|
+
URL.revokeObjectURL(url);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function copyRow(button) {
|
|
125
|
+
const row = button.closest('tr');
|
|
126
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
127
|
+
const value = row.querySelector('.cookie-value')?.innerText.trim();
|
|
128
|
+
if (key) {
|
|
129
|
+
navigator.clipboard.writeText(\`\${key}: \${value}\`)
|
|
130
|
+
.then(() => {
|
|
131
|
+
showToast(\`โ
Copied: \${key}\`);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
renderCookies();
|
|
137
|
+
</script>
|
|
138
|
+
`;
|
|
139
|
+
return html;
|
|
140
|
+
}
|
package/cjs/html/env.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
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><code>${safeKey}</code></td>
|
|
16
|
+
<td><code>${value}</code></td>
|
|
17
|
+
<td>
|
|
18
|
+
<button class="copy-btn" onclick="copyRowEnv('${safeKey}', \`${safeValue}\`)" title="Copy">
|
|
19
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
20
|
+
<path d="M10 1.5A1.5 1.5 0 0 1 11.5 3v1h-1V3a.5.5 0 0 0-.5-.5H4A1.5 1.5 0 0 0 2.5 4v8A1.5 1.5 0 0 0 4 13.5H5v1H4A2.5 2.5 0 0 1 1.5 12V4A2.5 2.5 0 0 1 4 1.5h6z"/>
|
|
21
|
+
<path d="M5.5 5A1.5 1.5 0 0 0 4 6.5v7A1.5 1.5 0 0 0 5.5 15h6A1.5 1.5 0 0 0 13 13.5v-7A1.5 1.5 0 0 0 11.5 5h-6zM5 6.5A.5.5 0 0 1 5.5 6h6a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-7z"/>
|
|
22
|
+
</svg>
|
|
23
|
+
</button>
|
|
24
|
+
</td>
|
|
25
|
+
</tr>`;
|
|
26
|
+
})
|
|
27
|
+
.join("")
|
|
28
|
+
: `
|
|
29
|
+
<tr>
|
|
30
|
+
<td colspan="4">
|
|
31
|
+
<p>โ ๏ธ <strong>No environment variables found.</strong></p>
|
|
32
|
+
<pre class="code-block">
|
|
33
|
+
const env = loadEnv();
|
|
34
|
+
|
|
35
|
+
export const app = new TezX({
|
|
36
|
+
env: env,
|
|
37
|
+
debugMode: true,
|
|
38
|
+
allowDuplicateMw: true,
|
|
39
|
+
});
|
|
40
|
+
</pre>
|
|
41
|
+
</td>
|
|
42
|
+
</tr>`;
|
|
43
|
+
return `
|
|
44
|
+
<style>
|
|
45
|
+
.tabs {
|
|
46
|
+
margin-bottom: 1rem;
|
|
47
|
+
}
|
|
48
|
+
.tabs a {
|
|
49
|
+
display: inline-block;
|
|
50
|
+
margin-right: 1rem;
|
|
51
|
+
text-decoration: none;
|
|
52
|
+
font-weight: 500;
|
|
53
|
+
color: #0f172a;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
table {
|
|
58
|
+
width: 100%;
|
|
59
|
+
border-collapse: collapse;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
table th, table td {
|
|
63
|
+
padding: 0.5rem;
|
|
64
|
+
border: 1px solid #e2e8f0;
|
|
65
|
+
text-align: left;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
table td button.copy-btn {
|
|
69
|
+
background: #f8fafc;
|
|
70
|
+
border: none;
|
|
71
|
+
padding: 0.3rem 0.4rem;
|
|
72
|
+
border-radius: 0.375rem;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
color: #334155;
|
|
75
|
+
transition: background 0.2s ease;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.search-container {
|
|
79
|
+
margin-bottom: 1rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.search-container input {
|
|
83
|
+
padding: 0.5rem;
|
|
84
|
+
width: 100%;
|
|
85
|
+
max-width: 400px;
|
|
86
|
+
border: 1px solid #cbd5e1;
|
|
87
|
+
border-radius: 0.375rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.code-block {
|
|
91
|
+
background: #f1f5f9;
|
|
92
|
+
padding: 1rem;
|
|
93
|
+
border-radius: 0.5rem;
|
|
94
|
+
font-size: 0.875rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
</style>
|
|
98
|
+
|
|
99
|
+
<div class="tabs">
|
|
100
|
+
<a onclick="exportEnv()">๐ค Export JSON</a>
|
|
101
|
+
<a onclick="copyAllEnv()">๐ Copy All</a>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="search-container">
|
|
105
|
+
<input type="text" id="env-search" placeholder="๐ Search environment variables..." oninput="filterEnvTable()" />
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="table-container">
|
|
109
|
+
<table>
|
|
110
|
+
<thead>
|
|
111
|
+
<tr>
|
|
112
|
+
<th>#</th>
|
|
113
|
+
<th>Key</th>
|
|
114
|
+
<th>Value</th>
|
|
115
|
+
<th>Copy</th>
|
|
116
|
+
</tr>
|
|
117
|
+
</thead>
|
|
118
|
+
<tbody id="env-body">
|
|
119
|
+
${tableRows}
|
|
120
|
+
</tbody>
|
|
121
|
+
</table>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<pre id="json-output">${JSON.stringify(env, null, 2)}</pre>
|
|
125
|
+
|
|
126
|
+
<script>
|
|
127
|
+
function exportEnv() {
|
|
128
|
+
const output = document.getElementById('json-output');
|
|
129
|
+
const blob = new Blob([output.textContent], { type: 'application/json' });
|
|
130
|
+
const url = URL.createObjectURL(blob);
|
|
131
|
+
const a = document.createElement('a');
|
|
132
|
+
a.href = url;
|
|
133
|
+
a.download = 'env.json';
|
|
134
|
+
a.click();
|
|
135
|
+
URL.revokeObjectURL(url);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function copyAllEnv() {
|
|
139
|
+
const output = document.getElementById('json-output');
|
|
140
|
+
navigator.clipboard.writeText(output.textContent).then(() => {
|
|
141
|
+
showToast('โ
All environment variables copied!');
|
|
142
|
+
}).catch(err => {
|
|
143
|
+
alert('โ Failed to copy: ' + err);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function copyRowEnv(key, value) {
|
|
148
|
+
const text = \`\${key}=\${value}\`;
|
|
149
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
150
|
+
showToast(\`โ
Copied: \${key}\`);
|
|
151
|
+
}).catch(err => {
|
|
152
|
+
alert('โ Failed to copy: ' + err);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function filterEnvTable() {
|
|
157
|
+
const input = document.getElementById('env-search').value.toLowerCase();
|
|
158
|
+
const rows = document.querySelectorAll('#env-body tr');
|
|
159
|
+
|
|
160
|
+
rows.forEach(row => {
|
|
161
|
+
const key = row.getAttribute('data-key') || '';
|
|
162
|
+
const value = row.getAttribute('data-value') || '';
|
|
163
|
+
row.style.display = (key.includes(input) || value.includes(input)) ? '' : 'none';
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
</script>
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.html = html;
|
|
4
|
+
const cookies_js_1 = require("./cookies.js");
|
|
5
|
+
const env_js_1 = require("./env.js");
|
|
6
|
+
const routes_js_1 = require("./routes.js");
|
|
7
|
+
const StaticFile_js_1 = require("./StaticFile.js");
|
|
8
|
+
function html(ctx, app) {
|
|
9
|
+
let tabDb = [
|
|
10
|
+
{
|
|
11
|
+
doc_title: "DevTools - Route Inspector",
|
|
12
|
+
label: "Routes",
|
|
13
|
+
tab: "routes",
|
|
14
|
+
content: (0, routes_js_1.Routes)(ctx, app),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
doc_title: "Static File",
|
|
18
|
+
label: "Static File",
|
|
19
|
+
tab: "static-file",
|
|
20
|
+
content: (0, StaticFile_js_1.StaticFile)(ctx, app),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
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
|
+
},
|
|
34
|
+
];
|
|
35
|
+
return tabDb;
|
|
36
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Routes = Routes;
|
|
4
|
+
function Routes(ctx, app) {
|
|
5
|
+
const routeMap = app.route.reduce((acc, curr) => {
|
|
6
|
+
const { pattern, method, handlers } = curr;
|
|
7
|
+
if (!acc[pattern])
|
|
8
|
+
acc[pattern] = {};
|
|
9
|
+
acc[pattern][method] = handlers;
|
|
10
|
+
return acc;
|
|
11
|
+
}, {});
|
|
12
|
+
const allRoutes = Object.entries(routeMap).map(([pattern, middlewareMap]) => ({
|
|
13
|
+
pattern,
|
|
14
|
+
handlers: Object.entries(middlewareMap).map(([method, handlers]) => ({
|
|
15
|
+
method,
|
|
16
|
+
handlerNames: handlers.map((fn) => fn.name || "[anonymous]"),
|
|
17
|
+
})),
|
|
18
|
+
}));
|
|
19
|
+
const rawJSON = JSON.stringify(allRoutes, null, 2);
|
|
20
|
+
const toCSV = (data) => {
|
|
21
|
+
const headers = ["Path", "Middleware", "Handler"];
|
|
22
|
+
const rows = data.flatMap((route) => route.handlers.flatMap((mw) => mw.handlerNames.map((h) => [
|
|
23
|
+
JSON.stringify(route.pattern),
|
|
24
|
+
JSON.stringify(mw.method),
|
|
25
|
+
JSON.stringify(h),
|
|
26
|
+
])));
|
|
27
|
+
return [headers.join(","), ...rows.map((r) => r.join(","))].join("\n");
|
|
28
|
+
};
|
|
29
|
+
const csvString = toCSV(allRoutes).replace(/"/g, """);
|
|
30
|
+
return `
|
|
31
|
+
<style>
|
|
32
|
+
.download {
|
|
33
|
+
margin-top: 1rem;
|
|
34
|
+
display: flex;
|
|
35
|
+
gap: 1rem;
|
|
36
|
+
}
|
|
37
|
+
.download a {
|
|
38
|
+
padding: 0.5rem 1rem;
|
|
39
|
+
border: 1px solid var(--accent);
|
|
40
|
+
color: var(--accent);
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
border-radius: 6px;
|
|
43
|
+
}
|
|
44
|
+
.download a:hover {
|
|
45
|
+
background: var(--accent);
|
|
46
|
+
color: white;
|
|
47
|
+
}
|
|
48
|
+
#searchBar {
|
|
49
|
+
margin: 1rem 0;
|
|
50
|
+
display: flex;
|
|
51
|
+
}
|
|
52
|
+
#searchBar input {
|
|
53
|
+
flex: 1;
|
|
54
|
+
}
|
|
55
|
+
table td button {
|
|
56
|
+
background: #e2e8f0;
|
|
57
|
+
border: none;
|
|
58
|
+
padding: 0.2rem 0.5rem;
|
|
59
|
+
border-radius: 0.375rem;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
margin-left: 0.5rem;
|
|
62
|
+
}
|
|
63
|
+
table td button:hover {
|
|
64
|
+
background: #cbd5e1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.router-title {
|
|
68
|
+
font-size: 1.25rem;
|
|
69
|
+
margin-bottom: 0.5rem;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
color: #334155;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
74
|
+
|
|
75
|
+
<div class="tabs toolbar">
|
|
76
|
+
<a class="tab-btn active" onclick="showTab('routes')">๐ Routes (${allRoutes.flat(2)?.length})</a>
|
|
77
|
+
<a class="tab-btn" onclick="showTab('json')">๐งพ Raw JSON</a>
|
|
78
|
+
<a class="tab-btn" onclick="showTab('export')">๐ค Export</a>
|
|
79
|
+
</div>
|
|
80
|
+
<h2 class="router-title">๐ Router: <span class="router-name">${app.router.name}</span></h2>
|
|
81
|
+
<div id="searchBar">
|
|
82
|
+
<input type="text" id="search" placeholder="Filter by path or middleware..." />
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div id="routesTab" class="table-container">
|
|
86
|
+
<table id="routesTable">
|
|
87
|
+
<thead>
|
|
88
|
+
<tr>
|
|
89
|
+
<th>#</th>
|
|
90
|
+
<th>Path</th>
|
|
91
|
+
<th>Middleware</th>
|
|
92
|
+
<th>Handlers</th>
|
|
93
|
+
</tr>
|
|
94
|
+
</thead>
|
|
95
|
+
<tbody>
|
|
96
|
+
${allRoutes.flatMap((r, i) => r.handlers.map((hn) => `
|
|
97
|
+
<tr>
|
|
98
|
+
<td>${i + 1}</td>
|
|
99
|
+
<td>${r.pattern}</td>
|
|
100
|
+
<td>
|
|
101
|
+
${hn.method === "ALL"
|
|
102
|
+
? `<span style="
|
|
103
|
+
display: inline-block;
|
|
104
|
+
background-color: var(--accent);
|
|
105
|
+
color: #fff;
|
|
106
|
+
padding: 4px 10px;
|
|
107
|
+
border-radius: 6px;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
box-shadow: 0 1px 4px rgba(0,0,0,0.15);
|
|
111
|
+
">${hn.method} <small style="opacity: 0.8;">(middleware)</small></span>`
|
|
112
|
+
: `<span style="
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
color: #333;
|
|
115
|
+
">${hn.method}</span>`}
|
|
116
|
+
</td>
|
|
117
|
+
<td>${hn.handlerNames.join(", ")}</td>
|
|
118
|
+
</tr>
|
|
119
|
+
`))
|
|
120
|
+
.join("")}
|
|
121
|
+
</tbody>
|
|
122
|
+
</table>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div id="jsonTab" style="display:none">
|
|
126
|
+
<div class="json-view"><pre>${rawJSON}</pre></div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<div id="exportTab" style="display:none">
|
|
130
|
+
<div class="download">
|
|
131
|
+
<a href="data:text/json;charset=utf-8,${encodeURIComponent(rawJSON)}" download="routes.json">๐ฅ JSON</a>
|
|
132
|
+
<a href="data:text/csv;charset=utf-8,${csvString}" download="routes.csv">๐ฅ CSV</a>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<script>
|
|
137
|
+
const tabs = ['routes', 'json', 'export'];
|
|
138
|
+
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
139
|
+
|
|
140
|
+
function showTab(tab) {
|
|
141
|
+
tabs.forEach(t => {
|
|
142
|
+
document.getElementById(t + 'Tab').style.display = t === tab ? 'block' : 'none';
|
|
143
|
+
});
|
|
144
|
+
tabBtns.forEach(btn => {
|
|
145
|
+
const active = btn.textContent.toLowerCase().includes(tab);
|
|
146
|
+
btn.classList.toggle('active', active);
|
|
147
|
+
});
|
|
148
|
+
document.getElementById('searchBar').style.display = (tab === 'routes') ? 'flex' : 'none';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
document.getElementById('search').addEventListener('input', (e) => {
|
|
152
|
+
const keyword = e.target.value.toLowerCase();
|
|
153
|
+
const rows = document.querySelectorAll('#routesTable tbody tr');
|
|
154
|
+
rows.forEach(row => {
|
|
155
|
+
row.style.display = row.textContent.toLowerCase().includes(keyword) ? '' : 'none';
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
</script>
|
|
159
|
+
`;
|
|
160
|
+
}
|