brakit 0.10.0 → 0.10.1
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/dist/api.d.ts +6 -5
- package/dist/api.js +23 -15
- package/dist/bin/brakit.js +120 -25
- package/dist/dashboard-client.global.js +470 -424
- package/dist/dashboard.html +558 -472
- package/dist/mcp/server.js +98 -22
- package/dist/runtime/index.js +361 -269
- package/package.json +1 -1
package/dist/dashboard.html
CHANGED
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
--red:#dc2626;
|
|
19
19
|
--cyan:#0891b2;
|
|
20
20
|
--green-bg:rgba(22,163,74,0.08);--green-bg-subtle:rgba(22,163,74,0.05);--green-border:rgba(22,163,74,0.2);--green-border-subtle:rgba(22,163,74,0.15);
|
|
21
|
-
--amber-bg:rgba(217,119,6,0.
|
|
21
|
+
--amber-bg:rgba(217,119,6,0.08);--amber-border:rgba(217,119,6,0.15);
|
|
22
|
+
--red-bg:rgba(220,38,38,0.08);--red-border:rgba(220,38,38,0.2);
|
|
23
|
+
--blue-bg:rgba(37,99,235,0.08);--cyan-bg:rgba(8,145,178,0.07);
|
|
24
|
+
--accent-bg:rgba(99,102,241,0.08);
|
|
22
25
|
--sidebar-width:232px;--header-height:52px;
|
|
23
26
|
--radius:8px;--radius-sm:6px;
|
|
24
27
|
--shadow-sm:0 1px 3px rgba(0,0,0,0.06),0 1px 2px rgba(0,0,0,0.03);
|
|
@@ -65,6 +68,7 @@ html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--s
|
|
|
65
68
|
.sidebar-logo .logo-version{font-weight:400;font-size:11px;color:var(--text-muted);margin-left:8px;letter-spacing:0}
|
|
66
69
|
.sidebar-nav{padding:12px;flex:1}
|
|
67
70
|
.sidebar-section{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);padding:16px 12px 8px}
|
|
71
|
+
.sidebar-divider{height:1px;background:var(--border-subtle);margin:8px 12px}
|
|
68
72
|
.sidebar-item{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:var(--radius);color:var(--text-dim);font-size:14px;font-weight:500;cursor:pointer;transition:all .15s;border:none;background:transparent;width:100%;text-align:left;font-family:var(--sans)}
|
|
69
73
|
.sidebar-item:hover{background:var(--bg-hover);color:var(--text)}
|
|
70
74
|
.sidebar-item.active{background:var(--bg-active);color:var(--accent)}
|
|
@@ -98,7 +102,7 @@ html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--s
|
|
|
98
102
|
/* Content */
|
|
99
103
|
.main-content{flex:1;overflow-y:auto}
|
|
100
104
|
bk-dashboard{display:contents}
|
|
101
|
-
bk-overview-view,bk-flows-view,bk-requests-view,bk-fetches-view,bk-queries-view,bk-errors-view,bk-logs-view,bk-security-view,bk-performance-view,bk-timeline-panel,bk-empty-state{display:block}
|
|
105
|
+
bk-overview-view,bk-flows-view,bk-requests-view,bk-fetches-view,bk-queries-view,bk-errors-view,bk-logs-view,bk-security-view,bk-performance-view,bk-explorer-view,bk-insights-view,bk-timeline-panel,bk-empty-state{display:block}
|
|
102
106
|
bk-graph-view{display:block}
|
|
103
107
|
bk-method-badge,bk-status-pill,bk-duration-label,bk-copy-button{display:inline-flex;flex-shrink:0}
|
|
104
108
|
bk-stat-card{display:inline-flex}
|
|
@@ -445,53 +449,25 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
445
449
|
.perf-trend-slower{color:var(--red)}
|
|
446
450
|
.perf-trend-faster{color:var(--green)}
|
|
447
451
|
|
|
448
|
-
|
|
449
|
-
.ov-container{padding:24px 28px}
|
|
450
|
-
|
|
451
|
-
/* Summary banner */
|
|
452
|
-
.ov-summary{display:flex;gap:10px;margin-bottom:24px;flex-wrap:wrap}
|
|
453
|
-
.ov-stat{display:flex;flex-direction:column;gap:4px;flex:1;min-width:100px;padding:16px 18px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow-sm);transition:box-shadow var(--transition, .15s ease)}
|
|
454
|
-
.ov-stat:hover{box-shadow:var(--shadow-md)}
|
|
455
|
-
.ov-stat-value{font-size:22px;font-weight:700;font-family:var(--mono);color:var(--text);line-height:1.2}
|
|
456
|
-
.ov-stat-label{font-size:10px;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);font-weight:600}
|
|
457
|
-
|
|
458
|
-
/* Section header */
|
|
459
|
-
.ov-section-title{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);margin-bottom:12px;display:flex;align-items:center;gap:8px}
|
|
460
|
-
.ov-issue-count{font-size:11px;font-family:var(--mono);color:var(--text-dim);background:var(--bg-muted);border:1px solid var(--border);padding:1px 8px;border-radius:10px}
|
|
461
|
-
|
|
462
|
-
/* Insight cards */
|
|
463
|
-
.ov-cards{display:flex;flex-direction:column;gap:8px}
|
|
464
|
-
.ov-card{display:flex;align-items:flex-start;gap:14px;padding:16px 20px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:all var(--transition, .15s ease);box-shadow:var(--shadow-sm)}
|
|
465
|
-
.ov-card:hover{background:var(--bg-hover);border-color:var(--border-light);box-shadow:var(--shadow-md);transform:translateY(-1px)}
|
|
466
|
-
.ov-card-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;border-radius:50%;margin-top:2px}
|
|
467
|
-
.ov-card-icon.critical{background:rgba(220,38,38,.08);color:var(--red)}
|
|
468
|
-
.ov-card-icon.warning{background:rgba(217,119,6,.08);color:var(--amber)}
|
|
469
|
-
.ov-card-icon.info{background:rgba(37,99,235,.08);color:var(--blue)}
|
|
470
|
-
.ov-card-icon.resolved{background:var(--green-bg);color:var(--green)}
|
|
471
|
-
.ov-card-body{flex:1;min-width:0}
|
|
472
|
-
.ov-card-title{font-size:13px;font-weight:600;color:var(--text);margin-bottom:2px}
|
|
473
|
-
.ov-card-desc{font-size:12px;color:var(--text-dim);line-height:1.5}
|
|
474
|
-
.ov-card-detail{font-size:11px;font-family:var(--mono);color:var(--text-muted);margin-top:6px;padding:8px 10px;background:var(--bg-muted);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);line-height:1.5}
|
|
475
|
-
.ov-card-desc strong{color:var(--text);font-family:var(--mono);font-weight:600}
|
|
476
|
-
.ov-card-arrow{color:var(--text-muted);font-size:12px;flex-shrink:0;margin-top:2px;font-family:var(--mono);transition:transform .15s}
|
|
477
|
-
|
|
478
|
-
/* Expanded card */
|
|
479
|
-
.ov-card.expanded{border-color:var(--border-light);box-shadow:var(--shadow-md)}
|
|
480
|
-
.ov-card-expand{display:none;margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}
|
|
481
|
-
.ov-card-hint{font-size:12px;color:var(--text-dim);line-height:1.5;margin-bottom:10px}
|
|
482
|
-
.ov-card-link{font-size:12px;font-weight:600;color:var(--blue);cursor:pointer;display:inline-block;padding:4px 0}
|
|
483
|
-
.ov-card-link:hover{text-decoration:underline}
|
|
484
|
-
.ov-detail-label{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}
|
|
485
|
-
.ov-detail-item{font-size:12px;color:var(--text);font-family:var(--mono);padding:2px 0}
|
|
486
|
-
|
|
487
|
-
/* All-clear banner */
|
|
488
|
-
.ov-clear{display:flex;align-items:center;gap:12px;padding:16px 20px;background:var(--green-bg-subtle);border:1px solid var(--green-border);border-radius:var(--radius);color:var(--green);font-size:13px;font-weight:500}
|
|
489
|
-
.ov-clear-icon{font-size:16px}
|
|
452
|
+
.ov-container{padding:28px}
|
|
490
453
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
.ov-card-
|
|
494
|
-
.ov-card-
|
|
454
|
+
.ov-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
|
|
455
|
+
|
|
456
|
+
.ov-card-nav{display:flex;align-items:center;gap:16px;padding:20px 24px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;cursor:pointer;transition:all .18s ease;box-shadow:0 1px 3px rgba(0,0,0,.04)}
|
|
457
|
+
.ov-card-nav:hover{border-color:var(--border-light);box-shadow:0 4px 16px rgba(0,0,0,.06);transform:translateY(-2px)}
|
|
458
|
+
.ov-card-nav:active{transform:translateY(0)}
|
|
459
|
+
|
|
460
|
+
.ov-card-empty{opacity:.55}
|
|
461
|
+
.ov-card-empty:hover{opacity:.8}
|
|
462
|
+
|
|
463
|
+
.ov-card-icon-lg{font-size:22px;width:40px;height:40px;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:var(--bg-muted);border-radius:10px;color:var(--text-muted)}
|
|
464
|
+
|
|
465
|
+
.ov-card-content{flex:1;min-width:0}
|
|
466
|
+
.ov-card-headline{font-size:16px;font-weight:700;color:var(--text);font-family:var(--mono);line-height:1.3}
|
|
467
|
+
.ov-card-context{font-size:12px;color:var(--text-muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
468
|
+
|
|
469
|
+
.ov-card-arrow{font-size:16px;color:var(--text-muted);opacity:0;transition:opacity .15s,transform .15s;flex-shrink:0}
|
|
470
|
+
.ov-card-nav:hover .ov-card-arrow{opacity:1;transform:translateX(2px)}
|
|
495
471
|
|
|
496
472
|
/* Security tab */
|
|
497
473
|
.sec-container{padding:24px 28px}
|
|
@@ -847,98 +823,162 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
847
823
|
/* Flow edge animation */
|
|
848
824
|
@keyframes graph-flow-dash{to{stroke-dashoffset:-24}}
|
|
849
825
|
.graph-flow-edge{animation:graph-flow-dash 1s linear infinite}
|
|
826
|
+
|
|
827
|
+
/* Explorer sub-tabs */
|
|
828
|
+
.explorer-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);padding:0 28px;background:var(--bg);position:sticky;top:0;z-index:2}
|
|
829
|
+
.explorer-tab{padding:10px 16px;font-size:13px;font-weight:500;color:var(--text-muted);background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:6px;font-family:var(--sans);white-space:nowrap}
|
|
830
|
+
.explorer-tab:hover{color:var(--text)}
|
|
831
|
+
.explorer-tab.active{color:var(--accent);border-bottom-color:var(--accent)}
|
|
832
|
+
.explorer-tab-count{font-size:11px;font-family:var(--mono);color:var(--text-muted);background:var(--bg-muted);padding:1px 6px;border-radius:8px}
|
|
833
|
+
.explorer-tab.active .explorer-tab-count{color:var(--accent);background:var(--accent-bg)}
|
|
834
|
+
|
|
835
|
+
/* Insights filter chips */
|
|
836
|
+
.insights-filters{display:flex;gap:6px;padding:16px 28px;border-bottom:1px solid var(--border);background:var(--bg);position:sticky;top:0;z-index:2}
|
|
837
|
+
.insights-chip{font-size:12px;font-weight:500;padding:5px 14px;border:1px solid var(--border);border-radius:20px;background:var(--bg);color:var(--text-muted);cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:5px;font-family:var(--sans)}
|
|
838
|
+
.insights-chip:hover{border-color:var(--text-muted);color:var(--text)}
|
|
839
|
+
.insights-chip.active{background:var(--accent);color:white;border-color:var(--accent)}
|
|
840
|
+
.insights-chip-count{font-size:10px;font-family:var(--mono);background:rgba(0,0,0,.08);padding:1px 5px;border-radius:8px}
|
|
841
|
+
.insights-chip.active .insights-chip-count{background:rgba(255,255,255,.25)}
|
|
842
|
+
|
|
843
|
+
/* Insights card list */
|
|
844
|
+
.insights-list{padding:16px 28px}
|
|
845
|
+
|
|
846
|
+
.insights-empty{display:flex;align-items:center;gap:10px;padding:24px;color:var(--green);font-size:14px;font-weight:500}
|
|
847
|
+
.insights-empty-icon{font-size:18px}
|
|
848
|
+
|
|
849
|
+
.insights-card{display:flex;align-items:flex-start;gap:12px;padding:14px 18px;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;cursor:pointer;transition:all .15s;margin-bottom:8px}
|
|
850
|
+
.insights-card:hover{border-color:var(--border-light);box-shadow:0 2px 8px rgba(0,0,0,.04)}
|
|
851
|
+
.insights-card.expanded{border-color:var(--border-light);box-shadow:0 2px 8px rgba(0,0,0,.04)}
|
|
852
|
+
.insights-card.resolved{opacity:.55}
|
|
853
|
+
.insights-card.resolved:hover{opacity:.8}
|
|
854
|
+
|
|
855
|
+
.insights-card-left{flex-shrink:0;padding-top:2px}
|
|
856
|
+
.insights-sev{width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-size:10px;border-radius:50%}
|
|
857
|
+
.insights-sev.critical{background:var(--red-bg);color:var(--red)}
|
|
858
|
+
.insights-sev.warning{background:var(--amber-bg);color:var(--amber)}
|
|
859
|
+
.insights-sev.info{background:var(--blue-bg);color:var(--blue)}
|
|
860
|
+
|
|
861
|
+
.insights-card-body{flex:1;min-width:0}
|
|
862
|
+
.insights-card-header{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:3px}
|
|
863
|
+
.insights-card-title{font-size:13px;font-weight:600;color:var(--text)}
|
|
864
|
+
.insights-card-title.resolved{text-decoration:line-through;color:var(--text-muted)}
|
|
865
|
+
.insights-card-cat{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);background:var(--bg-muted);padding:1px 6px;border-radius:4px}
|
|
866
|
+
.insights-card-count{font-size:11px;font-family:var(--mono);color:var(--text-muted)}
|
|
867
|
+
.insights-card-desc{font-size:12px;color:var(--text-dim);line-height:1.5}
|
|
868
|
+
.insights-card-detail{font-size:11px;font-family:var(--mono);color:var(--text-muted);margin-top:6px;padding:6px 10px;background:var(--bg-muted);border:1px solid var(--border-subtle);border-radius:6px;line-height:1.5}
|
|
869
|
+
.insights-card-progress{font-size:11px;color:var(--text-muted);margin-top:4px;font-family:var(--mono)}
|
|
870
|
+
.insights-card-hint{font-size:12px;color:var(--text-dim);line-height:1.6;margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}
|
|
871
|
+
|
|
872
|
+
.insights-badge-regressed{font-size:9px;font-weight:700;color:var(--red);background:var(--red-bg);padding:1px 6px;border-radius:4px}
|
|
873
|
+
.insights-badge-verifying{font-size:9px;font-weight:700;color:var(--amber);background:var(--amber-bg);padding:1px 6px;border-radius:4px}
|
|
874
|
+
.insights-badge-resolved{font-size:9px;font-weight:700;color:var(--green);background:var(--green-bg);padding:1px 6px;border-radius:4px}
|
|
875
|
+
|
|
876
|
+
.insights-card-arrow{color:var(--text-muted);font-size:12px;flex-shrink:0;padding-top:2px;font-family:var(--mono);transition:transform .15s}
|
|
877
|
+
|
|
878
|
+
.insights-section{display:flex;align-items:center;gap:8px;padding:14px 0 8px;margin-top:4px;font-size:12px;font-weight:700;color:var(--text);text-transform:uppercase;letter-spacing:.5px;border-top:1px solid var(--border);user-select:none}
|
|
879
|
+
.insights-section:first-child{border-top:none;margin-top:0}
|
|
880
|
+
.insights-section-icon{font-size:11px;width:16px;text-align:center}
|
|
881
|
+
.insights-section-count{font-size:11px;font-family:var(--mono);color:var(--text-muted);background:var(--bg-muted);padding:1px 7px;border-radius:8px;font-weight:500}
|
|
882
|
+
.insights-section-regressed{color:var(--red)}
|
|
883
|
+
.insights-section-regressed .insights-section-count{color:var(--red);background:var(--red-bg)}
|
|
884
|
+
.insights-section-verifying{color:var(--amber)}
|
|
885
|
+
.insights-section-verifying .insights-section-count{color:var(--amber);background:var(--amber-bg)}
|
|
886
|
+
.insights-section-resolved{color:var(--green)}
|
|
887
|
+
.insights-section-resolved .insights-section-count{color:var(--green);background:var(--green-bg)}
|
|
888
|
+
.insights-section-dismissed{color:var(--text-muted);cursor:pointer}
|
|
889
|
+
.insights-section-dismissed:hover{color:var(--text)}
|
|
850
890
|
</style>
|
|
851
891
|
</head>
|
|
852
892
|
<body>
|
|
853
893
|
<bk-dashboard></bk-dashboard>
|
|
854
894
|
<script>window.__BRAKIT_CONFIG__={port:{{PORT}},version:"{{VERSION}}"};</script>
|
|
855
|
-
<script>(function(){'use strict';var
|
|
856
|
-
\f\r]`,
|
|
857
|
-
\f\r"'\`<>=]|("|')|))|$)`,"g"),ms=/'/g,fs=/"/g,Es=/^(?:script|style|textarea|title)$/i,Ae=o=>(s,...t)=>({_$litType$:o,strings:s,values:t}),a=Ae(1),O=Ae(2),Y=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),vs=new WeakMap,it=ot.createTreeWalker(ot,129);function ys(o,s){if(!Re(o)||!o.hasOwnProperty("raw"))throw Error("invalid template strings array");return ps!==void 0?ps.createHTML(s):s}var Pr=(o,s)=>{let t=o.length-1,e=[],r,i=s===2?"<svg>":s===3?"<math>":"",n=Ot;for(let l=0;l<t;l++){let c=o[l],d,h,m=-1,v=0;for(;v<c.length&&(n.lastIndex=v,h=n.exec(c),h!==null);)v=n.lastIndex,n===Ot?h[1]==="!--"?n=hs:h[1]!==void 0?n=us:h[2]!==void 0?(Es.test(h[2])&&(r=RegExp("</"+h[2],"g")),n=rt):h[3]!==void 0&&(n=rt):n===rt?h[0]===">"?(n=r??Ot,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,d=h[1],n=h[3]===void 0?rt:h[3]==='"'?fs:ms):n===fs||n===ms?n=rt:n===hs||n===us?n=Ot:(n=rt,r=void 0);let x=n===rt&&o[l+1].startsWith("/>")?" ":"";i+=n===Ot?c+Dr:m>=0?(e.push(d),c.slice(0,m)+gs+c.slice(m)+z+x):c+z+(m===-2?l:x);}return [ys(o,i+(o[t]||"<?>")+(s===2?"</svg>":s===3?"</math>":"")),e]},Pt=class o{constructor({strings:s,_$litType$:t},e){let r;this.parts=[];let i=0,n=0,l=s.length-1,c=this.parts,[d,h]=Pr(s,t);if(this.el=o.createElement(d,e),it.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(r=it.nextNode())!==null&&c.length<l;){if(r.nodeType===1){if(r.hasAttributes())for(let m of r.getAttributeNames())if(m.endsWith(gs)){let v=h[n++],x=r.getAttribute(m).split(z),y=/([.?@])?(.*)/.exec(v);c.push({type:1,index:i,name:y[2],strings:x,ctor:y[1]==="."?xe:y[1]==="?"?Se:y[1]==="@"?Te:ht}),r.removeAttribute(m);}else m.startsWith(z)&&(c.push({type:6,index:i}),r.removeAttribute(m));if(Es.test(r.tagName)){let m=r.textContent.split(z),v=m.length-1;if(v>0){r.textContent=ee?ee.emptyScript:"";for(let x=0;x<v;x++)r.append(m[x],Dt()),it.nextNode(),c.push({type:2,index:++i});r.append(m[v],Dt());}}}else if(r.nodeType===8)if(r.data===bs)c.push({type:2,index:i});else {let m=-1;for(;(m=r.data.indexOf(z,m+1))!==-1;)c.push({type:7,index:i}),m+=z.length-1;}i++;}}static createElement(s,t){let e=ot.createElement("template");return e.innerHTML=s,e}};function pt(o,s,t=o,e){if(s===Y)return s;let r=e!==void 0?t._$Co?.[e]:t._$Cl,i=Ht(s)?void 0:s._$litDirective$;return r?.constructor!==i&&(r?._$AO?.(false),i===void 0?r=void 0:(r=new i(o),r._$AT(o,t,e)),e!==void 0?(t._$Co??(t._$Co=[]))[e]=r:t._$Cl=r),r!==void 0&&(s=pt(o,r._$AS(o,s.values),r,e)),s}var $e=class{constructor(s,t){this._$AV=[],this._$AN=void 0,this._$AD=s,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(s){let{el:{content:t},parts:e}=this._$AD,r=(s?.creationScope??ot).importNode(t,true);it.currentNode=r;let i=it.nextNode(),n=0,l=0,c=e[0];for(;c!==void 0;){if(n===c.index){let d;c.type===2?d=new qt(i,i.nextSibling,this,s):c.type===1?d=new c.ctor(i,c.name,c.strings,this,s):c.type===6&&(d=new we(i,this,s)),this._$AV.push(d),c=e[++l];}n!==c?.index&&(i=it.nextNode(),n++);}return it.currentNode=ot,r}p(s){let t=0;for(let e of this._$AV)e!==void 0&&(e.strings!==void 0?(e._$AI(s,e,t),t+=e.strings.length-2):e._$AI(s[t])),t++;}},qt=class o{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(s,t,e,r){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=s,this._$AB=t,this._$AM=e,this.options=r,this._$Cv=r?.isConnected??true;}get parentNode(){let s=this._$AA.parentNode,t=this._$AM;return t!==void 0&&s?.nodeType===11&&(s=t.parentNode),s}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(s,t=this){s=pt(this,s,t),Ht(s)?s===p||s==null||s===""?(this._$AH!==p&&this._$AR(),this._$AH=p):s!==this._$AH&&s!==Y&&this._(s):s._$litType$!==void 0?this.$(s):s.nodeType!==void 0?this.T(s):Hr(s)?this.k(s):this._(s);}O(s){return this._$AA.parentNode.insertBefore(s,this._$AB)}T(s){this._$AH!==s&&(this._$AR(),this._$AH=this.O(s));}_(s){this._$AH!==p&&Ht(this._$AH)?this._$AA.nextSibling.data=s:this.T(ot.createTextNode(s)),this._$AH=s;}$(s){let{values:t,_$litType$:e}=s,r=typeof e=="number"?this._$AC(s):(e.el===void 0&&(e.el=Pt.createElement(ys(e.h,e.h[0]),this.options)),e);if(this._$AH?._$AD===r)this._$AH.p(t);else {let i=new $e(r,this),n=i.u(this.options);i.p(t),this.T(n),this._$AH=i;}}_$AC(s){let t=vs.get(s.strings);return t===void 0&&vs.set(s.strings,t=new Pt(s)),t}k(s){Re(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,e,r=0;for(let i of s)r===t.length?t.push(e=new o(this.O(Dt()),this.O(Dt()),this,this.options)):e=t[r],e._$AI(i),r++;r<t.length&&(this._$AR(e&&e._$AB.nextSibling,r),t.length=r);}_$AR(s=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);s!==this._$AB;){let e=ds(s).nextSibling;ds(s).remove(),s=e;}}setConnected(s){this._$AM===void 0&&(this._$Cv=s,this._$AP?.(s));}},ht=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(s,t,e,r,i){this.type=1,this._$AH=p,this._$AN=void 0,this.element=s,this.name=t,this._$AM=r,this.options=i,e.length>2||e[0]!==""||e[1]!==""?(this._$AH=Array(e.length-1).fill(new String),this.strings=e):this._$AH=p;}_$AI(s,t=this,e,r){let i=this.strings,n=false;if(i===void 0)s=pt(this,s,t,0),n=!Ht(s)||s!==this._$AH&&s!==Y,n&&(this._$AH=s);else {let l=s,c,d;for(s=i[0],c=0;c<i.length-1;c++)d=pt(this,l[e+c],t,c),d===Y&&(d=this._$AH[c]),n||(n=!Ht(d)||d!==this._$AH[c]),d===p?s=p:s!==p&&(s+=(d??"")+i[c+1]),this._$AH[c]=d;}n&&!r&&this.j(s);}j(s){s===p?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,s??"");}},xe=class extends ht{constructor(){super(...arguments),this.type=3;}j(s){this.element[this.name]=s===p?void 0:s;}},Se=class extends ht{constructor(){super(...arguments),this.type=4;}j(s){this.element.toggleAttribute(this.name,!!s&&s!==p);}},Te=class extends ht{constructor(s,t,e,r,i){super(s,t,e,r,i),this.type=5;}_$AI(s,t=this){if((s=pt(this,s,t,0)??p)===Y)return;let e=this._$AH,r=s===p&&e!==p||s.capture!==e.capture||s.once!==e.once||s.passive!==e.passive,i=s!==p&&(e===p||r);r&&this.element.removeEventListener(this.name,this,e),i&&this.element.addEventListener(this.name,this,s),this._$AH=s;}handleEvent(s){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,s):this._$AH.handleEvent(s);}},we=class{constructor(s,t,e){this.element=s,this.type=6,this._$AN=void 0,this._$AM=t,this.options=e;}get _$AU(){return this._$AM._$AU}_$AI(s){pt(this,s);}};var qr=kt.litHtmlPolyfillSupport;qr?.(Pt,qt),(kt.litHtmlVersions??(kt.litHtmlVersions=[])).push("3.3.2");var _s=(o,s,t)=>{let e=t?.renderBefore??s,r=e._$litPart$;if(r===void 0){let i=t?.renderBefore??null;e._$litPart$=r=new qt(s.insertBefore(Dt(),i),i,void 0,t??{});}return r._$AI(o),r};var Ut=globalThis,E=class extends j{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let s=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=s.firstChild),s}update(s){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(s),this._$Do=_s(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return Y}};E._$litElement$=true,E.finalized=true,Ut.litElementHydrateSupport?.({LitElement:E});var Ur=Ut.litElementPolyfillSupport;Ur?.({LitElement:E});(Ut.litElementVersions??(Ut.litElementVersions=[])).push("4.2.2");var S=o=>(s,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(o,s);}):customElements.define(o,s);};var Fr={attribute:true,type:String,converter:Nt,reflect:false,hasChanged:te},Gr=(o=Fr,s,t)=>{let{kind:e,metadata:r}=t,i=globalThis.litPropertyMetadata.get(r);if(i===void 0&&globalThis.litPropertyMetadata.set(r,i=new Map),e==="setter"&&((o=Object.create(o)).wrapped=true),i.set(t.name,o),e==="accessor"){let{name:n}=t;return {set(l){let c=s.get.call(this);s.set.call(this,l),this.requestUpdate(n,c,o,true,l);},init(l){return l!==void 0&&this.C(n,void 0,o,l),l}}}if(e==="setter"){let{name:n}=t;return function(l){let c=this[n];s.call(this,l),this.requestUpdate(n,c,o,true,l);}}throw Error("Unsupported decorator location: "+e)};function L(o){return (s,t)=>typeof t=="object"?Gr(o,s,t):((e,r,i)=>{let n=r.hasOwnProperty(i);return r.constructor.createProperty(i,e),n?Object.getOwnPropertyDescriptor(r,i):void 0})(o,s,t)}function b(o){return L({...o,state:true,attribute:false})}var Ft=class extends E{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return a`<span class="method-badge method-badge-${t}">${t}</span>`}};u([L()],Ft.prototype,"method",2),Ft=u([S("bk-method-badge")],Ft);var P="/__brakit/api",F="/__brakit",w={flows:`${P}/flows`,requests:`${P}/requests`,events:`${P}/events`,clear:`${P}/clear`,fetches:`${P}/fetches`,errors:`${P}/errors`,logs:`${P}/logs`,queries:`${P}/queries`,metricsLive:`${P}/metrics/live`,insights:`${P}/insights`,tab:`${P}/tab`,activity:`${P}/activity`,graph:`${P}/graph`};var ut="polling",re="static",Br="auth-handshake",Wr="auth-check",Qr="middleware",Gt={[Br]:1,[Wr]:1,[Qr]:1};var ie="fetch";var oe="error_event",ne="query",ae="issues";var Ie={overview:"Overview",actions:"Actions",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",performance:"Performance",security:"Security",graph:"Graph"},Ce={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",performance:"Endpoint health and response time trends",security:"Security findings and recommendations",graph:"Runtime dependency graph of your application"};var Me=100,mt=300,ft=800,Ne=2e3,Oe=100,ke=50,De=500;var J="__all__",pe={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},As={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},qe=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],Bt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},gt=[{max:Me,label:"Fast",color:"var(--green)",bg:"rgba(22,163,74,0.08)",border:"rgba(22,163,74,0.2)"},{max:mt,label:"Good",color:"var(--green)",bg:"rgba(22,163,74,0.06)",border:"rgba(22,163,74,0.15)"},{max:ft,label:"OK",color:"var(--amber)",bg:"rgba(217,119,6,0.06)",border:"rgba(217,119,6,0.15)"},{max:Ne,label:"Slow",color:"var(--red)",bg:"rgba(220,38,38,0.06)",border:"rgba(220,38,38,0.15)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"rgba(220,38,38,0.08)",border:"rgba(220,38,38,0.2)"}],Is="rgba(228,228,231,0.8)",Ue="rgba(113,113,122,0.7)",Cs="10px monospace",Fe="9px monospace";var Ls={top:16,right:16,bottom:28,left:52},Ms={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},Ns={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},Os=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),ks={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},Ds=new Set(["host","connection","accept-encoding"]),Z={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function _(o){return o<1e3?o+"ms":(o/1e3).toFixed(1)+"s"}function et(o){return !o||o===0?"":o<1024?o+"b":(o/1024).toFixed(1)+"kb"}function tt(o){return o?o.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',"""):""}function bt(o){return o>=500?"status-pill-5xx":o>=400?"status-pill-4xx":o>=300?"status-pill-3xx":"status-pill-2xx"}function Hs(o){return ks[o]||(o>=500?"Server Error":o>=400?"Client Error":"OK")}function jr(o,s){if(Os.has(o.toLowerCase())){let t=String(s);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(s)}function Et(o){return !o||Object.keys(o).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(o).map(([s,t])=>'<span class="json-key">'+tt(s)+"</span>: "+tt(jr(s,t))).join(`
|
|
858
|
-
`)}function
|
|
895
|
+
<script>(function(){'use strict';var kr=Object.defineProperty;var Or=Object.getOwnPropertyDescriptor;var u=(o,r,t,e)=>{for(var s=e>1?void 0:e?Or(r,t):r,i=o.length-1,n;i>=0;i--)(n=o[i])&&(s=(e?n(r,t,s):n(s))||s);return e&&s&&kr(r,t,s),s};var oe=globalThis,ae=oe.ShadowRoot&&(oe.ShadyCSS===void 0||oe.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,ms=Symbol(),us=new WeakMap,ne=class{constructor(r,t,e){if(this._$cssResult$=true,e!==ms)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=r,this.t=t;}get styleSheet(){let r=this.o,t=this.t;if(ae&&r===void 0){let e=t!==void 0&&t.length===1;e&&(r=us.get(t)),r===void 0&&((this.o=r=new CSSStyleSheet).replaceSync(this.cssText),e&&us.set(t,r));}return r}toString(){return this.cssText}},fs=o=>new ne(typeof o=="string"?o:o+"",void 0,ms);var vs=(o,r)=>{if(ae)o.adoptedStyleSheets=r.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of r){let e=document.createElement("style"),s=oe.litNonce;s!==void 0&&e.setAttribute("nonce",s),e.textContent=t.cssText,o.appendChild(e);}},Ae=ae?o=>o:o=>o instanceof CSSStyleSheet?(r=>{let t="";for(let e of r.cssRules)t+=e.cssText;return fs(t)})(o):o;var{is:Dr,defineProperty:Pr,getOwnPropertyDescriptor:Hr,getOwnPropertyNames:qr,getOwnPropertySymbols:Ur,getPrototypeOf:Fr}=Object,et=globalThis,gs=et.trustedTypes,Gr=gs?gs.emptyScript:"",Br=et.reactiveElementPolyfillSupport,Pt=(o,r)=>o,Ht={toAttribute(o,r){switch(r){case Boolean:o=o?Gr:null;break;case Object:case Array:o=o==null?o:JSON.stringify(o);}return o},fromAttribute(o,r){let t=o;switch(r){case Boolean:t=o!==null;break;case Number:t=o===null?null:Number(o);break;case Object:case Array:try{t=JSON.parse(o);}catch{t=null;}}return t}},le=(o,r)=>!Dr(o,r),Es={attribute:true,type:String,converter:Ht,reflect:false,useDefault:false,hasChanged:le};Symbol.metadata??(Symbol.metadata=Symbol("metadata")),et.litPropertyMetadata??(et.litPropertyMetadata=new WeakMap);var K=class extends HTMLElement{static addInitializer(r){this._$Ei(),(this.l??(this.l=[])).push(r);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(r,t=Es){if(t.state&&(t.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(r)&&((t=Object.create(t)).wrapped=true),this.elementProperties.set(r,t),!t.noAccessor){let e=Symbol(),s=this.getPropertyDescriptor(r,e,t);s!==void 0&&Pr(this.prototype,r,s);}}static getPropertyDescriptor(r,t,e){let{get:s,set:i}=Hr(this.prototype,r)??{get(){return this[t]},set(n){this[t]=n;}};return {get:s,set(n){let a=s?.call(this);i?.call(this,n),this.requestUpdate(r,a,e);},configurable:true,enumerable:true}}static getPropertyOptions(r){return this.elementProperties.get(r)??Es}static _$Ei(){if(this.hasOwnProperty(Pt("elementProperties")))return;let r=Fr(this);r.finalize(),r.l!==void 0&&(this.l=[...r.l]),this.elementProperties=new Map(r.elementProperties);}static finalize(){if(this.hasOwnProperty(Pt("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(Pt("properties"))){let t=this.properties,e=[...qr(t),...Ur(t)];for(let s of e)this.createProperty(s,t[s]);}let r=this[Symbol.metadata];if(r!==null){let t=litPropertyMetadata.get(r);if(t!==void 0)for(let[e,s]of t)this.elementProperties.set(e,s);}this._$Eh=new Map;for(let[t,e]of this.elementProperties){let s=this._$Eu(t,e);s!==void 0&&this._$Eh.set(s,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(r){let t=[];if(Array.isArray(r)){let e=new Set(r.flat(1/0).reverse());for(let s of e)t.unshift(Ae(s));}else r!==void 0&&t.push(Ae(r));return t}static _$Eu(r,t){let e=t.attribute;return e===false?void 0:typeof e=="string"?e:typeof r=="string"?r.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,this._$Em=null,this._$Ev();}_$Ev(){this._$ES=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(r=>r(this));}addController(r){(this._$EO??(this._$EO=new Set)).add(r),this.renderRoot!==void 0&&this.isConnected&&r.hostConnected?.();}removeController(r){this._$EO?.delete(r);}_$E_(){let r=new Map,t=this.constructor.elementProperties;for(let e of t.keys())this.hasOwnProperty(e)&&(r.set(e,this[e]),delete this[e]);r.size>0&&(this._$Ep=r);}createRenderRoot(){let r=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return vs(r,this.constructor.elementStyles),r}connectedCallback(){this.renderRoot??(this.renderRoot=this.createRenderRoot()),this.enableUpdating(true),this._$EO?.forEach(r=>r.hostConnected?.());}enableUpdating(r){}disconnectedCallback(){this._$EO?.forEach(r=>r.hostDisconnected?.());}attributeChangedCallback(r,t,e){this._$AK(r,e);}_$ET(r,t){let e=this.constructor.elementProperties.get(r),s=this.constructor._$Eu(r,e);if(s!==void 0&&e.reflect===true){let i=(e.converter?.toAttribute!==void 0?e.converter:Ht).toAttribute(t,e.type);this._$Em=r,i==null?this.removeAttribute(s):this.setAttribute(s,i),this._$Em=null;}}_$AK(r,t){let e=this.constructor,s=e._$Eh.get(r);if(s!==void 0&&this._$Em!==s){let i=e.getPropertyOptions(s),n=typeof i.converter=="function"?{fromAttribute:i.converter}:i.converter?.fromAttribute!==void 0?i.converter:Ht;this._$Em=s;let a=n.fromAttribute(t,i.type);this[s]=a??this._$Ej?.get(s)??a,this._$Em=null;}}requestUpdate(r,t,e,s=false,i){if(r!==void 0){let n=this.constructor;if(s===false&&(i=this[r]),e??(e=n.getPropertyOptions(r)),!((e.hasChanged??le)(i,t)||e.useDefault&&e.reflect&&i===this._$Ej?.get(r)&&!this.hasAttribute(n._$Eu(r,e))))return;this.C(r,t,e);}this.isUpdatePending===false&&(this._$ES=this._$EP());}C(r,t,{useDefault:e,reflect:s,wrapped:i},n){e&&!(this._$Ej??(this._$Ej=new Map)).has(r)&&(this._$Ej.set(r,n??t??this[r]),i!==true||n!==void 0)||(this._$AL.has(r)||(this.hasUpdated||e||(t=void 0),this._$AL.set(r,t)),s===true&&this._$Em!==r&&(this._$Eq??(this._$Eq=new Set)).add(r));}async _$EP(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}let r=this.scheduleUpdate();return r!=null&&await r,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??(this.renderRoot=this.createRenderRoot()),this._$Ep){for(let[s,i]of this._$Ep)this[s]=i;this._$Ep=void 0;}let e=this.constructor.elementProperties;if(e.size>0)for(let[s,i]of e){let{wrapped:n}=i,a=this[s];n!==true||this._$AL.has(s)||a===void 0||this.C(s,void 0,i,a);}}let r=false,t=this._$AL;try{r=this.shouldUpdate(t),r?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM();}catch(e){throw r=false,this._$EM(),e}r&&this._$AE(t);}willUpdate(r){}_$AE(r){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(r)),this.updated(r);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(r){return true}update(r){this._$Eq&&(this._$Eq=this._$Eq.forEach(t=>this._$ET(t,this[t]))),this._$EM();}updated(r){}firstUpdated(r){}};K.elementStyles=[],K.shadowRootOptions={mode:"open"},K[Pt("elementProperties")]=new Map,K[Pt("finalized")]=new Map,Br?.({ReactiveElement:K}),(et.reactiveElementVersions??(et.reactiveElementVersions=[])).push("2.1.2");var Ut=globalThis,bs=o=>o,ce=Ut.trustedTypes,ys=ce?ce.createPolicy("lit-html",{createHTML:o=>o}):void 0,ws="$lit$",st=`lit$${Math.random().toFixed(9).slice(2)}$`,Rs="?"+st,Wr=`<${Rs}>`,ct=document,Ft=()=>ct.createComment(""),Gt=o=>o===null||typeof o!="object"&&typeof o!="function",Oe=Array.isArray,Qr=o=>Oe(o)||typeof o?.[Symbol.iterator]=="function",Ie=`[
|
|
896
|
+
\f\r]`,qt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_s=/-->/g,$s=/>/g,at=RegExp(`>|${Ie}(?:([^\\s"'>=/]+)(${Ie}*=${Ie}*(?:[^
|
|
897
|
+
\f\r"'\`<>=]|("|')|))|$)`,"g"),xs=/'/g,Ss=/"/g,As=/^(?:script|style|textarea|title)$/i,De=o=>(r,...t)=>({_$litType$:o,strings:r,values:t}),l=De(1),k=De(2),z=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),Ts=new WeakMap,lt=ct.createTreeWalker(ct,129);function Is(o,r){if(!Oe(o)||!o.hasOwnProperty("raw"))throw Error("invalid template strings array");return ys!==void 0?ys.createHTML(r):r}var jr=(o,r)=>{let t=o.length-1,e=[],s,i=r===2?"<svg>":r===3?"<math>":"",n=qt;for(let a=0;a<t;a++){let c=o[a],d,h,m=-1,f=0;for(;f<c.length&&(n.lastIndex=f,h=n.exec(c),h!==null);)f=n.lastIndex,n===qt?h[1]==="!--"?n=_s:h[1]!==void 0?n=$s:h[2]!==void 0?(As.test(h[2])&&(s=RegExp("</"+h[2],"g")),n=at):h[3]!==void 0&&(n=at):n===at?h[0]===">"?(n=s??qt,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,d=h[1],n=h[3]===void 0?at:h[3]==='"'?Ss:xs):n===Ss||n===xs?n=at:n===_s||n===$s?n=qt:(n=at,s=void 0);let S=n===at&&o[a+1].startsWith("/>")?" ":"";i+=n===qt?c+Wr:m>=0?(e.push(d),c.slice(0,m)+ws+c.slice(m)+st+S):c+st+(m===-2?a:S);}return [Is(o,i+(o[t]||"<?>")+(r===2?"</svg>":r===3?"</math>":"")),e]},Bt=class o{constructor({strings:r,_$litType$:t},e){let s;this.parts=[];let i=0,n=0,a=r.length-1,c=this.parts,[d,h]=jr(r,t);if(this.el=o.createElement(d,e),lt.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(s=lt.nextNode())!==null&&c.length<a;){if(s.nodeType===1){if(s.hasAttributes())for(let m of s.getAttributeNames())if(m.endsWith(ws)){let f=h[n++],S=s.getAttribute(m).split(st),R=/([.?@])?(.*)/.exec(f);c.push({type:1,index:i,name:R[2],strings:S,ctor:R[1]==="."?Le:R[1]==="?"?Me:R[1]==="@"?Ne:gt}),s.removeAttribute(m);}else m.startsWith(st)&&(c.push({type:6,index:i}),s.removeAttribute(m));if(As.test(s.tagName)){let m=s.textContent.split(st),f=m.length-1;if(f>0){s.textContent=ce?ce.emptyScript:"";for(let S=0;S<f;S++)s.append(m[S],Ft()),lt.nextNode(),c.push({type:2,index:++i});s.append(m[f],Ft());}}}else if(s.nodeType===8)if(s.data===Rs)c.push({type:2,index:i});else {let m=-1;for(;(m=s.data.indexOf(st,m+1))!==-1;)c.push({type:7,index:i}),m+=st.length-1;}i++;}}static createElement(r,t){let e=ct.createElement("template");return e.innerHTML=r,e}};function vt(o,r,t=o,e){if(r===z)return r;let s=e!==void 0?t._$Co?.[e]:t._$Cl,i=Gt(r)?void 0:r._$litDirective$;return s?.constructor!==i&&(s?._$AO?.(false),i===void 0?s=void 0:(s=new i(o),s._$AT(o,t,e)),e!==void 0?(t._$Co??(t._$Co=[]))[e]=s:t._$Cl=s),s!==void 0&&(r=vt(o,s._$AS(o,r.values),s,e)),r}var Ce=class{constructor(r,t){this._$AV=[],this._$AN=void 0,this._$AD=r,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(r){let{el:{content:t},parts:e}=this._$AD,s=(r?.creationScope??ct).importNode(t,true);lt.currentNode=s;let i=lt.nextNode(),n=0,a=0,c=e[0];for(;c!==void 0;){if(n===c.index){let d;c.type===2?d=new Wt(i,i.nextSibling,this,r):c.type===1?d=new c.ctor(i,c.name,c.strings,this,r):c.type===6&&(d=new ke(i,this,r)),this._$AV.push(d),c=e[++a];}n!==c?.index&&(i=lt.nextNode(),n++);}return lt.currentNode=ct,s}p(r){let t=0;for(let e of this._$AV)e!==void 0&&(e.strings!==void 0?(e._$AI(r,e,t),t+=e.strings.length-2):e._$AI(r[t])),t++;}},Wt=class o{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(r,t,e,s){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=r,this._$AB=t,this._$AM=e,this.options=s,this._$Cv=s?.isConnected??true;}get parentNode(){let r=this._$AA.parentNode,t=this._$AM;return t!==void 0&&r?.nodeType===11&&(r=t.parentNode),r}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(r,t=this){r=vt(this,r,t),Gt(r)?r===p||r==null||r===""?(this._$AH!==p&&this._$AR(),this._$AH=p):r!==this._$AH&&r!==z&&this._(r):r._$litType$!==void 0?this.$(r):r.nodeType!==void 0?this.T(r):Qr(r)?this.k(r):this._(r);}O(r){return this._$AA.parentNode.insertBefore(r,this._$AB)}T(r){this._$AH!==r&&(this._$AR(),this._$AH=this.O(r));}_(r){this._$AH!==p&&Gt(this._$AH)?this._$AA.nextSibling.data=r:this.T(ct.createTextNode(r)),this._$AH=r;}$(r){let{values:t,_$litType$:e}=r,s=typeof e=="number"?this._$AC(r):(e.el===void 0&&(e.el=Bt.createElement(Is(e.h,e.h[0]),this.options)),e);if(this._$AH?._$AD===s)this._$AH.p(t);else {let i=new Ce(s,this),n=i.u(this.options);i.p(t),this.T(n),this._$AH=i;}}_$AC(r){let t=Ts.get(r.strings);return t===void 0&&Ts.set(r.strings,t=new Bt(r)),t}k(r){Oe(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,e,s=0;for(let i of r)s===t.length?t.push(e=new o(this.O(Ft()),this.O(Ft()),this,this.options)):e=t[s],e._$AI(i),s++;s<t.length&&(this._$AR(e&&e._$AB.nextSibling,s),t.length=s);}_$AR(r=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);r!==this._$AB;){let e=bs(r).nextSibling;bs(r).remove(),r=e;}}setConnected(r){this._$AM===void 0&&(this._$Cv=r,this._$AP?.(r));}},gt=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(r,t,e,s,i){this.type=1,this._$AH=p,this._$AN=void 0,this.element=r,this.name=t,this._$AM=s,this.options=i,e.length>2||e[0]!==""||e[1]!==""?(this._$AH=Array(e.length-1).fill(new String),this.strings=e):this._$AH=p;}_$AI(r,t=this,e,s){let i=this.strings,n=false;if(i===void 0)r=vt(this,r,t,0),n=!Gt(r)||r!==this._$AH&&r!==z,n&&(this._$AH=r);else {let a=r,c,d;for(r=i[0],c=0;c<i.length-1;c++)d=vt(this,a[e+c],t,c),d===z&&(d=this._$AH[c]),n||(n=!Gt(d)||d!==this._$AH[c]),d===p?r=p:r!==p&&(r+=(d??"")+i[c+1]),this._$AH[c]=d;}n&&!s&&this.j(r);}j(r){r===p?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,r??"");}},Le=class extends gt{constructor(){super(...arguments),this.type=3;}j(r){this.element[this.name]=r===p?void 0:r;}},Me=class extends gt{constructor(){super(...arguments),this.type=4;}j(r){this.element.toggleAttribute(this.name,!!r&&r!==p);}},Ne=class extends gt{constructor(r,t,e,s,i){super(r,t,e,s,i),this.type=5;}_$AI(r,t=this){if((r=vt(this,r,t,0)??p)===z)return;let e=this._$AH,s=r===p&&e!==p||r.capture!==e.capture||r.once!==e.once||r.passive!==e.passive,i=r!==p&&(e===p||s);s&&this.element.removeEventListener(this.name,this,e),i&&this.element.addEventListener(this.name,this,r),this._$AH=r;}handleEvent(r){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,r):this._$AH.handleEvent(r);}},ke=class{constructor(r,t,e){this.element=r,this.type=6,this._$AN=void 0,this._$AM=t,this.options=e;}get _$AU(){return this._$AM._$AU}_$AI(r){vt(this,r);}};var Yr=Ut.litHtmlPolyfillSupport;Yr?.(Bt,Wt),(Ut.litHtmlVersions??(Ut.litHtmlVersions=[])).push("3.3.2");var Cs=(o,r,t)=>{let e=t?.renderBefore??r,s=e._$litPart$;if(s===void 0){let i=t?.renderBefore??null;e._$litPart$=s=new Wt(r.insertBefore(Ft(),i),i,void 0,t??{});}return s._$AI(o),s};var Qt=globalThis,b=class extends K{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let r=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=r.firstChild),r}update(r){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(r),this._$Do=Cs(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return z}};b._$litElement$=true,b.finalized=true,Qt.litElementHydrateSupport?.({LitElement:b});var Vr=Qt.litElementPolyfillSupport;Vr?.({LitElement:b});(Qt.litElementVersions??(Qt.litElementVersions=[])).push("4.2.2");var $=o=>(r,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(o,r);}):customElements.define(o,r);};var Xr={attribute:true,type:String,converter:Ht,reflect:false,hasChanged:le},Kr=(o=Xr,r,t)=>{let{kind:e,metadata:s}=t,i=globalThis.litPropertyMetadata.get(s);if(i===void 0&&globalThis.litPropertyMetadata.set(s,i=new Map),e==="setter"&&((o=Object.create(o)).wrapped=true),i.set(t.name,o),e==="accessor"){let{name:n}=t;return {set(a){let c=r.get.call(this);r.set.call(this,a),this.requestUpdate(n,c,o,true,a);},init(a){return a!==void 0&&this.C(n,void 0,o,a),a}}}if(e==="setter"){let{name:n}=t;return function(a){let c=this[n];r.call(this,a),this.requestUpdate(n,c,o,true,a);}}throw Error("Unsupported decorator location: "+e)};function L(o){return (r,t)=>typeof t=="object"?Kr(o,r,t):((e,s,i)=>{let n=s.hasOwnProperty(i);return s.constructor.createProperty(i,e),n?Object.getOwnPropertyDescriptor(s,i):void 0})(o,r,t)}function E(o){return L({...o,state:true,attribute:false})}var jt=class extends b{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return l`<span class="method-badge method-badge-${t}">${t}</span>`}};u([L()],jt.prototype,"method",2),jt=u([$("bk-method-badge")],jt);var q="/__brakit/api",U="/__brakit",T={flows:`${q}/flows`,requests:`${q}/requests`,events:`${q}/events`,clear:`${q}/clear`,fetches:`${q}/fetches`,errors:`${q}/errors`,logs:`${q}/logs`,queries:`${q}/queries`,metricsLive:`${q}/metrics/live`,insights:`${q}/insights`,tab:`${q}/tab`,activity:`${q}/activity`,graph:`${q}/graph`};var Et="polling",pe="static",zr="auth-handshake",Jr="auth-check",Zr="middleware",Yt={[zr]:1,[Jr]:1,[Zr]:1};var he="fetch";var ue="error_event",me="query",fe="issues";var Pe={overview:"Overview",actions:"Actions",insights:"Insights",performance:"Performance",graph:"Graph",explorer:"Explorer",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",security:"Security"},He={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",insights:"Security findings, error patterns, and issue tracking",performance:"Endpoint health and response time trends",graph:"Runtime dependency graph of your application",explorer:"Browse raw requests, fetches, queries, logs, and errors",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",security:"Security findings and recommendations"},ve=[{key:"requests",label:"Requests"},{key:"fetches",label:"Fetches"},{key:"queries",label:"Queries"},{key:"logs",label:"Logs"},{key:"errors",label:"Errors"}];var Ls=new Set(["exposed-secret","token-in-url","stack-trace-leak","error-info-leak","insecure-cookie","sensitive-logs","cors-credentials","response-pii-leak"]),Ms=new Set(["slow-endpoint","n1","high-query-count"]);function Vt(o){let r=o.issue.rule||o.issue.type||"";return Ls.has(r)?"security":Ms.has(r)?"performance":"quality"}var O={CLEAR_CONFIRM:"This will clear all data including performance metrics history. Continue?",CLEARED_TOAST:"Cleared",EMPTY_TITLE:"Waiting for requests...",EMPTY_SUBTITLE_OVERVIEW:"Start using your app to see your dashboard here",ALL_CLEAR:"All clear",ALL_CLEAR_DETAIL:"No issues detected",NO_ACTIONS:"No actions captured yet",NO_REQUEST_DATA:"No request data yet",ALL_FAST:"All fast",NO_SLOW_ENDPOINTS:"No slow endpoints detected",ZERO_ERRORS:"0 errors",ALL_REQUESTS_OK:"All requests successful",BUILD_GRAPH:"Navigate your app to build the graph",REVIEW_RECOMMENDED:"review recommended"};var Fe=100,yt=300,_t=800,Ge=2e3,Be=100,We=50,Qe=500;var rt="__all__",Ee={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},qs={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},Ve=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],Xt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},xt=[{max:Fe,label:"Fast",color:"var(--green)",bg:"var(--green-bg)",border:"var(--green-border)"},{max:yt,label:"Good",color:"var(--green)",bg:"var(--green-bg-subtle)",border:"var(--green-border-subtle)"},{max:_t,label:"OK",color:"var(--amber)",bg:"var(--amber-bg)",border:"var(--amber-border)"},{max:Ge,label:"Slow",color:"var(--red)",bg:"var(--red-bg)",border:"var(--red-border)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"var(--red-bg)",border:"var(--red-border)"}],Us="rgba(228,228,231,0.8)",Xe="rgba(113,113,122,0.7)",Fs="10px monospace",Ke="9px monospace";var Gs={top:16,right:16,bottom:28,left:52},Bs={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},Ws={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},Qs=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),js={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},Ys=new Set(["host","connection","accept-encoding"]),Y={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function y(o){return o<1e3?o+"ms":(o/1e3).toFixed(1)+"s"}function ot(o){return !o||o===0?"":o<1024?o+"b":(o/1024).toFixed(1)+"kb"}function it(o){return o?o.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',"""):""}function St(o){return o>=500?"status-pill-5xx":o>=400?"status-pill-4xx":o>=300?"status-pill-3xx":"status-pill-2xx"}function Vs(o){return js[o]||(o>=500?"Server Error":o>=400?"Client Error":"OK")}function ti(o,r){if(Qs.has(o.toLowerCase())){let t=String(r);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(r)}function Tt(o){return !o||Object.keys(o).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(o).map(([r,t])=>'<span class="json-key">'+it(r)+"</span>: "+it(ti(r,t))).join(`
|
|
898
|
+
`)}function dt(o){if(!o)return '<span style="color:var(--text-muted)">No body</span>';try{let r=JSON.parse(o);return ei(JSON.stringify(r,null,2))}catch{return it(o)}}function ei(o){return it(o).replace(/("(?:[^"\\]|\\.)*")(\s*:)?|\b(true|false)\b|\bnull\b|(-?\d+\.?\d*(?:[eE][+-]?\d+)?)/g,(r,t,e,s,i)=>t?e?'<span class="json-key">'+t+"</span>"+e:'<span class="json-str">'+t+"</span>":s?'<span class="json-bool">'+r+"</span>":i?'<span class="json-num">'+r+"</span>":r==="null"?'<span class="json-null">null</span>':r)}var Kt=class extends b{constructor(){super(...arguments);this.code=0;}createRenderRoot(){return this}render(){let t=St(this.code);return l`<span class="status-pill ${t}">${this.code}</span>`}};u([L({type:Number})],Kt.prototype,"code",2),Kt=u([$("bk-status-pill")],Kt);var zt=class extends b{constructor(){super(...arguments);this.ms=0;}createRenderRoot(){return this}render(){return l`<span class="req-duration">${y(this.ms)}</span>`}};u([L({type:Number})],zt.prototype,"ms",2),zt=u([$("bk-duration-label")],zt);var wt=class extends b{constructor(){super(...arguments);this.title="";this.subtitle="";}createRenderRoot(){return this}render(){return l`
|
|
859
899
|
<div class="empty">
|
|
860
900
|
<span class="empty-title">${this.title}</span>
|
|
861
901
|
<span class="empty-sub">${this.subtitle}</span>
|
|
862
902
|
</div>
|
|
863
|
-
`}};u([L()],
|
|
903
|
+
`}};u([L()],wt.prototype,"title",2),u([L()],wt.prototype,"subtitle",2),wt=u([$("bk-empty-state")],wt);var D=class extends b{constructor(){super(...arguments);this.message="";this.visible=false;}createRenderRoot(){return this}static show(t){let e=document.querySelector("bk-toast");e&&e.showMessage(t);}showMessage(t){this.hideTimer&&clearTimeout(this.hideTimer),this.message=t,this.visible=true,this.hideTimer=setTimeout(()=>{this.visible=false;},2e3);}render(){return l`<div class="toast ${this.visible?"show":""}">${this.message}</div>`}};u([E()],D.prototype,"message",2),u([E()],D.prototype,"visible",2),D=u([$("bk-toast")],D);var pt=class extends b{constructor(){super(...arguments);this.text="";this.label="Copy";this.toastMessage="Copied";}createRenderRoot(){return this}async copy(t){t.stopPropagation();try{await navigator.clipboard.writeText(this.text),D.show(this.toastMessage);}catch{}}render(){return l`<button class="query-detail-copy" @click=${this.copy}>${this.label}</button>`}};u([L()],pt.prototype,"text",2),u([L()],pt.prototype,"label",2),u([L({attribute:"toast-message"})],pt.prototype,"toastMessage",2),pt=u([$("bk-copy-button")],pt);var ht=class extends b{constructor(){super(...arguments);this.value="";this.label="";this.color="";}createRenderRoot(){return this}render(){return l`
|
|
864
904
|
<div class="fetch-stat">
|
|
865
905
|
<span class="fetch-stat-value" style="color:${this.color}">${this.value}</span>
|
|
866
906
|
<span class="fetch-stat-label">${this.label}</span>
|
|
867
907
|
</div>
|
|
868
|
-
`}};u([L()],
|
|
908
|
+
`}};u([L()],ht.prototype,"value",2),u([L()],ht.prototype,"label",2),u([L()],ht.prototype,"color",2),ht=u([$("bk-stat-card")],ht);var nt=class extends Event{constructor(r,t,e,s){super("context-request",{bubbles:true,composed:true}),this.context=r,this.contextTarget=t,this.callback=e,this.subscribe=s??false;}};var Rt=class{constructor(r,t,e,s){if(this.subscribe=false,this.provided=false,this.value=void 0,this.t=(i,n)=>{this.unsubscribe&&(this.unsubscribe!==n&&(this.provided=false,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=i,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=true,this.callback&&this.callback(i,n)),this.unsubscribe=n;},this.host=r,t.context!==void 0){let i=t;this.context=i.context,this.callback=i.callback,this.subscribe=i.subscribe??false;}else this.context=t,this.callback=e,this.subscribe=s??false;this.host.addController(this);}hostConnected(){this.dispatchRequest();}hostDisconnected(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=void 0);}dispatchRequest(){this.host.dispatchEvent(new nt(this.context,this.host,this.t,this.subscribe));}};var be=class{get value(){return this.o}set value(r){this.setValue(r);}setValue(r,t=false){let e=t||!Object.is(r,this.o);this.o=r,e&&this.updateObservers();}constructor(r){this.subscriptions=new Map,this.updateObservers=()=>{for(let[t,{disposer:e}]of this.subscriptions)t(this.o,e);},r!==void 0&&(this.value=r);}addCallback(r,t,e){if(!e)return void r(this.value);this.subscriptions.has(r)||this.subscriptions.set(r,{disposer:()=>{this.subscriptions.delete(r);},consumerHost:t});let{disposer:s}=this.subscriptions.get(r);r(this.value,s);}clearCallbacks(){this.subscriptions.clear();}};var ze=class extends Event{constructor(r,t){super("context-provider",{bubbles:true,composed:true}),this.context=r,this.contextTarget=t;}},At=class extends be{constructor(r,t,e){super(t.context!==void 0?t.initialValue:e),this.onContextRequest=s=>{if(s.context!==this.context)return;let i=s.contextTarget??s.composedPath()[0];i!==this.host&&(s.stopPropagation(),this.addCallback(s.callback,i,s.subscribe));},this.onProviderRequest=s=>{if(s.context!==this.context||(s.contextTarget??s.composedPath()[0])===this.host)return;let i=new Set;for(let[n,{consumerHost:a}]of this.subscriptions)i.has(n)||(i.add(n),a.dispatchEvent(new nt(this.context,a,n,true)));s.stopPropagation();},this.host=r,t.context!==void 0?this.context=t.context:this.context=t,this.attachListeners(),this.host.addController?.(this);}attachListeners(){this.host.addEventListener("context-request",this.onContextRequest),this.host.addEventListener("context-provider",this.onProviderRequest);}hostConnected(){this.host.dispatchEvent(new ze(this.context,this.host));}};function Je({context:o}){return (r,t)=>{let e=new WeakMap;if(typeof t=="object")return {get(){return r.get.call(this)},set(s){return e.get(this).setValue(s),r.set.call(this,s)},init(s){return e.set(this,new At(this,{context:o,initialValue:s})),s}};{r.constructor.addInitializer((n=>{e.set(n,new At(n,{context:o}));}));let s=Object.getOwnPropertyDescriptor(r,t),i;if(s===void 0){let n=new WeakMap;i={get(){return n.get(this)},set(a){e.get(this).setValue(a),n.set(this,a);},configurable:true,enumerable:true};}else {let n=s.set;i={...s,set(a){e.get(this).setValue(a),n?.call(this,a);}};}return void Object.defineProperty(r,t,i)}}}function I({context:o,subscribe:r}){return (t,e)=>{typeof e=="object"?e.addInitializer((function(){new Rt(this,{context:o,callback:s=>{t.set.call(this,s);},subscribe:r});})):t.constructor.addInitializer((s=>{new Rt(s,{context:o,callback:i=>{s[e]=i;},subscribe:r});}));}}var A="dashboard-store",ye=class extends EventTarget{constructor(){super(...arguments);this._state={flows:[],requests:[],fetches:[],errors:[],logs:[],queries:[],issues:[],metrics:[],viewMode:"simple",activeView:"overview"};}get state(){return this._state}setState(t,e){this._state={...this._state,[t]:e},this.notify(t);}setFlows(t){this.setState("flows",t);}setRequests(t){this.setState("requests",t);}setFetches(t){this.setState("fetches",t);}setErrors(t){this.setState("errors",t);}setLogs(t){this.setState("logs",t);}setQueries(t){this.setState("queries",t);}setIssues(t){this.setState("issues",t);}setMetrics(t){this.setState("metrics",t);}prependRequest(t){let e=[t,...this._state.requests.slice(0,999)];this._state={...this._state,requests:e},this.notify("requests");}prependFetch(t){let e=[t,...this._state.fetches.slice(0,999)];this._state={...this._state,fetches:e},this.notify("fetches");}prependError(t){let e=[t,...this._state.errors.slice(0,999)];this._state={...this._state,errors:e},this.notify("errors");}prependLog(t){let e=[t,...this._state.logs.slice(0,999)];this._state={...this._state,logs:e},this.notify("logs");}prependQuery(t){let e=[t,...this._state.queries.slice(0,999)];this._state={...this._state,queries:e},this.notify("queries");}setActiveView(t){this.setState("activeView",t);}setViewMode(t){this.setState("viewMode",t);}clearAll(){this._state={...this._state,flows:[],requests:[],fetches:[],errors:[],logs:[],queries:[],issues:[],metrics:[]},this.notify("all");}notify(t){this.dispatchEvent(new CustomEvent("state-changed",{detail:t}));}};var It=class extends b{constructor(){super(...arguments);this.expandedIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleError(t){this.expandedIdx=this.expandedIdx===t?-1:t;}renderErrorRow(t,e){let s=new Date(t.timestamp).toLocaleTimeString(),i=this.expandedIdx===e;return l`
|
|
869
909
|
<div
|
|
870
910
|
class="req-row tel-clickable ${i?"expanded":""}"
|
|
871
911
|
@click=${()=>this.toggleError(e)}
|
|
872
912
|
>
|
|
873
913
|
<span class="tel-error-name" title=${t.name}>${t.name}</span>
|
|
874
914
|
<span class="tel-message" title=${t.message}>${t.message}</span>
|
|
875
|
-
<span class="tel-timestamp">${
|
|
915
|
+
<span class="tel-timestamp">${s}</span>
|
|
876
916
|
</div>
|
|
877
|
-
${i&&t.stack?
|
|
878
|
-
`}render(){let t=this.store.state.errors;return t.length===0?
|
|
917
|
+
${i&&t.stack?l`<div class="error-stack">${t.stack}</div>`:p}
|
|
918
|
+
`}render(){let t=this.store.state.errors;return t.length===0?l`<bk-empty-state
|
|
879
919
|
title="No errors"
|
|
880
920
|
subtitle="No errors have been captured yet"
|
|
881
|
-
></bk-empty-state>`:
|
|
921
|
+
></bk-empty-state>`:l`
|
|
882
922
|
<div class="col-header">
|
|
883
923
|
<span style="width:180px">Type</span>
|
|
884
924
|
<span style="flex:1">Message</span>
|
|
885
925
|
<span style="width:130px;text-align:right">Time</span>
|
|
886
926
|
</div>
|
|
887
927
|
<div id="error-list">
|
|
888
|
-
${t.map((e,
|
|
928
|
+
${t.map((e,s)=>this.renderErrorRow(e,s))}
|
|
889
929
|
</div>
|
|
890
|
-
`}};u([I({context:A})],
|
|
930
|
+
`}};u([I({context:A})],It.prototype,"store",2),u([E()],It.prototype,"expandedIdx",2),It=u([$("bk-errors-view")],It);var Jt=class extends b{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}renderAnalysis(r){if(r.length===0)return p;let t={error:0,warn:0,info:0,debug:0,log:0};for(let e of r)t[e.level]!==void 0&&t[e.level]++;return l`
|
|
891
931
|
<div id="log-analysis">
|
|
892
932
|
<div class="fetch-summary">
|
|
893
|
-
<bk-stat-card value=${String(
|
|
894
|
-
${t.error>0?
|
|
895
|
-
${t.warn>0?
|
|
933
|
+
<bk-stat-card value=${String(r.length)} label="Total Logs"></bk-stat-card>
|
|
934
|
+
${t.error>0?l`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:p}
|
|
935
|
+
${t.warn>0?l`<bk-stat-card value=${String(t.warn)} label="Warnings" color="var(--amber)"></bk-stat-card>`:p}
|
|
896
936
|
<bk-stat-card value=${String(t.info)} label="Info"></bk-stat-card>
|
|
897
|
-
${t.debug>0?
|
|
898
|
-
${t.log>0?
|
|
937
|
+
${t.debug>0?l`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:p}
|
|
938
|
+
${t.log>0?l`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:p}
|
|
899
939
|
</div>
|
|
900
940
|
</div>
|
|
901
|
-
`}renderLogRow(
|
|
941
|
+
`}renderLogRow(r){let t=new Date(r.timestamp).toLocaleTimeString();return l`
|
|
902
942
|
<div class="req-row">
|
|
903
|
-
<span class="tel-level tel-level-${
|
|
904
|
-
<span class="tel-message tel-mono" title=${
|
|
943
|
+
<span class="tel-level tel-level-${r.level}">${r.level.toUpperCase()}</span>
|
|
944
|
+
<span class="tel-message tel-mono" title=${r.message}>${r.message}</span>
|
|
905
945
|
<span class="tel-timestamp">${t}</span>
|
|
906
946
|
</div>
|
|
907
|
-
`}render(){let
|
|
947
|
+
`}render(){let r=this.store.state.logs;return r.length===0?l`<bk-empty-state
|
|
908
948
|
title="No logs"
|
|
909
949
|
subtitle="No console output has been captured yet"
|
|
910
|
-
></bk-empty-state>`:
|
|
911
|
-
${this.renderAnalysis(
|
|
950
|
+
></bk-empty-state>`:l`
|
|
951
|
+
${this.renderAnalysis(r)}
|
|
912
952
|
<div class="col-header">
|
|
913
953
|
<span style="width:52px">Level</span>
|
|
914
954
|
<span style="flex:1">Message</span>
|
|
915
955
|
<span style="width:130px;text-align:right">Time</span>
|
|
916
956
|
</div>
|
|
917
957
|
<div id="log-list">
|
|
918
|
-
${
|
|
958
|
+
${r.map(t=>this.renderLogRow(t))}
|
|
919
959
|
</div>
|
|
920
|
-
`}};u([I({context:A})],
|
|
960
|
+
`}};u([I({context:A})],Jt.prototype,"store",2),Jt=u([$("bk-logs-view")],Jt);var Xs={CHILD:2},Ks=o=>(...r)=>({_$litDirective$:o,values:r}),_e=class{constructor(r){}get _$AU(){return this._$AM._$AU}_$AT(r,t,e){this._$Ct=r,this._$AM=t,this._$Ci=e;}_$AS(r,t){return this.update(r,t)}update(r,t){return this.render(...t)}};var Zt=class extends _e{constructor(r){if(super(r),this.it=p,r.type!==Xs.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(r){if(r===p||r==null)return this._t=void 0,this.it=r;if(r===z)return r;if(typeof r!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(r===this.it)return this._t;this.it=r;let t=[r];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}};Zt.directiveName="unsafeHTML",Zt.resultType=1;var G=Ks(Zt);var ri=new Set(["SELECT","FROM","WHERE","AND","OR","INSERT","INTO","VALUES","UPDATE","SET","DELETE","JOIN","LEFT","RIGHT","INNER","OUTER","ON","GROUP","BY","ORDER","HAVING","LIMIT","OFFSET","AS","IN","NOT","NULL","IS","LIKE","BETWEEN","EXISTS","CASE","WHEN","THEN","ELSE","END","COUNT","SUM","AVG","MIN","MAX","DISTINCT","UNION","ALL","CREATE","TABLE","ALTER","DROP","INDEX","RETURNING","WITH","RECURSIVE","OVER","PARTITION","WINDOW","FETCH","NEXT","ROWS","ONLY","CAST","COALESCE","NULLIF","EXTRACT","INTERVAL","TRUE","FALSE","ASC","DESC","USING","NATURAL","CROSS","FULL","ROLLBACK","COMMIT","BEGIN","TRANSACTION","SAVEPOINT","RELEASE"]);function zs(o){let r=o.trimStart().split(/\s/)[0];return r?r.toUpperCase():"?"}function Js(o){let r=o.replace(/\s+/g," ").trim(),t=r.match(/\bFROM\s+["'`]?(\w+)["'`]?/i);if(t)return t[1];let e=r.match(/\bINTO\s+["'`]?(\w+)["'`]?/i);if(e)return e[1];let s=r.match(/\bUPDATE\s+["'`]?(\w+)["'`]?/i);return s?s[1]:""}function Zs(o){return it(o).replace(/\b\w+\b/g,r=>ri.has(r.toUpperCase())?'<span class="sql-kw">'+r+"</span>":r)}var Ct=class extends b{constructor(){super(...arguments);this.expandedIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleQuery(t){this.expandedIdx=this.expandedIdx===t?-1:t;}queryDuration(t){return t===0?"<1ms":y(t)}getQueryInfo(t){let e=(t.normalizedOp||t.operation||(t.sql?zs(t.sql):"?")).toUpperCase(),s=t.table||t.model||(t.sql?Js(t.sql):""),i=t.sql||e+" "+s;return {op:e,table:s,sqlText:i}}renderQueryRow(t,e){let{op:s,table:i,sqlText:n}=this.getQueryInfo(t),a=Ee[s]||"var(--text-dim)",c=t.durationMs>Be,d=t.sql||s+" "+i,h=this.expandedIdx===e;return l`
|
|
921
961
|
<div>
|
|
922
962
|
<div
|
|
923
963
|
class="req-row query-row tel-clickable ${h?"expanded":""}"
|
|
924
964
|
@click=${()=>this.toggleQuery(e)}
|
|
925
965
|
>
|
|
926
|
-
<span class="query-op" title=${
|
|
966
|
+
<span class="query-op" title=${s} style="color:${a}">${s}</span>
|
|
927
967
|
<span class="query-table" title=${i}>${i}</span>
|
|
928
968
|
<span class="query-preview" title=${d}>${d}</span>
|
|
929
969
|
<span class="query-dur${c?" query-slow":""}">${this.queryDuration(t.durationMs)}</span>
|
|
930
970
|
</div>
|
|
931
971
|
<div class="query-detail ${h?"open":""}">
|
|
932
|
-
${h?
|
|
933
|
-
<pre class="query-detail-sql">${
|
|
972
|
+
${h?l`
|
|
973
|
+
<pre class="query-detail-sql">${G(Zs(n))}</pre>
|
|
934
974
|
<bk-copy-button .text=${n} label="Copy"></bk-copy-button>
|
|
935
975
|
`:p}
|
|
936
976
|
</div>
|
|
937
977
|
</div>
|
|
938
|
-
`}render(){let t=this.store.state.queries;return t.length===0?
|
|
978
|
+
`}render(){let t=this.store.state.queries;return t.length===0?l`<bk-empty-state
|
|
939
979
|
title="No queries"
|
|
940
980
|
subtitle="No database queries have been captured yet"
|
|
941
|
-
></bk-empty-state>`:
|
|
981
|
+
></bk-empty-state>`:l`
|
|
942
982
|
<div class="col-header">
|
|
943
983
|
<span style="width:70px;border-right:1px solid var(--border);padding-right:16px">Operation</span>
|
|
944
984
|
<span style="width:170px;border-right:1px solid var(--border);padding-right:16px">Table</span>
|
|
@@ -946,37 +986,37 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
946
986
|
<span style="width:60px;text-align:right">Time</span>
|
|
947
987
|
</div>
|
|
948
988
|
<div id="query-list">
|
|
949
|
-
${t.map((e,
|
|
989
|
+
${t.map((e,s)=>this.renderQueryRow(e,s))}
|
|
950
990
|
</div>
|
|
951
|
-
`}};u([I({context:A})],
|
|
991
|
+
`}};u([I({context:A})],Ct.prototype,"store",2),u([E()],Ct.prototype,"expandedIdx",2),Ct=u([$("bk-queries-view")],Ct);function Ze(o){return o.replaceAll("'","'\\''")}function ii(o,r){let t=Object.entries(o.headers||{}).filter(([i])=>!Ys.has(i)).map(([i,n])=>`-H '${Ze(i)}: ${Ze(n)}'`).join(" "),e=o.requestBody?` -d '${Ze(o.requestBody)}'`:"",s=r?`http://localhost:${r}`:"";return `curl -X ${o.method} ${t}${e} '${s}${o.url}'`}function Lt(o){let r=window.__BRAKIT_CONFIG__?.port??"",t=ii(o,r);navigator.clipboard.writeText(t).then(()=>D.show("Copied cURL command"));}var Mt=class extends b{constructor(){super(...arguments);this.expandedId=null;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleRequest(t){this.expandedId=this.expandedId===t?null:t;}handleCopyAsCurl(t,e){e.stopPropagation(),Lt(t);}renderDetail(t){return l`
|
|
952
992
|
<div class="detail-meta">
|
|
953
993
|
<span><bk-method-badge .method=${t.method}></bk-method-badge> ${t.url}</span>
|
|
954
994
|
<span><bk-status-pill .code=${t.statusCode}></bk-status-pill></span>
|
|
955
995
|
<span>${t.durationMs}ms</span>
|
|
956
|
-
${t.responseSize?
|
|
996
|
+
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:p}
|
|
957
997
|
</div>
|
|
958
998
|
<div class="request-timeline tl-hidden" data-request-id=${t.id} data-request-started=${String(t.startedAt)}></div>
|
|
959
999
|
<div class="detail-grid">
|
|
960
|
-
<div class="detail-section"><h4>Request Headers</h4><pre>${
|
|
961
|
-
<div class="detail-section"><h4>Response Headers</h4><pre>${
|
|
962
|
-
<div class="detail-section"><h4>Request Body</h4><pre>${
|
|
963
|
-
<div class="detail-section"><h4>Response Body</h4><pre>${
|
|
1000
|
+
<div class="detail-section"><h4>Request Headers</h4><pre>${G(Tt(t.headers))}</pre></div>
|
|
1001
|
+
<div class="detail-section"><h4>Response Headers</h4><pre>${G(Tt(t.responseHeaders))}</pre></div>
|
|
1002
|
+
<div class="detail-section"><h4>Request Body</h4><pre>${G(dt(t.requestBody))}</pre></div>
|
|
1003
|
+
<div class="detail-section"><h4>Response Body</h4><pre>${G(dt(t.responseBody))}</pre></div>
|
|
964
1004
|
</div>
|
|
965
1005
|
<div class="detail-actions">
|
|
966
1006
|
<button class="btn btn-curl" @click=${e=>this.handleCopyAsCurl(t,e)}>Copy cURL</button>
|
|
967
1007
|
</div>
|
|
968
|
-
`}renderRequestRow(t){let e=this.expandedId===t.id;return
|
|
1008
|
+
`}renderRequestRow(t){let e=this.expandedId===t.id;return l`
|
|
969
1009
|
<div class="req-row ${e?"expanded":""}" @click=${()=>this.toggleRequest(t.id)}>
|
|
970
1010
|
<div class="req-summary">
|
|
971
1011
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
972
1012
|
<span class="req-url">${t.url}</span>
|
|
973
1013
|
<bk-status-pill .code=${t.statusCode}></bk-status-pill>
|
|
974
1014
|
<bk-duration-label .ms=${t.durationMs}></bk-duration-label>
|
|
975
|
-
<span class="req-size">${
|
|
1015
|
+
<span class="req-size">${ot(t.responseSize)}</span>
|
|
976
1016
|
</div>
|
|
977
1017
|
</div>
|
|
978
1018
|
<div class="req-detail ${e?"open":""}">${e?this.renderDetail(t):p}</div>
|
|
979
|
-
`}render(){let t=this.store.state.requests.filter(e=>!e.path?.startsWith(
|
|
1019
|
+
`}render(){let t=this.store.state.requests.filter(e=>!e.path?.startsWith(U));return t.length===0?l`<bk-empty-state title="No requests" subtitle="No HTTP requests have been captured yet"></bk-empty-state>`:l`
|
|
980
1020
|
<div class="col-header">
|
|
981
1021
|
<span style="width:60px">Method</span>
|
|
982
1022
|
<span style="flex:1">URL</span>
|
|
@@ -985,53 +1025,53 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
985
1025
|
<span style="width:60px;text-align:right">Size</span>
|
|
986
1026
|
</div>
|
|
987
1027
|
<div id="request-list">${t.map(e=>this.renderRequestRow(e))}</div>
|
|
988
|
-
`}};u([I({context:A})],
|
|
1028
|
+
`}};u([I({context:A})],Mt.prototype,"store",2),u([E()],Mt.prototype,"expandedId",2),Mt=u([$("bk-requests-view")],Mt);var te=class extends b{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}buildGroups(r,t){let e=new Map;for(let i of t)e.set(i.id,i);let s={};for(let i of r){let n=i.method+" "+i.url;s[n]||(s[n]={method:i.method,url:i.url,count:0,totalDur:0,maxDur:0,errors:0,callers:{},statusCodes:{},firstTs:i.timestamp,lastTs:i.timestamp});let a=s[n];if(a.count++,a.totalDur+=i.durationMs,i.durationMs>a.maxDur&&(a.maxDur=i.durationMs),i.statusCode>=400&&a.errors++,a.statusCodes[i.statusCode]=(a.statusCodes[i.statusCode]||0)+1,i.timestamp<a.firstTs&&(a.firstTs=i.timestamp),i.timestamp>a.lastTs&&(a.lastTs=i.timestamp),i.parentRequestId){let c=e.get(i.parentRequestId);c&&(a.callers[c.method+" "+(c.path||c.url)]=1);}}return Object.values(s).sort((i,n)=>n.count-i.count)}renderSummary(r){let t=new Set,e=0,s=0;for(let n of r)t.add(n.url),n.statusCode>=400&&e++,s+=n.durationMs;let i=Math.round(s/r.length);return l`
|
|
989
1029
|
<div class="fetch-summary">
|
|
990
|
-
<bk-stat-card value=${String(
|
|
1030
|
+
<bk-stat-card value=${String(r.length)} label="Total Fetches"></bk-stat-card>
|
|
991
1031
|
<bk-stat-card value=${String(t.size)} label="Unique URLs"></bk-stat-card>
|
|
992
1032
|
<bk-stat-card value=${String(e)} label="Errors" color=${e>0?"var(--red)":""}></bk-stat-card>
|
|
993
|
-
<bk-stat-card value=${
|
|
1033
|
+
<bk-stat-card value=${y(i)} label="Avg Duration"></bk-stat-card>
|
|
994
1034
|
</div>
|
|
995
|
-
`}formatTime(
|
|
1035
|
+
`}formatTime(r){return new Date(r).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}renderGroup(r){let t=Math.round(r.totalDur/r.count),e=r.count>0?Math.round(r.errors/r.count*100):0,s=Object.keys(r.callers),i=Object.entries(r.statusCodes),n=i.length>0?Number(i.sort((a,c)=>c[1]-a[1])[0][0]):0;return l`
|
|
996
1036
|
<div class="fetch-group">
|
|
997
1037
|
<div class="fetch-group-header">
|
|
998
|
-
<bk-method-badge .method=${
|
|
999
|
-
<span class="fetch-group-url" title=${
|
|
1000
|
-
${n>0?
|
|
1001
|
-
<span class="fetch-group-count">${
|
|
1038
|
+
<bk-method-badge .method=${r.method}></bk-method-badge>
|
|
1039
|
+
<span class="fetch-group-url" title=${r.url}>${r.url}</span>
|
|
1040
|
+
${n>0?l`<bk-status-pill .code=${n}></bk-status-pill>`:p}
|
|
1041
|
+
<span class="fetch-group-count">${r.count}x</span>
|
|
1002
1042
|
</div>
|
|
1003
1043
|
<div class="fetch-group-meta">
|
|
1004
|
-
<span>avg ${
|
|
1044
|
+
<span>avg ${y(t)}</span>
|
|
1005
1045
|
<span class="fetch-group-sep">\u00b7</span>
|
|
1006
|
-
<span>max ${
|
|
1046
|
+
<span>max ${y(r.maxDur)}</span>
|
|
1007
1047
|
<span class="fetch-group-sep">\u00b7</span>
|
|
1008
|
-
${e>0?
|
|
1048
|
+
${e>0?l`<span class="fetch-group-err">${e}% errors</span>`:l`<span class="fetch-group-ok">0% errors</span>`}
|
|
1009
1049
|
</div>
|
|
1010
|
-
${
|
|
1050
|
+
${r.firstTs>0?l`
|
|
1011
1051
|
<div class="fetch-group-timeline">
|
|
1012
1052
|
<span class="fetch-group-timeline-dot"></span>
|
|
1013
1053
|
<span class="fetch-group-timeline-range">
|
|
1014
|
-
${this.formatTime(
|
|
1054
|
+
${this.formatTime(r.firstTs)}${r.firstTs!==r.lastTs?l` \u2192 ${this.formatTime(r.lastTs)}`:p}
|
|
1015
1055
|
</span>
|
|
1016
1056
|
</div>`:p}
|
|
1017
|
-
${
|
|
1057
|
+
${s.length>0?l`
|
|
1018
1058
|
<div class="fetch-group-callers">
|
|
1019
1059
|
<span class="fetch-group-callers-label">Called by</span>
|
|
1020
|
-
${
|
|
1060
|
+
${s.map(a=>l`<span class="fetch-group-caller-pill">${a}</span>`)}
|
|
1021
1061
|
</div>`:p}
|
|
1022
1062
|
</div>
|
|
1023
|
-
`}render(){let
|
|
1063
|
+
`}render(){let r=this.store.state.fetches,t=this.store.state.requests;if(r.length===0)return l`<bk-empty-state title="No fetches" subtitle="No outbound HTTP calls have been captured yet"></bk-empty-state>`;let e=this.buildGroups(r,t);return l`
|
|
1024
1064
|
<div class="fetch-analysis" id="fetch-analysis">
|
|
1025
|
-
${this.renderSummary(
|
|
1026
|
-
${e.length>0?
|
|
1065
|
+
${this.renderSummary(r)}
|
|
1066
|
+
${e.length>0?l`
|
|
1027
1067
|
<div class="fetch-groups-title">Grouped by URL (${e.length})</div>
|
|
1028
|
-
<div class="fetch-groups">${e.map(
|
|
1068
|
+
<div class="fetch-groups">${e.map(s=>this.renderGroup(s))}</div>
|
|
1029
1069
|
`:p}
|
|
1030
1070
|
</div>
|
|
1031
|
-
`}};u([I({context:A})],
|
|
1071
|
+
`}};u([I({context:A})],te.prototype,"store",2),te=u([$("bk-fetches-view")],te);function tr(o,r){if(r>=400)return "var(--red)";switch(o){case "GET":return "var(--green)";case "POST":return "var(--blue)";case "PUT":case "PATCH":return "var(--amber)";case "DELETE":return "var(--red)";default:return "var(--text-muted)"}}function ts(o){return o==="query"?"var(--accent)":"var(--cyan)"}function li(o){return o.type==="query"||o.type==="fetch"}function ci(o){let r=(o.normalizedOp||o.operation||"QUERY").toUpperCase(),t=o.table||o.model||"";return {label:`${r} ${t}`,tooltip:o.sql||`${r} ${t}`}}function di(o){return {label:`${o.method} ${o.url}`,tooltip:`${o.method} ${o.url}`}}function pi(o,r,t,e,s,i){let n=o.data.durationMs||0,a,c;if(i){let d=Math.max(o.timestamp-e,0);a=Math.min(d/s*100,95),c=Math.max(n/s*100,1.5);}else {let d=t[0].timestamp,m=t[t.length-1].timestamp-d;a=m>0?(o.timestamp-d)/m*85:r/Math.max(t.length-1,1)*85,c=Math.max(n/s*100,1.5);}return a+c>100&&(c=Math.max(100-a,1.5)),{leftPct:a,widthPct:c}}function hi(o,r){let t=o.timeline.filter(li);if(t.length===0)return [];let e=r.startedAt,s=r.durationMs||1,i=Math.abs(t[0].timestamp-e)<s*10;return t.map((n,a)=>{let c=n.data.durationMs||0,{leftPct:d,widthPct:h}=pi(n,a,t,e,s,i),m=n.type==="query"?ci(n.data):di(n.data);return {type:n.type,label:m.label,durMs:c,durLabel:y(c),tooltip:m.tooltip,leftPct:d,widthPct:h}})}function sr(o,r){let t=o.requests.filter(a=>!a.isStrictModeDupe);if(t.length===0)return {rows:[],totalMs:0};let e=Math.min(...t.map(a=>a.startedAt)),i=Math.max(...t.map(a=>a.startedAt+a.durationMs))-e;return i===0?{rows:[],totalMs:0}:{rows:t.map(a=>{let c=(a.startedAt-e)/i*100,d=Math.max(a.durationMs/i*100,.5),h=r?.activities?.[a.id],m=h?hi(h,a):[];return {label:`${a.method} ${a.label}`,leftPct:c,widthPct:d,color:tr(a.method,a.statusCode),durMs:a.durationMs,durLabel:y(a.durationMs),tooltip:`${a.method} ${a.label} (${y(a.durationMs)})`,subEvents:m}}),totalMs:i}}function rr(o){let r=o.requests,t=[],e=[],s=[],i=[],n=new Map;for(let d of r){let h=d.label,m=d.pollingDurationMs||d.durationMs;if(!Yt[d.category||""]){if(d.isDuplicate){let f=n.get(h);f?(f.count++,f.wastedMs+=m):n.set(h,{name:h,count:2,wastedMs:m});continue}if(d.statusCode>=400){e.push(h+" ("+Vs(d.statusCode)+")");continue}d.responseSize>51200&&s.push("Large response: "+h+" returned "+ot(d.responseSize)),t.push(h);}}for(let d of n.values())i.push(d);let a="";if(i.length>0){let d=i.map(m=>m.name).join(", "),h=i.reduce((m,f)=>m+f.wastedMs,0);a="Your app fetches "+d+" multiple times on this page. This wastes ~"+y(h)+". Try caching these calls, deduplicating with React Query/SWR, or moving them to a shared layout.";}else e.length>0&&(a="Some requests are failing. Check your API routes and make sure the endpoints exist.");let c=r.filter(d=>d.durationMs>2e3&&d.category!==Et);return c.length>0&&!a&&(a=c.map(d=>d.label).join(", ")+` is taking over ${y(2e3)}. Consider adding caching or optimizing the backend query.`),{successes:t,errors:e,warnings:s,duplicates:i,tip:a}}var V=class extends b{constructor(){super(...arguments);this.expandedFlowIdx=-1;this.expandedSubReqIdx=-1;this.flowDetailTab="insights";this.flowTimeline=null;this.flowTimelineLoading=false;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}get flows(){return this.store.state.flows}get viewMode(){return this.store.state.viewMode}flowDotClass(t){return t.hasErrors?"dot-error":t.redundancyPct>0?"dot-warn":"dot-clean"}flowBadgeInfo(t){if(t.hasErrors){let e=t.requests.filter(s=>s.statusCode>=400).length;return {text:e+" error"+(e!==1?"s":""),cls:"badge-error"}}return t.redundancyPct>0?{text:t.redundancyPct+"% redundant",cls:"badge-warn"}:{text:"clean",cls:"badge-clean"}}toggleFlow(t){this.expandedFlowIdx===t?this.expandedFlowIdx=-1:(this.expandedFlowIdx=t,this.expandedSubReqIdx=-1,this.flowDetailTab="insights",this.flowTimeline=null);}toggleSubReq(t,e){e.stopPropagation(),this.expandedSubReqIdx=this.expandedSubReqIdx===t?-1:t;}toggleBodyBlock(t){t.stopPropagation();let e=t.currentTarget,s=e.parentElement;if(!s)return;e.classList.toggle("open");let i=s.querySelector("pre");i&&i.classList.toggle("open");}switchTab(t,e,s){s.stopPropagation(),this.flowDetailTab=t,t==="timeline"&&!this.flowTimeline&&this.loadFlowTimeline(e);}async loadFlowTimeline(t){if(this.flowTimelineLoading)return;let e=t.requests.map(s=>s.id).filter(Boolean);if(e.length!==0){this.flowTimelineLoading=true;try{let s=await fetch(`${T.activity}?requestIds=${e.join(",")}`);if(!s.ok){this.flowTimelineLoading=!1;return}this.flowTimeline=await s.json();}catch{}this.flowTimelineLoading=false;}}loadTimelineForContainer(t){let e=t.querySelectorAll(".request-timeline");for(let s of e){let i=s.getAttribute("data-request-id");if(i&&!s.hasAttribute("data-loaded")){s.setAttribute("data-loaded","1");let n=document.createElement("bk-timeline-panel");n.setAttribute("request-id",i),n.setAttribute("request-started",s.getAttribute("data-request-started")||"0"),s.appendChild(n),s.classList.remove("tl-hidden");}}}updated(){if(this.expandedFlowIdx>=0&&this.flowDetailTab==="insights"){let t=this.querySelector(".flow-expand.open");t&&this.loadTimelineForContainer(t);}}render(){let t=this.flows;return t.length===0?l`<bk-empty-state
|
|
1032
1072
|
title="No actions yet"
|
|
1033
1073
|
subtitle="Start using your app to see user action flows here"
|
|
1034
|
-
></bk-empty-state>`:
|
|
1074
|
+
></bk-empty-state>`:l`
|
|
1035
1075
|
<div id="flow-col-header" class="col-header">
|
|
1036
1076
|
<span style="width:8px"></span>
|
|
1037
1077
|
<span style="flex:1">Action</span>
|
|
@@ -1040,11 +1080,11 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1040
1080
|
<span style="width:70px;text-align:right">Time</span>
|
|
1041
1081
|
</div>
|
|
1042
1082
|
<div id="flow-list">
|
|
1043
|
-
${t.map((e,
|
|
1083
|
+
${t.map((e,s)=>this.renderFlowRow(e,s))}
|
|
1044
1084
|
</div>
|
|
1045
|
-
`}renderFlowRow(t,e){let
|
|
1085
|
+
`}renderFlowRow(t,e){let s=this.expandedFlowIdx===e,i=this.flowDotClass(t),n=this.flowBadgeInfo(t);return l`
|
|
1046
1086
|
<div
|
|
1047
|
-
class="flow-row ${
|
|
1087
|
+
class="flow-row ${s?"expanded":""}"
|
|
1048
1088
|
@click=${()=>this.toggleFlow(e)}
|
|
1049
1089
|
>
|
|
1050
1090
|
<div class="flow-summary-row">
|
|
@@ -1056,39 +1096,39 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1056
1096
|
>
|
|
1057
1097
|
<span class="flow-badge-pill ${n.cls}">${n.text}</span>
|
|
1058
1098
|
<span class="flow-duration"
|
|
1059
|
-
>${
|
|
1099
|
+
>${y(t.totalDurationMs)}</span
|
|
1060
1100
|
>
|
|
1061
1101
|
</div>
|
|
1062
1102
|
</div>
|
|
1063
|
-
<div class="flow-expand ${
|
|
1064
|
-
${
|
|
1103
|
+
<div class="flow-expand ${s?"open":""}">
|
|
1104
|
+
${s?this.renderFlowDetail(t):p}
|
|
1065
1105
|
</div>
|
|
1066
|
-
`}renderFlowDetail(t){let e=this.viewMode==="simple"?"Insights":"Details";return
|
|
1106
|
+
`}renderFlowDetail(t){let e=this.viewMode==="simple"?"Insights":"Details";return l`
|
|
1067
1107
|
<div class="flow-detail-tabs">
|
|
1068
1108
|
<button
|
|
1069
1109
|
class="flow-tab ${this.flowDetailTab==="insights"?"active":""}"
|
|
1070
|
-
@click=${
|
|
1110
|
+
@click=${s=>this.switchTab("insights",t,s)}
|
|
1071
1111
|
>
|
|
1072
1112
|
${e}
|
|
1073
1113
|
</button>
|
|
1074
1114
|
<button
|
|
1075
1115
|
class="flow-tab ${this.flowDetailTab==="timeline"?"active":""}"
|
|
1076
|
-
@click=${
|
|
1116
|
+
@click=${s=>this.switchTab("timeline",t,s)}
|
|
1077
1117
|
>
|
|
1078
1118
|
Timeline
|
|
1079
1119
|
</button>
|
|
1080
1120
|
</div>
|
|
1081
1121
|
${this.flowDetailTab==="insights"?this.viewMode==="simple"?this.renderFlowInsights(t):this.renderFlowSubReqs(t):this.renderFlowWaterfall(t)}
|
|
1082
|
-
`}renderFlowWaterfall(t){if(this.flowTimelineLoading)return
|
|
1122
|
+
`}renderFlowWaterfall(t){if(this.flowTimelineLoading)return l`<div class="wf-loading">Loading timeline...</div>`;let{rows:e,totalMs:s}=sr(t,this.flowTimeline);if(e.length===0)return p;let i=[];for(let n=0;n<=5;n++)i.push(y(s/5*n));return l`
|
|
1083
1123
|
<div class="flow-waterfall">
|
|
1084
1124
|
<div class="wf-time-axis">
|
|
1085
|
-
${i.map(n=>
|
|
1125
|
+
${i.map(n=>l`<span>${n}</span>`)}
|
|
1086
1126
|
</div>
|
|
1087
1127
|
<div class="wf-rows">
|
|
1088
1128
|
${e.map(n=>this.renderWaterfallGroup(n))}
|
|
1089
1129
|
</div>
|
|
1090
1130
|
</div>
|
|
1091
|
-
`}renderWaterfallGroup(t){return
|
|
1131
|
+
`}renderWaterfallGroup(t){return l`
|
|
1092
1132
|
<div class="wf-request-group">
|
|
1093
1133
|
<div class="wf-req-row" title="${t.tooltip}">
|
|
1094
1134
|
<div class="wf-req-label">${t.label}</div>
|
|
@@ -1100,48 +1140,48 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1100
1140
|
</div>
|
|
1101
1141
|
<div class="wf-req-dur">${t.durLabel}</div>
|
|
1102
1142
|
</div>
|
|
1103
|
-
${t.subEvents.length>0?t.subEvents.map(e=>
|
|
1143
|
+
${t.subEvents.length>0?t.subEvents.map(e=>l`
|
|
1104
1144
|
<div class="wf-sub-row" title="${e.tooltip}">
|
|
1105
1145
|
<div class="wf-sub-label">
|
|
1106
1146
|
<span
|
|
1107
1147
|
class="wf-sub-dot"
|
|
1108
|
-
style="background:${
|
|
1148
|
+
style="background:${ts(e.type)}"
|
|
1109
1149
|
></span>
|
|
1110
1150
|
${e.label}
|
|
1111
1151
|
</div>
|
|
1112
1152
|
<div class="wf-bar-track">
|
|
1113
1153
|
<div
|
|
1114
1154
|
class="wf-bar wf-sub-bar-sized"
|
|
1115
|
-
style="left:${t.leftPct+e.leftPct/100*t.widthPct}%;width:${e.widthPct/100*t.widthPct}%;background:${
|
|
1155
|
+
style="left:${t.leftPct+e.leftPct/100*t.widthPct}%;width:${e.widthPct/100*t.widthPct}%;background:${ts(e.type)}"
|
|
1116
1156
|
></div>
|
|
1117
1157
|
</div>
|
|
1118
1158
|
<div class="wf-sub-dur">${e.durLabel}</div>
|
|
1119
1159
|
</div>
|
|
1120
1160
|
`):p}
|
|
1121
1161
|
</div>
|
|
1122
|
-
`}renderFlowInsights(t){let e=
|
|
1162
|
+
`}renderFlowInsights(t){let e=rr(t),s=e.errors.length>0||e.duplicates.length>0||e.warnings.length>0||!!e.tip;return l`
|
|
1123
1163
|
<div>
|
|
1124
1164
|
<div class="flow-traffic">
|
|
1125
1165
|
${t.requests.map(i=>this.renderTrafficCard(i))}
|
|
1126
1166
|
</div>
|
|
1127
|
-
${
|
|
1167
|
+
${s?l`
|
|
1128
1168
|
<div class="flow-divider"></div>
|
|
1129
1169
|
<div class="flow-insights">
|
|
1130
|
-
${e.errors.map(i=>
|
|
1170
|
+
${e.errors.map(i=>l`<div class="insight-line insight-error">
|
|
1131
1171
|
✗ ${i}
|
|
1132
1172
|
</div>`)}
|
|
1133
|
-
${e.duplicates.map(i=>
|
|
1173
|
+
${e.duplicates.map(i=>l`<div class="insight-line insight-warn">
|
|
1134
1174
|
⚠ ${i.name} — loaded ${i.count}x (wasting
|
|
1135
|
-
~${
|
|
1175
|
+
~${y(i.wastedMs)})
|
|
1136
1176
|
</div>`)}
|
|
1137
|
-
${e.warnings.map(i=>
|
|
1138
|
-
${e.tip?
|
|
1177
|
+
${e.warnings.map(i=>l`<div class="insight-line insight-warn">⚠ ${i}</div>`)}
|
|
1178
|
+
${e.tip?l`<div class="insight-line insight-tip">
|
|
1139
1179
|
Tip: ${e.tip}
|
|
1140
1180
|
</div>`:p}
|
|
1141
1181
|
</div>
|
|
1142
1182
|
`:p}
|
|
1143
1183
|
</div>
|
|
1144
|
-
`}renderTrafficCard(t){if(
|
|
1184
|
+
`}renderTrafficCard(t){if(Yt[t.category||""])return p;let e=St(t.statusCode),s=y(t.pollingDurationMs||t.durationMs),i=!t.isDuplicate&&t.category!==pe&&t.category!==Et||t.requestBody&&t.method!=="GET"||!!t.responseBody;return l`
|
|
1145
1185
|
<div
|
|
1146
1186
|
class="traffic-card ${t.isStrictModeDupe?"strict-mode-dupe":""}"
|
|
1147
1187
|
>
|
|
@@ -1151,15 +1191,15 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1151
1191
|
>${t.label}</span
|
|
1152
1192
|
>
|
|
1153
1193
|
<span class="status-pill ${e}">${t.statusCode}</span>
|
|
1154
|
-
<span class="traffic-card-dur">${
|
|
1155
|
-
${t.isDuplicate?
|
|
1156
|
-
>${
|
|
1194
|
+
<span class="traffic-card-dur">${s}</span>
|
|
1195
|
+
${t.isDuplicate?l`<span class="traffic-card-dup">duplicate</span>`:l`<span class="traffic-card-size"
|
|
1196
|
+
>${ot(t.responseSize)}</span
|
|
1157
1197
|
>`}
|
|
1158
1198
|
</div>
|
|
1159
|
-
${t.isStrictModeDupe?
|
|
1199
|
+
${t.isStrictModeDupe?l`<div class="strict-mode-banner">
|
|
1160
1200
|
React Strict Mode duplicate — does not happen in production
|
|
1161
1201
|
</div>`:p}
|
|
1162
|
-
${!t.isDuplicate&&t.category!==
|
|
1202
|
+
${!t.isDuplicate&&t.category!==pe&&t.category!==Et?l`<div
|
|
1163
1203
|
class="request-timeline tl-hidden"
|
|
1164
1204
|
data-request-id=${t.id}
|
|
1165
1205
|
data-request-started=${String(t.startedAt)}
|
|
@@ -1167,42 +1207,42 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1167
1207
|
${t.requestBody&&t.method!=="GET"?this.renderBodyToggle("out","Request Body",t.requestBody):p}
|
|
1168
1208
|
${t.responseBody?this.renderBodyToggle("in","Response Body",t.responseBody):p}
|
|
1169
1209
|
</div>
|
|
1170
|
-
`}renderBodyToggle(t,e,
|
|
1210
|
+
`}renderBodyToggle(t,e,s){let i=t==="out"?"\u2192":"\u2190";return l`
|
|
1171
1211
|
<div class="traffic-body">
|
|
1172
1212
|
<button class="traffic-body-toggle" @click=${this.toggleBodyBlock}>
|
|
1173
1213
|
<span class="chevron">▸</span
|
|
1174
1214
|
><span class="arrow-${t}">${i}</span> ${e}
|
|
1175
1215
|
</button>
|
|
1176
|
-
<pre>${
|
|
1216
|
+
<pre>${G(dt(s))}</pre>
|
|
1177
1217
|
</div>
|
|
1178
|
-
`}renderFlowSubReqs(t){return
|
|
1179
|
-
${t.requests.map((e,
|
|
1180
|
-
</div>`}renderSubReqRow(t,e){let
|
|
1218
|
+
`}renderFlowSubReqs(t){return l`<div class="flow-subreqs">
|
|
1219
|
+
${t.requests.map((e,s)=>this.renderSubReqRow(e,s))}
|
|
1220
|
+
</div>`}renderSubReqRow(t,e){let s=this.expandedSubReqIdx===e,i=St(t.statusCode),n=t.pollingDurationMs?y(t.pollingDurationMs):y(t.durationMs);return l`
|
|
1181
1221
|
<div
|
|
1182
|
-
class="flow-subreq ${
|
|
1183
|
-
@click=${
|
|
1222
|
+
class="flow-subreq ${s?"expanded":""}"
|
|
1223
|
+
@click=${a=>this.toggleSubReq(e,a)}
|
|
1184
1224
|
>
|
|
1185
1225
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
1186
1226
|
<span class="subreq-label ${t.isDuplicate?"is-dup":""}"
|
|
1187
1227
|
>${t.path||t.url}</span
|
|
1188
1228
|
>
|
|
1189
|
-
${t.isDuplicate?
|
|
1229
|
+
${t.isDuplicate?l`<span class="subreq-dup-tag">duplicate</span>`:p}
|
|
1190
1230
|
<span class="status-pill ${i}">${t.statusCode}</span>
|
|
1191
1231
|
<span class="subreq-dur">${n}</span>
|
|
1192
1232
|
</div>
|
|
1193
|
-
<div class="flow-subreq-detail ${
|
|
1194
|
-
${
|
|
1233
|
+
<div class="flow-subreq-detail ${s?"open":""}">
|
|
1234
|
+
${s?this.renderSubReqDetail(t):p}
|
|
1195
1235
|
</div>
|
|
1196
|
-
`}renderSubReqDetail(t){let e=
|
|
1236
|
+
`}renderSubReqDetail(t){let e=St(t.statusCode);return l`
|
|
1197
1237
|
<div class="detail-meta">
|
|
1198
1238
|
<span
|
|
1199
|
-
><bk-method-badge .method=${t.method}></bk-method-badge> ${
|
|
1239
|
+
><bk-method-badge .method=${t.method}></bk-method-badge> ${it(t.url)}</span
|
|
1200
1240
|
>
|
|
1201
1241
|
<span
|
|
1202
1242
|
><span class="status-pill ${e}">${t.statusCode}</span></span
|
|
1203
1243
|
>
|
|
1204
1244
|
<span>${t.durationMs}ms</span>
|
|
1205
|
-
${t.responseSize?
|
|
1245
|
+
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:p}
|
|
1206
1246
|
</div>
|
|
1207
1247
|
<div
|
|
1208
1248
|
class="request-timeline tl-hidden"
|
|
@@ -1212,30 +1252,30 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1212
1252
|
<div class="detail-grid">
|
|
1213
1253
|
<div class="detail-section">
|
|
1214
1254
|
<h4>Request Headers</h4>
|
|
1215
|
-
<pre>${
|
|
1255
|
+
<pre>${G(Tt(t.headers))}</pre>
|
|
1216
1256
|
</div>
|
|
1217
1257
|
<div class="detail-section">
|
|
1218
1258
|
<h4>Response Headers</h4>
|
|
1219
|
-
<pre>${
|
|
1259
|
+
<pre>${G(Tt(t.responseHeaders))}</pre>
|
|
1220
1260
|
</div>
|
|
1221
1261
|
<div class="detail-section">
|
|
1222
1262
|
<h4>Request Body</h4>
|
|
1223
|
-
<pre>${
|
|
1263
|
+
<pre>${G(dt(t.requestBody))}</pre>
|
|
1224
1264
|
</div>
|
|
1225
1265
|
<div class="detail-section">
|
|
1226
1266
|
<h4>Response Body</h4>
|
|
1227
|
-
<pre>${
|
|
1267
|
+
<pre>${G(dt(t.responseBody))}</pre>
|
|
1228
1268
|
</div>
|
|
1229
1269
|
</div>
|
|
1230
1270
|
<div class="detail-actions">
|
|
1231
1271
|
<button
|
|
1232
1272
|
class="btn btn-curl"
|
|
1233
|
-
@click=${
|
|
1273
|
+
@click=${s=>{s.stopPropagation(),Lt(t);}}
|
|
1234
1274
|
>
|
|
1235
1275
|
Copy cURL
|
|
1236
1276
|
</button>
|
|
1237
1277
|
</div>
|
|
1238
|
-
`}};u([I({context:A})],
|
|
1278
|
+
`}};u([I({context:A})],V.prototype,"store",2),u([E()],V.prototype,"expandedFlowIdx",2),u([E()],V.prototype,"expandedSubReqIdx",2),u([E()],V.prototype,"flowDetailTab",2),u([E()],V.prototype,"flowTimeline",2),u([E()],V.prototype,"flowTimelineLoading",2),V=u([$("bk-flows-view")],V);var ee=class extends b{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}render(){let r=(this.store.state.issues||[]).slice(),t=r.filter(a=>a.state==="open"||a.state==="fixing"||a.state==="regressed"),e=r.filter(a=>a.state==="resolved");if(t.length===0&&e.length===0)return this.store.state.requests.length>0||this.store.state.logs.length>0||this.store.state.queries.length>0?l`
|
|
1239
1279
|
<div class="sec-clear">
|
|
1240
1280
|
<span class="sec-clear-icon">\u2713</span>
|
|
1241
1281
|
<div class="sec-clear-text">
|
|
@@ -1243,10 +1283,10 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1243
1283
|
<div class="sec-clear-sub">No security or quality issues detected this session</div>
|
|
1244
1284
|
</div>
|
|
1245
1285
|
</div>
|
|
1246
|
-
`:
|
|
1286
|
+
`:l`<bk-empty-state title="Waiting for requests..." subtitle="Start using your app to see security findings here"></bk-empty-state>`;let s=0,i=0,n=0;for(let a of t){let c=a.issue.severity;c==="critical"?s++:c==="info"?n++:i++;}return l`
|
|
1247
1287
|
<div id="security-content">
|
|
1248
|
-
${this.renderSummary(t.length,e.length,
|
|
1249
|
-
${t.length===0&&e.length>0?
|
|
1288
|
+
${this.renderSummary(t.length,e.length,s,i,n)}
|
|
1289
|
+
${t.length===0&&e.length>0?l`
|
|
1250
1290
|
<div class="sec-clear">
|
|
1251
1291
|
<span class="sec-clear-icon">\u2713</span>
|
|
1252
1292
|
<div class="sec-clear-text">
|
|
@@ -1258,148 +1298,110 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1258
1298
|
${t.length>0?this.renderOpenGroups(t):p}
|
|
1259
1299
|
${e.length>0?this.renderResolved(e):p}
|
|
1260
1300
|
</div>
|
|
1261
|
-
`}renderSummary(
|
|
1301
|
+
`}renderSummary(r,t,e,s,i){return l`
|
|
1262
1302
|
<div class="sec-summary">
|
|
1263
1303
|
<div class="sec-summary-left">
|
|
1264
|
-
<span class="sec-summary-count">${
|
|
1265
|
-
<span class="sec-summary-label">open issue${
|
|
1266
|
-
${t>0?
|
|
1304
|
+
<span class="sec-summary-count">${r}</span>
|
|
1305
|
+
<span class="sec-summary-label">open issue${r!==1?"s":""}</span>
|
|
1306
|
+
${t>0?l`<span class="sec-resolved-badge">${t} resolved</span>`:p}
|
|
1267
1307
|
</div>
|
|
1268
1308
|
<div class="sec-summary-right">
|
|
1269
|
-
${e>0?
|
|
1270
|
-
${
|
|
1271
|
-
${i>0?
|
|
1309
|
+
${e>0?l`<span class="sec-badge critical">${e} critical</span>`:p}
|
|
1310
|
+
${s>0?l`<span class="sec-badge warning">${s} warning</span>`:p}
|
|
1311
|
+
${i>0?l`<span class="sec-badge info">${i} info</span>`:p}
|
|
1272
1312
|
</div>
|
|
1273
1313
|
</div>
|
|
1274
|
-
`}renderOpenGroups(
|
|
1314
|
+
`}renderOpenGroups(r){let t={},e=[];for(let s of r){let i=s.issue,n=i.rule||i.type;t[n]||(t[n]={rule:n,title:i.title,severity:i.severity,hint:i.hint,items:[]},e.push(n)),t[n].items.push(s);}return e.sort((s,i)=>{let n=Y[t[s].severity]?.sort??2,a=Y[t[i].severity]?.sort??2;return n!==a?n-a:t[i].items.length-t[s].items.length}),l`${e.map(s=>this.renderGroup(t[s]))}`}renderGroup(r){let t=Y[r.severity]||Y.info;return l`
|
|
1275
1315
|
<div class="sec-group">
|
|
1276
1316
|
<div class="sec-group-header">
|
|
1277
1317
|
<span class="sec-group-icon ${t.cls}">${t.icon}</span>
|
|
1278
|
-
<span class="sec-group-title">${
|
|
1279
|
-
<span class="sec-group-count">${
|
|
1318
|
+
<span class="sec-group-title">${r.title}</span>
|
|
1319
|
+
<span class="sec-group-count">${r.items.length}</span>
|
|
1280
1320
|
</div>
|
|
1281
|
-
${
|
|
1282
|
-
<div class="sec-items">${
|
|
1321
|
+
${r.hint?l`<div class="sec-hint">${r.hint}</div>`:p}
|
|
1322
|
+
<div class="sec-items">${r.items.map(e=>this.renderIssueItem(e))}</div>
|
|
1283
1323
|
</div>
|
|
1284
|
-
`}renderIssueItem(
|
|
1324
|
+
`}renderIssueItem(r){let t=r.issue;return l`
|
|
1285
1325
|
<div class="sec-item">
|
|
1286
1326
|
<div class="sec-item-desc">${t.desc}</div>
|
|
1287
|
-
${
|
|
1288
|
-
${
|
|
1289
|
-
${
|
|
1327
|
+
${r.occurrences>1?l`<span class="sec-item-count">${r.occurrences}x</span>`:p}
|
|
1328
|
+
${r.state==="fixing"&&r.aiStatus==="fixed"?l`<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>`:r.aiStatus==="wont_fix"?l`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:r.state==="regressed"?l`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:p}
|
|
1329
|
+
${r.aiNotes?l`<div class="sec-ai-notes">${r.aiNotes}</div>`:p}
|
|
1290
1330
|
</div>
|
|
1291
|
-
`}renderResolved(
|
|
1331
|
+
`}renderResolved(r){return l`
|
|
1292
1332
|
<div class="sec-resolved-title">
|
|
1293
1333
|
<span class="sec-resolved-check">\u2713</span> Resolved
|
|
1294
|
-
<span class="sec-resolved-count">${
|
|
1334
|
+
<span class="sec-resolved-count">${r.length}</span>
|
|
1295
1335
|
</div>
|
|
1296
1336
|
<div class="sec-group sec-group-resolved">
|
|
1297
1337
|
<div class="sec-items">
|
|
1298
|
-
${
|
|
1338
|
+
${r.map(t=>l`
|
|
1299
1339
|
<div class="sec-item sec-item-resolved">
|
|
1300
1340
|
<span class="sec-resolved-item-icon">\u2713</span>
|
|
1301
1341
|
<div class="sec-item-desc">${t.issue.title} \u2014 ${t.issue.endpoint||"global"}</div>
|
|
1302
|
-
${t.aiStatus==="fixed"?
|
|
1303
|
-
${t.aiNotes?
|
|
1342
|
+
${t.aiStatus==="fixed"?l`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:p}
|
|
1343
|
+
${t.aiNotes?l`<div class="sec-ai-notes">${t.aiNotes}</div>`:p}
|
|
1304
1344
|
</div>
|
|
1305
1345
|
`)}
|
|
1306
1346
|
</div>
|
|
1307
1347
|
</div>
|
|
1308
|
-
`}};u([I({context:A})],
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
</div>`:p}
|
|
1317
|
-
${m.length===0&&x.length>0?a`<div class="ov-clear">
|
|
1318
|
-
<span class="ov-clear-icon">\u2713</span>All issues resolved \u2014
|
|
1319
|
-
${x.length} finding${x.length!==1?"s were":" was"} detected and
|
|
1320
|
-
fixed
|
|
1321
|
-
</div>`:p}
|
|
1322
|
-
${m.length>0?this.renderOpenIssues(m):p}
|
|
1323
|
-
${v.length>0?this.renderVerifying(v):p}
|
|
1324
|
-
${x.length>0?this.renderResolvedIssues(x):p}
|
|
1325
|
-
</div>
|
|
1326
|
-
`}renderSummary(t,e,r,i,n,l){return a`
|
|
1327
|
-
<div class="ov-summary">
|
|
1328
|
-
<div class="ov-stat"><span class="ov-stat-value">${t}</span><span class="ov-stat-label">Requests</span></div>
|
|
1329
|
-
<div class="ov-stat"><span class="ov-stat-value">${e}</span><span class="ov-stat-label">Actions</span></div>
|
|
1330
|
-
<div class="ov-stat"><span class="ov-stat-value">${_(r)}</span><span class="ov-stat-label">Avg Response</span></div>
|
|
1331
|
-
<div class="ov-stat"><span class="ov-stat-value">${i}</span><span class="ov-stat-label">Queries</span></div>
|
|
1332
|
-
<div class="ov-stat"><span class="ov-stat-value" style="color:${n>0?"var(--red)":"var(--green)"}">${n}</span><span class="ov-stat-label">Errors</span></div>
|
|
1333
|
-
<div class="ov-stat"><span class="ov-stat-value">${l}</span><span class="ov-stat-label">Fetches</span></div>
|
|
1334
|
-
</div>
|
|
1335
|
-
`}renderOpenIssues(t){return a`
|
|
1336
|
-
<div class="ov-section-title">Issues Found <span class="ov-issue-count">${t.length}</span></div>
|
|
1337
|
-
<div class="ov-cards">${t.map((e,r)=>this.renderIssueCard(e,r))}</div>
|
|
1338
|
-
`}renderIssueCard(t,e){let r=t.issue,i=Z[r.severity]||Z.info,n=this.expandedCardIdx===e,l=t.aiStatus==="wont_fix"?a`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:t.state==="regressed"?a`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:p,c=t.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Resolving\u2026 ${t.cleanHitsSinceLastSeen}/${5} clean requests</div>`:p;return a`
|
|
1339
|
-
<div class="ov-card ${n?"expanded":""}" @click=${()=>this.toggleCard(e)}>
|
|
1340
|
-
<span class="ov-card-icon ${i.cls}">${i.icon}</span>
|
|
1341
|
-
<div class="ov-card-body">
|
|
1342
|
-
<div class="ov-card-title">${r.title}${l}</div>
|
|
1343
|
-
<div class="ov-card-desc">${r.desc}</div>
|
|
1344
|
-
${r.detail?a`<div class="ov-card-detail">${r.detail}</div>`:p}
|
|
1345
|
-
${c}
|
|
1346
|
-
${n&&r.hint?a`<div class="ov-card-hint">${r.hint}</div>`:p}
|
|
1348
|
+
`}};u([I({context:A})],ee.prototype,"store",2),ee=u([$("bk-security-view")],ee);var J=class extends b{constructor(){super(...arguments);this.handleStateChanged=()=>this.requestUpdate();}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",this.handleStateChanged);}disconnectedCallback(){super.disconnectedCallback(),this.store.removeEventListener("state-changed",this.handleStateChanged);}};u([I({context:A})],J.prototype,"store",2);async function Q(o){let r=await fetch(o);if(!r.ok)throw new Error(`HTTP ${r.status}: ${o}`);return r.json()}function B(o,r,t){return o===1?r:t??`${r}s`}function ir(o){return o.state!=="stale"}function $e(o){return (o.state==="open"||o.state==="regressed")&&o.aiStatus!=="wont_fix"}var se=class extends J{constructor(){super(...arguments);this.graphSummary=null;}connectedCallback(){super.connectedCallback(),this.loadGraphSummary();}async loadGraphSummary(){try{let e=(await Q(`${T.graph}?level=endpoints`)).nodes||[];this.graphSummary={endpoints:e.filter(s=>s.type==="endpoint").length,tables:e.filter(s=>s.type==="table").length,externals:e.filter(s=>s.type==="external").length};}catch{}}navigateTo(t){this.store.setActiveView(t);}navigateToExplorerErrors(){this.store.setActiveView("explorer"),window.dispatchEvent(new CustomEvent("navigate-explorer",{detail:"errors"}));}render(){let t=this.store.state,e=t.requests.filter(i=>!i.isStatic&&!i.isHealthCheck&&(!i.path||i.path.indexOf(U)!==0));return e.length>0||t.queries.length>0?l`
|
|
1349
|
+
<div class="ov-container">
|
|
1350
|
+
<div class="ov-grid">
|
|
1351
|
+
${this.renderActionsCard(t.flows)}
|
|
1352
|
+
${this.renderInsightsCard(t.issues)}
|
|
1353
|
+
${this.renderPerformanceCard(e)}
|
|
1354
|
+
${this.renderErrorsCard(e)}
|
|
1355
|
+
${this.renderGraphCard()}
|
|
1347
1356
|
</div>
|
|
1348
|
-
${r.hint?a`<span class="ov-card-arrow">${n?"\u2193":"\u2192"}</span>`:p}
|
|
1349
|
-
</div>
|
|
1350
|
-
`}renderVerifying(t){return a`
|
|
1351
|
-
<div class="ov-section-title ov-resolved-title">
|
|
1352
|
-
<span style="color:var(--yellow,#f5a623)">\u29d7</span> Awaiting Verification
|
|
1353
|
-
<span class="ov-issue-count">${t.length}</span>
|
|
1354
|
-
</div>
|
|
1355
|
-
<div class="ov-cards">
|
|
1356
|
-
${t.map(e=>{let r=e.issue,i=e.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Verifying\u2026 ${e.cleanHitsSinceLastSeen}/${5} clean requests</div>`:p;return a`
|
|
1357
|
-
<div class="ov-card ov-card-resolved">
|
|
1358
|
-
<span class="ov-card-icon resolved">\u29d7</span>
|
|
1359
|
-
<div class="ov-card-body">
|
|
1360
|
-
<div class="ov-card-title" style="color:var(--text-muted)">
|
|
1361
|
-
${r.title}
|
|
1362
|
-
<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>
|
|
1363
|
-
</div>
|
|
1364
|
-
<div class="ov-card-desc">${r.desc}</div>
|
|
1365
|
-
${i}
|
|
1366
|
-
</div>
|
|
1367
|
-
</div>
|
|
1368
|
-
`})}
|
|
1369
1357
|
</div>
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
<div class="ov-card
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1358
|
+
`:l`<bk-empty-state
|
|
1359
|
+
title="${O.EMPTY_TITLE}"
|
|
1360
|
+
subtitle="${O.EMPTY_SUBTITLE_OVERVIEW}"
|
|
1361
|
+
></bk-empty-state>`}renderActionsCard(t){let e=t.length;if(e===0)return this.renderEmptyCard("\u25B6","Actions",O.NO_ACTIONS,"actions");let s=Math.round(t.reduce((n,a)=>n+a.totalDurationMs,0)/e),i=t[0]?.label||"";return this.renderCard("\u25B6",`${e} ${B(e,"action")}`,`avg ${y(s)}${i?` \xB7 "${i}"`:""}`,"actions","var(--accent)")}renderInsightsCard(t){let e=(t||[]).filter($e);if(e.length===0)return this.renderCard("\u2713",O.ALL_CLEAR,O.ALL_CLEAR_DETAIL,"insights","var(--green)");let s=e.filter(a=>a.issue.severity==="critical").length,i=e.filter(a=>a.issue.severity==="warning").length,n=[];return s>0&&n.push(`${s} critical`),i>0&&n.push(`${i} warning`),this.renderCard("\u{1F4A1}",`${e.length} ${B(e.length,"issue")}`,n.join(" \xB7 ")||O.REVIEW_RECOMMENDED,"insights","var(--amber)")}renderPerformanceCard(t){if(t.length===0)return this.renderEmptyCard("\u26A1","Performance",O.NO_REQUEST_DATA,"performance");let e=new Map;for(let n of t){let a=`${n.method} ${n.path}`,c=e.get(a);c||(c=[],e.set(a,c)),c.push(n.durationMs);}let s=[];for(let[n,a]of e){let c=Math.round(a.reduce((d,h)=>d+h,0)/a.length);c>2e3&&s.push({key:n,avg:c});}if(s.sort((n,a)=>a.avg-n.avg),s.length===0)return this.renderCard("\u26A1",O.ALL_FAST,O.NO_SLOW_ENDPOINTS,"performance","var(--green)");let i=s[0];return this.renderCard("\u26A1",`${s.length} slow ${B(s.length,"endpoint")}`,`worst: ${i.key} \xB7 ${y(i.avg)}`,"performance","var(--amber)")}renderErrorsCard(t){let e=t.filter(n=>n.statusCode>=400);if(e.length===0)return this.renderCard("\u2713",O.ZERO_ERRORS,O.ALL_REQUESTS_OK,"explorer","var(--green)");let s=new Map;for(let n of e)s.set(n.statusCode,(s.get(n.statusCode)||0)+1);let i=[...s.entries()].sort((n,a)=>a[1]-n[1]).slice(0,3).map(([n,a])=>`${a}\xD7 ${n}`);return l`
|
|
1362
|
+
<div class="ov-card-nav" @click=${()=>this.navigateToExplorerErrors()}>
|
|
1363
|
+
<div class="ov-card-icon-lg" style="color:var(--red)">\u2715</div>
|
|
1364
|
+
<div class="ov-card-content">
|
|
1365
|
+
<div class="ov-card-headline">${e.length} ${B(e.length,"error")}</div>
|
|
1366
|
+
<div class="ov-card-context">${i.join(" \xB7 ")}</div>
|
|
1367
|
+
</div>
|
|
1368
|
+
<span class="ov-card-arrow">\u2192</span>
|
|
1369
|
+
</div>
|
|
1370
|
+
`}renderGraphCard(){let t=this.graphSummary;if(!t||t.endpoints===0&&t.tables===0)return this.renderEmptyCard("\u25CE","Graph",O.BUILD_GRAPH,"graph");let e=[];t.endpoints>0&&e.push(`${t.endpoints} ${B(t.endpoints,"endpoint")}`),t.tables>0&&e.push(`${t.tables} ${B(t.tables,"table")}`);let s=t.externals>0?`${t.externals} external ${B(t.externals,"service")}`:"";return this.renderCard("\u25CE",e.join(" \xB7 "),s,"graph","var(--accent)")}renderCard(t,e,s,i,n){return l`
|
|
1371
|
+
<div class="ov-card-nav" @click=${()=>this.navigateTo(i)}>
|
|
1372
|
+
<div class="ov-card-icon-lg" style="color:${n}">${t}</div>
|
|
1373
|
+
<div class="ov-card-content">
|
|
1374
|
+
<div class="ov-card-headline">${e}</div>
|
|
1375
|
+
${s?l`<div class="ov-card-context">${s}</div>`:p}
|
|
1376
|
+
</div>
|
|
1377
|
+
<span class="ov-card-arrow">\u2192</span>
|
|
1378
|
+
</div>
|
|
1379
|
+
`}renderEmptyCard(t,e,s,i){return l`
|
|
1380
|
+
<div class="ov-card-nav ov-card-empty" @click=${()=>this.navigateTo(i)}>
|
|
1381
|
+
<div class="ov-card-icon-lg">${t}</div>
|
|
1382
|
+
<div class="ov-card-content">
|
|
1383
|
+
<div class="ov-card-headline">${e}</div>
|
|
1384
|
+
<div class="ov-card-context">${s}</div>
|
|
1385
|
+
</div>
|
|
1386
|
+
<span class="ov-card-arrow">\u2192</span>
|
|
1385
1387
|
</div>
|
|
1386
|
-
`}};u([
|
|
1388
|
+
`}};u([E()],se.prototype,"graphSummary",2),se=u([$("bk-overview-view")],se);function or(o){return o<1?"<1ms":o<1e3?Math.round(o)+"ms":(o/1e3).toFixed(1)+"s"}function ui(o){return o<yt?Xt.green:o<_t?Xt.amber:Xt.red}function mi(o){return o.statusCode>=400?Xt.red:ui(o.durationMs)}function nr(o){return [parseInt(o.slice(1,3),16),parseInt(o.slice(3,5),16),parseInt(o.slice(5,7),16)]}function fi(o){let r=o.getContext("2d");if(!r)return null;let t=window.devicePixelRatio||1,e=o.clientWidth,s=o.clientHeight;return o.width=e*t,o.height=s*t,r.scale(t,t),{ctx:r,w:e,h:s}}function vi(o,r,t,e,s){let[i,n,a]=nr(s);o.beginPath(),o.arc(r,t,e+2,0,Math.PI*2),o.fillStyle=`rgba(${i},${n},${a},0.25)`,o.fill(),o.beginPath(),o.arc(r,t,e,0,Math.PI*2),o.fillStyle=s,o.fill();}function gi(o,r,t,e,s,i){let[n,a,c]=nr(s);o.strokeStyle=`rgba(${n},${a},${c},0.3)`,o.lineWidth=i+2,o.beginPath(),o.moveTo(r-e,t-e),o.lineTo(r+e,t+e),o.moveTo(r+e,t-e),o.lineTo(r-e,t+e),o.stroke(),o.strokeStyle=s,o.lineWidth=i,o.beginPath(),o.moveTo(r-e,t-e),o.lineTo(r+e,t+e),o.moveTo(r+e,t-e),o.lineTo(r-e,t+e),o.stroke();}function ar(o,r){let t=[],e=fi(o);if(!e||r.length===0)return t;let{ctx:s,w:i,h:n}=e,a=Gs,c=i-a.left-a.right,d=n-a.top-a.bottom,h=0,m=r[0].timestamp,f=r[0].timestamp;for(let _ of r)_.durationMs>h&&(h=_.durationMs),_.timestamp<m&&(m=_.timestamp),_.timestamp>f&&(f=_.timestamp);h=Math.max(h,10),h=Math.ceil(h*1.15/10)*10;let S=f-m||1;s.strokeStyle=Us,s.lineWidth=1;let R=4;for(let _=0;_<=R;_++){let v=a.top+d-_/R*d;s.beginPath(),s.moveTo(a.left,v),s.lineTo(a.left+c,v),s.stroke(),s.fillStyle=Xe,s.font=Fs,s.textAlign="right",s.fillText(or(Math.round(_/R*h)),a.left-8,v+3);}for(let _ of [{ms:yt},{ms:_t}]){if(_.ms>=h)continue;let v=a.top+d-_.ms/h*d;s.beginPath(),s.setLineDash([4,4]),s.strokeStyle="rgba(113,113,122,0.3)",s.lineWidth=1,s.moveTo(a.left,v),s.lineTo(a.left+c,v),s.stroke(),s.setLineDash([]),s.fillStyle="rgba(113,113,122,0.5)",s.font=Ke,s.textAlign="left",s.fillText(or(_.ms),a.left+c+2,v+3);}for(let _=0;_<r.length;_++){let v=r[_],g=r.length===1?a.left+c/2:a.left+(v.timestamp-m)/S*c,x=a.top+d-v.durationMs/h*d,w=mi(v);t.push({x:g,y:x,idx:_,r:v}),v.statusCode>=400?gi(s,g,x,4,w,2):vi(s,g,x,4,w);}s.fillStyle=Xe,s.font=Ke,s.textAlign="center";let P=[m,m+S/2,f];for(let _=0;_<P.length;_++){let v=a.left+_/2*c,g=new Date(P[_]);s.fillText(g.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),v,a.top+d+14);}return t}var xi={max:1/0,label:"Pending",color:"var(--text-muted)",bg:"var(--bg-muted)",border:"var(--border)"};function lr(o,r,t){return t>=20?o:r}function ss(o,r){if(!r||r<=0)return xi;let t=o/r;return t<.7?xt[0]:t<1.2?xt[1]:t<2?xt[2]:t<3?xt[3]:xt[4]}var X=class extends b{constructor(){super(...arguments);this.selectedEndpoint=rt;this.graphData=[];this.loadError=false;this.queryBreakdown=[];this.queryBreakdownLoading=false;this.scatterDots=[];}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate()),this.loadMetrics();}async loadMetrics(){try{let e=await(await fetch(T.metricsLive)).json();this.graphData=e.endpoints||[],this.loadError=!1,(!this.selectedEndpoint||this.selectedEndpoint===rt)&&(this.selectedEndpoint=rt);}catch{this.loadError=true;}}healthGradeForEndpoint(t){let e=lr(t.summary.p95Ms,t.summary.medianMs,t.summary.totalRequests);return ss(e,t.baselineP95Ms)}healthGradeForDuration(t,e){return ss(t,e)}getCallers(t){let e=this.store.state.flows,s=new Map;for(let i of e)for(let n of i.requests)if(`${n.method} ${n.path}`===t||this.normalizeEndpoint(n)===t){let c=s.get(i.label);c?(c.count++,c.totalMs+=n.durationMs):s.set(i.label,{count:1,totalMs:n.durationMs});}return [...s.entries()].map(([i,n])=>({label:i,count:n.count,avgMs:Math.round(n.totalMs/n.count)})).sort((i,n)=>n.count-i.count)}normalizeEndpoint(t){let s=t.path.split("?")[0].split("/").map(i=>{if(!i)return i;let n=i.length>0;for(let a=0;a<i.length;a++){let c=i.charCodeAt(a);if(c<48||c>57){n=false;break}}return n||i.length===36&&i[8]==="-"&&i[13]==="-"&&i[18]==="-"&&i[23]==="-"?":id":i}).join("/");return `${t.method} ${s}`}async loadQueryBreakdown(t){if(this.queryBreakdownLoading)return;let s=this.store.state.requests.filter(i=>`${i.method} ${i.path}`===t||this.normalizeEndpoint(i)===t).slice(-20).map(i=>i.id).filter(Boolean);if(s.length===0){this.queryBreakdown=[];return}this.queryBreakdownLoading=true;try{let i=await fetch(`${T.activity}?requestIds=${s.join(",")}`);if(!i.ok){this.queryBreakdownLoading=!1;return}let n=await i.json(),a=new Map;for(let c of Object.values(n.activities))for(let d of c.timeline){if(d.type!=="query")continue;let h=d.data,m=(h.normalizedOp||h.operation||"QUERY").toUpperCase(),f=h.table||h.model||"",S=`${m} ${f}`.trim(),R=a.get(S);R?(R.totalMs+=h.durationMs,R.count++):a.set(S,{label:S,totalMs:h.durationMs,count:1});}this.queryBreakdown=[...a.values()].map(c=>({...c,avgMs:Math.round(c.totalMs/c.count)})).sort((c,d)=>d.totalMs-c.totalMs);}catch{}this.queryBreakdownLoading=false;}renderScatterChart(t,e){this.scatterDots=ar(t,e),t.style.cursor="pointer",t.onclick=s=>{let i=t.getBoundingClientRect(),n=s.clientX-i.left,a=s.clientY-i.top,c=null,d=1/0;for(let h of this.scatterDots){let m=Math.sqrt((h.x-n)**2+(h.y-a)**2);m<d&&(d=m,c=h);}c&&d<16&&this.highlightRow(c.idx);};}highlightRow(t){let e=this.querySelector(".perf-hist-row-hl");e&&e.classList.remove("perf-hist-row-hl");let s=this.querySelector(`[data-req-idx="${t}"]`);s&&(s.classList.add("perf-hist-row-hl"),s.scrollIntoView({behavior:"smooth",block:"center"}));}updated(){if(this.selectedEndpoint===rt)return;let t=this.querySelector("#perf-detail-canvas");if(t){let e=this.graphData.find(s=>s.endpoint===this.selectedEndpoint);e&&this.renderScatterChart(t,e.requests);}}render(){return !this.graphData||this.graphData.length===0?l`<bk-empty-state title="No performance data yet" subtitle="Hit some endpoints and data will appear here"></bk-empty-state>`:l`
|
|
1387
1389
|
<div id="graph-content">
|
|
1388
1390
|
${this.renderSelector()}
|
|
1389
|
-
${this.selectedEndpoint===
|
|
1391
|
+
${this.selectedEndpoint===rt?this.renderOverview():this.renderDetail()}
|
|
1390
1392
|
</div>
|
|
1391
|
-
`}renderSelector(){return
|
|
1393
|
+
`}renderSelector(){return l`
|
|
1392
1394
|
<div class="perf-selector">
|
|
1393
|
-
<button class="perf-selector-btn ${this.selectedEndpoint===
|
|
1394
|
-
@click=${()=>{this.selectedEndpoint=
|
|
1395
|
-
${this.graphData.map((t,e)=>
|
|
1395
|
+
<button class="perf-selector-btn ${this.selectedEndpoint===rt?"active":""}"
|
|
1396
|
+
@click=${()=>{this.selectedEndpoint=rt;}}>Overview</button>
|
|
1397
|
+
${this.graphData.map((t,e)=>l`
|
|
1396
1398
|
<button class="perf-selector-btn ${t.endpoint===this.selectedEndpoint?"active":""}"
|
|
1397
1399
|
@click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
1398
|
-
<span class="perf-dot" style="background:${
|
|
1400
|
+
<span class="perf-dot" style="background:${Ve[e%Ve.length]}"></span>${t.endpoint}
|
|
1399
1401
|
</button>
|
|
1400
1402
|
`)}
|
|
1401
1403
|
</div>
|
|
1402
|
-
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return p;let e=t.reduce((c,d)=>c+d.summary.totalRequests,0),
|
|
1404
|
+
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return p;let e=t.reduce((c,d)=>c+d.summary.totalRequests,0),s=e>0?Math.round(t.reduce((c,d)=>c+d.summary.p95Ms*d.summary.totalRequests,0)/e):0,i=t.reduce((c,d)=>c+Math.round(d.summary.errorRate*d.summary.totalRequests),0),n=e>0?i/e:0,a=t[0];return l`
|
|
1403
1405
|
<div class="perf-overview">
|
|
1404
1406
|
<div class="perf-summary-row">
|
|
1405
1407
|
<div class="perf-summary-card">
|
|
@@ -1408,7 +1410,7 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1408
1410
|
</div>
|
|
1409
1411
|
<div class="perf-summary-card">
|
|
1410
1412
|
<span class="perf-summary-label">Avg P95</span>
|
|
1411
|
-
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(
|
|
1413
|
+
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(s).color}">${y(s)}</span>
|
|
1412
1414
|
</div>
|
|
1413
1415
|
<div class="perf-summary-card">
|
|
1414
1416
|
<span class="perf-summary-label">Error Rate</span>
|
|
@@ -1416,7 +1418,7 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1416
1418
|
</div>
|
|
1417
1419
|
<div class="perf-summary-card">
|
|
1418
1420
|
<span class="perf-summary-label">Slowest</span>
|
|
1419
|
-
<span class="perf-summary-value perf-summary-value-sm">${
|
|
1421
|
+
<span class="perf-summary-value perf-summary-value-sm">${a?.endpoint??"-"}</span>
|
|
1420
1422
|
</div>
|
|
1421
1423
|
</div>
|
|
1422
1424
|
|
|
@@ -1436,50 +1438,50 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1436
1438
|
</tbody>
|
|
1437
1439
|
</table>
|
|
1438
1440
|
</div>
|
|
1439
|
-
`}renderHeatmapRow(t){let e=t.summary,
|
|
1441
|
+
`}renderHeatmapRow(t){let e=t.summary,s=this.healthGradeForEndpoint(t),i=Math.round(e.errorRate*e.totalRequests),n=(e.avgQueryTimeMs||0)+(e.avgFetchTimeMs||0)+(e.avgAppTimeMs||0),a=0,c=0,d=100;return n>0&&(a=Math.round((e.avgQueryTimeMs||0)/n*100),c=Math.round((e.avgFetchTimeMs||0)/n*100),d=Math.max(0,100-a-c)),l`
|
|
1440
1442
|
<tr class="perf-table-row" @click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
1441
1443
|
<td class="perf-td-name">${t.endpoint}</td>
|
|
1442
1444
|
<td class="perf-td-right">${e.totalRequests}</td>
|
|
1443
1445
|
<td class="perf-td-center">
|
|
1444
|
-
<span class="perf-hm-p95" style="color:${
|
|
1446
|
+
<span class="perf-hm-p95" style="color:${s.color};background:${s.bg};border-color:${s.border}">${y(e.p95Ms)}</span>
|
|
1445
1447
|
</td>
|
|
1446
1448
|
<td class="perf-td-center" style="color:${i>0?"var(--red)":"var(--text-muted)"}">${i>0?i:"-"}</td>
|
|
1447
1449
|
<td class="perf-td-center" style="color:${e.avgQueryCount>5?"var(--amber)":"var(--text-muted)"}">${e.avgQueryCount}</td>
|
|
1448
1450
|
<td>
|
|
1449
1451
|
<span class="perf-hm-split-bar">
|
|
1450
|
-
${
|
|
1451
|
-
${c>0?
|
|
1452
|
-
${d>0?
|
|
1452
|
+
${a>0?l`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${a}%"></span>`:p}
|
|
1453
|
+
${c>0?l`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:p}
|
|
1454
|
+
${d>0?l`<span class="perf-breakdown-seg perf-breakdown-app" style="width:${d}%"></span>`:p}
|
|
1453
1455
|
</span>
|
|
1454
1456
|
</td>
|
|
1455
1457
|
</tr>
|
|
1456
|
-
`}renderDetail(){let t=this.graphData.find(n=>n.endpoint===this.selectedEndpoint);if(!t?.requests?.length)return
|
|
1457
|
-
${this.renderDetailHeader(t,
|
|
1458
|
-
${this.renderDetailMetrics(e,
|
|
1458
|
+
`}renderDetail(){let t=this.graphData.find(n=>n.endpoint===this.selectedEndpoint);if(!t?.requests?.length)return l`<bk-empty-state subtitle="No data for this endpoint"></bk-empty-state>`;let e=t.summary,s=this.healthGradeForEndpoint(t),i=Math.round(e.errorRate*e.totalRequests);return l`
|
|
1459
|
+
${this.renderDetailHeader(t,s)}
|
|
1460
|
+
${this.renderDetailMetrics(e,s,i)}
|
|
1459
1461
|
${this.renderDetailBreakdown(e)}
|
|
1460
1462
|
${this.renderCallers(t.endpoint)}
|
|
1461
1463
|
${this.renderQueryBreakdown()}
|
|
1462
1464
|
${this.renderTrends(t)}
|
|
1463
1465
|
${this.renderDetailChart()}
|
|
1464
1466
|
${this.renderDetailHistory(t)}
|
|
1465
|
-
`}renderDetailHeader(t,e){return
|
|
1467
|
+
`}renderDetailHeader(t,e){return l`
|
|
1466
1468
|
<div class="perf-detail-header">
|
|
1467
1469
|
<div class="perf-detail-title">
|
|
1468
1470
|
<span class="perf-badge perf-badge-lg" style="color:${e.color};background:${e.bg};border-color:${e.border}">${e.label}</span>
|
|
1469
1471
|
<span>${t.endpoint}</span>
|
|
1470
|
-
${t.baselineP95Ms?
|
|
1472
|
+
${t.baselineP95Ms?l`<span class="perf-baseline-hint">Baseline: ${y(t.baselineP95Ms)}</span>`:p}
|
|
1471
1473
|
</div>
|
|
1472
1474
|
</div>
|
|
1473
|
-
`}renderDetailMetrics(t,e,
|
|
1475
|
+
`}renderDetailMetrics(t,e,s){return l`
|
|
1474
1476
|
<div class="perf-metric-row">
|
|
1475
1477
|
<div class="perf-metric-card">
|
|
1476
1478
|
<span class="perf-metric-label">P95</span>
|
|
1477
|
-
<span class="perf-metric-value" style="color:${e.color}">${
|
|
1479
|
+
<span class="perf-metric-value" style="color:${e.color}">${y(t.p95Ms)}</span>
|
|
1478
1480
|
</div>
|
|
1479
1481
|
<div class="perf-metric-card">
|
|
1480
1482
|
<span class="perf-metric-label">Errors</span>
|
|
1481
|
-
<span class="perf-metric-value" style="color:${
|
|
1482
|
-
${
|
|
1483
|
+
<span class="perf-metric-value" style="color:${s>0?"var(--red)":"var(--green)"}">
|
|
1484
|
+
${s>0?s+" ("+Math.round(t.errorRate*100)+"%)":"0"}
|
|
1483
1485
|
</span>
|
|
1484
1486
|
</div>
|
|
1485
1487
|
<div class="perf-metric-card">
|
|
@@ -1487,71 +1489,71 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1487
1489
|
<span class="perf-metric-value" style="color:${t.avgQueryCount>5?"var(--amber)":"var(--text)"}">${t.avgQueryCount}</span>
|
|
1488
1490
|
</div>
|
|
1489
1491
|
</div>
|
|
1490
|
-
`}renderDetailBreakdown(t){let e=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(e<=0)return p;let
|
|
1492
|
+
`}renderDetailBreakdown(t){let e=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(e<=0)return p;let s=Math.round((t.avgQueryTimeMs||0)/e*100),i=Math.round((t.avgFetchTimeMs||0)/e*100),n=Math.max(0,100-s-i);return l`
|
|
1491
1493
|
<div class="perf-breakdown">
|
|
1492
1494
|
<div class="perf-section-title">Time Breakdown</div>
|
|
1493
1495
|
<div class="perf-breakdown-bar">
|
|
1494
|
-
${
|
|
1495
|
-
${i>0?
|
|
1496
|
-
${n>0?
|
|
1496
|
+
${s>0?l`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${s}%"></div>`:p}
|
|
1497
|
+
${i>0?l`<div class="perf-breakdown-seg perf-breakdown-fetch" style="width:${i}%"></div>`:p}
|
|
1498
|
+
${n>0?l`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:p}
|
|
1497
1499
|
</div>
|
|
1498
1500
|
<div class="perf-breakdown-legend">
|
|
1499
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${
|
|
1500
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${
|
|
1501
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${
|
|
1501
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${y(t.avgQueryTimeMs||0)} (${s}%)</span>
|
|
1502
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${y(t.avgFetchTimeMs||0)} (${i}%)</span>
|
|
1503
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${y(t.avgAppTimeMs||0)} (${n}%)</span>
|
|
1502
1504
|
</div>
|
|
1503
1505
|
</div>
|
|
1504
|
-
`}renderCallers(t){let e=this.getCallers(t);return e.length===0?p:
|
|
1506
|
+
`}renderCallers(t){let e=this.getCallers(t);return e.length===0?p:l`
|
|
1505
1507
|
<div class="perf-callers">
|
|
1506
1508
|
<div class="perf-section-title">Called By</div>
|
|
1507
1509
|
<div class="perf-callers-list">
|
|
1508
|
-
${e.map(
|
|
1510
|
+
${e.map(s=>l`
|
|
1509
1511
|
<div class="perf-caller-row">
|
|
1510
|
-
<span class="perf-caller-name">${
|
|
1511
|
-
<span class="perf-caller-count">${
|
|
1512
|
-
<span class="perf-caller-avg">avg ${
|
|
1512
|
+
<span class="perf-caller-name">${s.label}</span>
|
|
1513
|
+
<span class="perf-caller-count">${s.count} call${s.count!==1?"s":""}</span>
|
|
1514
|
+
<span class="perf-caller-avg">avg ${y(s.avgMs)}</span>
|
|
1513
1515
|
</div>
|
|
1514
1516
|
`)}
|
|
1515
1517
|
</div>
|
|
1516
1518
|
</div>
|
|
1517
|
-
`}renderQueryBreakdown(){return this.queryBreakdownLoading?
|
|
1519
|
+
`}renderQueryBreakdown(){return this.queryBreakdownLoading?l`<div class="perf-queries"><div class="perf-section-title">DB Queries</div><div class="perf-queries-loading">Loading...</div></div>`:this.queryBreakdown.length===0?p:l`
|
|
1518
1520
|
<div class="perf-queries">
|
|
1519
1521
|
<div class="perf-section-title">DB Queries</div>
|
|
1520
1522
|
<div class="perf-queries-list">
|
|
1521
|
-
${this.queryBreakdown.map(t=>
|
|
1523
|
+
${this.queryBreakdown.map(t=>l`
|
|
1522
1524
|
<div class="perf-query-row">
|
|
1523
1525
|
<span class="perf-query-label">${t.label}</span>
|
|
1524
|
-
<span class="perf-query-avg">avg ${
|
|
1526
|
+
<span class="perf-query-avg">avg ${y(t.avgMs)}</span>
|
|
1525
1527
|
<span class="perf-query-count">${t.count} call${t.count!==1?"s":""}</span>
|
|
1526
1528
|
</div>
|
|
1527
1529
|
`)}
|
|
1528
1530
|
</div>
|
|
1529
1531
|
</div>
|
|
1530
|
-
`}renderTrends(t){let e=t.sessions;if(!e||e.length===0)return p;let
|
|
1532
|
+
`}renderTrends(t){let e=t.sessions;if(!e||e.length===0)return p;let s=e.slice(-10);return l`
|
|
1531
1533
|
<div class="perf-trends">
|
|
1532
1534
|
<div class="perf-section-title">Session Trend</div>
|
|
1533
1535
|
<div class="perf-trends-list">
|
|
1534
|
-
${
|
|
1536
|
+
${s.map((i,n)=>{let a=n>0?s[n-1].p95DurationMs:null,c=a!==null?i.p95DurationMs>a*1.2?"slower":i.p95DurationMs<a*.8?"faster":"":"",d=this.formatTimeAgo(i.startedAt),h=n===s.length-1,m=this.healthGradeForDuration(i.p95DurationMs,t.baselineP95Ms);return l`
|
|
1535
1537
|
<div class="perf-trend-row ${h?"perf-trend-current":""}">
|
|
1536
1538
|
<span class="perf-trend-time">${h?"Current":d}</span>
|
|
1537
1539
|
<span class="perf-trend-p95">
|
|
1538
1540
|
<span class="perf-hm-p95" style="color:${m.color};background:${m.bg};border-color:${m.border}">
|
|
1539
|
-
p95: ${
|
|
1541
|
+
p95: ${y(i.p95DurationMs)}
|
|
1540
1542
|
</span>
|
|
1541
1543
|
</span>
|
|
1542
1544
|
<span class="perf-trend-reqs">${i.requestCount} req${i.requestCount!==1?"s":""}</span>
|
|
1543
1545
|
<span class="perf-trend-errs" style="color:${i.errorCount>0?"var(--red)":"var(--text-dim)"}">${i.errorCount} err${i.errorCount!==1?"s":""}</span>
|
|
1544
|
-
${c?
|
|
1546
|
+
${c?l`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:p}
|
|
1545
1547
|
</div>
|
|
1546
1548
|
`})}
|
|
1547
1549
|
</div>
|
|
1548
1550
|
</div>
|
|
1549
|
-
`}formatTimeAgo(t){let e=Date.now()-t,
|
|
1551
|
+
`}formatTimeAgo(t){let e=Date.now()-t,s=Math.round(e/6e4);if(s<1)return "just now";if(s<60)return `${s}m ago`;let i=Math.round(s/60);return i<24?`${i}h ago`:`${Math.round(i/24)}d ago`}renderDetailChart(){return l`
|
|
1550
1552
|
<div class="perf-chart-wrap">
|
|
1551
1553
|
<div class="perf-section-title">Response Time</div>
|
|
1552
1554
|
<canvas id="perf-detail-canvas" class="perf-canvas" style="width:100%;height:240px"></canvas>
|
|
1553
1555
|
</div>
|
|
1554
|
-
`}renderDetailHistory(t){if(t.requests.length===0)return p;let e=[];for(let
|
|
1556
|
+
`}renderDetailHistory(t){if(t.requests.length===0)return p;let e=[];for(let s=t.requests.length-1;s>=0&&e.length<50;s--)e.push({point:t.requests[s],originalIndex:s});return l`
|
|
1555
1557
|
<div class="perf-history-wrap">
|
|
1556
1558
|
<table class="perf-table">
|
|
1557
1559
|
<thead>
|
|
@@ -1565,39 +1567,39 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1565
1567
|
</tr>
|
|
1566
1568
|
</thead>
|
|
1567
1569
|
<tbody>
|
|
1568
|
-
${e.map(
|
|
1570
|
+
${e.map(s=>this.renderHistoryRow(s.point,s.originalIndex,t.baselineP95Ms))}
|
|
1569
1571
|
</tbody>
|
|
1570
1572
|
</table>
|
|
1571
1573
|
</div>
|
|
1572
|
-
`}renderHistoryRow(t,e,
|
|
1573
|
-
<tr class="perf-table-row ${
|
|
1574
|
+
`}renderHistoryRow(t,e,s){let i=this.healthGradeForDuration(t.durationMs,s),n=new Date(t.timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),a=t.statusCode>=400,c=t.queryTimeMs||0,d=t.fetchTimeMs||0,h=Math.max(0,t.durationMs-c-d);return l`
|
|
1575
|
+
<tr class="perf-table-row ${a?"perf-row-err":""}" data-req-idx=${e}>
|
|
1574
1576
|
<td class="perf-td-muted">${n}</td>
|
|
1575
1577
|
<td>
|
|
1576
1578
|
<span class="perf-badge perf-badge-sm" style="color:${i.color};background:${i.bg};border-color:${i.border}">${i.label}</span>
|
|
1577
1579
|
</td>
|
|
1578
|
-
<td>${
|
|
1580
|
+
<td>${y(t.durationMs)}</td>
|
|
1579
1581
|
<td>
|
|
1580
|
-
${c>0?
|
|
1581
|
-
${d>0?
|
|
1582
|
-
<span class="perf-bd-tag perf-bd-tag-app">App ${
|
|
1582
|
+
${c>0?l`<span class="perf-bd-tag perf-bd-tag-db">DB ${y(c)}</span>`:p}
|
|
1583
|
+
${d>0?l`<span class="perf-bd-tag perf-bd-tag-fetch">Fetch ${y(d)}</span>`:p}
|
|
1584
|
+
<span class="perf-bd-tag perf-bd-tag-app">App ${y(h)}</span>
|
|
1583
1585
|
</td>
|
|
1584
|
-
<td class="perf-td-center" style="color:${
|
|
1586
|
+
<td class="perf-td-center" style="color:${a?"var(--red)":"var(--text-muted)"}">${t.statusCode}</td>
|
|
1585
1587
|
<td class="perf-td-right perf-td-muted">${t.queryCount}</td>
|
|
1586
1588
|
</tr>
|
|
1587
|
-
`}};u([I({context:A})],Q.prototype,"store",2),u([b()],Q.prototype,"selectedEndpoint",2),u([b()],Q.prototype,"graphData",2),u([b()],Q.prototype,"loadError",2),u([b()],Q.prototype,"queryBreakdown",2),u([b()],Q.prototype,"queryBreakdownLoading",2),Q=u([S("bk-performance-view")],Q);var Xe={auth:{label:"Auth",color:"#059669",icon:"\u{1F6E1}",tooltip:"Highlight which endpoints require authentication and which are unprotected"},security:{label:"Security",color:"#dc2626",icon:"\u26A0",tooltip:"Show security findings like exposed secrets, token leaks, and PII exposure"},performance:{label:"Perf",color:"#2563eb",icon:"\u26A1",tooltip:"Color endpoints by P95 latency \u2014 green (fast) to red (slow)"},issues:{label:"Issues",color:"#d97706",icon:"\u25CF",tooltip:"Badge endpoints with open issues like N+1 queries or redundant calls"},heat:{label:"Heat",color:"#ef4444",icon:"\u{1F525}",tooltip:"Color nodes and edges by traffic volume \u2014 blue (low) to red (hot)"}},At={action:{fill:"#faf5ff",stroke:"#a855f7",icon:"\u25B6",columnHeader:"ACTIONS"},endpoint:{fill:"#f8fafc",stroke:"#6366f1",icon:"\u26A1",columnHeader:"ENDPOINTS"},table:{fill:"#f0fdf4",stroke:"#16a34a",icon:"\u229E",columnHeader:"TABLES"},external:{fill:"#fffbeb",stroke:"#d97706",icon:"\u25C6",columnHeader:"EXTERNAL"}},fe={triggers:"#a855f7",reads:"#6366f1",writes:"#ef4444",fetches:"#f59e0b",calls:"#22c55e"},yi=100,_i=300,$i=800;function It(o){return o<yi?"#22c55e":o<_i?"#3b82f6":o<$i?"#eab308":"#ef4444"}var xi=.25,Si=.5,Ti=.75;function Ve(o){return o<xi?"#3b82f6":o<Si?"#22c55e":o<Ti?"#eab308":"#ef4444"}var zs="#4338ca",Js="#e0e7ff",Zs="#818cf8",tr="#eef2ff",Ke="#7c3aed",er="#6366f1",ve="#f97316",sr="#ecfdf5",ze="#059669",rr="#fef2f2",Je="#dc2626",ir="#fffbeb",Ze="#d97706",ct="#ef4444",or=.2,nr=3,ts=1.2,Kt=40;var ar=800,lr=500;function Ci(o){return Math.max(140,o.length*7.2+36)}function es(o,s){return Math.round(36+o.stats.requestCount/s*28)}function Li(o){return o.length>0?Math.max(140,...o.map(s=>Ci(s.label))):0}function dr(o,s,t){let e=[];for(let r of s){let i=r.source===o?r.target:r.target===o?r.source:null;if(i){let n=t.get(i);n&&e.push(n.y+n.h/2);}}return e.length>0?e.reduce((r,i)=>r+i,0)/e.length:1/0}function hr(o,s){let t=f=>o.filter(g=>g.type===f).sort((g,T)=>T.stats.requestCount-g.stats.requestCount),e=t("action"),r=t("endpoint"),i=t("table"),n=t("external"),l=Math.max(1,...o.map(f=>f.stats.requestCount)),c=new Map,d=[],h=[],m=50,v=[{type:"action",items:e},{type:"endpoint",items:r},{type:"table",items:i},{type:"external",items:n}];for(let f of v)if(f.items.length>0){let g=Li(f.items);h.push({type:f.type,items:f.items,width:g,x:m}),m+=g+160;}let x=h[0];if(x){let f=74;for(let g of x.items){let T=es(g,l),R={id:g.id,x:x.x,y:f,w:x.width,h:T,label:g.label,type:g.type,stats:g.stats,annotations:g.annotations};d.push(R),c.set(g.id,R),f+=T+10;}}for(let f=1;f<h.length;f++){let g=h[f],T=[...g.items].sort((C,q)=>dr(C.id,s,c)-dr(q.id,s,c)),R=d.filter(C=>C.x===h[f-1].x),H=Math.min(...R.map(C=>C.y)),B=Math.max(...R.map(C=>C.y+C.h))-H,V=T.reduce((C,q)=>C+es(q,l)+10,-10),G=H+Math.max(0,(B-V)/2);for(let C of T){let q=es(C,l),rs={id:C.id,x:g.x,y:G,w:g.width,h:q,label:C.label,type:C.type,stats:C.stats,annotations:C.annotations};d.push(rs),c.set(C.id,rs),G+=q+10;}}let y=[];for(let f of s){let g=c.get(f.source),T=c.get(f.target);if(!g||!T)continue;let R=g.x<T.x,H=R?g.x+g.w:g.x,X=g.y+g.h/2,B=R?T.x:T.x+T.w,V=T.y+T.h/2,G=[];f.stats.frequency>1&&G.push(`${f.stats.frequency}\xD7`),G.push(f.type),f.stats.avgLatencyMs>0&&G.push(`${f.stats.avgLatencyMs}ms`),y.push({key:f.id,sx:H,sy:X,tx:B,ty:V,label:G.join(" \xB7 "),color:fe[f.type]||"#94a3b8",thickness:Math.min(.75+Math.log2(f.stats.frequency+1)*.35,2.5),dashed:f.type==="reads"||f.type==="writes",data:f});}let k=d.reduce((f,g)=>Math.max(f,g.x+g.w),0),$=d.reduce((f,g)=>Math.max(f,g.y+g.h),0);return {nodes:d,edges:y,width:k+100,height:Math.max($+50,250)}}function ur(o){let s=o.charCodeAt(0);return s>=48&&s<=57||s>=65&&s<=70||s>=97&&s<=102}function Mi(o){if(o.length!==36)return false;for(let s=0;s<o.length;s++)if(s===8||s===13||s===18||s===23){if(o[s]!=="-")return false}else if(!ur(o[s]))return false;return true}function Ni(o){if(!o.length)return false;for(let s=0;s<o.length;s++){let t=o.charCodeAt(s);if(t<48||t>57)return false}return true}function Oi(o){if(o.length<12)return false;for(let s=0;s<o.length;s++)if(!ur(o[s]))return false;return true}function ki(o){if(o.length<8)return false;let s=false,t=false;for(let e=0;e<o.length;e++){let r=o.charCodeAt(e);if(r>=65&&r<=90||r>=97&&r<=122)s=true;else if(r>=48&&r<=57)t=true;else if(r!==95&&r!==45)return false}return s&&t}var Di=":id";function Hi(o){return Mi(o)||Ni(o)||Oi(o)||ki(o)?Di:o}function ss(o,s){let t=s.split("?")[0];return `${o} ${t.split("/").map(e=>e&&Hi(e)).join("/")}`}function mr(o,s,t){let e=[...o],r=[...s],i=new Set(e.map(l=>l.id)),n=new Map;for(let l of t){let c=l.label||"Unknown",d=n.get(c);d||(d={endpointKeys:new Set,count:0,totalMs:0},n.set(c,d)),d.count++,d.totalMs+=l.totalDurationMs;for(let h of l.requests)h.path?.startsWith(F)||d.endpointKeys.add(ss(h.method,h.path));}for(let[l,c]of n){let d=`action:${l}`;i.has(d)||(e.push({id:d,type:"action",label:l,stats:{requestCount:c.count,avgLatencyMs:c.count>0?Math.round(c.totalMs/c.count):0,errorRate:0,avgQueryCount:0}}),i.add(d));for(let h of c.endpointKeys){let m=`endpoint:${h}`;if(i.has(m)){let v=`${d} -> ${m}`;r.find(x=>x.id===v)||r.push({id:v,source:d,target:m,type:"triggers",stats:{frequency:c.count,avgLatencyMs:0}});}}}return {nodes:e,edges:r}}function fr(o){let s=new Map;for(let t of o){let e=t.label||"Unknown",r=s.get(e);r||(r={keys:new Set,count:0,totalMs:0},s.set(e,r)),r.count++,r.totalMs+=t.totalDurationMs;for(let i of t.requests)i.path?.startsWith(F)||r.keys.add(ss(i.method,i.path));}return [...s.entries()].map(([t,e])=>({label:t,occurrences:e.count,endpointKeys:e.keys,avgDurationMs:e.count>0?Math.round(e.totalMs/e.count):0}))}function be(o,s){let t=`event=${encodeURIComponent(o)}${s?`&detail=${encodeURIComponent(s)}`:""}`;fetch(`${w.tab}?${t}`).catch(()=>{});}var M=class extends E{constructor(){super(...arguments);this.graphNodes=[];this.graphEdges=[];this.locked=null;this.hovered=null;this.loading=true;this.activeLayers=new Set;this.searchQuery="";this.viewTransform={x:0,y:0,scale:1};this.isPanning=false;this.panStart={x:0,y:0,vtx:0,vty:0};this.dragging=null;this.wasDragging=false;this.nodePositionOverrides=new Map;this.detailTab="overview";this.consolidatedFlows=[];this.activeFlowIdx=-1;this.focusIdx=-1;this.refreshTimer=null;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadData(),this.refreshTimer=setInterval(()=>this.loadData(),4e3);}disconnectedCallback(){super.disconnectedCallback(),this.refreshTimer&&clearInterval(this.refreshTimer);}async loadData(){try{let[t,e]=await Promise.all([fetch(`${w.graph}?level=endpoints`),fetch(w.flows)]),r=await t.json(),i=await e.json(),n=mr(r.nodes||[],r.edges||[],i.flows||[]);this.graphNodes=n.nodes,this.graphEdges=n.edges,this.consolidatedFlows=fr(i.flows||[]),this.nodePositionOverrides.size===0&&this.graphNodes.length>0&&this.tryLoadPersistedPositions(),this.loading=!1;}catch{this.loading=false;}}getPositionStorageKey(){let t=this.graphNodes.map(r=>r.id).sort().join(","),e=0;for(let r=0;r<t.length;r++)e=(e<<5)-e+t.charCodeAt(r)|0;return `bk-graph-pos-${e}`}persistPositions(){if(this.nodePositionOverrides.size!==0)try{let t=Object.fromEntries(this.nodePositionOverrides);localStorage.setItem(this.getPositionStorageKey(),JSON.stringify(t));}catch{}}tryLoadPersistedPositions(){try{let t=localStorage.getItem(this.getPositionStorageKey());if(t){let e=JSON.parse(t);this.nodePositionOverrides=new Map(Object.entries(e));}}catch{}}get activeNodeId(){return this.locked??this.hovered}getHighlightedNodeIds(){let t=this.activeNodeId;if(!t)return null;let e=new Set([t]),r=true;for(;r;){r=false;for(let i of this.graphEdges)e.has(i.source)&&!e.has(i.target)&&(e.add(i.target),r=true);}return e}getFlowTraceNodeIds(){if(this.activeFlowIdx<0||this.activeFlowIdx>=this.consolidatedFlows.length)return null;let t=this.consolidatedFlows[this.activeFlowIdx],e=new Set;e.add(`action:${t.label}`);for(let r of t.endpointKeys)e.add(`endpoint:${r}`);for(let r of this.graphEdges)e.has(r.source)&&e.add(r.target);return e}getFlowTraceEdgeIds(){let t=this.getFlowTraceNodeIds();if(!t)return null;let e=new Set;for(let r of this.graphEdges)t.has(r.source)&&t.has(r.target)&&e.add(r.id);return e}matchesSearch(t){return this.searchQuery?t.toLowerCase().includes(this.searchQuery.toLowerCase()):true}handlePanStart(t){t.button===0&&(t.target.closest(".graph-g")||(this.isPanning=true,this.panStart={x:t.clientX,y:t.clientY,vtx:this.viewTransform.x,vty:this.viewTransform.y}));}handlePanMove(t){if(this.dragging){let e=t.currentTarget.getBoundingClientRect(),r=(t.clientX-e.left-this.viewTransform.x)/this.viewTransform.scale,i=(t.clientY-e.top-this.viewTransform.y)/this.viewTransform.scale;this.nodePositionOverrides.set(this.dragging.nodeId,{x:r-this.dragging.offsetX,y:i-this.dragging.offsetY}),this.requestUpdate();return}this.isPanning&&(this.viewTransform={...this.viewTransform,x:this.panStart.vtx+(t.clientX-this.panStart.x),y:this.panStart.vty+(t.clientY-this.panStart.y)});}handlePanEnd(){this.dragging&&(this.persistPositions(),this.dragging=null,this.wasDragging=true,requestAnimationFrame(()=>{this.wasDragging=false;})),this.isPanning=false;}resetView(){this.viewTransform={x:0,y:0,scale:1};}zoomIn(){this.viewTransform={...this.viewTransform,scale:Math.min(nr,this.viewTransform.scale*ts)};}zoomOut(){this.viewTransform={...this.viewTransform,scale:Math.max(or,this.viewTransform.scale/ts)};}resetLayout(){this.nodePositionOverrides.clear();try{localStorage.removeItem(this.getPositionStorageKey());}catch{}this.viewTransform={x:0,y:0,scale:1},this.requestUpdate(),be("layout_reset");}startNodeDrag(t,e,r){t.stopPropagation();let i=t.currentTarget.closest("svg").getBoundingClientRect(),n=(t.clientX-i.left-this.viewTransform.x)/this.viewTransform.scale,l=(t.clientY-i.top-this.viewTransform.y)/this.viewTransform.scale;this.dragging={nodeId:e,offsetX:n-r.x,offsetY:l-r.y};}handleKeyDown(t){let e=this.graphNodes;if(t.key==="/"){t.preventDefault(),this.querySelector(".graph-search-input")?.focus();return}if(t.key==="Escape"){this.locked=null,this.searchQuery="",this.focusIdx=-1,this.activeFlowIdx=-1;return}if(t.key==="Tab"){t.preventDefault(),this.focusIdx=t.shiftKey?this.focusIdx<=0?e.length-1:this.focusIdx-1:(this.focusIdx+1)%e.length,this.hovered=e[this.focusIdx]?.id??null;return}if(t.key==="Enter"&&this.focusIdx>=0){let r=e[this.focusIdx];r&&(this.locked=this.locked===r.id?null:r.id);return}if(t.key==="+"||t.key==="="){this.zoomIn();return}if(t.key==="-"){this.zoomOut();return}if(t.key==="ArrowUp"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y+Kt};return}if(t.key==="ArrowDown"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y-Kt};return}if(t.key==="ArrowLeft"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x+Kt};return}if(t.key==="ArrowRight"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x-Kt};return}}render(){if(this.loading&&this.graphNodes.length===0)return a`<div class="graph-loading">Loading graph…</div>`;if(this.graphNodes.length===0)return a`
|
|
1589
|
+
`}};u([I({context:A})],X.prototype,"store",2),u([E()],X.prototype,"selectedEndpoint",2),u([E()],X.prototype,"graphData",2),u([E()],X.prototype,"loadError",2),u([E()],X.prototype,"queryBreakdown",2),u([E()],X.prototype,"queryBreakdownLoading",2),X=u([$("bk-performance-view")],X);var rs={auth:{label:"Auth",color:"#059669",icon:"\u{1F6E1}",tooltip:"Highlight which endpoints require authentication and which are unprotected"},security:{label:"Security",color:"#dc2626",icon:"\u26A0",tooltip:"Show security findings like exposed secrets, token leaks, and PII exposure"},performance:{label:"Perf",color:"#2563eb",icon:"\u26A1",tooltip:"Color endpoints by P95 latency \u2014 green (fast) to red (slow)"},issues:{label:"Issues",color:"#d97706",icon:"\u25CF",tooltip:"Badge endpoints with open issues like N+1 queries or redundant calls"},heat:{label:"Heat",color:"#ef4444",icon:"\u{1F525}",tooltip:"Color nodes and edges by traffic volume \u2014 blue (low) to red (hot)"}},Nt={action:{fill:"#faf5ff",stroke:"#a855f7",icon:"\u25B6",columnHeader:"ACTIONS"},endpoint:{fill:"#f8fafc",stroke:"#6366f1",icon:"\u26A1",columnHeader:"ENDPOINTS"},table:{fill:"#f0fdf4",stroke:"#16a34a",icon:"\u229E",columnHeader:"TABLES"},external:{fill:"#fffbeb",stroke:"#d97706",icon:"\u25C6",columnHeader:"EXTERNAL"}},xe={triggers:"#a855f7",reads:"#6366f1",writes:"#ef4444",fetches:"#f59e0b",calls:"#22c55e"},Ai=100,Ii=300,Ci=800;function kt(o){return o<Ai?"#22c55e":o<Ii?"#3b82f6":o<Ci?"#eab308":"#ef4444"}var Li=.25,Mi=.5,Ni=.75;function is(o){return o<Li?"#3b82f6":o<Mi?"#22c55e":o<Ni?"#eab308":"#ef4444"}var cr="#4338ca",dr="#e0e7ff",pr="#818cf8",hr="#eef2ff",os="#7c3aed",ur="#6366f1",Se="#f97316",mr="#ecfdf5",ns="#059669",fr="#fef2f2",as="#dc2626",vr="#fffbeb",ls="#d97706",ut="#ef4444",gr=.2,Er=3,cs=1.2,re=40;var br=800,yr=500;function Hi(o){return Math.max(140,o.length*7.2+36)}function ds(o,r){return Math.round(36+o.stats.requestCount/r*28)}function qi(o){return o.length>0?Math.max(140,...o.map(r=>Hi(r.label))):0}function $r(o,r,t){let e=[];for(let s of r){let i=s.source===o?s.target:s.target===o?s.source:null;if(i){let n=t.get(i);n&&e.push(n.y+n.h/2);}}return e.length>0?e.reduce((s,i)=>s+i,0)/e.length:1/0}function Sr(o,r){let t=v=>o.filter(g=>g.type===v).sort((g,x)=>x.stats.requestCount-g.stats.requestCount),e=t("action"),s=t("endpoint"),i=t("table"),n=t("external"),a=Math.max(1,...o.map(v=>v.stats.requestCount)),c=new Map,d=[],h=[],m=50,f=[{type:"action",items:e},{type:"endpoint",items:s},{type:"table",items:i},{type:"external",items:n}];for(let v of f)if(v.items.length>0){let g=qi(v.items);h.push({type:v.type,items:v.items,width:g,x:m}),m+=g+160;}let S=h[0];if(S){let v=74;for(let g of S.items){let x=ds(g,a),w={id:g.id,x:S.x,y:v,w:S.width,h:x,label:g.label,type:g.type,stats:g.stats,annotations:g.annotations};d.push(w),c.set(g.id,w),v+=x+10;}}for(let v=1;v<h.length;v++){let g=h[v],x=[...g.items].sort((C,F)=>$r(C.id,r,c)-$r(F.id,r,c)),w=d.filter(C=>C.x===h[v-1].x),H=Math.min(...w.map(C=>C.y)),j=Math.max(...w.map(C=>C.y+C.h))-H,tt=x.reduce((C,F)=>C+ds(F,a)+10,-10),W=H+Math.max(0,(j-tt)/2);for(let C of x){let F=ds(C,a),hs={id:C.id,x:g.x,y:W,w:g.width,h:F,label:C.label,type:C.type,stats:C.stats,annotations:C.annotations};d.push(hs),c.set(C.id,hs),W+=F+10;}}let R=[];for(let v of r){let g=c.get(v.source),x=c.get(v.target);if(!g||!x)continue;let w=g.x<x.x,H=w?g.x+g.w:g.x,Z=g.y+g.h/2,j=w?x.x:x.x+x.w,tt=x.y+x.h/2,W=[];v.stats.frequency>1&&W.push(`${v.stats.frequency}\xD7`),W.push(v.type),v.stats.avgLatencyMs>0&&W.push(`${v.stats.avgLatencyMs}ms`),R.push({key:v.id,sx:H,sy:Z,tx:j,ty:tt,label:W.join(" \xB7 "),color:xe[v.type]||"#94a3b8",thickness:Math.min(.75+Math.log2(v.stats.frequency+1)*.35,2.5),dashed:v.type==="reads"||v.type==="writes",data:v});}let P=d.reduce((v,g)=>Math.max(v,g.x+g.w),0),_=d.reduce((v,g)=>Math.max(v,g.y+g.h),0);return {nodes:d,edges:R,width:P+100,height:Math.max(_+50,250)}}function Tr(o){let r=o.charCodeAt(0);return r>=48&&r<=57||r>=65&&r<=70||r>=97&&r<=102}function Ui(o){if(o.length!==36)return false;for(let r=0;r<o.length;r++)if(r===8||r===13||r===18||r===23){if(o[r]!=="-")return false}else if(!Tr(o[r]))return false;return true}function Fi(o){if(!o.length)return false;for(let r=0;r<o.length;r++){let t=o.charCodeAt(r);if(t<48||t>57)return false}return true}function Gi(o){if(o.length<12)return false;for(let r=0;r<o.length;r++)if(!Tr(o[r]))return false;return true}function Bi(o){if(o.length<8)return false;let r=false,t=false;for(let e=0;e<o.length;e++){let s=o.charCodeAt(e);if(s>=65&&s<=90||s>=97&&s<=122)r=true;else if(s>=48&&s<=57)t=true;else if(s!==95&&s!==45)return false}return r&&t}var Wi=":id";function Qi(o){return Ui(o)||Fi(o)||Gi(o)||Bi(o)?Wi:o}function ps(o,r){let t=r.split("?")[0];return `${o} ${t.split("/").map(e=>e&&Qi(e)).join("/")}`}function wr(o,r,t){let e=[...o],s=[...r],i=new Set(e.map(a=>a.id)),n=new Map;for(let a of t){let c=a.label||"Unknown",d=n.get(c);d||(d={endpointKeys:new Set,count:0,totalMs:0},n.set(c,d)),d.count++,d.totalMs+=a.totalDurationMs;for(let h of a.requests)h.path?.startsWith(U)||d.endpointKeys.add(ps(h.method,h.path));}for(let[a,c]of n){let d=`action:${a}`;i.has(d)||(e.push({id:d,type:"action",label:a,stats:{requestCount:c.count,avgLatencyMs:c.count>0?Math.round(c.totalMs/c.count):0,errorRate:0,avgQueryCount:0}}),i.add(d));for(let h of c.endpointKeys){let m=`endpoint:${h}`;if(i.has(m)){let f=`${d} -> ${m}`;s.find(S=>S.id===f)||s.push({id:f,source:d,target:m,type:"triggers",stats:{frequency:c.count,avgLatencyMs:0}});}}}return {nodes:e,edges:s}}function Rr(o){let r=new Map;for(let t of o){let e=t.label||"Unknown",s=r.get(e);s||(s={keys:new Set,count:0,totalMs:0},r.set(e,s)),s.count++,s.totalMs+=t.totalDurationMs;for(let i of t.requests)i.path?.startsWith(U)||s.keys.add(ps(i.method,i.path));}return [...r.entries()].map(([t,e])=>({label:t,occurrences:e.count,endpointKeys:e.keys,avgDurationMs:e.count>0?Math.round(e.totalMs/e.count):0}))}function we(o,r){let t=`event=${encodeURIComponent(o)}${r?`&detail=${encodeURIComponent(r)}`:""}`;fetch(`${T.tab}?${t}`).catch(()=>{});}var M=class extends b{constructor(){super(...arguments);this.graphNodes=[];this.graphEdges=[];this.locked=null;this.hovered=null;this.loading=true;this.activeLayers=new Set;this.searchQuery="";this.viewTransform={x:0,y:0,scale:1};this.isPanning=false;this.panStart={x:0,y:0,vtx:0,vty:0};this.dragging=null;this.wasDragging=false;this.nodePositionOverrides=new Map;this.detailTab="overview";this.consolidatedFlows=[];this.activeFlowIdx=-1;this.focusIdx=-1;this.refreshTimer=null;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadData(),this.refreshTimer=setInterval(()=>this.loadData(),4e3);}disconnectedCallback(){super.disconnectedCallback(),this.refreshTimer&&clearInterval(this.refreshTimer);}async loadData(){try{let[t,e]=await Promise.all([fetch(`${T.graph}?level=endpoints`),fetch(T.flows)]),s=await t.json(),i=await e.json(),n=wr(s.nodes||[],s.edges||[],i.flows||[]);this.graphNodes=n.nodes,this.graphEdges=n.edges,this.consolidatedFlows=Rr(i.flows||[]),this.nodePositionOverrides.size===0&&this.graphNodes.length>0&&this.tryLoadPersistedPositions(),this.loading=!1;}catch{this.loading=false;}}getPositionStorageKey(){let t=this.graphNodes.map(s=>s.id).sort().join(","),e=0;for(let s=0;s<t.length;s++)e=(e<<5)-e+t.charCodeAt(s)|0;return `bk-graph-pos-${e}`}persistPositions(){if(this.nodePositionOverrides.size!==0)try{let t=Object.fromEntries(this.nodePositionOverrides);localStorage.setItem(this.getPositionStorageKey(),JSON.stringify(t));}catch{}}tryLoadPersistedPositions(){try{let t=localStorage.getItem(this.getPositionStorageKey());if(t){let e=JSON.parse(t);this.nodePositionOverrides=new Map(Object.entries(e));}}catch{}}get activeNodeId(){return this.locked??this.hovered}getHighlightedNodeIds(){let t=this.activeNodeId;if(!t)return null;let e=new Set([t]),s=true;for(;s;){s=false;for(let i of this.graphEdges)e.has(i.source)&&!e.has(i.target)&&(e.add(i.target),s=true);}return e}getFlowTraceNodeIds(){if(this.activeFlowIdx<0||this.activeFlowIdx>=this.consolidatedFlows.length)return null;let t=this.consolidatedFlows[this.activeFlowIdx],e=new Set;e.add(`action:${t.label}`);for(let s of t.endpointKeys)e.add(`endpoint:${s}`);for(let s of this.graphEdges)e.has(s.source)&&e.add(s.target);return e}getFlowTraceEdgeIds(){let t=this.getFlowTraceNodeIds();if(!t)return null;let e=new Set;for(let s of this.graphEdges)t.has(s.source)&&t.has(s.target)&&e.add(s.id);return e}matchesSearch(t){return this.searchQuery?t.toLowerCase().includes(this.searchQuery.toLowerCase()):true}handlePanStart(t){t.button===0&&(t.target.closest(".graph-g")||(this.isPanning=true,this.panStart={x:t.clientX,y:t.clientY,vtx:this.viewTransform.x,vty:this.viewTransform.y}));}handlePanMove(t){if(this.dragging){let e=t.currentTarget.getBoundingClientRect(),s=(t.clientX-e.left-this.viewTransform.x)/this.viewTransform.scale,i=(t.clientY-e.top-this.viewTransform.y)/this.viewTransform.scale;this.nodePositionOverrides.set(this.dragging.nodeId,{x:s-this.dragging.offsetX,y:i-this.dragging.offsetY}),this.requestUpdate();return}this.isPanning&&(this.viewTransform={...this.viewTransform,x:this.panStart.vtx+(t.clientX-this.panStart.x),y:this.panStart.vty+(t.clientY-this.panStart.y)});}handlePanEnd(){this.dragging&&(this.persistPositions(),this.dragging=null,this.wasDragging=true,requestAnimationFrame(()=>{this.wasDragging=false;})),this.isPanning=false;}resetView(){this.viewTransform={x:0,y:0,scale:1};}zoomIn(){this.viewTransform={...this.viewTransform,scale:Math.min(Er,this.viewTransform.scale*cs)};}zoomOut(){this.viewTransform={...this.viewTransform,scale:Math.max(gr,this.viewTransform.scale/cs)};}resetLayout(){this.nodePositionOverrides.clear();try{localStorage.removeItem(this.getPositionStorageKey());}catch{}this.viewTransform={x:0,y:0,scale:1},this.requestUpdate(),we("layout_reset");}startNodeDrag(t,e,s){t.stopPropagation();let i=t.currentTarget.closest("svg").getBoundingClientRect(),n=(t.clientX-i.left-this.viewTransform.x)/this.viewTransform.scale,a=(t.clientY-i.top-this.viewTransform.y)/this.viewTransform.scale;this.dragging={nodeId:e,offsetX:n-s.x,offsetY:a-s.y};}handleKeyDown(t){let e=this.graphNodes;if(t.key==="/"){t.preventDefault(),this.querySelector(".graph-search-input")?.focus();return}if(t.key==="Escape"){this.locked=null,this.searchQuery="",this.focusIdx=-1,this.activeFlowIdx=-1;return}if(t.key==="Tab"){t.preventDefault(),this.focusIdx=t.shiftKey?this.focusIdx<=0?e.length-1:this.focusIdx-1:(this.focusIdx+1)%e.length,this.hovered=e[this.focusIdx]?.id??null;return}if(t.key==="Enter"&&this.focusIdx>=0){let s=e[this.focusIdx];s&&(this.locked=this.locked===s.id?null:s.id);return}if(t.key==="+"||t.key==="="){this.zoomIn();return}if(t.key==="-"){this.zoomOut();return}if(t.key==="ArrowUp"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y+re};return}if(t.key==="ArrowDown"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y-re};return}if(t.key==="ArrowLeft"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x+re};return}if(t.key==="ArrowRight"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x-re};return}}render(){if(this.loading&&this.graphNodes.length===0)return l`<div class="graph-loading">Loading graph…</div>`;if(this.graphNodes.length===0)return l`
|
|
1588
1590
|
<div class="graph-empty">
|
|
1589
1591
|
<div class="graph-empty-icon">◎</div>
|
|
1590
1592
|
<div class="graph-empty-title">No data yet</div>
|
|
1591
1593
|
<div class="graph-empty-desc">Navigate your app to build the dependency graph.</div>
|
|
1592
1594
|
</div>
|
|
1593
|
-
`;let t=
|
|
1595
|
+
`;let t=Sr(this.graphNodes,this.graphEdges);this.applyPositionOverrides(t);let e=this.getSelectedNodeDetail(),s=this.getHighlightedNodeIds(),i=this.getFlowTraceNodeIds(),n=this.getFlowTraceEdgeIds(),a=this.deduplicateColumnHeaders(t.nodes),c=Math.max(1,...this.graphNodes.map(h=>h.stats.requestCount)),d=this.viewTransform;return l`
|
|
1594
1596
|
<div class="graph-wrapper" tabindex="0" @keydown=${this.handleKeyDown}
|
|
1595
1597
|
@click=${h=>{let m=h.target;m.closest(".graph-detail")||m.closest(".graph-toolbar")||m.closest(".graph-float")||m.closest(".graph-g")||(this.locked=null);}}>
|
|
1596
1598
|
${this.renderToolbar()}
|
|
1597
1599
|
<div class="graph-body">
|
|
1598
1600
|
<div class="graph-canvas" style="position:relative">
|
|
1599
1601
|
<svg width="100%" height="100%"
|
|
1600
|
-
viewBox="0 0 ${Math.max(t.width,
|
|
1602
|
+
viewBox="0 0 ${Math.max(t.width,br)} ${Math.max(t.height,yr)}"
|
|
1601
1603
|
class="graph-svg"
|
|
1602
1604
|
style="cursor:${this.isPanning?"grabbing":"grab"}"
|
|
1603
1605
|
@mousedown=${this.handlePanStart}
|
|
@@ -1606,12 +1608,12 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1606
1608
|
@mouseleave=${this.handlePanEnd}>
|
|
1607
1609
|
|
|
1608
1610
|
<g transform="translate(${d.x},${d.y}) scale(${d.scale})">
|
|
1609
|
-
${
|
|
1611
|
+
${k`${a.map(h=>k`
|
|
1610
1612
|
<text x="${h.x}" y="${58}" class="graph-col-header">${h.label}</text>
|
|
1611
1613
|
`)}`}
|
|
1612
1614
|
|
|
1613
|
-
${t.edges.map(h=>this.renderEdge(h,
|
|
1614
|
-
${t.nodes.map(h=>this.renderNode(h,
|
|
1615
|
+
${t.edges.map(h=>this.renderEdge(h,s,n,c))}
|
|
1616
|
+
${t.nodes.map(h=>this.renderNode(h,s,i,c))}
|
|
1615
1617
|
</g>
|
|
1616
1618
|
</svg>
|
|
1617
1619
|
${this.renderFloatingControls()}
|
|
@@ -1619,12 +1621,12 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1619
1621
|
${e?this.renderDetailPanel(e):p}
|
|
1620
1622
|
</div>
|
|
1621
1623
|
</div>
|
|
1622
|
-
`}applyPositionOverrides(t){for(let
|
|
1624
|
+
`}applyPositionOverrides(t){for(let s of t.nodes){let i=this.nodePositionOverrides.get(s.id);i&&(s.x=i.x,s.y=i.y);}let e=new Map(t.nodes.map(s=>[s.id,s]));for(let s of t.edges){let i=e.get(s.data.source),n=e.get(s.data.target);if(i&&n){let a=i.x<n.x;s.sx=a?i.x+i.w:i.x,s.sy=i.y+i.h/2,s.tx=a?n.x:n.x+n.w,s.ty=n.y+n.h/2;}}}deduplicateColumnHeaders(t){let e=[],s=new Set;for(let i of t)s.has(i.type)||(s.add(i.type),e.push({x:i.x,label:Nt[i.type]?.columnHeader||i.type.toUpperCase()}));return e}renderToolbar(){return l`
|
|
1623
1625
|
<div class="graph-toolbar">
|
|
1624
1626
|
<div class="graph-layer-toggles">
|
|
1625
|
-
${Object.keys(
|
|
1626
|
-
<button class="graph-layer-btn ${
|
|
1627
|
-
style="${
|
|
1627
|
+
${Object.keys(rs).map(t=>{let e=rs[t],s=this.activeLayers.has(t);return l`
|
|
1628
|
+
<button class="graph-layer-btn ${s?"active":""}"
|
|
1629
|
+
style="${s?`border-color:${e.color};color:${e.color}`:""}"
|
|
1628
1630
|
@click=${()=>this.toggleLayer(t)}
|
|
1629
1631
|
title="${e.tooltip}">
|
|
1630
1632
|
${e.icon} ${e.label}
|
|
@@ -1637,35 +1639,35 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1637
1639
|
<input class="graph-search-input" type="text" placeholder="Search nodes… ( / )"
|
|
1638
1640
|
.value=${this.searchQuery}
|
|
1639
1641
|
@input=${t=>{this.searchQuery=t.target.value;}}>
|
|
1640
|
-
${this.searchQuery?
|
|
1642
|
+
${this.searchQuery?l`
|
|
1641
1643
|
<button class="graph-search-clear" @click=${()=>{this.searchQuery="";}}>✕</button>
|
|
1642
1644
|
`:p}
|
|
1643
1645
|
</div>
|
|
1644
1646
|
|
|
1645
|
-
${this.consolidatedFlows.length>0?
|
|
1646
|
-
<select class="graph-flow-picker" @change=${t=>{this.activeFlowIdx=parseInt(t.target.value,10),this.activeFlowIdx>=0&&
|
|
1647
|
+
${this.consolidatedFlows.length>0?l`
|
|
1648
|
+
<select class="graph-flow-picker" @change=${t=>{this.activeFlowIdx=parseInt(t.target.value,10),this.activeFlowIdx>=0&&we("flow_traced");}}>
|
|
1647
1649
|
<option value="-1">Trace flow…</option>
|
|
1648
|
-
${this.consolidatedFlows.map((t,e)=>
|
|
1650
|
+
${this.consolidatedFlows.map((t,e)=>l`
|
|
1649
1651
|
<option value="${e}" ?selected=${this.activeFlowIdx===e}>${t.label} → ${t.endpointKeys.size} ep · ${t.occurrences}×</option>
|
|
1650
1652
|
`)}
|
|
1651
1653
|
</select>
|
|
1652
1654
|
`:p}
|
|
1653
1655
|
|
|
1654
|
-
${this.activeLayers.has("auth")?
|
|
1656
|
+
${this.activeLayers.has("auth")?l`
|
|
1655
1657
|
<div class="graph-auth-legend">
|
|
1656
|
-
<span class="graph-auth-legend-item"><span style="color:${
|
|
1657
|
-
<span class="graph-auth-legend-item"><span style="color:${
|
|
1658
|
+
<span class="graph-auth-legend-item"><span style="color:${ns}">🛡</span> protected</span>
|
|
1659
|
+
<span class="graph-auth-legend-item"><span style="color:${Se};font-weight:700">!</span> no auth</span>
|
|
1658
1660
|
</div>
|
|
1659
1661
|
`:p}
|
|
1660
1662
|
</div>
|
|
1661
|
-
`}renderFloatingControls(){let t=this.nodePositionOverrides.size>0,e=Math.round(this.viewTransform.scale*100);return
|
|
1663
|
+
`}renderFloatingControls(){let t=this.nodePositionOverrides.size>0,e=Math.round(this.viewTransform.scale*100);return l`
|
|
1662
1664
|
<div class="graph-float">
|
|
1663
1665
|
<button class="graph-float-btn" @click=${this.zoomOut} title="Zoom out (-)">−</button>
|
|
1664
1666
|
<span class="graph-float-zoom">${e}%</span>
|
|
1665
1667
|
<button class="graph-float-btn" @click=${this.zoomIn} title="Zoom in (+)">+</button>
|
|
1666
1668
|
<span class="graph-float-sep"></span>
|
|
1667
1669
|
<button class="graph-float-btn" @click=${this.resetView} title="Reset pan & zoom">⊙</button>
|
|
1668
|
-
${t?
|
|
1670
|
+
${t?l`
|
|
1669
1671
|
<span class="graph-float-sep"></span>
|
|
1670
1672
|
<button class="graph-float-btn graph-float-btn-accent" @click=${()=>this.resetLayout()} title="Reset layout to auto-arrange">
|
|
1671
1673
|
⟲ Reformat
|
|
@@ -1676,185 +1678,185 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1676
1678
|
📷
|
|
1677
1679
|
</button>
|
|
1678
1680
|
</div>
|
|
1679
|
-
`}async captureScreenshot(){
|
|
1681
|
+
`}async captureScreenshot(){we("screenshot_captured");let t=this.querySelector(".graph-svg");if(!t)return;let e=t.cloneNode(true);e.setAttribute("xmlns","http://www.w3.org/2000/svg"),e.style.cssText="",e.removeAttribute("class");let s=document.createElement("style");s.textContent=`
|
|
1680
1682
|
text { font-family: 'Inter', system-ui, -apple-system, sans-serif; }
|
|
1681
1683
|
.graph-col-header { fill: #c4c4cc; font-size: 9px; font-weight: 600; letter-spacing: 1.5px; }
|
|
1682
1684
|
.graph-flow-edge { stroke-dasharray: 6,4; }
|
|
1683
1685
|
.graph-pulse { }
|
|
1684
|
-
`,e.insertBefore(
|
|
1685
|
-
<g class="graph-g" transform="translate(${t.x},${t.y})" style="opacity:${
|
|
1686
|
-
@click=${
|
|
1686
|
+
`,e.insertBefore(s,e.firstChild);let n=document.createElementNS("http://www.w3.org/2000/svg","rect");n.setAttribute("width","100%"),n.setAttribute("height","100%"),n.setAttribute("fill","#ffffff"),e.insertBefore(n,e.firstChild);let a=e.getAttribute("viewBox")||"0 0 800 500",[,,c,d]=a.split(" ").map(Number),h=2,m=c*h,f=d*h,S=new XMLSerializer().serializeToString(e),R=new Blob([S],{type:"image/svg+xml;charset=utf-8"}),P=URL.createObjectURL(R),_=new Image;_.onload=()=>{let v=document.createElement("canvas");v.width=m,v.height=f;let g=v.getContext("2d");g.fillStyle="#ffffff",g.fillRect(0,0,m,f),g.drawImage(_,0,0,m,f),URL.revokeObjectURL(P),v.toBlob(x=>{if(!x)return;let w=document.createElement("a");w.href=URL.createObjectURL(x),w.download=`brakit-graph-${new Date().toISOString().slice(0,19).replace(/[T:]/g,"-")}.png`,document.body.appendChild(w),w.click(),document.body.removeChild(w),URL.revokeObjectURL(w.href);},"image/png");},_.src=P;}toggleLayer(t){let e=new Set(this.activeLayers);e.has(t)?e.delete(t):(e.add(t),we("layer_toggled",t)),this.activeLayers=e;}renderNode(t,e,s,i){let n=this.activeNodeId,a=e===null||e.has(t.id),c=n===t.id,d=this.locked===t.id,h=s?.has(t.id)??false,m=this.matchesSearch(t.label),f=Nt[t.type]||Nt.endpoint,S=d?cr:c?pr:f.stroke,R=d?dr:c?hr:f.fill,P=d?2:c?1.5:.75,_=a?1:.08;this.searchQuery&&!m&&(_=.05),s&&!h&&(_=Math.min(_,.08)),h&&(_=1,S=os);let v=this.searchQuery&&m,g=t.annotations,x=this.activeLayers;x.has("performance")&&g?.p95Ms!==void 0&&t.type==="endpoint"&&(R=kt(g.p95Ms)+"18"),x.has("heat")&&(R=is(t.stats.requestCount/i)+"20");let w=x.has("auth")&&t.type==="endpoint"&&!g?.hasAuth,H=x.has("auth")&&g?.hasAuth,Z=x.has("security")?g?.securityFindings?.length??0:0,j=g?.securityFindings?.some(F=>F.severity==="critical"),tt=x.has("issues")?g?.openIssueCount??0:0,W=this.nodeSubtitle(t),C=x.has("performance")&&g?.p95Ms!==void 0&&t.type==="endpoint";return k`
|
|
1687
|
+
<g class="graph-g" transform="translate(${t.x},${t.y})" style="opacity:${_};cursor:pointer;transition:opacity .15s,transform .1s"
|
|
1688
|
+
@click=${F=>{F.stopPropagation(),!this.wasDragging&&(this.locked=this.locked===t.id?null:t.id,this.detailTab="overview");}}
|
|
1687
1689
|
@mouseenter=${()=>{this.hovered=t.id;}}
|
|
1688
1690
|
@mouseleave=${()=>{this.hovered=null;}}
|
|
1689
|
-
@mousedown=${
|
|
1691
|
+
@mousedown=${F=>{F.detail>=2||this.startNodeDrag(F,t.id,t);}}>
|
|
1690
1692
|
|
|
1691
|
-
${
|
|
1693
|
+
${v?k`
|
|
1692
1694
|
<rect x="-3" y="-3" width="${t.w+6}" height="${t.h+6}" rx="9" fill="none"
|
|
1693
|
-
stroke="${
|
|
1695
|
+
stroke="${ur}" stroke-width="2" stroke-dasharray="4,2"/>
|
|
1694
1696
|
`:p}
|
|
1695
1697
|
|
|
1696
|
-
${
|
|
1697
|
-
<rect width="${t.w}" height="${t.h}" rx="8" fill="${
|
|
1698
|
+
${w?k`
|
|
1699
|
+
<rect width="${t.w}" height="${t.h}" rx="8" fill="${R}" stroke="${Se}"
|
|
1698
1700
|
stroke-width="1.2" stroke-dasharray="5,3"/>
|
|
1699
|
-
`:
|
|
1700
|
-
<rect width="${t.w}" height="${t.h}" rx="8" fill="${
|
|
1701
|
+
`:k`
|
|
1702
|
+
<rect width="${t.w}" height="${t.h}" rx="8" fill="${R}" stroke="${S}" stroke-width="${P}"/>
|
|
1701
1703
|
`}
|
|
1702
1704
|
|
|
1703
1705
|
<text x="12" y="${t.h/2-4}" fill="#1e293b" font-size="11.5" font-weight="600"
|
|
1704
|
-
font-family="'Inter',system-ui,sans-serif">${
|
|
1706
|
+
font-family="'Inter',system-ui,sans-serif">${f.icon} ${t.label}</text>
|
|
1705
1707
|
<text x="12" y="${t.h/2+10}" fill="#a1a1aa" font-size="9"
|
|
1706
|
-
font-family="ui-monospace,monospace">${
|
|
1708
|
+
font-family="ui-monospace,monospace">${W}</text>
|
|
1707
1709
|
|
|
1708
|
-
${C?
|
|
1709
|
-
<text x="${t.w-8}" y="${t.h-6}" fill="${
|
|
1710
|
+
${C?k`
|
|
1711
|
+
<text x="${t.w-8}" y="${t.h-6}" fill="${kt(g.p95Ms)}" font-size="9"
|
|
1710
1712
|
font-family="ui-monospace,monospace" text-anchor="end">p95: ${g.p95Ms}ms</text>
|
|
1711
1713
|
`:p}
|
|
1712
1714
|
|
|
1713
|
-
${H?
|
|
1715
|
+
${H?k`
|
|
1714
1716
|
<g transform="translate(${t.w-24},3)">
|
|
1715
1717
|
<title>Auth protected</title>
|
|
1716
|
-
<rect width="18" height="18" rx="3" fill="${
|
|
1718
|
+
<rect width="18" height="18" rx="3" fill="${mr}" stroke="${ns}" stroke-width="0.5"/>
|
|
1717
1719
|
<text x="9" y="13" text-anchor="middle" font-size="10">🛡</text>
|
|
1718
1720
|
</g>
|
|
1719
1721
|
`:p}
|
|
1720
1722
|
|
|
1721
|
-
${
|
|
1723
|
+
${w?k`
|
|
1722
1724
|
<g transform="translate(${t.w-24},3)">
|
|
1723
1725
|
<title>No auth detected — this endpoint may be unprotected</title>
|
|
1724
|
-
<rect width="18" height="18" rx="3" fill="#fff7ed" stroke="${
|
|
1726
|
+
<rect width="18" height="18" rx="3" fill="#fff7ed" stroke="${Se}" stroke-width="0.5"/>
|
|
1725
1727
|
<text x="9" y="13" text-anchor="middle" font-size="10" fill="#ea580c">!</text>
|
|
1726
1728
|
</g>
|
|
1727
1729
|
`:p}
|
|
1728
1730
|
|
|
1729
|
-
${
|
|
1730
|
-
<g transform="translate(${t.w-(H||
|
|
1731
|
-
<rect width="18" height="18" rx="3" fill="${
|
|
1732
|
-
stroke="${
|
|
1733
|
-
class="${
|
|
1731
|
+
${Z>0?k`
|
|
1732
|
+
<g transform="translate(${t.w-(H||w?46:24)},3)">
|
|
1733
|
+
<rect width="18" height="18" rx="3" fill="${j?fr:vr}"
|
|
1734
|
+
stroke="${j?as:ls}" stroke-width="0.5"
|
|
1735
|
+
class="${j?"graph-pulse":""}"/>
|
|
1734
1736
|
<text x="9" y="13" text-anchor="middle" font-size="9" font-weight="600"
|
|
1735
|
-
fill="${
|
|
1737
|
+
fill="${j?as:ls}">${Z}</text>
|
|
1736
1738
|
</g>
|
|
1737
1739
|
`:p}
|
|
1738
1740
|
|
|
1739
|
-
${
|
|
1740
|
-
<circle cx="${t.w-8}" cy="8" r="5" fill="${
|
|
1741
|
-
<text x="${t.w-8}" y="11" text-anchor="middle" font-size="7" fill="white" font-weight="700">${
|
|
1742
|
-
`:t.stats.errorRate>.05?
|
|
1741
|
+
${tt>0?k`
|
|
1742
|
+
<circle cx="${t.w-8}" cy="8" r="5" fill="${ut}" stroke="white" stroke-width="1"/>
|
|
1743
|
+
<text x="${t.w-8}" y="11" text-anchor="middle" font-size="7" fill="white" font-weight="700">${tt}</text>
|
|
1744
|
+
`:t.stats.errorRate>.05?k`<circle cx="${t.w-12}" cy="12" r="4" fill="${ut}"/>`:p}
|
|
1743
1745
|
|
|
1744
|
-
${g?.isMiddleware&&
|
|
1746
|
+
${g?.isMiddleware&&x.has("auth")?k`
|
|
1745
1747
|
<text x="${t.w}" y="${t.h+12}" text-anchor="end" font-size="8" fill="#6b7280"
|
|
1746
1748
|
font-family="ui-monospace,monospace">middleware</text>
|
|
1747
1749
|
`:p}
|
|
1748
1750
|
</g>
|
|
1749
|
-
`}nodeSubtitle(t){switch(t.type){case "endpoint":return `${t.stats.requestCount} req \xB7 ${t.stats.avgLatencyMs}ms${t.stats.avgQueryCount>0?` \xB7 ${t.stats.avgQueryCount}q`:""}`;case "action":return `${t.stats.requestCount}\xD7 \xB7 ${t.stats.avgLatencyMs}ms`;case "table":return "table";default:return "service"}}renderEdge(t,e,
|
|
1751
|
+
`}nodeSubtitle(t){switch(t.type){case "endpoint":return `${t.stats.requestCount} req \xB7 ${t.stats.avgLatencyMs}ms${t.stats.avgQueryCount>0?` \xB7 ${t.stats.avgQueryCount}q`:""}`;case "action":return `${t.stats.requestCount}\xD7 \xB7 ${t.stats.avgLatencyMs}ms`;case "table":return "table";default:return "service"}}renderEdge(t,e,s,i){let n=this.activeNodeId,a=e===null||e.has(t.data.source)&&e.has(t.data.target),c=s?.has(t.key)??false,d=a?n===null?.25:.6:.04,h=a&&n!==null,m=t.color,f=t.thickness;if(s&&!c&&(d=.04),c&&(d=.85,m=os,f=Math.max(f,1.8)),this.activeLayers.has("heat")&&!c){let tt=Math.max(1,...this.graphEdges.map(C=>C.stats.frequency)),W=t.data.stats.frequency/tt;m=is(W),a&&(d=Math.max(d,.4));}let S=this.activeLayers.has("issues")&&t.data.annotations?.hasIssue,R=Math.abs(t.tx-t.sx),P=Math.min(R*.45,120),_=t.sx<t.tx,v=_?t.sx+P:t.sx-P,g=_?t.tx-P:t.tx+P,x=(t.sx+t.tx)/2,w=(t.sy+t.ty)/2,H=4,Z=S?ut:m,j=S?Math.max(f,1.5):f;return k`
|
|
1750
1752
|
<g style="transition:opacity .15s">
|
|
1751
|
-
<path d="M${t.sx},${t.sy} C${
|
|
1752
|
-
fill="none" stroke="${
|
|
1753
|
+
<path d="M${t.sx},${t.sy} C${v},${t.sy} ${g},${t.ty} ${t.tx},${t.ty}"
|
|
1754
|
+
fill="none" stroke="${Z}" stroke-width="${j}"
|
|
1753
1755
|
stroke-opacity="${d}" stroke-linecap="round"
|
|
1754
1756
|
stroke-dasharray="${c?"6,4":t.dashed?"3,3":"none"}"
|
|
1755
1757
|
class="${c?"graph-flow-edge":""}"/>
|
|
1756
|
-
<polygon points="${t.tx},${t.ty} ${t.tx+(
|
|
1757
|
-
fill="${
|
|
1758
|
-
${h?
|
|
1759
|
-
<rect x="${
|
|
1758
|
+
<polygon points="${t.tx},${t.ty} ${t.tx+(_?-H*1.5:H*1.5)},${t.ty-H} ${t.tx+(_?-H*1.5:H*1.5)},${t.ty+H}"
|
|
1759
|
+
fill="${Z}" fill-opacity="${d}"/>
|
|
1760
|
+
${h?k`
|
|
1761
|
+
<rect x="${x-t.label.length*2.8-2}" y="${w-7}" width="${t.label.length*5.6+8}" height="14"
|
|
1760
1762
|
rx="4" fill="white" fill-opacity="0.92" stroke="${m}" stroke-width="0.4" stroke-opacity="0.15"/>
|
|
1761
|
-
<text x="${
|
|
1763
|
+
<text x="${x}" y="${w+3.5}" fill="${m}" font-size="8" font-weight="500"
|
|
1762
1764
|
font-family="ui-monospace,monospace" text-anchor="middle" opacity="0.85">${t.label}</text>
|
|
1763
1765
|
`:p}
|
|
1764
|
-
${
|
|
1765
|
-
<text x="${
|
|
1766
|
+
${S?k`
|
|
1767
|
+
<text x="${x}" y="${w-10}" fill="${ut}" font-size="8" font-weight="600"
|
|
1766
1768
|
text-anchor="middle">⚠ N+1</text>
|
|
1767
1769
|
`:p}
|
|
1768
1770
|
</g>
|
|
1769
|
-
`}renderDetailPanel(t){let{node:e,edges:
|
|
1771
|
+
`}renderDetailPanel(t){let{node:e,edges:s}=t,i=Nt[e.type]||Nt.endpoint,n=e.annotations,a=(n?.securityFindings?.length??0)>0,c=(n?.openIssueCount??0)>0,d=n?.p95Ms!==void 0,m=[{key:"overview",label:"Overview",show:true},{key:"security",label:`Security${a?` (${n.securityFindings.length})`:""}`,show:a},{key:"performance",label:"Perf",show:d},{key:"issues",label:`Issues${c?` (${n.openIssueCount})`:""}`,show:c}].filter(f=>f.show);return l`
|
|
1770
1772
|
<div class="graph-detail">
|
|
1771
1773
|
<div class="graph-detail-head">
|
|
1772
1774
|
<div>
|
|
1773
1775
|
<div class="graph-detail-badge" style="color:${i.stroke}">${i.icon} ${e.type}</div>
|
|
1774
1776
|
<div class="graph-detail-name">${e.label}</div>
|
|
1775
|
-
${n?.hasAuth?
|
|
1776
|
-
${n?.isMiddleware?
|
|
1777
|
+
${n?.hasAuth?l`<span class="graph-detail-auth-badge">🛡 Authenticated</span>`:p}
|
|
1778
|
+
${n?.isMiddleware?l`<span class="graph-detail-mw-badge">middleware</span>`:p}
|
|
1777
1779
|
</div>
|
|
1778
1780
|
<button class="graph-detail-close" @click=${()=>{this.locked=null;}}>✕</button>
|
|
1779
1781
|
</div>
|
|
1780
1782
|
|
|
1781
|
-
${m.length>1?
|
|
1783
|
+
${m.length>1?l`
|
|
1782
1784
|
<div class="graph-detail-tabs">
|
|
1783
|
-
${m.map(
|
|
1784
|
-
<button class="graph-detail-tab ${this.detailTab===
|
|
1785
|
-
@click=${()=>{this.detailTab=
|
|
1785
|
+
${m.map(f=>l`
|
|
1786
|
+
<button class="graph-detail-tab ${this.detailTab===f.key?"active":""}"
|
|
1787
|
+
@click=${()=>{this.detailTab=f.key;}}>${f.label}</button>
|
|
1786
1788
|
`)}
|
|
1787
1789
|
</div>
|
|
1788
1790
|
`:p}
|
|
1789
1791
|
|
|
1790
|
-
${this.detailTab==="overview"?this.renderOverviewTab(e,
|
|
1792
|
+
${this.detailTab==="overview"?this.renderOverviewTab(e,s):p}
|
|
1791
1793
|
${this.detailTab==="security"?this.renderSecurityTab(n):p}
|
|
1792
1794
|
${this.detailTab==="performance"?this.renderPerformanceTab(e,n):p}
|
|
1793
1795
|
${this.detailTab==="issues"?this.renderIssuesTab(n):p}
|
|
1794
1796
|
</div>
|
|
1795
|
-
`}renderOverviewTab(t,e){return
|
|
1797
|
+
`}renderOverviewTab(t,e){return l`
|
|
1796
1798
|
<div class="graph-detail-stats">
|
|
1797
1799
|
<div class="graph-detail-stat">
|
|
1798
1800
|
<div class="graph-detail-val">${t.stats.requestCount}</div>
|
|
1799
1801
|
<div class="graph-detail-lbl">${t.type==="action"?"OCCURRENCES":"REQUESTS"}</div>
|
|
1800
1802
|
</div>
|
|
1801
1803
|
<div class="graph-detail-stat">
|
|
1802
|
-
<div class="graph-detail-val" style="color:${
|
|
1804
|
+
<div class="graph-detail-val" style="color:${kt(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
1803
1805
|
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
1804
1806
|
</div>
|
|
1805
|
-
${t.stats.avgQueryCount>0?
|
|
1807
|
+
${t.stats.avgQueryCount>0?l`
|
|
1806
1808
|
<div class="graph-detail-stat">
|
|
1807
1809
|
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
1808
1810
|
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
1809
1811
|
</div>
|
|
1810
1812
|
`:p}
|
|
1811
|
-
${t.stats.errorRate>.01?
|
|
1813
|
+
${t.stats.errorRate>.01?l`
|
|
1812
1814
|
<div class="graph-detail-stat">
|
|
1813
|
-
<div class="graph-detail-val" style="color:${
|
|
1815
|
+
<div class="graph-detail-val" style="color:${ut}">${Math.round(t.stats.errorRate*100)}%</div>
|
|
1814
1816
|
<div class="graph-detail-lbl">ERRORS</div>
|
|
1815
1817
|
</div>
|
|
1816
1818
|
`:p}
|
|
1817
1819
|
</div>
|
|
1818
1820
|
|
|
1819
|
-
${e.length>0?
|
|
1821
|
+
${e.length>0?l`
|
|
1820
1822
|
<div class="graph-detail-sec">Connections</div>
|
|
1821
|
-
${e.map(
|
|
1823
|
+
${e.map(s=>{let i=s.source===this.locked,n=(i?s.target:s.source).replace(/^(action|endpoint|table|external):/,"");return l`
|
|
1822
1824
|
<div class="graph-detail-conn">
|
|
1823
|
-
<span class="graph-detail-edge-dot" style="background:${
|
|
1824
|
-
<span class="graph-detail-edge-type">${
|
|
1825
|
+
<span class="graph-detail-edge-dot" style="background:${xe[s.type]}"></span>
|
|
1826
|
+
<span class="graph-detail-edge-type">${s.type}</span>
|
|
1825
1827
|
<span>${i?"\u2192":"\u2190"} ${n}</span>
|
|
1826
|
-
<span class="graph-detail-dim">${
|
|
1828
|
+
<span class="graph-detail-dim">${s.stats.frequency}× · ${s.stats.avgLatencyMs}ms</span>
|
|
1827
1829
|
</div>
|
|
1828
1830
|
`})}
|
|
1829
1831
|
`:p}
|
|
1830
1832
|
|
|
1831
|
-
${e.some(
|
|
1833
|
+
${e.some(s=>s.patterns?.length)?l`
|
|
1832
1834
|
<div class="graph-detail-sec">SQL Patterns</div>
|
|
1833
|
-
${e.filter(
|
|
1834
|
-
<pre class="graph-detail-sql">${
|
|
1835
|
+
${e.filter(s=>s.patterns).flatMap(s=>s.patterns).map(s=>l`
|
|
1836
|
+
<pre class="graph-detail-sql">${s.length>200?s.slice(0,200)+"\u2026":s}</pre>
|
|
1835
1837
|
`)}
|
|
1836
1838
|
`:p}
|
|
1837
|
-
`}renderSecurityTab(t){return t?.securityFindings?.length?
|
|
1838
|
-
${t.securityFindings.map(e=>
|
|
1839
|
+
`}renderSecurityTab(t){return t?.securityFindings?.length?l`
|
|
1840
|
+
${t.securityFindings.map(e=>l`
|
|
1839
1841
|
<div class="graph-detail-finding">
|
|
1840
1842
|
<span class="graph-detail-severity graph-detail-severity-${e.severity}">${e.severity}</span>
|
|
1841
1843
|
<div class="graph-detail-finding-title">${e.title}</div>
|
|
1842
1844
|
<div class="graph-detail-finding-meta">${e.rule} · ${e.count} occurrence${e.count!==1?"s":""}</div>
|
|
1843
1845
|
</div>
|
|
1844
1846
|
`)}
|
|
1845
|
-
`:
|
|
1847
|
+
`:l`<div class="graph-detail-empty">No security findings</div>`}renderPerformanceTab(t,e){return l`
|
|
1846
1848
|
<div class="graph-detail-stats">
|
|
1847
|
-
${e?.p95Ms!==void 0?
|
|
1849
|
+
${e?.p95Ms!==void 0?l`
|
|
1848
1850
|
<div class="graph-detail-stat">
|
|
1849
|
-
<div class="graph-detail-val" style="color:${
|
|
1851
|
+
<div class="graph-detail-val" style="color:${kt(e.p95Ms)}">${e.p95Ms}ms</div>
|
|
1850
1852
|
<div class="graph-detail-lbl">P95 LATENCY</div>
|
|
1851
1853
|
</div>
|
|
1852
1854
|
`:p}
|
|
1853
1855
|
<div class="graph-detail-stat">
|
|
1854
|
-
<div class="graph-detail-val" style="color:${
|
|
1856
|
+
<div class="graph-detail-val" style="color:${kt(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
1855
1857
|
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
1856
1858
|
</div>
|
|
1857
|
-
${t.stats.avgQueryCount>0?
|
|
1859
|
+
${t.stats.avgQueryCount>0?l`
|
|
1858
1860
|
<div class="graph-detail-stat">
|
|
1859
1861
|
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
1860
1862
|
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
@@ -1866,97 +1868,193 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1866
1868
|
</div>
|
|
1867
1869
|
</div>
|
|
1868
1870
|
|
|
1869
|
-
${(e?.insights?.length??0)>0?
|
|
1871
|
+
${(e?.insights?.length??0)>0?l`
|
|
1870
1872
|
<div class="graph-detail-sec">Performance Insights</div>
|
|
1871
|
-
${e.insights.map(
|
|
1873
|
+
${e.insights.map(s=>l`
|
|
1872
1874
|
<div class="graph-detail-finding">
|
|
1873
|
-
<span class="graph-detail-severity graph-detail-severity-${
|
|
1874
|
-
<div class="graph-detail-finding-title">${
|
|
1875
|
-
<div class="graph-detail-finding-meta">${
|
|
1875
|
+
<span class="graph-detail-severity graph-detail-severity-${s.severity}">${s.severity}</span>
|
|
1876
|
+
<div class="graph-detail-finding-title">${s.title}</div>
|
|
1877
|
+
<div class="graph-detail-finding-meta">${s.type}</div>
|
|
1876
1878
|
</div>
|
|
1877
1879
|
`)}
|
|
1878
1880
|
`:p}
|
|
1879
|
-
`}renderIssuesTab(t){let e=t?.openIssueCount??0;return e===0?
|
|
1881
|
+
`}renderIssuesTab(t){let e=t?.openIssueCount??0;return e===0?l`<div class="graph-detail-empty">No open issues</div>`:l`
|
|
1880
1882
|
<div class="graph-detail-issue-summary">
|
|
1881
1883
|
<div class="graph-detail-stat">
|
|
1882
|
-
<div class="graph-detail-val" style="color:${
|
|
1884
|
+
<div class="graph-detail-val" style="color:${ut}">${e}</div>
|
|
1883
1885
|
<div class="graph-detail-lbl">OPEN ISSUES</div>
|
|
1884
1886
|
</div>
|
|
1885
1887
|
</div>
|
|
1886
1888
|
<p class="graph-detail-hint">View the Issues tab for full details and remediation hints.</p>
|
|
1887
|
-
`}getSelectedNodeDetail(){if(!this.locked)return null;let t=this.graphNodes.find(
|
|
1889
|
+
`}getSelectedNodeDetail(){if(!this.locked)return null;let t=this.graphNodes.find(s=>s.id===this.locked);if(!t)return null;let e=this.graphEdges.filter(s=>s.source===this.locked||s.target===this.locked);return {node:t,edges:e}}};u([I({context:A})],M.prototype,"store",2),u([E()],M.prototype,"graphNodes",2),u([E()],M.prototype,"graphEdges",2),u([E()],M.prototype,"locked",2),u([E()],M.prototype,"hovered",2),u([E()],M.prototype,"loading",2),u([E()],M.prototype,"activeLayers",2),u([E()],M.prototype,"searchQuery",2),u([E()],M.prototype,"viewTransform",2),u([E()],M.prototype,"dragging",2),u([E()],M.prototype,"detailTab",2),u([E()],M.prototype,"consolidatedFlows",2),u([E()],M.prototype,"activeFlowIdx",2),u([E()],M.prototype,"focusIdx",2),M=u([$("bk-graph-view")],M);var ie=class extends J{constructor(){super(...arguments);this.activeTab="requests";this.handleNavigateExplorer=t=>{let e=t.detail;ve.some(s=>s.key===e)&&(this.activeTab=e);};}connectedCallback(){super.connectedCallback(),window.addEventListener("navigate-explorer",this.handleNavigateExplorer);}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("navigate-explorer",this.handleNavigateExplorer);}switchTab(t){this.activeTab=t,fetch(`${T.tab}?event=explorer.${t}`).catch(()=>{});}getCount(t){let e=this.store.state;switch(t){case "requests":return e.requests.filter(s=>!s.path?.startsWith(U)).length;case "fetches":return e.fetches.length;case "queries":return e.queries.length;case "logs":return e.logs.length;case "errors":return e.errors.length}}render(){return l`
|
|
1890
|
+
<div class="explorer-tabs">
|
|
1891
|
+
${ve.map(t=>l`
|
|
1892
|
+
<button class="explorer-tab ${this.activeTab===t.key?"active":""}"
|
|
1893
|
+
@click=${()=>this.switchTab(t.key)}>
|
|
1894
|
+
${t.label}
|
|
1895
|
+
<span class="explorer-tab-count">${this.getCount(t.key)}</span>
|
|
1896
|
+
</button>
|
|
1897
|
+
`)}
|
|
1898
|
+
</div>
|
|
1899
|
+
<div style="display:${this.activeTab==="requests"?"block":"none"}">
|
|
1900
|
+
<bk-requests-view></bk-requests-view>
|
|
1901
|
+
</div>
|
|
1902
|
+
<div style="display:${this.activeTab==="fetches"?"block":"none"}">
|
|
1903
|
+
<bk-fetches-view></bk-fetches-view>
|
|
1904
|
+
</div>
|
|
1905
|
+
<div style="display:${this.activeTab==="queries"?"block":"none"}">
|
|
1906
|
+
<bk-queries-view></bk-queries-view>
|
|
1907
|
+
</div>
|
|
1908
|
+
<div style="display:${this.activeTab==="logs"?"block":"none"}">
|
|
1909
|
+
<bk-logs-view></bk-logs-view>
|
|
1910
|
+
</div>
|
|
1911
|
+
<div style="display:${this.activeTab==="errors"?"block":"none"}">
|
|
1912
|
+
<bk-errors-view></bk-errors-view>
|
|
1913
|
+
</div>
|
|
1914
|
+
`}};u([E()],ie.prototype,"activeTab",2),ie=u([$("bk-explorer-view")],ie);var Yi=[{key:"all",label:"All"},{key:"security",label:"Security"},{key:"performance",label:"Performance"},{key:"quality",label:"Quality"}],mt=class extends J{constructor(){super(...arguments);this.filter="all";this.expandedIdx=-1;this.showDismissed=false;}getFilteredIssues(t){return (this.filter==="all"?t:t.filter(s=>Vt(s)===this.filter)).sort((s,i)=>{let n=h=>h==="open"||h==="regressed"?0:h==="fixing"?1:2,a=n(s.state)-n(i.state);if(a!==0)return a;let c=Y[s.issue.severity]?.sort??3,d=Y[i.issue.severity]?.sort??3;return c-d})}getCounts(t){let e={all:t.length,security:0,performance:0,quality:0};for(let s of t)e[Vt(s)]++;return e}render(){if(!(this.store.state.requests.length>0||this.store.state.queries.length>0))return l`<bk-empty-state
|
|
1915
|
+
title="Waiting for requests..."
|
|
1916
|
+
subtitle="Start using your app to see insights here"
|
|
1917
|
+
></bk-empty-state>`;let e=(this.store.state.issues||[]).filter(ir),s=this.getFilteredIssues(e),i=this.getCounts(e),n=s.filter(f=>f.state==="open"&&f.aiStatus!=="wont_fix"),a=s.filter(f=>f.state==="regressed"&&f.aiStatus!=="wont_fix"),c=s.filter(f=>f.state==="fixing"),d=s.filter(f=>f.state==="resolved"),h=s.filter(f=>f.aiStatus==="wont_fix"),m=0;return l`
|
|
1918
|
+
<div class="insights-filters">
|
|
1919
|
+
${Yi.map(f=>l`
|
|
1920
|
+
<button class="insights-chip ${this.filter===f.key?"active":""}"
|
|
1921
|
+
@click=${()=>{this.filter=f.key,this.expandedIdx=-1;}}>
|
|
1922
|
+
${f.label}
|
|
1923
|
+
${i[f.key]>0?l`<span class="insights-chip-count">${i[f.key]}</span>`:p}
|
|
1924
|
+
</button>
|
|
1925
|
+
`)}
|
|
1926
|
+
</div>
|
|
1927
|
+
|
|
1928
|
+
<div class="insights-list">
|
|
1929
|
+
${n.length===0&&a.length===0&&c.length===0&&d.length===0&&h.length===0?l`<div class="insights-empty"><span class="insights-empty-icon">\u2713</span>${this.filter==="all"?"All clear \u2014 no issues detected":`No ${this.filter} issues`}</div>`:p}
|
|
1930
|
+
|
|
1931
|
+
${a.length>0?l`
|
|
1932
|
+
<div class="insights-section insights-section-regressed">
|
|
1933
|
+
<span class="insights-section-icon">\u21A9</span> Regressed
|
|
1934
|
+
<span class="insights-section-count">${a.length}</span>
|
|
1935
|
+
</div>
|
|
1936
|
+
${a.map(f=>this.renderIssueCard(f,m++))}
|
|
1937
|
+
`:p}
|
|
1938
|
+
|
|
1939
|
+
${n.length>0?l`
|
|
1940
|
+
<div class="insights-section">
|
|
1941
|
+
<span class="insights-section-icon">\u25CF</span> Open
|
|
1942
|
+
<span class="insights-section-count">${n.length}</span>
|
|
1943
|
+
</div>
|
|
1944
|
+
${n.map(f=>this.renderIssueCard(f,m++))}
|
|
1945
|
+
`:p}
|
|
1946
|
+
|
|
1947
|
+
${c.length>0?l`
|
|
1948
|
+
<div class="insights-section insights-section-verifying">
|
|
1949
|
+
<span class="insights-section-icon">\u29D7</span> Verifying
|
|
1950
|
+
<span class="insights-section-count">${c.length}</span>
|
|
1951
|
+
</div>
|
|
1952
|
+
${c.map(f=>this.renderIssueCard(f,m++))}
|
|
1953
|
+
`:p}
|
|
1954
|
+
|
|
1955
|
+
${d.length>0?l`
|
|
1956
|
+
<div class="insights-section insights-section-resolved">
|
|
1957
|
+
<span class="insights-section-icon">\u2713</span> Resolved
|
|
1958
|
+
<span class="insights-section-count">${d.length}</span>
|
|
1959
|
+
</div>
|
|
1960
|
+
${d.map(f=>this.renderIssueCard(f,m++))}
|
|
1961
|
+
`:p}
|
|
1962
|
+
|
|
1963
|
+
${h.length>0?l`
|
|
1964
|
+
<div class="insights-section insights-section-dismissed" @click=${()=>{this.showDismissed=!this.showDismissed;}}>
|
|
1965
|
+
<span class="insights-section-icon">${this.showDismissed?"\u25BE":"\u25B8"}</span> Won't Fix
|
|
1966
|
+
<span class="insights-section-count">${h.length}</span>
|
|
1967
|
+
</div>
|
|
1968
|
+
${this.showDismissed?h.map(f=>this.renderIssueCard(f,m++)):p}
|
|
1969
|
+
`:p}
|
|
1970
|
+
</div>
|
|
1971
|
+
`}renderIssueCard(t,e){let s=t.issue,i=Y[s.severity]||Y.info,n=this.expandedIdx===e,a=t.state==="resolved",c=t.state==="fixing",d=Vt(t);return l`
|
|
1972
|
+
<div class="insights-card ${n?"expanded":""} ${a?"resolved":""}"
|
|
1973
|
+
@click=${()=>{this.expandedIdx=this.expandedIdx===e?-1:e;}}>
|
|
1974
|
+
<div class="insights-card-left">
|
|
1975
|
+
<span class="insights-sev ${i.cls}">${i.icon}</span>
|
|
1976
|
+
</div>
|
|
1977
|
+
<div class="insights-card-body">
|
|
1978
|
+
<div class="insights-card-header">
|
|
1979
|
+
<span class="insights-card-title ${a?"resolved":""}">${s.title}</span>
|
|
1980
|
+
<span class="insights-card-cat">${d}</span>
|
|
1981
|
+
${s.count?l`<span class="insights-card-count">${s.count}\u00D7</span>`:p}
|
|
1982
|
+
${t.state==="regressed"?l`<span class="insights-badge-regressed">regressed</span>`:p}
|
|
1983
|
+
${c?l`<span class="insights-badge-verifying">verifying</span>`:p}
|
|
1984
|
+
${a?l`<span class="insights-badge-resolved">resolved</span>`:p}
|
|
1985
|
+
</div>
|
|
1986
|
+
<div class="insights-card-desc">${s.desc}</div>
|
|
1987
|
+
${s.detail?l`<div class="insights-card-detail">${s.detail}</div>`:p}
|
|
1988
|
+
${t.cleanHitsSinceLastSeen>0?l`
|
|
1989
|
+
<div class="insights-card-progress">${t.cleanHitsSinceLastSeen}/${5} clean requests</div>
|
|
1990
|
+
`:p}
|
|
1991
|
+
${n&&s.hint?l`<div class="insights-card-hint">${s.hint}</div>`:p}
|
|
1992
|
+
</div>
|
|
1993
|
+
${s.hint?l`<span class="insights-card-arrow">${n?"\u2193":"\u2192"}</span>`:p}
|
|
1994
|
+
</div>
|
|
1995
|
+
`}};u([E()],mt.prototype,"filter",2),u([E()],mt.prototype,"expandedIdx",2),u([E()],mt.prototype,"showDismissed",2),mt=u([$("bk-insights-view")],mt);function Vi(o){return o===0?"<1ms":y(o)}var N=class extends b{constructor(){super(...arguments);this.requestId="";this.requestStarted=0;this.data=null;this.loading=false;this.failed=false;this.expandedSqlIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate()),this.requestId&&this.loadTimeline();}async loadTimeline(){if(!this.requestId)return;let t=N.cache.get(this.requestId);if(t){this.data=t;return}this.loading=true;try{let e=await fetch(`${T.activity}?requestId=${this.requestId}`);if(!e.ok){this.failed=!0,this.loading=!1;return}let s=await e.json();if(N.cache.size>=We){let i=N.cache.keys().next().value;i!==void 0&&N.cache.delete(i);}N.cache.set(this.requestId,s),this.data=s,this.loading=!1;}catch(e){console.debug("[brakit] timeline load failed:",e),this.failed=true,this.loading=false;}}toggleSql(t,e){e.stopPropagation(),this.expandedSqlIdx=this.expandedSqlIdx===t?-1:t;}copySql(t,e){e.stopPropagation(),navigator.clipboard.writeText(t).then(()=>D.show("SQL copied")).catch(()=>D.show("Copy failed"));}render(){if(this.loading)return l`<div class="tl-loading">Loading activity...</div>`;if(this.failed||!this.data||this.data.total===0)return p;let t=this.data,e=t.timeline[0]?.timestamp??0;return l`
|
|
1888
1996
|
<div class="tl-header">
|
|
1889
1997
|
<span class="tl-title">Activity Timeline</span>
|
|
1890
1998
|
<span class="tl-counts">
|
|
1891
|
-
${t.counts.queries>0?
|
|
1892
|
-
${t.counts.fetches>0?
|
|
1893
|
-
${t.counts.logs>0?
|
|
1894
|
-
${t.counts.errors>0?
|
|
1999
|
+
${t.counts.queries>0?l`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:p}
|
|
2000
|
+
${t.counts.fetches>0?l`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:p}
|
|
2001
|
+
${t.counts.logs>0?l`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:p}
|
|
2002
|
+
${t.counts.errors>0?l`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:p}
|
|
1895
2003
|
</span>
|
|
1896
2004
|
</div>
|
|
1897
2005
|
<div class="tl-events">${this.renderTimeline(t.timeline,e)}</div>
|
|
1898
|
-
`}renderTimeline(t,e){let
|
|
1899
|
-
${this.renderEvent(
|
|
2006
|
+
`}renderTimeline(t,e){let s=new Map,i=[];for(let a of t){let c=a.type==="query"?a.data.parentFetchId:void 0;if(a.type==="query"&&c){let d=s.get(c);d||(d=[],s.set(c,d)),d.push(a);}else i.push(a);}let n=0;return i.map(a=>{let c=n++,d=a.type==="fetch"?a.data.fetchId:void 0,h=d?s.get(d):void 0;if(h&&h.length>0){let m=h.length;return l`
|
|
2007
|
+
${this.renderEvent(a,c,e)}
|
|
1900
2008
|
<div class="tl-nested">
|
|
1901
2009
|
<span class="tl-nested-label">${m} nested quer${m===1?"y":"ies"}</span>
|
|
1902
|
-
${h.map(
|
|
2010
|
+
${h.map(f=>{let S=n++;return this.renderEvent(f,S,e,true)})}
|
|
1903
2011
|
</div>
|
|
1904
|
-
`}return this.renderEvent(
|
|
2012
|
+
`}return this.renderEvent(a,c,e)})}renderEvent(t,e,s,i=false){let n=Bs[t.type]||"var(--text-dim)",a=Ws[t.type]||t.type,c="+"+y(Math.round(t.timestamp-s)),d=t.type==="query"?t.data.sql:void 0,h=!!d,m=this.expandedSqlIdx===e;return l`
|
|
1905
2013
|
<div class="tl-event ${h?"tl-clickable":""} ${i?"tl-nested-event":""}"
|
|
1906
2014
|
style="${h?"":`border-left-color:${n}`}"
|
|
1907
|
-
@click=${h?
|
|
2015
|
+
@click=${h?f=>this.toggleSql(e,f):p}>
|
|
1908
2016
|
<span class="tl-event-time">${c}</span>
|
|
1909
|
-
<span class="tl-event-type" style="color:${n}">${
|
|
2017
|
+
<span class="tl-event-type" style="color:${n}">${a}</span>
|
|
1910
2018
|
${this.renderEventContent(t)}
|
|
1911
|
-
${d?
|
|
2019
|
+
${d?l`
|
|
1912
2020
|
<div class="tl-event-sql ${m?"open":""}">
|
|
1913
|
-
<button class="tl-sql-copy" @click=${
|
|
2021
|
+
<button class="tl-sql-copy" @click=${f=>this.copySql(d,f)}>Copy</button>
|
|
1914
2022
|
${d}
|
|
1915
2023
|
</div>`:p}
|
|
1916
2024
|
</div>
|
|
1917
|
-
`}renderEventContent(t){switch(t.type){case "fetch":{let e=t.data,
|
|
2025
|
+
`}renderEventContent(t){switch(t.type){case "fetch":{let e=t.data,s=e.statusCode>=400;return l`
|
|
1918
2026
|
<span class="tl-event-summary">${e.method} ${e.url}</span>
|
|
1919
|
-
<span class="tl-event-status" style="${
|
|
1920
|
-
<span class="tl-event-dur">${
|
|
1921
|
-
`}case "query":{let e=t.data,
|
|
1922
|
-
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${
|
|
1923
|
-
<span class="tl-event-dur">${
|
|
1924
|
-
`}case "log":{let e=t.data,
|
|
2027
|
+
<span class="tl-event-status" style="${s?"color:var(--red)":""}">${e.statusCode}</span>
|
|
2028
|
+
<span class="tl-event-dur">${y(e.durationMs)}</span>
|
|
2029
|
+
`}case "query":{let e=t.data,s=(e.normalizedOp||e.operation||"?").toUpperCase(),i=e.table||e.model||"",n=Ee[s]||"var(--text-dim)";return l`
|
|
2030
|
+
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${s}</span> ${i}</span>
|
|
2031
|
+
<span class="tl-event-dur">${Vi(e.durationMs)}</span>
|
|
2032
|
+
`}case "log":{let e=t.data,s=qs[e.level]||"var(--text-dim)";return l`<span class="tl-event-summary"><span style="color:${s}">${e.level.toUpperCase()}</span> ${e.message}</span>`}case "error":{let e=t.data;return l`<span class="tl-event-summary" style="color:var(--red)">${e.name}: ${e.message}</span>`}default:return p}}};N.cache=new Map,u([I({context:A})],N.prototype,"store",2),u([L({attribute:"request-id"})],N.prototype,"requestId",2),u([L({attribute:"request-started",type:Number})],N.prototype,"requestStarted",2),u([E()],N.prototype,"data",2),u([E()],N.prototype,"loading",2),u([E()],N.prototype,"failed",2),u([E()],N.prototype,"expandedSqlIdx",2),N=u([$("bk-timeline-panel")],N);function Dt(o){try{return JSON.parse(o)}catch{return null}}var Re=class{constructor(r,t){this.host=r;this.store=t;this.retryCount=0;this.boundHandlers={fetch:r=>{let t=Dt(r.data);t&&this.store.prependFetch(t);},log:r=>{let t=Dt(r.data);t&&this.store.prependLog(t);},error:r=>{let t=Dt(r.data);t&&this.store.prependError(t);},query:r=>{let t=Dt(r.data);t&&this.store.prependQuery(t);},issues:r=>{let t=Dt(r.data);t&&this.store.setIssues(t);}};r.addController(this);}hostConnected(){this.connect();}hostDisconnected(){this.removeListeners(),this.eventSource?.close(),clearTimeout(this.reloadTimer),clearTimeout(this.perfReloadTimer),clearTimeout(this.reconnectTimer);}removeListeners(){this.eventSource&&(this.eventSource.removeEventListener(he,this.boundHandlers.fetch),this.eventSource.removeEventListener("log",this.boundHandlers.log),this.eventSource.removeEventListener(ue,this.boundHandlers.error),this.eventSource.removeEventListener(me,this.boundHandlers.query),this.eventSource.removeEventListener(fe,this.boundHandlers.issues));}connect(){this.removeListeners(),this.eventSource?.close(),this.eventSource=new EventSource(T.events),this.eventSource.onopen=()=>{this.retryCount=0;},this.eventSource.onerror=()=>{this.eventSource?.close(),this.scheduleReconnect();},this.eventSource.onmessage=r=>{let t=Dt(r.data);t&&(t.path?.startsWith(U)||(this.store.prependRequest(t),clearTimeout(this.reloadTimer),this.reloadTimer=setTimeout(()=>this.reloadFlows(),300),this.store.state.activeView==="performance"&&(clearTimeout(this.perfReloadTimer),this.perfReloadTimer=setTimeout(()=>this.reloadMetrics(),Qe))));},this.eventSource.addEventListener(he,this.boundHandlers.fetch),this.eventSource.addEventListener("log",this.boundHandlers.log),this.eventSource.addEventListener(ue,this.boundHandlers.error),this.eventSource.addEventListener(me,this.boundHandlers.query),this.eventSource.addEventListener(fe,this.boundHandlers.issues);}scheduleReconnect(){if(this.retryCount>=10)return;let r=Math.min(1e3*2**this.retryCount,3e4);this.retryCount++,this.reconnectTimer=setTimeout(()=>this.connect(),r);}async reloadFlows(){try{let t=await(await fetch(T.flows)).json();this.store.setFlows(t.flows);}catch{}}async reloadMetrics(){try{let t=await(await fetch(T.metricsLive)).json();this.store.setMetrics(t.endpoints||[]);}catch{}}};function Ar(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>`}function Ir(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`}function Cr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>`}function Lr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 12.7V17h8v-2.3A7 7 0 0 0 12 2z"/></svg>`}function Mr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>`}function Nr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="3"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="18" r="3"/><line x1="9" y1="6" x2="15" y2="6"/><line x1="6" y1="9" x2="6" y2="15"/><line x1="18" y1="9" x2="18" y2="15"/><line x1="9" y1="18" x2="15" y2="18"/></svg>`}var ft=class extends b{constructor(){super(...arguments);this.store=new ye;this.activeView="overview";this.viewMode="simple";this.sse=new Re(this,this.store);this.handleStateChanged=t=>{t.detail==="activeView"&&(this.activeView=this.store.state.activeView),this.requestUpdate();};}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadInitialData(),this.store.addEventListener("state-changed",this.handleStateChanged);}disconnectedCallback(){super.disconnectedCallback(),this.store.removeEventListener("state-changed",this.handleStateChanged);}async loadInitialData(){try{let[t,e]=await Promise.all([Q(T.flows),Q(T.requests)]);this.store.setFlows(t.flows),this.store.setRequests(e.requests);}catch(t){console.warn("[brakit]",t);}try{let[t,e,s,i,n]=await Promise.all([Q(T.fetches),Q(T.errors),Q(T.logs),Q(T.queries),Q(T.metricsLive)]);this.store.setFetches(t.entries),this.store.setErrors(e.entries),this.store.setLogs(s.entries),this.store.setQueries(i.entries),this.store.setMetrics(n.endpoints||[]);}catch(t){console.warn("[brakit]",t);}try{let t=await Q(T.insights);this.store.setIssues(t.issues||[]);}catch(t){console.warn("[brakit]",t);}}switchView(t){t!==this.activeView&&(this.activeView=t,this.store.setActiveView(t),fetch(`${T.tab}?tab=${encodeURIComponent(t)}`).catch(()=>{}),t==="performance"&&this.sse.reloadMetrics());}async handleClear(){confirm(O.CLEAR_CONFIRM)&&(await fetch(T.clear,{method:"POST"}),this.store.clearAll(),D.show(O.CLEARED_TOAST));}handleCopyAsCurl(t){Lt(t);}render(){let t=this.store.state,e=t.requests.filter(c=>!c.path?.startsWith(U)),s=e.filter(c=>c.statusCode>=400).length,i=e.length>0?Math.round(e.reduce((c,d)=>c+d.durationMs,0)/e.length):0,n=(t.issues||[]).filter($e).length,a=window.__BRAKIT_CONFIG__;return l`
|
|
1925
2033
|
<div class="app" id="app">
|
|
1926
2034
|
<aside class="sidebar">
|
|
1927
2035
|
<div class="sidebar-logo">
|
|
1928
2036
|
<span class="logo-text">brakit</span>
|
|
1929
|
-
<span class="logo-version">v${
|
|
2037
|
+
<span class="logo-version">v${a?.version??""}</span>
|
|
1930
2038
|
</div>
|
|
1931
2039
|
<nav class="sidebar-nav">
|
|
1932
|
-
${this.renderSidebarItem("overview","Overview",
|
|
1933
|
-
|
|
1934
|
-
${this.renderSidebarItem("
|
|
1935
|
-
${this.renderSidebarItem("
|
|
1936
|
-
${this.renderSidebarItem("
|
|
1937
|
-
<div class="sidebar-
|
|
1938
|
-
${this.renderSidebarItem("
|
|
1939
|
-
${this.renderSidebarItem("errors","Errors",_r(),t.errors.length)}
|
|
1940
|
-
${this.renderSidebarItem("logs","Logs",$r(),t.logs.length)}
|
|
1941
|
-
${this.renderSidebarItem("security","Security",xr(),n,n===0)}
|
|
1942
|
-
${this.renderSidebarItem("performance","Performance",Sr(),void 0)}
|
|
1943
|
-
<div class="sidebar-section">Topology</div>
|
|
1944
|
-
<button class="sidebar-item ${this.activeView==="graph"?"active":""}" @click=${()=>this.switchView("graph")}>
|
|
1945
|
-
<span class="item-icon">${Tr()}</span>
|
|
1946
|
-
<span class="item-label">Graph</span>
|
|
1947
|
-
<span class="sidebar-beta">beta</span>
|
|
1948
|
-
</button>
|
|
2040
|
+
${this.renderSidebarItem("overview","Overview",Ar(),void 0)}
|
|
2041
|
+
${this.renderSidebarItem("actions","Actions",Ir(),t.flows.length)}
|
|
2042
|
+
${this.renderSidebarItem("insights","Insights",Lr(),n,n===0)}
|
|
2043
|
+
${this.renderSidebarItem("performance","Performance",Cr(),void 0)}
|
|
2044
|
+
${this.renderSidebarItem("graph","Graph",Nr(),void 0)}
|
|
2045
|
+
<div class="sidebar-divider"></div>
|
|
2046
|
+
${this.renderSidebarItem("explorer","Explorer",Mr(),e.length+t.fetches.length+t.queries.length+t.logs.length+t.errors.length)}
|
|
1949
2047
|
</nav>
|
|
1950
|
-
<div class="sidebar-footer">:${
|
|
2048
|
+
<div class="sidebar-footer">:${a?.port??""}</div>
|
|
1951
2049
|
</aside>
|
|
1952
2050
|
<div class="main-panel">
|
|
1953
2051
|
<div class="header">
|
|
1954
2052
|
<div class="header-left">
|
|
1955
|
-
<span class="header-title" id="header-title">${
|
|
1956
|
-
<span class="header-sub" id="header-sub">${
|
|
2053
|
+
<span class="header-title" id="header-title">${Pe[this.activeView]||this.activeView}</span>
|
|
2054
|
+
<span class="header-sub" id="header-sub">${He[this.activeView]||""}</span>
|
|
1957
2055
|
</div>
|
|
1958
2056
|
<div class="header-right">
|
|
1959
|
-
${this.activeView==="actions"?
|
|
2057
|
+
${this.activeView==="actions"?l`
|
|
1960
2058
|
<div class="segmented-control" id="mode-toggle">
|
|
1961
2059
|
<button class="segmented-btn ${this.viewMode==="simple"?"active":""}" @click=${()=>{this.viewMode="simple",this.store.setViewMode("simple");}}>Quick</button>
|
|
1962
2060
|
<button class="segmented-btn ${this.viewMode==="detailed"?"active":""}" @click=${()=>{this.viewMode="detailed",this.store.setViewMode("detailed");}}>Detailed</button>
|
|
@@ -1966,53 +2064,41 @@ span.perf-breakdown-dot.perf-breakdown-app{background:var(--breakdown-app)}
|
|
|
1966
2064
|
</div>
|
|
1967
2065
|
</div>
|
|
1968
2066
|
<div class="main-content">
|
|
1969
|
-
<div
|
|
2067
|
+
<div style="display:${this.activeView==="overview"?"block":"none"}">
|
|
1970
2068
|
<bk-overview-view></bk-overview-view>
|
|
1971
2069
|
</div>
|
|
1972
|
-
<div
|
|
2070
|
+
<div style="display:${this.activeView==="actions"?"block":"none"}">
|
|
1973
2071
|
<bk-flows-view></bk-flows-view>
|
|
1974
2072
|
</div>
|
|
1975
|
-
<div
|
|
1976
|
-
<bk-
|
|
1977
|
-
</div>
|
|
1978
|
-
<div class="view-telemetry" id="fetch-container" style="display:${this.activeView==="fetches"?"block":"none"}">
|
|
1979
|
-
<bk-fetches-view></bk-fetches-view>
|
|
1980
|
-
</div>
|
|
1981
|
-
<div class="view-telemetry" id="query-container" style="display:${this.activeView==="queries"?"block":"none"}">
|
|
1982
|
-
<bk-queries-view></bk-queries-view>
|
|
1983
|
-
</div>
|
|
1984
|
-
<div class="view-telemetry" id="error-container" style="display:${this.activeView==="errors"?"block":"none"}">
|
|
1985
|
-
<bk-errors-view></bk-errors-view>
|
|
2073
|
+
<div style="display:${this.activeView==="insights"?"block":"none"}">
|
|
2074
|
+
<bk-insights-view></bk-insights-view>
|
|
1986
2075
|
</div>
|
|
1987
|
-
<div
|
|
1988
|
-
<bk-logs-view></bk-logs-view>
|
|
1989
|
-
</div>
|
|
1990
|
-
<div class="view-telemetry" id="security-container" style="display:${this.activeView==="security"?"block":"none"}">
|
|
1991
|
-
<bk-security-view></bk-security-view>
|
|
1992
|
-
</div>
|
|
1993
|
-
<div class="view-telemetry" id="performance-container" style="display:${this.activeView==="performance"?"block":"none"}">
|
|
2076
|
+
<div style="display:${this.activeView==="performance"?"block":"none"}">
|
|
1994
2077
|
<bk-performance-view></bk-performance-view>
|
|
1995
2078
|
</div>
|
|
1996
|
-
<div
|
|
2079
|
+
<div style="display:${this.activeView==="graph"?"block":"none"}">
|
|
1997
2080
|
<bk-graph-view></bk-graph-view>
|
|
1998
2081
|
</div>
|
|
2082
|
+
<div style="display:${this.activeView==="explorer"?"block":"none"}">
|
|
2083
|
+
<bk-explorer-view></bk-explorer-view>
|
|
2084
|
+
</div>
|
|
1999
2085
|
</div>
|
|
2000
2086
|
<div class="footer">
|
|
2001
|
-
<span id="stat-total">${e.length}
|
|
2002
|
-
<span id="stat-flows">${t.flows.length}
|
|
2003
|
-
<span id="stat-errors" class="error-count">${
|
|
2087
|
+
<span id="stat-total">${e.length} ${B(e.length,"request")}</span>
|
|
2088
|
+
<span id="stat-flows">${t.flows.length} ${B(t.flows.length,"action")}</span>
|
|
2089
|
+
<span id="stat-errors" class="error-count">${s} ${B(s,"error")}</span>
|
|
2004
2090
|
<span id="stat-avg">Avg: ${i}ms</span>
|
|
2005
2091
|
</div>
|
|
2006
2092
|
</div>
|
|
2007
2093
|
</div>
|
|
2008
2094
|
<bk-toast></bk-toast>
|
|
2009
|
-
`}renderSidebarItem(t,e,
|
|
2095
|
+
`}renderSidebarItem(t,e,s,i,n=false){return l`
|
|
2010
2096
|
<button class="sidebar-item ${this.activeView===t?"active":""}" @click=${()=>this.switchView(t)}>
|
|
2011
|
-
<span class="item-icon">${
|
|
2097
|
+
<span class="item-icon">${s}</span>
|
|
2012
2098
|
<span class="item-label">${e}</span>
|
|
2013
|
-
${i!==void 0?
|
|
2099
|
+
${i!==void 0?l`<span class="item-count" style="display:${n?"none":""}">${i}</span>`:p}
|
|
2014
2100
|
</button>
|
|
2015
|
-
`}};u([
|
|
2101
|
+
`}};u([Je({context:A})],ft.prototype,"store",2),u([E()],ft.prototype,"activeView",2),u([E()],ft.prototype,"viewMode",2),ft=u([$("bk-dashboard")],ft);
|
|
2016
2102
|
/*! Bundled license information:
|
|
2017
2103
|
|
|
2018
2104
|
@lit/reactive-element/css-tag.js:
|