nestjs-devtools 1.5.5 → 1.5.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.
@@ -64,8 +64,6 @@ const PROFILER_PAGE = (config) => {
64
64
  .panel-body tr:last-child td, .panel-body tr:last-child th { border-bottom: none; }
65
65
  .empty { color: #bbb; font-size: 12px; }
66
66
  .mono { font-family: SF Mono,Menlo,monospace; font-size: 11px; }
67
- #history-detail { display: none; margin-top: 12px; }
68
- #history-detail.open { display: block; }
69
67
  #history-body tr { cursor: pointer; }
70
68
  #history-body tr:hover { background: #f0f7ff; }
71
69
  #history-body tr.selected { background: #e1effa; }
@@ -99,6 +97,37 @@ const PROFILER_PAGE = (config) => {
99
97
  .status-300 { background: #fff3cd; color: #856404; }
100
98
  .status-400 { background: #f8d7da; color: #721c24; }
101
99
  .status-500 { background: #f8d7da; color: #721c24; }
100
+ #history-body tr { cursor: pointer; }
101
+ #history-body tr:hover { background: #f0f7ff; }
102
+ #history-body tr.selected { background: #e1effa; }
103
+ .sf-detail { display: none; }
104
+ .sf-detail.open { display: flex; }
105
+ .sf-toolbar {
106
+ background: #1a1a1a; color: #ccc; display: flex; align-items: center;
107
+ height: 30px; padding: 0 10px; font-size: 11px;
108
+ box-shadow: 0 1px 4px rgba(0,0,0,0.3);
109
+ }
110
+ .sf-toolbar .back { color: #888; cursor: pointer; font-size: 11px; padding: 0 8px; text-decoration: none; }
111
+ .sf-toolbar .back:hover { color: #fff; }
112
+ .sf-toolbar .spacer { flex: 1; }
113
+ .sf-toolbar .req-summary { display: flex; align-items: center; gap: 8px; font-size: 11px; }
114
+ .sf-sidebar {
115
+ width: 200px; background: #fff; border-right: 1px solid #ddd;
116
+ flex-shrink: 0; font-size: 12px; overflow-y: auto;
117
+ }
118
+ .sf-sidebar .sf-metrics { padding: 12px; border-bottom: 1px solid #eee; }
119
+ .sf-sidebar .sf-metrics .m-row { display: flex; justify-content: space-between; padding: 2px 0; font-size: 11px; }
120
+ .sf-sidebar .sf-metrics .m-label { color: #999; }
121
+ .sf-sidebar .sf-metrics .m-value { color: #333; font-weight: 500; text-align: right; font-size: 11px; }
122
+ .sf-sidebar .panel-nav { list-style: none; padding: 0; margin: 0; }
123
+ .sf-sidebar .panel-nav li {
124
+ padding: 6px 12px; cursor: pointer; font-size: 11px; color: #555;
125
+ border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; gap: 6px;
126
+ }
127
+ .sf-sidebar .panel-nav li:hover { background: #f5f9ff; color: #222; }
128
+ .sf-sidebar .panel-nav li.active { background: #e8f0fe; color: #1a73e8; font-weight: 600; border-right: 3px solid #1a73e8; }
129
+ .sf-main { flex: 1; padding: 16px; overflow-y: auto; }
130
+ .sf-main .panel { margin-bottom: 0; }
102
131
  </style>
103
132
  </head>
104
133
  <body>
@@ -127,9 +156,20 @@ const PROFILER_PAGE = (config) => {
127
156
  <thead><tr style="background:#fafafa"><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Time</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Method</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Path</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Status</th><th style="padding:8px 12px;text-align:left;color:#888;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:0.5px;border-bottom:1px solid #ddd">Duration</th></tr></thead>
128
157
  <tbody id="history-body"></tbody>
129
158
  </table>
130
- <div id="history-detail" class="panel">
131
- <div id="history-detail-body" class="panel-body open" style="display:block;padding:16px"></div>
159
+ </div>
160
+ </div>
161
+ <div id="tab-request" class="tab-content" style="display:none">
162
+ <div class="sf-toolbar">
163
+ <a class="back" id="back-to-history">&larr; Back to list</a>
164
+ <span class="spacer"></span>
165
+ <span class="req-summary" id="detail-toolbar-summary"></span>
166
+ </div>
167
+ <div class="layout" style="min-height:calc(100vh - 60px)">
168
+ <div class="sf-sidebar">
169
+ <div class="sf-metrics" id="detail-metrics"></div>
170
+ <ul class="panel-nav" id="detail-panel-nav"></ul>
132
171
  </div>
172
+ <div class="sf-main" id="detail-main"></div>
133
173
  </div>
134
174
  </div>
135
175
  </div>
@@ -144,12 +184,9 @@ const PROFILER_PAGE = (config) => {
144
184
 
145
185
  document.querySelectorAll('[data-tab]').forEach(function(el){
146
186
  el.addEventListener('click', function(){
147
- document.querySelectorAll('[data-tab]').forEach(function(b){b.classList.remove('active')});
148
- document.querySelectorAll('.tab-content').forEach(function(c){c.style.display='none'});
149
- el.classList.add('active');
150
- activeTab = el.getAttribute('data-tab');
151
- document.getElementById('tab-'+activeTab).style.display='';
152
- if (activeTab==='history') loadHistory();
187
+ var tab = el.getAttribute('data-tab');
188
+ switchTab(tab);
189
+ if (tab==='history') loadHistory();
153
190
  });
154
191
  });
155
192
 
@@ -257,76 +294,117 @@ const PROFILER_PAGE = (config) => {
257
294
  });
258
295
  }
259
296
 
297
+ var panelOrder = ['request','performance','database','routes','logs'];
298
+ var currentPanels = {};
299
+ var currentPanelKey = '';
300
+
301
+ function showPanel(key) {
302
+ currentPanelKey = key;
303
+ var p = currentPanels[key];
304
+ if (!p) return;
305
+ document.querySelectorAll('#detail-panel-nav li').forEach(function(l){l.classList.remove('active')});
306
+ var navItem = document.getElementById('nav-'+key);
307
+ if (navItem) navItem.classList.add('active');
308
+ document.getElementById('detail-main').innerHTML =
309
+ '<div class="panel"><div class="panel-header" style="cursor:default"><span>'+escape(p.label)+'</span><span class="summary">'+escape(p.summary||'')+'</span></div><div class="panel-body open" style="display:block">'+renderPanelContent(p)+'</div></div>';
310
+ }
311
+
312
+ function showRequestDetail(id) {
313
+ fetch(BASE + '/__devtools/request/' + id).then(function(r){return r.json()}).then(function(panels){
314
+ if (!panels) { return; }
315
+ currentPanels = panels;
316
+
317
+ var first = null;
318
+ var firstKey = null;
319
+ var order = panelOrder;
320
+ for (var i=0;i<order.length;i++) {
321
+ var p = panels[order[i]];
322
+ if (p && p.label) { first = p; firstKey = order[i]; break; }
323
+ }
324
+ if (!first) {
325
+ for (var k in panels) { first = panels[k]; firstKey = k; break; }
326
+ }
327
+ if (!first || !first.details) { return; }
328
+ var dd = first.details;
329
+
330
+ document.getElementById('detail-toolbar-summary').innerHTML =
331
+ '<span class="badge badge-'+((dd.method||'get').toLowerCase())+'">'+escape(dd.method||'')+'</span>'+
332
+ ' <span class="mono" style="color:#ddd">'+escape(dd.path||'')+'</span>'+
333
+ ' <span class="status-badge status-'+(dd.status||'')+'">'+escape(dd.status||'')+'</span>'+
334
+ ' <span style="color:#999">'+escape(dd.duration||'')+'</span>'+
335
+ ' <span style="color:#999">'+escape(dd.memory||'')+'</span>';
336
+
337
+ var metricsHtml = '';
338
+ var metricFields = ['method','path','status','duration','memory','route'];
339
+ for (var i=0;i<metricFields.length;i++) {
340
+ var k = metricFields[i];
341
+ var v = dd[k];
342
+ if (v==null) continue;
343
+ if (k==='method') {
344
+ metricsHtml += '<div class="m-row"><span class="m-label">Method</span><span class="m-value"><span class="badge badge-'+v.toLowerCase()+'">'+escape(v)+'</span></span></div>';
345
+ } else if (k==='status') {
346
+ metricsHtml += '<div class="m-row"><span class="m-label">Status</span><span class="m-value"><span class="status-badge status-'+v+'">'+escape(v)+'</span></span></div>';
347
+ } else {
348
+ metricsHtml += '<div class="m-row"><span class="m-label">'+escape(k.charAt(0).toUpperCase()+k.slice(1))+'</span><span class="m-value '+(k==='path'||k==='route'?'mono':'')+'">'+escape(v)+'</span></div>';
349
+ }
350
+ }
351
+ document.getElementById('detail-metrics').innerHTML = metricsHtml;
352
+
353
+ var navHtml = '';
354
+ for (var i=0;i<order.length;i++) {
355
+ var p = panels[order[i]];
356
+ if (!p || !p.label) continue;
357
+ var active = order[i]===firstKey ? ' class="active"' : '';
358
+ navHtml += '<li id="nav-'+order[i]+'"'+active+' data-panel="'+order[i]+'">'+escape(p.icon||'')+' '+escape(p.label)+'</li>';
359
+ }
360
+ for (var k in panels) {
361
+ if (order.indexOf(k)!==-1) continue;
362
+ var p = panels[k];
363
+ if (!p || !p.label) continue;
364
+ navHtml += '<li id="nav-'+k+'" data-panel="'+k+'">'+escape(p.icon||'')+' '+escape(p.label)+'</li>';
365
+ }
366
+ document.getElementById('detail-panel-nav').innerHTML = navHtml;
367
+
368
+ document.querySelectorAll('#detail-panel-nav li').forEach(function(el){
369
+ el.addEventListener('click', function(){ showPanel(this.getAttribute('data-panel')); });
370
+ });
371
+
372
+ switchTab('request');
373
+ showPanel(firstKey);
374
+ }).catch(function(e){
375
+ console.error('[Devtools] Failed to load request detail:', e);
376
+ });
377
+ }
378
+
379
+ function switchTab(tab) {
380
+ document.querySelectorAll('[data-tab]').forEach(function(b){b.classList.remove('active')});
381
+ document.querySelectorAll('.tab-content').forEach(function(c){c.style.display='none'});
382
+ activeTab = tab;
383
+ var tabBtn = document.querySelector('[data-tab="'+tab+'"]');
384
+ if (tabBtn) tabBtn.classList.add('active');
385
+ document.getElementById('tab-'+tab).style.display='';
386
+ }
387
+
260
388
  function loadHistory() {
261
389
  fetch(BASE + '/__devtools/history').then(function(r){return r.json()}).then(function(rows){
262
390
  var tbody = document.getElementById('history-body');
263
- var detail = document.getElementById('history-detail');
264
391
  tbody.innerHTML = '';
265
392
  if (!rows || rows.length===0) {
266
393
  tbody.innerHTML = '<tr><td colspan="5" style="padding:20px;text-align:center;color:#bbb">No requests recorded yet</td></tr>';
267
394
  return;
268
395
  }
269
- var selectedId = tbody.getAttribute('data-selected');
270
396
  rows.forEach(function(r){
271
397
  var sc = r.statusCode;
272
398
  var scCls = sc>=400 ? 'error-text' : sc>=300 ? 'warn-text' : 'ok-text';
273
399
  var mCls = 'badge-'+r.method.toLowerCase();
274
- var sel = r.id===selectedId ? ' selected' : '';
275
400
  var tr = document.createElement('tr');
276
- tr.className = sel;
277
401
  tr.innerHTML =
278
402
  '<td style="padding:8px 12px;border-bottom:1px solid #eee;color:#888">'+escape(new Date(r.timestamp).toLocaleTimeString())+'</td>'+
279
403
  '<td style="padding:8px 12px;border-bottom:1px solid #eee"><span class="badge '+mCls+'">'+escape(r.method)+'</span></td>'+
280
404
  '<td style="padding:8px 12px;border-bottom:1px solid #eee;font-family:SF Mono,Menlo,monospace;font-size:11px">'+escape(r.path)+'</td>'+
281
405
  '<td style="padding:8px 12px;border-bottom:1px solid #eee"><span class="'+scCls+'">'+sc+'</span></td>'+
282
406
  '<td style="padding:8px 12px;border-bottom:1px solid #eee;color:#888;font-family:SF Mono,Menlo,monospace">'+(r.duration!=null?r.duration.toFixed(1):'')+' ms</td>';
283
- tr.addEventListener('click', function(){
284
- document.querySelectorAll('#history-body tr').forEach(function(t){t.classList.remove('selected')});
285
- tr.classList.add('selected');
286
- tbody.setAttribute('data-selected', r.id);
287
- fetch(BASE + '/__devtools/request/' + r.id).then(function(r2){return r2.json()}).then(function(panels){
288
- var body = document.getElementById('history-detail-body');
289
- body.innerHTML = '';
290
- if (!panels) {
291
- body.innerHTML = '<span class="empty">No details available</span>';
292
- detail.classList.add('open');
293
- return;
294
- }
295
- var order = ['request','performance','database','routes','logs'];
296
- order.forEach(function(key){
297
- var p = panels[key];
298
- if (!p || !p.label) return;
299
- var panel = document.createElement('div');
300
- panel.className = 'panel';
301
- panel.innerHTML =
302
- '<div class="panel-header"><span>'+escape(p.label)+'</span><span class="summary">'+escape(p.summary||'')+'</span></div>'+
303
- '<div class="panel-body">'+renderPanelContent(p)+'</div>';
304
- panel.querySelector('.panel-header').addEventListener('click', function(){
305
- this.nextElementSibling.classList.toggle('open');
306
- });
307
- body.appendChild(panel);
308
- });
309
- for (var key in panels) {
310
- if (order.indexOf(key)!==-1) continue;
311
- var p = panels[key];
312
- if (!p || !p.label) continue;
313
- var panel = document.createElement('div');
314
- panel.className = 'panel';
315
- panel.innerHTML =
316
- '<div class="panel-header"><span>'+escape(p.label)+'</span><span class="summary">'+escape(p.summary||'')+'</span></div>'+
317
- '<div class="panel-body">'+renderPanelContent(p)+'</div>';
318
- panel.querySelector('.panel-header').addEventListener('click', function(){
319
- this.nextElementSibling.classList.toggle('open');
320
- });
321
- body.appendChild(panel);
322
- }
323
- detail.classList.add('open');
324
- }).catch(function(e){
325
- console.error('[Devtools] Failed to load request detail:', e);
326
- document.getElementById('history-detail-body').innerHTML = '<span class="empty">Failed to load detail</span>';
327
- detail.classList.add('open');
328
- });
329
- });
407
+ tr.addEventListener('click', function(){ showRequestDetail(r.id); });
330
408
  tbody.appendChild(tr);
331
409
  });
332
410
  }).catch(function(e){
@@ -335,6 +413,8 @@ const PROFILER_PAGE = (config) => {
335
413
  });
336
414
  }
337
415
 
416
+ document.getElementById('back-to-history').addEventListener('click', function(){ switchTab('history'); loadHistory(); });
417
+
338
418
  loadDashboard();
339
419
  })();
340
420
  </script>
@@ -1 +1 @@
1
- {"version":3,"file":"profiler.js","sourceRoot":"","sources":["../../toolbar/profiler.ts"],"names":[],"mappings":";;;AAIO,MAAM,aAAa,GAAG,CAAC,MAAoC,EAAE,EAAE;IACpE,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;IAExC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgVD,CAAC;AACT,CAAC,CAAC;AApVW,QAAA,aAAa,iBAoVxB"}
1
+ {"version":3,"file":"profiler.js","sourceRoot":"","sources":["../../toolbar/profiler.ts"],"names":[],"mappings":";;;AAIO,MAAM,aAAa,GAAG,CAAC,MAAoC,EAAE,EAAE;IACpE,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;IAExC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAgaD,CAAC;AACT,CAAC,CAAC;AApaW,QAAA,aAAa,iBAoaxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-devtools",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "description": "Symfony-style debug toolbar for NestJS applications",
5
5
  "keywords": [
6
6
  "nestjs",