@tezx/devtools 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.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/cjs/devtools/dumpRoutes.js +41 -0
- package/cjs/devtools/index.js +17 -0
- package/cjs/devtools/middlewares.js +23 -0
- package/cjs/html/cookies.js +145 -0
- package/cjs/html/index.js +22 -0
- package/cjs/html/routes.js +160 -0
- package/cjs/index.js +312 -0
- package/devtools/dumpRoutes.d.ts +9 -0
- package/devtools/dumpRoutes.js +38 -0
- package/devtools/index.d.ts +10 -0
- package/devtools/index.js +12 -0
- package/devtools/middlewares.d.ts +7 -0
- package/devtools/middlewares.js +20 -0
- package/html/cookies.d.ts +2 -0
- package/html/cookies.js +142 -0
- package/html/index.d.ts +9 -0
- package/html/index.js +19 -0
- package/html/routes.d.ts +2 -0
- package/html/routes.js +157 -0
- package/index.d.ts +8 -0
- package/index.js +309 -0
- package/package.json +55 -0
package/cjs/index.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DevTools = DevTools;
|
|
4
|
+
const index_js_1 = require("./html/index.js");
|
|
5
|
+
function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
|
|
6
|
+
let { disableTabs, extraTabs } = options;
|
|
7
|
+
return (ctx) => {
|
|
8
|
+
let html = [...(disableTabs?.length ? (0, index_js_1.html)(ctx, app)?.filter(r => !disableTabs?.includes(r?.tab)) : (0, index_js_1.html)(ctx, app)), ...extraTabs || []];
|
|
9
|
+
let tab = ctx.req.query?._tab || html?.[0]?.tab;
|
|
10
|
+
const navbar = `
|
|
11
|
+
<header>
|
|
12
|
+
<div class="tabs">
|
|
13
|
+
<img src="http://papernxt.com/favicon.ico" style="height:32px;"/>
|
|
14
|
+
${html?.map(r => `<a href = "?_tab=${r?.tab}" class="${tab === r?.tab ? 'active' : ''}" > ${r?.label} </a>`)?.join('\n')}
|
|
15
|
+
</div>
|
|
16
|
+
<div class="tabs">
|
|
17
|
+
<a class="toggle-dark" onclick="toggleTheme()">🌙 Toggle Dark</a>
|
|
18
|
+
</div>
|
|
19
|
+
</header>
|
|
20
|
+
`;
|
|
21
|
+
let find = html.find(r => r?.tab == tab);
|
|
22
|
+
return ctx.html `
|
|
23
|
+
<!DOCTYPE html>
|
|
24
|
+
<html lang="en">
|
|
25
|
+
<head>
|
|
26
|
+
<meta charset="UTF-8" />
|
|
27
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
28
|
+
<title>${find?.doc_title}</title>
|
|
29
|
+
<style>
|
|
30
|
+
:root {
|
|
31
|
+
--bg: #f8f9fa;
|
|
32
|
+
--text: #212529;
|
|
33
|
+
--accent: #FF581E;
|
|
34
|
+
--header-bg: rgba(255, 255, 255, 0.94);
|
|
35
|
+
--table-bg: #ffffff;
|
|
36
|
+
--table-border: #d1d5db;
|
|
37
|
+
/* Gray-300 */
|
|
38
|
+
--tr-hover: #e3f2fd;
|
|
39
|
+
/* Light Blue Hover */
|
|
40
|
+
--tr-odd: #f8fafc;
|
|
41
|
+
/* Lightest gray-blue */
|
|
42
|
+
--tr-even: #ffffff;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
body.dark {
|
|
46
|
+
--bg: #0e1117;
|
|
47
|
+
--text: #f1f3f5;
|
|
48
|
+
--accent: #58a6ff;
|
|
49
|
+
/* GitHub Blue */
|
|
50
|
+
--header-bg: rgba(18, 18, 18, 0.9);
|
|
51
|
+
--table-bg: #161b22;
|
|
52
|
+
--table-border: #30363d;
|
|
53
|
+
--tr-hover: #1f6feb33;
|
|
54
|
+
/* Blue hover with opacity */
|
|
55
|
+
--tr-odd: #1a1f24;
|
|
56
|
+
--tr-even: #0f1419;
|
|
57
|
+
|
|
58
|
+
--col-1: #9ca3af;
|
|
59
|
+
/* Gray-400 */
|
|
60
|
+
--col-2: #58a6ff;
|
|
61
|
+
/* Blue-300 */
|
|
62
|
+
--col-3: #4ade80;
|
|
63
|
+
/* Green-400 */
|
|
64
|
+
--col-4: #facc15;
|
|
65
|
+
/* Yellow-400 */
|
|
66
|
+
--col-5: #c084fc;
|
|
67
|
+
/* Purple-400 */
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
body {
|
|
71
|
+
margin: 0;
|
|
72
|
+
font-family: system-ui, sans-serif;
|
|
73
|
+
background: var(--bg);
|
|
74
|
+
color: var(--text);
|
|
75
|
+
transition: background 0.3s ease, color 0.3s ease;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
header {
|
|
80
|
+
position: sticky;
|
|
81
|
+
background: var(--header-bg);
|
|
82
|
+
border-bottom: 1px solid var(--accent);
|
|
83
|
+
top: 0px;
|
|
84
|
+
display: flex;
|
|
85
|
+
padding: 16px;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: space-between;
|
|
88
|
+
gap: 6px;
|
|
89
|
+
flex-wrap: wrap;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
header>div {
|
|
93
|
+
display: flex;
|
|
94
|
+
gap: 6px;
|
|
95
|
+
align-items: center;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.tabs a {
|
|
99
|
+
padding: 0.4rem 0.8rem;
|
|
100
|
+
text-decoration: none;
|
|
101
|
+
border: 1px solid var(--accent);
|
|
102
|
+
border-radius: 5px;
|
|
103
|
+
color: var(--accent);
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
text-transform: capitalize;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.tabs a.active {
|
|
109
|
+
background-color: var(--accent);
|
|
110
|
+
color: white;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
.tabs a.counting::after {
|
|
115
|
+
content: attr(data-count);
|
|
116
|
+
background: var(--accent);
|
|
117
|
+
color: white;
|
|
118
|
+
font-size: 0.7rem;
|
|
119
|
+
margin-left: 6px;
|
|
120
|
+
padding: 2px 6px;
|
|
121
|
+
border-radius: 10px;
|
|
122
|
+
display: inline-block;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.tabs a.counting.active::after {
|
|
126
|
+
content: attr(data-count);
|
|
127
|
+
background: white;
|
|
128
|
+
color: black;
|
|
129
|
+
font-size: 0.7rem;
|
|
130
|
+
margin-left: 6px;
|
|
131
|
+
padding: 2px 6px;
|
|
132
|
+
border-radius: 10px;
|
|
133
|
+
display: inline-block;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
section.content {
|
|
137
|
+
padding: 16px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
h1 {
|
|
141
|
+
font-size: 1.8rem;
|
|
142
|
+
margin-top: 0px;
|
|
143
|
+
margin-bottom: 20px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
input[type="text"] {
|
|
147
|
+
display: inline-flex;
|
|
148
|
+
margin-top: 16px;
|
|
149
|
+
padding: 0.5rem;
|
|
150
|
+
font-size: 1rem;
|
|
151
|
+
border: 1px solid var(--table-border);
|
|
152
|
+
border-radius: 6px;
|
|
153
|
+
max-width: 300px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
input[type="text"]:focus {
|
|
157
|
+
outline: 1px solid var(--accent);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.match-true {
|
|
161
|
+
color: green;
|
|
162
|
+
font-weight: bold;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.match-false {
|
|
166
|
+
color: red;
|
|
167
|
+
font-weight: bold;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.json-view {
|
|
171
|
+
margin-top: 16px;
|
|
172
|
+
white-space: pre-wrap;
|
|
173
|
+
background: var(--table-bg);
|
|
174
|
+
padding: 1rem;
|
|
175
|
+
border: 1px solid var(--table-border);
|
|
176
|
+
border-radius: 6px;
|
|
177
|
+
font-family: monospace;
|
|
178
|
+
max-height: 600px;
|
|
179
|
+
overflow: auto;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.table-container {
|
|
183
|
+
overflow-x: auto;
|
|
184
|
+
border-radius: 0.5rem;
|
|
185
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
table {
|
|
189
|
+
width: 100%;
|
|
190
|
+
border-collapse: collapse;
|
|
191
|
+
background: var(--table-bg);
|
|
192
|
+
margin-top: 1rem;
|
|
193
|
+
border: 1px solid var(--table-border);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
th,
|
|
197
|
+
td {
|
|
198
|
+
padding: 0.75rem;
|
|
199
|
+
text-align: left;
|
|
200
|
+
border: 1px solid var(--table-border);
|
|
201
|
+
font-size: 0.95rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
thead {
|
|
205
|
+
background-color: var(--accent);
|
|
206
|
+
color: white;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* Light mode zebra striping */
|
|
210
|
+
tbody tr:nth-child(odd) {
|
|
211
|
+
background-color: var(--tr-odd);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
tbody tr:nth-child(even) {
|
|
215
|
+
background-color: var(--tr-even);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Hover effect */
|
|
219
|
+
tbody tr:hover {
|
|
220
|
+
background-color: var(--tr-hover);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.status-true {
|
|
224
|
+
color: green;
|
|
225
|
+
font-weight: bold;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.status-false {
|
|
229
|
+
color: red;
|
|
230
|
+
font-weight: bold;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@media (max-width: 768px) {
|
|
234
|
+
|
|
235
|
+
table,
|
|
236
|
+
thead,
|
|
237
|
+
tbody,
|
|
238
|
+
th,
|
|
239
|
+
td,
|
|
240
|
+
tr {
|
|
241
|
+
display: block;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
thead {
|
|
245
|
+
display: none;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
tr {
|
|
249
|
+
margin-bottom: 1rem;
|
|
250
|
+
border: 1px solid var(--table-border);
|
|
251
|
+
border-radius: 8px;
|
|
252
|
+
background: var(--table-bg);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
td {
|
|
256
|
+
display: flex;
|
|
257
|
+
justify-content: space-between;
|
|
258
|
+
padding: 0.5rem 1rem;
|
|
259
|
+
border: none;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
td::before {
|
|
263
|
+
content: attr(data-label);
|
|
264
|
+
font-weight: bold;
|
|
265
|
+
color: var(--accent);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
</style>
|
|
269
|
+
</head>
|
|
270
|
+
<body>
|
|
271
|
+
${navbar}
|
|
272
|
+
|
|
273
|
+
<section class="content">
|
|
274
|
+
<h1>
|
|
275
|
+
${find?.doc_title}
|
|
276
|
+
</h1>
|
|
277
|
+
${find?.content}
|
|
278
|
+
<section>
|
|
279
|
+
<script>
|
|
280
|
+
|
|
281
|
+
const themeCookieName = "tezx-theme";
|
|
282
|
+
|
|
283
|
+
function setCookie(name, value, days = 30) {
|
|
284
|
+
const expires = new Date(Date.now() + days * 864e5).toUTCString();
|
|
285
|
+
document.cookie = ${"`${ name }=${ value }; expires = ${ expires }; path =/`"};
|
|
286
|
+
}
|
|
287
|
+
function getCookie(name) {
|
|
288
|
+
return document.cookie.split('; ').reduce((acc, cookie) => {
|
|
289
|
+
const [key, val] = cookie.split('=');
|
|
290
|
+
return key === name ? decodeURIComponent(val) : acc;
|
|
291
|
+
}, null);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function toggleTheme() {
|
|
295
|
+
document.body.classList.toggle('dark');
|
|
296
|
+
setCookie(themeCookieName, document.body.classList.contains('dark') ? 'dark' : 'light');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Load theme from cookie
|
|
300
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
301
|
+
const savedTheme = getCookie(themeCookieName);
|
|
302
|
+
if (savedTheme === 'dark') {
|
|
303
|
+
document.body.classList.add('dark');
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
</script>
|
|
307
|
+
</body>
|
|
308
|
+
</html>
|
|
309
|
+
`;
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
exports.default = DevTools;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
function collectRoutes(node, basePath = '/') {
|
|
2
|
+
const routes = [];
|
|
3
|
+
let fullPath = node.pathname;
|
|
4
|
+
if (node.isParam) {
|
|
5
|
+
fullPath = `${basePath}/:${node.paramName}`;
|
|
6
|
+
}
|
|
7
|
+
let pathname = fullPath?.replace(/\/+/g, '/');
|
|
8
|
+
for (const [method, _handler] of node.handlers.entries()) {
|
|
9
|
+
routes.push({
|
|
10
|
+
method,
|
|
11
|
+
match: node.pathname == pathname,
|
|
12
|
+
userPath: node.pathname,
|
|
13
|
+
routePattern: pathname,
|
|
14
|
+
appliedMiddlewares: [..._handler.middlewares].map(r => r?.name || "anonymous")
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
for (const child of node.children.values()) {
|
|
18
|
+
routes.push(...collectRoutes(child, fullPath));
|
|
19
|
+
}
|
|
20
|
+
return routes;
|
|
21
|
+
}
|
|
22
|
+
export function dumpRoutes(TezX) {
|
|
23
|
+
let app = TezX;
|
|
24
|
+
let tri = collectRoutes(app.triRouter);
|
|
25
|
+
for (const [path, handlers] of app.routers) {
|
|
26
|
+
handlers.forEach((_, key) => {
|
|
27
|
+
console.log();
|
|
28
|
+
tri.push({
|
|
29
|
+
match: true,
|
|
30
|
+
userPath: `/${path}`,
|
|
31
|
+
routePattern: `/${path}`,
|
|
32
|
+
method: key,
|
|
33
|
+
appliedMiddlewares: [..._?.middlewares]?.map(r => r?.name || "anonymous")
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return tri;
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { dumpRoutes } from "./dumpRoutes";
|
|
2
|
+
export type { RouteEntry } from "./dumpRoutes";
|
|
3
|
+
import { TezX } from "tezx";
|
|
4
|
+
export default class devtools {
|
|
5
|
+
static get runtime(): string;
|
|
6
|
+
static dumpMiddlewares(TezX: TezX): {
|
|
7
|
+
pathname: string;
|
|
8
|
+
middlewares: string[];
|
|
9
|
+
}[];
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { inspectMiddlewares } from "./middlewares";
|
|
2
|
+
export { dumpRoutes } from "./dumpRoutes";
|
|
3
|
+
export default class devtools {
|
|
4
|
+
static get runtime() {
|
|
5
|
+
return '';
|
|
6
|
+
}
|
|
7
|
+
static dumpMiddlewares(TezX) {
|
|
8
|
+
let app = TezX;
|
|
9
|
+
let middlewares = app?.triMiddlewares;
|
|
10
|
+
return inspectMiddlewares(middlewares);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function inspectMiddlewares(node, basePath = "") {
|
|
2
|
+
const fullPath = `${basePath}/${node.pathname}`
|
|
3
|
+
.replace(/\\/g, '')
|
|
4
|
+
.replace(/\/+/g, '/')
|
|
5
|
+
.replace(/^\/+|\/+$/g, '');
|
|
6
|
+
const middlewareList = Array.isArray(node.middlewares)
|
|
7
|
+
? node.middlewares
|
|
8
|
+
: Array.from(node.middlewares);
|
|
9
|
+
const entries = [];
|
|
10
|
+
if (middlewareList.length > 0) {
|
|
11
|
+
entries.push({
|
|
12
|
+
pathname: fullPath === "" ? "/" : `/${fullPath}`,
|
|
13
|
+
middlewares: middlewareList.map(String),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
for (const [, child] of node.children) {
|
|
17
|
+
entries.push(...inspectMiddlewares(child, `/${fullPath}`));
|
|
18
|
+
}
|
|
19
|
+
return entries;
|
|
20
|
+
}
|
package/html/cookies.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export function CookiesInspector(ctx) {
|
|
2
|
+
const cookies = ctx.cookies.all();
|
|
3
|
+
const rawJSON = JSON.stringify(cookies, null, 2);
|
|
4
|
+
const tableRows = Object.entries(cookies)
|
|
5
|
+
.map(([key, value], i) => `
|
|
6
|
+
<tr>
|
|
7
|
+
<td>${i + 1}</td>
|
|
8
|
+
<td contenteditable="true" class="cookie-key">${key}</td>
|
|
9
|
+
<td contenteditable="true" class="cookie-value">${value}</td>
|
|
10
|
+
<td><button class="delete-btn">🗑️</button></td>
|
|
11
|
+
</tr>
|
|
12
|
+
`).join("");
|
|
13
|
+
const html = `
|
|
14
|
+
<style>
|
|
15
|
+
td[contenteditable="true"] {
|
|
16
|
+
outline: 0px;
|
|
17
|
+
}
|
|
18
|
+
td[contenteditable="true"]:focus {
|
|
19
|
+
outline: 1px solid var(--accent);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.delete-btn {
|
|
23
|
+
background: #dc2626;
|
|
24
|
+
color: white;
|
|
25
|
+
border: none;
|
|
26
|
+
border-radius: 0.375rem;
|
|
27
|
+
padding: 0.3rem 0.6rem;
|
|
28
|
+
font-size: 0.8rem;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.delete-btn:hover {
|
|
33
|
+
background: #b91c1c;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pre#json-output {
|
|
37
|
+
margin-top: 1rem;
|
|
38
|
+
padding: 1rem;
|
|
39
|
+
border-radius: 0.5rem;
|
|
40
|
+
font-size: 0.875rem;
|
|
41
|
+
overflow-x: auto;
|
|
42
|
+
display: none;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
</style>
|
|
46
|
+
|
|
47
|
+
<div class="tabs">
|
|
48
|
+
<a onclick="addCookieRow()">➕ Add Cookie</a>
|
|
49
|
+
<a onclick="saveCookies()">💾 Save All</a>
|
|
50
|
+
<a onclick="exportCookies()">📤 Export JSON</a>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="table-container">
|
|
54
|
+
<table>
|
|
55
|
+
<thead>
|
|
56
|
+
<tr>
|
|
57
|
+
<th>#</th>
|
|
58
|
+
<th>Key</th>
|
|
59
|
+
<th>Value</th>
|
|
60
|
+
<th>Action</th>
|
|
61
|
+
</tr>
|
|
62
|
+
</thead>
|
|
63
|
+
<tbody id="cookie-body"></tbody>
|
|
64
|
+
</table>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<pre class="json-view" id="json-output"></pre>
|
|
68
|
+
|
|
69
|
+
<script>
|
|
70
|
+
function getCookies() {
|
|
71
|
+
const cookies = {};
|
|
72
|
+
document.cookie.split(';').forEach(c => {
|
|
73
|
+
const [k, ...v] = c.trim().split('=');
|
|
74
|
+
cookies[k] = decodeURIComponent(v.join('='));
|
|
75
|
+
});
|
|
76
|
+
return cookies;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function renderCookies() {
|
|
80
|
+
const tbody = document.getElementById('cookie-body');
|
|
81
|
+
tbody.innerHTML = '';
|
|
82
|
+
const cookies = getCookies();
|
|
83
|
+
let index = 1;
|
|
84
|
+
for (const [key, val] of Object.entries(cookies)) {
|
|
85
|
+
const row = document.createElement('tr');
|
|
86
|
+
row.innerHTML = \`
|
|
87
|
+
<td>\${index++}</td>
|
|
88
|
+
<td contenteditable="true" class="cookie-key">\${key}</td>
|
|
89
|
+
<td contenteditable="true" class="cookie-value">\${val}</td>
|
|
90
|
+
<td><button class="delete-btn" onclick="deleteRow(this)">Delete</button></td>
|
|
91
|
+
\`;
|
|
92
|
+
tbody.appendChild(row);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function addCookieRow() {
|
|
97
|
+
const tbody = document.getElementById('cookie-body');
|
|
98
|
+
const row = document.createElement('tr');
|
|
99
|
+
row.innerHTML = \`
|
|
100
|
+
<td>\${tbody.children.length + 1}</td>
|
|
101
|
+
<td contenteditable="true" class="cookie-key"></td>
|
|
102
|
+
<td contenteditable="true" class="cookie-value"></td>
|
|
103
|
+
<td><button class="delete-btn" onclick="deleteRow(this)">Delete</button></td>
|
|
104
|
+
\`;
|
|
105
|
+
tbody.appendChild(row);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function deleteRow(button) {
|
|
109
|
+
const row = button.closest('tr');
|
|
110
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
111
|
+
if (key) {
|
|
112
|
+
document.cookie = key + '=; Max-Age=0; path=/';
|
|
113
|
+
}
|
|
114
|
+
row.remove();
|
|
115
|
+
renderCookies();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function saveCookies() {
|
|
119
|
+
const rows = document.querySelectorAll('#cookie-body tr');
|
|
120
|
+
rows.forEach(row => {
|
|
121
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
122
|
+
const value = row.querySelector('.cookie-value')?.innerText.trim();
|
|
123
|
+
if (key) {
|
|
124
|
+
document.cookie = key + '=' + encodeURIComponent(value) + '; path=/';
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
renderCookies();
|
|
128
|
+
alert('✅ Cookies saved!');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function exportCookies() {
|
|
132
|
+
const cookies = getCookies();
|
|
133
|
+
const output = document.getElementById('json-output');
|
|
134
|
+
output.textContent = JSON.stringify(cookies, null, 2);
|
|
135
|
+
output.style.display = 'block';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
renderCookies();
|
|
139
|
+
</script>
|
|
140
|
+
`;
|
|
141
|
+
return html;
|
|
142
|
+
}
|
package/html/index.d.ts
ADDED
package/html/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Routes } from "./routes.js";
|
|
2
|
+
import { CookiesInspector } from "./cookies.js";
|
|
3
|
+
export function html(ctx, app) {
|
|
4
|
+
let tabDb = [
|
|
5
|
+
{
|
|
6
|
+
doc_title: "DevTools - Route Inspector",
|
|
7
|
+
label: "Routes",
|
|
8
|
+
tab: 'routes',
|
|
9
|
+
content: Routes(ctx, app)
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
tab: 'cookies',
|
|
13
|
+
label: 'Cookies',
|
|
14
|
+
doc_title: 'DevTools - Cookie Inspector',
|
|
15
|
+
content: CookiesInspector(ctx)
|
|
16
|
+
}
|
|
17
|
+
];
|
|
18
|
+
return tabDb;
|
|
19
|
+
}
|
package/html/routes.d.ts
ADDED