@preventive/triage 1.0.0-alpha.1 → 1.0.0-alpha.10

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.
Files changed (56) hide show
  1. package/api/reap.ts +79 -0
  2. package/common/save-error-reason.ts +20 -7
  3. package/common/server-info.ts +30 -0
  4. package/out/brotli-fallback.js +1 -1
  5. package/out/client-admin.js +28 -0
  6. package/out/client-managed.js +1 -0
  7. package/out/client-sync.js +16 -13
  8. package/out/graph.js +5 -4
  9. package/out/index.html +4 -15
  10. package/out/prism.js +2 -2
  11. package/out/terminal.js +32 -28
  12. package/out/view.css +1 -1
  13. package/out/view.js +78 -52
  14. package/package.json +70 -54
  15. package/{server → server-common}/origin.ts +5 -5
  16. package/{server → server-e2e}/auth.ts +5 -1
  17. package/{server → server-e2e}/bus-receiver.ts +8 -8
  18. package/server-e2e/cli.js +22 -0
  19. package/{server → server-e2e}/config.ts +21 -8
  20. package/{server → server-e2e}/db-neon.ts +31 -25
  21. package/{server → server-e2e}/db-revision-sql.ts +7 -10
  22. package/{server → server-e2e}/db-stmt.ts +2 -2
  23. package/{server → server-e2e}/db.ts +96 -135
  24. package/{server → server-e2e}/http.ts +97 -9
  25. package/{server → server-e2e}/hub.ts +7 -8
  26. package/{server → server-e2e}/index.ts +67 -43
  27. package/{server → server-e2e}/lifecycle.ts +14 -5
  28. package/{server → server-e2e}/npm-proxy.ts +1 -1
  29. package/{server → server-e2e}/objstore/blob-fs.ts +6 -8
  30. package/{server → server-e2e}/objstore/blob-vercel.ts +69 -36
  31. package/{server → server-e2e}/objstore/blob.ts +24 -9
  32. package/server-e2e/objstore/fetch-mint-guard.ts +74 -0
  33. package/{server → server-e2e}/objstore/handlers.ts +13 -15
  34. package/{server → server-e2e}/objstore/init.ts +47 -13
  35. package/{server → server-e2e}/objstore/reaper.ts +31 -11
  36. package/server-e2e/objstore/rest-deny.ts +28 -0
  37. package/server-e2e/objstore/rest-mint.ts +224 -0
  38. package/{server → server-e2e}/objstore/rest.ts +110 -93
  39. package/{server → server-e2e}/objstore/sign.ts +105 -0
  40. package/{server → server-e2e}/objstore/store-neon.ts +19 -19
  41. package/{server → server-e2e}/objstore/store.ts +98 -118
  42. package/{server → server-e2e}/objstore/tokens.ts +9 -12
  43. package/{server → server-e2e}/peer.ts +7 -9
  44. package/{server → server-e2e}/pubsub.ts +21 -31
  45. package/{server → server-e2e}/sign.ts +12 -14
  46. package/{server → server-e2e}/sse-server.ts +105 -73
  47. package/{server → server-e2e}/sse-session.ts +30 -16
  48. package/{server → server-e2e}/static.ts +22 -17
  49. package/{server → server-e2e}/sync-handlers.ts +172 -117
  50. package/{server → server-e2e}/util.ts +9 -0
  51. package/{server → server-e2e}/ws-server.ts +29 -23
  52. package/strip-types-loader.js +94 -0
  53. /package/{server → server-e2e}/config.example.json +0 -0
  54. /package/{server → server-e2e}/neon-driver.ts +0 -0
  55. /package/{server → server-e2e}/objstore/fs.ts +0 -0
  56. /package/{server → server-e2e}/validation.ts +0 -0
@@ -0,0 +1,28 @@
1
+ var N=globalThis,L=N.ShadowRoot&&(N.ShadyCSS===void 0||N.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,D=Symbol(),ie=new WeakMap,E=class{constructor(e,t,r){if(this._$cssResult$=!0,r!==D)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o,t=this.t;if(L&&e===void 0){let r=t!==void 0&&t.length===1;r&&(e=ie.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),r&&ie.set(t,e))}return e}toString(){return this.cssText}},oe=i=>new E(typeof i=="string"?i:i+"",void 0,D),y=(i,...e)=>{let t=i.length===1?i[0]:e.reduce((r,s,o)=>r+(a=>{if(a._$cssResult$===!0)return a.cssText;if(typeof a=="number")return a;throw Error("Value passed to 'css' function must be a 'css' function result: "+a+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+i[o+1],i[0]);return new E(t,i,D)},ne=(i,e)=>{if(L)i.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of e){let r=document.createElement("style"),s=N.litNonce;s!==void 0&&r.setAttribute("nonce",s),r.textContent=t.cssText,i.appendChild(r)}},j=L?i=>i:i=>i instanceof CSSStyleSheet?(e=>{let t="";for(let r of e.cssRules)t+=r.cssText;return oe(t)})(i):i;var{is:Re,defineProperty:Te,getOwnPropertyDescriptor:Ie,getOwnPropertyNames:Ue,getOwnPropertySymbols:Oe,getPrototypeOf:ze}=Object,H=globalThis,ae=H.trustedTypes,Ne=ae?ae.emptyScript:"",Le=H.reactiveElementPolyfillSupport,C=(i,e)=>i,B={toAttribute(i,e){switch(e){case Boolean:i=i?Ne:null;break;case Object:case Array:i=i==null?i:JSON.stringify(i)}return i},fromAttribute(i,e){let t=i;switch(e){case Boolean:t=i!==null;break;case Number:t=i===null?null:Number(i);break;case Object:case Array:try{t=JSON.parse(i)}catch{t=null}}return t}},de=(i,e)=>!Re(i,e),le={attribute:!0,type:String,converter:B,reflect:!1,useDefault:!1,hasChanged:de};Symbol.metadata??=Symbol("metadata"),H.litPropertyMetadata??=new WeakMap;var g=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=le){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){let r=Symbol(),s=this.getPropertyDescriptor(e,r,t);s!==void 0&&Te(this.prototype,e,s)}}static getPropertyDescriptor(e,t,r){let{get:s,set:o}=Ie(this.prototype,e)??{get(){return this[t]},set(a){this[t]=a}};return{get:s,set(a){let h=s?.call(this);o?.call(this,a),this.requestUpdate(e,h,r)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??le}static _$Ei(){if(this.hasOwnProperty(C("elementProperties")))return;let e=ze(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(C("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(C("properties"))){let t=this.properties,r=[...Ue(t),...Oe(t)];for(let s of r)this.createProperty(s,t[s])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[r,s]of t)this.elementProperties.set(r,s)}this._$Eh=new Map;for(let[t,r]of this.elementProperties){let s=this._$Eu(t,r);s!==void 0&&this._$Eh.set(s,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let r=new Set(e.flat(1/0).reverse());for(let s of r)t.unshift(j(s))}else e!==void 0&&t.push(j(e));return t}static _$Eu(e,t){let r=t.attribute;return r===!1?void 0:typeof r=="string"?r:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let r of t.keys())this.hasOwnProperty(r)&&(e.set(r,this[r]),delete this[r]);e.size>0&&(this._$Ep=e)}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return ne(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,r){this._$AK(e,r)}_$ET(e,t){let r=this.constructor.elementProperties.get(e),s=this.constructor._$Eu(e,r);if(s!==void 0&&r.reflect===!0){let o=(r.converter?.toAttribute!==void 0?r.converter:B).toAttribute(t,r.type);this._$Em=e,o==null?this.removeAttribute(s):this.setAttribute(s,o),this._$Em=null}}_$AK(e,t){let r=this.constructor,s=r._$Eh.get(e);if(s!==void 0&&this._$Em!==s){let o=r.getPropertyOptions(s),a=typeof o.converter=="function"?{fromAttribute:o.converter}:o.converter?.fromAttribute!==void 0?o.converter:B;this._$Em=s;let h=a.fromAttribute(t,o.type);this[s]=h??this._$Ej?.get(s)??h,this._$Em=null}}requestUpdate(e,t,r,s=!1,o){if(e!==void 0){let a=this.constructor;if(s===!1&&(o=this[e]),r??=a.getPropertyOptions(e),!((r.hasChanged??de)(o,t)||r.useDefault&&r.reflect&&o===this._$Ej?.get(e)&&!this.hasAttribute(a._$Eu(e,r))))return;this.C(e,t,r)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(e,t,{useDefault:r,reflect:s,wrapped:o},a){r&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,a??t??this[e]),o!==!0||a!==void 0)||(this._$AL.has(e)||(this.hasUpdated||r||(t=void 0),this._$AL.set(e,t)),s===!0&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[s,o]of this._$Ep)this[s]=o;this._$Ep=void 0}let r=this.constructor.elementProperties;if(r.size>0)for(let[s,o]of r){let{wrapped:a}=o,h=this[s];a!==!0||this._$AL.has(s)||h===void 0||this.C(s,void 0,o,h)}}let e=!1,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(r=>r.hostUpdate?.()),this.update(t)):this._$EM()}catch(r){throw e=!1,this._$EM(),r}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(e){}firstUpdated(e){}};g.elementStyles=[],g.shadowRootOptions={mode:"open"},g[C("elementProperties")]=new Map,g[C("finalized")]=new Map,Le?.({ReactiveElement:g}),(H.reactiveElementVersions??=[]).push("2.1.2");var K=globalThis,ce=i=>i,M=K.trustedTypes,pe=M?M.createPolicy("lit-html",{createHTML:i=>i}):void 0,_e="$lit$",b=`lit$${Math.random().toFixed(9).slice(2)}$`,be="?"+b,He=`<${be}>`,x=document,R=()=>x.createComment(""),T=i=>i===null||typeof i!="object"&&typeof i!="function",G=Array.isArray,Me=i=>G(i)||typeof i?.[Symbol.iterator]=="function",V=`[
2
+ \f\r]`,P=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,he=/-->/g,ue=/>/g,$=RegExp(`>|${V}(?:([^\\s"'>=/]+)(${V}*=${V}*(?:[^
3
+ \f\r"'\`<>=]|("|')|))|$)`,"g"),me=/'/g,fe=/"/g,ve=/^(?:script|style|textarea|title)$/i,Y=i=>(e,...t)=>({_$litType$:i,strings:e,values:t}),n=Y(1),dt=Y(2),ct=Y(3),A=Symbol.for("lit-noChange"),c=Symbol.for("lit-nothing"),ge=new WeakMap,w=x.createTreeWalker(x,129);function ye(i,e){if(!G(i)||!i.hasOwnProperty("raw"))throw Error("invalid template strings array");return pe!==void 0?pe.createHTML(e):e}var De=(i,e)=>{let t=i.length-1,r=[],s,o=e===2?"<svg>":e===3?"<math>":"",a=P;for(let h=0;h<t;h++){let l=i[h],d,u,p=-1,f=0;for(;f<l.length&&(a.lastIndex=f,u=a.exec(l),u!==null);)f=a.lastIndex,a===P?u[1]==="!--"?a=he:u[1]!==void 0?a=ue:u[2]!==void 0?(ve.test(u[2])&&(s=RegExp("</"+u[2],"g")),a=$):u[3]!==void 0&&(a=$):a===$?u[0]===">"?(a=s??P,p=-1):u[1]===void 0?p=-2:(p=a.lastIndex-u[2].length,d=u[1],a=u[3]===void 0?$:u[3]==='"'?fe:me):a===fe||a===me?a=$:a===he||a===ue?a=P:(a=$,s=void 0);let _=a===$&&i[h+1].startsWith("/>")?" ":"";o+=a===P?l+He:p>=0?(r.push(d),l.slice(0,p)+_e+l.slice(p)+b+_):l+b+(p===-2?h:_)}return[ye(i,o+(i[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),r]},I=class i{constructor({strings:e,_$litType$:t},r){let s;this.parts=[];let o=0,a=0,h=e.length-1,l=this.parts,[d,u]=De(e,t);if(this.el=i.createElement(d,r),w.currentNode=this.el.content,t===2||t===3){let p=this.el.content.firstChild;p.replaceWith(...p.childNodes)}for(;(s=w.nextNode())!==null&&l.length<h;){if(s.nodeType===1){if(s.hasAttributes())for(let p of s.getAttributeNames())if(p.endsWith(_e)){let f=u[a++],_=s.getAttribute(p).split(b),z=/([.?@])?(.*)/.exec(f);l.push({type:1,index:o,name:z[2],strings:_,ctor:z[1]==="."?F:z[1]==="?"?Z:z[1]==="@"?W:S}),s.removeAttribute(p)}else p.startsWith(b)&&(l.push({type:6,index:o}),s.removeAttribute(p));if(ve.test(s.tagName)){let p=s.textContent.split(b),f=p.length-1;if(f>0){s.textContent=M?M.emptyScript:"";for(let _=0;_<f;_++)s.append(p[_],R()),w.nextNode(),l.push({type:2,index:++o});s.append(p[f],R())}}}else if(s.nodeType===8)if(s.data===be)l.push({type:2,index:o});else{let p=-1;for(;(p=s.data.indexOf(b,p+1))!==-1;)l.push({type:7,index:o}),p+=b.length-1}o++}}static createElement(e,t){let r=x.createElement("template");return r.innerHTML=e,r}};function k(i,e,t=i,r){if(e===A)return e;let s=r!==void 0?t._$Co?.[r]:t._$Cl,o=T(e)?void 0:e._$litDirective$;return s?.constructor!==o&&(s?._$AO?.(!1),o===void 0?s=void 0:(s=new o(i),s._$AT(i,t,r)),r!==void 0?(t._$Co??=[])[r]=s:t._$Cl=s),s!==void 0&&(e=k(i,s._$AS(i,e.values),s,r)),e}var q=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:r}=this._$AD,s=(e?.creationScope??x).importNode(t,!0);w.currentNode=s;let o=w.nextNode(),a=0,h=0,l=r[0];for(;l!==void 0;){if(a===l.index){let d;l.type===2?d=new U(o,o.nextSibling,this,e):l.type===1?d=new l.ctor(o,l.name,l.strings,this,e):l.type===6&&(d=new J(o,this,e)),this._$AV.push(d),l=r[++h]}a!==l?.index&&(o=w.nextNode(),a++)}return w.currentNode=x,s}p(e){let t=0;for(let r of this._$AV)r!==void 0&&(r.strings!==void 0?(r._$AI(e,r,t),t+=r.strings.length-2):r._$AI(e[t])),t++}},U=class i{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,r,s){this.type=2,this._$AH=c,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=r,this.options=s,this._$Cv=s?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=k(this,e,t),T(e)?e===c||e==null||e===""?(this._$AH!==c&&this._$AR(),this._$AH=c):e!==this._$AH&&e!==A&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):Me(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==c&&T(this._$AH)?this._$AA.nextSibling.data=e:this.T(x.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:r}=e,s=typeof r=="number"?this._$AC(e):(r.el===void 0&&(r.el=I.createElement(ye(r.h,r.h[0]),this.options)),r);if(this._$AH?._$AD===s)this._$AH.p(t);else{let o=new q(s,this),a=o.u(this.options);o.p(t),this.T(a),this._$AH=o}}_$AC(e){let t=ge.get(e.strings);return t===void 0&&ge.set(e.strings,t=new I(e)),t}k(e){G(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,r,s=0;for(let o of e)s===t.length?t.push(r=new i(this.O(R()),this.O(R()),this,this.options)):r=t[s],r._$AI(o),s++;s<t.length&&(this._$AR(r&&r._$AB.nextSibling,s),t.length=s)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let r=ce(e).nextSibling;ce(e).remove(),e=r}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},S=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,r,s,o){this.type=1,this._$AH=c,this._$AN=void 0,this.element=e,this.name=t,this._$AM=s,this.options=o,r.length>2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=c}_$AI(e,t=this,r,s){let o=this.strings,a=!1;if(o===void 0)e=k(this,e,t,0),a=!T(e)||e!==this._$AH&&e!==A,a&&(this._$AH=e);else{let h=e,l,d;for(e=o[0],l=0;l<o.length-1;l++)d=k(this,h[r+l],t,l),d===A&&(d=this._$AH[l]),a||=!T(d)||d!==this._$AH[l],d===c?e=c:e!==c&&(e+=(d??"")+o[l+1]),this._$AH[l]=d}a&&!s&&this.j(e)}j(e){e===c?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"")}},F=class extends S{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===c?void 0:e}},Z=class extends S{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==c)}},W=class extends S{constructor(e,t,r,s,o){super(e,t,r,s,o),this.type=5}_$AI(e,t=this){if((e=k(this,e,t,0)??c)===A)return;let r=this._$AH,s=e===c&&r!==c||e.capture!==r.capture||e.once!==r.once||e.passive!==r.passive,o=e!==c&&(r===c||s);s&&this.element.removeEventListener(this.name,this,r),o&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}},J=class{constructor(e,t,r){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=r}get _$AU(){return this._$AM._$AU}_$AI(e){k(this,e)}};var je=K.litHtmlPolyfillSupport;je?.(I,U),(K.litHtmlVersions??=[]).push("3.3.3");var $e=(i,e,t)=>{let r=t?.renderBefore??e,s=r._$litPart$;if(s===void 0){let o=t?.renderBefore??null;r._$litPart$=s=new U(e.insertBefore(R(),o),o,void 0,t??{})}return s._$AI(i),s};var Q=globalThis,m=class extends g{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=$e(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return A}};m._$litElement$=!0,m.finalized=!0,Q.litElementHydrateSupport?.({LitElement:m});var Be=Q.litElementPolyfillSupport;Be?.({LitElement:m});(Q.litElementVersions??=[]).push("4.2.2");var we=["admin","manage","triage","view","none"];var xe={dependencies:"Dependencies",security:"Security"};async function O(){let i=await fetch("/api/auth/session",{credentials:"same-origin",headers:{accept:"application/json"}});if(!i.ok)return null;let e=await i.json();return{id:e?.user?.id??null,csrfToken:typeof e?.csrfToken=="string"?e.csrfToken:null}}async function Ve(){let i=await fetch("/api/admin/users",{credentials:"same-origin",headers:{accept:"application/json"}});if(!i.ok)throw new Error(`HTTP ${i.status}`);let e=await i.json();return Array.isArray(e?.users)?e.users:[]}async function qe(i,e,t){let r={"content-type":"application/json"};t&&(r["x-csrf-token"]=t);let s=await fetch("/api/admin/set-role",{method:"POST",credentials:"same-origin",headers:r,body:JSON.stringify({userId:i,role:e})});if(!s.ok)throw new Error(`HTTP ${s.status}`)}var X=class extends m{static properties={_users:{state:!0},_error:{state:!0}};static styles=y`:host{display:block;padding:1.5rem clamp(1rem,4vw,2.5rem);color:var(--text)}.wrap{max-width:48rem;margin:0 auto}h1{font-size:1.15rem;font-weight:600;margin:0 0 1rem;user-select:none}.users{list-style:none;margin:0;padding:0;border:1px solid var(--border);border-radius:8px;overflow:hidden}.users li{display:flex;align-items:center;gap:.7rem;padding:.55rem .85rem}.users li+li{border-top:1px solid var(--border)}.avatar{position:relative;flex-shrink:0;width:28px;height:28px;border-radius:50%;overflow:hidden;background:var(--accent);display:inline-grid;place-items:center;user-select:none}.avatar>span{font-size:.8rem;font-weight:600;color:var(--bg);text-transform:uppercase}.avatar img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.avatar img.broken{display:none}.who{display:flex;flex-direction:column;min-width:0}.login{font-weight:600;font-size:.9rem}.name{color:var(--muted);font-size:.8rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.role{margin-left:auto;flex-shrink:0;font:inherit;font-size:.82rem;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:.2rem .4rem}.role:disabled{opacity:.55}.msg{color:var(--muted);font-size:.9rem}.msg.error{color:var(--critical,#c00)}`;constructor(){super(),this._users=null,this._error=null,this._me=null,this._csrf=null}connectedCallback(){super.connectedCallback(),this._load()}async _load(){this._error=null,this._users=null;try{let[e,t]=await Promise.all([O(),Ve()]);this._me=e?.id??null,this._csrf=e?.csrfToken??null,this._users=t}catch(e){this._error=String(e?.message??e)}}render(){return n`<div class="wrap"><h1>Users</h1>${this._error==null?this._users==null?n`<p class="msg">Loading…</p>`:this._list():n`<p class="msg error">Couldn't load users: ${this._error}</p>`}</div>`}_list(){return this._users.length===0?n`<p class="msg">No users yet.</p>`:n`<ul class="users">${this._users.map(e=>this._row(e))}</ul>`}_row(e){let t=(e.login?.[0]??"?").toUpperCase(),r=e.id===this._me;return n`<li><span class="avatar"><span>${t}</span> <img alt="" src="${`/api/avatar/${encodeURIComponent(e.id)}`}" @error="${s=>s.currentTarget.classList.add("broken")}"> </span><span class="who"><span class="login">${e.login}</span> ${e.name?n`<span class="name">${e.name}</span>`:c} </span><select class="role" ?disabled="${r}" title="${r?"You can\u2019t change your own role":"Change role"}" @change="${s=>this._changeRole(e,s.target.value,s.target)}">${we.map(s=>n`<option value="${s}" ?selected="${s===e.role}">${s}</option>`)}</select></li>`}async _changeRole(e,t,r){let s=e.role;if(t!==s){try{await qe(e.id,t,this._csrf),e.role=t}catch(o){console.warn("admin: set role failed:",o),r.value=s}this.requestUpdate()}}};customElements.define("managed-admin-users",X);async function Fe(){let i=await fetch("/api/admin/repositories",{credentials:"same-origin",headers:{accept:"application/json"}});if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}async function Ze(i,e,t){let r={"content-type":"application/json"};t&&(r["x-csrf-token"]=t);let s=await fetch("/api/admin/repositories/select",{method:"POST",credentials:"same-origin",headers:r,body:JSON.stringify({repoId:i,selected:e})});if(!s.ok)throw new Error(`HTTP ${s.status}`)}var We=n`<svg class="repo-icon" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M2 2.75A2.75 2.75 0 0 1 4.75 0h7.5a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75H4.5a1 1 0 0 0 0 2h8a.75.75 0 0 1 0 1.5h-8A2.5 2.5 0 0 1 2 13V2.75Zm2.75-.25a1.25 1.25 0 0 0-1.25 1.25v7.32c.317-.114.66-.07 1 .18V2.5h-.5a.25.25 0 0 0 .75 0Zm6.75 0H5.5v8.5h6V2.5Z"/></svg>`,ee=class extends m{static properties={_data:{state:!0},_error:{state:!0},_tab:{state:!0}};static styles=y`:host{display:block;padding:1.5rem clamp(1rem,4vw,2.5rem);color:var(--text)}.wrap{max-width:48rem;margin:0 auto}.head{display:flex;align-items:center;gap:1rem;margin:0 0 .75rem}h1{font-size:1.15rem;font-weight:600;margin:0;user-select:none}.connect{margin-left:auto;flex-shrink:0;font-size:.85rem;font-weight:600;color:var(--bg);background:var(--accent);text-decoration:none;border-radius:6px;padding:.35rem .7rem;white-space:nowrap}.connect:hover{opacity:.9}.tabs{display:flex;gap:.25rem;margin:0 0 1rem;border-bottom:1px solid var(--border)}.tab{font:inherit;font-size:.85rem;font-weight:600;color:var(--muted);background:0 0;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;padding:.4rem .55rem;cursor:pointer;user-select:none}.tab:hover{color:var(--text)}.tab.active{color:var(--text);border-bottom-color:var(--accent)}.hint{color:var(--muted);font-size:.82rem;margin:0 0 .8rem}.repos{list-style:none;margin:0;padding:0;border:1px solid var(--border);border-radius:8px;overflow:hidden}.repos li{display:flex;align-items:center;gap:.6rem;padding:.55rem .85rem}.repos li+li{border-top:1px solid var(--border)}.select{flex-shrink:0;width:16px;height:16px;accent-color:var(--accent);cursor:pointer}.repo-icon{flex-shrink:0;color:var(--muted)}.full-name{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge{flex-shrink:0;font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);border:1px solid var(--border);border-radius:999px;padding:.05rem .4rem}.open{margin-left:auto;flex-shrink:0;font-size:.82rem;color:var(--accent);text-decoration:none}.open:hover{text-decoration:underline}.msg{color:var(--muted);font-size:.9rem;line-height:1.5}.msg.error{color:var(--critical,#c00)}`;constructor(){super(),this._data=null,this._error=null,this._csrf=null,this._tab="manage"}connectedCallback(){super.connectedCallback(),this._load()}async _load(){this._error=null,this._data=null;try{let[e,t]=await Promise.all([O(),Fe()]);this._csrf=e?.csrfToken??null,this._data=t}catch(e){this._error=String(e?.message??e)}}render(){let e=this._data?.installUrl;return n`<div class="wrap"><div class="head"><h1>Repositories</h1>${e?n`<a class="connect" href="${e}" target="_blank" rel="noopener noreferrer">Connect a repository</a>`:c}</div><div class="tabs" role="tablist"><button type="button" role="tab" class="tab ${this._tab==="manage"?"active":""}" aria-selected="${this._tab==="manage"}" @click="${()=>{this._tab="manage"}}">Manage repositories</button> <button type="button" role="tab" class="tab ${this._tab==="select"?"active":""}" aria-selected="${this._tab==="select"}" @click="${()=>{this._tab="select"}}">Select repositories</button></div>${this._body()}</div>`}_body(){if(this._error!=null)return n`<p class="msg error">Couldn't load repositories: ${this._error}</p>`;if(this._data==null)return n`<p class="msg">Loading…</p>`;let e=Array.isArray(this._data.repositories)?this._data.repositories:[];return e.length===0&&this._data.tokenMissing?n`<p class="msg">We don't have current GitHub access for your account. Please <strong>log out and log back in</strong> to grant repository access.</p>`:this._tab==="select"?this._selectView(e):this._manageView(e)}_manageView(e){let t=e.filter(r=>r.installed);return t.length===0?n`<p class="msg">No repositories connected yet. ${this._data.installUrl?n`Use “Connect a repository” to install the GitHub App on the repositories you want to read.`:n`Install the GitHub App on the repositories you want to read.`}</p>`:n`<p class="hint">Connected through the GitHub App — we can read these repositories.</p><ul class="repos">${t.map(r=>this._row(r,!1))}</ul>`}_selectView(e){return e.length===0?n`<p class="msg">No repositories found. ${this._data.installUrl?n`Use “Connect a repository” to install the GitHub App on the private repositories you want.`:n`Public repositories you own appear here.`}</p>`:n`${this._data.tokenMissing?n`<p class="hint">Some public repositories may be hidden — log out and back in to refresh GitHub access.</p>`:c}<p class="hint">Check a repository to operate on it — we'll read its contents.</p><ul class="repos">${e.map(t=>this._row(t,!0))}</ul>`}_row(e,t){return n`<li>${t?n`<input type="checkbox" class="select" .checked="${e.selected===!0}" title="${e.selected?"Selected \u2014 remove from the operate-on set":"Select this repository to operate on"}" @change="${r=>this._toggle(e,r.target)}">`:c} ${We} <span class="full-name">${e.fullName}</span> ${e.private?n`<span class="badge">private</span>`:c} ${e.htmlUrl?n`<a class="open" href="${e.htmlUrl}" target="_blank" rel="noopener noreferrer">open</a>`:c}</li>`}async _toggle(e,t){let r=t.checked;try{await Ze(e.id,r,this._csrf),e.selected=r}catch(s){console.warn("admin: repo selection failed:",s),t.checked=!r}this.requestUpdate()}};customElements.define("managed-admin-repos",ee);async function Je(){let i=await fetch("/api/admin/reports",{credentials:"same-origin",headers:{accept:"application/json"}});if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}async function Ke(i,e,t){let r={"content-type":i.type||"application/json","x-report-filename":encodeURIComponent(i.name)};e&&(r["x-csrf-token"]=e),t!=null&&(r["x-repo-id"]=String(t));let s=await fetch("/api/admin/reports",{method:"POST",credentials:"same-origin",headers:r,body:i});if(!s.ok)throw new Error(s.status===413?"too large":`HTTP ${s.status}`);return s.json()}async function Ge(i,e){let t=e?{"x-csrf-token":e}:{},r=await fetch(`/api/admin/reports/${encodeURIComponent(i)}`,{method:"DELETE",credentials:"same-origin",headers:t});if(!r.ok)throw new Error(`HTTP ${r.status}`)}async function Ye(i,e,t){let r={"content-type":"application/json"};t&&(r["x-csrf-token"]=t);let s=await fetch("/api/admin/reports/set-repo",{method:"POST",credentials:"same-origin",headers:r,body:JSON.stringify({reportId:i,repoId:e})});if(!s.ok)throw new Error(`HTTP ${s.status}`)}function Ae(i){return typeof i!="number"||!Number.isFinite(i)?"":i<1024?`${i} B`:i<1048576?`${(i/1024).toFixed(1)} KB`:`${(i/1048576).toFixed(1)} MB`}function ke(i,e){return n`<option value="" ?selected="${e==null||e===""}">No repository</option>${i.map(t=>n`<option value="${t.repoId}" ?selected="${Number(e)===t.repoId}">${t.fullName}</option>`)}`}function Se(i,e,t){return!Array.isArray(i)||i.length===0?c:n`<label class="repo-picker"><span class="repo-picker-label">Attach uploads to</span> <select class="repo-select" @change="${r=>t(r.target.value===""?null:Number(r.target.value))}">${ke(i,e)}</select></label>`}function Ee(i,e,t){return!Array.isArray(i)||i.length===0?c:n`<select class="repo-attach" title="Attach to a repository" @change="${r=>t(r.target.value===""?null:Number(r.target.value))}">${ke(i,e)}</select>`}function Ce(i,e=!0){let t=document.createElement("input");t.type="file",t.multiple=e,t.addEventListener("change",()=>{i([...t.files])},{once:!0}),t.click()}function Pe(i,e,t){let r=0,s=d=>Array.from(d.dataTransfer?.types??[]).includes("Files"),o=d=>{s(d)&&(d.preventDefault(),r+=1,t(!0))},a=d=>{s(d)&&d.preventDefault()},h=d=>{s(d)&&(r=Math.max(0,r-1),r===0&&t(!1))},l=d=>{s(d)&&(d.preventDefault(),d.stopPropagation(),r=0,t(!1),e([...d.dataTransfer.files]))};return i.addEventListener("dragenter",o),i.addEventListener("dragover",a),i.addEventListener("dragleave",h),i.addEventListener("drop",l),()=>{i.removeEventListener("dragenter",o),i.removeEventListener("dragover",a),i.removeEventListener("dragleave",h),i.removeEventListener("drop",l)}}var Qe=n`<svg class="report-icon" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 10 4.25V1.5Zm7.75.689V4.25c0 .138.112.25.25.25h2.061ZM4.5 8.75A.75.75 0 0 1 5.25 8h5.5a.75.75 0 0 1 0 1.5h-5.5A.75.75 0 0 1 4.5 8.75Zm0 2.5a.75.75 0 0 1 .75-.75h5.5a.75.75 0 0 1 0 1.5h-5.5a.75.75 0 0 1-.75-.75Z"/></svg>`,te=class extends m{static properties={_data:{state:!0},_error:{state:!0},_busy:{state:!0},_dragOver:{state:!0}};static styles=y`:host{display:block;position:relative;flex:1 1 auto;padding:1.5rem clamp(1rem,4vw,2.5rem);color:var(--text)}.wrap{max-width:48rem;margin:0 auto}.dropzone{position:absolute;inset:.6rem;z-index:5;display:grid;place-items:center;border:2px dashed var(--accent);border-radius:10px;background:rgb(from var(--bg) r g b / .9);color:var(--accent);font-size:1rem;font-weight:600;pointer-events:none}.head{display:flex;align-items:center;gap:1rem;margin:0 0 .75rem}h1{font-size:1.15rem;font-weight:600;margin:0;user-select:none}.head-actions{margin-left:auto;flex-shrink:0;display:flex;align-items:center;gap:.5rem}.repo-picker{display:inline-flex;align-items:center;gap:.4rem}.repo-picker-label{font-size:.8rem;color:var(--muted);white-space:nowrap}.repo-select{font:inherit;font-size:.82rem;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:.2rem .4rem;max-width:14rem}.repo-attach{flex-shrink:0;font:inherit;font-size:.78rem;color:var(--muted);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:.1rem .3rem;max-width:11rem}.upload{flex-shrink:0;font:inherit;font-size:.85rem;font-weight:600;color:var(--bg);background:var(--accent);border:none;cursor:pointer;border-radius:6px;padding:.35rem .7rem;white-space:nowrap}.upload:hover{opacity:.9}.upload:disabled{opacity:.55;cursor:default}.hint{color:var(--muted);font-size:.82rem;margin:0 0 .8rem}.reports{list-style:none;margin:0;padding:0;border:1px solid var(--border);border-radius:8px;overflow:hidden}.reports li{display:flex;align-items:center;gap:.6rem;padding:.55rem .85rem}.reports li+li{border-top:1px solid var(--border)}.report-icon{flex-shrink:0;color:var(--muted)}.who{display:flex;flex-direction:column;min-width:0}.filename{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta{color:var(--muted);font-size:.78rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.actions{margin-left:auto;flex-shrink:0;display:flex;gap:.7rem;align-items:center}.download{font-size:.82rem;color:var(--accent);text-decoration:none}.download:hover{text-decoration:underline}.delete{font:inherit;font-size:.82rem;color:var(--critical,#c00);background:0 0;border:none;cursor:pointer;padding:0}.delete:hover{text-decoration:underline}.msg{color:var(--muted);font-size:.9rem;line-height:1.5}.msg.error{color:var(--critical,#c00)}`;constructor(){super(),this._data=null,this._error=null,this._csrf=null,this._busy=!1,this._repoId=null,this._dragOver=!1,this._teardownDrop=null,this._queue=[]}connectedCallback(){super.connectedCallback(),this._load(),this._teardownDrop=Pe(this,e=>{this._upload(e)},e=>{this._dragOver=e})}disconnectedCallback(){super.disconnectedCallback(),this._teardownDrop?.()}async _load(){this._error=null,this._data=null;try{let[e,t]=await Promise.all([O(),Je()]);this._csrf=e?.csrfToken??null,this._data=t}catch(e){this._error=String(e?.message??e)}}render(){return n`${this._dragOver?n`<div class="dropzone">Drop reports to upload</div>`:c}<div class="wrap"><div class="head"><h1>Reports</h1><div class="head-actions">${Se(this._data?.repos,this._repoId,e=>{this._repoId=e})} <button type="button" class="upload" ?disabled="${this._busy}" @click="${()=>Ce(e=>{this._upload(e)})}">${this._busy?"Uploading\u2026":"Upload report"}</button></div></div>${this._body()}</div>`}_body(){if(this._error!=null)return n`<p class="msg error">Couldn't load reports: ${this._error}</p>`;if(this._data==null)return n`<p class="msg">Loading…</p>`;let e=Array.isArray(this._data.reports)?this._data.reports:[];return e.length===0?n`<p class="msg">No reports uploaded yet. Drag & drop files here, or use “Upload report”, to add a findings report (JSON, markdown, or CSV) for the server to operate on.</p>`:n`<p class="hint">Reports stored on the server.</p><ul class="reports">${e.map(t=>this._row(t))}</ul>`}_row(e){let t=e.uploadedByLogin?`by ${e.uploadedByLogin}`:"uploader removed",r=Number.isFinite(e.uploadedAt)?new Date(e.uploadedAt).toLocaleString():"",s=e.bundleFilename?`bundle: ${e.bundleFilename}`:e.bundleIntegrity?"bundle: (not uploaded)":null,o=[t,r,Ae(e.byteSize),s].filter(Boolean).join(" \xB7 ");return n`<li>${Qe} <span class="who"><span class="filename">${e.filename}</span> <span class="meta">${o}</span> </span><span class="actions">${Ee(this._data?.repos,e.repoId,a=>this._setRepo(e,a))} <a class="download" href="${`/api/admin/reports/${encodeURIComponent(e.id)}`}">download</a> <button type="button" class="delete" @click="${()=>this._delete(e)}">delete</button></span></li>`}async _setRepo(e,t){try{await Ye(e.id,t,this._csrf)}catch(r){this._error=`Couldn't change repo: ${String(r?.message??r)}`}await this._load()}async _upload(e){if(e.length!==0&&(this._queue.push(...e),!this._busy)){this._busy=!0,this._error=null;try{for(;this._queue.length>0;){let t=this._queue.shift();await Ke(t,this._csrf,this._repoId)}}catch(t){this._queue=[],this._error=`Upload failed: ${String(t?.message??t)}`}finally{this._busy=!1,await this._load()}}}async _delete(e){if(globalThis.confirm?.(`Delete \u201C${e.filename}\u201D? This can't be undone.`)){try{await Ge(e.id,this._csrf)}catch(t){this._error=`Delete failed: ${String(t?.message??t)}`}await this._load()}}};customElements.define("managed-admin-reports",te);async function Xe(){let i=await fetch("/api/admin/bundles",{credentials:"same-origin",headers:{accept:"application/json"}});if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}async function et(i,e,t){let r={"content-type":"application/octet-stream","x-bundle-filename":encodeURIComponent(i.name)};e&&(r["x-csrf-token"]=e),t!=null&&(r["x-repo-id"]=String(t));let s=await fetch("/api/admin/bundles",{method:"POST",credentials:"same-origin",headers:r,body:i});if(!s.ok)throw new Error(s.status===413?"too large":`HTTP ${s.status}`);return s.json()}async function tt(i,e){let t=e?{"x-csrf-token":e}:{},r=await fetch(`/api/admin/bundles/${encodeURIComponent(i)}`,{method:"DELETE",credentials:"same-origin",headers:t});if(!r.ok)throw new Error(`HTTP ${r.status}`)}async function rt(i,e,t){let r={"content-type":"application/json"};t&&(r["x-csrf-token"]=t);let s=await fetch("/api/admin/bundles/set-repo",{method:"POST",credentials:"same-origin",headers:r,body:JSON.stringify({bundleId:i,repoId:e})});if(!s.ok)throw new Error(`HTTP ${s.status}`)}var st=n`<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>`,re=class extends m{static properties={_data:{state:!0},_error:{state:!0},_busy:{state:!0},_dragOver:{state:!0}};static styles=y`:host{display:block;position:relative;flex:1 1 auto;padding:1.5rem clamp(1rem,4vw,2.5rem);color:var(--text)}.wrap{max-width:48rem;margin:0 auto}.dropzone{position:absolute;inset:.6rem;z-index:5;display:grid;place-items:center;border:2px dashed var(--accent);border-radius:10px;background:rgb(from var(--bg) r g b / .9);color:var(--accent);font-size:1rem;font-weight:600;pointer-events:none}.head{display:flex;align-items:center;gap:1rem;margin:0 0 .75rem}h1{font-size:1.15rem;font-weight:600;margin:0;user-select:none}.head-actions{margin-left:auto;flex-shrink:0;display:flex;align-items:center;gap:.5rem}.repo-picker{display:inline-flex;align-items:center;gap:.4rem}.repo-picker-label{font-size:.8rem;color:var(--muted);white-space:nowrap}.repo-select{font:inherit;font-size:.82rem;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:.2rem .4rem;max-width:14rem}.repo-attach{flex-shrink:0;font:inherit;font-size:.78rem;color:var(--muted);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:.1rem .3rem;max-width:11rem}.upload{flex-shrink:0;font:inherit;font-size:.85rem;font-weight:600;color:var(--bg);background:var(--accent);border:none;cursor:pointer;border-radius:6px;padding:.35rem .7rem;white-space:nowrap}.upload:hover{opacity:.9}.upload:disabled{opacity:.55;cursor:default}.hint{color:var(--muted);font-size:.82rem;margin:0 0 .8rem}.reports{list-style:none;margin:0;padding:0;border:1px solid var(--border);border-radius:8px;overflow:hidden}.reports li{display:flex;align-items:center;gap:.6rem;padding:.55rem .85rem}.reports li+li{border-top:1px solid var(--border)}.report-icon{flex-shrink:0;color:var(--muted)}.who{display:flex;flex-direction:column;min-width:0}.filename{font-weight:600;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.meta{color:var(--muted);font-size:.78rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge{flex-shrink:0;font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--muted);border:1px solid var(--border);border-radius:999px;padding:.05rem .4rem}.actions{margin-left:auto;flex-shrink:0;display:flex;gap:.7rem;align-items:center}.download{font-size:.82rem;color:var(--accent);text-decoration:none}.download:hover{text-decoration:underline}.delete{font:inherit;font-size:.82rem;color:var(--critical,#c00);background:0 0;border:none;cursor:pointer;padding:0}.delete:hover{text-decoration:underline}.msg{color:var(--muted);font-size:.9rem;line-height:1.5}.msg.error{color:var(--critical,#c00)}`;constructor(){super(),this._data=null,this._error=null,this._csrf=null,this._busy=!1,this._repoId=null,this._dragOver=!1,this._teardownDrop=null,this._queue=[]}connectedCallback(){super.connectedCallback(),this._load(),this._teardownDrop=Pe(this,e=>{this._upload(e)},e=>{this._dragOver=e})}disconnectedCallback(){super.disconnectedCallback(),this._teardownDrop?.()}async _load(){this._error=null,this._data=null;try{let[e,t]=await Promise.all([O(),Xe()]);this._csrf=e?.csrfToken??null,this._data=t}catch(e){this._error=String(e?.message??e)}}render(){return n`${this._dragOver?n`<div class="dropzone">Drop bundles to upload</div>`:c}<div class="wrap"><div class="head"><h1>Bundles</h1><div class="head-actions">${Se(this._data?.repos,this._repoId,e=>{this._repoId=e})} <button type="button" class="upload" ?disabled="${this._busy}" @click="${()=>Ce(e=>{this._upload(e)})}">${this._busy?"Uploading\u2026":"Upload bundle"}</button></div></div>${this._body()}</div>`}_body(){if(this._error!=null)return n`<p class="msg error">Couldn't load bundles: ${this._error}</p>`;if(this._data==null)return n`<p class="msg">Loading…</p>`;let e=Array.isArray(this._data.bundles)?this._data.bundles:[];return e.length===0?n`<p class="msg">No bundles uploaded yet. Drag & drop files here, or use “Upload bundle”, to add a sourcemap or stasis archive for the server to operate on; reports auto-link by content hash.</p>`:n`<p class="hint">Bundles stored on the server.</p><ul class="reports">${e.map(t=>this._row(t))}</ul>`}_row(e){let t=e.uploadedByLogin?`by ${e.uploadedByLogin}`:"uploader removed",r=Number.isFinite(e.uploadedAt)?new Date(e.uploadedAt).toLocaleString():"",s=[t,r,Ae(e.byteSize)].filter(Boolean).join(" \xB7 ");return n`<li title="${e.integrity??""}">${st} <span class="who"><span class="filename">${e.filename}</span> <span class="meta">${s}</span> </span>${e.kind?n`<span class="badge">${e.kind}</span>`:c} <span class="actions">${Ee(this._data?.repos,e.repoId,o=>this._setRepo(e,o))} <a class="download" href="${`/api/admin/bundles/${encodeURIComponent(e.id)}`}">download</a> <button type="button" class="delete" @click="${()=>this._delete(e)}">delete</button></span></li>`}async _setRepo(e,t){try{await rt(e.id,t,this._csrf)}catch(r){this._error=`Couldn't change repo: ${String(r?.message??r)}`}await this._load()}async _upload(e){if(e.length!==0&&(this._queue.push(...e),!this._busy)){this._busy=!0,this._error=null;try{for(;this._queue.length>0;){let t=this._queue.shift();await et(t,this._csrf,this._repoId)}}catch(t){this._queue=[],this._error=`Upload failed: ${String(t?.message??t)}`}finally{this._busy=!1,await this._load()}}}async _delete(e){if(globalThis.confirm?.(`Delete \u201C${e.filename}\u201D? Linked reports will keep their pending link.`)){try{await tt(e.id,this._csrf)}catch(t){this._error=`Delete failed: ${String(t?.message??t)}`}await this._load()}}};customElements.define("managed-admin-bundles",re);async function it(){let i=await fetch("/api/admin/teams",{credentials:"same-origin",headers:{accept:"application/json"}});if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}async function v(i,e,t){let r={"content-type":"application/json"};e&&(r["x-csrf-token"]=e);let s=await fetch(i,{method:"POST",credentials:"same-origin",headers:r,body:JSON.stringify(t)});if(!s.ok)throw new Error(s.status===409?"name already taken":`HTTP ${s.status}`)}var se=class extends m{static properties={_data:{state:!0},_error:{state:!0},_busy:{state:!0},_renamingId:{state:!0}};static styles=y`:host{display:block;padding:1.5rem clamp(1rem,4vw,2.5rem);color:var(--text)}.wrap{max-width:48rem;margin:0 auto}.head{display:flex;align-items:center;gap:.5rem;margin:0 0 1rem}h1{font-size:1.15rem;font-weight:600;margin:0 auto 0 0;user-select:none}input,select{font:inherit;font-size:.82rem;color:var(--text);background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:.25rem .4rem}.new-name{width:12rem}.btn{font:inherit;font-size:.82rem;font-weight:600;color:var(--bg);background:var(--accent);border:none;border-radius:6px;padding:.3rem .6rem;cursor:pointer;white-space:nowrap}.btn:hover{opacity:.9}.btn:disabled{opacity:.55;cursor:default}.team{border:1px solid var(--border);border-radius:8px;padding:.85rem;margin:0 0 1rem}.team-head{display:flex;align-items:center;gap:.6rem;margin:0 0 .6rem}.team-name{font-weight:600;font-size:1rem}.rename-input{width:12rem}.sub{margin:.5rem 0 0}.sub-title{font-size:.72rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:0 0 .3rem}.links{list-style:none;margin:0 0 .4rem;padding:0}.links li{display:flex;align-items:center;gap:.6rem;padding:.25rem 0}.ln{font-size:.88rem}.path{color:var(--muted);font-size:.82rem}.perms{margin-left:auto;display:flex;gap:.8rem}.perm{display:inline-flex;align-items:center;gap:.25rem;font-size:.8rem;color:var(--muted);user-select:none}.add-row{display:flex;align-items:center;gap:.4rem;margin:.3rem 0 0;flex-wrap:wrap}.add-row select{max-width:16rem}.x{font:inherit;font-size:.8rem;color:var(--critical,#c00);background:0 0;border:none;cursor:pointer;padding:0}.x:hover{text-decoration:underline}.delete{margin-left:auto}.muted{color:var(--muted);font-size:.82rem;margin:0 0 .3rem}.msg{color:var(--muted);font-size:.9rem}.msg.error{color:var(--critical,#c00)}`;constructor(){super(),this._data=null,this._error=null,this._csrf=null,this._busy=!1,this._renamingId=null}connectedCallback(){super.connectedCallback(),this._load()}async _load(){this._error=null;try{let[e,t]=await Promise.all([O(),it()]);this._csrf=e?.csrfToken??null,this._data=t}catch(e){this._error=String(e?.message??e)}}async _do(e){if(!this._busy){this._busy=!0,this._error=null;try{await e()}catch(t){this._error=String(t?.message??t)}finally{this._busy=!1,await this._load()}}}render(){return n`<div class="wrap"><div class="head"><h1>Teams</h1><input class="new-name" type="text" placeholder="New team name" maxlength="100" @keydown="${e=>{e.key==="Enter"&&this._create()}}"> <button class="btn" ?disabled="${this._busy}" @click="${()=>this._create()}">Create</button></div>${this._body()}</div>`}_body(){if(this._error!=null&&this._data==null)return n`<p class="msg error">Couldn't load teams: ${this._error}</p>`;if(this._data==null)return n`<p class="msg">Loading…</p>`;let e=Array.isArray(this._data.teams)?this._data.teams:[];return n`${this._error==null?c:n`<p class="msg error">${this._error}</p>`} ${e.length===0?n`<p class="msg">No teams yet. Create one above.</p>`:e.map(t=>this._team(t))}`}_team(e){return n`<div class="team"><div class="team-head">${this._renamingId===e.id?n`<input class="rename-input" type="text" .value="${e.name}" maxlength="100" @keydown="${t=>this._renameKey(t,e)}"> <button class="btn" ?disabled="${this._busy}" @click="${t=>this._saveRename(e,t)}">save</button> <button class="x" @click="${()=>{this._renamingId=null}}">cancel</button>`:n`<span class="team-name">${e.name}</span> <button class="x" @click="${()=>{this._renamingId=e.id}}">rename</button> <button class="x delete" @click="${()=>this._deleteTeam(e)}">delete team</button>`}</div><div class="sub"><div class="sub-title">Repositories</div>${e.repos.length===0?n`<p class="muted">No repositories linked.</p>`:n`<ul class="links">${e.repos.map(t=>this._repoRow(e,t))}</ul>`} ${this._addRepoRow(e)}</div><div class="sub"><div class="sub-title">Members</div>${e.members.length===0?n`<p class="muted">No members.</p>`:n`<ul class="links">${e.members.map(t=>this._memberRow(e,t))}</ul>`} ${this._addMemberRow(e)}</div></div>`}_repoRow(e,t){return n`<li><span class="ln">${t.fullName}${t.path?n`<span class="path">/${t.path}</span>`:c}</span> <button class="x" @click="${()=>this._do(()=>v("/api/admin/teams/remove-repo",this._csrf,{teamId:e.id,repoId:t.repoId}))}">remove</button></li>`}_addRepoRow(e){let t=Array.isArray(this._data.repos)?this._data.repos:[];if(t.length===0)return n`<p class="muted">No selected repositories to link — pick some on “Manage repositories”.</p>`;let r=new Set(e.repos.map(s=>s.repoId));return n`<div class="add-row"><select class="add-repo-sel"><option value="">Add repository…</option>${t.map(s=>n`<option value="${s.repoId}">${s.fullName}${r.has(s.repoId)?" \u2014 update path":""}</option>`)}</select> <input class="add-repo-path" type="text" placeholder="subpath (optional)" maxlength="500"> <button class="btn" @click="${s=>this._addRepo(e,s)}">add</button></div>`}_memberRow(e,t){let r=Array.isArray(this._data.permissions)?this._data.permissions:[];return n`<li><span class="ln">${t.login}</span> <span class="perms">${r.map(s=>n`<label class="perm"><input type="checkbox" .checked="${t[s]===!0}" @change="${o=>this._togglePerm(e,t,s,o.target.checked)}"> ${xe[s]??s}</label>`)} </span><button class="x" @click="${()=>this._do(()=>v("/api/admin/teams/remove-member",this._csrf,{teamId:e.id,userId:t.userId}))}">remove</button></li>`}_addMemberRow(e){let t=Array.isArray(this._data.users)?this._data.users:[],r=new Set(e.members.map(s=>s.userId));return n`<div class="add-row"><select class="add-member-sel"><option value="">Add member…</option>${t.map(s=>n`<option value="${s.id}" ?disabled="${r.has(s.id)}">${s.login}${r.has(s.id)?" (member)":""}</option>`)}</select> <button class="btn" @click="${s=>this._addMember(e,s)}">add</button></div>`}_create(){let e=this.renderRoot.querySelector(".new-name"),t=e?.value.trim();t&&this._do(async()=>{await v("/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(()=>v("/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 r=t.target.closest(".team-head")?.querySelector(".rename-input")?.value.trim()??"";this._renamingId=null,!(r===""||r===e.name)&&this._do(()=>v("/api/admin/teams/rename",this._csrf,{teamId:e.id,name:r}))}_addRepo(e,t){let r=t.target.closest(".add-row"),s=Number(r?.querySelector(".add-repo-sel")?.value);if(!Number.isSafeInteger(s)||s<=0)return;let o=r?.querySelector(".add-repo-path")?.value??"";this._do(()=>v("/api/admin/teams/set-repo",this._csrf,{teamId:e.id,repoId:s,path:o}))}_addMember(e,t){let r=t.target.closest(".add-row")?.querySelector(".add-member-sel")?.value;r&&this._do(()=>v("/api/admin/teams/set-member",this._csrf,{teamId:e.id,userId:r}))}_togglePerm(e,t,r,s){let o={};for(let a of this._data.permissions??[])o[a]=t[a]===!0;o[r]=s,this._do(()=>v("/api/admin/teams/set-member",this._csrf,{teamId:e.id,userId:t.userId,...o}))}};customElements.define("managed-admin-teams",se);export{re as ManagedAdminBundles,te as ManagedAdminReports,ee as ManagedAdminRepos,se as ManagedAdminTeams,X as ManagedAdminUsers};
4
+ /*! Bundled license information:
5
+
6
+ @lit/reactive-element/css-tag.js:
7
+ (**
8
+ * @license
9
+ * Copyright 2019 Google LLC
10
+ * SPDX-License-Identifier: BSD-3-Clause
11
+ *)
12
+
13
+ @lit/reactive-element/reactive-element.js:
14
+ lit-html/lit-html.js:
15
+ lit-element/lit-element.js:
16
+ (**
17
+ * @license
18
+ * Copyright 2017 Google LLC
19
+ * SPDX-License-Identifier: BSD-3-Clause
20
+ *)
21
+
22
+ lit-html/is-server.js:
23
+ (**
24
+ * @license
25
+ * Copyright 2022 Google LLC
26
+ * SPDX-License-Identifier: BSD-3-Clause
27
+ *)
28
+ */
@@ -0,0 +1 @@
1
+ async function a(t){let e;try{e=await fetch(t,{credentials:"same-origin",headers:{accept:"application/json"}})}catch{return null}if(!e.ok)return null;try{return await e.json()}catch{return null}}async function o(){let t=await a("/api/auth/session"),e=t?.user;return e==null||typeof e.login!="string"?null:{id:e.id,login:e.login,name:typeof e.name=="string"?e.name:null,avatarUrl:typeof e.avatarUrl=="string"?e.avatarUrl:null,role:typeof e.role=="string"?e.role:"none",csrfToken:typeof t.csrfToken=="string"?t.csrfToken:null}}async function i(){let e=(await a("/api/teams"))?.teams;return Array.isArray(e)?e.filter(n=>n!=null&&typeof n.id=="string"&&typeof n.name=="string").map(n=>({id:n.id,name:n.name,reports:Array.isArray(n.reports)?n.reports.filter(r=>r!=null&&typeof r.id=="string"&&typeof r.filename=="string").map(r=>({id:r.id,filename:r.filename})):[]})):[]}async function l(t){let e;try{e=await fetch(`/api/reports/${encodeURIComponent(t)}`,{credentials:"same-origin"})}catch{return null}if(!e.ok)return null;try{return await e.text()}catch{return null}}function s(t){t&&(location.href=t)}async function c(t){try{await fetch("/api/auth/logout",{method:"POST",credentials:"same-origin",headers:t?{"x-csrf-token":t}:{}})}catch{}location.reload()}export{l as fetchReport,s as login,c as logout,o as probeSession,i as probeTeams};