mockforme 5.0.1 → 5.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{mockforme:()=>U});const n=()=>"undefined"!=typeof process&&null!=process.versions?.node,s=e=>{try{return e?e.toLowerCase():e}catch(e){return console.log(e),null}},o=(e,t)=>{const n=e.split("/"),s=t.split("/");if(n.length!==s.length)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t!==s[e]&&":any"!==t)return!1}return!0};let r=[];function i(e){r=e}function a(){return r}function d(e,t="get"){const n=e=>e.startsWith("/")?e:`/${e}`,i=n(e);for(const a of r)if(s(a.apiMethod)===s(t)){const s=n(a.apiEndpoint);if(s===i||o(s,i)){console.log(`[MockForMe] Match Found: ${t} ${e} -> ${a.apiEndpoint}`);const n={url:e,method:a.apiMethod,_ack:a._ack||null};return a._ri_&&(n._ri_=a._ri_),n}}return null}const c="JAVASCRIPT",l="CHROME_EXTENSION",m="mfmver",u="mockforme",h="4.2.8",p="https://api.mockforme.com",f=`${p}/mockforme`,g=`${p}/gateway/3f4eae522b`;let b=null,y=null,x=null,v=[];function k({_o:e,mk:t,_mck:n,_rules:s}){b=e,y=t,x=n,v=s}function _(){return v}function w(){return`${f}/${b}/${y}`}function q(){return x}const E=e=>{try{return new URL(e,window.location.origin).toString()}catch{return e}};function R({url:e,method:t}){try{const t=_();if(!Array.isArray(t)||0===t.length)return null;const n=globalThis.location?.origin||"http://localhost";for(const s of t){const t=s.rule?.URL;if(!t)continue;const{condition:o}=t;if(!o)continue;let r="";switch(t.type){case"URL":{let t;try{t=new URL(e,n),r=t.pathname}catch{r=e}break}case"QUERY":{let t;try{t=new URL(e,n)}catch{t=null}t&&(r=t.searchParams.get(o.key)||"");break}case"BODY":case"HEADERS":r="";break;default:continue}let i=!1;switch(o.operator){case"contains":i=r.includes(o.value);break;case"equal":case"equals":i=r===o.value;break;case"regexp":try{let e=o.value;if(e.startsWith("/")&&e.lastIndexOf("/")>0){const t=e.lastIndexOf("/"),n=e.slice(1,t),s=e.slice(t+1);i=new RegExp(n,s).test(r)}else i=new RegExp(e).test(r)}catch{i=!1}}if(i){const e=o.activeAction;return{action:e,config:o.actions?.[e]||{}}}}return null}catch(e){return console.error("[mockforme] checkIfRulesToBeApplied error",e),null}}const T=function(e,t,s){const o=n();var r;function i(){if(4==r.readyState)if(r.status.toString().match(/^20[0-9]$/)){var e=function(){var e=r.getResponseHeader("Content-Type"),t=r.responseText;if(e){var n=e.split(";");try{return"application/json"===n[0]?JSON.parse(t):t}catch(e){throw"Unable to convert response header"}}}();t.call(this,e,r)}else s.call(this,r.responseText,r)}function a(e,t){var n=[];for(var s in e)if(e.hasOwnProperty(s)){var o=t?t+"["+s+"]":s,r=e[s];n.push("object"==typeof r?a(r,o):encodeURIComponent(o)+"="+encodeURIComponent(r))}return n.join("&")}return o||(window.XMLHttpRequest?r=new XMLHttpRequest:window.ActiveXObject&&(r=new ActiveXObject("Microsoft.XMLHTTP"))),{request:function(){if(!o){if(window.XMLHttpRequest)r.onload=i;else{if(!window.ActiveXObject)throw"unable to process ajax";r.onreadystatechange=i}var t=a(e.params);if("get"==e.method.toLowerCase()&&"object"==typeof e.params){if(-1==e.url.indexOf("?"))e.url+="?";else{var n=e.url.split("?");n[1]&&n[1].split("=")[1]&&(e.url+="&")}e.url+=t}e.hasOwnProperty("async")||(e.async=!0),r.open(e.method,e.url,e.async),e.headers&&function(e){for(let t in e)r.setRequestHeader(t,e[t])}(e.headers),r.send(t)}}}};function S({di:e,iv:t},n){var s;const o=function(e,t){const n=t.length;return e.split("").map(((e,s)=>String.fromCharCode(e.charCodeAt(0)^t.charCodeAt(s%n)))).join("")}((s=e,"undefined"!=typeof Buffer?Buffer.from(s,"base64").toString("utf8"):decodeURIComponent(escape(atob(s)))),n+t);return JSON.parse(o)}function M(e,t,n,s){fetch(g,{method:"GET",headers:{[u]:e,[m]:h,"x-mfm-adaptor":t}}).then((e=>{if(!e.ok)throw e;return e.json()})).then((e=>{if(e){const{di:t,iv:s,_o:o,_mck:r,mk:a,r:d}=e,c=S({di:t,iv:s},o);k({_o:o,mk:a,_mck:r,_rules:d}),c&&(i(c),n(c,{_o:o,mk:a,_mck:r,_rules:d}))}})).catch((e=>{console.error("Error in loading mocked APIs:",e),s(e)}))}const I=new class{constructor(e={}){n()||(this.token=e.token||("undefined"!=typeof localStorage?localStorage.getItem("mockforme-token"):null),this.isAuthenticated=!!this.token,this.onAuthSuccess=e.onAuthSuccess||(()=>{}),this.mappings=e.mappings||[],this.rules=e.rules||[],this.mockedRequests=[],this.otherRequests=[],this.state={isOpen:!1,activeTab:"mappings",selectedRequest:null,activeRequestTab:"response",lastError:null,searchQuery:""},"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>this.init())):this.init()))}init(){document.getElementById("mockforme-root")||(this.createStyles(),this.render())}createStyles(){if(document.getElementById("mockforme-styles"))return;const e=document.createElement("style");e.id="mockforme-styles",e.innerHTML="\n #mockforme-root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n z-index: 999999;\n position: fixed;\n }\n .mfm-bubble {\n position: fixed;\n bottom: 20px;\n right: 20px;\n height: 42px;\n padding: 4px 8px;\n border-radius: 24px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n background: white;\n display: flex;\n align-items: center;\n gap: 8px;\n transition: transform 0.2s;\n border: 1px solid #eee;\n }\n .mfm-bubble:hover { transform: scale(1.05); }\n .mfm-bubble img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }\n \n .mfm-refresh-icon {\n width: 25px;\n height: 25px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #2563eb;\n font-size: 25px;\n transition: background 0.2s;\n margin-right: 10px;\n background: #f3f4f6;\n padding: 8px;\n }\n .mfm-refresh-icon:hover { }\n .mfm-refresh-icon.loading { animation: mfm-spin 1s linear infinite; }\n @keyframes mfm-spin { 100% { transform: rotate(360deg); } }\n\n .mfm-bottomsheet {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background: white;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.15);\n padding: 20px;\n z-index: 999999;\n height: 80vh;\n max-width: 500px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s ease-out;\n transform: translateY(100%);\n visibility: hidden;\n overflow-y: auto;\n }\n @media (max-width: 480px) {\n .mfm-bottomsheet { padding: 16px; }\n }\n .mfm-bottomsheet.open { transform: translateY(0); visibility: visible; }\n \n .mfm-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n }\n .mfm-title-group { display: flex; align-items: center; gap: 10px; }\n .mfm-title { font-size: 18px; font-weight: 600; color: #111; }\n .mfm-delete-token-btn { \n background: #fee2e2; \n color: #dc2626; \n font-size: 13px; \n padding: 4px 12px; \n border-radius: 20px; \n cursor: pointer; \n display: flex;\n align-items: center;\n gap: 6px;\n border: none;\n font-weight: 500;\n transition: background 0.2s;\n }\n .mfm-delete-token-btn:hover { background: #fecaca; }\n .mfm-header-actions { display: flex; align-items: center; gap: 10px; }\n .mfm-close { background: none; border: none; font-size: 24px; cursor: pointer; color: #666; }\n /* Login Form */\n .mfm-logo-container { text-align: center; padding: 20px 0; width: 100%; display: flex; justify-content: center; flex-shrink: 0; }\n .mfm-logo { width: 120px; height: auto; }\n .mfm-link { color: #2563eb; text-decoration: none; font-size: 14px; margin-top: 5px; cursor: pointer; display: inline-block; }\n .mfm-link:hover { text-decoration: underline; }\n .mfm-form { display: flex; flex-direction: column; gap: 10px; }\n .mfm-input {\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n .mfm-input:focus { border-color: #2563eb; }\n .mfm-error { color: #dc2626; font-size: 13px; margin-top: -5px; }\n .mfm-btn {\n padding: 12px;\n background: #2563eb;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n .mfm-btn:hover { background: #1d4ed8; }\n .mfm-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n .mfm-tabs { display: flex; border-bottom: 1px solid #eee; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-tab {\n padding: 8px 16px;\n cursor: pointer;\n color: #666;\n border-bottom: 2px solid transparent;\n font-weight: 500;\n font-size: 14px;\n }\n .mfm-tab.active { color: #2563eb; border-bottom-color: #2563eb; }\n \n .mfm-list {\n flex: 1;\n overflow-y: auto;\n min-height: 200px;\n max-height: 60vh;\n }\n .mfm-list-item {\n padding: 10px;\n border-bottom: 1px solid #f3f4f6;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n min-height: 52px;\n box-sizing: border-box;\n }\n .mfm-list-item:hover { background: #f9fafb; }\n .mfm-method { \n font-weight: bold; \n padding: 4px 8px; \n border-radius: 4px; \n margin-right: 10px;\n font-size: 12px;\n }\n .GET { background: #dbfcfe; color: #006064; }\n .POST { background: #dcfce7; color: #14532d; }\n .PUT { background: #fef9c3; color: #713f12; }\n .DELETE { background: #fee2e2; color: #7f1d1d; }\n .PATCH { background: #f3e8ff; color: #581c87; }\n .RULE { background: #f3f4f6; color: #374151; }\n\n /* Search & Actions */\n .mfm-actions-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-search-input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; }\n .mfm-search-input:focus { border-color: #2563eb; }\n .mfm-icon-btn { background: none; border: none; font-size: 25px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 4px; }\n .mfm-icon-btn:hover { color: #333; }\n .mfm-icon-btn.refresh { color: #2563eb; padding: 0; height: 20px; width:20px; border-radius: 50%; }\n .mfm-icon-btn.refresh.loading { animation: mfm-spin 1s linear infinite; }\n .mfm-icon-btn.delete { color: #dc2626; }\n\n @media (max-height: 700px) {\n .mfm-bottomsheet { height: 85vh; padding: 12px; }\n .mfm-logo-container { padding: 10px 0; }\n .mfm-logo { width: 100px; }\n .mfm-header { margin-bottom: 10px; }\n .mfm-form { gap: 8px; }\n .mfm-input { padding: 10px; font-size: 14px; }\n .mfm-btn { padding: 10px; font-size: 14px; }\n }\n\n .mfm-url { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .mfm-status { font-weight: bold; margin-left: 10px; }\n .status-2xx { color: #16a34a; }\n .status-4xx { color: #dc2626; }\n .status-5xx { color: #ea580c; }\n .status-aborted, .status-timeout, .status-error { color: #dc2626; font-size: 11px; font-weight: 500; }\n\n .mfm-rule-tag {\n font-size: 9px;\n padding: 2px 4px;\n border-radius: 4px;\n background: #fef3c7;\n color: #92400e;\n border: 1px solid #fde68a;\n margin-right: 6px;\n text-transform: uppercase;\n font-weight: bold;\n }\n\n .mfm-detail-view { display: flex; flex-direction: column; height: 100%; }\n .mfm-detail-actions { margin-bottom: 10px; }\n .mfm-back-btn { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 0;}\n \n .mfm-code-block {\n background: #f8fafc;\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n font-family: monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-break: break-all;\n max-height: 300px;\n border: 1px solid #e2e8f0;\n }\n .section-title { font-size: 14px; font-weight: 600; margin: 10px 0 5px; color: #333; }\n\n /* Snackbar */\n .mfm-snackbar {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: #333;\n color: white;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n z-index: 1000000;\n opacity: 0;\n transition: opacity 0.3s, transform 0.3s;\n pointer-events: none;\n }\n .mfm-snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n /* Confirm Dialog */\n .mfm-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000001;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n .mfm-modal-overlay.show { opacity: 1; pointer-events: auto; }\n .mfm-modal {\n background: white;\n padding: 20px;\n border-radius: 12px;\n width: 300px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n transform: scale(0.9);\n transition: transform 0.2s;\n }\n .mfm-modal-overlay.show .mfm-modal { transform: scale(1); }\n .mfm-modal-title { font-size: 16px; font-weight: 600; margin-bottom: 10px; }\n .mfm-modal-body { font-size: 14px; color: #666; margin-bottom: 20px; }\n .mfm-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }\n .mfm-btn-alt { background: #f3f4f6; color: #333; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n .mfm-btn-danger { background: #dc2626; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n \n .mfm-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2563eb;\n border-radius: 50%;\n animation: mfm-spin 1s linear infinite;\n margin-right: 8px;\n display: inline-block;\n }\n .status-aborted { color: #dc2626; font-weight: bold; }\n .mfm-duration { color: #666; font-size: 11px; margin-left: 8px; font-weight: normal; }\n ",document.head.appendChild(e)}render(){const e=document.createElement("div");e.id="mockforme-root",document.body.appendChild(e),this.root=e,this.bubble=this.renderBubble(),this.root.appendChild(this.bubble),this.sheet=document.createElement("div"),this.sheet.className="mfm-bottomsheet",this.root.appendChild(this.sheet),this.snackbar=document.createElement("div"),this.snackbar.className="mfm-snackbar",this.root.appendChild(this.snackbar),this.modalOverlay=document.createElement("div"),this.modalOverlay.className="mfm-modal-overlay",this.root.appendChild(this.modalOverlay),this.updateUI()}updateUI(){this.state.isOpen?(this.renderBottomSheetContent(),this.sheet.classList.add("open")):this.sheet.classList.remove("open")}renderBubble(){const e=document.createElement("div");e.className="mfm-bubble";const t="undefined"!=typeof localStorage?JSON.parse(localStorage.getItem("mockforme-dot-pos")||"{}"):{};return t.bottom&&(e.style.bottom=t.bottom+"px"),t.right&&(e.style.right=t.right+"px"),e.innerHTML='\n <div class="mfm-refresh-icon" title="Reload Mappings">↻</div>\n <img src="https://ik.imagekit.io/mfm/static-collection/mfm-48x48.png" alt="MFM" />\n ',this.addDragLogic(e),e.querySelector("img").onclick=e=>{this.isDragging||this.toggleSheet()},e.querySelector(".mfm-refresh-icon").onclick=e=>{e.stopPropagation(),this.refreshMappings()},e}addDragLogic(e){let t,n,s,o,r=!1;const i=(i,a)=>{r=!1,this.isDragging=!1,t=i,n=a;const d=e.getBoundingClientRect();s=window.innerWidth-d.right,o=window.innerHeight-d.bottom},a=(i,a)=>{const d=t-i,c=n-a;(Math.abs(d)>3||Math.abs(c)>3)&&(r=!0,this.isDragging=!0),r&&(e.style.right=s+d+"px",e.style.bottom=o+c+"px")},d=()=>{r&&"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-dot-pos",JSON.stringify({bottom:parseInt(e.style.bottom),right:parseInt(e.style.right)}))},c=e=>{a(e.clientX,e.clientY)},l=()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",l),d()},m=e=>{const t=e.touches[0];a(t.clientX,t.clientY),r&&e.cancelable&&e.preventDefault()},u=()=>{document.removeEventListener("touchmove",m),document.removeEventListener("touchend",u),d()};e.addEventListener("mousedown",(e=>{i(e.clientX,e.clientY),document.addEventListener("mousemove",c),document.addEventListener("mouseup",l)})),e.addEventListener("touchstart",(e=>{const t=e.touches[0];i(t.clientX,t.clientY),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",u)}),{passive:!0})}toggleSheet(){this.state.isOpen=!this.state.isOpen,this.updateUI()}showSnackbar(e){this.snackbar.textContent=e,this.snackbar.classList.add("show"),setTimeout((()=>this.snackbar.classList.remove("show")),3e3)}showConfirm(e,t,n){this.modalOverlay.innerHTML=`\n <div class="mfm-modal">\n <div class="mfm-modal-title">${e}</div>\n <div class="mfm-modal-body">${t}</div>\n <div class="mfm-modal-actions">\n <button class="mfm-btn-alt" id="mfm-confirm-cancel">Cancel</button>\n <button class="mfm-btn-danger" id="mfm-confirm-yes">Delete</button>\n </div>\n </div>\n `,this.modalOverlay.classList.add("show"),this.modalOverlay.querySelector("#mfm-confirm-cancel").onclick=()=>{this.modalOverlay.classList.remove("show")},this.modalOverlay.querySelector("#mfm-confirm-yes").onclick=()=>{this.modalOverlay.classList.remove("show"),n()}}refreshMappings(){if(!this.token)return void this.toggleSheet();const e=document.querySelectorAll(".mfm-refresh-icon, #mfm-refresh-mappings-btn");e.forEach((e=>e.classList.add("loading"))),M(this.token,"JAVASCRIPT",((t,n)=>{console.log("[MockForMe] Mappings reloaded",{mappings:t?.length,rules:n?._rules?.length}),e.forEach((e=>e.classList.remove("loading"))),this.mappings=t||[],this.rules=n?._rules||[],this.onAuthSuccess(this.token,this.mappings,n),this.updateUI(),this.showSnackbar("Mappings reloaded!")}),(t=>{e.forEach((e=>e.classList.remove("loading"))),this.showSnackbar("Failed to reload mappings")}))}renderBottomSheetContent(){this.sheet.innerHTML="",this.isAuthenticated?this.state.selectedRequest?this.sheet.appendChild(this.renderRequestDetail(this.state.selectedRequest)):this.sheet.appendChild(this.renderDashboard()):this.sheet.appendChild(this.renderTokenForm())}renderTokenForm(){const e=document.createElement("div");e.innerHTML=`\n <div class="mfm-header">\n <span class="mfm-title">Save Token</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-logo-container">\n <img src="https://dashboard.mockforme.com/public/assets/images/logo.png" class="mfm-logo" alt="MockForMe Logo" />\n </div>\n <div class="mfm-form">\n <input type="text" class="mfm-input" placeholder="Enter MockForMe Access Token" id="mfm-token-input">\n <div class="mfm-error" id="mfm-error-msg" style="display: ${this.state.lastError?"block":"none"}">${this.state.lastError||""}</div>\n <a href="https://dashboard.mockforme.com/user/token" target="_blank" class="mfm-link">Get Access Token?</a>\n <button class="mfm-btn" id="mfm-save-token-btn">Save</button>\n </div>\n `,e.querySelector(".mfm-close").onclick=()=>{this.state.lastError=null,this.toggleSheet()};const t=e.querySelector("#mfm-save-token-btn"),n=e.querySelector("#mfm-token-input");return t.onclick=()=>{const s=n.value.trim();s&&(t.textContent="Validating...",t.disabled=!0,this.state.lastError=null,e.querySelector("#mfm-error-msg").style.display="none",M(s,c,((e,t)=>{"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-token",s),this.token=s,this.isAuthenticated=!0,this.mappings=e,this.rules=t?._rules||[],this.onAuthSuccess(s,e,t),this.updateUI()}),(e=>{t.textContent="Save",t.disabled=!1;let n="Invalid Token or Network Error";e&&"function"==typeof e.json?e.json().then((e=>{this.state.lastError=e.message||n,this.updateUI()})).catch((()=>{this.state.lastError=n,this.updateUI()})):(this.state.lastError=e.message||n,this.updateUI())})))},e}deleteToken(){"undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),this.token=null,this.isAuthenticated=!1,this.mappings=[],this.rules=[],this.state.lastError=null,this.updateUI()}renderDashboard(){const e=document.createElement("div");e.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">MockForMe</span>\n <div class="mfm-header-actions">\n <button class="mfm-delete-token-btn" id="mfm-delete-token" title="Delete Token">\n <span>🗑</span>\n <span>Token</span>\n </button>\n <button class="mfm-close">×</button>\n </div>\n </div>\n ';const t=document.createElement("div");t.className="mfm-tabs",["mappings","mocked","other"].forEach((e=>{const n=document.createElement("div");n.className="mfm-tab "+(this.state.activeTab===e?"active":""),n.textContent="mappings"===e?"Api Mappings":"mocked"===e?"Mocked Apis":"Other Apis",n.onclick=()=>{this.state.activeTab=e,this.updateUI()},t.appendChild(n)})),e.querySelector(".mfm-header").after(t);const n=document.createElement("div");n.className="mfm-actions-bar","mappings"===this.state.activeTab?(n.innerHTML=`\n <div style="flex: 1; font-size: 13px; color: #666;">Total: ${this.mappings.length+this.rules.length}</div>\n <div class="mfm-icon-btn refresh" id="mfm-refresh-mappings-btn" title="Refresh Mappings">↻</div>\n `,n.querySelector("#mfm-refresh-mappings-btn").onclick=()=>this.refreshMappings()):(n.innerHTML=`\n <input type="text" class="mfm-search-input" placeholder="Search requests..." id="mfm-search-input" value="${this.state.searchQuery}">\n <button class="mfm-icon-btn delete" id="mfm-clear-requests-btn" title="Clear List">🚫</button>\n `,n.querySelector("#mfm-search-input").oninput=e=>{this.state.searchQuery=e.target.value,this.updateRequestList(s)},n.querySelector("#mfm-clear-requests-btn").onclick=()=>{"mocked"===this.state.activeTab?this.mockedRequests=[]:this.otherRequests=[],this.updateUI()}),t.after(n);const s=document.createElement("div");return s.className="mfm-list",this.updateRequestList(s),e.appendChild(s),e.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),e.querySelector("#mfm-delete-token").onclick=()=>{this.showConfirm("Delete Token","Are you sure you want to Delete Token? This will clear your saved token.",(()=>{this.deleteToken()}))},e}updateRequestList(e){if(e.innerHTML="","mappings"===this.state.activeTab){const t=[...this.mappings.map((e=>({...e,type:"mapping"}))),...this.rules.map((e=>({...e,type:"rule"})))];0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");if(n.className="mfm-list-item","mapping"===t.type){const e=(t.apiMethod||"GET").toUpperCase();n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method ${e}">${e}</span><span class="mfm-url">${t.apiEndpoint}</span></div>`}else{const e=t.ruleName||"Network Rule",s=(t.rule?.URL?.condition||{}).value||e;n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method RULE">RULE</span><span class="mfm-url">${s}</span></div>`}e.appendChild(n)}))}else{let t="mocked"===this.state.activeTab?this.mockedRequests:this.otherRequests;if(this.state.searchQuery){const e=this.state.searchQuery.toLowerCase();t=t.filter((t=>(t.url||"").toLowerCase().includes(e)||(t.method||"").toLowerCase().includes(e)||String(t.status).includes(e)))}0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");n.className="mfm-list-item";const s=(t.method||"").toUpperCase(),o=`<span class="mfm-duration" style="color:#2563eb; font-size:11px; font-weight:500; display:block; height:14px; line-height:14px;">${void 0!==t.duration?t.duration+"ms":""}</span>`,r=(()=>{if(!t.rule)return"";let e="delay_and_timeout"===t.rule.action?"Delay and Timout":t.rule.action.split("_")[0];const n=t.rule.config?.value;return void 0!==n&&(t.rule.action.includes("delay")||t.rule.action.includes("timeout"))&&(e+=` (${n}s)`),`<span class="mfm-rule-tag" title="${t.rule.action}">${e}</span>`})();let i=t.status,a="mfm-status";t.pending?i='<span class="mfm-spinner"></span>':t.aborted?(i="Aborted",a+=" status-aborted"):t.timeout?(i="Timeout",a+=" status-timeout"):t.error?(i="Error/CORS",a+=" status-error"):(i=t.status,a+=t.status>=200&&t.status<300?" status-2xx":" status-4xx"),n.innerHTML=`\n <div style="display:flex; align-items:center; width:100%; gap:8px;">\n <span class="mfm-method ${s}">${s}</span>\n <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; justify-content:center;">\n <span class="mfm-url">${r}${t.url}</span>\n ${o}\n </div>\n <span class="${a}">${i}</span>\n </div>\n `,n.onclick=()=>{this.state.selectedRequest=t,this.updateUI()},e.appendChild(n)}))}}renderRequestDetail(e){const t=document.createElement("div");t.className="mfm-detail-view",t.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">Details</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-detail-actions">\n <button class="mfm-back-btn">← Back</button>\n </div>\n ';const n=document.createElement("div");n.className="mfm-tabs",["request","response"].forEach((e=>{const t=document.createElement("div");t.className="mfm-tab "+(this.state.activeRequestTab===e?"active":""),t.textContent=e.charAt(0).toUpperCase()+e.slice(1),t.onclick=()=>{this.state.activeRequestTab=e,this.updateUI()},n.appendChild(t)}));const s=document.createElement("div");s.style.overflowY="auto",s.style.flex="1";const o=(e.method||"").toUpperCase();return s.innerHTML="request"===this.state.activeRequestTab?`\n <div class="section-title">URL</div><div class="mfm-code-block">${o} ${e.url}</div>\n ${e.rule?`<div class="section-title">Matched Rule</div><div class="mfm-code-block">${e.rule.action} (value: ${e.rule.config?.value})</div>`:""}\n <div class="section-title">Headers</div><div class="mfm-code-block">${JSON.stringify(e.requestHeaders||{},null,2)}</div>\n <div class="section-title">Query Params</div><div class="mfm-code-block">${JSON.stringify(e.queryParams||{},null,2)}</div>\n <div class="section-title">Payload</div><div class="mfm-code-block">${this.formatBody(e.requestBody)}</div>\n `:`\n <div class="section-title">Status</div><div class="mfm-code-block">${e.status}</div>\n <div class="section-title">Response Headers</div><div class="mfm-code-block">${JSON.stringify(e.responseHeaders||{},null,2)}</div>\n <div class="section-title">Response Body</div><div class="mfm-code-block">${this.formatBody(e.responseBody)}</div>\n `,t.appendChild(n),t.appendChild(s),t.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),t.querySelector(".mfm-back-btn").onclick=()=>{this.state.selectedRequest=null,this.updateUI()},t}formatBody(e){if(!e)return"";if("undefined"!=typeof FormData&&e instanceof FormData){const t={};return e.forEach(((e,n)=>{"undefined"!=typeof File&&e instanceof File?t[n]=`[File: ${e.name} (${e.size} bytes)]`:"undefined"!=typeof Blob&&e instanceof Blob?t[n]=`[Blob: ${e.type} (${e.size} bytes)]`:t[n]=e})),JSON.stringify(t,null,2)}if("undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams)return JSON.stringify(Object.fromEntries(e.entries()),null,2);if("undefined"!=typeof Blob&&e instanceof Blob)return`[${"undefined"!=typeof File&&e instanceof File?"File":"Blob"}: ${e.type||"binary"} (${e.size} bytes)]`;if(e instanceof ArrayBuffer||"undefined"!=typeof Uint8Array&&e instanceof Uint8Array)return`[Binary Data: ${e.byteLength||e.length} bytes]`;if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}addRequest(e,t){const n=String(t.method||"").toUpperCase().trim(),s=String(t.url||"");console.log(`[MockForMe] addRequest type=${e} method=${n} url=${s} requestId=${t.requestId} pending=${t.pending}`);const o=s.includes("api.mockforme.com")||s.includes("mockforme.com/proxy")||s.includes("mockforme.com/gateway")||f&&s.includes(String(f));if("OPTIONS"===n||o)return;const r="mocked"===e?this.mockedRequests:this.otherRequests,i="mocked"===e?this.otherRequests:this.mockedRequests;if(t.requestId){const n=r.findIndex((e=>e.requestId===t.requestId));if(-1!==n)return r[n]={...r[n],...t},void(this.state.isOpen&&this.state.activeTab===e&&this.updateUI());const s=i.findIndex((e=>e.requestId===t.requestId));if(-1!==s){const e={...i[s],...t};return i.splice(s,1),r.unshift(e),r.length>50&&r.pop(),void(this.state.isOpen&&this.updateUI())}}r.unshift(t),r.length>50&&r.pop(),this.state.isOpen&&this.state.activeTab===e&&this.updateUI()}};function L(e,t){if("undefined"!=typeof window){if(window._mfm_intercepted)return;window._mfm_intercepted=!0}!function(e,t){const n=window.fetch;window.fetch=function(s,o={}){const r="string"==typeof s?s:s.url,i=String(o.method||(s instanceof Request?s.method:"GET")).toUpperCase().trim();if("OPTIONS"===i)return console.log(`[MockForMe] Filtered ${i} ${r} (isInternal=false)`),n(s,o);const a={url:r,method:i,requestHeaders:o.headers||(s instanceof Request?s.headers:{}),requestBody:o.body||(s instanceof Request?"{binary/stream}":null),queryParams:$(r)};try{const c=new URL(r,window.location.origin),{pathname:p,search:f}=c,g=R({url:r,method:i}),b=Math.random().toString(36).substring(7),y=Date.now();if(t!==l&&g){if(I.addRequest("mocked",{...a,requestId:b,pending:!0,startTime:y,rule:g}),"delay"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(s,o);t.then((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_redirect"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(g.config.redirectUrl,o);t.then((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_timeout"===g.action){const e=Number(g.config.value||0);if(0===e){const e=new AbortController,t=n(s,{...o,signal:e.signal});e.abort();const r=Date.now()-y;return I.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:r,aborted:!0}),t.catch((()=>Promise.reject(new Error("Fetch aborted by rules"))))}return new Promise(((t,n)=>{setTimeout((()=>{const e=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:e,aborted:!0}),n(new Error("Fetch timed out by rules"))}),1e3*e)}))}}const x=d(p,i),v=I.token||e;if(!x&&!g)return I.addRequest("other",{...a,requestId:b,pending:!0,startTime:y}),n(s,o).then((async e=>{const t=e.clone(),n=Date.now()-y;try{const s=await t.text();I.addRequest("other",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:s,pending:!1,duration:n})}catch(e){}return e})).catch((e=>{const t=Date.now()-y,n="AbortError"===e.name||e.message.includes("aborted"),s="TimeoutError"===e.name||e.message.includes("timed out"),o=!n&&!s;throw I.addRequest("other",{...a,requestId:b,status:n?"Aborted":s?"Timeout":"Error",responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n,timeout:s,error:o}),e}));let k=r;g||(k=`${w()}${x.url}${f}`);let _={};if(!g){_={[u]:v,[m]:h,"x-mfm-adaptor":t};const e=q();e&&x._ack&&(_["x-mfm-key"]=`${e}-${x._ack}`),x?._ri_&&(_._ri_=x._ri_)}const T=s instanceof Request?s.headers:o.headers,S=new Headers(T||{});Object.entries(_).forEach((([e,t])=>{S.set(e,t)}));let M=o.body;if(!M&&s instanceof Request){const e=s.clone();"GET"!==e.method&&"HEAD"!==e.method&&(M=e.text())}let L={method:i,headers:S,credentials:"include",mode:"cors"};g&&(L.credentials=s instanceof Request&&s.credentials||o.credentials||"same-origin",L.mode=s instanceof Request&&s.mode||o.mode||"cors");const $=e=>(e&&(L.body=e),t===l?(I.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:y}),(({actualUrl:e,url:t,method:n,headers:s,body:o,credentials:r,mode:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);return new Promise(((t,l)=>{window.addEventListener("message",(function e(n){if(n.source!==window)return;const s=n.data;if("INTERCEPTED_RESPONSE"===s?.type&&s?.requestId===d){if(window.removeEventListener("message",e),s?.response?.error)return l(new Error(s.response.message));const{status:n,headers:o,body:r}=s.response;t(new Response(r,{status:n,headers:o}))}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:s,body:o,credentials:r,mode:i,rule:a}},"*")}))})({actualUrl:r,url:k,method:i,headers:Object.fromEntries(S.entries()),body:e,credentials:L.credentials,mode:L.mode,rule:g}).then((e=>(e.clone().text().then((t=>{const n=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:t,pending:!1,duration:n})})),e))).catch((e=>{const t=Date.now()-y,n="AbortError"===e.name;throw I.addRequest("mocked",{...a,requestId:b,status:n?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n}),e}))):(I.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:y}),n(k,L).then((e=>{const t=e.clone(),n=t.headers.get("content-type")||"",s=Date.now()-y,o=n=>(I.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(t.headers.entries()),responseBody:n,pending:!1,duration:s}),new Response("string"==typeof n?n:JSON.stringify(n),{status:e.status,headers:t.headers}));return n.includes("application/json")?t.json().then(o):t.text().then(o)})).catch((e=>{console.warn("mockforme fetch interceptor error",e);const t=Date.now()-y,r="AbortError"===e.name;return I.addRequest("mocked",{...a,requestId:b,status:r?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:r}),n(s,o)}))));return M instanceof Promise?M.then((e=>$(e))):$(M)}catch(e){return console.warn("mockforme fetch interceptor error",e),n(s,o)}}}(e,t),function(e,t){const n=window.XMLHttpRequest;window.XMLHttpRequest=class{constructor(){return this._xhr=new n,this._headers={},this._method=null,this._url=null,this._body=null,this._isMocked=!1,this._captured=!1,this._requestId=null,this._startTime=null,this._capture=(e,t,n,s={})=>{if(this._captured)return;this._captured=!0;const o=this._isMocked?"mocked":"other",r=this._startTime?Date.now()-this._startTime:0;console.log(`[MockForMe] XHR Capture: status=${e} method=${this._method} url=${this._url} duration=${r}ms`);const i=e=>{const t=`on${e}`;(this[t]||this._xhr[t])?.(),this._xhr.dispatchEvent(new Event(e))};if(I.addRequest(o,{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,status:e,responseBody:t,responseHeaders:n,pending:!1,duration:r,...s}),"Aborted"!==e)try{Object.defineProperty(this,"responseText",{value:t,configurable:!0}),Object.defineProperty(this,"response",{value:t,configurable:!0}),Object.defineProperty(this,"status",{value:Number(e)||0,configurable:!0}),Object.defineProperty(this,"statusText",{value:200==e?"OK":"",configurable:!0})}catch(e){console.warn("[MockForMe] Error setting XHR props",e)}Object.defineProperty(this,"readyState",{value:4,configurable:!0}),i("readystatechange"),"Aborted"===e||s.aborted?i("abort"):0===e||"Error"===e||s.error?i("error"):"Timeout"===e||s.timeout?i("timeout"):i("load")},new Proxy(this,{get:(e,t)=>{if(t in e)return e[t];const n=e._xhr[t];return"function"==typeof n?n.bind(e._xhr):n},set:(e,t,n)=>t in e?(e[t]=n,!0):(e._xhr[t]=n,!0)})}open(e,t,n=!0,s=null,o=null){this._method=(e||"").toUpperCase().trim();try{this._url=new URL(t,window.location.href).toString()}catch(e){this._url=t}return console.log(`[MockForMe] XHR.open: ${this._method} ${this._url}`),this._xhr._mockOriginalUrl=this._url,this._xhr._mockMethod=this._method,this._xhr.open(e,t,n,s,o)}setRequestHeader(e,t){return this._headers[e]=t,this._xhr.setRequestHeader(e,t)}send(s){const o=String(this._method||"").toUpperCase().trim();if(console.log(`[MockForMe] XHR send method=${o} url=${this._url}`),"OPTIONS"===o)return console.log("[MockForMe] XHR Skip OPTIONS"),this._xhr.send(s);this._body=s,this._requestId=Math.random().toString(36).substring(7),this._startTime=Date.now(),this._xhr.addEventListener("abort",(()=>{this._capture("Aborted","Request aborted",{},{aborted:!0})}),{once:!0}),this._xhr.addEventListener("error",(()=>{this._capture(0,"Network Error",{},{error:!0})}),{once:!0}),this._xhr.addEventListener("timeout",(()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})}),{once:!0});try{const o=new URL(this._url),{pathname:r,search:i}=o,a=R({url:this._url,method:this._method});a&&(this._isMocked=!0,I.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime,rule:a}));const c=I.token||e;if(t!==l&&a){if("delay"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_redirect"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.open(this._method,a.config.redirectUrl,!0),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_timeout"===a.action){const e=Number(a.config.value||0);return 0===e?(this._capture("Aborted","Request aborted by rules",{},{aborted:!0}),void this._xhr.abort()):setTimeout((()=>{this._capture("Aborted","Request timed out by rules",{},{aborted:!0}),this._xhr.abort()}),1e3*e)}}const p=d(r,this._method);if(!p&&!a)return console.log("[MockForMe] XHR No Match:",{pathname:r,method:this._method}),this._isMocked=!1,I.addRequest("other",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(s);console.log("[MockForMe] XHR Matched:",{pathname:r,match:p}),this._isMocked=!0;let f=this._url;a||(f=`${w()}${p.url}${i}`);let g={};if(!a){g={[u]:c,[m]:h,"x-mfm-adaptor":t};const e=q();e&&p._ack&&(g["x-mfm-key"]=`${e}-${p._ack}`),p?._ri_&&(g._ri_=p._ri_)}const b={...this._headers,...g};if(I.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),t===l)(({actualUrl:e,url:t,method:n,headers:s,body:o,onSuccess:r,onError:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);window.addEventListener("message",(function e(t){if(t.source!==window)return;const n=t.data;if("INTERCEPTED_RESPONSE"===n.type&&n.requestId===d){if(window.removeEventListener("message",e),n.response?.error)return i?.(n.response.message);const{body:t,status:s,headers:o}=n.response;r?.({body:t,status:s,headers:o})}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:s,body:o,credentials:"same-origin",mode:"cors",rule:a}},"*")})({actualUrl:this._url,url:f,method:this._method,headers:b,body:this._body,rule:a,onSuccess:({body:e,status:t,headers:n})=>{this._capture(t,e,n||{})},onError:e=>{this._capture(0,e.message||"XHR Error",{},{error:!0})}});else{const e=new n;e.open(this._method,f,!0);for(const t in b)e.setRequestHeader(t,b[t]);e.withCredentials=this.withCredentials,e.onload=()=>{this._capture(e.status,e.responseText,e.getAllResponseHeaders())},e.onerror=()=>{this._capture(0,"Network Error",{},{error:!0})},e.ontimeout=()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})},e.send(this._body)}}catch(e){if(console.error("[MockForMe] XHR Interception Error",e),!this._xhr.readyState||1===this._xhr.readyState)return this._xhr.send(s)}}}}(e,t)}function $(e){try{const t=new URL(e,window.location.origin).searchParams;return Object.fromEntries(t.entries())}catch{return{}}}const O=(e,t=null,s=c)=>{if(n())return;if(s===c)try{e&&("undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),I.token=e,I.isAuthenticated=!0),!e&&I.token&&(e=I.token),I.onAuthSuccess=(e,t,n)=>{i(t),k(n),I.mappings=t,I.rules=n?._rules||[]}}catch(e){console.log("Error in showing mockforme widget",e)}t||={mappings:[],_o:null,mk:null,_mck:null,_rules:[]};const{mappings:o=[],_o:r,mk:l,_mck:p,_rules:f=[]}=t;return i(o),k({_o:r,mk:l,_mck:p,_rules:f}),{run:(t,n=()=>{})=>{L(e,s),r&&l?(I.mappings=o,I.rules=f,t?.(o,{_o:r,mk:l,_mck:p,_rules:f})):e&&function(e,n,s,o){T({method:"get",url:g,async:!1,headers:{[u]:e,[m]:h,"x-mfm-adaptor":n}},(e=>{if(e)try{const{di:o,iv:r,_o:a,_mck:d,mk:c,r:l}=e,m=S({di:o,iv:r},a);k({_o:a,mk:c,_mck:d,_rules:l}),m&&(i(m),n=m,s={_o:a,mk:c,_mck:d,_rules:l},I.mappings=n,I.rules=s?._rules||[],t?.(n,s))}catch(e){return void o(new Error("Unable to fetch mocked apis"))}var n,s}),(e=>{console.log("Error in loading mocked apis"),o(e)})).request()}(e,s,0,n)},checkIfApiToBeMocked:d,getMappings:a,doFetchMappings:(t,n)=>{M(e||I.token,s,((e,n)=>{I.mappings=e,I.rules=n?._rules||[],t?.(e,n)}),n)}}},C=n();C||(globalThis.mockforme=O);let H=O;C&&(H=e=>({run:()=>{}}));const U=H;module.exports=t})();
|
|
1
|
+
(()=>{"use strict";var e={d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{mockforme:()=>U});const n=()=>"undefined"!=typeof process&&null!=process.versions?.node,o=e=>{try{return e?e.toLowerCase():e}catch(e){return console.log(e),null}},s=(e,t)=>{const n=e.split("/"),o=t.split("/");if(n.length!==o.length)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t!==o[e]&&":any"!==t)return!1}return!0};let r=[];function i(e){r=e}function a(){return r}function d(e,t="get"){const n=e=>e.startsWith("/")?e:`/${e}`,i=n(e);for(const a of r)if(o(a.apiMethod)===o(t)){const o=n(a.apiEndpoint);if(o===i||s(o,i)){console.log(`[MockForMe] Match Found: ${t} ${e} -> ${a.apiEndpoint}`);const n={url:e,method:a.apiMethod,_ack:a._ack||null};return a._ri_&&(n._ri_=a._ri_),n}}return null}const c="JAVASCRIPT",l="CHROME_EXTENSION",m="mfmver",u="mockforme",p="4.2.8",h="https://api.mockforme.com",f=`${h}/mockforme`,g=`${h}/gateway/3f4eae522b`;let b=null,y=null,x=null,v=[];function k({_o:e,mk:t,_mck:n,_rules:o}){b=e,y=t,x=n,v=o}function w(){return v}function _(){return`${f}/${b}/${y}`}function q(){return x}const E=e=>{try{return new URL(e,window.location.origin).toString()}catch{return e}};function R({url:e,method:t}){try{const t=w();if(!Array.isArray(t)||0===t.length)return null;const n=globalThis.location?.origin||"http://localhost";for(const o of t){const t=o.rule?.URL;if(!t)continue;const{condition:s}=t;if(!s)continue;let r="";switch(t.type){case"URL":{let t;try{t=new URL(e,n),r=t.pathname}catch{r=e}break}case"QUERY":{let t;try{t=new URL(e,n)}catch{t=null}t&&(r=t.searchParams.get(s.key)||"");break}case"BODY":case"HEADERS":r="";break;default:continue}let i=!1;switch(s.operator){case"contains":i=r.includes(s.value);break;case"equal":case"equals":i=r===s.value;break;case"regexp":try{let e=s.value;if(e.startsWith("/")&&e.lastIndexOf("/")>0){const t=e.lastIndexOf("/"),n=e.slice(1,t),o=e.slice(t+1);i=new RegExp(n,o).test(r)}else i=new RegExp(e).test(r)}catch{i=!1}}if(i){const e=s.activeAction;return{action:e,config:s.actions?.[e]||{}}}}return null}catch(e){return console.error("[mockforme] checkIfRulesToBeApplied error",e),null}}const S=function(e,t,o){const s=n();var r;function i(){if(4==r.readyState)if(r.status.toString().match(/^20[0-9]$/)){var e=function(){var e=r.getResponseHeader("Content-Type"),t=r.responseText;if(e){var n=e.split(";");try{return"application/json"===n[0]?JSON.parse(t):t}catch(e){throw"Unable to convert response header"}}}();t.call(this,e,r)}else o.call(this,r.responseText,r)}function a(e,t){var n=[];for(var o in e)if(e.hasOwnProperty(o)){var s=t?t+"["+o+"]":o,r=e[o];n.push("object"==typeof r?a(r,s):encodeURIComponent(s)+"="+encodeURIComponent(r))}return n.join("&")}return s||(window.XMLHttpRequest?r=new XMLHttpRequest:window.ActiveXObject&&(r=new ActiveXObject("Microsoft.XMLHTTP"))),{request:function(){if(!s){if(window.XMLHttpRequest)r.onload=i;else{if(!window.ActiveXObject)throw"unable to process ajax";r.onreadystatechange=i}var t=a(e.params);if("get"==e.method.toLowerCase()&&"object"==typeof e.params){if(-1==e.url.indexOf("?"))e.url+="?";else{var n=e.url.split("?");n[1]&&n[1].split("=")[1]&&(e.url+="&")}e.url+=t}e.hasOwnProperty("async")||(e.async=!0),r.open(e.method,e.url,e.async),e.headers&&function(e){for(let t in e)r.setRequestHeader(t,e[t])}(e.headers),r.send(t)}}}};function T({di:e,iv:t},n){var o;const s=function(e,t){const n=t.length;return e.split("").map(((e,o)=>String.fromCharCode(e.charCodeAt(0)^t.charCodeAt(o%n)))).join("")}((o=e,"undefined"!=typeof Buffer?Buffer.from(o,"base64").toString("utf8"):decodeURIComponent(escape(atob(o)))),n+t);return JSON.parse(s)}function M(e,t,n,o){fetch(g,{method:"GET",headers:{[u]:e,[m]:p,"x-mfm-adaptor":t}}).then((e=>{if(!e.ok)throw e;return e.json()})).then((e=>{if(e){const{di:t,iv:o,_o:s,_mck:r,mk:a,r:d}=e,c=T({di:t,iv:o},s);k({_o:s,mk:a,_mck:r,_rules:d}),c&&(i(c),n(c,{_o:s,mk:a,_mck:r,_rules:d}))}})).catch((e=>{console.error("Error in loading mocked APIs:",e),o(e)}))}const I=new class{constructor(e={}){n()||(this.token=e.token||("undefined"!=typeof localStorage?localStorage.getItem("mockforme-token"):null),this.isAuthenticated=!!this.token,this.onAuthSuccess=e.onAuthSuccess||(()=>{}),this.mappings=e.mappings||[],this.rules=e.rules||[],this.mockedRequests=[],this.otherRequests=[],this.state={isOpen:"undefined"!=typeof localStorage&&"true"===localStorage.getItem("mockforme-widget-open"),activeTab:"mappings",selectedRequest:null,activeRequestTab:"response",lastError:null,searchQuery:""},"undefined"!=typeof document&&(window.addEventListener("resize",(()=>{this.bubble&&this.keepInBounds(this.bubble)})),document.addEventListener("click",(e=>{this.sharedDropdown&&this.sharedDropdown.classList.remove("show")})),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>this.init())):this.init()))}init(){document.getElementById("mockforme-root")||(this.createStyles(),this.render())}createStyles(){if(document.getElementById("mockforme-styles"))return;const e=document.createElement("style");e.id="mockforme-styles",e.innerHTML="\n #mockforme-root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n z-index: 999999;\n position: fixed;\n }\n .mfm-bubble {\n position: fixed;\n bottom: 20px;\n right: 20px;\n height: 42px;\n padding: 4px 8px;\n border-radius: 24px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n background: white;\n display: flex;\n align-items: center;\n gap: 8px;\n transition: transform 0.2s;\n border: 1px solid #eee;\n }\n .mfm-bubble:hover { transform: scale(1.05); }\n .mfm-bubble img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }\n \n .mfm-refresh-icon {\n width: 25px;\n height: 25px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #2563eb;\n font-size: 25px;\n transition: background 0.2s;\n margin-right: 10px;\n background: #f3f4f6;\n padding: 8px;\n }\n .mfm-refresh-icon:hover { }\n .mfm-refresh-icon.loading { animation: mfm-spin 1s linear infinite; }\n @keyframes mfm-spin { 100% { transform: rotate(360deg); } }\n\n .mfm-bottomsheet {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background: white;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.15);\n padding: 20px;\n z-index: 999999;\n height: 80vh;\n max-width: 500px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s ease-out;\n transform: translateY(100%);\n visibility: hidden;\n overflow-y: auto;\n }\n @media (max-width: 480px) {\n .mfm-bottomsheet { padding: 16px; }\n }\n .mfm-bottomsheet.open { transform: translateY(0); visibility: visible; }\n \n .mfm-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n }\n .mfm-title-group { display: flex; align-items: center; gap: 10px; }\n .mfm-title { font-size: 18px; font-weight: 600; color: #111; }\n .mfm-title img { height: 30px; }\n .mfm-delete-token-btn { \n background: #fee2e2; \n color: #dc2626; \n font-size: 13px; \n padding: 4px 12px; \n border-radius: 20px; \n cursor: pointer; \n display: flex;\n align-items: center;\n gap: 6px;\n border: none;\n font-weight: 500;\n transition: background 0.2s;\n }\n .mfm-delete-token-btn:hover { background: #fecaca; }\n .mfm-header-actions { display: flex; align-items: center; gap: 10px; }\n .mfm-close { background: none; border: none; font-size: 30px; cursor: pointer; color: #666; }\n /* Login Form */\n .mfm-logo-container { text-align: center; padding: 20px 0; width: 100%; display: flex; justify-content: center; flex-shrink: 0; }\n .mfm-logo { width: 120px; height: auto; }\n .mfm-link { color: #2563eb; text-decoration: none; font-size: 14px; margin-top: 5px; cursor: pointer; display: inline-block; }\n .mfm-link:hover { text-decoration: underline; }\n .mfm-form { display: flex; flex-direction: column; gap: 10px; }\n .mfm-input {\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n .mfm-input:focus { border-color: #2563eb; }\n .mfm-error { color: #dc2626; font-size: 13px; margin-top: -5px; }\n .mfm-btn {\n padding: 12px;\n background: #2563eb;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n .mfm-btn:hover { background: #1d4ed8; }\n .mfm-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n .mfm-tabs { \n display: flex; \n border-bottom: 1px solid #dee2e6; \n margin-bottom: 10px; \n flex-shrink: 0; \n gap: 2px;\n }\n .mfm-tab {\n padding: 8px 16px;\n cursor: pointer;\n color: #666;\n border: 1px solid transparent;\n border-bottom: none;\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n font-weight: 300;\n font-size: 14px;\n margin-bottom: -1px;\n transition: all 0.2s;\n }\n .mfm-tab:hover {\n background-color: #f8f9fa;\n border-color: #e9ecef #e9ecef transparent;\n }\n .mfm-tab.active { \n color: #2563eb; \n background-color: white;\n border: 1px solid #dee2e6;\n border-bottom: 1px solid white;\n font-weight: 500;\n }\n \n .mfm-list {\n flex: 1;\n overflow-y: auto;\n min-height: 200px;\n max-height: 60vh;\n }\n .mfm-list-item {\n padding: 10px;\n border-bottom: 1px solid #f3f4f6;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n min-height: 52px;\n box-sizing: border-box;\n }\n .mfm-list-item:hover { background: #f9fafb; }\n .mfm-method { \n font-weight: bold; \n padding: 4px 8px; \n border-radius: 4px; \n margin-right: 10px;\n font-size: 12px;\n }\n .GET { background: #dbfcfe; color: #006064; }\n .POST { background: #dcfce7; color: #14532d; }\n .PUT { background: #fef9c3; color: #713f12; }\n .DELETE { background: #fee2e2; color: #7f1d1d; }\n .PATCH { background: #f3e8ff; color: #581c87; }\n .RULE { background: #f3f4f6; color: #374151; }\n\n /* Search & Actions */\n .mfm-actions-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-search-input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; }\n .mfm-search-input:focus { border-color: #2563eb; }\n .mfm-icon-btn { background: none; border: none; font-size: 25px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 4px; }\n .mfm-icon-btn:hover { color: #333; }\n .mfm-icon-btn.refresh { color: #2563eb; padding: 0; height: 20px; width:20px; border-radius: 50%; }\n .mfm-icon-btn.refresh.loading { animation: mfm-spin 1s linear infinite; }\n .mfm-icon-btn.delete { color: #dc2626; }\n\n @media (max-height: 700px) {\n .mfm-bottomsheet { height: 85vh; padding: 12px; }\n .mfm-logo-container { padding: 10px 0; }\n .mfm-logo { width: 100px; }\n .mfm-header { margin-bottom: 10px; }\n .mfm-form { gap: 8px; }\n .mfm-input { padding: 10px; font-size: 14px; }\n .mfm-btn { padding: 10px; font-size: 14px; }\n }\n\n .mfm-url { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .mfm-status { font-weight: bold; margin-left: 10px; }\n .status-2xx { color: #16a34a; }\n .status-4xx { color: #dc2626; }\n .status-5xx { color: #ea580c; }\n .status-aborted, .status-timeout, .status-error { color: #dc2626; font-size: 11px; font-weight: 500; }\n\n .mfm-rule-tag {\n font-size: 9px;\n padding: 2px 4px;\n border-radius: 4px;\n background: #fef3c7;\n color: #92400e;\n border: 1px solid #fde68a;\n margin-right: 6px;\n text-transform: uppercase;\n font-weight: bold;\n }\n\n .mfm-detail-view { display: flex; flex-direction: column; height: 100%; }\n .mfm-detail-actions { margin-bottom: 10px; }\n .mfm-back-btn { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 0;}\n \n .mfm-code-block {\n background: #f8fafc;\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n font-family: monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-break: break-all;\n max-height: 300px;\n border: 1px solid #e2e8f0;\n }\n .section-title { font-size: 14px; font-weight: 600; margin: 10px 0 5px; color: #333; }\n .section-header { display: flex; align-items: center; justify-content: space-between; margin: 10px 0 5px; }\n .mfm-copy-btn {\n background: #f1f5f9;\n color: #64748b;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 10px;\n cursor: pointer;\n border: 1px solid #e2e8f0;\n transition: all 0.2s;\n font-weight: 600;\n text-transform: uppercase;\n }\n .mfm-copy-btn:hover { background: #e2e8f0; color: #334155; }\n \n .mfm-copy-group {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 4px;\n }\n .mfm-copy-group .mfm-copy-btn {\n font-size: 9px;\n padding: 2px 6px;\n }\n\n /* Dropdown Menu */\n .mfm-menu-dots {\n cursor: pointer;\n padding: 4px 8px;\n font-size: 18px;\n color: #666;\n border-radius: 4px;\n transition: background 0.2s;\n line-height: 1;\n margin-left: 4px;\n }\n .mfm-menu-dots:hover { background: #f3f4f6; color: #333; }\n \n .mfm-dropdown-shared {\n position: fixed;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n z-index: 1000005;\n display: none;\n flex-direction: column;\n width: 160px;\n overflow: hidden;\n }\n .mfm-dropdown-shared.show { display: flex; }\n .mfm-dropdown-item {\n padding: 10px 14px;\n font-size: 13px;\n color: #334155;\n cursor: pointer;\n transition: background 0.2s;\n text-align: left;\n border-bottom: 1px solid #f1f5f9;\n white-space: nowrap;\n }\n .mfm-dropdown-item:last-child { border-bottom: none; }\n .mfm-dropdown-item:hover { background: #f8fafc; color: #2563eb; }\n\n /* Snackbar */\n .mfm-snackbar {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: #333;\n color: white;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n z-index: 1000000;\n opacity: 0;\n transition: opacity 0.3s, transform 0.3s;\n pointer-events: none;\n }\n .mfm-snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n /* Confirm Dialog */\n .mfm-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000001;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n .mfm-modal-overlay.show { opacity: 1; pointer-events: auto; }\n .mfm-modal {\n background: white;\n padding: 20px;\n border-radius: 12px;\n width: 300px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n transform: scale(0.9);\n transition: transform 0.2s;\n }\n .mfm-modal-overlay.show .mfm-modal { transform: scale(1); }\n .mfm-modal-title { font-size: 16px; font-weight: 600; margin-bottom: 10px; }\n .mfm-modal-body { font-size: 14px; color: #666; margin-bottom: 20px; }\n .mfm-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }\n .mfm-btn-alt { background: #f3f4f6; color: #333; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n .mfm-btn-danger { background: #dc2626; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n \n .mfm-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2563eb;\n border-radius: 50%;\n animation: mfm-spin 1s linear infinite;\n margin-right: 8px;\n display: inline-block;\n }\n .status-aborted { color: #dc2626; font-weight: bold; }\n .mfm-duration { color: #666; font-size: 11px; margin-left: 8px; font-weight: normal; }\n ",document.head.appendChild(e)}render(){const e=document.createElement("div");e.id="mockforme-root",document.body.appendChild(e),this.root=e,this.bubble=this.renderBubble(),this.root.appendChild(this.bubble),this.sheet=document.createElement("div"),this.sheet.className="mfm-bottomsheet",this.root.appendChild(this.sheet),this.snackbar=document.createElement("div"),this.snackbar.className="mfm-snackbar",this.root.appendChild(this.snackbar),this.modalOverlay=document.createElement("div"),this.modalOverlay.className="mfm-modal-overlay",this.root.appendChild(this.modalOverlay),this.sharedDropdown=document.createElement("div"),this.sharedDropdown.className="mfm-dropdown-shared",this.sharedDropdown.innerHTML='\n <div class="mfm-dropdown-item" data-format="curl">Copy as cURL</div>\n <div class="mfm-dropdown-item" data-format="fetch">Copy as Fetch</div>\n <div class="mfm-dropdown-item" data-format="xhr">Copy as XHR</div>\n ',this.root.appendChild(this.sharedDropdown),this.updateUI()}updateUI(){this.state.isOpen?(this.renderBottomSheetContent(),this.sheet.classList.add("open")):this.sheet.classList.remove("open")}renderBubble(){const e=document.createElement("div");e.className="mfm-bubble";const t="undefined"!=typeof localStorage?JSON.parse(localStorage.getItem("mockforme-dot-pos")||"{}"):{};return t.bottom&&(e.style.bottom=t.bottom+"px"),t.right&&(e.style.right=t.right+"px"),e.innerHTML='\n <div class="mfm-refresh-icon" title="Reload Mappings">↻</div>\n <img src="https://ik.imagekit.io/mfm/static-collection/mfm-48x48.png" alt="MFM" />\n ',this.addDragLogic(e),this.keepInBounds(e),e.querySelector("img").onclick=e=>{this.isDragging||this.toggleSheet()},e.querySelector(".mfm-refresh-icon").onclick=e=>{e.stopPropagation(),this.refreshMappings()},e}keepInBounds(e){const t=e.getBoundingClientRect(),n=10;let o=parseInt(e.style.right)||20,s=parseInt(e.style.bottom)||20;const r=window.innerWidth-t.width-n,i=window.innerHeight-t.height-n;o<n&&(o=n),s<n&&(s=n),o>r&&(o=r),s>i&&(s=i),e.style.right=o+"px",e.style.bottom=s+"px"}addDragLogic(e){let t,n,o,s,r=!1;const i=(i,a)=>{r=!1,this.isDragging=!1,t=i,n=a;const d=e.getBoundingClientRect();o=window.innerWidth-d.right,s=window.innerHeight-d.bottom},a=(i,a)=>{const d=t-i,c=n-a;(Math.abs(d)>3||Math.abs(c)>3)&&(r=!0,this.isDragging=!0),r&&(e.style.right=o+d+"px",e.style.bottom=s+c+"px")},d=()=>{r&&(this.keepInBounds(e),"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-dot-pos",JSON.stringify({bottom:parseInt(e.style.bottom),right:parseInt(e.style.right)})))},c=e=>{a(e.clientX,e.clientY)},l=()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",l),d()},m=e=>{const t=e.touches[0];a(t.clientX,t.clientY),r&&e.cancelable&&e.preventDefault()},u=()=>{document.removeEventListener("touchmove",m),document.removeEventListener("touchend",u),d()};e.addEventListener("mousedown",(e=>{i(e.clientX,e.clientY),document.addEventListener("mousemove",c),document.addEventListener("mouseup",l)})),e.addEventListener("touchstart",(e=>{const t=e.touches[0];i(t.clientX,t.clientY),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",u)}),{passive:!0})}toggleSheet(){this.state.isOpen=!this.state.isOpen,"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-widget-open",this.state.isOpen),this.updateUI()}showSnackbar(e){this.snackbar.textContent=e,this.snackbar.classList.add("show"),setTimeout((()=>this.snackbar.classList.remove("show")),3e3)}showConfirm(e,t,n){this.modalOverlay.innerHTML=`\n <div class="mfm-modal">\n <div class="mfm-modal-title">${e}</div>\n <div class="mfm-modal-body">${t}</div>\n <div class="mfm-modal-actions">\n <button class="mfm-btn-alt" id="mfm-confirm-cancel">Cancel</button>\n <button class="mfm-btn-danger" id="mfm-confirm-yes">Delete</button>\n </div>\n </div>\n `,this.modalOverlay.classList.add("show"),this.modalOverlay.querySelector("#mfm-confirm-cancel").onclick=()=>{this.modalOverlay.classList.remove("show")},this.modalOverlay.querySelector("#mfm-confirm-yes").onclick=()=>{this.modalOverlay.classList.remove("show"),n()}}refreshMappings(){if(!this.token)return void this.toggleSheet();const e=document.querySelectorAll(".mfm-refresh-icon, #mfm-refresh-mappings-btn");e.forEach((e=>e.classList.add("loading"))),M(this.token,"JAVASCRIPT",((t,n)=>{console.log("[MockForMe] Mappings reloaded",{mappings:t?.length,rules:n?._rules?.length}),e.forEach((e=>e.classList.remove("loading"))),this.mappings=t||[],this.rules=n?._rules||[],this.onAuthSuccess(this.token,this.mappings,n),this.updateUI(),this.showSnackbar("Mappings reloaded!")}),(t=>{e.forEach((e=>e.classList.remove("loading"))),this.showSnackbar("Failed to reload mappings")}))}renderBottomSheetContent(){this.sheet.innerHTML="",this.isAuthenticated?this.state.selectedRequest?this.sheet.appendChild(this.renderRequestDetail(this.state.selectedRequest)):this.sheet.appendChild(this.renderDashboard()):this.sheet.appendChild(this.renderTokenForm())}renderTokenForm(){const e=document.createElement("div");e.innerHTML=`\n <div class="mfm-header">\n <span class="mfm-title">Save Token</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-logo-container">\n <img src="https://dashboard.mockforme.com/public/assets/images/logo.png" class="mfm-logo" alt="MockForMe Logo" />\n </div>\n <div class="mfm-form">\n <input type="text" class="mfm-input" placeholder="Enter MockForMe Access Token" id="mfm-token-input">\n <div class="mfm-error" id="mfm-error-msg" style="display: ${this.state.lastError?"block":"none"}">${this.state.lastError||""}</div>\n <a href="https://dashboard.mockforme.com/user/token" target="_blank" class="mfm-link">Get Access Token?</a>\n <button class="mfm-btn" id="mfm-save-token-btn">Save</button>\n </div>\n `,e.querySelector(".mfm-close").onclick=()=>{this.state.lastError=null,this.toggleSheet()};const t=e.querySelector("#mfm-save-token-btn"),n=e.querySelector("#mfm-token-input");return t.onclick=()=>{const o=n.value.trim();o&&(t.textContent="Validating...",t.disabled=!0,this.state.lastError=null,e.querySelector("#mfm-error-msg").style.display="none",M(o,c,((e,t)=>{"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-token",o),this.token=o,this.isAuthenticated=!0,this.mappings=e,this.rules=t?._rules||[],this.onAuthSuccess(o,e,t),this.updateUI()}),(e=>{t.textContent="Save",t.disabled=!1;let n="Invalid Token or Network Error";e&&"function"==typeof e.json?e.json().then((e=>{this.state.lastError=e.message||n,this.updateUI()})).catch((()=>{this.state.lastError=n,this.updateUI()})):(this.state.lastError=e.message||n,this.updateUI())})))},e}updateMappings(e,t){this.mappings=e||[],this.rules=t?._rules||[],this.root&&this.updateUI()}deleteToken(){"undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),this.token=null,this.isAuthenticated=!1,this.mappings=[],this.rules=[],this.state.lastError=null,this.updateUI()}renderDashboard(){const e=document.createElement("div");e.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title"><img src="https://www.mockforme.com/assets/images/logo.png" /></span>\n <div class="mfm-header-actions">\n <button class="mfm-close">×</button>\n </div>\n </div>\n ';const t=document.createElement("div");t.className="mfm-tabs",["mappings","mocked","other","settings"].forEach((e=>{const n=document.createElement("div");n.className="mfm-tab "+(this.state.activeTab===e?"active":""),n.textContent="mappings"===e?"Api Mappings":"mocked"===e?"Mocked Apis":"other"===e?"Other Apis":"Settings",n.onclick=()=>{this.state.activeTab=e,this.updateUI()},t.appendChild(n)})),e.querySelector(".mfm-header").after(t);const n=document.createElement("div");n.className="mfm-actions-bar","mappings"===this.state.activeTab?(n.innerHTML=`\n <div style="flex: 1; font-size: 13px; color: #666;">Total: ${this.mappings.length+this.rules.length}</div>\n <div class="mfm-icon-btn refresh" id="mfm-refresh-mappings-btn" title="Refresh Mappings">↻</div>\n `,n.querySelector("#mfm-refresh-mappings-btn").onclick=()=>this.refreshMappings()):"settings"!==this.state.activeTab?(n.innerHTML=`\n <input type="text" class="mfm-search-input" placeholder="Search requests..." id="mfm-search-input" value="${this.state.searchQuery}">\n <button class="mfm-icon-btn delete" id="mfm-clear-requests-btn" title="Clear List">🚫</button>\n `,n.querySelector("#mfm-search-input").oninput=e=>{this.state.searchQuery=e.target.value,this.updateRequestList(o)},n.querySelector("#mfm-clear-requests-btn").onclick=()=>{"mocked"===this.state.activeTab?this.mockedRequests=[]:this.otherRequests=[],this.updateUI()}):n.style.display="none",t.after(n);const o=document.createElement("div");return o.className="mfm-list",this.updateRequestList(o),e.appendChild(o),e.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),e}updateRequestList(e){if(e.innerHTML="","mappings"===this.state.activeTab){const t=[...this.mappings.map((e=>({...e,type:"mapping"}))),...this.rules.map((e=>({...e,type:"rule"})))];0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");if(n.className="mfm-list-item","mapping"===t.type){const e=(t.apiMethod||"GET").toUpperCase();n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method ${e}">${e}</span><span class="mfm-url">${t.apiEndpoint}</span></div>`}else{const e=t.ruleName||"Network Rule",o=(t.rule?.URL?.condition||{}).value||e;n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method RULE">RULE</span><span class="mfm-url">${o}</span></div>`}e.appendChild(n)}))}else if("settings"===this.state.activeTab)e.innerHTML='\n <div style="padding: 16px;">\n <div class="section-title" style="margin-top: 0; margin-bottom: 12px; font-size: 15px; border-bottom: 1px solid #eee; padding-bottom: 8px;">Token Management</div>\n <p style="font-size: 13px; color: #666; margin-bottom: 16px;">Removing your token will clear all saved mappings and rules from this browser instance.</p>\n <button class="mfm-delete-token-btn" id="mfm-settings-delete-token" style="padding: 8px 16px; font-size: 14px; width: fit-content;">\n <span>🗑</span>\n <span>Delete Token</span>\n </button>\n </div>\n ',e.querySelector("#mfm-settings-delete-token").onclick=()=>{this.showConfirm("Delete Token","Are you sure you want to Delete Token? This will clear your saved token.",(()=>{this.deleteToken()}))};else{let t="mocked"===this.state.activeTab?this.mockedRequests:this.otherRequests;if(this.state.searchQuery){const e=this.state.searchQuery.toLowerCase();t=t.filter((t=>(t.url||"").toLowerCase().includes(e)||(t.method||"").toLowerCase().includes(e)||String(t.status).includes(e)))}0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");n.className="mfm-list-item";const o=(t.method||"").toUpperCase(),s=`<span class="mfm-duration" style="color:#2563eb; font-size:11px; font-weight:500; display:block; height:14px; line-height:14px;">${void 0!==t.duration?t.duration+"ms":""}</span>`,r=(()=>{if(!t.rule)return"";let e="delay_and_timeout"===t.rule.action?"Delay and Timout":t.rule.action.split("_")[0];const n=t.rule.config?.value;return void 0!==n&&(t.rule.action.includes("delay")||t.rule.action.includes("timeout"))&&(e+=` (${n}s)`),`<span class="mfm-rule-tag" title="${t.rule.action}">${e}</span>`})();let i=t.status,a="mfm-status";t.pending?i='<span class="mfm-spinner"></span>':t.aborted?(i="Aborted",a+=" status-aborted"):t.timeout?(i="Timeout",a+=" status-timeout"):t.error?(i="Error/CORS",a+=" status-error"):(i=t.status,a+=t.status>=200&&t.status<300?" status-2xx":" status-4xx"),n.innerHTML=`\n <div style="display:flex; align-items:center; width:100%; gap:8px;">\n <span class="mfm-method ${o}">${o}</span>\n <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; justify-content:center;">\n <span class="mfm-url">${r}${t.url}</span>\n ${s}\n </div>\n <span class="${a}">${i}</span>\n <span class="mfm-menu-dots" title="Copy As...">⋮</span>\n </div>\n `;const d=n.querySelector(".mfm-menu-dots");d.onclick=e=>{e.stopPropagation();const n=d.getBoundingClientRect();this.sharedDropdown.style.top=n.bottom+5+"px",this.sharedDropdown.style.left=n.right-160+"px",this.sharedDropdown.classList.toggle("show"),this.sharedDropdown.onclick=e=>{e.stopPropagation();const n=e.target.getAttribute("data-format");if(!n)return;let o="";"curl"===n?o=this.generateCurl(t):"fetch"===n?o=this.generateFetch(t):"xhr"===n&&(o=this.generateXHR(t)),o&&navigator.clipboard.writeText(o).then((()=>{this.showSnackbar(`${n.toUpperCase()} copied!`),this.sharedDropdown.classList.remove("show")}))}},n.onclick=()=>{this.state.selectedRequest=t,this.updateUI()},e.appendChild(n)}))}}renderRequestDetail(e){const t=document.createElement("div");t.className="mfm-detail-view",t.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">Details</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-detail-actions">\n <button class="mfm-back-btn">← Back</button>\n </div>\n ';const n=document.createElement("div");n.className="mfm-tabs",["request","response"].forEach((e=>{const t=document.createElement("div");t.className="mfm-tab "+(this.state.activeRequestTab===e?"active":""),t.textContent=e.charAt(0).toUpperCase()+e.slice(1),t.onclick=()=>{this.state.activeRequestTab=e,this.updateUI()},n.appendChild(t)}));const o=document.createElement("div");o.style.overflowY="auto",o.style.flex="1";const s=(e.method||"").toUpperCase(),r=this.formatBody(e.requestBody),i=this.formatBody(e.responseBody);o.innerHTML="request"===this.state.activeRequestTab?`\n <div class="section-title">URL</div><div class="mfm-code-block">${s} ${e.url}</div>\n ${e.rule?`<div class="section-title">Matched Rule</div><div class="mfm-code-block">${e.rule.action} (value: ${e.rule.config?.value})</div>`:""}\n <div class="section-title">Headers</div><div class="mfm-code-block">${JSON.stringify(e.requestHeaders||{},null,2)}</div>\n <div class="section-title">Query Params</div><div class="mfm-code-block">${JSON.stringify(e.queryParams||{},null,2)}</div>\n <div class="section-header">\n <div class="section-title" style="margin: 0;">Payload</div>\n <div class="mfm-copy-btn" id="mfm-copy-payload">Copy</div>\n </div>\n <div class="mfm-code-block">${r}</div>\n `:`\n <div class="section-title">Status</div><div class="mfm-code-block">${e.status}</div>\n <div class="section-title">Response Headers</div><div class="mfm-code-block">${JSON.stringify(e.responseHeaders||{},null,2)}</div>\n <div class="section-header">\n <div class="section-title" style="margin: 0;">Response Body</div>\n <div class="mfm-copy-btn" id="mfm-copy-response">Copy</div>\n </div>\n <div class="mfm-code-block">${i}</div>\n `,t.appendChild(n),t.appendChild(o);const a=o.querySelector("#mfm-copy-payload");a&&(a.onclick=()=>{navigator.clipboard.writeText(r).then((()=>{this.showSnackbar("Payload copied!")}))});const d=o.querySelector("#mfm-copy-response");return d&&(d.onclick=()=>{navigator.clipboard.writeText(i).then((()=>{this.showSnackbar("Response copied!")}))}),t.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),t.querySelector(".mfm-back-btn").onclick=()=>{this.state.selectedRequest=null,this.updateUI()},t}formatBody(e){if(!e)return"";if("undefined"!=typeof FormData&&e instanceof FormData){const t={};return e.forEach(((e,n)=>{"undefined"!=typeof File&&e instanceof File?t[n]=`[File: ${e.name} (${e.size} bytes)]`:"undefined"!=typeof Blob&&e instanceof Blob?t[n]=`[Blob: ${e.type} (${e.size} bytes)]`:t[n]=e})),JSON.stringify(t,null,2)}if("undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams)return JSON.stringify(Object.fromEntries(e.entries()),null,2);if("undefined"!=typeof Blob&&e instanceof Blob)return`[${"undefined"!=typeof File&&e instanceof File?"File":"Blob"}: ${e.type||"binary"} (${e.size} bytes)]`;if(e instanceof ArrayBuffer||"undefined"!=typeof Uint8Array&&e instanceof Uint8Array)return`[Binary Data: ${e.byteLength||e.length} bytes]`;if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}generateCurl(e){const t=(e.method||"GET").toUpperCase();let n=`curl -X ${t} "${e.url}"`;if(e.requestHeaders&&Object.entries(e.requestHeaders).forEach((([e,t])=>{n+=` \\\n -H "${e}: ${t}"`})),e.requestBody&&"GET"!==t){const t=("string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody)).replace(/'/g,"'\\''");n+=` \\\n --data-raw '${t}'`}return n}generateFetch(e){const t=(e.method||"GET").toUpperCase();let n=`{\n method: "${t}",\n headers: ${(e.requestHeaders?JSON.stringify(e.requestHeaders,null,2):"{}").replace(/\n/g,"\n ")}`;if(e.requestBody&&"GET"!==t){const t="string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody,null,2);n+=`,\n body: ${"string"==typeof e.requestBody?"`"+t+"`":JSON.stringify(e.requestBody,null,2).replace(/\n/g,"\n ")}`}return n+="\n}",`fetch("${e.url}", ${n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error(error));`}generateXHR(e){const t=(e.method||"GET").toUpperCase();let n=`var xhr = new XMLHttpRequest();\nxhr.open("${t}", "${e.url}");\n`;if(e.requestHeaders&&Object.entries(e.requestHeaders).forEach((([e,t])=>{n+=`xhr.setRequestHeader("${e}", "${t}");\n`})),n+="\nxhr.onload = function() {\n console.log(xhr.responseText);\n};\n",e.requestBody&&"GET"!==t){const t="string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody);n+=`\nxhr.send(${"string"==typeof e.requestBody?"`"+t+"`":JSON.stringify(e.requestBody)});`}else n+="\nxhr.send();";return n}addRequest(e,t){const n=String(t.method||"").toUpperCase().trim(),o=String(t.url||"");console.log(`[MockForMe] addRequest type=${e} method=${n} url=${o} requestId=${t.requestId} pending=${t.pending}`);const s=o.includes("api.mockforme.com")||o.includes("mockforme.com/proxy")||o.includes("mockforme.com/gateway")||f&&o.includes(String(f));if("OPTIONS"===n||s)return;const r="mocked"===e?this.mockedRequests:this.otherRequests,i="mocked"===e?this.otherRequests:this.mockedRequests;if(t.requestId){const n=r.findIndex((e=>e.requestId===t.requestId));if(-1!==n)return r[n]={...r[n],...t},void(this.state.isOpen&&this.state.activeTab===e&&this.updateUI());const o=i.findIndex((e=>e.requestId===t.requestId));if(-1!==o){const e={...i[o],...t};return i.splice(o,1),r.unshift(e),r.length>50&&r.pop(),void(this.state.isOpen&&this.updateUI())}}r.unshift(t),r.length>50&&r.pop(),this.state.isOpen&&this.state.activeTab===e&&this.updateUI()}};function $(e,t){if("undefined"!=typeof window){if(window._mfm_intercepted)return;window._mfm_intercepted=!0}!function(e,t){const n=window.fetch;window.fetch=function(o,s={}){const r="string"==typeof o?o:o.url,i=String(s.method||(o instanceof Request?o.method:"GET")).toUpperCase().trim();if("OPTIONS"===i)return console.log(`[MockForMe] Filtered ${i} ${r} (isInternal=false)`),n(o,s);const a={url:r,method:i,requestHeaders:s.headers||(o instanceof Request?o.headers:{}),requestBody:s.body||(o instanceof Request?"{binary/stream}":null),queryParams:L(r)};try{const c=new URL(r,window.location.origin),{pathname:h,search:f}=c,g=R({url:r,method:i}),b=Math.random().toString(36).substring(7),y=Date.now();if(t!==l&&g){if(I.addRequest("mocked",{...a,requestId:b,pending:!0,startTime:y,rule:g}),"delay"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(o,s);t.then((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_redirect"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(g.config.redirectUrl,s);t.then((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_timeout"===g.action){const e=Number(g.config.value||0);if(0===e){const e=new AbortController,t=n(o,{...s,signal:e.signal});e.abort();const r=Date.now()-y;return I.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:r,aborted:!0}),t.catch((()=>Promise.reject(new Error("Fetch aborted by rules"))))}return new Promise(((t,n)=>{setTimeout((()=>{const e=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:e,aborted:!0}),n(new Error("Fetch timed out by rules"))}),1e3*e)}))}}const x=d(h,i),v=I.token||e;if(!x&&!g)return I.addRequest("other",{...a,requestId:b,pending:!0,startTime:y}),n(o,s).then((async e=>{const t=e.clone(),n=Date.now()-y;try{const o=await t.text();I.addRequest("other",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:o,pending:!1,duration:n})}catch(e){}return e})).catch((e=>{const t=Date.now()-y,n="AbortError"===e.name||e.message.includes("aborted"),o="TimeoutError"===e.name||e.message.includes("timed out"),s=!n&&!o;throw I.addRequest("other",{...a,requestId:b,status:n?"Aborted":o?"Timeout":"Error",responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n,timeout:o,error:s}),e}));let k=r;g||(k=`${_()}${x.url}${f}`);let w={};if(!g){w={[u]:v,[m]:p,"x-mfm-adaptor":t};const e=q();e&&x._ack&&(w["x-mfm-key"]=`${e}-${x._ack}`),x?._ri_&&(w._ri_=x._ri_)}const S=o instanceof Request?o.headers:s.headers,T=new Headers(S||{});Object.entries(w).forEach((([e,t])=>{T.set(e,t)}));let M=s.body;if(!M&&o instanceof Request){const e=o.clone();"GET"!==e.method&&"HEAD"!==e.method&&(M=e.text())}let $={method:i,headers:T,credentials:"include",mode:"cors"};g&&($.credentials=o instanceof Request&&o.credentials||s.credentials||"same-origin",$.mode=o instanceof Request&&o.mode||s.mode||"cors");const L=e=>(e&&($.body=e),t===l?(I.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:y}),(({actualUrl:e,url:t,method:n,headers:o,body:s,credentials:r,mode:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);return new Promise(((t,l)=>{window.addEventListener("message",(function e(n){if(n.source!==window)return;const o=n.data;if("INTERCEPTED_RESPONSE"===o?.type&&o?.requestId===d){if(window.removeEventListener("message",e),o?.response?.error)return l(new Error(o.response.message));const{status:n,headers:s,body:r}=o.response;t(new Response(r,{status:n,headers:s}))}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:o,body:s,credentials:r,mode:i,rule:a}},"*")}))})({actualUrl:r,url:k,method:i,headers:Object.fromEntries(T.entries()),body:e,credentials:$.credentials,mode:$.mode,rule:g}).then((e=>(e.clone().text().then((t=>{const n=Date.now()-y;I.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:t,pending:!1,duration:n})})),e))).catch((e=>{const t=Date.now()-y,n="AbortError"===e.name;throw I.addRequest("mocked",{...a,requestId:b,status:n?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n}),e}))):(I.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:y}),n(k,$).then((e=>{const t=e.clone(),n=t.headers.get("content-type")||"",o=Date.now()-y,s=n=>(I.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(t.headers.entries()),responseBody:n,pending:!1,duration:o}),new Response("string"==typeof n?n:JSON.stringify(n),{status:e.status,headers:t.headers}));return n.includes("application/json")?t.json().then(s):t.text().then(s)})).catch((e=>{console.warn("mockforme fetch interceptor error",e);const t=Date.now()-y,r="AbortError"===e.name;return I.addRequest("mocked",{...a,requestId:b,status:r?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:r}),n(o,s)}))));return M instanceof Promise?M.then((e=>L(e))):L(M)}catch(e){return console.warn("mockforme fetch interceptor error",e),n(o,s)}}}(e,t),function(e,t){const n=window.XMLHttpRequest;window.XMLHttpRequest=class{constructor(){return this._xhr=new n,this._headers={},this._method=null,this._url=null,this._body=null,this._isMocked=!1,this._captured=!1,this._requestId=null,this._startTime=null,this._capture=(e,t,n,o={})=>{if(this._captured)return;this._captured=!0;const s=this._isMocked?"mocked":"other",r=this._startTime?Date.now()-this._startTime:0;console.log(`[MockForMe] XHR Capture: status=${e} method=${this._method} url=${this._url} duration=${r}ms`);const i=e=>{const t=`on${e}`;(this[t]||this._xhr[t])?.(),this._xhr.dispatchEvent(new Event(e))};if(I.addRequest(s,{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,status:e,responseBody:t,responseHeaders:n,pending:!1,duration:r,...o}),"Aborted"!==e)try{Object.defineProperty(this,"responseText",{value:t,configurable:!0}),Object.defineProperty(this,"response",{value:t,configurable:!0}),Object.defineProperty(this,"status",{value:Number(e)||0,configurable:!0}),Object.defineProperty(this,"statusText",{value:200==e?"OK":"",configurable:!0})}catch(e){console.warn("[MockForMe] Error setting XHR props",e)}Object.defineProperty(this,"readyState",{value:4,configurable:!0}),i("readystatechange"),"Aborted"===e||o.aborted?i("abort"):0===e||"Error"===e||o.error?i("error"):"Timeout"===e||o.timeout?i("timeout"):i("load")},new Proxy(this,{get:(e,t)=>{if(t in e)return e[t];const n=e._xhr[t];return"function"==typeof n?n.bind(e._xhr):n},set:(e,t,n)=>t in e?(e[t]=n,!0):(e._xhr[t]=n,!0)})}open(e,t,n=!0,o=null,s=null){this._method=(e||"").toUpperCase().trim();try{this._url=new URL(t,window.location.href).toString()}catch(e){this._url=t}return console.log(`[MockForMe] XHR.open: ${this._method} ${this._url}`),this._xhr._mockOriginalUrl=this._url,this._xhr._mockMethod=this._method,this._xhr.open(e,t,n,o,s)}setRequestHeader(e,t){return this._headers[e]=t,this._xhr.setRequestHeader(e,t)}send(o){const s=String(this._method||"").toUpperCase().trim();if(console.log(`[MockForMe] XHR send method=${s} url=${this._url}`),"OPTIONS"===s)return console.log("[MockForMe] XHR Skip OPTIONS"),this._xhr.send(o);this._body=o,this._requestId=Math.random().toString(36).substring(7),this._startTime=Date.now(),this._xhr.addEventListener("abort",(()=>{this._capture("Aborted","Request aborted",{},{aborted:!0})}),{once:!0}),this._xhr.addEventListener("error",(()=>{this._capture(0,"Network Error",{},{error:!0})}),{once:!0}),this._xhr.addEventListener("timeout",(()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})}),{once:!0});try{const s=new URL(this._url),{pathname:r,search:i}=s,a=R({url:this._url,method:this._method});a&&(this._isMocked=!0,I.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime,rule:a}));const c=I.token||e;if(t!==l&&a){if("delay"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_redirect"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.open(this._method,a.config.redirectUrl,!0),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_timeout"===a.action){const e=Number(a.config.value||0);return 0===e?(this._capture("Aborted","Request aborted by rules",{},{aborted:!0}),void this._xhr.abort()):setTimeout((()=>{this._capture("Aborted","Request timed out by rules",{},{aborted:!0}),this._xhr.abort()}),1e3*e)}}const h=d(r,this._method);if(!h&&!a)return console.log("[MockForMe] XHR No Match:",{pathname:r,method:this._method}),this._isMocked=!1,I.addRequest("other",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(o);console.log("[MockForMe] XHR Matched:",{pathname:r,match:h}),this._isMocked=!0;let f=this._url;a||(f=`${_()}${h.url}${i}`);let g={};if(!a){g={[u]:c,[m]:p,"x-mfm-adaptor":t};const e=q();e&&h._ack&&(g["x-mfm-key"]=`${e}-${h._ack}`),h?._ri_&&(g._ri_=h._ri_)}const b={...this._headers,...g};if(I.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),t===l)(({actualUrl:e,url:t,method:n,headers:o,body:s,onSuccess:r,onError:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);window.addEventListener("message",(function e(t){if(t.source!==window)return;const n=t.data;if("INTERCEPTED_RESPONSE"===n.type&&n.requestId===d){if(window.removeEventListener("message",e),n.response?.error)return i?.(n.response.message);const{body:t,status:o,headers:s}=n.response;r?.({body:t,status:o,headers:s})}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:o,body:s,credentials:"same-origin",mode:"cors",rule:a}},"*")})({actualUrl:this._url,url:f,method:this._method,headers:b,body:this._body,rule:a,onSuccess:({body:e,status:t,headers:n})=>{this._capture(t,e,n||{})},onError:e=>{this._capture(0,e.message||"XHR Error",{},{error:!0})}});else{const e=new n;e.open(this._method,f,!0);for(const t in b)e.setRequestHeader(t,b[t]);e.withCredentials=this.withCredentials,e.onload=()=>{this._capture(e.status,e.responseText,e.getAllResponseHeaders())},e.onerror=()=>{this._capture(0,"Network Error",{},{error:!0})},e.ontimeout=()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})},e.send(this._body)}}catch(e){if(console.error("[MockForMe] XHR Interception Error",e),!this._xhr.readyState||1===this._xhr.readyState)return this._xhr.send(o)}}}}(e,t)}function L(e){try{const t=new URL(e,window.location.origin).searchParams;return Object.fromEntries(t.entries())}catch{return{}}}const O=(e,t=null,o=c)=>{if(n())return;if(o===c)try{e&&("undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),I.token=e,I.isAuthenticated=!0),!e&&I.token&&(e=I.token),I.onAuthSuccess=(e,t,n)=>{i(t),k(n),I.updateMappings(t,n)}}catch(e){console.log("Error in showing mockforme widget",e)}t||={mappings:[],_o:null,mk:null,_mck:null,_rules:[]};const{mappings:s=[],_o:r,mk:l,_mck:h,_rules:f=[]}=t;return i(s),k({_o:r,mk:l,_mck:h,_rules:f}),{run:(t,n=()=>{})=>{$(e,o),r&&l?(I.mappings=s,I.rules=f,t?.(s,{_o:r,mk:l,_mck:h,_rules:f})):e&&function(e,n,o,s){S({method:"get",url:g,async:!1,headers:{[u]:e,[m]:p,"x-mfm-adaptor":n}},(e=>{if(e)try{const{di:s,iv:r,_o:a,_mck:d,mk:c,r:l}=e,m=T({di:s,iv:r},a);k({_o:a,mk:c,_mck:d,_rules:l}),m&&(i(m),n=m,o={_o:a,mk:c,_mck:d,_rules:l},I.updateMappings(n,o),t?.(n,o))}catch(e){return void s(new Error("Unable to fetch mocked apis"))}var n,o}),(e=>{console.log("Error in loading mocked apis"),s(e)})).request()}(e,o,0,n)},checkIfApiToBeMocked:d,getMappings:a,doFetchMappings:(t,n)=>{M(e||I.token,o,((e,n)=>{I.updateMappings(e,n),t?.(e,n)}),n)}}},C=n();C||(globalThis.mockforme=O);let H=O;C&&(H=e=>({run:()=>{}}));const U=H;module.exports=t})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=()=>"undefined"!=typeof process&&null!=process.versions?.node,t=e=>{try{return e?e.toLowerCase():e}catch(e){return console.log(e),null}},n=(e,t)=>{const n=e.split("/"),s=t.split("/");if(n.length!==s.length)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t!==s[e]&&":any"!==t)return!1}return!0};let s=[];function o(e){s=e}function r(){return s}function i(e,o="get"){const r=e=>e.startsWith("/")?e:`/${e}`,i=r(e);for(const a of s)if(t(a.apiMethod)===t(o)){const t=r(a.apiEndpoint);if(t===i||n(t,i)){console.log(`[MockForMe] Match Found: ${o} ${e} -> ${a.apiEndpoint}`);const t={url:e,method:a.apiMethod,_ack:a._ack||null};return a._ri_&&(t._ri_=a._ri_),t}}return null}const a="JAVASCRIPT",d="CHROME_EXTENSION",c="mfmver",l="mockforme",m="4.2.8",u="https://api.mockforme.com",h=`${u}/mockforme`,p=`${u}/gateway/3f4eae522b`;let f=null,g=null,b=null,x=[];function y({_o:e,mk:t,_mck:n,_rules:s}){f=e,g=t,b=n,x=s}function k(){return x}function v(){return`${h}/${f}/${g}`}function _(){return b}const w=e=>{try{return new URL(e,window.location.origin).toString()}catch{return e}};function q({url:e,method:t}){try{const t=k();if(!Array.isArray(t)||0===t.length)return null;const n=globalThis.location?.origin||"http://localhost";for(const s of t){const t=s.rule?.URL;if(!t)continue;const{condition:o}=t;if(!o)continue;let r="";switch(t.type){case"URL":{let t;try{t=new URL(e,n),r=t.pathname}catch{r=e}break}case"QUERY":{let t;try{t=new URL(e,n)}catch{t=null}t&&(r=t.searchParams.get(o.key)||"");break}case"BODY":case"HEADERS":r="";break;default:continue}let i=!1;switch(o.operator){case"contains":i=r.includes(o.value);break;case"equal":case"equals":i=r===o.value;break;case"regexp":try{let e=o.value;if(e.startsWith("/")&&e.lastIndexOf("/")>0){const t=e.lastIndexOf("/"),n=e.slice(1,t),s=e.slice(t+1);i=new RegExp(n,s).test(r)}else i=new RegExp(e).test(r)}catch{i=!1}}if(i){const e=o.activeAction;return{action:e,config:o.actions?.[e]||{}}}}return null}catch(e){return console.error("[mockforme] checkIfRulesToBeApplied error",e),null}}const E=function(t,n,s){const o=e();var r;function i(){if(4==r.readyState)if(r.status.toString().match(/^20[0-9]$/)){var e=function(){var e=r.getResponseHeader("Content-Type"),t=r.responseText;if(e){var n=e.split(";");try{return"application/json"===n[0]?JSON.parse(t):t}catch(e){throw"Unable to convert response header"}}}();n.call(this,e,r)}else s.call(this,r.responseText,r)}function a(e,t){var n=[];for(var s in e)if(e.hasOwnProperty(s)){var o=t?t+"["+s+"]":s,r=e[s];n.push("object"==typeof r?a(r,o):encodeURIComponent(o)+"="+encodeURIComponent(r))}return n.join("&")}return o||(window.XMLHttpRequest?r=new XMLHttpRequest:window.ActiveXObject&&(r=new ActiveXObject("Microsoft.XMLHTTP"))),{request:function(){if(!o){if(window.XMLHttpRequest)r.onload=i;else{if(!window.ActiveXObject)throw"unable to process ajax";r.onreadystatechange=i}var e=a(t.params);if("get"==t.method.toLowerCase()&&"object"==typeof t.params){if(-1==t.url.indexOf("?"))t.url+="?";else{var n=t.url.split("?");n[1]&&n[1].split("=")[1]&&(t.url+="&")}t.url+=e}t.hasOwnProperty("async")||(t.async=!0),r.open(t.method,t.url,t.async),t.headers&&function(e){for(let t in e)r.setRequestHeader(t,e[t])}(t.headers),r.send(e)}}}};function R({di:e,iv:t},n){var s;const o=function(e,t){const n=t.length;return e.split("").map(((e,s)=>String.fromCharCode(e.charCodeAt(0)^t.charCodeAt(s%n)))).join("")}((s=e,"undefined"!=typeof Buffer?Buffer.from(s,"base64").toString("utf8"):decodeURIComponent(escape(atob(s)))),n+t);return JSON.parse(o)}function T(e,t,n,s){fetch(p,{method:"GET",headers:{[l]:e,[c]:m,"x-mfm-adaptor":t}}).then((e=>{if(!e.ok)throw e;return e.json()})).then((e=>{if(e){const{di:t,iv:s,_o:r,_mck:i,mk:a,r:d}=e,c=R({di:t,iv:s},r);y({_o:r,mk:a,_mck:i,_rules:d}),c&&(o(c),n(c,{_o:r,mk:a,_mck:i,_rules:d}))}})).catch((e=>{console.error("Error in loading mocked APIs:",e),s(e)}))}const S=new class{constructor(t={}){e()||(this.token=t.token||("undefined"!=typeof localStorage?localStorage.getItem("mockforme-token"):null),this.isAuthenticated=!!this.token,this.onAuthSuccess=t.onAuthSuccess||(()=>{}),this.mappings=t.mappings||[],this.rules=t.rules||[],this.mockedRequests=[],this.otherRequests=[],this.state={isOpen:!1,activeTab:"mappings",selectedRequest:null,activeRequestTab:"response",lastError:null,searchQuery:""},"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>this.init())):this.init()))}init(){document.getElementById("mockforme-root")||(this.createStyles(),this.render())}createStyles(){if(document.getElementById("mockforme-styles"))return;const e=document.createElement("style");e.id="mockforme-styles",e.innerHTML="\n #mockforme-root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n z-index: 999999;\n position: fixed;\n }\n .mfm-bubble {\n position: fixed;\n bottom: 20px;\n right: 20px;\n height: 42px;\n padding: 4px 8px;\n border-radius: 24px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n background: white;\n display: flex;\n align-items: center;\n gap: 8px;\n transition: transform 0.2s;\n border: 1px solid #eee;\n }\n .mfm-bubble:hover { transform: scale(1.05); }\n .mfm-bubble img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }\n \n .mfm-refresh-icon {\n width: 25px;\n height: 25px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #2563eb;\n font-size: 25px;\n transition: background 0.2s;\n margin-right: 10px;\n background: #f3f4f6;\n padding: 8px;\n }\n .mfm-refresh-icon:hover { }\n .mfm-refresh-icon.loading { animation: mfm-spin 1s linear infinite; }\n @keyframes mfm-spin { 100% { transform: rotate(360deg); } }\n\n .mfm-bottomsheet {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background: white;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.15);\n padding: 20px;\n z-index: 999999;\n height: 80vh;\n max-width: 500px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s ease-out;\n transform: translateY(100%);\n visibility: hidden;\n overflow-y: auto;\n }\n @media (max-width: 480px) {\n .mfm-bottomsheet { padding: 16px; }\n }\n .mfm-bottomsheet.open { transform: translateY(0); visibility: visible; }\n \n .mfm-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n }\n .mfm-title-group { display: flex; align-items: center; gap: 10px; }\n .mfm-title { font-size: 18px; font-weight: 600; color: #111; }\n .mfm-delete-token-btn { \n background: #fee2e2; \n color: #dc2626; \n font-size: 13px; \n padding: 4px 12px; \n border-radius: 20px; \n cursor: pointer; \n display: flex;\n align-items: center;\n gap: 6px;\n border: none;\n font-weight: 500;\n transition: background 0.2s;\n }\n .mfm-delete-token-btn:hover { background: #fecaca; }\n .mfm-header-actions { display: flex; align-items: center; gap: 10px; }\n .mfm-close { background: none; border: none; font-size: 24px; cursor: pointer; color: #666; }\n /* Login Form */\n .mfm-logo-container { text-align: center; padding: 20px 0; width: 100%; display: flex; justify-content: center; flex-shrink: 0; }\n .mfm-logo { width: 120px; height: auto; }\n .mfm-link { color: #2563eb; text-decoration: none; font-size: 14px; margin-top: 5px; cursor: pointer; display: inline-block; }\n .mfm-link:hover { text-decoration: underline; }\n .mfm-form { display: flex; flex-direction: column; gap: 10px; }\n .mfm-input {\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n .mfm-input:focus { border-color: #2563eb; }\n .mfm-error { color: #dc2626; font-size: 13px; margin-top: -5px; }\n .mfm-btn {\n padding: 12px;\n background: #2563eb;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n .mfm-btn:hover { background: #1d4ed8; }\n .mfm-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n .mfm-tabs { display: flex; border-bottom: 1px solid #eee; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-tab {\n padding: 8px 16px;\n cursor: pointer;\n color: #666;\n border-bottom: 2px solid transparent;\n font-weight: 500;\n font-size: 14px;\n }\n .mfm-tab.active { color: #2563eb; border-bottom-color: #2563eb; }\n \n .mfm-list {\n flex: 1;\n overflow-y: auto;\n min-height: 200px;\n max-height: 60vh;\n }\n .mfm-list-item {\n padding: 10px;\n border-bottom: 1px solid #f3f4f6;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n min-height: 52px;\n box-sizing: border-box;\n }\n .mfm-list-item:hover { background: #f9fafb; }\n .mfm-method { \n font-weight: bold; \n padding: 4px 8px; \n border-radius: 4px; \n margin-right: 10px;\n font-size: 12px;\n }\n .GET { background: #dbfcfe; color: #006064; }\n .POST { background: #dcfce7; color: #14532d; }\n .PUT { background: #fef9c3; color: #713f12; }\n .DELETE { background: #fee2e2; color: #7f1d1d; }\n .PATCH { background: #f3e8ff; color: #581c87; }\n .RULE { background: #f3f4f6; color: #374151; }\n\n /* Search & Actions */\n .mfm-actions-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-search-input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; }\n .mfm-search-input:focus { border-color: #2563eb; }\n .mfm-icon-btn { background: none; border: none; font-size: 25px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 4px; }\n .mfm-icon-btn:hover { color: #333; }\n .mfm-icon-btn.refresh { color: #2563eb; padding: 0; height: 20px; width:20px; border-radius: 50%; }\n .mfm-icon-btn.refresh.loading { animation: mfm-spin 1s linear infinite; }\n .mfm-icon-btn.delete { color: #dc2626; }\n\n @media (max-height: 700px) {\n .mfm-bottomsheet { height: 85vh; padding: 12px; }\n .mfm-logo-container { padding: 10px 0; }\n .mfm-logo { width: 100px; }\n .mfm-header { margin-bottom: 10px; }\n .mfm-form { gap: 8px; }\n .mfm-input { padding: 10px; font-size: 14px; }\n .mfm-btn { padding: 10px; font-size: 14px; }\n }\n\n .mfm-url { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .mfm-status { font-weight: bold; margin-left: 10px; }\n .status-2xx { color: #16a34a; }\n .status-4xx { color: #dc2626; }\n .status-5xx { color: #ea580c; }\n .status-aborted, .status-timeout, .status-error { color: #dc2626; font-size: 11px; font-weight: 500; }\n\n .mfm-rule-tag {\n font-size: 9px;\n padding: 2px 4px;\n border-radius: 4px;\n background: #fef3c7;\n color: #92400e;\n border: 1px solid #fde68a;\n margin-right: 6px;\n text-transform: uppercase;\n font-weight: bold;\n }\n\n .mfm-detail-view { display: flex; flex-direction: column; height: 100%; }\n .mfm-detail-actions { margin-bottom: 10px; }\n .mfm-back-btn { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 0;}\n \n .mfm-code-block {\n background: #f8fafc;\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n font-family: monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-break: break-all;\n max-height: 300px;\n border: 1px solid #e2e8f0;\n }\n .section-title { font-size: 14px; font-weight: 600; margin: 10px 0 5px; color: #333; }\n\n /* Snackbar */\n .mfm-snackbar {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: #333;\n color: white;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n z-index: 1000000;\n opacity: 0;\n transition: opacity 0.3s, transform 0.3s;\n pointer-events: none;\n }\n .mfm-snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n /* Confirm Dialog */\n .mfm-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000001;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n .mfm-modal-overlay.show { opacity: 1; pointer-events: auto; }\n .mfm-modal {\n background: white;\n padding: 20px;\n border-radius: 12px;\n width: 300px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n transform: scale(0.9);\n transition: transform 0.2s;\n }\n .mfm-modal-overlay.show .mfm-modal { transform: scale(1); }\n .mfm-modal-title { font-size: 16px; font-weight: 600; margin-bottom: 10px; }\n .mfm-modal-body { font-size: 14px; color: #666; margin-bottom: 20px; }\n .mfm-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }\n .mfm-btn-alt { background: #f3f4f6; color: #333; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n .mfm-btn-danger { background: #dc2626; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n \n .mfm-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2563eb;\n border-radius: 50%;\n animation: mfm-spin 1s linear infinite;\n margin-right: 8px;\n display: inline-block;\n }\n .status-aborted { color: #dc2626; font-weight: bold; }\n .mfm-duration { color: #666; font-size: 11px; margin-left: 8px; font-weight: normal; }\n ",document.head.appendChild(e)}render(){const e=document.createElement("div");e.id="mockforme-root",document.body.appendChild(e),this.root=e,this.bubble=this.renderBubble(),this.root.appendChild(this.bubble),this.sheet=document.createElement("div"),this.sheet.className="mfm-bottomsheet",this.root.appendChild(this.sheet),this.snackbar=document.createElement("div"),this.snackbar.className="mfm-snackbar",this.root.appendChild(this.snackbar),this.modalOverlay=document.createElement("div"),this.modalOverlay.className="mfm-modal-overlay",this.root.appendChild(this.modalOverlay),this.updateUI()}updateUI(){this.state.isOpen?(this.renderBottomSheetContent(),this.sheet.classList.add("open")):this.sheet.classList.remove("open")}renderBubble(){const e=document.createElement("div");e.className="mfm-bubble";const t="undefined"!=typeof localStorage?JSON.parse(localStorage.getItem("mockforme-dot-pos")||"{}"):{};return t.bottom&&(e.style.bottom=t.bottom+"px"),t.right&&(e.style.right=t.right+"px"),e.innerHTML='\n <div class="mfm-refresh-icon" title="Reload Mappings">↻</div>\n <img src="https://ik.imagekit.io/mfm/static-collection/mfm-48x48.png" alt="MFM" />\n ',this.addDragLogic(e),e.querySelector("img").onclick=e=>{this.isDragging||this.toggleSheet()},e.querySelector(".mfm-refresh-icon").onclick=e=>{e.stopPropagation(),this.refreshMappings()},e}addDragLogic(e){let t,n,s,o,r=!1;const i=(i,a)=>{r=!1,this.isDragging=!1,t=i,n=a;const d=e.getBoundingClientRect();s=window.innerWidth-d.right,o=window.innerHeight-d.bottom},a=(i,a)=>{const d=t-i,c=n-a;(Math.abs(d)>3||Math.abs(c)>3)&&(r=!0,this.isDragging=!0),r&&(e.style.right=s+d+"px",e.style.bottom=o+c+"px")},d=()=>{r&&"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-dot-pos",JSON.stringify({bottom:parseInt(e.style.bottom),right:parseInt(e.style.right)}))},c=e=>{a(e.clientX,e.clientY)},l=()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",l),d()},m=e=>{const t=e.touches[0];a(t.clientX,t.clientY),r&&e.cancelable&&e.preventDefault()},u=()=>{document.removeEventListener("touchmove",m),document.removeEventListener("touchend",u),d()};e.addEventListener("mousedown",(e=>{i(e.clientX,e.clientY),document.addEventListener("mousemove",c),document.addEventListener("mouseup",l)})),e.addEventListener("touchstart",(e=>{const t=e.touches[0];i(t.clientX,t.clientY),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",u)}),{passive:!0})}toggleSheet(){this.state.isOpen=!this.state.isOpen,this.updateUI()}showSnackbar(e){this.snackbar.textContent=e,this.snackbar.classList.add("show"),setTimeout((()=>this.snackbar.classList.remove("show")),3e3)}showConfirm(e,t,n){this.modalOverlay.innerHTML=`\n <div class="mfm-modal">\n <div class="mfm-modal-title">${e}</div>\n <div class="mfm-modal-body">${t}</div>\n <div class="mfm-modal-actions">\n <button class="mfm-btn-alt" id="mfm-confirm-cancel">Cancel</button>\n <button class="mfm-btn-danger" id="mfm-confirm-yes">Delete</button>\n </div>\n </div>\n `,this.modalOverlay.classList.add("show"),this.modalOverlay.querySelector("#mfm-confirm-cancel").onclick=()=>{this.modalOverlay.classList.remove("show")},this.modalOverlay.querySelector("#mfm-confirm-yes").onclick=()=>{this.modalOverlay.classList.remove("show"),n()}}refreshMappings(){if(!this.token)return void this.toggleSheet();const e=document.querySelectorAll(".mfm-refresh-icon, #mfm-refresh-mappings-btn");e.forEach((e=>e.classList.add("loading"))),T(this.token,"JAVASCRIPT",((t,n)=>{console.log("[MockForMe] Mappings reloaded",{mappings:t?.length,rules:n?._rules?.length}),e.forEach((e=>e.classList.remove("loading"))),this.mappings=t||[],this.rules=n?._rules||[],this.onAuthSuccess(this.token,this.mappings,n),this.updateUI(),this.showSnackbar("Mappings reloaded!")}),(t=>{e.forEach((e=>e.classList.remove("loading"))),this.showSnackbar("Failed to reload mappings")}))}renderBottomSheetContent(){this.sheet.innerHTML="",this.isAuthenticated?this.state.selectedRequest?this.sheet.appendChild(this.renderRequestDetail(this.state.selectedRequest)):this.sheet.appendChild(this.renderDashboard()):this.sheet.appendChild(this.renderTokenForm())}renderTokenForm(){const e=document.createElement("div");e.innerHTML=`\n <div class="mfm-header">\n <span class="mfm-title">Save Token</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-logo-container">\n <img src="https://dashboard.mockforme.com/public/assets/images/logo.png" class="mfm-logo" alt="MockForMe Logo" />\n </div>\n <div class="mfm-form">\n <input type="text" class="mfm-input" placeholder="Enter MockForMe Access Token" id="mfm-token-input">\n <div class="mfm-error" id="mfm-error-msg" style="display: ${this.state.lastError?"block":"none"}">${this.state.lastError||""}</div>\n <a href="https://dashboard.mockforme.com/user/token" target="_blank" class="mfm-link">Get Access Token?</a>\n <button class="mfm-btn" id="mfm-save-token-btn">Save</button>\n </div>\n `,e.querySelector(".mfm-close").onclick=()=>{this.state.lastError=null,this.toggleSheet()};const t=e.querySelector("#mfm-save-token-btn"),n=e.querySelector("#mfm-token-input");return t.onclick=()=>{const s=n.value.trim();s&&(t.textContent="Validating...",t.disabled=!0,this.state.lastError=null,e.querySelector("#mfm-error-msg").style.display="none",T(s,a,((e,t)=>{"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-token",s),this.token=s,this.isAuthenticated=!0,this.mappings=e,this.rules=t?._rules||[],this.onAuthSuccess(s,e,t),this.updateUI()}),(e=>{t.textContent="Save",t.disabled=!1;let n="Invalid Token or Network Error";e&&"function"==typeof e.json?e.json().then((e=>{this.state.lastError=e.message||n,this.updateUI()})).catch((()=>{this.state.lastError=n,this.updateUI()})):(this.state.lastError=e.message||n,this.updateUI())})))},e}deleteToken(){"undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),this.token=null,this.isAuthenticated=!1,this.mappings=[],this.rules=[],this.state.lastError=null,this.updateUI()}renderDashboard(){const e=document.createElement("div");e.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">MockForMe</span>\n <div class="mfm-header-actions">\n <button class="mfm-delete-token-btn" id="mfm-delete-token" title="Delete Token">\n <span>🗑</span>\n <span>Token</span>\n </button>\n <button class="mfm-close">×</button>\n </div>\n </div>\n ';const t=document.createElement("div");t.className="mfm-tabs",["mappings","mocked","other"].forEach((e=>{const n=document.createElement("div");n.className="mfm-tab "+(this.state.activeTab===e?"active":""),n.textContent="mappings"===e?"Api Mappings":"mocked"===e?"Mocked Apis":"Other Apis",n.onclick=()=>{this.state.activeTab=e,this.updateUI()},t.appendChild(n)})),e.querySelector(".mfm-header").after(t);const n=document.createElement("div");n.className="mfm-actions-bar","mappings"===this.state.activeTab?(n.innerHTML=`\n <div style="flex: 1; font-size: 13px; color: #666;">Total: ${this.mappings.length+this.rules.length}</div>\n <div class="mfm-icon-btn refresh" id="mfm-refresh-mappings-btn" title="Refresh Mappings">↻</div>\n `,n.querySelector("#mfm-refresh-mappings-btn").onclick=()=>this.refreshMappings()):(n.innerHTML=`\n <input type="text" class="mfm-search-input" placeholder="Search requests..." id="mfm-search-input" value="${this.state.searchQuery}">\n <button class="mfm-icon-btn delete" id="mfm-clear-requests-btn" title="Clear List">🚫</button>\n `,n.querySelector("#mfm-search-input").oninput=e=>{this.state.searchQuery=e.target.value,this.updateRequestList(s)},n.querySelector("#mfm-clear-requests-btn").onclick=()=>{"mocked"===this.state.activeTab?this.mockedRequests=[]:this.otherRequests=[],this.updateUI()}),t.after(n);const s=document.createElement("div");return s.className="mfm-list",this.updateRequestList(s),e.appendChild(s),e.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),e.querySelector("#mfm-delete-token").onclick=()=>{this.showConfirm("Delete Token","Are you sure you want to Delete Token? This will clear your saved token.",(()=>{this.deleteToken()}))},e}updateRequestList(e){if(e.innerHTML="","mappings"===this.state.activeTab){const t=[...this.mappings.map((e=>({...e,type:"mapping"}))),...this.rules.map((e=>({...e,type:"rule"})))];0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");if(n.className="mfm-list-item","mapping"===t.type){const e=(t.apiMethod||"GET").toUpperCase();n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method ${e}">${e}</span><span class="mfm-url">${t.apiEndpoint}</span></div>`}else{const e=t.ruleName||"Network Rule",s=(t.rule?.URL?.condition||{}).value||e;n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method RULE">RULE</span><span class="mfm-url">${s}</span></div>`}e.appendChild(n)}))}else{let t="mocked"===this.state.activeTab?this.mockedRequests:this.otherRequests;if(this.state.searchQuery){const e=this.state.searchQuery.toLowerCase();t=t.filter((t=>(t.url||"").toLowerCase().includes(e)||(t.method||"").toLowerCase().includes(e)||String(t.status).includes(e)))}0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");n.className="mfm-list-item";const s=(t.method||"").toUpperCase(),o=`<span class="mfm-duration" style="color:#2563eb; font-size:11px; font-weight:500; display:block; height:14px; line-height:14px;">${void 0!==t.duration?t.duration+"ms":""}</span>`,r=(()=>{if(!t.rule)return"";let e="delay_and_timeout"===t.rule.action?"Delay and Timout":t.rule.action.split("_")[0];const n=t.rule.config?.value;return void 0!==n&&(t.rule.action.includes("delay")||t.rule.action.includes("timeout"))&&(e+=` (${n}s)`),`<span class="mfm-rule-tag" title="${t.rule.action}">${e}</span>`})();let i=t.status,a="mfm-status";t.pending?i='<span class="mfm-spinner"></span>':t.aborted?(i="Aborted",a+=" status-aborted"):t.timeout?(i="Timeout",a+=" status-timeout"):t.error?(i="Error/CORS",a+=" status-error"):(i=t.status,a+=t.status>=200&&t.status<300?" status-2xx":" status-4xx"),n.innerHTML=`\n <div style="display:flex; align-items:center; width:100%; gap:8px;">\n <span class="mfm-method ${s}">${s}</span>\n <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; justify-content:center;">\n <span class="mfm-url">${r}${t.url}</span>\n ${o}\n </div>\n <span class="${a}">${i}</span>\n </div>\n `,n.onclick=()=>{this.state.selectedRequest=t,this.updateUI()},e.appendChild(n)}))}}renderRequestDetail(e){const t=document.createElement("div");t.className="mfm-detail-view",t.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">Details</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-detail-actions">\n <button class="mfm-back-btn">← Back</button>\n </div>\n ';const n=document.createElement("div");n.className="mfm-tabs",["request","response"].forEach((e=>{const t=document.createElement("div");t.className="mfm-tab "+(this.state.activeRequestTab===e?"active":""),t.textContent=e.charAt(0).toUpperCase()+e.slice(1),t.onclick=()=>{this.state.activeRequestTab=e,this.updateUI()},n.appendChild(t)}));const s=document.createElement("div");s.style.overflowY="auto",s.style.flex="1";const o=(e.method||"").toUpperCase();return s.innerHTML="request"===this.state.activeRequestTab?`\n <div class="section-title">URL</div><div class="mfm-code-block">${o} ${e.url}</div>\n ${e.rule?`<div class="section-title">Matched Rule</div><div class="mfm-code-block">${e.rule.action} (value: ${e.rule.config?.value})</div>`:""}\n <div class="section-title">Headers</div><div class="mfm-code-block">${JSON.stringify(e.requestHeaders||{},null,2)}</div>\n <div class="section-title">Query Params</div><div class="mfm-code-block">${JSON.stringify(e.queryParams||{},null,2)}</div>\n <div class="section-title">Payload</div><div class="mfm-code-block">${this.formatBody(e.requestBody)}</div>\n `:`\n <div class="section-title">Status</div><div class="mfm-code-block">${e.status}</div>\n <div class="section-title">Response Headers</div><div class="mfm-code-block">${JSON.stringify(e.responseHeaders||{},null,2)}</div>\n <div class="section-title">Response Body</div><div class="mfm-code-block">${this.formatBody(e.responseBody)}</div>\n `,t.appendChild(n),t.appendChild(s),t.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),t.querySelector(".mfm-back-btn").onclick=()=>{this.state.selectedRequest=null,this.updateUI()},t}formatBody(e){if(!e)return"";if("undefined"!=typeof FormData&&e instanceof FormData){const t={};return e.forEach(((e,n)=>{"undefined"!=typeof File&&e instanceof File?t[n]=`[File: ${e.name} (${e.size} bytes)]`:"undefined"!=typeof Blob&&e instanceof Blob?t[n]=`[Blob: ${e.type} (${e.size} bytes)]`:t[n]=e})),JSON.stringify(t,null,2)}if("undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams)return JSON.stringify(Object.fromEntries(e.entries()),null,2);if("undefined"!=typeof Blob&&e instanceof Blob)return`[${"undefined"!=typeof File&&e instanceof File?"File":"Blob"}: ${e.type||"binary"} (${e.size} bytes)]`;if(e instanceof ArrayBuffer||"undefined"!=typeof Uint8Array&&e instanceof Uint8Array)return`[Binary Data: ${e.byteLength||e.length} bytes]`;if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}addRequest(e,t){const n=String(t.method||"").toUpperCase().trim(),s=String(t.url||"");console.log(`[MockForMe] addRequest type=${e} method=${n} url=${s} requestId=${t.requestId} pending=${t.pending}`);const o=s.includes("api.mockforme.com")||s.includes("mockforme.com/proxy")||s.includes("mockforme.com/gateway")||h&&s.includes(String(h));if("OPTIONS"===n||o)return;const r="mocked"===e?this.mockedRequests:this.otherRequests,i="mocked"===e?this.otherRequests:this.mockedRequests;if(t.requestId){const n=r.findIndex((e=>e.requestId===t.requestId));if(-1!==n)return r[n]={...r[n],...t},void(this.state.isOpen&&this.state.activeTab===e&&this.updateUI());const s=i.findIndex((e=>e.requestId===t.requestId));if(-1!==s){const e={...i[s],...t};return i.splice(s,1),r.unshift(e),r.length>50&&r.pop(),void(this.state.isOpen&&this.updateUI())}}r.unshift(t),r.length>50&&r.pop(),this.state.isOpen&&this.state.activeTab===e&&this.updateUI()}};function M(e,t){if("undefined"!=typeof window){if(window._mfm_intercepted)return;window._mfm_intercepted=!0}!function(e,t){const n=window.fetch;window.fetch=function(s,o={}){const r="string"==typeof s?s:s.url,a=String(o.method||(s instanceof Request?s.method:"GET")).toUpperCase().trim();if("OPTIONS"===a)return console.log(`[MockForMe] Filtered ${a} ${r} (isInternal=false)`),n(s,o);const u={url:r,method:a,requestHeaders:o.headers||(s instanceof Request?s.headers:{}),requestBody:o.body||(s instanceof Request?"{binary/stream}":null),queryParams:I(r)};try{const h=new URL(r,window.location.origin),{pathname:p,search:f}=h,g=q({url:r,method:a}),b=Math.random().toString(36).substring(7),x=Date.now();if(t!==d&&g){if(S.addRequest("mocked",{...u,requestId:b,pending:!0,startTime:x,rule:g}),"delay"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(s,o);t.then((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_redirect"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(g.config.redirectUrl,o);t.then((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_timeout"===g.action){const e=Number(g.config.value||0);if(0===e){const e=new AbortController,t=n(s,{...o,signal:e.signal});e.abort();const r=Date.now()-x;return S.addRequest("mocked",{...u,requestId:b,status:"Aborted",pending:!1,duration:r,aborted:!0}),t.catch((()=>Promise.reject(new Error("Fetch aborted by rules"))))}return new Promise(((t,n)=>{setTimeout((()=>{const e=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:"Aborted",pending:!1,duration:e,aborted:!0}),n(new Error("Fetch timed out by rules"))}),1e3*e)}))}}const y=i(p,a),k=S.token||e;if(!y&&!g)return S.addRequest("other",{...u,requestId:b,pending:!0,startTime:x}),n(s,o).then((async e=>{const t=e.clone(),n=Date.now()-x;try{const s=await t.text();S.addRequest("other",{...u,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:s,pending:!1,duration:n})}catch(e){}return e})).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name||e.message.includes("aborted"),s="TimeoutError"===e.name||e.message.includes("timed out"),o=!n&&!s;throw S.addRequest("other",{...u,requestId:b,status:n?"Aborted":s?"Timeout":"Error",responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n,timeout:s,error:o}),e}));let E=r;g||(E=`${v()}${y.url}${f}`);let R={};if(!g){R={[l]:k,[c]:m,"x-mfm-adaptor":t};const e=_();e&&y._ack&&(R["x-mfm-key"]=`${e}-${y._ack}`),y?._ri_&&(R._ri_=y._ri_)}const T=s instanceof Request?s.headers:o.headers,M=new Headers(T||{});Object.entries(R).forEach((([e,t])=>{M.set(e,t)}));let I=o.body;if(!I&&s instanceof Request){const e=s.clone();"GET"!==e.method&&"HEAD"!==e.method&&(I=e.text())}let L={method:a,headers:M,credentials:"include",mode:"cors"};g&&(L.credentials=s instanceof Request&&s.credentials||o.credentials||"same-origin",L.mode=s instanceof Request&&s.mode||o.mode||"cors");const $=e=>(e&&(L.body=e),t===d?(S.addRequest("mocked",{...u,requestBody:e||u.requestBody,requestId:b,pending:!0,startTime:x}),(({actualUrl:e,url:t,method:n,headers:s,body:o,credentials:r,mode:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=w(t);return new Promise(((t,l)=>{window.addEventListener("message",(function e(n){if(n.source!==window)return;const s=n.data;if("INTERCEPTED_RESPONSE"===s?.type&&s?.requestId===d){if(window.removeEventListener("message",e),s?.response?.error)return l(new Error(s.response.message));const{status:n,headers:o,body:r}=s.response;t(new Response(r,{status:n,headers:o}))}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:s,body:o,credentials:r,mode:i,rule:a}},"*")}))})({actualUrl:r,url:E,method:a,headers:Object.fromEntries(M.entries()),body:e,credentials:L.credentials,mode:L.mode,rule:g}).then((e=>(e.clone().text().then((t=>{const n=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:t,pending:!1,duration:n})})),e))).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name;throw S.addRequest("mocked",{...u,requestId:b,status:n?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n}),e}))):(S.addRequest("mocked",{...u,requestBody:e||u.requestBody,requestId:b,pending:!0,startTime:x}),n(E,L).then((e=>{const t=e.clone(),n=t.headers.get("content-type")||"",s=Date.now()-x,o=n=>(S.addRequest("mocked",{...u,requestId:b,status:e.status,responseHeaders:Object.fromEntries(t.headers.entries()),responseBody:n,pending:!1,duration:s}),new Response("string"==typeof n?n:JSON.stringify(n),{status:e.status,headers:t.headers}));return n.includes("application/json")?t.json().then(o):t.text().then(o)})).catch((e=>{console.warn("mockforme fetch interceptor error",e);const t=Date.now()-x,r="AbortError"===e.name;return S.addRequest("mocked",{...u,requestId:b,status:r?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:r}),n(s,o)}))));return I instanceof Promise?I.then((e=>$(e))):$(I)}catch(e){return console.warn("mockforme fetch interceptor error",e),n(s,o)}}}(e,t),function(e,t){const n=window.XMLHttpRequest;window.XMLHttpRequest=class{constructor(){return this._xhr=new n,this._headers={},this._method=null,this._url=null,this._body=null,this._isMocked=!1,this._captured=!1,this._requestId=null,this._startTime=null,this._capture=(e,t,n,s={})=>{if(this._captured)return;this._captured=!0;const o=this._isMocked?"mocked":"other",r=this._startTime?Date.now()-this._startTime:0;console.log(`[MockForMe] XHR Capture: status=${e} method=${this._method} url=${this._url} duration=${r}ms`);const i=e=>{const t=`on${e}`;(this[t]||this._xhr[t])?.(),this._xhr.dispatchEvent(new Event(e))};if(S.addRequest(o,{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:I(this._url),requestId:this._requestId,status:e,responseBody:t,responseHeaders:n,pending:!1,duration:r,...s}),"Aborted"!==e)try{Object.defineProperty(this,"responseText",{value:t,configurable:!0}),Object.defineProperty(this,"response",{value:t,configurable:!0}),Object.defineProperty(this,"status",{value:Number(e)||0,configurable:!0}),Object.defineProperty(this,"statusText",{value:200==e?"OK":"",configurable:!0})}catch(e){console.warn("[MockForMe] Error setting XHR props",e)}Object.defineProperty(this,"readyState",{value:4,configurable:!0}),i("readystatechange"),"Aborted"===e||s.aborted?i("abort"):0===e||"Error"===e||s.error?i("error"):"Timeout"===e||s.timeout?i("timeout"):i("load")},new Proxy(this,{get:(e,t)=>{if(t in e)return e[t];const n=e._xhr[t];return"function"==typeof n?n.bind(e._xhr):n},set:(e,t,n)=>t in e?(e[t]=n,!0):(e._xhr[t]=n,!0)})}open(e,t,n=!0,s=null,o=null){this._method=(e||"").toUpperCase().trim();try{this._url=new URL(t,window.location.href).toString()}catch(e){this._url=t}return console.log(`[MockForMe] XHR.open: ${this._method} ${this._url}`),this._xhr._mockOriginalUrl=this._url,this._xhr._mockMethod=this._method,this._xhr.open(e,t,n,s,o)}setRequestHeader(e,t){return this._headers[e]=t,this._xhr.setRequestHeader(e,t)}send(s){const o=String(this._method||"").toUpperCase().trim();if(console.log(`[MockForMe] XHR send method=${o} url=${this._url}`),"OPTIONS"===o)return console.log("[MockForMe] XHR Skip OPTIONS"),this._xhr.send(s);this._body=s,this._requestId=Math.random().toString(36).substring(7),this._startTime=Date.now(),this._xhr.addEventListener("abort",(()=>{this._capture("Aborted","Request aborted",{},{aborted:!0})}),{once:!0}),this._xhr.addEventListener("error",(()=>{this._capture(0,"Network Error",{},{error:!0})}),{once:!0}),this._xhr.addEventListener("timeout",(()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})}),{once:!0});try{const o=new URL(this._url),{pathname:r,search:a}=o,u=q({url:this._url,method:this._method});u&&(this._isMocked=!0,S.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:I(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime,rule:u}));const h=S.token||e;if(t!==d&&u){if("delay"===u.action){const e=Number(u.config.value||0);return setTimeout((()=>{this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_redirect"===u.action){const e=Number(u.config.value||0);return setTimeout((()=>{this._xhr.open(this._method,u.config.redirectUrl,!0),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_timeout"===u.action){const e=Number(u.config.value||0);return 0===e?(this._capture("Aborted","Request aborted by rules",{},{aborted:!0}),void this._xhr.abort()):setTimeout((()=>{this._capture("Aborted","Request timed out by rules",{},{aborted:!0}),this._xhr.abort()}),1e3*e)}}const p=i(r,this._method);if(!p&&!u)return console.log("[MockForMe] XHR No Match:",{pathname:r,method:this._method}),this._isMocked=!1,S.addRequest("other",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:I(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(s);console.log("[MockForMe] XHR Matched:",{pathname:r,match:p}),this._isMocked=!0;let f=this._url;u||(f=`${v()}${p.url}${a}`);let g={};if(!u){g={[l]:h,[c]:m,"x-mfm-adaptor":t};const e=_();e&&p._ack&&(g["x-mfm-key"]=`${e}-${p._ack}`),p?._ri_&&(g._ri_=p._ri_)}const b={...this._headers,...g};if(S.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:I(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),t===d)(({actualUrl:e,url:t,method:n,headers:s,body:o,onSuccess:r,onError:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=w(t);window.addEventListener("message",(function e(t){if(t.source!==window)return;const n=t.data;if("INTERCEPTED_RESPONSE"===n.type&&n.requestId===d){if(window.removeEventListener("message",e),n.response?.error)return i?.(n.response.message);const{body:t,status:s,headers:o}=n.response;r?.({body:t,status:s,headers:o})}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:s,body:o,credentials:"same-origin",mode:"cors",rule:a}},"*")})({actualUrl:this._url,url:f,method:this._method,headers:b,body:this._body,rule:u,onSuccess:({body:e,status:t,headers:n})=>{this._capture(t,e,n||{})},onError:e=>{this._capture(0,e.message||"XHR Error",{},{error:!0})}});else{const e=new n;e.open(this._method,f,!0);for(const t in b)e.setRequestHeader(t,b[t]);e.withCredentials=this.withCredentials,e.onload=()=>{this._capture(e.status,e.responseText,e.getAllResponseHeaders())},e.onerror=()=>{this._capture(0,"Network Error",{},{error:!0})},e.ontimeout=()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})},e.send(this._body)}}catch(e){if(console.error("[MockForMe] XHR Interception Error",e),!this._xhr.readyState||1===this._xhr.readyState)return this._xhr.send(s)}}}}(e,t)}function I(e){try{const t=new URL(e,window.location.origin).searchParams;return Object.fromEntries(t.entries())}catch{return{}}}const L=(t,n=null,s=a)=>{if(e())return;if(s===a)try{t&&("undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),S.token=t,S.isAuthenticated=!0),!t&&S.token&&(t=S.token),S.onAuthSuccess=(e,t,n)=>{o(t),y(n),S.mappings=t,S.rules=n?._rules||[]}}catch(e){console.log("Error in showing mockforme widget",e)}n||={mappings:[],_o:null,mk:null,_mck:null,_rules:[]};const{mappings:d=[],_o:u,mk:h,_mck:f,_rules:g=[]}=n;return o(d),y({_o:u,mk:h,_mck:f,_rules:g}),{run:(e,n=()=>{})=>{M(t,s),u&&h?(S.mappings=d,S.rules=g,e?.(d,{_o:u,mk:h,_mck:f,_rules:g})):t&&function(t,n,s,r){E({method:"get",url:p,async:!1,headers:{[l]:t,[c]:m,"x-mfm-adaptor":n}},(t=>{if(t)try{const{di:r,iv:i,_o:a,_mck:d,mk:c,r:l}=t,m=R({di:r,iv:i},a);y({_o:a,mk:c,_mck:d,_rules:l}),m&&(o(m),n=m,s={_o:a,mk:c,_mck:d,_rules:l},S.mappings=n,S.rules=s?._rules||[],e?.(n,s))}catch(e){return void r(new Error("Unable to fetch mocked apis"))}var n,s}),(e=>{console.log("Error in loading mocked apis"),r(e)})).request()}(t,s,0,n)},checkIfApiToBeMocked:i,getMappings:r,doFetchMappings:(e,n)=>{T(t||S.token,s,((t,n)=>{S.mappings=t,S.rules=n?._rules||[],e?.(t,n)}),n)}}},$=e();$||(globalThis.mockforme=L);let O=L;$&&(O=e=>({run:()=>{}}));const C=O;export{C as mockforme};
|
|
1
|
+
const e=()=>"undefined"!=typeof process&&null!=process.versions?.node,t=e=>{try{return e?e.toLowerCase():e}catch(e){return console.log(e),null}},n=(e,t)=>{const n=e.split("/"),o=t.split("/");if(n.length!==o.length)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t!==o[e]&&":any"!==t)return!1}return!0};let o=[];function s(e){o=e}function r(){return o}function i(e,s="get"){const r=e=>e.startsWith("/")?e:`/${e}`,i=r(e);for(const a of o)if(t(a.apiMethod)===t(s)){const t=r(a.apiEndpoint);if(t===i||n(t,i)){console.log(`[MockForMe] Match Found: ${s} ${e} -> ${a.apiEndpoint}`);const t={url:e,method:a.apiMethod,_ack:a._ack||null};return a._ri_&&(t._ri_=a._ri_),t}}return null}const a="JAVASCRIPT",d="CHROME_EXTENSION",c="mfmver",l="mockforme",m="4.2.8",u="https://api.mockforme.com",p=`${u}/mockforme`,h=`${u}/gateway/3f4eae522b`;let f=null,g=null,b=null,x=[];function y({_o:e,mk:t,_mck:n,_rules:o}){f=e,g=t,b=n,x=o}function v(){return x}function k(){return`${p}/${f}/${g}`}function w(){return b}const _=e=>{try{return new URL(e,window.location.origin).toString()}catch{return e}};function q({url:e,method:t}){try{const t=v();if(!Array.isArray(t)||0===t.length)return null;const n=globalThis.location?.origin||"http://localhost";for(const o of t){const t=o.rule?.URL;if(!t)continue;const{condition:s}=t;if(!s)continue;let r="";switch(t.type){case"URL":{let t;try{t=new URL(e,n),r=t.pathname}catch{r=e}break}case"QUERY":{let t;try{t=new URL(e,n)}catch{t=null}t&&(r=t.searchParams.get(s.key)||"");break}case"BODY":case"HEADERS":r="";break;default:continue}let i=!1;switch(s.operator){case"contains":i=r.includes(s.value);break;case"equal":case"equals":i=r===s.value;break;case"regexp":try{let e=s.value;if(e.startsWith("/")&&e.lastIndexOf("/")>0){const t=e.lastIndexOf("/"),n=e.slice(1,t),o=e.slice(t+1);i=new RegExp(n,o).test(r)}else i=new RegExp(e).test(r)}catch{i=!1}}if(i){const e=s.activeAction;return{action:e,config:s.actions?.[e]||{}}}}return null}catch(e){return console.error("[mockforme] checkIfRulesToBeApplied error",e),null}}const E=function(t,n,o){const s=e();var r;function i(){if(4==r.readyState)if(r.status.toString().match(/^20[0-9]$/)){var e=function(){var e=r.getResponseHeader("Content-Type"),t=r.responseText;if(e){var n=e.split(";");try{return"application/json"===n[0]?JSON.parse(t):t}catch(e){throw"Unable to convert response header"}}}();n.call(this,e,r)}else o.call(this,r.responseText,r)}function a(e,t){var n=[];for(var o in e)if(e.hasOwnProperty(o)){var s=t?t+"["+o+"]":o,r=e[o];n.push("object"==typeof r?a(r,s):encodeURIComponent(s)+"="+encodeURIComponent(r))}return n.join("&")}return s||(window.XMLHttpRequest?r=new XMLHttpRequest:window.ActiveXObject&&(r=new ActiveXObject("Microsoft.XMLHTTP"))),{request:function(){if(!s){if(window.XMLHttpRequest)r.onload=i;else{if(!window.ActiveXObject)throw"unable to process ajax";r.onreadystatechange=i}var e=a(t.params);if("get"==t.method.toLowerCase()&&"object"==typeof t.params){if(-1==t.url.indexOf("?"))t.url+="?";else{var n=t.url.split("?");n[1]&&n[1].split("=")[1]&&(t.url+="&")}t.url+=e}t.hasOwnProperty("async")||(t.async=!0),r.open(t.method,t.url,t.async),t.headers&&function(e){for(let t in e)r.setRequestHeader(t,e[t])}(t.headers),r.send(e)}}}};function R({di:e,iv:t},n){var o;const s=function(e,t){const n=t.length;return e.split("").map(((e,o)=>String.fromCharCode(e.charCodeAt(0)^t.charCodeAt(o%n)))).join("")}((o=e,"undefined"!=typeof Buffer?Buffer.from(o,"base64").toString("utf8"):decodeURIComponent(escape(atob(o)))),n+t);return JSON.parse(s)}function T(e,t,n,o){fetch(h,{method:"GET",headers:{[l]:e,[c]:m,"x-mfm-adaptor":t}}).then((e=>{if(!e.ok)throw e;return e.json()})).then((e=>{if(e){const{di:t,iv:o,_o:r,_mck:i,mk:a,r:d}=e,c=R({di:t,iv:o},r);y({_o:r,mk:a,_mck:i,_rules:d}),c&&(s(c),n(c,{_o:r,mk:a,_mck:i,_rules:d}))}})).catch((e=>{console.error("Error in loading mocked APIs:",e),o(e)}))}const S=new class{constructor(t={}){e()||(this.token=t.token||("undefined"!=typeof localStorage?localStorage.getItem("mockforme-token"):null),this.isAuthenticated=!!this.token,this.onAuthSuccess=t.onAuthSuccess||(()=>{}),this.mappings=t.mappings||[],this.rules=t.rules||[],this.mockedRequests=[],this.otherRequests=[],this.state={isOpen:"undefined"!=typeof localStorage&&"true"===localStorage.getItem("mockforme-widget-open"),activeTab:"mappings",selectedRequest:null,activeRequestTab:"response",lastError:null,searchQuery:""},"undefined"!=typeof document&&(window.addEventListener("resize",(()=>{this.bubble&&this.keepInBounds(this.bubble)})),document.addEventListener("click",(e=>{this.sharedDropdown&&this.sharedDropdown.classList.remove("show")})),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>this.init())):this.init()))}init(){document.getElementById("mockforme-root")||(this.createStyles(),this.render())}createStyles(){if(document.getElementById("mockforme-styles"))return;const e=document.createElement("style");e.id="mockforme-styles",e.innerHTML="\n #mockforme-root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n z-index: 999999;\n position: fixed;\n }\n .mfm-bubble {\n position: fixed;\n bottom: 20px;\n right: 20px;\n height: 42px;\n padding: 4px 8px;\n border-radius: 24px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n background: white;\n display: flex;\n align-items: center;\n gap: 8px;\n transition: transform 0.2s;\n border: 1px solid #eee;\n }\n .mfm-bubble:hover { transform: scale(1.05); }\n .mfm-bubble img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }\n \n .mfm-refresh-icon {\n width: 25px;\n height: 25px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #2563eb;\n font-size: 25px;\n transition: background 0.2s;\n margin-right: 10px;\n background: #f3f4f6;\n padding: 8px;\n }\n .mfm-refresh-icon:hover { }\n .mfm-refresh-icon.loading { animation: mfm-spin 1s linear infinite; }\n @keyframes mfm-spin { 100% { transform: rotate(360deg); } }\n\n .mfm-bottomsheet {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background: white;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.15);\n padding: 20px;\n z-index: 999999;\n height: 80vh;\n max-width: 500px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s ease-out;\n transform: translateY(100%);\n visibility: hidden;\n overflow-y: auto;\n }\n @media (max-width: 480px) {\n .mfm-bottomsheet { padding: 16px; }\n }\n .mfm-bottomsheet.open { transform: translateY(0); visibility: visible; }\n \n .mfm-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n }\n .mfm-title-group { display: flex; align-items: center; gap: 10px; }\n .mfm-title { font-size: 18px; font-weight: 600; color: #111; }\n .mfm-title img { height: 30px; }\n .mfm-delete-token-btn { \n background: #fee2e2; \n color: #dc2626; \n font-size: 13px; \n padding: 4px 12px; \n border-radius: 20px; \n cursor: pointer; \n display: flex;\n align-items: center;\n gap: 6px;\n border: none;\n font-weight: 500;\n transition: background 0.2s;\n }\n .mfm-delete-token-btn:hover { background: #fecaca; }\n .mfm-header-actions { display: flex; align-items: center; gap: 10px; }\n .mfm-close { background: none; border: none; font-size: 30px; cursor: pointer; color: #666; }\n /* Login Form */\n .mfm-logo-container { text-align: center; padding: 20px 0; width: 100%; display: flex; justify-content: center; flex-shrink: 0; }\n .mfm-logo { width: 120px; height: auto; }\n .mfm-link { color: #2563eb; text-decoration: none; font-size: 14px; margin-top: 5px; cursor: pointer; display: inline-block; }\n .mfm-link:hover { text-decoration: underline; }\n .mfm-form { display: flex; flex-direction: column; gap: 10px; }\n .mfm-input {\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n .mfm-input:focus { border-color: #2563eb; }\n .mfm-error { color: #dc2626; font-size: 13px; margin-top: -5px; }\n .mfm-btn {\n padding: 12px;\n background: #2563eb;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n .mfm-btn:hover { background: #1d4ed8; }\n .mfm-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n .mfm-tabs { \n display: flex; \n border-bottom: 1px solid #dee2e6; \n margin-bottom: 10px; \n flex-shrink: 0; \n gap: 2px;\n }\n .mfm-tab {\n padding: 8px 16px;\n cursor: pointer;\n color: #666;\n border: 1px solid transparent;\n border-bottom: none;\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n font-weight: 300;\n font-size: 14px;\n margin-bottom: -1px;\n transition: all 0.2s;\n }\n .mfm-tab:hover {\n background-color: #f8f9fa;\n border-color: #e9ecef #e9ecef transparent;\n }\n .mfm-tab.active { \n color: #2563eb; \n background-color: white;\n border: 1px solid #dee2e6;\n border-bottom: 1px solid white;\n font-weight: 500;\n }\n \n .mfm-list {\n flex: 1;\n overflow-y: auto;\n min-height: 200px;\n max-height: 60vh;\n }\n .mfm-list-item {\n padding: 10px;\n border-bottom: 1px solid #f3f4f6;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n min-height: 52px;\n box-sizing: border-box;\n }\n .mfm-list-item:hover { background: #f9fafb; }\n .mfm-method { \n font-weight: bold; \n padding: 4px 8px; \n border-radius: 4px; \n margin-right: 10px;\n font-size: 12px;\n }\n .GET { background: #dbfcfe; color: #006064; }\n .POST { background: #dcfce7; color: #14532d; }\n .PUT { background: #fef9c3; color: #713f12; }\n .DELETE { background: #fee2e2; color: #7f1d1d; }\n .PATCH { background: #f3e8ff; color: #581c87; }\n .RULE { background: #f3f4f6; color: #374151; }\n\n /* Search & Actions */\n .mfm-actions-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-search-input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; }\n .mfm-search-input:focus { border-color: #2563eb; }\n .mfm-icon-btn { background: none; border: none; font-size: 25px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 4px; }\n .mfm-icon-btn:hover { color: #333; }\n .mfm-icon-btn.refresh { color: #2563eb; padding: 0; height: 20px; width:20px; border-radius: 50%; }\n .mfm-icon-btn.refresh.loading { animation: mfm-spin 1s linear infinite; }\n .mfm-icon-btn.delete { color: #dc2626; }\n\n @media (max-height: 700px) {\n .mfm-bottomsheet { height: 85vh; padding: 12px; }\n .mfm-logo-container { padding: 10px 0; }\n .mfm-logo { width: 100px; }\n .mfm-header { margin-bottom: 10px; }\n .mfm-form { gap: 8px; }\n .mfm-input { padding: 10px; font-size: 14px; }\n .mfm-btn { padding: 10px; font-size: 14px; }\n }\n\n .mfm-url { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .mfm-status { font-weight: bold; margin-left: 10px; }\n .status-2xx { color: #16a34a; }\n .status-4xx { color: #dc2626; }\n .status-5xx { color: #ea580c; }\n .status-aborted, .status-timeout, .status-error { color: #dc2626; font-size: 11px; font-weight: 500; }\n\n .mfm-rule-tag {\n font-size: 9px;\n padding: 2px 4px;\n border-radius: 4px;\n background: #fef3c7;\n color: #92400e;\n border: 1px solid #fde68a;\n margin-right: 6px;\n text-transform: uppercase;\n font-weight: bold;\n }\n\n .mfm-detail-view { display: flex; flex-direction: column; height: 100%; }\n .mfm-detail-actions { margin-bottom: 10px; }\n .mfm-back-btn { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 0;}\n \n .mfm-code-block {\n background: #f8fafc;\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n font-family: monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-break: break-all;\n max-height: 300px;\n border: 1px solid #e2e8f0;\n }\n .section-title { font-size: 14px; font-weight: 600; margin: 10px 0 5px; color: #333; }\n .section-header { display: flex; align-items: center; justify-content: space-between; margin: 10px 0 5px; }\n .mfm-copy-btn {\n background: #f1f5f9;\n color: #64748b;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 10px;\n cursor: pointer;\n border: 1px solid #e2e8f0;\n transition: all 0.2s;\n font-weight: 600;\n text-transform: uppercase;\n }\n .mfm-copy-btn:hover { background: #e2e8f0; color: #334155; }\n \n .mfm-copy-group {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 4px;\n }\n .mfm-copy-group .mfm-copy-btn {\n font-size: 9px;\n padding: 2px 6px;\n }\n\n /* Dropdown Menu */\n .mfm-menu-dots {\n cursor: pointer;\n padding: 4px 8px;\n font-size: 18px;\n color: #666;\n border-radius: 4px;\n transition: background 0.2s;\n line-height: 1;\n margin-left: 4px;\n }\n .mfm-menu-dots:hover { background: #f3f4f6; color: #333; }\n \n .mfm-dropdown-shared {\n position: fixed;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n z-index: 1000005;\n display: none;\n flex-direction: column;\n width: 160px;\n overflow: hidden;\n }\n .mfm-dropdown-shared.show { display: flex; }\n .mfm-dropdown-item {\n padding: 10px 14px;\n font-size: 13px;\n color: #334155;\n cursor: pointer;\n transition: background 0.2s;\n text-align: left;\n border-bottom: 1px solid #f1f5f9;\n white-space: nowrap;\n }\n .mfm-dropdown-item:last-child { border-bottom: none; }\n .mfm-dropdown-item:hover { background: #f8fafc; color: #2563eb; }\n\n /* Snackbar */\n .mfm-snackbar {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: #333;\n color: white;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n z-index: 1000000;\n opacity: 0;\n transition: opacity 0.3s, transform 0.3s;\n pointer-events: none;\n }\n .mfm-snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n /* Confirm Dialog */\n .mfm-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000001;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n .mfm-modal-overlay.show { opacity: 1; pointer-events: auto; }\n .mfm-modal {\n background: white;\n padding: 20px;\n border-radius: 12px;\n width: 300px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n transform: scale(0.9);\n transition: transform 0.2s;\n }\n .mfm-modal-overlay.show .mfm-modal { transform: scale(1); }\n .mfm-modal-title { font-size: 16px; font-weight: 600; margin-bottom: 10px; }\n .mfm-modal-body { font-size: 14px; color: #666; margin-bottom: 20px; }\n .mfm-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }\n .mfm-btn-alt { background: #f3f4f6; color: #333; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n .mfm-btn-danger { background: #dc2626; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n \n .mfm-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2563eb;\n border-radius: 50%;\n animation: mfm-spin 1s linear infinite;\n margin-right: 8px;\n display: inline-block;\n }\n .status-aborted { color: #dc2626; font-weight: bold; }\n .mfm-duration { color: #666; font-size: 11px; margin-left: 8px; font-weight: normal; }\n ",document.head.appendChild(e)}render(){const e=document.createElement("div");e.id="mockforme-root",document.body.appendChild(e),this.root=e,this.bubble=this.renderBubble(),this.root.appendChild(this.bubble),this.sheet=document.createElement("div"),this.sheet.className="mfm-bottomsheet",this.root.appendChild(this.sheet),this.snackbar=document.createElement("div"),this.snackbar.className="mfm-snackbar",this.root.appendChild(this.snackbar),this.modalOverlay=document.createElement("div"),this.modalOverlay.className="mfm-modal-overlay",this.root.appendChild(this.modalOverlay),this.sharedDropdown=document.createElement("div"),this.sharedDropdown.className="mfm-dropdown-shared",this.sharedDropdown.innerHTML='\n <div class="mfm-dropdown-item" data-format="curl">Copy as cURL</div>\n <div class="mfm-dropdown-item" data-format="fetch">Copy as Fetch</div>\n <div class="mfm-dropdown-item" data-format="xhr">Copy as XHR</div>\n ',this.root.appendChild(this.sharedDropdown),this.updateUI()}updateUI(){this.state.isOpen?(this.renderBottomSheetContent(),this.sheet.classList.add("open")):this.sheet.classList.remove("open")}renderBubble(){const e=document.createElement("div");e.className="mfm-bubble";const t="undefined"!=typeof localStorage?JSON.parse(localStorage.getItem("mockforme-dot-pos")||"{}"):{};return t.bottom&&(e.style.bottom=t.bottom+"px"),t.right&&(e.style.right=t.right+"px"),e.innerHTML='\n <div class="mfm-refresh-icon" title="Reload Mappings">↻</div>\n <img src="https://ik.imagekit.io/mfm/static-collection/mfm-48x48.png" alt="MFM" />\n ',this.addDragLogic(e),this.keepInBounds(e),e.querySelector("img").onclick=e=>{this.isDragging||this.toggleSheet()},e.querySelector(".mfm-refresh-icon").onclick=e=>{e.stopPropagation(),this.refreshMappings()},e}keepInBounds(e){const t=e.getBoundingClientRect(),n=10;let o=parseInt(e.style.right)||20,s=parseInt(e.style.bottom)||20;const r=window.innerWidth-t.width-n,i=window.innerHeight-t.height-n;o<n&&(o=n),s<n&&(s=n),o>r&&(o=r),s>i&&(s=i),e.style.right=o+"px",e.style.bottom=s+"px"}addDragLogic(e){let t,n,o,s,r=!1;const i=(i,a)=>{r=!1,this.isDragging=!1,t=i,n=a;const d=e.getBoundingClientRect();o=window.innerWidth-d.right,s=window.innerHeight-d.bottom},a=(i,a)=>{const d=t-i,c=n-a;(Math.abs(d)>3||Math.abs(c)>3)&&(r=!0,this.isDragging=!0),r&&(e.style.right=o+d+"px",e.style.bottom=s+c+"px")},d=()=>{r&&(this.keepInBounds(e),"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-dot-pos",JSON.stringify({bottom:parseInt(e.style.bottom),right:parseInt(e.style.right)})))},c=e=>{a(e.clientX,e.clientY)},l=()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",l),d()},m=e=>{const t=e.touches[0];a(t.clientX,t.clientY),r&&e.cancelable&&e.preventDefault()},u=()=>{document.removeEventListener("touchmove",m),document.removeEventListener("touchend",u),d()};e.addEventListener("mousedown",(e=>{i(e.clientX,e.clientY),document.addEventListener("mousemove",c),document.addEventListener("mouseup",l)})),e.addEventListener("touchstart",(e=>{const t=e.touches[0];i(t.clientX,t.clientY),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",u)}),{passive:!0})}toggleSheet(){this.state.isOpen=!this.state.isOpen,"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-widget-open",this.state.isOpen),this.updateUI()}showSnackbar(e){this.snackbar.textContent=e,this.snackbar.classList.add("show"),setTimeout((()=>this.snackbar.classList.remove("show")),3e3)}showConfirm(e,t,n){this.modalOverlay.innerHTML=`\n <div class="mfm-modal">\n <div class="mfm-modal-title">${e}</div>\n <div class="mfm-modal-body">${t}</div>\n <div class="mfm-modal-actions">\n <button class="mfm-btn-alt" id="mfm-confirm-cancel">Cancel</button>\n <button class="mfm-btn-danger" id="mfm-confirm-yes">Delete</button>\n </div>\n </div>\n `,this.modalOverlay.classList.add("show"),this.modalOverlay.querySelector("#mfm-confirm-cancel").onclick=()=>{this.modalOverlay.classList.remove("show")},this.modalOverlay.querySelector("#mfm-confirm-yes").onclick=()=>{this.modalOverlay.classList.remove("show"),n()}}refreshMappings(){if(!this.token)return void this.toggleSheet();const e=document.querySelectorAll(".mfm-refresh-icon, #mfm-refresh-mappings-btn");e.forEach((e=>e.classList.add("loading"))),T(this.token,"JAVASCRIPT",((t,n)=>{console.log("[MockForMe] Mappings reloaded",{mappings:t?.length,rules:n?._rules?.length}),e.forEach((e=>e.classList.remove("loading"))),this.mappings=t||[],this.rules=n?._rules||[],this.onAuthSuccess(this.token,this.mappings,n),this.updateUI(),this.showSnackbar("Mappings reloaded!")}),(t=>{e.forEach((e=>e.classList.remove("loading"))),this.showSnackbar("Failed to reload mappings")}))}renderBottomSheetContent(){this.sheet.innerHTML="",this.isAuthenticated?this.state.selectedRequest?this.sheet.appendChild(this.renderRequestDetail(this.state.selectedRequest)):this.sheet.appendChild(this.renderDashboard()):this.sheet.appendChild(this.renderTokenForm())}renderTokenForm(){const e=document.createElement("div");e.innerHTML=`\n <div class="mfm-header">\n <span class="mfm-title">Save Token</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-logo-container">\n <img src="https://dashboard.mockforme.com/public/assets/images/logo.png" class="mfm-logo" alt="MockForMe Logo" />\n </div>\n <div class="mfm-form">\n <input type="text" class="mfm-input" placeholder="Enter MockForMe Access Token" id="mfm-token-input">\n <div class="mfm-error" id="mfm-error-msg" style="display: ${this.state.lastError?"block":"none"}">${this.state.lastError||""}</div>\n <a href="https://dashboard.mockforme.com/user/token" target="_blank" class="mfm-link">Get Access Token?</a>\n <button class="mfm-btn" id="mfm-save-token-btn">Save</button>\n </div>\n `,e.querySelector(".mfm-close").onclick=()=>{this.state.lastError=null,this.toggleSheet()};const t=e.querySelector("#mfm-save-token-btn"),n=e.querySelector("#mfm-token-input");return t.onclick=()=>{const o=n.value.trim();o&&(t.textContent="Validating...",t.disabled=!0,this.state.lastError=null,e.querySelector("#mfm-error-msg").style.display="none",T(o,a,((e,t)=>{"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-token",o),this.token=o,this.isAuthenticated=!0,this.mappings=e,this.rules=t?._rules||[],this.onAuthSuccess(o,e,t),this.updateUI()}),(e=>{t.textContent="Save",t.disabled=!1;let n="Invalid Token or Network Error";e&&"function"==typeof e.json?e.json().then((e=>{this.state.lastError=e.message||n,this.updateUI()})).catch((()=>{this.state.lastError=n,this.updateUI()})):(this.state.lastError=e.message||n,this.updateUI())})))},e}updateMappings(e,t){this.mappings=e||[],this.rules=t?._rules||[],this.root&&this.updateUI()}deleteToken(){"undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),this.token=null,this.isAuthenticated=!1,this.mappings=[],this.rules=[],this.state.lastError=null,this.updateUI()}renderDashboard(){const e=document.createElement("div");e.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title"><img src="https://www.mockforme.com/assets/images/logo.png" /></span>\n <div class="mfm-header-actions">\n <button class="mfm-close">×</button>\n </div>\n </div>\n ';const t=document.createElement("div");t.className="mfm-tabs",["mappings","mocked","other","settings"].forEach((e=>{const n=document.createElement("div");n.className="mfm-tab "+(this.state.activeTab===e?"active":""),n.textContent="mappings"===e?"Api Mappings":"mocked"===e?"Mocked Apis":"other"===e?"Other Apis":"Settings",n.onclick=()=>{this.state.activeTab=e,this.updateUI()},t.appendChild(n)})),e.querySelector(".mfm-header").after(t);const n=document.createElement("div");n.className="mfm-actions-bar","mappings"===this.state.activeTab?(n.innerHTML=`\n <div style="flex: 1; font-size: 13px; color: #666;">Total: ${this.mappings.length+this.rules.length}</div>\n <div class="mfm-icon-btn refresh" id="mfm-refresh-mappings-btn" title="Refresh Mappings">↻</div>\n `,n.querySelector("#mfm-refresh-mappings-btn").onclick=()=>this.refreshMappings()):"settings"!==this.state.activeTab?(n.innerHTML=`\n <input type="text" class="mfm-search-input" placeholder="Search requests..." id="mfm-search-input" value="${this.state.searchQuery}">\n <button class="mfm-icon-btn delete" id="mfm-clear-requests-btn" title="Clear List">🚫</button>\n `,n.querySelector("#mfm-search-input").oninput=e=>{this.state.searchQuery=e.target.value,this.updateRequestList(o)},n.querySelector("#mfm-clear-requests-btn").onclick=()=>{"mocked"===this.state.activeTab?this.mockedRequests=[]:this.otherRequests=[],this.updateUI()}):n.style.display="none",t.after(n);const o=document.createElement("div");return o.className="mfm-list",this.updateRequestList(o),e.appendChild(o),e.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),e}updateRequestList(e){if(e.innerHTML="","mappings"===this.state.activeTab){const t=[...this.mappings.map((e=>({...e,type:"mapping"}))),...this.rules.map((e=>({...e,type:"rule"})))];0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");if(n.className="mfm-list-item","mapping"===t.type){const e=(t.apiMethod||"GET").toUpperCase();n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method ${e}">${e}</span><span class="mfm-url">${t.apiEndpoint}</span></div>`}else{const e=t.ruleName||"Network Rule",o=(t.rule?.URL?.condition||{}).value||e;n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method RULE">RULE</span><span class="mfm-url">${o}</span></div>`}e.appendChild(n)}))}else if("settings"===this.state.activeTab)e.innerHTML='\n <div style="padding: 16px;">\n <div class="section-title" style="margin-top: 0; margin-bottom: 12px; font-size: 15px; border-bottom: 1px solid #eee; padding-bottom: 8px;">Token Management</div>\n <p style="font-size: 13px; color: #666; margin-bottom: 16px;">Removing your token will clear all saved mappings and rules from this browser instance.</p>\n <button class="mfm-delete-token-btn" id="mfm-settings-delete-token" style="padding: 8px 16px; font-size: 14px; width: fit-content;">\n <span>🗑</span>\n <span>Delete Token</span>\n </button>\n </div>\n ',e.querySelector("#mfm-settings-delete-token").onclick=()=>{this.showConfirm("Delete Token","Are you sure you want to Delete Token? This will clear your saved token.",(()=>{this.deleteToken()}))};else{let t="mocked"===this.state.activeTab?this.mockedRequests:this.otherRequests;if(this.state.searchQuery){const e=this.state.searchQuery.toLowerCase();t=t.filter((t=>(t.url||"").toLowerCase().includes(e)||(t.method||"").toLowerCase().includes(e)||String(t.status).includes(e)))}0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");n.className="mfm-list-item";const o=(t.method||"").toUpperCase(),s=`<span class="mfm-duration" style="color:#2563eb; font-size:11px; font-weight:500; display:block; height:14px; line-height:14px;">${void 0!==t.duration?t.duration+"ms":""}</span>`,r=(()=>{if(!t.rule)return"";let e="delay_and_timeout"===t.rule.action?"Delay and Timout":t.rule.action.split("_")[0];const n=t.rule.config?.value;return void 0!==n&&(t.rule.action.includes("delay")||t.rule.action.includes("timeout"))&&(e+=` (${n}s)`),`<span class="mfm-rule-tag" title="${t.rule.action}">${e}</span>`})();let i=t.status,a="mfm-status";t.pending?i='<span class="mfm-spinner"></span>':t.aborted?(i="Aborted",a+=" status-aborted"):t.timeout?(i="Timeout",a+=" status-timeout"):t.error?(i="Error/CORS",a+=" status-error"):(i=t.status,a+=t.status>=200&&t.status<300?" status-2xx":" status-4xx"),n.innerHTML=`\n <div style="display:flex; align-items:center; width:100%; gap:8px;">\n <span class="mfm-method ${o}">${o}</span>\n <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; justify-content:center;">\n <span class="mfm-url">${r}${t.url}</span>\n ${s}\n </div>\n <span class="${a}">${i}</span>\n <span class="mfm-menu-dots" title="Copy As...">⋮</span>\n </div>\n `;const d=n.querySelector(".mfm-menu-dots");d.onclick=e=>{e.stopPropagation();const n=d.getBoundingClientRect();this.sharedDropdown.style.top=n.bottom+5+"px",this.sharedDropdown.style.left=n.right-160+"px",this.sharedDropdown.classList.toggle("show"),this.sharedDropdown.onclick=e=>{e.stopPropagation();const n=e.target.getAttribute("data-format");if(!n)return;let o="";"curl"===n?o=this.generateCurl(t):"fetch"===n?o=this.generateFetch(t):"xhr"===n&&(o=this.generateXHR(t)),o&&navigator.clipboard.writeText(o).then((()=>{this.showSnackbar(`${n.toUpperCase()} copied!`),this.sharedDropdown.classList.remove("show")}))}},n.onclick=()=>{this.state.selectedRequest=t,this.updateUI()},e.appendChild(n)}))}}renderRequestDetail(e){const t=document.createElement("div");t.className="mfm-detail-view",t.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">Details</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-detail-actions">\n <button class="mfm-back-btn">← Back</button>\n </div>\n ';const n=document.createElement("div");n.className="mfm-tabs",["request","response"].forEach((e=>{const t=document.createElement("div");t.className="mfm-tab "+(this.state.activeRequestTab===e?"active":""),t.textContent=e.charAt(0).toUpperCase()+e.slice(1),t.onclick=()=>{this.state.activeRequestTab=e,this.updateUI()},n.appendChild(t)}));const o=document.createElement("div");o.style.overflowY="auto",o.style.flex="1";const s=(e.method||"").toUpperCase(),r=this.formatBody(e.requestBody),i=this.formatBody(e.responseBody);o.innerHTML="request"===this.state.activeRequestTab?`\n <div class="section-title">URL</div><div class="mfm-code-block">${s} ${e.url}</div>\n ${e.rule?`<div class="section-title">Matched Rule</div><div class="mfm-code-block">${e.rule.action} (value: ${e.rule.config?.value})</div>`:""}\n <div class="section-title">Headers</div><div class="mfm-code-block">${JSON.stringify(e.requestHeaders||{},null,2)}</div>\n <div class="section-title">Query Params</div><div class="mfm-code-block">${JSON.stringify(e.queryParams||{},null,2)}</div>\n <div class="section-header">\n <div class="section-title" style="margin: 0;">Payload</div>\n <div class="mfm-copy-btn" id="mfm-copy-payload">Copy</div>\n </div>\n <div class="mfm-code-block">${r}</div>\n `:`\n <div class="section-title">Status</div><div class="mfm-code-block">${e.status}</div>\n <div class="section-title">Response Headers</div><div class="mfm-code-block">${JSON.stringify(e.responseHeaders||{},null,2)}</div>\n <div class="section-header">\n <div class="section-title" style="margin: 0;">Response Body</div>\n <div class="mfm-copy-btn" id="mfm-copy-response">Copy</div>\n </div>\n <div class="mfm-code-block">${i}</div>\n `,t.appendChild(n),t.appendChild(o);const a=o.querySelector("#mfm-copy-payload");a&&(a.onclick=()=>{navigator.clipboard.writeText(r).then((()=>{this.showSnackbar("Payload copied!")}))});const d=o.querySelector("#mfm-copy-response");return d&&(d.onclick=()=>{navigator.clipboard.writeText(i).then((()=>{this.showSnackbar("Response copied!")}))}),t.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),t.querySelector(".mfm-back-btn").onclick=()=>{this.state.selectedRequest=null,this.updateUI()},t}formatBody(e){if(!e)return"";if("undefined"!=typeof FormData&&e instanceof FormData){const t={};return e.forEach(((e,n)=>{"undefined"!=typeof File&&e instanceof File?t[n]=`[File: ${e.name} (${e.size} bytes)]`:"undefined"!=typeof Blob&&e instanceof Blob?t[n]=`[Blob: ${e.type} (${e.size} bytes)]`:t[n]=e})),JSON.stringify(t,null,2)}if("undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams)return JSON.stringify(Object.fromEntries(e.entries()),null,2);if("undefined"!=typeof Blob&&e instanceof Blob)return`[${"undefined"!=typeof File&&e instanceof File?"File":"Blob"}: ${e.type||"binary"} (${e.size} bytes)]`;if(e instanceof ArrayBuffer||"undefined"!=typeof Uint8Array&&e instanceof Uint8Array)return`[Binary Data: ${e.byteLength||e.length} bytes]`;if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}generateCurl(e){const t=(e.method||"GET").toUpperCase();let n=`curl -X ${t} "${e.url}"`;if(e.requestHeaders&&Object.entries(e.requestHeaders).forEach((([e,t])=>{n+=` \\\n -H "${e}: ${t}"`})),e.requestBody&&"GET"!==t){const t=("string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody)).replace(/'/g,"'\\''");n+=` \\\n --data-raw '${t}'`}return n}generateFetch(e){const t=(e.method||"GET").toUpperCase();let n=`{\n method: "${t}",\n headers: ${(e.requestHeaders?JSON.stringify(e.requestHeaders,null,2):"{}").replace(/\n/g,"\n ")}`;if(e.requestBody&&"GET"!==t){const t="string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody,null,2);n+=`,\n body: ${"string"==typeof e.requestBody?"`"+t+"`":JSON.stringify(e.requestBody,null,2).replace(/\n/g,"\n ")}`}return n+="\n}",`fetch("${e.url}", ${n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error(error));`}generateXHR(e){const t=(e.method||"GET").toUpperCase();let n=`var xhr = new XMLHttpRequest();\nxhr.open("${t}", "${e.url}");\n`;if(e.requestHeaders&&Object.entries(e.requestHeaders).forEach((([e,t])=>{n+=`xhr.setRequestHeader("${e}", "${t}");\n`})),n+="\nxhr.onload = function() {\n console.log(xhr.responseText);\n};\n",e.requestBody&&"GET"!==t){const t="string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody);n+=`\nxhr.send(${"string"==typeof e.requestBody?"`"+t+"`":JSON.stringify(e.requestBody)});`}else n+="\nxhr.send();";return n}addRequest(e,t){const n=String(t.method||"").toUpperCase().trim(),o=String(t.url||"");console.log(`[MockForMe] addRequest type=${e} method=${n} url=${o} requestId=${t.requestId} pending=${t.pending}`);const s=o.includes("api.mockforme.com")||o.includes("mockforme.com/proxy")||o.includes("mockforme.com/gateway")||p&&o.includes(String(p));if("OPTIONS"===n||s)return;const r="mocked"===e?this.mockedRequests:this.otherRequests,i="mocked"===e?this.otherRequests:this.mockedRequests;if(t.requestId){const n=r.findIndex((e=>e.requestId===t.requestId));if(-1!==n)return r[n]={...r[n],...t},void(this.state.isOpen&&this.state.activeTab===e&&this.updateUI());const o=i.findIndex((e=>e.requestId===t.requestId));if(-1!==o){const e={...i[o],...t};return i.splice(o,1),r.unshift(e),r.length>50&&r.pop(),void(this.state.isOpen&&this.updateUI())}}r.unshift(t),r.length>50&&r.pop(),this.state.isOpen&&this.state.activeTab===e&&this.updateUI()}};function I(e,t){if("undefined"!=typeof window){if(window._mfm_intercepted)return;window._mfm_intercepted=!0}!function(e,t){const n=window.fetch;window.fetch=function(o,s={}){const r="string"==typeof o?o:o.url,a=String(s.method||(o instanceof Request?o.method:"GET")).toUpperCase().trim();if("OPTIONS"===a)return console.log(`[MockForMe] Filtered ${a} ${r} (isInternal=false)`),n(o,s);const u={url:r,method:a,requestHeaders:s.headers||(o instanceof Request?o.headers:{}),requestBody:s.body||(o instanceof Request?"{binary/stream}":null),queryParams:M(r)};try{const p=new URL(r,window.location.origin),{pathname:h,search:f}=p,g=q({url:r,method:a}),b=Math.random().toString(36).substring(7),x=Date.now();if(t!==d&&g){if(S.addRequest("mocked",{...u,requestId:b,pending:!0,startTime:x,rule:g}),"delay"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(o,s);t.then((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_redirect"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(g.config.redirectUrl,s);t.then((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_timeout"===g.action){const e=Number(g.config.value||0);if(0===e){const e=new AbortController,t=n(o,{...s,signal:e.signal});e.abort();const r=Date.now()-x;return S.addRequest("mocked",{...u,requestId:b,status:"Aborted",pending:!1,duration:r,aborted:!0}),t.catch((()=>Promise.reject(new Error("Fetch aborted by rules"))))}return new Promise(((t,n)=>{setTimeout((()=>{const e=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:"Aborted",pending:!1,duration:e,aborted:!0}),n(new Error("Fetch timed out by rules"))}),1e3*e)}))}}const y=i(h,a),v=S.token||e;if(!y&&!g)return S.addRequest("other",{...u,requestId:b,pending:!0,startTime:x}),n(o,s).then((async e=>{const t=e.clone(),n=Date.now()-x;try{const o=await t.text();S.addRequest("other",{...u,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:o,pending:!1,duration:n})}catch(e){}return e})).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name||e.message.includes("aborted"),o="TimeoutError"===e.name||e.message.includes("timed out"),s=!n&&!o;throw S.addRequest("other",{...u,requestId:b,status:n?"Aborted":o?"Timeout":"Error",responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n,timeout:o,error:s}),e}));let E=r;g||(E=`${k()}${y.url}${f}`);let R={};if(!g){R={[l]:v,[c]:m,"x-mfm-adaptor":t};const e=w();e&&y._ack&&(R["x-mfm-key"]=`${e}-${y._ack}`),y?._ri_&&(R._ri_=y._ri_)}const T=o instanceof Request?o.headers:s.headers,I=new Headers(T||{});Object.entries(R).forEach((([e,t])=>{I.set(e,t)}));let M=s.body;if(!M&&o instanceof Request){const e=o.clone();"GET"!==e.method&&"HEAD"!==e.method&&(M=e.text())}let $={method:a,headers:I,credentials:"include",mode:"cors"};g&&($.credentials=o instanceof Request&&o.credentials||s.credentials||"same-origin",$.mode=o instanceof Request&&o.mode||s.mode||"cors");const L=e=>(e&&($.body=e),t===d?(S.addRequest("mocked",{...u,requestBody:e||u.requestBody,requestId:b,pending:!0,startTime:x}),(({actualUrl:e,url:t,method:n,headers:o,body:s,credentials:r,mode:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=_(t);return new Promise(((t,l)=>{window.addEventListener("message",(function e(n){if(n.source!==window)return;const o=n.data;if("INTERCEPTED_RESPONSE"===o?.type&&o?.requestId===d){if(window.removeEventListener("message",e),o?.response?.error)return l(new Error(o.response.message));const{status:n,headers:s,body:r}=o.response;t(new Response(r,{status:n,headers:s}))}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:o,body:s,credentials:r,mode:i,rule:a}},"*")}))})({actualUrl:r,url:E,method:a,headers:Object.fromEntries(I.entries()),body:e,credentials:$.credentials,mode:$.mode,rule:g}).then((e=>(e.clone().text().then((t=>{const n=Date.now()-x;S.addRequest("mocked",{...u,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:t,pending:!1,duration:n})})),e))).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name;throw S.addRequest("mocked",{...u,requestId:b,status:n?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n}),e}))):(S.addRequest("mocked",{...u,requestBody:e||u.requestBody,requestId:b,pending:!0,startTime:x}),n(E,$).then((e=>{const t=e.clone(),n=t.headers.get("content-type")||"",o=Date.now()-x,s=n=>(S.addRequest("mocked",{...u,requestId:b,status:e.status,responseHeaders:Object.fromEntries(t.headers.entries()),responseBody:n,pending:!1,duration:o}),new Response("string"==typeof n?n:JSON.stringify(n),{status:e.status,headers:t.headers}));return n.includes("application/json")?t.json().then(s):t.text().then(s)})).catch((e=>{console.warn("mockforme fetch interceptor error",e);const t=Date.now()-x,r="AbortError"===e.name;return S.addRequest("mocked",{...u,requestId:b,status:r?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:r}),n(o,s)}))));return M instanceof Promise?M.then((e=>L(e))):L(M)}catch(e){return console.warn("mockforme fetch interceptor error",e),n(o,s)}}}(e,t),function(e,t){const n=window.XMLHttpRequest;window.XMLHttpRequest=class{constructor(){return this._xhr=new n,this._headers={},this._method=null,this._url=null,this._body=null,this._isMocked=!1,this._captured=!1,this._requestId=null,this._startTime=null,this._capture=(e,t,n,o={})=>{if(this._captured)return;this._captured=!0;const s=this._isMocked?"mocked":"other",r=this._startTime?Date.now()-this._startTime:0;console.log(`[MockForMe] XHR Capture: status=${e} method=${this._method} url=${this._url} duration=${r}ms`);const i=e=>{const t=`on${e}`;(this[t]||this._xhr[t])?.(),this._xhr.dispatchEvent(new Event(e))};if(S.addRequest(s,{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:M(this._url),requestId:this._requestId,status:e,responseBody:t,responseHeaders:n,pending:!1,duration:r,...o}),"Aborted"!==e)try{Object.defineProperty(this,"responseText",{value:t,configurable:!0}),Object.defineProperty(this,"response",{value:t,configurable:!0}),Object.defineProperty(this,"status",{value:Number(e)||0,configurable:!0}),Object.defineProperty(this,"statusText",{value:200==e?"OK":"",configurable:!0})}catch(e){console.warn("[MockForMe] Error setting XHR props",e)}Object.defineProperty(this,"readyState",{value:4,configurable:!0}),i("readystatechange"),"Aborted"===e||o.aborted?i("abort"):0===e||"Error"===e||o.error?i("error"):"Timeout"===e||o.timeout?i("timeout"):i("load")},new Proxy(this,{get:(e,t)=>{if(t in e)return e[t];const n=e._xhr[t];return"function"==typeof n?n.bind(e._xhr):n},set:(e,t,n)=>t in e?(e[t]=n,!0):(e._xhr[t]=n,!0)})}open(e,t,n=!0,o=null,s=null){this._method=(e||"").toUpperCase().trim();try{this._url=new URL(t,window.location.href).toString()}catch(e){this._url=t}return console.log(`[MockForMe] XHR.open: ${this._method} ${this._url}`),this._xhr._mockOriginalUrl=this._url,this._xhr._mockMethod=this._method,this._xhr.open(e,t,n,o,s)}setRequestHeader(e,t){return this._headers[e]=t,this._xhr.setRequestHeader(e,t)}send(o){const s=String(this._method||"").toUpperCase().trim();if(console.log(`[MockForMe] XHR send method=${s} url=${this._url}`),"OPTIONS"===s)return console.log("[MockForMe] XHR Skip OPTIONS"),this._xhr.send(o);this._body=o,this._requestId=Math.random().toString(36).substring(7),this._startTime=Date.now(),this._xhr.addEventListener("abort",(()=>{this._capture("Aborted","Request aborted",{},{aborted:!0})}),{once:!0}),this._xhr.addEventListener("error",(()=>{this._capture(0,"Network Error",{},{error:!0})}),{once:!0}),this._xhr.addEventListener("timeout",(()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})}),{once:!0});try{const s=new URL(this._url),{pathname:r,search:a}=s,u=q({url:this._url,method:this._method});u&&(this._isMocked=!0,S.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:M(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime,rule:u}));const p=S.token||e;if(t!==d&&u){if("delay"===u.action){const e=Number(u.config.value||0);return setTimeout((()=>{this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_redirect"===u.action){const e=Number(u.config.value||0);return setTimeout((()=>{this._xhr.open(this._method,u.config.redirectUrl,!0),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_timeout"===u.action){const e=Number(u.config.value||0);return 0===e?(this._capture("Aborted","Request aborted by rules",{},{aborted:!0}),void this._xhr.abort()):setTimeout((()=>{this._capture("Aborted","Request timed out by rules",{},{aborted:!0}),this._xhr.abort()}),1e3*e)}}const h=i(r,this._method);if(!h&&!u)return console.log("[MockForMe] XHR No Match:",{pathname:r,method:this._method}),this._isMocked=!1,S.addRequest("other",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:M(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(o);console.log("[MockForMe] XHR Matched:",{pathname:r,match:h}),this._isMocked=!0;let f=this._url;u||(f=`${k()}${h.url}${a}`);let g={};if(!u){g={[l]:p,[c]:m,"x-mfm-adaptor":t};const e=w();e&&h._ack&&(g["x-mfm-key"]=`${e}-${h._ack}`),h?._ri_&&(g._ri_=h._ri_)}const b={...this._headers,...g};if(S.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:M(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),t===d)(({actualUrl:e,url:t,method:n,headers:o,body:s,onSuccess:r,onError:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=_(t);window.addEventListener("message",(function e(t){if(t.source!==window)return;const n=t.data;if("INTERCEPTED_RESPONSE"===n.type&&n.requestId===d){if(window.removeEventListener("message",e),n.response?.error)return i?.(n.response.message);const{body:t,status:o,headers:s}=n.response;r?.({body:t,status:o,headers:s})}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:o,body:s,credentials:"same-origin",mode:"cors",rule:a}},"*")})({actualUrl:this._url,url:f,method:this._method,headers:b,body:this._body,rule:u,onSuccess:({body:e,status:t,headers:n})=>{this._capture(t,e,n||{})},onError:e=>{this._capture(0,e.message||"XHR Error",{},{error:!0})}});else{const e=new n;e.open(this._method,f,!0);for(const t in b)e.setRequestHeader(t,b[t]);e.withCredentials=this.withCredentials,e.onload=()=>{this._capture(e.status,e.responseText,e.getAllResponseHeaders())},e.onerror=()=>{this._capture(0,"Network Error",{},{error:!0})},e.ontimeout=()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})},e.send(this._body)}}catch(e){if(console.error("[MockForMe] XHR Interception Error",e),!this._xhr.readyState||1===this._xhr.readyState)return this._xhr.send(o)}}}}(e,t)}function M(e){try{const t=new URL(e,window.location.origin).searchParams;return Object.fromEntries(t.entries())}catch{return{}}}const $=(t,n=null,o=a)=>{if(e())return;if(o===a)try{t&&("undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),S.token=t,S.isAuthenticated=!0),!t&&S.token&&(t=S.token),S.onAuthSuccess=(e,t,n)=>{s(t),y(n),S.updateMappings(t,n)}}catch(e){console.log("Error in showing mockforme widget",e)}n||={mappings:[],_o:null,mk:null,_mck:null,_rules:[]};const{mappings:d=[],_o:u,mk:p,_mck:f,_rules:g=[]}=n;return s(d),y({_o:u,mk:p,_mck:f,_rules:g}),{run:(e,n=()=>{})=>{I(t,o),u&&p?(S.mappings=d,S.rules=g,e?.(d,{_o:u,mk:p,_mck:f,_rules:g})):t&&function(t,n,o,r){E({method:"get",url:h,async:!1,headers:{[l]:t,[c]:m,"x-mfm-adaptor":n}},(t=>{if(t)try{const{di:r,iv:i,_o:a,_mck:d,mk:c,r:l}=t,m=R({di:r,iv:i},a);y({_o:a,mk:c,_mck:d,_rules:l}),m&&(s(m),n=m,o={_o:a,mk:c,_mck:d,_rules:l},S.updateMappings(n,o),e?.(n,o))}catch(e){return void r(new Error("Unable to fetch mocked apis"))}var n,o}),(e=>{console.log("Error in loading mocked apis"),r(e)})).request()}(t,o,0,n)},checkIfApiToBeMocked:i,getMappings:r,doFetchMappings:(e,n)=>{T(t||S.token,o,((t,n)=>{S.updateMappings(t,n),e?.(t,n)}),n)}}},L=e();L||(globalThis.mockforme=$);let C=$;L&&(C=e=>({run:()=>{}}));const H=C;export{H as mockforme};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.mockforme=t():e.mockforme=t()}(this,(()=>(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{mockforme:()=>U});const n=()=>"undefined"!=typeof process&&null!=process.versions?.node,s=e=>{try{return e?e.toLowerCase():e}catch(e){return console.log(e),null}},o=(e,t)=>{const n=e.split("/"),s=t.split("/");if(n.length!==s.length)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t!==s[e]&&":any"!==t)return!1}return!0};let r=[];function i(e){r=e}function a(){return r}function d(e,t="get"){const n=e=>e.startsWith("/")?e:`/${e}`,i=n(e);for(const a of r)if(s(a.apiMethod)===s(t)){const s=n(a.apiEndpoint);if(s===i||o(s,i)){console.log(`[MockForMe] Match Found: ${t} ${e} -> ${a.apiEndpoint}`);const n={url:e,method:a.apiMethod,_ack:a._ack||null};return a._ri_&&(n._ri_=a._ri_),n}}return null}const c="JAVASCRIPT",l="CHROME_EXTENSION",m="mfmver",u="mockforme",h="4.2.8",p="https://api.mockforme.com",f=`${p}/mockforme`,g=`${p}/gateway/3f4eae522b`;let b=null,x=null,y=null,k=[];function v({_o:e,mk:t,_mck:n,_rules:s}){b=e,x=t,y=n,k=s}function _(){return k}function w(){return`${f}/${b}/${x}`}function q(){return y}const E=e=>{try{return new URL(e,window.location.origin).toString()}catch{return e}};function R({url:e,method:t}){try{const t=_();if(!Array.isArray(t)||0===t.length)return null;const n=globalThis.location?.origin||"http://localhost";for(const s of t){const t=s.rule?.URL;if(!t)continue;const{condition:o}=t;if(!o)continue;let r="";switch(t.type){case"URL":{let t;try{t=new URL(e,n),r=t.pathname}catch{r=e}break}case"QUERY":{let t;try{t=new URL(e,n)}catch{t=null}t&&(r=t.searchParams.get(o.key)||"");break}case"BODY":case"HEADERS":r="";break;default:continue}let i=!1;switch(o.operator){case"contains":i=r.includes(o.value);break;case"equal":case"equals":i=r===o.value;break;case"regexp":try{let e=o.value;if(e.startsWith("/")&&e.lastIndexOf("/")>0){const t=e.lastIndexOf("/"),n=e.slice(1,t),s=e.slice(t+1);i=new RegExp(n,s).test(r)}else i=new RegExp(e).test(r)}catch{i=!1}}if(i){const e=o.activeAction;return{action:e,config:o.actions?.[e]||{}}}}return null}catch(e){return console.error("[mockforme] checkIfRulesToBeApplied error",e),null}}const T=function(e,t,s){const o=n();var r;function i(){if(4==r.readyState)if(r.status.toString().match(/^20[0-9]$/)){var e=function(){var e=r.getResponseHeader("Content-Type"),t=r.responseText;if(e){var n=e.split(";");try{return"application/json"===n[0]?JSON.parse(t):t}catch(e){throw"Unable to convert response header"}}}();t.call(this,e,r)}else s.call(this,r.responseText,r)}function a(e,t){var n=[];for(var s in e)if(e.hasOwnProperty(s)){var o=t?t+"["+s+"]":s,r=e[s];n.push("object"==typeof r?a(r,o):encodeURIComponent(o)+"="+encodeURIComponent(r))}return n.join("&")}return o||(window.XMLHttpRequest?r=new XMLHttpRequest:window.ActiveXObject&&(r=new ActiveXObject("Microsoft.XMLHTTP"))),{request:function(){if(!o){if(window.XMLHttpRequest)r.onload=i;else{if(!window.ActiveXObject)throw"unable to process ajax";r.onreadystatechange=i}var t=a(e.params);if("get"==e.method.toLowerCase()&&"object"==typeof e.params){if(-1==e.url.indexOf("?"))e.url+="?";else{var n=e.url.split("?");n[1]&&n[1].split("=")[1]&&(e.url+="&")}e.url+=t}e.hasOwnProperty("async")||(e.async=!0),r.open(e.method,e.url,e.async),e.headers&&function(e){for(let t in e)r.setRequestHeader(t,e[t])}(e.headers),r.send(t)}}}};function S({di:e,iv:t},n){var s;const o=function(e,t){const n=t.length;return e.split("").map(((e,s)=>String.fromCharCode(e.charCodeAt(0)^t.charCodeAt(s%n)))).join("")}((s=e,"undefined"!=typeof Buffer?Buffer.from(s,"base64").toString("utf8"):decodeURIComponent(escape(atob(s)))),n+t);return JSON.parse(o)}function M(e,t,n,s){fetch(g,{method:"GET",headers:{[u]:e,[m]:h,"x-mfm-adaptor":t}}).then((e=>{if(!e.ok)throw e;return e.json()})).then((e=>{if(e){const{di:t,iv:s,_o:o,_mck:r,mk:a,r:d}=e,c=S({di:t,iv:s},o);v({_o:o,mk:a,_mck:r,_rules:d}),c&&(i(c),n(c,{_o:o,mk:a,_mck:r,_rules:d}))}})).catch((e=>{console.error("Error in loading mocked APIs:",e),s(e)}))}const I=new class{constructor(e={}){n()||(this.token=e.token||("undefined"!=typeof localStorage?localStorage.getItem("mockforme-token"):null),this.isAuthenticated=!!this.token,this.onAuthSuccess=e.onAuthSuccess||(()=>{}),this.mappings=e.mappings||[],this.rules=e.rules||[],this.mockedRequests=[],this.otherRequests=[],this.state={isOpen:!1,activeTab:"mappings",selectedRequest:null,activeRequestTab:"response",lastError:null,searchQuery:""},"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>this.init())):this.init()))}init(){document.getElementById("mockforme-root")||(this.createStyles(),this.render())}createStyles(){if(document.getElementById("mockforme-styles"))return;const e=document.createElement("style");e.id="mockforme-styles",e.innerHTML="\n #mockforme-root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n z-index: 999999;\n position: fixed;\n }\n .mfm-bubble {\n position: fixed;\n bottom: 20px;\n right: 20px;\n height: 42px;\n padding: 4px 8px;\n border-radius: 24px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n background: white;\n display: flex;\n align-items: center;\n gap: 8px;\n transition: transform 0.2s;\n border: 1px solid #eee;\n }\n .mfm-bubble:hover { transform: scale(1.05); }\n .mfm-bubble img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }\n \n .mfm-refresh-icon {\n width: 25px;\n height: 25px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #2563eb;\n font-size: 25px;\n transition: background 0.2s;\n margin-right: 10px;\n background: #f3f4f6;\n padding: 8px;\n }\n .mfm-refresh-icon:hover { }\n .mfm-refresh-icon.loading { animation: mfm-spin 1s linear infinite; }\n @keyframes mfm-spin { 100% { transform: rotate(360deg); } }\n\n .mfm-bottomsheet {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background: white;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.15);\n padding: 20px;\n z-index: 999999;\n height: 80vh;\n max-width: 500px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s ease-out;\n transform: translateY(100%);\n visibility: hidden;\n overflow-y: auto;\n }\n @media (max-width: 480px) {\n .mfm-bottomsheet { padding: 16px; }\n }\n .mfm-bottomsheet.open { transform: translateY(0); visibility: visible; }\n \n .mfm-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n }\n .mfm-title-group { display: flex; align-items: center; gap: 10px; }\n .mfm-title { font-size: 18px; font-weight: 600; color: #111; }\n .mfm-delete-token-btn { \n background: #fee2e2; \n color: #dc2626; \n font-size: 13px; \n padding: 4px 12px; \n border-radius: 20px; \n cursor: pointer; \n display: flex;\n align-items: center;\n gap: 6px;\n border: none;\n font-weight: 500;\n transition: background 0.2s;\n }\n .mfm-delete-token-btn:hover { background: #fecaca; }\n .mfm-header-actions { display: flex; align-items: center; gap: 10px; }\n .mfm-close { background: none; border: none; font-size: 24px; cursor: pointer; color: #666; }\n /* Login Form */\n .mfm-logo-container { text-align: center; padding: 20px 0; width: 100%; display: flex; justify-content: center; flex-shrink: 0; }\n .mfm-logo { width: 120px; height: auto; }\n .mfm-link { color: #2563eb; text-decoration: none; font-size: 14px; margin-top: 5px; cursor: pointer; display: inline-block; }\n .mfm-link:hover { text-decoration: underline; }\n .mfm-form { display: flex; flex-direction: column; gap: 10px; }\n .mfm-input {\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n .mfm-input:focus { border-color: #2563eb; }\n .mfm-error { color: #dc2626; font-size: 13px; margin-top: -5px; }\n .mfm-btn {\n padding: 12px;\n background: #2563eb;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n .mfm-btn:hover { background: #1d4ed8; }\n .mfm-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n .mfm-tabs { display: flex; border-bottom: 1px solid #eee; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-tab {\n padding: 8px 16px;\n cursor: pointer;\n color: #666;\n border-bottom: 2px solid transparent;\n font-weight: 500;\n font-size: 14px;\n }\n .mfm-tab.active { color: #2563eb; border-bottom-color: #2563eb; }\n \n .mfm-list {\n flex: 1;\n overflow-y: auto;\n min-height: 200px;\n max-height: 60vh;\n }\n .mfm-list-item {\n padding: 10px;\n border-bottom: 1px solid #f3f4f6;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n min-height: 52px;\n box-sizing: border-box;\n }\n .mfm-list-item:hover { background: #f9fafb; }\n .mfm-method { \n font-weight: bold; \n padding: 4px 8px; \n border-radius: 4px; \n margin-right: 10px;\n font-size: 12px;\n }\n .GET { background: #dbfcfe; color: #006064; }\n .POST { background: #dcfce7; color: #14532d; }\n .PUT { background: #fef9c3; color: #713f12; }\n .DELETE { background: #fee2e2; color: #7f1d1d; }\n .PATCH { background: #f3e8ff; color: #581c87; }\n .RULE { background: #f3f4f6; color: #374151; }\n\n /* Search & Actions */\n .mfm-actions-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-search-input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; }\n .mfm-search-input:focus { border-color: #2563eb; }\n .mfm-icon-btn { background: none; border: none; font-size: 25px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 4px; }\n .mfm-icon-btn:hover { color: #333; }\n .mfm-icon-btn.refresh { color: #2563eb; padding: 0; height: 20px; width:20px; border-radius: 50%; }\n .mfm-icon-btn.refresh.loading { animation: mfm-spin 1s linear infinite; }\n .mfm-icon-btn.delete { color: #dc2626; }\n\n @media (max-height: 700px) {\n .mfm-bottomsheet { height: 85vh; padding: 12px; }\n .mfm-logo-container { padding: 10px 0; }\n .mfm-logo { width: 100px; }\n .mfm-header { margin-bottom: 10px; }\n .mfm-form { gap: 8px; }\n .mfm-input { padding: 10px; font-size: 14px; }\n .mfm-btn { padding: 10px; font-size: 14px; }\n }\n\n .mfm-url { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .mfm-status { font-weight: bold; margin-left: 10px; }\n .status-2xx { color: #16a34a; }\n .status-4xx { color: #dc2626; }\n .status-5xx { color: #ea580c; }\n .status-aborted, .status-timeout, .status-error { color: #dc2626; font-size: 11px; font-weight: 500; }\n\n .mfm-rule-tag {\n font-size: 9px;\n padding: 2px 4px;\n border-radius: 4px;\n background: #fef3c7;\n color: #92400e;\n border: 1px solid #fde68a;\n margin-right: 6px;\n text-transform: uppercase;\n font-weight: bold;\n }\n\n .mfm-detail-view { display: flex; flex-direction: column; height: 100%; }\n .mfm-detail-actions { margin-bottom: 10px; }\n .mfm-back-btn { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 0;}\n \n .mfm-code-block {\n background: #f8fafc;\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n font-family: monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-break: break-all;\n max-height: 300px;\n border: 1px solid #e2e8f0;\n }\n .section-title { font-size: 14px; font-weight: 600; margin: 10px 0 5px; color: #333; }\n\n /* Snackbar */\n .mfm-snackbar {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: #333;\n color: white;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n z-index: 1000000;\n opacity: 0;\n transition: opacity 0.3s, transform 0.3s;\n pointer-events: none;\n }\n .mfm-snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n /* Confirm Dialog */\n .mfm-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000001;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n .mfm-modal-overlay.show { opacity: 1; pointer-events: auto; }\n .mfm-modal {\n background: white;\n padding: 20px;\n border-radius: 12px;\n width: 300px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n transform: scale(0.9);\n transition: transform 0.2s;\n }\n .mfm-modal-overlay.show .mfm-modal { transform: scale(1); }\n .mfm-modal-title { font-size: 16px; font-weight: 600; margin-bottom: 10px; }\n .mfm-modal-body { font-size: 14px; color: #666; margin-bottom: 20px; }\n .mfm-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }\n .mfm-btn-alt { background: #f3f4f6; color: #333; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n .mfm-btn-danger { background: #dc2626; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n \n .mfm-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2563eb;\n border-radius: 50%;\n animation: mfm-spin 1s linear infinite;\n margin-right: 8px;\n display: inline-block;\n }\n .status-aborted { color: #dc2626; font-weight: bold; }\n .mfm-duration { color: #666; font-size: 11px; margin-left: 8px; font-weight: normal; }\n ",document.head.appendChild(e)}render(){const e=document.createElement("div");e.id="mockforme-root",document.body.appendChild(e),this.root=e,this.bubble=this.renderBubble(),this.root.appendChild(this.bubble),this.sheet=document.createElement("div"),this.sheet.className="mfm-bottomsheet",this.root.appendChild(this.sheet),this.snackbar=document.createElement("div"),this.snackbar.className="mfm-snackbar",this.root.appendChild(this.snackbar),this.modalOverlay=document.createElement("div"),this.modalOverlay.className="mfm-modal-overlay",this.root.appendChild(this.modalOverlay),this.updateUI()}updateUI(){this.state.isOpen?(this.renderBottomSheetContent(),this.sheet.classList.add("open")):this.sheet.classList.remove("open")}renderBubble(){const e=document.createElement("div");e.className="mfm-bubble";const t="undefined"!=typeof localStorage?JSON.parse(localStorage.getItem("mockforme-dot-pos")||"{}"):{};return t.bottom&&(e.style.bottom=t.bottom+"px"),t.right&&(e.style.right=t.right+"px"),e.innerHTML='\n <div class="mfm-refresh-icon" title="Reload Mappings">↻</div>\n <img src="https://ik.imagekit.io/mfm/static-collection/mfm-48x48.png" alt="MFM" />\n ',this.addDragLogic(e),e.querySelector("img").onclick=e=>{this.isDragging||this.toggleSheet()},e.querySelector(".mfm-refresh-icon").onclick=e=>{e.stopPropagation(),this.refreshMappings()},e}addDragLogic(e){let t,n,s,o,r=!1;const i=(i,a)=>{r=!1,this.isDragging=!1,t=i,n=a;const d=e.getBoundingClientRect();s=window.innerWidth-d.right,o=window.innerHeight-d.bottom},a=(i,a)=>{const d=t-i,c=n-a;(Math.abs(d)>3||Math.abs(c)>3)&&(r=!0,this.isDragging=!0),r&&(e.style.right=s+d+"px",e.style.bottom=o+c+"px")},d=()=>{r&&"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-dot-pos",JSON.stringify({bottom:parseInt(e.style.bottom),right:parseInt(e.style.right)}))},c=e=>{a(e.clientX,e.clientY)},l=()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",l),d()},m=e=>{const t=e.touches[0];a(t.clientX,t.clientY),r&&e.cancelable&&e.preventDefault()},u=()=>{document.removeEventListener("touchmove",m),document.removeEventListener("touchend",u),d()};e.addEventListener("mousedown",(e=>{i(e.clientX,e.clientY),document.addEventListener("mousemove",c),document.addEventListener("mouseup",l)})),e.addEventListener("touchstart",(e=>{const t=e.touches[0];i(t.clientX,t.clientY),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",u)}),{passive:!0})}toggleSheet(){this.state.isOpen=!this.state.isOpen,this.updateUI()}showSnackbar(e){this.snackbar.textContent=e,this.snackbar.classList.add("show"),setTimeout((()=>this.snackbar.classList.remove("show")),3e3)}showConfirm(e,t,n){this.modalOverlay.innerHTML=`\n <div class="mfm-modal">\n <div class="mfm-modal-title">${e}</div>\n <div class="mfm-modal-body">${t}</div>\n <div class="mfm-modal-actions">\n <button class="mfm-btn-alt" id="mfm-confirm-cancel">Cancel</button>\n <button class="mfm-btn-danger" id="mfm-confirm-yes">Delete</button>\n </div>\n </div>\n `,this.modalOverlay.classList.add("show"),this.modalOverlay.querySelector("#mfm-confirm-cancel").onclick=()=>{this.modalOverlay.classList.remove("show")},this.modalOverlay.querySelector("#mfm-confirm-yes").onclick=()=>{this.modalOverlay.classList.remove("show"),n()}}refreshMappings(){if(!this.token)return void this.toggleSheet();const e=document.querySelectorAll(".mfm-refresh-icon, #mfm-refresh-mappings-btn");e.forEach((e=>e.classList.add("loading"))),M(this.token,"JAVASCRIPT",((t,n)=>{console.log("[MockForMe] Mappings reloaded",{mappings:t?.length,rules:n?._rules?.length}),e.forEach((e=>e.classList.remove("loading"))),this.mappings=t||[],this.rules=n?._rules||[],this.onAuthSuccess(this.token,this.mappings,n),this.updateUI(),this.showSnackbar("Mappings reloaded!")}),(t=>{e.forEach((e=>e.classList.remove("loading"))),this.showSnackbar("Failed to reload mappings")}))}renderBottomSheetContent(){this.sheet.innerHTML="",this.isAuthenticated?this.state.selectedRequest?this.sheet.appendChild(this.renderRequestDetail(this.state.selectedRequest)):this.sheet.appendChild(this.renderDashboard()):this.sheet.appendChild(this.renderTokenForm())}renderTokenForm(){const e=document.createElement("div");e.innerHTML=`\n <div class="mfm-header">\n <span class="mfm-title">Save Token</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-logo-container">\n <img src="https://dashboard.mockforme.com/public/assets/images/logo.png" class="mfm-logo" alt="MockForMe Logo" />\n </div>\n <div class="mfm-form">\n <input type="text" class="mfm-input" placeholder="Enter MockForMe Access Token" id="mfm-token-input">\n <div class="mfm-error" id="mfm-error-msg" style="display: ${this.state.lastError?"block":"none"}">${this.state.lastError||""}</div>\n <a href="https://dashboard.mockforme.com/user/token" target="_blank" class="mfm-link">Get Access Token?</a>\n <button class="mfm-btn" id="mfm-save-token-btn">Save</button>\n </div>\n `,e.querySelector(".mfm-close").onclick=()=>{this.state.lastError=null,this.toggleSheet()};const t=e.querySelector("#mfm-save-token-btn"),n=e.querySelector("#mfm-token-input");return t.onclick=()=>{const s=n.value.trim();s&&(t.textContent="Validating...",t.disabled=!0,this.state.lastError=null,e.querySelector("#mfm-error-msg").style.display="none",M(s,c,((e,t)=>{"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-token",s),this.token=s,this.isAuthenticated=!0,this.mappings=e,this.rules=t?._rules||[],this.onAuthSuccess(s,e,t),this.updateUI()}),(e=>{t.textContent="Save",t.disabled=!1;let n="Invalid Token or Network Error";e&&"function"==typeof e.json?e.json().then((e=>{this.state.lastError=e.message||n,this.updateUI()})).catch((()=>{this.state.lastError=n,this.updateUI()})):(this.state.lastError=e.message||n,this.updateUI())})))},e}deleteToken(){"undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),this.token=null,this.isAuthenticated=!1,this.mappings=[],this.rules=[],this.state.lastError=null,this.updateUI()}renderDashboard(){const e=document.createElement("div");e.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">MockForMe</span>\n <div class="mfm-header-actions">\n <button class="mfm-delete-token-btn" id="mfm-delete-token" title="Delete Token">\n <span>🗑</span>\n <span>Token</span>\n </button>\n <button class="mfm-close">×</button>\n </div>\n </div>\n ';const t=document.createElement("div");t.className="mfm-tabs",["mappings","mocked","other"].forEach((e=>{const n=document.createElement("div");n.className="mfm-tab "+(this.state.activeTab===e?"active":""),n.textContent="mappings"===e?"Api Mappings":"mocked"===e?"Mocked Apis":"Other Apis",n.onclick=()=>{this.state.activeTab=e,this.updateUI()},t.appendChild(n)})),e.querySelector(".mfm-header").after(t);const n=document.createElement("div");n.className="mfm-actions-bar","mappings"===this.state.activeTab?(n.innerHTML=`\n <div style="flex: 1; font-size: 13px; color: #666;">Total: ${this.mappings.length+this.rules.length}</div>\n <div class="mfm-icon-btn refresh" id="mfm-refresh-mappings-btn" title="Refresh Mappings">↻</div>\n `,n.querySelector("#mfm-refresh-mappings-btn").onclick=()=>this.refreshMappings()):(n.innerHTML=`\n <input type="text" class="mfm-search-input" placeholder="Search requests..." id="mfm-search-input" value="${this.state.searchQuery}">\n <button class="mfm-icon-btn delete" id="mfm-clear-requests-btn" title="Clear List">🚫</button>\n `,n.querySelector("#mfm-search-input").oninput=e=>{this.state.searchQuery=e.target.value,this.updateRequestList(s)},n.querySelector("#mfm-clear-requests-btn").onclick=()=>{"mocked"===this.state.activeTab?this.mockedRequests=[]:this.otherRequests=[],this.updateUI()}),t.after(n);const s=document.createElement("div");return s.className="mfm-list",this.updateRequestList(s),e.appendChild(s),e.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),e.querySelector("#mfm-delete-token").onclick=()=>{this.showConfirm("Delete Token","Are you sure you want to Delete Token? This will clear your saved token.",(()=>{this.deleteToken()}))},e}updateRequestList(e){if(e.innerHTML="","mappings"===this.state.activeTab){const t=[...this.mappings.map((e=>({...e,type:"mapping"}))),...this.rules.map((e=>({...e,type:"rule"})))];0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");if(n.className="mfm-list-item","mapping"===t.type){const e=(t.apiMethod||"GET").toUpperCase();n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method ${e}">${e}</span><span class="mfm-url">${t.apiEndpoint}</span></div>`}else{const e=t.ruleName||"Network Rule",s=(t.rule?.URL?.condition||{}).value||e;n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method RULE">RULE</span><span class="mfm-url">${s}</span></div>`}e.appendChild(n)}))}else{let t="mocked"===this.state.activeTab?this.mockedRequests:this.otherRequests;if(this.state.searchQuery){const e=this.state.searchQuery.toLowerCase();t=t.filter((t=>(t.url||"").toLowerCase().includes(e)||(t.method||"").toLowerCase().includes(e)||String(t.status).includes(e)))}0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");n.className="mfm-list-item";const s=(t.method||"").toUpperCase(),o=`<span class="mfm-duration" style="color:#2563eb; font-size:11px; font-weight:500; display:block; height:14px; line-height:14px;">${void 0!==t.duration?t.duration+"ms":""}</span>`,r=(()=>{if(!t.rule)return"";let e="delay_and_timeout"===t.rule.action?"Delay and Timout":t.rule.action.split("_")[0];const n=t.rule.config?.value;return void 0!==n&&(t.rule.action.includes("delay")||t.rule.action.includes("timeout"))&&(e+=` (${n}s)`),`<span class="mfm-rule-tag" title="${t.rule.action}">${e}</span>`})();let i=t.status,a="mfm-status";t.pending?i='<span class="mfm-spinner"></span>':t.aborted?(i="Aborted",a+=" status-aborted"):t.timeout?(i="Timeout",a+=" status-timeout"):t.error?(i="Error/CORS",a+=" status-error"):(i=t.status,a+=t.status>=200&&t.status<300?" status-2xx":" status-4xx"),n.innerHTML=`\n <div style="display:flex; align-items:center; width:100%; gap:8px;">\n <span class="mfm-method ${s}">${s}</span>\n <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; justify-content:center;">\n <span class="mfm-url">${r}${t.url}</span>\n ${o}\n </div>\n <span class="${a}">${i}</span>\n </div>\n `,n.onclick=()=>{this.state.selectedRequest=t,this.updateUI()},e.appendChild(n)}))}}renderRequestDetail(e){const t=document.createElement("div");t.className="mfm-detail-view",t.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">Details</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-detail-actions">\n <button class="mfm-back-btn">← Back</button>\n </div>\n ';const n=document.createElement("div");n.className="mfm-tabs",["request","response"].forEach((e=>{const t=document.createElement("div");t.className="mfm-tab "+(this.state.activeRequestTab===e?"active":""),t.textContent=e.charAt(0).toUpperCase()+e.slice(1),t.onclick=()=>{this.state.activeRequestTab=e,this.updateUI()},n.appendChild(t)}));const s=document.createElement("div");s.style.overflowY="auto",s.style.flex="1";const o=(e.method||"").toUpperCase();return s.innerHTML="request"===this.state.activeRequestTab?`\n <div class="section-title">URL</div><div class="mfm-code-block">${o} ${e.url}</div>\n ${e.rule?`<div class="section-title">Matched Rule</div><div class="mfm-code-block">${e.rule.action} (value: ${e.rule.config?.value})</div>`:""}\n <div class="section-title">Headers</div><div class="mfm-code-block">${JSON.stringify(e.requestHeaders||{},null,2)}</div>\n <div class="section-title">Query Params</div><div class="mfm-code-block">${JSON.stringify(e.queryParams||{},null,2)}</div>\n <div class="section-title">Payload</div><div class="mfm-code-block">${this.formatBody(e.requestBody)}</div>\n `:`\n <div class="section-title">Status</div><div class="mfm-code-block">${e.status}</div>\n <div class="section-title">Response Headers</div><div class="mfm-code-block">${JSON.stringify(e.responseHeaders||{},null,2)}</div>\n <div class="section-title">Response Body</div><div class="mfm-code-block">${this.formatBody(e.responseBody)}</div>\n `,t.appendChild(n),t.appendChild(s),t.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),t.querySelector(".mfm-back-btn").onclick=()=>{this.state.selectedRequest=null,this.updateUI()},t}formatBody(e){if(!e)return"";if("undefined"!=typeof FormData&&e instanceof FormData){const t={};return e.forEach(((e,n)=>{"undefined"!=typeof File&&e instanceof File?t[n]=`[File: ${e.name} (${e.size} bytes)]`:"undefined"!=typeof Blob&&e instanceof Blob?t[n]=`[Blob: ${e.type} (${e.size} bytes)]`:t[n]=e})),JSON.stringify(t,null,2)}if("undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams)return JSON.stringify(Object.fromEntries(e.entries()),null,2);if("undefined"!=typeof Blob&&e instanceof Blob)return`[${"undefined"!=typeof File&&e instanceof File?"File":"Blob"}: ${e.type||"binary"} (${e.size} bytes)]`;if(e instanceof ArrayBuffer||"undefined"!=typeof Uint8Array&&e instanceof Uint8Array)return`[Binary Data: ${e.byteLength||e.length} bytes]`;if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}addRequest(e,t){const n=String(t.method||"").toUpperCase().trim(),s=String(t.url||"");console.log(`[MockForMe] addRequest type=${e} method=${n} url=${s} requestId=${t.requestId} pending=${t.pending}`);const o=s.includes("api.mockforme.com")||s.includes("mockforme.com/proxy")||s.includes("mockforme.com/gateway")||f&&s.includes(String(f));if("OPTIONS"===n||o)return;const r="mocked"===e?this.mockedRequests:this.otherRequests,i="mocked"===e?this.otherRequests:this.mockedRequests;if(t.requestId){const n=r.findIndex((e=>e.requestId===t.requestId));if(-1!==n)return r[n]={...r[n],...t},void(this.state.isOpen&&this.state.activeTab===e&&this.updateUI());const s=i.findIndex((e=>e.requestId===t.requestId));if(-1!==s){const e={...i[s],...t};return i.splice(s,1),r.unshift(e),r.length>50&&r.pop(),void(this.state.isOpen&&this.updateUI())}}r.unshift(t),r.length>50&&r.pop(),this.state.isOpen&&this.state.activeTab===e&&this.updateUI()}};function L(e,t){if("undefined"!=typeof window){if(window._mfm_intercepted)return;window._mfm_intercepted=!0}!function(e,t){const n=window.fetch;window.fetch=function(s,o={}){const r="string"==typeof s?s:s.url,i=String(o.method||(s instanceof Request?s.method:"GET")).toUpperCase().trim();if("OPTIONS"===i)return console.log(`[MockForMe] Filtered ${i} ${r} (isInternal=false)`),n(s,o);const a={url:r,method:i,requestHeaders:o.headers||(s instanceof Request?s.headers:{}),requestBody:o.body||(s instanceof Request?"{binary/stream}":null),queryParams:$(r)};try{const c=new URL(r,window.location.origin),{pathname:p,search:f}=c,g=R({url:r,method:i}),b=Math.random().toString(36).substring(7),x=Date.now();if(t!==l&&g){if(I.addRequest("mocked",{...a,requestId:b,pending:!0,startTime:x,rule:g}),"delay"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(s,o);t.then((e=>{const t=Date.now()-x;I.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;I.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_redirect"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(g.config.redirectUrl,o);t.then((e=>{const t=Date.now()-x;I.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;I.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_timeout"===g.action){const e=Number(g.config.value||0);if(0===e){const e=new AbortController,t=n(s,{...o,signal:e.signal});e.abort();const r=Date.now()-x;return I.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:r,aborted:!0}),t.catch((()=>Promise.reject(new Error("Fetch aborted by rules"))))}return new Promise(((t,n)=>{setTimeout((()=>{const e=Date.now()-x;I.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:e,aborted:!0}),n(new Error("Fetch timed out by rules"))}),1e3*e)}))}}const y=d(p,i),k=I.token||e;if(!y&&!g)return I.addRequest("other",{...a,requestId:b,pending:!0,startTime:x}),n(s,o).then((async e=>{const t=e.clone(),n=Date.now()-x;try{const s=await t.text();I.addRequest("other",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:s,pending:!1,duration:n})}catch(e){}return e})).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name||e.message.includes("aborted"),s="TimeoutError"===e.name||e.message.includes("timed out"),o=!n&&!s;throw I.addRequest("other",{...a,requestId:b,status:n?"Aborted":s?"Timeout":"Error",responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n,timeout:s,error:o}),e}));let v=r;g||(v=`${w()}${y.url}${f}`);let _={};if(!g){_={[u]:k,[m]:h,"x-mfm-adaptor":t};const e=q();e&&y._ack&&(_["x-mfm-key"]=`${e}-${y._ack}`),y?._ri_&&(_._ri_=y._ri_)}const T=s instanceof Request?s.headers:o.headers,S=new Headers(T||{});Object.entries(_).forEach((([e,t])=>{S.set(e,t)}));let M=o.body;if(!M&&s instanceof Request){const e=s.clone();"GET"!==e.method&&"HEAD"!==e.method&&(M=e.text())}let L={method:i,headers:S,credentials:"include",mode:"cors"};g&&(L.credentials=s instanceof Request&&s.credentials||o.credentials||"same-origin",L.mode=s instanceof Request&&s.mode||o.mode||"cors");const $=e=>(e&&(L.body=e),t===l?(I.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:x}),(({actualUrl:e,url:t,method:n,headers:s,body:o,credentials:r,mode:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);return new Promise(((t,l)=>{window.addEventListener("message",(function e(n){if(n.source!==window)return;const s=n.data;if("INTERCEPTED_RESPONSE"===s?.type&&s?.requestId===d){if(window.removeEventListener("message",e),s?.response?.error)return l(new Error(s.response.message));const{status:n,headers:o,body:r}=s.response;t(new Response(r,{status:n,headers:o}))}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:s,body:o,credentials:r,mode:i,rule:a}},"*")}))})({actualUrl:r,url:v,method:i,headers:Object.fromEntries(S.entries()),body:e,credentials:L.credentials,mode:L.mode,rule:g}).then((e=>(e.clone().text().then((t=>{const n=Date.now()-x;I.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:t,pending:!1,duration:n})})),e))).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name;throw I.addRequest("mocked",{...a,requestId:b,status:n?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n}),e}))):(I.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:x}),n(v,L).then((e=>{const t=e.clone(),n=t.headers.get("content-type")||"",s=Date.now()-x,o=n=>(I.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(t.headers.entries()),responseBody:n,pending:!1,duration:s}),new Response("string"==typeof n?n:JSON.stringify(n),{status:e.status,headers:t.headers}));return n.includes("application/json")?t.json().then(o):t.text().then(o)})).catch((e=>{console.warn("mockforme fetch interceptor error",e);const t=Date.now()-x,r="AbortError"===e.name;return I.addRequest("mocked",{...a,requestId:b,status:r?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:r}),n(s,o)}))));return M instanceof Promise?M.then((e=>$(e))):$(M)}catch(e){return console.warn("mockforme fetch interceptor error",e),n(s,o)}}}(e,t),function(e,t){const n=window.XMLHttpRequest;window.XMLHttpRequest=class{constructor(){return this._xhr=new n,this._headers={},this._method=null,this._url=null,this._body=null,this._isMocked=!1,this._captured=!1,this._requestId=null,this._startTime=null,this._capture=(e,t,n,s={})=>{if(this._captured)return;this._captured=!0;const o=this._isMocked?"mocked":"other",r=this._startTime?Date.now()-this._startTime:0;console.log(`[MockForMe] XHR Capture: status=${e} method=${this._method} url=${this._url} duration=${r}ms`);const i=e=>{const t=`on${e}`;(this[t]||this._xhr[t])?.(),this._xhr.dispatchEvent(new Event(e))};if(I.addRequest(o,{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,status:e,responseBody:t,responseHeaders:n,pending:!1,duration:r,...s}),"Aborted"!==e)try{Object.defineProperty(this,"responseText",{value:t,configurable:!0}),Object.defineProperty(this,"response",{value:t,configurable:!0}),Object.defineProperty(this,"status",{value:Number(e)||0,configurable:!0}),Object.defineProperty(this,"statusText",{value:200==e?"OK":"",configurable:!0})}catch(e){console.warn("[MockForMe] Error setting XHR props",e)}Object.defineProperty(this,"readyState",{value:4,configurable:!0}),i("readystatechange"),"Aborted"===e||s.aborted?i("abort"):0===e||"Error"===e||s.error?i("error"):"Timeout"===e||s.timeout?i("timeout"):i("load")},new Proxy(this,{get:(e,t)=>{if(t in e)return e[t];const n=e._xhr[t];return"function"==typeof n?n.bind(e._xhr):n},set:(e,t,n)=>t in e?(e[t]=n,!0):(e._xhr[t]=n,!0)})}open(e,t,n=!0,s=null,o=null){this._method=(e||"").toUpperCase().trim();try{this._url=new URL(t,window.location.href).toString()}catch(e){this._url=t}return console.log(`[MockForMe] XHR.open: ${this._method} ${this._url}`),this._xhr._mockOriginalUrl=this._url,this._xhr._mockMethod=this._method,this._xhr.open(e,t,n,s,o)}setRequestHeader(e,t){return this._headers[e]=t,this._xhr.setRequestHeader(e,t)}send(s){const o=String(this._method||"").toUpperCase().trim();if(console.log(`[MockForMe] XHR send method=${o} url=${this._url}`),"OPTIONS"===o)return console.log("[MockForMe] XHR Skip OPTIONS"),this._xhr.send(s);this._body=s,this._requestId=Math.random().toString(36).substring(7),this._startTime=Date.now(),this._xhr.addEventListener("abort",(()=>{this._capture("Aborted","Request aborted",{},{aborted:!0})}),{once:!0}),this._xhr.addEventListener("error",(()=>{this._capture(0,"Network Error",{},{error:!0})}),{once:!0}),this._xhr.addEventListener("timeout",(()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})}),{once:!0});try{const o=new URL(this._url),{pathname:r,search:i}=o,a=R({url:this._url,method:this._method});a&&(this._isMocked=!0,I.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime,rule:a}));const c=I.token||e;if(t!==l&&a){if("delay"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_redirect"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.open(this._method,a.config.redirectUrl,!0),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_timeout"===a.action){const e=Number(a.config.value||0);return 0===e?(this._capture("Aborted","Request aborted by rules",{},{aborted:!0}),void this._xhr.abort()):setTimeout((()=>{this._capture("Aborted","Request timed out by rules",{},{aborted:!0}),this._xhr.abort()}),1e3*e)}}const p=d(r,this._method);if(!p&&!a)return console.log("[MockForMe] XHR No Match:",{pathname:r,method:this._method}),this._isMocked=!1,I.addRequest("other",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(s);console.log("[MockForMe] XHR Matched:",{pathname:r,match:p}),this._isMocked=!0;let f=this._url;a||(f=`${w()}${p.url}${i}`);let g={};if(!a){g={[u]:c,[m]:h,"x-mfm-adaptor":t};const e=q();e&&p._ack&&(g["x-mfm-key"]=`${e}-${p._ack}`),p?._ri_&&(g._ri_=p._ri_)}const b={...this._headers,...g};if(I.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:$(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),t===l)(({actualUrl:e,url:t,method:n,headers:s,body:o,onSuccess:r,onError:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);window.addEventListener("message",(function e(t){if(t.source!==window)return;const n=t.data;if("INTERCEPTED_RESPONSE"===n.type&&n.requestId===d){if(window.removeEventListener("message",e),n.response?.error)return i?.(n.response.message);const{body:t,status:s,headers:o}=n.response;r?.({body:t,status:s,headers:o})}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:s,body:o,credentials:"same-origin",mode:"cors",rule:a}},"*")})({actualUrl:this._url,url:f,method:this._method,headers:b,body:this._body,rule:a,onSuccess:({body:e,status:t,headers:n})=>{this._capture(t,e,n||{})},onError:e=>{this._capture(0,e.message||"XHR Error",{},{error:!0})}});else{const e=new n;e.open(this._method,f,!0);for(const t in b)e.setRequestHeader(t,b[t]);e.withCredentials=this.withCredentials,e.onload=()=>{this._capture(e.status,e.responseText,e.getAllResponseHeaders())},e.onerror=()=>{this._capture(0,"Network Error",{},{error:!0})},e.ontimeout=()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})},e.send(this._body)}}catch(e){if(console.error("[MockForMe] XHR Interception Error",e),!this._xhr.readyState||1===this._xhr.readyState)return this._xhr.send(s)}}}}(e,t)}function $(e){try{const t=new URL(e,window.location.origin).searchParams;return Object.fromEntries(t.entries())}catch{return{}}}const O=(e,t=null,s=c)=>{if(n())return;if(s===c)try{e&&("undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),I.token=e,I.isAuthenticated=!0),!e&&I.token&&(e=I.token),I.onAuthSuccess=(e,t,n)=>{i(t),v(n),I.mappings=t,I.rules=n?._rules||[]}}catch(e){console.log("Error in showing mockforme widget",e)}t||={mappings:[],_o:null,mk:null,_mck:null,_rules:[]};const{mappings:o=[],_o:r,mk:l,_mck:p,_rules:f=[]}=t;return i(o),v({_o:r,mk:l,_mck:p,_rules:f}),{run:(t,n=()=>{})=>{L(e,s),r&&l?(I.mappings=o,I.rules=f,t?.(o,{_o:r,mk:l,_mck:p,_rules:f})):e&&function(e,n,s,o){T({method:"get",url:g,async:!1,headers:{[u]:e,[m]:h,"x-mfm-adaptor":n}},(e=>{if(e)try{const{di:o,iv:r,_o:a,_mck:d,mk:c,r:l}=e,m=S({di:o,iv:r},a);v({_o:a,mk:c,_mck:d,_rules:l}),m&&(i(m),n=m,s={_o:a,mk:c,_mck:d,_rules:l},I.mappings=n,I.rules=s?._rules||[],t?.(n,s))}catch(e){return void o(new Error("Unable to fetch mocked apis"))}var n,s}),(e=>{console.log("Error in loading mocked apis"),o(e)})).request()}(e,s,0,n)},checkIfApiToBeMocked:d,getMappings:a,doFetchMappings:(t,n)=>{M(e||I.token,s,((e,n)=>{I.mappings=e,I.rules=n?._rules||[],t?.(e,n)}),n)}}},C=n();C||(globalThis.mockforme=O);let H=O;C&&(H=e=>({run:()=>{}}));const U=H;return t.mockforme})()));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.mockforme=t():e.mockforme=t()}(this,(()=>(()=>{"use strict";var e={d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{mockforme:()=>U});const n=()=>"undefined"!=typeof process&&null!=process.versions?.node,o=e=>{try{return e?e.toLowerCase():e}catch(e){return console.log(e),null}},s=(e,t)=>{const n=e.split("/"),o=t.split("/");if(n.length!==o.length)return!1;for(let e=0;e<n.length;e++){const t=n[e];if(t!==o[e]&&":any"!==t)return!1}return!0};let r=[];function i(e){r=e}function a(){return r}function d(e,t="get"){const n=e=>e.startsWith("/")?e:`/${e}`,i=n(e);for(const a of r)if(o(a.apiMethod)===o(t)){const o=n(a.apiEndpoint);if(o===i||s(o,i)){console.log(`[MockForMe] Match Found: ${t} ${e} -> ${a.apiEndpoint}`);const n={url:e,method:a.apiMethod,_ack:a._ack||null};return a._ri_&&(n._ri_=a._ri_),n}}return null}const c="JAVASCRIPT",l="CHROME_EXTENSION",m="mfmver",u="mockforme",p="4.2.8",h="https://api.mockforme.com",f=`${h}/mockforme`,g=`${h}/gateway/3f4eae522b`;let b=null,x=null,y=null,v=[];function k({_o:e,mk:t,_mck:n,_rules:o}){b=e,x=t,y=n,v=o}function w(){return v}function _(){return`${f}/${b}/${x}`}function q(){return y}const E=e=>{try{return new URL(e,window.location.origin).toString()}catch{return e}};function R({url:e,method:t}){try{const t=w();if(!Array.isArray(t)||0===t.length)return null;const n=globalThis.location?.origin||"http://localhost";for(const o of t){const t=o.rule?.URL;if(!t)continue;const{condition:s}=t;if(!s)continue;let r="";switch(t.type){case"URL":{let t;try{t=new URL(e,n),r=t.pathname}catch{r=e}break}case"QUERY":{let t;try{t=new URL(e,n)}catch{t=null}t&&(r=t.searchParams.get(s.key)||"");break}case"BODY":case"HEADERS":r="";break;default:continue}let i=!1;switch(s.operator){case"contains":i=r.includes(s.value);break;case"equal":case"equals":i=r===s.value;break;case"regexp":try{let e=s.value;if(e.startsWith("/")&&e.lastIndexOf("/")>0){const t=e.lastIndexOf("/"),n=e.slice(1,t),o=e.slice(t+1);i=new RegExp(n,o).test(r)}else i=new RegExp(e).test(r)}catch{i=!1}}if(i){const e=s.activeAction;return{action:e,config:s.actions?.[e]||{}}}}return null}catch(e){return console.error("[mockforme] checkIfRulesToBeApplied error",e),null}}const T=function(e,t,o){const s=n();var r;function i(){if(4==r.readyState)if(r.status.toString().match(/^20[0-9]$/)){var e=function(){var e=r.getResponseHeader("Content-Type"),t=r.responseText;if(e){var n=e.split(";");try{return"application/json"===n[0]?JSON.parse(t):t}catch(e){throw"Unable to convert response header"}}}();t.call(this,e,r)}else o.call(this,r.responseText,r)}function a(e,t){var n=[];for(var o in e)if(e.hasOwnProperty(o)){var s=t?t+"["+o+"]":o,r=e[o];n.push("object"==typeof r?a(r,s):encodeURIComponent(s)+"="+encodeURIComponent(r))}return n.join("&")}return s||(window.XMLHttpRequest?r=new XMLHttpRequest:window.ActiveXObject&&(r=new ActiveXObject("Microsoft.XMLHTTP"))),{request:function(){if(!s){if(window.XMLHttpRequest)r.onload=i;else{if(!window.ActiveXObject)throw"unable to process ajax";r.onreadystatechange=i}var t=a(e.params);if("get"==e.method.toLowerCase()&&"object"==typeof e.params){if(-1==e.url.indexOf("?"))e.url+="?";else{var n=e.url.split("?");n[1]&&n[1].split("=")[1]&&(e.url+="&")}e.url+=t}e.hasOwnProperty("async")||(e.async=!0),r.open(e.method,e.url,e.async),e.headers&&function(e){for(let t in e)r.setRequestHeader(t,e[t])}(e.headers),r.send(t)}}}};function S({di:e,iv:t},n){var o;const s=function(e,t){const n=t.length;return e.split("").map(((e,o)=>String.fromCharCode(e.charCodeAt(0)^t.charCodeAt(o%n)))).join("")}((o=e,"undefined"!=typeof Buffer?Buffer.from(o,"base64").toString("utf8"):decodeURIComponent(escape(atob(o)))),n+t);return JSON.parse(s)}function I(e,t,n,o){fetch(g,{method:"GET",headers:{[u]:e,[m]:p,"x-mfm-adaptor":t}}).then((e=>{if(!e.ok)throw e;return e.json()})).then((e=>{if(e){const{di:t,iv:o,_o:s,_mck:r,mk:a,r:d}=e,c=S({di:t,iv:o},s);k({_o:s,mk:a,_mck:r,_rules:d}),c&&(i(c),n(c,{_o:s,mk:a,_mck:r,_rules:d}))}})).catch((e=>{console.error("Error in loading mocked APIs:",e),o(e)}))}const M=new class{constructor(e={}){n()||(this.token=e.token||("undefined"!=typeof localStorage?localStorage.getItem("mockforme-token"):null),this.isAuthenticated=!!this.token,this.onAuthSuccess=e.onAuthSuccess||(()=>{}),this.mappings=e.mappings||[],this.rules=e.rules||[],this.mockedRequests=[],this.otherRequests=[],this.state={isOpen:"undefined"!=typeof localStorage&&"true"===localStorage.getItem("mockforme-widget-open"),activeTab:"mappings",selectedRequest:null,activeRequestTab:"response",lastError:null,searchQuery:""},"undefined"!=typeof document&&(window.addEventListener("resize",(()=>{this.bubble&&this.keepInBounds(this.bubble)})),document.addEventListener("click",(e=>{this.sharedDropdown&&this.sharedDropdown.classList.remove("show")})),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>this.init())):this.init()))}init(){document.getElementById("mockforme-root")||(this.createStyles(),this.render())}createStyles(){if(document.getElementById("mockforme-styles"))return;const e=document.createElement("style");e.id="mockforme-styles",e.innerHTML="\n #mockforme-root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n z-index: 999999;\n position: fixed;\n }\n .mfm-bubble {\n position: fixed;\n bottom: 20px;\n right: 20px;\n height: 42px;\n padding: 4px 8px;\n border-radius: 24px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n background: white;\n display: flex;\n align-items: center;\n gap: 8px;\n transition: transform 0.2s;\n border: 1px solid #eee;\n }\n .mfm-bubble:hover { transform: scale(1.05); }\n .mfm-bubble img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }\n \n .mfm-refresh-icon {\n width: 25px;\n height: 25px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #2563eb;\n font-size: 25px;\n transition: background 0.2s;\n margin-right: 10px;\n background: #f3f4f6;\n padding: 8px;\n }\n .mfm-refresh-icon:hover { }\n .mfm-refresh-icon.loading { animation: mfm-spin 1s linear infinite; }\n @keyframes mfm-spin { 100% { transform: rotate(360deg); } }\n\n .mfm-bottomsheet {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background: white;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.15);\n padding: 20px;\n z-index: 999999;\n height: 80vh;\n max-width: 500px;\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s ease-out;\n transform: translateY(100%);\n visibility: hidden;\n overflow-y: auto;\n }\n @media (max-width: 480px) {\n .mfm-bottomsheet { padding: 16px; }\n }\n .mfm-bottomsheet.open { transform: translateY(0); visibility: visible; }\n \n .mfm-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 15px;\n padding-bottom: 10px;\n border-bottom: 1px solid #eee;\n flex-shrink: 0;\n }\n .mfm-title-group { display: flex; align-items: center; gap: 10px; }\n .mfm-title { font-size: 18px; font-weight: 600; color: #111; }\n .mfm-title img { height: 30px; }\n .mfm-delete-token-btn { \n background: #fee2e2; \n color: #dc2626; \n font-size: 13px; \n padding: 4px 12px; \n border-radius: 20px; \n cursor: pointer; \n display: flex;\n align-items: center;\n gap: 6px;\n border: none;\n font-weight: 500;\n transition: background 0.2s;\n }\n .mfm-delete-token-btn:hover { background: #fecaca; }\n .mfm-header-actions { display: flex; align-items: center; gap: 10px; }\n .mfm-close { background: none; border: none; font-size: 30px; cursor: pointer; color: #666; }\n /* Login Form */\n .mfm-logo-container { text-align: center; padding: 20px 0; width: 100%; display: flex; justify-content: center; flex-shrink: 0; }\n .mfm-logo { width: 120px; height: auto; }\n .mfm-link { color: #2563eb; text-decoration: none; font-size: 14px; margin-top: 5px; cursor: pointer; display: inline-block; }\n .mfm-link:hover { text-decoration: underline; }\n .mfm-form { display: flex; flex-direction: column; gap: 10px; }\n .mfm-input {\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n outline: none;\n transition: border-color 0.2s;\n }\n .mfm-input:focus { border-color: #2563eb; }\n .mfm-error { color: #dc2626; font-size: 13px; margin-top: -5px; }\n .mfm-btn {\n padding: 12px;\n background: #2563eb;\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n .mfm-btn:hover { background: #1d4ed8; }\n .mfm-btn:disabled { background: #9ca3af; cursor: not-allowed; }\n\n .mfm-tabs { \n display: flex; \n border-bottom: 1px solid #dee2e6; \n margin-bottom: 10px; \n flex-shrink: 0; \n gap: 2px;\n }\n .mfm-tab {\n padding: 8px 16px;\n cursor: pointer;\n color: #666;\n border: 1px solid transparent;\n border-bottom: none;\n border-top-left-radius: 6px;\n border-top-right-radius: 6px;\n font-weight: 300;\n font-size: 14px;\n margin-bottom: -1px;\n transition: all 0.2s;\n }\n .mfm-tab:hover {\n background-color: #f8f9fa;\n border-color: #e9ecef #e9ecef transparent;\n }\n .mfm-tab.active { \n color: #2563eb; \n background-color: white;\n border: 1px solid #dee2e6;\n border-bottom: 1px solid white;\n font-weight: 500;\n }\n \n .mfm-list {\n flex: 1;\n overflow-y: auto;\n min-height: 200px;\n max-height: 60vh;\n }\n .mfm-list-item {\n padding: 10px;\n border-bottom: 1px solid #f3f4f6;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 14px;\n min-height: 52px;\n box-sizing: border-box;\n }\n .mfm-list-item:hover { background: #f9fafb; }\n .mfm-method { \n font-weight: bold; \n padding: 4px 8px; \n border-radius: 4px; \n margin-right: 10px;\n font-size: 12px;\n }\n .GET { background: #dbfcfe; color: #006064; }\n .POST { background: #dcfce7; color: #14532d; }\n .PUT { background: #fef9c3; color: #713f12; }\n .DELETE { background: #fee2e2; color: #7f1d1d; }\n .PATCH { background: #f3e8ff; color: #581c87; }\n .RULE { background: #f3f4f6; color: #374151; }\n\n /* Search & Actions */\n .mfm-actions-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-shrink: 0; }\n .mfm-search-input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; outline: none; }\n .mfm-search-input:focus { border-color: #2563eb; }\n .mfm-icon-btn { background: none; border: none; font-size: 25px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; padding: 4px; border-radius: 4px; }\n .mfm-icon-btn:hover { color: #333; }\n .mfm-icon-btn.refresh { color: #2563eb; padding: 0; height: 20px; width:20px; border-radius: 50%; }\n .mfm-icon-btn.refresh.loading { animation: mfm-spin 1s linear infinite; }\n .mfm-icon-btn.delete { color: #dc2626; }\n\n @media (max-height: 700px) {\n .mfm-bottomsheet { height: 85vh; padding: 12px; }\n .mfm-logo-container { padding: 10px 0; }\n .mfm-logo { width: 100px; }\n .mfm-header { margin-bottom: 10px; }\n .mfm-form { gap: 8px; }\n .mfm-input { padding: 10px; font-size: 14px; }\n .mfm-btn { padding: 10px; font-size: 14px; }\n }\n\n .mfm-url { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .mfm-status { font-weight: bold; margin-left: 10px; }\n .status-2xx { color: #16a34a; }\n .status-4xx { color: #dc2626; }\n .status-5xx { color: #ea580c; }\n .status-aborted, .status-timeout, .status-error { color: #dc2626; font-size: 11px; font-weight: 500; }\n\n .mfm-rule-tag {\n font-size: 9px;\n padding: 2px 4px;\n border-radius: 4px;\n background: #fef3c7;\n color: #92400e;\n border: 1px solid #fde68a;\n margin-right: 6px;\n text-transform: uppercase;\n font-weight: bold;\n }\n\n .mfm-detail-view { display: flex; flex-direction: column; height: 100%; }\n .mfm-detail-actions { margin-bottom: 10px; }\n .mfm-back-btn { background: none; border: none; color: #2563eb; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 0;}\n \n .mfm-code-block {\n background: #f8fafc;\n padding: 10px;\n border-radius: 8px;\n overflow-x: auto;\n font-family: monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-break: break-all;\n max-height: 300px;\n border: 1px solid #e2e8f0;\n }\n .section-title { font-size: 14px; font-weight: 600; margin: 10px 0 5px; color: #333; }\n .section-header { display: flex; align-items: center; justify-content: space-between; margin: 10px 0 5px; }\n .mfm-copy-btn {\n background: #f1f5f9;\n color: #64748b;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 10px;\n cursor: pointer;\n border: 1px solid #e2e8f0;\n transition: all 0.2s;\n font-weight: 600;\n text-transform: uppercase;\n }\n .mfm-copy-btn:hover { background: #e2e8f0; color: #334155; }\n \n .mfm-copy-group {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 4px;\n }\n .mfm-copy-group .mfm-copy-btn {\n font-size: 9px;\n padding: 2px 6px;\n }\n\n /* Dropdown Menu */\n .mfm-menu-dots {\n cursor: pointer;\n padding: 4px 8px;\n font-size: 18px;\n color: #666;\n border-radius: 4px;\n transition: background 0.2s;\n line-height: 1;\n margin-left: 4px;\n }\n .mfm-menu-dots:hover { background: #f3f4f6; color: #333; }\n \n .mfm-dropdown-shared {\n position: fixed;\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n z-index: 1000005;\n display: none;\n flex-direction: column;\n width: 160px;\n overflow: hidden;\n }\n .mfm-dropdown-shared.show { display: flex; }\n .mfm-dropdown-item {\n padding: 10px 14px;\n font-size: 13px;\n color: #334155;\n cursor: pointer;\n transition: background 0.2s;\n text-align: left;\n border-bottom: 1px solid #f1f5f9;\n white-space: nowrap;\n }\n .mfm-dropdown-item:last-child { border-bottom: none; }\n .mfm-dropdown-item:hover { background: #f8fafc; color: #2563eb; }\n\n /* Snackbar */\n .mfm-snackbar {\n position: fixed;\n bottom: 80px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: #333;\n color: white;\n padding: 10px 20px;\n border-radius: 8px;\n font-size: 14px;\n z-index: 1000000;\n opacity: 0;\n transition: opacity 0.3s, transform 0.3s;\n pointer-events: none;\n }\n .mfm-snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); }\n\n /* Confirm Dialog */\n .mfm-modal-overlay {\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000001;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n }\n .mfm-modal-overlay.show { opacity: 1; pointer-events: auto; }\n .mfm-modal {\n background: white;\n padding: 20px;\n border-radius: 12px;\n width: 300px;\n box-shadow: 0 10px 25px rgba(0,0,0,0.2);\n transform: scale(0.9);\n transition: transform 0.2s;\n }\n .mfm-modal-overlay.show .mfm-modal { transform: scale(1); }\n .mfm-modal-title { font-size: 16px; font-weight: 600; margin-bottom: 10px; }\n .mfm-modal-body { font-size: 14px; color: #666; margin-bottom: 20px; }\n .mfm-modal-actions { display: flex; justify-content: flex-end; gap: 10px; }\n .mfm-btn-alt { background: #f3f4f6; color: #333; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n .mfm-btn-danger { background: #dc2626; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }\n \n .mfm-spinner {\n width: 12px;\n height: 12px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2563eb;\n border-radius: 50%;\n animation: mfm-spin 1s linear infinite;\n margin-right: 8px;\n display: inline-block;\n }\n .status-aborted { color: #dc2626; font-weight: bold; }\n .mfm-duration { color: #666; font-size: 11px; margin-left: 8px; font-weight: normal; }\n ",document.head.appendChild(e)}render(){const e=document.createElement("div");e.id="mockforme-root",document.body.appendChild(e),this.root=e,this.bubble=this.renderBubble(),this.root.appendChild(this.bubble),this.sheet=document.createElement("div"),this.sheet.className="mfm-bottomsheet",this.root.appendChild(this.sheet),this.snackbar=document.createElement("div"),this.snackbar.className="mfm-snackbar",this.root.appendChild(this.snackbar),this.modalOverlay=document.createElement("div"),this.modalOverlay.className="mfm-modal-overlay",this.root.appendChild(this.modalOverlay),this.sharedDropdown=document.createElement("div"),this.sharedDropdown.className="mfm-dropdown-shared",this.sharedDropdown.innerHTML='\n <div class="mfm-dropdown-item" data-format="curl">Copy as cURL</div>\n <div class="mfm-dropdown-item" data-format="fetch">Copy as Fetch</div>\n <div class="mfm-dropdown-item" data-format="xhr">Copy as XHR</div>\n ',this.root.appendChild(this.sharedDropdown),this.updateUI()}updateUI(){this.state.isOpen?(this.renderBottomSheetContent(),this.sheet.classList.add("open")):this.sheet.classList.remove("open")}renderBubble(){const e=document.createElement("div");e.className="mfm-bubble";const t="undefined"!=typeof localStorage?JSON.parse(localStorage.getItem("mockforme-dot-pos")||"{}"):{};return t.bottom&&(e.style.bottom=t.bottom+"px"),t.right&&(e.style.right=t.right+"px"),e.innerHTML='\n <div class="mfm-refresh-icon" title="Reload Mappings">↻</div>\n <img src="https://ik.imagekit.io/mfm/static-collection/mfm-48x48.png" alt="MFM" />\n ',this.addDragLogic(e),this.keepInBounds(e),e.querySelector("img").onclick=e=>{this.isDragging||this.toggleSheet()},e.querySelector(".mfm-refresh-icon").onclick=e=>{e.stopPropagation(),this.refreshMappings()},e}keepInBounds(e){const t=e.getBoundingClientRect(),n=10;let o=parseInt(e.style.right)||20,s=parseInt(e.style.bottom)||20;const r=window.innerWidth-t.width-n,i=window.innerHeight-t.height-n;o<n&&(o=n),s<n&&(s=n),o>r&&(o=r),s>i&&(s=i),e.style.right=o+"px",e.style.bottom=s+"px"}addDragLogic(e){let t,n,o,s,r=!1;const i=(i,a)=>{r=!1,this.isDragging=!1,t=i,n=a;const d=e.getBoundingClientRect();o=window.innerWidth-d.right,s=window.innerHeight-d.bottom},a=(i,a)=>{const d=t-i,c=n-a;(Math.abs(d)>3||Math.abs(c)>3)&&(r=!0,this.isDragging=!0),r&&(e.style.right=o+d+"px",e.style.bottom=s+c+"px")},d=()=>{r&&(this.keepInBounds(e),"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-dot-pos",JSON.stringify({bottom:parseInt(e.style.bottom),right:parseInt(e.style.right)})))},c=e=>{a(e.clientX,e.clientY)},l=()=>{document.removeEventListener("mousemove",c),document.removeEventListener("mouseup",l),d()},m=e=>{const t=e.touches[0];a(t.clientX,t.clientY),r&&e.cancelable&&e.preventDefault()},u=()=>{document.removeEventListener("touchmove",m),document.removeEventListener("touchend",u),d()};e.addEventListener("mousedown",(e=>{i(e.clientX,e.clientY),document.addEventListener("mousemove",c),document.addEventListener("mouseup",l)})),e.addEventListener("touchstart",(e=>{const t=e.touches[0];i(t.clientX,t.clientY),document.addEventListener("touchmove",m,{passive:!1}),document.addEventListener("touchend",u)}),{passive:!0})}toggleSheet(){this.state.isOpen=!this.state.isOpen,"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-widget-open",this.state.isOpen),this.updateUI()}showSnackbar(e){this.snackbar.textContent=e,this.snackbar.classList.add("show"),setTimeout((()=>this.snackbar.classList.remove("show")),3e3)}showConfirm(e,t,n){this.modalOverlay.innerHTML=`\n <div class="mfm-modal">\n <div class="mfm-modal-title">${e}</div>\n <div class="mfm-modal-body">${t}</div>\n <div class="mfm-modal-actions">\n <button class="mfm-btn-alt" id="mfm-confirm-cancel">Cancel</button>\n <button class="mfm-btn-danger" id="mfm-confirm-yes">Delete</button>\n </div>\n </div>\n `,this.modalOverlay.classList.add("show"),this.modalOverlay.querySelector("#mfm-confirm-cancel").onclick=()=>{this.modalOverlay.classList.remove("show")},this.modalOverlay.querySelector("#mfm-confirm-yes").onclick=()=>{this.modalOverlay.classList.remove("show"),n()}}refreshMappings(){if(!this.token)return void this.toggleSheet();const e=document.querySelectorAll(".mfm-refresh-icon, #mfm-refresh-mappings-btn");e.forEach((e=>e.classList.add("loading"))),I(this.token,"JAVASCRIPT",((t,n)=>{console.log("[MockForMe] Mappings reloaded",{mappings:t?.length,rules:n?._rules?.length}),e.forEach((e=>e.classList.remove("loading"))),this.mappings=t||[],this.rules=n?._rules||[],this.onAuthSuccess(this.token,this.mappings,n),this.updateUI(),this.showSnackbar("Mappings reloaded!")}),(t=>{e.forEach((e=>e.classList.remove("loading"))),this.showSnackbar("Failed to reload mappings")}))}renderBottomSheetContent(){this.sheet.innerHTML="",this.isAuthenticated?this.state.selectedRequest?this.sheet.appendChild(this.renderRequestDetail(this.state.selectedRequest)):this.sheet.appendChild(this.renderDashboard()):this.sheet.appendChild(this.renderTokenForm())}renderTokenForm(){const e=document.createElement("div");e.innerHTML=`\n <div class="mfm-header">\n <span class="mfm-title">Save Token</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-logo-container">\n <img src="https://dashboard.mockforme.com/public/assets/images/logo.png" class="mfm-logo" alt="MockForMe Logo" />\n </div>\n <div class="mfm-form">\n <input type="text" class="mfm-input" placeholder="Enter MockForMe Access Token" id="mfm-token-input">\n <div class="mfm-error" id="mfm-error-msg" style="display: ${this.state.lastError?"block":"none"}">${this.state.lastError||""}</div>\n <a href="https://dashboard.mockforme.com/user/token" target="_blank" class="mfm-link">Get Access Token?</a>\n <button class="mfm-btn" id="mfm-save-token-btn">Save</button>\n </div>\n `,e.querySelector(".mfm-close").onclick=()=>{this.state.lastError=null,this.toggleSheet()};const t=e.querySelector("#mfm-save-token-btn"),n=e.querySelector("#mfm-token-input");return t.onclick=()=>{const o=n.value.trim();o&&(t.textContent="Validating...",t.disabled=!0,this.state.lastError=null,e.querySelector("#mfm-error-msg").style.display="none",I(o,c,((e,t)=>{"undefined"!=typeof localStorage&&localStorage.setItem("mockforme-token",o),this.token=o,this.isAuthenticated=!0,this.mappings=e,this.rules=t?._rules||[],this.onAuthSuccess(o,e,t),this.updateUI()}),(e=>{t.textContent="Save",t.disabled=!1;let n="Invalid Token or Network Error";e&&"function"==typeof e.json?e.json().then((e=>{this.state.lastError=e.message||n,this.updateUI()})).catch((()=>{this.state.lastError=n,this.updateUI()})):(this.state.lastError=e.message||n,this.updateUI())})))},e}updateMappings(e,t){this.mappings=e||[],this.rules=t?._rules||[],this.root&&this.updateUI()}deleteToken(){"undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),this.token=null,this.isAuthenticated=!1,this.mappings=[],this.rules=[],this.state.lastError=null,this.updateUI()}renderDashboard(){const e=document.createElement("div");e.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title"><img src="https://www.mockforme.com/assets/images/logo.png" /></span>\n <div class="mfm-header-actions">\n <button class="mfm-close">×</button>\n </div>\n </div>\n ';const t=document.createElement("div");t.className="mfm-tabs",["mappings","mocked","other","settings"].forEach((e=>{const n=document.createElement("div");n.className="mfm-tab "+(this.state.activeTab===e?"active":""),n.textContent="mappings"===e?"Api Mappings":"mocked"===e?"Mocked Apis":"other"===e?"Other Apis":"Settings",n.onclick=()=>{this.state.activeTab=e,this.updateUI()},t.appendChild(n)})),e.querySelector(".mfm-header").after(t);const n=document.createElement("div");n.className="mfm-actions-bar","mappings"===this.state.activeTab?(n.innerHTML=`\n <div style="flex: 1; font-size: 13px; color: #666;">Total: ${this.mappings.length+this.rules.length}</div>\n <div class="mfm-icon-btn refresh" id="mfm-refresh-mappings-btn" title="Refresh Mappings">↻</div>\n `,n.querySelector("#mfm-refresh-mappings-btn").onclick=()=>this.refreshMappings()):"settings"!==this.state.activeTab?(n.innerHTML=`\n <input type="text" class="mfm-search-input" placeholder="Search requests..." id="mfm-search-input" value="${this.state.searchQuery}">\n <button class="mfm-icon-btn delete" id="mfm-clear-requests-btn" title="Clear List">🚫</button>\n `,n.querySelector("#mfm-search-input").oninput=e=>{this.state.searchQuery=e.target.value,this.updateRequestList(o)},n.querySelector("#mfm-clear-requests-btn").onclick=()=>{"mocked"===this.state.activeTab?this.mockedRequests=[]:this.otherRequests=[],this.updateUI()}):n.style.display="none",t.after(n);const o=document.createElement("div");return o.className="mfm-list",this.updateRequestList(o),e.appendChild(o),e.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),e}updateRequestList(e){if(e.innerHTML="","mappings"===this.state.activeTab){const t=[...this.mappings.map((e=>({...e,type:"mapping"}))),...this.rules.map((e=>({...e,type:"rule"})))];0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");if(n.className="mfm-list-item","mapping"===t.type){const e=(t.apiMethod||"GET").toUpperCase();n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method ${e}">${e}</span><span class="mfm-url">${t.apiEndpoint}</span></div>`}else{const e=t.ruleName||"Network Rule",o=(t.rule?.URL?.condition||{}).value||e;n.innerHTML=`<div style="display:flex; align-items:center; width:100%"><span class="mfm-method RULE">RULE</span><span class="mfm-url">${o}</span></div>`}e.appendChild(n)}))}else if("settings"===this.state.activeTab)e.innerHTML='\n <div style="padding: 16px;">\n <div class="section-title" style="margin-top: 0; margin-bottom: 12px; font-size: 15px; border-bottom: 1px solid #eee; padding-bottom: 8px;">Token Management</div>\n <p style="font-size: 13px; color: #666; margin-bottom: 16px;">Removing your token will clear all saved mappings and rules from this browser instance.</p>\n <button class="mfm-delete-token-btn" id="mfm-settings-delete-token" style="padding: 8px 16px; font-size: 14px; width: fit-content;">\n <span>🗑</span>\n <span>Delete Token</span>\n </button>\n </div>\n ',e.querySelector("#mfm-settings-delete-token").onclick=()=>{this.showConfirm("Delete Token","Are you sure you want to Delete Token? This will clear your saved token.",(()=>{this.deleteToken()}))};else{let t="mocked"===this.state.activeTab?this.mockedRequests:this.otherRequests;if(this.state.searchQuery){const e=this.state.searchQuery.toLowerCase();t=t.filter((t=>(t.url||"").toLowerCase().includes(e)||(t.method||"").toLowerCase().includes(e)||String(t.status).includes(e)))}0===t.length?e.innerHTML='<div style="padding: 20px; text-align: center; color: #999;">No items found</div>':t.forEach((t=>{const n=document.createElement("div");n.className="mfm-list-item";const o=(t.method||"").toUpperCase(),s=`<span class="mfm-duration" style="color:#2563eb; font-size:11px; font-weight:500; display:block; height:14px; line-height:14px;">${void 0!==t.duration?t.duration+"ms":""}</span>`,r=(()=>{if(!t.rule)return"";let e="delay_and_timeout"===t.rule.action?"Delay and Timout":t.rule.action.split("_")[0];const n=t.rule.config?.value;return void 0!==n&&(t.rule.action.includes("delay")||t.rule.action.includes("timeout"))&&(e+=` (${n}s)`),`<span class="mfm-rule-tag" title="${t.rule.action}">${e}</span>`})();let i=t.status,a="mfm-status";t.pending?i='<span class="mfm-spinner"></span>':t.aborted?(i="Aborted",a+=" status-aborted"):t.timeout?(i="Timeout",a+=" status-timeout"):t.error?(i="Error/CORS",a+=" status-error"):(i=t.status,a+=t.status>=200&&t.status<300?" status-2xx":" status-4xx"),n.innerHTML=`\n <div style="display:flex; align-items:center; width:100%; gap:8px;">\n <span class="mfm-method ${o}">${o}</span>\n <div style="flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; justify-content:center;">\n <span class="mfm-url">${r}${t.url}</span>\n ${s}\n </div>\n <span class="${a}">${i}</span>\n <span class="mfm-menu-dots" title="Copy As...">⋮</span>\n </div>\n `;const d=n.querySelector(".mfm-menu-dots");d.onclick=e=>{e.stopPropagation();const n=d.getBoundingClientRect();this.sharedDropdown.style.top=n.bottom+5+"px",this.sharedDropdown.style.left=n.right-160+"px",this.sharedDropdown.classList.toggle("show"),this.sharedDropdown.onclick=e=>{e.stopPropagation();const n=e.target.getAttribute("data-format");if(!n)return;let o="";"curl"===n?o=this.generateCurl(t):"fetch"===n?o=this.generateFetch(t):"xhr"===n&&(o=this.generateXHR(t)),o&&navigator.clipboard.writeText(o).then((()=>{this.showSnackbar(`${n.toUpperCase()} copied!`),this.sharedDropdown.classList.remove("show")}))}},n.onclick=()=>{this.state.selectedRequest=t,this.updateUI()},e.appendChild(n)}))}}renderRequestDetail(e){const t=document.createElement("div");t.className="mfm-detail-view",t.innerHTML='\n <div class="mfm-header">\n <span class="mfm-title">Details</span>\n <button class="mfm-close">×</button>\n </div>\n <div class="mfm-detail-actions">\n <button class="mfm-back-btn">← Back</button>\n </div>\n ';const n=document.createElement("div");n.className="mfm-tabs",["request","response"].forEach((e=>{const t=document.createElement("div");t.className="mfm-tab "+(this.state.activeRequestTab===e?"active":""),t.textContent=e.charAt(0).toUpperCase()+e.slice(1),t.onclick=()=>{this.state.activeRequestTab=e,this.updateUI()},n.appendChild(t)}));const o=document.createElement("div");o.style.overflowY="auto",o.style.flex="1";const s=(e.method||"").toUpperCase(),r=this.formatBody(e.requestBody),i=this.formatBody(e.responseBody);o.innerHTML="request"===this.state.activeRequestTab?`\n <div class="section-title">URL</div><div class="mfm-code-block">${s} ${e.url}</div>\n ${e.rule?`<div class="section-title">Matched Rule</div><div class="mfm-code-block">${e.rule.action} (value: ${e.rule.config?.value})</div>`:""}\n <div class="section-title">Headers</div><div class="mfm-code-block">${JSON.stringify(e.requestHeaders||{},null,2)}</div>\n <div class="section-title">Query Params</div><div class="mfm-code-block">${JSON.stringify(e.queryParams||{},null,2)}</div>\n <div class="section-header">\n <div class="section-title" style="margin: 0;">Payload</div>\n <div class="mfm-copy-btn" id="mfm-copy-payload">Copy</div>\n </div>\n <div class="mfm-code-block">${r}</div>\n `:`\n <div class="section-title">Status</div><div class="mfm-code-block">${e.status}</div>\n <div class="section-title">Response Headers</div><div class="mfm-code-block">${JSON.stringify(e.responseHeaders||{},null,2)}</div>\n <div class="section-header">\n <div class="section-title" style="margin: 0;">Response Body</div>\n <div class="mfm-copy-btn" id="mfm-copy-response">Copy</div>\n </div>\n <div class="mfm-code-block">${i}</div>\n `,t.appendChild(n),t.appendChild(o);const a=o.querySelector("#mfm-copy-payload");a&&(a.onclick=()=>{navigator.clipboard.writeText(r).then((()=>{this.showSnackbar("Payload copied!")}))});const d=o.querySelector("#mfm-copy-response");return d&&(d.onclick=()=>{navigator.clipboard.writeText(i).then((()=>{this.showSnackbar("Response copied!")}))}),t.querySelector(".mfm-close").onclick=()=>this.toggleSheet(),t.querySelector(".mfm-back-btn").onclick=()=>{this.state.selectedRequest=null,this.updateUI()},t}formatBody(e){if(!e)return"";if("undefined"!=typeof FormData&&e instanceof FormData){const t={};return e.forEach(((e,n)=>{"undefined"!=typeof File&&e instanceof File?t[n]=`[File: ${e.name} (${e.size} bytes)]`:"undefined"!=typeof Blob&&e instanceof Blob?t[n]=`[Blob: ${e.type} (${e.size} bytes)]`:t[n]=e})),JSON.stringify(t,null,2)}if("undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams)return JSON.stringify(Object.fromEntries(e.entries()),null,2);if("undefined"!=typeof Blob&&e instanceof Blob)return`[${"undefined"!=typeof File&&e instanceof File?"File":"Blob"}: ${e.type||"binary"} (${e.size} bytes)]`;if(e instanceof ArrayBuffer||"undefined"!=typeof Uint8Array&&e instanceof Uint8Array)return`[Binary Data: ${e.byteLength||e.length} bytes]`;if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}generateCurl(e){const t=(e.method||"GET").toUpperCase();let n=`curl -X ${t} "${e.url}"`;if(e.requestHeaders&&Object.entries(e.requestHeaders).forEach((([e,t])=>{n+=` \\\n -H "${e}: ${t}"`})),e.requestBody&&"GET"!==t){const t=("string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody)).replace(/'/g,"'\\''");n+=` \\\n --data-raw '${t}'`}return n}generateFetch(e){const t=(e.method||"GET").toUpperCase();let n=`{\n method: "${t}",\n headers: ${(e.requestHeaders?JSON.stringify(e.requestHeaders,null,2):"{}").replace(/\n/g,"\n ")}`;if(e.requestBody&&"GET"!==t){const t="string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody,null,2);n+=`,\n body: ${"string"==typeof e.requestBody?"`"+t+"`":JSON.stringify(e.requestBody,null,2).replace(/\n/g,"\n ")}`}return n+="\n}",`fetch("${e.url}", ${n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error(error));`}generateXHR(e){const t=(e.method||"GET").toUpperCase();let n=`var xhr = new XMLHttpRequest();\nxhr.open("${t}", "${e.url}");\n`;if(e.requestHeaders&&Object.entries(e.requestHeaders).forEach((([e,t])=>{n+=`xhr.setRequestHeader("${e}", "${t}");\n`})),n+="\nxhr.onload = function() {\n console.log(xhr.responseText);\n};\n",e.requestBody&&"GET"!==t){const t="string"==typeof e.requestBody?e.requestBody:JSON.stringify(e.requestBody);n+=`\nxhr.send(${"string"==typeof e.requestBody?"`"+t+"`":JSON.stringify(e.requestBody)});`}else n+="\nxhr.send();";return n}addRequest(e,t){const n=String(t.method||"").toUpperCase().trim(),o=String(t.url||"");console.log(`[MockForMe] addRequest type=${e} method=${n} url=${o} requestId=${t.requestId} pending=${t.pending}`);const s=o.includes("api.mockforme.com")||o.includes("mockforme.com/proxy")||o.includes("mockforme.com/gateway")||f&&o.includes(String(f));if("OPTIONS"===n||s)return;const r="mocked"===e?this.mockedRequests:this.otherRequests,i="mocked"===e?this.otherRequests:this.mockedRequests;if(t.requestId){const n=r.findIndex((e=>e.requestId===t.requestId));if(-1!==n)return r[n]={...r[n],...t},void(this.state.isOpen&&this.state.activeTab===e&&this.updateUI());const o=i.findIndex((e=>e.requestId===t.requestId));if(-1!==o){const e={...i[o],...t};return i.splice(o,1),r.unshift(e),r.length>50&&r.pop(),void(this.state.isOpen&&this.updateUI())}}r.unshift(t),r.length>50&&r.pop(),this.state.isOpen&&this.state.activeTab===e&&this.updateUI()}};function $(e,t){if("undefined"!=typeof window){if(window._mfm_intercepted)return;window._mfm_intercepted=!0}!function(e,t){const n=window.fetch;window.fetch=function(o,s={}){const r="string"==typeof o?o:o.url,i=String(s.method||(o instanceof Request?o.method:"GET")).toUpperCase().trim();if("OPTIONS"===i)return console.log(`[MockForMe] Filtered ${i} ${r} (isInternal=false)`),n(o,s);const a={url:r,method:i,requestHeaders:s.headers||(o instanceof Request?o.headers:{}),requestBody:s.body||(o instanceof Request?"{binary/stream}":null),queryParams:L(r)};try{const c=new URL(r,window.location.origin),{pathname:h,search:f}=c,g=R({url:r,method:i}),b=Math.random().toString(36).substring(7),x=Date.now();if(t!==l&&g){if(M.addRequest("mocked",{...a,requestId:b,pending:!0,startTime:x,rule:g}),"delay"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(o,s);t.then((e=>{const t=Date.now()-x;M.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;M.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_redirect"===g.action)return new Promise(((e,t)=>{setTimeout((()=>{const t=n(g.config.redirectUrl,s);t.then((e=>{const t=Date.now()-x;M.addRequest("mocked",{...a,requestId:b,status:e.status,pending:!1,duration:t})})).catch((e=>{const t=Date.now()-x;M.addRequest("mocked",{...a,requestId:b,status:0,pending:!1,duration:t})})),e(t)}),1e3*g.config.value)}));if("delay_and_timeout"===g.action){const e=Number(g.config.value||0);if(0===e){const e=new AbortController,t=n(o,{...s,signal:e.signal});e.abort();const r=Date.now()-x;return M.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:r,aborted:!0}),t.catch((()=>Promise.reject(new Error("Fetch aborted by rules"))))}return new Promise(((t,n)=>{setTimeout((()=>{const e=Date.now()-x;M.addRequest("mocked",{...a,requestId:b,status:"Aborted",pending:!1,duration:e,aborted:!0}),n(new Error("Fetch timed out by rules"))}),1e3*e)}))}}const y=d(h,i),v=M.token||e;if(!y&&!g)return M.addRequest("other",{...a,requestId:b,pending:!0,startTime:x}),n(o,s).then((async e=>{const t=e.clone(),n=Date.now()-x;try{const o=await t.text();M.addRequest("other",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:o,pending:!1,duration:n})}catch(e){}return e})).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name||e.message.includes("aborted"),o="TimeoutError"===e.name||e.message.includes("timed out"),s=!n&&!o;throw M.addRequest("other",{...a,requestId:b,status:n?"Aborted":o?"Timeout":"Error",responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n,timeout:o,error:s}),e}));let k=r;g||(k=`${_()}${y.url}${f}`);let w={};if(!g){w={[u]:v,[m]:p,"x-mfm-adaptor":t};const e=q();e&&y._ack&&(w["x-mfm-key"]=`${e}-${y._ack}`),y?._ri_&&(w._ri_=y._ri_)}const T=o instanceof Request?o.headers:s.headers,S=new Headers(T||{});Object.entries(w).forEach((([e,t])=>{S.set(e,t)}));let I=s.body;if(!I&&o instanceof Request){const e=o.clone();"GET"!==e.method&&"HEAD"!==e.method&&(I=e.text())}let $={method:i,headers:S,credentials:"include",mode:"cors"};g&&($.credentials=o instanceof Request&&o.credentials||s.credentials||"same-origin",$.mode=o instanceof Request&&o.mode||s.mode||"cors");const L=e=>(e&&($.body=e),t===l?(M.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:x}),(({actualUrl:e,url:t,method:n,headers:o,body:s,credentials:r,mode:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);return new Promise(((t,l)=>{window.addEventListener("message",(function e(n){if(n.source!==window)return;const o=n.data;if("INTERCEPTED_RESPONSE"===o?.type&&o?.requestId===d){if(window.removeEventListener("message",e),o?.response?.error)return l(new Error(o.response.message));const{status:n,headers:s,body:r}=o.response;t(new Response(r,{status:n,headers:s}))}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:o,body:s,credentials:r,mode:i,rule:a}},"*")}))})({actualUrl:r,url:k,method:i,headers:Object.fromEntries(S.entries()),body:e,credentials:$.credentials,mode:$.mode,rule:g}).then((e=>(e.clone().text().then((t=>{const n=Date.now()-x;M.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(e.headers.entries()),responseBody:t,pending:!1,duration:n})})),e))).catch((e=>{const t=Date.now()-x,n="AbortError"===e.name;throw M.addRequest("mocked",{...a,requestId:b,status:n?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:n}),e}))):(M.addRequest("mocked",{...a,requestBody:e||a.requestBody,requestId:b,pending:!0,startTime:x}),n(k,$).then((e=>{const t=e.clone(),n=t.headers.get("content-type")||"",o=Date.now()-x,s=n=>(M.addRequest("mocked",{...a,requestId:b,status:e.status,responseHeaders:Object.fromEntries(t.headers.entries()),responseBody:n,pending:!1,duration:o}),new Response("string"==typeof n?n:JSON.stringify(n),{status:e.status,headers:t.headers}));return n.includes("application/json")?t.json().then(s):t.text().then(s)})).catch((e=>{console.warn("mockforme fetch interceptor error",e);const t=Date.now()-x,r="AbortError"===e.name;return M.addRequest("mocked",{...a,requestId:b,status:r?"Aborted":0,responseHeaders:{},responseBody:e.message,pending:!1,duration:t,aborted:r}),n(o,s)}))));return I instanceof Promise?I.then((e=>L(e))):L(I)}catch(e){return console.warn("mockforme fetch interceptor error",e),n(o,s)}}}(e,t),function(e,t){const n=window.XMLHttpRequest;window.XMLHttpRequest=class{constructor(){return this._xhr=new n,this._headers={},this._method=null,this._url=null,this._body=null,this._isMocked=!1,this._captured=!1,this._requestId=null,this._startTime=null,this._capture=(e,t,n,o={})=>{if(this._captured)return;this._captured=!0;const s=this._isMocked?"mocked":"other",r=this._startTime?Date.now()-this._startTime:0;console.log(`[MockForMe] XHR Capture: status=${e} method=${this._method} url=${this._url} duration=${r}ms`);const i=e=>{const t=`on${e}`;(this[t]||this._xhr[t])?.(),this._xhr.dispatchEvent(new Event(e))};if(M.addRequest(s,{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,status:e,responseBody:t,responseHeaders:n,pending:!1,duration:r,...o}),"Aborted"!==e)try{Object.defineProperty(this,"responseText",{value:t,configurable:!0}),Object.defineProperty(this,"response",{value:t,configurable:!0}),Object.defineProperty(this,"status",{value:Number(e)||0,configurable:!0}),Object.defineProperty(this,"statusText",{value:200==e?"OK":"",configurable:!0})}catch(e){console.warn("[MockForMe] Error setting XHR props",e)}Object.defineProperty(this,"readyState",{value:4,configurable:!0}),i("readystatechange"),"Aborted"===e||o.aborted?i("abort"):0===e||"Error"===e||o.error?i("error"):"Timeout"===e||o.timeout?i("timeout"):i("load")},new Proxy(this,{get:(e,t)=>{if(t in e)return e[t];const n=e._xhr[t];return"function"==typeof n?n.bind(e._xhr):n},set:(e,t,n)=>t in e?(e[t]=n,!0):(e._xhr[t]=n,!0)})}open(e,t,n=!0,o=null,s=null){this._method=(e||"").toUpperCase().trim();try{this._url=new URL(t,window.location.href).toString()}catch(e){this._url=t}return console.log(`[MockForMe] XHR.open: ${this._method} ${this._url}`),this._xhr._mockOriginalUrl=this._url,this._xhr._mockMethod=this._method,this._xhr.open(e,t,n,o,s)}setRequestHeader(e,t){return this._headers[e]=t,this._xhr.setRequestHeader(e,t)}send(o){const s=String(this._method||"").toUpperCase().trim();if(console.log(`[MockForMe] XHR send method=${s} url=${this._url}`),"OPTIONS"===s)return console.log("[MockForMe] XHR Skip OPTIONS"),this._xhr.send(o);this._body=o,this._requestId=Math.random().toString(36).substring(7),this._startTime=Date.now(),this._xhr.addEventListener("abort",(()=>{this._capture("Aborted","Request aborted",{},{aborted:!0})}),{once:!0}),this._xhr.addEventListener("error",(()=>{this._capture(0,"Network Error",{},{error:!0})}),{once:!0}),this._xhr.addEventListener("timeout",(()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})}),{once:!0});try{const s=new URL(this._url),{pathname:r,search:i}=s,a=R({url:this._url,method:this._method});a&&(this._isMocked=!0,M.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime,rule:a}));const c=M.token||e;if(t!==l&&a){if("delay"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_redirect"===a.action){const e=Number(a.config.value||0);return setTimeout((()=>{this._xhr.open(this._method,a.config.redirectUrl,!0),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(this._body)}),1e3*e)}if("delay_and_timeout"===a.action){const e=Number(a.config.value||0);return 0===e?(this._capture("Aborted","Request aborted by rules",{},{aborted:!0}),void this._xhr.abort()):setTimeout((()=>{this._capture("Aborted","Request timed out by rules",{},{aborted:!0}),this._xhr.abort()}),1e3*e)}}const h=d(r,this._method);if(!h&&!a)return console.log("[MockForMe] XHR No Match:",{pathname:r,method:this._method}),this._isMocked=!1,M.addRequest("other",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),this._xhr.addEventListener("load",(()=>{this._capture(this._xhr.status,this._xhr.responseText,this._xhr.getAllResponseHeaders())}),{once:!0}),this._xhr.send(o);console.log("[MockForMe] XHR Matched:",{pathname:r,match:h}),this._isMocked=!0;let f=this._url;a||(f=`${_()}${h.url}${i}`);let g={};if(!a){g={[u]:c,[m]:p,"x-mfm-adaptor":t};const e=q();e&&h._ack&&(g["x-mfm-key"]=`${e}-${h._ack}`),h?._ri_&&(g._ri_=h._ri_)}const b={...this._headers,...g};if(M.addRequest("mocked",{url:this._url,method:this._method,requestHeaders:this._headers,requestBody:this._body,queryParams:L(this._url),requestId:this._requestId,pending:!0,startTime:this._startTime}),t===l)(({actualUrl:e,url:t,method:n,headers:o,body:s,onSuccess:r,onError:i,rule:a})=>{const d=`${Date.now()}-${Math.random()}`,c=E(t);window.addEventListener("message",(function e(t){if(t.source!==window)return;const n=t.data;if("INTERCEPTED_RESPONSE"===n.type&&n.requestId===d){if(window.removeEventListener("message",e),n.response?.error)return i?.(n.response.message);const{body:t,status:o,headers:s}=n.response;r?.({body:t,status:o,headers:s})}})),window.postMessage({type:"REQUEST_INTERCEPTED",requestId:d,payload:{actualUrl:e,url:c,method:n,headers:o,body:s,credentials:"same-origin",mode:"cors",rule:a}},"*")})({actualUrl:this._url,url:f,method:this._method,headers:b,body:this._body,rule:a,onSuccess:({body:e,status:t,headers:n})=>{this._capture(t,e,n||{})},onError:e=>{this._capture(0,e.message||"XHR Error",{},{error:!0})}});else{const e=new n;e.open(this._method,f,!0);for(const t in b)e.setRequestHeader(t,b[t]);e.withCredentials=this.withCredentials,e.onload=()=>{this._capture(e.status,e.responseText,e.getAllResponseHeaders())},e.onerror=()=>{this._capture(0,"Network Error",{},{error:!0})},e.ontimeout=()=>{this._capture("Timeout","Request timed out",{},{timeout:!0})},e.send(this._body)}}catch(e){if(console.error("[MockForMe] XHR Interception Error",e),!this._xhr.readyState||1===this._xhr.readyState)return this._xhr.send(o)}}}}(e,t)}function L(e){try{const t=new URL(e,window.location.origin).searchParams;return Object.fromEntries(t.entries())}catch{return{}}}const O=(e,t=null,o=c)=>{if(n())return;if(o===c)try{e&&("undefined"!=typeof localStorage&&localStorage.removeItem("mockforme-token"),M.token=e,M.isAuthenticated=!0),!e&&M.token&&(e=M.token),M.onAuthSuccess=(e,t,n)=>{i(t),k(n),M.updateMappings(t,n)}}catch(e){console.log("Error in showing mockforme widget",e)}t||={mappings:[],_o:null,mk:null,_mck:null,_rules:[]};const{mappings:s=[],_o:r,mk:l,_mck:h,_rules:f=[]}=t;return i(s),k({_o:r,mk:l,_mck:h,_rules:f}),{run:(t,n=()=>{})=>{$(e,o),r&&l?(M.mappings=s,M.rules=f,t?.(s,{_o:r,mk:l,_mck:h,_rules:f})):e&&function(e,n,o,s){T({method:"get",url:g,async:!1,headers:{[u]:e,[m]:p,"x-mfm-adaptor":n}},(e=>{if(e)try{const{di:s,iv:r,_o:a,_mck:d,mk:c,r:l}=e,m=S({di:s,iv:r},a);k({_o:a,mk:c,_mck:d,_rules:l}),m&&(i(m),n=m,o={_o:a,mk:c,_mck:d,_rules:l},M.updateMappings(n,o),t?.(n,o))}catch(e){return void s(new Error("Unable to fetch mocked apis"))}var n,o}),(e=>{console.log("Error in loading mocked apis"),s(e)})).request()}(e,o,0,n)},checkIfApiToBeMocked:d,getMappings:a,doFetchMappings:(t,n)=>{I(e||M.token,o,((e,n)=>{M.updateMappings(e,n),t?.(e,n)}),n)}}},C=n();C||(globalThis.mockforme=O);let H=O;C&&(H=e=>({run:()=>{}}));const U=H;return t.mockforme})()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mockforme",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "MockForMe is a fast and simple mock API solution for CSR and SSR apps. Create mock APIs in 2 minutes with support for delays, conditional responses, public URLs, random data, variables, Faker, and postFunctions — all designed to boost developer productivity.",
|
|
5
5
|
"main": "dist/mockforme.client.cjs.js",
|
|
6
6
|
"module": "dist/mockforme.client.esm.js",
|