@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/README.md
CHANGED
|
@@ -1 +1,143 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
# 📊 TezX DevTools
|
|
3
|
+
|
|
4
|
+
> Developer-friendly diagnostics and inspector panel for TezX-based applications. Plug in to see routes, middlewares, env variables, cookies, and add your own custom debug tabs.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## ✅ Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @tezx/devtools
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Ensure you also have:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install tezx
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🚀 Quick Usage
|
|
23
|
+
|
|
24
|
+
In your TezX app entry (e.g., `server.ts` or `index.ts`):
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { TezX } from "tezx";
|
|
28
|
+
import {nodeAdapter} from "tezx/node";
|
|
29
|
+
import DevTools from "@tezx/devtools";
|
|
30
|
+
|
|
31
|
+
const app = new TezX();
|
|
32
|
+
|
|
33
|
+
app.get(
|
|
34
|
+
"/devtools",
|
|
35
|
+
DevTools(app, {
|
|
36
|
+
// Optional
|
|
37
|
+
// disableTabs: ['cookies', 'routes'],
|
|
38
|
+
// extraTabs: (ctx) => [ ... ]
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
nodeAdapter(app).listen(3000);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Now visit:
|
|
46
|
+
**`http://localhost:3000/devtools`**
|
|
47
|
+
to see a real-time diagnostic dashboard.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🧩 Built-in Tabs
|
|
52
|
+
|
|
53
|
+
| Tab | Description |
|
|
54
|
+
| ------------- | -------------------------------------------------------- |
|
|
55
|
+
| `routes` | Lists all loaded routes with method, path, and source |
|
|
56
|
+
| `middlewares` | Displays registered middleware and which routes use them |
|
|
57
|
+
| `cookies` | Shows request cookies (parsed from `ctx`) |
|
|
58
|
+
| `.env` | Displays environment variables loaded via `.env` |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## ⚙️ API: `DevTools(app, options)`
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
DevTools(app: TezX<any>, options?: Options): Callback
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Options
|
|
69
|
+
|
|
70
|
+
| Option | Type | Description |
|
|
71
|
+
| ------------- | --------------------------------------------------------- | ----------------------- |
|
|
72
|
+
| `extraTabs` | `(ctx) => TabType \| Promise<TabType>` | Add your own tab panels |
|
|
73
|
+
| `disableTabs` | `Array<'cookies' \| 'routes' \| '.env' \| 'middlewares'>` | Hide built-in tabs |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🛠️ Add Custom Tabs
|
|
78
|
+
|
|
79
|
+
You can inject your own debug panels using the `extraTabs` option.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import DevTools , { dumpMiddlewares } from "@tezx/devtools";
|
|
83
|
+
|
|
84
|
+
app.get(
|
|
85
|
+
"/devtools",
|
|
86
|
+
DevTools(app, {
|
|
87
|
+
extraTabs(ctx) {
|
|
88
|
+
const rows = dumpMiddlewares(app)
|
|
89
|
+
.map(r => `<tr><td>${r.endpoint}</td><td>${r.pattern}</td><td>${r.appliedMiddlewares}</td></tr>`)
|
|
90
|
+
.join("");
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
tab: "middlewares",
|
|
94
|
+
label: "Middleware Table",
|
|
95
|
+
doc_title: "Middleware Overview",
|
|
96
|
+
content: `<table>${rows}</table>`
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 📚 Types
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
type Tab = "cookies" | "routes" | ".env" | "middlewares";
|
|
110
|
+
|
|
111
|
+
type TabType = {
|
|
112
|
+
doc_title: string;
|
|
113
|
+
label: string;
|
|
114
|
+
tab: Tab | string;
|
|
115
|
+
content: string; // Rendered HTML content
|
|
116
|
+
}[];
|
|
117
|
+
|
|
118
|
+
type Options = {
|
|
119
|
+
extraTabs?: (ctx: Context) => Promise<TabType> | TabType;
|
|
120
|
+
disableTabs?: Tab[];
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 📁 Directory Example
|
|
127
|
+
|
|
128
|
+
**Using `tezx/router`**
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
my-app/
|
|
132
|
+
├── routes/
|
|
133
|
+
│ ├── _middleware.ts
|
|
134
|
+
│ └── ...
|
|
135
|
+
├── public/
|
|
136
|
+
│ └── ...
|
|
137
|
+
├── tezx.config.mjs ← setup TezX + DevTools here
|
|
138
|
+
├── .env
|
|
139
|
+
├── package.json
|
|
140
|
+
└── tsconfig.json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sanitizePathSplit = sanitizePathSplit;
|
|
4
3
|
exports.dumpRoutes = dumpRoutes;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.replace(/\\/g, '')
|
|
8
|
-
.replace(/\/+/g, '/')
|
|
9
|
-
?.split("/")
|
|
10
|
-
.filter(Boolean);
|
|
11
|
-
return parts;
|
|
12
|
-
}
|
|
13
|
-
function collectRoutes(node, basePath = '/') {
|
|
4
|
+
const helper_1 = require("tezx/helper");
|
|
5
|
+
function collectRoutes(node, basePath = "/") {
|
|
14
6
|
const routes = [];
|
|
15
7
|
let fullPath = basePath;
|
|
16
8
|
if (node.isParam && node.paramName) {
|
|
17
9
|
fullPath = `${basePath.replace(/\/+$/, "")}${node.paramName}`;
|
|
18
10
|
}
|
|
19
|
-
const pathname = sanitizePathSplit("/", fullPath).join("/");
|
|
11
|
+
const pathname = (0, helper_1.sanitizePathSplit)("/", fullPath).join("/");
|
|
20
12
|
for (const [method, handler] of node.handlers.entries()) {
|
|
21
13
|
routes.push({
|
|
22
14
|
method,
|
|
@@ -26,7 +18,7 @@ function collectRoutes(node, basePath = '/') {
|
|
|
26
18
|
});
|
|
27
19
|
}
|
|
28
20
|
for (const [childPath, childNode] of node.children.entries()) {
|
|
29
|
-
const newPath = sanitizePathSplit(fullPath, childPath).join("/");
|
|
21
|
+
const newPath = (0, helper_1.sanitizePathSplit)(fullPath, childPath).join("/");
|
|
30
22
|
routes.push(...collectRoutes(childNode, newPath));
|
|
31
23
|
}
|
|
32
24
|
return routes;
|
|
@@ -38,7 +30,6 @@ function dumpRoutes(TezX) {
|
|
|
38
30
|
for (const [path, handlers] of app.routers) {
|
|
39
31
|
for (const [method, handler] of handlers) {
|
|
40
32
|
staticRoutes.push({
|
|
41
|
-
match: true,
|
|
42
33
|
endpoint: `/${path}`,
|
|
43
34
|
pattern: `/${path}`,
|
|
44
35
|
method,
|
package/cjs/devtools/index.js
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dumpRoutes = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
static get runtime() {
|
|
9
|
-
return '';
|
|
10
|
-
}
|
|
11
|
-
static dumpMiddlewares(TezX) {
|
|
12
|
-
let app = TezX;
|
|
13
|
-
let middlewares = app?.triMiddlewares;
|
|
14
|
-
return (0, middlewares_1.inspectMiddlewares)(middlewares);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
exports.default = devtools;
|
|
3
|
+
exports.dumpMiddlewares = exports.dumpRoutes = void 0;
|
|
4
|
+
var dumpRoutes_js_1 = require("./dumpRoutes.js");
|
|
5
|
+
Object.defineProperty(exports, "dumpRoutes", { enumerable: true, get: function () { return dumpRoutes_js_1.dumpRoutes; } });
|
|
6
|
+
var middlewares_js_1 = require("./middlewares.js");
|
|
7
|
+
Object.defineProperty(exports, "dumpMiddlewares", { enumerable: true, get: function () { return middlewares_js_1.dumpMiddlewares; } });
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
exports.dumpMiddlewares = dumpMiddlewares;
|
|
4
|
+
const helper_1 = require("tezx/helper");
|
|
5
|
+
function detectRouteType(pathname, isOptional) {
|
|
6
|
+
if (pathname.includes("*"))
|
|
7
|
+
return "wildcard";
|
|
8
|
+
if (pathname.includes(":"))
|
|
9
|
+
return "dynamic params";
|
|
10
|
+
if (isOptional)
|
|
11
|
+
return "optional params";
|
|
12
|
+
return "static";
|
|
13
|
+
}
|
|
14
|
+
function collectMiddlewares(node, basePath = "/") {
|
|
15
|
+
const routes = [];
|
|
16
|
+
const fullPath = (0, helper_1.sanitizePathSplit)("/", basePath).join("/");
|
|
17
|
+
const routeType = detectRouteType(fullPath, node.isOptional);
|
|
18
|
+
routes.push({
|
|
19
|
+
type: routeType,
|
|
20
|
+
pattern: `/${fullPath}`,
|
|
21
|
+
appliedMiddlewares: Array.isArray(node.middlewares)
|
|
22
|
+
? node.middlewares.map((mw) => mw?.name || "anonymous")
|
|
23
|
+
: Array.from(node.middlewares).map((mw) => mw?.name || "anonymous"),
|
|
24
|
+
});
|
|
25
|
+
for (const [childPath, childNode] of node.children.entries()) {
|
|
26
|
+
const newPath = (0, helper_1.sanitizePathSplit)(basePath, childPath).join("/");
|
|
27
|
+
routes.push(...collectMiddlewares(childNode, newPath));
|
|
21
28
|
}
|
|
22
|
-
return
|
|
29
|
+
return routes;
|
|
30
|
+
}
|
|
31
|
+
function dumpMiddlewares(TezX) {
|
|
32
|
+
return collectMiddlewares(TezX.triMiddlewares);
|
|
23
33
|
}
|
package/cjs/html/cookies.js
CHANGED
|
@@ -3,143 +3,162 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CookiesInspector = CookiesInspector;
|
|
4
4
|
function CookiesInspector(ctx) {
|
|
5
5
|
const cookies = ctx.cookies.all();
|
|
6
|
-
const rawJSON = JSON.stringify(cookies, null, 2);
|
|
7
|
-
const tableRows = Object.entries(cookies)
|
|
8
|
-
.map(([key, value], i) => `
|
|
9
|
-
<tr>
|
|
10
|
-
<td>${i + 1}</td>
|
|
11
|
-
<td contenteditable="true" class="cookie-key">${key}</td>
|
|
12
|
-
<td contenteditable="true" class="cookie-value">${value}</td>
|
|
13
|
-
<td><button class="delete-btn">🗑️</button></td>
|
|
14
|
-
</tr>
|
|
15
|
-
`).join("");
|
|
16
6
|
const html = `
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
7
|
+
<style>
|
|
8
|
+
td[contenteditable="true"] {
|
|
9
|
+
outline: 0px;
|
|
10
|
+
}
|
|
11
|
+
td[contenteditable="true"]:focus {
|
|
12
|
+
outline: 1px solid var(--accent);
|
|
13
|
+
}
|
|
24
14
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
function addCookieRow() {
|
|
100
|
-
const tbody = document.getElementById('cookie-body');
|
|
15
|
+
.delete-btn,
|
|
16
|
+
.copy-btn {
|
|
17
|
+
background: #dc2626;
|
|
18
|
+
color: white;
|
|
19
|
+
border: none;
|
|
20
|
+
border-radius: 0.375rem;
|
|
21
|
+
padding: 0.3rem 0.6rem;
|
|
22
|
+
font-size: 0.8rem;
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
margin-left: 4px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.copy-btn {
|
|
28
|
+
background: #2563eb;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.delete-btn:hover {
|
|
32
|
+
background: #b91c1c;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.copy-btn:hover {
|
|
36
|
+
background: #1e40af;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pre#json-output {
|
|
40
|
+
margin-top: 1rem;
|
|
41
|
+
padding: 1rem;
|
|
42
|
+
border-radius: 0.5rem;
|
|
43
|
+
font-size: 0.875rem;
|
|
44
|
+
overflow-x: auto;
|
|
45
|
+
display: none;
|
|
46
|
+
background: #f3f4f6;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
49
|
+
|
|
50
|
+
<div class="tabs">
|
|
51
|
+
<a onclick="addCookieRow()">➕ Add Cookie</a>
|
|
52
|
+
<a onclick="saveCookies()">💾 Save All</a>
|
|
53
|
+
<a onclick="exportCookies()">📤 Export JSON</a>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="table-container">
|
|
57
|
+
<table>
|
|
58
|
+
<thead>
|
|
59
|
+
<tr>
|
|
60
|
+
<th>#</th>
|
|
61
|
+
<th>Key</th>
|
|
62
|
+
<th>Value</th>
|
|
63
|
+
<th>Actions</th>
|
|
64
|
+
</tr>
|
|
65
|
+
</thead>
|
|
66
|
+
<tbody id="cookie-body"></tbody>
|
|
67
|
+
</table>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<pre class="json-view" id="json-output"></pre>
|
|
71
|
+
|
|
72
|
+
<script>
|
|
73
|
+
function getCookies() {
|
|
74
|
+
const cookies = {};
|
|
75
|
+
document.cookie.split(';').forEach(c => {
|
|
76
|
+
const [k, ...v] = c.trim().split('=');
|
|
77
|
+
cookies[k] = decodeURIComponent(v.join('='));
|
|
78
|
+
});
|
|
79
|
+
return cookies;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderCookies() {
|
|
83
|
+
const tbody = document.getElementById('cookie-body');
|
|
84
|
+
tbody.innerHTML = '';
|
|
85
|
+
const cookies = getCookies();
|
|
86
|
+
let index = 1;
|
|
87
|
+
for (const [key, val] of Object.entries(cookies)) {
|
|
101
88
|
const row = document.createElement('tr');
|
|
102
89
|
row.innerHTML = \`
|
|
103
|
-
<td>\${
|
|
104
|
-
<td contenteditable="true" class="cookie-key"
|
|
105
|
-
<td contenteditable="true" class="cookie-value"
|
|
106
|
-
<td
|
|
90
|
+
<td>\${index++}</td>
|
|
91
|
+
<td contenteditable="true" class="cookie-key" style="white-space: nowrap">\${key}</td>
|
|
92
|
+
<td contenteditable="true" class="cookie-value">\${val}</td>
|
|
93
|
+
<td class="action">
|
|
94
|
+
<button class="copy-btn" onclick="copyRow(this)">📋</button>
|
|
95
|
+
<button class="delete-btn" onclick="deleteRow(this)">Delete</button>
|
|
96
|
+
</td>
|
|
107
97
|
\`;
|
|
108
98
|
tbody.appendChild(row);
|
|
109
99
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function addCookieRow() {
|
|
103
|
+
const tbody = document.getElementById('cookie-body');
|
|
104
|
+
const row = document.createElement('tr');
|
|
105
|
+
row.innerHTML = \`
|
|
106
|
+
<td>\${tbody.children.length + 1}</td>
|
|
107
|
+
<td contenteditable="true" class="cookie-key" style="white-space: nowrap"></td>
|
|
108
|
+
<td contenteditable="true" class="cookie-value"></td>
|
|
109
|
+
<td class="action">
|
|
110
|
+
<button class="copy-btn" onclick="copyRow(this)">📋</button>
|
|
111
|
+
<button class="delete-btn" onclick="deleteRow(this)">Delete</button>
|
|
112
|
+
</td>
|
|
113
|
+
\`;
|
|
114
|
+
tbody.appendChild(row);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function deleteRow(button) {
|
|
118
|
+
const row = button.closest('tr');
|
|
119
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
120
|
+
if (key) {
|
|
121
|
+
document.cookie = key + '=; Max-Age=0; path=/';
|
|
122
|
+
}
|
|
123
|
+
row.remove();
|
|
124
|
+
renderCookies();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function saveCookies() {
|
|
128
|
+
const rows = document.querySelectorAll('#cookie-body tr');
|
|
129
|
+
rows.forEach(row => {
|
|
113
130
|
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
131
|
+
const value = row.querySelector('.cookie-value')?.innerText.trim();
|
|
114
132
|
if (key) {
|
|
115
|
-
document.cookie = key + '
|
|
133
|
+
document.cookie = key + '=' + encodeURIComponent(value) + '; path=/';
|
|
116
134
|
}
|
|
117
|
-
|
|
118
|
-
renderCookies();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function saveCookies() {
|
|
122
|
-
const rows = document.querySelectorAll('#cookie-body tr');
|
|
123
|
-
rows.forEach(row => {
|
|
124
|
-
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
125
|
-
const value = row.querySelector('.cookie-value')?.innerText.trim();
|
|
126
|
-
if (key) {
|
|
127
|
-
document.cookie = key + '=' + encodeURIComponent(value) + '; path=/';
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
renderCookies();
|
|
131
|
-
alert('✅ Cookies saved!');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function exportCookies() {
|
|
135
|
-
const cookies = getCookies();
|
|
136
|
-
const output = document.getElementById('json-output');
|
|
137
|
-
output.textContent = JSON.stringify(cookies, null, 2);
|
|
138
|
-
output.style.display = 'block';
|
|
139
|
-
}
|
|
140
|
-
|
|
135
|
+
});
|
|
141
136
|
renderCookies();
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
alert('✅ Cookies saved!');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function exportCookies() {
|
|
141
|
+
const cookies = getCookies();
|
|
142
|
+
const output = document.getElementById('json-output');
|
|
143
|
+
output.textContent = JSON.stringify(cookies, null, 2);
|
|
144
|
+
output.style.display = 'block';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function copyRow(button) {
|
|
148
|
+
const row = button.closest('tr');
|
|
149
|
+
const key = row.querySelector('.cookie-key')?.innerText.trim();
|
|
150
|
+
const value = row.querySelector('.cookie-value')?.innerText.trim();
|
|
151
|
+
if (key) {
|
|
152
|
+
navigator.clipboard.writeText(\`\${key}: \${value}\`)
|
|
153
|
+
.then(() => {
|
|
154
|
+
button.innerText = '✅';
|
|
155
|
+
setTimeout(() => button.innerText = '📋', 1000);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
renderCookies();
|
|
161
|
+
</script>
|
|
162
|
+
`;
|
|
144
163
|
return html;
|
|
145
164
|
}
|