brakit 0.9.2 → 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 +148 -5
- package/dist/api.js +232 -54
- package/dist/bin/brakit.js +228 -35
- package/dist/dashboard-client.global.js +742 -385
- package/dist/dashboard.html +933 -432
- package/dist/mcp/server.js +99 -21
- package/dist/runtime/index.js +1551 -430
- package/package.json +1 -1
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
(function(){'use strict';var
|
|
2
|
-
\f\r]`,
|
|
3
|
-
\f\r"'\`<>=]|("|')|))|$)`,"g"),Ne=/'/g,ke=/"/g,Pe=/^(?:script|style|textarea|title)$/i,te=i=>(e,...t)=>({_$litType$:i,strings:e,values:t}),a=te(1),j=Symbol.for("lit-noChange"),d=Symbol.for("lit-nothing"),De=new WeakMap,G=W.createTreeWalker(W,129);function Ue(i,e){if(!Zt(i)||!i.hasOwnProperty("raw"))throw Error("invalid template strings array");return Le!==void 0?Le.createHTML(e):e}var qs=(i,e)=>{let t=i.length-1,s=[],r,o=e===2?"<svg>":e===3?"<math>":"",n=vt;for(let l=0;l<t;l++){let c=i[l],p,h,m=-1,E=0;for(;E<c.length&&(n.lastIndex=E,h=n.exec(c),h!==null);)E=n.lastIndex,n===vt?h[1]==="!--"?n=Me:h[1]!==void 0?n=Oe:h[2]!==void 0?(Pe.test(h[2])&&(r=RegExp("</"+h[2],"g")),n=B):h[3]!==void 0&&(n=B):n===B?h[0]===">"?(n=r??vt,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,p=h[1],n=h[3]===void 0?B:h[3]==='"'?ke:Ne):n===ke||n===Ne?n=B:n===Me||n===Oe?n=vt:(n=B,r=void 0);let T=n===B&&i[l+1].startsWith("/>")?" ":"";o+=n===vt?c+Ds:m>=0?(s.push(p),c.slice(0,m)+He+c.slice(m)+D+T):c+D+(m===-2?l:T);}return [Ue(i,o+(i[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),s]},bt=class i{constructor({strings:e,_$litType$:t},s){let r;this.parts=[];let o=0,n=0,l=e.length-1,c=this.parts,[p,h]=qs(e,t);if(this.el=i.createElement(p,s),G.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(r=G.nextNode())!==null&&c.length<l;){if(r.nodeType===1){if(r.hasAttributes())for(let m of r.getAttributeNames())if(m.endsWith(He)){let E=h[n++],T=r.getAttribute(m).split(D),b=/([.?@])?(.*)/.exec(E);c.push({type:1,index:o,name:b[2],strings:T,ctor:b[1]==="."?Xt:b[1]==="?"?Kt:b[1]==="@"?zt:z}),r.removeAttribute(m);}else m.startsWith(D)&&(c.push({type:6,index:o}),r.removeAttribute(m));if(Pe.test(r.tagName)){let m=r.textContent.split(D),E=m.length-1;if(E>0){r.textContent=kt?kt.emptyScript:"";for(let T=0;T<E;T++)r.append(m[T],gt()),G.nextNode(),c.push({type:2,index:++o});r.append(m[E],gt());}}}else if(r.nodeType===8)if(r.data===qe)c.push({type:2,index:o});else {let m=-1;for(;(m=r.data.indexOf(D,m+1))!==-1;)c.push({type:7,index:o}),m+=D.length-1;}o++;}}static createElement(e,t){let s=W.createElement("template");return s.innerHTML=e,s}};function K(i,e,t=i,s){if(e===j)return e;let r=s!==void 0?t._$Co?.[s]:t._$Cl,o=Et(e)?void 0:e._$litDirective$;return r?.constructor!==o&&(r?._$AO?.(false),o===void 0?r=void 0:(r=new o(i),r._$AT(i,t,s)),s!==void 0?(t._$Co??(t._$Co=[]))[s]=r:t._$Cl=r),r!==void 0&&(e=K(i,r._$AS(i,e.values),r,s)),e}var Yt=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:s}=this._$AD,r=(e?.creationScope??W).importNode(t,true);G.currentNode=r;let o=G.nextNode(),n=0,l=0,c=s[0];for(;c!==void 0;){if(n===c.index){let p;c.type===2?p=new _t(o,o.nextSibling,this,e):c.type===1?p=new c.ctor(o,c.name,c.strings,this,e):c.type===6&&(p=new Jt(o,this,e)),this._$AV.push(p),c=s[++l];}n!==c?.index&&(o=G.nextNode(),n++);}return G.currentNode=W,r}p(e){let t=0;for(let s of this._$AV)s!==void 0&&(s.strings!==void 0?(s._$AI(e,s,t),t+=s.strings.length-2):s._$AI(e[t])),t++;}},_t=class i{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,s,r){this.type=2,this._$AH=d,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=s,this.options=r,this._$Cv=r?.isConnected??true;}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=K(this,e,t),Et(e)?e===d||e==null||e===""?(this._$AH!==d&&this._$AR(),this._$AH=d):e!==this._$AH&&e!==j&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):Hs(e)?this.k(e):this._(e);}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e));}_(e){this._$AH!==d&&Et(this._$AH)?this._$AA.nextSibling.data=e:this.T(W.createTextNode(e)),this._$AH=e;}$(e){let{values:t,_$litType$:s}=e,r=typeof s=="number"?this._$AC(e):(s.el===void 0&&(s.el=bt.createElement(Ue(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===r)this._$AH.p(t);else {let o=new Yt(r,this),n=o.u(this.options);o.p(t),this.T(n),this._$AH=o;}}_$AC(e){let t=De.get(e.strings);return t===void 0&&De.set(e.strings,t=new bt(e)),t}k(e){Zt(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,s,r=0;for(let o of e)r===t.length?t.push(s=new i(this.O(gt()),this.O(gt()),this,this.options)):s=t[r],s._$AI(o),r++;r<t.length&&(this._$AR(s&&s._$AB.nextSibling,r),t.length=r);}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);e!==this._$AB;){let s=Ie(e).nextSibling;Ie(e).remove(),e=s;}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e));}},z=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,s,r,o){this.type=1,this._$AH=d,this._$AN=void 0,this.element=e,this.name=t,this._$AM=r,this.options=o,s.length>2||s[0]!==""||s[1]!==""?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=d;}_$AI(e,t=this,s,r){let o=this.strings,n=false;if(o===void 0)e=K(this,e,t,0),n=!Et(e)||e!==this._$AH&&e!==j,n&&(this._$AH=e);else {let l=e,c,p;for(e=o[0],c=0;c<o.length-1;c++)p=K(this,l[s+c],t,c),p===j&&(p=this._$AH[c]),n||(n=!Et(p)||p!==this._$AH[c]),p===d?e=d:e!==d&&(e+=(p??"")+o[c+1]),this._$AH[c]=p;}n&&!r&&this.j(e);}j(e){e===d?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"");}},Xt=class extends z{constructor(){super(...arguments),this.type=3;}j(e){this.element[this.name]=e===d?void 0:e;}},Kt=class extends z{constructor(){super(...arguments),this.type=4;}j(e){this.element.toggleAttribute(this.name,!!e&&e!==d);}},zt=class extends z{constructor(e,t,s,r,o){super(e,t,s,r,o),this.type=5;}_$AI(e,t=this){if((e=K(this,e,t,0)??d)===j)return;let s=this._$AH,r=e===d&&s!==d||e.capture!==s.capture||e.once!==s.once||e.passive!==s.passive,o=e!==d&&(s===d||r);r&&this.element.removeEventListener(this.name,this,s),o&&this.element.addEventListener(this.name,this,e),this._$AH=e;}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e);}},Jt=class{constructor(e,t,s){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(e){K(this,e);}};var Ps=ft.litHtmlPolyfillSupport;Ps?.(bt,_t),(ft.litHtmlVersions??(ft.litHtmlVersions=[])).push("3.3.2");var Fe=(i,e,t)=>{let s=t?.renderBefore??e,r=s._$litPart$;if(r===void 0){let o=t?.renderBefore??null;s._$litPart$=r=new _t(e.insertBefore(gt(),o),o,void 0,t??{});}return r._$AI(i),r};var St=globalThis,f=class extends O{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let e=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=e.firstChild),e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Fe(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return j}};f._$litElement$=true,f.finalized=true,St.litElementHydrateSupport?.({LitElement:f});var Us=St.litElementPolyfillSupport;Us?.({LitElement:f});(St.litElementVersions??(St.litElementVersions=[])).push("4.2.2");var g=i=>(e,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(i,e);}):customElements.define(i,e);};var Fs={attribute:true,type:String,converter:mt,reflect:false,hasChanged:Nt},Bs=(i=Fs,e,t)=>{let{kind:s,metadata:r}=t,o=globalThis.litPropertyMetadata.get(r);if(o===void 0&&globalThis.litPropertyMetadata.set(r,o=new Map),s==="setter"&&((i=Object.create(i)).wrapped=true),o.set(t.name,i),s==="accessor"){let{name:n}=t;return {set(l){let c=e.get.call(this);e.set.call(this,l),this.requestUpdate(n,c,i,true,l);},init(l){return l!==void 0&&this.C(n,void 0,i,l),l}}}if(s==="setter"){let{name:n}=t;return function(l){let c=this[n];e.call(this,l),this.requestUpdate(n,c,i,true,l);}}throw Error("Unsupported decorator location: "+s)};function y(i){return (e,t)=>typeof t=="object"?Bs(i,e,t):((s,r,o)=>{let n=r.hasOwnProperty(o);return r.constructor.createProperty(o,s),n?Object.getOwnPropertyDescriptor(r,o):void 0})(i,e,t)}function _(i){return y({...i,state:true,attribute:false})}var Tt=class extends f{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([y()],Tt.prototype,"method",2),Tt=u([g("bk-method-badge")],Tt);var I="/__brakit/api",N="/__brakit",$={flows:`${I}/flows`,requests:`${I}/requests`,events:`${I}/events`,clear:`${I}/clear`,fetches:`${I}/fetches`,errors:`${I}/errors`,logs:`${I}/logs`,queries:`${I}/queries`,metricsLive:`${I}/metrics/live`,insights:`${I}/insights`,tab:`${I}/tab`,activity:`${I}/activity`};var J="polling",Ht="static",Gs="auth-handshake",Ws="auth-check",js="middleware",$t={[Gs]:1,[Ws]:1,[js]:1};var ee="fetch";var se="error_event",re="query",oe="issues";var ie={overview:"Overview",actions:"Actions",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",performance:"Performance",security:"Security"},ne={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"};var le=100,Z=300,tt=800,ce=2e3,de=100,pe=50,ue=500;var H="__all__",Ft={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},Ke={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},me=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],yt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},et=[{max:le,label:"Fast",color:"var(--green)",bg:"rgba(22,163,74,0.08)",border:"rgba(22,163,74,0.2)"},{max:Z,label:"Good",color:"var(--green)",bg:"rgba(22,163,74,0.06)",border:"rgba(22,163,74,0.15)"},{max:tt,label:"OK",color:"var(--amber)",bg:"rgba(217,119,6,0.06)",border:"rgba(217,119,6,0.15)"},{max:ce,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)"}],ze="rgba(228,228,231,0.8)",ve="rgba(113,113,122,0.7)",Je="10px monospace",fe="9px monospace";var Ze={top:16,right:16,bottom:28,left:52},ts={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},es={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},ss=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),rs={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"},os=new Set(["host","connection","accept-encoding"]),q={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function v(i){return i<1e3?i+"ms":(i/1e3).toFixed(1)+"s"}function U(i){return !i||i===0?"":i<1024?i+"b":(i/1024).toFixed(1)+"kb"}function P(i){return i?i.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}function st(i){return i>=500?"status-pill-5xx":i>=400?"status-pill-4xx":i>=300?"status-pill-3xx":"status-pill-2xx"}function is(i){return rs[i]||(i>=500?"Server Error":i>=400?"Client Error":"OK")}function Qs(i,e){if(ss.has(i.toLowerCase())){let t=String(e);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(e)}function rt(i){return !i||Object.keys(i).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(i).map(([e,t])=>'<span class="json-key">'+P(e)+"</span>: "+P(Qs(e,t))).join(`
|
|
4
|
-
`)}function
|
|
1
|
+
(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=`[
|
|
2
|
+
\f\r]`,qt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_s=/-->/g,$s=/>/g,at=RegExp(`>|${Ie}(?:([^\\s"'>=/]+)(${Ie}*=${Ie}*(?:[^
|
|
3
|
+
\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(`
|
|
4
|
+
`)}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`
|
|
5
5
|
<div class="empty">
|
|
6
6
|
<span class="empty-title">${this.title}</span>
|
|
7
7
|
<span class="empty-sub">${this.subtitle}</span>
|
|
8
8
|
</div>
|
|
9
|
-
`}};u([
|
|
9
|
+
`}};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`
|
|
10
10
|
<div class="fetch-stat">
|
|
11
11
|
<span class="fetch-stat-value" style="color:${this.color}">${this.value}</span>
|
|
12
12
|
<span class="fetch-stat-label">${this.label}</span>
|
|
13
13
|
</div>
|
|
14
|
-
`}};u([
|
|
14
|
+
`}};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`
|
|
15
15
|
<div
|
|
16
|
-
class="req-row tel-clickable ${
|
|
17
|
-
@click=${()=>this.toggleError(
|
|
16
|
+
class="req-row tel-clickable ${i?"expanded":""}"
|
|
17
|
+
@click=${()=>this.toggleError(e)}
|
|
18
18
|
>
|
|
19
19
|
<span class="tel-error-name" title=${t.name}>${t.name}</span>
|
|
20
20
|
<span class="tel-message" title=${t.message}>${t.message}</span>
|
|
21
|
-
<span class="tel-timestamp">${
|
|
21
|
+
<span class="tel-timestamp">${s}</span>
|
|
22
22
|
</div>
|
|
23
|
-
${
|
|
24
|
-
`}render(){let t=this.store.state.errors;return t.length===0?
|
|
23
|
+
${i&&t.stack?l`<div class="error-stack">${t.stack}</div>`:p}
|
|
24
|
+
`}render(){let t=this.store.state.errors;return t.length===0?l`<bk-empty-state
|
|
25
25
|
title="No errors"
|
|
26
26
|
subtitle="No errors have been captured yet"
|
|
27
|
-
></bk-empty-state>`:
|
|
27
|
+
></bk-empty-state>`:l`
|
|
28
28
|
<div class="col-header">
|
|
29
29
|
<span style="width:180px">Type</span>
|
|
30
30
|
<span style="flex:1">Message</span>
|
|
31
31
|
<span style="width:130px;text-align:right">Time</span>
|
|
32
32
|
</div>
|
|
33
33
|
<div id="error-list">
|
|
34
|
-
${t.map((s
|
|
34
|
+
${t.map((e,s)=>this.renderErrorRow(e,s))}
|
|
35
35
|
</div>
|
|
36
|
-
`}};u([
|
|
36
|
+
`}};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`
|
|
37
37
|
<div id="log-analysis">
|
|
38
38
|
<div class="fetch-summary">
|
|
39
|
-
<bk-stat-card value=${String(
|
|
40
|
-
${t.error>0?
|
|
41
|
-
${t.warn>0?
|
|
39
|
+
<bk-stat-card value=${String(r.length)} label="Total Logs"></bk-stat-card>
|
|
40
|
+
${t.error>0?l`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:p}
|
|
41
|
+
${t.warn>0?l`<bk-stat-card value=${String(t.warn)} label="Warnings" color="var(--amber)"></bk-stat-card>`:p}
|
|
42
42
|
<bk-stat-card value=${String(t.info)} label="Info"></bk-stat-card>
|
|
43
|
-
${t.debug>0?
|
|
44
|
-
${t.log>0?
|
|
43
|
+
${t.debug>0?l`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:p}
|
|
44
|
+
${t.log>0?l`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:p}
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
|
-
`}renderLogRow(
|
|
47
|
+
`}renderLogRow(r){let t=new Date(r.timestamp).toLocaleTimeString();return l`
|
|
48
48
|
<div class="req-row">
|
|
49
|
-
<span class="tel-level tel-level-${
|
|
50
|
-
<span class="tel-message tel-mono" title=${
|
|
49
|
+
<span class="tel-level tel-level-${r.level}">${r.level.toUpperCase()}</span>
|
|
50
|
+
<span class="tel-message tel-mono" title=${r.message}>${r.message}</span>
|
|
51
51
|
<span class="tel-timestamp">${t}</span>
|
|
52
52
|
</div>
|
|
53
|
-
`}render(){let
|
|
53
|
+
`}render(){let r=this.store.state.logs;return r.length===0?l`<bk-empty-state
|
|
54
54
|
title="No logs"
|
|
55
55
|
subtitle="No console output has been captured yet"
|
|
56
|
-
></bk-empty-state>`:
|
|
57
|
-
${this.renderAnalysis(
|
|
56
|
+
></bk-empty-state>`:l`
|
|
57
|
+
${this.renderAnalysis(r)}
|
|
58
58
|
<div class="col-header">
|
|
59
59
|
<span style="width:52px">Level</span>
|
|
60
60
|
<span style="flex:1">Message</span>
|
|
61
61
|
<span style="width:130px;text-align:right">Time</span>
|
|
62
62
|
</div>
|
|
63
63
|
<div id="log-list">
|
|
64
|
-
${
|
|
64
|
+
${r.map(t=>this.renderLogRow(t))}
|
|
65
65
|
</div>
|
|
66
|
-
`}};u([
|
|
66
|
+
`}};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`
|
|
67
67
|
<div>
|
|
68
68
|
<div
|
|
69
69
|
class="req-row query-row tel-clickable ${h?"expanded":""}"
|
|
70
|
-
@click=${()=>this.toggleQuery(
|
|
70
|
+
@click=${()=>this.toggleQuery(e)}
|
|
71
71
|
>
|
|
72
|
-
<span class="query-op" title=${
|
|
73
|
-
<span class="query-table" title=${
|
|
74
|
-
<span class="query-preview" title=${
|
|
72
|
+
<span class="query-op" title=${s} style="color:${a}">${s}</span>
|
|
73
|
+
<span class="query-table" title=${i}>${i}</span>
|
|
74
|
+
<span class="query-preview" title=${d}>${d}</span>
|
|
75
75
|
<span class="query-dur${c?" query-slow":""}">${this.queryDuration(t.durationMs)}</span>
|
|
76
76
|
</div>
|
|
77
77
|
<div class="query-detail ${h?"open":""}">
|
|
78
|
-
${h?
|
|
79
|
-
<pre class="query-detail-sql"
|
|
78
|
+
${h?l`
|
|
79
|
+
<pre class="query-detail-sql">${G(Zs(n))}</pre>
|
|
80
80
|
<bk-copy-button .text=${n} label="Copy"></bk-copy-button>
|
|
81
|
-
`:
|
|
81
|
+
`:p}
|
|
82
82
|
</div>
|
|
83
83
|
</div>
|
|
84
|
-
`}render(){let t=this.store.state.queries;return t.length===0?
|
|
84
|
+
`}render(){let t=this.store.state.queries;return t.length===0?l`<bk-empty-state
|
|
85
85
|
title="No queries"
|
|
86
86
|
subtitle="No database queries have been captured yet"
|
|
87
|
-
></bk-empty-state>`:
|
|
87
|
+
></bk-empty-state>`:l`
|
|
88
88
|
<div class="col-header">
|
|
89
89
|
<span style="width:70px;border-right:1px solid var(--border);padding-right:16px">Operation</span>
|
|
90
90
|
<span style="width:170px;border-right:1px solid var(--border);padding-right:16px">Table</span>
|
|
@@ -92,37 +92,37 @@
|
|
|
92
92
|
<span style="width:60px;text-align:right">Time</span>
|
|
93
93
|
</div>
|
|
94
94
|
<div id="query-list">
|
|
95
|
-
${t.map((s
|
|
95
|
+
${t.map((e,s)=>this.renderQueryRow(e,s))}
|
|
96
96
|
</div>
|
|
97
|
-
`}};u([
|
|
97
|
+
`}};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`
|
|
98
98
|
<div class="detail-meta">
|
|
99
99
|
<span><bk-method-badge .method=${t.method}></bk-method-badge> ${t.url}</span>
|
|
100
100
|
<span><bk-status-pill .code=${t.statusCode}></bk-status-pill></span>
|
|
101
101
|
<span>${t.durationMs}ms</span>
|
|
102
|
-
${t.responseSize?
|
|
102
|
+
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:p}
|
|
103
103
|
</div>
|
|
104
104
|
<div class="request-timeline tl-hidden" data-request-id=${t.id} data-request-started=${String(t.startedAt)}></div>
|
|
105
105
|
<div class="detail-grid">
|
|
106
|
-
<div class="detail-section"><h4>Request Headers</h4><pre
|
|
107
|
-
<div class="detail-section"><h4>Response Headers</h4><pre
|
|
108
|
-
<div class="detail-section"><h4>Request Body</h4><pre
|
|
109
|
-
<div class="detail-section"><h4>Response Body</h4><pre
|
|
106
|
+
<div class="detail-section"><h4>Request Headers</h4><pre>${G(Tt(t.headers))}</pre></div>
|
|
107
|
+
<div class="detail-section"><h4>Response Headers</h4><pre>${G(Tt(t.responseHeaders))}</pre></div>
|
|
108
|
+
<div class="detail-section"><h4>Request Body</h4><pre>${G(dt(t.requestBody))}</pre></div>
|
|
109
|
+
<div class="detail-section"><h4>Response Body</h4><pre>${G(dt(t.responseBody))}</pre></div>
|
|
110
110
|
</div>
|
|
111
111
|
<div class="detail-actions">
|
|
112
|
-
<button class="btn btn-curl" @click=${
|
|
112
|
+
<button class="btn btn-curl" @click=${e=>this.handleCopyAsCurl(t,e)}>Copy cURL</button>
|
|
113
113
|
</div>
|
|
114
|
-
`}renderRequestRow(t){let
|
|
115
|
-
<div class="req-row ${
|
|
114
|
+
`}renderRequestRow(t){let e=this.expandedId===t.id;return l`
|
|
115
|
+
<div class="req-row ${e?"expanded":""}" @click=${()=>this.toggleRequest(t.id)}>
|
|
116
116
|
<div class="req-summary">
|
|
117
117
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
118
118
|
<span class="req-url">${t.url}</span>
|
|
119
119
|
<bk-status-pill .code=${t.statusCode}></bk-status-pill>
|
|
120
120
|
<bk-duration-label .ms=${t.durationMs}></bk-duration-label>
|
|
121
|
-
<span class="req-size">${
|
|
121
|
+
<span class="req-size">${ot(t.responseSize)}</span>
|
|
122
122
|
</div>
|
|
123
123
|
</div>
|
|
124
|
-
<div class="req-detail ${
|
|
125
|
-
`}render(){let t=this.store.state.requests.filter(
|
|
124
|
+
<div class="req-detail ${e?"open":""}">${e?this.renderDetail(t):p}</div>
|
|
125
|
+
`}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`
|
|
126
126
|
<div class="col-header">
|
|
127
127
|
<span style="width:60px">Method</span>
|
|
128
128
|
<span style="flex:1">URL</span>
|
|
@@ -130,54 +130,54 @@
|
|
|
130
130
|
<span style="width:70px;text-align:right">Time</span>
|
|
131
131
|
<span style="width:60px;text-align:right">Size</span>
|
|
132
132
|
</div>
|
|
133
|
-
<div id="request-list">${t.map(
|
|
134
|
-
`}};u([
|
|
133
|
+
<div id="request-list">${t.map(e=>this.renderRequestRow(e))}</div>
|
|
134
|
+
`}};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`
|
|
135
135
|
<div class="fetch-summary">
|
|
136
|
-
<bk-stat-card value=${String(
|
|
136
|
+
<bk-stat-card value=${String(r.length)} label="Total Fetches"></bk-stat-card>
|
|
137
137
|
<bk-stat-card value=${String(t.size)} label="Unique URLs"></bk-stat-card>
|
|
138
|
-
<bk-stat-card value=${String(
|
|
139
|
-
<bk-stat-card value=${
|
|
138
|
+
<bk-stat-card value=${String(e)} label="Errors" color=${e>0?"var(--red)":""}></bk-stat-card>
|
|
139
|
+
<bk-stat-card value=${y(i)} label="Avg Duration"></bk-stat-card>
|
|
140
140
|
</div>
|
|
141
|
-
`}formatTime(
|
|
141
|
+
`}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`
|
|
142
142
|
<div class="fetch-group">
|
|
143
143
|
<div class="fetch-group-header">
|
|
144
|
-
<bk-method-badge .method=${
|
|
145
|
-
<span class="fetch-group-url" title=${
|
|
146
|
-
${n>0?
|
|
147
|
-
<span class="fetch-group-count">${
|
|
144
|
+
<bk-method-badge .method=${r.method}></bk-method-badge>
|
|
145
|
+
<span class="fetch-group-url" title=${r.url}>${r.url}</span>
|
|
146
|
+
${n>0?l`<bk-status-pill .code=${n}></bk-status-pill>`:p}
|
|
147
|
+
<span class="fetch-group-count">${r.count}x</span>
|
|
148
148
|
</div>
|
|
149
149
|
<div class="fetch-group-meta">
|
|
150
|
-
<span>avg ${
|
|
150
|
+
<span>avg ${y(t)}</span>
|
|
151
151
|
<span class="fetch-group-sep">\u00b7</span>
|
|
152
|
-
<span>max ${
|
|
152
|
+
<span>max ${y(r.maxDur)}</span>
|
|
153
153
|
<span class="fetch-group-sep">\u00b7</span>
|
|
154
|
-
${
|
|
154
|
+
${e>0?l`<span class="fetch-group-err">${e}% errors</span>`:l`<span class="fetch-group-ok">0% errors</span>`}
|
|
155
155
|
</div>
|
|
156
|
-
${
|
|
156
|
+
${r.firstTs>0?l`
|
|
157
157
|
<div class="fetch-group-timeline">
|
|
158
158
|
<span class="fetch-group-timeline-dot"></span>
|
|
159
159
|
<span class="fetch-group-timeline-range">
|
|
160
|
-
${this.formatTime(
|
|
160
|
+
${this.formatTime(r.firstTs)}${r.firstTs!==r.lastTs?l` \u2192 ${this.formatTime(r.lastTs)}`:p}
|
|
161
161
|
</span>
|
|
162
|
-
</div>`:
|
|
163
|
-
${
|
|
162
|
+
</div>`:p}
|
|
163
|
+
${s.length>0?l`
|
|
164
164
|
<div class="fetch-group-callers">
|
|
165
165
|
<span class="fetch-group-callers-label">Called by</span>
|
|
166
|
-
${
|
|
167
|
-
</div>`:
|
|
166
|
+
${s.map(a=>l`<span class="fetch-group-caller-pill">${a}</span>`)}
|
|
167
|
+
</div>`:p}
|
|
168
168
|
</div>
|
|
169
|
-
`}render(){let
|
|
169
|
+
`}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`
|
|
170
170
|
<div class="fetch-analysis" id="fetch-analysis">
|
|
171
|
-
${this.renderSummary(
|
|
172
|
-
${
|
|
173
|
-
<div class="fetch-groups-title">Grouped by URL (${
|
|
174
|
-
<div class="fetch-groups">${
|
|
175
|
-
`:
|
|
171
|
+
${this.renderSummary(r)}
|
|
172
|
+
${e.length>0?l`
|
|
173
|
+
<div class="fetch-groups-title">Grouped by URL (${e.length})</div>
|
|
174
|
+
<div class="fetch-groups">${e.map(s=>this.renderGroup(s))}</div>
|
|
175
|
+
`:p}
|
|
176
176
|
</div>
|
|
177
|
-
`}};u([
|
|
177
|
+
`}};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
|
|
178
178
|
title="No actions yet"
|
|
179
179
|
subtitle="Start using your app to see user action flows here"
|
|
180
|
-
></bk-empty-state>`:
|
|
180
|
+
></bk-empty-state>`:l`
|
|
181
181
|
<div id="flow-col-header" class="col-header">
|
|
182
182
|
<span style="width:8px"></span>
|
|
183
183
|
<span style="flex:1">Action</span>
|
|
@@ -186,15 +186,15 @@
|
|
|
186
186
|
<span style="width:70px;text-align:right">Time</span>
|
|
187
187
|
</div>
|
|
188
188
|
<div id="flow-list">
|
|
189
|
-
${t.map((s
|
|
189
|
+
${t.map((e,s)=>this.renderFlowRow(e,s))}
|
|
190
190
|
</div>
|
|
191
|
-
`}renderFlowRow(t,
|
|
191
|
+
`}renderFlowRow(t,e){let s=this.expandedFlowIdx===e,i=this.flowDotClass(t),n=this.flowBadgeInfo(t);return l`
|
|
192
192
|
<div
|
|
193
|
-
class="flow-row ${
|
|
194
|
-
@click=${()=>this.toggleFlow(
|
|
193
|
+
class="flow-row ${s?"expanded":""}"
|
|
194
|
+
@click=${()=>this.toggleFlow(e)}
|
|
195
195
|
>
|
|
196
196
|
<div class="flow-summary-row">
|
|
197
|
-
<span class="flow-status-dot ${
|
|
197
|
+
<span class="flow-status-dot ${i}"></span>
|
|
198
198
|
<span class="flow-label">${t.label}</span>
|
|
199
199
|
<span class="flow-req-count"
|
|
200
200
|
>${t.requests.length}
|
|
@@ -202,39 +202,39 @@
|
|
|
202
202
|
>
|
|
203
203
|
<span class="flow-badge-pill ${n.cls}">${n.text}</span>
|
|
204
204
|
<span class="flow-duration"
|
|
205
|
-
>${
|
|
205
|
+
>${y(t.totalDurationMs)}</span
|
|
206
206
|
>
|
|
207
207
|
</div>
|
|
208
208
|
</div>
|
|
209
|
-
<div class="flow-expand ${
|
|
210
|
-
${
|
|
209
|
+
<div class="flow-expand ${s?"open":""}">
|
|
210
|
+
${s?this.renderFlowDetail(t):p}
|
|
211
211
|
</div>
|
|
212
|
-
`}renderFlowDetail(t){let
|
|
212
|
+
`}renderFlowDetail(t){let e=this.viewMode==="simple"?"Insights":"Details";return l`
|
|
213
213
|
<div class="flow-detail-tabs">
|
|
214
214
|
<button
|
|
215
215
|
class="flow-tab ${this.flowDetailTab==="insights"?"active":""}"
|
|
216
|
-
@click=${
|
|
216
|
+
@click=${s=>this.switchTab("insights",t,s)}
|
|
217
217
|
>
|
|
218
|
-
${
|
|
218
|
+
${e}
|
|
219
219
|
</button>
|
|
220
220
|
<button
|
|
221
221
|
class="flow-tab ${this.flowDetailTab==="timeline"?"active":""}"
|
|
222
|
-
@click=${
|
|
222
|
+
@click=${s=>this.switchTab("timeline",t,s)}
|
|
223
223
|
>
|
|
224
224
|
Timeline
|
|
225
225
|
</button>
|
|
226
226
|
</div>
|
|
227
227
|
${this.flowDetailTab==="insights"?this.viewMode==="simple"?this.renderFlowInsights(t):this.renderFlowSubReqs(t):this.renderFlowWaterfall(t)}
|
|
228
|
-
`}renderFlowWaterfall(t){if(this.flowTimelineLoading)return
|
|
228
|
+
`}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`
|
|
229
229
|
<div class="flow-waterfall">
|
|
230
230
|
<div class="wf-time-axis">
|
|
231
|
-
${
|
|
231
|
+
${i.map(n=>l`<span>${n}</span>`)}
|
|
232
232
|
</div>
|
|
233
233
|
<div class="wf-rows">
|
|
234
|
-
${
|
|
234
|
+
${e.map(n=>this.renderWaterfallGroup(n))}
|
|
235
235
|
</div>
|
|
236
236
|
</div>
|
|
237
|
-
`}renderWaterfallGroup(t){return
|
|
237
|
+
`}renderWaterfallGroup(t){return l`
|
|
238
238
|
<div class="wf-request-group">
|
|
239
239
|
<div class="wf-req-row" title="${t.tooltip}">
|
|
240
240
|
<div class="wf-req-label">${t.label}</div>
|
|
@@ -246,109 +246,109 @@
|
|
|
246
246
|
</div>
|
|
247
247
|
<div class="wf-req-dur">${t.durLabel}</div>
|
|
248
248
|
</div>
|
|
249
|
-
${t.subEvents.length>0?t.subEvents.map(
|
|
250
|
-
<div class="wf-sub-row" title="${
|
|
249
|
+
${t.subEvents.length>0?t.subEvents.map(e=>l`
|
|
250
|
+
<div class="wf-sub-row" title="${e.tooltip}">
|
|
251
251
|
<div class="wf-sub-label">
|
|
252
252
|
<span
|
|
253
253
|
class="wf-sub-dot"
|
|
254
|
-
style="background:${
|
|
254
|
+
style="background:${ts(e.type)}"
|
|
255
255
|
></span>
|
|
256
|
-
${
|
|
256
|
+
${e.label}
|
|
257
257
|
</div>
|
|
258
258
|
<div class="wf-bar-track">
|
|
259
259
|
<div
|
|
260
260
|
class="wf-bar wf-sub-bar-sized"
|
|
261
|
-
style="left:${t.leftPct+
|
|
261
|
+
style="left:${t.leftPct+e.leftPct/100*t.widthPct}%;width:${e.widthPct/100*t.widthPct}%;background:${ts(e.type)}"
|
|
262
262
|
></div>
|
|
263
263
|
</div>
|
|
264
|
-
<div class="wf-sub-dur">${
|
|
264
|
+
<div class="wf-sub-dur">${e.durLabel}</div>
|
|
265
265
|
</div>
|
|
266
|
-
`):
|
|
266
|
+
`):p}
|
|
267
267
|
</div>
|
|
268
|
-
`}renderFlowInsights(t){let
|
|
268
|
+
`}renderFlowInsights(t){let e=rr(t),s=e.errors.length>0||e.duplicates.length>0||e.warnings.length>0||!!e.tip;return l`
|
|
269
269
|
<div>
|
|
270
270
|
<div class="flow-traffic">
|
|
271
|
-
${t.requests.map(
|
|
271
|
+
${t.requests.map(i=>this.renderTrafficCard(i))}
|
|
272
272
|
</div>
|
|
273
|
-
${
|
|
273
|
+
${s?l`
|
|
274
274
|
<div class="flow-divider"></div>
|
|
275
275
|
<div class="flow-insights">
|
|
276
|
-
${
|
|
277
|
-
✗ ${
|
|
276
|
+
${e.errors.map(i=>l`<div class="insight-line insight-error">
|
|
277
|
+
✗ ${i}
|
|
278
278
|
</div>`)}
|
|
279
|
-
${
|
|
280
|
-
⚠ ${
|
|
281
|
-
~${
|
|
279
|
+
${e.duplicates.map(i=>l`<div class="insight-line insight-warn">
|
|
280
|
+
⚠ ${i.name} — loaded ${i.count}x (wasting
|
|
281
|
+
~${y(i.wastedMs)})
|
|
282
282
|
</div>`)}
|
|
283
|
-
${
|
|
284
|
-
${
|
|
285
|
-
Tip: ${
|
|
286
|
-
</div>`:
|
|
283
|
+
${e.warnings.map(i=>l`<div class="insight-line insight-warn">⚠ ${i}</div>`)}
|
|
284
|
+
${e.tip?l`<div class="insight-line insight-tip">
|
|
285
|
+
Tip: ${e.tip}
|
|
286
|
+
</div>`:p}
|
|
287
287
|
</div>
|
|
288
|
-
`:
|
|
288
|
+
`:p}
|
|
289
289
|
</div>
|
|
290
|
-
`}renderTrafficCard(t){if(
|
|
290
|
+
`}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`
|
|
291
291
|
<div
|
|
292
292
|
class="traffic-card ${t.isStrictModeDupe?"strict-mode-dupe":""}"
|
|
293
293
|
>
|
|
294
|
-
<div class="traffic-card-header ${
|
|
294
|
+
<div class="traffic-card-header ${i?"has-details":""}">
|
|
295
295
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
296
296
|
<span class="traffic-card-path ${t.isDuplicate?"is-dup":""}"
|
|
297
297
|
>${t.label}</span
|
|
298
298
|
>
|
|
299
|
-
<span class="status-pill ${
|
|
300
|
-
<span class="traffic-card-dur">${
|
|
301
|
-
${t.isDuplicate?
|
|
302
|
-
>${
|
|
299
|
+
<span class="status-pill ${e}">${t.statusCode}</span>
|
|
300
|
+
<span class="traffic-card-dur">${s}</span>
|
|
301
|
+
${t.isDuplicate?l`<span class="traffic-card-dup">duplicate</span>`:l`<span class="traffic-card-size"
|
|
302
|
+
>${ot(t.responseSize)}</span
|
|
303
303
|
>`}
|
|
304
304
|
</div>
|
|
305
|
-
${t.isStrictModeDupe?
|
|
305
|
+
${t.isStrictModeDupe?l`<div class="strict-mode-banner">
|
|
306
306
|
React Strict Mode duplicate — does not happen in production
|
|
307
|
-
</div>`:
|
|
308
|
-
${!t.isDuplicate&&t.category!==
|
|
307
|
+
</div>`:p}
|
|
308
|
+
${!t.isDuplicate&&t.category!==pe&&t.category!==Et?l`<div
|
|
309
309
|
class="request-timeline tl-hidden"
|
|
310
310
|
data-request-id=${t.id}
|
|
311
311
|
data-request-started=${String(t.startedAt)}
|
|
312
|
-
></div>`:
|
|
313
|
-
${t.requestBody&&t.method!=="GET"?this.renderBodyToggle("out","Request Body",t.requestBody):
|
|
314
|
-
${t.responseBody?this.renderBodyToggle("in","Response Body",t.responseBody):
|
|
312
|
+
></div>`:p}
|
|
313
|
+
${t.requestBody&&t.method!=="GET"?this.renderBodyToggle("out","Request Body",t.requestBody):p}
|
|
314
|
+
${t.responseBody?this.renderBodyToggle("in","Response Body",t.responseBody):p}
|
|
315
315
|
</div>
|
|
316
|
-
`}renderBodyToggle(t,s
|
|
316
|
+
`}renderBodyToggle(t,e,s){let i=t==="out"?"\u2192":"\u2190";return l`
|
|
317
317
|
<div class="traffic-body">
|
|
318
318
|
<button class="traffic-body-toggle" @click=${this.toggleBodyBlock}>
|
|
319
319
|
<span class="chevron">▸</span
|
|
320
|
-
><span class="arrow-${t}">${
|
|
320
|
+
><span class="arrow-${t}">${i}</span> ${e}
|
|
321
321
|
</button>
|
|
322
|
-
<pre
|
|
322
|
+
<pre>${G(dt(s))}</pre>
|
|
323
323
|
</div>
|
|
324
|
-
`}renderFlowSubReqs(t){return
|
|
325
|
-
${t.requests.map((s
|
|
326
|
-
</div>`}renderSubReqRow(t,
|
|
324
|
+
`}renderFlowSubReqs(t){return l`<div class="flow-subreqs">
|
|
325
|
+
${t.requests.map((e,s)=>this.renderSubReqRow(e,s))}
|
|
326
|
+
</div>`}renderSubReqRow(t,e){let s=this.expandedSubReqIdx===e,i=St(t.statusCode),n=t.pollingDurationMs?y(t.pollingDurationMs):y(t.durationMs);return l`
|
|
327
327
|
<div
|
|
328
|
-
class="flow-subreq ${
|
|
329
|
-
@click=${
|
|
328
|
+
class="flow-subreq ${s?"expanded":""}"
|
|
329
|
+
@click=${a=>this.toggleSubReq(e,a)}
|
|
330
330
|
>
|
|
331
331
|
<bk-method-badge .method=${t.method}></bk-method-badge>
|
|
332
332
|
<span class="subreq-label ${t.isDuplicate?"is-dup":""}"
|
|
333
333
|
>${t.path||t.url}</span
|
|
334
334
|
>
|
|
335
|
-
${t.isDuplicate?
|
|
336
|
-
<span class="status-pill ${
|
|
335
|
+
${t.isDuplicate?l`<span class="subreq-dup-tag">duplicate</span>`:p}
|
|
336
|
+
<span class="status-pill ${i}">${t.statusCode}</span>
|
|
337
337
|
<span class="subreq-dur">${n}</span>
|
|
338
338
|
</div>
|
|
339
|
-
<div class="flow-subreq-detail ${
|
|
340
|
-
${
|
|
339
|
+
<div class="flow-subreq-detail ${s?"open":""}">
|
|
340
|
+
${s?this.renderSubReqDetail(t):p}
|
|
341
341
|
</div>
|
|
342
|
-
`}renderSubReqDetail(t){let
|
|
342
|
+
`}renderSubReqDetail(t){let e=St(t.statusCode);return l`
|
|
343
343
|
<div class="detail-meta">
|
|
344
344
|
<span
|
|
345
|
-
><bk-method-badge .method=${t.method}></bk-method-badge> ${
|
|
345
|
+
><bk-method-badge .method=${t.method}></bk-method-badge> ${it(t.url)}</span
|
|
346
346
|
>
|
|
347
347
|
<span
|
|
348
|
-
><span class="status-pill ${
|
|
348
|
+
><span class="status-pill ${e}">${t.statusCode}</span></span
|
|
349
349
|
>
|
|
350
350
|
<span>${t.durationMs}ms</span>
|
|
351
|
-
${t.responseSize?
|
|
351
|
+
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:p}
|
|
352
352
|
</div>
|
|
353
353
|
<div
|
|
354
354
|
class="request-timeline tl-hidden"
|
|
@@ -358,30 +358,30 @@
|
|
|
358
358
|
<div class="detail-grid">
|
|
359
359
|
<div class="detail-section">
|
|
360
360
|
<h4>Request Headers</h4>
|
|
361
|
-
<pre
|
|
361
|
+
<pre>${G(Tt(t.headers))}</pre>
|
|
362
362
|
</div>
|
|
363
363
|
<div class="detail-section">
|
|
364
364
|
<h4>Response Headers</h4>
|
|
365
|
-
<pre
|
|
365
|
+
<pre>${G(Tt(t.responseHeaders))}</pre>
|
|
366
366
|
</div>
|
|
367
367
|
<div class="detail-section">
|
|
368
368
|
<h4>Request Body</h4>
|
|
369
|
-
<pre
|
|
369
|
+
<pre>${G(dt(t.requestBody))}</pre>
|
|
370
370
|
</div>
|
|
371
371
|
<div class="detail-section">
|
|
372
372
|
<h4>Response Body</h4>
|
|
373
|
-
<pre
|
|
373
|
+
<pre>${G(dt(t.responseBody))}</pre>
|
|
374
374
|
</div>
|
|
375
375
|
</div>
|
|
376
376
|
<div class="detail-actions">
|
|
377
377
|
<button
|
|
378
378
|
class="btn btn-curl"
|
|
379
|
-
@click=${
|
|
379
|
+
@click=${s=>{s.stopPropagation(),Lt(t);}}
|
|
380
380
|
>
|
|
381
381
|
Copy cURL
|
|
382
382
|
</button>
|
|
383
383
|
</div>
|
|
384
|
-
`}};u([
|
|
384
|
+
`}};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`
|
|
385
385
|
<div class="sec-clear">
|
|
386
386
|
<span class="sec-clear-icon">\u2713</span>
|
|
387
387
|
<div class="sec-clear-text">
|
|
@@ -389,180 +389,142 @@
|
|
|
389
389
|
<div class="sec-clear-sub">No security or quality issues detected this session</div>
|
|
390
390
|
</div>
|
|
391
391
|
</div>
|
|
392
|
-
`:
|
|
392
|
+
`: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`
|
|
393
393
|
<div id="security-content">
|
|
394
|
-
${this.renderSummary(t.length,
|
|
395
|
-
${t.length===0&&
|
|
394
|
+
${this.renderSummary(t.length,e.length,s,i,n)}
|
|
395
|
+
${t.length===0&&e.length>0?l`
|
|
396
396
|
<div class="sec-clear">
|
|
397
397
|
<span class="sec-clear-icon">\u2713</span>
|
|
398
398
|
<div class="sec-clear-text">
|
|
399
399
|
<div class="sec-clear-title">All issues resolved</div>
|
|
400
|
-
<div class="sec-clear-sub">${
|
|
400
|
+
<div class="sec-clear-sub">${e.length} finding${e.length!==1?"s were":" was"} detected and fixed</div>
|
|
401
401
|
</div>
|
|
402
402
|
</div>
|
|
403
|
-
`:
|
|
404
|
-
${t.length>0?this.renderOpenGroups(t):
|
|
405
|
-
${
|
|
403
|
+
`:p}
|
|
404
|
+
${t.length>0?this.renderOpenGroups(t):p}
|
|
405
|
+
${e.length>0?this.renderResolved(e):p}
|
|
406
406
|
</div>
|
|
407
|
-
`}renderSummary(
|
|
407
|
+
`}renderSummary(r,t,e,s,i){return l`
|
|
408
408
|
<div class="sec-summary">
|
|
409
409
|
<div class="sec-summary-left">
|
|
410
|
-
<span class="sec-summary-count">${
|
|
411
|
-
<span class="sec-summary-label">open issue${
|
|
412
|
-
${t>0?
|
|
410
|
+
<span class="sec-summary-count">${r}</span>
|
|
411
|
+
<span class="sec-summary-label">open issue${r!==1?"s":""}</span>
|
|
412
|
+
${t>0?l`<span class="sec-resolved-badge">${t} resolved</span>`:p}
|
|
413
413
|
</div>
|
|
414
414
|
<div class="sec-summary-right">
|
|
415
|
-
${
|
|
416
|
-
${
|
|
417
|
-
${
|
|
415
|
+
${e>0?l`<span class="sec-badge critical">${e} critical</span>`:p}
|
|
416
|
+
${s>0?l`<span class="sec-badge warning">${s} warning</span>`:p}
|
|
417
|
+
${i>0?l`<span class="sec-badge info">${i} info</span>`:p}
|
|
418
418
|
</div>
|
|
419
419
|
</div>
|
|
420
|
-
`}renderOpenGroups(
|
|
420
|
+
`}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`
|
|
421
421
|
<div class="sec-group">
|
|
422
422
|
<div class="sec-group-header">
|
|
423
423
|
<span class="sec-group-icon ${t.cls}">${t.icon}</span>
|
|
424
|
-
<span class="sec-group-title">${
|
|
425
|
-
<span class="sec-group-count">${
|
|
424
|
+
<span class="sec-group-title">${r.title}</span>
|
|
425
|
+
<span class="sec-group-count">${r.items.length}</span>
|
|
426
426
|
</div>
|
|
427
|
-
${
|
|
428
|
-
<div class="sec-items">${
|
|
427
|
+
${r.hint?l`<div class="sec-hint">${r.hint}</div>`:p}
|
|
428
|
+
<div class="sec-items">${r.items.map(e=>this.renderIssueItem(e))}</div>
|
|
429
429
|
</div>
|
|
430
|
-
`}renderIssueItem(
|
|
430
|
+
`}renderIssueItem(r){let t=r.issue;return l`
|
|
431
431
|
<div class="sec-item">
|
|
432
432
|
<div class="sec-item-desc">${t.desc}</div>
|
|
433
|
-
${
|
|
434
|
-
${
|
|
435
|
-
${
|
|
433
|
+
${r.occurrences>1?l`<span class="sec-item-count">${r.occurrences}x</span>`:p}
|
|
434
|
+
${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}
|
|
435
|
+
${r.aiNotes?l`<div class="sec-ai-notes">${r.aiNotes}</div>`:p}
|
|
436
436
|
</div>
|
|
437
|
-
`}renderResolved(
|
|
437
|
+
`}renderResolved(r){return l`
|
|
438
438
|
<div class="sec-resolved-title">
|
|
439
439
|
<span class="sec-resolved-check">\u2713</span> Resolved
|
|
440
|
-
<span class="sec-resolved-count">${
|
|
440
|
+
<span class="sec-resolved-count">${r.length}</span>
|
|
441
441
|
</div>
|
|
442
442
|
<div class="sec-group sec-group-resolved">
|
|
443
443
|
<div class="sec-items">
|
|
444
|
-
${
|
|
444
|
+
${r.map(t=>l`
|
|
445
445
|
<div class="sec-item sec-item-resolved">
|
|
446
446
|
<span class="sec-resolved-item-icon">\u2713</span>
|
|
447
447
|
<div class="sec-item-desc">${t.issue.title} \u2014 ${t.issue.endpoint||"global"}</div>
|
|
448
|
-
${t.aiStatus==="fixed"?
|
|
449
|
-
${t.aiNotes?
|
|
448
|
+
${t.aiStatus==="fixed"?l`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:p}
|
|
449
|
+
${t.aiNotes?l`<div class="sec-ai-notes">${t.aiNotes}</div>`:p}
|
|
450
450
|
</div>
|
|
451
451
|
`)}
|
|
452
452
|
</div>
|
|
453
453
|
</div>
|
|
454
|
-
`}};u([
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
${m.length===0&&T.length>0?a`<div class="ov-clear">
|
|
464
|
-
<span class="ov-clear-icon">\u2713</span>All issues resolved \u2014
|
|
465
|
-
${T.length} finding${T.length!==1?"s were":" was"} detected and
|
|
466
|
-
fixed
|
|
467
|
-
</div>`:d}
|
|
468
|
-
${m.length>0?this.renderOpenIssues(m):d}
|
|
469
|
-
${E.length>0?this.renderVerifying(E):d}
|
|
470
|
-
${T.length>0?this.renderResolvedIssues(T):d}
|
|
471
|
-
</div>
|
|
472
|
-
`}renderSummary(t,s,r,o,n,l){return a`
|
|
473
|
-
<div class="ov-summary">
|
|
474
|
-
<div class="ov-stat"><span class="ov-stat-value">${t}</span><span class="ov-stat-label">Requests</span></div>
|
|
475
|
-
<div class="ov-stat"><span class="ov-stat-value">${s}</span><span class="ov-stat-label">Actions</span></div>
|
|
476
|
-
<div class="ov-stat"><span class="ov-stat-value">${v(r)}</span><span class="ov-stat-label">Avg Response</span></div>
|
|
477
|
-
<div class="ov-stat"><span class="ov-stat-value">${o}</span><span class="ov-stat-label">Queries</span></div>
|
|
478
|
-
<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>
|
|
479
|
-
<div class="ov-stat"><span class="ov-stat-value">${l}</span><span class="ov-stat-label">Fetches</span></div>
|
|
480
|
-
</div>
|
|
481
|
-
`}renderOpenIssues(t){return a`
|
|
482
|
-
<div class="ov-section-title">Issues Found <span class="ov-issue-count">${t.length}</span></div>
|
|
483
|
-
<div class="ov-cards">${t.map((s,r)=>this.renderIssueCard(s,r))}</div>
|
|
484
|
-
`}renderIssueCard(t,s){let r=t.issue,o=q[r.severity]||q.info,n=this.expandedCardIdx===s,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>`:d,c=t.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Resolving\u2026 ${t.cleanHitsSinceLastSeen}/${5} clean requests</div>`:d;return a`
|
|
485
|
-
<div class="ov-card ${n?"expanded":""}" @click=${()=>this.toggleCard(s)}>
|
|
486
|
-
<span class="ov-card-icon ${o.cls}">${o.icon}</span>
|
|
487
|
-
<div class="ov-card-body">
|
|
488
|
-
<div class="ov-card-title">${r.title}${l}</div>
|
|
489
|
-
<div class="ov-card-desc">${r.desc}</div>
|
|
490
|
-
${r.detail?a`<div class="ov-card-detail">${r.detail}</div>`:d}
|
|
491
|
-
${c}
|
|
492
|
-
${n&&r.hint?a`<div class="ov-card-hint">${r.hint}</div>`:d}
|
|
493
|
-
</div>
|
|
494
|
-
${r.hint?a`<span class="ov-card-arrow">${n?"\u2193":"\u2192"}</span>`:d}
|
|
495
|
-
</div>
|
|
496
|
-
`}renderVerifying(t){return a`
|
|
497
|
-
<div class="ov-section-title ov-resolved-title">
|
|
498
|
-
<span style="color:var(--yellow,#f5a623)">\u29d7</span> Awaiting Verification
|
|
499
|
-
<span class="ov-issue-count">${t.length}</span>
|
|
500
|
-
</div>
|
|
501
|
-
<div class="ov-cards">
|
|
502
|
-
${t.map(s=>{let r=s.issue,o=s.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Verifying\u2026 ${s.cleanHitsSinceLastSeen}/${5} clean requests</div>`:d;return a`
|
|
503
|
-
<div class="ov-card ov-card-resolved">
|
|
504
|
-
<span class="ov-card-icon resolved">\u29d7</span>
|
|
505
|
-
<div class="ov-card-body">
|
|
506
|
-
<div class="ov-card-title" style="color:var(--text-muted)">
|
|
507
|
-
${r.title}
|
|
508
|
-
<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>
|
|
509
|
-
</div>
|
|
510
|
-
<div class="ov-card-desc">${r.desc}</div>
|
|
511
|
-
${o}
|
|
512
|
-
</div>
|
|
513
|
-
</div>
|
|
514
|
-
`})}
|
|
454
|
+
`}};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`
|
|
455
|
+
<div class="ov-container">
|
|
456
|
+
<div class="ov-grid">
|
|
457
|
+
${this.renderActionsCard(t.flows)}
|
|
458
|
+
${this.renderInsightsCard(t.issues)}
|
|
459
|
+
${this.renderPerformanceCard(e)}
|
|
460
|
+
${this.renderErrorsCard(e)}
|
|
461
|
+
${this.renderGraphCard()}
|
|
462
|
+
</div>
|
|
515
463
|
</div>
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
<div class="ov-card
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
464
|
+
`:l`<bk-empty-state
|
|
465
|
+
title="${O.EMPTY_TITLE}"
|
|
466
|
+
subtitle="${O.EMPTY_SUBTITLE_OVERVIEW}"
|
|
467
|
+
></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`
|
|
468
|
+
<div class="ov-card-nav" @click=${()=>this.navigateToExplorerErrors()}>
|
|
469
|
+
<div class="ov-card-icon-lg" style="color:var(--red)">\u2715</div>
|
|
470
|
+
<div class="ov-card-content">
|
|
471
|
+
<div class="ov-card-headline">${e.length} ${B(e.length,"error")}</div>
|
|
472
|
+
<div class="ov-card-context">${i.join(" \xB7 ")}</div>
|
|
473
|
+
</div>
|
|
474
|
+
<span class="ov-card-arrow">\u2192</span>
|
|
475
|
+
</div>
|
|
476
|
+
`}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`
|
|
477
|
+
<div class="ov-card-nav" @click=${()=>this.navigateTo(i)}>
|
|
478
|
+
<div class="ov-card-icon-lg" style="color:${n}">${t}</div>
|
|
479
|
+
<div class="ov-card-content">
|
|
480
|
+
<div class="ov-card-headline">${e}</div>
|
|
481
|
+
${s?l`<div class="ov-card-context">${s}</div>`:p}
|
|
482
|
+
</div>
|
|
483
|
+
<span class="ov-card-arrow">\u2192</span>
|
|
531
484
|
</div>
|
|
532
|
-
`}
|
|
485
|
+
`}renderEmptyCard(t,e,s,i){return l`
|
|
486
|
+
<div class="ov-card-nav ov-card-empty" @click=${()=>this.navigateTo(i)}>
|
|
487
|
+
<div class="ov-card-icon-lg">${t}</div>
|
|
488
|
+
<div class="ov-card-content">
|
|
489
|
+
<div class="ov-card-headline">${e}</div>
|
|
490
|
+
<div class="ov-card-context">${s}</div>
|
|
491
|
+
</div>
|
|
492
|
+
<span class="ov-card-arrow">\u2192</span>
|
|
493
|
+
</div>
|
|
494
|
+
`}};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`
|
|
533
495
|
<div id="graph-content">
|
|
534
496
|
${this.renderSelector()}
|
|
535
|
-
${this.selectedEndpoint===
|
|
497
|
+
${this.selectedEndpoint===rt?this.renderOverview():this.renderDetail()}
|
|
536
498
|
</div>
|
|
537
|
-
`}renderSelector(){return
|
|
499
|
+
`}renderSelector(){return l`
|
|
538
500
|
<div class="perf-selector">
|
|
539
|
-
<button class="perf-selector-btn ${this.selectedEndpoint===
|
|
540
|
-
@click=${()=>{this.selectedEndpoint=
|
|
541
|
-
${this.graphData.map((t,
|
|
501
|
+
<button class="perf-selector-btn ${this.selectedEndpoint===rt?"active":""}"
|
|
502
|
+
@click=${()=>{this.selectedEndpoint=rt;}}>Overview</button>
|
|
503
|
+
${this.graphData.map((t,e)=>l`
|
|
542
504
|
<button class="perf-selector-btn ${t.endpoint===this.selectedEndpoint?"active":""}"
|
|
543
505
|
@click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
544
|
-
<span class="perf-dot" style="background:${
|
|
506
|
+
<span class="perf-dot" style="background:${Ve[e%Ve.length]}"></span>${t.endpoint}
|
|
545
507
|
</button>
|
|
546
508
|
`)}
|
|
547
509
|
</div>
|
|
548
|
-
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return
|
|
510
|
+
`}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`
|
|
549
511
|
<div class="perf-overview">
|
|
550
512
|
<div class="perf-summary-row">
|
|
551
513
|
<div class="perf-summary-card">
|
|
552
514
|
<span class="perf-summary-label">Total Requests</span>
|
|
553
|
-
<span class="perf-summary-value">${
|
|
515
|
+
<span class="perf-summary-value">${e}</span>
|
|
554
516
|
</div>
|
|
555
517
|
<div class="perf-summary-card">
|
|
556
518
|
<span class="perf-summary-label">Avg P95</span>
|
|
557
|
-
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(
|
|
519
|
+
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(s).color}">${y(s)}</span>
|
|
558
520
|
</div>
|
|
559
521
|
<div class="perf-summary-card">
|
|
560
522
|
<span class="perf-summary-label">Error Rate</span>
|
|
561
|
-
<span class="perf-summary-value" style="color:${
|
|
523
|
+
<span class="perf-summary-value" style="color:${i>0?"var(--red)":"var(--green)"}">${Math.round(n*100)}%</span>
|
|
562
524
|
</div>
|
|
563
525
|
<div class="perf-summary-card">
|
|
564
526
|
<span class="perf-summary-label">Slowest</span>
|
|
565
|
-
<span class="perf-summary-value perf-summary-value-sm">${
|
|
527
|
+
<span class="perf-summary-value perf-summary-value-sm">${a?.endpoint??"-"}</span>
|
|
566
528
|
</div>
|
|
567
529
|
</div>
|
|
568
530
|
|
|
@@ -582,50 +544,50 @@
|
|
|
582
544
|
</tbody>
|
|
583
545
|
</table>
|
|
584
546
|
</div>
|
|
585
|
-
`}renderHeatmapRow(t){let
|
|
547
|
+
`}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`
|
|
586
548
|
<tr class="perf-table-row" @click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
587
549
|
<td class="perf-td-name">${t.endpoint}</td>
|
|
588
|
-
<td class="perf-td-right">${
|
|
550
|
+
<td class="perf-td-right">${e.totalRequests}</td>
|
|
589
551
|
<td class="perf-td-center">
|
|
590
|
-
<span class="perf-hm-p95" style="color:${
|
|
552
|
+
<span class="perf-hm-p95" style="color:${s.color};background:${s.bg};border-color:${s.border}">${y(e.p95Ms)}</span>
|
|
591
553
|
</td>
|
|
592
|
-
<td class="perf-td-center" style="color:${
|
|
593
|
-
<td class="perf-td-center" style="color:${
|
|
554
|
+
<td class="perf-td-center" style="color:${i>0?"var(--red)":"var(--text-muted)"}">${i>0?i:"-"}</td>
|
|
555
|
+
<td class="perf-td-center" style="color:${e.avgQueryCount>5?"var(--amber)":"var(--text-muted)"}">${e.avgQueryCount}</td>
|
|
594
556
|
<td>
|
|
595
557
|
<span class="perf-hm-split-bar">
|
|
596
|
-
${
|
|
597
|
-
${c>0?
|
|
598
|
-
${
|
|
558
|
+
${a>0?l`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${a}%"></span>`:p}
|
|
559
|
+
${c>0?l`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:p}
|
|
560
|
+
${d>0?l`<span class="perf-breakdown-seg perf-breakdown-app" style="width:${d}%"></span>`:p}
|
|
599
561
|
</span>
|
|
600
562
|
</td>
|
|
601
563
|
</tr>
|
|
602
|
-
`}renderDetail(){let t=this.graphData.find(n=>n.endpoint===this.selectedEndpoint);if(!t?.requests?.length)return
|
|
603
|
-
${this.renderDetailHeader(t,
|
|
604
|
-
${this.renderDetailMetrics(s,
|
|
605
|
-
${this.renderDetailBreakdown(
|
|
564
|
+
`}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`
|
|
565
|
+
${this.renderDetailHeader(t,s)}
|
|
566
|
+
${this.renderDetailMetrics(e,s,i)}
|
|
567
|
+
${this.renderDetailBreakdown(e)}
|
|
606
568
|
${this.renderCallers(t.endpoint)}
|
|
607
569
|
${this.renderQueryBreakdown()}
|
|
608
570
|
${this.renderTrends(t)}
|
|
609
571
|
${this.renderDetailChart()}
|
|
610
572
|
${this.renderDetailHistory(t)}
|
|
611
|
-
`}renderDetailHeader(t,
|
|
573
|
+
`}renderDetailHeader(t,e){return l`
|
|
612
574
|
<div class="perf-detail-header">
|
|
613
575
|
<div class="perf-detail-title">
|
|
614
|
-
<span class="perf-badge perf-badge-lg" style="color:${
|
|
576
|
+
<span class="perf-badge perf-badge-lg" style="color:${e.color};background:${e.bg};border-color:${e.border}">${e.label}</span>
|
|
615
577
|
<span>${t.endpoint}</span>
|
|
616
|
-
${t.baselineP95Ms?
|
|
578
|
+
${t.baselineP95Ms?l`<span class="perf-baseline-hint">Baseline: ${y(t.baselineP95Ms)}</span>`:p}
|
|
617
579
|
</div>
|
|
618
580
|
</div>
|
|
619
|
-
`}renderDetailMetrics(t,s
|
|
581
|
+
`}renderDetailMetrics(t,e,s){return l`
|
|
620
582
|
<div class="perf-metric-row">
|
|
621
583
|
<div class="perf-metric-card">
|
|
622
584
|
<span class="perf-metric-label">P95</span>
|
|
623
|
-
<span class="perf-metric-value" style="color:${
|
|
585
|
+
<span class="perf-metric-value" style="color:${e.color}">${y(t.p95Ms)}</span>
|
|
624
586
|
</div>
|
|
625
587
|
<div class="perf-metric-card">
|
|
626
588
|
<span class="perf-metric-label">Errors</span>
|
|
627
|
-
<span class="perf-metric-value" style="color:${
|
|
628
|
-
${
|
|
589
|
+
<span class="perf-metric-value" style="color:${s>0?"var(--red)":"var(--green)"}">
|
|
590
|
+
${s>0?s+" ("+Math.round(t.errorRate*100)+"%)":"0"}
|
|
629
591
|
</span>
|
|
630
592
|
</div>
|
|
631
593
|
<div class="perf-metric-card">
|
|
@@ -633,71 +595,71 @@
|
|
|
633
595
|
<span class="perf-metric-value" style="color:${t.avgQueryCount>5?"var(--amber)":"var(--text)"}">${t.avgQueryCount}</span>
|
|
634
596
|
</div>
|
|
635
597
|
</div>
|
|
636
|
-
`}renderDetailBreakdown(t){let
|
|
598
|
+
`}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`
|
|
637
599
|
<div class="perf-breakdown">
|
|
638
600
|
<div class="perf-section-title">Time Breakdown</div>
|
|
639
601
|
<div class="perf-breakdown-bar">
|
|
640
|
-
${
|
|
641
|
-
${
|
|
642
|
-
${n>0?
|
|
602
|
+
${s>0?l`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${s}%"></div>`:p}
|
|
603
|
+
${i>0?l`<div class="perf-breakdown-seg perf-breakdown-fetch" style="width:${i}%"></div>`:p}
|
|
604
|
+
${n>0?l`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:p}
|
|
643
605
|
</div>
|
|
644
606
|
<div class="perf-breakdown-legend">
|
|
645
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${
|
|
646
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${
|
|
647
|
-
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${
|
|
607
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${y(t.avgQueryTimeMs||0)} (${s}%)</span>
|
|
608
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${y(t.avgFetchTimeMs||0)} (${i}%)</span>
|
|
609
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${y(t.avgAppTimeMs||0)} (${n}%)</span>
|
|
648
610
|
</div>
|
|
649
611
|
</div>
|
|
650
|
-
`}renderCallers(t){let
|
|
612
|
+
`}renderCallers(t){let e=this.getCallers(t);return e.length===0?p:l`
|
|
651
613
|
<div class="perf-callers">
|
|
652
614
|
<div class="perf-section-title">Called By</div>
|
|
653
615
|
<div class="perf-callers-list">
|
|
654
|
-
${
|
|
616
|
+
${e.map(s=>l`
|
|
655
617
|
<div class="perf-caller-row">
|
|
656
|
-
<span class="perf-caller-name">${
|
|
657
|
-
<span class="perf-caller-count">${
|
|
658
|
-
<span class="perf-caller-avg">avg ${
|
|
618
|
+
<span class="perf-caller-name">${s.label}</span>
|
|
619
|
+
<span class="perf-caller-count">${s.count} call${s.count!==1?"s":""}</span>
|
|
620
|
+
<span class="perf-caller-avg">avg ${y(s.avgMs)}</span>
|
|
659
621
|
</div>
|
|
660
622
|
`)}
|
|
661
623
|
</div>
|
|
662
624
|
</div>
|
|
663
|
-
`}renderQueryBreakdown(){return this.queryBreakdownLoading?
|
|
625
|
+
`}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`
|
|
664
626
|
<div class="perf-queries">
|
|
665
627
|
<div class="perf-section-title">DB Queries</div>
|
|
666
628
|
<div class="perf-queries-list">
|
|
667
|
-
${this.queryBreakdown.map(t=>
|
|
629
|
+
${this.queryBreakdown.map(t=>l`
|
|
668
630
|
<div class="perf-query-row">
|
|
669
631
|
<span class="perf-query-label">${t.label}</span>
|
|
670
|
-
<span class="perf-query-avg">avg ${
|
|
632
|
+
<span class="perf-query-avg">avg ${y(t.avgMs)}</span>
|
|
671
633
|
<span class="perf-query-count">${t.count} call${t.count!==1?"s":""}</span>
|
|
672
634
|
</div>
|
|
673
635
|
`)}
|
|
674
636
|
</div>
|
|
675
637
|
</div>
|
|
676
|
-
`}renderTrends(t){let
|
|
638
|
+
`}renderTrends(t){let e=t.sessions;if(!e||e.length===0)return p;let s=e.slice(-10);return l`
|
|
677
639
|
<div class="perf-trends">
|
|
678
640
|
<div class="perf-section-title">Session Trend</div>
|
|
679
641
|
<div class="perf-trends-list">
|
|
680
|
-
${
|
|
642
|
+
${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`
|
|
681
643
|
<div class="perf-trend-row ${h?"perf-trend-current":""}">
|
|
682
|
-
<span class="perf-trend-time">${h?"Current":
|
|
644
|
+
<span class="perf-trend-time">${h?"Current":d}</span>
|
|
683
645
|
<span class="perf-trend-p95">
|
|
684
646
|
<span class="perf-hm-p95" style="color:${m.color};background:${m.bg};border-color:${m.border}">
|
|
685
|
-
p95: ${
|
|
647
|
+
p95: ${y(i.p95DurationMs)}
|
|
686
648
|
</span>
|
|
687
649
|
</span>
|
|
688
|
-
<span class="perf-trend-reqs">${
|
|
689
|
-
<span class="perf-trend-errs" style="color:${
|
|
690
|
-
${c?
|
|
650
|
+
<span class="perf-trend-reqs">${i.requestCount} req${i.requestCount!==1?"s":""}</span>
|
|
651
|
+
<span class="perf-trend-errs" style="color:${i.errorCount>0?"var(--red)":"var(--text-dim)"}">${i.errorCount} err${i.errorCount!==1?"s":""}</span>
|
|
652
|
+
${c?l`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:p}
|
|
691
653
|
</div>
|
|
692
654
|
`})}
|
|
693
655
|
</div>
|
|
694
656
|
</div>
|
|
695
|
-
`}formatTimeAgo(t){let
|
|
657
|
+
`}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`
|
|
696
658
|
<div class="perf-chart-wrap">
|
|
697
659
|
<div class="perf-section-title">Response Time</div>
|
|
698
660
|
<canvas id="perf-detail-canvas" class="perf-canvas" style="width:100%;height:240px"></canvas>
|
|
699
661
|
</div>
|
|
700
|
-
`}renderDetailHistory(t){if(t.requests.length===0)return
|
|
662
|
+
`}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`
|
|
701
663
|
<div class="perf-history-wrap">
|
|
702
664
|
<table class="perf-table">
|
|
703
665
|
<thead>
|
|
@@ -711,145 +673,538 @@
|
|
|
711
673
|
</tr>
|
|
712
674
|
</thead>
|
|
713
675
|
<tbody>
|
|
714
|
-
${
|
|
676
|
+
${e.map(s=>this.renderHistoryRow(s.point,s.originalIndex,t.baselineP95Ms))}
|
|
715
677
|
</tbody>
|
|
716
678
|
</table>
|
|
717
679
|
</div>
|
|
718
|
-
`}renderHistoryRow(t,s
|
|
719
|
-
<tr class="perf-table-row ${
|
|
680
|
+
`}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`
|
|
681
|
+
<tr class="perf-table-row ${a?"perf-row-err":""}" data-req-idx=${e}>
|
|
720
682
|
<td class="perf-td-muted">${n}</td>
|
|
721
683
|
<td>
|
|
722
|
-
<span class="perf-badge perf-badge-sm" style="color:${
|
|
684
|
+
<span class="perf-badge perf-badge-sm" style="color:${i.color};background:${i.bg};border-color:${i.border}">${i.label}</span>
|
|
723
685
|
</td>
|
|
724
|
-
<td>${
|
|
686
|
+
<td>${y(t.durationMs)}</td>
|
|
725
687
|
<td>
|
|
726
|
-
${c>0?
|
|
727
|
-
${
|
|
728
|
-
<span class="perf-bd-tag perf-bd-tag-app">App ${
|
|
688
|
+
${c>0?l`<span class="perf-bd-tag perf-bd-tag-db">DB ${y(c)}</span>`:p}
|
|
689
|
+
${d>0?l`<span class="perf-bd-tag perf-bd-tag-fetch">Fetch ${y(d)}</span>`:p}
|
|
690
|
+
<span class="perf-bd-tag perf-bd-tag-app">App ${y(h)}</span>
|
|
729
691
|
</td>
|
|
730
|
-
<td class="perf-td-center" style="color:${
|
|
692
|
+
<td class="perf-td-center" style="color:${a?"var(--red)":"var(--text-muted)"}">${t.statusCode}</td>
|
|
731
693
|
<td class="perf-td-right perf-td-muted">${t.queryCount}</td>
|
|
732
694
|
</tr>
|
|
733
|
-
`}};u([R({context:x})],M.prototype,"store",2),u([_()],M.prototype,"selectedEndpoint",2),u([_()],M.prototype,"graphData",2),u([_()],M.prototype,"loadError",2),u([_()],M.prototype,"queryBreakdown",2),u([_()],M.prototype,"queryBreakdownLoading",2),M=u([g("bk-performance-view")],M);function _r(i){return i===0?"<1ms":v(i)}var w=class extends f{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=w.cache.get(this.requestId);if(t){this.data=t;return}this.loading=true;try{let s=await fetch(`${$.activity}?requestId=${this.requestId}`);if(!s.ok){this.failed=!0,this.loading=!1;return}let r=await s.json();if(w.cache.size>=pe){let o=w.cache.keys().next().value;o!==void 0&&w.cache.delete(o);}w.cache.set(this.requestId,r),this.data=r,this.loading=!1;}catch(s){console.debug("[brakit] timeline load failed:",s),this.failed=true,this.loading=false;}}toggleSql(t,s){s.stopPropagation(),this.expandedSqlIdx=this.expandedSqlIdx===t?-1:t;}copySql(t,s){s.stopPropagation(),navigator.clipboard.writeText(t).then(()=>C.show("SQL copied")).catch(()=>C.show("Copy failed"));}render(){if(this.loading)return a`<div class="tl-loading">Loading activity...</div>`;if(this.failed||!this.data||this.data.total===0)return d;let t=this.data,s=t.timeline[0]?.timestamp??0;return a`
|
|
695
|
+
`}};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`
|
|
696
|
+
<div class="graph-empty">
|
|
697
|
+
<div class="graph-empty-icon">◎</div>
|
|
698
|
+
<div class="graph-empty-title">No data yet</div>
|
|
699
|
+
<div class="graph-empty-desc">Navigate your app to build the dependency graph.</div>
|
|
700
|
+
</div>
|
|
701
|
+
`;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`
|
|
702
|
+
<div class="graph-wrapper" tabindex="0" @keydown=${this.handleKeyDown}
|
|
703
|
+
@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);}}>
|
|
704
|
+
${this.renderToolbar()}
|
|
705
|
+
<div class="graph-body">
|
|
706
|
+
<div class="graph-canvas" style="position:relative">
|
|
707
|
+
<svg width="100%" height="100%"
|
|
708
|
+
viewBox="0 0 ${Math.max(t.width,br)} ${Math.max(t.height,yr)}"
|
|
709
|
+
class="graph-svg"
|
|
710
|
+
style="cursor:${this.isPanning?"grabbing":"grab"}"
|
|
711
|
+
@mousedown=${this.handlePanStart}
|
|
712
|
+
@mousemove=${this.handlePanMove}
|
|
713
|
+
@mouseup=${this.handlePanEnd}
|
|
714
|
+
@mouseleave=${this.handlePanEnd}>
|
|
715
|
+
|
|
716
|
+
<g transform="translate(${d.x},${d.y}) scale(${d.scale})">
|
|
717
|
+
${k`${a.map(h=>k`
|
|
718
|
+
<text x="${h.x}" y="${58}" class="graph-col-header">${h.label}</text>
|
|
719
|
+
`)}`}
|
|
720
|
+
|
|
721
|
+
${t.edges.map(h=>this.renderEdge(h,s,n,c))}
|
|
722
|
+
${t.nodes.map(h=>this.renderNode(h,s,i,c))}
|
|
723
|
+
</g>
|
|
724
|
+
</svg>
|
|
725
|
+
${this.renderFloatingControls()}
|
|
726
|
+
</div>
|
|
727
|
+
${e?this.renderDetailPanel(e):p}
|
|
728
|
+
</div>
|
|
729
|
+
</div>
|
|
730
|
+
`}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`
|
|
731
|
+
<div class="graph-toolbar">
|
|
732
|
+
<div class="graph-layer-toggles">
|
|
733
|
+
${Object.keys(rs).map(t=>{let e=rs[t],s=this.activeLayers.has(t);return l`
|
|
734
|
+
<button class="graph-layer-btn ${s?"active":""}"
|
|
735
|
+
style="${s?`border-color:${e.color};color:${e.color}`:""}"
|
|
736
|
+
@click=${()=>this.toggleLayer(t)}
|
|
737
|
+
title="${e.tooltip}">
|
|
738
|
+
${e.icon} ${e.label}
|
|
739
|
+
</button>
|
|
740
|
+
`})}
|
|
741
|
+
</div>
|
|
742
|
+
|
|
743
|
+
<div class="graph-search">
|
|
744
|
+
<span class="graph-search-icon">⌕</span>
|
|
745
|
+
<input class="graph-search-input" type="text" placeholder="Search nodes… ( / )"
|
|
746
|
+
.value=${this.searchQuery}
|
|
747
|
+
@input=${t=>{this.searchQuery=t.target.value;}}>
|
|
748
|
+
${this.searchQuery?l`
|
|
749
|
+
<button class="graph-search-clear" @click=${()=>{this.searchQuery="";}}>✕</button>
|
|
750
|
+
`:p}
|
|
751
|
+
</div>
|
|
752
|
+
|
|
753
|
+
${this.consolidatedFlows.length>0?l`
|
|
754
|
+
<select class="graph-flow-picker" @change=${t=>{this.activeFlowIdx=parseInt(t.target.value,10),this.activeFlowIdx>=0&&we("flow_traced");}}>
|
|
755
|
+
<option value="-1">Trace flow…</option>
|
|
756
|
+
${this.consolidatedFlows.map((t,e)=>l`
|
|
757
|
+
<option value="${e}" ?selected=${this.activeFlowIdx===e}>${t.label} → ${t.endpointKeys.size} ep · ${t.occurrences}×</option>
|
|
758
|
+
`)}
|
|
759
|
+
</select>
|
|
760
|
+
`:p}
|
|
761
|
+
|
|
762
|
+
${this.activeLayers.has("auth")?l`
|
|
763
|
+
<div class="graph-auth-legend">
|
|
764
|
+
<span class="graph-auth-legend-item"><span style="color:${ns}">🛡</span> protected</span>
|
|
765
|
+
<span class="graph-auth-legend-item"><span style="color:${Se};font-weight:700">!</span> no auth</span>
|
|
766
|
+
</div>
|
|
767
|
+
`:p}
|
|
768
|
+
</div>
|
|
769
|
+
`}renderFloatingControls(){let t=this.nodePositionOverrides.size>0,e=Math.round(this.viewTransform.scale*100);return l`
|
|
770
|
+
<div class="graph-float">
|
|
771
|
+
<button class="graph-float-btn" @click=${this.zoomOut} title="Zoom out (-)">−</button>
|
|
772
|
+
<span class="graph-float-zoom">${e}%</span>
|
|
773
|
+
<button class="graph-float-btn" @click=${this.zoomIn} title="Zoom in (+)">+</button>
|
|
774
|
+
<span class="graph-float-sep"></span>
|
|
775
|
+
<button class="graph-float-btn" @click=${this.resetView} title="Reset pan & zoom">⊙</button>
|
|
776
|
+
${t?l`
|
|
777
|
+
<span class="graph-float-sep"></span>
|
|
778
|
+
<button class="graph-float-btn graph-float-btn-accent" @click=${()=>this.resetLayout()} title="Reset layout to auto-arrange">
|
|
779
|
+
⟲ Reformat
|
|
780
|
+
</button>
|
|
781
|
+
`:p}
|
|
782
|
+
<span class="graph-float-sep"></span>
|
|
783
|
+
<button class="graph-float-btn" @click=${()=>this.captureScreenshot()} title="Save graph as PNG">
|
|
784
|
+
📷
|
|
785
|
+
</button>
|
|
786
|
+
</div>
|
|
787
|
+
`}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=`
|
|
788
|
+
text { font-family: 'Inter', system-ui, -apple-system, sans-serif; }
|
|
789
|
+
.graph-col-header { fill: #c4c4cc; font-size: 9px; font-weight: 600; letter-spacing: 1.5px; }
|
|
790
|
+
.graph-flow-edge { stroke-dasharray: 6,4; }
|
|
791
|
+
.graph-pulse { }
|
|
792
|
+
`,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`
|
|
793
|
+
<g class="graph-g" transform="translate(${t.x},${t.y})" style="opacity:${_};cursor:pointer;transition:opacity .15s,transform .1s"
|
|
794
|
+
@click=${F=>{F.stopPropagation(),!this.wasDragging&&(this.locked=this.locked===t.id?null:t.id,this.detailTab="overview");}}
|
|
795
|
+
@mouseenter=${()=>{this.hovered=t.id;}}
|
|
796
|
+
@mouseleave=${()=>{this.hovered=null;}}
|
|
797
|
+
@mousedown=${F=>{F.detail>=2||this.startNodeDrag(F,t.id,t);}}>
|
|
798
|
+
|
|
799
|
+
${v?k`
|
|
800
|
+
<rect x="-3" y="-3" width="${t.w+6}" height="${t.h+6}" rx="9" fill="none"
|
|
801
|
+
stroke="${ur}" stroke-width="2" stroke-dasharray="4,2"/>
|
|
802
|
+
`:p}
|
|
803
|
+
|
|
804
|
+
${w?k`
|
|
805
|
+
<rect width="${t.w}" height="${t.h}" rx="8" fill="${R}" stroke="${Se}"
|
|
806
|
+
stroke-width="1.2" stroke-dasharray="5,3"/>
|
|
807
|
+
`:k`
|
|
808
|
+
<rect width="${t.w}" height="${t.h}" rx="8" fill="${R}" stroke="${S}" stroke-width="${P}"/>
|
|
809
|
+
`}
|
|
810
|
+
|
|
811
|
+
<text x="12" y="${t.h/2-4}" fill="#1e293b" font-size="11.5" font-weight="600"
|
|
812
|
+
font-family="'Inter',system-ui,sans-serif">${f.icon} ${t.label}</text>
|
|
813
|
+
<text x="12" y="${t.h/2+10}" fill="#a1a1aa" font-size="9"
|
|
814
|
+
font-family="ui-monospace,monospace">${W}</text>
|
|
815
|
+
|
|
816
|
+
${C?k`
|
|
817
|
+
<text x="${t.w-8}" y="${t.h-6}" fill="${kt(g.p95Ms)}" font-size="9"
|
|
818
|
+
font-family="ui-monospace,monospace" text-anchor="end">p95: ${g.p95Ms}ms</text>
|
|
819
|
+
`:p}
|
|
820
|
+
|
|
821
|
+
${H?k`
|
|
822
|
+
<g transform="translate(${t.w-24},3)">
|
|
823
|
+
<title>Auth protected</title>
|
|
824
|
+
<rect width="18" height="18" rx="3" fill="${mr}" stroke="${ns}" stroke-width="0.5"/>
|
|
825
|
+
<text x="9" y="13" text-anchor="middle" font-size="10">🛡</text>
|
|
826
|
+
</g>
|
|
827
|
+
`:p}
|
|
828
|
+
|
|
829
|
+
${w?k`
|
|
830
|
+
<g transform="translate(${t.w-24},3)">
|
|
831
|
+
<title>No auth detected — this endpoint may be unprotected</title>
|
|
832
|
+
<rect width="18" height="18" rx="3" fill="#fff7ed" stroke="${Se}" stroke-width="0.5"/>
|
|
833
|
+
<text x="9" y="13" text-anchor="middle" font-size="10" fill="#ea580c">!</text>
|
|
834
|
+
</g>
|
|
835
|
+
`:p}
|
|
836
|
+
|
|
837
|
+
${Z>0?k`
|
|
838
|
+
<g transform="translate(${t.w-(H||w?46:24)},3)">
|
|
839
|
+
<rect width="18" height="18" rx="3" fill="${j?fr:vr}"
|
|
840
|
+
stroke="${j?as:ls}" stroke-width="0.5"
|
|
841
|
+
class="${j?"graph-pulse":""}"/>
|
|
842
|
+
<text x="9" y="13" text-anchor="middle" font-size="9" font-weight="600"
|
|
843
|
+
fill="${j?as:ls}">${Z}</text>
|
|
844
|
+
</g>
|
|
845
|
+
`:p}
|
|
846
|
+
|
|
847
|
+
${tt>0?k`
|
|
848
|
+
<circle cx="${t.w-8}" cy="8" r="5" fill="${ut}" stroke="white" stroke-width="1"/>
|
|
849
|
+
<text x="${t.w-8}" y="11" text-anchor="middle" font-size="7" fill="white" font-weight="700">${tt}</text>
|
|
850
|
+
`:t.stats.errorRate>.05?k`<circle cx="${t.w-12}" cy="12" r="4" fill="${ut}"/>`:p}
|
|
851
|
+
|
|
852
|
+
${g?.isMiddleware&&x.has("auth")?k`
|
|
853
|
+
<text x="${t.w}" y="${t.h+12}" text-anchor="end" font-size="8" fill="#6b7280"
|
|
854
|
+
font-family="ui-monospace,monospace">middleware</text>
|
|
855
|
+
`:p}
|
|
856
|
+
</g>
|
|
857
|
+
`}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`
|
|
858
|
+
<g style="transition:opacity .15s">
|
|
859
|
+
<path d="M${t.sx},${t.sy} C${v},${t.sy} ${g},${t.ty} ${t.tx},${t.ty}"
|
|
860
|
+
fill="none" stroke="${Z}" stroke-width="${j}"
|
|
861
|
+
stroke-opacity="${d}" stroke-linecap="round"
|
|
862
|
+
stroke-dasharray="${c?"6,4":t.dashed?"3,3":"none"}"
|
|
863
|
+
class="${c?"graph-flow-edge":""}"/>
|
|
864
|
+
<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}"
|
|
865
|
+
fill="${Z}" fill-opacity="${d}"/>
|
|
866
|
+
${h?k`
|
|
867
|
+
<rect x="${x-t.label.length*2.8-2}" y="${w-7}" width="${t.label.length*5.6+8}" height="14"
|
|
868
|
+
rx="4" fill="white" fill-opacity="0.92" stroke="${m}" stroke-width="0.4" stroke-opacity="0.15"/>
|
|
869
|
+
<text x="${x}" y="${w+3.5}" fill="${m}" font-size="8" font-weight="500"
|
|
870
|
+
font-family="ui-monospace,monospace" text-anchor="middle" opacity="0.85">${t.label}</text>
|
|
871
|
+
`:p}
|
|
872
|
+
${S?k`
|
|
873
|
+
<text x="${x}" y="${w-10}" fill="${ut}" font-size="8" font-weight="600"
|
|
874
|
+
text-anchor="middle">⚠ N+1</text>
|
|
875
|
+
`:p}
|
|
876
|
+
</g>
|
|
877
|
+
`}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`
|
|
878
|
+
<div class="graph-detail">
|
|
879
|
+
<div class="graph-detail-head">
|
|
880
|
+
<div>
|
|
881
|
+
<div class="graph-detail-badge" style="color:${i.stroke}">${i.icon} ${e.type}</div>
|
|
882
|
+
<div class="graph-detail-name">${e.label}</div>
|
|
883
|
+
${n?.hasAuth?l`<span class="graph-detail-auth-badge">🛡 Authenticated</span>`:p}
|
|
884
|
+
${n?.isMiddleware?l`<span class="graph-detail-mw-badge">middleware</span>`:p}
|
|
885
|
+
</div>
|
|
886
|
+
<button class="graph-detail-close" @click=${()=>{this.locked=null;}}>✕</button>
|
|
887
|
+
</div>
|
|
888
|
+
|
|
889
|
+
${m.length>1?l`
|
|
890
|
+
<div class="graph-detail-tabs">
|
|
891
|
+
${m.map(f=>l`
|
|
892
|
+
<button class="graph-detail-tab ${this.detailTab===f.key?"active":""}"
|
|
893
|
+
@click=${()=>{this.detailTab=f.key;}}>${f.label}</button>
|
|
894
|
+
`)}
|
|
895
|
+
</div>
|
|
896
|
+
`:p}
|
|
897
|
+
|
|
898
|
+
${this.detailTab==="overview"?this.renderOverviewTab(e,s):p}
|
|
899
|
+
${this.detailTab==="security"?this.renderSecurityTab(n):p}
|
|
900
|
+
${this.detailTab==="performance"?this.renderPerformanceTab(e,n):p}
|
|
901
|
+
${this.detailTab==="issues"?this.renderIssuesTab(n):p}
|
|
902
|
+
</div>
|
|
903
|
+
`}renderOverviewTab(t,e){return l`
|
|
904
|
+
<div class="graph-detail-stats">
|
|
905
|
+
<div class="graph-detail-stat">
|
|
906
|
+
<div class="graph-detail-val">${t.stats.requestCount}</div>
|
|
907
|
+
<div class="graph-detail-lbl">${t.type==="action"?"OCCURRENCES":"REQUESTS"}</div>
|
|
908
|
+
</div>
|
|
909
|
+
<div class="graph-detail-stat">
|
|
910
|
+
<div class="graph-detail-val" style="color:${kt(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
911
|
+
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
912
|
+
</div>
|
|
913
|
+
${t.stats.avgQueryCount>0?l`
|
|
914
|
+
<div class="graph-detail-stat">
|
|
915
|
+
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
916
|
+
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
917
|
+
</div>
|
|
918
|
+
`:p}
|
|
919
|
+
${t.stats.errorRate>.01?l`
|
|
920
|
+
<div class="graph-detail-stat">
|
|
921
|
+
<div class="graph-detail-val" style="color:${ut}">${Math.round(t.stats.errorRate*100)}%</div>
|
|
922
|
+
<div class="graph-detail-lbl">ERRORS</div>
|
|
923
|
+
</div>
|
|
924
|
+
`:p}
|
|
925
|
+
</div>
|
|
926
|
+
|
|
927
|
+
${e.length>0?l`
|
|
928
|
+
<div class="graph-detail-sec">Connections</div>
|
|
929
|
+
${e.map(s=>{let i=s.source===this.locked,n=(i?s.target:s.source).replace(/^(action|endpoint|table|external):/,"");return l`
|
|
930
|
+
<div class="graph-detail-conn">
|
|
931
|
+
<span class="graph-detail-edge-dot" style="background:${xe[s.type]}"></span>
|
|
932
|
+
<span class="graph-detail-edge-type">${s.type}</span>
|
|
933
|
+
<span>${i?"\u2192":"\u2190"} ${n}</span>
|
|
934
|
+
<span class="graph-detail-dim">${s.stats.frequency}× · ${s.stats.avgLatencyMs}ms</span>
|
|
935
|
+
</div>
|
|
936
|
+
`})}
|
|
937
|
+
`:p}
|
|
938
|
+
|
|
939
|
+
${e.some(s=>s.patterns?.length)?l`
|
|
940
|
+
<div class="graph-detail-sec">SQL Patterns</div>
|
|
941
|
+
${e.filter(s=>s.patterns).flatMap(s=>s.patterns).map(s=>l`
|
|
942
|
+
<pre class="graph-detail-sql">${s.length>200?s.slice(0,200)+"\u2026":s}</pre>
|
|
943
|
+
`)}
|
|
944
|
+
`:p}
|
|
945
|
+
`}renderSecurityTab(t){return t?.securityFindings?.length?l`
|
|
946
|
+
${t.securityFindings.map(e=>l`
|
|
947
|
+
<div class="graph-detail-finding">
|
|
948
|
+
<span class="graph-detail-severity graph-detail-severity-${e.severity}">${e.severity}</span>
|
|
949
|
+
<div class="graph-detail-finding-title">${e.title}</div>
|
|
950
|
+
<div class="graph-detail-finding-meta">${e.rule} · ${e.count} occurrence${e.count!==1?"s":""}</div>
|
|
951
|
+
</div>
|
|
952
|
+
`)}
|
|
953
|
+
`:l`<div class="graph-detail-empty">No security findings</div>`}renderPerformanceTab(t,e){return l`
|
|
954
|
+
<div class="graph-detail-stats">
|
|
955
|
+
${e?.p95Ms!==void 0?l`
|
|
956
|
+
<div class="graph-detail-stat">
|
|
957
|
+
<div class="graph-detail-val" style="color:${kt(e.p95Ms)}">${e.p95Ms}ms</div>
|
|
958
|
+
<div class="graph-detail-lbl">P95 LATENCY</div>
|
|
959
|
+
</div>
|
|
960
|
+
`:p}
|
|
961
|
+
<div class="graph-detail-stat">
|
|
962
|
+
<div class="graph-detail-val" style="color:${kt(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
963
|
+
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
964
|
+
</div>
|
|
965
|
+
${t.stats.avgQueryCount>0?l`
|
|
966
|
+
<div class="graph-detail-stat">
|
|
967
|
+
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
968
|
+
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
969
|
+
</div>
|
|
970
|
+
`:p}
|
|
971
|
+
<div class="graph-detail-stat">
|
|
972
|
+
<div class="graph-detail-val">${t.stats.requestCount}</div>
|
|
973
|
+
<div class="graph-detail-lbl">TOTAL REQS</div>
|
|
974
|
+
</div>
|
|
975
|
+
</div>
|
|
976
|
+
|
|
977
|
+
${(e?.insights?.length??0)>0?l`
|
|
978
|
+
<div class="graph-detail-sec">Performance Insights</div>
|
|
979
|
+
${e.insights.map(s=>l`
|
|
980
|
+
<div class="graph-detail-finding">
|
|
981
|
+
<span class="graph-detail-severity graph-detail-severity-${s.severity}">${s.severity}</span>
|
|
982
|
+
<div class="graph-detail-finding-title">${s.title}</div>
|
|
983
|
+
<div class="graph-detail-finding-meta">${s.type}</div>
|
|
984
|
+
</div>
|
|
985
|
+
`)}
|
|
986
|
+
`:p}
|
|
987
|
+
`}renderIssuesTab(t){let e=t?.openIssueCount??0;return e===0?l`<div class="graph-detail-empty">No open issues</div>`:l`
|
|
988
|
+
<div class="graph-detail-issue-summary">
|
|
989
|
+
<div class="graph-detail-stat">
|
|
990
|
+
<div class="graph-detail-val" style="color:${ut}">${e}</div>
|
|
991
|
+
<div class="graph-detail-lbl">OPEN ISSUES</div>
|
|
992
|
+
</div>
|
|
993
|
+
</div>
|
|
994
|
+
<p class="graph-detail-hint">View the Issues tab for full details and remediation hints.</p>
|
|
995
|
+
`}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`
|
|
996
|
+
<div class="explorer-tabs">
|
|
997
|
+
${ve.map(t=>l`
|
|
998
|
+
<button class="explorer-tab ${this.activeTab===t.key?"active":""}"
|
|
999
|
+
@click=${()=>this.switchTab(t.key)}>
|
|
1000
|
+
${t.label}
|
|
1001
|
+
<span class="explorer-tab-count">${this.getCount(t.key)}</span>
|
|
1002
|
+
</button>
|
|
1003
|
+
`)}
|
|
1004
|
+
</div>
|
|
1005
|
+
<div style="display:${this.activeTab==="requests"?"block":"none"}">
|
|
1006
|
+
<bk-requests-view></bk-requests-view>
|
|
1007
|
+
</div>
|
|
1008
|
+
<div style="display:${this.activeTab==="fetches"?"block":"none"}">
|
|
1009
|
+
<bk-fetches-view></bk-fetches-view>
|
|
1010
|
+
</div>
|
|
1011
|
+
<div style="display:${this.activeTab==="queries"?"block":"none"}">
|
|
1012
|
+
<bk-queries-view></bk-queries-view>
|
|
1013
|
+
</div>
|
|
1014
|
+
<div style="display:${this.activeTab==="logs"?"block":"none"}">
|
|
1015
|
+
<bk-logs-view></bk-logs-view>
|
|
1016
|
+
</div>
|
|
1017
|
+
<div style="display:${this.activeTab==="errors"?"block":"none"}">
|
|
1018
|
+
<bk-errors-view></bk-errors-view>
|
|
1019
|
+
</div>
|
|
1020
|
+
`}};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
|
|
1021
|
+
title="Waiting for requests..."
|
|
1022
|
+
subtitle="Start using your app to see insights here"
|
|
1023
|
+
></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`
|
|
1024
|
+
<div class="insights-filters">
|
|
1025
|
+
${Yi.map(f=>l`
|
|
1026
|
+
<button class="insights-chip ${this.filter===f.key?"active":""}"
|
|
1027
|
+
@click=${()=>{this.filter=f.key,this.expandedIdx=-1;}}>
|
|
1028
|
+
${f.label}
|
|
1029
|
+
${i[f.key]>0?l`<span class="insights-chip-count">${i[f.key]}</span>`:p}
|
|
1030
|
+
</button>
|
|
1031
|
+
`)}
|
|
1032
|
+
</div>
|
|
1033
|
+
|
|
1034
|
+
<div class="insights-list">
|
|
1035
|
+
${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}
|
|
1036
|
+
|
|
1037
|
+
${a.length>0?l`
|
|
1038
|
+
<div class="insights-section insights-section-regressed">
|
|
1039
|
+
<span class="insights-section-icon">\u21A9</span> Regressed
|
|
1040
|
+
<span class="insights-section-count">${a.length}</span>
|
|
1041
|
+
</div>
|
|
1042
|
+
${a.map(f=>this.renderIssueCard(f,m++))}
|
|
1043
|
+
`:p}
|
|
1044
|
+
|
|
1045
|
+
${n.length>0?l`
|
|
1046
|
+
<div class="insights-section">
|
|
1047
|
+
<span class="insights-section-icon">\u25CF</span> Open
|
|
1048
|
+
<span class="insights-section-count">${n.length}</span>
|
|
1049
|
+
</div>
|
|
1050
|
+
${n.map(f=>this.renderIssueCard(f,m++))}
|
|
1051
|
+
`:p}
|
|
1052
|
+
|
|
1053
|
+
${c.length>0?l`
|
|
1054
|
+
<div class="insights-section insights-section-verifying">
|
|
1055
|
+
<span class="insights-section-icon">\u29D7</span> Verifying
|
|
1056
|
+
<span class="insights-section-count">${c.length}</span>
|
|
1057
|
+
</div>
|
|
1058
|
+
${c.map(f=>this.renderIssueCard(f,m++))}
|
|
1059
|
+
`:p}
|
|
1060
|
+
|
|
1061
|
+
${d.length>0?l`
|
|
1062
|
+
<div class="insights-section insights-section-resolved">
|
|
1063
|
+
<span class="insights-section-icon">\u2713</span> Resolved
|
|
1064
|
+
<span class="insights-section-count">${d.length}</span>
|
|
1065
|
+
</div>
|
|
1066
|
+
${d.map(f=>this.renderIssueCard(f,m++))}
|
|
1067
|
+
`:p}
|
|
1068
|
+
|
|
1069
|
+
${h.length>0?l`
|
|
1070
|
+
<div class="insights-section insights-section-dismissed" @click=${()=>{this.showDismissed=!this.showDismissed;}}>
|
|
1071
|
+
<span class="insights-section-icon">${this.showDismissed?"\u25BE":"\u25B8"}</span> Won't Fix
|
|
1072
|
+
<span class="insights-section-count">${h.length}</span>
|
|
1073
|
+
</div>
|
|
1074
|
+
${this.showDismissed?h.map(f=>this.renderIssueCard(f,m++)):p}
|
|
1075
|
+
`:p}
|
|
1076
|
+
</div>
|
|
1077
|
+
`}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`
|
|
1078
|
+
<div class="insights-card ${n?"expanded":""} ${a?"resolved":""}"
|
|
1079
|
+
@click=${()=>{this.expandedIdx=this.expandedIdx===e?-1:e;}}>
|
|
1080
|
+
<div class="insights-card-left">
|
|
1081
|
+
<span class="insights-sev ${i.cls}">${i.icon}</span>
|
|
1082
|
+
</div>
|
|
1083
|
+
<div class="insights-card-body">
|
|
1084
|
+
<div class="insights-card-header">
|
|
1085
|
+
<span class="insights-card-title ${a?"resolved":""}">${s.title}</span>
|
|
1086
|
+
<span class="insights-card-cat">${d}</span>
|
|
1087
|
+
${s.count?l`<span class="insights-card-count">${s.count}\u00D7</span>`:p}
|
|
1088
|
+
${t.state==="regressed"?l`<span class="insights-badge-regressed">regressed</span>`:p}
|
|
1089
|
+
${c?l`<span class="insights-badge-verifying">verifying</span>`:p}
|
|
1090
|
+
${a?l`<span class="insights-badge-resolved">resolved</span>`:p}
|
|
1091
|
+
</div>
|
|
1092
|
+
<div class="insights-card-desc">${s.desc}</div>
|
|
1093
|
+
${s.detail?l`<div class="insights-card-detail">${s.detail}</div>`:p}
|
|
1094
|
+
${t.cleanHitsSinceLastSeen>0?l`
|
|
1095
|
+
<div class="insights-card-progress">${t.cleanHitsSinceLastSeen}/${5} clean requests</div>
|
|
1096
|
+
`:p}
|
|
1097
|
+
${n&&s.hint?l`<div class="insights-card-hint">${s.hint}</div>`:p}
|
|
1098
|
+
</div>
|
|
1099
|
+
${s.hint?l`<span class="insights-card-arrow">${n?"\u2193":"\u2192"}</span>`:p}
|
|
1100
|
+
</div>
|
|
1101
|
+
`}};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`
|
|
734
1102
|
<div class="tl-header">
|
|
735
1103
|
<span class="tl-title">Activity Timeline</span>
|
|
736
1104
|
<span class="tl-counts">
|
|
737
|
-
${t.counts.queries>0?
|
|
738
|
-
${t.counts.fetches>0?
|
|
739
|
-
${t.counts.logs>0?
|
|
740
|
-
${t.counts.errors>0?
|
|
1105
|
+
${t.counts.queries>0?l`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:p}
|
|
1106
|
+
${t.counts.fetches>0?l`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:p}
|
|
1107
|
+
${t.counts.logs>0?l`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:p}
|
|
1108
|
+
${t.counts.errors>0?l`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:p}
|
|
741
1109
|
</span>
|
|
742
1110
|
</div>
|
|
743
|
-
<div class="tl-events">${this.renderTimeline(t.timeline,
|
|
744
|
-
`}renderTimeline(t,
|
|
745
|
-
${this.renderEvent(
|
|
1111
|
+
<div class="tl-events">${this.renderTimeline(t.timeline,e)}</div>
|
|
1112
|
+
`}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`
|
|
1113
|
+
${this.renderEvent(a,c,e)}
|
|
746
1114
|
<div class="tl-nested">
|
|
747
1115
|
<span class="tl-nested-label">${m} nested quer${m===1?"y":"ies"}</span>
|
|
748
|
-
${h.map(
|
|
1116
|
+
${h.map(f=>{let S=n++;return this.renderEvent(f,S,e,true)})}
|
|
749
1117
|
</div>
|
|
750
|
-
`}return this.renderEvent(
|
|
751
|
-
<div class="tl-event ${h?"tl-clickable":""} ${
|
|
1118
|
+
`}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`
|
|
1119
|
+
<div class="tl-event ${h?"tl-clickable":""} ${i?"tl-nested-event":""}"
|
|
752
1120
|
style="${h?"":`border-left-color:${n}`}"
|
|
753
|
-
@click=${h?
|
|
1121
|
+
@click=${h?f=>this.toggleSql(e,f):p}>
|
|
754
1122
|
<span class="tl-event-time">${c}</span>
|
|
755
|
-
<span class="tl-event-type" style="color:${n}">${
|
|
1123
|
+
<span class="tl-event-type" style="color:${n}">${a}</span>
|
|
756
1124
|
${this.renderEventContent(t)}
|
|
757
|
-
${
|
|
1125
|
+
${d?l`
|
|
758
1126
|
<div class="tl-event-sql ${m?"open":""}">
|
|
759
|
-
<button class="tl-sql-copy" @click=${
|
|
760
|
-
${
|
|
761
|
-
</div>`:
|
|
762
|
-
</div>
|
|
763
|
-
`}renderEventContent(t){switch(t.type){case "fetch":{let
|
|
764
|
-
<span class="tl-event-summary">${
|
|
765
|
-
<span class="tl-event-status" style="${
|
|
766
|
-
<span class="tl-event-dur">${
|
|
767
|
-
`}case "query":{let
|
|
768
|
-
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${
|
|
769
|
-
<span class="tl-event-dur">${
|
|
770
|
-
`}case "log":{let
|
|
1127
|
+
<button class="tl-sql-copy" @click=${f=>this.copySql(d,f)}>Copy</button>
|
|
1128
|
+
${d}
|
|
1129
|
+
</div>`:p}
|
|
1130
|
+
</div>
|
|
1131
|
+
`}renderEventContent(t){switch(t.type){case "fetch":{let e=t.data,s=e.statusCode>=400;return l`
|
|
1132
|
+
<span class="tl-event-summary">${e.method} ${e.url}</span>
|
|
1133
|
+
<span class="tl-event-status" style="${s?"color:var(--red)":""}">${e.statusCode}</span>
|
|
1134
|
+
<span class="tl-event-dur">${y(e.durationMs)}</span>
|
|
1135
|
+
`}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`
|
|
1136
|
+
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${s}</span> ${i}</span>
|
|
1137
|
+
<span class="tl-event-dur">${Vi(e.durationMs)}</span>
|
|
1138
|
+
`}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`
|
|
771
1139
|
<div class="app" id="app">
|
|
772
1140
|
<aside class="sidebar">
|
|
773
1141
|
<div class="sidebar-logo">
|
|
774
1142
|
<span class="logo-text">brakit</span>
|
|
775
|
-
<span class="logo-version">v${
|
|
1143
|
+
<span class="logo-version">v${a?.version??""}</span>
|
|
776
1144
|
</div>
|
|
777
1145
|
<nav class="sidebar-nav">
|
|
778
|
-
${this.renderSidebarItem("overview","Overview",
|
|
779
|
-
|
|
780
|
-
${this.renderSidebarItem("
|
|
781
|
-
${this.renderSidebarItem("
|
|
782
|
-
${this.renderSidebarItem("
|
|
783
|
-
<div class="sidebar-
|
|
784
|
-
${this.renderSidebarItem("
|
|
785
|
-
${this.renderSidebarItem("errors","Errors",Ts(),t.errors.length)}
|
|
786
|
-
${this.renderSidebarItem("logs","Logs",$s(),t.logs.length)}
|
|
787
|
-
${this.renderSidebarItem("security","Security",ys(),n,n===0)}
|
|
788
|
-
${this.renderSidebarItem("performance","Performance",xs(),void 0)}
|
|
1146
|
+
${this.renderSidebarItem("overview","Overview",Ar(),void 0)}
|
|
1147
|
+
${this.renderSidebarItem("actions","Actions",Ir(),t.flows.length)}
|
|
1148
|
+
${this.renderSidebarItem("insights","Insights",Lr(),n,n===0)}
|
|
1149
|
+
${this.renderSidebarItem("performance","Performance",Cr(),void 0)}
|
|
1150
|
+
${this.renderSidebarItem("graph","Graph",Nr(),void 0)}
|
|
1151
|
+
<div class="sidebar-divider"></div>
|
|
1152
|
+
${this.renderSidebarItem("explorer","Explorer",Mr(),e.length+t.fetches.length+t.queries.length+t.logs.length+t.errors.length)}
|
|
789
1153
|
</nav>
|
|
790
|
-
<div class="sidebar-footer">:${
|
|
1154
|
+
<div class="sidebar-footer">:${a?.port??""}</div>
|
|
791
1155
|
</aside>
|
|
792
1156
|
<div class="main-panel">
|
|
793
1157
|
<div class="header">
|
|
794
1158
|
<div class="header-left">
|
|
795
|
-
<span class="header-title" id="header-title">${
|
|
796
|
-
<span class="header-sub" id="header-sub">${
|
|
1159
|
+
<span class="header-title" id="header-title">${Pe[this.activeView]||this.activeView}</span>
|
|
1160
|
+
<span class="header-sub" id="header-sub">${He[this.activeView]||""}</span>
|
|
797
1161
|
</div>
|
|
798
1162
|
<div class="header-right">
|
|
799
|
-
${this.activeView==="actions"?
|
|
1163
|
+
${this.activeView==="actions"?l`
|
|
800
1164
|
<div class="segmented-control" id="mode-toggle">
|
|
801
1165
|
<button class="segmented-btn ${this.viewMode==="simple"?"active":""}" @click=${()=>{this.viewMode="simple",this.store.setViewMode("simple");}}>Quick</button>
|
|
802
1166
|
<button class="segmented-btn ${this.viewMode==="detailed"?"active":""}" @click=${()=>{this.viewMode="detailed",this.store.setViewMode("detailed");}}>Detailed</button>
|
|
803
1167
|
</div>
|
|
804
|
-
`:
|
|
1168
|
+
`:p}
|
|
805
1169
|
<button class="btn btn-danger" @click=${this.handleClear}>Clear</button>
|
|
806
1170
|
</div>
|
|
807
1171
|
</div>
|
|
808
1172
|
<div class="main-content">
|
|
809
|
-
<div
|
|
1173
|
+
<div style="display:${this.activeView==="overview"?"block":"none"}">
|
|
810
1174
|
<bk-overview-view></bk-overview-view>
|
|
811
1175
|
</div>
|
|
812
|
-
<div
|
|
1176
|
+
<div style="display:${this.activeView==="actions"?"block":"none"}">
|
|
813
1177
|
<bk-flows-view></bk-flows-view>
|
|
814
1178
|
</div>
|
|
815
|
-
<div
|
|
816
|
-
<bk-
|
|
817
|
-
</div>
|
|
818
|
-
<div class="view-telemetry" id="fetch-container" style="display:${this.activeView==="fetches"?"block":"none"}">
|
|
819
|
-
<bk-fetches-view></bk-fetches-view>
|
|
820
|
-
</div>
|
|
821
|
-
<div class="view-telemetry" id="query-container" style="display:${this.activeView==="queries"?"block":"none"}">
|
|
822
|
-
<bk-queries-view></bk-queries-view>
|
|
1179
|
+
<div style="display:${this.activeView==="insights"?"block":"none"}">
|
|
1180
|
+
<bk-insights-view></bk-insights-view>
|
|
823
1181
|
</div>
|
|
824
|
-
<div
|
|
825
|
-
<bk-
|
|
826
|
-
</div>
|
|
827
|
-
<div class="view-telemetry" id="log-container" style="display:${this.activeView==="logs"?"block":"none"}">
|
|
828
|
-
<bk-logs-view></bk-logs-view>
|
|
1182
|
+
<div style="display:${this.activeView==="performance"?"block":"none"}">
|
|
1183
|
+
<bk-performance-view></bk-performance-view>
|
|
829
1184
|
</div>
|
|
830
|
-
<div
|
|
831
|
-
<bk-
|
|
1185
|
+
<div style="display:${this.activeView==="graph"?"block":"none"}">
|
|
1186
|
+
<bk-graph-view></bk-graph-view>
|
|
832
1187
|
</div>
|
|
833
|
-
<div
|
|
834
|
-
<bk-
|
|
1188
|
+
<div style="display:${this.activeView==="explorer"?"block":"none"}">
|
|
1189
|
+
<bk-explorer-view></bk-explorer-view>
|
|
835
1190
|
</div>
|
|
836
1191
|
</div>
|
|
837
1192
|
<div class="footer">
|
|
838
|
-
<span id="stat-total">${
|
|
839
|
-
<span id="stat-flows">${t.flows.length}
|
|
840
|
-
<span id="stat-errors" class="error-count">${
|
|
841
|
-
<span id="stat-avg">Avg: ${
|
|
1193
|
+
<span id="stat-total">${e.length} ${B(e.length,"request")}</span>
|
|
1194
|
+
<span id="stat-flows">${t.flows.length} ${B(t.flows.length,"action")}</span>
|
|
1195
|
+
<span id="stat-errors" class="error-count">${s} ${B(s,"error")}</span>
|
|
1196
|
+
<span id="stat-avg">Avg: ${i}ms</span>
|
|
842
1197
|
</div>
|
|
843
1198
|
</div>
|
|
844
1199
|
</div>
|
|
845
1200
|
<bk-toast></bk-toast>
|
|
846
|
-
`}renderSidebarItem(t,s,
|
|
1201
|
+
`}renderSidebarItem(t,e,s,i,n=false){return l`
|
|
847
1202
|
<button class="sidebar-item ${this.activeView===t?"active":""}" @click=${()=>this.switchView(t)}>
|
|
848
|
-
<span class="item-icon">${
|
|
849
|
-
<span class="item-label">${
|
|
850
|
-
${
|
|
1203
|
+
<span class="item-icon">${s}</span>
|
|
1204
|
+
<span class="item-label">${e}</span>
|
|
1205
|
+
${i!==void 0?l`<span class="item-count" style="display:${n?"none":""}">${i}</span>`:p}
|
|
851
1206
|
</button>
|
|
852
|
-
`}};u([
|
|
1207
|
+
`}};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);
|
|
853
1208
|
/*! Bundled license information:
|
|
854
1209
|
|
|
855
1210
|
@lit/reactive-element/css-tag.js:
|
|
@@ -872,6 +1227,8 @@ lit-element/lit-element.js:
|
|
|
872
1227
|
@lit/reactive-element/decorators/query-async.js:
|
|
873
1228
|
@lit/reactive-element/decorators/query-assigned-nodes.js:
|
|
874
1229
|
@lit/context/lib/decorators/provide.js:
|
|
1230
|
+
lit-html/directive.js:
|
|
1231
|
+
lit-html/directives/unsafe-html.js:
|
|
875
1232
|
(**
|
|
876
1233
|
* @license
|
|
877
1234
|
* Copyright 2017 Google LLC
|