brakit 0.9.1 → 0.10.0
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 +142 -0
- package/dist/api.js +212 -42
- package/dist/bin/brakit.js +138 -19
- package/dist/dashboard-client.global.js +587 -276
- package/dist/dashboard.html +691 -276
- package/dist/mcp/server.js +4 -2
- package/dist/runtime/index.js +1103 -74
- package/package.json +1 -1
|
@@ -1,26 +1,26 @@
|
|
|
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 wr=Object.defineProperty;var Rr=Object.getOwnPropertyDescriptor;var u=(o,s,t,e)=>{for(var r=e>1?void 0:e?Rr(s,t):s,i=o.length-1,n;i>=0;i--)(n=o[i])&&(r=(e?n(s,t,r):n(r))||r);return e&&r&&wr(s,t,r),r};var zt=globalThis,Zt=zt.ShadowRoot&&(zt.ShadyCSS===void 0||zt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,os=Symbol(),is=new WeakMap,Jt=class{constructor(s,t,e){if(this._$cssResult$=true,e!==os)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=t;}get styleSheet(){let s=this.o,t=this.t;if(Zt&&s===void 0){let e=t!==void 0&&t.length===1;e&&(s=is.get(t)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),e&&is.set(t,s));}return s}toString(){return this.cssText}},ns=o=>new Jt(typeof o=="string"?o:o+"",void 0,os);var as=(o,s)=>{if(Zt)o.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of s){let e=document.createElement("style"),r=zt.litNonce;r!==void 0&&e.setAttribute("nonce",r),e.textContent=t.cssText,o.appendChild(e);}},ye=Zt?o=>o:o=>o instanceof CSSStyleSheet?(s=>{let t="";for(let e of s.cssRules)t+=e.cssText;return ns(t)})(o):o;var{is:Ar,defineProperty:Ir,getOwnPropertyDescriptor:Cr,getOwnPropertyNames:Lr,getOwnPropertySymbols:Mr,getPrototypeOf:Nr}=Object,K=globalThis,ls=K.trustedTypes,Or=ls?ls.emptyScript:"",kr=K.reactiveElementPolyfillSupport,Mt=(o,s)=>o,Nt={toAttribute(o,s){switch(s){case Boolean:o=o?Or:null;break;case Object:case Array:o=o==null?o:JSON.stringify(o);}return o},fromAttribute(o,s){let t=o;switch(s){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}},te=(o,s)=>!Ar(o,s),cs={attribute:true,type:String,converter:Nt,reflect:false,useDefault:false,hasChanged:te};Symbol.metadata??(Symbol.metadata=Symbol("metadata")),K.litPropertyMetadata??(K.litPropertyMetadata=new WeakMap);var j=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??(this.l=[])).push(s);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,t=cs){if(t.state&&(t.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(s)&&((t=Object.create(t)).wrapped=true),this.elementProperties.set(s,t),!t.noAccessor){let e=Symbol(),r=this.getPropertyDescriptor(s,e,t);r!==void 0&&Ir(this.prototype,s,r);}}static getPropertyDescriptor(s,t,e){let{get:r,set:i}=Cr(this.prototype,s)??{get(){return this[t]},set(n){this[t]=n;}};return {get:r,set(n){let l=r?.call(this);i?.call(this,n),this.requestUpdate(s,l,e);},configurable:true,enumerable:true}}static getPropertyOptions(s){return this.elementProperties.get(s)??cs}static _$Ei(){if(this.hasOwnProperty(Mt("elementProperties")))return;let s=Nr(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties);}static finalize(){if(this.hasOwnProperty(Mt("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(Mt("properties"))){let t=this.properties,e=[...Lr(t),...Mr(t)];for(let r of e)this.createProperty(r,t[r]);}let s=this[Symbol.metadata];if(s!==null){let t=litPropertyMetadata.get(s);if(t!==void 0)for(let[e,r]of t)this.elementProperties.set(e,r);}this._$Eh=new Map;for(let[t,e]of this.elementProperties){let r=this._$Eu(t,e);r!==void 0&&this._$Eh.set(r,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(s){let t=[];if(Array.isArray(s)){let e=new Set(s.flat(1/0).reverse());for(let r of e)t.unshift(ye(r));}else s!==void 0&&t.push(ye(s));return t}static _$Eu(s,t){let e=t.attribute;return e===false?void 0:typeof e=="string"?e:typeof s=="string"?s.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(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this));}addController(s){(this._$EO??(this._$EO=new Set)).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.();}removeController(s){this._$EO?.delete(s);}_$E_(){let s=new Map,t=this.constructor.elementProperties;for(let e of t.keys())this.hasOwnProperty(e)&&(s.set(e,this[e]),delete this[e]);s.size>0&&(this._$Ep=s);}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return as(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??(this.renderRoot=this.createRenderRoot()),this.enableUpdating(true),this._$EO?.forEach(s=>s.hostConnected?.());}enableUpdating(s){}disconnectedCallback(){this._$EO?.forEach(s=>s.hostDisconnected?.());}attributeChangedCallback(s,t,e){this._$AK(s,e);}_$ET(s,t){let e=this.constructor.elementProperties.get(s),r=this.constructor._$Eu(s,e);if(r!==void 0&&e.reflect===true){let i=(e.converter?.toAttribute!==void 0?e.converter:Nt).toAttribute(t,e.type);this._$Em=s,i==null?this.removeAttribute(r):this.setAttribute(r,i),this._$Em=null;}}_$AK(s,t){let e=this.constructor,r=e._$Eh.get(s);if(r!==void 0&&this._$Em!==r){let i=e.getPropertyOptions(r),n=typeof i.converter=="function"?{fromAttribute:i.converter}:i.converter?.fromAttribute!==void 0?i.converter:Nt;this._$Em=r;let l=n.fromAttribute(t,i.type);this[r]=l??this._$Ej?.get(r)??l,this._$Em=null;}}requestUpdate(s,t,e,r=false,i){if(s!==void 0){let n=this.constructor;if(r===false&&(i=this[s]),e??(e=n.getPropertyOptions(s)),!((e.hasChanged??te)(i,t)||e.useDefault&&e.reflect&&i===this._$Ej?.get(s)&&!this.hasAttribute(n._$Eu(s,e))))return;this.C(s,t,e);}this.isUpdatePending===false&&(this._$ES=this._$EP());}C(s,t,{useDefault:e,reflect:r,wrapped:i},n){e&&!(this._$Ej??(this._$Ej=new Map)).has(s)&&(this._$Ej.set(s,n??t??this[s]),i!==true||n!==void 0)||(this._$AL.has(s)||(this.hasUpdated||e||(t=void 0),this._$AL.set(s,t)),r===true&&this._$Em!==s&&(this._$Eq??(this._$Eq=new Set)).add(s));}async _$EP(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}let s=this.scheduleUpdate();return s!=null&&await s,!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[r,i]of this._$Ep)this[r]=i;this._$Ep=void 0;}let e=this.constructor.elementProperties;if(e.size>0)for(let[r,i]of e){let{wrapped:n}=i,l=this[r];n!==true||this._$AL.has(r)||l===void 0||this.C(r,void 0,i,l);}}let s=false,t=this._$AL;try{s=this.shouldUpdate(t),s?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM();}catch(e){throw s=false,this._$EM(),e}s&&this._$AE(t);}willUpdate(s){}_$AE(s){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(s)),this.updated(s);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(s){return true}update(s){this._$Eq&&(this._$Eq=this._$Eq.forEach(t=>this._$ET(t,this[t]))),this._$EM();}updated(s){}firstUpdated(s){}};j.elementStyles=[],j.shadowRootOptions={mode:"open"},j[Mt("elementProperties")]=new Map,j[Mt("finalized")]=new Map,kr?.({ReactiveElement:j}),(K.reactiveElementVersions??(K.reactiveElementVersions=[])).push("2.1.2");var kt=globalThis,ds=o=>o,ee=kt.trustedTypes,ps=ee?ee.createPolicy("lit-html",{createHTML:o=>o}):void 0,gs="$lit$",z=`lit$${Math.random().toFixed(9).slice(2)}$`,bs="?"+z,Dr=`<${bs}>`,ot=document,Dt=()=>ot.createComment(""),Ht=o=>o===null||typeof o!="object"&&typeof o!="function",Re=Array.isArray,Hr=o=>Re(o)||typeof o?.[Symbol.iterator]=="function",_e=`[
|
|
2
|
+
\f\r]`,Ot=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,hs=/-->/g,us=/>/g,rt=RegExp(`>|${_e}(?:([^\\s"'>=/]+)(${_e}*=${_e}*(?:[^
|
|
3
|
+
\f\r"'\`<>=]|("|')|))|$)`,"g"),ms=/'/g,fs=/"/g,Es=/^(?:script|style|textarea|title)$/i,Ae=o=>(s,...t)=>({_$litType$:o,strings:s,values:t}),a=Ae(1),O=Ae(2),Y=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),vs=new WeakMap,it=ot.createTreeWalker(ot,129);function ys(o,s){if(!Re(o)||!o.hasOwnProperty("raw"))throw Error("invalid template strings array");return ps!==void 0?ps.createHTML(s):s}var Pr=(o,s)=>{let t=o.length-1,e=[],r,i=s===2?"<svg>":s===3?"<math>":"",n=Ot;for(let l=0;l<t;l++){let c=o[l],d,h,m=-1,v=0;for(;v<c.length&&(n.lastIndex=v,h=n.exec(c),h!==null);)v=n.lastIndex,n===Ot?h[1]==="!--"?n=hs:h[1]!==void 0?n=us:h[2]!==void 0?(Es.test(h[2])&&(r=RegExp("</"+h[2],"g")),n=rt):h[3]!==void 0&&(n=rt):n===rt?h[0]===">"?(n=r??Ot,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,d=h[1],n=h[3]===void 0?rt:h[3]==='"'?fs:ms):n===fs||n===ms?n=rt:n===hs||n===us?n=Ot:(n=rt,r=void 0);let x=n===rt&&o[l+1].startsWith("/>")?" ":"";i+=n===Ot?c+Dr:m>=0?(e.push(d),c.slice(0,m)+gs+c.slice(m)+z+x):c+z+(m===-2?l:x);}return [ys(o,i+(o[t]||"<?>")+(s===2?"</svg>":s===3?"</math>":"")),e]},Pt=class o{constructor({strings:s,_$litType$:t},e){let r;this.parts=[];let i=0,n=0,l=s.length-1,c=this.parts,[d,h]=Pr(s,t);if(this.el=o.createElement(d,e),it.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(r=it.nextNode())!==null&&c.length<l;){if(r.nodeType===1){if(r.hasAttributes())for(let m of r.getAttributeNames())if(m.endsWith(gs)){let v=h[n++],x=r.getAttribute(m).split(z),y=/([.?@])?(.*)/.exec(v);c.push({type:1,index:i,name:y[2],strings:x,ctor:y[1]==="."?xe:y[1]==="?"?Se:y[1]==="@"?Te:ht}),r.removeAttribute(m);}else m.startsWith(z)&&(c.push({type:6,index:i}),r.removeAttribute(m));if(Es.test(r.tagName)){let m=r.textContent.split(z),v=m.length-1;if(v>0){r.textContent=ee?ee.emptyScript:"";for(let x=0;x<v;x++)r.append(m[x],Dt()),it.nextNode(),c.push({type:2,index:++i});r.append(m[v],Dt());}}}else if(r.nodeType===8)if(r.data===bs)c.push({type:2,index:i});else {let m=-1;for(;(m=r.data.indexOf(z,m+1))!==-1;)c.push({type:7,index:i}),m+=z.length-1;}i++;}}static createElement(s,t){let e=ot.createElement("template");return e.innerHTML=s,e}};function pt(o,s,t=o,e){if(s===Y)return s;let r=e!==void 0?t._$Co?.[e]:t._$Cl,i=Ht(s)?void 0:s._$litDirective$;return r?.constructor!==i&&(r?._$AO?.(false),i===void 0?r=void 0:(r=new i(o),r._$AT(o,t,e)),e!==void 0?(t._$Co??(t._$Co=[]))[e]=r:t._$Cl=r),r!==void 0&&(s=pt(o,r._$AS(o,s.values),r,e)),s}var $e=class{constructor(s,t){this._$AV=[],this._$AN=void 0,this._$AD=s,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(s){let{el:{content:t},parts:e}=this._$AD,r=(s?.creationScope??ot).importNode(t,true);it.currentNode=r;let i=it.nextNode(),n=0,l=0,c=e[0];for(;c!==void 0;){if(n===c.index){let d;c.type===2?d=new qt(i,i.nextSibling,this,s):c.type===1?d=new c.ctor(i,c.name,c.strings,this,s):c.type===6&&(d=new we(i,this,s)),this._$AV.push(d),c=e[++l];}n!==c?.index&&(i=it.nextNode(),n++);}return it.currentNode=ot,r}p(s){let t=0;for(let e of this._$AV)e!==void 0&&(e.strings!==void 0?(e._$AI(s,e,t),t+=e.strings.length-2):e._$AI(s[t])),t++;}},qt=class o{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(s,t,e,r){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=s,this._$AB=t,this._$AM=e,this.options=r,this._$Cv=r?.isConnected??true;}get parentNode(){let s=this._$AA.parentNode,t=this._$AM;return t!==void 0&&s?.nodeType===11&&(s=t.parentNode),s}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(s,t=this){s=pt(this,s,t),Ht(s)?s===p||s==null||s===""?(this._$AH!==p&&this._$AR(),this._$AH=p):s!==this._$AH&&s!==Y&&this._(s):s._$litType$!==void 0?this.$(s):s.nodeType!==void 0?this.T(s):Hr(s)?this.k(s):this._(s);}O(s){return this._$AA.parentNode.insertBefore(s,this._$AB)}T(s){this._$AH!==s&&(this._$AR(),this._$AH=this.O(s));}_(s){this._$AH!==p&&Ht(this._$AH)?this._$AA.nextSibling.data=s:this.T(ot.createTextNode(s)),this._$AH=s;}$(s){let{values:t,_$litType$:e}=s,r=typeof e=="number"?this._$AC(s):(e.el===void 0&&(e.el=Pt.createElement(ys(e.h,e.h[0]),this.options)),e);if(this._$AH?._$AD===r)this._$AH.p(t);else {let i=new $e(r,this),n=i.u(this.options);i.p(t),this.T(n),this._$AH=i;}}_$AC(s){let t=vs.get(s.strings);return t===void 0&&vs.set(s.strings,t=new Pt(s)),t}k(s){Re(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,e,r=0;for(let i of s)r===t.length?t.push(e=new o(this.O(Dt()),this.O(Dt()),this,this.options)):e=t[r],e._$AI(i),r++;r<t.length&&(this._$AR(e&&e._$AB.nextSibling,r),t.length=r);}_$AR(s=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);s!==this._$AB;){let e=ds(s).nextSibling;ds(s).remove(),s=e;}}setConnected(s){this._$AM===void 0&&(this._$Cv=s,this._$AP?.(s));}},ht=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(s,t,e,r,i){this.type=1,this._$AH=p,this._$AN=void 0,this.element=s,this.name=t,this._$AM=r,this.options=i,e.length>2||e[0]!==""||e[1]!==""?(this._$AH=Array(e.length-1).fill(new String),this.strings=e):this._$AH=p;}_$AI(s,t=this,e,r){let i=this.strings,n=false;if(i===void 0)s=pt(this,s,t,0),n=!Ht(s)||s!==this._$AH&&s!==Y,n&&(this._$AH=s);else {let l=s,c,d;for(s=i[0],c=0;c<i.length-1;c++)d=pt(this,l[e+c],t,c),d===Y&&(d=this._$AH[c]),n||(n=!Ht(d)||d!==this._$AH[c]),d===p?s=p:s!==p&&(s+=(d??"")+i[c+1]),this._$AH[c]=d;}n&&!r&&this.j(s);}j(s){s===p?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,s??"");}},xe=class extends ht{constructor(){super(...arguments),this.type=3;}j(s){this.element[this.name]=s===p?void 0:s;}},Se=class extends ht{constructor(){super(...arguments),this.type=4;}j(s){this.element.toggleAttribute(this.name,!!s&&s!==p);}},Te=class extends ht{constructor(s,t,e,r,i){super(s,t,e,r,i),this.type=5;}_$AI(s,t=this){if((s=pt(this,s,t,0)??p)===Y)return;let e=this._$AH,r=s===p&&e!==p||s.capture!==e.capture||s.once!==e.once||s.passive!==e.passive,i=s!==p&&(e===p||r);r&&this.element.removeEventListener(this.name,this,e),i&&this.element.addEventListener(this.name,this,s),this._$AH=s;}handleEvent(s){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,s):this._$AH.handleEvent(s);}},we=class{constructor(s,t,e){this.element=s,this.type=6,this._$AN=void 0,this._$AM=t,this.options=e;}get _$AU(){return this._$AM._$AU}_$AI(s){pt(this,s);}};var qr=kt.litHtmlPolyfillSupport;qr?.(Pt,qt),(kt.litHtmlVersions??(kt.litHtmlVersions=[])).push("3.3.2");var _s=(o,s,t)=>{let e=t?.renderBefore??s,r=e._$litPart$;if(r===void 0){let i=t?.renderBefore??null;e._$litPart$=r=new qt(s.insertBefore(Dt(),i),i,void 0,t??{});}return r._$AI(o),r};var Ut=globalThis,E=class extends j{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let s=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=s.firstChild),s}update(s){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(s),this._$Do=_s(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return Y}};E._$litElement$=true,E.finalized=true,Ut.litElementHydrateSupport?.({LitElement:E});var Ur=Ut.litElementPolyfillSupport;Ur?.({LitElement:E});(Ut.litElementVersions??(Ut.litElementVersions=[])).push("4.2.2");var S=o=>(s,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(o,s);}):customElements.define(o,s);};var Fr={attribute:true,type:String,converter:Nt,reflect:false,hasChanged:te},Gr=(o=Fr,s,t)=>{let{kind:e,metadata:r}=t,i=globalThis.litPropertyMetadata.get(r);if(i===void 0&&globalThis.litPropertyMetadata.set(r,i=new Map),e==="setter"&&((o=Object.create(o)).wrapped=true),i.set(t.name,o),e==="accessor"){let{name:n}=t;return {set(l){let c=s.get.call(this);s.set.call(this,l),this.requestUpdate(n,c,o,true,l);},init(l){return l!==void 0&&this.C(n,void 0,o,l),l}}}if(e==="setter"){let{name:n}=t;return function(l){let c=this[n];s.call(this,l),this.requestUpdate(n,c,o,true,l);}}throw Error("Unsupported decorator location: "+e)};function L(o){return (s,t)=>typeof t=="object"?Gr(o,s,t):((e,r,i)=>{let n=r.hasOwnProperty(i);return r.constructor.createProperty(i,e),n?Object.getOwnPropertyDescriptor(r,i):void 0})(o,s,t)}function b(o){return L({...o,state:true,attribute:false})}var Ft=class extends E{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return a`<span class="method-badge method-badge-${t}">${t}</span>`}};u([L()],Ft.prototype,"method",2),Ft=u([S("bk-method-badge")],Ft);var P="/__brakit/api",F="/__brakit",w={flows:`${P}/flows`,requests:`${P}/requests`,events:`${P}/events`,clear:`${P}/clear`,fetches:`${P}/fetches`,errors:`${P}/errors`,logs:`${P}/logs`,queries:`${P}/queries`,metricsLive:`${P}/metrics/live`,insights:`${P}/insights`,tab:`${P}/tab`,activity:`${P}/activity`,graph:`${P}/graph`};var ut="polling",re="static",Br="auth-handshake",Wr="auth-check",Qr="middleware",Gt={[Br]:1,[Wr]:1,[Qr]:1};var ie="fetch";var oe="error_event",ne="query",ae="issues";var Ie={overview:"Overview",actions:"Actions",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",performance:"Performance",security:"Security",graph:"Graph"},Ce={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",performance:"Endpoint health and response time trends",security:"Security findings and recommendations",graph:"Runtime dependency graph of your application"};var Me=100,mt=300,ft=800,Ne=2e3,Oe=100,ke=50,De=500;var J="__all__",pe={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},As={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},qe=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],Bt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},gt=[{max:Me,label:"Fast",color:"var(--green)",bg:"rgba(22,163,74,0.08)",border:"rgba(22,163,74,0.2)"},{max:mt,label:"Good",color:"var(--green)",bg:"rgba(22,163,74,0.06)",border:"rgba(22,163,74,0.15)"},{max:ft,label:"OK",color:"var(--amber)",bg:"rgba(217,119,6,0.06)",border:"rgba(217,119,6,0.15)"},{max:Ne,label:"Slow",color:"var(--red)",bg:"rgba(220,38,38,0.06)",border:"rgba(220,38,38,0.15)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"rgba(220,38,38,0.08)",border:"rgba(220,38,38,0.2)"}],Is="rgba(228,228,231,0.8)",Ue="rgba(113,113,122,0.7)",Cs="10px monospace",Fe="9px monospace";var Ls={top:16,right:16,bottom:28,left:52},Ms={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},Ns={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},Os=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),ks={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},Ds=new Set(["host","connection","accept-encoding"]),Z={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function _(o){return o<1e3?o+"ms":(o/1e3).toFixed(1)+"s"}function et(o){return !o||o===0?"":o<1024?o+"b":(o/1024).toFixed(1)+"kb"}function tt(o){return o?o.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',"""):""}function bt(o){return o>=500?"status-pill-5xx":o>=400?"status-pill-4xx":o>=300?"status-pill-3xx":"status-pill-2xx"}function Hs(o){return ks[o]||(o>=500?"Server Error":o>=400?"Client Error":"OK")}function jr(o,s){if(Os.has(o.toLowerCase())){let t=String(s);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(s)}function Et(o){return !o||Object.keys(o).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(o).map(([s,t])=>'<span class="json-key">'+tt(s)+"</span>: "+tt(jr(s,t))).join(`
|
|
4
|
+
`)}function nt(o){if(!o)return '<span style="color:var(--text-muted)">No body</span>';try{let s=JSON.parse(o);return Yr(JSON.stringify(s,null,2))}catch{return tt(o)}}function Yr(o){return tt(o).replace(/("(?:[^"\\]|\\.)*")(\s*:)?|\b(true|false)\b|\bnull\b|(-?\d+\.?\d*(?:[eE][+-]?\d+)?)/g,(s,t,e,r,i)=>t?e?'<span class="json-key">'+t+"</span>"+e:'<span class="json-str">'+t+"</span>":r?'<span class="json-bool">'+s+"</span>":i?'<span class="json-num">'+s+"</span>":s==="null"?'<span class="json-null">null</span>':s)}var Wt=class extends E{constructor(){super(...arguments);this.code=0;}createRenderRoot(){return this}render(){let t=bt(this.code);return a`<span class="status-pill ${t}">${this.code}</span>`}};u([L({type:Number})],Wt.prototype,"code",2),Wt=u([S("bk-status-pill")],Wt);var Qt=class extends E{constructor(){super(...arguments);this.ms=0;}createRenderRoot(){return this}render(){return a`<span class="req-duration">${_(this.ms)}</span>`}};u([L({type:Number})],Qt.prototype,"ms",2),Qt=u([S("bk-duration-label")],Qt);var yt=class extends E{constructor(){super(...arguments);this.title="";this.subtitle="";}createRenderRoot(){return this}render(){return a`
|
|
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()],yt.prototype,"title",2),u([L()],yt.prototype,"subtitle",2),yt=u([S("bk-empty-state")],yt);var D=class extends E{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 a`<div class="toast ${this.visible?"show":""}">${this.message}</div>`}};u([b()],D.prototype,"message",2),u([b()],D.prototype,"visible",2),D=u([S("bk-toast")],D);var at=class extends E{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 a`<button class="query-detail-copy" @click=${this.copy}>${this.label}</button>`}};u([L()],at.prototype,"text",2),u([L()],at.prototype,"label",2),u([L({attribute:"toast-message"})],at.prototype,"toastMessage",2),at=u([S("bk-copy-button")],at);var lt=class extends E{constructor(){super(...arguments);this.value="";this.label="";this.color="";}createRenderRoot(){return this}render(){return a`
|
|
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()],lt.prototype,"value",2),u([L()],lt.prototype,"label",2),u([L()],lt.prototype,"color",2),lt=u([S("bk-stat-card")],lt);var st=class extends Event{constructor(s,t,e,r){super("context-request",{bubbles:true,composed:true}),this.context=s,this.contextTarget=t,this.callback=e,this.subscribe=r??false;}};var _t=class{constructor(s,t,e,r){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=s,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=r??false;this.host.addController(this);}hostConnected(){this.dispatchRequest();}hostDisconnected(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=void 0);}dispatchRequest(){this.host.dispatchEvent(new st(this.context,this.host,this.t,this.subscribe));}};var he=class{get value(){return this.o}set value(s){this.setValue(s);}setValue(s,t=false){let e=t||!Object.is(s,this.o);this.o=s,e&&this.updateObservers();}constructor(s){this.subscriptions=new Map,this.updateObservers=()=>{for(let[t,{disposer:e}]of this.subscriptions)t(this.o,e);},s!==void 0&&(this.value=s);}addCallback(s,t,e){if(!e)return void s(this.value);this.subscriptions.has(s)||this.subscriptions.set(s,{disposer:()=>{this.subscriptions.delete(s);},consumerHost:t});let{disposer:r}=this.subscriptions.get(s);s(this.value,r);}clearCallbacks(){this.subscriptions.clear();}};var Ge=class extends Event{constructor(s,t){super("context-provider",{bubbles:true,composed:true}),this.context=s,this.contextTarget=t;}},$t=class extends he{constructor(s,t,e){super(t.context!==void 0?t.initialValue:e),this.onContextRequest=r=>{if(r.context!==this.context)return;let i=r.contextTarget??r.composedPath()[0];i!==this.host&&(r.stopPropagation(),this.addCallback(r.callback,i,r.subscribe));},this.onProviderRequest=r=>{if(r.context!==this.context||(r.contextTarget??r.composedPath()[0])===this.host)return;let i=new Set;for(let[n,{consumerHost:l}]of this.subscriptions)i.has(n)||(i.add(n),l.dispatchEvent(new st(this.context,l,n,true)));r.stopPropagation();},this.host=s,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 Ge(this.context,this.host));}};function Be({context:o}){return (s,t)=>{let e=new WeakMap;if(typeof t=="object")return {get(){return s.get.call(this)},set(r){return e.get(this).setValue(r),s.set.call(this,r)},init(r){return e.set(this,new $t(this,{context:o,initialValue:r})),r}};{s.constructor.addInitializer((n=>{e.set(n,new $t(n,{context:o}));}));let r=Object.getOwnPropertyDescriptor(s,t),i;if(r===void 0){let n=new WeakMap;i={get(){return n.get(this)},set(l){e.get(this).setValue(l),n.set(this,l);},configurable:true,enumerable:true};}else {let n=r.set;i={...r,set(l){e.get(this).setValue(l),n?.call(this,l);}};}return void Object.defineProperty(s,t,i)}}}function I({context:o,subscribe:s}){return (t,e)=>{typeof e=="object"?e.addInitializer((function(){new _t(this,{context:o,callback:r=>{t.set.call(this,r);},subscribe:s});})):t.constructor.addInitializer((r=>{new _t(r,{context:o,callback:i=>{r[e]=i;},subscribe:s});}));}}var A="dashboard-store",ue=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}setFlows(t){this._state={...this._state,flows:t},this.notify("flows");}setRequests(t){this._state={...this._state,requests:t},this.notify("requests");}setFetches(t){this._state={...this._state,fetches:t},this.notify("fetches");}setErrors(t){this._state={...this._state,errors:t},this.notify("errors");}setLogs(t){this._state={...this._state,logs:t},this.notify("logs");}setQueries(t){this._state={...this._state,queries:t},this.notify("queries");}setIssues(t){this._state={...this._state,issues:t},this.notify("issues");}setMetrics(t){this._state={...this._state,metrics:t},this.notify("metrics");}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._state={...this._state,activeView:t},this.notify("activeView");}setViewMode(t){this._state={...this._state,viewMode:t},this.notify("viewMode");}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 xt=class extends E{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 r=new Date(t.timestamp).toLocaleTimeString(),i=this.expandedIdx===e;return a`
|
|
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
21
|
<span class="tel-timestamp">${r}</span>
|
|
22
22
|
</div>
|
|
23
|
-
${
|
|
23
|
+
${i&&t.stack?a`<div class="error-stack">${t.stack}</div>`:p}
|
|
24
24
|
`}render(){let t=this.store.state.errors;return t.length===0?a`<bk-empty-state
|
|
25
25
|
title="No errors"
|
|
26
26
|
subtitle="No errors have been captured yet"
|
|
@@ -31,54 +31,54 @@
|
|
|
31
31
|
<span style="width:130px;text-align:right">Time</span>
|
|
32
32
|
</div>
|
|
33
33
|
<div id="error-list">
|
|
34
|
-
${t.map((
|
|
34
|
+
${t.map((e,r)=>this.renderErrorRow(e,r))}
|
|
35
35
|
</div>
|
|
36
|
-
`}};u([
|
|
36
|
+
`}};u([I({context:A})],xt.prototype,"store",2),u([b()],xt.prototype,"expandedIdx",2),xt=u([S("bk-errors-view")],xt);var jt=class extends E{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}renderAnalysis(s){if(s.length===0)return p;let t={error:0,warn:0,info:0,debug:0,log:0};for(let e of s)t[e.level]!==void 0&&t[e.level]++;return a`
|
|
37
37
|
<div id="log-analysis">
|
|
38
38
|
<div class="fetch-summary">
|
|
39
|
-
<bk-stat-card value=${String(
|
|
40
|
-
${t.error>0?a`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:
|
|
41
|
-
${t.warn>0?a`<bk-stat-card value=${String(t.warn)} label="Warnings" color="var(--amber)"></bk-stat-card>`:
|
|
39
|
+
<bk-stat-card value=${String(s.length)} label="Total Logs"></bk-stat-card>
|
|
40
|
+
${t.error>0?a`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:p}
|
|
41
|
+
${t.warn>0?a`<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?a`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:
|
|
44
|
-
${t.log>0?a`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:
|
|
43
|
+
${t.debug>0?a`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:p}
|
|
44
|
+
${t.log>0?a`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:p}
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
|
-
`}renderLogRow(
|
|
47
|
+
`}renderLogRow(s){let t=new Date(s.timestamp).toLocaleTimeString();return a`
|
|
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-${s.level}">${s.level.toUpperCase()}</span>
|
|
50
|
+
<span class="tel-message tel-mono" title=${s.message}>${s.message}</span>
|
|
51
51
|
<span class="tel-timestamp">${t}</span>
|
|
52
52
|
</div>
|
|
53
|
-
`}render(){let
|
|
53
|
+
`}render(){let s=this.store.state.logs;return s.length===0?a`<bk-empty-state
|
|
54
54
|
title="No logs"
|
|
55
55
|
subtitle="No console output has been captured yet"
|
|
56
56
|
></bk-empty-state>`:a`
|
|
57
|
-
${this.renderAnalysis(
|
|
57
|
+
${this.renderAnalysis(s)}
|
|
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
|
+
${s.map(t=>this.renderLogRow(t))}
|
|
65
65
|
</div>
|
|
66
|
-
`}};u([
|
|
66
|
+
`}};u([I({context:A})],jt.prototype,"store",2),jt=u([S("bk-logs-view")],jt);var Ps={CHILD:2},qs=o=>(...s)=>({_$litDirective$:o,values:s}),me=class{constructor(s){}get _$AU(){return this._$AM._$AU}_$AT(s,t,e){this._$Ct=s,this._$AM=t,this._$Ci=e;}_$AS(s,t){return this.update(s,t)}update(s,t){return this.render(...t)}};var Yt=class extends me{constructor(s){if(super(s),this.it=p,s.type!==Ps.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(s){if(s===p||s==null)return this._t=void 0,this.it=s;if(s===Y)return s;if(typeof s!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(s===this.it)return this._t;this.it=s;let t=[s];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}};Yt.directiveName="unsafeHTML",Yt.resultType=1;var U=qs(Yt);var Vr=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 Us(o){let s=o.trimStart().split(/\s/)[0];return s?s.toUpperCase():"?"}function Fs(o){let s=o.replace(/\s+/g," ").trim(),t=s.match(/\bFROM\s+["'`]?(\w+)["'`]?/i);if(t)return t[1];let e=s.match(/\bINTO\s+["'`]?(\w+)["'`]?/i);if(e)return e[1];let r=s.match(/\bUPDATE\s+["'`]?(\w+)["'`]?/i);return r?r[1]:""}function Gs(o){return tt(o).replace(/\b\w+\b/g,s=>Vr.has(s.toUpperCase())?'<span class="sql-kw">'+s+"</span>":s)}var St=class extends E{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":_(t)}getQueryInfo(t){let e=(t.normalizedOp||t.operation||(t.sql?Us(t.sql):"?")).toUpperCase(),r=t.table||t.model||(t.sql?Fs(t.sql):""),i=t.sql||e+" "+r;return {op:e,table:r,sqlText:i}}renderQueryRow(t,e){let{op:r,table:i,sqlText:n}=this.getQueryInfo(t),l=pe[r]||"var(--text-dim)",c=t.durationMs>Oe,d=t.sql||r+" "+i,h=this.expandedIdx===e;return a`
|
|
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
72
|
<span class="query-op" title=${r} style="color:${l}">${r}</span>
|
|
73
|
-
<span class="query-table" title=${
|
|
74
|
-
<span class="query-preview" title=${
|
|
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
78
|
${h?a`
|
|
79
|
-
<pre class="query-detail-sql"
|
|
79
|
+
<pre class="query-detail-sql">${U(Gs(n))}</pre>
|
|
80
80
|
<bk-copy-button .text=${n} label="Copy"></bk-copy-button>
|
|
81
|
-
`:
|
|
81
|
+
`:p}
|
|
82
82
|
</div>
|
|
83
83
|
</div>
|
|
84
84
|
`}render(){let t=this.store.state.queries;return t.length===0?a`<bk-empty-state
|
|
@@ -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((
|
|
95
|
+
${t.map((e,r)=>this.renderQueryRow(e,r))}
|
|
96
96
|
</div>
|
|
97
|
-
`}};u([
|
|
97
|
+
`}};u([I({context:A})],St.prototype,"store",2),u([b()],St.prototype,"expandedIdx",2),St=u([S("bk-queries-view")],St);function We(o){return o.replaceAll("'","'\\''")}function Kr(o,s){let t=Object.entries(o.headers||{}).filter(([i])=>!Ds.has(i)).map(([i,n])=>`-H '${We(i)}: ${We(n)}'`).join(" "),e=o.requestBody?` -d '${We(o.requestBody)}'`:"",r=s?`http://localhost:${s}`:"";return `curl -X ${o.method} ${t}${e} '${r}${o.url}'`}function Tt(o){let s=window.__BRAKIT_CONFIG__?.port??"",t=Kr(o,s);navigator.clipboard.writeText(t).then(()=>D.show("Copied cURL command"));}var wt=class extends E{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(),Tt(t);}renderDetail(t){return a`
|
|
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?a`<span>${
|
|
102
|
+
${t.responseSize?a`<span>${et(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>${U(Et(t.headers))}</pre></div>
|
|
107
|
+
<div class="detail-section"><h4>Response Headers</h4><pre>${U(Et(t.responseHeaders))}</pre></div>
|
|
108
|
+
<div class="detail-section"><h4>Request Body</h4><pre>${U(nt(t.requestBody))}</pre></div>
|
|
109
|
+
<div class="detail-section"><h4>Response Body</h4><pre>${U(nt(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 a`
|
|
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">${et(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(F));return t.length===0?a`<bk-empty-state title="No requests" subtitle="No HTTP requests have been captured yet"></bk-empty-state>`:a`
|
|
126
126
|
<div class="col-header">
|
|
127
127
|
<span style="width:60px">Method</span>
|
|
128
128
|
<span style="flex:1">URL</span>
|
|
@@ -130,51 +130,51 @@
|
|
|
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})],wt.prototype,"store",2),u([b()],wt.prototype,"expandedId",2),wt=u([S("bk-requests-view")],wt);var Xt=class extends E{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}buildGroups(s,t){let e=new Map;for(let i of t)e.set(i.id,i);let r={};for(let i of s){let n=i.method+" "+i.url;r[n]||(r[n]={method:i.method,url:i.url,count:0,totalDur:0,maxDur:0,errors:0,callers:{},statusCodes:{},firstTs:i.timestamp,lastTs:i.timestamp});let l=r[n];if(l.count++,l.totalDur+=i.durationMs,i.durationMs>l.maxDur&&(l.maxDur=i.durationMs),i.statusCode>=400&&l.errors++,l.statusCodes[i.statusCode]=(l.statusCodes[i.statusCode]||0)+1,i.timestamp<l.firstTs&&(l.firstTs=i.timestamp),i.timestamp>l.lastTs&&(l.lastTs=i.timestamp),i.parentRequestId){let c=e.get(i.parentRequestId);c&&(l.callers[c.method+" "+(c.path||c.url)]=1);}}return Object.values(r).sort((i,n)=>n.count-i.count)}renderSummary(s){let t=new Set,e=0,r=0;for(let n of s)t.add(n.url),n.statusCode>=400&&e++,r+=n.durationMs;let i=Math.round(r/s.length);return a`
|
|
135
135
|
<div class="fetch-summary">
|
|
136
|
-
<bk-stat-card value=${String(
|
|
136
|
+
<bk-stat-card value=${String(s.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=${_(i)} label="Avg Duration"></bk-stat-card>
|
|
140
140
|
</div>
|
|
141
|
-
`}formatTime(
|
|
141
|
+
`}formatTime(s){return new Date(s).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}renderGroup(s){let t=Math.round(s.totalDur/s.count),e=s.count>0?Math.round(s.errors/s.count*100):0,r=Object.keys(s.callers),i=Object.entries(s.statusCodes),n=i.length>0?Number(i.sort((l,c)=>c[1]-l[1])[0][0]):0;return a`
|
|
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?a`<bk-status-pill .code=${n}></bk-status-pill>`:
|
|
147
|
-
<span class="fetch-group-count">${
|
|
144
|
+
<bk-method-badge .method=${s.method}></bk-method-badge>
|
|
145
|
+
<span class="fetch-group-url" title=${s.url}>${s.url}</span>
|
|
146
|
+
${n>0?a`<bk-status-pill .code=${n}></bk-status-pill>`:p}
|
|
147
|
+
<span class="fetch-group-count">${s.count}x</span>
|
|
148
148
|
</div>
|
|
149
149
|
<div class="fetch-group-meta">
|
|
150
|
-
<span>avg ${
|
|
150
|
+
<span>avg ${_(t)}</span>
|
|
151
151
|
<span class="fetch-group-sep">\u00b7</span>
|
|
152
|
-
<span>max ${
|
|
152
|
+
<span>max ${_(s.maxDur)}</span>
|
|
153
153
|
<span class="fetch-group-sep">\u00b7</span>
|
|
154
|
-
${
|
|
154
|
+
${e>0?a`<span class="fetch-group-err">${e}% errors</span>`:a`<span class="fetch-group-ok">0% errors</span>`}
|
|
155
155
|
</div>
|
|
156
|
-
${
|
|
156
|
+
${s.firstTs>0?a`
|
|
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(s.firstTs)}${s.firstTs!==s.lastTs?a` \u2192 ${this.formatTime(s.lastTs)}`:p}
|
|
161
161
|
</span>
|
|
162
|
-
</div>`:
|
|
162
|
+
</div>`:p}
|
|
163
163
|
${r.length>0?a`
|
|
164
164
|
<div class="fetch-group-callers">
|
|
165
165
|
<span class="fetch-group-callers-label">Called by</span>
|
|
166
166
|
${r.map(l=>a`<span class="fetch-group-caller-pill">${l}</span>`)}
|
|
167
|
-
</div>`:
|
|
167
|
+
</div>`:p}
|
|
168
168
|
</div>
|
|
169
|
-
`}render(){let
|
|
169
|
+
`}render(){let s=this.store.state.fetches,t=this.store.state.requests;if(s.length===0)return a`<bk-empty-state title="No fetches" subtitle="No outbound HTTP calls have been captured yet"></bk-empty-state>`;let e=this.buildGroups(s,t);return a`
|
|
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(s)}
|
|
172
|
+
${e.length>0?a`
|
|
173
|
+
<div class="fetch-groups-title">Grouped by URL (${e.length})</div>
|
|
174
|
+
<div class="fetch-groups">${e.map(r=>this.renderGroup(r))}</div>
|
|
175
|
+
`:p}
|
|
176
176
|
</div>
|
|
177
|
-
`}};u([
|
|
177
|
+
`}};u([I({context:A})],Xt.prototype,"store",2),Xt=u([S("bk-fetches-view")],Xt);function Bs(o,s){if(s>=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 Qe(o){return o==="query"?"var(--accent)":"var(--cyan)"}function ti(o){return o.type==="query"||o.type==="fetch"}function ei(o){let s=(o.normalizedOp||o.operation||"QUERY").toUpperCase(),t=o.table||o.model||"";return {label:`${s} ${t}`,tooltip:o.sql||`${s} ${t}`}}function si(o){return {label:`${o.method} ${o.url}`,tooltip:`${o.method} ${o.url}`}}function ri(o,s,t,e,r,i){let n=o.data.durationMs||0,l,c;if(i){let d=Math.max(o.timestamp-e,0);l=Math.min(d/r*100,95),c=Math.max(n/r*100,1.5);}else {let d=t[0].timestamp,m=t[t.length-1].timestamp-d;l=m>0?(o.timestamp-d)/m*85:s/Math.max(t.length-1,1)*85,c=Math.max(n/r*100,1.5);}return l+c>100&&(c=Math.max(100-l,1.5)),{leftPct:l,widthPct:c}}function ii(o,s){let t=o.timeline.filter(ti);if(t.length===0)return [];let e=s.startedAt,r=s.durationMs||1,i=Math.abs(t[0].timestamp-e)<r*10;return t.map((n,l)=>{let c=n.data.durationMs||0,{leftPct:d,widthPct:h}=ri(n,l,t,e,r,i),m=n.type==="query"?ei(n.data):si(n.data);return {type:n.type,label:m.label,durMs:c,durLabel:_(c),tooltip:m.tooltip,leftPct:d,widthPct:h}})}function Qs(o,s){let t=o.requests.filter(l=>!l.isStrictModeDupe);if(t.length===0)return {rows:[],totalMs:0};let e=Math.min(...t.map(l=>l.startedAt)),i=Math.max(...t.map(l=>l.startedAt+l.durationMs))-e;return i===0?{rows:[],totalMs:0}:{rows:t.map(l=>{let c=(l.startedAt-e)/i*100,d=Math.max(l.durationMs/i*100,.5),h=s?.activities?.[l.id],m=h?ii(h,l):[];return {label:`${l.method} ${l.label}`,leftPct:c,widthPct:d,color:Bs(l.method,l.statusCode),durMs:l.durationMs,durLabel:_(l.durationMs),tooltip:`${l.method} ${l.label} (${_(l.durationMs)})`,subEvents:m}}),totalMs:i}}function js(o){let s=o.requests,t=[],e=[],r=[],i=[],n=new Map;for(let d of s){let h=d.label,m=d.pollingDurationMs||d.durationMs;if(!Gt[d.category||""]){if(d.isDuplicate){let v=n.get(h);v?(v.count++,v.wastedMs+=m):n.set(h,{name:h,count:2,wastedMs:m});continue}if(d.statusCode>=400){e.push(h+" ("+Hs(d.statusCode)+")");continue}d.responseSize>51200&&r.push("Large response: "+h+" returned "+et(d.responseSize)),t.push(h);}}for(let d of n.values())i.push(d);let l="";if(i.length>0){let d=i.map(m=>m.name).join(", "),h=i.reduce((m,v)=>m+v.wastedMs,0);l="Your app fetches "+d+" multiple times on this page. This wastes ~"+_(h)+". Try caching these calls, deduplicating with React Query/SWR, or moving them to a shared layout.";}else e.length>0&&(l="Some requests are failing. Check your API routes and make sure the endpoints exist.");let c=s.filter(d=>d.durationMs>2e3&&d.category!==ut);return c.length>0&&!l&&(l=c.map(d=>d.label).join(", ")+` is taking over ${_(2e3)}. Consider adding caching or optimizing the backend query.`),{successes:t,errors:e,warnings:r,duplicates:i,tip:l}}var W=class extends E{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(r=>r.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,r=e.parentElement;if(!r)return;e.classList.toggle("open");let i=r.querySelector("pre");i&&i.classList.toggle("open");}switchTab(t,e,r){r.stopPropagation(),this.flowDetailTab=t,t==="timeline"&&!this.flowTimeline&&this.loadFlowTimeline(e);}async loadFlowTimeline(t){if(this.flowTimelineLoading)return;let e=t.requests.map(r=>r.id).filter(Boolean);if(e.length!==0){this.flowTimelineLoading=true;try{let r=await fetch(`${w.activity}?requestIds=${e.join(",")}`);if(!r.ok){this.flowTimelineLoading=!1;return}this.flowTimeline=await r.json();}catch{}this.flowTimelineLoading=false;}}loadTimelineForContainer(t){let e=t.querySelectorAll(".request-timeline");for(let r of e){let i=r.getAttribute("data-request-id");if(i&&!r.hasAttribute("data-loaded")){r.setAttribute("data-loaded","1");let n=document.createElement("bk-timeline-panel");n.setAttribute("request-id",i),n.setAttribute("request-started",r.getAttribute("data-request-started")||"0"),r.appendChild(n),r.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?a`<bk-empty-state
|
|
178
178
|
title="No actions yet"
|
|
179
179
|
subtitle="Start using your app to see user action flows here"
|
|
180
180
|
></bk-empty-state>`:a`
|
|
@@ -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((
|
|
189
|
+
${t.map((e,r)=>this.renderFlowRow(e,r))}
|
|
190
190
|
</div>
|
|
191
|
-
`}renderFlowRow(t,
|
|
191
|
+
`}renderFlowRow(t,e){let r=this.expandedFlowIdx===e,i=this.flowDotClass(t),n=this.flowBadgeInfo(t);return a`
|
|
192
192
|
<div
|
|
193
193
|
class="flow-row ${r?"expanded":""}"
|
|
194
|
-
@click=${()=>this.toggleFlow(
|
|
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,20 +202,20 @@
|
|
|
202
202
|
>
|
|
203
203
|
<span class="flow-badge-pill ${n.cls}">${n.text}</span>
|
|
204
204
|
<span class="flow-duration"
|
|
205
|
-
>${
|
|
205
|
+
>${_(t.totalDurationMs)}</span
|
|
206
206
|
>
|
|
207
207
|
</div>
|
|
208
208
|
</div>
|
|
209
209
|
<div class="flow-expand ${r?"open":""}">
|
|
210
|
-
${r?this.renderFlowDetail(t):
|
|
210
|
+
${r?this.renderFlowDetail(t):p}
|
|
211
211
|
</div>
|
|
212
|
-
`}renderFlowDetail(t){let
|
|
212
|
+
`}renderFlowDetail(t){let e=this.viewMode==="simple"?"Insights":"Details";return a`
|
|
213
213
|
<div class="flow-detail-tabs">
|
|
214
214
|
<button
|
|
215
215
|
class="flow-tab ${this.flowDetailTab==="insights"?"active":""}"
|
|
216
216
|
@click=${r=>this.switchTab("insights",t,r)}
|
|
217
217
|
>
|
|
218
|
-
${
|
|
218
|
+
${e}
|
|
219
219
|
</button>
|
|
220
220
|
<button
|
|
221
221
|
class="flow-tab ${this.flowDetailTab==="timeline"?"active":""}"
|
|
@@ -225,13 +225,13 @@
|
|
|
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 a`<div class="wf-loading">Loading timeline...</div>`;let{rows:
|
|
228
|
+
`}renderFlowWaterfall(t){if(this.flowTimelineLoading)return a`<div class="wf-loading">Loading timeline...</div>`;let{rows:e,totalMs:r}=Qs(t,this.flowTimeline);if(e.length===0)return p;let i=[];for(let n=0;n<=5;n++)i.push(_(r/5*n));return a`
|
|
229
229
|
<div class="flow-waterfall">
|
|
230
230
|
<div class="wf-time-axis">
|
|
231
|
-
${
|
|
231
|
+
${i.map(n=>a`<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
237
|
`}renderWaterfallGroup(t){return a`
|
|
@@ -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=>a`
|
|
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:${Qe(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:${Qe(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=js(t),r=e.errors.length>0||e.duplicates.length>0||e.warnings.length>0||!!e.tip;return a`
|
|
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
|
${r?a`
|
|
274
274
|
<div class="flow-divider"></div>
|
|
275
275
|
<div class="flow-insights">
|
|
276
|
-
${
|
|
277
|
-
✗ ${
|
|
276
|
+
${e.errors.map(i=>a`<div class="insight-line insight-error">
|
|
277
|
+
✗ ${i}
|
|
278
278
|
</div>`)}
|
|
279
|
-
${
|
|
280
|
-
⚠ ${
|
|
281
|
-
~${
|
|
279
|
+
${e.duplicates.map(i=>a`<div class="insight-line insight-warn">
|
|
280
|
+
⚠ ${i.name} — loaded ${i.count}x (wasting
|
|
281
|
+
~${_(i.wastedMs)})
|
|
282
282
|
</div>`)}
|
|
283
|
-
${
|
|
284
|
-
${
|
|
285
|
-
Tip: ${
|
|
286
|
-
</div>`:
|
|
283
|
+
${e.warnings.map(i=>a`<div class="insight-line insight-warn">⚠ ${i}</div>`)}
|
|
284
|
+
${e.tip?a`<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(Gt[t.category||""])return p;let e=bt(t.statusCode),r=_(t.pollingDurationMs||t.durationMs),i=!t.isDuplicate&&t.category!==re&&t.category!==ut||t.requestBody&&t.method!=="GET"||!!t.responseBody;return a`
|
|
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 ${
|
|
299
|
+
<span class="status-pill ${e}">${t.statusCode}</span>
|
|
300
300
|
<span class="traffic-card-dur">${r}</span>
|
|
301
301
|
${t.isDuplicate?a`<span class="traffic-card-dup">duplicate</span>`:a`<span class="traffic-card-size"
|
|
302
|
-
>${
|
|
302
|
+
>${et(t.responseSize)}</span
|
|
303
303
|
>`}
|
|
304
304
|
</div>
|
|
305
305
|
${t.isStrictModeDupe?a`<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!==re&&t.category!==ut?a`<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,
|
|
316
|
+
`}renderBodyToggle(t,e,r){let i=t==="out"?"\u2192":"\u2190";return a`
|
|
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>${U(nt(r))}</pre>
|
|
323
323
|
</div>
|
|
324
324
|
`}renderFlowSubReqs(t){return a`<div class="flow-subreqs">
|
|
325
|
-
${t.requests.map((
|
|
326
|
-
</div>`}renderSubReqRow(t,
|
|
325
|
+
${t.requests.map((e,r)=>this.renderSubReqRow(e,r))}
|
|
326
|
+
</div>`}renderSubReqRow(t,e){let r=this.expandedSubReqIdx===e,i=bt(t.statusCode),n=t.pollingDurationMs?_(t.pollingDurationMs):_(t.durationMs);return a`
|
|
327
327
|
<div
|
|
328
328
|
class="flow-subreq ${r?"expanded":""}"
|
|
329
|
-
@click=${l=>this.toggleSubReq(
|
|
329
|
+
@click=${l=>this.toggleSubReq(e,l)}
|
|
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?a`<span class="subreq-dup-tag">duplicate</span>`:
|
|
336
|
-
<span class="status-pill ${
|
|
335
|
+
${t.isDuplicate?a`<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
339
|
<div class="flow-subreq-detail ${r?"open":""}">
|
|
340
|
-
${r?this.renderSubReqDetail(t):
|
|
340
|
+
${r?this.renderSubReqDetail(t):p}
|
|
341
341
|
</div>
|
|
342
|
-
`}renderSubReqDetail(t){let
|
|
342
|
+
`}renderSubReqDetail(t){let e=bt(t.statusCode);return a`
|
|
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> ${tt(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?a`<span>${
|
|
351
|
+
${t.responseSize?a`<span>${et(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>${U(Et(t.headers))}</pre>
|
|
362
362
|
</div>
|
|
363
363
|
<div class="detail-section">
|
|
364
364
|
<h4>Response Headers</h4>
|
|
365
|
-
<pre
|
|
365
|
+
<pre>${U(Et(t.responseHeaders))}</pre>
|
|
366
366
|
</div>
|
|
367
367
|
<div class="detail-section">
|
|
368
368
|
<h4>Request Body</h4>
|
|
369
|
-
<pre
|
|
369
|
+
<pre>${U(nt(t.requestBody))}</pre>
|
|
370
370
|
</div>
|
|
371
371
|
<div class="detail-section">
|
|
372
372
|
<h4>Response Body</h4>
|
|
373
|
-
<pre
|
|
373
|
+
<pre>${U(nt(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=${r=>{r.stopPropagation(),
|
|
379
|
+
@click=${r=>{r.stopPropagation(),Tt(t);}}
|
|
380
380
|
>
|
|
381
381
|
Copy cURL
|
|
382
382
|
</button>
|
|
383
383
|
</div>
|
|
384
|
-
`}};u([
|
|
384
|
+
`}};u([I({context:A})],W.prototype,"store",2),u([b()],W.prototype,"expandedFlowIdx",2),u([b()],W.prototype,"expandedSubReqIdx",2),u([b()],W.prototype,"flowDetailTab",2),u([b()],W.prototype,"flowTimeline",2),u([b()],W.prototype,"flowTimelineLoading",2),W=u([S("bk-flows-view")],W);var Vt=class extends E{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}render(){let s=(this.store.state.issues||[]).slice(),t=s.filter(l=>l.state==="open"||l.state==="fixing"||l.state==="regressed"),e=s.filter(l=>l.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?a`
|
|
385
385
|
<div class="sec-clear">
|
|
386
386
|
<span class="sec-clear-icon">\u2713</span>
|
|
387
387
|
<div class="sec-clear-text">
|
|
@@ -389,109 +389,109 @@
|
|
|
389
389
|
<div class="sec-clear-sub">No security or quality issues detected this session</div>
|
|
390
390
|
</div>
|
|
391
391
|
</div>
|
|
392
|
-
`:a`<bk-empty-state title="Waiting for requests..." subtitle="Start using your app to see security findings here"></bk-empty-state>`;let r=0,
|
|
392
|
+
`:a`<bk-empty-state title="Waiting for requests..." subtitle="Start using your app to see security findings here"></bk-empty-state>`;let r=0,i=0,n=0;for(let l of t){let c=l.issue.severity;c==="critical"?r++:c==="info"?n++:i++;}return a`
|
|
393
393
|
<div id="security-content">
|
|
394
|
-
${this.renderSummary(t.length,
|
|
395
|
-
${t.length===0&&
|
|
394
|
+
${this.renderSummary(t.length,e.length,r,i,n)}
|
|
395
|
+
${t.length===0&&e.length>0?a`
|
|
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(s,t,e,r,i){return a`
|
|
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?a`<span class="sec-resolved-badge">${t} resolved</span>`:
|
|
410
|
+
<span class="sec-summary-count">${s}</span>
|
|
411
|
+
<span class="sec-summary-label">open issue${s!==1?"s":""}</span>
|
|
412
|
+
${t>0?a`<span class="sec-resolved-badge">${t} resolved</span>`:p}
|
|
413
413
|
</div>
|
|
414
414
|
<div class="sec-summary-right">
|
|
415
|
-
${
|
|
416
|
-
${r>0?a`<span class="sec-badge warning">${r} warning</span>`:
|
|
417
|
-
${
|
|
415
|
+
${e>0?a`<span class="sec-badge critical">${e} critical</span>`:p}
|
|
416
|
+
${r>0?a`<span class="sec-badge warning">${r} warning</span>`:p}
|
|
417
|
+
${i>0?a`<span class="sec-badge info">${i} info</span>`:p}
|
|
418
418
|
</div>
|
|
419
419
|
</div>
|
|
420
|
-
`}renderOpenGroups(
|
|
420
|
+
`}renderOpenGroups(s){let t={},e=[];for(let r of s){let i=r.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(r);}return e.sort((r,i)=>{let n=Z[t[r].severity]?.sort??2,l=Z[t[i].severity]?.sort??2;return n!==l?n-l:t[i].items.length-t[r].items.length}),a`${e.map(r=>this.renderGroup(t[r]))}`}renderGroup(s){let t=Z[s.severity]||Z.info;return a`
|
|
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">${s.title}</span>
|
|
425
|
+
<span class="sec-group-count">${s.items.length}</span>
|
|
426
426
|
</div>
|
|
427
|
-
${
|
|
428
|
-
<div class="sec-items">${
|
|
427
|
+
${s.hint?a`<div class="sec-hint">${s.hint}</div>`:p}
|
|
428
|
+
<div class="sec-items">${s.items.map(e=>this.renderIssueItem(e))}</div>
|
|
429
429
|
</div>
|
|
430
|
-
`}renderIssueItem(
|
|
430
|
+
`}renderIssueItem(s){let t=s.issue;return a`
|
|
431
431
|
<div class="sec-item">
|
|
432
432
|
<div class="sec-item-desc">${t.desc}</div>
|
|
433
|
-
${
|
|
434
|
-
${
|
|
435
|
-
${
|
|
433
|
+
${s.occurrences>1?a`<span class="sec-item-count">${s.occurrences}x</span>`:p}
|
|
434
|
+
${s.state==="fixing"&&s.aiStatus==="fixed"?a`<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>`:s.aiStatus==="wont_fix"?a`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:s.state==="regressed"?a`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:p}
|
|
435
|
+
${s.aiNotes?a`<div class="sec-ai-notes">${s.aiNotes}</div>`:p}
|
|
436
436
|
</div>
|
|
437
|
-
`}renderResolved(
|
|
437
|
+
`}renderResolved(s){return a`
|
|
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">${s.length}</span>
|
|
441
441
|
</div>
|
|
442
442
|
<div class="sec-group sec-group-resolved">
|
|
443
443
|
<div class="sec-items">
|
|
444
|
-
${
|
|
444
|
+
${s.map(t=>a`
|
|
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"?a`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:
|
|
449
|
-
${t.aiNotes?a`<div class="sec-ai-notes">${t.aiNotes}</div>`:
|
|
448
|
+
${t.aiStatus==="fixed"?a`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:p}
|
|
449
|
+
${t.aiNotes?a`<div class="sec-ai-notes">${t.aiNotes}</div>`:p}
|
|
450
450
|
</div>
|
|
451
451
|
`)}
|
|
452
452
|
</div>
|
|
453
453
|
</div>
|
|
454
|
-
`}};u([
|
|
454
|
+
`}};u([I({context:A})],Vt.prototype,"store",2),Vt=u([S("bk-security-view")],Vt);var Rt=class extends E{constructor(){super(...arguments);this.expandedCardIdx=-1;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}toggleCard(t){this.expandedCardIdx=this.expandedCardIdx===t?-1:t;}render(){let t=this.store.state,e=t.requests.filter(y=>!y.isStatic&&!y.isHealthCheck&&(!y.path||y.path.indexOf(F)!==0));if(!(e.length>0||t.queries.length>0||t.errors.length>0))return a`<bk-empty-state
|
|
455
455
|
title="Waiting for requests..."
|
|
456
456
|
subtitle="Start using your app to see insights here"
|
|
457
|
-
></bk-empty-state>`;let
|
|
457
|
+
></bk-empty-state>`;let i=e.filter(y=>y.statusCode>=400).length,n=new Set(["data-fetch","api-call","server-action","page-load"]),l=e.filter(y=>y.category&&n.has(y.category)),c=l.length>0?l:e,d=c.length>0?Math.round(c.reduce((y,k)=>y+k.durationMs,0)/c.length):0,h=t.issues||[],m=h.filter(y=>y.state==="open"||y.state==="regressed"),v=h.filter(y=>y.state==="fixing"),x=h.filter(y=>y.state==="resolved");return a`
|
|
458
458
|
<div class="ov-container" id="overview-content">
|
|
459
|
-
${this.renderSummary(
|
|
460
|
-
${m.length===0&&
|
|
459
|
+
${this.renderSummary(e.length,t.flows.length,d,t.queries.length,i,t.fetches.length)}
|
|
460
|
+
${m.length===0&&v.length===0&&x.length===0?a`<div class="ov-clear">
|
|
461
461
|
<span class="ov-clear-icon">\u2713</span>All clear \u2014 no issues detected
|
|
462
|
-
</div>`:
|
|
463
|
-
${m.length===0&&
|
|
462
|
+
</div>`:p}
|
|
463
|
+
${m.length===0&&x.length>0?a`<div class="ov-clear">
|
|
464
464
|
<span class="ov-clear-icon">\u2713</span>All issues resolved \u2014
|
|
465
|
-
${
|
|
465
|
+
${x.length} finding${x.length!==1?"s were":" was"} detected and
|
|
466
466
|
fixed
|
|
467
|
-
</div>`:
|
|
468
|
-
${m.length>0?this.renderOpenIssues(m):
|
|
469
|
-
${
|
|
470
|
-
${
|
|
467
|
+
</div>`:p}
|
|
468
|
+
${m.length>0?this.renderOpenIssues(m):p}
|
|
469
|
+
${v.length>0?this.renderVerifying(v):p}
|
|
470
|
+
${x.length>0?this.renderResolvedIssues(x):p}
|
|
471
471
|
</div>
|
|
472
|
-
`}renderSummary(t,
|
|
472
|
+
`}renderSummary(t,e,r,i,n,l){return a`
|
|
473
473
|
<div class="ov-summary">
|
|
474
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">${
|
|
476
|
-
<div class="ov-stat"><span class="ov-stat-value">${
|
|
477
|
-
<div class="ov-stat"><span class="ov-stat-value">${
|
|
475
|
+
<div class="ov-stat"><span class="ov-stat-value">${e}</span><span class="ov-stat-label">Actions</span></div>
|
|
476
|
+
<div class="ov-stat"><span class="ov-stat-value">${_(r)}</span><span class="ov-stat-label">Avg Response</span></div>
|
|
477
|
+
<div class="ov-stat"><span class="ov-stat-value">${i}</span><span class="ov-stat-label">Queries</span></div>
|
|
478
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
479
|
<div class="ov-stat"><span class="ov-stat-value">${l}</span><span class="ov-stat-label">Fetches</span></div>
|
|
480
480
|
</div>
|
|
481
481
|
`}renderOpenIssues(t){return a`
|
|
482
482
|
<div class="ov-section-title">Issues Found <span class="ov-issue-count">${t.length}</span></div>
|
|
483
|
-
<div class="ov-cards">${t.map((
|
|
484
|
-
`}renderIssueCard(t,
|
|
485
|
-
<div class="ov-card ${n?"expanded":""}" @click=${()=>this.toggleCard(
|
|
486
|
-
<span class="ov-card-icon ${
|
|
483
|
+
<div class="ov-cards">${t.map((e,r)=>this.renderIssueCard(e,r))}</div>
|
|
484
|
+
`}renderIssueCard(t,e){let r=t.issue,i=Z[r.severity]||Z.info,n=this.expandedCardIdx===e,l=t.aiStatus==="wont_fix"?a`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:t.state==="regressed"?a`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:p,c=t.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Resolving\u2026 ${t.cleanHitsSinceLastSeen}/${5} clean requests</div>`:p;return a`
|
|
485
|
+
<div class="ov-card ${n?"expanded":""}" @click=${()=>this.toggleCard(e)}>
|
|
486
|
+
<span class="ov-card-icon ${i.cls}">${i.icon}</span>
|
|
487
487
|
<div class="ov-card-body">
|
|
488
488
|
<div class="ov-card-title">${r.title}${l}</div>
|
|
489
489
|
<div class="ov-card-desc">${r.desc}</div>
|
|
490
|
-
${r.detail?a`<div class="ov-card-detail">${r.detail}</div>`:
|
|
490
|
+
${r.detail?a`<div class="ov-card-detail">${r.detail}</div>`:p}
|
|
491
491
|
${c}
|
|
492
|
-
${n&&r.hint?a`<div class="ov-card-hint">${r.hint}</div>`:
|
|
492
|
+
${n&&r.hint?a`<div class="ov-card-hint">${r.hint}</div>`:p}
|
|
493
493
|
</div>
|
|
494
|
-
${r.hint?a`<span class="ov-card-arrow">${n?"\u2193":"\u2192"}</span>`:
|
|
494
|
+
${r.hint?a`<span class="ov-card-arrow">${n?"\u2193":"\u2192"}</span>`:p}
|
|
495
495
|
</div>
|
|
496
496
|
`}renderVerifying(t){return a`
|
|
497
497
|
<div class="ov-section-title ov-resolved-title">
|
|
@@ -499,7 +499,7 @@
|
|
|
499
499
|
<span class="ov-issue-count">${t.length}</span>
|
|
500
500
|
</div>
|
|
501
501
|
<div class="ov-cards">
|
|
502
|
-
${t.map(
|
|
502
|
+
${t.map(e=>{let r=e.issue,i=e.cleanHitsSinceLastSeen>0?a`<div class="ov-card-resolving">Verifying\u2026 ${e.cleanHitsSinceLastSeen}/${5} clean requests</div>`:p;return a`
|
|
503
503
|
<div class="ov-card ov-card-resolved">
|
|
504
504
|
<span class="ov-card-icon resolved">\u29d7</span>
|
|
505
505
|
<div class="ov-card-body">
|
|
@@ -508,7 +508,7 @@
|
|
|
508
508
|
<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>
|
|
509
509
|
</div>
|
|
510
510
|
<div class="ov-card-desc">${r.desc}</div>
|
|
511
|
-
${
|
|
511
|
+
${i}
|
|
512
512
|
</div>
|
|
513
513
|
</div>
|
|
514
514
|
`})}
|
|
@@ -519,46 +519,46 @@
|
|
|
519
519
|
<span class="ov-issue-count">${t.length}</span>
|
|
520
520
|
</div>
|
|
521
521
|
<div class="ov-cards">
|
|
522
|
-
${t.map(
|
|
522
|
+
${t.map(e=>a`
|
|
523
523
|
<div class="ov-card ov-card-resolved">
|
|
524
524
|
<span class="ov-card-icon resolved">\u2713</span>
|
|
525
525
|
<div class="ov-card-body">
|
|
526
|
-
<div class="ov-card-title" style="text-decoration:line-through;color:var(--text-muted)">${
|
|
527
|
-
<div class="ov-card-desc">${
|
|
526
|
+
<div class="ov-card-title" style="text-decoration:line-through;color:var(--text-muted)">${e.issue.title}</div>
|
|
527
|
+
<div class="ov-card-desc">${e.issue.desc}</div>
|
|
528
528
|
</div>
|
|
529
529
|
</div>
|
|
530
530
|
`)}
|
|
531
531
|
</div>
|
|
532
|
-
`}};u([
|
|
532
|
+
`}};u([I({context:A})],Rt.prototype,"store",2),u([b()],Rt.prototype,"expandedCardIdx",2),Rt=u([S("bk-overview-view")],Rt);function Ys(o){return o<1?"<1ms":o<1e3?Math.round(o)+"ms":(o/1e3).toFixed(1)+"s"}function oi(o){return o<mt?Bt.green:o<ft?Bt.amber:Bt.red}function ni(o){return o.statusCode>=400?Bt.red:oi(o.durationMs)}function Xs(o){return [parseInt(o.slice(1,3),16),parseInt(o.slice(3,5),16),parseInt(o.slice(5,7),16)]}function ai(o){let s=o.getContext("2d");if(!s)return null;let t=window.devicePixelRatio||1,e=o.clientWidth,r=o.clientHeight;return o.width=e*t,o.height=r*t,s.scale(t,t),{ctx:s,w:e,h:r}}function li(o,s,t,e,r){let[i,n,l]=Xs(r);o.beginPath(),o.arc(s,t,e+2,0,Math.PI*2),o.fillStyle=`rgba(${i},${n},${l},0.25)`,o.fill(),o.beginPath(),o.arc(s,t,e,0,Math.PI*2),o.fillStyle=r,o.fill();}function ci(o,s,t,e,r,i){let[n,l,c]=Xs(r);o.strokeStyle=`rgba(${n},${l},${c},0.3)`,o.lineWidth=i+2,o.beginPath(),o.moveTo(s-e,t-e),o.lineTo(s+e,t+e),o.moveTo(s+e,t-e),o.lineTo(s-e,t+e),o.stroke(),o.strokeStyle=r,o.lineWidth=i,o.beginPath(),o.moveTo(s-e,t-e),o.lineTo(s+e,t+e),o.moveTo(s+e,t-e),o.lineTo(s-e,t+e),o.stroke();}function Vs(o,s){let t=[],e=ai(o);if(!e||s.length===0)return t;let{ctx:r,w:i,h:n}=e,l=Ls,c=i-l.left-l.right,d=n-l.top-l.bottom,h=0,m=s[0].timestamp,v=s[0].timestamp;for(let $ of s)$.durationMs>h&&(h=$.durationMs),$.timestamp<m&&(m=$.timestamp),$.timestamp>v&&(v=$.timestamp);h=Math.max(h,10),h=Math.ceil(h*1.15/10)*10;let x=v-m||1;r.strokeStyle=Is,r.lineWidth=1;let y=4;for(let $=0;$<=y;$++){let f=l.top+d-$/y*d;r.beginPath(),r.moveTo(l.left,f),r.lineTo(l.left+c,f),r.stroke(),r.fillStyle=Ue,r.font=Cs,r.textAlign="right",r.fillText(Ys(Math.round($/y*h)),l.left-8,f+3);}for(let $ of [{ms:mt},{ms:ft}]){if($.ms>=h)continue;let f=l.top+d-$.ms/h*d;r.beginPath(),r.setLineDash([4,4]),r.strokeStyle="rgba(113,113,122,0.3)",r.lineWidth=1,r.moveTo(l.left,f),r.lineTo(l.left+c,f),r.stroke(),r.setLineDash([]),r.fillStyle="rgba(113,113,122,0.5)",r.font=Fe,r.textAlign="left",r.fillText(Ys($.ms),l.left+c+2,f+3);}for(let $=0;$<s.length;$++){let f=s[$],g=s.length===1?l.left+c/2:l.left+(f.timestamp-m)/x*c,T=l.top+d-f.durationMs/h*d,R=ni(f);t.push({x:g,y:T,idx:$,r:f}),f.statusCode>=400?ci(r,g,T,4,R,2):li(r,g,T,4,R);}r.fillStyle=Ue,r.font=Fe,r.textAlign="center";let k=[m,m+x/2,v];for(let $=0;$<k.length;$++){let f=l.left+$/2*c,g=new Date(k[$]);r.fillText(g.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),f,l.top+d+14);}return t}var fi={max:1/0,label:"Pending",color:"var(--text-muted)",bg:"var(--bg-muted)",border:"var(--border)"};function Ks(o,s,t){return t>=20?o:s}function Ye(o,s){if(!s||s<=0)return fi;let t=o/s;return t<.7?gt[0]:t<1.2?gt[1]:t<2?gt[2]:t<3?gt[3]:gt[4]}var Q=class extends E{constructor(){super(...arguments);this.selectedEndpoint=J;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(w.metricsLive)).json();this.graphData=e.endpoints||[],this.loadError=!1,(!this.selectedEndpoint||this.selectedEndpoint===J)&&(this.selectedEndpoint=J);}catch{this.loadError=true;}}healthGradeForEndpoint(t){let e=Ks(t.summary.p95Ms,t.summary.medianMs,t.summary.totalRequests);return Ye(e,t.baselineP95Ms)}healthGradeForDuration(t,e){return Ye(t,e)}getCallers(t){let e=this.store.state.flows,r=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=r.get(i.label);c?(c.count++,c.totalMs+=n.durationMs):r.set(i.label,{count:1,totalMs:n.durationMs});}return [...r.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 r=t.path.split("?")[0].split("/").map(i=>{if(!i)return i;let n=i.length>0;for(let l=0;l<i.length;l++){let c=i.charCodeAt(l);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} ${r}`}async loadQueryBreakdown(t){if(this.queryBreakdownLoading)return;let r=this.store.state.requests.filter(i=>`${i.method} ${i.path}`===t||this.normalizeEndpoint(i)===t).slice(-20).map(i=>i.id).filter(Boolean);if(r.length===0){this.queryBreakdown=[];return}this.queryBreakdownLoading=true;try{let i=await fetch(`${w.activity}?requestIds=${r.join(",")}`);if(!i.ok){this.queryBreakdownLoading=!1;return}let n=await i.json(),l=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(),v=h.table||h.model||"",x=`${m} ${v}`.trim(),y=l.get(x);y?(y.totalMs+=h.durationMs,y.count++):l.set(x,{label:x,totalMs:h.durationMs,count:1});}this.queryBreakdown=[...l.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=Vs(t,e),t.style.cursor="pointer",t.onclick=r=>{let i=t.getBoundingClientRect(),n=r.clientX-i.left,l=r.clientY-i.top,c=null,d=1/0;for(let h of this.scatterDots){let m=Math.sqrt((h.x-n)**2+(h.y-l)**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 r=this.querySelector(`[data-req-idx="${t}"]`);r&&(r.classList.add("perf-hist-row-hl"),r.scrollIntoView({behavior:"smooth",block:"center"}));}updated(){if(this.selectedEndpoint===J)return;let t=this.querySelector("#perf-detail-canvas");if(t){let e=this.graphData.find(r=>r.endpoint===this.selectedEndpoint);e&&this.renderScatterChart(t,e.requests);}}render(){return !this.graphData||this.graphData.length===0?a`<bk-empty-state title="No performance data yet" subtitle="Hit some endpoints and data will appear here"></bk-empty-state>`:a`
|
|
533
533
|
<div id="graph-content">
|
|
534
534
|
${this.renderSelector()}
|
|
535
|
-
${this.selectedEndpoint===
|
|
535
|
+
${this.selectedEndpoint===J?this.renderOverview():this.renderDetail()}
|
|
536
536
|
</div>
|
|
537
537
|
`}renderSelector(){return a`
|
|
538
538
|
<div class="perf-selector">
|
|
539
|
-
<button class="perf-selector-btn ${this.selectedEndpoint===
|
|
540
|
-
@click=${()=>{this.selectedEndpoint=
|
|
541
|
-
${this.graphData.map((t,
|
|
539
|
+
<button class="perf-selector-btn ${this.selectedEndpoint===J?"active":""}"
|
|
540
|
+
@click=${()=>{this.selectedEndpoint=J;}}>Overview</button>
|
|
541
|
+
${this.graphData.map((t,e)=>a`
|
|
542
542
|
<button class="perf-selector-btn ${t.endpoint===this.selectedEndpoint?"active":""}"
|
|
543
543
|
@click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
544
|
-
<span class="perf-dot" style="background:${
|
|
544
|
+
<span class="perf-dot" style="background:${qe[e%qe.length]}"></span>${t.endpoint}
|
|
545
545
|
</button>
|
|
546
546
|
`)}
|
|
547
547
|
</div>
|
|
548
|
-
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return
|
|
548
|
+
`}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),r=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,l=t[0];return a`
|
|
549
549
|
<div class="perf-overview">
|
|
550
550
|
<div class="perf-summary-row">
|
|
551
551
|
<div class="perf-summary-card">
|
|
552
552
|
<span class="perf-summary-label">Total Requests</span>
|
|
553
|
-
<span class="perf-summary-value">${
|
|
553
|
+
<span class="perf-summary-value">${e}</span>
|
|
554
554
|
</div>
|
|
555
555
|
<div class="perf-summary-card">
|
|
556
556
|
<span class="perf-summary-label">Avg P95</span>
|
|
557
|
-
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(r).color}">${
|
|
557
|
+
<span class="perf-summary-value" style="color:${this.healthGradeForDuration(r).color}">${_(r)}</span>
|
|
558
558
|
</div>
|
|
559
559
|
<div class="perf-summary-card">
|
|
560
560
|
<span class="perf-summary-label">Error Rate</span>
|
|
561
|
-
<span class="perf-summary-value" style="color:${
|
|
561
|
+
<span class="perf-summary-value" style="color:${i>0?"var(--red)":"var(--green)"}">${Math.round(n*100)}%</span>
|
|
562
562
|
</div>
|
|
563
563
|
<div class="perf-summary-card">
|
|
564
564
|
<span class="perf-summary-label">Slowest</span>
|
|
@@ -582,45 +582,45 @@
|
|
|
582
582
|
</tbody>
|
|
583
583
|
</table>
|
|
584
584
|
</div>
|
|
585
|
-
`}renderHeatmapRow(t){let
|
|
585
|
+
`}renderHeatmapRow(t){let e=t.summary,r=this.healthGradeForEndpoint(t),i=Math.round(e.errorRate*e.totalRequests),n=(e.avgQueryTimeMs||0)+(e.avgFetchTimeMs||0)+(e.avgAppTimeMs||0),l=0,c=0,d=100;return n>0&&(l=Math.round((e.avgQueryTimeMs||0)/n*100),c=Math.round((e.avgFetchTimeMs||0)/n*100),d=Math.max(0,100-l-c)),a`
|
|
586
586
|
<tr class="perf-table-row" @click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
587
587
|
<td class="perf-td-name">${t.endpoint}</td>
|
|
588
|
-
<td class="perf-td-right">${
|
|
588
|
+
<td class="perf-td-right">${e.totalRequests}</td>
|
|
589
589
|
<td class="perf-td-center">
|
|
590
|
-
<span class="perf-hm-p95" style="color:${r.color};background:${r.bg};border-color:${r.border}">${
|
|
590
|
+
<span class="perf-hm-p95" style="color:${r.color};background:${r.bg};border-color:${r.border}">${_(e.p95Ms)}</span>
|
|
591
591
|
</td>
|
|
592
|
-
<td class="perf-td-center" style="color:${
|
|
593
|
-
<td class="perf-td-center" style="color:${
|
|
592
|
+
<td class="perf-td-center" style="color:${i>0?"var(--red)":"var(--text-muted)"}">${i>0?i:"-"}</td>
|
|
593
|
+
<td class="perf-td-center" style="color:${e.avgQueryCount>5?"var(--amber)":"var(--text-muted)"}">${e.avgQueryCount}</td>
|
|
594
594
|
<td>
|
|
595
595
|
<span class="perf-hm-split-bar">
|
|
596
|
-
${l>0?a`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${l}%"></span>`:
|
|
597
|
-
${c>0?a`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:
|
|
598
|
-
${
|
|
596
|
+
${l>0?a`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${l}%"></span>`:p}
|
|
597
|
+
${c>0?a`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:p}
|
|
598
|
+
${d>0?a`<span class="perf-breakdown-seg perf-breakdown-app" style="width:${d}%"></span>`:p}
|
|
599
599
|
</span>
|
|
600
600
|
</td>
|
|
601
601
|
</tr>
|
|
602
|
-
`}renderDetail(){let t=this.graphData.find(n=>n.endpoint===this.selectedEndpoint);if(!t?.requests?.length)return a`<bk-empty-state subtitle="No data for this endpoint"></bk-empty-state>`;let
|
|
602
|
+
`}renderDetail(){let t=this.graphData.find(n=>n.endpoint===this.selectedEndpoint);if(!t?.requests?.length)return a`<bk-empty-state subtitle="No data for this endpoint"></bk-empty-state>`;let e=t.summary,r=this.healthGradeForEndpoint(t),i=Math.round(e.errorRate*e.totalRequests);return a`
|
|
603
603
|
${this.renderDetailHeader(t,r)}
|
|
604
|
-
${this.renderDetailMetrics(
|
|
605
|
-
${this.renderDetailBreakdown(
|
|
604
|
+
${this.renderDetailMetrics(e,r,i)}
|
|
605
|
+
${this.renderDetailBreakdown(e)}
|
|
606
606
|
${this.renderCallers(t.endpoint)}
|
|
607
607
|
${this.renderQueryBreakdown()}
|
|
608
608
|
${this.renderTrends(t)}
|
|
609
609
|
${this.renderDetailChart()}
|
|
610
610
|
${this.renderDetailHistory(t)}
|
|
611
|
-
`}renderDetailHeader(t,
|
|
611
|
+
`}renderDetailHeader(t,e){return a`
|
|
612
612
|
<div class="perf-detail-header">
|
|
613
613
|
<div class="perf-detail-title">
|
|
614
|
-
<span class="perf-badge perf-badge-lg" style="color:${
|
|
614
|
+
<span class="perf-badge perf-badge-lg" style="color:${e.color};background:${e.bg};border-color:${e.border}">${e.label}</span>
|
|
615
615
|
<span>${t.endpoint}</span>
|
|
616
|
-
${t.baselineP95Ms?a`<span class="perf-baseline-hint">Baseline: ${
|
|
616
|
+
${t.baselineP95Ms?a`<span class="perf-baseline-hint">Baseline: ${_(t.baselineP95Ms)}</span>`:p}
|
|
617
617
|
</div>
|
|
618
618
|
</div>
|
|
619
|
-
`}renderDetailMetrics(t,
|
|
619
|
+
`}renderDetailMetrics(t,e,r){return a`
|
|
620
620
|
<div class="perf-metric-row">
|
|
621
621
|
<div class="perf-metric-card">
|
|
622
622
|
<span class="perf-metric-label">P95</span>
|
|
623
|
-
<span class="perf-metric-value" style="color:${
|
|
623
|
+
<span class="perf-metric-value" style="color:${e.color}">${_(t.p95Ms)}</span>
|
|
624
624
|
</div>
|
|
625
625
|
<div class="perf-metric-card">
|
|
626
626
|
<span class="perf-metric-label">Errors</span>
|
|
@@ -633,71 +633,71 @@
|
|
|
633
633
|
<span class="perf-metric-value" style="color:${t.avgQueryCount>5?"var(--amber)":"var(--text)"}">${t.avgQueryCount}</span>
|
|
634
634
|
</div>
|
|
635
635
|
</div>
|
|
636
|
-
`}renderDetailBreakdown(t){let
|
|
636
|
+
`}renderDetailBreakdown(t){let e=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(e<=0)return p;let r=Math.round((t.avgQueryTimeMs||0)/e*100),i=Math.round((t.avgFetchTimeMs||0)/e*100),n=Math.max(0,100-r-i);return a`
|
|
637
637
|
<div class="perf-breakdown">
|
|
638
638
|
<div class="perf-section-title">Time Breakdown</div>
|
|
639
639
|
<div class="perf-breakdown-bar">
|
|
640
|
-
${r>0?a`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${r}%"></div>`:
|
|
641
|
-
${
|
|
642
|
-
${n>0?a`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:
|
|
640
|
+
${r>0?a`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${r}%"></div>`:p}
|
|
641
|
+
${i>0?a`<div class="perf-breakdown-seg perf-breakdown-fetch" style="width:${i}%"></div>`:p}
|
|
642
|
+
${n>0?a`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:p}
|
|
643
643
|
</div>
|
|
644
644
|
<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 ${
|
|
645
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${_(t.avgQueryTimeMs||0)} (${r}%)</span>
|
|
646
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-fetch"></span>Fetch ${_(t.avgFetchTimeMs||0)} (${i}%)</span>
|
|
647
|
+
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${_(t.avgAppTimeMs||0)} (${n}%)</span>
|
|
648
648
|
</div>
|
|
649
649
|
</div>
|
|
650
|
-
`}renderCallers(t){let
|
|
650
|
+
`}renderCallers(t){let e=this.getCallers(t);return e.length===0?p:a`
|
|
651
651
|
<div class="perf-callers">
|
|
652
652
|
<div class="perf-section-title">Called By</div>
|
|
653
653
|
<div class="perf-callers-list">
|
|
654
|
-
${
|
|
654
|
+
${e.map(r=>a`
|
|
655
655
|
<div class="perf-caller-row">
|
|
656
656
|
<span class="perf-caller-name">${r.label}</span>
|
|
657
657
|
<span class="perf-caller-count">${r.count} call${r.count!==1?"s":""}</span>
|
|
658
|
-
<span class="perf-caller-avg">avg ${
|
|
658
|
+
<span class="perf-caller-avg">avg ${_(r.avgMs)}</span>
|
|
659
659
|
</div>
|
|
660
660
|
`)}
|
|
661
661
|
</div>
|
|
662
662
|
</div>
|
|
663
|
-
`}renderQueryBreakdown(){return this.queryBreakdownLoading?a`<div class="perf-queries"><div class="perf-section-title">DB Queries</div><div class="perf-queries-loading">Loading...</div></div>`:this.queryBreakdown.length===0?
|
|
663
|
+
`}renderQueryBreakdown(){return this.queryBreakdownLoading?a`<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:a`
|
|
664
664
|
<div class="perf-queries">
|
|
665
665
|
<div class="perf-section-title">DB Queries</div>
|
|
666
666
|
<div class="perf-queries-list">
|
|
667
667
|
${this.queryBreakdown.map(t=>a`
|
|
668
668
|
<div class="perf-query-row">
|
|
669
669
|
<span class="perf-query-label">${t.label}</span>
|
|
670
|
-
<span class="perf-query-avg">avg ${
|
|
670
|
+
<span class="perf-query-avg">avg ${_(t.avgMs)}</span>
|
|
671
671
|
<span class="perf-query-count">${t.count} call${t.count!==1?"s":""}</span>
|
|
672
672
|
</div>
|
|
673
673
|
`)}
|
|
674
674
|
</div>
|
|
675
675
|
</div>
|
|
676
|
-
`}renderTrends(t){let
|
|
676
|
+
`}renderTrends(t){let e=t.sessions;if(!e||e.length===0)return p;let r=e.slice(-10);return a`
|
|
677
677
|
<div class="perf-trends">
|
|
678
678
|
<div class="perf-section-title">Session Trend</div>
|
|
679
679
|
<div class="perf-trends-list">
|
|
680
|
-
${r.map((
|
|
680
|
+
${r.map((i,n)=>{let l=n>0?r[n-1].p95DurationMs:null,c=l!==null?i.p95DurationMs>l*1.2?"slower":i.p95DurationMs<l*.8?"faster":"":"",d=this.formatTimeAgo(i.startedAt),h=n===r.length-1,m=this.healthGradeForDuration(i.p95DurationMs,t.baselineP95Ms);return a`
|
|
681
681
|
<div class="perf-trend-row ${h?"perf-trend-current":""}">
|
|
682
|
-
<span class="perf-trend-time">${h?"Current":
|
|
682
|
+
<span class="perf-trend-time">${h?"Current":d}</span>
|
|
683
683
|
<span class="perf-trend-p95">
|
|
684
684
|
<span class="perf-hm-p95" style="color:${m.color};background:${m.bg};border-color:${m.border}">
|
|
685
|
-
p95: ${
|
|
685
|
+
p95: ${_(i.p95DurationMs)}
|
|
686
686
|
</span>
|
|
687
687
|
</span>
|
|
688
|
-
<span class="perf-trend-reqs">${
|
|
689
|
-
<span class="perf-trend-errs" style="color:${
|
|
690
|
-
${c?a`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:
|
|
688
|
+
<span class="perf-trend-reqs">${i.requestCount} req${i.requestCount!==1?"s":""}</span>
|
|
689
|
+
<span class="perf-trend-errs" style="color:${i.errorCount>0?"var(--red)":"var(--text-dim)"}">${i.errorCount} err${i.errorCount!==1?"s":""}</span>
|
|
690
|
+
${c?a`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:p}
|
|
691
691
|
</div>
|
|
692
692
|
`})}
|
|
693
693
|
</div>
|
|
694
694
|
</div>
|
|
695
|
-
`}formatTimeAgo(t){let
|
|
695
|
+
`}formatTimeAgo(t){let e=Date.now()-t,r=Math.round(e/6e4);if(r<1)return "just now";if(r<60)return `${r}m ago`;let i=Math.round(r/60);return i<24?`${i}h ago`:`${Math.round(i/24)}d ago`}renderDetailChart(){return a`
|
|
696
696
|
<div class="perf-chart-wrap">
|
|
697
697
|
<div class="perf-section-title">Response Time</div>
|
|
698
698
|
<canvas id="perf-detail-canvas" class="perf-canvas" style="width:100%;height:240px"></canvas>
|
|
699
699
|
</div>
|
|
700
|
-
`}renderDetailHistory(t){if(t.requests.length===0)return
|
|
700
|
+
`}renderDetailHistory(t){if(t.requests.length===0)return p;let e=[];for(let r=t.requests.length-1;r>=0&&e.length<50;r--)e.push({point:t.requests[r],originalIndex:r});return a`
|
|
701
701
|
<div class="perf-history-wrap">
|
|
702
702
|
<table class="perf-table">
|
|
703
703
|
<thead>
|
|
@@ -711,63 +711,363 @@
|
|
|
711
711
|
</tr>
|
|
712
712
|
</thead>
|
|
713
713
|
<tbody>
|
|
714
|
-
${
|
|
714
|
+
${e.map(r=>this.renderHistoryRow(r.point,r.originalIndex,t.baselineP95Ms))}
|
|
715
715
|
</tbody>
|
|
716
716
|
</table>
|
|
717
717
|
</div>
|
|
718
|
-
`}renderHistoryRow(t,
|
|
719
|
-
<tr class="perf-table-row ${l?"perf-row-err":""}" data-req-idx=${
|
|
718
|
+
`}renderHistoryRow(t,e,r){let i=this.healthGradeForDuration(t.durationMs,r),n=new Date(t.timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),l=t.statusCode>=400,c=t.queryTimeMs||0,d=t.fetchTimeMs||0,h=Math.max(0,t.durationMs-c-d);return a`
|
|
719
|
+
<tr class="perf-table-row ${l?"perf-row-err":""}" data-req-idx=${e}>
|
|
720
720
|
<td class="perf-td-muted">${n}</td>
|
|
721
721
|
<td>
|
|
722
|
-
<span class="perf-badge perf-badge-sm" style="color:${
|
|
722
|
+
<span class="perf-badge perf-badge-sm" style="color:${i.color};background:${i.bg};border-color:${i.border}">${i.label}</span>
|
|
723
723
|
</td>
|
|
724
|
-
<td>${
|
|
724
|
+
<td>${_(t.durationMs)}</td>
|
|
725
725
|
<td>
|
|
726
|
-
${c>0?a`<span class="perf-bd-tag perf-bd-tag-db">DB ${
|
|
727
|
-
${
|
|
728
|
-
<span class="perf-bd-tag perf-bd-tag-app">App ${
|
|
726
|
+
${c>0?a`<span class="perf-bd-tag perf-bd-tag-db">DB ${_(c)}</span>`:p}
|
|
727
|
+
${d>0?a`<span class="perf-bd-tag perf-bd-tag-fetch">Fetch ${_(d)}</span>`:p}
|
|
728
|
+
<span class="perf-bd-tag perf-bd-tag-app">App ${_(h)}</span>
|
|
729
729
|
</td>
|
|
730
730
|
<td class="perf-td-center" style="color:${l?"var(--red)":"var(--text-muted)"}">${t.statusCode}</td>
|
|
731
731
|
<td class="perf-td-right perf-td-muted">${t.queryCount}</td>
|
|
732
732
|
</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`
|
|
733
|
+
`}};u([I({context:A})],Q.prototype,"store",2),u([b()],Q.prototype,"selectedEndpoint",2),u([b()],Q.prototype,"graphData",2),u([b()],Q.prototype,"loadError",2),u([b()],Q.prototype,"queryBreakdown",2),u([b()],Q.prototype,"queryBreakdownLoading",2),Q=u([S("bk-performance-view")],Q);var Xe={auth:{label:"Auth",color:"#059669",icon:"\u{1F6E1}",tooltip:"Highlight which endpoints require authentication and which are unprotected"},security:{label:"Security",color:"#dc2626",icon:"\u26A0",tooltip:"Show security findings like exposed secrets, token leaks, and PII exposure"},performance:{label:"Perf",color:"#2563eb",icon:"\u26A1",tooltip:"Color endpoints by P95 latency \u2014 green (fast) to red (slow)"},issues:{label:"Issues",color:"#d97706",icon:"\u25CF",tooltip:"Badge endpoints with open issues like N+1 queries or redundant calls"},heat:{label:"Heat",color:"#ef4444",icon:"\u{1F525}",tooltip:"Color nodes and edges by traffic volume \u2014 blue (low) to red (hot)"}},At={action:{fill:"#faf5ff",stroke:"#a855f7",icon:"\u25B6",columnHeader:"ACTIONS"},endpoint:{fill:"#f8fafc",stroke:"#6366f1",icon:"\u26A1",columnHeader:"ENDPOINTS"},table:{fill:"#f0fdf4",stroke:"#16a34a",icon:"\u229E",columnHeader:"TABLES"},external:{fill:"#fffbeb",stroke:"#d97706",icon:"\u25C6",columnHeader:"EXTERNAL"}},fe={triggers:"#a855f7",reads:"#6366f1",writes:"#ef4444",fetches:"#f59e0b",calls:"#22c55e"},yi=100,_i=300,$i=800;function It(o){return o<yi?"#22c55e":o<_i?"#3b82f6":o<$i?"#eab308":"#ef4444"}var xi=.25,Si=.5,Ti=.75;function Ve(o){return o<xi?"#3b82f6":o<Si?"#22c55e":o<Ti?"#eab308":"#ef4444"}var zs="#4338ca",Js="#e0e7ff",Zs="#818cf8",tr="#eef2ff",Ke="#7c3aed",er="#6366f1",ve="#f97316",sr="#ecfdf5",ze="#059669",rr="#fef2f2",Je="#dc2626",ir="#fffbeb",Ze="#d97706",ct="#ef4444",or=.2,nr=3,ts=1.2,Kt=40;var ar=800,lr=500;function Ci(o){return Math.max(140,o.length*7.2+36)}function es(o,s){return Math.round(36+o.stats.requestCount/s*28)}function Li(o){return o.length>0?Math.max(140,...o.map(s=>Ci(s.label))):0}function dr(o,s,t){let e=[];for(let r of s){let i=r.source===o?r.target:r.target===o?r.source:null;if(i){let n=t.get(i);n&&e.push(n.y+n.h/2);}}return e.length>0?e.reduce((r,i)=>r+i,0)/e.length:1/0}function hr(o,s){let t=f=>o.filter(g=>g.type===f).sort((g,T)=>T.stats.requestCount-g.stats.requestCount),e=t("action"),r=t("endpoint"),i=t("table"),n=t("external"),l=Math.max(1,...o.map(f=>f.stats.requestCount)),c=new Map,d=[],h=[],m=50,v=[{type:"action",items:e},{type:"endpoint",items:r},{type:"table",items:i},{type:"external",items:n}];for(let f of v)if(f.items.length>0){let g=Li(f.items);h.push({type:f.type,items:f.items,width:g,x:m}),m+=g+160;}let x=h[0];if(x){let f=74;for(let g of x.items){let T=es(g,l),R={id:g.id,x:x.x,y:f,w:x.width,h:T,label:g.label,type:g.type,stats:g.stats,annotations:g.annotations};d.push(R),c.set(g.id,R),f+=T+10;}}for(let f=1;f<h.length;f++){let g=h[f],T=[...g.items].sort((C,q)=>dr(C.id,s,c)-dr(q.id,s,c)),R=d.filter(C=>C.x===h[f-1].x),H=Math.min(...R.map(C=>C.y)),B=Math.max(...R.map(C=>C.y+C.h))-H,V=T.reduce((C,q)=>C+es(q,l)+10,-10),G=H+Math.max(0,(B-V)/2);for(let C of T){let q=es(C,l),rs={id:C.id,x:g.x,y:G,w:g.width,h:q,label:C.label,type:C.type,stats:C.stats,annotations:C.annotations};d.push(rs),c.set(C.id,rs),G+=q+10;}}let y=[];for(let f of s){let g=c.get(f.source),T=c.get(f.target);if(!g||!T)continue;let R=g.x<T.x,H=R?g.x+g.w:g.x,X=g.y+g.h/2,B=R?T.x:T.x+T.w,V=T.y+T.h/2,G=[];f.stats.frequency>1&&G.push(`${f.stats.frequency}\xD7`),G.push(f.type),f.stats.avgLatencyMs>0&&G.push(`${f.stats.avgLatencyMs}ms`),y.push({key:f.id,sx:H,sy:X,tx:B,ty:V,label:G.join(" \xB7 "),color:fe[f.type]||"#94a3b8",thickness:Math.min(.75+Math.log2(f.stats.frequency+1)*.35,2.5),dashed:f.type==="reads"||f.type==="writes",data:f});}let k=d.reduce((f,g)=>Math.max(f,g.x+g.w),0),$=d.reduce((f,g)=>Math.max(f,g.y+g.h),0);return {nodes:d,edges:y,width:k+100,height:Math.max($+50,250)}}function ur(o){let s=o.charCodeAt(0);return s>=48&&s<=57||s>=65&&s<=70||s>=97&&s<=102}function Mi(o){if(o.length!==36)return false;for(let s=0;s<o.length;s++)if(s===8||s===13||s===18||s===23){if(o[s]!=="-")return false}else if(!ur(o[s]))return false;return true}function Ni(o){if(!o.length)return false;for(let s=0;s<o.length;s++){let t=o.charCodeAt(s);if(t<48||t>57)return false}return true}function Oi(o){if(o.length<12)return false;for(let s=0;s<o.length;s++)if(!ur(o[s]))return false;return true}function ki(o){if(o.length<8)return false;let s=false,t=false;for(let e=0;e<o.length;e++){let r=o.charCodeAt(e);if(r>=65&&r<=90||r>=97&&r<=122)s=true;else if(r>=48&&r<=57)t=true;else if(r!==95&&r!==45)return false}return s&&t}var Di=":id";function Hi(o){return Mi(o)||Ni(o)||Oi(o)||ki(o)?Di:o}function ss(o,s){let t=s.split("?")[0];return `${o} ${t.split("/").map(e=>e&&Hi(e)).join("/")}`}function mr(o,s,t){let e=[...o],r=[...s],i=new Set(e.map(l=>l.id)),n=new Map;for(let l of t){let c=l.label||"Unknown",d=n.get(c);d||(d={endpointKeys:new Set,count:0,totalMs:0},n.set(c,d)),d.count++,d.totalMs+=l.totalDurationMs;for(let h of l.requests)h.path?.startsWith(F)||d.endpointKeys.add(ss(h.method,h.path));}for(let[l,c]of n){let d=`action:${l}`;i.has(d)||(e.push({id:d,type:"action",label:l,stats:{requestCount:c.count,avgLatencyMs:c.count>0?Math.round(c.totalMs/c.count):0,errorRate:0,avgQueryCount:0}}),i.add(d));for(let h of c.endpointKeys){let m=`endpoint:${h}`;if(i.has(m)){let v=`${d} -> ${m}`;r.find(x=>x.id===v)||r.push({id:v,source:d,target:m,type:"triggers",stats:{frequency:c.count,avgLatencyMs:0}});}}}return {nodes:e,edges:r}}function fr(o){let s=new Map;for(let t of o){let e=t.label||"Unknown",r=s.get(e);r||(r={keys:new Set,count:0,totalMs:0},s.set(e,r)),r.count++,r.totalMs+=t.totalDurationMs;for(let i of t.requests)i.path?.startsWith(F)||r.keys.add(ss(i.method,i.path));}return [...s.entries()].map(([t,e])=>({label:t,occurrences:e.count,endpointKeys:e.keys,avgDurationMs:e.count>0?Math.round(e.totalMs/e.count):0}))}function be(o,s){let t=`event=${encodeURIComponent(o)}${s?`&detail=${encodeURIComponent(s)}`:""}`;fetch(`${w.tab}?${t}`).catch(()=>{});}var M=class extends E{constructor(){super(...arguments);this.graphNodes=[];this.graphEdges=[];this.locked=null;this.hovered=null;this.loading=true;this.activeLayers=new Set;this.searchQuery="";this.viewTransform={x:0,y:0,scale:1};this.isPanning=false;this.panStart={x:0,y:0,vtx:0,vty:0};this.dragging=null;this.wasDragging=false;this.nodePositionOverrides=new Map;this.detailTab="overview";this.consolidatedFlows=[];this.activeFlowIdx=-1;this.focusIdx=-1;this.refreshTimer=null;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadData(),this.refreshTimer=setInterval(()=>this.loadData(),4e3);}disconnectedCallback(){super.disconnectedCallback(),this.refreshTimer&&clearInterval(this.refreshTimer);}async loadData(){try{let[t,e]=await Promise.all([fetch(`${w.graph}?level=endpoints`),fetch(w.flows)]),r=await t.json(),i=await e.json(),n=mr(r.nodes||[],r.edges||[],i.flows||[]);this.graphNodes=n.nodes,this.graphEdges=n.edges,this.consolidatedFlows=fr(i.flows||[]),this.nodePositionOverrides.size===0&&this.graphNodes.length>0&&this.tryLoadPersistedPositions(),this.loading=!1;}catch{this.loading=false;}}getPositionStorageKey(){let t=this.graphNodes.map(r=>r.id).sort().join(","),e=0;for(let r=0;r<t.length;r++)e=(e<<5)-e+t.charCodeAt(r)|0;return `bk-graph-pos-${e}`}persistPositions(){if(this.nodePositionOverrides.size!==0)try{let t=Object.fromEntries(this.nodePositionOverrides);localStorage.setItem(this.getPositionStorageKey(),JSON.stringify(t));}catch{}}tryLoadPersistedPositions(){try{let t=localStorage.getItem(this.getPositionStorageKey());if(t){let e=JSON.parse(t);this.nodePositionOverrides=new Map(Object.entries(e));}}catch{}}get activeNodeId(){return this.locked??this.hovered}getHighlightedNodeIds(){let t=this.activeNodeId;if(!t)return null;let e=new Set([t]),r=true;for(;r;){r=false;for(let i of this.graphEdges)e.has(i.source)&&!e.has(i.target)&&(e.add(i.target),r=true);}return e}getFlowTraceNodeIds(){if(this.activeFlowIdx<0||this.activeFlowIdx>=this.consolidatedFlows.length)return null;let t=this.consolidatedFlows[this.activeFlowIdx],e=new Set;e.add(`action:${t.label}`);for(let r of t.endpointKeys)e.add(`endpoint:${r}`);for(let r of this.graphEdges)e.has(r.source)&&e.add(r.target);return e}getFlowTraceEdgeIds(){let t=this.getFlowTraceNodeIds();if(!t)return null;let e=new Set;for(let r of this.graphEdges)t.has(r.source)&&t.has(r.target)&&e.add(r.id);return e}matchesSearch(t){return this.searchQuery?t.toLowerCase().includes(this.searchQuery.toLowerCase()):true}handlePanStart(t){t.button===0&&(t.target.closest(".graph-g")||(this.isPanning=true,this.panStart={x:t.clientX,y:t.clientY,vtx:this.viewTransform.x,vty:this.viewTransform.y}));}handlePanMove(t){if(this.dragging){let e=t.currentTarget.getBoundingClientRect(),r=(t.clientX-e.left-this.viewTransform.x)/this.viewTransform.scale,i=(t.clientY-e.top-this.viewTransform.y)/this.viewTransform.scale;this.nodePositionOverrides.set(this.dragging.nodeId,{x:r-this.dragging.offsetX,y:i-this.dragging.offsetY}),this.requestUpdate();return}this.isPanning&&(this.viewTransform={...this.viewTransform,x:this.panStart.vtx+(t.clientX-this.panStart.x),y:this.panStart.vty+(t.clientY-this.panStart.y)});}handlePanEnd(){this.dragging&&(this.persistPositions(),this.dragging=null,this.wasDragging=true,requestAnimationFrame(()=>{this.wasDragging=false;})),this.isPanning=false;}resetView(){this.viewTransform={x:0,y:0,scale:1};}zoomIn(){this.viewTransform={...this.viewTransform,scale:Math.min(nr,this.viewTransform.scale*ts)};}zoomOut(){this.viewTransform={...this.viewTransform,scale:Math.max(or,this.viewTransform.scale/ts)};}resetLayout(){this.nodePositionOverrides.clear();try{localStorage.removeItem(this.getPositionStorageKey());}catch{}this.viewTransform={x:0,y:0,scale:1},this.requestUpdate(),be("layout_reset");}startNodeDrag(t,e,r){t.stopPropagation();let i=t.currentTarget.closest("svg").getBoundingClientRect(),n=(t.clientX-i.left-this.viewTransform.x)/this.viewTransform.scale,l=(t.clientY-i.top-this.viewTransform.y)/this.viewTransform.scale;this.dragging={nodeId:e,offsetX:n-r.x,offsetY:l-r.y};}handleKeyDown(t){let e=this.graphNodes;if(t.key==="/"){t.preventDefault(),this.querySelector(".graph-search-input")?.focus();return}if(t.key==="Escape"){this.locked=null,this.searchQuery="",this.focusIdx=-1,this.activeFlowIdx=-1;return}if(t.key==="Tab"){t.preventDefault(),this.focusIdx=t.shiftKey?this.focusIdx<=0?e.length-1:this.focusIdx-1:(this.focusIdx+1)%e.length,this.hovered=e[this.focusIdx]?.id??null;return}if(t.key==="Enter"&&this.focusIdx>=0){let r=e[this.focusIdx];r&&(this.locked=this.locked===r.id?null:r.id);return}if(t.key==="+"||t.key==="="){this.zoomIn();return}if(t.key==="-"){this.zoomOut();return}if(t.key==="ArrowUp"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y+Kt};return}if(t.key==="ArrowDown"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y-Kt};return}if(t.key==="ArrowLeft"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x+Kt};return}if(t.key==="ArrowRight"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x-Kt};return}}render(){if(this.loading&&this.graphNodes.length===0)return a`<div class="graph-loading">Loading graph…</div>`;if(this.graphNodes.length===0)return a`
|
|
734
|
+
<div class="graph-empty">
|
|
735
|
+
<div class="graph-empty-icon">◎</div>
|
|
736
|
+
<div class="graph-empty-title">No data yet</div>
|
|
737
|
+
<div class="graph-empty-desc">Navigate your app to build the dependency graph.</div>
|
|
738
|
+
</div>
|
|
739
|
+
`;let t=hr(this.graphNodes,this.graphEdges);this.applyPositionOverrides(t);let e=this.getSelectedNodeDetail(),r=this.getHighlightedNodeIds(),i=this.getFlowTraceNodeIds(),n=this.getFlowTraceEdgeIds(),l=this.deduplicateColumnHeaders(t.nodes),c=Math.max(1,...this.graphNodes.map(h=>h.stats.requestCount)),d=this.viewTransform;return a`
|
|
740
|
+
<div class="graph-wrapper" tabindex="0" @keydown=${this.handleKeyDown}
|
|
741
|
+
@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);}}>
|
|
742
|
+
${this.renderToolbar()}
|
|
743
|
+
<div class="graph-body">
|
|
744
|
+
<div class="graph-canvas" style="position:relative">
|
|
745
|
+
<svg width="100%" height="100%"
|
|
746
|
+
viewBox="0 0 ${Math.max(t.width,ar)} ${Math.max(t.height,lr)}"
|
|
747
|
+
class="graph-svg"
|
|
748
|
+
style="cursor:${this.isPanning?"grabbing":"grab"}"
|
|
749
|
+
@mousedown=${this.handlePanStart}
|
|
750
|
+
@mousemove=${this.handlePanMove}
|
|
751
|
+
@mouseup=${this.handlePanEnd}
|
|
752
|
+
@mouseleave=${this.handlePanEnd}>
|
|
753
|
+
|
|
754
|
+
<g transform="translate(${d.x},${d.y}) scale(${d.scale})">
|
|
755
|
+
${O`${l.map(h=>O`
|
|
756
|
+
<text x="${h.x}" y="${58}" class="graph-col-header">${h.label}</text>
|
|
757
|
+
`)}`}
|
|
758
|
+
|
|
759
|
+
${t.edges.map(h=>this.renderEdge(h,r,n,c))}
|
|
760
|
+
${t.nodes.map(h=>this.renderNode(h,r,i,c))}
|
|
761
|
+
</g>
|
|
762
|
+
</svg>
|
|
763
|
+
${this.renderFloatingControls()}
|
|
764
|
+
</div>
|
|
765
|
+
${e?this.renderDetailPanel(e):p}
|
|
766
|
+
</div>
|
|
767
|
+
</div>
|
|
768
|
+
`}applyPositionOverrides(t){for(let r of t.nodes){let i=this.nodePositionOverrides.get(r.id);i&&(r.x=i.x,r.y=i.y);}let e=new Map(t.nodes.map(r=>[r.id,r]));for(let r of t.edges){let i=e.get(r.data.source),n=e.get(r.data.target);if(i&&n){let l=i.x<n.x;r.sx=l?i.x+i.w:i.x,r.sy=i.y+i.h/2,r.tx=l?n.x:n.x+n.w,r.ty=n.y+n.h/2;}}}deduplicateColumnHeaders(t){let e=[],r=new Set;for(let i of t)r.has(i.type)||(r.add(i.type),e.push({x:i.x,label:At[i.type]?.columnHeader||i.type.toUpperCase()}));return e}renderToolbar(){return a`
|
|
769
|
+
<div class="graph-toolbar">
|
|
770
|
+
<div class="graph-layer-toggles">
|
|
771
|
+
${Object.keys(Xe).map(t=>{let e=Xe[t],r=this.activeLayers.has(t);return a`
|
|
772
|
+
<button class="graph-layer-btn ${r?"active":""}"
|
|
773
|
+
style="${r?`border-color:${e.color};color:${e.color}`:""}"
|
|
774
|
+
@click=${()=>this.toggleLayer(t)}
|
|
775
|
+
title="${e.tooltip}">
|
|
776
|
+
${e.icon} ${e.label}
|
|
777
|
+
</button>
|
|
778
|
+
`})}
|
|
779
|
+
</div>
|
|
780
|
+
|
|
781
|
+
<div class="graph-search">
|
|
782
|
+
<span class="graph-search-icon">⌕</span>
|
|
783
|
+
<input class="graph-search-input" type="text" placeholder="Search nodes… ( / )"
|
|
784
|
+
.value=${this.searchQuery}
|
|
785
|
+
@input=${t=>{this.searchQuery=t.target.value;}}>
|
|
786
|
+
${this.searchQuery?a`
|
|
787
|
+
<button class="graph-search-clear" @click=${()=>{this.searchQuery="";}}>✕</button>
|
|
788
|
+
`:p}
|
|
789
|
+
</div>
|
|
790
|
+
|
|
791
|
+
${this.consolidatedFlows.length>0?a`
|
|
792
|
+
<select class="graph-flow-picker" @change=${t=>{this.activeFlowIdx=parseInt(t.target.value,10),this.activeFlowIdx>=0&&be("flow_traced");}}>
|
|
793
|
+
<option value="-1">Trace flow…</option>
|
|
794
|
+
${this.consolidatedFlows.map((t,e)=>a`
|
|
795
|
+
<option value="${e}" ?selected=${this.activeFlowIdx===e}>${t.label} → ${t.endpointKeys.size} ep · ${t.occurrences}×</option>
|
|
796
|
+
`)}
|
|
797
|
+
</select>
|
|
798
|
+
`:p}
|
|
799
|
+
|
|
800
|
+
${this.activeLayers.has("auth")?a`
|
|
801
|
+
<div class="graph-auth-legend">
|
|
802
|
+
<span class="graph-auth-legend-item"><span style="color:${ze}">🛡</span> protected</span>
|
|
803
|
+
<span class="graph-auth-legend-item"><span style="color:${ve};font-weight:700">!</span> no auth</span>
|
|
804
|
+
</div>
|
|
805
|
+
`:p}
|
|
806
|
+
</div>
|
|
807
|
+
`}renderFloatingControls(){let t=this.nodePositionOverrides.size>0,e=Math.round(this.viewTransform.scale*100);return a`
|
|
808
|
+
<div class="graph-float">
|
|
809
|
+
<button class="graph-float-btn" @click=${this.zoomOut} title="Zoom out (-)">−</button>
|
|
810
|
+
<span class="graph-float-zoom">${e}%</span>
|
|
811
|
+
<button class="graph-float-btn" @click=${this.zoomIn} title="Zoom in (+)">+</button>
|
|
812
|
+
<span class="graph-float-sep"></span>
|
|
813
|
+
<button class="graph-float-btn" @click=${this.resetView} title="Reset pan & zoom">⊙</button>
|
|
814
|
+
${t?a`
|
|
815
|
+
<span class="graph-float-sep"></span>
|
|
816
|
+
<button class="graph-float-btn graph-float-btn-accent" @click=${()=>this.resetLayout()} title="Reset layout to auto-arrange">
|
|
817
|
+
⟲ Reformat
|
|
818
|
+
</button>
|
|
819
|
+
`:p}
|
|
820
|
+
<span class="graph-float-sep"></span>
|
|
821
|
+
<button class="graph-float-btn" @click=${()=>this.captureScreenshot()} title="Save graph as PNG">
|
|
822
|
+
📷
|
|
823
|
+
</button>
|
|
824
|
+
</div>
|
|
825
|
+
`}async captureScreenshot(){be("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 r=document.createElement("style");r.textContent=`
|
|
826
|
+
text { font-family: 'Inter', system-ui, -apple-system, sans-serif; }
|
|
827
|
+
.graph-col-header { fill: #c4c4cc; font-size: 9px; font-weight: 600; letter-spacing: 1.5px; }
|
|
828
|
+
.graph-flow-edge { stroke-dasharray: 6,4; }
|
|
829
|
+
.graph-pulse { }
|
|
830
|
+
`,e.insertBefore(r,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 l=e.getAttribute("viewBox")||"0 0 800 500",[,,c,d]=l.split(" ").map(Number),h=2,m=c*h,v=d*h,x=new XMLSerializer().serializeToString(e),y=new Blob([x],{type:"image/svg+xml;charset=utf-8"}),k=URL.createObjectURL(y),$=new Image;$.onload=()=>{let f=document.createElement("canvas");f.width=m,f.height=v;let g=f.getContext("2d");g.fillStyle="#ffffff",g.fillRect(0,0,m,v),g.drawImage($,0,0,m,v),URL.revokeObjectURL(k),f.toBlob(T=>{if(!T)return;let R=document.createElement("a");R.href=URL.createObjectURL(T),R.download=`brakit-graph-${new Date().toISOString().slice(0,19).replace(/[T:]/g,"-")}.png`,document.body.appendChild(R),R.click(),document.body.removeChild(R),URL.revokeObjectURL(R.href);},"image/png");},$.src=k;}toggleLayer(t){let e=new Set(this.activeLayers);e.has(t)?e.delete(t):(e.add(t),be("layer_toggled",t)),this.activeLayers=e;}renderNode(t,e,r,i){let n=this.activeNodeId,l=e===null||e.has(t.id),c=n===t.id,d=this.locked===t.id,h=r?.has(t.id)??false,m=this.matchesSearch(t.label),v=At[t.type]||At.endpoint,x=d?zs:c?Zs:v.stroke,y=d?Js:c?tr:v.fill,k=d?2:c?1.5:.75,$=l?1:.08;this.searchQuery&&!m&&($=.05),r&&!h&&($=Math.min($,.08)),h&&($=1,x=Ke);let f=this.searchQuery&&m,g=t.annotations,T=this.activeLayers;T.has("performance")&&g?.p95Ms!==void 0&&t.type==="endpoint"&&(y=It(g.p95Ms)+"18"),T.has("heat")&&(y=Ve(t.stats.requestCount/i)+"20");let R=T.has("auth")&&t.type==="endpoint"&&!g?.hasAuth,H=T.has("auth")&&g?.hasAuth,X=T.has("security")?g?.securityFindings?.length??0:0,B=g?.securityFindings?.some(q=>q.severity==="critical"),V=T.has("issues")?g?.openIssueCount??0:0,G=this.nodeSubtitle(t),C=T.has("performance")&&g?.p95Ms!==void 0&&t.type==="endpoint";return O`
|
|
831
|
+
<g class="graph-g" transform="translate(${t.x},${t.y})" style="opacity:${$};cursor:pointer;transition:opacity .15s,transform .1s"
|
|
832
|
+
@click=${q=>{q.stopPropagation(),!this.wasDragging&&(this.locked=this.locked===t.id?null:t.id,this.detailTab="overview");}}
|
|
833
|
+
@mouseenter=${()=>{this.hovered=t.id;}}
|
|
834
|
+
@mouseleave=${()=>{this.hovered=null;}}
|
|
835
|
+
@mousedown=${q=>{q.detail>=2||this.startNodeDrag(q,t.id,t);}}>
|
|
836
|
+
|
|
837
|
+
${f?O`
|
|
838
|
+
<rect x="-3" y="-3" width="${t.w+6}" height="${t.h+6}" rx="9" fill="none"
|
|
839
|
+
stroke="${er}" stroke-width="2" stroke-dasharray="4,2"/>
|
|
840
|
+
`:p}
|
|
841
|
+
|
|
842
|
+
${R?O`
|
|
843
|
+
<rect width="${t.w}" height="${t.h}" rx="8" fill="${y}" stroke="${ve}"
|
|
844
|
+
stroke-width="1.2" stroke-dasharray="5,3"/>
|
|
845
|
+
`:O`
|
|
846
|
+
<rect width="${t.w}" height="${t.h}" rx="8" fill="${y}" stroke="${x}" stroke-width="${k}"/>
|
|
847
|
+
`}
|
|
848
|
+
|
|
849
|
+
<text x="12" y="${t.h/2-4}" fill="#1e293b" font-size="11.5" font-weight="600"
|
|
850
|
+
font-family="'Inter',system-ui,sans-serif">${v.icon} ${t.label}</text>
|
|
851
|
+
<text x="12" y="${t.h/2+10}" fill="#a1a1aa" font-size="9"
|
|
852
|
+
font-family="ui-monospace,monospace">${G}</text>
|
|
853
|
+
|
|
854
|
+
${C?O`
|
|
855
|
+
<text x="${t.w-8}" y="${t.h-6}" fill="${It(g.p95Ms)}" font-size="9"
|
|
856
|
+
font-family="ui-monospace,monospace" text-anchor="end">p95: ${g.p95Ms}ms</text>
|
|
857
|
+
`:p}
|
|
858
|
+
|
|
859
|
+
${H?O`
|
|
860
|
+
<g transform="translate(${t.w-24},3)">
|
|
861
|
+
<title>Auth protected</title>
|
|
862
|
+
<rect width="18" height="18" rx="3" fill="${sr}" stroke="${ze}" stroke-width="0.5"/>
|
|
863
|
+
<text x="9" y="13" text-anchor="middle" font-size="10">🛡</text>
|
|
864
|
+
</g>
|
|
865
|
+
`:p}
|
|
866
|
+
|
|
867
|
+
${R?O`
|
|
868
|
+
<g transform="translate(${t.w-24},3)">
|
|
869
|
+
<title>No auth detected — this endpoint may be unprotected</title>
|
|
870
|
+
<rect width="18" height="18" rx="3" fill="#fff7ed" stroke="${ve}" stroke-width="0.5"/>
|
|
871
|
+
<text x="9" y="13" text-anchor="middle" font-size="10" fill="#ea580c">!</text>
|
|
872
|
+
</g>
|
|
873
|
+
`:p}
|
|
874
|
+
|
|
875
|
+
${X>0?O`
|
|
876
|
+
<g transform="translate(${t.w-(H||R?46:24)},3)">
|
|
877
|
+
<rect width="18" height="18" rx="3" fill="${B?rr:ir}"
|
|
878
|
+
stroke="${B?Je:Ze}" stroke-width="0.5"
|
|
879
|
+
class="${B?"graph-pulse":""}"/>
|
|
880
|
+
<text x="9" y="13" text-anchor="middle" font-size="9" font-weight="600"
|
|
881
|
+
fill="${B?Je:Ze}">${X}</text>
|
|
882
|
+
</g>
|
|
883
|
+
`:p}
|
|
884
|
+
|
|
885
|
+
${V>0?O`
|
|
886
|
+
<circle cx="${t.w-8}" cy="8" r="5" fill="${ct}" stroke="white" stroke-width="1"/>
|
|
887
|
+
<text x="${t.w-8}" y="11" text-anchor="middle" font-size="7" fill="white" font-weight="700">${V}</text>
|
|
888
|
+
`:t.stats.errorRate>.05?O`<circle cx="${t.w-12}" cy="12" r="4" fill="${ct}"/>`:p}
|
|
889
|
+
|
|
890
|
+
${g?.isMiddleware&&T.has("auth")?O`
|
|
891
|
+
<text x="${t.w}" y="${t.h+12}" text-anchor="end" font-size="8" fill="#6b7280"
|
|
892
|
+
font-family="ui-monospace,monospace">middleware</text>
|
|
893
|
+
`:p}
|
|
894
|
+
</g>
|
|
895
|
+
`}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,r,i){let n=this.activeNodeId,l=e===null||e.has(t.data.source)&&e.has(t.data.target),c=r?.has(t.key)??false,d=l?n===null?.25:.6:.04,h=l&&n!==null,m=t.color,v=t.thickness;if(r&&!c&&(d=.04),c&&(d=.85,m=Ke,v=Math.max(v,1.8)),this.activeLayers.has("heat")&&!c){let V=Math.max(1,...this.graphEdges.map(C=>C.stats.frequency)),G=t.data.stats.frequency/V;m=Ve(G),l&&(d=Math.max(d,.4));}let x=this.activeLayers.has("issues")&&t.data.annotations?.hasIssue,y=Math.abs(t.tx-t.sx),k=Math.min(y*.45,120),$=t.sx<t.tx,f=$?t.sx+k:t.sx-k,g=$?t.tx-k:t.tx+k,T=(t.sx+t.tx)/2,R=(t.sy+t.ty)/2,H=4,X=x?ct:m,B=x?Math.max(v,1.5):v;return O`
|
|
896
|
+
<g style="transition:opacity .15s">
|
|
897
|
+
<path d="M${t.sx},${t.sy} C${f},${t.sy} ${g},${t.ty} ${t.tx},${t.ty}"
|
|
898
|
+
fill="none" stroke="${X}" stroke-width="${B}"
|
|
899
|
+
stroke-opacity="${d}" stroke-linecap="round"
|
|
900
|
+
stroke-dasharray="${c?"6,4":t.dashed?"3,3":"none"}"
|
|
901
|
+
class="${c?"graph-flow-edge":""}"/>
|
|
902
|
+
<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}"
|
|
903
|
+
fill="${X}" fill-opacity="${d}"/>
|
|
904
|
+
${h?O`
|
|
905
|
+
<rect x="${T-t.label.length*2.8-2}" y="${R-7}" width="${t.label.length*5.6+8}" height="14"
|
|
906
|
+
rx="4" fill="white" fill-opacity="0.92" stroke="${m}" stroke-width="0.4" stroke-opacity="0.15"/>
|
|
907
|
+
<text x="${T}" y="${R+3.5}" fill="${m}" font-size="8" font-weight="500"
|
|
908
|
+
font-family="ui-monospace,monospace" text-anchor="middle" opacity="0.85">${t.label}</text>
|
|
909
|
+
`:p}
|
|
910
|
+
${x?O`
|
|
911
|
+
<text x="${T}" y="${R-10}" fill="${ct}" font-size="8" font-weight="600"
|
|
912
|
+
text-anchor="middle">⚠ N+1</text>
|
|
913
|
+
`:p}
|
|
914
|
+
</g>
|
|
915
|
+
`}renderDetailPanel(t){let{node:e,edges:r}=t,i=At[e.type]||At.endpoint,n=e.annotations,l=(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${l?` (${n.securityFindings.length})`:""}`,show:l},{key:"performance",label:"Perf",show:d},{key:"issues",label:`Issues${c?` (${n.openIssueCount})`:""}`,show:c}].filter(v=>v.show);return a`
|
|
916
|
+
<div class="graph-detail">
|
|
917
|
+
<div class="graph-detail-head">
|
|
918
|
+
<div>
|
|
919
|
+
<div class="graph-detail-badge" style="color:${i.stroke}">${i.icon} ${e.type}</div>
|
|
920
|
+
<div class="graph-detail-name">${e.label}</div>
|
|
921
|
+
${n?.hasAuth?a`<span class="graph-detail-auth-badge">🛡 Authenticated</span>`:p}
|
|
922
|
+
${n?.isMiddleware?a`<span class="graph-detail-mw-badge">middleware</span>`:p}
|
|
923
|
+
</div>
|
|
924
|
+
<button class="graph-detail-close" @click=${()=>{this.locked=null;}}>✕</button>
|
|
925
|
+
</div>
|
|
926
|
+
|
|
927
|
+
${m.length>1?a`
|
|
928
|
+
<div class="graph-detail-tabs">
|
|
929
|
+
${m.map(v=>a`
|
|
930
|
+
<button class="graph-detail-tab ${this.detailTab===v.key?"active":""}"
|
|
931
|
+
@click=${()=>{this.detailTab=v.key;}}>${v.label}</button>
|
|
932
|
+
`)}
|
|
933
|
+
</div>
|
|
934
|
+
`:p}
|
|
935
|
+
|
|
936
|
+
${this.detailTab==="overview"?this.renderOverviewTab(e,r):p}
|
|
937
|
+
${this.detailTab==="security"?this.renderSecurityTab(n):p}
|
|
938
|
+
${this.detailTab==="performance"?this.renderPerformanceTab(e,n):p}
|
|
939
|
+
${this.detailTab==="issues"?this.renderIssuesTab(n):p}
|
|
940
|
+
</div>
|
|
941
|
+
`}renderOverviewTab(t,e){return a`
|
|
942
|
+
<div class="graph-detail-stats">
|
|
943
|
+
<div class="graph-detail-stat">
|
|
944
|
+
<div class="graph-detail-val">${t.stats.requestCount}</div>
|
|
945
|
+
<div class="graph-detail-lbl">${t.type==="action"?"OCCURRENCES":"REQUESTS"}</div>
|
|
946
|
+
</div>
|
|
947
|
+
<div class="graph-detail-stat">
|
|
948
|
+
<div class="graph-detail-val" style="color:${It(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
949
|
+
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
950
|
+
</div>
|
|
951
|
+
${t.stats.avgQueryCount>0?a`
|
|
952
|
+
<div class="graph-detail-stat">
|
|
953
|
+
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
954
|
+
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
955
|
+
</div>
|
|
956
|
+
`:p}
|
|
957
|
+
${t.stats.errorRate>.01?a`
|
|
958
|
+
<div class="graph-detail-stat">
|
|
959
|
+
<div class="graph-detail-val" style="color:${ct}">${Math.round(t.stats.errorRate*100)}%</div>
|
|
960
|
+
<div class="graph-detail-lbl">ERRORS</div>
|
|
961
|
+
</div>
|
|
962
|
+
`:p}
|
|
963
|
+
</div>
|
|
964
|
+
|
|
965
|
+
${e.length>0?a`
|
|
966
|
+
<div class="graph-detail-sec">Connections</div>
|
|
967
|
+
${e.map(r=>{let i=r.source===this.locked,n=(i?r.target:r.source).replace(/^(action|endpoint|table|external):/,"");return a`
|
|
968
|
+
<div class="graph-detail-conn">
|
|
969
|
+
<span class="graph-detail-edge-dot" style="background:${fe[r.type]}"></span>
|
|
970
|
+
<span class="graph-detail-edge-type">${r.type}</span>
|
|
971
|
+
<span>${i?"\u2192":"\u2190"} ${n}</span>
|
|
972
|
+
<span class="graph-detail-dim">${r.stats.frequency}× · ${r.stats.avgLatencyMs}ms</span>
|
|
973
|
+
</div>
|
|
974
|
+
`})}
|
|
975
|
+
`:p}
|
|
976
|
+
|
|
977
|
+
${e.some(r=>r.patterns?.length)?a`
|
|
978
|
+
<div class="graph-detail-sec">SQL Patterns</div>
|
|
979
|
+
${e.filter(r=>r.patterns).flatMap(r=>r.patterns).map(r=>a`
|
|
980
|
+
<pre class="graph-detail-sql">${r.length>200?r.slice(0,200)+"\u2026":r}</pre>
|
|
981
|
+
`)}
|
|
982
|
+
`:p}
|
|
983
|
+
`}renderSecurityTab(t){return t?.securityFindings?.length?a`
|
|
984
|
+
${t.securityFindings.map(e=>a`
|
|
985
|
+
<div class="graph-detail-finding">
|
|
986
|
+
<span class="graph-detail-severity graph-detail-severity-${e.severity}">${e.severity}</span>
|
|
987
|
+
<div class="graph-detail-finding-title">${e.title}</div>
|
|
988
|
+
<div class="graph-detail-finding-meta">${e.rule} · ${e.count} occurrence${e.count!==1?"s":""}</div>
|
|
989
|
+
</div>
|
|
990
|
+
`)}
|
|
991
|
+
`:a`<div class="graph-detail-empty">No security findings</div>`}renderPerformanceTab(t,e){return a`
|
|
992
|
+
<div class="graph-detail-stats">
|
|
993
|
+
${e?.p95Ms!==void 0?a`
|
|
994
|
+
<div class="graph-detail-stat">
|
|
995
|
+
<div class="graph-detail-val" style="color:${It(e.p95Ms)}">${e.p95Ms}ms</div>
|
|
996
|
+
<div class="graph-detail-lbl">P95 LATENCY</div>
|
|
997
|
+
</div>
|
|
998
|
+
`:p}
|
|
999
|
+
<div class="graph-detail-stat">
|
|
1000
|
+
<div class="graph-detail-val" style="color:${It(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
1001
|
+
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
1002
|
+
</div>
|
|
1003
|
+
${t.stats.avgQueryCount>0?a`
|
|
1004
|
+
<div class="graph-detail-stat">
|
|
1005
|
+
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
1006
|
+
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
1007
|
+
</div>
|
|
1008
|
+
`:p}
|
|
1009
|
+
<div class="graph-detail-stat">
|
|
1010
|
+
<div class="graph-detail-val">${t.stats.requestCount}</div>
|
|
1011
|
+
<div class="graph-detail-lbl">TOTAL REQS</div>
|
|
1012
|
+
</div>
|
|
1013
|
+
</div>
|
|
1014
|
+
|
|
1015
|
+
${(e?.insights?.length??0)>0?a`
|
|
1016
|
+
<div class="graph-detail-sec">Performance Insights</div>
|
|
1017
|
+
${e.insights.map(r=>a`
|
|
1018
|
+
<div class="graph-detail-finding">
|
|
1019
|
+
<span class="graph-detail-severity graph-detail-severity-${r.severity}">${r.severity}</span>
|
|
1020
|
+
<div class="graph-detail-finding-title">${r.title}</div>
|
|
1021
|
+
<div class="graph-detail-finding-meta">${r.type}</div>
|
|
1022
|
+
</div>
|
|
1023
|
+
`)}
|
|
1024
|
+
`:p}
|
|
1025
|
+
`}renderIssuesTab(t){let e=t?.openIssueCount??0;return e===0?a`<div class="graph-detail-empty">No open issues</div>`:a`
|
|
1026
|
+
<div class="graph-detail-issue-summary">
|
|
1027
|
+
<div class="graph-detail-stat">
|
|
1028
|
+
<div class="graph-detail-val" style="color:${ct}">${e}</div>
|
|
1029
|
+
<div class="graph-detail-lbl">OPEN ISSUES</div>
|
|
1030
|
+
</div>
|
|
1031
|
+
</div>
|
|
1032
|
+
<p class="graph-detail-hint">View the Issues tab for full details and remediation hints.</p>
|
|
1033
|
+
`}getSelectedNodeDetail(){if(!this.locked)return null;let t=this.graphNodes.find(r=>r.id===this.locked);if(!t)return null;let e=this.graphEdges.filter(r=>r.source===this.locked||r.target===this.locked);return {node:t,edges:e}}};u([I({context:A})],M.prototype,"store",2),u([b()],M.prototype,"graphNodes",2),u([b()],M.prototype,"graphEdges",2),u([b()],M.prototype,"locked",2),u([b()],M.prototype,"hovered",2),u([b()],M.prototype,"loading",2),u([b()],M.prototype,"activeLayers",2),u([b()],M.prototype,"searchQuery",2),u([b()],M.prototype,"viewTransform",2),u([b()],M.prototype,"dragging",2),u([b()],M.prototype,"detailTab",2),u([b()],M.prototype,"consolidatedFlows",2),u([b()],M.prototype,"activeFlowIdx",2),u([b()],M.prototype,"focusIdx",2),M=u([S("bk-graph-view")],M);function qi(o){return o===0?"<1ms":_(o)}var N=class extends E{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(`${w.activity}?requestId=${this.requestId}`);if(!e.ok){this.failed=!0,this.loading=!1;return}let r=await e.json();if(N.cache.size>=ke){let i=N.cache.keys().next().value;i!==void 0&&N.cache.delete(i);}N.cache.set(this.requestId,r),this.data=r,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 a`<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 a`
|
|
734
1034
|
<div class="tl-header">
|
|
735
1035
|
<span class="tl-title">Activity Timeline</span>
|
|
736
1036
|
<span class="tl-counts">
|
|
737
|
-
${t.counts.queries>0?a`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:
|
|
738
|
-
${t.counts.fetches>0?a`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:
|
|
739
|
-
${t.counts.logs>0?a`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:
|
|
740
|
-
${t.counts.errors>0?a`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:
|
|
1037
|
+
${t.counts.queries>0?a`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:p}
|
|
1038
|
+
${t.counts.fetches>0?a`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:p}
|
|
1039
|
+
${t.counts.logs>0?a`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:p}
|
|
1040
|
+
${t.counts.errors>0?a`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:p}
|
|
741
1041
|
</span>
|
|
742
1042
|
</div>
|
|
743
|
-
<div class="tl-events">${this.renderTimeline(t.timeline,
|
|
744
|
-
`}renderTimeline(t,
|
|
745
|
-
${this.renderEvent(l,c,
|
|
1043
|
+
<div class="tl-events">${this.renderTimeline(t.timeline,e)}</div>
|
|
1044
|
+
`}renderTimeline(t,e){let r=new Map,i=[];for(let l of t){let c=l.type==="query"?l.data.parentFetchId:void 0;if(l.type==="query"&&c){let d=r.get(c);d||(d=[],r.set(c,d)),d.push(l);}else i.push(l);}let n=0;return i.map(l=>{let c=n++,d=l.type==="fetch"?l.data.fetchId:void 0,h=d?r.get(d):void 0;if(h&&h.length>0){let m=h.length;return a`
|
|
1045
|
+
${this.renderEvent(l,c,e)}
|
|
746
1046
|
<div class="tl-nested">
|
|
747
1047
|
<span class="tl-nested-label">${m} nested quer${m===1?"y":"ies"}</span>
|
|
748
|
-
${h.map(
|
|
1048
|
+
${h.map(v=>{let x=n++;return this.renderEvent(v,x,e,true)})}
|
|
749
1049
|
</div>
|
|
750
|
-
`}return this.renderEvent(l,c,
|
|
751
|
-
<div class="tl-event ${h?"tl-clickable":""} ${
|
|
1050
|
+
`}return this.renderEvent(l,c,e)})}renderEvent(t,e,r,i=false){let n=Ms[t.type]||"var(--text-dim)",l=Ns[t.type]||t.type,c="+"+_(Math.round(t.timestamp-r)),d=t.type==="query"?t.data.sql:void 0,h=!!d,m=this.expandedSqlIdx===e;return a`
|
|
1051
|
+
<div class="tl-event ${h?"tl-clickable":""} ${i?"tl-nested-event":""}"
|
|
752
1052
|
style="${h?"":`border-left-color:${n}`}"
|
|
753
|
-
@click=${h?
|
|
1053
|
+
@click=${h?v=>this.toggleSql(e,v):p}>
|
|
754
1054
|
<span class="tl-event-time">${c}</span>
|
|
755
1055
|
<span class="tl-event-type" style="color:${n}">${l}</span>
|
|
756
1056
|
${this.renderEventContent(t)}
|
|
757
|
-
${
|
|
1057
|
+
${d?a`
|
|
758
1058
|
<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="${r?"color:var(--red)":""}">${
|
|
766
|
-
<span class="tl-event-dur">${
|
|
767
|
-
`}case "query":{let
|
|
768
|
-
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${r}</span> ${
|
|
769
|
-
<span class="tl-event-dur">${
|
|
770
|
-
`}case "log":{let
|
|
1059
|
+
<button class="tl-sql-copy" @click=${v=>this.copySql(d,v)}>Copy</button>
|
|
1060
|
+
${d}
|
|
1061
|
+
</div>`:p}
|
|
1062
|
+
</div>
|
|
1063
|
+
`}renderEventContent(t){switch(t.type){case "fetch":{let e=t.data,r=e.statusCode>=400;return a`
|
|
1064
|
+
<span class="tl-event-summary">${e.method} ${e.url}</span>
|
|
1065
|
+
<span class="tl-event-status" style="${r?"color:var(--red)":""}">${e.statusCode}</span>
|
|
1066
|
+
<span class="tl-event-dur">${_(e.durationMs)}</span>
|
|
1067
|
+
`}case "query":{let e=t.data,r=(e.normalizedOp||e.operation||"?").toUpperCase(),i=e.table||e.model||"",n=pe[r]||"var(--text-dim)";return a`
|
|
1068
|
+
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${r}</span> ${i}</span>
|
|
1069
|
+
<span class="tl-event-dur">${qi(e.durationMs)}</span>
|
|
1070
|
+
`}case "log":{let e=t.data,r=As[e.level]||"var(--text-dim)";return a`<span class="tl-event-summary"><span style="color:${r}">${e.level.toUpperCase()}</span> ${e.message}</span>`}case "error":{let e=t.data;return a`<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([b()],N.prototype,"data",2),u([b()],N.prototype,"loading",2),u([b()],N.prototype,"failed",2),u([b()],N.prototype,"expandedSqlIdx",2),N=u([S("bk-timeline-panel")],N);function Lt(o){try{return JSON.parse(o)}catch{return null}}var Ee=class{constructor(s,t){this.host=s;this.store=t;this.retryCount=0;this.boundHandlers={fetch:s=>{let t=Lt(s.data);t&&this.store.prependFetch(t);},log:s=>{let t=Lt(s.data);t&&this.store.prependLog(t);},error:s=>{let t=Lt(s.data);t&&this.store.prependError(t);},query:s=>{let t=Lt(s.data);t&&this.store.prependQuery(t);},issues:s=>{let t=Lt(s.data);t&&this.store.setIssues(t);}};s.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(ie,this.boundHandlers.fetch),this.eventSource.removeEventListener("log",this.boundHandlers.log),this.eventSource.removeEventListener(oe,this.boundHandlers.error),this.eventSource.removeEventListener(ne,this.boundHandlers.query),this.eventSource.removeEventListener(ae,this.boundHandlers.issues));}connect(){this.removeListeners(),this.eventSource?.close(),this.eventSource=new EventSource(w.events),this.eventSource.onopen=()=>{this.retryCount=0;},this.eventSource.onerror=()=>{this.eventSource?.close(),this.scheduleReconnect();},this.eventSource.onmessage=s=>{let t=Lt(s.data);t&&(t.path?.startsWith(F)||(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(),De))));},this.eventSource.addEventListener(ie,this.boundHandlers.fetch),this.eventSource.addEventListener("log",this.boundHandlers.log),this.eventSource.addEventListener(oe,this.boundHandlers.error),this.eventSource.addEventListener(ne,this.boundHandlers.query),this.eventSource.addEventListener(ae,this.boundHandlers.issues);}scheduleReconnect(){if(this.retryCount>=10)return;let s=Math.min(1e3*2**this.retryCount,3e4);this.retryCount++,this.reconnectTimer=setTimeout(()=>this.connect(),s);}async reloadFlows(){try{let t=await(await fetch(w.flows)).json();this.store.setFlows(t.flows);}catch{}}async reloadMetrics(){try{let t=await(await fetch(w.metricsLive)).json();this.store.setMetrics(t.endpoints||[]);}catch{}}};function vr(){return a`<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 gr(){return a`<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 br(){return a`<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="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>`}function Er(){return a`<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="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>`}function yr(){return a`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>`}function _r(){return a`<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="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>`}function $r(){return a`<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="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>`}function xr(){return a`<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="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>`}function Sr(){return a`<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 Tr(){return a`<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 dt=class extends E{constructor(){super(...arguments);this.store=new ue;this.activeView="overview";this.viewMode="simple";this.sse=new Ee(this,this.store);}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadInitialData(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}async loadInitialData(){try{let[t,e]=await Promise.all([fetch(w.flows),fetch(w.requests)]),[r,i]=await Promise.all([t.json(),e.json()]);this.store.setFlows(r.flows),this.store.setRequests(i.requests);}catch(t){console.warn("[brakit]",t);}try{let[t,e,r,i,n]=await Promise.all([fetch(w.fetches),fetch(w.errors),fetch(w.logs),fetch(w.queries),fetch(w.metricsLive)]),[l,c,d,h,m]=await Promise.all([t.json(),e.json(),r.json(),i.json(),n.json()]);this.store.setFetches(l.entries),this.store.setErrors(c.entries),this.store.setLogs(d.entries),this.store.setQueries(h.entries),this.store.setMetrics(m.endpoints||[]);}catch(t){console.warn("[brakit]",t);}try{let e=await(await fetch(w.insights)).json();this.store.setIssues(e.issues||[]);}catch(t){console.warn("[brakit]",t);}}switchView(t){t!==this.activeView&&(this.activeView=t,this.store.setActiveView(t),fetch(`${w.tab}?tab=${encodeURIComponent(t)}`).catch(()=>{}),t==="performance"&&this.sse.reloadMetrics());}async handleClear(){confirm("This will clear all data including performance metrics history. Continue?")&&(await fetch(w.clear,{method:"POST"}),this.store.clearAll(),D.show("Cleared"));}handleCopyAsCurl(t){Tt(t);}render(){let t=this.store.state,e=t.requests.filter(c=>!c.path?.startsWith(F)),r=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(c=>c.state!=="resolved"&&c.state!=="stale").length,l=window.__BRAKIT_CONFIG__;return a`
|
|
771
1071
|
<div class="app" id="app">
|
|
772
1072
|
<aside class="sidebar">
|
|
773
1073
|
<div class="sidebar-logo">
|
|
@@ -775,25 +1075,31 @@
|
|
|
775
1075
|
<span class="logo-version">v${l?.version??""}</span>
|
|
776
1076
|
</div>
|
|
777
1077
|
<nav class="sidebar-nav">
|
|
778
|
-
${this.renderSidebarItem("overview","Overview",
|
|
1078
|
+
${this.renderSidebarItem("overview","Overview",vr(),void 0)}
|
|
779
1079
|
<div class="sidebar-section">Monitor</div>
|
|
780
|
-
${this.renderSidebarItem("actions","Actions",
|
|
781
|
-
${this.renderSidebarItem("requests","Requests",
|
|
782
|
-
${this.renderSidebarItem("fetches","Fetches",
|
|
1080
|
+
${this.renderSidebarItem("actions","Actions",gr(),t.flows.length)}
|
|
1081
|
+
${this.renderSidebarItem("requests","Requests",br(),e.length)}
|
|
1082
|
+
${this.renderSidebarItem("fetches","Fetches",Er(),t.fetches.length)}
|
|
783
1083
|
<div class="sidebar-section">Insights</div>
|
|
784
|
-
${this.renderSidebarItem("queries","Queries",
|
|
785
|
-
${this.renderSidebarItem("errors","Errors",
|
|
786
|
-
${this.renderSidebarItem("logs","Logs",$
|
|
787
|
-
${this.renderSidebarItem("security","Security",
|
|
788
|
-
${this.renderSidebarItem("performance","Performance",
|
|
1084
|
+
${this.renderSidebarItem("queries","Queries",yr(),t.queries.length)}
|
|
1085
|
+
${this.renderSidebarItem("errors","Errors",_r(),t.errors.length)}
|
|
1086
|
+
${this.renderSidebarItem("logs","Logs",$r(),t.logs.length)}
|
|
1087
|
+
${this.renderSidebarItem("security","Security",xr(),n,n===0)}
|
|
1088
|
+
${this.renderSidebarItem("performance","Performance",Sr(),void 0)}
|
|
1089
|
+
<div class="sidebar-section">Topology</div>
|
|
1090
|
+
<button class="sidebar-item ${this.activeView==="graph"?"active":""}" @click=${()=>this.switchView("graph")}>
|
|
1091
|
+
<span class="item-icon">${Tr()}</span>
|
|
1092
|
+
<span class="item-label">Graph</span>
|
|
1093
|
+
<span class="sidebar-beta">beta</span>
|
|
1094
|
+
</button>
|
|
789
1095
|
</nav>
|
|
790
1096
|
<div class="sidebar-footer">:${l?.port??""}</div>
|
|
791
1097
|
</aside>
|
|
792
1098
|
<div class="main-panel">
|
|
793
1099
|
<div class="header">
|
|
794
1100
|
<div class="header-left">
|
|
795
|
-
<span class="header-title" id="header-title">${
|
|
796
|
-
<span class="header-sub" id="header-sub">${
|
|
1101
|
+
<span class="header-title" id="header-title">${Ie[this.activeView]||this.activeView}</span>
|
|
1102
|
+
<span class="header-sub" id="header-sub">${Ce[this.activeView]||""}</span>
|
|
797
1103
|
</div>
|
|
798
1104
|
<div class="header-right">
|
|
799
1105
|
${this.activeView==="actions"?a`
|
|
@@ -801,7 +1107,7 @@
|
|
|
801
1107
|
<button class="segmented-btn ${this.viewMode==="simple"?"active":""}" @click=${()=>{this.viewMode="simple",this.store.setViewMode("simple");}}>Quick</button>
|
|
802
1108
|
<button class="segmented-btn ${this.viewMode==="detailed"?"active":""}" @click=${()=>{this.viewMode="detailed",this.store.setViewMode("detailed");}}>Detailed</button>
|
|
803
1109
|
</div>
|
|
804
|
-
`:
|
|
1110
|
+
`:p}
|
|
805
1111
|
<button class="btn btn-danger" @click=${this.handleClear}>Clear</button>
|
|
806
1112
|
</div>
|
|
807
1113
|
</div>
|
|
@@ -833,23 +1139,26 @@
|
|
|
833
1139
|
<div class="view-telemetry" id="performance-container" style="display:${this.activeView==="performance"?"block":"none"}">
|
|
834
1140
|
<bk-performance-view></bk-performance-view>
|
|
835
1141
|
</div>
|
|
1142
|
+
<div class="view-telemetry" id="graph-container" style="display:${this.activeView==="graph"?"block":"none"}">
|
|
1143
|
+
<bk-graph-view></bk-graph-view>
|
|
1144
|
+
</div>
|
|
836
1145
|
</div>
|
|
837
1146
|
<div class="footer">
|
|
838
|
-
<span id="stat-total">${
|
|
1147
|
+
<span id="stat-total">${e.length} request${e.length!==1?"s":""}</span>
|
|
839
1148
|
<span id="stat-flows">${t.flows.length} action${t.flows.length!==1?"s":""}</span>
|
|
840
1149
|
<span id="stat-errors" class="error-count">${r} error${r!==1?"s":""}</span>
|
|
841
|
-
<span id="stat-avg">Avg: ${
|
|
1150
|
+
<span id="stat-avg">Avg: ${i}ms</span>
|
|
842
1151
|
</div>
|
|
843
1152
|
</div>
|
|
844
1153
|
</div>
|
|
845
1154
|
<bk-toast></bk-toast>
|
|
846
|
-
`}renderSidebarItem(t,
|
|
1155
|
+
`}renderSidebarItem(t,e,r,i,n=false){return a`
|
|
847
1156
|
<button class="sidebar-item ${this.activeView===t?"active":""}" @click=${()=>this.switchView(t)}>
|
|
848
1157
|
<span class="item-icon">${r}</span>
|
|
849
|
-
<span class="item-label">${
|
|
850
|
-
${
|
|
1158
|
+
<span class="item-label">${e}</span>
|
|
1159
|
+
${i!==void 0?a`<span class="item-count" style="display:${n?"none":""}">${i}</span>`:p}
|
|
851
1160
|
</button>
|
|
852
|
-
`}};u([
|
|
1161
|
+
`}};u([Be({context:A})],dt.prototype,"store",2),u([b()],dt.prototype,"activeView",2),u([b()],dt.prototype,"viewMode",2),dt=u([S("bk-dashboard")],dt);
|
|
853
1162
|
/*! Bundled license information:
|
|
854
1163
|
|
|
855
1164
|
@lit/reactive-element/css-tag.js:
|
|
@@ -872,6 +1181,8 @@ lit-element/lit-element.js:
|
|
|
872
1181
|
@lit/reactive-element/decorators/query-async.js:
|
|
873
1182
|
@lit/reactive-element/decorators/query-assigned-nodes.js:
|
|
874
1183
|
@lit/context/lib/decorators/provide.js:
|
|
1184
|
+
lit-html/directive.js:
|
|
1185
|
+
lit-html/directives/unsafe-html.js:
|
|
875
1186
|
(**
|
|
876
1187
|
* @license
|
|
877
1188
|
* Copyright 2017 Google LLC
|