@sphoro/bugcatch-web-sdk 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- (function(v,$){typeof exports=="object"&&typeof module<"u"?module.exports=$():typeof define=="function"&&define.amd?define($):(v=typeof globalThis<"u"?globalThis:v||self,v.BugCatch=$())})(this,function(){"use strict";var pt=v=>{throw TypeError(v)};var uo=(v,$,U)=>$.has(v)||pt("Cannot "+U);var gt=(v,$,U)=>$.has(v)?pt("Cannot add the same private member more than once"):$ instanceof WeakSet?$.add(v):$.set(v,U);var m=(v,$,U)=>(uo(v,$,"access private method"),U);var d,ne,mt,K,wt,ke,oe,ie,ve,bt,yt,xt,re,kt;const v="[REDACTED]",$=new Set(["auth","bearer","session","sid","phpsessid","otp","pass","passwd","pwd","sig","codeverifier"]),U=["token","secret","password","passwd","apikey","signature","credential","sessionid","privatekey","accesskey","authorization"],vt=new Set(["key","code","state","nonce","salt","hash"]),Ce=[/eyJ[\w-]{8,}\.[\w-]{8,}\.[\w-]+/g,/(?:AKIA|ASIA|AROA|AIDA|AGPA|ANPA|ANVA|APKA)[0-9A-Z]{16}/g,/AIza[\w-]{35}/g,/gh[pousr]_[A-Za-z0-9]{36,}/g,/github_pat_\w{22,}/g,/[sprk]k_(?:live|test)_[0-9A-Za-z]{10,}/g,/xox[abposr]-[A-Za-z0-9-]{10,}/g,/sk-ant-[\w-]{20,}/g,/sk-[\w-]{20,}/g,/glpat-[\w-]{20,}/g,/shp(?:at|ss|ca|pa)_[A-Za-z0-9]{32}/g],Ct=/\bBearer\s+[\w.~+/=-]{8,}/gi,It=/^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#@]+)@/;function Ie(t){try{return decodeURIComponent(t.replace(/\+/g," "))}catch{return t}}function St(t){return Ie(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function $t(t){return t.length>=20?!0:Ce.some(e=>(e.lastIndex=0,e.test(t)))}function Bt(t,e){const n=St(t);if(!n)return!1;if($.has(n))return!0;for(const o of U)if(n.includes(o))return!0;return vt.has(n)?$t(Ie(e)):!1}function se(t){return t.split("&").map(e=>{const n=e.indexOf("=");if(n===-1)return e;const o=e.slice(0,n),s=e.slice(n+1);return s&&Bt(o,s)?`${o}=${v}`:e}).join("&")}function Se(t){let e=t;for(const n of Ce)n.lastIndex=0,e=e.replace(n,v);return e}function $e(t){if(t==null)return t;let e=String(t);if(!e)return e;e=e.replace(It,`$1${v}@`);const n=e.indexOf("#");let o=n===-1?e:e.slice(0,n),s=n===-1?"":e.slice(n);const i=o.indexOf("?");if(i!==-1&&(o=o.slice(0,i+1)+se(o.slice(i+1))),s.length>1){const r=s.slice(1),a=r.indexOf("?");a!==-1?s=`#${r.slice(0,a+1)}${se(r.slice(a+1))}`:r.includes("=")&&(s=`#${se(r)}`)}return Se(o+s)}function Et(t){if(t==null)return t;let e=String(t);return e&&(e=e.replace(/[A-Za-z][A-Za-z0-9+.-]{0,30}:\/\/[^\s"'<>)\]]+/g,n=>$e(n)),e=e.replace(Ct,`Bearer ${v}`),Se(e))}function W(t){const e=[];return{push(n){e.push(n),e.length>t&&e.splice(0,e.length-t)},all:()=>e.slice(),clear:()=>{e.length=0},get size(){return e.length}}}const Be=["log","info","warn","error","debug"];function Tt(t,{levels:e=["warn","error","log","info"]}={}){const n={};for(const o of Be)n[o]=console[o],e.includes(o)&&(console[o]=(...s)=>{try{t.push({level:o,text:s.map(Ae).join(" ").slice(0,2e3),at:new Date().toISOString()})}catch{}n[o].apply(console,s)});return()=>{for(const o of Be)console[o]=n[o]}}function At(t,{slowMs:e=2e3}={}){const n=window.fetch,o=XMLHttpRequest.prototype.open,s=XMLHttpRequest.prototype.send;return n&&(window.fetch=async function(...r){var h,u,B;const a=performance.now(),c=typeof r[0]=="string"?r[0]:(h=r[0])==null?void 0:h.url,l=((u=r[1])==null?void 0:u.method)||(typeof r[0]=="object"?(B=r[0])==null?void 0:B.method:"GET")||"GET";try{const y=await n.apply(this,r);return ae(t,{url:c,method:l,status:y.status,ms:Math.round(performance.now()-a),slowMs:e}),y}catch(y){throw ae(t,{url:c,method:l,status:0,error:String((y==null?void 0:y.message)||y),ms:Math.round(performance.now()-a),slowMs:e}),y}}),XMLHttpRequest.prototype.open=function(r,a,...c){return this.__bc={method:r,url:a,started:0},o.call(this,r,a,...c)},XMLHttpRequest.prototype.send=function(...r){return this.__bc&&(this.__bc.started=performance.now(),this.addEventListener("loadend",()=>{ae(t,{url:this.__bc.url,method:this.__bc.method,status:this.status,ms:Math.round(performance.now()-this.__bc.started),slowMs:e})})),s.apply(this,r)},()=>{n&&(window.fetch=n),XMLHttpRequest.prototype.open=o,XMLHttpRequest.prototype.send=s}}function ae(t,{url:e,method:n,status:o,ms:s,error:i,slowMs:r}){try{t.push({url:$e(String(e)).slice(0,500),method:n,status:o,ms:s,error:i===void 0?void 0:Et(i),failed:o===0||o>=400,slow:s>=r,at:new Date().toISOString()})}catch{}}function jt(t){const e=o=>{const s=String(o.message||"Error"),i=o.filename?` (${o.filename}:${o.lineno}:${o.colno})`:"";t.push({level:"error",text:(/^uncaught/i.test(s)?s:`Uncaught ${s}`)+i,at:new Date().toISOString()})},n=o=>{t.push({level:"error",text:`Unhandled rejection: ${Ae(o.reason)}`,at:new Date().toISOString()})};return window.addEventListener("error",e),window.addEventListener("unhandledrejection",n),()=>{window.removeEventListener("error",e),window.removeEventListener("unhandledrejection",n)}}async function Ee(){const t=Te();try{if(navigator.getBattery){const e=await navigator.getBattery();t.battery=Math.round(e.level*100),t.charging=e.charging}}catch{}return t}function Te(){var e,n,o,s,i,r,a,c,l;const t=window.navigator||{};return{userAgent:t.userAgent,platform:t.platform,language:t.language,languages:t.languages?[...t.languages]:null,online:t.onLine,cookieEnabled:t.cookieEnabled,maxTouchPoints:t.maxTouchPoints??null,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:(e=window.screen)==null?void 0:e.width,height:(n=window.screen)==null?void 0:n.height,availWidth:(o=window.screen)==null?void 0:o.availWidth,availHeight:(s=window.screen)==null?void 0:s.availHeight,colorDepth:(i=window.screen)==null?void 0:i.colorDepth,orientation:((a=(r=window.screen)==null?void 0:r.orientation)==null?void 0:a.type)||null},devicePixelRatio:window.devicePixelRatio,referrer:document.referrer||null,title:document.title,capturedAt:new Date().toISOString(),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timezoneOffsetMinutes:new Date().getTimezoneOffset(),browserMode:t.webdriver?"Automated":"Normal",doNotTrack:t.doNotTrack||null,hardwareConcurrency:t.hardwareConcurrency??null,deviceMemory:t.deviceMemory??null,connection:t.connection?{effectiveType:t.connection.effectiveType,downlink:t.connection.downlink,rtt:t.connection.rtt,saveData:t.connection.saveData}:null,prefersColorScheme:(c=window.matchMedia)!=null&&c.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light",prefersReducedMotion:((l=window.matchMedia)==null?void 0:l.call(window,"(prefers-reduced-motion: reduce)").matches)||!1}}function Ae(t){if(typeof t=="string")return t;if(t instanceof Error)return`${t.name}: ${t.message}`;try{return JSON.stringify(t)}catch{return String(t)}}const Ot='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9.5l6 6 6-6"/></svg>',Lt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2M5.4 5.4l1.6 1.6M17 17l1.6 1.6M18.6 5.4L17 7M7 17l-1.6 1.6"/></svg>',Rt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13.4A8.2 8.2 0 1 1 10.6 4a6.6 6.6 0 0 0 9.4 9.4z"/></svg>',Mt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="2.8" y="4.4" width="18.4" height="12.6" rx="2.2"/><path d="M8.6 20.2h6.8"/><path d="M12 4.4v12.6"/><path d="M12 7.2a3.6 3.6 0 0 1 0 7.2z" fill="currentColor" stroke="none"/></svg>',je='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7.5v5.2M12 16.3h.01"/></svg>',Oe='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>',Kt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.8l4.6 4.4L19 7"/></svg>',Le='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 11v5.5M12 7.7h.01"/></svg>',Ut='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.3 4.2a2 2 0 0 1 3.4 0l7.6 13.1a2 2 0 0 1-1.7 3H4.4a2 2 0 0 1-1.7-3z"/><path d="M12 9.5v4.2M12 17h.01"/></svg>',ce="0.19.0",Nt="__bugcatchChunks",Pt=t=>`${t}@${ce}`;function Re(t){var e;return typeof window<"u"?(e=window[Nt])==null?void 0:e[Pt(t)]:void 0}function le(t,e){return console.warn(`[BugCatch] ${t}`),new Error("[BugCatch] part of the widget could not be loaded — check the connection and try again.")}function Me(t){let e=null;return(...n)=>(e||(e=t(...n).catch(o=>{throw e=null,o})),e)}const E=typeof document<"u"?document.currentScript:null,_t=(E==null?void 0:E.src)||null,Ke=(E==null?void 0:E.nonce)||"",Ue=(E==null?void 0:E.getAttribute("crossorigin"))??null,Ne=(E==null?void 0:E.referrerPolicy)||"";function Dt({chunkBase:t,loadedFrom:e,baseURI:n}){if(t)return new URL(String(t).replace(/\/*$/,"/"),n).href;if(!e)return null;const o=new URL(e);return/\.js$/i.test(o.pathname)?new URL(".",o).href:new URL(`${o.pathname.replace(/\/*$/,"/")}dist/`,o).href}function Pe(t,e){const n=Re(t);if(n)return Promise.resolve(n);const o=Dt({chunkBase:e,loadedFrom:_t,baseURI:document.baseURI});if(!o)return Promise.reject(le(`bugcatch-${t}.umd.js could not be loaded: this copy of the SDK was not loaded from an address, so there is nowhere to look for it. Set init({ chunkBase }) to the folder that holds it.`));const s=`${o}bugcatch-${t}.umd.js?v=${encodeURIComponent(ce)}`;return new Promise((i,r)=>{const a=document.createElement("script");a.src=s,a.async=!0,Ke&&(a.nonce=Ke),Ue!==null&&(a.crossOrigin=Ue),Ne&&(a.referrerPolicy=Ne);const c=()=>{a.onload=null,a.onerror=null,a.remove()};a.onload=()=>{c();const l=Re(t);l?i(l):r(le(`${s} loaded, but not as version ${ce} of the SDK — serve every file in dist/ from the same release.`))},a.onerror=()=>{c(),r(le(`could not load ${s}. The files in dist/ are served together: a self-hosted copy needs all of them (or init({ chunkBase }) pointing at them), and a Content-Security-Policy has to allow them in script-src.`))},(document.head||document.documentElement).appendChild(a)})}const _e=Me(t=>Pe("panel",t)),J=Me(t=>Pe("call",t)),Ht={info:Le,success:Kt,warning:Ut,critical:je};function zt(t,{where:e,onDismiss:n}){const o=document.createElement("div");o.className=`notice tone-${t.tone} at-${e}`,o.setAttribute("role","status");const s=document.createElement("span");s.className="notice-ico",s.setAttribute("aria-hidden","true"),s.innerHTML=Ht[t.tone]||Le;const i=document.createElement("div");i.className="notice-text";const r=document.createElement("strong");if(r.textContent=t.title,i.append(r),t.body){const a=document.createElement("p");a.textContent=t.body,i.append(a)}if(t.ctaHref&&t.ctaLabel){const a=document.createElement("a");a.className="notice-cta",a.href=t.ctaHref,a.textContent=t.ctaLabel,/^https?:/i.test(t.ctaHref)&&(a.target="_blank",a.rel="noopener noreferrer"),i.append(a)}if(o.append(s,i),t.dismissible&&n){const a=document.createElement("button");a.type="button",a.className="notice-x",a.setAttribute("aria-label","Dismiss this notice"),a.innerHTML=Oe,a.addEventListener("click",()=>n(t)),o.append(a)}return o}const De="#0e0e10",ue="#ffffff";function He(t){const e=Ft(t);if(!e)return ue;const[n,o,s]=e.map(r=>{const a=r/255;return a<=.03928?a/12.92:((a+.055)/1.055)**2.4});return .2126*n+.7152*o+.0722*s>.179?De:ue}function Ft(t){const e=String(t||"").trim();let n=/^#([0-9a-f]{3})$/i.exec(e);return n?[...n[1]].map(o=>parseInt(o+o,16)):(n=/^#([0-9a-f]{6})$/i.exec(e),n?[0,2,4].map(o=>parseInt(n[1].slice(o,o+2),16)):(n=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(e),n?[n[1],n[2],n[3]].map(Number):null))}const ze={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"},H=[{value:"light",label:"Light",icon:Lt},{value:"dark",label:"Dark",icon:Rt},{value:"auto",label:"System default",icon:Mt}],Fe="bugcatch:theme";function Xt(){try{const t=localStorage.getItem(Fe);return H.some(e=>e.value===t)?t:null}catch{return null}}function qt(t){try{localStorage.setItem(Fe,t)}catch{}}const Xe=`
1
+ (function(v,$){typeof exports=="object"&&typeof module<"u"?module.exports=$():typeof define=="function"&&define.amd?define($):(v=typeof globalThis<"u"?globalThis:v||self,v.BugCatch=$())})(this,function(){"use strict";var pt=v=>{throw TypeError(v)};var uo=(v,$,U)=>$.has(v)||pt("Cannot "+U);var gt=(v,$,U)=>$.has(v)?pt("Cannot add the same private member more than once"):$ instanceof WeakSet?$.add(v):$.set(v,U);var m=(v,$,U)=>(uo(v,$,"access private method"),U);var d,ne,mt,K,wt,ke,oe,ie,ve,bt,yt,xt,re,kt;const v="[REDACTED]",$=new Set(["auth","bearer","session","sid","phpsessid","otp","pass","passwd","pwd","sig","codeverifier"]),U=["token","secret","password","passwd","apikey","signature","credential","sessionid","privatekey","accesskey","authorization"],vt=new Set(["key","code","state","nonce","salt","hash"]),Ce=[/eyJ[\w-]{8,}\.[\w-]{8,}\.[\w-]+/g,/(?:AKIA|ASIA|AROA|AIDA|AGPA|ANPA|ANVA|APKA)[0-9A-Z]{16}/g,/AIza[\w-]{35}/g,/gh[pousr]_[A-Za-z0-9]{36,}/g,/github_pat_\w{22,}/g,/[sprk]k_(?:live|test)_[0-9A-Za-z]{10,}/g,/xox[abposr]-[A-Za-z0-9-]{10,}/g,/sk-ant-[\w-]{20,}/g,/sk-[\w-]{20,}/g,/glpat-[\w-]{20,}/g,/shp(?:at|ss|ca|pa)_[A-Za-z0-9]{32}/g],Ct=/\bBearer\s+[\w.~+/=-]{8,}/gi,It=/^([A-Za-z][A-Za-z0-9+.-]*:\/\/)([^/?#@]+)@/;function Ie(t){try{return decodeURIComponent(t.replace(/\+/g," "))}catch{return t}}function St(t){return Ie(t).toLowerCase().replace(/[^a-z0-9]/g,"")}function $t(t){return t.length>=20?!0:Ce.some(e=>(e.lastIndex=0,e.test(t)))}function Bt(t,e){const n=St(t);if(!n)return!1;if($.has(n))return!0;for(const o of U)if(n.includes(o))return!0;return vt.has(n)?$t(Ie(e)):!1}function se(t){return t.split("&").map(e=>{const n=e.indexOf("=");if(n===-1)return e;const o=e.slice(0,n),s=e.slice(n+1);return s&&Bt(o,s)?`${o}=${v}`:e}).join("&")}function Se(t){let e=t;for(const n of Ce)n.lastIndex=0,e=e.replace(n,v);return e}function $e(t){if(t==null)return t;let e=String(t);if(!e)return e;e=e.replace(It,`$1${v}@`);const n=e.indexOf("#");let o=n===-1?e:e.slice(0,n),s=n===-1?"":e.slice(n);const i=o.indexOf("?");if(i!==-1&&(o=o.slice(0,i+1)+se(o.slice(i+1))),s.length>1){const r=s.slice(1),a=r.indexOf("?");a!==-1?s=`#${r.slice(0,a+1)}${se(r.slice(a+1))}`:r.includes("=")&&(s=`#${se(r)}`)}return Se(o+s)}function Et(t){if(t==null)return t;let e=String(t);return e&&(e=e.replace(/[A-Za-z][A-Za-z0-9+.-]{0,30}:\/\/[^\s"'<>)\]]+/g,n=>$e(n)),e=e.replace(Ct,`Bearer ${v}`),Se(e))}function W(t){const e=[];return{push(n){e.push(n),e.length>t&&e.splice(0,e.length-t)},all:()=>e.slice(),clear:()=>{e.length=0},get size(){return e.length}}}const Be=["log","info","warn","error","debug"];function Tt(t,{levels:e=["warn","error","log","info"]}={}){const n={};for(const o of Be)n[o]=console[o],e.includes(o)&&(console[o]=(...s)=>{try{t.push({level:o,text:s.map(Ae).join(" ").slice(0,2e3),at:new Date().toISOString()})}catch{}n[o].apply(console,s)});return()=>{for(const o of Be)console[o]=n[o]}}function At(t,{slowMs:e=2e3}={}){const n=window.fetch,o=XMLHttpRequest.prototype.open,s=XMLHttpRequest.prototype.send;return n&&(window.fetch=async function(...r){var h,u,B;const a=performance.now(),c=typeof r[0]=="string"?r[0]:(h=r[0])==null?void 0:h.url,l=((u=r[1])==null?void 0:u.method)||(typeof r[0]=="object"?(B=r[0])==null?void 0:B.method:"GET")||"GET";try{const y=await n.apply(this,r);return ae(t,{url:c,method:l,status:y.status,ms:Math.round(performance.now()-a),slowMs:e}),y}catch(y){throw ae(t,{url:c,method:l,status:0,error:String((y==null?void 0:y.message)||y),ms:Math.round(performance.now()-a),slowMs:e}),y}}),XMLHttpRequest.prototype.open=function(r,a,...c){return this.__bc={method:r,url:a,started:0},o.call(this,r,a,...c)},XMLHttpRequest.prototype.send=function(...r){return this.__bc&&(this.__bc.started=performance.now(),this.addEventListener("loadend",()=>{ae(t,{url:this.__bc.url,method:this.__bc.method,status:this.status,ms:Math.round(performance.now()-this.__bc.started),slowMs:e})})),s.apply(this,r)},()=>{n&&(window.fetch=n),XMLHttpRequest.prototype.open=o,XMLHttpRequest.prototype.send=s}}function ae(t,{url:e,method:n,status:o,ms:s,error:i,slowMs:r}){try{t.push({url:$e(String(e)).slice(0,500),method:n,status:o,ms:s,error:i===void 0?void 0:Et(i),failed:o===0||o>=400,slow:s>=r,at:new Date().toISOString()})}catch{}}function jt(t){const e=o=>{const s=String(o.message||"Error"),i=o.filename?` (${o.filename}:${o.lineno}:${o.colno})`:"";t.push({level:"error",text:(/^uncaught/i.test(s)?s:`Uncaught ${s}`)+i,at:new Date().toISOString()})},n=o=>{t.push({level:"error",text:`Unhandled rejection: ${Ae(o.reason)}`,at:new Date().toISOString()})};return window.addEventListener("error",e),window.addEventListener("unhandledrejection",n),()=>{window.removeEventListener("error",e),window.removeEventListener("unhandledrejection",n)}}async function Ee(){const t=Te();try{if(navigator.getBattery){const e=await navigator.getBattery();t.battery=Math.round(e.level*100),t.charging=e.charging}}catch{}return t}function Te(){var e,n,o,s,i,r,a,c,l;const t=window.navigator||{};return{userAgent:t.userAgent,platform:t.platform,language:t.language,languages:t.languages?[...t.languages]:null,online:t.onLine,cookieEnabled:t.cookieEnabled,maxTouchPoints:t.maxTouchPoints??null,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:(e=window.screen)==null?void 0:e.width,height:(n=window.screen)==null?void 0:n.height,availWidth:(o=window.screen)==null?void 0:o.availWidth,availHeight:(s=window.screen)==null?void 0:s.availHeight,colorDepth:(i=window.screen)==null?void 0:i.colorDepth,orientation:((a=(r=window.screen)==null?void 0:r.orientation)==null?void 0:a.type)||null},devicePixelRatio:window.devicePixelRatio,referrer:document.referrer||null,title:document.title,capturedAt:new Date().toISOString(),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,timezoneOffsetMinutes:new Date().getTimezoneOffset(),browserMode:t.webdriver?"Automated":"Normal",doNotTrack:t.doNotTrack||null,hardwareConcurrency:t.hardwareConcurrency??null,deviceMemory:t.deviceMemory??null,connection:t.connection?{effectiveType:t.connection.effectiveType,downlink:t.connection.downlink,rtt:t.connection.rtt,saveData:t.connection.saveData}:null,prefersColorScheme:(c=window.matchMedia)!=null&&c.call(window,"(prefers-color-scheme: dark)").matches?"dark":"light",prefersReducedMotion:((l=window.matchMedia)==null?void 0:l.call(window,"(prefers-reduced-motion: reduce)").matches)||!1}}function Ae(t){if(typeof t=="string")return t;if(t instanceof Error)return`${t.name}: ${t.message}`;try{return JSON.stringify(t)}catch{return String(t)}}const Ot='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9.5l6 6 6-6"/></svg>',Lt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.2"/><path d="M12 2.6v2.2M12 19.2v2.2M2.6 12h2.2M19.2 12h2.2M5.4 5.4l1.6 1.6M17 17l1.6 1.6M18.6 5.4L17 7M7 17l-1.6 1.6"/></svg>',Rt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13.4A8.2 8.2 0 1 1 10.6 4a6.6 6.6 0 0 0 9.4 9.4z"/></svg>',Mt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="2.8" y="4.4" width="18.4" height="12.6" rx="2.2"/><path d="M8.6 20.2h6.8"/><path d="M12 4.4v12.6"/><path d="M12 7.2a3.6 3.6 0 0 1 0 7.2z" fill="currentColor" stroke="none"/></svg>',je='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7.5v5.2M12 16.3h.01"/></svg>',Oe='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg>',Kt='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.8l4.6 4.4L19 7"/></svg>',Le='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 11v5.5M12 7.7h.01"/></svg>',Ut='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.3 4.2a2 2 0 0 1 3.4 0l7.6 13.1a2 2 0 0 1-1.7 3H4.4a2 2 0 0 1-1.7-3z"/><path d="M12 9.5v4.2M12 17h.01"/></svg>',ce="0.20.0",Nt="__bugcatchChunks",Pt=t=>`${t}@${ce}`;function Re(t){var e;return typeof window<"u"?(e=window[Nt])==null?void 0:e[Pt(t)]:void 0}function le(t,e){return console.warn(`[BugCatch] ${t}`),new Error("[BugCatch] part of the widget could not be loaded — check the connection and try again.")}function Me(t){let e=null;return(...n)=>(e||(e=t(...n).catch(o=>{throw e=null,o})),e)}const E=typeof document<"u"?document.currentScript:null,_t=(E==null?void 0:E.src)||null,Ke=(E==null?void 0:E.nonce)||"",Ue=(E==null?void 0:E.getAttribute("crossorigin"))??null,Ne=(E==null?void 0:E.referrerPolicy)||"";function Dt({chunkBase:t,loadedFrom:e,baseURI:n}){if(t)return new URL(String(t).replace(/\/*$/,"/"),n).href;if(!e)return null;const o=new URL(e);return/\.js$/i.test(o.pathname)?new URL(".",o).href:new URL(`${o.pathname.replace(/\/*$/,"/")}dist/`,o).href}function Pe(t,e){const n=Re(t);if(n)return Promise.resolve(n);const o=Dt({chunkBase:e,loadedFrom:_t,baseURI:document.baseURI});if(!o)return Promise.reject(le(`bugcatch-${t}.umd.js could not be loaded: this copy of the SDK was not loaded from an address, so there is nowhere to look for it. Set init({ chunkBase }) to the folder that holds it.`));const s=`${o}bugcatch-${t}.umd.js?v=${encodeURIComponent(ce)}`;return new Promise((i,r)=>{const a=document.createElement("script");a.src=s,a.async=!0,Ke&&(a.nonce=Ke),Ue!==null&&(a.crossOrigin=Ue),Ne&&(a.referrerPolicy=Ne);const c=()=>{a.onload=null,a.onerror=null,a.remove()};a.onload=()=>{c();const l=Re(t);l?i(l):r(le(`${s} loaded, but not as version ${ce} of the SDK — serve every file in dist/ from the same release.`))},a.onerror=()=>{c(),r(le(`could not load ${s}. The files in dist/ are served together: a self-hosted copy needs all of them (or init({ chunkBase }) pointing at them), and a Content-Security-Policy has to allow them in script-src.`))},(document.head||document.documentElement).appendChild(a)})}const _e=Me(t=>Pe("panel",t)),J=Me(t=>Pe("call",t)),Ht={info:Le,success:Kt,warning:Ut,critical:je};function zt(t,{where:e,onDismiss:n}){const o=document.createElement("div");o.className=`notice tone-${t.tone} at-${e}`,o.setAttribute("role","status");const s=document.createElement("span");s.className="notice-ico",s.setAttribute("aria-hidden","true"),s.innerHTML=Ht[t.tone]||Le;const i=document.createElement("div");i.className="notice-text";const r=document.createElement("strong");if(r.textContent=t.title,i.append(r),t.body){const a=document.createElement("p");a.textContent=t.body,i.append(a)}if(t.ctaHref&&t.ctaLabel){const a=document.createElement("a");a.className="notice-cta",a.href=t.ctaHref,a.textContent=t.ctaLabel,/^https?:/i.test(t.ctaHref)&&(a.target="_blank",a.rel="noopener noreferrer"),i.append(a)}if(o.append(s,i),t.dismissible&&n){const a=document.createElement("button");a.type="button",a.className="notice-x",a.setAttribute("aria-label","Dismiss this notice"),a.innerHTML=Oe,a.addEventListener("click",()=>n(t)),o.append(a)}return o}const De="#0e0e10",ue="#ffffff";function He(t){const e=Ft(t);if(!e)return ue;const[n,o,s]=e.map(r=>{const a=r/255;return a<=.03928?a/12.92:((a+.055)/1.055)**2.4});return .2126*n+.7152*o+.0722*s>.179?De:ue}function Ft(t){const e=String(t||"").trim();let n=/^#([0-9a-f]{3})$/i.exec(e);return n?[...n[1]].map(o=>parseInt(o+o,16)):(n=/^#([0-9a-f]{6})$/i.exec(e),n?[0,2,4].map(o=>parseInt(n[1].slice(o,o+2),16)):(n=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(e),n?[n[1],n[2],n[3]].map(Number):null))}const ze={"bottom-right":"bottom:20px;right:20px;","bottom-left":"bottom:20px;left:20px;","top-right":"top:20px;right:20px;","top-left":"top:20px;left:20px;"},H=[{value:"light",label:"Light",icon:Lt},{value:"dark",label:"Dark",icon:Rt},{value:"auto",label:"System default",icon:Mt}],Fe="bugcatch:theme";function Xt(){try{const t=localStorage.getItem(Fe);return H.some(e=>e.value===t)?t:null}catch{return null}}function qt(t){try{localStorage.setItem(Fe,t)}catch{}}const Xe=`
2
2
  --bc-bg: #151517;
3
3
  --bc-elevated: #1c1c1f;
4
4
  --bc-fg: #f3f2ef;
@@ -306,5 +306,5 @@ button:focus-visible, a:focus-visible, select:focus-visible, [tabindex]:focus-vi
306
306
  `).filter(r=>r.startsWith("data:")).map(r=>r.slice(5).trim()).join(`
307
307
  `);if(i)try{e(JSON.parse(i))}catch{}o=n.indexOf(`
308
308
 
309
- `)}return n}async function dn({apiBase:t}){const e=C();if(!e)return{reporter:null,reports:[]};const n=await fetch(`${t}/ingest/watch/reports${V()}`,{headers:{Authorization:`Bearer ${e}`}});if(n.status===401)return N(),{reporter:null,reports:[]};if(!n.ok)throw new Error(`[BugCatch] could not load your reports (HTTP ${n.status})`);return n.json()}async function hn({apiBase:t,bugId:e}){const n=C();if(!n)return null;const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}${V()}`,{headers:{Authorization:`Bearer ${n}`}});if(o.status===401)return N(),null;if(o.status===404)return null;if(!o.ok)throw new Error(`[BugCatch] could not load that report (HTTP ${o.status})`);return o.json()}async function fn({apiBase:t,bugId:e,body:n,attachments:o}){const s=C();if(!s)throw new Error("[BugCatch] nothing filed from this browser yet.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/messages`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({...n?{body:n}:{},...o!=null&&o.length?{attachments:o}:{}})});if(i.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!i.ok){let r="";try{const a=await i.json();r=Array.isArray(a.message)?a.message.join(", "):a.message||""}catch{}throw i.status===429?new Error(_(r)):new Error(r||`[BugCatch] could not send that (HTTP ${i.status}).`)}return i.json()}async function pn({apiBase:t,apiKey:e,projectId:n}){const o={agent:!1,live:!1};try{const s=await fetch(`${t}/ingest/calls?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!s.ok)return o;const i=await s.json();return i!=null&&i.available?i.kinds&&typeof i.kinds=="object"?{agent:!1,live:!!i.kinds.live}:{agent:!1,live:i.kind==="live"}:o}catch{return o}}async function gn({apiBase:t,apiKey:e,token:n,payload:o,watchToken:s}){const i=await fetch(`${t}/ingest/calls`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":e,...n?{"X-BugCatch-Session":n}:{}},body:JSON.stringify(s?{...o,watchToken:s}:o)});if(!i.ok){let r="";try{const c=await i.json();r=Array.isArray(c.message)?c.message.join(", "):c.message||""}catch{}const a=new Error(i.status===429?_(r):r||`[BugCatch] the call could not be started (HTTP ${i.status}).`);throw a.status=i.status,a}return i.json()}async function Ze({apiBase:t,bugId:e}){const n=C();if(!n)return!1;try{const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)return!1;const s=await o.json();return!!(s!=null&&s.offered)}catch{return!1}}async function mn({apiBase:t,bugId:e}){const n=C();if(!n)throw new Error("[BugCatch] nothing filed from this browser yet.");const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{method:"POST",headers:{Authorization:`Bearer ${n}`}});if(o.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!o.ok){let s="";try{const i=await o.json();s=Array.isArray(i.message)?i.message.join(", "):i.message||""}catch{}throw new Error(s||`[BugCatch] the call could not be started (HTTP ${o.status}).`)}return o.json()}async function wn({apiBase:t,bugId:e,callId:n}){const o=C();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/give-up`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}async function bn({apiBase:t,bugId:e,callId:n}){const o=C();if(!o)throw new Error("[BugCatch] nothing filed from this browser yet.");const s=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/answer`,{method:"POST",headers:{Authorization:`Bearer ${o}`}});if(s.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(s.status===409)throw new Error("[BugCatch] that call is no longer ringing.");if(!s.ok){let i="";try{const r=await s.json();i=Array.isArray(r.message)?r.message.join(", "):r.message||""}catch{}throw new Error(i||`[BugCatch] the call could not be answered (HTTP ${s.status}).`)}return s.json()}async function Ge({apiBase:t,bugId:e,callId:n}){const o=C();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/decline`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}function yn({apiBase:t,onUpdate:e}){let n=!0,o=0,s=null;async function i(){const r=C();if(!r)throw new Error("nothing filed from this browser yet");s=new AbortController;const a=await fetch(`${t}/ingest/watch/stream${V()}`,{headers:{Authorization:`Bearer ${r}`,Accept:"text/event-stream"},signal:s.signal});if(a.status===401)throw n=!1,N(),new Error("subscription no longer valid");if(!a.ok||!a.body)throw new Error(`stream failed (${a.status})`);const c=Date.now(),l=a.body.getReader(),h=new TextDecoder;let u="";try{for(;;){const{value:B,done:y}=await l.read();if(y)break;u+=h.decode(B,{stream:!0}),u=un(u,w=>{if(w.type!=="ping")try{e(w)}catch{}})}}finally{Date.now()-c>=on&&(o=0)}}return(async()=>{for(;n;){try{await i()}catch(a){if((a==null?void 0:a.name)==="AbortError")return}if(!n)return;const r=Ye[Math.min(o,Ye.length-1)];o+=1,await new Promise(a=>setTimeout(a,r))}})(),()=>{n=!1,s==null||s.abort(),s=null}}const xn={OPEN:"is open",IN_PROGRESS:"is being worked on",RESOLVED:"has been fixed",CLOSED:"has been closed"},kn={status:null,severity:"#f2a54a",assignment:"#79a8ff",reply:"#4fd19a"},T={bg:"#111113",fg:"#f3f2ef",muted:"#b3b0aa",faint:"#8f8c86",ring:"rgba(255,255,255,.09)",brand:"#a59cff"};function Qe(t){return t!=null&&t.summary?t.summary:`Your report ${xn[t==null?void 0:t.status]||"was updated"}`}function vn(t){var e;return(t==null?void 0:t.kind)==="reply"&&((e=t.reply)!=null&&e.body)?`${t.reply.author?`${t.reply.author}: `:""}${t.reply.body}`:(t==null?void 0:t.title)||""}function Cn(t){const e=Date.parse(t||"");if(!Number.isFinite(e))return"";const n=Math.round((Date.now()-e)/6e4);if(n<1)return"just now";if(n<60)return`${n}m ago`;const o=Math.round(n/60);if(o<24)return`${o}h ago`;const s=Math.round(o/24);return s===1?"yesterday":`${s}d ago`}function In(){var t;try{const e=document.querySelector('[data-bugcatch="widget"]'),n=(t=e==null?void 0:e.shadowRoot)==null?void 0:t.querySelector(".dock"),o=(n==null?void 0:n.getBoundingClientRect().height)||0;return o?Math.round(o)+12:0}catch{return 0}}function Sn({accent:t=null,position:e="bottom-right",onRead:n=null,onReadAll:o=null,onCall:s=null}={}){const[i,r]=String(e).split("-"),a=i==="top",c=r==="left",l=new Map;let h=null,u=null;function B(){return h!=null&&h.isConnected||(h=document.createElement("div"),h.setAttribute("data-bugcatch","updates"),h.style.cssText=["position:fixed",`${a?"top":"bottom"}:${20+In()}px`,`${c?"left":"right"}:20px`,"z-index:2147482998","display:flex","flex-direction:column","gap:10px",`align-items:${c?"flex-start":"flex-end"}`,"width:min(320px,calc(100vw - 40px))","pointer-events:none"].join(";"),document.body.appendChild(h)),h}function y(f){if(!f._bcShown){f.remove();return}f.style.opacity="0",f.style.transform=`translateY(${a?-8:8}px)`,setTimeout(()=>f.remove(),250)}const w=t||ue,M=t?He(t):De;function j(f,k){const p=document.createElement("button");return p.type="button",p.textContent=f,p.style.cssText=["padding:6px 12px","border:0",`box-shadow:${k?"none":`inset 0 0 0 1px ${T.ring}`}`,"border-radius:999px",`background:${k?w:"rgba(255,255,255,.06)"}`,`color:${k?M:T.fg}`,"font:560 12px/1.4 inherit","cursor:pointer","white-space:nowrap"].join(";"),p}function A(f){var P;const k=document.createElement("div");k.setAttribute("role","status"),k.style.cssText=["box-sizing:border-box","width:100%","padding:13px 14px","border-radius:14px",`background:${T.bg}`,`color:${T.fg}`,'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${T.ring},0 20px 44px -12px rgba(0,0,0,.55),0 4px 12px rgba(0,0,0,.25)`,"opacity:0",`transform:translateY(${a?-8:8}px)`,"transition:opacity .2s ease,transform .2s ease","pointer-events:auto"].join(";");const p=document.createElement("div");p.style.cssText="display:flex;align-items:baseline;gap:8px;font-weight:600;margin-bottom:2px";const b=document.createElement("span");b.setAttribute("aria-hidden","true"),b.style.cssText=`flex:none;width:7px;height:7px;border-radius:50%;transform:translateY(-1px);background:${kn[f==null?void 0:f.kind]||t||T.brand}`;const I=document.createElement("span");I.textContent=Qe(f),p.append(b,I);const g=document.createElement("div");g.style.cssText=(f==null?void 0:f.kind)==="reply"?`color:${T.muted};padding-left:15px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden`:`color:${T.muted};padding-left:15px`,g.textContent=vn(f);const S=document.createElement("div");S.style.cssText="display:flex;align-items:center;gap:8px;margin-top:10px;padding-left:15px;flex-wrap:wrap";const O=document.createElement("span");O.style.cssText=`color:${T.faint};font-size:11px;margin-right:auto`,O.textContent=Cn(f==null?void 0:f.at);const x=j("Mark as read",!1);if(x.setAttribute("aria-label",`Mark as read: ${Qe(f)}`),x.addEventListener("click",()=>n==null?void 0:n(f.id)),S.append(O,x),(P=f==null?void 0:f.call)!=null&&P.offered&&typeof s=="function"){const ft=j("Talk it through",!0);ft.addEventListener("click",()=>s(f)),S.insertBefore(ft,x)}return k.append(p,g,S),k}function D(f){const k=document.createElement("div");k.style.cssText=["box-sizing:border-box","width:100%","display:flex","align-items:center","gap:8px","padding:8px 10px 8px 14px","border-radius:999px",`background:${T.bg}`,`color:${T.muted}`,'font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${T.ring},0 12px 30px -10px rgba(0,0,0,.5)`,"pointer-events:auto"].join(";");const p=document.createElement("span");p.style.cssText="margin-right:auto",p.textContent=`${f} more update${f===1?"":"s"}`;const b=j("Mark all as read",!1);return b.style.cssText+=";padding:4px 9px",b.addEventListener("click",()=>o==null?void 0:o()),k.append(p,b),k}return{render(f){const k=Array.isArray(f)?f:[];if(!k.length){for(const x of l.values())y(x);l.clear(),u==null||u.remove(),u=null,h==null||h.remove(),h=null;return}const p=k.slice(-3),b=new Set(p.map(x=>x.id)),I=B();for(const[x,P]of l)b.has(x)||(y(P),l.delete(x));const g=[];for(const x of p){if(l.has(x.id))continue;const P=A(x);l.set(x.id,P),g.push(P)}const S=a?[...p].reverse():p;for(const x of S)I.appendChild(l.get(x.id));const O=k.length-p.length;O>0?(u==null||u.remove(),u=D(O),a?I.appendChild(u):I.insertBefore(u,I.firstChild)):(u==null||u.remove(),u=null),requestAnimationFrame(()=>{for(const x of g)x.isConnected&&(x._bcShown=!0,x.style.opacity="1",x.style.transform="translateY(0)")})},destroy(){l.clear(),u=null,h==null||h.remove(),h=null}}}async function $n({apiBase:t,apiKey:e,projectId:n,sessionToken:o}){try{const s=await fetch(`${t}/ingest/known-issues?projectId=${encodeURIComponent(n)}`,{headers:pe(e,o)});if(!s.ok)return[];const i=await s.json();return Array.isArray(i==null?void 0:i.issues)?i.issues:[]}catch{return[]}}async function Bn({apiBase:t,apiKey:e,projectId:n,q:o,sessionToken:s,signal:i}){try{const r=`${t}/ingest/known-issues/match?projectId=${encodeURIComponent(n)}&q=${encodeURIComponent(o)}`,a=await fetch(r,{headers:pe(e,s),signal:i});if(!a.ok)return[];const c=await a.json();return Array.isArray(c==null?void 0:c.issues)?c.issues:[]}catch{return[]}}async function En({apiBase:t,apiKey:e,projectId:n,bugId:o,reporterEmail:s,reporterName:i,metadata:r,logs:a,sessionToken:c}){const l=C(),h=await fetch(`${t}/ingest/known-issues/${encodeURIComponent(o)}/join`,{method:"POST",headers:pe(e,c),body:JSON.stringify({projectId:n,reporterEmail:s,reporterName:i,metadata:r,logs:a,...l?{watchToken:l}:{}})});if(!h.ok)throw new Error(await Tn(h));const u=await h.json();return u!=null&&u.watchToken&&fe(u.watchToken),u}function pe(t,e){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function Tn(t){let e="";try{const n=await t.json();e=Array.isArray(n.message)?n.message.join(", "):n.message||""}catch{}return t.status===429?_(e):e||`Could not add you to this issue (HTTP ${t.status})`}async function An({apiBase:t,bugId:e,score:n,comment:o}){const s=C();if(!s)throw new Error("This browser cannot access that report.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/rating${V()}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({score:n,...o?{comment:o}:{}})}),r=await i.json().catch(()=>({}));if(!i.ok){const a=Array.isArray(r.message)?r.message.join(", "):r.message;throw new Error(a||"Could not send your rating. Please try again.")}return r}async function jn({apiBase:t,bugId:e,...n}){const o=C();if(!o)throw new Error("This browser cannot access that report.");const s=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/verification`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify(n)}),i=await s.json().catch(()=>({}));if(!s.ok)throw new Error(i.message||"Could not record feedback. Please refresh and try again.");return i}const On="bugcatch.session.",ge=t=>`${On}${t}`,me=new Map;function R(t){var e;if(!t)return null;try{const n=(e=window.localStorage)==null?void 0:e.getItem(ge(t));if(n){const o=JSON.parse(n);if(o!=null&&o.token)return o}}catch{}return me.get(t)||null}function Ln(t,e){var o;if(!t||!(e!=null&&e.token))return;const n={token:e.token,email:e.email||null,name:e.name||null};me.set(t,n);try{(o=window.localStorage)==null||o.setItem(ge(t),JSON.stringify(n))}catch{}}function we(t){var e;if(t){me.delete(t);try{(e=window.localStorage)==null||e.removeItem(ge(t))}catch{}}}function Q({apiKey:t,token:e}){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function et(t){let e="";try{const n=await t.json();e=Array.isArray(n.message)?n.message.join(", "):n.message||""}catch{}return t.status===429?_(e):e||`Request failed (HTTP ${t.status})`}async function Rn({apiBase:t,apiKey:e,projectId:n,token:o}){const s={mode:"off",signedIn:null,signedOutReason:null};try{const i=await fetch(`${t}/ingest/login?projectId=${encodeURIComponent(n)}`,{headers:Q({apiKey:e,token:o})});if(!i.ok)return s;const r=await i.json();return{mode:r.mode==="optional"||r.mode==="required"?r.mode:"off",signedIn:r.signedIn||null,signedOutReason:r.signedOutReason||null}}catch{return s}}async function Mn({apiBase:t,apiKey:e,projectId:n,email:o,name:s}){const i=await fetch(`${t}/ingest/login/start`,{method:"POST",headers:Q({apiKey:e}),body:JSON.stringify({projectId:n,email:o,...s?{name:s}:{}})});if(!i.ok)throw new Error(await et(i));return i.json()}async function Kn({apiBase:t,apiKey:e,projectId:n,email:o,code:s}){const i=await fetch(`${t}/ingest/login`,{method:"POST",headers:Q({apiKey:e}),body:JSON.stringify({projectId:n,email:o,code:s})});if(!i.ok)throw new Error(await et(i));const r=await i.json(),a={token:r.sessionToken,email:r.email,name:r.name};return Ln(n,a),a}async function Un({apiBase:t,apiKey:e,projectId:n,token:o}){if(we(n),!o)return{signedOut:!0};try{await fetch(`${t}/ingest/logout`,{method:"POST",headers:Q({apiKey:e,token:o}),body:JSON.stringify({projectId:n})})}catch{}return{signedOut:!0}}let tt=!1;if(typeof window<"u"&&window.addEventListener)for(const t of["pointerdown","keydown","touchstart"])window.addEventListener(t,()=>{tt=!0},{once:!0,capture:!0,passive:!0});function Nn(){return tt}async function Pn({apiBase:t,apiKey:e,projectId:n}){const o={policy:"off",fields:[]};try{const s=await fetch(`${t}/ingest/reference-fields?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!s.ok)return o;const i=await s.json(),r=i.policy==="optional"||i.policy==="required"?i.policy:"off",a=Array.isArray(i.fields)?i.fields:[];return a.length?{policy:r,fields:a}:o}catch{return o}}async function _n({apiBase:t,apiKey:e,projectId:n}){try{const o=await fetch(`${t}/ingest/agent-issues?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!o.ok)return[];const s=await o.json();return(Array.isArray(s==null?void 0:s.issues)?s.issues:[]).filter(i=>i&&typeof i.key=="string"&&typeof i.title=="string").map(i=>({key:i.key,title:i.title,hint:typeof i.hint=="string"?i.hint:""}))}catch{return[]}}const Dn=5*6e4,nt="bugcatch:announcements:seen",Hn=40,zn=["info","success","warning","critical"],ot=t=>`bugcatch:announcements:${t}`;async function Fn({apiBase:t,apiKey:e,projectId:n,fresh:o=!1}){var s;if(!o){const i=Zn(n);if(i)return i}try{const i=await fetch(`${t}/ingest/announcements?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}}),r=i.ok?Vn((s=await i.json())==null?void 0:s.announcements):[];return Gn(n,r),r}catch{return[]}}function Xn(t,{on:e=Jn(),seen:n=rt()}={}){return(t||[]).filter(o=>Yn(o,e)&&!(o.dismissible&&n.includes(it(o))))}function qn(t){if(!(!(t!=null&&t.id)||!t.dismissible))try{const e=it(t),n=[...rt().filter(o=>o!==e),e];localStorage.setItem(nt,JSON.stringify(n.slice(-Hn)))}catch{}}function Wn(t){return typeof t!="string"||!t||t.startsWith("//")?null:t.startsWith("/")||/^(https?:\/\/|mailto:)./i.test(t)?t:null}function Jn(t=new Date){const e=n=>String(n).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`}function Yn(t,e){return!(t.startsOn&&e<t.startsOn||t.endsOn&&e>t.endsOn)}function it(t){return`${t.id}#${t.revision||0}`}function Vn(t){return(Array.isArray(t)?t:[]).filter(e=>e&&typeof e.id=="string"&&typeof e.title=="string"&&e.title.trim()).map(e=>{const n=e.placement==="launcher"?"launcher":"panel",o=Wn(e.ctaHref);return{id:e.id,revision:Number.isInteger(e.revision)?e.revision:0,title:e.title,body:typeof e.body=="string"?e.body:"",tone:zn.includes(e.tone)?e.tone:"info",ctaLabel:o&&typeof e.ctaLabel=="string"?e.ctaLabel:"",ctaHref:o&&typeof e.ctaLabel=="string"?o:"",placement:n,dismissible:n==="launcher"||e.dismissible!==!1,startsOn:typeof e.startsOn=="string"?e.startsOn:"",endsOn:typeof e.endsOn=="string"?e.endsOn:""}})}function rt(){try{const t=JSON.parse(localStorage.getItem(nt)||"[]");return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}catch{return[]}}function Zn(t){try{const e=JSON.parse(localStorage.getItem(ot(t))||"null");if(!e||!Array.isArray(e.list)||typeof e.at!="number")return null;const n=Date.now()-e.at;return n>=0&&n<Dn?e.list:null}catch{return null}}function Gn(t,e){try{localStorage.setItem(ot(t),JSON.stringify({at:Date.now(),list:e}))}catch{}}const ee=120,Qn=500,te=30,eo=1e3,be={loanId:["loanId","loanNumber"],applicationId:["applicationId"],customerNumber:["customerNumber","customerId"]},to=/[\u0000-\u001f\u007f]/;function no(t){if(typeof t=="number")return Number.isFinite(t)?String(t):null;if(typeof t!="string")return null;const e=t.trim();return!e||e.length>ee||to.test(e)?null:e}function L(t){return t===null?"null":Array.isArray(t)?"an array":typeof t=="object"?"an object":`a ${typeof t}`}function ye(t,e,n,o,s){for(const i of n){if(!(i in t)||t[i]===void 0||t[i]===null||t[i]==="")continue;const r=no(t[i]);if(r)return r;const a=t[i];s(typeof a=="string"&&a.trim().length>ee?`${o}.${i} is longer than ${ee} characters and was dropped — pass the identifier itself, not a sentence or a URL.`:`${o}.${i} should be a string or number, got ${L(a)} — it was dropped.`)}return null}function st(t){if(t===null||typeof t=="boolean")return{ok:!0,value:t};if(typeof t=="number")return Number.isFinite(t)?{ok:!0,value:t}:{ok:!1};if(typeof t=="string")return{ok:!0,value:t.slice(0,Qn)};if(typeof t=="object")try{const e=JSON.stringify(t);if(e&&e.length<=eo)return{ok:!0,value:JSON.parse(e)}}catch{}return{ok:!1}}const at=["email","name","phone"],oo=new Set(["id","userId",...at,...Object.values(be).flat()]);function ct(t,e=()=>{}){if(t==null)return null;if(typeof t!="object"||Array.isArray(t))return e(`identify() takes an object like { id, email, name } or null, got ${L(t)} — ignored.`),null;const n={};try{const o=ye(t,"id",["id","userId"],"identify()",e);o&&(n.id=o);for(const i of at){const r=t[i];if(r==null||r==="")continue;if(i==="phone"&&typeof r=="number"&&Number.isFinite(r)){n.phone=String(r);continue}if(typeof r!="string"){e(`identify().${i} should be a string, got ${L(r)} — it was dropped.`);continue}const a=r.trim().slice(0,i==="name"?200:ee);a&&(i==="email"&&!a.includes("@")&&e(`identify().email "${a}" does not look like an email address — reports cannot be threaded or followed up without one.`),n[i]=a)}for(const[i,r]of Object.entries(be)){const a=ye(t,i,r,"identify()",e);a&&(n[i]=a)}let s=0;for(const[i,r]of Object.entries(t)){if(oo.has(i)||r===void 0||typeof r=="function")continue;if(s>=te){e(`identify() was given more than ${te} extra fields; the rest were dropped.`);break}const a=st(r);if(!a.ok){e(`identify().${i} could not be sent (${L(r)} too large or not serialisable) — dropped.`);continue}n[i]=a.value,s+=1}}catch{}return!n.id&&!n.email&&e("identify() was called without an id or email — reports will not be attributed to anyone, and nothing can be looked up by user."),n}function lt(t,e,n=()=>{}){if(e===null)return{};const o={...t||{}};if(e===void 0)return o;if(typeof e!="object"||Array.isArray(e))return n(`setContext() takes an object like { loanId, screen } or null, got ${L(e)} — ignored.`),o;try{const s=new Map;for(const[i,r]of Object.entries(be))for(const a of r)s.set(a,i);for(const[i,r]of Object.entries(e)){const a=s.get(i);if(r==null){delete o[a||i];continue}if(a){const l=ye({[i]:r},a,[i],"setContext()",n);l?o[a]=l:delete o[a];continue}if(typeof r=="function")continue;if(!(i in o)&&Object.keys(o).length>=te){n(`setContext() holds at most ${te} keys; "${i}" was dropped.`);continue}const c=st(r);if(!c.ok){n(`setContext().${i} could not be sent (${L(r)} too large or not serialisable) — dropped.`);continue}o[i]=c.value}}catch{}return o}const ut=["apiBase","apiKey","projectId","consoleLimit","networkLimit","captureConsole","captureNetwork","captureErrors","widget","widgetPosition","theme","accent","attachments","maxAttachments","maxAttachmentBytes","maxRecordingMs","beforeSend","user","context","updates","updateToast","onUpdate","voice","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError","debug","drafts","knownIssues","agentIssues","release","environment","buildId","chunkBase","announcements"],io=["beforeSend","onUpdate","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError"];function ro(t){const e=t.toLowerCase().replace(/[-_]/g,"");return ut.find(n=>n.toLowerCase()===e)||null}const dt=/^(your[_-]|<|xxx|todo|changeme)/i,so=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function ao(t,{pageHost:e=""}={}){const n=[],o=[];if(!t||typeof t!="object")return n.push("init() takes an options object: BugCatch.init({ apiBase, apiKey, projectId })."),{errors:n,warnings:o};const{apiKey:s,projectId:i,apiBase:r}=t;s?typeof s!="string"?n.push(`apiKey must be a string, got ${L(s)}.`):dt.test(s)?n.push(`apiKey is still the placeholder "${s}" — paste the key you saved when you created it.`):so.test(s)?o.push("apiKey looks like a project id — did you swap apiKey and projectId?"):s.startsWith("bc_")||o.push('apiKey does not look like a BugCatch key (they start with "bc_live_") — reports will be refused with HTTP 401.'):n.push("apiKey is required — create one under Workspace Settings → API Keys."),i?typeof i!="string"?n.push(`projectId must be a string, got ${L(i)}.`):dt.test(i)?n.push(`projectId is still the placeholder "${i}".`):i.startsWith("bc_")?o.push("projectId looks like an API key — did you swap apiKey and projectId?"):/\s/.test(i)&&o.push("projectId contains whitespace — check it was copied cleanly."):n.push("projectId is required — copy it from Project Settings → Web SDK."),r==null?!e||/^(localhost|127\.|0\.0\.0\.0|\[::1\])/.test(e)||o.push("apiBase is not set, so reports go to http://localhost:3000 — set apiBase to your BugCatch API URL."):(typeof r!="string"||!/^https?:\/\//.test(r))&&o.push(`apiBase should be an absolute http(s) URL, got ${JSON.stringify(r)}.`);for(const a of Object.keys(t)){if(ut.includes(a))continue;const c=ro(a);o.push(c?`unknown option "${a}" — did you mean "${c}"?`:`unknown option "${a}" is ignored.`)}for(const a of io){const c=t[a];c!=null&&typeof c!="function"&&o.push(`${a} should be a function, got ${L(c)} — it will not be called.`)}return{errors:n,warnings:o}}function co(t){return typeof t=="string"?t.replace(/\/+$/,""):t}const X={name:"@sphoro/bugcatch-web-sdk",version:"0.19.0"},q={apiBase:"http://localhost:3000",apiKey:null,projectId:null,consoleLimit:100,networkLimit:50,captureConsole:!0,captureNetwork:!0,captureErrors:!0,widget:!0,widgetPosition:"bottom-right",theme:"auto",accent:null,announcements:!0,attachments:!0,maxAttachments:5,maxAttachmentBytes:25*1024*1024,maxRecordingMs:12e4,beforeSend:null,user:null,updates:!0,updateToast:!0,onUpdate:null,voice:!0,onCallState:null,onIncomingCall:null,onReportSubmitted:null,onAssistantPhase:null,onError:null,context:null,chunkBase:null,debug:!1};function lo(t){const e=C();return e?{...t,watchToken:e}:t}class ht{constructor(){gt(this,d);this.config={...q},this.started=!1,this.teardown=[],this.consoleBuffer=W(q.consoleLimit),this.networkBuffer=W(q.networkLimit),this.stopWatching=null,this.notifier=null,this.call=null,this.callStarting=!1,this.ringing=null,this.listeners=new Map,this.context={},this.warned=new Set,this.phases=new Map}warn(e){this.warned.has(e)||(this.warned.add(e),console.warn(`[BugCatch] ${e}`))}debugLog(...e){this.config.debug&&console.info("[BugCatch]",...e)}init(e={}){if(this.started)return console.warn("[BugCatch] init() called twice — ignoring the second call."),this;const{errors:n,warnings:o}=ao(e,{pageHost:typeof location<"u"?location.hostname:""});if(n.length)throw new Error(`[BugCatch] ${n[0]}`);for(const i of o)this.warn(i);const s=this.config.user;return this.config={...q,...e,apiBase:co(e.apiBase??q.apiBase)},this.config.user=e.user!==void 0?ct(e.user,i=>this.warn(i.replace("identify()","init({ user })"))):s||null,e.context&&this.setContext(e.context),this.consoleBuffer=W(this.config.consoleLimit),this.networkBuffer=W(this.config.networkLimit),this.config.captureConsole&&this.teardown.push(Tt(this.consoleBuffer)),this.config.captureNetwork&&this.teardown.push(At(this.networkBuffer)),this.config.captureErrors&&this.teardown.push(jt(this.consoleBuffer)),rn(this.config.projectId),this.config.widget&&this.teardown.push(Jt(this)),this.config.updates&&C()&&(this.startWatching(),queueMicrotask(()=>m(this,d,K).call(this))),this.started=!0,m(this,d,mt).call(this),this.debugLog("initialised",{version:X.version,apiBase:this.config.apiBase,projectId:this.config.projectId,user:this.config.user,context:this.context}),this}startWatching(){if(this.stopWatching)return this;this.stopWatching=yn({apiBase:this.config.apiBase,onUpdate:n=>{if(n!=null&&n.incomingCall){m(this,d,xt).call(this,n);return}if(n!=null&&n.callWithdrawn){m(this,d,re).call(this,n.callWithdrawn.callId);return}const o=an(n);typeof this.config.onUpdate=="function"&&this.config.onUpdate(o),this.emit("update",o),m(this,d,K).call(this)}}),this.teardown.push(()=>{var n;return(n=this.stopWatching)==null?void 0:n.call(this)}),this.teardown.push(()=>m(this,d,re).call(this));const e=n=>{(n.key===Z||n.key===null)&&m(this,d,K).call(this)};return window.addEventListener("storage",e),this.teardown.push(()=>window.removeEventListener("storage",e)),this}on(e,n){if(typeof n!="function")return()=>{};const o=this.listeners.get(e)||new Set;return o.add(n),this.listeners.set(e,o),()=>this.off(e,n)}off(e,n){var o;return(o=this.listeners.get(e))==null||o.delete(n),this}emit(e,n){for(const o of this.listeners.get(e)||[])try{o(n)}catch(s){console.error(`[BugCatch] a '${e}' listener threw:`,s)}}unreadUpdates(){return F()}markUpdateRead(e){return cn(e),m(this,d,K).call(this),this}markReportRead(e){return ln(e),m(this,d,K).call(this),this}markUpdatesRead(){return Ve(),m(this,d,K).call(this),this}async verifyFix(e,{result:n,resolvedAt:o,note:s}){const i=n==="STILL_HAPPENING"?await m(this,d,ie).call(this,{}):{};return jn({apiBase:this.config.apiBase,bugId:e,result:n,resolvedAt:o,note:s,metadata:i.metadata,logs:i.logs})}async rate(e,{score:n,comment:o}={}){return An({apiBase:this.config.apiBase,bugId:e,score:n,comment:o})}async myReport(e){const n=await hn({apiBase:this.config.apiBase,bugId:e});return m(this,d,wt).call(this,e,n==null?void 0:n.assistant),n}replyToReport(e,{body:n,attachments:o}={}){return fn({apiBase:this.config.apiBase,bugId:e,body:n,attachments:o})}async canCallNow(){return!!await this.callKind()}async callKind(){return(await this.callKinds()).live?"live":null}async callKinds(){var n;const e={agent:!1,live:!1};return!this.config.voice||!((n=this.config.user)!=null&&n.email)?e:this.kinds!==void 0?this.kinds:(this.kinds=await pn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.kinds)}canCall(e){return this.config.voice?Ze({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}async startCall(e){var n;if(!this.config.voice)throw new Error("[BugCatch] calling is switched off.");if(this.call||this.callStarting)throw new Error("[BugCatch] a call is already running.");this.callStarting=!0;try{if(!await this.callKind())throw new Error((n=this.config.user)!=null&&n.email?"[BugCatch] calling is not available on this project.":"[BugCatch] a call needs to know who is calling — call identify() with an email first.");return await m(this,d,yt).call(this,e)}finally{this.callStarting=!1}}startLiveCall(e){return this.startCall(e)}canCallTeam(e){return this.config.voice?Ze({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}endCall(){var e;return(e=this.call)==null||e.hangUp(),this.call=null,this}myReports(){return dn({apiBase:this.config.apiBase})}hasReports(){return!!C()}forgetReports(){var e;return(e=this.stopWatching)==null||e.call(this),this.stopWatching=null,this.endCall(),N(),m(this,d,K).call(this),this}identify(e){const n=ct(e,o=>this.warn(o));return e!=null&&!n?this:(this.config.user=n,this.debugLog("identify",n),this)}setContext(e){return this.context=lt(this.context,e,n=>this.warn(n)),this.debugLog("context",this.context),this}getContext(){return{...this.context}}open(){return this.started?this.widgetControls?(this.widgetControls.open(),this):(this.warn("open() needs the built-in widget; it was turned off with widget: false."),this):(this.warn("open() was called before init() — nothing to open yet."),this)}close(){var e;return(e=this.widgetControls)==null||e.close(),this}isOpen(){var e;return!!((e=this.widgetControls)!=null&&e.isOpen())}async uploadAttachment(e,n,o){if(!this.started)throw new Error("[BugCatch] call init() first.");return tn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,blob:e,mime:n||(e==null?void 0:e.type),onProgress:o})}async report({title:e,description:n="",priority:o="MEDIUM",metadata:s={},attachments:i=[],references:r=[],context:a=null,issue:c=null}={}){var j,A,D,f,k,p,b,I;if(!this.started)throw new Error("[BugCatch] call init() first.");if(typeof e!="string"||!e.trim())throw new Error("[BugCatch] a title is required.");["LOW","MEDIUM","HIGH","CRITICAL"].includes(String(o).toUpperCase())||(this.warn(`priority "${o}" is not one of LOW, MEDIUM, HIGH, CRITICAL — sent as MEDIUM.`),o="MEDIUM");const l=(i||[]).filter(g=>(g==null?void 0:g.storageKey)&&de(g.mime,Je)).map(g=>({type:g.type==="VIDEO"?"VIDEO":"SCREENSHOT",storageKey:g.storageKey,mime:de(g.mime,Je)})),h=(Array.isArray(r)?r:[]).filter(g=>g&&typeof g.key=="string"&&g.key&&(typeof g.value=="string"||typeof g.value=="number")).map(g=>({key:g.key.slice(0,64),value:String(g.value).trim().slice(0,120)})).filter(g=>g.value);Array.isArray(r)&&h.length<r.length&&this.warn("report({ references }) takes [{ key, value }] with string values; malformed entries were dropped.");let u=await m(this,d,ie).call(this,{title:e.trim(),description:typeof n=="string"?n:String(n??""),priority:String(o).toUpperCase(),metadata:s,media:l,references:h,context:a,issue:typeof c=="string"&&c?c.slice(0,32):null});const B=m(this,d,ve).call(this,u);if(B===!1)return{cancelled:!0};u=B,this.debugLog("sending report",{title:u.title,user:(j=u.metadata)==null?void 0:j.user,context:(A=u.metadata)==null?void 0:A.context,references:u.references,console:(k=(f=(D=u.logs)==null?void 0:D.console)==null?void 0:f.entries)==null?void 0:k.length,network:(I=(b=(p=u.logs)==null?void 0:p.network)==null?void 0:b.entries)==null?void 0:I.length});const y=R(this.config.projectId);let w;try{w=await fetch(`${this.config.apiBase}/ingest/bugs`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey,...y!=null&&y.token?{"X-BugCatch-Session":y.token}:{}},body:JSON.stringify(lo(u))})}catch(g){const S=new Error(`[BugCatch] could not reach ${this.config.apiBase} — check apiBase, and that this site's origin is allowed by the API.`);throw S.cause=g,m(this,d,oe).call(this,"report",S),S}if(w.status===401&&y&&we(this.config.projectId),!w.ok){let g="";try{const O=await w.json();g=Array.isArray(O.message)?O.message.join(", "):O.message||""}catch{}w.status===401&&!y&&(g||(g="the apiKey was not accepted")),w.status===403&&(g||(g="the apiKey is not allowed to report to this projectId"));const S=w.status===429?new Error(`[BugCatch] ${_(g)}`):new Error(`[BugCatch] report failed (HTTP ${w.status})${g?": "+g:""}`);throw S.status=w.status,m(this,d,oe).call(this,"report",S),S}const M=await w.json();return m(this,d,ke).call(this,M,{title:u.title??e.trim(),via:"form"}),M.watchToken&&(fe(M.watchToken),this.config.updates&&this.startWatching()),M}loginState(){var e;return Rn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:(e=R(this.config.projectId))==null?void 0:e.token})}referenceFields(){return this.refFields?this.refFields:(this.refFields=Pn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.refFields)}agentIssues(){return this.agentIssueList?this.agentIssueList:(this.agentIssueList=_n({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.agentIssueList)}async announcements(){const e=await Fn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId});return Xn(e)}dismissAnnouncement(e){qn(e)}signedInAs(){const e=R(this.config.projectId);return e?{email:e.email,name:e.name}:null}signIn(e,{name:n}={}){var o;return Mn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,name:n??((o=this.config.user)==null?void 0:o.name)??null})}async confirmSignIn(e,n){var s;const o=await Kn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,code:n});return(s=this.config.user)!=null&&s.email||(this.config.user={email:o.email,name:o.name||void 0}),this.emit("signin",{email:o.email,name:o.name}),{email:o.email,name:o.name}}async signOut(){var o;const e=R(this.config.projectId),n=await Un({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:e==null?void 0:e.token});return e!=null&&e.email&&((o=this.config.user)==null?void 0:o.email)===e.email&&(this.config.user=null),this.emit("signout",{email:(e==null?void 0:e.email)||null}),n}knownIssues(){var e;return $n({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,sessionToken:(e=R(this.config.projectId))==null?void 0:e.token})}matchKnownIssues(e,{signal:n}={}){var o;return Bn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,q:e||"",sessionToken:(o=R(this.config.projectId))==null?void 0:o.token,signal:n})}async meToo(e,{email:n,name:o}={}){var r,a;const s=R(this.config.projectId),i=await Ee();return En({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,bugId:e,reporterEmail:n||((r=this.config.user)==null?void 0:r.email),reporterName:o||((a=this.config.user)==null?void 0:a.name),metadata:{...i,user:this.config.user||void 0,...Object.keys(this.context).length?{context:this.context}:{},sdk:X},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},sessionToken:s==null?void 0:s.token})}snapshot(){return{user:this.config.user||null,context:{...this.context},console:this.consoleBuffer.all(),network:this.networkBuffer.all(),environment:Te()}}clear(){this.consoleBuffer.clear(),this.networkBuffer.clear()}destroy(){this.endCall(),this.teardown.forEach(e=>{try{e()}catch{}}),this.teardown=[],this.notifier=null,this.started=!1}}d=new WeakSet,ne=function(e,n){const o=this.config[e];if(typeof o=="function")try{o(n)}catch(s){console.error(`[BugCatch] your ${e} callback threw:`,s)}},mt=function(){if(typeof window>"u"||typeof fetch!="function")return;const e=`bugcatch:hello:${this.config.projectId}`,n=new Date().toISOString().slice(0,10);try{if(window.localStorage.getItem(e)===n)return}catch{}const o=setTimeout(()=>{try{window.localStorage.setItem(e,n)}catch{}fetch(`${this.config.apiBase}/ingest/hello`,{method:"POST",keepalive:!0,headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey},body:JSON.stringify({projectId:this.config.projectId,version:X.version,identify:!!this.config.user,context:!!(this.context&&Object.keys(this.context).length),widget:!!this.config.widget,environment:this.config.environment||null,release:this.config.release||null})}).catch(()=>{})},5e3);this.teardown.push(()=>clearTimeout(o))},K=function(){const e=F();if(this.emit("unread",{count:e.length,updates:e}),!!this.config.updateToast){if(!this.notifier){if(!e.length)return;this.notifier=Sn({accent:this.config.accent,position:this.config.widgetPosition,onRead:n=>this.markUpdateRead(n),onReadAll:()=>this.markUpdatesRead(),onCall:this.config.voice?n=>{this.markUpdateRead(n.id),this.startCall(n.bugId)}:null}),this.teardown.push(()=>{var n;return(n=this.notifier)==null?void 0:n.destroy()})}this.notifier.render(e)}},wt=function(e,n){const o=n==null?void 0:n.phase;if(!e||typeof o!="string"||this.phases.get(e)===o)return;this.phases.set(e,o);const s={bugId:e,phase:o,title:n.title??null,detail:n.detail??null};this.debugLog("assistant",s),m(this,d,ne).call(this,"onAssistantPhase",s),this.emit("assistant",s)},ke=function(e,{title:n,via:o}){if(!(e!=null&&e.id))return;const s={bugId:e.id,title:n??null,via:o,result:e};this.debugLog("report accepted",s),m(this,d,ne).call(this,"onReportSubmitted",s),this.emit("report",s)},oe=function(e,n){m(this,d,ne).call(this,"onError",{action:e,error:n}),this.emit("error",{action:e,error:n})},ie=async function({title:e,description:n="",priority:o="MEDIUM",metadata:s={},media:i=[],references:r=[],context:a=null,issue:c=null}){var h,u;const l=a?lt(this.context,a,B=>this.warn(B.replace("setContext()","report({ context })"))):this.context;return{projectId:this.config.projectId,...e?{title:e}:{},description:n,priority:o,url:window.location.href,reporterEmail:(h=this.config.user)==null?void 0:h.email,reporterName:(u=this.config.user)==null?void 0:u.name,metadata:{...await Ee(),release:this.config.release,environment:this.config.environment,buildId:this.config.buildId,...s,user:this.config.user||void 0,...Object.keys(l).length?{context:l}:{},sdk:X},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},...i.length?{attachments:i}:{},...r.length?{references:r}:{},...c?{issue:c}:{}}},ve=function(e){if(typeof this.config.beforeSend!="function")return e;let n;try{n=this.config.beforeSend(e)}catch(o){return this.warn(`beforeSend threw, so nothing was sent: ${(o==null?void 0:o.message)||o}`),!1}return n===!1?!1:n&&typeof n=="object"?n:e},bt=async function(){const e=R(this.config.projectId),n=m(this,d,ve).call(this,await m(this,d,ie).call(this,{}));if(n===!1)return this.debugLog("call cancelled by beforeSend"),{cancelled:!0};let o;try{o=await gn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,token:e==null?void 0:e.token,payload:n,watchToken:C()})}catch(s){throw s.status===401&&e&&we(this.config.projectId),m(this,d,oe).call(this,"call",s),s}return m(this,d,ke).call(this,o,{title:null,via:"call"}),o.watchToken&&(fe(o.watchToken),this.config.updates&&this.startWatching()),o},yt=async function(e){const{joinLiveCall:n,liveCallSupported:o,openMicrophone:s}=await J(this.config.chunkBase);if(!o())throw new Error("[BugCatch] this browser cannot make live calls.");const i=await s();let r,a=null;try{if(e)r=await mn({apiBase:this.config.apiBase,bugId:e});else if(a=await m(this,d,bt).call(this),e=a.id,r=a.live_call,!(r!=null&&r.realtime_url)){const l=new Error(`[BugCatch] the report was filed, but the team could not be rung${a.call_error?`: ${a.call_error}`:"."}`);throw l.filed=a,l.reason=a.call_error||null,l}}catch(l){for(const h of i.getTracks())h.stop();throw l}const c=await n({realtimeUrl:r.realtime_url,stream:i,ringsUntil:r.rings_until,onState:(l,h)=>{(l==="ended"||l==="failed"||l==="missed")&&(this.call=null),l==="missed"&&wn({apiBase:this.config.apiBase,bugId:e,callId:r.call_id}),typeof this.config.onCallState=="function"&&this.config.onCallState(l,{bugId:e,live:!0,...h}),this.emit("call",{bugId:e,live:!0,state:l,...h})}});return this.call=c,Object.assign(c,{bugId:e,callId:r.call_id,live:!0,filed:a,recorded:!!r.recorded})},xt=function(e){var s;const n=e.incomingCall;if(!this.config.voice||((s=this.ringing)==null?void 0:s.callId)===n.callId||this.call||this.callStarting)return;m(this,d,re).call(this);const o={callId:n.callId,dismiss:()=>{}};this.ringing=o,J(this.config.chunkBase).then(({liveCallSupported:i,showIncomingCall:r})=>{if(this.ringing!==o||(this.ringing=null,!i())||this.call||this.callStarting)return;const a=r({call:n,accent:this.config.accent,position:this.config.widgetPosition,touched:Nn,onAnswer:()=>{m(this,d,kt).call(this,e.bugId,n)},onDecline:()=>{this.ringing=null,Ge({apiBase:this.config.apiBase,bugId:e.bugId,callId:n.callId}),this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"declined"})},onExpire:()=>{this.ringing=null,this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"missed"})}});if(this.ringing={callId:n.callId,dismiss:a},typeof this.config.onIncomingCall=="function")try{this.config.onIncomingCall({bugId:e.bugId,...n})}catch{}this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"ringing"})},()=>{this.ringing===o&&(this.ringing=null)})},re=function(e){if(this.ringing&&!(e&&this.ringing.callId!==e)){try{this.ringing.dismiss()}catch{}this.ringing=null}},kt=async function(e,n){if(this.ringing=null,this.call||this.callStarting)return;this.callStarting=!0;let o,s;try{const r=await J(this.config.chunkBase);s=r.joinLiveCall,o=await r.openMicrophone()}catch(r){this.callStarting=!1,Ge({apiBase:this.config.apiBase,bugId:e,callId:n.callId}),this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r});return}let i;try{i=await bn({apiBase:this.config.apiBase,bugId:e,callId:n.callId})}catch(r){for(const a of o.getTracks())a.stop();this.callStarting=!1,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"missed",error:r});return}try{const r=await s({realtimeUrl:i.realtime_url,stream:o,onState:(a,c)=>{(a==="ended"||a==="failed"||a==="missed")&&(this.call=null),typeof this.config.onCallState=="function"&&this.config.onCallState(a,{bugId:e,live:!0,incoming:!0,...c}),this.emit("call",{bugId:e,live:!0,incoming:!0,state:a,...c})}});this.call=r,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"answered"})}catch(r){for(const a of o.getTracks())a.stop();this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r})}finally{this.callStarting=!1}};const xe=new ht;return xe.Client=ht,xe.version=X.version,xe});
309
+ `)}return n}async function dn({apiBase:t}){const e=C();if(!e)return{reporter:null,reports:[]};const n=await fetch(`${t}/ingest/watch/reports${V()}`,{headers:{Authorization:`Bearer ${e}`}});if(n.status===401)return N(),{reporter:null,reports:[]};if(!n.ok)throw new Error(`[BugCatch] could not load your reports (HTTP ${n.status})`);return n.json()}async function hn({apiBase:t,bugId:e}){const n=C();if(!n)return null;const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}${V()}`,{headers:{Authorization:`Bearer ${n}`}});if(o.status===401)return N(),null;if(o.status===404)return null;if(!o.ok)throw new Error(`[BugCatch] could not load that report (HTTP ${o.status})`);return o.json()}async function fn({apiBase:t,bugId:e,body:n,attachments:o}){const s=C();if(!s)throw new Error("[BugCatch] nothing filed from this browser yet.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/messages`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({...n?{body:n}:{},...o!=null&&o.length?{attachments:o}:{}})});if(i.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!i.ok){let r="";try{const a=await i.json();r=Array.isArray(a.message)?a.message.join(", "):a.message||""}catch{}throw i.status===429?new Error(_(r)):new Error(r||`[BugCatch] could not send that (HTTP ${i.status}).`)}return i.json()}async function pn({apiBase:t,apiKey:e,projectId:n}){const o={agent:!1,live:!1};try{const s=await fetch(`${t}/ingest/calls?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!s.ok)return o;const i=await s.json();return i!=null&&i.available?i.kinds&&typeof i.kinds=="object"?{agent:!1,live:!!i.kinds.live}:{agent:!1,live:i.kind==="live"}:o}catch{return o}}async function gn({apiBase:t,apiKey:e,token:n,payload:o,watchToken:s}){const i=await fetch(`${t}/ingest/calls`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":e,...n?{"X-BugCatch-Session":n}:{}},body:JSON.stringify(s?{...o,watchToken:s}:o)});if(!i.ok){let r="";try{const c=await i.json();r=Array.isArray(c.message)?c.message.join(", "):c.message||""}catch{}const a=new Error(i.status===429?_(r):r||`[BugCatch] the call could not be started (HTTP ${i.status}).`);throw a.status=i.status,a}return i.json()}async function Ze({apiBase:t,bugId:e}){const n=C();if(!n)return!1;try{const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)return!1;const s=await o.json();return!!(s!=null&&s.offered)}catch{return!1}}async function mn({apiBase:t,bugId:e}){const n=C();if(!n)throw new Error("[BugCatch] nothing filed from this browser yet.");const o=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call`,{method:"POST",headers:{Authorization:`Bearer ${n}`}});if(o.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(!o.ok){let s="";try{const i=await o.json();s=Array.isArray(i.message)?i.message.join(", "):i.message||""}catch{}throw new Error(s||`[BugCatch] the call could not be started (HTTP ${o.status}).`)}return o.json()}async function wn({apiBase:t,bugId:e,callId:n}){const o=C();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/give-up`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}async function bn({apiBase:t,bugId:e,callId:n}){const o=C();if(!o)throw new Error("[BugCatch] nothing filed from this browser yet.");const s=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/answer`,{method:"POST",headers:{Authorization:`Bearer ${o}`}});if(s.status===401)throw N(),new Error("[BugCatch] this browser can no longer prove it filed that report.");if(s.status===409)throw new Error("[BugCatch] that call is no longer ringing.");if(!s.ok){let i="";try{const r=await s.json();i=Array.isArray(r.message)?r.message.join(", "):r.message||""}catch{}throw new Error(i||`[BugCatch] the call could not be answered (HTTP ${s.status}).`)}return s.json()}async function Ge({apiBase:t,bugId:e,callId:n}){const o=C();if(o)try{await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/live-call/${encodeURIComponent(n)}/decline`,{method:"POST",headers:{Authorization:`Bearer ${o}`}})}catch{}}function yn({apiBase:t,onUpdate:e}){let n=!0,o=0,s=null;async function i(){const r=C();if(!r)throw new Error("nothing filed from this browser yet");s=new AbortController;const a=await fetch(`${t}/ingest/watch/stream${V()}`,{headers:{Authorization:`Bearer ${r}`,Accept:"text/event-stream"},signal:s.signal});if(a.status===401)throw n=!1,N(),new Error("subscription no longer valid");if(!a.ok||!a.body)throw new Error(`stream failed (${a.status})`);const c=Date.now(),l=a.body.getReader(),h=new TextDecoder;let u="";try{for(;;){const{value:B,done:y}=await l.read();if(y)break;u+=h.decode(B,{stream:!0}),u=un(u,w=>{if(w.type!=="ping")try{e(w)}catch{}})}}finally{Date.now()-c>=on&&(o=0)}}return(async()=>{for(;n;){try{await i()}catch(a){if((a==null?void 0:a.name)==="AbortError")return}if(!n)return;const r=Ye[Math.min(o,Ye.length-1)];o+=1,await new Promise(a=>setTimeout(a,r))}})(),()=>{n=!1,s==null||s.abort(),s=null}}const xn={OPEN:"is open",IN_PROGRESS:"is being worked on",RESOLVED:"has been fixed",CLOSED:"has been closed"},kn={status:null,severity:"#f2a54a",assignment:"#79a8ff",reply:"#4fd19a"},T={bg:"#111113",fg:"#f3f2ef",muted:"#b3b0aa",faint:"#8f8c86",ring:"rgba(255,255,255,.09)",brand:"#a59cff"};function Qe(t){return t!=null&&t.summary?t.summary:`Your report ${xn[t==null?void 0:t.status]||"was updated"}`}function vn(t){var e;return(t==null?void 0:t.kind)==="reply"&&((e=t.reply)!=null&&e.body)?`${t.reply.author?`${t.reply.author}: `:""}${t.reply.body}`:(t==null?void 0:t.title)||""}function Cn(t){const e=Date.parse(t||"");if(!Number.isFinite(e))return"";const n=Math.round((Date.now()-e)/6e4);if(n<1)return"just now";if(n<60)return`${n}m ago`;const o=Math.round(n/60);if(o<24)return`${o}h ago`;const s=Math.round(o/24);return s===1?"yesterday":`${s}d ago`}function In(){var t;try{const e=document.querySelector('[data-bugcatch="widget"]'),n=(t=e==null?void 0:e.shadowRoot)==null?void 0:t.querySelector(".dock"),o=(n==null?void 0:n.getBoundingClientRect().height)||0;return o?Math.round(o)+12:0}catch{return 0}}function Sn({accent:t=null,position:e="bottom-right",onRead:n=null,onReadAll:o=null,onCall:s=null}={}){const[i,r]=String(e).split("-"),a=i==="top",c=r==="left",l=new Map;let h=null,u=null;function B(){return h!=null&&h.isConnected||(h=document.createElement("div"),h.setAttribute("data-bugcatch","updates"),h.style.cssText=["position:fixed",`${a?"top":"bottom"}:${20+In()}px`,`${c?"left":"right"}:20px`,"z-index:2147482998","display:flex","flex-direction:column","gap:10px",`align-items:${c?"flex-start":"flex-end"}`,"width:min(320px,calc(100vw - 40px))","pointer-events:none"].join(";"),document.body.appendChild(h)),h}function y(f){if(!f._bcShown){f.remove();return}f.style.opacity="0",f.style.transform=`translateY(${a?-8:8}px)`,setTimeout(()=>f.remove(),250)}const w=t||ue,M=t?He(t):De;function j(f,k){const p=document.createElement("button");return p.type="button",p.textContent=f,p.style.cssText=["padding:6px 12px","border:0",`box-shadow:${k?"none":`inset 0 0 0 1px ${T.ring}`}`,"border-radius:999px",`background:${k?w:"rgba(255,255,255,.06)"}`,`color:${k?M:T.fg}`,"font:560 12px/1.4 inherit","cursor:pointer","white-space:nowrap"].join(";"),p}function A(f){var P;const k=document.createElement("div");k.setAttribute("role","status"),k.style.cssText=["box-sizing:border-box","width:100%","padding:13px 14px","border-radius:14px",`background:${T.bg}`,`color:${T.fg}`,'font:13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${T.ring},0 20px 44px -12px rgba(0,0,0,.55),0 4px 12px rgba(0,0,0,.25)`,"opacity:0",`transform:translateY(${a?-8:8}px)`,"transition:opacity .2s ease,transform .2s ease","pointer-events:auto"].join(";");const p=document.createElement("div");p.style.cssText="display:flex;align-items:baseline;gap:8px;font-weight:600;margin-bottom:2px";const b=document.createElement("span");b.setAttribute("aria-hidden","true"),b.style.cssText=`flex:none;width:7px;height:7px;border-radius:50%;transform:translateY(-1px);background:${kn[f==null?void 0:f.kind]||t||T.brand}`;const I=document.createElement("span");I.textContent=Qe(f),p.append(b,I);const g=document.createElement("div");g.style.cssText=(f==null?void 0:f.kind)==="reply"?`color:${T.muted};padding-left:15px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden`:`color:${T.muted};padding-left:15px`,g.textContent=vn(f);const S=document.createElement("div");S.style.cssText="display:flex;align-items:center;gap:8px;margin-top:10px;padding-left:15px;flex-wrap:wrap";const O=document.createElement("span");O.style.cssText=`color:${T.faint};font-size:11px;margin-right:auto`,O.textContent=Cn(f==null?void 0:f.at);const x=j("Mark as read",!1);if(x.setAttribute("aria-label",`Mark as read: ${Qe(f)}`),x.addEventListener("click",()=>n==null?void 0:n(f.id)),S.append(O,x),(P=f==null?void 0:f.call)!=null&&P.offered&&typeof s=="function"){const ft=j("Talk it through",!0);ft.addEventListener("click",()=>s(f)),S.insertBefore(ft,x)}return k.append(p,g,S),k}function D(f){const k=document.createElement("div");k.style.cssText=["box-sizing:border-box","width:100%","display:flex","align-items:center","gap:8px","padding:8px 10px 8px 14px","border-radius:999px",`background:${T.bg}`,`color:${T.muted}`,'font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif',`box-shadow:0 0 0 1px ${T.ring},0 12px 30px -10px rgba(0,0,0,.5)`,"pointer-events:auto"].join(";");const p=document.createElement("span");p.style.cssText="margin-right:auto",p.textContent=`${f} more update${f===1?"":"s"}`;const b=j("Mark all as read",!1);return b.style.cssText+=";padding:4px 9px",b.addEventListener("click",()=>o==null?void 0:o()),k.append(p,b),k}return{render(f){const k=Array.isArray(f)?f:[];if(!k.length){for(const x of l.values())y(x);l.clear(),u==null||u.remove(),u=null,h==null||h.remove(),h=null;return}const p=k.slice(-3),b=new Set(p.map(x=>x.id)),I=B();for(const[x,P]of l)b.has(x)||(y(P),l.delete(x));const g=[];for(const x of p){if(l.has(x.id))continue;const P=A(x);l.set(x.id,P),g.push(P)}const S=a?[...p].reverse():p;for(const x of S)I.appendChild(l.get(x.id));const O=k.length-p.length;O>0?(u==null||u.remove(),u=D(O),a?I.appendChild(u):I.insertBefore(u,I.firstChild)):(u==null||u.remove(),u=null),requestAnimationFrame(()=>{for(const x of g)x.isConnected&&(x._bcShown=!0,x.style.opacity="1",x.style.transform="translateY(0)")})},destroy(){l.clear(),u=null,h==null||h.remove(),h=null}}}async function $n({apiBase:t,apiKey:e,projectId:n,sessionToken:o}){try{const s=await fetch(`${t}/ingest/known-issues?projectId=${encodeURIComponent(n)}`,{headers:pe(e,o)});if(!s.ok)return[];const i=await s.json();return Array.isArray(i==null?void 0:i.issues)?i.issues:[]}catch{return[]}}async function Bn({apiBase:t,apiKey:e,projectId:n,q:o,sessionToken:s,signal:i}){try{const r=`${t}/ingest/known-issues/match?projectId=${encodeURIComponent(n)}&q=${encodeURIComponent(o)}`,a=await fetch(r,{headers:pe(e,s),signal:i});if(!a.ok)return[];const c=await a.json();return Array.isArray(c==null?void 0:c.issues)?c.issues:[]}catch{return[]}}async function En({apiBase:t,apiKey:e,projectId:n,bugId:o,reporterEmail:s,reporterName:i,metadata:r,logs:a,sessionToken:c}){const l=C(),h=await fetch(`${t}/ingest/known-issues/${encodeURIComponent(o)}/join`,{method:"POST",headers:pe(e,c),body:JSON.stringify({projectId:n,reporterEmail:s,reporterName:i,metadata:r,logs:a,...l?{watchToken:l}:{}})});if(!h.ok)throw new Error(await Tn(h));const u=await h.json();return u!=null&&u.watchToken&&fe(u.watchToken),u}function pe(t,e){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function Tn(t){let e="";try{const n=await t.json();e=Array.isArray(n.message)?n.message.join(", "):n.message||""}catch{}return t.status===429?_(e):e||`Could not add you to this issue (HTTP ${t.status})`}async function An({apiBase:t,bugId:e,score:n,comment:o}){const s=C();if(!s)throw new Error("This browser cannot access that report.");const i=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/rating${V()}`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({score:n,...o?{comment:o}:{}})}),r=await i.json().catch(()=>({}));if(!i.ok){const a=Array.isArray(r.message)?r.message.join(", "):r.message;throw new Error(a||"Could not send your rating. Please try again.")}return r}async function jn({apiBase:t,bugId:e,...n}){const o=C();if(!o)throw new Error("This browser cannot access that report.");const s=await fetch(`${t}/ingest/watch/reports/${encodeURIComponent(e)}/verification`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify(n)}),i=await s.json().catch(()=>({}));if(!s.ok)throw new Error(i.message||"Could not record feedback. Please refresh and try again.");return i}const On="bugcatch.session.",ge=t=>`${On}${t}`,me=new Map;function R(t){var e;if(!t)return null;try{const n=(e=window.localStorage)==null?void 0:e.getItem(ge(t));if(n){const o=JSON.parse(n);if(o!=null&&o.token)return o}}catch{}return me.get(t)||null}function Ln(t,e){var o;if(!t||!(e!=null&&e.token))return;const n={token:e.token,email:e.email||null,name:e.name||null};me.set(t,n);try{(o=window.localStorage)==null||o.setItem(ge(t),JSON.stringify(n))}catch{}}function we(t){var e;if(t){me.delete(t);try{(e=window.localStorage)==null||e.removeItem(ge(t))}catch{}}}function Q({apiKey:t,token:e}){return{"Content-Type":"application/json","X-BugCatch-Key":t,...e?{"X-BugCatch-Session":e}:{}}}async function et(t){let e="";try{const n=await t.json();e=Array.isArray(n.message)?n.message.join(", "):n.message||""}catch{}return t.status===429?_(e):e||`Request failed (HTTP ${t.status})`}async function Rn({apiBase:t,apiKey:e,projectId:n,token:o}){const s={mode:"off",signedIn:null,signedOutReason:null};try{const i=await fetch(`${t}/ingest/login?projectId=${encodeURIComponent(n)}`,{headers:Q({apiKey:e,token:o})});if(!i.ok)return s;const r=await i.json();return{mode:r.mode==="optional"||r.mode==="required"?r.mode:"off",signedIn:r.signedIn||null,signedOutReason:r.signedOutReason||null}}catch{return s}}async function Mn({apiBase:t,apiKey:e,projectId:n,email:o,name:s}){const i=await fetch(`${t}/ingest/login/start`,{method:"POST",headers:Q({apiKey:e}),body:JSON.stringify({projectId:n,email:o,...s?{name:s}:{}})});if(!i.ok)throw new Error(await et(i));return i.json()}async function Kn({apiBase:t,apiKey:e,projectId:n,email:o,code:s}){const i=await fetch(`${t}/ingest/login`,{method:"POST",headers:Q({apiKey:e}),body:JSON.stringify({projectId:n,email:o,code:s})});if(!i.ok)throw new Error(await et(i));const r=await i.json(),a={token:r.sessionToken,email:r.email,name:r.name};return Ln(n,a),a}async function Un({apiBase:t,apiKey:e,projectId:n,token:o}){if(we(n),!o)return{signedOut:!0};try{await fetch(`${t}/ingest/logout`,{method:"POST",headers:Q({apiKey:e,token:o}),body:JSON.stringify({projectId:n})})}catch{}return{signedOut:!0}}let tt=!1;if(typeof window<"u"&&window.addEventListener)for(const t of["pointerdown","keydown","touchstart"])window.addEventListener(t,()=>{tt=!0},{once:!0,capture:!0,passive:!0});function Nn(){return tt}async function Pn({apiBase:t,apiKey:e,projectId:n}){const o={policy:"off",fields:[]};try{const s=await fetch(`${t}/ingest/reference-fields?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!s.ok)return o;const i=await s.json(),r=i.policy==="optional"||i.policy==="required"?i.policy:"off",a=Array.isArray(i.fields)?i.fields:[];return a.length?{policy:r,fields:a}:o}catch{return o}}async function _n({apiBase:t,apiKey:e,projectId:n}){try{const o=await fetch(`${t}/ingest/agent-issues?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}});if(!o.ok)return[];const s=await o.json();return(Array.isArray(s==null?void 0:s.issues)?s.issues:[]).filter(i=>i&&typeof i.key=="string"&&typeof i.title=="string").map(i=>({key:i.key,title:i.title,hint:typeof i.hint=="string"?i.hint:""}))}catch{return[]}}const Dn=5*6e4,nt="bugcatch:announcements:seen",Hn=40,zn=["info","success","warning","critical"],ot=t=>`bugcatch:announcements:${t}`;async function Fn({apiBase:t,apiKey:e,projectId:n,fresh:o=!1}){var s;if(!o){const i=Zn(n);if(i)return i}try{const i=await fetch(`${t}/ingest/announcements?projectId=${encodeURIComponent(n)}`,{headers:{"X-BugCatch-Key":e}}),r=i.ok?Vn((s=await i.json())==null?void 0:s.announcements):[];return Gn(n,r),r}catch{return[]}}function Xn(t,{on:e=Jn(),seen:n=rt()}={}){return(t||[]).filter(o=>Yn(o,e)&&!(o.dismissible&&n.includes(it(o))))}function qn(t){if(!(!(t!=null&&t.id)||!t.dismissible))try{const e=it(t),n=[...rt().filter(o=>o!==e),e];localStorage.setItem(nt,JSON.stringify(n.slice(-Hn)))}catch{}}function Wn(t){return typeof t!="string"||!t||t.startsWith("//")?null:t.startsWith("/")||/^(https?:\/\/|mailto:)./i.test(t)?t:null}function Jn(t=new Date){const e=n=>String(n).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`}function Yn(t,e){return!(t.startsOn&&e<t.startsOn||t.endsOn&&e>t.endsOn)}function it(t){return`${t.id}#${t.revision||0}`}function Vn(t){return(Array.isArray(t)?t:[]).filter(e=>e&&typeof e.id=="string"&&typeof e.title=="string"&&e.title.trim()).map(e=>{const n=e.placement==="launcher"?"launcher":"panel",o=Wn(e.ctaHref);return{id:e.id,revision:Number.isInteger(e.revision)?e.revision:0,title:e.title,body:typeof e.body=="string"?e.body:"",tone:zn.includes(e.tone)?e.tone:"info",ctaLabel:o&&typeof e.ctaLabel=="string"?e.ctaLabel:"",ctaHref:o&&typeof e.ctaLabel=="string"?o:"",placement:n,dismissible:n==="launcher"||e.dismissible!==!1,startsOn:typeof e.startsOn=="string"?e.startsOn:"",endsOn:typeof e.endsOn=="string"?e.endsOn:""}})}function rt(){try{const t=JSON.parse(localStorage.getItem(nt)||"[]");return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}catch{return[]}}function Zn(t){try{const e=JSON.parse(localStorage.getItem(ot(t))||"null");if(!e||!Array.isArray(e.list)||typeof e.at!="number")return null;const n=Date.now()-e.at;return n>=0&&n<Dn?e.list:null}catch{return null}}function Gn(t,e){try{localStorage.setItem(ot(t),JSON.stringify({at:Date.now(),list:e}))}catch{}}const ee=120,Qn=500,te=30,eo=1e3,be={loanId:["loanId","loanNumber"],applicationId:["applicationId"],customerNumber:["customerNumber","customerId"]},to=/[\u0000-\u001f\u007f]/;function no(t){if(typeof t=="number")return Number.isFinite(t)?String(t):null;if(typeof t!="string")return null;const e=t.trim();return!e||e.length>ee||to.test(e)?null:e}function L(t){return t===null?"null":Array.isArray(t)?"an array":typeof t=="object"?"an object":`a ${typeof t}`}function ye(t,e,n,o,s){for(const i of n){if(!(i in t)||t[i]===void 0||t[i]===null||t[i]==="")continue;const r=no(t[i]);if(r)return r;const a=t[i];s(typeof a=="string"&&a.trim().length>ee?`${o}.${i} is longer than ${ee} characters and was dropped — pass the identifier itself, not a sentence or a URL.`:`${o}.${i} should be a string or number, got ${L(a)} — it was dropped.`)}return null}function st(t){if(t===null||typeof t=="boolean")return{ok:!0,value:t};if(typeof t=="number")return Number.isFinite(t)?{ok:!0,value:t}:{ok:!1};if(typeof t=="string")return{ok:!0,value:t.slice(0,Qn)};if(typeof t=="object")try{const e=JSON.stringify(t);if(e&&e.length<=eo)return{ok:!0,value:JSON.parse(e)}}catch{}return{ok:!1}}const at=["email","name","phone"],oo=new Set(["id","userId",...at,...Object.values(be).flat()]);function ct(t,e=()=>{}){if(t==null)return null;if(typeof t!="object"||Array.isArray(t))return e(`identify() takes an object like { id, email, name } or null, got ${L(t)} — ignored.`),null;const n={};try{const o=ye(t,"id",["id","userId"],"identify()",e);o&&(n.id=o);for(const i of at){const r=t[i];if(r==null||r==="")continue;if(i==="phone"&&typeof r=="number"&&Number.isFinite(r)){n.phone=String(r);continue}if(typeof r!="string"){e(`identify().${i} should be a string, got ${L(r)} — it was dropped.`);continue}const a=r.trim().slice(0,i==="name"?200:ee);a&&(i==="email"&&!a.includes("@")&&e(`identify().email "${a}" does not look like an email address — reports cannot be threaded or followed up without one.`),n[i]=a)}for(const[i,r]of Object.entries(be)){const a=ye(t,i,r,"identify()",e);a&&(n[i]=a)}let s=0;for(const[i,r]of Object.entries(t)){if(oo.has(i)||r===void 0||typeof r=="function")continue;if(s>=te){e(`identify() was given more than ${te} extra fields; the rest were dropped.`);break}const a=st(r);if(!a.ok){e(`identify().${i} could not be sent (${L(r)} too large or not serialisable) — dropped.`);continue}n[i]=a.value,s+=1}}catch{}return!n.id&&!n.email&&e("identify() was called without an id or email — reports will not be attributed to anyone, and nothing can be looked up by user."),n}function lt(t,e,n=()=>{}){if(e===null)return{};const o={...t||{}};if(e===void 0)return o;if(typeof e!="object"||Array.isArray(e))return n(`setContext() takes an object like { loanId, screen } or null, got ${L(e)} — ignored.`),o;try{const s=new Map;for(const[i,r]of Object.entries(be))for(const a of r)s.set(a,i);for(const[i,r]of Object.entries(e)){const a=s.get(i);if(r==null){delete o[a||i];continue}if(a){const l=ye({[i]:r},a,[i],"setContext()",n);l?o[a]=l:delete o[a];continue}if(typeof r=="function")continue;if(!(i in o)&&Object.keys(o).length>=te){n(`setContext() holds at most ${te} keys; "${i}" was dropped.`);continue}const c=st(r);if(!c.ok){n(`setContext().${i} could not be sent (${L(r)} too large or not serialisable) — dropped.`);continue}o[i]=c.value}}catch{}return o}const ut=["apiBase","apiKey","projectId","consoleLimit","networkLimit","captureConsole","captureNetwork","captureErrors","widget","widgetPosition","theme","accent","attachments","maxAttachments","maxAttachmentBytes","maxRecordingMs","beforeSend","user","context","updates","updateToast","onUpdate","voice","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError","debug","drafts","knownIssues","agentIssues","release","environment","buildId","chunkBase","announcements"],io=["beforeSend","onUpdate","onCallState","onIncomingCall","onReportSubmitted","onAssistantPhase","onError"];function ro(t){const e=t.toLowerCase().replace(/[-_]/g,"");return ut.find(n=>n.toLowerCase()===e)||null}const dt=/^(your[_-]|<|xxx|todo|changeme)/i,so=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function ao(t,{pageHost:e=""}={}){const n=[],o=[];if(!t||typeof t!="object")return n.push("init() takes an options object: BugCatch.init({ apiBase, apiKey, projectId })."),{errors:n,warnings:o};const{apiKey:s,projectId:i,apiBase:r}=t;s?typeof s!="string"?n.push(`apiKey must be a string, got ${L(s)}.`):dt.test(s)?n.push(`apiKey is still the placeholder "${s}" — paste the key you saved when you created it.`):so.test(s)?o.push("apiKey looks like a project id — did you swap apiKey and projectId?"):s.startsWith("bc_")||o.push('apiKey does not look like a BugCatch key (they start with "bc_live_") — reports will be refused with HTTP 401.'):n.push("apiKey is required — create one under Workspace Settings → API Keys."),i?typeof i!="string"?n.push(`projectId must be a string, got ${L(i)}.`):dt.test(i)?n.push(`projectId is still the placeholder "${i}".`):i.startsWith("bc_")?o.push("projectId looks like an API key — did you swap apiKey and projectId?"):/\s/.test(i)&&o.push("projectId contains whitespace — check it was copied cleanly."):n.push("projectId is required — copy it from Project Settings → Web SDK."),r==null?!e||/^(localhost|127\.|0\.0\.0\.0|\[::1\])/.test(e)||o.push("apiBase is not set, so reports go to http://localhost:3000 — set apiBase to your BugCatch API URL."):(typeof r!="string"||!/^https?:\/\//.test(r))&&o.push(`apiBase should be an absolute http(s) URL, got ${JSON.stringify(r)}.`);for(const a of Object.keys(t)){if(ut.includes(a))continue;const c=ro(a);o.push(c?`unknown option "${a}" — did you mean "${c}"?`:`unknown option "${a}" is ignored.`)}for(const a of io){const c=t[a];c!=null&&typeof c!="function"&&o.push(`${a} should be a function, got ${L(c)} — it will not be called.`)}return{errors:n,warnings:o}}function co(t){return typeof t=="string"?t.replace(/\/+$/,""):t}const X={name:"@sphoro/bugcatch-web-sdk",version:"0.20.0"},q={apiBase:"http://localhost:3000",apiKey:null,projectId:null,consoleLimit:100,networkLimit:50,captureConsole:!0,captureNetwork:!0,captureErrors:!0,widget:!0,widgetPosition:"bottom-right",theme:"auto",accent:null,announcements:!0,attachments:!0,maxAttachments:5,maxAttachmentBytes:25*1024*1024,maxRecordingMs:12e4,beforeSend:null,user:null,updates:!0,updateToast:!0,onUpdate:null,voice:!0,onCallState:null,onIncomingCall:null,onReportSubmitted:null,onAssistantPhase:null,onError:null,context:null,chunkBase:null,debug:!1};function lo(t){const e=C();return e?{...t,watchToken:e}:t}class ht{constructor(){gt(this,d);this.config={...q},this.started=!1,this.teardown=[],this.consoleBuffer=W(q.consoleLimit),this.networkBuffer=W(q.networkLimit),this.stopWatching=null,this.notifier=null,this.call=null,this.callStarting=!1,this.ringing=null,this.listeners=new Map,this.context={},this.warned=new Set,this.phases=new Map}warn(e){this.warned.has(e)||(this.warned.add(e),console.warn(`[BugCatch] ${e}`))}debugLog(...e){this.config.debug&&console.info("[BugCatch]",...e)}init(e={}){if(this.started)return console.warn("[BugCatch] init() called twice — ignoring the second call."),this;const{errors:n,warnings:o}=ao(e,{pageHost:typeof location<"u"?location.hostname:""});if(n.length)throw new Error(`[BugCatch] ${n[0]}`);for(const i of o)this.warn(i);const s=this.config.user;return this.config={...q,...e,apiBase:co(e.apiBase??q.apiBase)},this.config.user=e.user!==void 0?ct(e.user,i=>this.warn(i.replace("identify()","init({ user })"))):s||null,e.context&&this.setContext(e.context),this.consoleBuffer=W(this.config.consoleLimit),this.networkBuffer=W(this.config.networkLimit),this.config.captureConsole&&this.teardown.push(Tt(this.consoleBuffer)),this.config.captureNetwork&&this.teardown.push(At(this.networkBuffer)),this.config.captureErrors&&this.teardown.push(jt(this.consoleBuffer)),rn(this.config.projectId),this.config.widget&&this.teardown.push(Jt(this)),this.config.updates&&C()&&(this.startWatching(),queueMicrotask(()=>m(this,d,K).call(this))),this.started=!0,m(this,d,mt).call(this),this.debugLog("initialised",{version:X.version,apiBase:this.config.apiBase,projectId:this.config.projectId,user:this.config.user,context:this.context}),this}startWatching(){if(this.stopWatching)return this;this.stopWatching=yn({apiBase:this.config.apiBase,onUpdate:n=>{if(n!=null&&n.incomingCall){m(this,d,xt).call(this,n);return}if(n!=null&&n.callWithdrawn){m(this,d,re).call(this,n.callWithdrawn.callId);return}const o=an(n);typeof this.config.onUpdate=="function"&&this.config.onUpdate(o),this.emit("update",o),m(this,d,K).call(this)}}),this.teardown.push(()=>{var n;return(n=this.stopWatching)==null?void 0:n.call(this)}),this.teardown.push(()=>m(this,d,re).call(this));const e=n=>{(n.key===Z||n.key===null)&&m(this,d,K).call(this)};return window.addEventListener("storage",e),this.teardown.push(()=>window.removeEventListener("storage",e)),this}on(e,n){if(typeof n!="function")return()=>{};const o=this.listeners.get(e)||new Set;return o.add(n),this.listeners.set(e,o),()=>this.off(e,n)}off(e,n){var o;return(o=this.listeners.get(e))==null||o.delete(n),this}emit(e,n){for(const o of this.listeners.get(e)||[])try{o(n)}catch(s){console.error(`[BugCatch] a '${e}' listener threw:`,s)}}unreadUpdates(){return F()}markUpdateRead(e){return cn(e),m(this,d,K).call(this),this}markReportRead(e){return ln(e),m(this,d,K).call(this),this}markUpdatesRead(){return Ve(),m(this,d,K).call(this),this}async verifyFix(e,{result:n,resolvedAt:o,note:s}){const i=n==="STILL_HAPPENING"?await m(this,d,ie).call(this,{}):{};return jn({apiBase:this.config.apiBase,bugId:e,result:n,resolvedAt:o,note:s,metadata:i.metadata,logs:i.logs})}async rate(e,{score:n,comment:o}={}){return An({apiBase:this.config.apiBase,bugId:e,score:n,comment:o})}async myReport(e){const n=await hn({apiBase:this.config.apiBase,bugId:e});return m(this,d,wt).call(this,e,n==null?void 0:n.assistant),n}replyToReport(e,{body:n,attachments:o}={}){return fn({apiBase:this.config.apiBase,bugId:e,body:n,attachments:o})}async canCallNow(){return!!await this.callKind()}async callKind(){return(await this.callKinds()).live?"live":null}async callKinds(){var n;const e={agent:!1,live:!1};return!this.config.voice||!((n=this.config.user)!=null&&n.email)?e:this.kinds!==void 0?this.kinds:(this.kinds=await pn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.kinds)}canCall(e){return this.config.voice?Ze({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}async startCall(e){var n;if(!this.config.voice)throw new Error("[BugCatch] calling is switched off.");if(this.call||this.callStarting)throw new Error("[BugCatch] a call is already running.");this.callStarting=!0;try{if(!await this.callKind())throw new Error((n=this.config.user)!=null&&n.email?"[BugCatch] calling is not available on this project.":"[BugCatch] a call needs to know who is calling — call identify() with an email first.");return await m(this,d,yt).call(this,e)}finally{this.callStarting=!1}}startLiveCall(e){return this.startCall(e)}canCallTeam(e){return this.config.voice?Ze({apiBase:this.config.apiBase,bugId:e}):Promise.resolve(!1)}endCall(){var e;return(e=this.call)==null||e.hangUp(),this.call=null,this}myReports(){return dn({apiBase:this.config.apiBase})}hasReports(){return!!C()}forgetReports(){var e;return(e=this.stopWatching)==null||e.call(this),this.stopWatching=null,this.endCall(),N(),m(this,d,K).call(this),this}identify(e){const n=ct(e,o=>this.warn(o));return e!=null&&!n?this:(this.config.user=n,this.debugLog("identify",n),this)}setContext(e){return this.context=lt(this.context,e,n=>this.warn(n)),this.debugLog("context",this.context),this}getContext(){return{...this.context}}open(){return this.started?this.widgetControls?(this.widgetControls.open(),this):(this.warn("open() needs the built-in widget; it was turned off with widget: false."),this):(this.warn("open() was called before init() — nothing to open yet."),this)}close(){var e;return(e=this.widgetControls)==null||e.close(),this}isOpen(){var e;return!!((e=this.widgetControls)!=null&&e.isOpen())}async uploadAttachment(e,n,o){if(!this.started)throw new Error("[BugCatch] call init() first.");return tn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,blob:e,mime:n||(e==null?void 0:e.type),onProgress:o})}async report({title:e,description:n="",priority:o="MEDIUM",metadata:s={},attachments:i=[],references:r=[],context:a=null,issue:c=null}={}){var j,A,D,f,k,p,b,I;if(!this.started)throw new Error("[BugCatch] call init() first.");if(typeof e!="string"||!e.trim())throw new Error("[BugCatch] a title is required.");["LOW","MEDIUM","HIGH","CRITICAL"].includes(String(o).toUpperCase())||(this.warn(`priority "${o}" is not one of LOW, MEDIUM, HIGH, CRITICAL — sent as MEDIUM.`),o="MEDIUM");const l=(i||[]).filter(g=>(g==null?void 0:g.storageKey)&&de(g.mime,Je)).map(g=>({type:g.type==="VIDEO"?"VIDEO":"SCREENSHOT",storageKey:g.storageKey,mime:de(g.mime,Je)})),h=(Array.isArray(r)?r:[]).filter(g=>g&&typeof g.key=="string"&&g.key&&(typeof g.value=="string"||typeof g.value=="number")).map(g=>({key:g.key.slice(0,64),value:String(g.value).trim().slice(0,120)})).filter(g=>g.value);Array.isArray(r)&&h.length<r.length&&this.warn("report({ references }) takes [{ key, value }] with string values; malformed entries were dropped.");let u=await m(this,d,ie).call(this,{title:e.trim(),description:typeof n=="string"?n:String(n??""),priority:String(o).toUpperCase(),metadata:s,media:l,references:h,context:a,issue:typeof c=="string"&&c?c.slice(0,32):null});const B=m(this,d,ve).call(this,u);if(B===!1)return{cancelled:!0};u=B,this.debugLog("sending report",{title:u.title,user:(j=u.metadata)==null?void 0:j.user,context:(A=u.metadata)==null?void 0:A.context,references:u.references,console:(k=(f=(D=u.logs)==null?void 0:D.console)==null?void 0:f.entries)==null?void 0:k.length,network:(I=(b=(p=u.logs)==null?void 0:p.network)==null?void 0:b.entries)==null?void 0:I.length});const y=R(this.config.projectId);let w;try{w=await fetch(`${this.config.apiBase}/ingest/bugs`,{method:"POST",headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey,...y!=null&&y.token?{"X-BugCatch-Session":y.token}:{}},body:JSON.stringify(lo(u))})}catch(g){const S=new Error(`[BugCatch] could not reach ${this.config.apiBase} — check apiBase, and that this site's origin is allowed by the API.`);throw S.cause=g,m(this,d,oe).call(this,"report",S),S}if(w.status===401&&y&&we(this.config.projectId),!w.ok){let g="";try{const O=await w.json();g=Array.isArray(O.message)?O.message.join(", "):O.message||""}catch{}w.status===401&&!y&&(g||(g="the apiKey was not accepted")),w.status===403&&(g||(g="the apiKey is not allowed to report to this projectId"));const S=w.status===429?new Error(`[BugCatch] ${_(g)}`):new Error(`[BugCatch] report failed (HTTP ${w.status})${g?": "+g:""}`);throw S.status=w.status,m(this,d,oe).call(this,"report",S),S}const M=await w.json();return m(this,d,ke).call(this,M,{title:u.title??e.trim(),via:"form"}),M.watchToken&&(fe(M.watchToken),this.config.updates&&this.startWatching()),M}loginState(){var e;return Rn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:(e=R(this.config.projectId))==null?void 0:e.token})}referenceFields(){return this.refFields?this.refFields:(this.refFields=Pn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.refFields)}agentIssues(){return this.agentIssueList?this.agentIssueList:(this.agentIssueList=_n({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId}),this.agentIssueList)}async announcements(){const e=await Fn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId});return Xn(e)}dismissAnnouncement(e){qn(e)}signedInAs(){const e=R(this.config.projectId);return e?{email:e.email,name:e.name}:null}signIn(e,{name:n}={}){var o;return Mn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,name:n??((o=this.config.user)==null?void 0:o.name)??null})}async confirmSignIn(e,n){var s;const o=await Kn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,email:e,code:n});return(s=this.config.user)!=null&&s.email||(this.config.user={email:o.email,name:o.name||void 0}),this.emit("signin",{email:o.email,name:o.name}),{email:o.email,name:o.name}}async signOut(){var o;const e=R(this.config.projectId),n=await Un({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,token:e==null?void 0:e.token});return e!=null&&e.email&&((o=this.config.user)==null?void 0:o.email)===e.email&&(this.config.user=null),this.emit("signout",{email:(e==null?void 0:e.email)||null}),n}knownIssues(){var e;return $n({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,sessionToken:(e=R(this.config.projectId))==null?void 0:e.token})}matchKnownIssues(e,{signal:n}={}){var o;return Bn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,q:e||"",sessionToken:(o=R(this.config.projectId))==null?void 0:o.token,signal:n})}async meToo(e,{email:n,name:o}={}){var r,a;const s=R(this.config.projectId),i=await Ee();return En({apiBase:this.config.apiBase,apiKey:this.config.apiKey,projectId:this.config.projectId,bugId:e,reporterEmail:n||((r=this.config.user)==null?void 0:r.email),reporterName:o||((a=this.config.user)==null?void 0:a.name),metadata:{...i,user:this.config.user||void 0,...Object.keys(this.context).length?{context:this.context}:{},sdk:X},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},sessionToken:s==null?void 0:s.token})}snapshot(){return{user:this.config.user||null,context:{...this.context},console:this.consoleBuffer.all(),network:this.networkBuffer.all(),environment:Te()}}clear(){this.consoleBuffer.clear(),this.networkBuffer.clear()}destroy(){this.endCall(),this.teardown.forEach(e=>{try{e()}catch{}}),this.teardown=[],this.notifier=null,this.started=!1}}d=new WeakSet,ne=function(e,n){const o=this.config[e];if(typeof o=="function")try{o(n)}catch(s){console.error(`[BugCatch] your ${e} callback threw:`,s)}},mt=function(){if(typeof window>"u"||typeof fetch!="function")return;const e=`bugcatch:hello:${this.config.projectId}`,n=new Date().toISOString().slice(0,10);try{if(window.localStorage.getItem(e)===n)return}catch{}const o=setTimeout(()=>{try{window.localStorage.setItem(e,n)}catch{}fetch(`${this.config.apiBase}/ingest/hello`,{method:"POST",keepalive:!0,headers:{"Content-Type":"application/json","X-BugCatch-Key":this.config.apiKey},body:JSON.stringify({projectId:this.config.projectId,version:X.version,identify:!!this.config.user,context:!!(this.context&&Object.keys(this.context).length),widget:!!this.config.widget,environment:this.config.environment||null,release:this.config.release||null})}).catch(()=>{})},5e3);this.teardown.push(()=>clearTimeout(o))},K=function(){const e=F();if(this.emit("unread",{count:e.length,updates:e}),!!this.config.updateToast){if(!this.notifier){if(!e.length)return;this.notifier=Sn({accent:this.config.accent,position:this.config.widgetPosition,onRead:n=>this.markUpdateRead(n),onReadAll:()=>this.markUpdatesRead(),onCall:this.config.voice?n=>{this.markUpdateRead(n.id),this.startCall(n.bugId)}:null}),this.teardown.push(()=>{var n;return(n=this.notifier)==null?void 0:n.destroy()})}this.notifier.render(e)}},wt=function(e,n){const o=n==null?void 0:n.phase;if(!e||typeof o!="string"||this.phases.get(e)===o)return;this.phases.set(e,o);const s={bugId:e,phase:o,title:n.title??null,detail:n.detail??null};this.debugLog("assistant",s),m(this,d,ne).call(this,"onAssistantPhase",s),this.emit("assistant",s)},ke=function(e,{title:n,via:o}){if(!(e!=null&&e.id))return;const s={bugId:e.id,title:n??null,via:o,result:e};this.debugLog("report accepted",s),m(this,d,ne).call(this,"onReportSubmitted",s),this.emit("report",s)},oe=function(e,n){m(this,d,ne).call(this,"onError",{action:e,error:n}),this.emit("error",{action:e,error:n})},ie=async function({title:e,description:n="",priority:o="MEDIUM",metadata:s={},media:i=[],references:r=[],context:a=null,issue:c=null}){var h,u;const l=a?lt(this.context,a,B=>this.warn(B.replace("setContext()","report({ context })"))):this.context;return{projectId:this.config.projectId,...e?{title:e}:{},description:n,priority:o,url:window.location.href,reporterEmail:(h=this.config.user)==null?void 0:h.email,reporterName:(u=this.config.user)==null?void 0:u.name,metadata:{...await Ee(),release:this.config.release,environment:this.config.environment,buildId:this.config.buildId,...s,user:this.config.user||void 0,...Object.keys(l).length?{context:l}:{},sdk:X},logs:{console:{entries:this.consoleBuffer.all()},network:{entries:this.networkBuffer.all()}},...i.length?{attachments:i}:{},...r.length?{references:r}:{},...c?{issue:c}:{}}},ve=function(e){if(typeof this.config.beforeSend!="function")return e;let n;try{n=this.config.beforeSend(e)}catch(o){return this.warn(`beforeSend threw, so nothing was sent: ${(o==null?void 0:o.message)||o}`),!1}return n===!1?!1:n&&typeof n=="object"?n:e},bt=async function(){const e=R(this.config.projectId),n=m(this,d,ve).call(this,await m(this,d,ie).call(this,{}));if(n===!1)return this.debugLog("call cancelled by beforeSend"),{cancelled:!0};let o;try{o=await gn({apiBase:this.config.apiBase,apiKey:this.config.apiKey,token:e==null?void 0:e.token,payload:n,watchToken:C()})}catch(s){throw s.status===401&&e&&we(this.config.projectId),m(this,d,oe).call(this,"call",s),s}return m(this,d,ke).call(this,o,{title:null,via:"call"}),o.watchToken&&(fe(o.watchToken),this.config.updates&&this.startWatching()),o},yt=async function(e){const{joinLiveCall:n,liveCallSupported:o,openMicrophone:s}=await J(this.config.chunkBase);if(!o())throw new Error("[BugCatch] this browser cannot make live calls.");const i=await s();let r,a=null;try{if(e)r=await mn({apiBase:this.config.apiBase,bugId:e});else if(a=await m(this,d,bt).call(this),e=a.id,r=a.live_call,!(r!=null&&r.realtime_url)){const l=new Error(`[BugCatch] the report was filed, but the team could not be rung${a.call_error?`: ${a.call_error}`:"."}`);throw l.filed=a,l.reason=a.call_error||null,l}}catch(l){for(const h of i.getTracks())h.stop();throw l}const c=await n({realtimeUrl:r.realtime_url,stream:i,ringsUntil:r.rings_until,onState:(l,h)=>{(l==="ended"||l==="failed"||l==="missed")&&(this.call=null),l==="missed"&&wn({apiBase:this.config.apiBase,bugId:e,callId:r.call_id}),typeof this.config.onCallState=="function"&&this.config.onCallState(l,{bugId:e,live:!0,...h}),this.emit("call",{bugId:e,live:!0,state:l,...h})}});return this.call=c,Object.assign(c,{bugId:e,callId:r.call_id,live:!0,filed:a,recorded:!!r.recorded})},xt=function(e){var s;const n=e.incomingCall;if(!this.config.voice||((s=this.ringing)==null?void 0:s.callId)===n.callId||this.call||this.callStarting)return;m(this,d,re).call(this);const o={callId:n.callId,dismiss:()=>{}};this.ringing=o,J(this.config.chunkBase).then(({liveCallSupported:i,showIncomingCall:r})=>{if(this.ringing!==o||(this.ringing=null,!i())||this.call||this.callStarting)return;const a=r({call:n,accent:this.config.accent,position:this.config.widgetPosition,touched:Nn,onAnswer:()=>{m(this,d,kt).call(this,e.bugId,n)},onDecline:()=>{this.ringing=null,Ge({apiBase:this.config.apiBase,bugId:e.bugId,callId:n.callId}),this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"declined"})},onExpire:()=>{this.ringing=null,this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"missed"})}});if(this.ringing={callId:n.callId,dismiss:a},typeof this.config.onIncomingCall=="function")try{this.config.onIncomingCall({bugId:e.bugId,...n})}catch{}this.emit("incoming-call",{bugId:e.bugId,callId:n.callId,state:"ringing"})},()=>{this.ringing===o&&(this.ringing=null)})},re=function(e){if(this.ringing&&!(e&&this.ringing.callId!==e)){try{this.ringing.dismiss()}catch{}this.ringing=null}},kt=async function(e,n){if(this.ringing=null,this.call||this.callStarting)return;this.callStarting=!0;let o,s;try{const r=await J(this.config.chunkBase);s=r.joinLiveCall,o=await r.openMicrophone()}catch(r){this.callStarting=!1,Ge({apiBase:this.config.apiBase,bugId:e,callId:n.callId}),this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r});return}let i;try{i=await bn({apiBase:this.config.apiBase,bugId:e,callId:n.callId})}catch(r){for(const a of o.getTracks())a.stop();this.callStarting=!1,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"missed",error:r});return}try{const r=await s({realtimeUrl:i.realtime_url,stream:o,onState:(a,c)=>{(a==="ended"||a==="failed"||a==="missed")&&(this.call=null),typeof this.config.onCallState=="function"&&this.config.onCallState(a,{bugId:e,live:!0,incoming:!0,...c}),this.emit("call",{bugId:e,live:!0,incoming:!0,state:a,...c})}});this.call=r,this.emit("incoming-call",{bugId:e,callId:n.callId,state:"answered"})}catch(r){for(const a of o.getTracks())a.stop();this.emit("incoming-call",{bugId:e,callId:n.callId,state:"failed",error:r})}finally{this.callStarting=!1}};const xe=new ht;return xe.Client=ht,xe.version=X.version,xe});
310
310
  //# sourceMappingURL=bugcatch.umd.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphoro/bugcatch-web-sdk",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Drop-in browser SDK — report bugs with console, network and environment context, without the extension.",
5
5
  "type": "module",
6
6
  "main": "dist/bugcatch.es.js",
package/src/index.d.ts CHANGED
@@ -470,6 +470,15 @@ export interface MyReport {
470
470
  rating: number | null;
471
471
  /** Settled, on a project that asks for ratings — so a list can say "rate it". */
472
472
  ratingAsked: boolean;
473
+ /** When it was resolved or closed; null while it is open. */
474
+ resolvedAt: string | null;
475
+ /**
476
+ * Whether the widget's first screen asks for this report's rating: 'ask' with
477
+ * a Not now, 'require' before another report can be sent from the widget, or
478
+ * null. Set by the project (Settings → Resolutions → Where the widget asks),
479
+ * for reports settled in the last 30 days and not yet rated for this fix.
480
+ */
481
+ ratingDue: 'ask' | 'require' | null;
473
482
  }
474
483
 
475
484
  /**
package/src/panel/call.js CHANGED
@@ -34,8 +34,9 @@ export function mountCallOffer(ctx) {
34
34
  ctx.preloadCall();
35
35
  // Never over a panel that is still asking who this is: the call would
36
36
  // file a report, and on a project that requires sign-in that is the one
37
- // thing this panel is holding back.
38
- callBtn.hidden = ctx.mustSignIn();
37
+ // thing this panel is holding back. Nor over one holding the form for
38
+ // a rating (rate-due.js), for the same reason.
39
+ callBtn.hidden = ctx.mustSignIn() || ctx.mustRate();
39
40
  })
40
41
  .catch(() => { /* not available is the safe answer to every failure */ });
41
42
  }
@@ -132,7 +132,9 @@ export function mountCompose(ctx) {
132
132
  void persistDraft();
133
133
  };
134
134
  }).catch(() => { draftStatus.textContent = 'Draft storage is unavailable. Keep this tab open.'; })
135
- .finally(() => { compose.inert = false; if (!draftStatus.querySelector('[data-resume]')) draftLoaded = true; });
135
+ // Back to whatever the rating card wants: it may have held the form
136
+ // while storage was being read (rate-due.js).
137
+ .finally(() => { compose.inert = ctx.mustRate(); if (!draftStatus.querySelector('[data-resume]')) draftLoaded = true; });
136
138
  }
137
139
 
138
140
  // Saved on the way out, before anything else is taken down.
@@ -173,8 +175,10 @@ export function mountCompose(ctx) {
173
175
 
174
176
  function refreshSendState() {
175
177
  const busy = items.some((it) => it.status === 'uploading');
176
- sendBtn.disabled = ctx.sending || busy || items.some((it) => it.status === 'failed');
177
- note.textContent = busy ? 'Waiting for the upload to finish…' : items.some((it) => it.status === 'failed') ? 'Retry or remove failed attachments before sending.' : '';
178
+ const held = ctx.mustRate();
179
+ sendBtn.disabled = ctx.sending || busy || held || items.some((it) => it.status === 'failed');
180
+ note.textContent = held ? 'Rate your last report to send a new one.'
181
+ : busy ? 'Waiting for the upload to finish…' : items.some((it) => it.status === 'failed') ? 'Retry or remove failed attachments before sending.' : '';
178
182
  const max = cfg.maxAttachments ?? 5;
179
183
  scrim.querySelectorAll('.tool').forEach((t) => {
180
184
  t.disabled = ctx.sending || items.length >= max;
@@ -8,6 +8,7 @@
8
8
  // One dialog doing several jobs, one file each:
9
9
  //
10
10
  // compose.js the report form: drafts, severity, attachments, recording, Send
11
+ // rate-due.js "how did we do?" about a settled report, above the form
11
12
  // refs.js "which record is this about", asked at Send
12
13
  // notices.js what the project's team is telling visitors, at the top
13
14
  // known.js what the project already knows is broken, and "me too"
@@ -37,6 +38,7 @@ import { mountPicks } from './picks.js';
37
38
  import { mountCallOffer } from './call.js';
38
39
  import { mountSignIn } from './signin.js';
39
40
  import { mountThread } from './thread.js';
41
+ import { mountRateDue } from './rate-due.js';
40
42
 
41
43
  /**
42
44
  * Open the panel over the page. `preloadCall` fetches the call code ahead of a
@@ -68,6 +70,9 @@ export function openPanel({
68
70
  })}
69
71
  <div class="body">
70
72
  <div class="slot" role="alert"></div>
73
+ <!-- A settled report still waiting for its rating. Drawn only when the
74
+ project asks for one here; see rate-due.js. -->
75
+ <div class="rate-due fix-rating" tabindex="-1" hidden></div>
71
76
  <!-- What the project's team is telling visitors. Drawn only when it
72
77
  has published something. -->
73
78
  <div class="notices" hidden></div>
@@ -131,6 +136,7 @@ export function openPanel({
131
136
  notices: $('.notices'),
132
137
  known: $('.known'),
133
138
  picks: $('.picks'),
139
+ rateDue: $('.rate-due'),
134
140
  // A report or a call is on its way. Escape and a click outside wait for it.
135
141
  sending: false,
136
142
  closed: false,
@@ -155,6 +161,8 @@ export function openPanel({
155
161
  // signin.js
156
162
  mustSignIn: null,
157
163
  recheckSignIn: null,
164
+ // rate-due.js
165
+ mustRate: () => false,
158
166
  // thread.js
159
167
  succeed: null,
160
168
  refreshThread: null,
@@ -171,6 +179,7 @@ export function openPanel({
171
179
  mountCallOffer(ctx);
172
180
  mountSignIn(ctx);
173
181
  mountThread(ctx);
182
+ mountRateDue(ctx);
174
183
 
175
184
  /* ---------- lifecycle ---------- */
176
185
 
@@ -0,0 +1,155 @@
1
+ // The rating a settled report is still waiting for, asked on the first screen,
2
+ // above the form, rather than only inside Your reports, which few reporters
3
+ // ever open again.
4
+ //
5
+ // How hard it asks is the project's choice (Settings → Resolutions → Where the
6
+ // widget asks), sent with each report as `ratingDue`:
7
+ //
8
+ // 'ask' a card with a Not now.
9
+ // 'require' the same card, and the form waits: Send and Talk to the team
10
+ // come back once the report is rated.
11
+ //
12
+ // One report per open, the one settled most recently. A reporter with three
13
+ // settled reports is not handed three questions at the door; the next one is
14
+ // waiting the next time they come.
15
+
16
+ import * as ICONS from './icons.js';
17
+ import { escapeHtml, relativeDay } from './render.js';
18
+ import { drawRating } from './rating.js';
19
+
20
+ // The reports somebody pressed Not now on, on this device. Kept so the card
21
+ // does not come back on every open; a project that wants an answer regardless
22
+ // asks with 'require', which this list does not excuse.
23
+ const LATER_KEY = 'bugcatch.rateLater';
24
+ const LATER_MAX = 50;
25
+
26
+ function putOffList() {
27
+ try {
28
+ const list = JSON.parse(window.localStorage.getItem(LATER_KEY) || '[]');
29
+ return Array.isArray(list) ? list : [];
30
+ } catch {
31
+ return [];
32
+ }
33
+ }
34
+
35
+ function putOff(bugId) {
36
+ try {
37
+ const list = putOffList().filter((id) => id !== bugId);
38
+ list.push(bugId);
39
+ window.localStorage.setItem(LATER_KEY, JSON.stringify(list.slice(-LATER_MAX)));
40
+ } catch { /* private mode: the card comes back next time, which is all that is lost */ }
41
+ }
42
+
43
+ /** The report to ask about now, or null. Exported for the tests. */
44
+ export function pickDue(reports, putOffIds = putOffList()) {
45
+ const later = new Set(putOffIds);
46
+ const due = (reports || []).filter((r) => (r.ratingDue === 'require'
47
+ || (r.ratingDue === 'ask' && !later.has(r.id))));
48
+ if (!due.length) return null;
49
+ const settledAt = (r) => new Date(r.resolvedAt || r.updatedAt || 0).getTime() || 0;
50
+ return due.reduce((latest, r) => (settledAt(r) > settledAt(latest) ? r : latest));
51
+ }
52
+
53
+ export function mountRateDue(ctx) {
54
+ const {
55
+ client, rateDue: card, compose, picks, known, callBtn, titleInput, scrim,
56
+ } = ctx;
57
+
58
+ let held = false;
59
+ ctx.mustRate = () => held;
60
+
61
+ /**
62
+ * Hold the form, or let it go. Held, it stays on screen, dimmed: what they
63
+ * came to do is still in sight, one rating away.
64
+ */
65
+ function hold(on) {
66
+ held = on;
67
+ for (const el of [compose, picks, known]) {
68
+ el.classList.toggle('held', on);
69
+ el.inert = on;
70
+ }
71
+ if (on) callBtn.hidden = true;
72
+ else if (ctx.callOffered() && !ctx.mustSignIn()) callBtn.hidden = false;
73
+ ctx.refreshSendState();
74
+ }
75
+
76
+ function release() {
77
+ if (held) hold(false);
78
+ }
79
+
80
+ function dismiss() {
81
+ card.hidden = true;
82
+ card.replaceChildren();
83
+ release();
84
+ titleInput.focus();
85
+ }
86
+
87
+ function draw(report) {
88
+ const required = report.ratingDue === 'require';
89
+ const when = report.resolvedAt ? relativeDay(report.resolvedAt) : '';
90
+ const done = report.status === 'CLOSED' ? 'closed' : 'fixed';
91
+ card.hidden = false;
92
+ card.classList.toggle('is-required', required);
93
+ card.innerHTML = `
94
+ <div class="rd-head">
95
+ <span class="rd-eyebrow">${required ? 'Before you report something new' : `Your report was ${done}`}</span>
96
+ ${required ? '' : '<button type="button" class="link rd-later" data-act="rate-later">Not now</button>'}
97
+ </div>
98
+ <p class="rd-report">
99
+ <span class="rd-ico" aria-hidden="true">${ICONS.check}</span>
100
+ <span class="rd-ttl">${escapeHtml(report.title)}</span>
101
+ <span class="rd-sub">${escapeHtml(report.reference)}${when ? ` · ${done} ${escapeHtml(when)}` : ''}</span>
102
+ </p>
103
+ <div class="rd-stars"></div>`;
104
+
105
+ card.querySelector('[data-act="rate-later"]')?.addEventListener('click', () => {
106
+ putOff(report.id);
107
+ dismiss();
108
+ });
109
+
110
+ drawRating(card.querySelector('.rd-stars'), {
111
+ client,
112
+ report,
113
+ rating: null,
114
+ question: required ? 'How did we handle it? Rate it to send a new one.' : 'How did we do?',
115
+ onRated: () => {
116
+ card.querySelector('.rd-eyebrow').textContent = `Your report was ${done}`;
117
+ card.querySelector('[data-act="skip"]')?.remove();
118
+ card.classList.remove('is-required');
119
+ release();
120
+ },
121
+ // The rating could not be sent. A held form must not turn into a locked
122
+ // door because of our outage: offer the way past.
123
+ onError: () => {
124
+ if (!held || card.querySelector('[data-act="skip"]')) return;
125
+ const skip = document.createElement('button');
126
+ skip.type = 'button';
127
+ skip.className = 'link rd-later';
128
+ skip.dataset.act = 'skip';
129
+ skip.textContent = 'Skip for now';
130
+ skip.addEventListener('click', dismiss);
131
+ card.querySelector('.rd-head').append(skip);
132
+ },
133
+ });
134
+
135
+ if (required) {
136
+ hold(true);
137
+ // Focus moves to the card, out of the form that just went inert. The
138
+ // card, not its first star: a focused star lights up, and one lit star
139
+ // reads as a rating of 1 somebody never gave.
140
+ const active = scrim.getRootNode().activeElement;
141
+ if (!active || !card.contains(active)) card.focus();
142
+ }
143
+ }
144
+
145
+ // Only a browser that has filed something has anything to rate. A failed
146
+ // read asks nothing and holds nothing: the form is what they came for.
147
+ if (!client.hasReports()) return;
148
+ client.myReports()
149
+ .then(({ reports }) => {
150
+ if (ctx.closed) return;
151
+ const report = pickDue(reports);
152
+ if (report) draw(report);
153
+ })
154
+ .catch(() => {});
155
+ }