@tezx/devtools 1.0.4 โ†’ 1.0.5

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/html/routes.js CHANGED
@@ -12,14 +12,14 @@ export function Routes(ctx, app) {
12
12
  }
13
13
  const rawJSON = JSON.stringify(allRoutes, null, 2);
14
14
  const toCSV = (data) => {
15
- const headers = ['method', 'endpoint', 'pattern', 'appliedMiddlewares'];
15
+ const headers = ["method", "endpoint", "pattern", "appliedMiddlewares"];
16
16
  const csvRows = [
17
- headers.join(','),
18
- ...data.map(r => headers.map(h => JSON.stringify(r[h] ?? '')).join(','))
17
+ headers.join(","),
18
+ ...data.map((r) => headers.map((h) => JSON.stringify(r[h] ?? "")).join(",")),
19
19
  ];
20
- return csvRows.join('\n');
20
+ return csvRows.join("\n");
21
21
  };
22
- const csvString = toCSV(allRoutes).replace(/"/g, '"');
22
+ const csvString = toCSV(allRoutes).replace(/"/g, """);
23
23
  return `
24
24
  <style>
25
25
  .download {
@@ -49,107 +49,136 @@ export function Routes(ctx, app) {
49
49
  #searchBar input {
50
50
  flex: 1;
51
51
  }
52
- </style>
53
52
 
54
- <div class="tabs">
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">
55
68
  <a class="tab-btn active counting" data-count="${totalRoutes}" onclick="showTab('routes')">๐Ÿ“‹ Routes</a>
56
- <a class="tab-btn counting" data-count="${Object.keys(middlewareStats).length}" onclick="showTab('stats')">๐Ÿ“ˆ
57
- Stats</a>
69
+ <a class="tab-btn counting" data-count="${Object.keys(middlewareStats).length}" onclick="showTab('stats')">๐Ÿ“ˆ Stats</a>
58
70
  <a class="tab-btn" onclick="showTab('middlewares')">๐Ÿงฉ Middlewares</a>
59
71
  <a class="tab-btn" onclick="showTab('json')">๐Ÿงพ Raw JSON</a>
60
72
  <a class="tab-btn" onclick="showTab('export')">๐Ÿ“ค Export</a>
61
- </div>
73
+ </div>
62
74
 
63
- <div id="searchBar" style="display:flex">
75
+ <div id="searchBar">
64
76
  <input type="text" id="search" placeholder="Filter by method/path/middleware..." />
65
- </div>
77
+ </div>
66
78
 
67
- <!-- ROUTES -->
68
- <div id="routesTab">
79
+ <!-- ROUTES -->
80
+ <div id="routesTab" class="table-container">
69
81
  <table id="routesTable">
70
- <thead>
71
- <tr>
72
- <th>#</th>
73
- <th>Method</th>
74
- <th>Endpoint</th>
75
- <th>Pattern</th>
76
- <th>Middlewares</th>
77
- </tr>
78
- </thead>
79
- <tbody>
80
- ${allRoutes.map((r, i) => `
81
- <tr>
82
- <td>${i + 1}</td>
83
- <td>${r.method}</td>
84
- <td>${r.endpoint}</td>
85
- <td>${r.pattern}</td>
86
- <td>${(r.appliedMiddlewares || []).join(', ')}</td>
87
- </tr>`).join('')}
88
- </tbody>
82
+ <thead>
83
+ <tr>
84
+ <th>#</th>
85
+ <th>Method</th>
86
+ <th>Endpoint</th>
87
+ <th>Pattern</th>
88
+ <th>Middlewares</th>
89
+ </tr>
90
+ </thead>
91
+ <tbody>
92
+ ${allRoutes
93
+ .map((r, i) => `
94
+ <tr>
95
+ <td>${i + 1}</td>
96
+ <td>${r.method}</td>
97
+ <td>
98
+ ${r.endpoint}
99
+ <button onclick="copyText(\`${r.endpoint}\`)">๐Ÿ“‹</button>
100
+ </td>
101
+ <td>
102
+ ${r.pattern}
103
+ <button onclick="copyText(\`${r.pattern}\`)">๐Ÿ“‹</button>
104
+ </td>
105
+ <td>${(r.appliedMiddlewares || []).join(", ")}</td>
106
+ </tr>
107
+ `)
108
+ .join("")}
109
+ </tbody>
89
110
  </table>
90
- </div>
111
+ </div>
91
112
 
92
- <!-- STATS -->
93
- <div id="statsTab" style="display:none">
113
+ <!-- STATS -->
114
+ <div id="statsTab" style="display:none">
94
115
  <div class="json-view">
95
- Total Routes: ${totalRoutes}
96
- Middleware Used: ${Object.keys(middlewareStats).length}
97
-
98
- ${Object.entries(middlewareStats).map(([mw, count]) => `- ${mw}: ${count} routes`).join('\n')}
116
+ Total Routes: ${totalRoutes}<br />
117
+ Middleware Used: ${Object.keys(middlewareStats).length}<br />
118
+ <pre>
119
+ ${Object.entries(middlewareStats)
120
+ .map(([mw, count]) => `- ${mw}: ${count} routes`)
121
+ .join("\n")}
122
+ </pre>
99
123
  </div>
100
- </div>
101
-
102
- <!-- MIDDLEWARES -->
103
- <div id="middlewaresTab" style="display:none">
104
- ${Object.entries(middlewareRoutes).map(([mw, routes]) => `
105
- <h3>๐Ÿ”น ${mw} (${routes.length})</h3>
106
- <ul>
107
- ${routes.map(r => `<li><code>${r.method} ${r.endpoint}</code></li>`).join('')}
108
- </ul>
109
- `).join('')}
110
- </div>
111
-
112
- <!-- RAW JSON -->
113
- <div id="jsonTab" style="display:none">
114
- <div class="json-view">${rawJSON}</div>
115
- </div>
116
-
117
- <!-- EXPORT -->
118
- <div id="exportTab" style="display:none">
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.method} ${r.endpoint}</code></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">
119
145
  <div class="download">
120
- <a href="data:text/json;charset=utf-8,${encodeURIComponent(rawJSON)}" download="routes.json">๐Ÿ“ฅ JSON</a>
121
- <a href="data:text/csv;charset=utf-8,${csvString}" download="routes.csv">๐Ÿ“ฅ CSV</a>
146
+ <a href="data:text/json;charset=utf-8,${encodeURIComponent(rawJSON)}" download="routes.json">๐Ÿ“ฅ JSON</a>
147
+ <a href="data:text/csv;charset=utf-8,${csvString}" download="routes.csv">๐Ÿ“ฅ CSV</a>
122
148
  </div>
123
- </div>
149
+ </div>
124
150
 
125
- <script>
151
+ <script>
126
152
  const tabs = ['routes', 'stats', 'middlewares', 'json', 'export'];
127
153
  const tabBtns = document.querySelectorAll('.tab-btn');
128
154
 
129
155
  function showTab(tab) {
130
- tabs.forEach(t => {
131
- document.getElementById(t + 'Tab').style.display = t === tab ? 'block' : 'none';
132
- if (t === tab) {
133
- document.getElementById(t + 'Tab').classList.add('active');
134
- }
135
- else {
136
- document.getElementById(t + 'Tab').classList.remove('active');
137
- }
138
- });
139
- tabBtns.forEach(btn => {
140
- const active = btn.textContent.toLowerCase().includes(tab);
141
- btn.classList.toggle('active', active);
142
- });
143
- document.getElementById('searchBar').style.display = (tab === 'routes') ? 'flex' : 'none';
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';
144
165
  }
145
166
 
146
167
  document.getElementById('search').addEventListener('input', (e) => {
147
- const keyword = e.target.value.toLowerCase();
148
- const rows = document.querySelectorAll('#routesTable tbody tr');
149
- rows.forEach(row => {
150
- row.style.display = row.textContent.toLowerCase().includes(keyword) ? '' : 'none';
151
- });
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
+ });
152
173
  });
153
- </script>
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>
154
183
  `;
155
184
  }
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { Callback, TezX } from "tezx";
1
+ import { Callback, Context, TezX } from "tezx";
2
2
  import { Tab, TabType } from "./html/index.js";
3
3
  export type Options = {
4
- extraTabs?: TabType;
4
+ extraTabs?: (ctx: Context) => Promise<TabType> | TabType;
5
5
  disableTabs?: Tab[];
6
6
  };
7
7
  export declare function DevTools(app: TezX<any>, options?: Options): Callback;
package/index.js CHANGED
@@ -1,21 +1,29 @@
1
1
  import { html as htmlTab } from "./html/index.js";
2
- export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
2
+ export function DevTools(app, options = { disableTabs: [] }) {
3
3
  let { disableTabs, extraTabs } = options;
4
- return (ctx) => {
5
- let html = [...(disableTabs?.length ? htmlTab(ctx, app)?.filter(r => !disableTabs?.includes(r?.tab)) : htmlTab(ctx, app)), ...extraTabs || []];
4
+ return async (ctx) => {
5
+ let extraTabs = await (typeof options.extraTabs === "function"
6
+ ? options.extraTabs(ctx)
7
+ : []);
8
+ let html = [
9
+ ...(disableTabs?.length
10
+ ? htmlTab(ctx, app)?.filter((r) => !disableTabs?.includes(r?.tab))
11
+ : htmlTab(ctx, app)),
12
+ ...extraTabs,
13
+ ];
6
14
  let tab = ctx.req.query?._tab || html?.[0]?.tab;
7
15
  const navbar = `
8
16
  <header>
9
17
  <div class="tabs">
10
18
  <img src="http://papernxt.com/favicon.ico" style="height:32px;"/>
11
- ${html?.map(r => `<a href = "?_tab=${r?.tab}" class="${tab === r?.tab ? 'active' : ''}" > ${r?.label} </a>`)?.join('\n')}
19
+ ${html?.map((r) => `<a href = "?_tab=${r?.tab}" class="${tab === r?.tab ? "active" : ""}" > ${r?.label} </a>`)?.join("\n")}
12
20
  </div>
13
21
  <div class="tabs">
14
22
  <a class="toggle-dark" onclick="toggleTheme()">๐ŸŒ™ Toggle Dark</a>
15
23
  </div>
16
24
  </header>
17
25
  `;
18
- let find = html.find(r => r?.tab == tab);
26
+ let find = html.find((r) => r?.tab == tab);
19
27
  return ctx.html `
20
28
  <!DOCTYPE html>
21
29
  <html lang="en">
@@ -91,7 +99,13 @@ export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
91
99
  gap: 6px;
92
100
  align-items: center;
93
101
  }
94
-
102
+ .toolbar ,.action{
103
+ display: flex;
104
+ padding: 16px;
105
+ align-items: center;
106
+ gap: 6px;
107
+ flex-wrap: wrap;
108
+ }
95
109
  .tabs a {
96
110
  padding: 0.4rem 0.8rem;
97
111
  text-decoration: none;
@@ -177,8 +191,10 @@ export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
177
191
  }
178
192
 
179
193
  .table-container {
180
- overflow-x: auto;
194
+ overflow: auto;
195
+ margin-top: 16px;
181
196
  border-radius: 0.5rem;
197
+ height: 75vh;
182
198
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
183
199
  }
184
200
 
@@ -201,6 +217,8 @@ export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
201
217
  thead {
202
218
  background-color: var(--accent);
203
219
  color: white;
220
+ position: sticky;
221
+ top: 0px;
204
222
  }
205
223
 
206
224
  /* Light mode zebra striping */
@@ -226,9 +244,8 @@ export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
226
244
  color: red;
227
245
  font-weight: bold;
228
246
  }
229
-
247
+ /**
230
248
  @media (max-width: 768px) {
231
-
232
249
  table,
233
250
  thead,
234
251
  tbody,
@@ -262,6 +279,7 @@ export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
262
279
  color: var(--accent);
263
280
  }
264
281
  }
282
+ **/
265
283
  </style>
266
284
  </head>
267
285
  <body>
@@ -274,7 +292,6 @@ export function DevTools(app, options = { disableTabs: [], extraTabs: [] }) {
274
292
  ${find?.content}
275
293
  </section>
276
294
  <script>
277
-
278
295
  const themeCookieName = "tezx-theme";
279
296
 
280
297
  function setCookie(name, value, days = 30) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tezx/devtools",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Developer tools for the TezX framework, including route inspector, cookie manager, and real-time diagnostics. Lightweight and plug-and-play compatible with Node.js, Bun, and Deno.",
5
5
  "main": "cjs/index.js",
6
6
  "module": "index.js",