Jake Vanderwerf
2025-09-30 2143fc670fa245750ac7f774fcb38ea8cfbf7edb
1
(()=>{class e{constructor(e={}){this.options={apiUrl:"",logToServer:!0,displayNotifications:!0,notificationDuration:5e3,retryEnabled:!0,maxRetries:3,...e},this.retryCount=0}async log(e,t={},r=null){console.error("API Error:",e,t);const o=this.getErrorType(e),n=this.getErrorMessage(e,o);switch(this.options.logToServer&&await this.logErrorToServer(o,n,t),o){case"network":case"server":if(this.options.retryEnabled&&this.retryCount<this.options.maxRetries&&r)return this.retryCount++,this.retryWithBackoff(r);break;case"auth":this.handleAuthError();break;case"rate_limit":return this.handleRateLimitError(r)}return this.options.displayNotifications&&this.displayErrorNotification(n,o,r),r&&this.options.retryEnabled||(this.retryCount=0),{success:!1,error:o,message:n,context:t}}getErrorType(e){if("AbortError"===e.name)return"timeout";if(!navigator.onLine)return"offline";if(e.response){const t=e.response.status;if(t>=400&&t<500)return 401===t||403===t?"auth":429===t?"rate_limit":"client";if(t>=500)return"server"}return"network"}getErrorMessage(e,t){const r={network:"We couldn't connect to the server. Please check your connection and try again.",timeout:"The request took too long to complete. Please try again.",offline:"You appear to be offline. Please check your internet connection.",auth:"Your session may have expired. Please log in again.",rate_limit:"You've made too many requests. Please wait a moment and try again.",server:"We're experiencing technical difficulties. Please try again later.",client:"Something went wrong with your request. Please try again.",unknown:"An unexpected error occurred. Please try again."};return e.response&&e.response.data&&e.response.data.message?e.response.data.message:e.message?e.message:r[t]||r.unknown}async logErrorToServer(e,t,r){try{if(!this.options.apiUrl)return;const o=new FormData;o.append("error_type",e),o.append("message",t),o.append("context",JSON.stringify({...r,url:window.location.href,userAgent:navigator.userAgent,timestamp:(new Date).toISOString()})),await fetch(`${this.options.apiUrl}errors/log`,{method:"POST",headers:{"X-WP-Nonce":window.feedSettings?.nonce||""},body:o})}catch(e){console.warn("Failed to log error to server",e)}}displayErrorNotification(e,t,r){if(window.jvbNotifications){const t=[];return r&&t.push({label:"Try Again",icon:"refresh",action:r}),void window.jvbNotifications.queuePopupNotification({type:"error",message:e,icon:"alert",priority:"high",displayDuration:this.options.notificationDuration,actions:t})}alert(e)}handleAuthError(){window.feedSettings&&window.feedSettings.loginUrl?window.location.href=window.feedSettings.loginUrl:window.location.reload()}async handleRateLimitError(e){const t=2e3*(this.retryCount+1);if(await new Promise((e=>setTimeout(e,t))),e)return this.retryCount++,e()}async retryWithBackoff(e){const t=Math.min(1e3*Math.pow(2,this.retryCount),1e4);return this.options.displayNotifications&&this.displayRetryNotification(t),await new Promise((e=>setTimeout(e,t))),e()}displayRetryNotification(e){window.jvbNotifications&&window.jvbNotifications.queuePopupNotification({type:"info",message:`Retrying in ${e/1e3} seconds...`,icon:"refresh",priority:"medium",displayDuration:e})}resetRetryCount(){this.retryCount=0}collectUserFeedback(e){const t=document.createElement("dialog");return t.className="error-feedback-modal",t.innerHTML='\n            <h2>Help Us Improve</h2>\n            <p>We encountered an error. Would you like to tell us what happened?</p>\n            <form method="dialog" data-save="error">\n                <textarea placeholder="What were you trying to do when this error occurred?"></textarea>\n                <div class="actions">\n                    <button value="cancel">Skip</button>\n                    <button value="submit" class="primary">Send Feedback</button>\n                </div>\n            </form>\n        ',document.body.appendChild(t),new Promise((e=>{t.addEventListener("close",(()=>{const r="submit"===t.returnValue?t.querySelector("textarea").value:null;document.body.removeChild(t),e(r)})),t.showModal()}))}setupGlobalErrorHandling(){window.addEventListener("error",(e=>{this.log(e.error||new Error(e.message),{message:e.message,filename:e.filename,lineno:e.lineno,colno:e.colno,type:"global_error"})})),window.addEventListener("unhandledrejection",(e=>{this.log(e.reason,{type:"unhandled_promise",message:e.reason?.message||"Unhandled promise rejection"})}))}}document.addEventListener("DOMContentLoaded",(function(){window.jvbError=new e({api:jvbSettings.api,logToServer:!0,displayNotifications:!0,notificationDuration:5e3,retryEnabled:!0,maxRetries:3})}))})();