@preventive/triage 1.0.0-alpha.41 → 1.0.0-alpha.42

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.
@@ -1 +1 @@
1
- import{Tc as a,Uc as b,Vc as c,Wc as d,Xc as e}from"./chunk-TY4RZD3Z.js";import"./chunk-K3X5EJBQ.js";import"./chunk-CVVJAXVY.js";import"./chunk-VIHGO77Y.js";import"./chunk-RFZQQXSG.js";import"./chunk-RSHMVYNS.js";import"./chunk-2AFQKRAM.js";import"./chunk-UYAXVRVL.js";import"./chunk-NRPC74AM.js";import"./chunk-VQJCXGIY.js";import"./chunk-W4J3MGLB.js";import"./chunk-GJ4IUCNS.js";import"./chunk-PNHBZGDD.js";import"./chunk-UXNUAIIC.js";import"./chunk-R5O6FAW4.js";import"./chunk-CMQA4DM6.js";import"./chunk-4F6JSF57.js";import"./chunk-YVZH5SHP.js";import"./chunk-4XCDFGDP.js";import"./chunk-3BZ7JERB.js";import"./chunk-TZ5KB2Y5.js";import"./chunk-XYFXXB2G.js";import"./chunk-DIJJNTRH.js";import"./chunk-WZDTZE64.js";import"./chunk-GNXOKIGP.js";import"./chunk-C4Z6ZBXV.js";import"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import"./chunk-V5O7M2YT.js";import"./chunk-OOJM4CTU.js";export{e as createManagedAnnotationRead,c as hydrateManagedReportTriage,a as initManagedTriagePush,d as refreshManagedReportTriage,b as resetManagedTriage};
1
+ import{Tc as a,Uc as b,Vc as c,Wc as d,Xc as e}from"./chunk-WOTMRDM3.js";import"./chunk-K3X5EJBQ.js";import"./chunk-CVVJAXVY.js";import"./chunk-VIHGO77Y.js";import"./chunk-RFZQQXSG.js";import"./chunk-RSHMVYNS.js";import"./chunk-CLPKNNC7.js";import"./chunk-UYAXVRVL.js";import"./chunk-NRPC74AM.js";import"./chunk-VQJCXGIY.js";import"./chunk-W4J3MGLB.js";import"./chunk-GJ4IUCNS.js";import"./chunk-PNHBZGDD.js";import"./chunk-UXNUAIIC.js";import"./chunk-R5O6FAW4.js";import"./chunk-CMQA4DM6.js";import"./chunk-4F6JSF57.js";import"./chunk-YVZH5SHP.js";import"./chunk-4XCDFGDP.js";import"./chunk-3BZ7JERB.js";import"./chunk-TZ5KB2Y5.js";import"./chunk-XYFXXB2G.js";import"./chunk-DIJJNTRH.js";import"./chunk-WZDTZE64.js";import"./chunk-GNXOKIGP.js";import"./chunk-C4Z6ZBXV.js";import"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import"./chunk-V5O7M2YT.js";import"./chunk-OOJM4CTU.js";export{e as createManagedAnnotationRead,c as hydrateManagedReportTriage,a as initManagedTriagePush,d as refreshManagedReportTriage,b as resetManagedTriage};
@@ -1 +1 @@
1
- import{nd as a,od as b,pd as c,qd as d,rd as e}from"./chunk-TY4RZD3Z.js";import"./chunk-K3X5EJBQ.js";import"./chunk-CVVJAXVY.js";import"./chunk-VIHGO77Y.js";import"./chunk-RFZQQXSG.js";import"./chunk-RSHMVYNS.js";import"./chunk-2AFQKRAM.js";import"./chunk-UYAXVRVL.js";import"./chunk-NRPC74AM.js";import"./chunk-VQJCXGIY.js";import"./chunk-W4J3MGLB.js";import"./chunk-GJ4IUCNS.js";import"./chunk-PNHBZGDD.js";import"./chunk-UXNUAIIC.js";import"./chunk-R5O6FAW4.js";import"./chunk-CMQA4DM6.js";import"./chunk-4F6JSF57.js";import"./chunk-YVZH5SHP.js";import"./chunk-4XCDFGDP.js";import"./chunk-3BZ7JERB.js";import"./chunk-TZ5KB2Y5.js";import"./chunk-XYFXXB2G.js";import"./chunk-DIJJNTRH.js";import"./chunk-WZDTZE64.js";import"./chunk-GNXOKIGP.js";import"./chunk-C4Z6ZBXV.js";import"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import"./chunk-V5O7M2YT.js";import"./chunk-OOJM4CTU.js";export{e as configureReportRevalidation,a as refreshGraph2Sidebar,b as refreshGraph2TopPkgs,c as render,d as renderEmptyWorkspace};
1
+ import{nd as a,od as b,pd as c,qd as d,rd as e}from"./chunk-WOTMRDM3.js";import"./chunk-K3X5EJBQ.js";import"./chunk-CVVJAXVY.js";import"./chunk-VIHGO77Y.js";import"./chunk-RFZQQXSG.js";import"./chunk-RSHMVYNS.js";import"./chunk-CLPKNNC7.js";import"./chunk-UYAXVRVL.js";import"./chunk-NRPC74AM.js";import"./chunk-VQJCXGIY.js";import"./chunk-W4J3MGLB.js";import"./chunk-GJ4IUCNS.js";import"./chunk-PNHBZGDD.js";import"./chunk-UXNUAIIC.js";import"./chunk-R5O6FAW4.js";import"./chunk-CMQA4DM6.js";import"./chunk-4F6JSF57.js";import"./chunk-YVZH5SHP.js";import"./chunk-4XCDFGDP.js";import"./chunk-3BZ7JERB.js";import"./chunk-TZ5KB2Y5.js";import"./chunk-XYFXXB2G.js";import"./chunk-DIJJNTRH.js";import"./chunk-WZDTZE64.js";import"./chunk-GNXOKIGP.js";import"./chunk-C4Z6ZBXV.js";import"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import"./chunk-V5O7M2YT.js";import"./chunk-OOJM4CTU.js";export{e as configureReportRevalidation,a as refreshGraph2Sidebar,b as refreshGraph2TopPkgs,c as render,d as renderEmptyWorkspace};
@@ -1,4 +1,4 @@
1
- import{a as ss,b as as,c as ae,d as pt,e as ut,f as ht,g as os,h as ns,i as ls,j as ds,k as cs,l as ps,m as us,n as hs,o as ms,p as mt,q as gs,r as bs,s as fs,t as vs}from"./chunk-ZBBOBQ43.js";import{a as g,b as Oi,c as Pi,d as zi}from"./chunk-KYCNJVB7.js";import{a as ge,b as L,c as Li,d as m}from"./chunk-IU3CCINR.js";import{K as vt,U as _t,W as yt,b as be,c as q,e as G,f as ct,g as gt,j as bt,ma as oe,n as fe,o as ft}from"./chunk-2AFQKRAM.js";import{a as dt,c as se}from"./chunk-UYAXVRVL.js";import{a as w,b as y,c as b}from"./chunk-35VE6CDH.js";import{b as lt}from"./chunk-NRPC74AM.js";import"./chunk-PNHBZGDD.js";import"./chunk-V3ME6FI5.js";import{a as M}from"./chunk-YVZH5SHP.js";import{a as wt}from"./chunk-4XCDFGDP.js";import"./chunk-3BZ7JERB.js";import"./chunk-TZ5KB2Y5.js";import"./chunk-XYFXXB2G.js";import{a as ie}from"./chunk-DIJJNTRH.js";import{f as nt,j}from"./chunk-WZDTZE64.js";import{B as at,D as re,J as ot}from"./chunk-GNXOKIGP.js";import"./chunk-C4Z6ZBXV.js";import{a as h,b as tt,c as a,d as te,g as d,j as B}from"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import{b as it,s as rt,t as st}from"./chunk-V5O7M2YT.js";import"./chunk-OOJM4CTU.js";var f=class extends B{static properties={session:{attribute:!1},allowShare:{attribute:!1},_loading:{state:!0}};constructor(){super(),this.session=null,this.allowShare=!1,this.appState=g,this._loading=!1,this._loadRequest=null}get _role(){return this.session?.role??null}get _csrf(){return this.session?.csrfToken??null}get _me(){return this.session?.id??null}disconnectedCallback(){this._loadRequest?.abort(),super.disconnectedCallback()}async _loadCollection(e,t,i,r){this._loadRequest?.abort();let o=new AbortController;this._loadRequest=o,this._loading=!0;try{await this.appState.load(e,t,i,{signal:o.signal,apply:r})}catch(n){!o.signal.aborted&&n?.name!=="AbortError"&&this.appState.read(e)===void 0&&(this._error=String(n?.message??n))}finally{this._loadRequest===o&&(this._loading=!1)}}};function $(s){return a`<div class="loading-rows" role="status"><span class="sr-only">${s}</span><div aria-hidden="true">${[0,1,2,3].map(()=>a`<div class="loading-row"><span></span><span></span></div>`)}</div></div>`}var ve=class extends G{get changeEvent(){return"local-item-change"}get searchLabel(){return"Search local files"}get optionsLabel(){return"Local files"}get noMatchesLabel(){return"No matching files."}get emptyLabel(){return"No local files available."}choices(){let e=this._query.trim().toLocaleLowerCase(),t=this.options.filter(i=>`${i.label} ${i.secondary??""}`.toLocaleLowerCase().includes(e));return{pinned:[],sections:[{options:t}],count:t.length,total:this.options.length,showFacets:!1}}};customElements.define("managed-local-item-selector",ve);var K=class{constructor(e,t,i){this.host=e,this.kind=t,this.upload=i,this.source=null,this.hasData=!1,this.open=!1,this.options=[],this.value=null,this.busy=!1,this.loading=!1,this.error="",this.success="",this.generation=0,this.readAbort=null,this.onChange=r=>{if(r?.kind){if(r.kind!==this.kind)return;if(r.value!==this.value){this.refresh();return}}this.readAbort&&!this.readAbort.signal.aborted&&(this.readAbort.abort(),this.error=`The local ${this.kind} changed or the list was refreshed. Select it again before importing.`),this.options=[],this.value=null,this.success="",this.refresh()},this.onBlur=()=>{this.readAbort&&!this.readAbort.signal.aborted&&this.onChange()},this.onVisibilityChange=()=>{globalThis.document?.visibilityState==="hidden"&&this.onBlur()},e.addController(this)}hostConnected(){globalThis.addEventListener("focus",this.onChange),globalThis.addEventListener("blur",this.onBlur),globalThis.addEventListener("storage",this.onChange),globalThis.document?.addEventListener("visibilitychange",this.onVisibilityChange),this.connectSource()}hostUpdated(){this.connectSource()}connectSource(){this.source!==this.host.localImportSource&&(this.unsubscribe?.(),this.abort?.abort(),this.source=this.host.localImportSource,this.unsubscribe=this.source?.subscribe(this.onChange),this.onChange())}hostDisconnected(){++this.generation,this.abort?.abort(),this.unsubscribe?.(),this.source=null,globalThis.removeEventListener("focus",this.onChange),globalThis.removeEventListener("blur",this.onBlur),globalThis.removeEventListener("storage",this.onChange),globalThis.document?.removeEventListener("visibilitychange",this.onVisibilityChange)}async refresh(){let e=++this.generation,t=this.source;this.loading=!0,this.host.requestUpdate();try{let i=t?await t.hasData(this.kind):!1;if(e!==this.generation)return;this.hasData=i;let r=this.open&&!t?.locked?await t?.list(this.kind)??[]:[];if(e!==this.generation)return;this.options=r,r.some(o=>o.value===this.value)||(this.value=null)}catch(i){e===this.generation&&(this.error=String(i?.message??i))}finally{e===this.generation&&(this.loading=!1,this.host.requestUpdate())}}toggle(){this.busy||(this.open=!this.open,this.error="",this.success="",this.options=[],this.value=null,this.refresh())}async unlock(){if(this.busy)return;this.busy=!0,this.error="";let e=this.abort=new AbortController;this.host.requestUpdate();try{await this.source.unlock({signal:e.signal})&&!e.signal.aborted&&await this.refresh()}catch(t){e.signal.aborted||(this.error=String(t?.message??t))}finally{this.busy=!1,this.host.requestUpdate()}}async importSelected(){if(this.busy||this.loading||!this.value||this.source?.locked)return;this.busy=!0,this.error="",this.success="";let e=this.options.find(i=>i.value===this.value)?.label,t=this.abort=new AbortController;this.readAbort=t,this.host.requestUpdate();try{await this.source.importItem(this.kind,this.value,i=>(this.readAbort=null,this.upload(i)),{signal:t.signal}),!t.signal.aborted&&!this.source?.locked&&(this.success=`Imported ${e}.`,this.value=null)}catch(i){t.signal.aborted||(this.error=String(i?.message??i))}finally{this.readAbort===t&&(this.readAbort=null),this.busy=!1,this.host.requestUpdate()}}renderAction(){return this.hasData||this.open?a`<button type="button" class="local-import-toggle" aria-expanded="${this.open}" aria-controls="local-import-panel" ?disabled="${this.busy}" @click="${()=>this.toggle()}">Import</button>`:d}renderPanel(e){if(!this.open)return d;let t=this.source?.locked;return a`<section class="local-import-panel" id="local-import-panel" aria-label="${`Import local ${this.kind}`}"><div class="local-import-copy"><strong>Import local ${this.kind}</strong><p>${t?"Unlock local data with your passkey to choose a file.":"Copy a file from this browser to the managed workspace. The local copy stays on this device."}</p></div><div class="local-import-controls">${t?a`<button type="button" class="local-import-submit" ?disabled="${this.busy}" @click="${()=>this.unlock()}">${this.busy?"Waiting for passkey\u2026":"Unlock with passkey"}</button>`:a`<managed-local-item-selector label="${`Choose local ${this.kind}`}" placeholder="${this.loading?"Loading local files\u2026":`Choose local ${this.kind}\u2026`}" .options="${this.options}" .value="${this.value}" ?disabled="${this.busy||this.loading}" @local-item-change="${i=>{this.value=i.detail.value,this.success="",this.host.requestUpdate()}}"></managed-local-item-selector><button type="button" class="local-import-submit" ?disabled="${e||this.busy||this.loading||!this.value}" @click="${()=>this.importSelected()}">${this.busy?"Importing\u2026":`Import ${this.kind}`}</button>`} <button type="button" class="local-import-close" ?disabled="${this.busy}" @click="${()=>this.toggle()}">Close</button></div>${!t&&!this.loading&&this.options.length===0?a`<p class="local-import-message">No local ${this.kind}s available.</p>`:d} ${this.error?a`<p class="local-import-message error" role="alert">${this.error} <button type="button" ?disabled="${this.busy}" @click="${()=>{this.error="",this.refresh()}}">Retry</button></p>`:d} ${this.success?a`<p class="local-import-message" role="status">${this.success}</p>`:d}</section>`}};async function Di({signal:s}={}){s=s?AbortSignal.any([s,g.sessionController.signal]):g.sessionController.signal,s.throwIfAborted();let e=await m("/api/admin/bundles",{credentials:"same-origin",signal:s});if(s.throwIfAborted(),!e.ok)throw Object.assign(new Error(`Bundle catalogue request failed (${e.status})`),{status:e.status});let t=await e.json();return s.throwIfAborted(),t.bundles??[]}async function ne(s,e,t){let i=g.generation,r=await m(`/api/bundles/${encodeURIComponent(s)}/${e}`,{credentials:"same-origin",signal:t});if(t?.throwIfAborted(),!r.ok)throw Object.assign(new Error(`Bundle ${e} request failed (${r.status})`),{status:r.status});let o=e==="contents"?await r.text():await r.json();if(t?.throwIfAborted(),i!==g.generation)throw new DOMException("Managed session changed","AbortError");return o}function _e(s,{signal:e}={}){return g.load(`bundle-metadata:${s}`,"bundle metadata",t=>ne(s,"metadata",t),{signal:e,retryInvalidated:!0})}async function $t(s,{signal:e}={}){e=e?AbortSignal.any([e,g.sessionController.signal]):g.sessionController.signal;let t=await ne(s,"metadata",e),i=t?.bundle?.repo??null,r=re({repo:i});if(!r)return i;let o=new URLSearchParams({repo:r,directory:i.directory??""}),n=new Set(t.unsized??[]),l=lt((t.files??[]).filter(([u,v])=>v!=null||n.has(u)).map(([u])=>u));l&&o.set("filePrefix",l);let c=await m(`/api/admin/repositories/resolve?${o}`,{credentials:"same-origin",cache:"no-store",signal:e});if(e.throwIfAborted(),!c.ok)throw new Error(`Repository suggestion request failed (${c.status})`);let{location:p}=await c.json();return e.throwIfAborted(),p?{...i,github:p.github,...p.mapped?{directory:p.directory}:{},repoId:p.repoId}:i}async function Bi(s,{signal:e}={}){e=e?AbortSignal.any([e,g.sessionController.signal]):g.sessionController.signal;try{return await ne(s,"contents",e)}catch(t){throw t.name!=="AbortError"&&g.notify(`Couldn't load bundle contents: ${t.message}`),t}}function qi(s,e,t="",i=!1,r=!1){let o=[e?`team=${encodeURIComponent(e)}`:"",t?`reason=${encodeURIComponent(t)}`:"",i?"repoAdvisories=true":"",r?"details=true":""].filter(Boolean).join("&"),n=`advisories${o?`?${o}`:""}`;return ne(s,n,g.sessionController.signal)}var U={400:"Not an npm package name and version.",404:"No such package version, or it is not available to you.",413:"This package is too large to open here.",429:"Too many packages are loading. Try again shortly.",502:"The npm registry couldn't be reached."};async function O(s,e,t,i){i=i?AbortSignal.any([i,g.sessionController.signal]):g.sessionController.signal;let r=g.generation,o=await m(s,{credentials:"same-origin",signal:i});if(i.throwIfAborted(),!o.ok){let l=(await o.json().catch(()=>null))?.error??null;throw Object.assign(new Error(e[l]??e[o.status]??`Request failed (${o.status})`),{status:o.status,code:l})}let n=await t(o);if(i.throwIfAborted(),r!==g.generation)throw new DOMException("Managed session changed","AbortError");return n}var A=(s,e,t)=>O(`/api/npm/${s}?${new URLSearchParams(e)}`,U,i=>i.json(),t);function Mi(s,e,{signal:t}={}){return A("package",{name:s,version:e},t)}function Ui(s,{signal:e}={}){return A("versions",{name:s},e)}function Fi(s,{signal:e}={}){return A("stats",{name:s},e)}function Hi(s,{signal:e}={}){return A("advisories",{name:s},e)}function ji(s,e,{signal:t}={}){return A("tags",{name:s,version:e},t)}function Gi(s,e,{signal:t}={}){return A("socket",{name:s,version:e},t)}function Ki(s,e,t=null,{signal:i}={}){return A("risk",{name:s,version:e,...t===null?{}:{otherVersion:t}},i)}function Vi(s,e,{signal:t}={}){return A("provenance",{name:s,version:e},t)}var P={404:"This file is no longer available.",409:"This file has changed since it was opened.",413:"This file is too large to pretty-print.",422:"This file couldn't be read as code.","pretty-mismatch":"Pretty-printing would change more than its layout, so it is shown as published.",unsupported:"This kind of file can't be checked once pretty-printed, so it is shown as published.",429:"Too many files are being pretty-printed. Try again shortly.",502:"The npm registry couldn't be reached."};function Wi(s,e,t,{signal:i}={}){return O(`/api/bundles/${encodeURIComponent(s)}/pretty?${new URLSearchParams({path:e,hash:t})}`,P,kt,i)}function Ji(s,e,t,i,{signal:r}={}){return O(`/api/npm/pretty?${new URLSearchParams({name:s,version:e,path:t,hash:i})}`,{...U,...P},kt,r)}var kt=async s=>({text:await s.text(),source:s.headers.get("x-source-hash")});async function*ye(s){let e=s.body.pipeThrough(new TextDecoderStream).getReader(),t=[];for(;;){let{done:i,value:r}=await e.read();if(i)return;let o=0;for(let n=r.indexOf(`
1
+ import{a as ss,b as as,c as ae,d as pt,e as ut,f as ht,g as os,h as ns,i as ls,j as ds,k as cs,l as ps,m as us,n as hs,o as ms,p as mt,q as gs,r as bs,s as fs,t as vs}from"./chunk-ZBBOBQ43.js";import{a as g,b as Oi,c as Pi,d as zi}from"./chunk-KYCNJVB7.js";import{a as ge,b as L,c as Li,d as m}from"./chunk-IU3CCINR.js";import{K as vt,U as _t,W as yt,b as be,c as q,e as G,f as ct,g as gt,j as bt,ma as oe,n as fe,o as ft}from"./chunk-CLPKNNC7.js";import{a as dt,c as se}from"./chunk-UYAXVRVL.js";import{a as w,b as y,c as b}from"./chunk-35VE6CDH.js";import{b as lt}from"./chunk-NRPC74AM.js";import"./chunk-PNHBZGDD.js";import"./chunk-V3ME6FI5.js";import{a as M}from"./chunk-YVZH5SHP.js";import{a as wt}from"./chunk-4XCDFGDP.js";import"./chunk-3BZ7JERB.js";import"./chunk-TZ5KB2Y5.js";import"./chunk-XYFXXB2G.js";import{a as ie}from"./chunk-DIJJNTRH.js";import{f as nt,j}from"./chunk-WZDTZE64.js";import{B as at,D as re,J as ot}from"./chunk-GNXOKIGP.js";import"./chunk-C4Z6ZBXV.js";import{a as h,b as tt,c as a,d as te,g as d,j as B}from"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import{b as it,s as rt,t as st}from"./chunk-V5O7M2YT.js";import"./chunk-OOJM4CTU.js";var f=class extends B{static properties={session:{attribute:!1},allowShare:{attribute:!1},_loading:{state:!0}};constructor(){super(),this.session=null,this.allowShare=!1,this.appState=g,this._loading=!1,this._loadRequest=null}get _role(){return this.session?.role??null}get _csrf(){return this.session?.csrfToken??null}get _me(){return this.session?.id??null}disconnectedCallback(){this._loadRequest?.abort(),super.disconnectedCallback()}async _loadCollection(e,t,i,r){this._loadRequest?.abort();let o=new AbortController;this._loadRequest=o,this._loading=!0;try{await this.appState.load(e,t,i,{signal:o.signal,apply:r})}catch(n){!o.signal.aborted&&n?.name!=="AbortError"&&this.appState.read(e)===void 0&&(this._error=String(n?.message??n))}finally{this._loadRequest===o&&(this._loading=!1)}}};function $(s){return a`<div class="loading-rows" role="status"><span class="sr-only">${s}</span><div aria-hidden="true">${[0,1,2,3].map(()=>a`<div class="loading-row"><span></span><span></span></div>`)}</div></div>`}var ve=class extends G{get changeEvent(){return"local-item-change"}get searchLabel(){return"Search local files"}get optionsLabel(){return"Local files"}get noMatchesLabel(){return"No matching files."}get emptyLabel(){return"No local files available."}choices(){let e=this._query.trim().toLocaleLowerCase(),t=this.options.filter(i=>`${i.label} ${i.secondary??""}`.toLocaleLowerCase().includes(e));return{pinned:[],sections:[{options:t}],count:t.length,total:this.options.length,showFacets:!1}}};customElements.define("managed-local-item-selector",ve);var K=class{constructor(e,t,i){this.host=e,this.kind=t,this.upload=i,this.source=null,this.hasData=!1,this.open=!1,this.options=[],this.value=null,this.busy=!1,this.loading=!1,this.error="",this.success="",this.generation=0,this.readAbort=null,this.onChange=r=>{if(r?.kind){if(r.kind!==this.kind)return;if(r.value!==this.value){this.refresh();return}}this.readAbort&&!this.readAbort.signal.aborted&&(this.readAbort.abort(),this.error=`The local ${this.kind} changed or the list was refreshed. Select it again before importing.`),this.options=[],this.value=null,this.success="",this.refresh()},this.onBlur=()=>{this.readAbort&&!this.readAbort.signal.aborted&&this.onChange()},this.onVisibilityChange=()=>{globalThis.document?.visibilityState==="hidden"&&this.onBlur()},e.addController(this)}hostConnected(){globalThis.addEventListener("focus",this.onChange),globalThis.addEventListener("blur",this.onBlur),globalThis.addEventListener("storage",this.onChange),globalThis.document?.addEventListener("visibilitychange",this.onVisibilityChange),this.connectSource()}hostUpdated(){this.connectSource()}connectSource(){this.source!==this.host.localImportSource&&(this.unsubscribe?.(),this.abort?.abort(),this.source=this.host.localImportSource,this.unsubscribe=this.source?.subscribe(this.onChange),this.onChange())}hostDisconnected(){++this.generation,this.abort?.abort(),this.unsubscribe?.(),this.source=null,globalThis.removeEventListener("focus",this.onChange),globalThis.removeEventListener("blur",this.onBlur),globalThis.removeEventListener("storage",this.onChange),globalThis.document?.removeEventListener("visibilitychange",this.onVisibilityChange)}async refresh(){let e=++this.generation,t=this.source;this.loading=!0,this.host.requestUpdate();try{let i=t?await t.hasData(this.kind):!1;if(e!==this.generation)return;this.hasData=i;let r=this.open&&!t?.locked?await t?.list(this.kind)??[]:[];if(e!==this.generation)return;this.options=r,r.some(o=>o.value===this.value)||(this.value=null)}catch(i){e===this.generation&&(this.error=String(i?.message??i))}finally{e===this.generation&&(this.loading=!1,this.host.requestUpdate())}}toggle(){this.busy||(this.open=!this.open,this.error="",this.success="",this.options=[],this.value=null,this.refresh())}async unlock(){if(this.busy)return;this.busy=!0,this.error="";let e=this.abort=new AbortController;this.host.requestUpdate();try{await this.source.unlock({signal:e.signal})&&!e.signal.aborted&&await this.refresh()}catch(t){e.signal.aborted||(this.error=String(t?.message??t))}finally{this.busy=!1,this.host.requestUpdate()}}async importSelected(){if(this.busy||this.loading||!this.value||this.source?.locked)return;this.busy=!0,this.error="",this.success="";let e=this.options.find(i=>i.value===this.value)?.label,t=this.abort=new AbortController;this.readAbort=t,this.host.requestUpdate();try{await this.source.importItem(this.kind,this.value,i=>(this.readAbort=null,this.upload(i)),{signal:t.signal}),!t.signal.aborted&&!this.source?.locked&&(this.success=`Imported ${e}.`,this.value=null)}catch(i){t.signal.aborted||(this.error=String(i?.message??i))}finally{this.readAbort===t&&(this.readAbort=null),this.busy=!1,this.host.requestUpdate()}}renderAction(){return this.hasData||this.open?a`<button type="button" class="local-import-toggle" aria-expanded="${this.open}" aria-controls="local-import-panel" ?disabled="${this.busy}" @click="${()=>this.toggle()}">Import</button>`:d}renderPanel(e){if(!this.open)return d;let t=this.source?.locked;return a`<section class="local-import-panel" id="local-import-panel" aria-label="${`Import local ${this.kind}`}"><div class="local-import-copy"><strong>Import local ${this.kind}</strong><p>${t?"Unlock local data with your passkey to choose a file.":"Copy a file from this browser to the managed workspace. The local copy stays on this device."}</p></div><div class="local-import-controls">${t?a`<button type="button" class="local-import-submit" ?disabled="${this.busy}" @click="${()=>this.unlock()}">${this.busy?"Waiting for passkey\u2026":"Unlock with passkey"}</button>`:a`<managed-local-item-selector label="${`Choose local ${this.kind}`}" placeholder="${this.loading?"Loading local files\u2026":`Choose local ${this.kind}\u2026`}" .options="${this.options}" .value="${this.value}" ?disabled="${this.busy||this.loading}" @local-item-change="${i=>{this.value=i.detail.value,this.success="",this.host.requestUpdate()}}"></managed-local-item-selector><button type="button" class="local-import-submit" ?disabled="${e||this.busy||this.loading||!this.value}" @click="${()=>this.importSelected()}">${this.busy?"Importing\u2026":`Import ${this.kind}`}</button>`} <button type="button" class="local-import-close" ?disabled="${this.busy}" @click="${()=>this.toggle()}">Close</button></div>${!t&&!this.loading&&this.options.length===0?a`<p class="local-import-message">No local ${this.kind}s available.</p>`:d} ${this.error?a`<p class="local-import-message error" role="alert">${this.error} <button type="button" ?disabled="${this.busy}" @click="${()=>{this.error="",this.refresh()}}">Retry</button></p>`:d} ${this.success?a`<p class="local-import-message" role="status">${this.success}</p>`:d}</section>`}};async function Di({signal:s}={}){s=s?AbortSignal.any([s,g.sessionController.signal]):g.sessionController.signal,s.throwIfAborted();let e=await m("/api/admin/bundles",{credentials:"same-origin",signal:s});if(s.throwIfAborted(),!e.ok)throw Object.assign(new Error(`Bundle catalogue request failed (${e.status})`),{status:e.status});let t=await e.json();return s.throwIfAborted(),t.bundles??[]}async function ne(s,e,t){let i=g.generation,r=await m(`/api/bundles/${encodeURIComponent(s)}/${e}`,{credentials:"same-origin",signal:t});if(t?.throwIfAborted(),!r.ok)throw Object.assign(new Error(`Bundle ${e} request failed (${r.status})`),{status:r.status});let o=e==="contents"?await r.text():await r.json();if(t?.throwIfAborted(),i!==g.generation)throw new DOMException("Managed session changed","AbortError");return o}function _e(s,{signal:e}={}){return g.load(`bundle-metadata:${s}`,"bundle metadata",t=>ne(s,"metadata",t),{signal:e,retryInvalidated:!0})}async function $t(s,{signal:e}={}){e=e?AbortSignal.any([e,g.sessionController.signal]):g.sessionController.signal;let t=await ne(s,"metadata",e),i=t?.bundle?.repo??null,r=re({repo:i});if(!r)return i;let o=new URLSearchParams({repo:r,directory:i.directory??""}),n=new Set(t.unsized??[]),l=lt((t.files??[]).filter(([u,v])=>v!=null||n.has(u)).map(([u])=>u));l&&o.set("filePrefix",l);let c=await m(`/api/admin/repositories/resolve?${o}`,{credentials:"same-origin",cache:"no-store",signal:e});if(e.throwIfAborted(),!c.ok)throw new Error(`Repository suggestion request failed (${c.status})`);let{location:p}=await c.json();return e.throwIfAborted(),p?{...i,github:p.github,...p.mapped?{directory:p.directory}:{},repoId:p.repoId}:i}async function Bi(s,{signal:e}={}){e=e?AbortSignal.any([e,g.sessionController.signal]):g.sessionController.signal;try{return await ne(s,"contents",e)}catch(t){throw t.name!=="AbortError"&&g.notify(`Couldn't load bundle contents: ${t.message}`),t}}function qi(s,e,t="",i=!1,r=!1){let o=[e?`team=${encodeURIComponent(e)}`:"",t?`reason=${encodeURIComponent(t)}`:"",i?"repoAdvisories=true":"",r?"details=true":""].filter(Boolean).join("&"),n=`advisories${o?`?${o}`:""}`;return ne(s,n,g.sessionController.signal)}var U={400:"Not an npm package name and version.",404:"No such package version, or it is not available to you.",413:"This package is too large to open here.",429:"Too many packages are loading. Try again shortly.",502:"The npm registry couldn't be reached."};async function O(s,e,t,i){i=i?AbortSignal.any([i,g.sessionController.signal]):g.sessionController.signal;let r=g.generation,o=await m(s,{credentials:"same-origin",signal:i});if(i.throwIfAborted(),!o.ok){let l=(await o.json().catch(()=>null))?.error??null;throw Object.assign(new Error(e[l]??e[o.status]??`Request failed (${o.status})`),{status:o.status,code:l})}let n=await t(o);if(i.throwIfAborted(),r!==g.generation)throw new DOMException("Managed session changed","AbortError");return n}var A=(s,e,t)=>O(`/api/npm/${s}?${new URLSearchParams(e)}`,U,i=>i.json(),t);function Mi(s,e,{signal:t}={}){return A("package",{name:s,version:e},t)}function Ui(s,{signal:e}={}){return A("versions",{name:s},e)}function Fi(s,{signal:e}={}){return A("stats",{name:s},e)}function Hi(s,{signal:e}={}){return A("advisories",{name:s},e)}function ji(s,e,{signal:t}={}){return A("tags",{name:s,version:e},t)}function Gi(s,e,{signal:t}={}){return A("socket",{name:s,version:e},t)}function Ki(s,e,t=null,{signal:i}={}){return A("risk",{name:s,version:e,...t===null?{}:{otherVersion:t}},i)}function Vi(s,e,{signal:t}={}){return A("provenance",{name:s,version:e},t)}var P={404:"This file is no longer available.",409:"This file has changed since it was opened.",413:"This file is too large to pretty-print.",422:"This file couldn't be read as code.","pretty-mismatch":"Pretty-printing would change more than its layout, so it is shown as published.",unsupported:"This kind of file can't be checked once pretty-printed, so it is shown as published.",429:"Too many files are being pretty-printed. Try again shortly.",502:"The npm registry couldn't be reached."};function Wi(s,e,t,{signal:i}={}){return O(`/api/bundles/${encodeURIComponent(s)}/pretty?${new URLSearchParams({path:e,hash:t})}`,P,kt,i)}function Ji(s,e,t,i,{signal:r}={}){return O(`/api/npm/pretty?${new URLSearchParams({name:s,version:e,path:t,hash:i})}`,{...U,...P},kt,r)}var kt=async s=>({text:await s.text(),source:s.headers.get("x-source-hash")});async function*ye(s){let e=s.body.pipeThrough(new TextDecoderStream).getReader(),t=[];for(;;){let{done:i,value:r}=await e.read();if(i)return;let o=0;for(let n=r.indexOf(`
2
2
  `);n!==-1;o=n+1,n=r.indexOf(`
3
3
  `,o))t.push(r.slice(o,n)),yield JSON.parse(t.join("")),t=[];t.push(r.slice(o))}}function Yi(s,e,t,{signal:i}={}){return z(`/api/npm/pretty?${new URLSearchParams({name:s,version:e})}`,U,t,i)}function Zi(s,e,t,{signal:i}={}){return z(`/api/bundles/${encodeURIComponent(s)}/pretty?${new URLSearchParams({other:e})}`,P,t,i)}function Xi(s,e,t,i,{signal:r}={}){return z(`/api/npm/pretty?${new URLSearchParams({name:s,version:e,otherVersion:t})}`,U,i,r)}function z(s,e,t,i,r=P){return O(s,e,async o=>{let n=g.generation;for await(let{error:l,status:c,...p}of ye(o)){if(n!==g.generation)throw new DOMException("Managed session changed","AbortError");await t(l?{...p,status:c,message:r[l]??r[c]??`Request failed (${c})`}:p)}},i)}var we={...P,413:"This file is too large to compare with renamed names hidden.",422:"This file couldn't be compared as code.","diff-busy":"Too many files are being compared. Try again shortly.",504:"Comparing this file took too long."},xt=(s,e)=>O(s,we,t=>t.json(),e);function Qi(s,e,{path:t,hash:i,otherPath:r,otherHash:o},{signal:n}={}){return xt(`/api/bundles/${encodeURIComponent(s)}/rename-diff?${new URLSearchParams({path:t,hash:i,other:e,otherPath:r,otherHash:o})}`,n)}function er(s,e,t,{path:i,hash:r,otherPath:o,otherHash:n},{signal:l}={}){return xt(`/api/npm/rename-diff?${new URLSearchParams({name:s,version:e,path:i,hash:r,otherVersion:t,otherPath:o,otherHash:n})}`,l)}var Rt=(s,e)=>new URLSearchParams({other:s,...e?{scope:e}:{}});function tr(s,e,t,{signal:i,scope:r=""}={}){return z(`/api/bundles/${encodeURIComponent(s)}/rename-diff?${Rt(e,r)}`,P,t,i,we)}function ir(s,e,t,i,{signal:r}={}){return z(`/api/npm/rename-diff?${new URLSearchParams({name:s,version:e,otherVersion:t})}`,U,i,r,we)}function rr(s,e,t,{signal:i,scope:r=""}={}){return z(`/api/bundles/${encodeURIComponent(s)}/type-only?${Rt(e,r)}`,P,t,i)}function sr(s,e,t,i,{signal:r}={}){return z(`/api/npm/type-only?${new URLSearchParams({name:s,version:e,otherVersion:t})}`,U,i,r)}async function T(s,e){let t=await m(s,{signal:e,credentials:"same-origin",headers:{accept:"application/json"}});if(!t.ok)throw new Error(`HTTP ${t.status}`);return t.json()}function I(s,e,t){let i={"content-type":"application/json"};return e&&(i["x-csrf-token"]=e),m(s,{method:"POST",credentials:"same-origin",headers:i,body:JSON.stringify(t)})}async function le(s){let e=await s.json().then(t=>typeof t?.error=="string"?t.error:"",()=>"");return["repo-forbidden","forbidden"].includes(e)?"choose a repository and directory within your team access":`HTTP 403${e?`: ${e}`:""}`}async function St(s,e,t){let i=t?{"x-csrf-token":t}:{},r=await m(`/api/admin/${s}/${encodeURIComponent(e)}`,{method:"DELETE",credentials:"same-origin",headers:i});if(!r.ok)throw new Error(`HTTP ${r.status}`)}async function Ct(s,e,t,i,r,o){let n=new URLSearchParams({page:String(e),limit:"100",kind:t,q:i.trim()});r&&n.set("repo",r),o&&n.set("actor",o);let l=await T(`/api/admin/history?${n}`,s);if(!Array.isArray(l?.history)||!Number.isSafeInteger(l.total)||l.total<0||!Number.isSafeInteger(l.page)||l.page<1)throw new Error("No history returned");return l}async function Et(s){let e=await T("/api/admin/users",s);return Array.isArray(e?.users)?e.users:[]}async function At(s,e,t){let i=await I("/api/admin/set-role",t,{userId:s,role:e});if(!i.ok)throw new Error(`HTTP ${i.status}`)}async function Tt(s,e,t,i){let r=new URLSearchParams({scope:s});return s==="installed"&&r.set("showAll",String(e)),t&&r.set("refresh","true"),await T(`/api/admin/repositories?${r}`,i)}async function It(s,e,t){let i=await I("/api/admin/repositories/select",t,{repoId:s,selected:e});if(!i.ok)throw new Error(`HTTP ${i.status}`)}async function Nt(s,e){let t=await m("/api/admin/repositories/add-public",{method:"POST",credentials:"same-origin",headers:{"content-type":"application/json","x-csrf-token":e},body:JSON.stringify({repository:s})});if(!t.ok){let i={400:"Enter a repository as owner/repo or a GitHub repository URL.",403:"You do not have permission to add arbitrary public repositories.",404:"No public repository was found at that address.",409:"Choose a public, non-archived repository."};throw new Error(i[t.status]??`GitHub lookup failed (HTTP ${t.status}). Try again.`)}}async function Lt(s,e){let t=await T(`/api/admin/repositories/impact?repoId=${encodeURIComponent(s)}`,e);if(t?.repoId!==s||!Array.isArray(t.reports)||!Array.isArray(t.bundles)||!Number.isSafeInteger(t.triageCount)||t.triageCount<0)throw new Error("Invalid repository data");return t}async function Ot(s,e){let t=await m("/api/admin/repositories/connect-app",{method:"POST",credentials:"same-origin",headers:{"content-type":"application/json","x-csrf-token":e},body:JSON.stringify({repoId:s})}),i=await t.json();if(!t.ok){let r={"github-app-not-configured":"The GitHub App is not configured on this server.","repo-identity-changed":"The repository has changed on GitHub. Refresh and check its connection.","repo-connection-changed":"The connection or your permissions changed. Refresh and try again.","repo-not-connected":"This repository is no longer connected.",forbidden:"Administrator access is required to connect the GitHub App."};throw new Error(r[i.error]??`Could not connect the GitHub App (HTTP ${t.status}). Try again.`)}if(i.connected===!0)return i;if(i.connected!==!1||typeof i.installUrl!="string"||!/^https:\/\/github\.com\/apps\/[^/?#]+\/installations\/new$/u.test(i.installUrl))throw new Error("Invalid GitHub installation response.");return i}async function Pt(s,e,t,i){let r=await I("/api/admin/repositories/remove",i,{repoId:s,fullName:e,acknowledge:!0,deleteTriage:t});if(!r.ok)throw new Error(`HTTP ${r.status}`);return r.json()}async function zt(s){return await T("/api/admin/reports",s)}async function Dt(s,e,t){let i=await m("/api/admin/bundles/create",{method:"POST",credentials:"same-origin",signal:t,headers:{"content-type":"application/json","x-csrf-token":e},body:JSON.stringify(s)}),r=await i.json();if(!i.ok){let o={"unsupported-entries":"Select JavaScript/TypeScript files (including JSX/TSX) or Solidity files. Stasis cannot mix languages or use JSON or Rust as remote entry points.","bad-conditions":"Check the export conditions and selected platforms.","metro-conditions":"Stasis\u2019s Metro preset requires the react-native condition without manual changes.","build-lockfile":"Stasis needs a supported lockfile: pnpm-lock.yaml, yarn.lock (Yarn 1), package-lock.json, or soldeer.lock.","build-scope":"Your team needs access to the bundle\u2019s project root, including its lockfile and dependencies.","build-busy":"A bundle is already being built, or the server is busy. Try again shortly.","build-timeout":"The bundle build timed out. Try a smaller selection or retry.","too-large":"This bundle exceeds the server\u2019s size limit.","github-rate-limited":"GitHub\u2019s API rate limit has been reached. Retry after the limit resets.","github-build-failed":"Could not fetch the repository from GitHub. Check access and retry.","repository-changed":"The repository connection changed during the build. Reload and try again.","bundle-conflict":"This bundle already exists but is outside your current access.","build-failed":"Stasis could not build this selection. Check that its lockfile is supported and all entry points and imports exist at this commit."};throw new Error(o[r.error]??(i.status===401?"Sign in again to create a bundle.":i.status===403||i.status===404?"Repository access is required to create this bundle.":"Could not create the bundle. Try again."))}return r}async function ke(s,e,t=null,i=""){let r=(await T("/api/config")).managed?.uploadMaxBytes?.reports;if(Number.isSafeInteger(r)&&r>0){if(s.size>r)throw new Error("too large")}else if(s.size>3*1024*1024)return $e(s,e,t,i);let o=ct(await s.text(),s.name);if(!o)return $e(s,e,t,i);let n=[],l=[];for(let{name:c,content:p}of o)try{l.push(await $e(new File([p],c,{type:"application/json"}),e,t,i))}catch(u){n.push(`${c}: ${String(u?.message??u)}`)}if(n.length>0)throw new Error(`${n.length} of ${o.length} products failed (${n.join("; ")})`);return l.at(-1)}async function $e(s,e,t,i){let r={"content-type":s.type||"application/json","x-report-filename":encodeURIComponent(s.name)};e&&(r["x-csrf-token"]=e),t!=null&&(r["x-repo-id"]=String(t)),i!==""&&(r["x-repo-directory"]=encodeURIComponent(i));let o=await m("/api/admin/reports",{method:"POST",credentials:"same-origin",headers:r,body:s});if(!o.ok){if(o.status===413)throw new Error("too large");if(o.status===403)throw new Error(await le(o));let n=await o.json().catch(()=>null);if(n?.error==="storage-encryption-required")throw new Error("Link reports require managed storage encryption. Configure MANAGED_STORAGE_ENCRYPTION_KEY.");if(n?.error==="invalid-report")throw new Error(typeof n.reason=="string"&&n.reason?`This file is not a report: ${n.reason}`:"This file is not a recognized report.");let l=n?.error==="repo-not-connected"&&typeof n.repo=="string"?`: ${n.repo} is not connected`:"";throw new Error(`HTTP ${o.status}${l}`)}return o.json()}async function Bt(s,e,t){let i=await I("/api/admin/reports/set-visible",t,{reportId:s,visible:e});if(!i.ok)throw new Error(`HTTP ${i.status}`)}async function qt(s,e,t,i){let r=await I("/api/admin/reports/set-repo",i,{reportId:s,repoId:e,directory:t});if(!r.ok)throw r.status===409?new Error("this report already defines its repository"):r.status===403?new Error(await le(r)):new Error(r.status===400?"invalid repository or directory":`HTTP ${r.status}`);return r.json()}async function Mt(s,e){let t=await m(`/api/admin/reports/${encodeURIComponent(s)}/location`,{signal:e,credentials:"same-origin"});if(!t.ok)throw new Error(`Report location suggestion request failed (${t.status})`);return(await t.json())?.location??null}async function Ut(s,e){await St("reports",s,e)}async function xe(s){return await T("/api/admin/bundles",s)}async function Re(s,e,t,i=""){let r={"content-type":"application/octet-stream","x-bundle-filename":encodeURIComponent(s.name)};e&&(r["x-csrf-token"]=e),t!=null&&(r["x-repo-id"]=String(t)),i&&(r["x-repo-directory"]=encodeURIComponent(i));let o=await m("/api/admin/bundles",{method:"POST",credentials:"same-origin",headers:r,body:s});if(!o.ok)throw new Error(o.status===413?"too large":o.status===403?await le(o):`HTTP ${o.status}`);return o.json()}async function Ft(s,e){await St("bundles",s,e)}async function Ht(s,e,t){let i=await I("/api/admin/bundles/set-visible",t,{bundleId:s,visible:e});if(!i.ok)throw new Error(`HTTP ${i.status}`)}async function jt(s,e,t,i){let r=await I("/api/admin/bundles/set-repo",i,{bundleId:s,repoId:e,directory:t});if(!r.ok)throw new Error(r.status===403?await le(r):r.status===400?"invalid repository or directory":`HTTP ${r.status}`)}async function V(s){return await T("/api/admin/teams",s)}async function x(s,e,t){let i=await I(s,e,t);if(!i.ok)throw new Error(i.status===409?"name already taken":`HTTP ${i.status}`)}function W(s,e=!0){let t=document.createElement("input");t.type="file",t.multiple=e,t.addEventListener("change",()=>{s([...t.files])},{once:!0}),t.click()}function J(s,e,t){let i=0,r=p=>Array.from(p.dataTransfer?.types??[]).includes("Files"),o=p=>{r(p)&&(p.preventDefault(),i+=1,t(!0))},n=p=>{r(p)&&p.preventDefault()},l=p=>{r(p)&&(i=Math.max(0,i-1),i===0&&t(!1))},c=p=>{r(p)&&(p.preventDefault(),p.stopPropagation(),i=0,t(!1),e([...p.dataTransfer.files]))};return s.addEventListener("dragenter",o),s.addEventListener("dragover",n),s.addEventListener("dragleave",l),s.addEventListener("drop",c),()=>{s.removeEventListener("dragenter",o),s.removeEventListener("dragover",n),s.removeEventListener("dragleave",l),s.removeEventListener("drop",c)}}async function Gt(s,e,t,i){let r=s.appState.generation;try{return await s.appState.mutate(()=>t(e),i)}catch(o){throw r===s.appState.generation&&s.appState.invalidate(i),o}}async function Se(s,e,t,i){if(s._busy||!s._csrf)throw new Error("Wait for the current operation to finish, then try again.");s._busy=!0;try{await Gt(s,e,t,i)}finally{await s._load(),s._busy=!1;let r=s._queue.splice(0);r.length>0&&s._upload(r)}}async function Y(s,e,t,i){if(e.length===0||(s._queue.push(...e),s._busy))return;s._busy=!0,s._error=null;let r=s.appState.generation,o=[],n=0;try{for(;s._queue.length>0;){if(s.appState.generation!==r){s._queue=[];break}let l=s._queue.shift();try{await Gt(s,l,t,i)}catch(c){if(c?.name==="AbortError"||s.appState.generation!==r){s._queue=[];break}o.push(`${l.name}: ${String(c?.message??c)}`)}n++}}finally{o.length>0&&(s._error=n===1?`Upload failed: ${o[0]}`:`Upload failed for ${o.length} of ${n} files: ${o.join("; ")}`),s._busy=!1,await s._load({preserveError:!0})}}var Ce=`.local-import-toggle{flex-shrink:0;margin-block:calc(-.25rem - 1px);margin-left:auto;padding:.25rem .6rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:transparent;color:var(--muted);font:inherit;font-size:.75rem;line-height:1.5}.local-import-toggle:hover,.local-import-toggle[aria-expanded=true]{color:var(--text);background:rgb(from var(--text) r g b / .06)}.local-import-panel{display:grid;gap:.8rem;padding:1rem;margin-bottom:1.25rem;border:1px solid var(--border);border-radius:var(--ui-radius, 8px);background:rgb(from var(--accent) r g b / .025)}.local-import-copy strong{font-size:.82rem;font-weight:600}.local-import-copy p,.local-import-message{margin:.3rem 0 0;color:var(--muted);font-size:.75rem;line-height:1.5}.local-import-controls{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem}managed-local-item-selector{flex:1 1 16rem;min-width:0}.local-import-controls button,.local-import-message button{padding:.35rem .7rem;border:1px solid var(--border);border-radius:var(--ui-radius, 5px);background:transparent;color:var(--text);font:inherit;font-size:.75rem;line-height:1.5}.local-import-controls .local-import-submit{border-color:rgb(from var(--accent) r g b / .4);background:rgb(from var(--accent) r g b / .1);color:var(--accent)}.local-import-controls .local-import-close{color:var(--muted)}.local-import-panel button:disabled,.local-import-toggle:disabled{opacity:.5}.local-import-panel button:focus-visible,.local-import-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.local-import-message.error{color:var(--critical)}
4
4
  `;var Kt=`.home-section{margin-top:1.1rem;margin-bottom:2rem}.section-heading{display:flex;align-items:baseline;gap:1rem;margin-bottom:.85rem}.section-heading h2{margin:0;font-size:.86rem;font-weight:600}.section-heading>span{color:var(--muted);font-size:.75rem}.pages{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.9rem}.page{display:flex;flex-direction:column;gap:.85rem;padding:1.35rem;border:1px solid var(--border);border-radius:var(--ui-radius, 12px);color:var(--text);background:var(--panel);text-align:left;font:inherit}.page:hover{background:var(--panel-hover);border-color:color-mix(in srgb,var(--muted) 60%,var(--border))}.page-top{display:flex;align-items:center;gap:.75rem;width:100%}.page-icon{display:grid;place-items:center;flex-shrink:0;width:2.5rem;height:2.5rem;border:1px solid color-mix(in srgb,var(--accent) 16%,var(--border));border-radius:var(--ui-radius, 9px);background:color-mix(in srgb,var(--accent) 7%,var(--panel));color:var(--accent)}.page-bundle .page-icon{color:var(--green);border-color:color-mix(in srgb,var(--green) 18%,var(--border));background:color-mix(in srgb,var(--green) 6%,var(--panel))}.page-scan .page-icon{color:var(--scan-icon-color, var(--yellow));border-color:color-mix(in srgb,var(--scan-icon-color, var(--yellow)) 18%,var(--border));background:color-mix(in srgb,var(--scan-icon-color, var(--yellow)) 6%,var(--panel))}:host-context(body.theme-light) .page-scan .page-icon{--scan-icon-color: #bf8700}:host-context(body.theme-green) :is(.page-bundle,.page-scan) .page-icon,:host-context(body.theme-pink) :is(.page-bundle,.page-scan) .page-icon{color:var(--accent);border-color:color-mix(in srgb,var(--accent) 16%,var(--border));background:color-mix(in srgb,var(--accent) 7%,var(--panel))}.page-icon svg{width:1.2rem;height:1.2rem}.arrow{display:flex;margin-left:auto;flex-shrink:0;color:var(--muted)}.arrow svg{width:1rem;height:1rem}.page:hover .arrow{color:var(--text)}.page-copy{display:grid;gap:.45rem}.page>.page-copy{margin-inline-start:3.25rem}.page-copy strong,.page-title{font-size:1rem;font-weight:600;letter-spacing:-.015em}.page-copy>span{color:var(--muted);font-size:.8rem;line-height:1.65}.workspace-pages .page{gap:.85rem;padding:1.2rem 1.35rem}.workspace-pages .page-icon{width:2rem;height:2rem;color:var(--muted);background:transparent;border-color:var(--border)}.workspace-pages .page-title{font-size:.9rem}.workspace-pages .page-copy{margin-inline-start:2.75rem}.history-link{display:flex;align-items:center;gap:1rem;width:100%;padding:1.25rem 0;border:0;border-top:1px solid var(--border);text-align:left;color:var(--text);background:transparent}.history-link>.page-icon{color:var(--muted);background:transparent;border-color:var(--border)}.history-link .page-copy strong{font-size:.85rem}.history-cta{display:flex;align-items:center;gap:.6rem;flex-shrink:0;margin-left:auto;color:var(--muted);font-size:.78rem}.history-cta svg{width:1rem;height:1rem}.history-link:hover .history-cta{color:var(--accent)}@container (max-width: 48rem){.pages{grid-template-columns:1fr;gap:.65rem}.page,.workspace-pages .page{gap:.65rem;padding:1.15rem}.page-top .arrow{display:none}.workspace-pages .page-icon{width:2.5rem;height:2.5rem}.workspace-pages .page-copy{margin-inline-start:3.25rem}.page-copy{gap:.2rem}.page-copy strong,.page-title{font-size:.95rem}.section-heading>span{display:none}.history-cta{font-size:0}}
@@ -15,7 +15,7 @@ import{a as ss,b as as,c as ae,d as pt,e as ut,f as ht,g as os,h as ns,i as ls,j
15
15
  `;var kr="/api/admin/repositories/aliases";async function mi(s="",e={}){let t=await m(`${kr}${s}`,{credentials:"same-origin",...e}),i=await t.json();if(!t.ok){let r={"alias-exists":"An alias already exists for this old repository and path. Edit that row instead.","bad-old-repo":"Enter the old repository as owner/name or a GitHub URL.","bad-directory":"Enter repository directories without parent segments, backslashes, or surrounding whitespace.","bad-repo":"Choose a connected repository.","no-alias":"This alias was removed. Refresh the list and try again."};throw new Error(r[i?.error]??`HTTP ${t.status}`)}return i}var Pe=class extends f{static properties={repositories:{attribute:!1},_data:{state:!0},_edit:{state:!0},_error:{state:!0},_busy:{state:!0}};static styles=[h(b),h(hi)];constructor(){super(),this._data=null,this._edit=null,this._error=null,this._busy=!1}connectedCallback(){super.connectedCallback(),this._load()}updated(e){e.has("repositories")&&e.get("repositories")!==void 0&&(this.appState.invalidate(["repository-aliases"]),this._load())}async _load(){this._error=null,await this._loadCollection("repository-aliases","repository aliases",e=>mi("",{signal:e}),e=>{this._data=e})}_start(e=null){this._error=null,this._edit=e?{...e}:{id:null,oldRepo:"",oldPath:"",repoId:null,newPath:""}}async _mutate(e,t,i){if(!(this._busy||!this._csrf||this._role!=="admin")){this._busy=!0,this._error=null;try{await this.appState.mutate(()=>mi(t?`/${encodeURIComponent(t)}`:"",{method:e,headers:{"content-type":"application/json","x-csrf-token":this._csrf},...i?{body:JSON.stringify(i)}:{}}),["repository-aliases","workspace-import"]),this._edit=null,await this._load()}catch(r){this._error=String(r?.message??r)}finally{this._busy=!1}}}_save(){let{id:e,...t}=this._edit;return this._mutate(e?"PATCH":"POST",e,t)}_field(e,t,i){return a`<td><input type="text" aria-label="${t}" placeholder="${i}" .value="${this._edit[e]}" ?disabled="${this._busy}" @input="${r=>{this._edit={...this._edit,[e]:r.target.value}}}"></td>`}_editor(){let e=(this._data?.repos??[]).filter(t=>t.active!==!1).map(t=>({value:t.repoId,label:t.fullName}));return a`<tr>${this._field("oldRepo","Old repository","owner/repository")}${this._field("oldPath","Old path","Repository root")}<td><repository-selector label="New repository" placeholder="Choose repository…" .options="${e}" .value="${this._edit.repoId}" ?disabled="${this._busy}" @repository-change="${t=>{this._edit={...this._edit,repoId:t.detail.value}}}"></repository-selector></td>${this._field("newPath","New path","Repository root")}<td class="actions"><button type="button" class="btn" ?disabled="${this._busy||!this._edit.oldRepo.trim()||this._edit.repoId==null}" @click="${()=>{this._save()}}">${this._busy?"Saving\u2026":"Save"}</button> <button type="button" class="btn" ?disabled="${this._busy}" @click="${()=>{this._edit=null,this._error=null}}">Cancel</button></td></tr>`}render(){let e=this._data?.aliases??[],t=this._data?.repos??[];return a`<section aria-label="Repository aliases"><div class="page-intro"><p class="intro">Map repository locations when importing new reports and bundles.</p><button type="button" class="btn" ?disabled="${this._busy||this._edit!=null||!this._data}" @click="${()=>this._start()}">Add alias</button></div>${this._error?a`<p class="msg error" role="alert">${this._error}</p>`:d}<div aria-busy="${this._loading}">${this._data===null?this._error?a`<button type="button" class="btn" @click="${()=>{this._load()}}">Try again</button>`:$("Loading repository aliases\u2026"):a`<div class="alias-table"><table><thead><tr><th>Old repository</th><th>Old path</th><th>New repository</th><th>New path</th><th><span class="sr-only">Actions</span></th></tr></thead><tbody>${e.map(i=>this._edit?.id===i.id?this._editor():a`<tr><td>${i.oldRepo}</td><td class="mono">${i.oldPath||"/"}</td><td>${t.find(r=>r.repoId===i.repoId)?.fullName??"Removed repository"}${t.find(r=>r.repoId===i.repoId)?.active===!1?" (inactive)":""}</td><td class="mono">${i.newPath||"/"}</td><td class="actions"><button type="button" class="btn" ?disabled="${this._busy||this._edit!=null}" @click="${()=>this._start(i)}">Edit</button> <button type="button" class="btn" ?disabled="${this._busy||this._edit!=null}" @click="${()=>{this._mutate("DELETE",i.id)}}">Delete</button></td></tr>`)} ${this._edit?.id===null?this._editor():d} ${e.length===0&&!this._edit?a`<tr><td colspan="5" class="empty">No repository aliases yet.</td></tr>`:d}</tbody></table></div>`}</div></section>`}};customElements.define("managed-repository-aliases",Pe);function gi(s){document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:s},bubbles:!0,composed:!0}))}function vi(s,e){return a`<span class="avatar" aria-hidden="true"><span>${(e?.[0]??"?").toUpperCase()}</span> ${L()?d:a`<img alt="" src="${`/api/avatar/${encodeURIComponent(s)}`}" @error="${t=>t.currentTarget.classList.add("broken")}">`}</span>`}var xr=ie(rt),Ke=ie(st),ze=a`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" aria-hidden="true"><path d="m4 4 8 8m0-8-8 8"/></svg>`,ce=a`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg>`,Rr={admin:"Admin",manage:"Manager",triage:"Triage",view:"Viewer",none:"No access"},Sr={admin:"Manage workspace access and teams",manage:"Manage content and triage within team access",triage:"Review and triage findings",view:"Read-only access",none:"No workspace access"},De=class extends f{static styles=[h(Kt),h(b)];render(){let e=[["manage-bundles","Bundles","Keep source archives ready for review.","bundle"],["manage-scans","Scans","Run and monitor scans from stored bundles.","scan"],["manage-reports","Reports","Review, publish, and organize findings.","report"]],t=[["manage-repos","Repositories","Connect sources and manage repository settings.","repo"],["admin-users","Users","Manage accounts, roles, and workspace access.","users"],["manage-teams","Teams","Bring people and repositories together.","team"]];return a`<div class="wrap">${w("manage",this._role,this.allowShare)}<h1 class="sr-only">Manage</h1>${this._role==null?a`<p class="msg" role="status">Loading management pages…</p>`:a`<section class="home-section" aria-labelledby="content-heading"><div class="section-heading"><h2 id="content-heading">Content & activity</h2><span>From source to findings</span></div><div class="pages">${e.filter(([i])=>i!=="manage-scans"||["admin","manage"].includes(this._role)).map(i=>this._page(i))}</div></section>${this._role==="admin"?a`<section class="home-section" aria-labelledby="workspace-heading"><div class="section-heading"><h2 id="workspace-heading">Workspace & access</h2><span>Sources, people, and permissions</span></div><div class="pages workspace-pages">${t.map(i=>this._page(i))}</div></section>`:d} <button type="button" class="history-link" @click="${()=>gi("manage-history")}"><span class="page-icon" aria-hidden="true">${y("history")}</span> <span class="page-copy"><strong>Workspace history</strong><span>Follow activity and triage changes across your workspace.</span></span> <span class="history-cta">View history ${y("arrow")}</span></button>`}</div>`}_page([e,t,i,r]){return a`<button type="button" class="${`page page-${r}`}" @click="${()=>gi(e)}"><span class="page-top"><span class="page-icon" aria-hidden="true">${y(r)}</span><strong class="page-title">${t}</strong><span class="arrow" aria-hidden="true">${y("arrow")}</span></span> <span class="page-copy"><span>${i}</span></span></button>`}};customElements.define("managed-admin-home",De);var Be=class extends f{static properties={_page:{state:!0},_history:{state:!0},_total:{state:!0},_error:{state:!0},_filter:{state:!0},_query:{state:!0},_repo:{state:!0},_actor:{state:!0},_options:{state:!0}};static styles=[h(Vt),h(b)];constructor(){super(),this._history=null,this._total=0,this._searchTimer=null,this._error=null,this._page=1,this._filter="all",this._query="",this._repo="",this._actor="",this._options={repos:[],users:[]},this._onActorFilter=e=>{let t=e.detail?.actor;if(typeof t!="string")return;let i=t.startsWith("user:")||t.startsWith("legacy:");this._query=i?"":t,this._filter="all",this._repo="",this._actor=i?t:"",this._load()}}connectedCallback(){super.connectedCallback(),document.addEventListener("managed-history-filter",this._onActorFilter),this._load()}disconnectedCallback(){clearTimeout(this._searchTimer),document.removeEventListener("managed-history-filter",this._onActorFilter),super.disconnectedCallback()}async _load(e=1){clearTimeout(this._searchTimer),this._error=null;let t=this._filter,i=this._query.trim(),r=this._actor,o=this._repo,n=`history:${JSON.stringify([e,t,i,o,r])}`;await this._loadCollection(n,"history",l=>Ct(l,e,t,i,o,r),l=>{this._history=l.history,this._total=l.total,this._page=l.page,this._options=l.filters??{repos:[],users:[]}})}_search(e){this._query=e,clearTimeout(this._searchTimer),this._loadRequest?.abort(),this._loadRequest=null,this._loading=!0,this._searchTimer=setTimeout(()=>{this._load()},250)}_setRepo(e){return this._repo=e,this._load()}_setActor(e){return this._actor=e,this._load()}_clearContext(){return this._repo="",this._actor="",this._load()}_contextFilters(){let e=this._options.repos,t=this._options.users??[],i=[{value:"",label:"All repositories",special:!0,reset:!0},...this._repo&&!e.includes(this._repo)?[{value:this._repo,label:this._repo}]:[],...e.map(r=>({value:r,label:r}))];return a`<repository-selector label="Filter history by repository" .options="${i}" .value="${this._repo}" @repository-change="${r=>{this._setRepo(r.detail.value)}}"></repository-selector><user-selector label="Filter history by user" reset-label="All users" placeholder="${this._actor?"Selected user":"All users"}" .users="${t}" .value="${this._actor}" @user-change="${r=>{this._setActor(r.detail.value)}}"></user-selector><button type="button" class="btn" aria-label="Clear repository and user filters" ?disabled="${!this._repo&&!this._actor}" @click="${()=>{this._clearContext()}}">Clear selection</button>`}render(){let e=this._history??[],t=this._page,i=(t-1)*100;return a`<div class="wrap">${w("manage-history",this._role,this.allowShare)}<h1 class="sr-only">History</h1><div class="page-intro"><p class="intro">${this._role==="admin"?"Uploads, builds, access changes, repository changes, deletions, and triage.":"Bundle, report, and triage history within your team access."}</p><span class="result-count">${this._history?this._total:"\u2026"} entries</span></div><div class="toolbar" role="search"><input type="search" maxlength="500" aria-label="Search history" placeholder="Search history…" .value="${this._query}" @input="${r=>this._search(r.target.value)}"> <select aria-label="Filter history by type" .value="${this._filter}" @change="${r=>{this._filter=r.target.value,this._load()}}"><option value="all">All activity</option><option value="triage">Triage</option><option value="visibility">Visibility</option><option value="upload">Uploads and builds</option><option value="repository">${this._role==="admin"?"Repositories":"Assignments"}</option>${this._role==="admin"?a`<option value="access">Access</option>`:d}<option value="delete">Deletions</option></select> ${this._contextFilters()} <button type="button" class="btn" ?disabled="${this._loading}" @click="${()=>this._load(t)}">Refresh</button></div>${this._error?a`<p class="msg error" role="alert">Couldn’t load history: ${this._error} <button type="button" class="btn" @click="${()=>this._load()}">Retry</button></p>`:d}<div class="manage-list" aria-busy="${this._loading}">${this._history==null?this._error?d:$("Loading history\u2026"):e.length===0?a`<div class="history"><p class="empty">${this._query.trim()||this._filter!=="all"||this._repo||this._actor?"No activity matches your filters.":"No history available yet."}</p></div>`:a`<div class="history" aria-label="Workspace history"><div class="history-head" aria-hidden="true"><span>Type</span><span>Activity</span><span>Repository / report / finding</span><span>Time</span></div>${e.map(r=>this._row(r))}</div>`}</div>${this._total>100?a`<nav class="pagination" aria-label="History pages"><span role="status">${i+1}–${Math.min(i+100,this._total)} of ${this._total} entries</span><button type="button" class="btn" ?disabled="${this._loading||t===1}" @click="${()=>this._changePage(t-1)}">Previous</button><span>Page ${t} of ${Math.ceil(this._total/100)}</span><button type="button" class="btn" ?disabled="${this._loading||i+100>=this._total}" @click="${()=>this._changePage(t+1)}">Next</button></nav>`:d}</div>`}async _changePage(e){await this._load(e),await this.updateComplete,this.renderRoot.querySelector(".manage-list")?.scrollTo({top:0})}_row(e){let t=[e.repo??e.repository,e.report,e.finding].filter(Boolean).join(" \xB7 "),i=e.actor??e.user??"Unknown user",r=e.action??"updated workspace data";return a`<div class="row"><span class="${`kind ${e.kind??""}`}">${e.kind??"activity"}</span><span class="activity-description" data-tooltip-truncated data-tooltip="${`${i} ${r}`}"><strong class="actor">${i}</strong> <span class="action">${r}</span></span><span class="detail" data-tooltip-truncated data-tooltip="${t}">${t||"\u2014"}</span><span class="history-time">${qe(e.at)}</span></div>`}};customElements.define("managed-admin-history",Be);function qe(s){if(s==null||s==="")return a`<span>Unknown</span>`;let e=typeof s=="number"?s:Number(s);if(!Number.isFinite(e))return a`<span>Unknown</span>`;let t=new Date(e);return Number.isNaN(t.getTime())?a`<span>Unknown</span>`:a`<time datetime="${t.toISOString()}">${t.toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"})}</time><span>${t.toLocaleTimeString(void 0,{hour:"numeric",minute:"2-digit"})}</span>`}function bi(s){if(s==null||s==="")return"Unknown";let e=typeof s=="number"?s:Number(s);return Number.isFinite(e)&&!Number.isNaN(new Date(e).getTime())?`Last seen ${new Date(e).toLocaleString()}`:"Unknown"}function fi(s){return s?.lastActivityAt??s?.lastActivity??s?.lastWriteAt??s?.lastActiveAt}var Me=class extends f{static properties={_query:{state:!0},_users:{state:!0},_teams:{state:!0},_error:{state:!0}};static styles=[h(Wt),h(b)];constructor(){super(),this._users=null,this._query="",this._teams=null,this._error=null}connectedCallback(){super.connectedCallback(),this._load()}async _load(){this._error=null,await this._loadCollection("users","users",e=>Promise.all([Et(e),V(e)]),([e,t])=>{this._users=e,this._teams=Array.isArray(t?.teams)?t.teams:[]})}_filteredUsers(){let e=this._query.trim().toLocaleLowerCase();return(this._users??[]).filter(t=>[t.name,t.login,t.role,...(this._teams??[]).filter(i=>i.members?.some(r=>r.userId===t.id)).map(i=>i.name)].filter(Boolean).join(" ").toLocaleLowerCase().includes(e))}render(){let e=this._filteredUsers();return a`<div class="wrap">${w("admin-users",this._role,this.allowShare)}<h1 class="sr-only">Users</h1><div class="page-intro"><p class="intro">Manage workspace access and roles.</p></div><div class="collection-toolbar" role="search"><input type="search" aria-label="Search users" placeholder="Search by name, username, or team…" .value="${this._query}" @input="${t=>{this._query=t.target.value}}"><span class="result-count" role="status">${this._users&&this._query.trim()?`${e.length} matching out of `:""}${this._users?.length??"\u2026"} users</span></div>${this._error?a`<p class="msg error" role="alert">Couldn't load users: ${this._error}</p>`:d}<div class="manage-list" aria-busy="${this._loading}">${this._users==null?this._error?d:$("Loading users\u2026"):this._list(e)}</div></div>`}_list(e){return this._users.length===0?a`<p class="msg">No users yet.</p>`:e.length===0?a`<div class="empty"><strong>No matching users</strong><p>Try another name, username, or team.</p></div>`:a`<div class="directory"><div class="list-head" aria-hidden="true"><span>Account</span><span>Team access</span><span>Last seen</span><span>Last activity</span><span>Role</span><span></span></div><ul class="users">${e.map(t=>this._row(t))}</ul></div>${this._users.some(t=>t.id===this._me)?a`<p class="self-note ui-hint">Your own role can only be changed by another admin.</p>`:d}`}_row(e){let t=e.id===this._me,i=(this._teams??[]).flatMap(r=>{let o=r.members?.find(n=>n.userId===e.id);return o==null?[]:[{team:r,member:o}]});return a`<li><span class="person">${vi(e.id,e.login)} <span class="who"><span class="name"><span class="name-text">${e.name||e.login}</span>${t?a`<span class="you">You</span>`:d}</span> <span class="login">@${e.login}</span> </span></span><span class="memberships">${se(e.role,"manage")?a`<span class="team-access"><strong class="team-access-role">${e.role==="admin"?"All reports":"Owned and team reports"}</strong><span class="team-perms"><span class="permission-granted">+ Deps</span><span class="permission-granted">+ Security</span></span></span>`:i.length===0?a`<span class="no-team">No team access</span>`:i.map(({team:r,member:o})=>a`<span class="team-access"><strong>${y("team")}<span>${r.name}</span></strong><span class="team-perms">${["dependencies","security"].map(n=>a`<span class="${o[n]===!0?"permission-granted":"permission-denied"}">${o[n]===!0?"+":"\u2212"} ${n==="dependencies"?"Deps":"Security"}</span>`)}</span></span>`)} </span><span class="last-seen" aria-label="${bi(e.lastSeenAt)}">${qe(e.lastSeenAt)}</span> <button type="button" class="activity-link" aria-label="${`Show activity by ${e.login}`}" @click="${()=>this._showActivity(e.id)}"><span class="last-activity" aria-label="${`Last activity ${bi(fi(e)).replace(/^Last seen /u,"")}`}">${qe(fi(e))}</span></button> <span class="access"><select class="role" ?disabled="${t}" aria-label="${`Change ${e.login}\u2019s role`}" aria-description="${Sr[e.role]??""}" @change="${r=>this._changeRole(e,r.target.value,r.target)}">${dt.map(r=>a`<option value="${r}" ?selected="${r===e.role}">${Rr[r]}</option>`)}</select> </span><span class="view-as">${t?d:a`<button type="button" class="icon-btn" aria-label="${`View as ${e.login}`}" data-tooltip="See the app as this user" @click="${r=>{this._viewAs(e,r.currentTarget)}}">${y("show")}</button>`}</span></li>`}async _viewAs(e,t){t.disabled=!0;try{await mt(e.id,this._csrf)}catch(i){t.disabled=!1,this.appState.notify(`Couldn't view as @${e.login}: ${i?.message??i}`)}}async _changeRole(e,t,i){let r=e.role;if(t!==r){try{await this.appState.mutate(()=>At(e.id,t,this._csrf),["users","teams","reports","history"]),e.role=t,await this._load()}catch(o){console.warn("admin: set role failed:",o),i.value=r}this.requestUpdate()}}_showActivity(e){document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:"manage-history",actor:`user:${e}`},bubbles:!0,composed:!0}))}};customElements.define("managed-admin-users",Me);var _i=y("repo"),Ue=class extends f{static properties={_publicRepoOpen:{state:!0},_publicRepository:{state:!0},_publicRepoError:{state:!0},_addingPublic:{state:!0},_data:{state:!0},_error:{state:!0},_actionError:{state:!0},_scope:{state:!0},_showAll:{state:!0},_query:{state:!0},_organization:{state:!0},_loading:{state:!0},_busy:{state:!0},_connectingApp:{state:!0},_detail:{state:!0},_aliases:{state:!0},_impact:{state:!0},_impactLoading:{state:!0},_removeOpen:{state:!0},_acknowledge:{state:!0},_deleteTriage:{state:!0},_confirmName:{state:!0}};static styles=[h(Jt),h(b)];constructor(){super(),this._publicRepoOpen=!1,this._publicRepository="",this._publicRepoError=null,this._addingPublic=!1,this._data=null,this._error=null,this._actionError=null,this._scope="connected",this._showAll=!1,this._query="",this._organization=null,this._loading=!0,this._busy=null,this._connectingApp=!1,this._detail=null,this._aliases=!1,this._impact=null,this._impactLoading=!1,this._impactFresh=!1,this._removeOpen=!1,this._acknowledge=!1,this._deleteTriage=!1,this._confirmName="",this._impactRequest=null}connectedCallback(){super.connectedCallback(),this._load()}disconnectedCallback(){super.disconnectedCallback(),this._impactRequest?.abort()}_repositoryKey(){return`repos:${JSON.stringify([this._scope,this._showAll])}`}async _load(e=!1){this._error=null;let{_scope:t,_showAll:i}=this;await this._loadCollection(this._repositoryKey(),"repositories",r=>Tt(t,i,e,r),r=>{this._data=r,this._organization=this._repositoryChoices().activeFacet})}_open(e){this._impactRequest?.abort(),this._publicRepoOpen=!1,this._scope=e,this._showAll=!1,this._query="",this._organization=null,this._detail=null,this._aliases=!1,this._impact=null,this._removeOpen=!1,this._acknowledge=!1,this._deleteTriage=!1,this._confirmName="",this._data=null,this._actionError=null,this._load()}_setShowAll(e){this._showAll=e,this._organization=null,this._data=null,this._load()}_search(e){this._query=e}_repositoryChoices(){let e=(this._data?.repositories??[]).map(t=>({value:t.id,label:t.fullName,repo:t}));return bt(e,this._query,this._organization,{includeSingletonOrganizations:!0})}_back(e=null){return a`<div class="breadcrumb"><button type="button" class="breadcrumb-manage" @click="${()=>{document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:"manage"},bubbles:!0,composed:!0}))}}">Manage</button> <span class="breadcrumb-separator" aria-hidden="true">›</span> <button type="button" class="breadcrumb-manage" aria-label="Back to repositories" @click="${()=>{this._detail?(this._impactRequest?.abort(),this._detail=null,this._actionError=null):this._aliases?this._aliases=!1:this._open("connected")}}">Repositories</button> <span class="breadcrumb-separator" aria-hidden="true">›</span> ${e?a`<h1 class="breadcrumb-current">${e}</h1>`:d}</div>`}_openDetail(e){this._detail=e,this._actionError=null,this._impact=this.appState.read(`repo-impact:${e.id}`)??null,this._impactFresh=!1,this._removeOpen=!1,this._acknowledge=!1,this._deleteTriage=!1,this._confirmName="",this._impactLoading=!0,this._impactRequest?.abort();let t=new AbortController;this._impactRequest=t,this.appState.load(`repo-impact:${e.id}`,"repository data",i=>Lt(e.id,i),{signal:t.signal,apply:i=>{this._impact=i}}).then(i=>(t.signal.aborted||(this._impactFresh=!0),i)).catch(i=>{!t.signal.aborted&&i?.name!=="AbortError"&&!this._impact&&(this._actionError=`Couldn't load repository data: ${i?.message??i}`)}).finally(()=>{this._impactRequest===t&&(this._impactLoading=!1)})}render(){if(this._detail)return this._detailPage(this._detail);if(this._aliases)return this._aliasesPage();let e=this._scope==="connected",t=e?"Repositories":`Add ${this._scope} repository`,i=this._repositoryChoices();return a`<div class="wrap">${w("manage-repos",this._role,this.allowShare)} ${e?a`<h1 class="sr-only">${t}</h1>`:this._back(t)}<div class="page-intro"><p class="intro">${e?"Manage connected repositories and their settings.":this._scope==="installed"?this._showAll?"Showing all repositories the GitHub App can read.":"Showing installed repositories you can access on GitHub.":"Choose a public repository your GitHub account is involved with."}</p>${e?a`<div class="repository-actions"><button type="button" class="btn" @click="${()=>this._open("installed")}">${this._accessIcon("private")} Add installed repository</button> <button type="button" class="btn" @click="${()=>this._open("public")}">${this._accessIcon("public")} Add your public repository</button> ${this._role==="admin"&&this._data?.canAddAnyPublicRepository?a`<button type="button" class="btn" aria-expanded="${this._publicRepoOpen}" @click="${()=>this._openPublicRepository()}">${this._accessIcon("public")} Add a public repository</button>`:d}</div>`:d}</div>${!e&&this._scope==="installed"?a`<div class="access-note"><p class="ui-hint">Installed repositories are readable through the GitHub App. Install it on a repository or organization to make it available here.</p><span class="access-action">${this._data?.installUrl?a`<a class="btn" href="${this._data.installUrl}" target="_blank" rel="noopener noreferrer">Configure GitHub access</a>`:a`<button type="button" class="btn" disabled="disabled">Configure GitHub access</button>`}</span></div>`:d}<div class="toolbar"><label class="search"><input type="search" aria-label="${e?"Search connected repositories":`Search ${this._scope} repositories`}" placeholder="Search by repository or owner…" .value="${this._query}" @input="${r=>this._search(r.target.value)}"></label> ${this._scope==="installed"?a`<label class="show-all"><input type="checkbox" role="switch" .checked="${this._showAll}" @change="${r=>this._setShowAll(r.target.checked)}"><span>Show all</span></label><label class="show-all-paper"><input type="checkbox" .checked="${this._showAll}" @change="${r=>this._setShowAll(r.target.checked)}"><span>Show all</span></label>`:d} ${e?a`<span class="result-count" role="status">${this._data&&(this._query.trim()||i.activeFacet!=null)?`${i.count} ${i.count===1?"repository":"repositories"} out of `:""}${this._data?.connectedCount??"\u2026"} connected</span>`:a`<button type="button" class="btn" ?disabled="${this._loading}" @click="${()=>{this._load(!0)}}">Refresh</button>`}</div>${e&&this._publicRepoOpen&&this._role==="admin"&&this._data?.canAddAnyPublicRepository?this._publicRepositoryForm():d} ${this._actionError?a`<p class="msg error" role="alert">${this._actionError}</p>`:d} ${this._error?a`<p class="msg error" role="alert">Couldn't load repositories: ${this._error}</p><button type="button" class="btn" @click="${()=>{this._load()}}">Try again</button>`:d} ${!e&&this._data?a`<p class="repository-count" role="status">${i.count}${i.count===i.total?"":` of ${i.total}`} ${i.total===1?"repository":"repositories"}</p>`:d}<div class="manage-list"><div aria-busy="${this._loading}">${this._body(i)}</div>${e&&this._role==="admin"?a`<div class="aliases-link"><button type="button" class="btn" @click="${()=>{this._publicRepoOpen=!1,this._actionError=null,this._aliases=!0}}">Aliases</button></div>`:d}</div></div>`}_aliasesPage(){return a`<div class="wrap">${w("manage-repos",this._role,this.allowShare)} ${this._back("Aliases")}<div class="manage-list"><managed-repository-aliases .session="${this.session}" .repositories="${this._data?.repositories}"></managed-repository-aliases></div></div>`}_body(e){return this._data?a`${this._data.tokenMissing&&!this._showAll&&this._scope!=="connected"?a`<p class="msg">Log out and back in to refresh your GitHub membership access.</p>`:d}<div class="repository-browser">${e.showFacets?a`<nav class="organization-list" aria-label="Filter by organization"><button type="button" class="organization" aria-pressed="${e.activeFacet==null}" @click="${()=>{this._organization=null}}"><span class="organization-name">All organizations</span><span class="organization-count">${e.total}</span></button> ${e.facets.map(t=>a`<button type="button" class="organization" aria-pressed="${e.activeFacet===t.value}" @click="${()=>{this._organization=t.value}}"><span class="organization-name" data-tooltip-truncated data-tooltip="${t.name}">${t.name}</span><span class="organization-count">${t.count}</span></button>`)}</nav>`:d}<div class="repo-results">${e.sections.map(t=>a`<section class="repo-group" aria-label="${t.label??"Repositories"}">${t.label?a`<h2 class="owner-head"><span class="owner-icon" aria-hidden="true">${t.label[0]??"?"}</span>${t.label}</h2>`:d}<ul class="repos">${t.options.map(i=>this._row(i.repo,t.organization?i.name:i.label))}</ul></section>`)} ${e.count?d:a`<div class="empty"><strong>${e.total?"No matching repositories":this._scope==="connected"?"No connected repositories yet":`No ${this._scope} repositories available`}</strong><p>${e.total?"Try a different search or organization.":this._scope==="connected"?"Add an installed or public repository to get started.":this._scope==="installed"?this._showAll?"Install the GitHub App, then refresh this list.":"No installed repositories match your GitHub access. Turn on Show all to browse every installation.":"Public repositories associated with your GitHub account will appear here."}</p></div>`}</div></div>`:this._error?d:$("Loading repositories\u2026")}_openPublicRepository(){this._publicRepoOpen=!0,this._publicRepoError=null,this.updateComplete.then(()=>this.renderRoot?.querySelector("#public-repository")?.focus())}_publicRepositoryForm(){return a`<form class="public-repository-form" aria-label="Add a public repository" @submit="${e=>{e.preventDefault(),this._addPublicRepository()}}"><label class="confirm-field" for="public-repository"><span>Public GitHub repository</span> <input id="public-repository" class="confirm-name" type="text" placeholder="owner/repo or https://github.com/owner/repo" autocomplete="off" required .value="${this._publicRepository}" ?disabled="${this._addingPublic}" @input="${e=>{this._publicRepository=e.target.value}}"></label><div class="dialog-actions"><button type="button" class="btn" ?disabled="${this._addingPublic}" @click="${()=>{this._publicRepoOpen=!1}}">Cancel</button><button type="submit" class="btn" ?disabled="${this._addingPublic||!this._publicRepository.trim()}">${this._addingPublic?"Adding\u2026":"Add repository"}</button></div>${this._publicRepoError?a`<p class="msg error" role="alert">${this._publicRepoError}</p>`:d}</form>`}async _addPublicRepository(){if(!(this._addingPublic||!this._publicRepository.trim())){this._addingPublic=!0,this._publicRepoError=null;try{await this.appState.mutate(()=>Nt(this._publicRepository.trim(),this._csrf),["repos","reports","bundles","teams","users","history","scan-sources"]),this._publicRepoOpen=!1,this._publicRepository="",await this._load()}catch(e){this._publicRepoError=e?.message??String(e)}finally{this._addingPublic=!1}}}_row(e,t){let i=`${this._accessLabel(e)}${this._scope==="connected"&&e.active===!1?" \xB7 Deactivated":""}`,r=a`${_i}<span class="repo-copy"><span class="repo-name">${t}</span><span class="repo-meta">${i}</span></span>`;return this._scope==="connected"?a`<li><button type="button" class="repo-row" aria-label="${`Manage ${e.fullName}`}" @click="${()=>this._openDetail(e)}">${r}<svg class="arrow" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true"><path d="m6 4 4 4-4 4"/></svg></button></li>`:a`<li class="repo-row">${r} ${e.selected?a`<span class="connected"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="m3 8 3 3 7-7"/></svg>Connected</span>`:e.private&&!e.installed?a`<span class="repo-meta">Needs App access</span>`:a`<button type="button" class="btn" aria-label="${`Add ${e.fullName}`}" ?disabled="${this._busy!=null}" @click="${()=>{this._setActive(e,!0)}}">${this._busy===e.id?"Adding\u2026":"Add"}</button>`}</li>`}_accessLabel(e){let t=e.visibility==="public"?"Public":e.visibility==="internal"?"Internal":e.private?"Private":null,i=e.installed?"GitHub App":"GitHub";return t?`${t} \xB7 ${i}`:i}_connectAppButton(e){return e.installed?d:a`<button type="button" class="btn connect-app" aria-label="${`Connect GitHub App to ${e.fullName}`}" ?disabled="${this._busy!=null}" @click="${()=>{this._connectApp(e)}}">${this._connectingApp&&this._busy===e.id?"Connecting\u2026":"Connect GitHub App"}</button>`}async _connectApp(e){if(!(this._busy!=null||e.installed)){this._busy=e.id,this._connectingApp=!0,this._actionError=null;try{let t=await this.appState.mutate(()=>Ot(e.id,this._csrf),["repos","history","scan-sources"]);if(!this.isConnected)return;if(!t.connected){globalThis.location.assign(t.installUrl);return}this._detail?.id===e.id&&(this._detail={...this._detail,installed:!0}),this._data&&(this._data={...this._data,repositories:this._data.repositories.map(i=>i.id===e.id?{...i,installed:!0}:i)}),await this._load()}catch(t){this._actionError=`Couldn't connect ${e.fullName}: ${t?.message??t}`}finally{this._busy=null,this._connectingApp=!1}}}_detailPage(e){let t=e.active!==!1,i=this._impact?.reports??[],r=this._impact?.bundles??[];return a`<div class="wrap">${w("manage-repos",this._role,this.allowShare)}<div class="head">${this._back()}<h1>${e.fullName}</h1></div><p class="intro ui-hint">Repository settings and stored data.</p>${this._actionError?a`<p class="msg error" role="alert">${this._actionError}</p>`:d}<section class="section" aria-label="Repository connection"><h2>Connection</h2><div class="settings-row"><div class="settings-copy"><strong>${this._accessLabel(e)}</strong><p>${t?"Active for new scans and uploads.":"Deactivated. Stored reports and bundles are retained."}</p></div><span class="${`status-line ${t?"":"inactive"}`}">${t?"Active":"Deactivated"}</span> ${this._connectAppButton(e)} ${e.htmlUrl?a`<a class="btn" href="${e.htmlUrl}" target="_blank" rel="noopener noreferrer">View on GitHub</a>`:d}</div><div class="settings-row"><div class="settings-copy"><strong>${t?"Deactivate repository":"Reactivate repository"}</strong><p>${t?"Pause new scans while preserving all stored data and attachments.":"Make this repository available for new scans and bundle attachments again."}</p></div><button type="button" class="btn" ?disabled="${this._busy!=null}" @click="${()=>{this._setActive(e,!t)}}">${this._busy===e.id&&!this._connectingApp?t?"Deactivating\u2026":"Reactivating\u2026":t?"Deactivate":"Reactivate"}</button></div></section><section class="data-section" aria-label="Stored repository data"><h2>Stored data</h2><div class="dialog-data" aria-busy="${this._impactLoading}">${[["Reports",i],["Bundles",r]].map(([o,n])=>a`<section><h3>${o} (${this._impact==null?"\u2026":n.length})</h3>${this._impactLoading&&this._impact==null?a`<p class="data-empty" role="status">Loading ${o.toLowerCase()}…</p>`:this._impact==null?a`<p class="data-empty">Attached data could not be loaded.</p><button type="button" class="btn" @click="${()=>this._openDetail(e)}">Try again</button>`:n.length>0?a`<ul>${n.map(l=>a`<li>${l.filename}${l.repoDirectory?` \xB7 ${l.repoDirectory}`:d}</li>`)}</ul>`:a`<p class="data-empty">No ${o.toLowerCase()} attached.</p>`}</section>`)}</div></section><section class="section" aria-label="Permanent repository removal"><h2>Permanent removal</h2><div class="settings-row"><div class="settings-copy"><strong>Delete repository and stored data</strong><p>Deactivation is reversible. Permanent removal deletes this repository’s attached reports and bundles; it cannot be undone.</p></div><button type="button" class="btn danger" ?disabled="${this._busy!=null||this._impactLoading||!this._impactFresh||this._impact==null}" @click="${()=>{this._removeOpen=!0,this._acknowledge=!1,this._deleteTriage=!1,this._confirmName=""}}">Remove permanently</button></div></section>${this._removeOpen&&this._impact!=null&&!this._impactLoading?this._removeDialog(e,i,r):d}</div>`}_removeDialog(e,t,i){let r=t.length>0||i.length>0,o=this._canRemove(e);return a`<div class="dialog-backdrop" role="presentation" @click="${n=>{n.target===n.currentTarget&&(this._removeOpen=!1)}}"><section class="${`dialog ${r?"":"small"}`}" role="dialog" aria-modal="true" aria-labelledby="remove-repo-title"><h2 id="remove-repo-title">Remove ${e.fullName} permanently?</h2><p class="dialog-copy">${r?"This destructive action will remove the repository and the stored data listed below. Deactivation keeps all of it.":"This repository has no attached reports or bundles. Permanent removal deletes its connection record."}</p>${r?a`<div class="dialog-data"><section><h3>Reports to delete (${t.length})</h3><ul>${t.map(n=>a`<li>${q(n.filename)}${n.repoDirectory?` \xB7 ${n.repoDirectory}`:d}</li>`)}</ul></section><section><h3>Bundles to delete (${i.length})</h3><ul>${i.map(n=>a`<li>${n.filename}</li>`)}</ul></section></div>`:d} <label class="confirm-line"><input type="checkbox" .checked="${this._acknowledge}" @change="${n=>{this._acknowledge=n.target.checked}}"><span>I understand this permanently deletes the repository connection${r?", reports, and bundles":""}.</span></label> ${(this._impact?.triageCount??0)>0?a`<label class="confirm-line"><input type="checkbox" .checked="${this._deleteTriage}" @change="${n=>{this._deleteTriage=n.target.checked}}"><span>Also delete ${this._impact.triageCount} triage entr${this._impact.triageCount===1?"y":"ies"} belonging only to this repository.</span></label>`:d} ${r?a`<label class="confirm-field"><span>Type <strong>${e.fullName}</strong> to confirm.</span><input class="confirm-name" type="text" autocomplete="off" .value="${this._confirmName}" @input="${n=>{this._confirmName=n.target.value}}"></label>`:d}<div class="dialog-actions"><button type="button" class="btn" @click="${()=>{this._removeOpen=!1}}">Cancel</button><button type="button" class="btn danger" ?disabled="${!o}" @click="${()=>{this._remove(e)}}">${this._busy===e.id?"Removing\u2026":"Remove permanently"}</button></div></section></div>`}_accessIcon(e){return e==="private"?a`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="7" rx="1"/><path d="M5 7V5a3 3 0 0 1 6 0v2"/></svg>`:a`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6"/><path d="M2.5 8h11M8 2c1.5 1.7 2.2 3.7 2.2 6S9.5 12.3 8 14c-1.5-1.7-2.2-3.7-2.2-6S6.5 3.7 8 2Z"/></svg>`}async _setActive(e,t){if(this._busy==null){this._busy=e.id,this._actionError=null;try{if(await this.appState.mutate(()=>It(e.id,t,this._csrf),["repos","reports","bundles","teams","users","history"]),!this.isConnected)return;this._detail?.id===e.id&&(this._detail={...this._detail,active:t}),this._data&&(this._data={...this._data,repositories:this._data.repositories.map(i=>i.id===e.id?{...i,active:t}:i)}),await this._load()}catch(i){let r=t?e.active===!1?"reactivate":"add":"deactivate";this._actionError=`Couldn't ${r} ${e.fullName}: ${i?.message??i}`}finally{this._busy=null}}}_canRemove(e){return this._busy!=null||this._impactLoading||!this._impactFresh||this._impact?.repoId!==e.id||!this._acknowledge?!1:!(this._impact.reports.length>0||this._impact.bundles.length>0)||this._confirmName===e.fullName}async _remove(e){if(this._canRemove(e)){this._busy=e.id,this._actionError=null;try{await this.appState.mutate(()=>Pt(e.id,e.fullName,this._deleteTriage,this._csrf),["repos","repo-impact","reports","report-preview","bundles","teams","users","history","scan-sources"]),this._removeOpen=!1,this._detail=null,this._impact=null,await this._load()}catch(t){this._actionError=`Couldn't remove ${e.fullName}: ${t?.message??t}`}finally{this._busy=null}}}};customElements.define("managed-admin-repos",Ue);function yi(s){return[{value:null,label:"No repository",special:!0},...s.map(e=>({value:e.repoId,label:e.fullName}))]}function wi(s,e,t,i,r){let o=a`<span class="ui-hint">${s}: </span><span class="location-metadata-value" data-tooltip-truncated data-tooltip="${e}">${e}</span>`;return i?a`<button type="button" class="location-metadata" aria-label="${`Use ${t} from ${s}: ${e}`}" ?disabled="${r}" @click="${i}">${o}</button>`:a`<span class="location-metadata">${o}</span>`}var Fe=class extends f{static properties={localImportSource:{attribute:!1},_query:{state:!0},_visibility:{state:!0},_data:{state:!0},_error:{state:!0},_busy:{state:!0},_dragOver:{state:!0},_preview:{state:!0},_previewLoading:{state:!0},_locationReport:{state:!0},_locationOrigin:{state:!0},_locationOriginError:{state:!0},_locationRepo:{state:!0},_locationDirectory:{state:!0},_locationBusy:{state:!0}};static styles=[h(de),h(Ee),h(b),h(Ce)];constructor(){super(),this._query="",this._visibility="all",this._data=null,this._error=null,this._busy=!1,this._dragOver=!1,this._preview=null,this._previewLoading=null,this._previewRequest=null,this._locationReport=null,this._locationOrigin=null,this._locationOriginError=null,this._locationOriginRequest=null,this._locationRepo=null,this._locationDirectory="",this._locationBusy=!1,this._teardownDrop=null,this._queue=[],this._localImport=new K(this,"report",e=>Se(this,e,t=>ke(t,this._csrf),["reports","repo-impact","history","scan-sources"]))}connectedCallback(){super.connectedCallback(),this._load(),this._teardownDrop=J(this,e=>{this._upload(e)},e=>{this._dragOver=e})}disconnectedCallback(){super.disconnectedCallback(),this._teardownDrop?.(),this._cancelPreview(),this._closeLocation()}async _load({preserveError:e=!1}={}){e||(this._error=null),await this._loadCollection("reports","reports",zt,t=>{this._data=t,this._preview&&!t.reports?.some(i=>i.id===this._preview)&&this._cancelPreview(),this._locationReport&&!t.reports?.some(i=>i.id===this._locationReport)&&this._closeLocation()})}render(){return a`${this._dragOver?a`<div class="dropzone">Drop reports to upload</div>`:d}<div class="wrap">${w("manage-reports",this._role,this.allowShare)}<h1 class="sr-only">Reports</h1><div class="page-intro"><p class="intro">Upload reports. New reports stay hidden until you make them visible.</p>${this._localImport.renderAction()}</div>${this._localImport.renderPanel(this._busy||!this._csrf)}<div class="drop-card"><span class="drop-icon" aria-hidden="true">${y("upload")}</span><span class="drop-copy"><strong>Upload reports</strong><span>Drop files anywhere on this page.</span></span><button type="button" class="drop-browse" ?disabled="${this._busy}" @click="${()=>W(e=>{this._upload(e)})}">${this._busy?"Uploading\u2026":"Browse files"}</button></div>${this._data?.repoScopes?.length?a`<p class="intro">Team paths: ${this._data.repoScopes.map(e=>`${this._data.repos.find(t=>t.repoId===e.repoId)?.fullName??e.repoId}/${e.path??""}`).join(", ")}</p>`:d} ${this._body()}</div>`}_body(){let e=Array.isArray(this._data?.reports)?this._data.reports:[],t=this._query.trim().toLocaleLowerCase(),i=e.filter(o=>[o.filename,q(o.filename),o.repoFullName,o.repoDirectory,o.uploadedByLogin].filter(Boolean).join(" ").toLocaleLowerCase().includes(t)&&(this._visibility==="all"||!!o.visible==(this._visibility==="visible"))),r=`${t||this._visibility!=="all"?`${i.length} of `:""}${e.length} ${e.length===1?"report":"reports"}`;return a`<div class="collection-toolbar" role="search"><input type="search" aria-label="Search reports" placeholder="Search reports or repositories…" .value="${this._query}" @input="${o=>{this._query=o.target.value}}"><select aria-label="Report visibility" .value="${this._visibility}" @change="${o=>{this._visibility=o.target.value}}"><option value="all">All reports</option><option value="visible">Visible to teams</option><option value="hidden">Hidden reports</option></select><span class="result-count" role="status">${this._data==null?"\u2026 reports":r}</span></div>${this._error?a`<p class="msg error" role="alert">${this._error}</p>`:d}<div class="manage-list" aria-busy="${this._loading}">${this._data==null?this._error?d:$("Loading reports\u2026"):i.length>0?a`<div class="report-list"><div class="report-list-head" aria-hidden="true"><span class="report-heading">Report</span><span>Repository</span><span>Uploaded by</span><span>Date</span><span class="size-heading">Size</span><span>Visibility</span><span class="actions-heading">Actions</span></div><ul class="reports">${i.map(o=>this._row(o))}</ul></div>`:a`<div class="empty"><strong>${e.length===0?"No reports uploaded yet":"No matching reports"}</strong><p>${e.length===0?"Drop a report here or browse files above.":"Try a different search or visibility filter."}</p></div>`}</div>`}_row(e){let t=q(e.filename),i=e.analyzer??e.source??e.producer??"default",r=be[i]??be.default,o=e.repoEmbedded!==!0&&e.canChangeRepo!==!1,n=e.repoEmbedded===!0||e.repoId!=null,l=e.repoFullName?`${e.repoFullName}${e.repoDirectory?`/${e.repoDirectory}`:""}`:"No repository assigned",c=Number.isFinite(e.uploadedAt)?new Date(e.uploadedAt).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"}):"";return a`<li class="${`report${e.visible===!1?" content-hidden":""}`}"><div class="report-main"><span class="report-mark" aria-hidden="true">${ie(r)}</span> <span class="report-name" data-tooltip-truncated data-tooltip="${t}">${t}</span> <span class="report-location" data-tooltip-truncated data-tooltip="${l}">${l}</span> <span class="report-meta report-uploader" data-tooltip-truncated data-tooltip="${e.uploadedByLogin??"Uploader removed"}">${e.uploadedByLogin??"Uploader removed"}</span> <span class="report-meta report-date">${c}</span> <span class="report-meta report-size">${M(e.byteSize)}</span> <span class="${`status ${e.visible?"visible":"hidden"}`}">${e.visible?"Visible":"Hidden"}</span> <span class="report-actions">${o?a`<button type="button" class="action" data-tooltip="Set repository location" aria-label="${`Set location for ${t}`}" @click="${()=>this._openLocation(e)}">${y("repo")}</button>`:a`<span class="action-spacer"></span>`} <button type="button" class="action" aria-label="${`Preview ${t}`}" aria-expanded="${this._preview===e.id}" @click="${()=>{this._togglePreview(e)}}">${y("preview")}</button> <button type="button" class="action" data-tooltip="${e.visible?"Hide from teams":n?"Make visible to teams":"Assign a repository before publishing"}" aria-label="${`${e.visible?"Hide":"Make visible"} ${t}`}" ?disabled="${e.canChangeRepo===!1||!n&&!e.visible}" @click="${()=>{this._setVisible(e,!e.visible)}}">${y(e.visible?"hide":"show")}</button> <a class="action" aria-label="${`Download ${t}`}" href="${`/api/admin/reports/${encodeURIComponent(e.id)}`}">${y("download")}</a> <button type="button" class="action danger" aria-label="${`Delete ${t}`}" ?disabled="${e.canChangeRepo===!1}" @click="${()=>this._delete(e)}">${Ke}</button></span></div>${this._preview===e.id?a`<div class="preview">${this._previewLoading===e.id?a`<span class="preview-loading">Loading preview…</span>`:a`<pre>${this._previewText??""}</pre>`}</div>`:d} ${this._locationReport===e.id?this._locationEditor(e):d}</li>`}_openLocation(e){e.canChangeRepo!==!1&&(this._closeLocation(),this._locationReport=e.id,this._locationRepo=e.repoId??null,this._locationDirectory=e.repoDirectory??"",this._error=null,this._loadLocationOrigin(e))}_closeLocation(){this._locationOriginRequest?.abort(),this._locationOriginRequest=null,this._locationReport=null,this._locationOrigin=null,this._locationOriginError=null}async _loadLocationOrigin(e){this._locationOriginRequest?.abort();let t=new AbortController;this._locationOriginRequest=t,this._locationOrigin=void 0,this._locationOriginError=null;try{let i=await Mt(e.id,AbortSignal.any([t.signal,this.appState.sessionController.signal]));if(this._locationOriginRequest!==t)return;i?.repoId!=null&&this._data&&(this._data={...this._data,repos:[...(this._data.repos??[]).filter(r=>r.repoId!==i.repoId),{repoId:i.repoId,fullName:i.github}]}),this._locationOrigin=typeof i?.github=="string"&&i.github?{github:i.github,repoId:i.repoId??null,directory:typeof i.directory=="string"?i.directory||"/":null}:null}catch(i){if(this._locationOriginRequest!==t)return;this._locationOrigin=null,i?.name!=="AbortError"&&(this._locationOriginError="Couldn't load the report\u2019s suggested location.")}}_locationEditor(e){let t=Array.isArray(this._data?.repos)?this._data.repos:[],i=this._locationOrigin,r=i?.repoId!=null&&t.find(n=>n.repoId===i.repoId),o=(n,l,c)=>wi("report",n,l,c,this._locationBusy);return a`<div class="location-editor"><div class="location-field"><div class="location-field-header"><span class="ui-hint">Repository</span>${i?o(i.github,"repository",r?()=>{this._locationRepo=r.repoId,i.directory!=null&&(this._locationDirectory=i.directory)}:null):d}</div><repository-selector label="Repository for report" .options="${yi(t)}" .value="${this._locationRepo}" ?disabled="${this._locationBusy}" @repository-change="${n=>{this._locationRepo=n.detail.value}}"></repository-selector></div><div class="location-field"><div class="location-field-header"><label for="${`report-dir-${e.id}`}">Directory (optional)</label>${i?o(i.directory??"Directory not specified","directory",i.directory==null?null:()=>{this._locationDirectory=i.directory}):d}</div><input id="${`report-dir-${e.id}`}" type="text" placeholder="Repository root" .value="${this._locationDirectory}" ?disabled="${this._locationBusy}" @input="${n=>{this._locationDirectory=n.target.value}}"></div><div class="location-actions"><button type="button" class="action" ?disabled="${this._locationBusy}" @click="${()=>this._closeLocation()}">Cancel</button><button type="button" class="action" ?disabled="${this._locationBusy}" @click="${()=>{this._saveLocation(e)}}">Save</button></div>${this._locationOriginError?a`<p class="location-origin" role="status">${this._locationOriginError} <button type="button" class="origin-retry" @click="${()=>{this._loadLocationOrigin(e)}}">Retry</button></p>`:d}</div>`}async _saveLocation(e){if(!this._locationBusy){this._locationBusy=!0,this._error=null;try{await this.appState.mutate(()=>qt(e.id,this._locationRepo,this._locationDirectory.trim(),this._csrf),["reports","repo-impact","history","scan-sources"]),this._closeLocation(),await this._load({preserveError:!0})}catch(t){this._error=`Couldn't set report location: ${String(t?.message??t)}`}finally{this._locationBusy=!1}}}_cancelPreview(){this._previewRequest?.abort(),this._previewRequest=null,this._preview=null,this._previewLoading=null,this._previewText=""}async _togglePreview(e){let t=this._preview===e.id;if(this._cancelPreview(),t)return;let i=new AbortController;this._previewRequest=i;let r=()=>this._previewRequest===i&&!i.signal.aborted;this._preview=e.id,this._previewText=this.appState.read(`report-preview:${e.id}`)??"",this._previewLoading=this.appState.read(`report-preview:${e.id}`)===void 0?e.id:null;try{await this.appState.load(`report-preview:${e.id}`,"report preview",async o=>{let n=await m(`/api/admin/reports/${encodeURIComponent(e.id)}`,{credentials:"same-origin",signal:o});if(!n.ok)throw new Error(`HTTP ${n.status}`);let l=await n.text();return l.slice(0,4e3)+(l.length>4e3?`
16
16
  \u2026`:"")},{signal:i.signal,apply:o=>{this._previewText=o}})}catch(o){r()&&o?.name!=="AbortError"&&!this._previewText&&(this._previewText=`Preview unavailable: ${o?.message??o}`)}finally{r()&&(this._previewLoading=null)}}async _setVisible(e,t){this._error=null;try{await this.appState.mutate(()=>Bt(e.id,t,this._csrf),["reports","history","scan-sources"]),e.visible=t,this.requestUpdate(),await this._load()}catch(i){this._error=`Couldn't change report visibility: ${String(i?.message??i)}`}}async _upload(e){await Y(this,e,t=>ke(t,this._csrf),["reports","repo-impact","history","scan-sources"])}async _delete(e){if(globalThis.confirm?.(`Delete \u201C${q(e.filename)}\u201D? This can't be undone.`)){this._error=null;try{await this.appState.mutate(()=>Ut(e.id,this._csrf),["reports",`report-preview:${e.id}`,"repo-impact","history","scan-sources"])}catch(t){this._error=`Delete failed: ${String(t?.message??t)}`}await this._load({preserveError:!0})}}};customElements.define("managed-admin-reports",Fe);var Cr=a`<svg class="report-icon" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M8.878.392a1.75 1.75 0 0 0-1.756 0l-5.25 3.045A1.75 1.75 0 0 0 1 4.951v6.098c0 .624.332 1.2.872 1.514l5.25 3.045a1.75 1.75 0 0 0 1.756 0l5.25-3.045c.54-.313.872-.89.872-1.514V4.951c0-.624-.332-1.2-.872-1.514ZM7.875 1.69a.25.25 0 0 1 .25 0l4.63 2.685L8 7.133 3.245 4.375Zm-5.125 4.4 4.5 2.61v5.317l-4.375-2.537a.25.25 0 0 1-.125-.216Zm6 7.927V8.7l4.5-2.61v3.96a.25.25 0 0 1-.125.216Z"/></svg>`,He=class extends f{static properties={createRepoId:{attribute:!1},installTooltips:{attribute:!1},localImportSource:{attribute:!1},_query:{state:!0},_visibility:{state:!0},_visibilityBusy:{state:!0},_creating:{state:!0},_data:{state:!0},_locationBundle:{state:!0},_locationOrigin:{state:!0},_locationOriginError:{state:!0},_locationRepo:{state:!0},_locationDirectory:{state:!0},_locationBusy:{state:!0},_error:{state:!0},_busy:{state:!0},_dragOver:{state:!0}};static styles=[h(Yt),h(Ee),h(b),h(Ce)];constructor(){super(),this._query="",this._visibility="all",this._visibilityBusy=null,this._creating=!1,this._data=null,this._error=null,this._busy=!1,this._locationBundle=null,this._locationOrigin=null,this._locationOriginError=null,this._locationOriginRequest=null,this._locationRepo=null,this._locationDirectory="",this._locationBusy=!1,this._dragOver=!1,this._teardownDrop=null,this._queue=[],this._localImport=new K(this,"bundle",e=>Se(this,e,t=>Re(t,this._csrf),["bundles","reports","repo-impact","history","scan-sources"]))}connectedCallback(){super.connectedCallback(),this._load(),this._teardownDrop=J(this,e=>{this._creating||this._upload(e)},e=>{this._dragOver=!this._creating&&e})}disconnectedCallback(){super.disconnectedCallback(),this._teardownDrop?.(),this._closeLocation()}async _load({preserveError:e=!1}={}){e||(this._error=null),await this._loadCollection("bundles","bundles",xe,t=>{this._data=t,this._locationBundle&&!t.bundles?.some(i=>i.id===this._locationBundle)&&this._closeLocation()})}_showCreate(e){if(!e&&this.createRepoId!=null){document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:"manage-bundles"},bubbles:!0,composed:!0}));return}this._creating=e,this._dragOver=!1,e||this.updateComplete.then(()=>this.renderRoot.querySelector(".create-bundle-action")?.focus())}willUpdate(e){e.has("createRepoId")&&(this._creating=this.createRepoId!=null)}render(){return this._creating?a`<div class="wrap">${w("manage-bundles",this._role,this.allowShare)}<div class="breadcrumb"><button type="button" class="breadcrumb-manage" @click="${()=>document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:"manage"},bubbles:!0,composed:!0}))}">Manage</button> <span class="breadcrumb-separator" aria-hidden="true">›</span> <button type="button" class="breadcrumb-manage" aria-label="Back to bundles" @click="${()=>this._showCreate(!1)}">Bundles</button> <span class="breadcrumb-separator" aria-hidden="true">›</span><h1 class="breadcrumb-current">Create a bundle</h1></div><managed-create-bundle .initialRepoId="${this.createRepoId??null}" .installTooltips="${this.installTooltips}" .createBundle="${this._csrf?(e,t)=>this.appState.mutate(()=>Dt(e,this._csrf,t),["bundles","bundle-metadata","reports","repo-impact","history","scan-sources"]):void 0}" @bundle-created="${e=>this.dispatchEvent(new CustomEvent("managed-bundle-open",{detail:e.detail,bubbles:!0,composed:!0}))}"></managed-create-bundle></div>`:a`${this._dragOver?a`<div class="dropzone">Drop bundles to upload</div>`:d}<div class="wrap">${w("manage-bundles",this._role,this.allowShare)}<h1 class="sr-only">Bundles</h1><div class="page-intro"><p class="intro">Source bundles for your repositories.</p><div class="bundle-actions">${this._localImport.renderAction()}<button type="button" class="local-import-toggle create-bundle-action" ?disabled="${!this._data}" @click="${()=>this._showCreate(!0)}">Create</button></div></div>${this._localImport.renderPanel(this._busy||!this._csrf)}<section class="drop-card" aria-label="Upload bundles"><span class="drop-icon" aria-hidden="true">${y("upload")}</span><span class="drop-copy"><strong>Upload source bundles</strong><span>Drop source archives anywhere on this page.</span></span> <button type="button" class="drop-browse" ?disabled="${this._busy}" @click="${()=>W(e=>{this._upload(e)})}">${this._busy?"Uploading\u2026":"Browse files"}</button></section>${this._body()}</div>`}_body(){let e=Array.isArray(this._data?.bundles)?this._data.bundles:[],t=this._query.trim().toLocaleLowerCase(),i=e.filter(c=>[c.filename,c.repoFullName,c.repoDirectory,c.kind,c.provenance==="build"?"built":null,c.uploadedByLogin].filter(Boolean).join(" ").toLocaleLowerCase().includes(t)&&(this._visibility==="all"||c.visible!==!1==(this._visibility==="visible"))),r=i.filter(c=>c.repoId==null).length,o=i.reduce((c,p)=>c+(Number.isFinite(p.byteSize)?p.byteSize:0),0),n=`${t||this._visibility!=="all"?`${i.length} of `:""}${e.length} ${e.length===1?"bundle":"bundles"}`,l=Map.groupBy(i,c=>c.repoFullName||"Unattached");return a`<div class="collection-toolbar" role="search"><input type="search" aria-label="Search bundles" placeholder="Search bundles or repositories…" .value="${this._query}" @input="${c=>{this._query=c.target.value}}"> <select aria-label="Bundle visibility" .value="${this._visibility}" @change="${c=>{this._visibility=c.target.value}}"><option value="all">All bundles</option><option value="visible">Visible to teams</option><option value="hidden">Hidden bundles</option></select> <span class="summary result-count" role="status"><span>${this._data==null?"\u2026 bundles":n}</span><span>${this._data==null?"\u2026":M(o)}</span> ${this._data==null||r?a`<span class="unassigned">${this._data==null?"\u2026 unattached":`${r} unattached`}</span>`:d}</span></div>${this._error?a`<p class="msg error" role="alert">${this._error}</p>`:d}<div class="manage-list" aria-busy="${this._loading}">${this._data==null?this._error?d:$("Loading bundles\u2026"):i.length>0?a`<div class="bundle-groups">${[...l].toSorted(([c],[p])=>c==="Unattached"?-1:p==="Unattached"?1:c.localeCompare(p)).map(([c,p])=>a`<section class="bundle-group"><div class="bundle-group-head"><strong>${c}</strong><span>${p.length} ${p.length===1?"bundle":"bundles"}</span></div><ul class="bundles">${p.map(u=>this._row(u))}</ul></section>`)}</div>`:a`<div class="empty"><strong>${e.length>0?"No matching bundles":"No bundles uploaded yet"}</strong><p>${e.length>0?"Try a different search or visibility filter.":"Drop source archives here or browse files above."}</p></div>`}</div>`}_row(e){let t=e.visible!==!1,i=e.repoFullName?`${e.repoFullName}${e.repoDirectory?`/${e.repoDirectory}`:""}`:"No repository assigned",r=Number.isFinite(e.uploadedAt)?new Date(e.uploadedAt).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"}):"";return a`<li class="${`bundle-row${t?"":" content-hidden"}`}"><span class="identity"><span class="bundle-icon" aria-hidden="true">${e.kind==="stasis"?a`<img src="./stasis.svg" width="16" height="16" alt="">`:Cr}</span> <button type="button" class="filename bundle-open" data-tooltip-truncated data-tooltip="${e.filename}" @click="${()=>this.dispatchEvent(new CustomEvent("managed-bundle-open",{detail:e,bubbles:!0,composed:!0}))}">${e.filename}</button> ${e.provenance==="build"?a`<span class="bundle-provenance" data-tooltip="Generated on this server from the repository">Built</span>`:d} </span><span class="meta bundle-uploader" data-tooltip-truncated data-tooltip="${e.uploadedByLogin?`@${e.uploadedByLogin}`:""}">${e.uploadedByLogin?`@${e.uploadedByLogin}`:""}</span> <span class="meta bundle-date">${r}</span> <span class="meta bundle-size">${M(e.byteSize)}</span> <span class="bundle-location" data-tooltip-truncated data-tooltip="${i}">${i}</span> <span class="${`status ${t?"visible":"hidden"}`}">${t?"Visible":"Hidden"}</span> <span class="actions">${e.canChangeRepo===!1?d:a`<button type="button" class="action" aria-label="${`Set location for ${e.filename}`}" data-tooltip="Set repository location" ?disabled="${this._locationBusy}" @click="${()=>this._openLocation(e)}">${y("repo")}</button>`} <button type="button" class="action" aria-label="${`${t?"Hide":"Make visible"} ${e.filename}`}" data-tooltip="${t?"Hide from teams":e.repoId==null?"Assign a repository before publishing":"Make visible to teams"}" ?disabled="${this._visibilityBusy!=null||e.canChangeRepo===!1||!t&&e.repoId==null}" @click="${()=>{this._setVisible(e,!t)}}">${y(t?"hide":"show")}</button> <a class="action" aria-label="${`Download ${e.filename}`}" href="${`/api/admin/bundles/${encodeURIComponent(e.id)}`}"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2v8m-3-3 3 3 3-3M3 11v3h10v-3"/></svg></a><button type="button" class="action danger" aria-label="${`Delete ${e.filename}`}" ?disabled="${e.canChangeRepo===!1}" data-tooltip="${e.canChangeRepo===!1?"Repository access is required to detach or delete this bundle":d}" @click="${()=>this._delete(e)}">${Ke}</button> </span>${this._locationBundle===e.id?this._locationEditor(e):d}</li>`}async _setVisible(e,t){if(!(this._visibilityBusy!=null||e.canChangeRepo===!1)){this._visibilityBusy=e.id,this._error=null;try{await this.appState.mutate(()=>Ht(e.id,t,this._csrf),["bundles","teams","history","scan-sources"]),e.visible=t,this.requestUpdate(),await this._load()}catch(i){this._error=`Couldn't change bundle visibility: ${String(i?.message??i)}`}finally{this._visibilityBusy=null}}}_openLocation(e){this._locationBusy||e.canChangeRepo===!1||(this._closeLocation(),this._locationBundle=e.id,this._locationRepo=e.repoId??null,this._locationDirectory=e.repoDirectory??"",this._error=null,e.kind==="stasis"&&this._loadLocationOrigin(e))}_closeLocation(){this._locationOriginRequest?.abort(),this._locationOriginRequest=null,this._locationBundle=null,this._locationOrigin=null,this._locationOriginError=null}async _loadLocationOrigin(e){this._locationOriginRequest?.abort();let t=new AbortController;this._locationOriginRequest=t,this._locationOrigin=void 0,this._locationOriginError=null;try{let i=await $t(e.id,{signal:t.signal});if(this._locationOriginRequest!==t)return;i?.repoId!=null&&this._data&&(this._data={...this._data,repos:[...(this._data.repos??[]).filter(r=>r.repoId!==i.repoId),{repoId:i.repoId,fullName:i.github}]}),this._locationOrigin=typeof i?.github=="string"&&i.github?{github:i.github,directory:typeof i.directory=="string"?i.directory:i.root===!0?"/":null}:null}catch(i){if(this._locationOriginRequest!==t)return;this._locationOrigin=null,i?.name!=="AbortError"&&(this._locationOriginError="Couldn't load the bundle\u2019s suggested location.")}}_locationEditor(e){let t=Array.isArray(this._data?.repos)?this._data.repos:[],i=this._locationOrigin,r=re({repo:i})?.toLowerCase(),o=r&&t.find(l=>l.fullName.toLowerCase()===r),n=(l,c,p)=>wi("metadata",l,c,p,this._locationBusy);return a`<div class="location-editor"><div class="location-field"><div class="location-field-header"><span class="ui-hint">Repository</span>${i?n(i.github,"repository",o?()=>{this._locationRepo=o.repoId,i.directory!=null&&(this._locationDirectory=i.directory)}:null):d}</div><repository-selector label="Repository for bundle" .options="${yi(t)}" .value="${this._locationRepo}" ?disabled="${this._locationBusy}" @repository-change="${l=>{this._locationRepo=l.detail.value}}"></repository-selector></div><div class="location-field"><div class="location-field-header"><label for="${`bundle-dir-${e.id}`}">Directory (optional)</label>${i?n(i.directory??"Directory not specified","directory",i.directory==null?null:()=>{this._locationDirectory=i.directory}):d}</div><input id="${`bundle-dir-${e.id}`}" type="text" placeholder="Repository root" .value="${this._locationDirectory}" ?disabled="${this._locationBusy}" @input="${l=>{this._locationDirectory=l.target.value}}"></div><div class="location-actions"><button type="button" class="action" ?disabled="${this._locationBusy}" @click="${()=>this._closeLocation()}">Cancel</button><button type="button" class="action" ?disabled="${this._locationBusy}" @click="${()=>{this._saveLocation(e)}}">Save</button></div>${this._locationOriginError?a`<p class="location-origin" role="status">${this._locationOriginError} <button type="button" class="origin-retry" @click="${()=>{this._loadLocationOrigin(e)}}">Retry</button></p>`:d}</div>`}async _saveLocation(e){if(!(this._locationBusy||e.canChangeRepo===!1)){this._locationBusy=!0,this._error=null;try{await this.appState.mutate(()=>jt(e.id,this._locationRepo,this._locationDirectory.trim(),this._csrf),["bundles","teams","repo-impact","history","scan-sources"]),this._closeLocation(),await this._load({preserveError:!0})}catch(t){this._error=`Couldn't set bundle location: ${String(t?.message??t)}`}finally{this._locationBusy=!1}}}async _upload(e){await Y(this,e,t=>Re(t,this._csrf),["bundles","bundle-metadata","reports","repo-impact","history","scan-sources"])}async _delete(e){if(globalThis.confirm?.(`Delete \u201C${e.filename}\u201D? Linked reports will keep their pending link.`)){this._error=null;try{await this.appState.mutate(()=>Ft(e.id,this._csrf),["bundles","bundle-metadata","reports","repo-impact","history","scan-sources"])}catch(t){this._error=`Delete failed: ${String(t?.message??t)}`}await this._load({preserveError:!0})}}};customElements.define("managed-admin-bundles",He);var je=class extends f{static properties={selection:{attribute:!1},_source:{state:!0},_error:{state:!0}};static styles=h(b);constructor(){super(),this._loadModels=(e,t)=>this.appState.load("models","scan models",i=>gt(i,m),{signal:e,apply:t}),this._source=null,this._error=null,this._loadReportSources=(e,t)=>this.appState.load("scan-sources","scan report inputs",async i=>{let[r,o]=await Promise.all([m("/api/admin/reports",{signal:i,credentials:"same-origin"}),m("/api/admin/scan-results",{signal:i,credentials:"same-origin"})]);if(!r.ok)throw new Error(`Reports: HTTP ${r.status}`);if(!o.ok&&o.status!==404)throw new Error(`Scan results: HTTP ${o.status}`);let n=await r.json();return n.reports=await Promise.all((n.reports??[]).filter(l=>l.visible&&l.repoId!=null).map(async l=>{let c=await m(`/api/admin/reports/${encodeURIComponent(l.id)}`,{signal:i,credentials:"same-origin"});if(!c.ok)throw new Error(`Report ${q(l.filename)}: HTTP ${c.status}`);return{...l,content:await c.text()}})),yt(n,o.ok?await o.json():{bundles:[],results:[]})},{signal:e,apply:t})}connectedCallback(){super.connectedCallback(),this._load()}async _load(){this._error=null,await this._loadCollection("bundles","bundles",xe,e=>{this._source=di(e)})}render(){return a`<div class="wrap">${w("manage-scans",this._role,this.allowShare)} ${this._error?a`<p class="msg error" role="alert">Couldn’t load scan sources: ${this._error} <button type="button" class="btn" @click="${()=>{this._load()}}">Retry</button></p>`:d}<deepview-scan-page hide-heading .selection="${this.selection}" .source="${this._source}" .sourceLoading="${this._loading&&this._source==null}" .loadBundle="${ci}" .loadModels="${this._loadModels}" .loadReportSources="${this._loadReportSources}" .createBundleHref="${e=>Number.isSafeInteger(e)&&e>0?j({view:"manage-bundles",createRepoId:e}):null}"></deepview-scan-page></div>`}};customElements.define("managed-admin-scans",je);var Ge=class extends f{static properties={_data:{state:!0},_error:{state:!0},_busy:{state:!0},_renamingId:{state:!0},_repoChoices:{state:!0},_memberChoices:{state:!0}};static styles=[h(Zt),h(b)];constructor(){super(),this._data=null,this._error=null,this._busy=!1,this._renamingId=null,this._repoChoices=new Map,this._memberChoices=new Map}connectedCallback(){super.connectedCallback(),this._load()}async _load({preserveError:e=!1}={}){e||(this._error=null),await this._loadCollection("teams","teams",V,t=>{this._data=t})}async _do(e){if(!this._busy){this._busy=!0,this._error=null;try{await this.appState.mutate(e,["teams","users","reports","history"])}catch(t){this._error=String(t?.message??t)}finally{this._busy=!1,await this._load({preserveError:!0})}}}render(){return a`<div class="wrap">${w("manage-teams",this._role,this.allowShare)}<h1 class="sr-only">Teams</h1><div class="page-intro"><p class="intro">Manage repository and finding access for each team.</p><span class="result-count">${this._data?.teams?.length??"\u2026"} teams</span></div><div class="create-team"><label class="sr-only" for="new-team-name">New team</label> <input id="new-team-name" class="new-name" type="text" placeholder="Team name" maxlength="100" ?disabled="${this._busy}" @keydown="${e=>{e.key==="Enter"&&this._create()}}"> <button class="btn primary" ?disabled="${this._busy}" @click="${()=>this._create()}">${ce} Create team</button><p class="access-note ui-hint">All members can view standard findings. Set dependencies and security access per member.</p></div><div class="manage-list" aria-busy="${this._loading}">${this._body()}</div></div>`}_body(){if(this._error!=null&&this._data==null)return a`<p class="msg error">Couldn't load teams: ${this._error}</p>`;if(this._data==null)return $("Loading teams\u2026");let e=Array.isArray(this._data.teams)?this._data.teams:[];return a`${this._error==null?d:a`<p class="msg error">${this._error}</p>`} ${e.length===0?a`<p class="msg">No teams yet. Create one above.</p>`:e.map(t=>this._team(t))}`}_team(e){return a`<section class="team" aria-label="${e.name}"><div class="team-head"><span class="team-icon" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="5" r="2.5"/><path d="M1.5 13v-1c0-2 1.8-3.5 4.5-3.5s4.5 1.5 4.5 3.5v1M11 2.5a2.5 2.5 0 0 1 0 5M12 9c1.7.5 2.5 1.5 2.5 3v1"/></svg></span>${this._renamingId===e.id?a`<input class="rename-input" type="text" .value="${e.name}" maxlength="100" aria-label="Team name" @keydown="${t=>this._renameKey(t,e)}"> <button class="btn" ?disabled="${this._busy}" @click="${t=>this._saveRename(e,t)}">Save</button> <button class="btn" @click="${()=>{this._renamingId=null}}">Cancel</button>`:a`<h2 class="team-name">${e.name}</h2>${e.hidden?a`<span class="muted ui-hint">Disabled</span>`:d} <span class="team-actions"><button class="icon-btn" aria-label="${`${e.hidden?"Restore":"Hide"} ${e.name}`}" ?disabled="${this._busy}" @click="${()=>this._do(()=>x("/api/admin/teams/set-hidden",this._csrf,{teamId:e.id,hidden:!e.hidden}))}">${y(e.hidden?"show":"hide")}</button> <button class="icon-btn" aria-label="${`Rename ${e.name}`}" ?disabled="${this._busy}" @click="${()=>{this._renamingId=e.id}}">${xr}</button> <button class="icon-btn danger" aria-label="${`Delete ${e.name}`}" ?disabled="${this._busy}" @click="${()=>this._deleteTeam(e)}">${Ke}</button></span>`}</div><div class="team-body"><div class="sub"><h3 class="sub-title">Repositories <span class="count">${e.repos.length}</span></h3>${e.repos.length===0?a`<p class="muted ui-hint">No repositories linked.</p>`:a`<ul class="links">${e.repos.map(t=>this._repoRow(e,t))}</ul>`} ${this._addRepoRow(e)}</div><div class="sub"><h3 class="sub-title">Members <span class="count">${e.members.length}</span></h3>${e.members.length===0?a`<p class="muted ui-hint">No members.</p>`:a`<ul class="links">${e.members.map(t=>this._memberRow(e,t))}</ul>`} ${this._addMemberRow(e)}</div>${this._npmScopes(e)}</div></section>`}_npmScopes(e){let t=Array.isArray(e.npmScopes)?e.npmScopes:[];return a`<div class="sub npm-scopes"><div class="sub-head"><h3 class="sub-title">npm scopes <span class="count">${t.length}</span><mode-switch class="npm-review-toggle" label="Enable dependency review" .checked="${!!e.dependencyReview}" .disabled="${this._busy}" @click="${()=>{this._busy||this._do(()=>x("/api/admin/teams/set-dependency-review",this._csrf,{teamId:e.id,enabled:!e.dependencyReview}))}}"></mode-switch></h3><p class="muted ui-hint">Members can open private packages in these scopes. Everyone can open public packages.</p></div><div class="add-row npm-scope-row"><input class="add-npm-scope" type="text" placeholder="@scope" aria-label="${`npm scope to add to ${e.name}`}" maxlength="215" ?disabled="${this._busy}" @keydown="${i=>{i.key==="Enter"&&this._addNpmScope(e,i)}}"> <button class="btn" aria-label="${`Add npm scope to ${e.name}`}" ?disabled="${this._busy}" @click="${i=>this._addNpmScope(e,i)}">${ce} Add</button> ${t.length===0?d:a`<ul class="npm-scope-list">${t.map(i=>a`<li class="npm-scope"><span>${i}</span> <button class="icon-btn danger" aria-label="${`Remove ${i} from ${e.name}`}" ?disabled="${this._busy}" @click="${()=>this._setNpmScopes(e,t.filter(r=>r!==i))}">${ze}</button></li>`)}</ul>`}</div></div>`}_addNpmScope(e,t){let i=t.target.closest(".add-row")?.querySelector(".add-npm-scope");if(!i?.value.trim())return;let r=nt(i.value);if(r===null){this._error=`\u201C${i.value.trim()}\u201D is not an npm scope. Scopes look like @example.`;return}let o=Array.isArray(e.npmScopes)?e.npmScopes:[];if(o.includes(r)){i.value="";return}this._setNpmScopes(e,[...o,r],()=>{i.value=""})}_setNpmScopes(e,t,i=()=>{}){return this._do(async()=>{await x("/api/admin/teams/set-npm-scopes",this._csrf,{teamId:e.id,scopes:t}),i()})}_repoRow(e,t){return a`<li class="repo-row">${_i} <span class="repo-copy"><span class="ln">${t.fullName}</span>${t.path?a`<span class="path">/${t.path.replace(/^\/+/u,"")}</span>`:d}</span> <button class="icon-btn danger" aria-label="${`Remove ${t.fullName}${t.path?`/${t.path}`:""} from ${e.name}`}" ?disabled="${this._busy}" @click="${()=>this._do(()=>x("/api/admin/teams/remove-repo",this._csrf,{teamId:e.id,repoId:t.repoId,path:t.path??null}))}">${ze}</button></li>`}_addRepoRow(e){let t=Array.isArray(this._data.repos)?this._data.repos:[];return t.length===0?a`<p class="muted ui-hint">No selected repositories to link — pick some on “Manage repositories”.</p>`:a`<div class="add-row"><repository-selector class="add-repo-sel" label="${`Repository to add to ${e.name}`}" placeholder="Add repository…" ?disabled="${this._busy}" .options="${t.map(i=>({value:i.repoId,label:i.fullName}))}" .value="${this._repoChoices.get(e.id)??null}" @repository-change="${i=>{this._repoChoices=new Map(this._repoChoices).set(e.id,i.detail.value)}}"></repository-selector><input class="add-repo-path" type="text" placeholder="Path (optional)" aria-label="${`Repository path in ${e.name} (optional)`}" maxlength="500" ?disabled="${this._busy}"> <button class="btn" aria-label="${`Add repository to ${e.name}`}" ?disabled="${this._busy||!this._repoChoices.has(e.id)}" @click="${i=>this._addRepo(e,i)}">${ce} Add</button></div>`}_memberRow(e,t){let i=Array.isArray(this._data.permissions)?this._data.permissions:[],r=this._data.users?.some(o=>o.id===t.userId&&se(o.role,"manage"));return a`<li class="member-row"><span class="member">${vi(t.userId,t.login)}<span class="ln">${t.login}</span></span> <span class="perms">${i.map(o=>a`<label class="perm"><input type="checkbox" .checked="${r||t[o]===!0}" ?disabled="${this._busy||r}" aria-label="${`${ge[o]??o} access for ${t.login} in ${e.name}`}" @change="${n=>this._togglePerm(e,t,o,n.target.checked)}"> ${ge[o]??o}</label>`)} </span><button class="icon-btn danger" aria-label="${`Remove ${t.login} from ${e.name}`}" ?disabled="${this._busy}" @click="${()=>this._do(()=>x("/api/admin/teams/remove-member",this._csrf,{teamId:e.id,userId:t.userId}))}">${ze}</button></li>`}_addMemberRow(e){let t=Array.isArray(this._data.users)?this._data.users:[],i=new Set(e.members.map(r=>r.userId));return a`<div class="add-row"><user-selector class="add-member-sel" label="${`Member to add to ${e.name}`}" placeholder="Add member…" ?disabled="${this._busy}" .users="${t.map(r=>({...r,disabled:i.has(r.id),detail:i.has(r.id)?"Member":""}))}" .value="${this._memberChoices.get(e.id)??null}" @user-change="${r=>{this._memberChoices=new Map(this._memberChoices).set(e.id,r.detail.value)}}"></user-selector><button class="btn" aria-label="${`Add member to ${e.name}`}" ?disabled="${this._busy||!this._memberChoices.has(e.id)||i.has(this._memberChoices.get(e.id))}" @click="${()=>this._addMember(e)}">${ce} Add</button></div>`}_create(){let e=this.renderRoot.querySelector(".new-name"),t=e?.value.trim();t&&this._do(async()=>{await x("/api/admin/teams",this._csrf,{name:t}),e&&(e.value="")})}_deleteTeam(e){globalThis.confirm?.(`Delete team \u201C${e.name}\u201D? Its repo + member links are removed.`)&&this._do(()=>x("/api/admin/teams/delete",this._csrf,{teamId:e.id}))}_renameKey(e,t){e.key==="Enter"?this._saveRename(t,e):e.key==="Escape"&&(this._renamingId=null)}_saveRename(e,t){if(this._busy)return;let i=t.target.closest(".team-head")?.querySelector(".rename-input")?.value.trim()??"";this._renamingId=null,!(i===""||i===e.name)&&this._do(()=>x("/api/admin/teams/rename",this._csrf,{teamId:e.id,name:i}))}_addRepo(e,t){let i=t.target.closest(".add-row"),r=this._repoChoices.get(e.id);if(!Number.isSafeInteger(r)||r<=0)return;let o=i?.querySelector(".add-repo-path")?.value??"";this._do(async()=>{await x("/api/admin/teams/set-repo",this._csrf,{teamId:e.id,repoId:r,path:o});let n=new Map(this._repoChoices);n.delete(e.id),this._repoChoices=n;let l=i?.querySelector(".add-repo-path");l&&(l.value="")})}_addMember(e){let t=this._memberChoices.get(e.id);!t||e.members.some(i=>i.userId===t)||this._do(async()=>{await x("/api/admin/teams/set-member",this._csrf,{teamId:e.id,userId:t});let i=new Map(this._memberChoices);i.delete(e.id),this._memberChoices=i})}_togglePerm(e,t,i,r){if(this._data.users?.some(n=>n.id===t.userId&&se(n.role,"manage")))return;let o={};for(let n of this._data.permissions??[])o[n]=t[n]===!0;o[i]=r,this._do(()=>x("/api/admin/teams/set-member",this._csrf,{teamId:e.id,userId:t.userId,...o}))}};customElements.define("managed-admin-teams",Ge);var F=`.link-team{margin-top:1.5rem}.link-team h2{margin:0 0 .5rem;font-size:1rem;font-weight:600}.link-table{overflow-x:auto}table{width:100%;border-collapse:collapse;text-align:left;font-size:.8rem}th{color:var(--muted);font-weight:500}th,td{padding:.6rem .65rem;border-bottom:1px solid var(--border);white-space:nowrap}th:first-child,td:first-child{padding-left:0}th:last-child,td:last-child{padding-right:0;text-align:right}
17
17
  `;async function Ar(s){let e=await m("/api/admin/links",{signal:s,credentials:"same-origin"});if(!e.ok)throw new Error(`HTTP ${e.status}`);let t=await e.json();if(!Array.isArray(t?.shares))throw new Error("No public links returned");return t}var Ve=class extends f{static properties={_data:{state:!0},_error:{state:!0}};static styles=[h(b),h(F)];constructor(){super(),this._data=null,this._error=null}connectedCallback(){super.connectedCallback(),this._load()}async _load(){this._error=null,await this._loadCollection("links","public links",Ar,e=>{this._data=e})}async _edit(e){let{openManagedShareDialog:t}=await import("./chunk-ZMQQCGF6.js");await t({id:e.teamId,name:e.teamName},e.id,this.session),this.isConnected&&await this._load()}_groups(){let e=new Map;for(let t of this._data?.shares??[])e.has(t.teamId)||e.set(t.teamId,{name:t.teamName,links:[]}),e.get(t.teamId).links.push(t);return[...e.values()]}_body(){return this._data==null?this._error?a`<p class="msg error">Couldn't load public links: ${this._error}</p>`:$("Loading public links\u2026"):this._data.shares.length===0?a`<p class="msg">No public links. Create one with the share button beside a workspace in the sidebar.</p>`:this._groups().map(e=>a`<section class="link-team" aria-label="${e.name}"><h2>${e.name}</h2><div class="link-table"><table><thead><tr><th>Link</th><th>Created by</th><th>Created</th><th>Security</th><th>Dependencies</th><th><span class="sr-only">Actions</span></th></tr></thead><tbody>${e.links.map(t=>a`<tr><td><span data-tooltip="${t.id}">${t.id.slice(0,8)}</span></td><td>${t.createdBy}</td><td>${new Date(t.createdAt).toLocaleString()}</td><td>${t.permissions.security?"On":"Off"}</td><td>${t.permissions.dependencies?"On":"Off"}</td><td><button class="btn" aria-label="${`Edit public link ${t.id.slice(0,8)} for ${e.name}`}" @click="${()=>this._edit(t)}">Edit</button></td></tr>`)}</tbody></table></div></section>`)}render(){return a`<div class="wrap">${w("manage-links",this._role,this.allowShare)}<h1 class="sr-only">Public links</h1><div class="page-intro"><p class="intro">Manage public links and the findings each link can show.</p><span class="result-count">${this._data?.shares.length??"\u2026"} links</span></div>${this._loading&&this._data?a`<span class="sr-only" role="status">Refreshing public links…</span>`:d}<div class="manage-list" aria-busy="${this._loading}">${this._body()}</div></div>`}};customElements.define("managed-admin-links",Ve);var Tr="/api/admin/deduplication";async function We(s="",e={}){let t=await m(`${Tr}${s}`,{credentials:"same-origin",...e}),i=await t.json();if(!t.ok){let r={"storage-encryption-required":"Configure MANAGED_STORAGE_ENCRYPTION_KEY on the server to import encrypted link reports.","invalid-link-report":'Choose a link report containing arrays of finding IDs, such as [["id1", "id2"]].',"too-large":"This link report exceeds the server upload limit."};throw new Error(r[i?.error]??`HTTP ${t.status}`)}return i}var Je=class extends f{static properties={_data:{state:!0},_error:{state:!0},_busy:{state:!0},_dragOver:{state:!0}};static styles=[h(b),h(de),h(F)];constructor(){super(),this._data=null,this._error=null,this._busy=!1,this._dragOver=!1,this._queue=[]}connectedCallback(){super.connectedCallback(),this._load(),this._teardownDrop=J(this,e=>{this._upload(e)},e=>{this._dragOver=e})}disconnectedCallback(){this._teardownDrop?.(),super.disconnectedCallback()}async _load({preserveError:e=!1}={}){e||(this._error=null),await this._loadCollection("deduplication","deduplication reports",t=>We("",{signal:t}),t=>{this._data=t})}_upload(e){if(!(!this._csrf||this._role!=="admin"))return Y(this,e,t=>We("",{method:"POST",headers:{"content-type":"application/json","x-report-filename":encodeURIComponent(t.name),"x-csrf-token":this._csrf},body:t}),["deduplication","reports","teams"])}async _toggle(e){if(!(this._busy||!this._csrf)){this._busy=!0,this._error=null;try{await this.appState.mutate(()=>We(`/${encodeURIComponent(e.id)}`,{method:"PATCH",headers:{"content-type":"application/json","x-csrf-token":this._csrf},body:JSON.stringify({enabled:!e.enabled})}),["deduplication","reports","teams"])}catch(t){this._error=String(t?.message??t)}finally{this._busy=!1,await this._load({preserveError:!0});let t=this._queue.splice(0);t.length>0&&this._upload(t)}}}_view(e){document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:"manage-deduplication",linkId:e.id},bubbles:!0,composed:!0}))}_linkReports(){document.dispatchEvent(new CustomEvent("managed-admin-navigate",{detail:{view:"manage-scans",scanMode:"link"},bubbles:!0,composed:!0}))}render(){let e=this._data?.reports??[];return a`${this._dragOver?a`<div class="dropzone">Drop link reports to import</div>`:d}<div class="wrap">${w("manage-deduplication",this._role,this.allowShare)}<h1 class="sr-only">Deduplication</h1><div class="page-intro"><p class="intro">Link duplicate findings across all teams.</p><span class="result-count">${this._data?e.length:"\u2026"} reports</span><button type="button" class="btn" @click="${()=>this._linkReports()}">Link reports</button></div><div class="drop-card"><span class="drop-icon" aria-hidden="true">${y("upload")}</span><span class="drop-copy"><strong>Import link reports</strong><span>Drop *.link.json files anywhere on this page.</span></span><button type="button" class="drop-browse" ?disabled="${this._busy||!this._csrf}" @click="${()=>W(t=>{this._upload(t)})}">${this._busy?"Saving\u2026":"Browse files"}</button></div>${this._error?a`<p class="msg error" role="alert">${this._error}</p>`:d}<div class="manage-list" aria-busy="${this._loading}">${this._data===null?this._error?d:$("Loading deduplication reports\u2026"):e.length===0?a`<div class="empty"><strong>No link reports imported yet</strong><p>Each row in a link report lists IDs for the same finding.</p></div>`:a`<div class="link-table"><table><thead><tr><th>Report</th><th>Groups</th><th>Finding IDs</th><th>Imported by</th><th>Imported</th><th>Status</th><th><span class="sr-only">Actions</span></th></tr></thead><tbody>${e.map(t=>a`<tr><td>${t.filename}</td><td>${t.groupCount}</td><td>${t.findingCount}</td><td>${t.uploadedByLogin??"Removed user"}</td><td>${new Date(t.uploadedAt).toLocaleDateString()}</td><td>${t.enabled?"Enabled":"Disabled"}</td><td><button type="button" class="btn" aria-label="${`View ${t.filename}`}" @click="${()=>this._view(t)}">View</button> <button type="button" class="btn" ?disabled="${this._busy}" aria-label="${`${t.enabled?"Disable":"Enable"} ${t.filename}`}" @click="${()=>{this._toggle(t)}}">${t.enabled?"Disable":"Enable"}</button></td></tr>`)}</tbody></table></div>`}</div></div>`}};customElements.define("managed-admin-deduplication",Je);var Ir=[[75,"critical"],[50,"high"],[25,"moderate"],[0,"low"]],$i=s=>Ir.find(([e])=>s>=e)[1];var ki=`.risk-progress{margin:0 0 1rem;color:var(--muted);font-size:.8rem}.risk-section{margin-top:1rem}.risk-section:first-child{margin-top:0}.risk-section h2{margin:0 0 .5rem;font-size:1rem;font-weight:600}.risk-results-head{position:sticky;top:0;z-index:1;padding-bottom:.25rem;background:var(--bg)}.risk-results-head .collection-toolbar{margin-bottom:0}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}.risk-name,.risk-name:visited{color:var(--text)}.risk-name:hover{color:var(--accent);text-decoration:none}.risk-repos{margin:0;padding:0;list-style:none;font-size:.8rem}.risk-repos>li{display:grid;grid-template-columns:minmax(8rem,16rem) 1fr;gap:.75rem;align-items:baseline;padding:.3rem 0;border-bottom:1px solid var(--border)}.risk-repo-name{overflow:hidden;font-weight:500;text-overflow:ellipsis;white-space:nowrap}.risk-lockfiles{display:flex;flex-wrap:wrap;gap:.15rem 1.5rem;margin:0;padding:0;list-style:none}.risk-lockfiles li{white-space:nowrap}.risk-lockfile-meta{margin-left:.5rem;color:var(--muted)}@container (max-width: 40rem){.risk-repos>li{grid-template-columns:1fr;gap:0}}th:last-child,td:last-child{text-align:left}td{vertical-align:top}.risk-findings-column{width:100%}.risk-clip{display:block;max-width:18rem;overflow:hidden;text-overflow:ellipsis}.risk-used-in{max-width:14rem}.risk-pending,.risk-none{color:var(--muted)}.risk-error{color:var(--critical)}.risk-chip{display:inline-block;padding:0 .45rem;border-radius:999px;font-weight:600;white-space:nowrap;color:var(--sev);background:color-mix(in srgb,var(--sev) 14%,transparent)}.risk-score{display:inline-block;min-width:2.25rem;padding:0 .4rem;border-radius:999px;font-weight:600;font-variant-numeric:tabular-nums;text-align:center;color:var(--sev);background:color-mix(in srgb,var(--sev) 14%,transparent)}.risk-score.risk-low{color:var(--muted);background:none;font-weight:400}.risk-findings{display:flex;flex-wrap:wrap;gap:.25rem .75rem;margin:0;padding:0;list-style:none}.risk-findings li{color:var(--sev);white-space:nowrap}.risk-critical{--sev: var(--critical)}.risk-high{--sev: var(--high)}.risk-moderate{--sev: var(--yellow)}.risk-low,.risk-notice{--sev: var(--muted)}.risk-scope-bar{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;margin:0 0 1rem}.risk-scope,.risk-tabs{display:flex;gap:2px;padding:3px;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:var(--bg)}.risk-scope button,.risk-tabs button{padding:.3rem .65rem;border:0;border-radius:var(--ui-radius, 3px);background:transparent;color:var(--muted);font-size:.75rem}.risk-scope button:hover:not(:disabled),.risk-tabs button:hover{background:var(--surface-active);color:var(--text)}.risk-scope button[aria-pressed=true],.risk-tabs button[aria-selected=true]{background:color-mix(in srgb,var(--accent) 15%,var(--bg));color:var(--accent)}.risk-scope button:focus-visible,.risk-tabs button:focus-visible{outline-offset:-1px}.risk-scope-bar select{min-width:14rem}.risk-scope-bar repository-selector{flex:0 1 20rem;min-width:14rem}.risk-tabs{width:fit-content;margin-bottom:.5rem}
18
- `;var Lr={malware:"Malware",deprecated:"Deprecated",publisher:"Publisher changed","provenance-lost":"Provenance lost","too-new":"Too new",popularity:"Unpopular","held-install-scripts":"Install scripts","held-runs-code":"Runs code","held-exfiltration":"Exfiltration host","held-remote":"Remote host","held-ip-lookup":"IP lookup","held-obfuscated":"Obfuscated","held-non-registry-deps":"Non-registry dependencies","held-unused-deps":"Unused deps","large-code":"Large new code","now-unreadable":"Now unreadable","binary-code":"Binary code","new-hosts":"New hosts"},Or={"no-repository":"No GitHub access","github-not-found":"Not found on GitHub","github-unauthorized":"GitHub sign-in expired","github-rate-limited":"GitHub rate limit reached","github-unreachable":"GitHub unreachable","github-malformed":"GitHub answered unexpectedly","unsupported-format":"Format not read","lockfile-unreadable":"Unreadable","package-not-found":"Not on npm","version-not-found":"Version not on npm","integrity-mismatch":"Differs from npm\u2019s","npm-busy":"Server busy","package-too-large":"Too large to assess","upstream-busy":"npm busy","upstream-unavailable":"npm unavailable","upstream-too-large":"npm answered too much","upstream-invalid":"npm answered unexpectedly"},Ye=s=>Or[s]??s,Pr={401:"Sign in again to scan repositories.",403:"Only admins can scan repositories.",404:"That team or repository is no longer connected. Choose again."},zr=[["all","All repositories"],["team","Team"],["repo","Repository"]],Dr={registry:"npm",mirror:"Mirror",git:"Git",tarball:"Tarball",file:"File"},D=s=>s==="registry"||s==="mirror",pe=s=>s.risk?.error==="integrity-mismatch"?null:s.malware??null,Ei=s=>pe(s)?"critical":s.risk?.severity??null,Ai=s=>pe(s)?100:s.risk&&!s.risk.error?s.risk.score??null:null,Ze=s=>{let e=s.risk?.findings??[];return pe(s)?[{id:"malware",severity:"critical"},...e.filter(({id:t})=>t!=="malware")]:e},Br=s=>[...pe(s)??[],...s.risk?.findings?.some(({id:e,takenDown:t})=>e==="malware"&&t)?["Taken down by npm"]:[]],xi=s=>oe.includes(s)?oe.indexOf(s):oe.length,qr=s=>`${s[0].toUpperCase()}${s.slice(1)}`,Ri={all:["All packages",()=>!0],flagged:["Flagged",s=>!!Ei(s)],failed:["Not assessed: failed",s=>!!s.risk?.error],elsewhere:["Not assessed: not from npm",s=>!D(s.source)]},Mr="Out of 100: what its findings call for, lowered by how long the version has been out among how many download its package. Malware is 100.",H=1e3,Ur=250,k=new Intl.NumberFormat;function Si(s){let e=[...s];return e.length===1?a`<span class="risk-clip risk-used-in" data-tooltip-truncated data-tooltip="${e[0]}">${e[0]}</span>`:a`<span class="risk-clip risk-used-in" data-tooltip="${e.join(`
18
+ `;var Lr={malware:"Malware",deprecated:"Deprecated",publisher:"Publisher changed","provenance-lost":"Provenance lost","too-new":"Too new",popularity:"Unpopular","held-install-scripts":"Install scripts","held-runs-code":"Runs code","held-credentials":"Credentials","held-exfiltration":"Exfiltration host","held-remote":"Remote host","held-ip-lookup":"IP lookup","held-obfuscated":"Obfuscated","held-non-registry-deps":"Non-registry dependencies","held-unused-deps":"Unused deps","large-code":"Large new code","now-unreadable":"Now unreadable","binary-code":"Binary code","new-hosts":"New hosts"},Or={"no-repository":"No GitHub access","github-not-found":"Not found on GitHub","github-unauthorized":"GitHub sign-in expired","github-rate-limited":"GitHub rate limit reached","github-unreachable":"GitHub unreachable","github-malformed":"GitHub answered unexpectedly","unsupported-format":"Format not read","lockfile-unreadable":"Unreadable","package-not-found":"Not on npm","version-not-found":"Version not on npm","integrity-mismatch":"Differs from npm\u2019s","npm-busy":"Server busy","package-too-large":"Too large to assess","upstream-busy":"npm busy","upstream-unavailable":"npm unavailable","upstream-too-large":"npm answered too much","upstream-invalid":"npm answered unexpectedly"},Ye=s=>Or[s]??s,Pr={401:"Sign in again to scan repositories.",403:"Only admins can scan repositories.",404:"That team or repository is no longer connected. Choose again."},zr=[["all","All repositories"],["team","Team"],["repo","Repository"]],Dr={registry:"npm",mirror:"Mirror",git:"Git",tarball:"Tarball",file:"File"},D=s=>s==="registry"||s==="mirror",pe=s=>s.risk?.error==="integrity-mismatch"?null:s.malware??null,Ei=s=>pe(s)?"critical":s.risk?.severity??null,Ai=s=>pe(s)?100:s.risk&&!s.risk.error?s.risk.score??null:null,Ze=s=>{let e=s.risk?.findings??[];return pe(s)?[{id:"malware",severity:"critical"},...e.filter(({id:t})=>t!=="malware")]:e},Br=s=>[...pe(s)??[],...s.risk?.findings?.some(({id:e,takenDown:t})=>e==="malware"&&t)?["Taken down by npm"]:[]],xi=s=>oe.includes(s)?oe.indexOf(s):oe.length,qr=s=>`${s[0].toUpperCase()}${s.slice(1)}`,Ri={all:["All packages",()=>!0],flagged:["Flagged",s=>!!Ei(s)],failed:["Not assessed: failed",s=>!!s.risk?.error],elsewhere:["Not assessed: not from npm",s=>!D(s.source)]},Mr="Out of 100: what its findings call for, lowered by how long the version has been out among how many download its package. Malware is 100.",H=1e3,Ur=250,k=new Intl.NumberFormat;function Si(s){let e=[...s];return e.length===1?a`<span class="risk-clip risk-used-in" data-tooltip-truncated data-tooltip="${e[0]}">${e[0]}</span>`:a`<span class="risk-clip risk-used-in" data-tooltip="${e.join(`
19
19
  `)}">${k.format(e.length)} lockfiles</span>`}var Fr=(s,e,t)=>O(`/api/admin/repositories/risk${s.size>0?`?${s}`:""}`,Pr,async i=>{for await(let r of ye(i))t(r)},e),Ti=({name:s,version:e,integrity:t})=>`${s}@${e}\0${t??""}`,Hr=s=>D(s.source)?Ti(s):`${s.source}\0${s.name}@${s.version??""}\0${s.from??""}`;function Ci(s){let e=Ai(s);return e>0?-e:D(s.source)?e===0?3:s.risk?2:1:4}var Xe=class extends f{static properties={_state:{state:!0},_error:{state:!0},_filter:{state:!0},_query:{state:!0},_tab:{state:!0},_scope:{state:!0},_target:{state:!0},_choices:{state:!0}};static styles=[h(b),h(F),h(ki)];constructor(){super(),this._state="idle",this._error=null,this._filter="all",this._query="",this._tab="packages",this._scan=null,this._drawTimer=null,this._scope="all",this._target="",this._choices=null,this._reset()}connectedCallback(){super.connectedCallback(),this._loadCollection("teams","teams",V,e=>{this._choices=e})}_choose(e,t=""){this._state!=="running"&&(this._scope=e,this._target=t,this._reset(),this._error=null,this._state="idle")}_reset(){this._repos=new Map,this._packages=new Map,this._advisories=new Map}disconnectedCallback(){this.stop(),super.disconnectedCallback()}async start(){if(this._state==="running"||this._scope!=="all"&&!this._target)return;let e=new URLSearchParams(this._scope==="all"?{}:{[this._scope]:this._target});this._reset(),this._error=null,this._state="running";let t=new AbortController;this._scan=t;let i=!1;try{if(await Fr(e,t.signal,r=>{r.type==="done"&&(i=!0),this._apply(r),this._draw()}),!i)throw new Error("The scan ended before it finished. Try again.");this._state="done"}catch(r){if(t.signal.aborted||r?.name==="AbortError")return;this._error=r?.message??String(r),this._state="stopped"}finally{this._scan===t&&(this._scan=null,this._state==="running"&&(this._state="stopped")),this._draw(!0)}}stop(){this._scan&&(this._scan.abort(),this._scan=null,this._state="stopped",this._draw(!0))}_draw(e=!1){if(e){clearTimeout(this._drawTimer),this._drawTimer=null,this.requestUpdate();return}this._drawTimer??=setTimeout(()=>{this._drawTimer=null,this.requestUpdate()},Ur)}_apply(e){if(e.type==="repos")for(let{id:t,fullName:i}of e.repos)this._repos.set(t,{id:t,fullName:i,commit:null,lockfiles:null,error:null,detail:null});else if(e.type==="repo"){let t=this._repos.get(e.id);if(!t)return;if(e.error){Object.assign(t,{error:e.error,detail:e.detail??null});return}t.commit=e.commit,t.lockfiles=new Map(e.lockfiles.map(i=>[i.path,{...i,packages:null,error:null}]))}else if(e.type==="lockfile"){let t=this._repos.get(e.repo),i=t?.lockfiles?.get(e.path);if(!i)return;if(e.error){Object.assign(i,{error:e.error,detail:e.detail??null});return}i.packages=e.packages.length;let r=`${t.fullName}/${e.path}`;for(let o of e.packages){let n=Hr(o),l=this._packages.get(n);l||this._packages.set(n,l={...o,usedIn:new Set,risk:null}),l.usedIn.add(r)}}else if(e.type==="risk"){let t=this._packages.get(Ti(e));t&&(t.risk=e.error?{error:e.error,detail:e.detail??null}:{severity:e.severity,score:e.score??null,findings:e.findings,published:e.published})}else e.type==="advisories"&&this._applyAdvisories(e)}_applyAdvisories(e){let t=this._repos.get(e.repo),i=t?.lockfiles?.get(e.path);if(!i)return;if(e.error){i.advisoriesError=e.error;return}let r=`${t.fullName}/${e.path}`;for(let{range:o,versions:n,...l}of e.advisories){let c=`${l.id}\0${l.name}`,p=this._advisories.get(c);p||this._advisories.set(c,p={...l,ranges:new Set,versions:new Set,usedIn:new Set}),o&&p.ranges.add(o);for(let u of n)p.versions.add(u);if(p.malware||=l.malware,p.usedIn.add(r),!!p.malware)for(let u of this._packages.values())D(u.source)&&u.name===p.name&&p.versions.has(u.version)&&(u.malware??=new Set).add(p.id)}}_counts(){let e=[...this._repos.values()],t=e.flatMap(r=>[...r.lockfiles?.values()??[]]),i=[...this._packages.values()].filter(r=>D(r.source));return{repos:e.length,reposRead:e.filter(r=>r.lockfiles||r.error).length,lockfiles:t.length,lockfilesRead:t.filter(r=>r.packages!==null||r.error).length,assessable:i.length,told:i.filter(r=>r.risk).length,flagged:i.filter(Ei).length,advisories:this._advisories.size,malware:[...this._advisories.values()].filter(r=>r.malware).length}}_rows(){let e=this._query.trim().toLowerCase(),[,t]=Ri[this._filter];return[...this._packages.values()].filter(i=>t(i)&&(!e||i.name.toLowerCase().includes(e))).toSorted((i,r)=>Ci(i)-Ci(r)||Ze(r).length-Ze(i).length||i.name.localeCompare(r.name)||(i.version??"").localeCompare(r.version??""))}_advisoryRows(){let e=this._query.trim().toLowerCase();return[...this._advisories.values()].filter(t=>!e||t.name.toLowerCase().includes(e)).toSorted((t,i)=>Number(i.malware)-Number(t.malware)||xi(t.severity)-xi(i.severity)||t.name.localeCompare(i.name)||t.id.localeCompare(i.id))}render(){return a`<div class="wrap">${w("manage-repos",this._role,this.allowShare)}<h1 class="sr-only">Repository risk</h1><div class="page-intro"><p class="intro">Each connected repository’s npm lockfiles at its default branch, and what risk finds in each package they lock.</p>${this._state==="running"?a`<button type="button" class="btn" @click="${()=>this.stop()}">Stop</button>`:a`<button type="button" class="btn primary" ?disabled="${this._scope!=="all"&&!this._target}" @click="${()=>{this.start()}}">${this._state==="idle"?"Start scan":"Scan again"}</button>`}</div>${this._scopeBar()}<p class="risk-progress" role="status">${this._progress()}</p>${this._error?a`<p class="msg error" role="alert">${this._error}</p>`:d} ${this._repos.size===0?d:a`<div class="manage-list">${this._lockfiles()} ${this._packages.size===0?d:this._tab==="advisories"?this._advisoryTable():this._packageTable()}</div>`}</div>`}_tabs(){let e=[["packages","Packages"],["advisories",`Advisories${this._advisories.size>0?` (${k.format(this._advisories.size)})`:""}`]];return a`<div class="risk-tabs" role="tablist" aria-label="Results">${e.map(([t,i])=>a`<button type="button" role="tab" aria-selected="${this._tab===t}" @click="${()=>{this._tab=t}}">${i}</button>`)}</div>`}_search(e,t,i){return a`<input type="search" aria-label="${`Search ${i}`}" placeholder="Search by package…" .value="${this._query}" @input="${r=>{this._query=r.target.value}}"> <span class="result-count">${e===t?"":`${k.format(e)} of `}${k.format(t)} ${i}</span>`}_scopeBar(){let e=this._state==="running",t=this._choices?"":"Loading\u2026";return a`<div class="risk-scope-bar"><div class="risk-scope" role="group" aria-label="What to scan">${zr.map(([i,r])=>a`<button type="button" aria-pressed="${this._scope===i}" ?disabled="${e}" @click="${()=>this._choose(i)}">${r}</button>`)}</div>${this._scope==="team"?a`<select aria-label="Which team" ?disabled="${e}" @change="${i=>this._choose("team",i.target.value)}"><option value="" ?selected="${!this._target}">${t||"Choose a team\u2026"}</option>${(this._choices?.teams??[]).map(i=>a`<option value="${i.id}" ?selected="${i.id===this._target}">${i.hidden?`${i.name} (hidden)`:i.name}</option>`)}</select>`:d} ${this._scope==="repo"?a`<repository-selector label="Which repository" placeholder="${t||"Choose a repository\u2026"}" ?disabled="${e||!this._choices}" .options="${(this._choices?.repos??[]).map(i=>({value:String(i.repoId),label:i.fullName}))}" .value="${this._target||null}" @repository-change="${i=>this._choose("repo",i.detail.value)}"></repository-selector>`:d}</div>`}_progress(){if(this._state==="idle")return"Not scanned yet. A scan reads each repository from GitHub and assesses each npm package it locks, which can take a while; a scan again within the hour is quicker.";let e=this._counts(),t=(r,o,n)=>`${k.format(r)} of ${k.format(o)} ${n}`;return[{running:"Scanning",stopped:"Stopped",done:"Done"}[this._state],t(e.reposRead,e.repos,"repositories read"),t(e.lockfilesRead,e.lockfiles,"lockfiles"),t(e.told,e.assessable,"packages assessed"),`${k.format(e.flagged)} flagged`,...e.advisories>0?[`${k.format(e.advisories)} advisories${e.malware>0?`, ${k.format(e.malware)} of malware`:""}`]:[]].join(" \xB7 ")}_pending(e,t){return a`<span class="risk-pending">${this._state==="running"?e:t}</span>`}_repoStatus(e){return e.error?a`<span class="risk-error" data-tooltip="${e.detail??d}">${Ye(e.error)}</span>`:e.lockfiles===null?this._pending("Waiting\u2026","Not read"):e.lockfiles.size===0?a`<span class="risk-none">No npm lockfiles</span>`:null}_lockfileStatus(e){return e.error?a`<span class="risk-error" data-tooltip="${e.detail??d}">${Ye(e.error)}</span>`:e.packages===null?this._pending("Reading\u2026","Not read"):`${k.format(e.packages)} packages`}_lockfiles(){return a`<section class="risk-section" aria-label="Lockfiles"><h2>Lockfiles</h2><ul class="risk-repos">${[...this._repos.values()].map(e=>a`<li><span class="risk-repo-name" data-tooltip-truncated data-tooltip="${e.fullName}">${e.fullName}</span> ${this._repoStatus(e)??a`<ul class="risk-lockfiles">${[...e.lockfiles.values()].map(t=>a`<li><span class="mono">${t.path}</span><span class="risk-lockfile-meta">${t.format} · ${this._lockfileStatus(t)}</span></li>`)}</ul>`}</li>`)}</ul></section>`}_resultsHead(e){return a`<div class="risk-results-head">${this._tabs()}<div class="collection-toolbar">${e}</div></div>`}_packageTable(){let e=this._rows();return a`<section class="risk-section" aria-label="Packages">${this._resultsHead(a`<select aria-label="Show packages" .value="${this._filter}" @change="${t=>{this._filter=t.target.value}}">${Object.entries(Ri).map(([t,[i]])=>a`<option value="${t}">${i}</option>`)}</select> ${this._search(e.length,this._packages.size,"packages")}`)}<div class="link-table"><table><thead><tr><th>Package</th><th>Version</th><th><span data-tooltip="${Mr}">Risk</span></th><th class="risk-findings-column">Findings</th><th>Source</th><th>Used in</th></tr></thead><tbody>${e.slice(0,H).map(t=>this._packageRow(t))}</tbody></table></div>${e.length>H?a`<p class="msg">Showing the first ${k.format(H)}. Search or filter to see others.</p>`:d}</section>`}_packageRow(e){let t=D(e.source)?j({view:"npm",packageName:e.name,packageSpec:e.version}):null;return a`<tr><td>${t?a`<a class="risk-name" href="${t}">${e.name}</a>`:e.name}</td><td class="mono">${e.version??"\u2014"}</td><td>${this._score(e)}</td><td>${this._findings(e)}</td><td><span data-tooltip="${e.from??d}">${Dr[e.source]??e.source}${e.source==="mirror"&&e.from?` (${new URL(e.from).host})`:""}</span></td><td>${Si(e.usedIn)}</td></tr>`}_score(e){let t=Ai(e);return t===null?d:a`<span class="${`risk-score risk-${$i(t)}`}">${t}</span>`}_findings(e){let t=Ze(e);return t.length>0?a`<ul class="risk-findings">${t.map(i=>a`<li class="${`risk-${i.severity}`}" data-tooltip="${i.id==="malware"?Br(e).join(`
20
20
  `):d}">${Lr[i.id]??i.id}</li>`)}</ul>`:D(e.source)?e.risk?e.risk.error?a`<span class="risk-error" data-tooltip="${e.risk.detail??d}">${Ye(e.risk.error)}</span>`:a`<span class="risk-none">None found</span>`:this._pending("Waiting\u2026","Not assessed"):a`<span class="risk-none">Not assessed</span>`}_advisoryTable(){let e=this._advisoryRows(),t=[...this._repos.values()].flatMap(i=>[...i.lockfiles?.values()??[]].filter(r=>r.advisoriesError).map(r=>`${i.fullName}/${r.path}`));return a`<section class="risk-section" aria-label="Advisories">${this._resultsHead(this._search(e.length,this._advisories.size,"advisories"))} ${t.length>0?a`<p class="msg error">npm’s advisories couldn’t be had for <span data-tooltip="${t.join(`
21
21
  `)}">${t.length===1?t[0]:`${k.format(t.length)} lockfiles`}</span>.</p>`:d} ${e.length===0?a`<p class="msg">${this._state==="running"?"None yet.":"npm has no advisories of these packages."}</p>`:a`<div class="link-table"><table><thead><tr><th>Package</th><th>Versions</th><th>Severity</th><th>Advisory</th><th>Title</th><th>Affected</th><th>Used in</th></tr></thead><tbody>${e.slice(0,H).map(i=>this._advisoryRow(i))}</tbody></table></div>`} ${e.length>H?a`<p class="msg">Showing the first ${k.format(H)}. Search to see others.</p>`:d}</section>`}_advisoryRow(e){let t=[...e.versions],i=e.malware?"critical":e.severity;return a`<tr><td><a class="risk-name" href="${j({view:"npm",packageName:e.name,packageSpec:t[0]})}">${e.name}</a></td><td class="mono">${t.join(", ")}</td><td>${e.malware?a`<span class="risk-chip risk-critical">Malware</span>`:i?a`<span class="${`risk-chip risk-${i}`}">${qr(i)}</span>`:d}</td><td class="mono">${at(e.url)?a`<a href="${e.url}" target="_blank" rel="noopener noreferrer">${e.id}</a>`:e.id}</td><td><span class="risk-clip" data-tooltip-truncated data-tooltip="${e.title??d}">${e.title??""}</span></td><td class="mono">${[...e.ranges].join(" || ")}</td><td>${Si(e.usedIn)}</td></tr>`}};customElements.define("managed-admin-repo-risk",Xe);function jr(s,e){return new Promise(t=>{if(e.aborted){t();return}let i=()=>{clearTimeout(r),e.removeEventListener("abort",i),t()},r=setTimeout(i,s);e.addEventListener("abort",i,{once:!0})})}async function Gr(s,{signal:e,onUpdate:t,onTeams:i,onClose:r,reportId:o=null}){if(L())return;let n=1e3,l=async(c,p,u)=>{if(u.throwIfAborted(),c==="close")return await r(),!1;if(c==="triage"||c==="teams"){let v=null;if(c==="teams")try{let _=JSON.parse(p);typeof _?.revision=="string"&&(v=_.revision)}catch{}if(await(c==="teams"?i?.(u,v):t(u))===!1)throw new Error("Team refresh failed");u.throwIfAborted(),n=1e3}return!0};for(;!e.aborted;){let c=new AbortController,p=()=>c.abort();e.addEventListener("abort",p,{once:!0});let u,v,_=()=>{clearTimeout(v),v=setTimeout(p,45e3)};try{_();let S=o===null?"":`?reportId=${encodeURIComponent(o)}`,R=await m(s?`/api/teams/${encodeURIComponent(s)}/feed${S}`:"/api/teams/feed",{credentials:"same-origin",headers:{accept:"text/event-stream"},signal:c.signal});if([401,403,404].includes(R.status)){await R.body?.cancel(),e.aborted||await r();return}if(!R.ok||!R.headers.get("content-type")?.startsWith("text/event-stream"))throw await R.body?.cancel(),new Error("Team feed unavailable");if(u=R.body.getReader(),await Kr(u,e,_,(Q,ee)=>l(Q,ee,c.signal)))return}catch{}finally{clearTimeout(v),p(),await u?.cancel().catch(()=>{}),e.removeEventListener("abort",p)}await jr(n,e),n=Math.min(n*2,3e4)}}async function Kr(s,e,t,i){let r=new TextDecoder,o="";for(;!e.aborted;){let{done:n,value:l}=await s.read();if(n)return!1;if(t(),o=(o+r.decode(l,{stream:!0})).replaceAll(`\r
package/out/index.html CHANGED
@@ -21,16 +21,16 @@
21
21
  <meta name="apple-mobile-web-app-title" content="DeepView" />
22
22
  <link rel="preload" href="./view.css" as="style" />
23
23
  <link rel="modulepreload" href="./view.js" />
24
- <link rel="modulepreload" href="./chunk-XL35ZV6R.js" />
24
+ <link rel="modulepreload" href="./chunk-D7AF2JXL.js" />
25
25
  <link rel="modulepreload" href="./chunk-KEX2ATEW.js" />
26
- <link rel="modulepreload" href="./chunk-TY4RZD3Z.js" />
26
+ <link rel="modulepreload" href="./chunk-WOTMRDM3.js" />
27
27
  <link rel="modulepreload" href="./chunk-2KBF5TZ7.js" />
28
28
  <link rel="modulepreload" href="./chunk-K3X5EJBQ.js" />
29
29
  <link rel="modulepreload" href="./chunk-CVVJAXVY.js" />
30
30
  <link rel="modulepreload" href="./chunk-VIHGO77Y.js" />
31
31
  <link rel="modulepreload" href="./chunk-RFZQQXSG.js" />
32
32
  <link rel="modulepreload" href="./chunk-RSHMVYNS.js" />
33
- <link rel="modulepreload" href="./chunk-2AFQKRAM.js" />
33
+ <link rel="modulepreload" href="./chunk-CLPKNNC7.js" />
34
34
  <link rel="modulepreload" href="./chunk-UYAXVRVL.js" />
35
35
  <link rel="modulepreload" href="./chunk-NRPC74AM.js" />
36
36
  <link rel="modulepreload" href="./chunk-VQJCXGIY.js" />