brakit 0.10.1 → 0.10.2
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 +41 -14
- package/dist/api.js +105 -17
- package/dist/bin/brakit.js +307 -37
- package/dist/dashboard-client.global.js +204 -191
- package/dist/dashboard.html +216 -192
- package/dist/mcp/server.js +78 -1
- package/dist/runtime/index.js +1330 -1122
- package/package.json +1 -1
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
(function(){'use strict';var
|
|
2
|
-
\f\r]`,qt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_s
|
|
3
|
-
\f\r"'\`<>=]|("|')|))|$)`,"g"),xs=/'/g,Ss=/"/g,As=/^(?:script|style|textarea|title)$/i,De=o=>(r,...t)=>({_$litType$:o,strings:r,values:t}),l=De(1),k=De(2),z=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),Ts=new WeakMap,lt=ct.createTreeWalker(ct,129);function Is(o,r){if(!Oe(o)||!o.hasOwnProperty("raw"))throw Error("invalid template strings array");return ys!==void 0?ys.createHTML(r):r}var jr=(o,r)=>{let t=o.length-1,e=[],s,i=r===2?"<svg>":r===3?"<math>":"",n=qt;for(let a=0;a<t;a++){let c=o[a],d,h,m=-1,f=0;for(;f<c.length&&(n.lastIndex=f,h=n.exec(c),h!==null);)f=n.lastIndex,n===qt?h[1]==="!--"?n=_s:h[1]!==void 0?n=$s:h[2]!==void 0?(As.test(h[2])&&(s=RegExp("</"+h[2],"g")),n=at):h[3]!==void 0&&(n=at):n===at?h[0]===">"?(n=s??qt,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,d=h[1],n=h[3]===void 0?at:h[3]==='"'?Ss:xs):n===Ss||n===xs?n=at:n===_s||n===$s?n=qt:(n=at,s=void 0);let S=n===at&&o[a+1].startsWith("/>")?" ":"";i+=n===qt?c+Wr:m>=0?(e.push(d),c.slice(0,m)+ws+c.slice(m)+st+S):c+st+(m===-2?a:S);}return [Is(o,i+(o[t]||"<?>")+(r===2?"</svg>":r===3?"</math>":"")),e]},Bt=class o{constructor({strings:r,_$litType$:t},e){let s;this.parts=[];let i=0,n=0,a=r.length-1,c=this.parts,[d,h]=jr(r,t);if(this.el=o.createElement(d,e),lt.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(s=lt.nextNode())!==null&&c.length<a;){if(s.nodeType===1){if(s.hasAttributes())for(let m of s.getAttributeNames())if(m.endsWith(ws)){let f=h[n++],S=s.getAttribute(m).split(st),R=/([.?@])?(.*)/.exec(f);c.push({type:1,index:i,name:R[2],strings:S,ctor:R[1]==="."?Le:R[1]==="?"?Me:R[1]==="@"?Ne:gt}),s.removeAttribute(m);}else m.startsWith(st)&&(c.push({type:6,index:i}),s.removeAttribute(m));if(As.test(s.tagName)){let m=s.textContent.split(st),f=m.length-1;if(f>0){s.textContent=ce?ce.emptyScript:"";for(let S=0;S<f;S++)s.append(m[S],Ft()),lt.nextNode(),c.push({type:2,index:++i});s.append(m[f],Ft());}}}else if(s.nodeType===8)if(s.data===Rs)c.push({type:2,index:i});else {let m=-1;for(;(m=s.data.indexOf(st,m+1))!==-1;)c.push({type:7,index:i}),m+=st.length-1;}i++;}}static createElement(r,t){let e=ct.createElement("template");return e.innerHTML=r,e}};function vt(o,r,t=o,e){if(r===z)return r;let s=e!==void 0?t._$Co?.[e]:t._$Cl,i=Gt(r)?void 0:r._$litDirective$;return s?.constructor!==i&&(s?._$AO?.(false),i===void 0?s=void 0:(s=new i(o),s._$AT(o,t,e)),e!==void 0?(t._$Co??(t._$Co=[]))[e]=s:t._$Cl=s),s!==void 0&&(r=vt(o,s._$AS(o,r.values),s,e)),r}var Ce=class{constructor(r,t){this._$AV=[],this._$AN=void 0,this._$AD=r,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(r){let{el:{content:t},parts:e}=this._$AD,s=(r?.creationScope??ct).importNode(t,true);lt.currentNode=s;let i=lt.nextNode(),n=0,a=0,c=e[0];for(;c!==void 0;){if(n===c.index){let d;c.type===2?d=new Wt(i,i.nextSibling,this,r):c.type===1?d=new c.ctor(i,c.name,c.strings,this,r):c.type===6&&(d=new ke(i,this,r)),this._$AV.push(d),c=e[++a];}n!==c?.index&&(i=lt.nextNode(),n++);}return lt.currentNode=ct,s}p(r){let t=0;for(let e of this._$AV)e!==void 0&&(e.strings!==void 0?(e._$AI(r,e,t),t+=e.strings.length-2):e._$AI(r[t])),t++;}},Wt=class o{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(r,t,e,s){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=r,this._$AB=t,this._$AM=e,this.options=s,this._$Cv=s?.isConnected??true;}get parentNode(){let r=this._$AA.parentNode,t=this._$AM;return t!==void 0&&r?.nodeType===11&&(r=t.parentNode),r}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(r,t=this){r=vt(this,r,t),Gt(r)?r===p||r==null||r===""?(this._$AH!==p&&this._$AR(),this._$AH=p):r!==this._$AH&&r!==z&&this._(r):r._$litType$!==void 0?this.$(r):r.nodeType!==void 0?this.T(r):Qr(r)?this.k(r):this._(r);}O(r){return this._$AA.parentNode.insertBefore(r,this._$AB)}T(r){this._$AH!==r&&(this._$AR(),this._$AH=this.O(r));}_(r){this._$AH!==p&&Gt(this._$AH)?this._$AA.nextSibling.data=r:this.T(ct.createTextNode(r)),this._$AH=r;}$(r){let{values:t,_$litType$:e}=r,s=typeof e=="number"?this._$AC(r):(e.el===void 0&&(e.el=Bt.createElement(Is(e.h,e.h[0]),this.options)),e);if(this._$AH?._$AD===s)this._$AH.p(t);else {let i=new Ce(s,this),n=i.u(this.options);i.p(t),this.T(n),this._$AH=i;}}_$AC(r){let t=Ts.get(r.strings);return t===void 0&&Ts.set(r.strings,t=new Bt(r)),t}k(r){Oe(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,e,s=0;for(let i of r)s===t.length?t.push(e=new o(this.O(Ft()),this.O(Ft()),this,this.options)):e=t[s],e._$AI(i),s++;s<t.length&&(this._$AR(e&&e._$AB.nextSibling,s),t.length=s);}_$AR(r=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);r!==this._$AB;){let e=bs(r).nextSibling;bs(r).remove(),r=e;}}setConnected(r){this._$AM===void 0&&(this._$Cv=r,this._$AP?.(r));}},gt=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(r,t,e,s,i){this.type=1,this._$AH=p,this._$AN=void 0,this.element=r,this.name=t,this._$AM=s,this.options=i,e.length>2||e[0]!==""||e[1]!==""?(this._$AH=Array(e.length-1).fill(new String),this.strings=e):this._$AH=p;}_$AI(r,t=this,e,s){let i=this.strings,n=false;if(i===void 0)r=vt(this,r,t,0),n=!Gt(r)||r!==this._$AH&&r!==z,n&&(this._$AH=r);else {let a=r,c,d;for(r=i[0],c=0;c<i.length-1;c++)d=vt(this,a[e+c],t,c),d===z&&(d=this._$AH[c]),n||(n=!Gt(d)||d!==this._$AH[c]),d===p?r=p:r!==p&&(r+=(d??"")+i[c+1]),this._$AH[c]=d;}n&&!s&&this.j(r);}j(r){r===p?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,r??"");}},Le=class extends gt{constructor(){super(...arguments),this.type=3;}j(r){this.element[this.name]=r===p?void 0:r;}},Me=class extends gt{constructor(){super(...arguments),this.type=4;}j(r){this.element.toggleAttribute(this.name,!!r&&r!==p);}},Ne=class extends gt{constructor(r,t,e,s,i){super(r,t,e,s,i),this.type=5;}_$AI(r,t=this){if((r=vt(this,r,t,0)??p)===z)return;let e=this._$AH,s=r===p&&e!==p||r.capture!==e.capture||r.once!==e.once||r.passive!==e.passive,i=r!==p&&(e===p||s);s&&this.element.removeEventListener(this.name,this,e),i&&this.element.addEventListener(this.name,this,r),this._$AH=r;}handleEvent(r){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,r):this._$AH.handleEvent(r);}},ke=class{constructor(r,t,e){this.element=r,this.type=6,this._$AN=void 0,this._$AM=t,this.options=e;}get _$AU(){return this._$AM._$AU}_$AI(r){vt(this,r);}};var Yr=Ut.litHtmlPolyfillSupport;Yr?.(Bt,Wt),(Ut.litHtmlVersions??(Ut.litHtmlVersions=[])).push("3.3.2");var Cs=(o,r,t)=>{let e=t?.renderBefore??r,s=e._$litPart$;if(s===void 0){let i=t?.renderBefore??null;e._$litPart$=s=new Wt(r.insertBefore(Ft(),i),i,void 0,t??{});}return s._$AI(o),s};var Qt=globalThis,b=class extends K{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let r=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=r.firstChild),r}update(r){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(r),this._$Do=Cs(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return z}};b._$litElement$=true,b.finalized=true,Qt.litElementHydrateSupport?.({LitElement:b});var Vr=Qt.litElementPolyfillSupport;Vr?.({LitElement:b});(Qt.litElementVersions??(Qt.litElementVersions=[])).push("4.2.2");var $=o=>(r,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(o,r);}):customElements.define(o,r);};var Xr={attribute:true,type:String,converter:Ht,reflect:false,hasChanged:le},Kr=(o=Xr,r,t)=>{let{kind:e,metadata:s}=t,i=globalThis.litPropertyMetadata.get(s);if(i===void 0&&globalThis.litPropertyMetadata.set(s,i=new Map),e==="setter"&&((o=Object.create(o)).wrapped=true),i.set(t.name,o),e==="accessor"){let{name:n}=t;return {set(a){let c=r.get.call(this);r.set.call(this,a),this.requestUpdate(n,c,o,true,a);},init(a){return a!==void 0&&this.C(n,void 0,o,a),a}}}if(e==="setter"){let{name:n}=t;return function(a){let c=this[n];r.call(this,a),this.requestUpdate(n,c,o,true,a);}}throw Error("Unsupported decorator location: "+e)};function L(o){return (r,t)=>typeof t=="object"?Kr(o,r,t):((e,s,i)=>{let n=s.hasOwnProperty(i);return s.constructor.createProperty(i,e),n?Object.getOwnPropertyDescriptor(s,i):void 0})(o,r,t)}function E(o){return L({...o,state:true,attribute:false})}var jt=class extends b{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return l`<span class="method-badge method-badge-${t}">${t}</span>`}};u([L()],jt.prototype,"method",2),jt=u([$("bk-method-badge")],jt);var q="/__brakit/api",U="/__brakit",T={flows:`${q}/flows`,requests:`${q}/requests`,events:`${q}/events`,clear:`${q}/clear`,fetches:`${q}/fetches`,errors:`${q}/errors`,logs:`${q}/logs`,queries:`${q}/queries`,metricsLive:`${q}/metrics/live`,insights:`${q}/insights`,tab:`${q}/tab`,activity:`${q}/activity`,graph:`${q}/graph`};var Et="polling",pe="static",zr="auth-handshake",Jr="auth-check",Zr="middleware",Yt={[zr]:1,[Jr]:1,[Zr]:1};var he="fetch";var ue="error_event",me="query",fe="issues";var Pe={overview:"Overview",actions:"Actions",insights:"Insights",performance:"Performance",graph:"Graph",explorer:"Explorer",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",security:"Security"},He={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",insights:"Security findings, error patterns, and issue tracking",performance:"Endpoint health and response time trends",graph:"Runtime dependency graph of your application",explorer:"Browse raw requests, fetches, queries, logs, and errors",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",security:"Security findings and recommendations"},ve=[{key:"requests",label:"Requests"},{key:"fetches",label:"Fetches"},{key:"queries",label:"Queries"},{key:"logs",label:"Logs"},{key:"errors",label:"Errors"}];var Ls=new Set(["exposed-secret","token-in-url","stack-trace-leak","error-info-leak","insecure-cookie","sensitive-logs","cors-credentials","response-pii-leak"]),Ms=new Set(["slow-endpoint","n1","high-query-count"]);function Vt(o){let r=o.issue.rule||o.issue.type||"";return Ls.has(r)?"security":Ms.has(r)?"performance":"quality"}var O={CLEAR_CONFIRM:"This will clear all data including performance metrics history. Continue?",CLEARED_TOAST:"Cleared",EMPTY_TITLE:"Waiting for requests...",EMPTY_SUBTITLE_OVERVIEW:"Start using your app to see your dashboard here",ALL_CLEAR:"All clear",ALL_CLEAR_DETAIL:"No issues detected",NO_ACTIONS:"No actions captured yet",NO_REQUEST_DATA:"No request data yet",ALL_FAST:"All fast",NO_SLOW_ENDPOINTS:"No slow endpoints detected",ZERO_ERRORS:"0 errors",ALL_REQUESTS_OK:"All requests successful",BUILD_GRAPH:"Navigate your app to build the graph",REVIEW_RECOMMENDED:"review recommended"};var Fe=100,yt=300,_t=800,Ge=2e3,Be=100,We=50,Qe=500;var rt="__all__",Ee={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",COUNT:"var(--text-muted)"},qs={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},Ve=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],Xt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},xt=[{max:Fe,label:"Fast",color:"var(--green)",bg:"var(--green-bg)",border:"var(--green-border)"},{max:yt,label:"Good",color:"var(--green)",bg:"var(--green-bg-subtle)",border:"var(--green-border-subtle)"},{max:_t,label:"OK",color:"var(--amber)",bg:"var(--amber-bg)",border:"var(--amber-border)"},{max:Ge,label:"Slow",color:"var(--red)",bg:"var(--red-bg)",border:"var(--red-border)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"var(--red-bg)",border:"var(--red-border)"}],Us="rgba(228,228,231,0.8)",Xe="rgba(113,113,122,0.7)",Fs="10px monospace",Ke="9px monospace";var Gs={top:16,right:16,bottom:28,left:52},Bs={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},Ws={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},Qs=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),js={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},Ys=new Set(["host","connection","accept-encoding"]),Y={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function y(o){return o<1e3?o+"ms":(o/1e3).toFixed(1)+"s"}function ot(o){return !o||o===0?"":o<1024?o+"b":(o/1024).toFixed(1)+"kb"}function it(o){return o?o.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',"""):""}function St(o){return o>=500?"status-pill-5xx":o>=400?"status-pill-4xx":o>=300?"status-pill-3xx":"status-pill-2xx"}function Vs(o){return js[o]||(o>=500?"Server Error":o>=400?"Client Error":"OK")}function ti(o,r){if(Qs.has(o.toLowerCase())){let t=String(r);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(r)}function Tt(o){return !o||Object.keys(o).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(o).map(([r,t])=>'<span class="json-key">'+it(r)+"</span>: "+it(ti(r,t))).join(`
|
|
4
|
-
`)}function dt(o){if(!o)return '<span style="color:var(--text-muted)">No body</span>';try{let r=JSON.parse(o);return
|
|
1
|
+
(function(){'use strict';var Or=Object.defineProperty;var Nr=Object.getOwnPropertyDescriptor;var u=(o,r,t,e)=>{for(var s=e>1?void 0:e?Nr(r,t):r,i=o.length-1,n;i>=0;i--)(n=o[i])&&(s=(e?n(r,t,s):n(s))||s);return e&&s&&Or(r,t,s),s};var oe=globalThis,ae=oe.ShadowRoot&&(oe.ShadyCSS===void 0||oe.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,ms=Symbol(),us=new WeakMap,ne=class{constructor(r,t,e){if(this._$cssResult$=true,e!==ms)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=r,this.t=t;}get styleSheet(){let r=this.o,t=this.t;if(ae&&r===void 0){let e=t!==void 0&&t.length===1;e&&(r=us.get(t)),r===void 0&&((this.o=r=new CSSStyleSheet).replaceSync(this.cssText),e&&us.set(t,r));}return r}toString(){return this.cssText}},fs=o=>new ne(typeof o=="string"?o:o+"",void 0,ms);var vs=(o,r)=>{if(ae)o.adoptedStyleSheets=r.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of r){let e=document.createElement("style"),s=oe.litNonce;s!==void 0&&e.setAttribute("nonce",s),e.textContent=t.cssText,o.appendChild(e);}},Ae=ae?o=>o:o=>o instanceof CSSStyleSheet?(r=>{let t="";for(let e of r.cssRules)t+=e.cssText;return fs(t)})(o):o;var{is:Dr,defineProperty:Pr,getOwnPropertyDescriptor:Hr,getOwnPropertyNames:qr,getOwnPropertySymbols:Ur,getPrototypeOf:Fr}=Object,et=globalThis,gs=et.trustedTypes,Gr=gs?gs.emptyScript:"",Br=et.reactiveElementPolyfillSupport,Pt=(o,r)=>o,Ht={toAttribute(o,r){switch(r){case Boolean:o=o?Gr:null;break;case Object:case Array:o=o==null?o:JSON.stringify(o);}return o},fromAttribute(o,r){let t=o;switch(r){case Boolean:t=o!==null;break;case Number:t=o===null?null:Number(o);break;case Object:case Array:try{t=JSON.parse(o);}catch{t=null;}}return t}},le=(o,r)=>!Dr(o,r),bs={attribute:true,type:String,converter:Ht,reflect:false,useDefault:false,hasChanged:le};Symbol.metadata??(Symbol.metadata=Symbol("metadata")),et.litPropertyMetadata??(et.litPropertyMetadata=new WeakMap);var K=class extends HTMLElement{static addInitializer(r){this._$Ei(),(this.l??(this.l=[])).push(r);}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(r,t=bs){if(t.state&&(t.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(r)&&((t=Object.create(t)).wrapped=true),this.elementProperties.set(r,t),!t.noAccessor){let e=Symbol(),s=this.getPropertyDescriptor(r,e,t);s!==void 0&&Pr(this.prototype,r,s);}}static getPropertyDescriptor(r,t,e){let{get:s,set:i}=Hr(this.prototype,r)??{get(){return this[t]},set(n){this[t]=n;}};return {get:s,set(n){let a=s?.call(this);i?.call(this,n),this.requestUpdate(r,a,e);},configurable:true,enumerable:true}}static getPropertyOptions(r){return this.elementProperties.get(r)??bs}static _$Ei(){if(this.hasOwnProperty(Pt("elementProperties")))return;let r=Fr(this);r.finalize(),r.l!==void 0&&(this.l=[...r.l]),this.elementProperties=new Map(r.elementProperties);}static finalize(){if(this.hasOwnProperty(Pt("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(Pt("properties"))){let t=this.properties,e=[...qr(t),...Ur(t)];for(let s of e)this.createProperty(s,t[s]);}let r=this[Symbol.metadata];if(r!==null){let t=litPropertyMetadata.get(r);if(t!==void 0)for(let[e,s]of t)this.elementProperties.set(e,s);}this._$Eh=new Map;for(let[t,e]of this.elementProperties){let s=this._$Eu(t,e);s!==void 0&&this._$Eh.set(s,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(r){let t=[];if(Array.isArray(r)){let e=new Set(r.flat(1/0).reverse());for(let s of e)t.unshift(Ae(s));}else r!==void 0&&t.push(Ae(r));return t}static _$Eu(r,t){let e=t.attribute;return e===false?void 0:typeof e=="string"?e:typeof r=="string"?r.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,this._$Em=null,this._$Ev();}_$Ev(){this._$ES=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(r=>r(this));}addController(r){(this._$EO??(this._$EO=new Set)).add(r),this.renderRoot!==void 0&&this.isConnected&&r.hostConnected?.();}removeController(r){this._$EO?.delete(r);}_$E_(){let r=new Map,t=this.constructor.elementProperties;for(let e of t.keys())this.hasOwnProperty(e)&&(r.set(e,this[e]),delete this[e]);r.size>0&&(this._$Ep=r);}createRenderRoot(){let r=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return vs(r,this.constructor.elementStyles),r}connectedCallback(){this.renderRoot??(this.renderRoot=this.createRenderRoot()),this.enableUpdating(true),this._$EO?.forEach(r=>r.hostConnected?.());}enableUpdating(r){}disconnectedCallback(){this._$EO?.forEach(r=>r.hostDisconnected?.());}attributeChangedCallback(r,t,e){this._$AK(r,e);}_$ET(r,t){let e=this.constructor.elementProperties.get(r),s=this.constructor._$Eu(r,e);if(s!==void 0&&e.reflect===true){let i=(e.converter?.toAttribute!==void 0?e.converter:Ht).toAttribute(t,e.type);this._$Em=r,i==null?this.removeAttribute(s):this.setAttribute(s,i),this._$Em=null;}}_$AK(r,t){let e=this.constructor,s=e._$Eh.get(r);if(s!==void 0&&this._$Em!==s){let i=e.getPropertyOptions(s),n=typeof i.converter=="function"?{fromAttribute:i.converter}:i.converter?.fromAttribute!==void 0?i.converter:Ht;this._$Em=s;let a=n.fromAttribute(t,i.type);this[s]=a??this._$Ej?.get(s)??a,this._$Em=null;}}requestUpdate(r,t,e,s=false,i){if(r!==void 0){let n=this.constructor;if(s===false&&(i=this[r]),e??(e=n.getPropertyOptions(r)),!((e.hasChanged??le)(i,t)||e.useDefault&&e.reflect&&i===this._$Ej?.get(r)&&!this.hasAttribute(n._$Eu(r,e))))return;this.C(r,t,e);}this.isUpdatePending===false&&(this._$ES=this._$EP());}C(r,t,{useDefault:e,reflect:s,wrapped:i},n){e&&!(this._$Ej??(this._$Ej=new Map)).has(r)&&(this._$Ej.set(r,n??t??this[r]),i!==true||n!==void 0)||(this._$AL.has(r)||(this.hasUpdated||e||(t=void 0),this._$AL.set(r,t)),s===true&&this._$Em!==r&&(this._$Eq??(this._$Eq=new Set)).add(r));}async _$EP(){this.isUpdatePending=true;try{await this._$ES;}catch(t){Promise.reject(t);}let r=this.scheduleUpdate();return r!=null&&await r,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??(this.renderRoot=this.createRenderRoot()),this._$Ep){for(let[s,i]of this._$Ep)this[s]=i;this._$Ep=void 0;}let e=this.constructor.elementProperties;if(e.size>0)for(let[s,i]of e){let{wrapped:n}=i,a=this[s];n!==true||this._$AL.has(s)||a===void 0||this.C(s,void 0,i,a);}}let r=false,t=this._$AL;try{r=this.shouldUpdate(t),r?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM();}catch(e){throw r=false,this._$EM(),e}r&&this._$AE(t);}willUpdate(r){}_$AE(r){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(r)),this.updated(r);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(r){return true}update(r){this._$Eq&&(this._$Eq=this._$Eq.forEach(t=>this._$ET(t,this[t]))),this._$EM();}updated(r){}firstUpdated(r){}};K.elementStyles=[],K.shadowRootOptions={mode:"open"},K[Pt("elementProperties")]=new Map,K[Pt("finalized")]=new Map,Br?.({ReactiveElement:K}),(et.reactiveElementVersions??(et.reactiveElementVersions=[])).push("2.1.2");var Ut=globalThis,Es=o=>o,ce=Ut.trustedTypes,ys=ce?ce.createPolicy("lit-html",{createHTML:o=>o}):void 0,ws="$lit$",st=`lit$${Math.random().toFixed(9).slice(2)}$`,Rs="?"+st,Wr=`<${Rs}>`,ct=document,Ft=()=>ct.createComment(""),Gt=o=>o===null||typeof o!="object"&&typeof o!="function",Ne=Array.isArray,jr=o=>Ne(o)||typeof o?.[Symbol.iterator]=="function",Ce=`[
|
|
2
|
+
\f\r]`,qt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,$s=/-->/g,_s=/>/g,at=RegExp(`>|${Ce}(?:([^\\s"'>=/]+)(${Ce}*=${Ce}*(?:[^
|
|
3
|
+
\f\r"'\`<>=]|("|')|))|$)`,"g"),xs=/'/g,Ss=/"/g,As=/^(?:script|style|textarea|title)$/i,De=o=>(r,...t)=>({_$litType$:o,strings:r,values:t}),l=De(1),O=De(2),z=Symbol.for("lit-noChange"),d=Symbol.for("lit-nothing"),Ts=new WeakMap,lt=ct.createTreeWalker(ct,129);function Cs(o,r){if(!Ne(o)||!o.hasOwnProperty("raw"))throw Error("invalid template strings array");return ys!==void 0?ys.createHTML(r):r}var Qr=(o,r)=>{let t=o.length-1,e=[],s,i=r===2?"<svg>":r===3?"<math>":"",n=qt;for(let a=0;a<t;a++){let c=o[a],p,h,m=-1,f=0;for(;f<c.length&&(n.lastIndex=f,h=n.exec(c),h!==null);)f=n.lastIndex,n===qt?h[1]==="!--"?n=$s:h[1]!==void 0?n=_s:h[2]!==void 0?(As.test(h[2])&&(s=RegExp("</"+h[2],"g")),n=at):h[3]!==void 0&&(n=at):n===at?h[0]===">"?(n=s??qt,m=-1):h[1]===void 0?m=-2:(m=n.lastIndex-h[2].length,p=h[1],n=h[3]===void 0?at:h[3]==='"'?Ss:xs):n===Ss||n===xs?n=at:n===$s||n===_s?n=qt:(n=at,s=void 0);let S=n===at&&o[a+1].startsWith("/>")?" ":"";i+=n===qt?c+Wr:m>=0?(e.push(p),c.slice(0,m)+ws+c.slice(m)+st+S):c+st+(m===-2?a:S);}return [Cs(o,i+(o[t]||"<?>")+(r===2?"</svg>":r===3?"</math>":"")),e]},Bt=class o{constructor({strings:r,_$litType$:t},e){let s;this.parts=[];let i=0,n=0,a=r.length-1,c=this.parts,[p,h]=Qr(r,t);if(this.el=o.createElement(p,e),lt.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes);}for(;(s=lt.nextNode())!==null&&c.length<a;){if(s.nodeType===1){if(s.hasAttributes())for(let m of s.getAttributeNames())if(m.endsWith(ws)){let f=h[n++],S=s.getAttribute(m).split(st),R=/([.?@])?(.*)/.exec(f);c.push({type:1,index:i,name:R[2],strings:S,ctor:R[1]==="."?Le:R[1]==="?"?Me:R[1]==="@"?ke:gt}),s.removeAttribute(m);}else m.startsWith(st)&&(c.push({type:6,index:i}),s.removeAttribute(m));if(As.test(s.tagName)){let m=s.textContent.split(st),f=m.length-1;if(f>0){s.textContent=ce?ce.emptyScript:"";for(let S=0;S<f;S++)s.append(m[S],Ft()),lt.nextNode(),c.push({type:2,index:++i});s.append(m[f],Ft());}}}else if(s.nodeType===8)if(s.data===Rs)c.push({type:2,index:i});else {let m=-1;for(;(m=s.data.indexOf(st,m+1))!==-1;)c.push({type:7,index:i}),m+=st.length-1;}i++;}}static createElement(r,t){let e=ct.createElement("template");return e.innerHTML=r,e}};function vt(o,r,t=o,e){if(r===z)return r;let s=e!==void 0?t._$Co?.[e]:t._$Cl,i=Gt(r)?void 0:r._$litDirective$;return s?.constructor!==i&&(s?._$AO?.(false),i===void 0?s=void 0:(s=new i(o),s._$AT(o,t,e)),e!==void 0?(t._$Co??(t._$Co=[]))[e]=s:t._$Cl=s),s!==void 0&&(r=vt(o,s._$AS(o,r.values),s,e)),r}var Ie=class{constructor(r,t){this._$AV=[],this._$AN=void 0,this._$AD=r,this._$AM=t;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(r){let{el:{content:t},parts:e}=this._$AD,s=(r?.creationScope??ct).importNode(t,true);lt.currentNode=s;let i=lt.nextNode(),n=0,a=0,c=e[0];for(;c!==void 0;){if(n===c.index){let p;c.type===2?p=new Wt(i,i.nextSibling,this,r):c.type===1?p=new c.ctor(i,c.name,c.strings,this,r):c.type===6&&(p=new Oe(i,this,r)),this._$AV.push(p),c=e[++a];}n!==c?.index&&(i=lt.nextNode(),n++);}return lt.currentNode=ct,s}p(r){let t=0;for(let e of this._$AV)e!==void 0&&(e.strings!==void 0?(e._$AI(r,e,t),t+=e.strings.length-2):e._$AI(r[t])),t++;}},Wt=class o{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(r,t,e,s){this.type=2,this._$AH=d,this._$AN=void 0,this._$AA=r,this._$AB=t,this._$AM=e,this.options=s,this._$Cv=s?.isConnected??true;}get parentNode(){let r=this._$AA.parentNode,t=this._$AM;return t!==void 0&&r?.nodeType===11&&(r=t.parentNode),r}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(r,t=this){r=vt(this,r,t),Gt(r)?r===d||r==null||r===""?(this._$AH!==d&&this._$AR(),this._$AH=d):r!==this._$AH&&r!==z&&this._(r):r._$litType$!==void 0?this.$(r):r.nodeType!==void 0?this.T(r):jr(r)?this.k(r):this._(r);}O(r){return this._$AA.parentNode.insertBefore(r,this._$AB)}T(r){this._$AH!==r&&(this._$AR(),this._$AH=this.O(r));}_(r){this._$AH!==d&&Gt(this._$AH)?this._$AA.nextSibling.data=r:this.T(ct.createTextNode(r)),this._$AH=r;}$(r){let{values:t,_$litType$:e}=r,s=typeof e=="number"?this._$AC(r):(e.el===void 0&&(e.el=Bt.createElement(Cs(e.h,e.h[0]),this.options)),e);if(this._$AH?._$AD===s)this._$AH.p(t);else {let i=new Ie(s,this),n=i.u(this.options);i.p(t),this.T(n),this._$AH=i;}}_$AC(r){let t=Ts.get(r.strings);return t===void 0&&Ts.set(r.strings,t=new Bt(r)),t}k(r){Ne(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,e,s=0;for(let i of r)s===t.length?t.push(e=new o(this.O(Ft()),this.O(Ft()),this,this.options)):e=t[s],e._$AI(i),s++;s<t.length&&(this._$AR(e&&e._$AB.nextSibling,s),t.length=s);}_$AR(r=this._$AA.nextSibling,t){for(this._$AP?.(false,true,t);r!==this._$AB;){let e=Es(r).nextSibling;Es(r).remove(),r=e;}}setConnected(r){this._$AM===void 0&&(this._$Cv=r,this._$AP?.(r));}},gt=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(r,t,e,s,i){this.type=1,this._$AH=d,this._$AN=void 0,this.element=r,this.name=t,this._$AM=s,this.options=i,e.length>2||e[0]!==""||e[1]!==""?(this._$AH=Array(e.length-1).fill(new String),this.strings=e):this._$AH=d;}_$AI(r,t=this,e,s){let i=this.strings,n=false;if(i===void 0)r=vt(this,r,t,0),n=!Gt(r)||r!==this._$AH&&r!==z,n&&(this._$AH=r);else {let a=r,c,p;for(r=i[0],c=0;c<i.length-1;c++)p=vt(this,a[e+c],t,c),p===z&&(p=this._$AH[c]),n||(n=!Gt(p)||p!==this._$AH[c]),p===d?r=d:r!==d&&(r+=(p??"")+i[c+1]),this._$AH[c]=p;}n&&!s&&this.j(r);}j(r){r===d?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,r??"");}},Le=class extends gt{constructor(){super(...arguments),this.type=3;}j(r){this.element[this.name]=r===d?void 0:r;}},Me=class extends gt{constructor(){super(...arguments),this.type=4;}j(r){this.element.toggleAttribute(this.name,!!r&&r!==d);}},ke=class extends gt{constructor(r,t,e,s,i){super(r,t,e,s,i),this.type=5;}_$AI(r,t=this){if((r=vt(this,r,t,0)??d)===z)return;let e=this._$AH,s=r===d&&e!==d||r.capture!==e.capture||r.once!==e.once||r.passive!==e.passive,i=r!==d&&(e===d||s);s&&this.element.removeEventListener(this.name,this,e),i&&this.element.addEventListener(this.name,this,r),this._$AH=r;}handleEvent(r){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,r):this._$AH.handleEvent(r);}},Oe=class{constructor(r,t,e){this.element=r,this.type=6,this._$AN=void 0,this._$AM=t,this.options=e;}get _$AU(){return this._$AM._$AU}_$AI(r){vt(this,r);}};var Yr=Ut.litHtmlPolyfillSupport;Yr?.(Bt,Wt),(Ut.litHtmlVersions??(Ut.litHtmlVersions=[])).push("3.3.2");var Is=(o,r,t)=>{let e=t?.renderBefore??r,s=e._$litPart$;if(s===void 0){let i=t?.renderBefore??null;e._$litPart$=s=new Wt(r.insertBefore(Ft(),i),i,void 0,t??{});}return s._$AI(o),s};var jt=globalThis,E=class extends K{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0;}createRenderRoot(){var t;let r=super.createRenderRoot();return (t=this.renderOptions).renderBefore??(t.renderBefore=r.firstChild),r}update(r){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(r),this._$Do=Is(t,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return z}};E._$litElement$=true,E.finalized=true,jt.litElementHydrateSupport?.({LitElement:E});var Xr=jt.litElementPolyfillSupport;Xr?.({LitElement:E});(jt.litElementVersions??(jt.litElementVersions=[])).push("4.2.2");var _=o=>(r,t)=>{t!==void 0?t.addInitializer(()=>{customElements.define(o,r);}):customElements.define(o,r);};var Vr={attribute:true,type:String,converter:Ht,reflect:false,hasChanged:le},Kr=(o=Vr,r,t)=>{let{kind:e,metadata:s}=t,i=globalThis.litPropertyMetadata.get(s);if(i===void 0&&globalThis.litPropertyMetadata.set(s,i=new Map),e==="setter"&&((o=Object.create(o)).wrapped=true),i.set(t.name,o),e==="accessor"){let{name:n}=t;return {set(a){let c=r.get.call(this);r.set.call(this,a),this.requestUpdate(n,c,o,true,a);},init(a){return a!==void 0&&this.C(n,void 0,o,a),a}}}if(e==="setter"){let{name:n}=t;return function(a){let c=this[n];r.call(this,a),this.requestUpdate(n,c,o,true,a);}}throw Error("Unsupported decorator location: "+e)};function L(o){return (r,t)=>typeof t=="object"?Kr(o,r,t):((e,s,i)=>{let n=s.hasOwnProperty(i);return s.constructor.createProperty(i,e),n?Object.getOwnPropertyDescriptor(s,i):void 0})(o,r,t)}function b(o){return L({...o,state:true,attribute:false})}var Qt=class extends E{constructor(){super(...arguments);this.method="";}createRenderRoot(){return this}render(){let t=this.method.toUpperCase();return l`<span class="method-badge method-badge-${t}">${t}</span>`}};u([L()],Qt.prototype,"method",2),Qt=u([_("bk-method-badge")],Qt);var q="/__brakit/api",U="/__brakit",T={flows:`${q}/flows`,requests:`${q}/requests`,events:`${q}/events`,clear:`${q}/clear`,fetches:`${q}/fetches`,errors:`${q}/errors`,logs:`${q}/logs`,queries:`${q}/queries`,metricsLive:`${q}/metrics/live`,insights:`${q}/insights`,tab:`${q}/tab`,activity:`${q}/activity`,graph:`${q}/graph`};var bt="polling",pe="static",zr="auth-handshake",Jr="auth-check",Zr="middleware",Yt={[zr]:1,[Jr]:1,[Zr]:1};var he="fetch";var ue="error_event",me="query",fe="issues";var Pe={overview:"Overview",actions:"Actions",insights:"Insights",performance:"Performance",graph:"Graph",explorer:"Explorer",requests:"Requests",fetches:"Server Fetches",queries:"Queries",errors:"Errors",logs:"Logs",security:"Security"},He={overview:"Live summary of your application",actions:"User actions captured as sequences of HTTP requests",insights:"Security findings, error patterns, and issue tracking",performance:"Endpoint health and response time trends",graph:"Runtime dependency graph of your application",explorer:"Browse raw requests, fetches, queries, logs, and errors",requests:"All HTTP requests proxied through brakit",fetches:"Outbound HTTP calls made by your server to external services",queries:"Database queries executed during request handling",errors:"Unhandled exceptions and errors thrown by your application",logs:"Console output from your application",security:"Security findings and recommendations"},ve=[{key:"requests",label:"Requests"},{key:"fetches",label:"Fetches"},{key:"queries",label:"Queries"},{key:"logs",label:"Logs"},{key:"errors",label:"Errors"}];var Ls=new Set(["exposed-secret","token-in-url","stack-trace-leak","error-info-leak","insecure-cookie","sensitive-logs","cors-credentials","response-pii-leak"]),Ms=new Set(["slow-endpoint","n1","high-query-count"]);function Xt(o){let r=o.issue.rule;return Ls.has(r)?"security":Ms.has(r)?"performance":"reliability"}var N={CLEAR_CONFIRM:"This will clear all data including performance metrics history. Continue?",CLEARED_TOAST:"Cleared",EMPTY_TITLE:"Waiting for requests...",EMPTY_SUBTITLE_OVERVIEW:"Start using your app to see your dashboard here",ALL_CLEAR:"All clear",ALL_CLEAR_DETAIL:"No issues detected",NO_ACTIONS:"No actions captured yet",NO_REQUEST_DATA:"No request data yet",ALL_FAST:"All fast",NO_SLOW_ENDPOINTS:"No slow endpoints detected",ZERO_ERRORS:"0 errors",ALL_REQUESTS_OK:"All requests successful",BUILD_GRAPH:"Navigate your app to build the graph",REVIEW_RECOMMENDED:"review recommended"};var Fe=100,yt=300,$t=800,Ge=2e3,Be=100,We=50,je=500;var rt="__all__",ti={SELECT:"var(--blue)",INSERT:"var(--green)",UPDATE:"var(--amber)",DELETE:"var(--red)",OTHER:"var(--text-muted)"},be=ti,ei={error:"var(--red)",warn:"var(--amber)",info:"var(--blue)",debug:"var(--text-muted)",log:"var(--text-dim)"},qs=ei,Xe=["#2563eb","#7c3aed","#16a34a","#d97706","#dc2626","#0891b2","#ea580c","#c026d3","#059669","#db2777"],Vt={green:"#4ade80",amber:"#fbbf24",red:"#f87171"},xt=[{max:Fe,label:"Fast",color:"var(--green)",bg:"var(--green-bg)",border:"var(--green-border)"},{max:yt,label:"Good",color:"var(--green)",bg:"var(--green-bg-subtle)",border:"var(--green-border-subtle)"},{max:$t,label:"OK",color:"var(--amber)",bg:"var(--amber-bg)",border:"var(--amber-border)"},{max:Ge,label:"Slow",color:"var(--red)",bg:"var(--red-bg)",border:"var(--red-border)"},{max:1/0,label:"Critical",color:"var(--red)",bg:"var(--red-bg)",border:"var(--red-border)"}],Us="rgba(228,228,231,0.8)",Ve="rgba(113,113,122,0.7)",Fs="10px monospace",Ke="9px monospace";var Gs={top:16,right:16,bottom:28,left:52},Bs={fetch:"var(--blue)",log:"var(--text-muted)",error:"var(--red)",query:"var(--accent)"},Ws={fetch:"FETCH",log:"LOG",error:"ERROR",query:"QUERY"},js=new Set(["cookie","set-cookie","authorization","proxy-authorization","x-api-key","x-auth-token"]),Qs={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",408:"Timeout",409:"Conflict",422:"Unprocessable",429:"Too Many Requests",500:"Internal Server Error",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout"},Ys=new Set(["host","connection","accept-encoding"]),Y={critical:{icon:"\u2717",cls:"critical",sort:0},warning:{icon:"\u26A0",cls:"warning",sort:1},info:{icon:"\u2139",cls:"info",sort:2}};function y(o){return o<1e3?o+"ms":(o/1e3).toFixed(1)+"s"}function ot(o){return !o||o===0?"":o<1024?o+"b":(o/1024).toFixed(1)+"kb"}function it(o){return o?o.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',"""):""}function St(o){return o>=500?"status-pill-5xx":o>=400?"status-pill-4xx":o>=300?"status-pill-3xx":"status-pill-2xx"}function Xs(o){return Qs[o]||(o>=500?"Server Error":o>=400?"Client Error":"OK")}function si(o,r){if(js.has(o.toLowerCase())){let t=String(r);return t.length<=8?"****":t.slice(0,4)+"..."+t.slice(-4)+" ("+t.length+" chars)"}return String(r)}function Tt(o){return !o||Object.keys(o).length===0?'<span style="color:var(--text-muted)">No headers</span>':Object.entries(o).map(([r,t])=>'<span class="json-key">'+it(r)+"</span>: "+it(si(r,t))).join(`
|
|
4
|
+
`)}function dt(o){if(!o)return '<span style="color:var(--text-muted)">No body</span>';try{let r=JSON.parse(o);return ri(JSON.stringify(r,null,2))}catch{return it(o)}}function ri(o){return it(o).replace(/("(?:[^"\\]|\\.)*")(\s*:)?|\b(true|false)\b|\bnull\b|(-?\d+\.?\d*(?:[eE][+-]?\d+)?)/g,(r,t,e,s,i)=>t?e?'<span class="json-key">'+t+"</span>"+e:'<span class="json-str">'+t+"</span>":s?'<span class="json-bool">'+r+"</span>":i?'<span class="json-num">'+r+"</span>":r==="null"?'<span class="json-null">null</span>':r)}var Kt=class extends E{constructor(){super(...arguments);this.code=0;}createRenderRoot(){return this}render(){let t=St(this.code);return l`<span class="status-pill ${t}">${this.code}</span>`}};u([L({type:Number})],Kt.prototype,"code",2),Kt=u([_("bk-status-pill")],Kt);var zt=class extends E{constructor(){super(...arguments);this.ms=0;}createRenderRoot(){return this}render(){return l`<span class="req-duration">${y(this.ms)}</span>`}};u([L({type:Number})],zt.prototype,"ms",2),zt=u([_("bk-duration-label")],zt);var wt=class extends E{constructor(){super(...arguments);this.title="";this.subtitle="";}createRenderRoot(){return this}render(){return l`
|
|
5
5
|
<div class="empty">
|
|
6
6
|
<span class="empty-title">${this.title}</span>
|
|
7
7
|
<span class="empty-sub">${this.subtitle}</span>
|
|
8
8
|
</div>
|
|
9
|
-
`}};u([L()],wt.prototype,"title",2),u([L()],wt.prototype,"subtitle",2),wt=u([
|
|
9
|
+
`}};u([L()],wt.prototype,"title",2),u([L()],wt.prototype,"subtitle",2),wt=u([_("bk-empty-state")],wt);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 l`<div class="toast ${this.visible?"show":""}">${this.message}</div>`}};u([b()],D.prototype,"message",2),u([b()],D.prototype,"visible",2),D=u([_("bk-toast")],D);var pt=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 l`<button class="query-detail-copy" @click=${this.copy}>${this.label}</button>`}};u([L()],pt.prototype,"text",2),u([L()],pt.prototype,"label",2),u([L({attribute:"toast-message"})],pt.prototype,"toastMessage",2),pt=u([_("bk-copy-button")],pt);var ht=class extends E{constructor(){super(...arguments);this.value="";this.label="";this.color="";}createRenderRoot(){return this}render(){return l`
|
|
10
10
|
<div class="fetch-stat">
|
|
11
11
|
<span class="fetch-stat-value" style="color:${this.color}">${this.value}</span>
|
|
12
12
|
<span class="fetch-stat-label">${this.label}</span>
|
|
13
13
|
</div>
|
|
14
|
-
`}};u([L()],ht.prototype,"value",2),u([L()],ht.prototype,"label",2),u([L()],ht.prototype,"color",2),ht=u([
|
|
14
|
+
`}};u([L()],ht.prototype,"value",2),u([L()],ht.prototype,"label",2),u([L()],ht.prototype,"color",2),ht=u([_("bk-stat-card")],ht);var nt=class extends Event{constructor(r,t,e,s){super("context-request",{bubbles:true,composed:true}),this.context=r,this.contextTarget=t,this.callback=e,this.subscribe=s??false;}};var Rt=class{constructor(r,t,e,s){if(this.subscribe=false,this.provided=false,this.value=void 0,this.t=(i,n)=>{this.unsubscribe&&(this.unsubscribe!==n&&(this.provided=false,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=i,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=true,this.callback&&this.callback(i,n)),this.unsubscribe=n;},this.host=r,t.context!==void 0){let i=t;this.context=i.context,this.callback=i.callback,this.subscribe=i.subscribe??false;}else this.context=t,this.callback=e,this.subscribe=s??false;this.host.addController(this);}hostConnected(){this.dispatchRequest();}hostDisconnected(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=void 0);}dispatchRequest(){this.host.dispatchEvent(new nt(this.context,this.host,this.t,this.subscribe));}};var Ee=class{get value(){return this.o}set value(r){this.setValue(r);}setValue(r,t=false){let e=t||!Object.is(r,this.o);this.o=r,e&&this.updateObservers();}constructor(r){this.subscriptions=new Map,this.updateObservers=()=>{for(let[t,{disposer:e}]of this.subscriptions)t(this.o,e);},r!==void 0&&(this.value=r);}addCallback(r,t,e){if(!e)return void r(this.value);this.subscriptions.has(r)||this.subscriptions.set(r,{disposer:()=>{this.subscriptions.delete(r);},consumerHost:t});let{disposer:s}=this.subscriptions.get(r);r(this.value,s);}clearCallbacks(){this.subscriptions.clear();}};var ze=class extends Event{constructor(r,t){super("context-provider",{bubbles:true,composed:true}),this.context=r,this.contextTarget=t;}},At=class extends Ee{constructor(r,t,e){super(t.context!==void 0?t.initialValue:e),this.onContextRequest=s=>{if(s.context!==this.context)return;let i=s.contextTarget??s.composedPath()[0];i!==this.host&&(s.stopPropagation(),this.addCallback(s.callback,i,s.subscribe));},this.onProviderRequest=s=>{if(s.context!==this.context||(s.contextTarget??s.composedPath()[0])===this.host)return;let i=new Set;for(let[n,{consumerHost:a}]of this.subscriptions)i.has(n)||(i.add(n),a.dispatchEvent(new nt(this.context,a,n,true)));s.stopPropagation();},this.host=r,t.context!==void 0?this.context=t.context:this.context=t,this.attachListeners(),this.host.addController?.(this);}attachListeners(){this.host.addEventListener("context-request",this.onContextRequest),this.host.addEventListener("context-provider",this.onProviderRequest);}hostConnected(){this.host.dispatchEvent(new ze(this.context,this.host));}};function Je({context:o}){return (r,t)=>{let e=new WeakMap;if(typeof t=="object")return {get(){return r.get.call(this)},set(s){return e.get(this).setValue(s),r.set.call(this,s)},init(s){return e.set(this,new At(this,{context:o,initialValue:s})),s}};{r.constructor.addInitializer((n=>{e.set(n,new At(n,{context:o}));}));let s=Object.getOwnPropertyDescriptor(r,t),i;if(s===void 0){let n=new WeakMap;i={get(){return n.get(this)},set(a){e.get(this).setValue(a),n.set(this,a);},configurable:true,enumerable:true};}else {let n=s.set;i={...s,set(a){e.get(this).setValue(a),n?.call(this,a);}};}return void Object.defineProperty(r,t,i)}}}function C({context:o,subscribe:r}){return (t,e)=>{typeof e=="object"?e.addInitializer((function(){new Rt(this,{context:o,callback:s=>{t.set.call(this,s);},subscribe:r});})):t.constructor.addInitializer((s=>{new Rt(s,{context:o,callback:i=>{s[e]=i;},subscribe:r});}));}}var A="dashboard-store",ye=class extends EventTarget{constructor(){super(...arguments);this._state={flows:[],requests:[],fetches:[],errors:[],logs:[],queries:[],issues:[],metrics:[],viewMode:"simple",activeView:"overview"};}get state(){return this._state}setState(t,e){this._state={...this._state,[t]:e},this.notify(t);}setFlows(t){this.setState("flows",t);}setRequests(t){this.setState("requests",t);}setFetches(t){this.setState("fetches",t);}setErrors(t){this.setState("errors",t);}setLogs(t){this.setState("logs",t);}setQueries(t){this.setState("queries",t);}setIssues(t){this.setState("issues",t);}setMetrics(t){this.setState("metrics",t);}prependRequest(t){let e=[t,...this._state.requests.slice(0,999)];this._state={...this._state,requests:e},this.notify("requests");}prependFetch(t){let e=[t,...this._state.fetches.slice(0,999)];this._state={...this._state,fetches:e},this.notify("fetches");}prependError(t){let e=[t,...this._state.errors.slice(0,999)];this._state={...this._state,errors:e},this.notify("errors");}prependLog(t){let e=[t,...this._state.logs.slice(0,999)];this._state={...this._state,logs:e},this.notify("logs");}prependQuery(t){let e=[t,...this._state.queries.slice(0,999)];this._state={...this._state,queries:e},this.notify("queries");}setActiveView(t){this.setState("activeView",t);}setViewMode(t){this.setState("viewMode",t);}clearAll(){this._state={...this._state,flows:[],requests:[],fetches:[],errors:[],logs:[],queries:[],issues:[],metrics:[]},this.notify("all");}notify(t){this.dispatchEvent(new CustomEvent("state-changed",{detail:t}));}};var Ct=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 s=new Date(t.timestamp).toLocaleTimeString(),i=this.expandedIdx===e;return l`
|
|
15
15
|
<div
|
|
16
16
|
class="req-row tel-clickable ${i?"expanded":""}"
|
|
17
17
|
@click=${()=>this.toggleError(e)}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<span class="tel-message" title=${t.message}>${t.message}</span>
|
|
21
21
|
<span class="tel-timestamp">${s}</span>
|
|
22
22
|
</div>
|
|
23
|
-
${i&&t.stack?l`<div class="error-stack">${t.stack}</div>`:
|
|
23
|
+
${i&&t.stack?l`<div class="error-stack">${t.stack}</div>`:d}
|
|
24
24
|
`}render(){let t=this.store.state.errors;return t.length===0?l`<bk-empty-state
|
|
25
25
|
title="No errors"
|
|
26
26
|
subtitle="No errors have been captured yet"
|
|
@@ -33,15 +33,15 @@
|
|
|
33
33
|
<div id="error-list">
|
|
34
34
|
${t.map((e,s)=>this.renderErrorRow(e,s))}
|
|
35
35
|
</div>
|
|
36
|
-
`}};u([
|
|
36
|
+
`}};u([C({context:A})],Ct.prototype,"store",2),u([b()],Ct.prototype,"expandedIdx",2),Ct=u([_("bk-errors-view")],Ct);var Jt=class extends E{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}renderAnalysis(r){if(r.length===0)return d;let t={error:0,warn:0,info:0,debug:0,log:0};for(let e of r)t[e.level]!==void 0&&t[e.level]++;return l`
|
|
37
37
|
<div id="log-analysis">
|
|
38
38
|
<div class="fetch-summary">
|
|
39
39
|
<bk-stat-card value=${String(r.length)} label="Total Logs"></bk-stat-card>
|
|
40
|
-
${t.error>0?l`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:
|
|
41
|
-
${t.warn>0?l`<bk-stat-card value=${String(t.warn)} label="Warnings" color="var(--amber)"></bk-stat-card>`:
|
|
40
|
+
${t.error>0?l`<bk-stat-card value=${String(t.error)} label="Errors" color="var(--red)"></bk-stat-card>`:d}
|
|
41
|
+
${t.warn>0?l`<bk-stat-card value=${String(t.warn)} label="Warnings" color="var(--amber)"></bk-stat-card>`:d}
|
|
42
42
|
<bk-stat-card value=${String(t.info)} label="Info"></bk-stat-card>
|
|
43
|
-
${t.debug>0?l`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:
|
|
44
|
-
${t.log>0?l`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:
|
|
43
|
+
${t.debug>0?l`<bk-stat-card value=${String(t.debug)} label="Debug"></bk-stat-card>`:d}
|
|
44
|
+
${t.log>0?l`<bk-stat-card value=${String(t.log)} label="Log"></bk-stat-card>`:d}
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
47
|
`}renderLogRow(r){let t=new Date(r.timestamp).toLocaleTimeString();return l`
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
<div id="log-list">
|
|
64
64
|
${r.map(t=>this.renderLogRow(t))}
|
|
65
65
|
</div>
|
|
66
|
-
`}};u([
|
|
66
|
+
`}};u([C({context:A})],Jt.prototype,"store",2),Jt=u([_("bk-logs-view")],Jt);var Vs={CHILD:2},Ks=o=>(...r)=>({_$litDirective$:o,values:r}),$e=class{constructor(r){}get _$AU(){return this._$AM._$AU}_$AT(r,t,e){this._$Ct=r,this._$AM=t,this._$Ci=e;}_$AS(r,t){return this.update(r,t)}update(r,t){return this.render(...t)}};var Zt=class extends $e{constructor(r){if(super(r),this.it=d,r.type!==Vs.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(r){if(r===d||r==null)return this._t=void 0,this.it=r;if(r===z)return r;if(typeof r!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(r===this.it)return this._t;this.it=r;let t=[r];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}};Zt.directiveName="unsafeHTML",Zt.resultType=1;var G=Ks(Zt);var oi=new Set(["SELECT","FROM","WHERE","AND","OR","INSERT","INTO","VALUES","UPDATE","SET","DELETE","JOIN","LEFT","RIGHT","INNER","OUTER","ON","GROUP","BY","ORDER","HAVING","LIMIT","OFFSET","AS","IN","NOT","NULL","IS","LIKE","BETWEEN","EXISTS","CASE","WHEN","THEN","ELSE","END","COUNT","SUM","AVG","MIN","MAX","DISTINCT","UNION","ALL","CREATE","TABLE","ALTER","DROP","INDEX","RETURNING","WITH","RECURSIVE","OVER","PARTITION","WINDOW","FETCH","NEXT","ROWS","ONLY","CAST","COALESCE","NULLIF","EXTRACT","INTERVAL","TRUE","FALSE","ASC","DESC","USING","NATURAL","CROSS","FULL","ROLLBACK","COMMIT","BEGIN","TRANSACTION","SAVEPOINT","RELEASE"]);function zs(o){let r=o.trimStart().split(/\s/)[0];return r?r.toUpperCase():"?"}function Js(o){let r=o.replace(/\s+/g," ").trim(),t=r.match(/\bFROM\s+["'`]?(\w+)["'`]?/i);if(t)return t[1];let e=r.match(/\bINTO\s+["'`]?(\w+)["'`]?/i);if(e)return e[1];let s=r.match(/\bUPDATE\s+["'`]?(\w+)["'`]?/i);return s?s[1]:""}function Zs(o){return it(o).replace(/\b\w+\b/g,r=>oi.has(r.toUpperCase())?'<span class="sql-kw">'+r+"</span>":r)}var It=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":y(t)}getQueryInfo(t){let e=(t.normalizedOp||t.operation||(t.sql?zs(t.sql):"?")).toUpperCase(),s=t.table||t.model||(t.sql?Js(t.sql):""),i=t.sql||e+" "+s;return {op:e,table:s,sqlText:i}}renderQueryRow(t,e){let{op:s,table:i,sqlText:n}=this.getQueryInfo(t),a=be[s]||"var(--text-dim)",c=t.durationMs>Be,p=t.sql||s+" "+i,h=this.expandedIdx===e;return l`
|
|
67
67
|
<div>
|
|
68
68
|
<div
|
|
69
69
|
class="req-row query-row tel-clickable ${h?"expanded":""}"
|
|
@@ -71,14 +71,14 @@
|
|
|
71
71
|
>
|
|
72
72
|
<span class="query-op" title=${s} style="color:${a}">${s}</span>
|
|
73
73
|
<span class="query-table" title=${i}>${i}</span>
|
|
74
|
-
<span class="query-preview" title=${
|
|
74
|
+
<span class="query-preview" title=${p}>${p}</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?l`
|
|
79
79
|
<pre class="query-detail-sql">${G(Zs(n))}</pre>
|
|
80
80
|
<bk-copy-button .text=${n} label="Copy"></bk-copy-button>
|
|
81
|
-
`:
|
|
81
|
+
`:d}
|
|
82
82
|
</div>
|
|
83
83
|
</div>
|
|
84
84
|
`}render(){let t=this.store.state.queries;return t.length===0?l`<bk-empty-state
|
|
@@ -94,12 +94,12 @@
|
|
|
94
94
|
<div id="query-list">
|
|
95
95
|
${t.map((e,s)=>this.renderQueryRow(e,s))}
|
|
96
96
|
</div>
|
|
97
|
-
`}};u([
|
|
97
|
+
`}};u([C({context:A})],It.prototype,"store",2),u([b()],It.prototype,"expandedIdx",2),It=u([_("bk-queries-view")],It);function Ze(o){return o.replaceAll("'","'\\''")}function ni(o,r){let t=Object.entries(o.headers||{}).filter(([i])=>!Ys.has(i)).map(([i,n])=>`-H '${Ze(i)}: ${Ze(n)}'`).join(" "),e=o.requestBody?` -d '${Ze(o.requestBody)}'`:"",s=r?`http://localhost:${r}`:"";return `curl -X ${o.method} ${t}${e} '${s}${o.url}'`}function Lt(o){let r=window.__BRAKIT_CONFIG__?.port??"",t=ni(o,r);navigator.clipboard.writeText(t).then(()=>D.show("Copied cURL command"));}var Mt=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(),Lt(t);}renderDetail(t){return l`
|
|
98
98
|
<div class="detail-meta">
|
|
99
99
|
<span><bk-method-badge .method=${t.method}></bk-method-badge> ${t.url}</span>
|
|
100
100
|
<span><bk-status-pill .code=${t.statusCode}></bk-status-pill></span>
|
|
101
101
|
<span>${t.durationMs}ms</span>
|
|
102
|
-
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:
|
|
102
|
+
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:d}
|
|
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">
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
<span class="req-size">${ot(t.responseSize)}</span>
|
|
122
122
|
</div>
|
|
123
123
|
</div>
|
|
124
|
-
<div class="req-detail ${e?"open":""}">${e?this.renderDetail(t):
|
|
124
|
+
<div class="req-detail ${e?"open":""}">${e?this.renderDetail(t):d}</div>
|
|
125
125
|
`}render(){let t=this.store.state.requests.filter(e=>!e.path?.startsWith(U));return t.length===0?l`<bk-empty-state title="No requests" subtitle="No HTTP requests have been captured yet"></bk-empty-state>`:l`
|
|
126
126
|
<div class="col-header">
|
|
127
127
|
<span style="width:60px">Method</span>
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
<span style="width:60px;text-align:right">Size</span>
|
|
132
132
|
</div>
|
|
133
133
|
<div id="request-list">${t.map(e=>this.renderRequestRow(e))}</div>
|
|
134
|
-
`}};u([
|
|
134
|
+
`}};u([C({context:A})],Mt.prototype,"store",2),u([b()],Mt.prototype,"expandedId",2),Mt=u([_("bk-requests-view")],Mt);var te=class extends E{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}buildGroups(r,t){let e=new Map;for(let i of t)e.set(i.id,i);let s={};for(let i of r){let n=i.method+" "+i.url;s[n]||(s[n]={method:i.method,url:i.url,count:0,totalDur:0,maxDur:0,errors:0,callers:{},statusCodes:{},firstTs:i.timestamp,lastTs:i.timestamp});let a=s[n];if(a.count++,a.totalDur+=i.durationMs,i.durationMs>a.maxDur&&(a.maxDur=i.durationMs),i.statusCode>=400&&a.errors++,a.statusCodes[i.statusCode]=(a.statusCodes[i.statusCode]||0)+1,i.timestamp<a.firstTs&&(a.firstTs=i.timestamp),i.timestamp>a.lastTs&&(a.lastTs=i.timestamp),i.parentRequestId){let c=e.get(i.parentRequestId);c&&(a.callers[c.method+" "+(c.path||c.url)]=1);}}return Object.values(s).sort((i,n)=>n.count-i.count)}renderSummary(r){let t=new Set,e=0,s=0;for(let n of r)t.add(n.url),n.statusCode>=400&&e++,s+=n.durationMs;let i=Math.round(s/r.length);return l`
|
|
135
135
|
<div class="fetch-summary">
|
|
136
136
|
<bk-stat-card value=${String(r.length)} label="Total Fetches"></bk-stat-card>
|
|
137
137
|
<bk-stat-card value=${String(t.size)} label="Unique URLs"></bk-stat-card>
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
<div class="fetch-group-header">
|
|
144
144
|
<bk-method-badge .method=${r.method}></bk-method-badge>
|
|
145
145
|
<span class="fetch-group-url" title=${r.url}>${r.url}</span>
|
|
146
|
-
${n>0?l`<bk-status-pill .code=${n}></bk-status-pill>`:
|
|
146
|
+
${n>0?l`<bk-status-pill .code=${n}></bk-status-pill>`:d}
|
|
147
147
|
<span class="fetch-group-count">${r.count}x</span>
|
|
148
148
|
</div>
|
|
149
149
|
<div class="fetch-group-meta">
|
|
@@ -157,14 +157,14 @@
|
|
|
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(r.firstTs)}${r.firstTs!==r.lastTs?l` \u2192 ${this.formatTime(r.lastTs)}`:
|
|
160
|
+
${this.formatTime(r.firstTs)}${r.firstTs!==r.lastTs?l` \u2192 ${this.formatTime(r.lastTs)}`:d}
|
|
161
161
|
</span>
|
|
162
|
-
</div>`:
|
|
162
|
+
</div>`:d}
|
|
163
163
|
${s.length>0?l`
|
|
164
164
|
<div class="fetch-group-callers">
|
|
165
165
|
<span class="fetch-group-callers-label">Called by</span>
|
|
166
166
|
${s.map(a=>l`<span class="fetch-group-caller-pill">${a}</span>`)}
|
|
167
|
-
</div>`:
|
|
167
|
+
</div>`:d}
|
|
168
168
|
</div>
|
|
169
169
|
`}render(){let r=this.store.state.fetches,t=this.store.state.requests;if(r.length===0)return l`<bk-empty-state title="No fetches" subtitle="No outbound HTTP calls have been captured yet"></bk-empty-state>`;let e=this.buildGroups(r,t);return l`
|
|
170
170
|
<div class="fetch-analysis" id="fetch-analysis">
|
|
@@ -172,9 +172,9 @@
|
|
|
172
172
|
${e.length>0?l`
|
|
173
173
|
<div class="fetch-groups-title">Grouped by URL (${e.length})</div>
|
|
174
174
|
<div class="fetch-groups">${e.map(s=>this.renderGroup(s))}</div>
|
|
175
|
-
`:
|
|
175
|
+
`:d}
|
|
176
176
|
</div>
|
|
177
|
-
`}};u([
|
|
177
|
+
`}};u([C({context:A})],te.prototype,"store",2),te=u([_("bk-fetches-view")],te);function tr(o,r){if(r>=400)return "var(--red)";switch(o){case "GET":return "var(--green)";case "POST":return "var(--blue)";case "PUT":case "PATCH":return "var(--amber)";case "DELETE":return "var(--red)";default:return "var(--text-muted)"}}function ts(o){return o==="query"?"var(--accent)":"var(--cyan)"}function di(o){return o.type==="query"||o.type==="fetch"}function pi(o){let r=(o.normalizedOp||o.operation||"QUERY").toUpperCase(),t=o.table||o.model||"";return {label:`${r} ${t}`,tooltip:o.sql||`${r} ${t}`}}function hi(o){return {label:`${o.method} ${o.url}`,tooltip:`${o.method} ${o.url}`}}function ui(o,r,t,e,s,i){let n=o.data.durationMs||0,a,c;if(i){let p=Math.max(o.timestamp-e,0);a=Math.min(p/s*100,95),c=Math.max(n/s*100,1.5);}else {let p=t[0].timestamp,m=t[t.length-1].timestamp-p;a=m>0?(o.timestamp-p)/m*85:r/Math.max(t.length-1,1)*85,c=Math.max(n/s*100,1.5);}return a+c>100&&(c=Math.max(100-a,1.5)),{leftPct:a,widthPct:c}}function mi(o,r){let t=o.timeline.filter(di);if(t.length===0)return [];let e=r.startedAt,s=r.durationMs||1,i=Math.abs(t[0].timestamp-e)<s*10;return t.map((n,a)=>{let c=n.data.durationMs||0,{leftPct:p,widthPct:h}=ui(n,a,t,e,s,i),m=n.type==="query"?pi(n.data):hi(n.data);return {type:n.type,label:m.label,durMs:c,durLabel:y(c),tooltip:m.tooltip,leftPct:p,widthPct:h}})}function sr(o,r){let t=o.requests.filter(a=>!a.isStrictModeDupe);if(t.length===0)return {rows:[],totalMs:0};let e=Math.min(...t.map(a=>a.startedAt)),i=Math.max(...t.map(a=>a.startedAt+a.durationMs))-e;return i===0?{rows:[],totalMs:0}:{rows:t.map(a=>{let c=(a.startedAt-e)/i*100,p=Math.max(a.durationMs/i*100,.5),h=r?.activities?.[a.id],m=h?mi(h,a):[];return {label:`${a.method} ${a.label}`,leftPct:c,widthPct:p,color:tr(a.method,a.statusCode),durMs:a.durationMs,durLabel:y(a.durationMs),tooltip:`${a.method} ${a.label} (${y(a.durationMs)})`,subEvents:m}}),totalMs:i}}function rr(o){let r=o.requests,t=[],e=[],s=[],i=[],n=new Map;for(let p of r){let h=p.label,m=p.pollingDurationMs||p.durationMs;if(!Yt[p.category||""]){if(p.isDuplicate){let f=n.get(h);f?(f.count++,f.wastedMs+=m):n.set(h,{name:h,count:2,wastedMs:m});continue}if(p.statusCode>=400){e.push(h+" ("+Xs(p.statusCode)+")");continue}p.responseSize>51200&&s.push("Large response: "+h+" returned "+ot(p.responseSize)),t.push(h);}}for(let p of n.values())i.push(p);let a="";if(i.length>0){let p=i.map(m=>m.name).join(", "),h=i.reduce((m,f)=>m+f.wastedMs,0);a="Your app fetches "+p+" multiple times on this page. This wastes ~"+y(h)+". Try caching these calls, deduplicating with React Query/SWR, or moving them to a shared layout.";}else e.length>0&&(a="Some requests are failing. Check your API routes and make sure the endpoints exist.");let c=r.filter(p=>p.durationMs>2e3&&p.category!==bt);return c.length>0&&!a&&(a=c.map(p=>p.label).join(", ")+` is taking over ${y(2e3)}. Consider adding caching or optimizing the backend query.`),{successes:t,errors:e,warnings:s,duplicates:i,tip:a}}var X=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(s=>s.statusCode>=400).length;return {text:e+" error"+(e!==1?"s":""),cls:"badge-error"}}return t.redundancyPct>0?{text:t.redundancyPct+"% redundant",cls:"badge-warn"}:{text:"clean",cls:"badge-clean"}}toggleFlow(t){this.expandedFlowIdx===t?this.expandedFlowIdx=-1:(this.expandedFlowIdx=t,this.expandedSubReqIdx=-1,this.flowDetailTab="insights",this.flowTimeline=null);}toggleSubReq(t,e){e.stopPropagation(),this.expandedSubReqIdx=this.expandedSubReqIdx===t?-1:t;}toggleBodyBlock(t){t.stopPropagation();let e=t.currentTarget,s=e.parentElement;if(!s)return;e.classList.toggle("open");let i=s.querySelector("pre");i&&i.classList.toggle("open");}switchTab(t,e,s){s.stopPropagation(),this.flowDetailTab=t,t==="timeline"&&!this.flowTimeline&&this.loadFlowTimeline(e);}async loadFlowTimeline(t){if(this.flowTimelineLoading)return;let e=t.requests.map(s=>s.id).filter(Boolean);if(e.length!==0){this.flowTimelineLoading=true;try{let s=await fetch(`${T.activity}?requestIds=${e.join(",")}`);if(!s.ok){this.flowTimelineLoading=!1;return}this.flowTimeline=await s.json();}catch{}this.flowTimelineLoading=false;}}loadTimelineForContainer(t){let e=t.querySelectorAll(".request-timeline");for(let s of e){let i=s.getAttribute("data-request-id");if(i&&!s.hasAttribute("data-loaded")){s.setAttribute("data-loaded","1");let n=document.createElement("bk-timeline-panel");n.setAttribute("request-id",i),n.setAttribute("request-started",s.getAttribute("data-request-started")||"0"),s.appendChild(n),s.classList.remove("tl-hidden");}}}updated(){if(this.expandedFlowIdx>=0&&this.flowDetailTab==="insights"){let t=this.querySelector(".flow-expand.open");t&&this.loadTimelineForContainer(t);}}render(){let t=this.flows;return t.length===0?l`<bk-empty-state
|
|
178
178
|
title="No actions yet"
|
|
179
179
|
subtitle="Start using your app to see user action flows here"
|
|
180
180
|
></bk-empty-state>`:l`
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
</div>
|
|
208
208
|
</div>
|
|
209
209
|
<div class="flow-expand ${s?"open":""}">
|
|
210
|
-
${s?this.renderFlowDetail(t):
|
|
210
|
+
${s?this.renderFlowDetail(t):d}
|
|
211
211
|
</div>
|
|
212
212
|
`}renderFlowDetail(t){let e=this.viewMode==="simple"?"Insights":"Details";return l`
|
|
213
213
|
<div class="flow-detail-tabs">
|
|
@@ -225,7 +225,7 @@
|
|
|
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 l`<div class="wf-loading">Loading timeline...</div>`;let{rows:e,totalMs:s}=sr(t,this.flowTimeline);if(e.length===0)return
|
|
228
|
+
`}renderFlowWaterfall(t){if(this.flowTimelineLoading)return l`<div class="wf-loading">Loading timeline...</div>`;let{rows:e,totalMs:s}=sr(t,this.flowTimeline);if(e.length===0)return d;let i=[];for(let n=0;n<=5;n++)i.push(y(s/5*n));return l`
|
|
229
229
|
<div class="flow-waterfall">
|
|
230
230
|
<div class="wf-time-axis">
|
|
231
231
|
${i.map(n=>l`<span>${n}</span>`)}
|
|
@@ -263,7 +263,7 @@
|
|
|
263
263
|
</div>
|
|
264
264
|
<div class="wf-sub-dur">${e.durLabel}</div>
|
|
265
265
|
</div>
|
|
266
|
-
`):
|
|
266
|
+
`):d}
|
|
267
267
|
</div>
|
|
268
268
|
`}renderFlowInsights(t){let e=rr(t),s=e.errors.length>0||e.duplicates.length>0||e.warnings.length>0||!!e.tip;return l`
|
|
269
269
|
<div>
|
|
@@ -283,11 +283,11 @@
|
|
|
283
283
|
${e.warnings.map(i=>l`<div class="insight-line insight-warn">⚠ ${i}</div>`)}
|
|
284
284
|
${e.tip?l`<div class="insight-line insight-tip">
|
|
285
285
|
Tip: ${e.tip}
|
|
286
|
-
</div>`:
|
|
286
|
+
</div>`:d}
|
|
287
287
|
</div>
|
|
288
|
-
`:
|
|
288
|
+
`:d}
|
|
289
289
|
</div>
|
|
290
|
-
`}renderTrafficCard(t){if(Yt[t.category||""])return
|
|
290
|
+
`}renderTrafficCard(t){if(Yt[t.category||""])return d;let e=St(t.statusCode),s=y(t.pollingDurationMs||t.durationMs),i=!t.isDuplicate&&t.category!==pe&&t.category!==bt||t.requestBody&&t.method!=="GET"||!!t.responseBody;return l`
|
|
291
291
|
<div
|
|
292
292
|
class="traffic-card ${t.isStrictModeDupe?"strict-mode-dupe":""}"
|
|
293
293
|
>
|
|
@@ -304,14 +304,14 @@
|
|
|
304
304
|
</div>
|
|
305
305
|
${t.isStrictModeDupe?l`<div class="strict-mode-banner">
|
|
306
306
|
React Strict Mode duplicate — does not happen in production
|
|
307
|
-
</div>`:
|
|
308
|
-
${!t.isDuplicate&&t.category!==pe&&t.category!==
|
|
307
|
+
</div>`:d}
|
|
308
|
+
${!t.isDuplicate&&t.category!==pe&&t.category!==bt?l`<div
|
|
309
309
|
class="request-timeline tl-hidden"
|
|
310
310
|
data-request-id=${t.id}
|
|
311
311
|
data-request-started=${String(t.startedAt)}
|
|
312
|
-
></div>`:
|
|
313
|
-
${t.requestBody&&t.method!=="GET"?this.renderBodyToggle("out","Request Body",t.requestBody):
|
|
314
|
-
${t.responseBody?this.renderBodyToggle("in","Response Body",t.responseBody):
|
|
312
|
+
></div>`:d}
|
|
313
|
+
${t.requestBody&&t.method!=="GET"?this.renderBodyToggle("out","Request Body",t.requestBody):d}
|
|
314
|
+
${t.responseBody?this.renderBodyToggle("in","Response Body",t.responseBody):d}
|
|
315
315
|
</div>
|
|
316
316
|
`}renderBodyToggle(t,e,s){let i=t==="out"?"\u2192":"\u2190";return l`
|
|
317
317
|
<div class="traffic-body">
|
|
@@ -332,12 +332,12 @@
|
|
|
332
332
|
<span class="subreq-label ${t.isDuplicate?"is-dup":""}"
|
|
333
333
|
>${t.path||t.url}</span
|
|
334
334
|
>
|
|
335
|
-
${t.isDuplicate?l`<span class="subreq-dup-tag">duplicate</span>`:
|
|
335
|
+
${t.isDuplicate?l`<span class="subreq-dup-tag">duplicate</span>`:d}
|
|
336
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 ${s?"open":""}">
|
|
340
|
-
${s?this.renderSubReqDetail(t):
|
|
340
|
+
${s?this.renderSubReqDetail(t):d}
|
|
341
341
|
</div>
|
|
342
342
|
`}renderSubReqDetail(t){let e=St(t.statusCode);return l`
|
|
343
343
|
<div class="detail-meta">
|
|
@@ -348,7 +348,7 @@
|
|
|
348
348
|
><span class="status-pill ${e}">${t.statusCode}</span></span
|
|
349
349
|
>
|
|
350
350
|
<span>${t.durationMs}ms</span>
|
|
351
|
-
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:
|
|
351
|
+
${t.responseSize?l`<span>${ot(t.responseSize)}</span>`:d}
|
|
352
352
|
</div>
|
|
353
353
|
<div
|
|
354
354
|
class="request-timeline tl-hidden"
|
|
@@ -381,7 +381,7 @@
|
|
|
381
381
|
Copy cURL
|
|
382
382
|
</button>
|
|
383
383
|
</div>
|
|
384
|
-
`}};u([
|
|
384
|
+
`}};u([C({context:A})],X.prototype,"store",2),u([b()],X.prototype,"expandedFlowIdx",2),u([b()],X.prototype,"expandedSubReqIdx",2),u([b()],X.prototype,"flowDetailTab",2),u([b()],X.prototype,"flowTimeline",2),u([b()],X.prototype,"flowTimelineLoading",2),X=u([_("bk-flows-view")],X);var ee=class extends E{createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate());}render(){let r=(this.store.state.issues||[]).slice(),t=r.filter(a=>a.state==="open"||a.state==="fixing"||a.state==="regressed"),e=r.filter(a=>a.state==="resolved");if(t.length===0&&e.length===0)return this.store.state.requests.length>0||this.store.state.logs.length>0||this.store.state.queries.length>0?l`
|
|
385
385
|
<div class="sec-clear">
|
|
386
386
|
<span class="sec-clear-icon">\u2713</span>
|
|
387
387
|
<div class="sec-clear-text">
|
|
@@ -400,39 +400,39 @@
|
|
|
400
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
|
-
${e.length>0?this.renderResolved(e):
|
|
403
|
+
`:d}
|
|
404
|
+
${t.length>0?this.renderOpenGroups(t):d}
|
|
405
|
+
${e.length>0?this.renderResolved(e):d}
|
|
406
406
|
</div>
|
|
407
407
|
`}renderSummary(r,t,e,s,i){return l`
|
|
408
408
|
<div class="sec-summary">
|
|
409
409
|
<div class="sec-summary-left">
|
|
410
410
|
<span class="sec-summary-count">${r}</span>
|
|
411
411
|
<span class="sec-summary-label">open issue${r!==1?"s":""}</span>
|
|
412
|
-
${t>0?l`<span class="sec-resolved-badge">${t} resolved</span>`:
|
|
412
|
+
${t>0?l`<span class="sec-resolved-badge">${t} resolved</span>`:d}
|
|
413
413
|
</div>
|
|
414
414
|
<div class="sec-summary-right">
|
|
415
|
-
${e>0?l`<span class="sec-badge critical">${e} critical</span>`:
|
|
416
|
-
${s>0?l`<span class="sec-badge warning">${s} warning</span>`:
|
|
417
|
-
${i>0?l`<span class="sec-badge info">${i} info</span>`:
|
|
415
|
+
${e>0?l`<span class="sec-badge critical">${e} critical</span>`:d}
|
|
416
|
+
${s>0?l`<span class="sec-badge warning">${s} warning</span>`:d}
|
|
417
|
+
${i>0?l`<span class="sec-badge info">${i} info</span>`:d}
|
|
418
418
|
</div>
|
|
419
419
|
</div>
|
|
420
|
-
`}renderOpenGroups(r){let t={},e=[];for(let s of r){let i=s.issue,n=i.rule
|
|
420
|
+
`}renderOpenGroups(r){let t={},e=[];for(let s of r){let i=s.issue,n=i.rule;t[n]||(t[n]={rule:n,title:i.title,severity:i.severity,hint:i.hint,items:[]},e.push(n)),t[n].items.push(s);}return e.sort((s,i)=>{let n=Y[t[s].severity]?.sort??2,a=Y[t[i].severity]?.sort??2;return n!==a?n-a:t[i].items.length-t[s].items.length}),l`${e.map(s=>this.renderGroup(t[s]))}`}renderGroup(r){let t=Y[r.severity]||Y.info;return l`
|
|
421
421
|
<div class="sec-group">
|
|
422
422
|
<div class="sec-group-header">
|
|
423
423
|
<span class="sec-group-icon ${t.cls}">${t.icon}</span>
|
|
424
424
|
<span class="sec-group-title">${r.title}</span>
|
|
425
425
|
<span class="sec-group-count">${r.items.length}</span>
|
|
426
426
|
</div>
|
|
427
|
-
${r.hint?l`<div class="sec-hint">${r.hint}</div>`:
|
|
427
|
+
${r.hint?l`<div class="sec-hint">${r.hint}</div>`:d}
|
|
428
428
|
<div class="sec-items">${r.items.map(e=>this.renderIssueItem(e))}</div>
|
|
429
429
|
</div>
|
|
430
430
|
`}renderIssueItem(r){let t=r.issue;return l`
|
|
431
431
|
<div class="sec-item">
|
|
432
432
|
<div class="sec-item-desc">${t.desc}</div>
|
|
433
|
-
${r.occurrences>1?l`<span class="sec-item-count">${r.occurrences}x</span>`:
|
|
434
|
-
${r.state==="fixing"&&r.aiStatus==="fixed"?l`<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>`:r.aiStatus==="wont_fix"?l`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:r.state==="regressed"?l`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:
|
|
435
|
-
${r.aiNotes?l`<div class="sec-ai-notes">${r.aiNotes}</div>`:
|
|
433
|
+
${r.occurrences>1?l`<span class="sec-item-count">${r.occurrences}x</span>`:d}
|
|
434
|
+
${r.state==="fixing"&&r.aiStatus==="fixed"?l`<span class="sec-ai-badge sec-ai-fixing">AI fixed \u2014 awaiting verification</span>`:r.aiStatus==="wont_fix"?l`<span class="sec-ai-badge sec-ai-wontfix">AI: won\u2019t fix</span>`:r.state==="regressed"?l`<span class="sec-ai-badge sec-ai-fixing" style="background:var(--red)">regressed</span>`:d}
|
|
435
|
+
${r.aiNotes?l`<div class="sec-ai-notes">${r.aiNotes}</div>`:d}
|
|
436
436
|
</div>
|
|
437
437
|
`}renderResolved(r){return l`
|
|
438
438
|
<div class="sec-resolved-title">
|
|
@@ -445,13 +445,13 @@
|
|
|
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"?l`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:
|
|
449
|
-
${t.aiNotes?l`<div class="sec-ai-notes">${t.aiNotes}</div>`:
|
|
448
|
+
${t.aiStatus==="fixed"?l`<span class="sec-ai-badge sec-ai-verified">Verified fix</span>`:d}
|
|
449
|
+
${t.aiNotes?l`<div class="sec-ai-notes">${t.aiNotes}</div>`:d}
|
|
450
450
|
</div>
|
|
451
451
|
`)}
|
|
452
452
|
</div>
|
|
453
453
|
</div>
|
|
454
|
-
`}};u([
|
|
454
|
+
`}};u([C({context:A})],ee.prototype,"store",2),ee=u([_("bk-security-view")],ee);var J=class extends E{constructor(){super(...arguments);this.handleStateChanged=()=>this.requestUpdate();}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",this.handleStateChanged);}disconnectedCallback(){super.disconnectedCallback(),this.store.removeEventListener("state-changed",this.handleStateChanged);}};u([C({context:A})],J.prototype,"store",2);async function j(o){let r=await fetch(o);if(!r.ok)throw new Error(`HTTP ${r.status}: ${o}`);return r.json()}function B(o,r,t){return o===1?r:t??`${r}s`}function ir(o){return o.state!=="stale"}function _e(o){return (o.state==="open"||o.state==="regressed")&&o.aiStatus!=="wont_fix"}var se=class extends J{constructor(){super(...arguments);this.graphSummary=null;}connectedCallback(){super.connectedCallback(),this.loadGraphSummary();}async loadGraphSummary(){try{let e=(await j(`${T.graph}?level=endpoints`)).nodes||[];this.graphSummary={endpoints:e.filter(s=>s.type==="endpoint").length,tables:e.filter(s=>s.type==="table").length,externals:e.filter(s=>s.type==="external").length};}catch{}}navigateTo(t){this.store.setActiveView(t);}navigateToExplorerErrors(){this.store.setActiveView("explorer"),window.dispatchEvent(new CustomEvent("navigate-explorer",{detail:"errors"}));}render(){let t=this.store.state,e=t.requests.filter(i=>!i.isStatic&&!i.isHealthCheck&&(!i.path||i.path.indexOf(U)!==0));return e.length>0||t.queries.length>0?l`
|
|
455
455
|
<div class="ov-container">
|
|
456
456
|
<div class="ov-grid">
|
|
457
457
|
${this.renderActionsCard(t.flows)}
|
|
@@ -462,9 +462,9 @@
|
|
|
462
462
|
</div>
|
|
463
463
|
</div>
|
|
464
464
|
`:l`<bk-empty-state
|
|
465
|
-
title="${
|
|
466
|
-
subtitle="${
|
|
467
|
-
></bk-empty-state>`}renderActionsCard(t){let e=t.length;if(e===0)return this.renderEmptyCard("\u25B6","Actions",
|
|
465
|
+
title="${N.EMPTY_TITLE}"
|
|
466
|
+
subtitle="${N.EMPTY_SUBTITLE_OVERVIEW}"
|
|
467
|
+
></bk-empty-state>`}renderActionsCard(t){let e=t.length;if(e===0)return this.renderEmptyCard("\u25B6","Actions",N.NO_ACTIONS,"actions");let s=Math.round(t.reduce((n,a)=>n+a.totalDurationMs,0)/e),i=t[0]?.label||"";return this.renderCard("\u25B6",`${e} ${B(e,"action")}`,`avg ${y(s)}${i?` \xB7 "${i}"`:""}`,"actions","var(--accent)")}renderInsightsCard(t){let e=(t||[]).filter(_e);if(e.length===0)return this.renderCard("\u2713",N.ALL_CLEAR,N.ALL_CLEAR_DETAIL,"insights","var(--green)");let s=e.filter(a=>a.issue.severity==="critical").length,i=e.filter(a=>a.issue.severity==="warning").length,n=[];return s>0&&n.push(`${s} critical`),i>0&&n.push(`${i} warning`),this.renderCard("\u{1F4A1}",`${e.length} ${B(e.length,"issue")}`,n.join(" \xB7 ")||N.REVIEW_RECOMMENDED,"insights","var(--amber)")}renderPerformanceCard(t){if(t.length===0)return this.renderEmptyCard("\u26A1","Performance",N.NO_REQUEST_DATA,"performance");let e=new Map;for(let n of t){let a=`${n.method} ${n.path}`,c=e.get(a);c||(c=[],e.set(a,c)),c.push(n.durationMs);}let s=[];for(let[n,a]of e){let c=Math.round(a.reduce((p,h)=>p+h,0)/a.length);c>2e3&&s.push({key:n,avg:c});}if(s.sort((n,a)=>a.avg-n.avg),s.length===0)return this.renderCard("\u26A1",N.ALL_FAST,N.NO_SLOW_ENDPOINTS,"performance","var(--green)");let i=s[0];return this.renderCard("\u26A1",`${s.length} slow ${B(s.length,"endpoint")}`,`worst: ${i.key} \xB7 ${y(i.avg)}`,"performance","var(--amber)")}renderErrorsCard(t){let e=t.filter(n=>n.statusCode>=400);if(e.length===0)return this.renderCard("\u2713",N.ZERO_ERRORS,N.ALL_REQUESTS_OK,"explorer","var(--green)");let s=new Map;for(let n of e)s.set(n.statusCode,(s.get(n.statusCode)||0)+1);let i=[...s.entries()].sort((n,a)=>a[1]-n[1]).slice(0,3).map(([n,a])=>`${a}\xD7 ${n}`);return l`
|
|
468
468
|
<div class="ov-card-nav" @click=${()=>this.navigateToExplorerErrors()}>
|
|
469
469
|
<div class="ov-card-icon-lg" style="color:var(--red)">\u2715</div>
|
|
470
470
|
<div class="ov-card-content">
|
|
@@ -473,12 +473,12 @@
|
|
|
473
473
|
</div>
|
|
474
474
|
<span class="ov-card-arrow">\u2192</span>
|
|
475
475
|
</div>
|
|
476
|
-
`}renderGraphCard(){let t=this.graphSummary;if(!t||t.endpoints===0&&t.tables===0)return this.renderEmptyCard("\u25CE","Graph",
|
|
476
|
+
`}renderGraphCard(){let t=this.graphSummary;if(!t||t.endpoints===0&&t.tables===0)return this.renderEmptyCard("\u25CE","Graph",N.BUILD_GRAPH,"graph");let e=[];t.endpoints>0&&e.push(`${t.endpoints} ${B(t.endpoints,"endpoint")}`),t.tables>0&&e.push(`${t.tables} ${B(t.tables,"table")}`);let s=t.externals>0?`${t.externals} external ${B(t.externals,"service")}`:"";return this.renderCard("\u25CE",e.join(" \xB7 "),s,"graph","var(--accent)")}renderCard(t,e,s,i,n){return l`
|
|
477
477
|
<div class="ov-card-nav" @click=${()=>this.navigateTo(i)}>
|
|
478
478
|
<div class="ov-card-icon-lg" style="color:${n}">${t}</div>
|
|
479
479
|
<div class="ov-card-content">
|
|
480
480
|
<div class="ov-card-headline">${e}</div>
|
|
481
|
-
${s?l`<div class="ov-card-context">${s}</div>`:
|
|
481
|
+
${s?l`<div class="ov-card-context">${s}</div>`:d}
|
|
482
482
|
</div>
|
|
483
483
|
<span class="ov-card-arrow">\u2192</span>
|
|
484
484
|
</div>
|
|
@@ -491,7 +491,7 @@
|
|
|
491
491
|
</div>
|
|
492
492
|
<span class="ov-card-arrow">\u2192</span>
|
|
493
493
|
</div>
|
|
494
|
-
`}};u([
|
|
494
|
+
`}};u([b()],se.prototype,"graphSummary",2),se=u([_("bk-overview-view")],se);function or(o){return o<1?"<1ms":o<1e3?Math.round(o)+"ms":(o/1e3).toFixed(1)+"s"}function fi(o){return o<yt?Vt.green:o<$t?Vt.amber:Vt.red}function vi(o){return o.statusCode>=400?Vt.red:fi(o.durationMs)}function nr(o){return [parseInt(o.slice(1,3),16),parseInt(o.slice(3,5),16),parseInt(o.slice(5,7),16)]}function gi(o){let r=o.getContext("2d");if(!r)return null;let t=window.devicePixelRatio||1,e=o.clientWidth,s=o.clientHeight;return o.width=e*t,o.height=s*t,r.scale(t,t),{ctx:r,w:e,h:s}}function bi(o,r,t,e,s){let[i,n,a]=nr(s);o.beginPath(),o.arc(r,t,e+2,0,Math.PI*2),o.fillStyle=`rgba(${i},${n},${a},0.25)`,o.fill(),o.beginPath(),o.arc(r,t,e,0,Math.PI*2),o.fillStyle=s,o.fill();}function Ei(o,r,t,e,s,i){let[n,a,c]=nr(s);o.strokeStyle=`rgba(${n},${a},${c},0.3)`,o.lineWidth=i+2,o.beginPath(),o.moveTo(r-e,t-e),o.lineTo(r+e,t+e),o.moveTo(r+e,t-e),o.lineTo(r-e,t+e),o.stroke(),o.strokeStyle=s,o.lineWidth=i,o.beginPath(),o.moveTo(r-e,t-e),o.lineTo(r+e,t+e),o.moveTo(r+e,t-e),o.lineTo(r-e,t+e),o.stroke();}function ar(o,r){let t=[],e=gi(o);if(!e||r.length===0)return t;let{ctx:s,w:i,h:n}=e,a=Gs,c=i-a.left-a.right,p=n-a.top-a.bottom,h=0,m=r[0].timestamp,f=r[0].timestamp;for(let $ of r)$.durationMs>h&&(h=$.durationMs),$.timestamp<m&&(m=$.timestamp),$.timestamp>f&&(f=$.timestamp);h=Math.max(h,10),h=Math.ceil(h*1.15/10)*10;let S=f-m||1;s.strokeStyle=Us,s.lineWidth=1;let R=4;for(let $=0;$<=R;$++){let v=a.top+p-$/R*p;s.beginPath(),s.moveTo(a.left,v),s.lineTo(a.left+c,v),s.stroke(),s.fillStyle=Ve,s.font=Fs,s.textAlign="right",s.fillText(or(Math.round($/R*h)),a.left-8,v+3);}for(let $ of [{ms:yt},{ms:$t}]){if($.ms>=h)continue;let v=a.top+p-$.ms/h*p;s.beginPath(),s.setLineDash([4,4]),s.strokeStyle="rgba(113,113,122,0.3)",s.lineWidth=1,s.moveTo(a.left,v),s.lineTo(a.left+c,v),s.stroke(),s.setLineDash([]),s.fillStyle="rgba(113,113,122,0.5)",s.font=Ke,s.textAlign="left",s.fillText(or($.ms),a.left+c+2,v+3);}for(let $=0;$<r.length;$++){let v=r[$],g=r.length===1?a.left+c/2:a.left+(v.timestamp-m)/S*c,x=a.top+p-v.durationMs/h*p,w=vi(v);t.push({x:g,y:x,idx:$,r:v}),v.statusCode>=400?Ei(s,g,x,4,w,2):bi(s,g,x,4,w);}s.fillStyle=Ve,s.font=Ke,s.textAlign="center";let P=[m,m+S/2,f];for(let $=0;$<P.length;$++){let v=a.left+$/2*c,g=new Date(P[$]);s.fillText(g.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),v,a.top+p+14);}return t}var Ti={max:1/0,label:"Pending",color:"var(--text-muted)",bg:"var(--bg-muted)",border:"var(--border)"};function lr(o,r,t){return t>=20?o:r}function ss(o,r){if(!r||r<=0)return Ti;let t=o/r;return t<.7?xt[0]:t<1.2?xt[1]:t<2?xt[2]:t<3?xt[3]:xt[4]}var V=class extends E{constructor(){super(...arguments);this.selectedEndpoint=rt;this.graphData=[];this.loadError=false;this.queryBreakdown=[];this.queryBreakdownLoading=false;this.scatterDots=[];}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.store.addEventListener("state-changed",()=>this.requestUpdate()),this.loadMetrics();}async loadMetrics(){try{let e=await(await fetch(T.metricsLive)).json();this.graphData=e.endpoints||[],this.loadError=!1,(!this.selectedEndpoint||this.selectedEndpoint===rt)&&(this.selectedEndpoint=rt);}catch{this.loadError=true;}}healthGradeForEndpoint(t){let e=lr(t.summary.p95Ms,t.summary.medianMs,t.summary.totalRequests);return ss(e,t.baselineP95Ms)}healthGradeForDuration(t,e){return ss(t,e)}getCallers(t){let e=this.store.state.flows,s=new Map;for(let i of e)for(let n of i.requests)if(`${n.method} ${n.path}`===t||this.normalizeEndpoint(n)===t){let c=s.get(i.label);c?(c.count++,c.totalMs+=n.durationMs):s.set(i.label,{count:1,totalMs:n.durationMs});}return [...s.entries()].map(([i,n])=>({label:i,count:n.count,avgMs:Math.round(n.totalMs/n.count)})).sort((i,n)=>n.count-i.count)}normalizeEndpoint(t){let s=t.path.split("?")[0].split("/").map(i=>{if(!i)return i;let n=i.length>0;for(let a=0;a<i.length;a++){let c=i.charCodeAt(a);if(c<48||c>57){n=false;break}}return n||i.length===36&&i[8]==="-"&&i[13]==="-"&&i[18]==="-"&&i[23]==="-"?":id":i}).join("/");return `${t.method} ${s}`}async loadQueryBreakdown(t){if(this.queryBreakdownLoading)return;let s=this.store.state.requests.filter(i=>`${i.method} ${i.path}`===t||this.normalizeEndpoint(i)===t).slice(-20).map(i=>i.id).filter(Boolean);if(s.length===0){this.queryBreakdown=[];return}this.queryBreakdownLoading=true;try{let i=await fetch(`${T.activity}?requestIds=${s.join(",")}`);if(!i.ok){this.queryBreakdownLoading=!1;return}let n=await i.json(),a=new Map;for(let c of Object.values(n.activities))for(let p of c.timeline){if(p.type!=="query")continue;let h=p.data,m=(h.normalizedOp||h.operation||"QUERY").toUpperCase(),f=h.table||h.model||"",S=`${m} ${f}`.trim(),R=a.get(S);R?(R.totalMs+=h.durationMs,R.count++):a.set(S,{label:S,totalMs:h.durationMs,count:1});}this.queryBreakdown=[...a.values()].map(c=>({...c,avgMs:Math.round(c.totalMs/c.count)})).sort((c,p)=>p.totalMs-c.totalMs);}catch{}this.queryBreakdownLoading=false;}renderScatterChart(t,e){this.scatterDots=ar(t,e),t.style.cursor="pointer",t.onclick=s=>{let i=t.getBoundingClientRect(),n=s.clientX-i.left,a=s.clientY-i.top,c=null,p=1/0;for(let h of this.scatterDots){let m=Math.sqrt((h.x-n)**2+(h.y-a)**2);m<p&&(p=m,c=h);}c&&p<16&&this.highlightRow(c.idx);};}highlightRow(t){let e=this.querySelector(".perf-hist-row-hl");e&&e.classList.remove("perf-hist-row-hl");let s=this.querySelector(`[data-req-idx="${t}"]`);s&&(s.classList.add("perf-hist-row-hl"),s.scrollIntoView({behavior:"smooth",block:"center"}));}updated(){if(this.selectedEndpoint===rt)return;let t=this.querySelector("#perf-detail-canvas");if(t){let e=this.graphData.find(s=>s.endpoint===this.selectedEndpoint);e&&this.renderScatterChart(t,e.requests);}}render(){return !this.graphData||this.graphData.length===0?l`<bk-empty-state title="No performance data yet" subtitle="Hit some endpoints and data will appear here"></bk-empty-state>`:l`
|
|
495
495
|
<div id="graph-content">
|
|
496
496
|
${this.renderSelector()}
|
|
497
497
|
${this.selectedEndpoint===rt?this.renderOverview():this.renderDetail()}
|
|
@@ -503,11 +503,11 @@
|
|
|
503
503
|
${this.graphData.map((t,e)=>l`
|
|
504
504
|
<button class="perf-selector-btn ${t.endpoint===this.selectedEndpoint?"active":""}"
|
|
505
505
|
@click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
506
|
-
<span class="perf-dot" style="background:${
|
|
506
|
+
<span class="perf-dot" style="background:${Xe[e%Xe.length]}"></span>${t.endpoint}
|
|
507
507
|
</button>
|
|
508
508
|
`)}
|
|
509
509
|
</div>
|
|
510
|
-
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return
|
|
510
|
+
`}renderOverview(){let t=this.graphData.filter(c=>c.requests.length>0);if(t.length===0)return d;let e=t.reduce((c,p)=>c+p.summary.totalRequests,0),s=e>0?Math.round(t.reduce((c,p)=>c+p.summary.p95Ms*p.summary.totalRequests,0)/e):0,i=t.reduce((c,p)=>c+Math.round(p.summary.errorRate*p.summary.totalRequests),0),n=e>0?i/e:0,a=t[0];return l`
|
|
511
511
|
<div class="perf-overview">
|
|
512
512
|
<div class="perf-summary-row">
|
|
513
513
|
<div class="perf-summary-card">
|
|
@@ -544,7 +544,7 @@
|
|
|
544
544
|
</tbody>
|
|
545
545
|
</table>
|
|
546
546
|
</div>
|
|
547
|
-
`}renderHeatmapRow(t){let e=t.summary,s=this.healthGradeForEndpoint(t),i=Math.round(e.errorRate*e.totalRequests),n=(e.avgQueryTimeMs||0)+(e.avgFetchTimeMs||0)+(e.avgAppTimeMs||0),a=0,c=0,
|
|
547
|
+
`}renderHeatmapRow(t){let e=t.summary,s=this.healthGradeForEndpoint(t),i=Math.round(e.errorRate*e.totalRequests),n=(e.avgQueryTimeMs||0)+(e.avgFetchTimeMs||0)+(e.avgAppTimeMs||0),a=0,c=0,p=100;return n>0&&(a=Math.round((e.avgQueryTimeMs||0)/n*100),c=Math.round((e.avgFetchTimeMs||0)/n*100),p=Math.max(0,100-a-c)),l`
|
|
548
548
|
<tr class="perf-table-row" @click=${()=>{this.selectedEndpoint=t.endpoint,this.queryBreakdown=[],this.loadQueryBreakdown(t.endpoint);}}>
|
|
549
549
|
<td class="perf-td-name">${t.endpoint}</td>
|
|
550
550
|
<td class="perf-td-right">${e.totalRequests}</td>
|
|
@@ -555,9 +555,9 @@
|
|
|
555
555
|
<td class="perf-td-center" style="color:${e.avgQueryCount>5?"var(--amber)":"var(--text-muted)"}">${e.avgQueryCount}</td>
|
|
556
556
|
<td>
|
|
557
557
|
<span class="perf-hm-split-bar">
|
|
558
|
-
${a>0?l`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${a}%"></span>`:
|
|
559
|
-
${c>0?l`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:
|
|
560
|
-
${
|
|
558
|
+
${a>0?l`<span class="perf-breakdown-seg perf-breakdown-db" style="width:${a}%"></span>`:d}
|
|
559
|
+
${c>0?l`<span class="perf-breakdown-seg perf-breakdown-fetch" style="width:${c}%"></span>`:d}
|
|
560
|
+
${p>0?l`<span class="perf-breakdown-seg perf-breakdown-app" style="width:${p}%"></span>`:d}
|
|
561
561
|
</span>
|
|
562
562
|
</td>
|
|
563
563
|
</tr>
|
|
@@ -575,7 +575,7 @@
|
|
|
575
575
|
<div class="perf-detail-title">
|
|
576
576
|
<span class="perf-badge perf-badge-lg" style="color:${e.color};background:${e.bg};border-color:${e.border}">${e.label}</span>
|
|
577
577
|
<span>${t.endpoint}</span>
|
|
578
|
-
${t.baselineP95Ms?l`<span class="perf-baseline-hint">Baseline: ${y(t.baselineP95Ms)}</span>`:
|
|
578
|
+
${t.baselineP95Ms?l`<span class="perf-baseline-hint">Baseline: ${y(t.baselineP95Ms)}</span>`:d}
|
|
579
579
|
</div>
|
|
580
580
|
</div>
|
|
581
581
|
`}renderDetailMetrics(t,e,s){return l`
|
|
@@ -595,13 +595,13 @@
|
|
|
595
595
|
<span class="perf-metric-value" style="color:${t.avgQueryCount>5?"var(--amber)":"var(--text)"}">${t.avgQueryCount}</span>
|
|
596
596
|
</div>
|
|
597
597
|
</div>
|
|
598
|
-
`}renderDetailBreakdown(t){let e=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(e<=0)return
|
|
598
|
+
`}renderDetailBreakdown(t){let e=(t.avgQueryTimeMs||0)+(t.avgFetchTimeMs||0)+(t.avgAppTimeMs||0);if(e<=0)return d;let s=Math.round((t.avgQueryTimeMs||0)/e*100),i=Math.round((t.avgFetchTimeMs||0)/e*100),n=Math.max(0,100-s-i);return l`
|
|
599
599
|
<div class="perf-breakdown">
|
|
600
600
|
<div class="perf-section-title">Time Breakdown</div>
|
|
601
601
|
<div class="perf-breakdown-bar">
|
|
602
|
-
${s>0?l`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${s}%"></div>`:
|
|
603
|
-
${i>0?l`<div class="perf-breakdown-seg perf-breakdown-fetch" style="width:${i}%"></div>`:
|
|
604
|
-
${n>0?l`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:
|
|
602
|
+
${s>0?l`<div class="perf-breakdown-seg perf-breakdown-db" style="width:${s}%"></div>`:d}
|
|
603
|
+
${i>0?l`<div class="perf-breakdown-seg perf-breakdown-fetch" style="width:${i}%"></div>`:d}
|
|
604
|
+
${n>0?l`<div class="perf-breakdown-seg perf-breakdown-app" style="width:${n}%"></div>`:d}
|
|
605
605
|
</div>
|
|
606
606
|
<div class="perf-breakdown-legend">
|
|
607
607
|
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-db"></span>DB ${y(t.avgQueryTimeMs||0)} (${s}%)</span>
|
|
@@ -609,7 +609,7 @@
|
|
|
609
609
|
<span class="perf-breakdown-item"><span class="perf-breakdown-dot perf-breakdown-app"></span>App ${y(t.avgAppTimeMs||0)} (${n}%)</span>
|
|
610
610
|
</div>
|
|
611
611
|
</div>
|
|
612
|
-
`}renderCallers(t){let e=this.getCallers(t);return e.length===0?
|
|
612
|
+
`}renderCallers(t){let e=this.getCallers(t);return e.length===0?d:l`
|
|
613
613
|
<div class="perf-callers">
|
|
614
614
|
<div class="perf-section-title">Called By</div>
|
|
615
615
|
<div class="perf-callers-list">
|
|
@@ -622,7 +622,7 @@
|
|
|
622
622
|
`)}
|
|
623
623
|
</div>
|
|
624
624
|
</div>
|
|
625
|
-
`}renderQueryBreakdown(){return this.queryBreakdownLoading?l`<div class="perf-queries"><div class="perf-section-title">DB Queries</div><div class="perf-queries-loading">Loading...</div></div>`:this.queryBreakdown.length===0?
|
|
625
|
+
`}renderQueryBreakdown(){return this.queryBreakdownLoading?l`<div class="perf-queries"><div class="perf-section-title">DB Queries</div><div class="perf-queries-loading">Loading...</div></div>`:this.queryBreakdown.length===0?d:l`
|
|
626
626
|
<div class="perf-queries">
|
|
627
627
|
<div class="perf-section-title">DB Queries</div>
|
|
628
628
|
<div class="perf-queries-list">
|
|
@@ -635,13 +635,13 @@
|
|
|
635
635
|
`)}
|
|
636
636
|
</div>
|
|
637
637
|
</div>
|
|
638
|
-
`}renderTrends(t){let e=t.sessions;if(!e||e.length===0)return
|
|
638
|
+
`}renderTrends(t){let e=t.sessions;if(!e||e.length===0)return d;let s=e.slice(-10);return l`
|
|
639
639
|
<div class="perf-trends">
|
|
640
640
|
<div class="perf-section-title">Session Trend</div>
|
|
641
641
|
<div class="perf-trends-list">
|
|
642
|
-
${s.map((i,n)=>{let a=n>0?s[n-1].p95DurationMs:null,c=a!==null?i.p95DurationMs>a*1.2?"slower":i.p95DurationMs<a*.8?"faster":"":"",
|
|
642
|
+
${s.map((i,n)=>{let a=n>0?s[n-1].p95DurationMs:null,c=a!==null?i.p95DurationMs>a*1.2?"slower":i.p95DurationMs<a*.8?"faster":"":"",p=this.formatTimeAgo(i.startedAt),h=n===s.length-1,m=this.healthGradeForDuration(i.p95DurationMs,t.baselineP95Ms);return l`
|
|
643
643
|
<div class="perf-trend-row ${h?"perf-trend-current":""}">
|
|
644
|
-
<span class="perf-trend-time">${h?"Current":
|
|
644
|
+
<span class="perf-trend-time">${h?"Current":p}</span>
|
|
645
645
|
<span class="perf-trend-p95">
|
|
646
646
|
<span class="perf-hm-p95" style="color:${m.color};background:${m.bg};border-color:${m.border}">
|
|
647
647
|
p95: ${y(i.p95DurationMs)}
|
|
@@ -649,7 +649,7 @@
|
|
|
649
649
|
</span>
|
|
650
650
|
<span class="perf-trend-reqs">${i.requestCount} req${i.requestCount!==1?"s":""}</span>
|
|
651
651
|
<span class="perf-trend-errs" style="color:${i.errorCount>0?"var(--red)":"var(--text-dim)"}">${i.errorCount} err${i.errorCount!==1?"s":""}</span>
|
|
652
|
-
${c?l`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:
|
|
652
|
+
${c?l`<span class="perf-trend-arrow ${c==="slower"?"perf-trend-slower":"perf-trend-faster"}">${c==="slower"?"\u2191 slower":"\u2193 faster"}</span>`:d}
|
|
653
653
|
</div>
|
|
654
654
|
`})}
|
|
655
655
|
</div>
|
|
@@ -659,7 +659,7 @@
|
|
|
659
659
|
<div class="perf-section-title">Response Time</div>
|
|
660
660
|
<canvas id="perf-detail-canvas" class="perf-canvas" style="width:100%;height:240px"></canvas>
|
|
661
661
|
</div>
|
|
662
|
-
`}renderDetailHistory(t){if(t.requests.length===0)return
|
|
662
|
+
`}renderDetailHistory(t){if(t.requests.length===0)return d;let e=[];for(let s=t.requests.length-1;s>=0&&e.length<50;s--)e.push({point:t.requests[s],originalIndex:s});return l`
|
|
663
663
|
<div class="perf-history-wrap">
|
|
664
664
|
<table class="perf-table">
|
|
665
665
|
<thead>
|
|
@@ -677,7 +677,7 @@
|
|
|
677
677
|
</tbody>
|
|
678
678
|
</table>
|
|
679
679
|
</div>
|
|
680
|
-
`}renderHistoryRow(t,e,s){let i=this.healthGradeForDuration(t.durationMs,s),n=new Date(t.timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),a=t.statusCode>=400,c=t.queryTimeMs||0,
|
|
680
|
+
`}renderHistoryRow(t,e,s){let i=this.healthGradeForDuration(t.durationMs,s),n=new Date(t.timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}),a=t.statusCode>=400,c=t.queryTimeMs||0,p=t.fetchTimeMs||0,h=Math.max(0,t.durationMs-c-p);return l`
|
|
681
681
|
<tr class="perf-table-row ${a?"perf-row-err":""}" data-req-idx=${e}>
|
|
682
682
|
<td class="perf-td-muted">${n}</td>
|
|
683
683
|
<td>
|
|
@@ -685,27 +685,27 @@
|
|
|
685
685
|
</td>
|
|
686
686
|
<td>${y(t.durationMs)}</td>
|
|
687
687
|
<td>
|
|
688
|
-
${c>0?l`<span class="perf-bd-tag perf-bd-tag-db">DB ${y(c)}</span>`:
|
|
689
|
-
${
|
|
688
|
+
${c>0?l`<span class="perf-bd-tag perf-bd-tag-db">DB ${y(c)}</span>`:d}
|
|
689
|
+
${p>0?l`<span class="perf-bd-tag perf-bd-tag-fetch">Fetch ${y(p)}</span>`:d}
|
|
690
690
|
<span class="perf-bd-tag perf-bd-tag-app">App ${y(h)}</span>
|
|
691
691
|
</td>
|
|
692
692
|
<td class="perf-td-center" style="color:${a?"var(--red)":"var(--text-muted)"}">${t.statusCode}</td>
|
|
693
693
|
<td class="perf-td-right perf-td-muted">${t.queryCount}</td>
|
|
694
694
|
</tr>
|
|
695
|
-
`}};u([I({context:A})],X.prototype,"store",2),u([E()],X.prototype,"selectedEndpoint",2),u([E()],X.prototype,"graphData",2),u([E()],X.prototype,"loadError",2),u([E()],X.prototype,"queryBreakdown",2),u([E()],X.prototype,"queryBreakdownLoading",2),X=u([$("bk-performance-view")],X);var rs={auth:{label:"Auth",color:"#059669",icon:"\u{1F6E1}",tooltip:"Highlight which endpoints require authentication and which are unprotected"},security:{label:"Security",color:"#dc2626",icon:"\u26A0",tooltip:"Show security findings like exposed secrets, token leaks, and PII exposure"},performance:{label:"Perf",color:"#2563eb",icon:"\u26A1",tooltip:"Color endpoints by P95 latency \u2014 green (fast) to red (slow)"},issues:{label:"Issues",color:"#d97706",icon:"\u25CF",tooltip:"Badge endpoints with open issues like N+1 queries or redundant calls"},heat:{label:"Heat",color:"#ef4444",icon:"\u{1F525}",tooltip:"Color nodes and edges by traffic volume \u2014 blue (low) to red (hot)"}},Nt={action:{fill:"#faf5ff",stroke:"#a855f7",icon:"\u25B6",columnHeader:"ACTIONS"},endpoint:{fill:"#f8fafc",stroke:"#6366f1",icon:"\u26A1",columnHeader:"ENDPOINTS"},table:{fill:"#f0fdf4",stroke:"#16a34a",icon:"\u229E",columnHeader:"TABLES"},external:{fill:"#fffbeb",stroke:"#d97706",icon:"\u25C6",columnHeader:"EXTERNAL"}},xe={triggers:"#a855f7",reads:"#6366f1",writes:"#ef4444",fetches:"#f59e0b",calls:"#22c55e"},Ai=100,Ii=300,Ci=800;function kt(o){return o<Ai?"#22c55e":o<Ii?"#3b82f6":o<Ci?"#eab308":"#ef4444"}var Li=.25,Mi=.5,Ni=.75;function is(o){return o<Li?"#3b82f6":o<Mi?"#22c55e":o<Ni?"#eab308":"#ef4444"}var cr="#4338ca",dr="#e0e7ff",pr="#818cf8",hr="#eef2ff",os="#7c3aed",ur="#6366f1",Se="#f97316",mr="#ecfdf5",ns="#059669",fr="#fef2f2",as="#dc2626",vr="#fffbeb",ls="#d97706",ut="#ef4444",gr=.2,Er=3,cs=1.2,re=40;var br=800,yr=500;function Hi(o){return Math.max(140,o.length*7.2+36)}function ds(o,r){return Math.round(36+o.stats.requestCount/r*28)}function qi(o){return o.length>0?Math.max(140,...o.map(r=>Hi(r.label))):0}function $r(o,r,t){let e=[];for(let s of r){let i=s.source===o?s.target:s.target===o?s.source:null;if(i){let n=t.get(i);n&&e.push(n.y+n.h/2);}}return e.length>0?e.reduce((s,i)=>s+i,0)/e.length:1/0}function Sr(o,r){let t=v=>o.filter(g=>g.type===v).sort((g,x)=>x.stats.requestCount-g.stats.requestCount),e=t("action"),s=t("endpoint"),i=t("table"),n=t("external"),a=Math.max(1,...o.map(v=>v.stats.requestCount)),c=new Map,d=[],h=[],m=50,f=[{type:"action",items:e},{type:"endpoint",items:s},{type:"table",items:i},{type:"external",items:n}];for(let v of f)if(v.items.length>0){let g=qi(v.items);h.push({type:v.type,items:v.items,width:g,x:m}),m+=g+160;}let S=h[0];if(S){let v=74;for(let g of S.items){let x=ds(g,a),w={id:g.id,x:S.x,y:v,w:S.width,h:x,label:g.label,type:g.type,stats:g.stats,annotations:g.annotations};d.push(w),c.set(g.id,w),v+=x+10;}}for(let v=1;v<h.length;v++){let g=h[v],x=[...g.items].sort((C,F)=>$r(C.id,r,c)-$r(F.id,r,c)),w=d.filter(C=>C.x===h[v-1].x),H=Math.min(...w.map(C=>C.y)),j=Math.max(...w.map(C=>C.y+C.h))-H,tt=x.reduce((C,F)=>C+ds(F,a)+10,-10),W=H+Math.max(0,(j-tt)/2);for(let C of x){let F=ds(C,a),hs={id:C.id,x:g.x,y:W,w:g.width,h:F,label:C.label,type:C.type,stats:C.stats,annotations:C.annotations};d.push(hs),c.set(C.id,hs),W+=F+10;}}let R=[];for(let v of r){let g=c.get(v.source),x=c.get(v.target);if(!g||!x)continue;let w=g.x<x.x,H=w?g.x+g.w:g.x,Z=g.y+g.h/2,j=w?x.x:x.x+x.w,tt=x.y+x.h/2,W=[];v.stats.frequency>1&&W.push(`${v.stats.frequency}\xD7`),W.push(v.type),v.stats.avgLatencyMs>0&&W.push(`${v.stats.avgLatencyMs}ms`),R.push({key:v.id,sx:H,sy:Z,tx:j,ty:tt,label:W.join(" \xB7 "),color:xe[v.type]||"#94a3b8",thickness:Math.min(.75+Math.log2(v.stats.frequency+1)*.35,2.5),dashed:v.type==="reads"||v.type==="writes",data:v});}let P=d.reduce((v,g)=>Math.max(v,g.x+g.w),0),_=d.reduce((v,g)=>Math.max(v,g.y+g.h),0);return {nodes:d,edges:R,width:P+100,height:Math.max(_+50,250)}}function Tr(o){let r=o.charCodeAt(0);return r>=48&&r<=57||r>=65&&r<=70||r>=97&&r<=102}function Ui(o){if(o.length!==36)return false;for(let r=0;r<o.length;r++)if(r===8||r===13||r===18||r===23){if(o[r]!=="-")return false}else if(!Tr(o[r]))return false;return true}function Fi(o){if(!o.length)return false;for(let r=0;r<o.length;r++){let t=o.charCodeAt(r);if(t<48||t>57)return false}return true}function Gi(o){if(o.length<12)return false;for(let r=0;r<o.length;r++)if(!Tr(o[r]))return false;return true}function Bi(o){if(o.length<8)return false;let r=false,t=false;for(let e=0;e<o.length;e++){let s=o.charCodeAt(e);if(s>=65&&s<=90||s>=97&&s<=122)r=true;else if(s>=48&&s<=57)t=true;else if(s!==95&&s!==45)return false}return r&&t}var Wi=":id";function Qi(o){return Ui(o)||Fi(o)||Gi(o)||Bi(o)?Wi:o}function ps(o,r){let t=r.split("?")[0];return `${o} ${t.split("/").map(e=>e&&Qi(e)).join("/")}`}function wr(o,r,t){let e=[...o],s=[...r],i=new Set(e.map(a=>a.id)),n=new Map;for(let a of t){let c=a.label||"Unknown",d=n.get(c);d||(d={endpointKeys:new Set,count:0,totalMs:0},n.set(c,d)),d.count++,d.totalMs+=a.totalDurationMs;for(let h of a.requests)h.path?.startsWith(U)||d.endpointKeys.add(ps(h.method,h.path));}for(let[a,c]of n){let d=`action:${a}`;i.has(d)||(e.push({id:d,type:"action",label:a,stats:{requestCount:c.count,avgLatencyMs:c.count>0?Math.round(c.totalMs/c.count):0,errorRate:0,avgQueryCount:0}}),i.add(d));for(let h of c.endpointKeys){let m=`endpoint:${h}`;if(i.has(m)){let f=`${d} -> ${m}`;s.find(S=>S.id===f)||s.push({id:f,source:d,target:m,type:"triggers",stats:{frequency:c.count,avgLatencyMs:0}});}}}return {nodes:e,edges:s}}function Rr(o){let r=new Map;for(let t of o){let e=t.label||"Unknown",s=r.get(e);s||(s={keys:new Set,count:0,totalMs:0},r.set(e,s)),s.count++,s.totalMs+=t.totalDurationMs;for(let i of t.requests)i.path?.startsWith(U)||s.keys.add(ps(i.method,i.path));}return [...r.entries()].map(([t,e])=>({label:t,occurrences:e.count,endpointKeys:e.keys,avgDurationMs:e.count>0?Math.round(e.totalMs/e.count):0}))}function we(o,r){let t=`event=${encodeURIComponent(o)}${r?`&detail=${encodeURIComponent(r)}`:""}`;fetch(`${T.tab}?${t}`).catch(()=>{});}var M=class extends b{constructor(){super(...arguments);this.graphNodes=[];this.graphEdges=[];this.locked=null;this.hovered=null;this.loading=true;this.activeLayers=new Set;this.searchQuery="";this.viewTransform={x:0,y:0,scale:1};this.isPanning=false;this.panStart={x:0,y:0,vtx:0,vty:0};this.dragging=null;this.wasDragging=false;this.nodePositionOverrides=new Map;this.detailTab="overview";this.consolidatedFlows=[];this.activeFlowIdx=-1;this.focusIdx=-1;this.refreshTimer=null;}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadData(),this.refreshTimer=setInterval(()=>this.loadData(),4e3);}disconnectedCallback(){super.disconnectedCallback(),this.refreshTimer&&clearInterval(this.refreshTimer);}async loadData(){try{let[t,e]=await Promise.all([fetch(`${T.graph}?level=endpoints`),fetch(T.flows)]),s=await t.json(),i=await e.json(),n=wr(s.nodes||[],s.edges||[],i.flows||[]);this.graphNodes=n.nodes,this.graphEdges=n.edges,this.consolidatedFlows=Rr(i.flows||[]),this.nodePositionOverrides.size===0&&this.graphNodes.length>0&&this.tryLoadPersistedPositions(),this.loading=!1;}catch{this.loading=false;}}getPositionStorageKey(){let t=this.graphNodes.map(s=>s.id).sort().join(","),e=0;for(let s=0;s<t.length;s++)e=(e<<5)-e+t.charCodeAt(s)|0;return `bk-graph-pos-${e}`}persistPositions(){if(this.nodePositionOverrides.size!==0)try{let t=Object.fromEntries(this.nodePositionOverrides);localStorage.setItem(this.getPositionStorageKey(),JSON.stringify(t));}catch{}}tryLoadPersistedPositions(){try{let t=localStorage.getItem(this.getPositionStorageKey());if(t){let e=JSON.parse(t);this.nodePositionOverrides=new Map(Object.entries(e));}}catch{}}get activeNodeId(){return this.locked??this.hovered}getHighlightedNodeIds(){let t=this.activeNodeId;if(!t)return null;let e=new Set([t]),s=true;for(;s;){s=false;for(let i of this.graphEdges)e.has(i.source)&&!e.has(i.target)&&(e.add(i.target),s=true);}return e}getFlowTraceNodeIds(){if(this.activeFlowIdx<0||this.activeFlowIdx>=this.consolidatedFlows.length)return null;let t=this.consolidatedFlows[this.activeFlowIdx],e=new Set;e.add(`action:${t.label}`);for(let s of t.endpointKeys)e.add(`endpoint:${s}`);for(let s of this.graphEdges)e.has(s.source)&&e.add(s.target);return e}getFlowTraceEdgeIds(){let t=this.getFlowTraceNodeIds();if(!t)return null;let e=new Set;for(let s of this.graphEdges)t.has(s.source)&&t.has(s.target)&&e.add(s.id);return e}matchesSearch(t){return this.searchQuery?t.toLowerCase().includes(this.searchQuery.toLowerCase()):true}handlePanStart(t){t.button===0&&(t.target.closest(".graph-g")||(this.isPanning=true,this.panStart={x:t.clientX,y:t.clientY,vtx:this.viewTransform.x,vty:this.viewTransform.y}));}handlePanMove(t){if(this.dragging){let e=t.currentTarget.getBoundingClientRect(),s=(t.clientX-e.left-this.viewTransform.x)/this.viewTransform.scale,i=(t.clientY-e.top-this.viewTransform.y)/this.viewTransform.scale;this.nodePositionOverrides.set(this.dragging.nodeId,{x:s-this.dragging.offsetX,y:i-this.dragging.offsetY}),this.requestUpdate();return}this.isPanning&&(this.viewTransform={...this.viewTransform,x:this.panStart.vtx+(t.clientX-this.panStart.x),y:this.panStart.vty+(t.clientY-this.panStart.y)});}handlePanEnd(){this.dragging&&(this.persistPositions(),this.dragging=null,this.wasDragging=true,requestAnimationFrame(()=>{this.wasDragging=false;})),this.isPanning=false;}resetView(){this.viewTransform={x:0,y:0,scale:1};}zoomIn(){this.viewTransform={...this.viewTransform,scale:Math.min(Er,this.viewTransform.scale*cs)};}zoomOut(){this.viewTransform={...this.viewTransform,scale:Math.max(gr,this.viewTransform.scale/cs)};}resetLayout(){this.nodePositionOverrides.clear();try{localStorage.removeItem(this.getPositionStorageKey());}catch{}this.viewTransform={x:0,y:0,scale:1},this.requestUpdate(),we("layout_reset");}startNodeDrag(t,e,s){t.stopPropagation();let i=t.currentTarget.closest("svg").getBoundingClientRect(),n=(t.clientX-i.left-this.viewTransform.x)/this.viewTransform.scale,a=(t.clientY-i.top-this.viewTransform.y)/this.viewTransform.scale;this.dragging={nodeId:e,offsetX:n-s.x,offsetY:a-s.y};}handleKeyDown(t){let e=this.graphNodes;if(t.key==="/"){t.preventDefault(),this.querySelector(".graph-search-input")?.focus();return}if(t.key==="Escape"){this.locked=null,this.searchQuery="",this.focusIdx=-1,this.activeFlowIdx=-1;return}if(t.key==="Tab"){t.preventDefault(),this.focusIdx=t.shiftKey?this.focusIdx<=0?e.length-1:this.focusIdx-1:(this.focusIdx+1)%e.length,this.hovered=e[this.focusIdx]?.id??null;return}if(t.key==="Enter"&&this.focusIdx>=0){let s=e[this.focusIdx];s&&(this.locked=this.locked===s.id?null:s.id);return}if(t.key==="+"||t.key==="="){this.zoomIn();return}if(t.key==="-"){this.zoomOut();return}if(t.key==="ArrowUp"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y+re};return}if(t.key==="ArrowDown"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y-re};return}if(t.key==="ArrowLeft"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x+re};return}if(t.key==="ArrowRight"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x-re};return}}render(){if(this.loading&&this.graphNodes.length===0)return l`<div class="graph-loading">Loading graph…</div>`;if(this.graphNodes.length===0)return l`
|
|
695
|
+
`}};u([C({context:A})],V.prototype,"store",2),u([b()],V.prototype,"selectedEndpoint",2),u([b()],V.prototype,"graphData",2),u([b()],V.prototype,"loadError",2),u([b()],V.prototype,"queryBreakdown",2),u([b()],V.prototype,"queryBreakdownLoading",2),V=u([_("bk-performance-view")],V);var rs={auth:{label:"Auth",color:"#059669",icon:"\u{1F6E1}",tooltip:"Highlight which endpoints require authentication and which are unprotected"},security:{label:"Security",color:"#dc2626",icon:"\u26A0",tooltip:"Show security findings like exposed secrets, token leaks, and PII exposure"},performance:{label:"Perf",color:"#2563eb",icon:"\u26A1",tooltip:"Color endpoints by P95 latency \u2014 green (fast) to red (slow)"},issues:{label:"Issues",color:"#d97706",icon:"\u25CF",tooltip:"Badge endpoints with open issues like N+1 queries or redundant calls"},heat:{label:"Heat",color:"#ef4444",icon:"\u{1F525}",tooltip:"Color nodes and edges by traffic volume \u2014 blue (low) to red (hot)"}},kt={action:{fill:"#faf5ff",stroke:"#a855f7",icon:"\u25B6",columnHeader:"ACTIONS"},endpoint:{fill:"#f8fafc",stroke:"#6366f1",icon:"\u26A1",columnHeader:"ENDPOINTS"},table:{fill:"#f0fdf4",stroke:"#16a34a",icon:"\u229E",columnHeader:"TABLES"},external:{fill:"#fffbeb",stroke:"#d97706",icon:"\u25C6",columnHeader:"EXTERNAL"}},xe={triggers:"#a855f7",reads:"#6366f1",writes:"#ef4444",fetches:"#f59e0b",calls:"#22c55e"},Ii=100,Li=300,Mi=800;function Ot(o){return o<Ii?"#22c55e":o<Li?"#3b82f6":o<Mi?"#eab308":"#ef4444"}var ki=.25,Oi=.5,Ni=.75;function is(o){return o<ki?"#3b82f6":o<Oi?"#22c55e":o<Ni?"#eab308":"#ef4444"}var cr="#4338ca",dr="#e0e7ff",pr="#818cf8",hr="#eef2ff",os="#7c3aed",ur="#6366f1",Se="#f97316",mr="#ecfdf5",ns="#059669",fr="#fef2f2",as="#dc2626",vr="#fffbeb",ls="#d97706",ut="#ef4444",gr=.2,br=3,cs=1.2,re=40;var Er=800,yr=500;function Ui(o){return Math.max(140,o.length*7.2+36)}function ds(o,r){return Math.round(36+o.stats.requestCount/r*28)}function Fi(o){return o.length>0?Math.max(140,...o.map(r=>Ui(r.label))):0}function _r(o,r,t){let e=[];for(let s of r){let i=s.source===o?s.target:s.target===o?s.source:null;if(i){let n=t.get(i);n&&e.push(n.y+n.h/2);}}return e.length>0?e.reduce((s,i)=>s+i,0)/e.length:1/0}function Sr(o,r){let t=v=>o.filter(g=>g.type===v).sort((g,x)=>x.stats.requestCount-g.stats.requestCount),e=t("action"),s=t("endpoint"),i=t("table"),n=t("external"),a=Math.max(1,...o.map(v=>v.stats.requestCount)),c=new Map,p=[],h=[],m=50,f=[{type:"action",items:e},{type:"endpoint",items:s},{type:"table",items:i},{type:"external",items:n}];for(let v of f)if(v.items.length>0){let g=Fi(v.items);h.push({type:v.type,items:v.items,width:g,x:m}),m+=g+160;}let S=h[0];if(S){let v=74;for(let g of S.items){let x=ds(g,a),w={id:g.id,x:S.x,y:v,w:S.width,h:x,label:g.label,type:g.type,stats:g.stats,annotations:g.annotations};p.push(w),c.set(g.id,w),v+=x+10;}}for(let v=1;v<h.length;v++){let g=h[v],x=[...g.items].sort((I,F)=>_r(I.id,r,c)-_r(F.id,r,c)),w=p.filter(I=>I.x===h[v-1].x),H=Math.min(...w.map(I=>I.y)),Q=Math.max(...w.map(I=>I.y+I.h))-H,tt=x.reduce((I,F)=>I+ds(F,a)+10,-10),W=H+Math.max(0,(Q-tt)/2);for(let I of x){let F=ds(I,a),hs={id:I.id,x:g.x,y:W,w:g.width,h:F,label:I.label,type:I.type,stats:I.stats,annotations:I.annotations};p.push(hs),c.set(I.id,hs),W+=F+10;}}let R=[];for(let v of r){let g=c.get(v.source),x=c.get(v.target);if(!g||!x)continue;let w=g.x<x.x,H=w?g.x+g.w:g.x,Z=g.y+g.h/2,Q=w?x.x:x.x+x.w,tt=x.y+x.h/2,W=[];v.stats.frequency>1&&W.push(`${v.stats.frequency}\xD7`),W.push(v.type),v.stats.avgLatencyMs>0&&W.push(`${v.stats.avgLatencyMs}ms`),R.push({key:v.id,sx:H,sy:Z,tx:Q,ty:tt,label:W.join(" \xB7 "),color:xe[v.type]||"#94a3b8",thickness:Math.min(.75+Math.log2(v.stats.frequency+1)*.35,2.5),dashed:v.type==="reads"||v.type==="writes",data:v});}let P=p.reduce((v,g)=>Math.max(v,g.x+g.w),0),$=p.reduce((v,g)=>Math.max(v,g.y+g.h),0);return {nodes:p,edges:R,width:P+100,height:Math.max($+50,250)}}function Tr(o){let r=o.charCodeAt(0);return r>=48&&r<=57||r>=65&&r<=70||r>=97&&r<=102}function Gi(o){if(o.length!==36)return false;for(let r=0;r<o.length;r++)if(r===8||r===13||r===18||r===23){if(o[r]!=="-")return false}else if(!Tr(o[r]))return false;return true}function Bi(o){if(!o.length)return false;for(let r=0;r<o.length;r++){let t=o.charCodeAt(r);if(t<48||t>57)return false}return true}function Wi(o){if(o.length<12)return false;for(let r=0;r<o.length;r++)if(!Tr(o[r]))return false;return true}function ji(o){if(o.length<8)return false;let r=false,t=false;for(let e=0;e<o.length;e++){let s=o.charCodeAt(e);if(s>=65&&s<=90||s>=97&&s<=122)r=true;else if(s>=48&&s<=57)t=true;else if(s!==95&&s!==45)return false}return r&&t}var Qi=":id";function Yi(o){return Gi(o)||Bi(o)||Wi(o)||ji(o)?Qi:o}function ps(o,r){let t=r.split("?")[0];return `${o} ${t.split("/").map(e=>e&&Yi(e)).join("/")}`}function wr(o,r,t){let e=[...o],s=[...r],i=new Set(e.map(a=>a.id)),n=new Map;for(let a of t){let c=a.label||"Unknown",p=n.get(c);p||(p={endpointKeys:new Set,count:0,totalMs:0},n.set(c,p)),p.count++,p.totalMs+=a.totalDurationMs;for(let h of a.requests)h.path?.startsWith(U)||p.endpointKeys.add(ps(h.method,h.path));}for(let[a,c]of n){let p=`action:${a}`;i.has(p)||(e.push({id:p,type:"action",label:a,stats:{requestCount:c.count,avgLatencyMs:c.count>0?Math.round(c.totalMs/c.count):0,errorRate:0,avgQueryCount:0}}),i.add(p));for(let h of c.endpointKeys){let m=`endpoint:${h}`;if(i.has(m)){let f=`${p} -> ${m}`;s.find(S=>S.id===f)||s.push({id:f,source:p,target:m,type:"triggers",stats:{frequency:c.count,avgLatencyMs:0}});}}}return {nodes:e,edges:s}}function Rr(o){let r=new Map;for(let t of o){let e=t.label||"Unknown",s=r.get(e);s||(s={keys:new Set,count:0,totalMs:0},r.set(e,s)),s.count++,s.totalMs+=t.totalDurationMs;for(let i of t.requests)i.path?.startsWith(U)||s.keys.add(ps(i.method,i.path));}return [...r.entries()].map(([t,e])=>({label:t,occurrences:e.count,endpointKeys:e.keys,avgDurationMs:e.count>0?Math.round(e.totalMs/e.count):0}))}function we(o,r){let t=`event=${encodeURIComponent(o)}${r?`&detail=${encodeURIComponent(r)}`:""}`;fetch(`${T.tab}?${t}`).catch(()=>{});}var M=class extends 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(`${T.graph}?level=endpoints`),fetch(T.flows)]),s=await t.json(),i=await e.json(),n=wr(s.nodes||[],s.edges||[],i.flows||[]);this.graphNodes=n.nodes,this.graphEdges=n.edges,this.consolidatedFlows=Rr(i.flows||[]),this.nodePositionOverrides.size===0&&this.graphNodes.length>0&&this.tryLoadPersistedPositions(),this.loading=!1;}catch{this.loading=false;}}getPositionStorageKey(){let t=this.graphNodes.map(s=>s.id).sort().join(","),e=0;for(let s=0;s<t.length;s++)e=(e<<5)-e+t.charCodeAt(s)|0;return `bk-graph-pos-${e}`}persistPositions(){if(this.nodePositionOverrides.size!==0)try{let t=Object.fromEntries(this.nodePositionOverrides);localStorage.setItem(this.getPositionStorageKey(),JSON.stringify(t));}catch{}}tryLoadPersistedPositions(){try{let t=localStorage.getItem(this.getPositionStorageKey());if(t){let e=JSON.parse(t);this.nodePositionOverrides=new Map(Object.entries(e));}}catch{}}get activeNodeId(){return this.locked??this.hovered}getHighlightedNodeIds(){let t=this.activeNodeId;if(!t)return null;let e=new Set([t]),s=true;for(;s;){s=false;for(let i of this.graphEdges)e.has(i.source)&&!e.has(i.target)&&(e.add(i.target),s=true);}return e}getFlowTraceNodeIds(){if(this.activeFlowIdx<0||this.activeFlowIdx>=this.consolidatedFlows.length)return null;let t=this.consolidatedFlows[this.activeFlowIdx],e=new Set;e.add(`action:${t.label}`);for(let s of t.endpointKeys)e.add(`endpoint:${s}`);for(let s of this.graphEdges)e.has(s.source)&&e.add(s.target);return e}getFlowTraceEdgeIds(){let t=this.getFlowTraceNodeIds();if(!t)return null;let e=new Set;for(let s of this.graphEdges)t.has(s.source)&&t.has(s.target)&&e.add(s.id);return e}matchesSearch(t){return this.searchQuery?t.toLowerCase().includes(this.searchQuery.toLowerCase()):true}handlePanStart(t){t.button===0&&(t.target.closest(".graph-g")||(this.isPanning=true,this.panStart={x:t.clientX,y:t.clientY,vtx:this.viewTransform.x,vty:this.viewTransform.y}));}handlePanMove(t){if(this.dragging){let e=t.currentTarget.getBoundingClientRect(),s=(t.clientX-e.left-this.viewTransform.x)/this.viewTransform.scale,i=(t.clientY-e.top-this.viewTransform.y)/this.viewTransform.scale;this.nodePositionOverrides.set(this.dragging.nodeId,{x:s-this.dragging.offsetX,y:i-this.dragging.offsetY}),this.requestUpdate();return}this.isPanning&&(this.viewTransform={...this.viewTransform,x:this.panStart.vtx+(t.clientX-this.panStart.x),y:this.panStart.vty+(t.clientY-this.panStart.y)});}handlePanEnd(){this.dragging&&(this.persistPositions(),this.dragging=null,this.wasDragging=true,requestAnimationFrame(()=>{this.wasDragging=false;})),this.isPanning=false;}resetView(){this.viewTransform={x:0,y:0,scale:1};}zoomIn(){this.viewTransform={...this.viewTransform,scale:Math.min(br,this.viewTransform.scale*cs)};}zoomOut(){this.viewTransform={...this.viewTransform,scale:Math.max(gr,this.viewTransform.scale/cs)};}resetLayout(){this.nodePositionOverrides.clear();try{localStorage.removeItem(this.getPositionStorageKey());}catch{}this.viewTransform={x:0,y:0,scale:1},this.requestUpdate(),we("layout_reset");}startNodeDrag(t,e,s){t.stopPropagation();let i=t.currentTarget.closest("svg").getBoundingClientRect(),n=(t.clientX-i.left-this.viewTransform.x)/this.viewTransform.scale,a=(t.clientY-i.top-this.viewTransform.y)/this.viewTransform.scale;this.dragging={nodeId:e,offsetX:n-s.x,offsetY:a-s.y};}handleKeyDown(t){let e=this.graphNodes;if(t.key==="/"){t.preventDefault(),this.querySelector(".graph-search-input")?.focus();return}if(t.key==="Escape"){this.locked=null,this.searchQuery="",this.focusIdx=-1,this.activeFlowIdx=-1;return}if(t.key==="Tab"){t.preventDefault(),this.focusIdx=t.shiftKey?this.focusIdx<=0?e.length-1:this.focusIdx-1:(this.focusIdx+1)%e.length,this.hovered=e[this.focusIdx]?.id??null;return}if(t.key==="Enter"&&this.focusIdx>=0){let s=e[this.focusIdx];s&&(this.locked=this.locked===s.id?null:s.id);return}if(t.key==="+"||t.key==="="){this.zoomIn();return}if(t.key==="-"){this.zoomOut();return}if(t.key==="ArrowUp"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y+re};return}if(t.key==="ArrowDown"){this.viewTransform={...this.viewTransform,y:this.viewTransform.y-re};return}if(t.key==="ArrowLeft"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x+re};return}if(t.key==="ArrowRight"){this.viewTransform={...this.viewTransform,x:this.viewTransform.x-re};return}}render(){if(this.loading&&this.graphNodes.length===0)return l`<div class="graph-loading">Loading graph…</div>`;if(this.graphNodes.length===0)return l`
|
|
696
696
|
<div class="graph-empty">
|
|
697
697
|
<div class="graph-empty-icon">◎</div>
|
|
698
698
|
<div class="graph-empty-title">No data yet</div>
|
|
699
699
|
<div class="graph-empty-desc">Navigate your app to build the dependency graph.</div>
|
|
700
700
|
</div>
|
|
701
|
-
`;let t=Sr(this.graphNodes,this.graphEdges);this.applyPositionOverrides(t);let e=this.getSelectedNodeDetail(),s=this.getHighlightedNodeIds(),i=this.getFlowTraceNodeIds(),n=this.getFlowTraceEdgeIds(),a=this.deduplicateColumnHeaders(t.nodes),c=Math.max(1,...this.graphNodes.map(h=>h.stats.requestCount)),
|
|
701
|
+
`;let t=Sr(this.graphNodes,this.graphEdges);this.applyPositionOverrides(t);let e=this.getSelectedNodeDetail(),s=this.getHighlightedNodeIds(),i=this.getFlowTraceNodeIds(),n=this.getFlowTraceEdgeIds(),a=this.deduplicateColumnHeaders(t.nodes),c=Math.max(1,...this.graphNodes.map(h=>h.stats.requestCount)),p=this.viewTransform;return l`
|
|
702
702
|
<div class="graph-wrapper" tabindex="0" @keydown=${this.handleKeyDown}
|
|
703
703
|
@click=${h=>{let m=h.target;m.closest(".graph-detail")||m.closest(".graph-toolbar")||m.closest(".graph-float")||m.closest(".graph-g")||(this.locked=null);}}>
|
|
704
704
|
${this.renderToolbar()}
|
|
705
705
|
<div class="graph-body">
|
|
706
706
|
<div class="graph-canvas" style="position:relative">
|
|
707
707
|
<svg width="100%" height="100%"
|
|
708
|
-
viewBox="0 0 ${Math.max(t.width,
|
|
708
|
+
viewBox="0 0 ${Math.max(t.width,Er)} ${Math.max(t.height,yr)}"
|
|
709
709
|
class="graph-svg"
|
|
710
710
|
style="cursor:${this.isPanning?"grabbing":"grab"}"
|
|
711
711
|
@mousedown=${this.handlePanStart}
|
|
@@ -713,8 +713,8 @@
|
|
|
713
713
|
@mouseup=${this.handlePanEnd}
|
|
714
714
|
@mouseleave=${this.handlePanEnd}>
|
|
715
715
|
|
|
716
|
-
<g transform="translate(${
|
|
717
|
-
${
|
|
716
|
+
<g transform="translate(${p.x},${p.y}) scale(${p.scale})">
|
|
717
|
+
${O`${a.map(h=>O`
|
|
718
718
|
<text x="${h.x}" y="${58}" class="graph-col-header">${h.label}</text>
|
|
719
719
|
`)}`}
|
|
720
720
|
|
|
@@ -724,10 +724,10 @@
|
|
|
724
724
|
</svg>
|
|
725
725
|
${this.renderFloatingControls()}
|
|
726
726
|
</div>
|
|
727
|
-
${e?this.renderDetailPanel(e):
|
|
727
|
+
${e?this.renderDetailPanel(e):d}
|
|
728
728
|
</div>
|
|
729
729
|
</div>
|
|
730
|
-
`}applyPositionOverrides(t){for(let s of t.nodes){let i=this.nodePositionOverrides.get(s.id);i&&(s.x=i.x,s.y=i.y);}let e=new Map(t.nodes.map(s=>[s.id,s]));for(let s of t.edges){let i=e.get(s.data.source),n=e.get(s.data.target);if(i&&n){let a=i.x<n.x;s.sx=a?i.x+i.w:i.x,s.sy=i.y+i.h/2,s.tx=a?n.x:n.x+n.w,s.ty=n.y+n.h/2;}}}deduplicateColumnHeaders(t){let e=[],s=new Set;for(let i of t)s.has(i.type)||(s.add(i.type),e.push({x:i.x,label:
|
|
730
|
+
`}applyPositionOverrides(t){for(let s of t.nodes){let i=this.nodePositionOverrides.get(s.id);i&&(s.x=i.x,s.y=i.y);}let e=new Map(t.nodes.map(s=>[s.id,s]));for(let s of t.edges){let i=e.get(s.data.source),n=e.get(s.data.target);if(i&&n){let a=i.x<n.x;s.sx=a?i.x+i.w:i.x,s.sy=i.y+i.h/2,s.tx=a?n.x:n.x+n.w,s.ty=n.y+n.h/2;}}}deduplicateColumnHeaders(t){let e=[],s=new Set;for(let i of t)s.has(i.type)||(s.add(i.type),e.push({x:i.x,label:kt[i.type]?.columnHeader||i.type.toUpperCase()}));return e}renderToolbar(){return l`
|
|
731
731
|
<div class="graph-toolbar">
|
|
732
732
|
<div class="graph-layer-toggles">
|
|
733
733
|
${Object.keys(rs).map(t=>{let e=rs[t],s=this.activeLayers.has(t);return l`
|
|
@@ -747,7 +747,7 @@
|
|
|
747
747
|
@input=${t=>{this.searchQuery=t.target.value;}}>
|
|
748
748
|
${this.searchQuery?l`
|
|
749
749
|
<button class="graph-search-clear" @click=${()=>{this.searchQuery="";}}>✕</button>
|
|
750
|
-
`:
|
|
750
|
+
`:d}
|
|
751
751
|
</div>
|
|
752
752
|
|
|
753
753
|
${this.consolidatedFlows.length>0?l`
|
|
@@ -757,14 +757,14 @@
|
|
|
757
757
|
<option value="${e}" ?selected=${this.activeFlowIdx===e}>${t.label} → ${t.endpointKeys.size} ep · ${t.occurrences}×</option>
|
|
758
758
|
`)}
|
|
759
759
|
</select>
|
|
760
|
-
`:
|
|
760
|
+
`:d}
|
|
761
761
|
|
|
762
762
|
${this.activeLayers.has("auth")?l`
|
|
763
763
|
<div class="graph-auth-legend">
|
|
764
764
|
<span class="graph-auth-legend-item"><span style="color:${ns}">🛡</span> protected</span>
|
|
765
765
|
<span class="graph-auth-legend-item"><span style="color:${Se};font-weight:700">!</span> no auth</span>
|
|
766
766
|
</div>
|
|
767
|
-
`:
|
|
767
|
+
`:d}
|
|
768
768
|
</div>
|
|
769
769
|
`}renderFloatingControls(){let t=this.nodePositionOverrides.size>0,e=Math.round(this.viewTransform.scale*100);return l`
|
|
770
770
|
<div class="graph-float">
|
|
@@ -778,7 +778,7 @@
|
|
|
778
778
|
<button class="graph-float-btn graph-float-btn-accent" @click=${()=>this.resetLayout()} title="Reset layout to auto-arrange">
|
|
779
779
|
⟲ Reformat
|
|
780
780
|
</button>
|
|
781
|
-
`:
|
|
781
|
+
`:d}
|
|
782
782
|
<span class="graph-float-sep"></span>
|
|
783
783
|
<button class="graph-float-btn" @click=${()=>this.captureScreenshot()} title="Save graph as PNG">
|
|
784
784
|
📷
|
|
@@ -789,22 +789,22 @@
|
|
|
789
789
|
.graph-col-header { fill: #c4c4cc; font-size: 9px; font-weight: 600; letter-spacing: 1.5px; }
|
|
790
790
|
.graph-flow-edge { stroke-dasharray: 6,4; }
|
|
791
791
|
.graph-pulse { }
|
|
792
|
-
`,e.insertBefore(s,e.firstChild);let n=document.createElementNS("http://www.w3.org/2000/svg","rect");n.setAttribute("width","100%"),n.setAttribute("height","100%"),n.setAttribute("fill","#ffffff"),e.insertBefore(n,e.firstChild);let a=e.getAttribute("viewBox")||"0 0 800 500",[,,c,
|
|
793
|
-
<g class="graph-g" transform="translate(${t.x},${t.y})" style="opacity:${
|
|
792
|
+
`,e.insertBefore(s,e.firstChild);let n=document.createElementNS("http://www.w3.org/2000/svg","rect");n.setAttribute("width","100%"),n.setAttribute("height","100%"),n.setAttribute("fill","#ffffff"),e.insertBefore(n,e.firstChild);let a=e.getAttribute("viewBox")||"0 0 800 500",[,,c,p]=a.split(" ").map(Number),h=2,m=c*h,f=p*h,S=new XMLSerializer().serializeToString(e),R=new Blob([S],{type:"image/svg+xml;charset=utf-8"}),P=URL.createObjectURL(R),$=new Image;$.onload=()=>{let v=document.createElement("canvas");v.width=m,v.height=f;let g=v.getContext("2d");g.fillStyle="#ffffff",g.fillRect(0,0,m,f),g.drawImage($,0,0,m,f),URL.revokeObjectURL(P),v.toBlob(x=>{if(!x)return;let w=document.createElement("a");w.href=URL.createObjectURL(x),w.download=`brakit-graph-${new Date().toISOString().slice(0,19).replace(/[T:]/g,"-")}.png`,document.body.appendChild(w),w.click(),document.body.removeChild(w),URL.revokeObjectURL(w.href);},"image/png");},$.src=P;}toggleLayer(t){let e=new Set(this.activeLayers);e.has(t)?e.delete(t):(e.add(t),we("layer_toggled",t)),this.activeLayers=e;}renderNode(t,e,s,i){let n=this.activeNodeId,a=e===null||e.has(t.id),c=n===t.id,p=this.locked===t.id,h=s?.has(t.id)??false,m=this.matchesSearch(t.label),f=kt[t.type]||kt.endpoint,S=p?cr:c?pr:f.stroke,R=p?dr:c?hr:f.fill,P=p?2:c?1.5:.75,$=a?1:.08;this.searchQuery&&!m&&($=.05),s&&!h&&($=Math.min($,.08)),h&&($=1,S=os);let v=this.searchQuery&&m,g=t.annotations,x=this.activeLayers;x.has("performance")&&g?.p95Ms!==void 0&&t.type==="endpoint"&&(R=Ot(g.p95Ms)+"18"),x.has("heat")&&(R=is(t.stats.requestCount/i)+"20");let w=x.has("auth")&&t.type==="endpoint"&&!g?.hasAuth,H=x.has("auth")&&g?.hasAuth,Z=x.has("security")?g?.securityFindings?.length??0:0,Q=g?.securityFindings?.some(F=>F.severity==="critical"),tt=x.has("issues")?g?.openIssueCount??0:0,W=this.nodeSubtitle(t),I=x.has("performance")&&g?.p95Ms!==void 0&&t.type==="endpoint";return O`
|
|
793
|
+
<g class="graph-g" transform="translate(${t.x},${t.y})" style="opacity:${$};cursor:pointer;transition:opacity .15s,transform .1s"
|
|
794
794
|
@click=${F=>{F.stopPropagation(),!this.wasDragging&&(this.locked=this.locked===t.id?null:t.id,this.detailTab="overview");}}
|
|
795
795
|
@mouseenter=${()=>{this.hovered=t.id;}}
|
|
796
796
|
@mouseleave=${()=>{this.hovered=null;}}
|
|
797
797
|
@mousedown=${F=>{F.detail>=2||this.startNodeDrag(F,t.id,t);}}>
|
|
798
798
|
|
|
799
|
-
${v?
|
|
799
|
+
${v?O`
|
|
800
800
|
<rect x="-3" y="-3" width="${t.w+6}" height="${t.h+6}" rx="9" fill="none"
|
|
801
801
|
stroke="${ur}" stroke-width="2" stroke-dasharray="4,2"/>
|
|
802
|
-
`:
|
|
802
|
+
`:d}
|
|
803
803
|
|
|
804
|
-
${w?
|
|
804
|
+
${w?O`
|
|
805
805
|
<rect width="${t.w}" height="${t.h}" rx="8" fill="${R}" stroke="${Se}"
|
|
806
806
|
stroke-width="1.2" stroke-dasharray="5,3"/>
|
|
807
|
-
`:
|
|
807
|
+
`:O`
|
|
808
808
|
<rect width="${t.w}" height="${t.h}" rx="8" fill="${R}" stroke="${S}" stroke-width="${P}"/>
|
|
809
809
|
`}
|
|
810
810
|
|
|
@@ -813,75 +813,75 @@
|
|
|
813
813
|
<text x="12" y="${t.h/2+10}" fill="#a1a1aa" font-size="9"
|
|
814
814
|
font-family="ui-monospace,monospace">${W}</text>
|
|
815
815
|
|
|
816
|
-
${
|
|
817
|
-
<text x="${t.w-8}" y="${t.h-6}" fill="${
|
|
816
|
+
${I?O`
|
|
817
|
+
<text x="${t.w-8}" y="${t.h-6}" fill="${Ot(g.p95Ms)}" font-size="9"
|
|
818
818
|
font-family="ui-monospace,monospace" text-anchor="end">p95: ${g.p95Ms}ms</text>
|
|
819
|
-
`:
|
|
819
|
+
`:d}
|
|
820
820
|
|
|
821
|
-
${H?
|
|
821
|
+
${H?O`
|
|
822
822
|
<g transform="translate(${t.w-24},3)">
|
|
823
823
|
<title>Auth protected</title>
|
|
824
824
|
<rect width="18" height="18" rx="3" fill="${mr}" stroke="${ns}" stroke-width="0.5"/>
|
|
825
825
|
<text x="9" y="13" text-anchor="middle" font-size="10">🛡</text>
|
|
826
826
|
</g>
|
|
827
|
-
`:
|
|
827
|
+
`:d}
|
|
828
828
|
|
|
829
|
-
${w?
|
|
829
|
+
${w?O`
|
|
830
830
|
<g transform="translate(${t.w-24},3)">
|
|
831
831
|
<title>No auth detected — this endpoint may be unprotected</title>
|
|
832
832
|
<rect width="18" height="18" rx="3" fill="#fff7ed" stroke="${Se}" stroke-width="0.5"/>
|
|
833
833
|
<text x="9" y="13" text-anchor="middle" font-size="10" fill="#ea580c">!</text>
|
|
834
834
|
</g>
|
|
835
|
-
`:
|
|
835
|
+
`:d}
|
|
836
836
|
|
|
837
|
-
${Z>0?
|
|
837
|
+
${Z>0?O`
|
|
838
838
|
<g transform="translate(${t.w-(H||w?46:24)},3)">
|
|
839
|
-
<rect width="18" height="18" rx="3" fill="${
|
|
840
|
-
stroke="${
|
|
841
|
-
class="${
|
|
839
|
+
<rect width="18" height="18" rx="3" fill="${Q?fr:vr}"
|
|
840
|
+
stroke="${Q?as:ls}" stroke-width="0.5"
|
|
841
|
+
class="${Q?"graph-pulse":""}"/>
|
|
842
842
|
<text x="9" y="13" text-anchor="middle" font-size="9" font-weight="600"
|
|
843
|
-
fill="${
|
|
843
|
+
fill="${Q?as:ls}">${Z}</text>
|
|
844
844
|
</g>
|
|
845
|
-
`:
|
|
845
|
+
`:d}
|
|
846
846
|
|
|
847
|
-
${tt>0?
|
|
847
|
+
${tt>0?O`
|
|
848
848
|
<circle cx="${t.w-8}" cy="8" r="5" fill="${ut}" stroke="white" stroke-width="1"/>
|
|
849
849
|
<text x="${t.w-8}" y="11" text-anchor="middle" font-size="7" fill="white" font-weight="700">${tt}</text>
|
|
850
|
-
`:t.stats.errorRate>.05?
|
|
850
|
+
`:t.stats.errorRate>.05?O`<circle cx="${t.w-12}" cy="12" r="4" fill="${ut}"/>`:d}
|
|
851
851
|
|
|
852
|
-
${g?.isMiddleware&&x.has("auth")?
|
|
852
|
+
${g?.isMiddleware&&x.has("auth")?O`
|
|
853
853
|
<text x="${t.w}" y="${t.h+12}" text-anchor="end" font-size="8" fill="#6b7280"
|
|
854
854
|
font-family="ui-monospace,monospace">middleware</text>
|
|
855
|
-
`:
|
|
855
|
+
`:d}
|
|
856
856
|
</g>
|
|
857
|
-
`}nodeSubtitle(t){switch(t.type){case "endpoint":return `${t.stats.requestCount} req \xB7 ${t.stats.avgLatencyMs}ms${t.stats.avgQueryCount>0?` \xB7 ${t.stats.avgQueryCount}q`:""}`;case "action":return `${t.stats.requestCount}\xD7 \xB7 ${t.stats.avgLatencyMs}ms`;case "table":return "table";default:return "service"}}renderEdge(t,e,s,i){let n=this.activeNodeId,a=e===null||e.has(t.data.source)&&e.has(t.data.target),c=s?.has(t.key)??false,
|
|
857
|
+
`}nodeSubtitle(t){switch(t.type){case "endpoint":return `${t.stats.requestCount} req \xB7 ${t.stats.avgLatencyMs}ms${t.stats.avgQueryCount>0?` \xB7 ${t.stats.avgQueryCount}q`:""}`;case "action":return `${t.stats.requestCount}\xD7 \xB7 ${t.stats.avgLatencyMs}ms`;case "table":return "table";default:return "service"}}renderEdge(t,e,s,i){let n=this.activeNodeId,a=e===null||e.has(t.data.source)&&e.has(t.data.target),c=s?.has(t.key)??false,p=a?n===null?.25:.6:.04,h=a&&n!==null,m=t.color,f=t.thickness;if(s&&!c&&(p=.04),c&&(p=.85,m=os,f=Math.max(f,1.8)),this.activeLayers.has("heat")&&!c){let tt=Math.max(1,...this.graphEdges.map(I=>I.stats.frequency)),W=t.data.stats.frequency/tt;m=is(W),a&&(p=Math.max(p,.4));}let S=this.activeLayers.has("issues")&&t.data.annotations?.hasIssue,R=Math.abs(t.tx-t.sx),P=Math.min(R*.45,120),$=t.sx<t.tx,v=$?t.sx+P:t.sx-P,g=$?t.tx-P:t.tx+P,x=(t.sx+t.tx)/2,w=(t.sy+t.ty)/2,H=4,Z=S?ut:m,Q=S?Math.max(f,1.5):f;return O`
|
|
858
858
|
<g style="transition:opacity .15s">
|
|
859
859
|
<path d="M${t.sx},${t.sy} C${v},${t.sy} ${g},${t.ty} ${t.tx},${t.ty}"
|
|
860
|
-
fill="none" stroke="${Z}" stroke-width="${
|
|
861
|
-
stroke-opacity="${
|
|
860
|
+
fill="none" stroke="${Z}" stroke-width="${Q}"
|
|
861
|
+
stroke-opacity="${p}" stroke-linecap="round"
|
|
862
862
|
stroke-dasharray="${c?"6,4":t.dashed?"3,3":"none"}"
|
|
863
863
|
class="${c?"graph-flow-edge":""}"/>
|
|
864
|
-
<polygon points="${t.tx},${t.ty} ${t.tx+(
|
|
865
|
-
fill="${Z}" fill-opacity="${
|
|
866
|
-
${h?
|
|
864
|
+
<polygon points="${t.tx},${t.ty} ${t.tx+($?-H*1.5:H*1.5)},${t.ty-H} ${t.tx+($?-H*1.5:H*1.5)},${t.ty+H}"
|
|
865
|
+
fill="${Z}" fill-opacity="${p}"/>
|
|
866
|
+
${h?O`
|
|
867
867
|
<rect x="${x-t.label.length*2.8-2}" y="${w-7}" width="${t.label.length*5.6+8}" height="14"
|
|
868
868
|
rx="4" fill="white" fill-opacity="0.92" stroke="${m}" stroke-width="0.4" stroke-opacity="0.15"/>
|
|
869
869
|
<text x="${x}" y="${w+3.5}" fill="${m}" font-size="8" font-weight="500"
|
|
870
870
|
font-family="ui-monospace,monospace" text-anchor="middle" opacity="0.85">${t.label}</text>
|
|
871
|
-
`:
|
|
872
|
-
${S?
|
|
871
|
+
`:d}
|
|
872
|
+
${S?O`
|
|
873
873
|
<text x="${x}" y="${w-10}" fill="${ut}" font-size="8" font-weight="600"
|
|
874
874
|
text-anchor="middle">⚠ N+1</text>
|
|
875
|
-
`:
|
|
875
|
+
`:d}
|
|
876
876
|
</g>
|
|
877
|
-
`}renderDetailPanel(t){let{node:e,edges:s}=t,i=
|
|
877
|
+
`}renderDetailPanel(t){let{node:e,edges:s}=t,i=kt[e.type]||kt.endpoint,n=e.annotations,a=(n?.securityFindings?.length??0)>0,c=(n?.openIssueCount??0)>0,p=n?.p95Ms!==void 0,m=[{key:"overview",label:"Overview",show:true},{key:"security",label:`Security${a?` (${n.securityFindings.length})`:""}`,show:a},{key:"performance",label:"Perf",show:p},{key:"issues",label:`Issues${c?` (${n.openIssueCount})`:""}`,show:c}].filter(f=>f.show);return l`
|
|
878
878
|
<div class="graph-detail">
|
|
879
879
|
<div class="graph-detail-head">
|
|
880
880
|
<div>
|
|
881
881
|
<div class="graph-detail-badge" style="color:${i.stroke}">${i.icon} ${e.type}</div>
|
|
882
882
|
<div class="graph-detail-name">${e.label}</div>
|
|
883
|
-
${n?.hasAuth?l`<span class="graph-detail-auth-badge">🛡 Authenticated</span>`:
|
|
884
|
-
${n?.isMiddleware?l`<span class="graph-detail-mw-badge">middleware</span>`:
|
|
883
|
+
${n?.hasAuth?l`<span class="graph-detail-auth-badge">🛡 Authenticated</span>`:d}
|
|
884
|
+
${n?.isMiddleware?l`<span class="graph-detail-mw-badge">middleware</span>`:d}
|
|
885
885
|
</div>
|
|
886
886
|
<button class="graph-detail-close" @click=${()=>{this.locked=null;}}>✕</button>
|
|
887
887
|
</div>
|
|
@@ -893,12 +893,12 @@
|
|
|
893
893
|
@click=${()=>{this.detailTab=f.key;}}>${f.label}</button>
|
|
894
894
|
`)}
|
|
895
895
|
</div>
|
|
896
|
-
`:
|
|
896
|
+
`:d}
|
|
897
897
|
|
|
898
|
-
${this.detailTab==="overview"?this.renderOverviewTab(e,s):
|
|
899
|
-
${this.detailTab==="security"?this.renderSecurityTab(n):
|
|
900
|
-
${this.detailTab==="performance"?this.renderPerformanceTab(e,n):
|
|
901
|
-
${this.detailTab==="issues"?this.renderIssuesTab(n):
|
|
898
|
+
${this.detailTab==="overview"?this.renderOverviewTab(e,s):d}
|
|
899
|
+
${this.detailTab==="security"?this.renderSecurityTab(n):d}
|
|
900
|
+
${this.detailTab==="performance"?this.renderPerformanceTab(e,n):d}
|
|
901
|
+
${this.detailTab==="issues"?this.renderIssuesTab(n):d}
|
|
902
902
|
</div>
|
|
903
903
|
`}renderOverviewTab(t,e){return l`
|
|
904
904
|
<div class="graph-detail-stats">
|
|
@@ -907,7 +907,7 @@
|
|
|
907
907
|
<div class="graph-detail-lbl">${t.type==="action"?"OCCURRENCES":"REQUESTS"}</div>
|
|
908
908
|
</div>
|
|
909
909
|
<div class="graph-detail-stat">
|
|
910
|
-
<div class="graph-detail-val" style="color:${
|
|
910
|
+
<div class="graph-detail-val" style="color:${Ot(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
911
911
|
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
912
912
|
</div>
|
|
913
913
|
${t.stats.avgQueryCount>0?l`
|
|
@@ -915,13 +915,13 @@
|
|
|
915
915
|
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
916
916
|
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
917
917
|
</div>
|
|
918
|
-
`:
|
|
918
|
+
`:d}
|
|
919
919
|
${t.stats.errorRate>.01?l`
|
|
920
920
|
<div class="graph-detail-stat">
|
|
921
921
|
<div class="graph-detail-val" style="color:${ut}">${Math.round(t.stats.errorRate*100)}%</div>
|
|
922
922
|
<div class="graph-detail-lbl">ERRORS</div>
|
|
923
923
|
</div>
|
|
924
|
-
`:
|
|
924
|
+
`:d}
|
|
925
925
|
</div>
|
|
926
926
|
|
|
927
927
|
${e.length>0?l`
|
|
@@ -934,14 +934,14 @@
|
|
|
934
934
|
<span class="graph-detail-dim">${s.stats.frequency}× · ${s.stats.avgLatencyMs}ms</span>
|
|
935
935
|
</div>
|
|
936
936
|
`})}
|
|
937
|
-
`:
|
|
937
|
+
`:d}
|
|
938
938
|
|
|
939
939
|
${e.some(s=>s.patterns?.length)?l`
|
|
940
940
|
<div class="graph-detail-sec">SQL Patterns</div>
|
|
941
941
|
${e.filter(s=>s.patterns).flatMap(s=>s.patterns).map(s=>l`
|
|
942
942
|
<pre class="graph-detail-sql">${s.length>200?s.slice(0,200)+"\u2026":s}</pre>
|
|
943
943
|
`)}
|
|
944
|
-
`:
|
|
944
|
+
`:d}
|
|
945
945
|
`}renderSecurityTab(t){return t?.securityFindings?.length?l`
|
|
946
946
|
${t.securityFindings.map(e=>l`
|
|
947
947
|
<div class="graph-detail-finding">
|
|
@@ -954,12 +954,12 @@
|
|
|
954
954
|
<div class="graph-detail-stats">
|
|
955
955
|
${e?.p95Ms!==void 0?l`
|
|
956
956
|
<div class="graph-detail-stat">
|
|
957
|
-
<div class="graph-detail-val" style="color:${
|
|
957
|
+
<div class="graph-detail-val" style="color:${Ot(e.p95Ms)}">${e.p95Ms}ms</div>
|
|
958
958
|
<div class="graph-detail-lbl">P95 LATENCY</div>
|
|
959
959
|
</div>
|
|
960
|
-
`:
|
|
960
|
+
`:d}
|
|
961
961
|
<div class="graph-detail-stat">
|
|
962
|
-
<div class="graph-detail-val" style="color:${
|
|
962
|
+
<div class="graph-detail-val" style="color:${Ot(t.stats.avgLatencyMs)}">${t.stats.avgLatencyMs}ms</div>
|
|
963
963
|
<div class="graph-detail-lbl">AVG LATENCY</div>
|
|
964
964
|
</div>
|
|
965
965
|
${t.stats.avgQueryCount>0?l`
|
|
@@ -967,7 +967,7 @@
|
|
|
967
967
|
<div class="graph-detail-val">${t.stats.avgQueryCount}</div>
|
|
968
968
|
<div class="graph-detail-lbl">QUERIES/REQ</div>
|
|
969
969
|
</div>
|
|
970
|
-
`:
|
|
970
|
+
`:d}
|
|
971
971
|
<div class="graph-detail-stat">
|
|
972
972
|
<div class="graph-detail-val">${t.stats.requestCount}</div>
|
|
973
973
|
<div class="graph-detail-lbl">TOTAL REQS</div>
|
|
@@ -983,7 +983,7 @@
|
|
|
983
983
|
<div class="graph-detail-finding-meta">${s.type}</div>
|
|
984
984
|
</div>
|
|
985
985
|
`)}
|
|
986
|
-
`:
|
|
986
|
+
`:d}
|
|
987
987
|
`}renderIssuesTab(t){let e=t?.openIssueCount??0;return e===0?l`<div class="graph-detail-empty">No open issues</div>`:l`
|
|
988
988
|
<div class="graph-detail-issue-summary">
|
|
989
989
|
<div class="graph-detail-stat">
|
|
@@ -992,7 +992,7 @@
|
|
|
992
992
|
</div>
|
|
993
993
|
</div>
|
|
994
994
|
<p class="graph-detail-hint">View the Issues tab for full details and remediation hints.</p>
|
|
995
|
-
`}getSelectedNodeDetail(){if(!this.locked)return null;let t=this.graphNodes.find(s=>s.id===this.locked);if(!t)return null;let e=this.graphEdges.filter(s=>s.source===this.locked||s.target===this.locked);return {node:t,edges:e}}};u([
|
|
995
|
+
`}getSelectedNodeDetail(){if(!this.locked)return null;let t=this.graphNodes.find(s=>s.id===this.locked);if(!t)return null;let e=this.graphEdges.filter(s=>s.source===this.locked||s.target===this.locked);return {node:t,edges:e}}};u([C({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([_("bk-graph-view")],M);var ie=class extends J{constructor(){super(...arguments);this.activeTab="requests";this.handleNavigateExplorer=t=>{let e=t.detail;ve.some(s=>s.key===e)&&(this.activeTab=e);};}connectedCallback(){super.connectedCallback(),window.addEventListener("navigate-explorer",this.handleNavigateExplorer);}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("navigate-explorer",this.handleNavigateExplorer);}switchTab(t){this.activeTab=t,fetch(`${T.tab}?event=explorer.${t}`).catch(()=>{});}getCount(t){let e=this.store.state;switch(t){case "requests":return e.requests.filter(s=>!s.path?.startsWith(U)).length;case "fetches":return e.fetches.length;case "queries":return e.queries.length;case "logs":return e.logs.length;case "errors":return e.errors.length}}render(){return l`
|
|
996
996
|
<div class="explorer-tabs">
|
|
997
997
|
${ve.map(t=>l`
|
|
998
998
|
<button class="explorer-tab ${this.activeTab===t.key?"active":""}"
|
|
@@ -1017,22 +1017,27 @@
|
|
|
1017
1017
|
<div style="display:${this.activeTab==="errors"?"block":"none"}">
|
|
1018
1018
|
<bk-errors-view></bk-errors-view>
|
|
1019
1019
|
</div>
|
|
1020
|
-
`}};u([
|
|
1020
|
+
`}};u([b()],ie.prototype,"activeTab",2),ie=u([_("bk-explorer-view")],ie);var Vi=[{key:"all",label:"All"},{key:"security",label:"Security"},{key:"performance",label:"Performance"},{key:"reliability",label:"Reliability"}],mt=class extends J{constructor(){super(...arguments);this.filter="all";this.expandedIdx=-1;this.showDismissed=false;}getFilteredIssues(t){return (this.filter==="all"?t:t.filter(s=>Xt(s)===this.filter)).sort((s,i)=>{let n=h=>h==="open"||h==="regressed"?0:h==="fixing"?1:2,a=n(s.state)-n(i.state);if(a!==0)return a;let c=Y[s.issue.severity]?.sort??3,p=Y[i.issue.severity]?.sort??3;return c-p})}getCounts(t){let e={all:t.length,security:0,performance:0,reliability:0};for(let s of t)e[Xt(s)]++;return e}render(){if(!(this.store.state.requests.length>0||this.store.state.queries.length>0))return l`<bk-empty-state
|
|
1021
1021
|
title="Waiting for requests..."
|
|
1022
1022
|
subtitle="Start using your app to see insights here"
|
|
1023
|
-
></bk-empty-state>`;let e=(this.store.state.issues||[]).filter(ir),s=this.getFilteredIssues(e),i=this.getCounts(e),n=s.filter(f=>f.state==="open"&&f.aiStatus!=="wont_fix"),a=s.filter(f=>f.state==="regressed"&&f.aiStatus!=="wont_fix"),c=s.filter(f=>f.state==="fixing"),
|
|
1023
|
+
></bk-empty-state>`;let e=(this.store.state.issues||[]).filter(ir),s=this.getFilteredIssues(e),i=this.getCounts(e),n=s.filter(f=>f.state==="open"&&f.aiStatus!=="wont_fix"),a=s.filter(f=>f.state==="regressed"&&f.aiStatus!=="wont_fix"),c=s.filter(f=>f.state==="fixing"),p=s.filter(f=>f.state==="resolved"),h=s.filter(f=>f.aiStatus==="wont_fix"),m=0;return l`
|
|
1024
1024
|
<div class="insights-filters">
|
|
1025
|
-
${
|
|
1025
|
+
${Vi.map(f=>l`
|
|
1026
1026
|
<button class="insights-chip ${this.filter===f.key?"active":""}"
|
|
1027
1027
|
@click=${()=>{this.filter=f.key,this.expandedIdx=-1;}}>
|
|
1028
1028
|
${f.label}
|
|
1029
|
-
${i[f.key]>0?l`<span class="insights-chip-count">${i[f.key]}</span>`:
|
|
1029
|
+
${i[f.key]>0?l`<span class="insights-chip-count">${i[f.key]}</span>`:d}
|
|
1030
1030
|
</button>
|
|
1031
1031
|
`)}
|
|
1032
1032
|
</div>
|
|
1033
1033
|
|
|
1034
|
+
${this.renderSummaryBar(e)}
|
|
1035
|
+
|
|
1034
1036
|
<div class="insights-list">
|
|
1035
|
-
${n.length
|
|
1037
|
+
${n.length+a.length>0?l`
|
|
1038
|
+
<div class="insights-ai-hint">Copy to your AI: <code>"Fix brakit findings"</code></div>
|
|
1039
|
+
`:d}
|
|
1040
|
+
${n.length===0&&a.length===0&&c.length===0&&p.length===0&&h.length===0?l`<div class="insights-empty"><span class="insights-empty-icon">\u2713</span>${this.filter==="all"?"All clear \u2014 no issues detected":`No ${this.filter} issues`}</div>`:d}
|
|
1036
1041
|
|
|
1037
1042
|
${a.length>0?l`
|
|
1038
1043
|
<div class="insights-section insights-section-regressed">
|
|
@@ -1040,7 +1045,7 @@
|
|
|
1040
1045
|
<span class="insights-section-count">${a.length}</span>
|
|
1041
1046
|
</div>
|
|
1042
1047
|
${a.map(f=>this.renderIssueCard(f,m++))}
|
|
1043
|
-
`:
|
|
1048
|
+
`:d}
|
|
1044
1049
|
|
|
1045
1050
|
${n.length>0?l`
|
|
1046
1051
|
<div class="insights-section">
|
|
@@ -1048,7 +1053,7 @@
|
|
|
1048
1053
|
<span class="insights-section-count">${n.length}</span>
|
|
1049
1054
|
</div>
|
|
1050
1055
|
${n.map(f=>this.renderIssueCard(f,m++))}
|
|
1051
|
-
`:
|
|
1056
|
+
`:d}
|
|
1052
1057
|
|
|
1053
1058
|
${c.length>0?l`
|
|
1054
1059
|
<div class="insights-section insights-section-verifying">
|
|
@@ -1056,25 +1061,31 @@
|
|
|
1056
1061
|
<span class="insights-section-count">${c.length}</span>
|
|
1057
1062
|
</div>
|
|
1058
1063
|
${c.map(f=>this.renderIssueCard(f,m++))}
|
|
1059
|
-
`:
|
|
1064
|
+
`:d}
|
|
1060
1065
|
|
|
1061
|
-
${
|
|
1066
|
+
${p.length>0?l`
|
|
1062
1067
|
<div class="insights-section insights-section-resolved">
|
|
1063
1068
|
<span class="insights-section-icon">\u2713</span> Resolved
|
|
1064
|
-
<span class="insights-section-count">${
|
|
1069
|
+
<span class="insights-section-count">${p.length}</span>
|
|
1065
1070
|
</div>
|
|
1066
|
-
${
|
|
1067
|
-
`:
|
|
1071
|
+
${p.map(f=>this.renderIssueCard(f,m++))}
|
|
1072
|
+
`:d}
|
|
1068
1073
|
|
|
1069
1074
|
${h.length>0?l`
|
|
1070
1075
|
<div class="insights-section insights-section-dismissed" @click=${()=>{this.showDismissed=!this.showDismissed;}}>
|
|
1071
1076
|
<span class="insights-section-icon">${this.showDismissed?"\u25BE":"\u25B8"}</span> Won't Fix
|
|
1072
1077
|
<span class="insights-section-count">${h.length}</span>
|
|
1073
1078
|
</div>
|
|
1074
|
-
${this.showDismissed?h.map(f=>this.renderIssueCard(f,m++)):
|
|
1075
|
-
`:
|
|
1079
|
+
${this.showDismissed?h.map(f=>this.renderIssueCard(f,m++)):d}
|
|
1080
|
+
`:d}
|
|
1081
|
+
</div>
|
|
1082
|
+
`}renderSummaryBar(t){let e=t.filter(a=>(a.state==="open"||a.state==="regressed")&&a.aiStatus!=="wont_fix"),s=e.filter(a=>a.issue.severity==="critical").length,i=e.filter(a=>a.issue.severity==="warning").length,n=t.filter(a=>a.state==="resolved").length;return s===0&&i===0&&n===0?d:l`
|
|
1083
|
+
<div class="insights-summary">
|
|
1084
|
+
${s>0?l`<span class="insights-summary-stat critical">${s} critical</span>`:d}
|
|
1085
|
+
${i>0?l`<span class="insights-summary-stat warning">${i} warning</span>`:d}
|
|
1086
|
+
${n>0?l`<span class="insights-summary-stat resolved">${n} resolved</span>`:d}
|
|
1076
1087
|
</div>
|
|
1077
|
-
`}renderIssueCard(t,e){let s=t.issue,i=Y[s.severity]||Y.info,n=this.expandedIdx===e,a=t.state==="resolved",c=t.state==="fixing",
|
|
1088
|
+
`}renderIssueCard(t,e){let s=t.issue,i=Y[s.severity]||Y.info,n=this.expandedIdx===e,a=t.state==="resolved",c=t.state==="fixing",p=Xt(t);return l`
|
|
1078
1089
|
<div class="insights-card ${n?"expanded":""} ${a?"resolved":""}"
|
|
1079
1090
|
@click=${()=>{this.expandedIdx=this.expandedIdx===e?-1:e;}}>
|
|
1080
1091
|
<div class="insights-card-left">
|
|
@@ -1083,59 +1094,61 @@
|
|
|
1083
1094
|
<div class="insights-card-body">
|
|
1084
1095
|
<div class="insights-card-header">
|
|
1085
1096
|
<span class="insights-card-title ${a?"resolved":""}">${s.title}</span>
|
|
1086
|
-
<span class="insights-card-cat">${
|
|
1087
|
-
${s.count?l`<span class="insights-card-count">${s.count}\u00D7</span>`:
|
|
1088
|
-
${t.state==="regressed"?l`<span class="insights-badge-regressed">regressed</span>`:
|
|
1089
|
-
${c?l`<span class="insights-badge-verifying">verifying</span>`:
|
|
1090
|
-
${a?l`<span class="insights-badge-resolved">resolved</span>`:
|
|
1097
|
+
<span class="insights-card-cat">${p}</span>
|
|
1098
|
+
${s.count?l`<span class="insights-card-count">${s.count}\u00D7</span>`:d}
|
|
1099
|
+
${t.state==="regressed"?l`<span class="insights-badge-regressed">regressed</span>`:d}
|
|
1100
|
+
${c?l`<span class="insights-badge-verifying">verifying</span>`:d}
|
|
1101
|
+
${a?l`<span class="insights-badge-resolved">resolved</span>`:d}
|
|
1091
1102
|
</div>
|
|
1092
1103
|
<div class="insights-card-desc">${s.desc}</div>
|
|
1093
|
-
${
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1104
|
+
${n?l`
|
|
1105
|
+
${s.detail?l`<div class="insights-card-detail">${s.detail}</div>`:d}
|
|
1106
|
+
${t.cleanHitsSinceLastSeen>0?l`
|
|
1107
|
+
<div class="insights-card-progress">${t.cleanHitsSinceLastSeen}/${5} clean requests</div>
|
|
1108
|
+
`:d}
|
|
1109
|
+
${s.hint?l`<div class="insights-card-hint">${s.hint}</div>`:d}
|
|
1110
|
+
`:d}
|
|
1098
1111
|
</div>
|
|
1099
|
-
${s.hint?l`<span class="insights-card-arrow">${n?"\u2193":"\u2192"}</span>`:
|
|
1112
|
+
${s.hint?l`<span class="insights-card-arrow">${n?"\u2193":"\u2192"}</span>`:d}
|
|
1100
1113
|
</div>
|
|
1101
|
-
`}};u([
|
|
1114
|
+
`}};u([b()],mt.prototype,"filter",2),u([b()],mt.prototype,"expandedIdx",2),u([b()],mt.prototype,"showDismissed",2),mt=u([_("bk-insights-view")],mt);function Ki(o){return o===0?"<1ms":y(o)}var k=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=k.cache.get(this.requestId);if(t){this.data=t;return}this.loading=true;try{let e=await fetch(`${T.activity}?requestId=${this.requestId}`);if(!e.ok){this.failed=!0,this.loading=!1;return}let s=await e.json();if(k.cache.size>=We){let i=k.cache.keys().next().value;i!==void 0&&k.cache.delete(i);}k.cache.set(this.requestId,s),this.data=s,this.loading=!1;}catch(e){console.debug("[brakit] timeline load failed:",e),this.failed=true,this.loading=false;}}toggleSql(t,e){e.stopPropagation(),this.expandedSqlIdx=this.expandedSqlIdx===t?-1:t;}copySql(t,e){e.stopPropagation(),navigator.clipboard.writeText(t).then(()=>D.show("SQL copied")).catch(()=>D.show("Copy failed"));}render(){if(this.loading)return l`<div class="tl-loading">Loading activity...</div>`;if(this.failed||!this.data||this.data.total===0)return d;let t=this.data,e=t.timeline[0]?.timestamp??0;return l`
|
|
1102
1115
|
<div class="tl-header">
|
|
1103
1116
|
<span class="tl-title">Activity Timeline</span>
|
|
1104
1117
|
<span class="tl-counts">
|
|
1105
|
-
${t.counts.queries>0?l`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:
|
|
1106
|
-
${t.counts.fetches>0?l`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:
|
|
1107
|
-
${t.counts.logs>0?l`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:
|
|
1108
|
-
${t.counts.errors>0?l`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:
|
|
1118
|
+
${t.counts.queries>0?l`<span class="tl-count tl-count-query">${t.counts.queries} quer${t.counts.queries===1?"y":"ies"}</span>`:d}
|
|
1119
|
+
${t.counts.fetches>0?l`<span class="tl-count tl-count-fetch">${t.counts.fetches} fetch${t.counts.fetches===1?"":"es"}</span>`:d}
|
|
1120
|
+
${t.counts.logs>0?l`<span class="tl-count tl-count-log">${t.counts.logs} log${t.counts.logs===1?"":"s"}</span>`:d}
|
|
1121
|
+
${t.counts.errors>0?l`<span class="tl-count tl-count-error">${t.counts.errors} error${t.counts.errors===1?"":"s"}</span>`:d}
|
|
1109
1122
|
</span>
|
|
1110
1123
|
</div>
|
|
1111
1124
|
<div class="tl-events">${this.renderTimeline(t.timeline,e)}</div>
|
|
1112
|
-
`}renderTimeline(t,e){let s=new Map,i=[];for(let a of t){let c=a.type==="query"?a.data.parentFetchId:void 0;if(a.type==="query"&&c){let
|
|
1125
|
+
`}renderTimeline(t,e){let s=new Map,i=[];for(let a of t){let c=a.type==="query"?a.data.parentFetchId:void 0;if(a.type==="query"&&c){let p=s.get(c);p||(p=[],s.set(c,p)),p.push(a);}else i.push(a);}let n=0;return i.map(a=>{let c=n++,p=a.type==="fetch"?a.data.fetchId:void 0,h=p?s.get(p):void 0;if(h&&h.length>0){let m=h.length;return l`
|
|
1113
1126
|
${this.renderEvent(a,c,e)}
|
|
1114
1127
|
<div class="tl-nested">
|
|
1115
1128
|
<span class="tl-nested-label">${m} nested quer${m===1?"y":"ies"}</span>
|
|
1116
1129
|
${h.map(f=>{let S=n++;return this.renderEvent(f,S,e,true)})}
|
|
1117
1130
|
</div>
|
|
1118
|
-
`}return this.renderEvent(a,c,e)})}renderEvent(t,e,s,i=false){let n=Bs[t.type]||"var(--text-dim)",a=Ws[t.type]||t.type,c="+"+y(Math.round(t.timestamp-s)),
|
|
1131
|
+
`}return this.renderEvent(a,c,e)})}renderEvent(t,e,s,i=false){let n=Bs[t.type]||"var(--text-dim)",a=Ws[t.type]||t.type,c="+"+y(Math.round(t.timestamp-s)),p=t.type==="query"?t.data.sql:void 0,h=!!p,m=this.expandedSqlIdx===e;return l`
|
|
1119
1132
|
<div class="tl-event ${h?"tl-clickable":""} ${i?"tl-nested-event":""}"
|
|
1120
1133
|
style="${h?"":`border-left-color:${n}`}"
|
|
1121
|
-
@click=${h?f=>this.toggleSql(e,f):
|
|
1134
|
+
@click=${h?f=>this.toggleSql(e,f):d}>
|
|
1122
1135
|
<span class="tl-event-time">${c}</span>
|
|
1123
1136
|
<span class="tl-event-type" style="color:${n}">${a}</span>
|
|
1124
1137
|
${this.renderEventContent(t)}
|
|
1125
|
-
${
|
|
1138
|
+
${p?l`
|
|
1126
1139
|
<div class="tl-event-sql ${m?"open":""}">
|
|
1127
|
-
<button class="tl-sql-copy" @click=${f=>this.copySql(
|
|
1128
|
-
${
|
|
1129
|
-
</div>`:
|
|
1140
|
+
<button class="tl-sql-copy" @click=${f=>this.copySql(p,f)}>Copy</button>
|
|
1141
|
+
${p}
|
|
1142
|
+
</div>`:d}
|
|
1130
1143
|
</div>
|
|
1131
1144
|
`}renderEventContent(t){switch(t.type){case "fetch":{let e=t.data,s=e.statusCode>=400;return l`
|
|
1132
1145
|
<span class="tl-event-summary">${e.method} ${e.url}</span>
|
|
1133
1146
|
<span class="tl-event-status" style="${s?"color:var(--red)":""}">${e.statusCode}</span>
|
|
1134
1147
|
<span class="tl-event-dur">${y(e.durationMs)}</span>
|
|
1135
|
-
`}case "query":{let e=t.data,s=(e.normalizedOp||e.operation||"?").toUpperCase(),i=e.table||e.model||"",n=
|
|
1148
|
+
`}case "query":{let e=t.data,s=(e.normalizedOp||e.operation||"?").toUpperCase(),i=e.table||e.model||"",n=be[s]||"var(--text-dim)";return l`
|
|
1136
1149
|
<span class="tl-event-summary"><span style="color:${n};font-weight:600">${s}</span> ${i}</span>
|
|
1137
|
-
<span class="tl-event-dur">${
|
|
1138
|
-
`}case "log":{let e=t.data,s=qs[e.level]||"var(--text-dim)";return l`<span class="tl-event-summary"><span style="color:${s}">${e.level.toUpperCase()}</span> ${e.message}</span>`}case "error":{let e=t.data;return l`<span class="tl-event-summary" style="color:var(--red)">${e.name}: ${e.message}</span>`}default:return
|
|
1150
|
+
<span class="tl-event-dur">${Ki(e.durationMs)}</span>
|
|
1151
|
+
`}case "log":{let e=t.data,s=qs[e.level]||"var(--text-dim)";return l`<span class="tl-event-summary"><span style="color:${s}">${e.level.toUpperCase()}</span> ${e.message}</span>`}case "error":{let e=t.data;return l`<span class="tl-event-summary" style="color:var(--red)">${e.name}: ${e.message}</span>`}default:return d}}};k.cache=new Map,u([C({context:A})],k.prototype,"store",2),u([L({attribute:"request-id"})],k.prototype,"requestId",2),u([L({attribute:"request-started",type:Number})],k.prototype,"requestStarted",2),u([b()],k.prototype,"data",2),u([b()],k.prototype,"loading",2),u([b()],k.prototype,"failed",2),u([b()],k.prototype,"expandedSqlIdx",2),k=u([_("bk-timeline-panel")],k);function Dt(o){try{return JSON.parse(o)}catch{return null}}var Re=class{constructor(r,t){this.host=r;this.store=t;this.retryCount=0;this.boundHandlers={fetch:r=>{let t=Dt(r.data);t&&this.store.prependFetch(t);},log:r=>{let t=Dt(r.data);t&&this.store.prependLog(t);},error:r=>{let t=Dt(r.data);t&&this.store.prependError(t);},query:r=>{let t=Dt(r.data);t&&this.store.prependQuery(t);},issues:r=>{let t=Dt(r.data);t&&this.store.setIssues(t);}};r.addController(this);}hostConnected(){this.connect();}hostDisconnected(){this.removeListeners(),this.eventSource?.close(),clearTimeout(this.reloadTimer),clearTimeout(this.perfReloadTimer),clearTimeout(this.reconnectTimer);}removeListeners(){this.eventSource&&(this.eventSource.removeEventListener(he,this.boundHandlers.fetch),this.eventSource.removeEventListener("log",this.boundHandlers.log),this.eventSource.removeEventListener(ue,this.boundHandlers.error),this.eventSource.removeEventListener(me,this.boundHandlers.query),this.eventSource.removeEventListener(fe,this.boundHandlers.issues));}connect(){this.removeListeners(),this.eventSource?.close(),this.eventSource=new EventSource(T.events),this.eventSource.onopen=()=>{this.retryCount=0;},this.eventSource.onerror=()=>{this.eventSource?.close(),this.scheduleReconnect();},this.eventSource.onmessage=r=>{let t=Dt(r.data);t&&(t.path?.startsWith(U)||(this.store.prependRequest(t),clearTimeout(this.reloadTimer),this.reloadTimer=setTimeout(()=>this.reloadFlows(),300),this.store.state.activeView==="performance"&&(clearTimeout(this.perfReloadTimer),this.perfReloadTimer=setTimeout(()=>this.reloadMetrics(),je))));},this.eventSource.addEventListener(he,this.boundHandlers.fetch),this.eventSource.addEventListener("log",this.boundHandlers.log),this.eventSource.addEventListener(ue,this.boundHandlers.error),this.eventSource.addEventListener(me,this.boundHandlers.query),this.eventSource.addEventListener(fe,this.boundHandlers.issues);}scheduleReconnect(){if(this.retryCount>=10)return;let r=Math.min(1e3*2**this.retryCount,3e4);this.retryCount++,this.reconnectTimer=setTimeout(()=>this.connect(),r);}async reloadFlows(){try{let t=await(await fetch(T.flows)).json();this.store.setFlows(t.flows);}catch{}}async reloadMetrics(){try{let t=await(await fetch(T.metricsLive)).json();this.store.setMetrics(t.endpoints||[]);}catch{}}};function Ar(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>`}function Cr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`}function Ir(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>`}function Lr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 12.7V17h8v-2.3A7 7 0 0 0 12 2z"/></svg>`}function Mr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>`}function kr(){return l`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="3"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="18" r="3"/><line x1="9" y1="6" x2="15" y2="6"/><line x1="6" y1="9" x2="6" y2="15"/><line x1="18" y1="9" x2="18" y2="15"/><line x1="9" y1="18" x2="15" y2="18"/></svg>`}var ft=class extends E{constructor(){super(...arguments);this.store=new ye;this.activeView="overview";this.viewMode="simple";this.sse=new Re(this,this.store);this.handleStateChanged=t=>{t.detail==="activeView"&&(this.activeView=this.store.state.activeView),this.requestUpdate();};}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.loadInitialData(),this.store.addEventListener("state-changed",this.handleStateChanged);}disconnectedCallback(){super.disconnectedCallback(),this.store.removeEventListener("state-changed",this.handleStateChanged);}async loadInitialData(){try{let[t,e]=await Promise.all([j(T.flows),j(T.requests)]);this.store.setFlows(t.flows),this.store.setRequests(e.requests);}catch(t){console.warn("[brakit]",t);}try{let[t,e,s,i,n]=await Promise.all([j(T.fetches),j(T.errors),j(T.logs),j(T.queries),j(T.metricsLive)]);this.store.setFetches(t.entries),this.store.setErrors(e.entries),this.store.setLogs(s.entries),this.store.setQueries(i.entries),this.store.setMetrics(n.endpoints||[]);}catch(t){console.warn("[brakit]",t);}try{let t=await j(T.insights);this.store.setIssues(t.issues||[]);}catch(t){console.warn("[brakit]",t);}}switchView(t){t!==this.activeView&&(this.activeView=t,this.store.setActiveView(t),fetch(`${T.tab}?tab=${encodeURIComponent(t)}`).catch(()=>{}),t==="performance"&&this.sse.reloadMetrics());}async handleClear(){confirm(N.CLEAR_CONFIRM)&&(await fetch(T.clear,{method:"POST"}),this.store.clearAll(),D.show(N.CLEARED_TOAST));}handleCopyAsCurl(t){Lt(t);}render(){let t=this.store.state,e=t.requests.filter(c=>!c.path?.startsWith(U)),s=e.filter(c=>c.statusCode>=400).length,i=e.length>0?Math.round(e.reduce((c,p)=>c+p.durationMs,0)/e.length):0,n=(t.issues||[]).filter(_e).length,a=window.__BRAKIT_CONFIG__;return l`
|
|
1139
1152
|
<div class="app" id="app">
|
|
1140
1153
|
<aside class="sidebar">
|
|
1141
1154
|
<div class="sidebar-logo">
|
|
@@ -1144,10 +1157,10 @@
|
|
|
1144
1157
|
</div>
|
|
1145
1158
|
<nav class="sidebar-nav">
|
|
1146
1159
|
${this.renderSidebarItem("overview","Overview",Ar(),void 0)}
|
|
1147
|
-
${this.renderSidebarItem("actions","Actions",
|
|
1160
|
+
${this.renderSidebarItem("actions","Actions",Cr(),t.flows.length)}
|
|
1148
1161
|
${this.renderSidebarItem("insights","Insights",Lr(),n,n===0)}
|
|
1149
|
-
${this.renderSidebarItem("performance","Performance",
|
|
1150
|
-
${this.renderSidebarItem("graph","Graph",
|
|
1162
|
+
${this.renderSidebarItem("performance","Performance",Ir(),void 0)}
|
|
1163
|
+
${this.renderSidebarItem("graph","Graph",kr(),void 0)}
|
|
1151
1164
|
<div class="sidebar-divider"></div>
|
|
1152
1165
|
${this.renderSidebarItem("explorer","Explorer",Mr(),e.length+t.fetches.length+t.queries.length+t.logs.length+t.errors.length)}
|
|
1153
1166
|
</nav>
|
|
@@ -1165,7 +1178,7 @@
|
|
|
1165
1178
|
<button class="segmented-btn ${this.viewMode==="simple"?"active":""}" @click=${()=>{this.viewMode="simple",this.store.setViewMode("simple");}}>Quick</button>
|
|
1166
1179
|
<button class="segmented-btn ${this.viewMode==="detailed"?"active":""}" @click=${()=>{this.viewMode="detailed",this.store.setViewMode("detailed");}}>Detailed</button>
|
|
1167
1180
|
</div>
|
|
1168
|
-
`:
|
|
1181
|
+
`:d}
|
|
1169
1182
|
<button class="btn btn-danger" @click=${this.handleClear}>Clear</button>
|
|
1170
1183
|
</div>
|
|
1171
1184
|
</div>
|
|
@@ -1202,9 +1215,9 @@
|
|
|
1202
1215
|
<button class="sidebar-item ${this.activeView===t?"active":""}" @click=${()=>this.switchView(t)}>
|
|
1203
1216
|
<span class="item-icon">${s}</span>
|
|
1204
1217
|
<span class="item-label">${e}</span>
|
|
1205
|
-
${i!==void 0?l`<span class="item-count" style="display:${n?"none":""}">${i}</span>`:
|
|
1218
|
+
${i!==void 0?l`<span class="item-count" style="display:${n?"none":""}">${i}</span>`:d}
|
|
1206
1219
|
</button>
|
|
1207
|
-
`}};u([Je({context:A})],ft.prototype,"store",2),u([
|
|
1220
|
+
`}};u([Je({context:A})],ft.prototype,"store",2),u([b()],ft.prototype,"activeView",2),u([b()],ft.prototype,"viewMode",2),ft=u([_("bk-dashboard")],ft);
|
|
1208
1221
|
/*! Bundled license information:
|
|
1209
1222
|
|
|
1210
1223
|
@lit/reactive-element/css-tag.js:
|