@databehave/server 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/admin/ui.js CHANGED
@@ -1,168 +1,168 @@
1
- (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=()=>({search:``,methods:new Set([`GET`,`POST`,`PUT`,`DELETE`,`PATCH`]),activeOnly:!1,rightPanelOpen:!1,helpOpen:!1,globalOpen:!1,expandedKey:null,focusedKey:null,routes:[],doc:null,overrides:[],scenarios:[],loading:!0}),t=(e,t)=>`${e.toUpperCase()} ${t}`,n=(...t)=>{let n=e(),r=new Set;return{get:()=>n,set:e=>{n={...n,...e};for(let e of r)e(n)},subscribe:e=>(r.add(e),()=>{r.delete(e)})}},r=(e,t)=>{let n=[];for(let r of e){let e=t?.paths?.[r.path]?.[r.method.toLowerCase()]??{},i=e.tags?.[0],a=r.path.split(`/`).filter(Boolean)[0]??`other`,o=i??a;n.push({method:r.method.toUpperCase(),path:r.path,op:{...e,...r.summary&&!e.summary?{summary:r.summary}:{}},groupKey:o.toLowerCase(),groupLabel:o,source:r.source??`openapi`})}return n},i=(e,t)=>{let n=new Map;for(let t of e){let e=n.get(t.groupKey)??{key:t.groupKey,label:t.groupLabel,ops:[]};e.ops.push(t),n.set(t.groupKey,e)}for(let e of n.values())e.ops.sort((e,t)=>e.path===t.path?e.method.localeCompare(t.method):e.path.localeCompare(t.path));return[...n.values()].sort((e,t)=>e.label.localeCompare(t.label))},a=e=>{let n=new Map;for(let r of e){if(r.matcher.kind!==`exact`)continue;let e=t(r.matcher.method,r.matcher.path);n.set(e,(n.get(e)??0)+1)}return n},o=typeof window<`u`&&window.__DATABEHAVE_BASE__||``,s=void 0,c=async e=>{let t=await e.text(),n=t?JSON.parse(t):void 0;if(!e.ok){let t=typeof n==`object`&&n?n.error:void 0;throw Error(t??`HTTP ${e.status}`)}return n},l={async listOverrides(){return(await c(await fetch(o+`/overrides`))).overrides??[]},async addOverride(e){return(await c(await fetch(o+`/overrides`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)}))).override},async removeOverride(e){await c(await fetch(o+`/overrides/`+encodeURIComponent(e),{method:`DELETE`}))},async clearOverrides(){await c(await fetch(o+`/overrides`,{method:`DELETE`}))},async listRoutes(){return(await c(await fetch(o+`/openapi-routes`))).routes??[]},async fetchOpenApi(){if(s!==void 0)return s;try{let e=await fetch(o+`/openapi.json`);return e.ok?(s=await e.json(),s):(s=null,null)}catch{return s=null,null}},async listScenarios(){let e=await fetch(o+`/scenarios`);return e.status===404?[]:(await c(e)).scenarios??[]},async saveScenario(e){await c(await fetch(o+`/scenarios`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({name:e})}))},async loadScenario(e){await c(await fetch(o+`/scenarios/`+encodeURIComponent(e)+`/load`,{method:`POST`}))},async deleteScenario(e){await c(await fetch(o+`/scenarios/`+encodeURIComponent(e),{method:`DELETE`}))}},u=n(),d=0,f=new Set,p=[],m=e=>(f.add(e),()=>{f.delete(e)}),h=()=>{for(let e of f)e(p)},g=(e,t)=>{let n=++d;p=[...p,{id:n,kind:e,message:t}],h(),setTimeout(()=>{p=p.filter(e=>e.id!==n),h()},3e3)},ee=e=>{p=p.filter(t=>t.id!==e),h()},te=()=>p,_=async()=>{try{u.set({overrides:await l.listOverrides()})}catch{}},v=async()=>{try{u.set({scenarios:await l.listScenarios()})}catch{}},y=async(e,t,n)=>{let r=await l.addOverride({matcher:e,mode:t,description:n});return g(`success`,`Override applied`),await _(),r},b=async e=>{await l.removeOverride(e),g(`success`,`Override removed`),await _()},x=async()=>{if(window.confirm(`Clear ALL sticky overrides?`))try{await l.clearOverrides(),g(`success`,`Cleared`),await _()}catch(e){g(`error`,e instanceof Error?e.message:String(e))}},S=async e=>{try{await l.saveScenario(e),g(`success`,`Saved: `+e),await v()}catch(e){g(`error`,e instanceof Error?e.message:String(e))}},C=async e=>{try{await l.loadScenario(e),g(`success`,`Loaded: `+e),await _()}catch(e){g(`error`,e instanceof Error?e.message:String(e))}},w=async e=>{try{await l.deleteScenario(e),g(`success`,`Deleted: `+e),await v()}catch(e){g(`error`,e instanceof Error?e.message:String(e))}},T=()=>u.set({globalOpen:!0,rightPanelOpen:!1}),ne={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`},E=e=>e==null?``:String(e).replace(/[&<>"']/g,e=>ne[e]??e);function D(e){"@babel/helpers - typeof";return D=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},D(e)}function re(e,t){if(D(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(D(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function ie(e){var t=re(e,`string`);return D(t)==`symbol`?t:t+``}function O(e,t,n){return(t=ie(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var ae=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0),O(this,`_open`,!1)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){let e=u.get().scenarios;this.innerHTML=`
1
+ var R=Object.defineProperty;var F=(a,i,e)=>i in a?R(a,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[i]=e;var l=(a,i,e)=>F(a,typeof i!="symbol"?i+"":i,e);(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))t(s);new MutationObserver(s=>{for(const n of s)if(n.type==="childList")for(const o of n.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&t(o)}).observe(document,{childList:!0,subtree:!0});function e(s){const n={};return s.integrity&&(n.integrity=s.integrity),s.referrerPolicy&&(n.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?n.credentials="include":s.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function t(s){if(s.ep)return;s.ep=!0;const n=e(s);fetch(s.href,n)}})();const U=()=>({search:"",methods:new Set(["GET","POST","PUT","DELETE","PATCH"]),activeOnly:!1,rightPanelOpen:!1,helpOpen:!1,globalOpen:!1,expandedKey:null,focusedKey:null,routes:[],doc:null,overrides:[],scenarios:[],loading:!0}),L=(a,i)=>`${a.toUpperCase()} ${i}`,B=(...a)=>{let i=U();const e=new Set;return{get:()=>i,set:o=>{i={...i,...o};for(const r of e)r(i)},subscribe:o=>(e.add(o),()=>{e.delete(o)})}},G=(a,i)=>{const e=[];for(const t of a){const s=i?.paths?.[t.path]?.[t.method.toLowerCase()]??{},n=s.tags?.[0],o=t.path.split("/").filter(Boolean)[0]??"other",r=n??o;e.push({method:t.method.toUpperCase(),path:t.path,op:{...s,...t.summary&&!s.summary?{summary:t.summary}:{}},groupKey:r.toLowerCase(),groupLabel:r,source:t.source??"openapi"})}return e},Q=(a,i)=>{const e=new Map;for(const t of a){const s=e.get(t.groupKey)??{key:t.groupKey,label:t.groupLabel,ops:[]};s.ops.push(t),e.set(t.groupKey,s)}for(const t of e.values())t.ops.sort((s,n)=>s.path===n.path?s.method.localeCompare(n.method):s.path.localeCompare(n.path));return[...e.values()].sort((t,s)=>t.label.localeCompare(s.label))},z=a=>{const i=new Map;for(const e of a){if(e.matcher.kind!=="exact")continue;const t=L(e.matcher.method,e.matcher.path);i.set(t,(i.get(t)??0)+1)}return i},m=typeof window<"u"&&window.__DATABEHAVE_BASE__||"";let k;const v=async a=>{const i=await a.text(),e=i?JSON.parse(i):void 0;if(!a.ok){const t=typeof e=="object"&&e!==null?e.error:void 0;throw new Error(t??`HTTP ${a.status}`)}return e},f={async listOverrides(){const a=await fetch(m+"/overrides");return(await v(a)).overrides??[]},async addOverride(a){const i=await fetch(m+"/overrides",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)});return(await v(i)).override},async removeOverride(a){await v(await fetch(m+"/overrides/"+encodeURIComponent(a),{method:"DELETE"}))},async clearOverrides(){await v(await fetch(m+"/overrides",{method:"DELETE"}))},async listRoutes(){const a=await fetch(m+"/openapi-routes");return(await v(a)).routes??[]},async fetchOpenApi(){if(k!==void 0)return k;try{const a=await fetch(m+"/openapi.json");return a.ok?(k=await a.json(),k):(k=null,null)}catch{return k=null,null}},async listScenarios(){const a=await fetch(m+"/scenarios");return a.status===404?[]:(await v(a)).scenarios??[]},async saveScenario(a){await v(await fetch(m+"/scenarios",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:a})}))},async loadScenario(a){await v(await fetch(m+"/scenarios/"+encodeURIComponent(a)+"/load",{method:"POST"}))},async deleteScenario(a){await v(await fetch(m+"/scenarios/"+encodeURIComponent(a),{method:"DELETE"}))}},c=B();let J=0;const T=new Set;let g=[];const V=a=>(T.add(a),()=>{T.delete(a)}),O=()=>{for(const a of T)a(g)},p=(a,i)=>{const e=++J;g=[...g,{id:e,kind:a,message:i}],O(),setTimeout(()=>{g=g.filter(t=>t.id!==e),O()},3e3)},X=a=>{g=g.filter(i=>i.id!==a),O()},Z=()=>g,w=async()=>{try{c.set({overrides:await f.listOverrides()})}catch{}},q=async()=>{try{c.set({scenarios:await f.listScenarios()})}catch{}},D=async(a,i,e)=>{const t=await f.addOverride({matcher:a,mode:i,description:e});return p("success","Override applied"),await w(),t},E=async a=>{await f.removeOverride(a),p("success","Override removed"),await w()},W=async()=>{if(window.confirm("Clear ALL sticky overrides?"))try{await f.clearOverrides(),p("success","Cleared"),await w()}catch(a){p("error",a instanceof Error?a.message:String(a))}},Y=async a=>{try{await f.saveScenario(a),p("success","Saved: "+a),await q()}catch(i){p("error",i instanceof Error?i.message:String(i))}},ee=async a=>{try{await f.loadScenario(a),p("success","Loaded: "+a),await w()}catch(i){p("error",i instanceof Error?i.message:String(i))}},te=async a=>{try{await f.deleteScenario(a),p("success","Deleted: "+a),await q()}catch(i){p("error",i instanceof Error?i.message:String(i))}},C=()=>c.set({globalOpen:!0,rightPanelOpen:!1}),se={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},h=a=>a==null?"":String(a).replace(/[&<>"']/g,i=>se[i]??i);class ae extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub");l(this,"_open",!1)}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get().scenarios;this.innerHTML=`
2
2
  <div style="position:relative">
3
3
  <button class="ghost" type="button" data-toggle>Scenarios (${e.length})</button>
4
4
  ${this._open?`
5
5
  <div style="position:absolute;top:110%;right:0;background:#fff;color:#1f2328;border:1px solid #d0d7de;border-radius:6px;padding:8px;min-width:240px;z-index:50;box-shadow:0 2px 8px rgba(0,0,0,0.1)">
6
6
  <button class="btn" style="width:100%;margin-bottom:8px" type="button" data-save>Save current as…</button>
7
- ${e.map(e=>`
7
+ ${e.map(t=>`
8
8
  <div style="display:flex;gap:4px;align-items:center;padding:4px 0;border-top:1px solid #eaeef2">
9
- <span style="flex:1;font-size:12px">${E(e.name)} <small>(${e.count})</small></span>
10
- <button class="btn" type="button" data-load="${E(e.name)}">Load</button>
11
- <button class="btn danger" type="button" data-delete="${E(e.name)}">Delete</button>
12
- </div>`).join(``)}
13
- </div>`:``}
14
- </div>`,this.querySelector(`[data-toggle]`)?.addEventListener(`click`,()=>{this._open=!this._open,this._render()}),this.querySelector(`[data-save]`)?.addEventListener(`click`,()=>{let e=window.prompt(`Scenario name (a-z, 0-9, _ -):`);e&&S(e),this._open=!1,this._render()});for(let e of this.querySelectorAll(`[data-load]`))e.addEventListener(`click`,()=>{C(e.dataset.load??``),this._open=!1,this._render()});for(let e of this.querySelectorAll(`[data-delete]`))e.addEventListener(`click`,()=>{w(e.dataset.delete??``)})}};customElements.define(`dbh-scenarios-menu`,ae);var k=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){let e=u.get(),t=e.overrides.length,n=e.overrides.filter(e=>e.matcher.kind===`global`).length,r=typeof window<`u`?window.location.origin:``;this.innerHTML=`
9
+ <span style="flex:1;font-size:12px">${h(t.name)} <small>(${t.count})</small></span>
10
+ <button class="btn" type="button" data-load="${h(t.name)}">Load</button>
11
+ <button class="btn danger" type="button" data-delete="${h(t.name)}">Delete</button>
12
+ </div>`).join("")}
13
+ </div>`:""}
14
+ </div>`,this.querySelector("[data-toggle]")?.addEventListener("click",()=>{this._open=!this._open,this._render()}),this.querySelector("[data-save]")?.addEventListener("click",()=>{const t=window.prompt("Scenario name (a-z, 0-9, _ -):");t&&Y(t),this._open=!1,this._render()});for(const t of this.querySelectorAll("[data-load]"))t.addEventListener("click",()=>{ee(t.dataset.load??""),this._open=!1,this._render()});for(const t of this.querySelectorAll("[data-delete]"))t.addEventListener("click",()=>{te(t.dataset.delete??"")})}}customElements.define("dbh-scenarios-menu",ae);class ne extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get(),t=e.overrides.length,s=e.overrides.filter(o=>o.matcher.kind==="global").length,n=typeof window<"u"?window.location.origin:"";this.innerHTML=`
15
15
  <div class="top-bar">
16
16
  <h1>Mock Injection</h1>
17
- <span class="url">${r}</span>
17
+ <span class="url">${n}</span>
18
18
  <span class="badge">${t}</span>
19
19
  <button class="ghost" type="button" data-clear>Clear all</button>
20
20
  <button class="ghost global-override-btn" type="button" data-global title="Global override">
21
- Global override${n>0?`<span class="badge global-badge">${n}</span>`:``}
21
+ Global override${s>0?`<span class="badge global-badge">${s}</span>`:""}
22
22
  </button>
23
23
  <dbh-scenarios-menu></dbh-scenarios-menu>
24
24
  <button class="ghost" type="button" data-help title="?">?</button>
25
25
  <button class="ghost" type="button" data-panel>Active overrides</button>
26
- </div>`,this.querySelector(`[data-clear]`)?.addEventListener(`click`,()=>{x()}),this.querySelector(`[data-global]`)?.addEventListener(`click`,()=>T()),this.querySelector(`[data-help]`)?.addEventListener(`click`,()=>u.set({helpOpen:!0})),this.querySelector(`[data-panel]`)?.addEventListener(`click`,()=>u.set({rightPanelOpen:!u.get().rightPanelOpen}))}};customElements.define(`dbh-top-bar`,k);var A=[`GET`,`POST`,`PUT`,`DELETE`,`PATCH`],j=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0),O(this,`_rendered`,!1),O(this,`searchInput`,void 0)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._sync())}disconnectedCallback(){this._unsub?.()}_render(){let e=u.get();this.innerHTML=`<div class="filter-bar"><input type="text" placeholder="Search path or summary…" value="${M(e.search)}" data-search />`+A.map(t=>`<label><input type="checkbox" data-method="${t}"${e.methods.has(t)?` checked`:``}/> ${t}</label>`).join(``)+`<label><input type="checkbox" data-active${e.activeOnly?` checked`:``}/> Active overrides only</label></div>`,this.searchInput=this.querySelector(`[data-search]`)??void 0,this.searchInput?.addEventListener(`input`,e=>{let t=e.target.value;u.set({search:t})});for(let e of this.querySelectorAll(`[data-method]`))e.addEventListener(`change`,()=>{let t=new Set(u.get().methods),n=e.dataset.method??``;e.checked?t.add(n):t.delete(n),u.set({methods:t})});this.querySelector(`[data-active]`)?.addEventListener(`change`,e=>{u.set({activeOnly:e.target.checked})}),this._rendered=!0}_sync(){if(!this._rendered)return;let e=u.get();this.searchInput&&this.searchInput.value!==e.search&&(this.searchInput.value=e.search);for(let t of this.querySelectorAll(`[data-method]`))t.checked=e.methods.has(t.dataset.method??``);let t=this.querySelector(`[data-active]`);t&&(t.checked=e.activeOnly)}},M=e=>e.replace(/&/g,`&amp;`).replace(/"/g,`&quot;`).replace(/</g,`&lt;`);customElements.define(`dbh-filter-bar`,j);var N={GET:`#1f883d`,POST:`#0969da`,PUT:`#bf8700`,DELETE:`#cf222e`,PATCH:`#1f6feb`},P=e=>e===`http-status`||e===`business-failure`||e===`destroy`?`#cf222e`:e===`delay`||e===`hang`?`#bf8700`:`#0969da`,F=class extends HTMLElement{static get observedAttributes(){return[`method`]}connectedCallback(){this._render()}attributeChangedCallback(){this.isConnected&&this._render()}_render(){let e=(this.getAttribute(`method`)??``).toUpperCase(),t=N[e]??`#57606a`;this.innerHTML=`<span class="method-badge" style="background:${t}">${e}</span>`}};customElements.define(`dbh-method-badge`,F);var I=class extends HTMLElement{setRow(e){let{op:t,override:n,expanded:r,focused:i}=e,a=n?P(n.mode.kind):`transparent`,o=t.source===`handler`;this.innerHTML=`
27
- <div class="op-row${i?` focused`:``}${r?` expanded`:``}" data-key="${E(t.method+` `+t.path)}">
28
- <dbh-method-badge method="${E(t.method)}"></dbh-method-badge>
29
- <span class="path">${E(t.path)}${o?` <span class="no-oas-pill" title="No OpenAPI spec for this route">(no OAS)</span>`:``}</span>
30
- <span class="summary">${E(t.op.summary??``)}</span>
31
- <span class="dot${n?` active`:``}" style="${n?`background:${a}`:``}" title="${E(n?.mode.kind??``)}"></span>
32
- </div>`,this.querySelector(`.op-row`)?.addEventListener(`click`,()=>{let e=t.method+` `+t.path,n=u.get().expandedKey;u.set({expandedKey:n===e?null:e})})}};customElements.define(`dbh-operation-row`,I);var L=e=>e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`),R=e=>e?e.split(/\n{2,}/).map(e=>`<p>${L(e.trim()).replace(/`([^`]+)`/g,(e,t)=>`<code>${t}</code>`).replace(/\n/g,`<br/>`)}</p>`).join(``):``,z={string:`string`,integer:0,number:0,boolean:!0,null:null},B=(e,t,n=0,r=new Set)=>{if(!e||n>8)return null;if(e.example!==void 0)return e.example;if(e.default!==void 0)return e.default;if(e.enum&&e.enum.length>0)return e.enum[0];if(e.$ref){if(r.has(e.$ref))return null;let i=new Set(r);i.add(e.$ref);let a=e.$ref.replace(/^#\/components\/schemas\//,``),o=t?.components?.schemas?.[a];return B(o,t,n+1,i)}if(e.oneOf?.[0])return B(e.oneOf[0],t,n+1,r);if(e.anyOf?.[0])return B(e.anyOf[0],t,n+1,r);if(e.allOf&&e.allOf.length>0){let i={};for(let a of e.allOf){let e=B(a,t,n+1,r);e&&typeof e==`object`&&!Array.isArray(e)&&Object.assign(i,e)}return i}if(e.type===`object`||e.properties){let i={},a=e.properties??{};for(let[e,o]of Object.entries(a))i[e]=B(o,t,n+1,r);return i}return e.type===`array`?[B(e.items,t,n+1,r)]:typeof e.type==`string`&&e.type in z?z[e.type]:null},V=(e,t)=>{let n=e.replace(/^#\/components\/schemas\//,``);return t?.components?.schemas?.[n]},H=e=>e?e.$ref?e.$ref.split(`/`).pop()??`ref`:e.type===`array`?`${H(e.items)}[]`:e.type??(e.properties?`object`:`any`):`any`,U=(e,t,n=0,r,i)=>{if(!e)return`<span class="type">any</span>`;if(n>6)return`<span class="type">…</span>`;let a=e.$ref?V(e.$ref,t)??e:e,o=r?`<span class="key">${E(r)}${i?`<span class="req">*</span>`:``}: </span>`:``,s=`<span class="type">${E(H(a))}</span>`,c=n<2?` open`:``;return a.type===`object`||a.properties?`<details${c}><summary>${o}${s}</summary>`+Object.entries(a.properties??{}).map(([e,r])=>U(r,t,n+1,e,a.required?.includes(e))).join(``)+`</details>`:a.type===`array`?`<details${c}><summary>${o}${s}</summary>`+U(a.items,t,n+1)+`</details>`:`<div style="margin-left:${n?14:0}px">${o}${s}</div>`},W=class extends HTMLElement{constructor(...e){super(...e),O(this,`_op`,null),O(this,`_doc`,null)}setOp(e,t){this._op=e,this._doc=t,this._render()}_render(){let e=this._op;if(!e){this.innerHTML=``;return}if(e.source===`handler`){this.innerHTML=`
26
+ </div>`,this.querySelector("[data-clear]")?.addEventListener("click",()=>{W()}),this.querySelector("[data-global]")?.addEventListener("click",()=>C()),this.querySelector("[data-help]")?.addEventListener("click",()=>c.set({helpOpen:!0})),this.querySelector("[data-panel]")?.addEventListener("click",()=>c.set({rightPanelOpen:!c.get().rightPanelOpen}))}}customElements.define("dbh-top-bar",ne);const ie=["GET","POST","PUT","DELETE","PATCH"];class oe extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub");l(this,"_rendered",!1);l(this,"searchInput")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._sync())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get();this.innerHTML=`<div class="filter-bar"><input type="text" placeholder="Search path or summary…" value="${re(e.search)}" data-search />`+ie.map(t=>`<label><input type="checkbox" data-method="${t}"${e.methods.has(t)?" checked":""}/> ${t}</label>`).join("")+`<label><input type="checkbox" data-active${e.activeOnly?" checked":""}/> Active overrides only</label></div>`,this.searchInput=this.querySelector("[data-search]")??void 0,this.searchInput?.addEventListener("input",t=>{const s=t.target.value;c.set({search:s})});for(const t of this.querySelectorAll("[data-method]"))t.addEventListener("change",()=>{const s=new Set(c.get().methods),n=t.dataset.method??"";t.checked?s.add(n):s.delete(n),c.set({methods:s})});this.querySelector("[data-active]")?.addEventListener("change",t=>{c.set({activeOnly:t.target.checked})}),this._rendered=!0}_sync(){if(!this._rendered)return;const e=c.get();this.searchInput&&this.searchInput.value!==e.search&&(this.searchInput.value=e.search);for(const s of this.querySelectorAll("[data-method]"))s.checked=e.methods.has(s.dataset.method??"");const t=this.querySelector("[data-active]");t&&(t.checked=e.activeOnly)}}const re=a=>a.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;");customElements.define("dbh-filter-bar",oe);const de={GET:"#1f883d",POST:"#0969da",PUT:"#bf8700",DELETE:"#cf222e",PATCH:"#1f6feb"},M=a=>a==="http-status"||a==="business-failure"||a==="destroy"?"#cf222e":a==="delay"||a==="hang"?"#bf8700":"#0969da";class le extends HTMLElement{static get observedAttributes(){return["method"]}connectedCallback(){this._render()}attributeChangedCallback(){this.isConnected&&this._render()}_render(){const i=(this.getAttribute("method")??"").toUpperCase(),e=de[i]??"#57606a";this.innerHTML=`<span class="method-badge" style="background:${e}">${i}</span>`}}customElements.define("dbh-method-badge",le);class ce extends HTMLElement{setRow(i){const{op:e,override:t,expanded:s,focused:n}=i,o=t?M(t.mode.kind):"transparent",r=e.source==="handler";this.innerHTML=`
27
+ <div class="op-row${n?" focused":""}${s?" expanded":""}" data-key="${h(e.method+" "+e.path)}">
28
+ <dbh-method-badge method="${h(e.method)}"></dbh-method-badge>
29
+ <span class="path">${h(e.path)}${r?' <span class="no-oas-pill" title="No OpenAPI spec for this route">(no OAS)</span>':""}</span>
30
+ <span class="summary">${h(e.op.summary??"")}</span>
31
+ <span class="dot${t?" active":""}" style="${t?`background:${o}`:""}" title="${h(t?.mode.kind??"")}"></span>
32
+ </div>`,this.querySelector(".op-row")?.addEventListener("click",()=>{const b=e.method+" "+e.path,u=c.get().expandedKey;c.set({expandedKey:u===b?null:b})})}}customElements.define("dbh-operation-row",ce);const he=a=>a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),ue=a=>a?a.split(/\n{2,}/).map(e=>`<p>${he(e.trim()).replace(/`([^`]+)`/g,(o,r)=>`<code>${r}</code>`).replace(/\n/g,"<br/>")}</p>`).join(""):"",H={string:"string",integer:0,number:0,boolean:!0,null:null},_=(a,i,e=0,t=new Set)=>{if(!a||e>8)return null;if(a.example!==void 0)return a.example;if(a.default!==void 0)return a.default;if(a.enum&&a.enum.length>0)return a.enum[0];if(a.$ref){if(t.has(a.$ref))return null;const s=new Set(t);s.add(a.$ref);const n=a.$ref.replace(/^#\/components\/schemas\//,""),o=i?.components?.schemas?.[n];return _(o,i,e+1,s)}if(a.oneOf?.[0])return _(a.oneOf[0],i,e+1,t);if(a.anyOf?.[0])return _(a.anyOf[0],i,e+1,t);if(a.allOf&&a.allOf.length>0){const s={};for(const n of a.allOf){const o=_(n,i,e+1,t);o&&typeof o=="object"&&!Array.isArray(o)&&Object.assign(s,o)}return s}if(a.type==="object"||a.properties){const s={},n=a.properties??{};for(const[o,r]of Object.entries(n))s[o]=_(r,i,e+1,t);return s}return a.type==="array"?[_(a.items,i,e+1,t)]:typeof a.type=="string"&&a.type in H?H[a.type]:null},pe=(a,i)=>{const e=a.replace(/^#\/components\/schemas\//,"");return i?.components?.schemas?.[e]},P=a=>a?a.$ref?a.$ref.split("/").pop()??"ref":a.type==="array"?`${P(a.items)}[]`:a.type??(a.properties?"object":"any"):"any",x=(a,i,e=0,t,s)=>{if(!a)return'<span class="type">any</span>';if(e>6)return'<span class="type">…</span>';const n=a.$ref?pe(a.$ref,i)??a:a,o=t?`<span class="key">${h(t)}${s?'<span class="req">*</span>':""}: </span>`:"",r=`<span class="type">${h(P(n))}</span>`,d=e<2?" open":"";return n.type==="object"||n.properties?`<details${d}><summary>${o}${r}</summary>`+Object.entries(n.properties??{}).map(([b,u])=>x(u,i,e+1,b,n.required?.includes(b))).join("")+"</details>":n.type==="array"?`<details${d}><summary>${o}${r}</summary>`+x(n.items,i,e+1)+"</details>":`<div style="margin-left:${e?14:0}px">${o}${r}</div>`};class be extends HTMLElement{constructor(){super(...arguments);l(this,"_op",null);l(this,"_doc",null)}setOp(e,t){this._op=e,this._doc=t,this._render()}_render(){const e=this._op;if(!e){this.innerHTML="";return}if(e.source==="handler"){this.innerHTML=`
33
33
  <div class="spec-zone">
34
34
  <h4>Spec</h4>
35
35
  <div class="desc" style="color:#57606a;font-style:italic">
36
36
  No OpenAPI spec for this route. Inject controls and Try-it-out still
37
37
  work; request/response schemas are unavailable.
38
38
  </div>
39
- </div>`;return}let t=this._doc,n=e.op.requestBody?.content?.[`application/json`],r=n?.schema,i=n?n.example??B(r,t):null,a=i==null?``:JSON.stringify(i,null,2),o=e.op.parameters??[],s=e.op.responses??{};this.innerHTML=`
39
+ </div>`;return}const t=this._doc,s=e.op.requestBody?.content?.["application/json"],n=s?.schema,o=s?s.example??_(n,t):null,r=o!=null?JSON.stringify(o,null,2):"",d=e.op.parameters??[],b=e.op.responses??{};this.innerHTML=`
40
40
  <div class="spec-zone">
41
41
  <h4>Spec</h4>
42
- ${e.op.description?`<div class="desc">${R(e.op.description)}</div>`:``}
43
- ${o.length>0?`
42
+ ${e.op.description?`<div class="desc">${ue(e.op.description)}</div>`:""}
43
+ ${d.length>0?`
44
44
  <div style="margin-top:8px">
45
45
  <h4>Parameters</h4>
46
46
  <table>
47
47
  <thead><tr><th>name</th><th>in</th><th>type</th><th>required</th><th>description</th></tr></thead>
48
48
  <tbody>
49
- ${o.map(e=>`
49
+ ${d.map(u=>`
50
50
  <tr>
51
- <td><code>${E(e.name)}</code></td>
52
- <td>${E(e.in)}</td>
53
- <td>${E(e.schema?.type??`any`)}</td>
54
- <td>${e.required?`yes`:``}</td>
55
- <td>${E(e.description??``)}</td>
56
- </tr>`).join(``)}
51
+ <td><code>${h(u.name)}</code></td>
52
+ <td>${h(u.in)}</td>
53
+ <td>${h(u.schema?.type??"any")}</td>
54
+ <td>${u.required?"yes":""}</td>
55
+ <td>${h(u.description??"")}</td>
56
+ </tr>`).join("")}
57
57
  </tbody>
58
58
  </table>
59
- </div>`:``}
60
- ${r?`
59
+ </div>`:""}
60
+ ${n?`
61
61
  <div style="margin-top:8px">
62
62
  <h4>Request body</h4>
63
- <div class="schema-tree">${U(r,t)}</div>
64
- ${a?`<button class="btn ghost" style="margin-top:6px" type="button" data-copy>Copy as JSON example</button>`:``}
65
- </div>`:``}
66
- ${Object.keys(s).length>0?`
63
+ <div class="schema-tree">${x(n,t)}</div>
64
+ ${r?'<button class="btn ghost" style="margin-top:6px" type="button" data-copy>Copy as JSON example</button>':""}
65
+ </div>`:""}
66
+ ${Object.keys(b).length>0?`
67
67
  <div style="margin-top:8px">
68
68
  <h4>Responses</h4>
69
- ${Object.entries(s).map(([e,n])=>{let r=n.content?.[`application/json`]?.schema;return`
69
+ ${Object.entries(b).map(([u,y])=>{const S=y.content?.["application/json"]?.schema;return`
70
70
  <details style="margin-top:4px">
71
- <summary><strong>${E(e)}</strong> — ${E(n.description??``)}</summary>
72
- ${r?`<div class="schema-tree">${U(r,t)}</div>`:``}
73
- </details>`}).join(``)}
74
- </div>`:``}
75
- </div>`,this.querySelector(`[data-copy]`)?.addEventListener(`click`,()=>{navigator.clipboard?.writeText(a),g(`info`,`Copied`)})}};customElements.define(`dbh-spec-zone`,W);var G=e=>{let t=new TextEncoder().encode(e),n=``;for(let e of t)n+=String.fromCharCode(e);return btoa(n)},K=e=>{for(let t=0;t<e.length;t++)if(e.charCodeAt(t)>127)return!1;return!0},q=[`http-status`,`business-failure`,`custom-body`,`empty-body`,`malformed-json`,`delay`,`hang`,`destroy`],J=class extends HTMLElement{constructor(...e){super(...e),O(this,`_op`,null),O(this,`_existing`,void 0),O(this,`_globalActive`,!1),O(this,`headerOverride`,null),O(this,`_scope`,`exact`),O(this,`_kind`,`http-status`),O(this,`_status`,500),O(this,`_message`,``),O(this,`_body`,`{}`),O(this,`_contentType`,`application/json`),O(this,`_ms`,1e3),O(this,`_description`,``)}setOp(e){let t=this._existing?.id===e.existing?.id&&this._op?.path===e.op.path&&this._op?.method===e.op.method;this._op=e.op,this._existing=e.existing,this._globalActive=e.globalActive,t||this._syncFromExisting(),this._render()}_syncFromExisting(){let e=this._existing;if(!e){this._scope=`exact`,this._kind=`http-status`,this._status=500,this._message=``,this._body=`{}`,this._contentType=`application/json`,this._ms=1e3,this._description=``;return}(e.matcher.kind===`exact`||e.matcher.kind===`path`)&&(this._scope=e.matcher.kind),this._kind=e.mode.kind,this._description=e.description??``;let t=e.mode;t.kind===`http-status`?this._status=t.status:t.kind===`business-failure`?this._message=t.message:t.kind===`custom-body`?(this._status=t.status??200,this._body=JSON.stringify(t.body,null,2),this._contentType=t.contentType??`application/json`):t.kind===`empty-body`?this._status=t.status??204:t.kind===`delay`&&(this._ms=t.ms)}_renderFieldsHtml(){let e=this._kind;return e===`http-status`||e===`empty-body`||e===`malformed-json`?`<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>`:e===`business-failure`?`<div class="row"><label>Message</label><input type="text" data-field="message" value="${E(this._message)}" placeholder="Optional message returned with the failure" /></div>`:e===`custom-body`?`
71
+ <summary><strong>${h(u)}</strong> — ${h(y.description??"")}</summary>
72
+ ${S?`<div class="schema-tree">${x(S,t)}</div>`:""}
73
+ </details>`}).join("")}
74
+ </div>`:""}
75
+ </div>`,this.querySelector("[data-copy]")?.addEventListener("click",()=>{navigator.clipboard?.writeText(r),p("info","Copied")})}}customElements.define("dbh-spec-zone",be);const K=a=>{const i=new TextEncoder().encode(a);let e="";for(const t of i)e+=String.fromCharCode(t);return btoa(e)},N=a=>{for(let i=0;i<a.length;i++)if(a.charCodeAt(i)>127)return!1;return!0},me=["http-status","business-failure","custom-body","empty-body","malformed-json","delay","hang","destroy"];class fe extends HTMLElement{constructor(){super(...arguments);l(this,"_op",null);l(this,"_existing");l(this,"_globalActive",!1);l(this,"headerOverride",null);l(this,"_scope","exact");l(this,"_kind","http-status");l(this,"_status",500);l(this,"_message","");l(this,"_body","{}");l(this,"_contentType","application/json");l(this,"_ms",1e3);l(this,"_description","")}setOp(e){const t=this._existing?.id===e.existing?.id&&this._op?.path===e.op.path&&this._op?.method===e.op.method;this._op=e.op,this._existing=e.existing,this._globalActive=e.globalActive,t||this._syncFromExisting(),this._render()}_syncFromExisting(){const e=this._existing;if(!e){this._scope="exact",this._kind="http-status",this._status=500,this._message="",this._body="{}",this._contentType="application/json",this._ms=1e3,this._description="";return}(e.matcher.kind==="exact"||e.matcher.kind==="path")&&(this._scope=e.matcher.kind),this._kind=e.mode.kind,this._description=e.description??"";const t=e.mode;t.kind==="http-status"?this._status=t.status:t.kind==="business-failure"?this._message=t.message:t.kind==="custom-body"?(this._status=t.status??200,this._body=JSON.stringify(t.body,null,2),this._contentType=t.contentType??"application/json"):t.kind==="empty-body"?this._status=t.status??204:t.kind==="delay"&&(this._ms=t.ms)}_renderFieldsHtml(){const e=this._kind;return e==="http-status"||e==="empty-body"||e==="malformed-json"?`<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>`:e==="business-failure"?`<div class="row"><label>Message</label><input type="text" data-field="message" value="${h(this._message)}" placeholder="Optional message returned with the failure" /></div>`:e==="custom-body"?`
76
76
  <div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>
77
- <div class="row"><label>Content-Type</label><input type="text" data-field="contentType" value="${E(this._contentType)}" /></div>
78
- <div class="row"><label>Body</label><textarea data-field="body">${E(this._body)}</textarea></div>`:e===`delay`?`<div class="row"><label>Delay (ms)</label><input type="number" data-field="ms" value="${this._ms}" /></div>`:``}_render(){if(!this._op){this.innerHTML=``;return}let e=this._existing;this.innerHTML=`
77
+ <div class="row"><label>Content-Type</label><input type="text" data-field="contentType" value="${h(this._contentType)}" /></div>
78
+ <div class="row"><label>Body</label><textarea data-field="body">${h(this._body)}</textarea></div>`:e==="delay"?`<div class="row"><label>Delay (ms)</label><input type="number" data-field="ms" value="${this._ms}" /></div>`:""}_render(){if(!this._op){this.innerHTML="";return}const e=this._existing;this.innerHTML=`
79
79
  <div class="mock-zone">
80
80
  <h4>Inject error</h4>
81
81
  ${this._globalActive?`
82
82
  <div class="global-active-hint" role="note">
83
83
  <span>A global override is active. Manage it from the top bar.</span>
84
84
  <button type="button" class="btn ghost" data-open-global>Open</button>
85
- </div>`:``}
85
+ </div>`:""}
86
86
  <div class="row">
87
87
  <label>Scope</label>
88
88
  <div class="radio-group">
89
- <label><input type="radio" name="scope" data-scope="exact"${this._scope===`exact`?` checked`:``}/> Exact (METHOD + this path)</label>
90
- <label><input type="radio" name="scope" data-scope="path"${this._scope===`path`?` checked`:``}/> Path (any method on this path)</label>
89
+ <label><input type="radio" name="scope" data-scope="exact"${this._scope==="exact"?" checked":""}/> Exact (METHOD + this path)</label>
90
+ <label><input type="radio" name="scope" data-scope="path"${this._scope==="path"?" checked":""}/> Path (any method on this path)</label>
91
91
  </div>
92
92
  </div>
93
93
  <div class="row">
94
94
  <label>Mode</label>
95
- <select data-mode>${q.map(e=>`<option value="${e}"${e===this._kind?` selected`:``}>${e}</option>`).join(``)}</select>
95
+ <select data-mode>${me.map(t=>`<option value="${t}"${t===this._kind?" selected":""}>${t}</option>`).join("")}</select>
96
96
  </div>
97
97
  <div data-fields>${this._renderFieldsHtml()}</div>
98
98
  <div class="row">
99
99
  <label>Description</label>
100
- <input type="text" data-field="description" value="${E(this._description)}" />
100
+ <input type="text" data-field="description" value="${h(this._description)}" />
101
101
  </div>
102
102
  <div class="actions">
103
- <button class="btn primary" type="button" data-submit>${e?`Update`:`Apply`}</button>
104
- ${e?`<button class="btn danger" type="button" data-remove="${E(e.id)}">Remove</button>`:``}
103
+ <button class="btn primary" type="button" data-submit>${e?"Update":"Apply"}</button>
104
+ ${e?`<button class="btn danger" type="button" data-remove="${h(e.id)}">Remove</button>`:""}
105
105
  </div>
106
- </div>`,this._wire()}_wire(){this.querySelector(`[data-open-global]`)?.addEventListener(`click`,()=>T());for(let e of this.querySelectorAll(`[data-scope]`))e.addEventListener(`change`,()=>{e.checked&&(this._scope=e.dataset.scope)});let e=this.querySelector(`[data-mode]`);e?.addEventListener(`change`,()=>{this._kind=e.value;let t=this.querySelector(`[data-fields]`);t&&(t.innerHTML=this._renderFieldsHtml(),this._wireFields())}),this._wireFields(),this.querySelector(`[data-submit]`)?.addEventListener(`click`,()=>this._submit());let t=this.querySelector(`[data-remove]`);t?.addEventListener(`click`,()=>{b(t.dataset.remove??``)})}_wireFields(){let e=this.querySelector(`[data-field="description"]`);e?.addEventListener(`input`,()=>{this._description=e.value});for(let e of this.querySelectorAll(`[data-fields] [data-field]`))e.addEventListener(`input`,()=>{let t=e.dataset.field;t===`status`?this._status=Number(e.value):t===`ms`?this._ms=Number(e.value):t===`message`?this._message=e.value:t===`body`?this._body=e.value:t===`contentType`&&(this._contentType=e.value)})}_submit(){let e=this._op;if(!e)return;let t=this._scope===`exact`?{kind:`exact`,method:e.method,path:e.path}:{kind:`path`,path:e.path},n,r=null,i=this._kind;switch(i){case`http-status`:n={kind:i,status:this._status};break;case`business-failure`:n={kind:i,message:this._message},K(this._message)||(r={name:`x-mock-business-failure-b64`,value:G(this._message)});break;case`custom-body`:{let e;try{e=JSON.parse(this._body)}catch{e=this._body}n={kind:i,status:this._status,body:e,contentType:this._contentType};break}case`empty-body`:n={kind:i,status:this._status};break;case`malformed-json`:n={kind:i,status:this._status};break;case`delay`:n={kind:i,ms:this._ms};break;case`hang`:n={kind:i};break;case`destroy`:n={kind:i};break}this.headerOverride=r,this.dispatchEvent(new CustomEvent(`dbh-header-override`,{detail:r,bubbles:!0})),(async()=>{let e=this._existing;if(e&&t.kind===e.matcher.kind)try{await b(e.id)}catch{}try{await y(t,n,this._description||void 0)}catch(e){g(`error`,e instanceof Error?e.message:String(e))}})()}};customElements.define(`dbh-inject-error-panel`,J);var oe=(e,t)=>{let n=e.op.requestBody?.content?.[`application/json`]?.schema;return n?JSON.stringify(B(n,t),null,2):``},se=class extends HTMLElement{constructor(...e){super(...e),O(this,`_op`,null),O(this,`_doc`,null),O(this,`extraHeader`,null),O(this,`_pvals`,{}),O(this,`_freeQuery`,[]),O(this,`_body`,``),O(this,`_busy`,!1)}setOp(e,t){this._op=e,this._doc=t,this._pvals={};for(let t of e.op.parameters??[])this._pvals[t.name]=String(t.schema?.default??``);this._body=oe(e,t),this._freeQuery=e.source===`handler`?[{key:``,value:``}]:[],this._render()}_render(){let e=this._op;if(!e){this.innerHTML=``;return}let t=e.op.parameters??[],n=e.method!==`GET`&&e.method!==`HEAD`,r=e.source===`handler`;this.innerHTML=`
106
+ </div>`,this._wire()}_wire(){this.querySelector("[data-open-global]")?.addEventListener("click",()=>C());for(const s of this.querySelectorAll("[data-scope]"))s.addEventListener("change",()=>{s.checked&&(this._scope=s.dataset.scope)});const e=this.querySelector("[data-mode]");e?.addEventListener("change",()=>{this._kind=e.value;const s=this.querySelector("[data-fields]");s&&(s.innerHTML=this._renderFieldsHtml(),this._wireFields())}),this._wireFields(),this.querySelector("[data-submit]")?.addEventListener("click",()=>this._submit());const t=this.querySelector("[data-remove]");t?.addEventListener("click",()=>{E(t.dataset.remove??"")})}_wireFields(){const e=this.querySelector('[data-field="description"]');e?.addEventListener("input",()=>{this._description=e.value});for(const t of this.querySelectorAll("[data-fields] [data-field]"))t.addEventListener("input",()=>{const s=t.dataset.field;s==="status"?this._status=Number(t.value):s==="ms"?this._ms=Number(t.value):s==="message"?this._message=t.value:s==="body"?this._body=t.value:s==="contentType"&&(this._contentType=t.value)})}_submit(){const e=this._op;if(!e)return;const t=this._scope==="exact"?{kind:"exact",method:e.method,path:e.path}:{kind:"path",path:e.path};let s,n=null;const o=this._kind;switch(o){case"http-status":s={kind:o,status:this._status};break;case"business-failure":s={kind:o,message:this._message},N(this._message)||(n={name:"x-mock-business-failure-b64",value:K(this._message)});break;case"custom-body":{let r;try{r=JSON.parse(this._body)}catch{r=this._body}s={kind:o,status:this._status,body:r,contentType:this._contentType};break}case"empty-body":s={kind:o,status:this._status};break;case"malformed-json":s={kind:o,status:this._status};break;case"delay":s={kind:o,ms:this._ms};break;case"hang":s={kind:o};break;case"destroy":s={kind:o};break}this.headerOverride=n,this.dispatchEvent(new CustomEvent("dbh-header-override",{detail:n,bubbles:!0})),(async()=>{const r=this._existing;if(r&&t.kind===r.matcher.kind)try{await E(r.id)}catch{}try{await D(t,s,this._description||void 0)}catch(d){p("error",d instanceof Error?d.message:String(d))}})()}}customElements.define("dbh-inject-error-panel",fe);const ye=(a,i)=>{const e=a.op.requestBody?.content?.["application/json"]?.schema;return e?JSON.stringify(_(e,i),null,2):""};class ve extends HTMLElement{constructor(){super(...arguments);l(this,"_op",null);l(this,"_doc",null);l(this,"extraHeader",null);l(this,"_pvals",{});l(this,"_freeQuery",[]);l(this,"_body","");l(this,"_busy",!1)}setOp(e,t){this._op=e,this._doc=t,this._pvals={};for(const s of e.op.parameters??[])this._pvals[s.name]=String(s.schema?.default??"");this._body=ye(e,t),this._freeQuery=e.source==="handler"?[{key:"",value:""}]:[],this._render()}_render(){const e=this._op;if(!e){this.innerHTML="";return}const t=e.op.parameters??[],s=e.method!=="GET"&&e.method!=="HEAD",n=e.source==="handler";this.innerHTML=`
107
107
  <details class="try-it">
108
108
  <summary>Try it out</summary>
109
- ${t.map(e=>`
109
+ ${t.map(r=>`
110
110
  <div class="field">
111
- <label>${E(e.name)} <small>(${E(e.in)}${e.required?`, required`:``})</small></label>
112
- <input type="text" data-pname="${E(e.name)}" value="${E(this._pvals[e.name]??``)}" />
113
- </div>`).join(``)}
114
- ${r?`
111
+ <label>${h(r.name)} <small>(${h(r.in)}${r.required?", required":""})</small></label>
112
+ <input type="text" data-pname="${h(r.name)}" value="${h(this._pvals[r.name]??"")}" />
113
+ </div>`).join("")}
114
+ ${n?`
115
115
  <div class="field">
116
116
  <label>query params <small>(free-form)</small></label>
117
117
  <div data-free-query>
118
- ${this._freeQuery.map((e,t)=>`
118
+ ${this._freeQuery.map((r,d)=>`
119
119
  <div class="free-q-row" style="display:flex;gap:6px;margin-bottom:4px">
120
- <input type="text" data-fq-key="${t}" placeholder="name" value="${E(e.key)}" style="flex:1" />
121
- <input type="text" data-fq-val="${t}" placeholder="value" value="${E(e.value)}" style="flex:2" />
122
- <button class="btn ghost" type="button" data-fq-rm="${t}" title="Remove">\u2212</button>
123
- </div>`).join(``)}
120
+ <input type="text" data-fq-key="${d}" placeholder="name" value="${h(r.key)}" style="flex:1" />
121
+ <input type="text" data-fq-val="${d}" placeholder="value" value="${h(r.value)}" style="flex:2" />
122
+ <button class="btn ghost" type="button" data-fq-rm="${d}" title="Remove">−</button>
123
+ </div>`).join("")}
124
124
  <button class="btn ghost" type="button" data-fq-add>+ Add query param</button>
125
125
  </div>
126
- </div>`:``}
127
- ${n?`
126
+ </div>`:""}
127
+ ${s?`
128
128
  <div class="field">
129
- <label>body${r?` <small>(free-form)</small>`:``}</label>
130
- <textarea data-body style="width:100%;min-height:100px;font-family:ui-monospace,Menlo,monospace">${E(this._body)}</textarea>
131
- </div>`:``}
132
- <button class="btn primary" type="button" data-send${this._busy?` disabled`:``}>${this._busy?`…`:`Send`}</button>
129
+ <label>body${n?" <small>(free-form)</small>":""}</label>
130
+ <textarea data-body style="width:100%;min-height:100px;font-family:ui-monospace,Menlo,monospace">${h(this._body)}</textarea>
131
+ </div>`:""}
132
+ <button class="btn primary" type="button" data-send${this._busy?" disabled":""}>${this._busy?"…":"Send"}</button>
133
133
  <dbh-response-viewer></dbh-response-viewer>
134
- </details>`;for(let e of this.querySelectorAll(`[data-pname]`))e.addEventListener(`input`,()=>{this._pvals[e.dataset.pname??``]=e.value});for(let e of this.querySelectorAll(`[data-fq-key]`))e.addEventListener(`input`,()=>{let t=Number(e.dataset.fqKey);this._freeQuery[t]&&(this._freeQuery[t].key=e.value)});for(let e of this.querySelectorAll(`[data-fq-val]`))e.addEventListener(`input`,()=>{let t=Number(e.dataset.fqVal);this._freeQuery[t]&&(this._freeQuery[t].value=e.value)});for(let e of this.querySelectorAll(`[data-fq-rm]`))e.addEventListener(`click`,()=>{let t=Number(e.dataset.fqRm);this._freeQuery.splice(t,1),this._freeQuery.length===0&&this._freeQuery.push({key:``,value:``}),this._render()});this.querySelector(`[data-fq-add]`)?.addEventListener(`click`,()=>{this._freeQuery.push({key:``,value:``}),this._render()});let i=this.querySelector(`[data-body]`);i?.addEventListener(`input`,()=>{this._body=i.value}),this.querySelector(`[data-send]`)?.addEventListener(`click`,()=>{this._send()})}_buildUrl(){let e=this._op;if(!e)return``;let t=e.op.parameters??[],n=e.path,r=new URLSearchParams;for(let e of t){let t=this._pvals[e.name];t&&(e.in===`path`?n=n.replace(`{${e.name}}`,encodeURIComponent(t)):e.in===`query`&&r.set(e.name,t))}for(let e of this._freeQuery)e.key&&r.append(e.key,e.value);let i=r.toString();return i?`${n}?${i}`:n}async _send(){let e=this._op;if(!e)return;this._busy=!0;let t=this.querySelector(`dbh-response-viewer`);t?.setResult(null);let n=this.querySelector(`[data-send]`);n&&(n.disabled=!0,n.textContent=`…`);let r=performance.now();try{let n={};for(let t of e.op.parameters??[])t.in===`header`&&this._pvals[t.name]&&(n[t.name]=this._pvals[t.name]);this.extraHeader&&(n[this.extraHeader.name]=this.extraHeader.value);let i={method:e.method,headers:n};e.method!==`GET`&&e.method!==`HEAD`&&this._body&&(n[`content-type`]=n[`content-type`]??`application/json`,i.body=this._body);let a=await fetch(this._buildUrl(),i),o=await a.text(),s={status:a.status,headers:{},body:o,elapsedMs:Math.round(performance.now()-r)};a.headers.forEach((e,t)=>{s.headers[t]=e}),t?.setResult(s)}catch(e){t?.setResult({error:e instanceof Error?e.message:String(e)})}finally{this._busy=!1,n&&(n.disabled=!1,n.textContent=`Send`)}}};customElements.define(`dbh-try-it-out`,se);var Y=(e,t)=>{if(!e.methods||e.methods.length===0)return!0;let n=t.toUpperCase();return e.methods.some(e=>e.toUpperCase()===n)},ce=class extends HTMLElement{constructor(...e){super(...e),O(this,`_spec`,void 0),O(this,`_inject`,void 0),O(this,`_try`,void 0)}connectedCallback(){this.childElementCount===0&&(this.innerHTML=`
134
+ </details>`;for(const r of this.querySelectorAll("[data-pname]"))r.addEventListener("input",()=>{this._pvals[r.dataset.pname??""]=r.value});for(const r of this.querySelectorAll("[data-fq-key]"))r.addEventListener("input",()=>{const d=Number(r.dataset.fqKey);this._freeQuery[d]&&(this._freeQuery[d].key=r.value)});for(const r of this.querySelectorAll("[data-fq-val]"))r.addEventListener("input",()=>{const d=Number(r.dataset.fqVal);this._freeQuery[d]&&(this._freeQuery[d].value=r.value)});for(const r of this.querySelectorAll("[data-fq-rm]"))r.addEventListener("click",()=>{const d=Number(r.dataset.fqRm);this._freeQuery.splice(d,1),this._freeQuery.length===0&&this._freeQuery.push({key:"",value:""}),this._render()});this.querySelector("[data-fq-add]")?.addEventListener("click",()=>{this._freeQuery.push({key:"",value:""}),this._render()});const o=this.querySelector("[data-body]");o?.addEventListener("input",()=>{this._body=o.value}),this.querySelector("[data-send]")?.addEventListener("click",()=>{this._send()})}_buildUrl(){const e=this._op;if(!e)return"";const t=e.op.parameters??[];let s=e.path;const n=new URLSearchParams;for(const r of t){const d=this._pvals[r.name];d&&(r.in==="path"?s=s.replace(`{${r.name}}`,encodeURIComponent(d)):r.in==="query"&&n.set(r.name,d))}for(const r of this._freeQuery)r.key&&n.append(r.key,r.value);const o=n.toString();return o?`${s}?${o}`:s}async _send(){const e=this._op;if(!e)return;this._busy=!0;const t=this.querySelector("dbh-response-viewer");t?.setResult(null);const s=this.querySelector("[data-send]");s&&(s.disabled=!0,s.textContent="…");const n=performance.now();try{const o={};for(const y of e.op.parameters??[])y.in==="header"&&this._pvals[y.name]&&(o[y.name]=this._pvals[y.name]);this.extraHeader&&(o[this.extraHeader.name]=this.extraHeader.value);const r={method:e.method,headers:o};e.method!=="GET"&&e.method!=="HEAD"&&this._body&&(o["content-type"]=o["content-type"]??"application/json",r.body=this._body);const d=await fetch(this._buildUrl(),r),b=await d.text(),u={status:d.status,headers:{},body:b,elapsedMs:Math.round(performance.now()-n)};d.headers.forEach((y,S)=>{u.headers[S]=y}),t?.setResult(u)}catch(o){t?.setResult({error:o instanceof Error?o.message:String(o)})}finally{this._busy=!1,s&&(s.disabled=!1,s.textContent="Send")}}}customElements.define("dbh-try-it-out",ve);const I=(a,i)=>{if(!a.methods||a.methods.length===0)return!0;const e=i.toUpperCase();return a.methods.some(t=>t.toUpperCase()===e)};class _e extends HTMLElement{constructor(){super(...arguments);l(this,"_spec");l(this,"_inject");l(this,"_try")}connectedCallback(){this.childElementCount===0&&(this.innerHTML=`
135
135
  <div class="op-card">
136
136
  <dbh-spec-zone></dbh-spec-zone>
137
137
  <dbh-inject-error-panel></dbh-inject-error-panel>
138
138
  <dbh-try-it-out></dbh-try-it-out>
139
- </div>`,this._spec=this.querySelector(`dbh-spec-zone`),this._inject=this.querySelector(`dbh-inject-error-panel`),this._try=this.querySelector(`dbh-try-it-out`),this._inject.addEventListener(`dbh-header-override`,e=>{this._try.extraHeader=e.detail}))}setData(e){this._spec||this.connectedCallback();let{op:t,doc:n,overrides:r}=e,i=r.find(e=>e.matcher.kind===`exact`&&e.matcher.method===t.method&&e.matcher.path===t.path),a=r.some(e=>e.matcher.kind===`global`&&Y(e.matcher,t.method));this._spec.setOp(t,n),this._inject.setOp({op:t,existing:i,globalActive:a}),this._try.setOp(t,n)}};customElements.define(`dbh-operation-card`,ce);var le=class extends HTMLElement{constructor(...e){super(...e),O(this,`_label`,``),O(this,`_ops`,[]),O(this,`_doc`,null),O(this,`_overrides`,[]),O(this,`_expandedKey`,null),O(this,`_focusedKey`,null),O(this,`_open`,!0)}setData(e){this._label=e.label,this._ops=e.ops,this._doc=e.doc,this._overrides=e.overrides,this._expandedKey=e.expandedKey,this._focusedKey=e.focusedKey,this._render()}_render(){let e=this._ops.reduce((e,t)=>e+ +!!this._overrides.some(e=>e.matcher.kind===`exact`&&e.matcher.method===t.method&&e.matcher.path===t.path),0);this.innerHTML=`
140
- <div class="group${this._open?` open`:``}">
139
+ </div>`,this._spec=this.querySelector("dbh-spec-zone"),this._inject=this.querySelector("dbh-inject-error-panel"),this._try=this.querySelector("dbh-try-it-out"),this._inject.addEventListener("dbh-header-override",e=>{this._try.extraHeader=e.detail}))}setData(e){this._spec||this.connectedCallback();const{op:t,doc:s,overrides:n}=e,o=n.find(d=>d.matcher.kind==="exact"&&d.matcher.method===t.method&&d.matcher.path===t.path),r=n.some(d=>d.matcher.kind==="global"&&I(d.matcher,t.method));this._spec.setOp(t,s),this._inject.setOp({op:t,existing:o,globalActive:r}),this._try.setOp(t,s)}}customElements.define("dbh-operation-card",_e);class ge extends HTMLElement{constructor(){super(...arguments);l(this,"_label","");l(this,"_ops",[]);l(this,"_doc",null);l(this,"_overrides",[]);l(this,"_expandedKey",null);l(this,"_focusedKey",null);l(this,"_open",!0)}setData(e){this._label=e.label,this._ops=e.ops,this._doc=e.doc,this._overrides=e.overrides,this._expandedKey=e.expandedKey,this._focusedKey=e.focusedKey,this._render()}_render(){const e=this._ops.reduce((s,n)=>s+(this._overrides.some(o=>o.matcher.kind==="exact"&&o.matcher.method===n.method&&o.matcher.path===n.path)?1:0),0);this.innerHTML=`
140
+ <div class="group${this._open?" open":""}">
141
141
  <div class="group-header" data-header>
142
- <h3>${E(this._label)}</h3>
143
- <span class="count">${this._ops.length} ops${e?` · ${e} active`:``}</span>
144
- <span>${this._open?`−`:`+`}</span>
142
+ <h3>${h(this._label)}</h3>
143
+ <span class="count">${this._ops.length} ops${e?` · ${e} active`:""}</span>
144
+ <span>${this._open?"−":"+"}</span>
145
145
  </div>
146
146
  <div class="group-body" data-body></div>
147
- </div>`,this.querySelector(`[data-header]`)?.addEventListener(`click`,()=>{this._open=!this._open,this._render()});let t=this.querySelector(`[data-body]`);if(t)for(let e of this._ops){let n=`${e.method} ${e.path}`,r=this._overrides.find(t=>t.matcher.kind===`exact`&&t.matcher.method===e.method&&t.matcher.path===e.path),i=document.createElement(`dbh-operation-row`);if(i.setRow({op:e,override:r,expanded:this._expandedKey===n,focused:this._focusedKey===n}),t.appendChild(i),this._expandedKey===n){let n=document.createElement(`dbh-operation-card`);t.appendChild(n),n.setData({op:e,doc:this._doc,overrides:this._overrides})}}}};customElements.define(`dbh-tag-group`,le);var ue=e=>{if(e.matcher.kind===`exact`)return`${e.matcher.method} ${e.matcher.path}`;if(e.matcher.kind===`path`)return`* ${e.matcher.path}`;let t=e.matcher.methods;return t&&t.length>0?`${t.join(`|`)} (global)`:`* (global)`},X=e=>e.matcher.kind===`global`?0:e.matcher.kind===`path`?1:2,de=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){let e=u.get(),n=[...e.overrides].sort((e,t)=>X(e)-X(t));this.innerHTML=`
148
- <aside class="right-panel${e.rightPanelOpen?` open`:``}">
147
+ </div>`,this.querySelector("[data-header]")?.addEventListener("click",()=>{this._open=!this._open,this._render()});const t=this.querySelector("[data-body]");if(t)for(const s of this._ops){const n=`${s.method} ${s.path}`,o=this._overrides.find(d=>d.matcher.kind==="exact"&&d.matcher.method===s.method&&d.matcher.path===s.path),r=document.createElement("dbh-operation-row");if(r.setRow({op:s,override:o,expanded:this._expandedKey===n,focused:this._focusedKey===n}),t.appendChild(r),this._expandedKey===n){const d=document.createElement("dbh-operation-card");t.appendChild(d),d.setData({op:s,doc:this._doc,overrides:this._overrides})}}}}customElements.define("dbh-tag-group",ge);const ke=a=>{if(a.matcher.kind==="exact")return`${a.matcher.method} ${a.matcher.path}`;if(a.matcher.kind==="path")return`* ${a.matcher.path}`;const i=a.matcher.methods;return i&&i.length>0?`${i.join("|")} (global)`:"* (global)"},A=a=>a.matcher.kind==="global"?0:a.matcher.kind==="path"?1:2;class $e extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get(),t=[...e.overrides].sort((s,n)=>A(s)-A(n));this.innerHTML=`
148
+ <aside class="right-panel${e.rightPanelOpen?" open":""}">
149
149
  <header>
150
150
  <h2>Active overrides (${e.overrides.length})</h2>
151
151
  <button class="btn ghost" type="button" data-close>×</button>
152
152
  </header>
153
153
  <div class="list">
154
- ${e.overrides.length===0?`<p style="padding:12px;color:#57606a">No active overrides</p>`:n.map(e=>`
155
- <div class="override-item${e.matcher.kind===`global`?` is-global`:``}" data-jump="${E(e.id)}">
156
- <span class="dot" style="background:${P(e.mode.kind)}"></span>
157
- ${e.matcher.kind===`global`?`<span class="scope-badge scope-global">GLOBAL</span>`:``}
154
+ ${e.overrides.length===0?'<p style="padding:12px;color:#57606a">No active overrides</p>':t.map(s=>`
155
+ <div class="override-item${s.matcher.kind==="global"?" is-global":""}" data-jump="${h(s.id)}">
156
+ <span class="dot" style="background:${M(s.mode.kind)}"></span>
157
+ ${s.matcher.kind==="global"?'<span class="scope-badge scope-global">GLOBAL</span>':""}
158
158
  <div class="info">
159
- <div class="route">${E(ue(e))}</div>
160
- <small>${E(e.mode.kind)}${e.description?` — ${E(e.description)}`:``}</small>
159
+ <div class="route">${h(ke(s))}</div>
160
+ <small>${h(s.mode.kind)}${s.description?` — ${h(s.description)}`:""}</small>
161
161
  </div>
162
- <button class="btn danger" type="button" data-remove="${E(e.id)}">Remove</button>
163
- </div>`).join(``)}
162
+ <button class="btn danger" type="button" data-remove="${h(s.id)}">Remove</button>
163
+ </div>`).join("")}
164
164
  </div>
165
- </aside>`,this.querySelector(`[data-close]`)?.addEventListener(`click`,()=>u.set({rightPanelOpen:!1}));for(let e of this.querySelectorAll(`[data-jump]`))e.addEventListener(`click`,()=>{let n=e.dataset.jump??``,r=u.get().overrides.find(e=>e.id===n);if(!r)return;if(r.matcher.kind===`global`){T();return}if(r.matcher.kind!==`exact`){u.set({rightPanelOpen:!1});return}let i=t(r.matcher.method,r.matcher.path);u.set({expandedKey:i,focusedKey:i,rightPanelOpen:!1}),setTimeout(()=>{let e=document.querySelector(`[data-key="${i.replace(/"/g,`\\"`)}"]`);e&&`scrollIntoView`in e&&e.scrollIntoView({behavior:`smooth`,block:`center`})},50)});for(let e of this.querySelectorAll(`[data-remove]`))e.addEventListener(`click`,t=>{t.stopPropagation(),b(e.dataset.remove??``)})}};customElements.define(`dbh-active-overrides-panel`,de);var fe=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){let e=u.get().overrides.length;if(e===0){this.innerHTML=``;return}this.innerHTML=`<button class="pill-float" type="button">${e} Active overrides</button>`,this.querySelector(`button`)?.addEventListener(`click`,()=>u.set({rightPanelOpen:!0}))}};customElements.define(`dbh-floating-pill`,fe);var pe=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0)}connectedCallback(){this._render(te()),this._unsub=m(e=>this._render(e))}disconnectedCallback(){this._unsub?.()}_render(e){this.innerHTML=`<div class="toast-stack">`+e.map(e=>`<div class="toast ${E(e.kind)}" data-id="${e.id}">${E(e.message)}</div>`).join(``)+`</div>`;for(let e of this.querySelectorAll(`.toast`))e.addEventListener(`click`,()=>{let t=Number(e.dataset.id);Number.isFinite(t)&&ee(t)})}};customElements.define(`dbh-toasts`,pe);var me=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){if(!u.get().helpOpen){this.innerHTML=``;return}this.innerHTML=`
165
+ </aside>`,this.querySelector("[data-close]")?.addEventListener("click",()=>c.set({rightPanelOpen:!1}));for(const s of this.querySelectorAll("[data-jump]"))s.addEventListener("click",()=>{const n=s.dataset.jump??"",o=c.get().overrides.find(d=>d.id===n);if(!o)return;if(o.matcher.kind==="global"){C();return}if(o.matcher.kind!=="exact"){c.set({rightPanelOpen:!1});return}const r=L(o.matcher.method,o.matcher.path);c.set({expandedKey:r,focusedKey:r,rightPanelOpen:!1}),setTimeout(()=>{const d=document.querySelector(`[data-key="${r.replace(/"/g,'\\"')}"]`);d&&"scrollIntoView"in d&&d.scrollIntoView({behavior:"smooth",block:"center"})},50)});for(const s of this.querySelectorAll("[data-remove]"))s.addEventListener("click",n=>{n.stopPropagation(),E(s.dataset.remove??"")})}}customElements.define("dbh-active-overrides-panel",$e);class Ee extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const t=c.get().overrides.length;if(t===0){this.innerHTML="";return}this.innerHTML=`<button class="pill-float" type="button">${t} Active overrides</button>`,this.querySelector("button")?.addEventListener("click",()=>c.set({rightPanelOpen:!0}))}}customElements.define("dbh-floating-pill",Ee);class we extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(Z()),this._unsub=V(e=>this._render(e))}disconnectedCallback(){this._unsub?.()}_render(e){this.innerHTML='<div class="toast-stack">'+e.map(t=>`<div class="toast ${h(t.kind)}" data-id="${t.id}">${h(t.message)}</div>`).join("")+"</div>";for(const t of this.querySelectorAll(".toast"))t.addEventListener("click",()=>{const s=Number(t.dataset.id);Number.isFinite(s)&&X(s)})}}customElements.define("dbh-toasts",we);class Se extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){if(!c.get().helpOpen){this.innerHTML="";return}this.innerHTML=`
166
166
  <div class="modal-backdrop">
167
167
  <div class="modal">
168
168
  <h2>Keyboard shortcuts</h2>
@@ -179,10 +179,10 @@
179
179
  <button class="btn" type="button" data-ok>OK</button>
180
180
  </div>
181
181
  </div>
182
- </div>`;let e=()=>u.set({helpOpen:!1});this.querySelector(`.modal-backdrop`)?.addEventListener(`click`,e),this.querySelector(`.modal`)?.addEventListener(`click`,e=>e.stopPropagation()),this.querySelector(`[data-ok]`)?.addEventListener(`click`,e)}};customElements.define(`dbh-help-modal`,me);var he=[`http-status`,`business-failure`,`custom-body`,`empty-body`,`malformed-json`,`delay`,`hang`,`destroy`],Z=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],ge=class extends HTMLElement{constructor(...e){super(...e),O(this,`_unsub`,void 0),O(this,`_kind`,`http-status`),O(this,`_status`,503),O(this,`_message`,``),O(this,`_body`,`{}`),O(this,`_contentType`,`application/json`),O(this,`_ms`,1e3),O(this,`_description`,``),O(this,`_methods`,new Set(Z)),O(this,`_lastExistingId`,void 0)}connectedCallback(){this._render(),this._unsub=u.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_globals(){return u.get().overrides.filter(e=>e.matcher.kind===`global`)}_syncFromExisting(e,t){if(t&&!(e&&e.id===this._lastExistingId))if(this._lastExistingId=e?.id,e){this._kind=e.mode.kind,this._description=e.description??``;let t=e.matcher.kind===`global`?e.matcher.methods:void 0;this._methods=new Set(t&&t.length>0?t.map(e=>e.toUpperCase()):Z);let n=e.mode;n.kind===`http-status`?this._status=n.status:n.kind===`business-failure`?this._message=n.message:n.kind===`custom-body`?(this._status=n.status??200,this._body=JSON.stringify(n.body,null,2),this._contentType=n.contentType??`application/json`):n.kind===`empty-body`?this._status=n.status??204:n.kind===`delay`&&(this._ms=n.ms)}else this._kind=`http-status`,this._status=503,this._message=``,this._body=`{}`,this._contentType=`application/json`,this._ms=1e3,this._description=``,this._methods=new Set(Z)}_renderFieldsHtml(){let e=this._kind;return e===`http-status`||e===`empty-body`||e===`malformed-json`?`<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>`:e===`business-failure`?`<div class="row"><label>Message</label><input type="text" data-field="message" value="${E(this._message)}" /></div>`:e===`custom-body`?`
182
+ </div>`;const t=()=>c.set({helpOpen:!1});this.querySelector(".modal-backdrop")?.addEventListener("click",t),this.querySelector(".modal")?.addEventListener("click",s=>s.stopPropagation()),this.querySelector("[data-ok]")?.addEventListener("click",t)}}customElements.define("dbh-help-modal",Se);const xe=["http-status","business-failure","custom-body","empty-body","malformed-json","delay","hang","destroy"],$=["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"];class Te extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub");l(this,"_kind","http-status");l(this,"_status",503);l(this,"_message","");l(this,"_body","{}");l(this,"_contentType","application/json");l(this,"_ms",1e3);l(this,"_description","");l(this,"_methods",new Set($));l(this,"_lastExistingId")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_globals(){return c.get().overrides.filter(e=>e.matcher.kind==="global")}_syncFromExisting(e,t){if(t&&!(e&&e.id===this._lastExistingId))if(this._lastExistingId=e?.id,e){this._kind=e.mode.kind,this._description=e.description??"";const s=e.matcher.kind==="global"?e.matcher.methods:void 0;this._methods=new Set(s&&s.length>0?s.map(o=>o.toUpperCase()):$);const n=e.mode;n.kind==="http-status"?this._status=n.status:n.kind==="business-failure"?this._message=n.message:n.kind==="custom-body"?(this._status=n.status??200,this._body=JSON.stringify(n.body,null,2),this._contentType=n.contentType??"application/json"):n.kind==="empty-body"?this._status=n.status??204:n.kind==="delay"&&(this._ms=n.ms)}else this._kind="http-status",this._status=503,this._message="",this._body="{}",this._contentType="application/json",this._ms=1e3,this._description="",this._methods=new Set($)}_renderFieldsHtml(){const e=this._kind;return e==="http-status"||e==="empty-body"||e==="malformed-json"?`<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>`:e==="business-failure"?`<div class="row"><label>Message</label><input type="text" data-field="message" value="${h(this._message)}" /></div>`:e==="custom-body"?`
183
183
  <div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>
184
- <div class="row"><label>Content-Type</label><input type="text" data-field="contentType" value="${E(this._contentType)}" /></div>
185
- <div class="row"><label>Body</label><textarea data-field="body">${E(this._body)}</textarea></div>`:e===`delay`?`<div class="row"><label>Delay (ms)</label><input type="number" data-field="ms" value="${this._ms}" /></div>`:``}_render(){let e=u.get().globalOpen;if(!e){this.innerHTML=``,this._lastExistingId=void 0;return}let t=this._globals(),n=t[0];this._syncFromExisting(n,e),this.innerHTML=`
184
+ <div class="row"><label>Content-Type</label><input type="text" data-field="contentType" value="${h(this._contentType)}" /></div>
185
+ <div class="row"><label>Body</label><textarea data-field="body">${h(this._body)}</textarea></div>`:e==="delay"?`<div class="row"><label>Delay (ms)</label><input type="number" data-field="ms" value="${this._ms}" /></div>`:""}_render(){const e=c.get().globalOpen;if(!e){this.innerHTML="",this._lastExistingId=void 0;return}const t=this._globals(),s=t[0];this._syncFromExisting(s,e),this.innerHTML=`
186
186
  <div class="modal-backdrop">
187
187
  <div class="modal global-override-modal">
188
188
  <header class="global-override-header">
@@ -192,42 +192,42 @@
192
192
  <p class="global-override-desc">Applies to every route on this server. Use to simulate the backend being down, slow, etc.</p>
193
193
  <div class="row">
194
194
  <label>Mode</label>
195
- <select data-mode>${he.map(e=>`<option value="${e}"${e===this._kind?` selected`:``}>${e}</option>`).join(``)}</select>
195
+ <select data-mode>${xe.map(n=>`<option value="${n}"${n===this._kind?" selected":""}>${n}</option>`).join("")}</select>
196
196
  </div>
197
197
  <div class="row global-override-methods">
198
198
  <label>Methods</label>
199
199
  <div class="method-checkboxes">
200
- ${Z.map(e=>`
200
+ ${$.map(n=>`
201
201
  <label class="method-checkbox">
202
- <input type="checkbox" data-method="${e}"${this._methods.has(e)?` checked`:``} />
203
- <span>${e}</span>
204
- </label>`).join(``)}
202
+ <input type="checkbox" data-method="${n}"${this._methods.has(n)?" checked":""} />
203
+ <span>${n}</span>
204
+ </label>`).join("")}
205
205
  </div>
206
206
  </div>
207
207
  <div data-fields>${this._renderFieldsHtml()}</div>
208
208
  <div class="row">
209
209
  <label>Description</label>
210
- <input type="text" data-field="description" value="${E(this._description)}" />
210
+ <input type="text" data-field="description" value="${h(this._description)}" />
211
211
  </div>
212
212
  <div class="actions">
213
- <button type="button" class="btn primary" data-submit>${n?`Update`:`Apply`}</button>
214
- ${n?`<button type="button" class="btn danger" data-remove="${E(n.id)}">Remove</button>`:``}
213
+ <button type="button" class="btn primary" data-submit>${s?"Update":"Apply"}</button>
214
+ ${s?`<button type="button" class="btn danger" data-remove="${h(s.id)}">Remove</button>`:""}
215
215
  </div>
216
216
  ${t.length>0?`
217
217
  <div class="global-override-list">
218
218
  <h3>Active global overrides (${t.length})</h3>
219
- ${t.map(e=>{let t=e.matcher.kind===`global`?e.matcher.methods:void 0,n=t&&t.length>0?`${t.join(`|`)} (global)`:`* (global)`;return`
219
+ ${t.map(n=>{const o=n.matcher.kind==="global"?n.matcher.methods:void 0,r=o&&o.length>0?`${o.join("|")} (global)`:"* (global)";return`
220
220
  <div class="override-item">
221
- <span class="dot" style="background:${P(e.mode.kind)}"></span>
221
+ <span class="dot" style="background:${M(n.mode.kind)}"></span>
222
222
  <div class="info">
223
- <div class="route">${E(n)}</div>
224
- <small>${E(e.mode.kind)}${e.description?` — ${E(e.description)}`:``}</small>
223
+ <div class="route">${h(r)}</div>
224
+ <small>${h(n.mode.kind)}${n.description?` — ${h(n.description)}`:""}</small>
225
225
  </div>
226
- <button type="button" class="btn danger" data-remove="${E(e.id)}">Remove</button>
227
- </div>`}).join(``)}
228
- </div>`:``}
226
+ <button type="button" class="btn danger" data-remove="${h(n.id)}">Remove</button>
227
+ </div>`}).join("")}
228
+ </div>`:""}
229
229
  </div>
230
- </div>`,this._wire()}_wire(){let e=()=>u.set({globalOpen:!1});this.querySelector(`.modal-backdrop`)?.addEventListener(`click`,e),this.querySelector(`.modal`)?.addEventListener(`click`,e=>e.stopPropagation()),this.querySelector(`[data-close]`)?.addEventListener(`click`,e);let t=this.querySelector(`[data-mode]`);t?.addEventListener(`change`,()=>{this._kind=t.value;let e=this.querySelector(`[data-fields]`);e&&(e.innerHTML=this._renderFieldsHtml(),this._wireFields())}),this._wireFields();for(let e of this.querySelectorAll(`[data-method]`))e.addEventListener(`change`,()=>{let t=e.dataset.method??``;e.checked?this._methods.add(t):this._methods.delete(t)});this.querySelector(`[data-submit]`)?.addEventListener(`click`,()=>this._submit());for(let e of this.querySelectorAll(`[data-remove]`))e.addEventListener(`click`,()=>{b(e.dataset.remove??``)})}_wireFields(){let e=this.querySelector(`[data-field="description"]`);e?.addEventListener(`input`,()=>{this._description=e.value});for(let e of this.querySelectorAll(`[data-fields] [data-field]`))e.addEventListener(`input`,()=>{let t=e.dataset.field;t===`status`?this._status=Number(e.value):t===`ms`?this._ms=Number(e.value):t===`message`?this._message=e.value:t===`body`?this._body=e.value:t===`contentType`&&(this._contentType=e.value)})}_submit(){let e,t=this._kind;switch(t){case`http-status`:e={kind:t,status:this._status};break;case`business-failure`:e={kind:t,message:this._message},K(this._message)||G(this._message);break;case`custom-body`:{let n;try{n=JSON.parse(this._body)}catch{n=this._body}e={kind:t,status:this._status,body:n,contentType:this._contentType};break}case`empty-body`:e={kind:t,status:this._status};break;case`malformed-json`:e={kind:t,status:this._status};break;case`delay`:e={kind:t,ms:this._ms};break;case`hang`:e={kind:t};break;case`destroy`:e={kind:t};break}let n=Z.length,r=Z.filter(e=>this._methods.has(e));if(r.length===0){g(`error`,`Select at least one HTTP method.`);return}let i=r.length===n?{kind:`global`}:{kind:`global`,methods:r};(async()=>{let t=this._globals()[0];if(t)try{await b(t.id)}catch{}try{await y(i,e,this._description||void 0)}catch(e){g(`error`,e instanceof Error?e.message:String(e))}})()}};customElements.define(`dbh-global-override-panel`,ge);var _e=[`GET`,`POST`,`PUT`,`DELETE`,`PATCH`],Q=class extends HTMLElement{constructor(...e){super(...e),O(this,`_filter`,void 0),O(this,`_main`,void 0),O(this,`_unsub`,void 0),O(this,`_debounce`,void 0),O(this,`_debouncedSearch`,``),O(this,`_onKeyDown`,e=>this._handleKey(e))}connectedCallback(){this.innerHTML=`
230
+ </div>`,this._wire()}_wire(){const e=()=>c.set({globalOpen:!1});this.querySelector(".modal-backdrop")?.addEventListener("click",e),this.querySelector(".modal")?.addEventListener("click",s=>s.stopPropagation()),this.querySelector("[data-close]")?.addEventListener("click",e);const t=this.querySelector("[data-mode]");t?.addEventListener("change",()=>{this._kind=t.value;const s=this.querySelector("[data-fields]");s&&(s.innerHTML=this._renderFieldsHtml(),this._wireFields())}),this._wireFields();for(const s of this.querySelectorAll("[data-method]"))s.addEventListener("change",()=>{const n=s.dataset.method??"";s.checked?this._methods.add(n):this._methods.delete(n)});this.querySelector("[data-submit]")?.addEventListener("click",()=>this._submit());for(const s of this.querySelectorAll("[data-remove]"))s.addEventListener("click",()=>{E(s.dataset.remove??"")})}_wireFields(){const e=this.querySelector('[data-field="description"]');e?.addEventListener("input",()=>{this._description=e.value});for(const t of this.querySelectorAll("[data-fields] [data-field]"))t.addEventListener("input",()=>{const s=t.dataset.field;s==="status"?this._status=Number(t.value):s==="ms"?this._ms=Number(t.value):s==="message"?this._message=t.value:s==="body"?this._body=t.value:s==="contentType"&&(this._contentType=t.value)})}_submit(){let e;const t=this._kind;switch(t){case"http-status":e={kind:t,status:this._status};break;case"business-failure":e={kind:t,message:this._message},N(this._message)||K(this._message);break;case"custom-body":{let r;try{r=JSON.parse(this._body)}catch{r=this._body}e={kind:t,status:this._status,body:r,contentType:this._contentType};break}case"empty-body":e={kind:t,status:this._status};break;case"malformed-json":e={kind:t,status:this._status};break;case"delay":e={kind:t,ms:this._ms};break;case"hang":e={kind:t};break;case"destroy":e={kind:t};break}const s=$.length,n=$.filter(r=>this._methods.has(r));if(n.length===0){p("error","Select at least one HTTP method.");return}const o=n.length===s?{kind:"global"}:{kind:"global",methods:n};(async()=>{const r=this._globals()[0];if(r)try{await E(r.id)}catch{}try{await D(o,e,this._description||void 0)}catch(d){p("error",d instanceof Error?d.message:String(d))}})()}}customElements.define("dbh-global-override-panel",Te);const Oe=["GET","POST","PUT","DELETE","PATCH"];class Le extends HTMLElement{constructor(){super(...arguments);l(this,"_filter");l(this,"_main");l(this,"_unsub");l(this,"_debounce");l(this,"_debouncedSearch","");l(this,"_onKeyDown",e=>this._handleKey(e))}connectedCallback(){this.innerHTML=`
231
231
  <div class="app">
232
232
  <dbh-top-bar></dbh-top-bar>
233
233
  <dbh-filter-bar></dbh-filter-bar>
@@ -237,4 +237,4 @@
237
237
  <dbh-toasts></dbh-toasts>
238
238
  <dbh-help-modal></dbh-help-modal>
239
239
  <dbh-global-override-panel></dbh-global-override-panel>
240
- </div>`,this._filter=this.querySelector(`dbh-filter-bar`),this._main=this.querySelector(`[data-main]`),this._unsub=u.subscribe(()=>this._onStateChange()),window.addEventListener(`keydown`,this._onKeyDown),(async()=>{let[e,t]=await Promise.all([l.listRoutes(),l.fetchOpenApi()]);u.set({routes:e,doc:t??null,loading:!1}),await _(),await v(),this._renderList()})()}disconnectedCallback(){this._unsub?.(),window.removeEventListener(`keydown`,this._onKeyDown),this._debounce&&clearTimeout(this._debounce)}_onStateChange(){u.get().search===this._debouncedSearch?this._renderList():(this._debounce&&clearTimeout(this._debounce),this._debounce=setTimeout(()=>{this._debouncedSearch=u.get().search,this._renderList()},150))}_filtered(){let e=u.get(),t=r(e.routes,e.doc),n=this._debouncedSearch.trim().toLowerCase(),i=e.methods.size>0?e.methods:new Set(_e);return t.filter(t=>!(!i.has(t.method)||n&&!t.path.toLowerCase().includes(n)&&!t.op.summary?.toLowerCase().includes(n)||e.activeOnly&&!e.overrides.some(e=>e.matcher.kind===`exact`&&e.matcher.method===t.method&&e.matcher.path===t.path||e.matcher.kind===`path`&&e.matcher.path===t.path||e.matcher.kind===`global`&&Y(e.matcher,t.method))))}_renderList(){let e=u.get(),t=i(this._filtered(),a(e.overrides));if(this._main.innerHTML=``,t.length===0){let e=document.createElement(`p`);e.style.color=`#57606a`,e.textContent=`No operations match these filters.`,this._main.appendChild(e);return}for(let n of t){let t=document.createElement(`dbh-tag-group`);this._main.appendChild(t),t.setData({label:n.label,ops:n.ops,doc:e.doc,overrides:e.overrides,expandedKey:e.expandedKey,focusedKey:e.focusedKey})}}_handleKey(e){let n=e.target,r=!!n&&(n.tagName===`INPUT`||n.tagName===`TEXTAREA`||n.tagName===`SELECT`),i=this._filter?.searchInput,a=u.get();if(e.key===`/`&&!r)e.preventDefault(),i?.focus();else if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`k`)e.preventDefault(),i?.focus();else if(e.key===`?`&&!r)e.preventDefault(),u.set({helpOpen:!0});else if(e.key===`Escape`)a.helpOpen?u.set({helpOpen:!1}):a.globalOpen?u.set({globalOpen:!1}):a.rightPanelOpen?u.set({rightPanelOpen:!1}):a.expandedKey?u.set({expandedKey:null}):i?.blur();else if((e.key===`ArrowDown`||e.key===`ArrowUp`)&&document.activeElement===i){e.preventDefault();let n=this._filtered().map(e=>t(e.method,e.path));if(n.length===0)return;let r=a.focusedKey?n.indexOf(a.focusedKey):-1,i=e.key===`ArrowDown`?Math.min(n.length-1,r+1):Math.max(0,r-1);u.set({focusedKey:n[i]??null})}else e.key===`Enter`&&document.activeElement===i&&a.focusedKey&&(e.preventDefault(),u.set({expandedKey:a.expandedKey===a.focusedKey?null:a.focusedKey}))}};customElements.define(`dbh-app`,Q);var $=document.getElementById(`root`);if($){let e=document.createElement(`dbh-app`);$.appendChild(e)}
240
+ </div>`,this._filter=this.querySelector("dbh-filter-bar"),this._main=this.querySelector("[data-main]"),this._unsub=c.subscribe(()=>this._onStateChange()),window.addEventListener("keydown",this._onKeyDown),(async()=>{const[e,t]=await Promise.all([f.listRoutes(),f.fetchOpenApi()]);c.set({routes:e,doc:t??null,loading:!1}),await w(),await q(),this._renderList()})()}disconnectedCallback(){this._unsub?.(),window.removeEventListener("keydown",this._onKeyDown),this._debounce&&clearTimeout(this._debounce)}_onStateChange(){c.get().search!==this._debouncedSearch?(this._debounce&&clearTimeout(this._debounce),this._debounce=setTimeout(()=>{this._debouncedSearch=c.get().search,this._renderList()},150)):this._renderList()}_filtered(){const e=c.get(),t=G(e.routes,e.doc),s=this._debouncedSearch.trim().toLowerCase(),n=e.methods.size>0?e.methods:new Set(Oe);return t.filter(o=>!(!n.has(o.method)||s&&!o.path.toLowerCase().includes(s)&&!o.op.summary?.toLowerCase().includes(s)||e.activeOnly&&!e.overrides.some(d=>d.matcher.kind==="exact"&&d.matcher.method===o.method&&d.matcher.path===o.path||d.matcher.kind==="path"&&d.matcher.path===o.path||d.matcher.kind==="global"&&I(d.matcher,o.method))))}_renderList(){const e=c.get(),t=this._filtered(),s=Q(t,z(e.overrides));if(this._main.innerHTML="",s.length===0){const n=document.createElement("p");n.style.color="#57606a",n.textContent="No operations match these filters.",this._main.appendChild(n);return}for(const n of s){const o=document.createElement("dbh-tag-group");this._main.appendChild(o),o.setData({label:n.label,ops:n.ops,doc:e.doc,overrides:e.overrides,expandedKey:e.expandedKey,focusedKey:e.focusedKey})}}_handleKey(e){const t=e.target,s=!!t&&(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.tagName==="SELECT"),n=this._filter?.searchInput,o=c.get();if(e.key==="/"&&!s)e.preventDefault(),n?.focus();else if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==="k")e.preventDefault(),n?.focus();else if(e.key==="?"&&!s)e.preventDefault(),c.set({helpOpen:!0});else if(e.key==="Escape")o.helpOpen?c.set({helpOpen:!1}):o.globalOpen?c.set({globalOpen:!1}):o.rightPanelOpen?c.set({rightPanelOpen:!1}):o.expandedKey?c.set({expandedKey:null}):n?.blur();else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&document.activeElement===n){e.preventDefault();const r=this._filtered().map(u=>L(u.method,u.path));if(r.length===0)return;const d=o.focusedKey?r.indexOf(o.focusedKey):-1,b=e.key==="ArrowDown"?Math.min(r.length-1,d+1):Math.max(0,d-1);c.set({focusedKey:r[b]??null})}else e.key==="Enter"&&document.activeElement===n&&o.focusedKey&&(e.preventDefault(),c.set({expandedKey:o.expandedKey===o.focusedKey?null:o.focusedKey}))}}customElements.define("dbh-app",Le);const j=document.getElementById("root");if(j){const a=document.createElement("dbh-app");j.appendChild(a)}