@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 CHANGED
@@ -1 +1,143 @@
1
- coming soon
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
- function sanitizePathSplit(basePath, path) {
6
- const parts = `${basePath}/${path}`
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,
@@ -1,17 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dumpRoutes = void 0;
4
- const middlewares_1 = require("./middlewares");
5
- var dumpRoutes_1 = require("./dumpRoutes");
6
- Object.defineProperty(exports, "dumpRoutes", { enumerable: true, get: function () { return dumpRoutes_1.dumpRoutes; } });
7
- class devtools {
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.inspectMiddlewares = inspectMiddlewares;
4
- function inspectMiddlewares(node, basePath = "") {
5
- const fullPath = `${basePath}/${node.pathname}`
6
- .replace(/\\/g, '')
7
- .replace(/\/+/g, '/')
8
- .replace(/^\/+|\/+$/g, '');
9
- const middlewareList = Array.isArray(node.middlewares)
10
- ? node.middlewares
11
- : Array.from(node.middlewares);
12
- const entries = [];
13
- if (middlewareList.length > 0) {
14
- entries.push({
15
- pathname: fullPath === "" ? "/" : `/${fullPath}`,
16
- middlewares: middlewareList.map(String),
17
- });
18
- }
19
- for (const [, child] of node.children) {
20
- entries.push(...inspectMiddlewares(child, `/${fullPath}`));
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 entries;
29
+ return routes;
30
+ }
31
+ function dumpMiddlewares(TezX) {
32
+ return collectMiddlewares(TezX.triMiddlewares);
23
33
  }
@@ -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
- <style>
18
- td[contenteditable="true"] {
19
- outline: 0px;
20
- }
21
- td[contenteditable="true"]:focus {
22
- outline: 1px solid var(--accent);
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
- .delete-btn {
26
- background: #dc2626;
27
- color: white;
28
- border: none;
29
- border-radius: 0.375rem;
30
- padding: 0.3rem 0.6rem;
31
- font-size: 0.8rem;
32
- cursor: pointer;
33
- }
34
-
35
- .delete-btn:hover {
36
- background: #b91c1c;
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
- }
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>Action</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)) {
88
- const row = document.createElement('tr');
89
- row.innerHTML = \`
90
- <td>\${index++}</td>
91
- <td contenteditable="true" class="cookie-key">\${key}</td>
92
- <td contenteditable="true" class="cookie-value">\${val}</td>
93
- <td><button class="delete-btn" onclick="deleteRow(this)">Delete</button></td>
94
- \`;
95
- tbody.appendChild(row);
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>\${tbody.children.length + 1}</td>
104
- <td contenteditable="true" class="cookie-key"></td>
105
- <td contenteditable="true" class="cookie-value"></td>
106
- <td><button class="delete-btn" onclick="deleteRow(this)">Delete</button></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
- function deleteRow(button) {
112
- const row = button.closest('tr');
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 + '=; Max-Age=0; path=/';
133
+ document.cookie = key + '=' + encodeURIComponent(value) + '; path=/';
116
134
  }
117
- row.remove();
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
- </script>
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
  }